动态符号绑定:
__nl_symbol_ptr是指向非延迟绑定数据的指针数组(这些指针在加载库时绑定)。
__la_symbol_ptr是指向导入函数的指针数组,通常在第一次调用该符号时由名为dyld_stub_binder的例程填充(也可以在启动时告诉dyld绑定这些指针)。
!symtab_cmd || !dysymtab_cmd || !linkedit_segment || !dysymtab_cmd->nindirectsyms
// 找到linkedit的头地址
uintptr_t linkedit_base = (uintptr_t)slide + linkedit_segment->vmaddr - linkedit_segment->fileoff;
dyld: the dynamic link editor
动态链接的基本实现:
LC_LOAD_DYLINKER 指定dyld路径
LC_LOAD_DYLIB 加载dylib
dyld加载命令:
struct dylinker_command {
uint32_t cmd; /* LC_ID_DYLINKER, LC_LOAD_DYLINKER or LC_DYLD_ENVIRONMENT */
uint32_t cmdsize; /* includes pathname string */
union lc_str name; /* dynamic linker's path name */
};
dylib加载命令
struct dylib_command {
uint32_t cmd; /* LC_ID_DYLIB, LC_LOAD_{,WEAK_}DYLIB, LC_REEXPORT_DYLIB */
uint32_t cmdsize; /* includes pathname string */
struct dylib dylib; /* the library identification */
};
//name 放在加载命令最后,lc_str是告诉偏移位置。加载命令是4字节倍数,字符串填充后,不满足这要求,填充0来满足4字节倍数。
struct dylib {
union lc_str name; /* library's path name 名字*/
uint32_t timestamp; /* library's build time stamp 构建的时间戳*/
uint32_t current_version; /* library's current version number 版本号*/
uint32_t compatibility_version; /* library's compatibility vers number 兼容的版本号*/
};
加载命令 dylinker_command(LC_LOAD_DYLINKER) dylib_command(LC_LOAD_DYLIB)
加载命令 dysymtab_command(LC_DYSYMTAB)
struct dysymtab_command {
uint32_t cmd;
uint32_t cmdsize;
...
uint32_t indirectsymoff; // 指向间接符号表位置
uint32_t nindirectsyms; // 间接符号表里元素的个数
};
got la_symbol_ptr
外部数据地址放到got(Non-Lazy Symbol Pointers)
外部函数地址都放到la_symbol_str(Lazy symbol Pointers)
fishhook
A library that enables dynamically rebinding symbols in Mach-O binaries running on iOS.
fishhook 用到LINKEDIT去计算基址。LC_SEGMENT_64 (_LINKEDIT)
LINKEDIT segment是link editor在链接时创建生成的segment, 这个段包含了符号表(symtab), 间接符号表(dysymtab),字符串表(string table)等
找nl_symbol_ptr(got)/la_symbol_ptr
#define S_NON_LAZY_SYMBOL_POINTERS 0x6
#define S_LAZY_SYMBOL_POINTERS 0x7
value = IndirectSymbolTable[got.section_64.reserved1];
计算基址
找到符号表、字符串表、间接符号表
找到nl_symbol_ptr(got), la_smbol_ptr
// 本来是:基址=linkedit内存地址 - linkedit的fileoff
//由于ASLR:真实基址 = 基址 + slide
uintptr_t linkedit_base = (uintptr_t)slide + linkedit_segment->vmaddr - linkedit_segment->fileoff;
//根据真实基址,得到符号表、间接符号表、字符串表的虚拟内存地址
nlist_t *symtab = (nlist_t *)(linkedit_base + symtab_cmd->symoff);
char *strtab = (char *)(linkedit_base + symtab_cmd->stroff);
uint32_t *indirect_symtab = (uint32_t *)(linkedit_base + dysymtab_cmd->indirectsymoff);
x64 传参
当参数少于7个时, 参数从左到右放入寄存器: rdi, rsi, rdx, rcx, r8, r9。
当参数为7个以上时, 前 6 个与前面一样, 但后面的依次从 “右向左” 放入栈中,即和32位汇编一样。
ref
https://juejin.cn/post/6844903922654511112 MachO动态链接
https://juejin.cn/post/6844903926051897358 fishhook原理
http://abcdxyzk.github.io/blog/2012/11/23/assembly-args/ x64传参
https://blog.csdn.net/liaoshengshi/article/details/39989797