眾所周知Android的動態(tài)鏈接器是linker办桨,iOS是dyld巨坊,linux是ld-linux.so.2够坐。由于同是linux內(nèi)核,所以Android的linker與linux很相似巩检,最大的區(qū)別就是linker不支持懶綁定,懶綁定的相關知識請參考我的另一篇博客linux plt 的實現(xiàn)示启。所以當Android的so或可執(zhí)行文件在調(diào)用外部定義的函數(shù)之前l(fā)inker已經(jīng)把函數(shù)的偏移寫到got表中兢哭,我們看一下如何通過ELF文件的結構獲取到got表的偏移。
首先我要獲取.dynsym .dynstr .rel.plt三個節(jié)的數(shù)據(jù)
.rel.plt和dynsym的定義如下:
typedef struct{
Elf32_Addr r_offset;
Elf32_Word r_info;
} Elf32_Rel;
typedef structelf32_sym{
Elf32_Word st_name;
Elf32_Addr st_value;
Elf32_Word st_size;
unsignedcharst_info;
unsignedcharst_other;
Elf32_Half st_shndx;
} Elf32_Sym;
獲取.rel.plt每個重定位表所對應的符號的步驟:
1夫嗓、使用ELF32_R_SYM宏(參數(shù)為.rel.plt的r_info)獲取符號在.dynsym中的偏移
2迟螺、找到對應的dynsym在獲取.dynsym的st_name字段,但這個字段不是字符串舍咖,也是一個偏移矩父,是.dynstr節(jié)的偏移
3、通過偏移可以獲取到相應的符號
以下代碼來自于網(wǎng)絡排霉,是尋找got符號的程序?qū)崿F(xiàn):
for (i = 0; i < relplt_shdr->sh_size / sizeof(Elf32_Rel); i++){
uint16_t ndx = ELF32_R_SYM(rel_ent->r_info);
LOGD("ndx = %d, str = %s", ndx, dynstr + dynsymtab[ndx].st_name);
if (strcmp(dynstr + dynsymtab[ndx].st_name, symbol_name) == 0) {
LOGD("符號%s在got表的偏移地址為: 0x%x", symbol_name, rel_ent->r_offset);
offset = rel_ent->r_offset;
break;
}
if(read(fd, rel_ent, sizeof(Elf32_Rel)) != sizeof(Elf32_Rel)) {
LOGD("獲取符號%s的重定位信息失敗", symbol_name); return -1; }
}
如果是一個靜態(tài)綁定的符號獲取方式就是獲取.dynsym結構體st_value字段的值
for(i = 0; i < (dynsym_shdr->sh_size) / sizeof(Elf32_Sym); ++i) {
if(strcmp(dynstr + dynsymtab[i].st_name, symbol_name) == 0) {
LOGD("符號%s的地址位: 0x%x", symbol_name, dynsymtab[i].st_value);
offset = dynsymtab[i].st_value;
break;
}
}
以上代碼出自Android GOT表HOOK技術