iOS Swift逆向——被编译优化后的函数参数调用约定修复

头文件导入:

cpp 复制代码
typedef long long s64;
typedef unsigned long long u64;

typedef s64 Int;
typedef u64 Bool;

struct Swift::String
{
  u64 _countAndFlagsBits;
  void *_object;
};

union Swift_ElementAny {
    Swift::String stringElement;
};

struct Swift_Any {
    Swift_ElementAny element;
    u64 unknown;
    s64 type;
};

struct Swift_ArrayAny {
    s64 length;
    Swift_Any *items;
};

https://github.com/doronz88/swift_reversing

https://github.com/doronz88/ida-scripts/blob/main/swift.py

Swift <=> OC的兼容层

小gadget(片段)

cpp 复制代码
; void sub_101A34B60()
sub_101A34B60
MOV             X0, X20 ; id
B               _objc_release
; End of function sub_101A34B60

因为_objc_release的参数只要X0。修复为:

cpp 复制代码
void __usercall sub_101A34B60(__int64 a1@<X20>)

根据swift官方文档,X20是self。所以这是内存引用计数-1。

https://github.com/swiftlang/swift/blob/13da4c2ad3077282f7c8922b293a74c32e79e428/docs/ABI/CallConvSummary.rst#L151


字符串操作

字符串对象由两个寄存器构成。

以String.append为例进行修复调用约定:

X20是self。

cpp 复制代码
Swift::Void __usercall String.append(_:)(void *a1@<X20>, Swift::String a2@<X0:X1>)
{
  __imp__$sSS6appendyySSF(a2._object, a2._countAndFlagsBits);
}
cpp 复制代码
Swift::String __usercall dispatch thunk of CustomStringConvertible.description.getter@<X0:X1>(
        void *a1@<X20>,
        __int64 *a2@<X0>,
        __int64 *a3@<X1>)

a=100;print("myIntVariable1= \(a)")的整理后IDA结果:

cpp 复制代码
  v11 = 0xD000000000000010LL;                   // myIntVariable1= 
  v12 = (id)0x800000010000CDF0LL;
  v10 = 100LL;
  v3 = dispatch thunk of CustomStringConvertible.description.getter(
         &v10,
         &type metadata for Int,
         &protocol witness table for Int);
  String.append(_:)(&v11, (Swift::String)__PAIR128__(v3._countAndFlagsBits, (unsigned __int64)v3._object));
  swift_bridgeObjectRelease(v3._object);
  v4 = v11;
  v5 = v12;
  *(_QWORD *)(v2 + 56) = &type metadata for String;
  *(_QWORD *)(v2 + 32) = v4;
  *(_QWORD *)(v2 + 40) = v5;
  v15._countAndFlagsBits = 32LL;
  v15._object = (void *)0xE100000000000000LL;
  v18._countAndFlagsBits = 10LL;
  v18._object = (void *)0xE100000000000000LL;
  print(_:separator:terminator:)((Swift_ArrayAny *)v2, v15, v18);
  swift_release(v2);

字符串Array

Swift::String __usercall BidirectionalCollection___joined_separator__@<X0:X1>(Swift_ArrayAny@<X20:X21>, Swift::String@<X0:X1>, __int64@<X2>, __int64@<X3>)

栈内存偏移计算相关小函数

原先的函数点开里面是空的。

cpp 复制代码
sub_101A348B8
LDUR            X8, [X0,#-8]
LDR             X8, [X8,#0x40]
MOV             X9, SP
ADD             X8, X8, #0xF
AND             X8, X8, #0xFFFFFFFFFFFFFFF0
SUB             X19, X9, X8
MOV             SP, X19
RET
; End of function sub_101A348B8

定义结构体:

cpp 复制代码
00000000 stack_offset_struc struc ; (sizeof=0x18, copyof_7619)
00000000 offset          DCQ ?
00000008 sp              DCQ ?
00000010 addr            DCQ ?
00000018 stack_offset_struc ends

定义调用约定。F5之后,函数体原来出现内容。

cpp 复制代码
stack_offset_struc __usercall __spoils<> sub_101A348B8@<0:X8, 8:X9, 16:X19>(__int64 a1@<X0>)
{
  __int64 *v1; // x9
  __int64 v2; // x8
  char *v3; // x19
  __int64 v4; // [xsp+0h] [xbp+0h] BYREF
  stack_offset_struc result; // 0:x8.16,16:x19.8

  v1 = &v4;
  v2 = (*(_QWORD *)(*(_QWORD *)(a1 - 8) + 64LL) + 15LL) & 0xFFFFFFFFFFFFFFF0LL;
  v3 = (char *)&v4 - v2;
  result.addr = (__int64)v3;
  result.sp = (__int64)v1;
  result.offset = v2;
  return result;
}

另一个函数也进行修复:

cpp 复制代码
sub_101A34B84
LDUR            X28, [X0,#-8]
LDR             X8, [X28,#0x40]
MOV             X9, SP
ADD             X8, X8, #0xF
AND             X8, X8, #0xFFFFFFFFFFFFFFF0
RET
cpp 复制代码
stack_offset_struc __usercall __spoils<> sub_101A34B84@<0:X8, 8:X9, 16:X28>(__int64 a1@<X0>)
{
  __int64 v1; // x28
  __int64 *v2; // x9
  __int64 v3; // x8
  __int64 v4; // [xsp+0h] [xbp+0h] BYREF
  stack_offset_struc result; // 0:x8.16,16:x28.8

  v1 = *(_QWORD *)(a1 - 8);
  v2 = &v4;
  v3 = (*(_QWORD *)(v1 + 64) + 15LL) & 0xFFFFFFFFFFFFFFF0LL;
  result.addr = v1;
  result.sp = (__int64)v2;
  result.offset = v3;
  return result;
}

这两个小函数,其实是完全一样的。

可以发现第二个函数sub_101A34B84相比较于sub_101A348B8少了最后的SUB。其实这个SUB在上层调用者函数里面,看最后两行汇编:

cpp 复制代码
STP             X28, X27, [SP,#-0x10+var_50]!
STP             X26, X25, [SP,#0x50+var_40]
STP             X24, X23, [SP,#0x50+var_30]
STP             X22, X21, [SP,#0x50+var_20]
STP             X20, X19, [SP,#0x50+var_10]
STP             X29, X30, [SP,#0x50+var_s0]
ADD             X29, SP, #0x50
SUB             SP, SP, #0x70
MOV             X20, X3
MOV             X23, X2
MOV             X24, X1
MOV             X25, X0
ADRL            X21, unk_1039839C0
MOV             X0, X21
BL              sub_1000B6ED0
BL              sub_101A348B8
BL              sub_101A34D54
MOV             X22, X0
BL              sub_101A34B84
SUB             X27, X9, X8
MOV             SP, X27

修复完成后,返回调用该函数的上层函数,在按F5。可以看到恢复之后的代码。

参考IDA官网博客里面的内容:

https://hex-rays.com/blog/author/igor-skochinsky

相关推荐
Ajiang28247353041 小时前
对于C++中stack和queue的认识以及priority_queue的模拟实现
开发语言·c++
幽兰的天空1 小时前
Python 中的模式匹配:深入了解 match 语句
开发语言·python
Theodore_10224 小时前
4 设计模式原则之接口隔离原则
java·开发语言·设计模式·java-ee·接口隔离原则·javaee
----云烟----6 小时前
QT中QString类的各种使用
开发语言·qt
lsx2024066 小时前
SQL SELECT 语句:基础与进阶应用
开发语言
开心工作室_kaic7 小时前
ssm161基于web的资源共享平台的共享与开发+jsp(论文+源码)_kaic
java·开发语言·前端
向宇it7 小时前
【unity小技巧】unity 什么是反射?反射的作用?反射的使用场景?反射的缺点?常用的反射操作?反射常见示例
开发语言·游戏·unity·c#·游戏引擎
武子康7 小时前
Java-06 深入浅出 MyBatis - 一对一模型 SqlMapConfig 与 Mapper 详细讲解测试
java·开发语言·数据仓库·sql·mybatis·springboot·springcloud
转世成为计算机大神7 小时前
易考八股文之Java中的设计模式?
java·开发语言·设计模式
宅小海7 小时前
scala String
大数据·开发语言·scala