调用rva2data函数将相对虚拟地址(RVA)转换为实际的数据指针,指针无效返回失败。

判断字节补丁存在,计算补丁的结束地址patch_end和4字节对齐的代码结束地址和code_end,计算补丁大小patch_size,创建补丁的数据缓冲patchBytes,memcpy复制原始代码到补丁缓冲区,用hex2bytes将十六进制字符串转换为字节数据失败返回。

获取目标代码段地址和数据,保存桥接代码的地址到变量InstrumentBridgeRVA,获取数据段地址和数据。

初始化static-inline.h里定义的结构体StaticInlineHookBlock,for遍历Hook块数组,检查是否已经Hook过该地址,检查地址是否被其他Hook占用,找到空闲的Hook块,用dobby_create_instrument_bridge创建桥接代码,更新目标地址。

调用dobby_static_inline_hook函数执行实际的Hook操作,如果Hook失败则返回错误信息。当存在补丁数据时,更新Hook块的信息,记录补丁大小( patch_size ),计算并存储补丁的哈希值( patch_hash )。使用 NSFileManager 创建必要的目录Documents/static-inline-hook,将修改后的Mach-O文件写入新位置,如果写入失败则返回错误信息。将新文件路径保存到全局字典 gStaticInlineHookMachO 中。

而ActiveCodePatch函数和DeactiveCodePatch函数分别是激活修补、停用修补。
前面的代码都一样,通过路径查找已加载的模块基址,查找对应地址的Hook块,验证补丁哈希值,确保补丁内容未被修改,ActiveCodePatch是设置目标替换地址为基址加上补丁的虚拟地址,而DeactiveCodePatch清空目标替换地址。

Static-inline Hook修补基址总结:
-
创建新的Segment Command,创建__HOOK_TEXT段用于存放代码,创建__HOOK_DATA段用于存放数据,设置正确的段属性和权限,配置段命令(LC_SEGMENT_64)的各项参数
-
分配内存页对齐的空间,确保所有地址都按页面大小(通常是4KB)对齐,为代码段分配可执行内存空间,为数据段分配可读写内存空间,考虑虚拟内存映射的要求
-
写入Hook代码,将Hook代码写入__HOOK_TEXT段,设置正确的指令跳转,处理代码重定位,确保代码的4字节对齐
-
更新加载命令,增加新的加载命令,更新段表信息,修改文件头信息,维护符号表和重定位信息
本作品采用 知识共享署名 4.0 国际许可协议 进行许可
文章评论