先贴个图
1.原理,Block的内存结构,出自Clang编译器的官方文档
参考了https://blog.csdn.net/u013378438/article/details/87167133
struct Block_literal_1 {
void *isa; // initialized to &_NSConcreteStackBlock or &_NSConcreteGlobalBlock
int flags;
int reserved;
void (*invoke)(void *, ...);
struct Block_descriptor_1 {
unsigned long int reserved; // NULL
unsigned long int size; // sizeof(struct Block_literal_1)
// optional helper functions
void (*copy_helper)(void *dst, void *src); // IFF (1<<25)
void (*dispose_helper)(void *src); // IFF (1<<25)
// required ABI.2010.3.16
const char *signature; // IFF (1<<30)
} *descriptor;
// imported variables
};
在 64 位系统上,指针类型的大小是 8 个字节,而 int 是 4 个字节,
因此,invoke 函数指针的地址就是在第 16 个字节之后。我们可以通过 lldb 的 memory 命令来打印出指定地址的内存,我们上面已经得到了 block 的地址,现在就打印出它的内存内容
memory read --size 8 --format x 0x16d1a6050
如前所述,函数指针的地址是在第 16 个字节之后,并占用 8 个字节,所以可以得到函数的地址是 0x0000000105e014a8。
找出 Block 的函数签名
要找出 Block 的函数签名,需要通过 descriptor
结构体中的 signature
成员,然后通过它得到一个 NSMethodSignature
对象。
首先,需要找到 descriptor
结构体。这个结构体在 Block 中是通过指针持有的,它的位置正好在 invoke
成员后面,占用 8 个字节。可以从上面的内存打印中看到 descriptor
指针的地址是 0x0000000106336398。
为了找出 signature
的地址,我们还需要确认这个 Block 是否拥有 copy_helper
和 disponse_helper
这两个可选的函数指针。由于 ((0xc2000000 & (1 << 25)) != 0)
,因此我们可以确认这个 Block 拥有刚刚提到的两个函数指针。
现在可以总结下:signature
的地址是在 descriptor
下偏移两个 unsiged long 和两个指针后的地址,即 32 个字节后。现在让我们找出它的地址,并打印出它的字符串内容:
memory read --size 8 --format x 0x00000001075496c2
看到这一串乱码是不是觉得有点崩溃,折腾了半天,怎么打印出这么一串鬼东西,虽然里面有一个熟悉的 NSDictionary,但是其它的东西完全看不懂啊。
不要慌,这确实就是一个函数签名,只是我们需要通过 NSMethodSignature
找出它的参数类型:
po [NSMethodSignature signatureWithObjCTypes:"v16@?0@\"XSearchService\"8"]
注意在双引号”前加转义\
(lldb) memory read --size 8 --format x 0x16d1a6050
0x16d1a6050: 0x00000001b6ca1e88 0x00000000c2000000
0x16d1a6060: 0x0000000105e014a8 0x0000000106336398
0x16d1a6070: 0x00000001c4329ba0 0x00000001c4329ba0
0x16d1a6080: 0x4094615555555555 0x0000000000000000
(lldb) memory read --size 8 --format x 0x0000000106336398
0x106336398: 0x0000000000000000 0x0000000000000028
0x1063363a8: 0x0000000105e014dc 0x0000000105e014e8
0x1063363b8: 0x00000001075496c2 0x0000000000000001
0x1063363c8: 0x0000000000000000 0x0000000000000028
(lldb) p (char *)0x00000001075496c2
(char *) $0 = 0x00000001075496c2 "v16@?0@"XSearchService"8"
(lldb) po [NSMethodSignature signatureWithObjCTypes:"v16@?0@\"XSearchService\"8"]
<NSMethodSignature: 0x1c1a71100>
number of arguments = 2
frame size = 224
is special struct return? NO
return value: -------- -------- -------- --------
type encoding (v) 'v'
flags {}
modifiers {}
frame {offset = 0, offset adjust = 0, size = 0, size adjust = 0}
memory {offset = 0, size = 0}
argument 0: -------- -------- -------- --------
type encoding (@) '@?'
flags {isObject, isBlock}
modifiers {}
frame {offset = 0, offset adjust = 0, size = 8, size adjust = 0}
memory {offset = 0, size = 8}
argument 1: -------- -------- -------- --------
type encoding (@) '@"XSearchService"'
flags {isObject}
modifiers {}
frame {offset = 8, offset adjust = 0, size = 8, size adjust = 0}
memory {offset = 0, size = 8}
class 'XSearchService'
对我们最有用的 type encoding 字段,这些符号对应的解释可以参考 Type Encoding 官方文档 83。
这里还有个方法签名的相关原理
最终可还原的参数为
%hook TBSRPXSearchService
- (void)doSearchStart:(id)arg1 Success:(void (^)(id response))completionHandler Fail:(id)arg3 isMore:(_Bool)arg4{
NSLog(@"gouzhule3333");
NSLog(@"%@",[NSThread callStackSymbols]);
//或者在这里加个断点,运行时看调用栈吧
void (^interception)(XSearchService *response) = ^(XSearchService *response){
NSLog(@"%@", response);
completionHandler(response);
};
%orig(arg1,completionHandler,arg3,arg4);
}
%end