这几天一直在逆向某个APP,发现该APP很多监控数据都是通过某单例调用dispatch_async(queue, block)在block中同步到服务器上去的。以往某个带有block参数的函数在IDA或者hooper中都会相应地给出__function_invokeblock 这样的汇编代码块。但是在很多次跟dispatch_async这样的方法时,没有(及时)发现相对应的block代码块,搞的自己很郁闷,不知道应该怎么来下断点继续跟踪,于是决定今天研究一下这块的block对应的代码块怎么在内存中获取。
首先看了一下block的声明方式,参考文章Block的实现。文章中说道Block可以理解为一个结构体,结构体的简单表示为:
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
};
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
};
static struct __main_block_desc_0 {
size_t reserved;
size_t Block_size;
};
其中我最关注的是 void *FuncPtr; 这个函数指针,因为他可以告诉我当dispatch_async(queue, block)执行之后,下一步的断点应该下在哪一行。看一下调用它的汇编代码吧
dispatch_async(queue, block) 中共有两个参数,第一个queue是r0, 第二个参数是r1,结果r1直接被赋值了一个sp的地址。断点停留在0x2e66c上的时候,查看了一下r1寄存器的情况,如图所示:
表明了r1是一个 NSStackBlock类型的对象(不知道这样描述是否正确),然后读取了一下0xa2fbfc这块的内存数据,如图:
根据上面说的block的结构,第一个数据应该为 block的 isa指针,po一下,如图:
然后依次推导,0xe469d就是block代码块的函数指针。于是从该位置下断点,发现确实程序从该处停下来了。
再次验证,0xe469d - 0xb6000(ASLR) = 0x2e69d
正好也是函数开始的地方。这样整个dispatch_async(queue, block)中block的调用过程算是弄明白了,如何从dispatch_async切入,定位到第二个参数block的实现,也有章可循了。
有什么不对的地方大家多多指出来,一起讨论讨论。