头文件导入:
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。
字符串操作
字符串对象由两个寄存器构成。
以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官网博客里面的内容: