逆向中少不了各种 Hook:Hook C 函数、Hook OC 方法,以及 Hook Block。C 函数在乎的是 C 函数的符号(Symbol)标识、OC 方法在乎的是 SEL 中的各个段,Block 就不一样的了、在乎的是有多少个参数以及具体的参数的类型。不装了、我们继续吧...
对于本次研究的主题,请看图片:

代码也很简单,也贴一下:
正向代码
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
// 调用方法
[self beta_blockWithName:@"CoderHG" block:^(NSDictionary * dict, int age) {
NSLog(@"beta_blockWithName -: %@, %d", dict, age);
}];
}
// 自定义方法
- (void)beta_blockWithName:(NSString*)name
block:(void (^)(NSDictionary*, int age))block {
block(@{@"KEY":@"VALUE"}, 18);
}
@end
逆向代码:
%hook ViewController
// hook
- (void)beta_blockWithName:(NSString*)name block:(id)block {
NSLog(@"Hook && beta_blockWithName = %@, %@", name, block);
%orig;
}
%end
假装我们没有看到过 正向代码,那么问题来了,如果在 逆向 中解析出 block 的参数个数以及各自的参数类型呢?
其实这个在 17 年 的时候就有大神出过文章:<逆向中获取 Block 的参数和返回值>(文章末尾有连接),这篇文章很棒,在其中收获很多,但是我按照这篇文章的步骤操作,最终还差那么一点点就成功了。
比如在操作的过程中,这个指令就困惑了我不少的时间:
(lldb) p (char *)0x0000000100dfec18
后来在 <iOS 使用NSMethodSignature和 NSInvocation进行 method 或 block的调用>, 因为这篇文章提到了 Aspects。我滴个神呐,一下子就豁然开朗了。在中看到了这一句代码:
const char *signature = (*(const char **)desc);
将上面的指令换成这样的:
(lldb) p (*(const char **)0x0000000100dfec18)
厉害了,尽然 成功 了。
后来经过一番的折腾,在逆向代码中添加这些代码:
// Block internals.
typedef NS_OPTIONS(int, AspectBlockFlags) {
AspectBlockFlagsHasCopyDisposeHelpers = (1 << 25),
AspectBlockFlagsHasSignature = (1 << 30)
};
typedef struct _AspectBlock {
__unused Class isa;
AspectBlockFlags flags;
__unused int reserved;
void (__unused *invoke)(struct _AspectBlock *block, ...);
struct {
unsigned long int reserved;
unsigned long int size;
// requires AspectBlockFlagsHasCopyDisposeHelpers
void (*copy)(void *dst, const void *src);
void (*dispose)(const void *);
// requires AspectBlockFlagsHasSignature
const char *signature;
const char *layout;
} *descriptor;
// imported variables
} *AspectBlockRef;
哦豁、不装了,直接上图吧。

最后还发现一个问题,这种 lldb 指令在现在的 Xcode 中已经不能使用了:
po [NSMethodSignature signatureWithObjCTypes:"v28@?0q8@"NSDictionary"16B24"]
执行直接报错,其实这个直接 copy 到源代码中运行就可以看到具体的类型了。
看到这里应该会很懵,因为跳过了很多的细节,强烈建议看看下面这两篇文章。
感谢这两篇文章才让我彻底的弄明白、大家也去阅读,相信当你看完之后也能收获多多:
逆向中获取 Block 的参数和返回值
iOS 使用NSMethodSignature和 NSInvocation进行 method 或 block的调用
题外话、题外话、题外话,这是我初期的一个小实验,以供各位大神欣赏:

图中有一个亮点是在 hook 代码中,我故意将 block 的参数多弄了一个,然后尽然能正常运行,但是会随机的 crash。这也间接的说明一个问题,如果这个 block 实际的参数都是 id 类型的话,可以一次性弄他个 N 多个,运行起来几乎都能猜中具体的参数个数以及参数类型了。在试验的过程中还发现,hook 中的参数少于实际的参数也是可以的。但是如果有类型对应不上的话肯定会 crash。