iOS-Debug-Hacks involves the dynamic debugging, static analysis and decompile of third-party libraries.git
《Advanced Apple Debugging & Reverse Engineering》值得一看github
谈谈对函数的理解bootstrap
一个函数调用包括将数据(以参数和返回值的形式)和控制从代码的一部分转移到另外一部分。在函数调用过程当中,数据传递、局部变量的分配和释放是经过栈来实现的,而为单个函数调用分配的那部分栈称为栈帧(Stack Frame)。函数
(lldb) bt * thread #1, stop reason = trace * frame #0: 0x000000010e533fcf dyld`_dyld_debugger_notification + 1 frame #1: 0x000000010e533795 dyld`gdb_image_notifier(dyld_image_mode, unsigned int, dyld_image_info const*) + 111 frame #2: 0x000000010a36b269 dyld_sim`notifyGDB(dyld_image_states, unsigned int, dyld_image_info const*) + 40 frame #3: 0x000000010a364142 dyld_sim`dyld::notifyBatchPartial(dyld_image_states, bool, char const* (*)(dyld_image_states, unsigned int, dyld_image_info const*), bool, bool) + 814 frame #4: 0x000000010a36d107 dyld_sim`ImageLoader::link(ImageLoader::LinkContext const&, bool, bool, bool, ImageLoader::RPathChain const&, char const*) + 101 frame #5: 0x000000010a364548 dyld_sim`dyld::link(ImageLoader*, bool, bool, ImageLoader::RPathChain const&, unsigned int) + 161 frame #6: 0x000000010a365a5c dyld_sim`dyld::_main(macho_header const*, unsigned long, int, char const**, char const**, char const**, unsigned long*) + 3840 frame #7: 0x000000010a3613d4 dyld_sim`start_sim + 136 frame #8: 0x000000010e52bded dyld`dyld::useSimulatorDyld(int, macho_header const*, char const*, int, char const**, char const**, char const**, unsigned long*, unsigned long*) + 2200 frame #9: 0x000000010e5297a3 dyld`dyld::_main(macho_header const*, unsigned long, int, char const**, char const**, char const**, unsigned long*) + 436 frame #10: 0x000000010e5253d4 dyld`dyldbootstrap::start(macho_header const*, int, char const**, long, macho_header const*, unsigned long*) + 453 frame #11: 0x000000010e5251d2 dyld`_dyld_start + 54
bt 命令是得益于栈帧才能实现的,栈帧能够当作是函数执行的上下文,其中保存了函数的返回地址和局部变量,咱们知道堆是从低地址向高地址延伸的,而栈是从高地址向低地址延伸的。
每一个函数的每次调用,都会分配给它一个独立的栈帧,rbp 寄存器指向当前栈帧的底部(高地址),被称做帧指针,rsp 寄存器指向栈帧的顶部(低地址),被称做栈指针spa
上述 2 和 3 步骤其实就是 call 指令的任务,而 4 和 5 经过汇编指令表示以下:线程
0x1054e09c0 <+0>: pushq %rbp //第四步 0x1054e09c1 <+1>: movq %rsp, %rbp //第五步
call function
参数中的 function 是 TEXT 段的程序,
call 指令其实能够拆解成两步,debug
call 指令其实等价于下面的命令:3d
push next_instruction jmp function
在 OSX 中,最多能够有 6 个整型(整数和指针)经过寄存器传递,这 6 个寄存器分别是 rdi, rsi, rdx, rcx, r8 和 r9(顺序和参数的顺序保持一致),指针
此时就须要借助栈了,能够将剩下的参数逆序压入栈中。OSX 容许将 8 个浮点数经过浮点数寄存器 xmm0-xmm7 进行传递。code
使用 rax 寄存器做为整数返回值,浮点数返回值则使用 xmm0-xmm1 寄存器
当寄存器的值是字符串的时候,LLDB 能够经过
po (char *) $rsi
命令输出寄存器对应的字符串值,
不然直接使用 po $rsi
,只会按照整数格式输出 rsi 寄存器的值。
消息转发流程越日后,处理消息所付出的代价也就越大。因此若非必要,应当尽早结束消息转发流程。若是消息转发的流程中都没有处理未知消息,最终会调用 doesNotRecognizeSelector: 抛出异常,表示对象没法正确识别此 SEL。
@dynamic
使用 @dynamic 告诉编译器不作处理,而后 Getter 和 Setter 方法是在运行时动态建立
br s 是 breakpoint set 的意思,-n 表示根据函数名称来下断点,做用和符号断点同样,
br s -F
b -[TCWebViewKit open]
只不过 b 命令是 _regexp-break 的缩写,是用正则匹配的方式下断点。
-给在指定的内存地址设置断点,
下断点的命令为 br s -a 0x000000010940b24e
,这种方式能够用在知道 block 内存地址的时候,给 block 设置断点。