Android so注入(inject)和Hook技术学习(三)——Got表hook之导出表hook
内容导读
互联网集市收集整理的这篇技术教程文章主要介绍了Android so注入(inject)和Hook技术学习(三)——Got表hook之导出表hook,小编现在分享给大家,供广大互联网技能从业者学习和参考。文章包含8904字,纯文字阅读大概需要13分钟。
内容图文
![Android so注入(inject)和Hook技术学习(三)——Got表hook之导出表hook](/upload/InfoBanner/zyjiaocheng/1335/8920242fdf1c4a818a2587d15c810e83.jpg)
前文介绍了导入表hook,现在来说下导出表的hook。导出表的hook的流程如下。
1、获取动态库基值
1 void* get_module_base(pid_t pid, constchar* module_name){ 2 FILE* fp; 3long addr = 0; 4char* pch; 5char filename[32]; 6char line[1024]; 7 8// 格式化字符串得到 "/proc/pid/maps" 9if(pid < 0){ 10 snprintf(filename, sizeof(filename), "/proc/self/maps"); 11 }else{ 12 snprintf(filename, sizeof(filename), "/proc/%d/maps", pid); 13 } 1415// 打开文件/proc/pid/maps,获取指定pid进程加载的内存模块信息16 fp = fopen(filename, "r"); 17if(fp != NULL){ 18// 每次一行,读取文件 /proc/pid/maps中内容19while(fgets(line, sizeof(line), fp)){ 20// 查找指定的so模块21if(strstr(line, module_name)){ 22// 分割字符串23 pch = strtok(line, "-"); 24// 字符串转长整形25 addr = strtoul(pch, NULL, 16); 2627// 特殊内存地址的处理28if(addr == 0x8000){ 29 addr = 0; 30 } 31break; 32 } 33 } 34 } 35 fclose(fp); 36return (void*)addr; 37 }
2、计算program header table实际地址
通过ELF文件头获取到程序表头的偏移地址及表头的个数
1 Elf32_Ehdr *header = (Elf32_Ehdr*)(base_addr); 2if (memcmp(header->e_ident, "\177ELF", 4) != 0) { 3return0; 4 } 5int phOffset = header->e_phoff; 6int phNumber = header->e_phnum; 7int phPhyAddr = phOffset + base_addr; 8 9int i = 0; 1011 Elf32_Phdr* phdr_table = (Elf32_Phdr*)(base_addr + phOffset); 12if (phdr_table == 0) 13 { 14 LOGD("[+] phdr_table address : 0"); 15return0; 16 }
3、遍历program header table,ptype等于2即为dynameic,获取到p_offset
这里需要参照程序表头结构体的相关信息,程序表头结构体结构如下:
struct Elf32_Phdr { Elf32_Word p_type; // Type of segment Elf32_Off p_offset; // File offset where segment is located, in bytes Elf32_Addr p_vaddr; // Virtual address of beginning of segment Elf32_Addr p_paddr; // Physical address of beginning of segment (OS-specific) Elf32_Word p_filesz; // Num. of bytes in file image of segment (may be zero) Elf32_Word p_memsz; // Num. of bytes in mem image of segment (may be zero) Elf32_Word p_flags; // Segment flags Elf32_Word p_align; // Segment alignment constraint };
因此得到dynam段对应的地址:
1 for (i = 0; i < phNumber; i++) 2{ 3if (phdr_table[i].p_type == PT_DYNAMIC) 4 { 5 dynamicAddr = phdr_table[i].p_vaddr + base_addr; 6 dynamicSize = phdr_table[i].p_memsz; 7break; 8 } 9 }
4、开始遍历dynamic段结构,d_tag为6即为GOT表地址
同样需要参考动态链接段每项的结构体:
typedef struct dynamic { Elf32_Sword d_tag; union { Elf32_Sword d_val; Elf32_Addr d_ptr; } d_un; } Elf32_Dyn;
遍历方法为:
1 for(i=0; i < dynamicSize / 8; i++) 2{ 3int val = dynamic_table[i].d_un.d_val; 4if (dynamic_table[i].d_tag == 6) 5 { 6 symbolTableAddr = val + base_addr; 7break; 8 } 9 }
5、遍历GOT表,查找GOT表中标记的目标函数地址,替换为新函数的地址。
我们需要知道符号表的结构:
/* Symbol Table Entry */ typedef struct elf32_sym { Elf32_Word st_name; /* name - index into string table */ Elf32_Addr st_value; /* symbol value */ Elf32_Word st_size; /* symbol size */ unsigned char st_info; /* type and binding */ unsigned char st_other; /* 0 - no defined meaning */ Elf32_Half st_shndx; /* section header index */ } Elf32_Sym;
然后替换成目标函数的st_value值,即偏移地址
1 while(1) 2{ 3//LOGD("[+] func Addr : %x", symTab[i].st_value); 4if(symTab[i].st_value == oldFunc) 5 { 6//st_value 保存的是偏移地址 7 symTab[i].st_value = newFunc; 8 LOGD("[+] New Give func Addr : %x", symTab[i].st_value); 9break; 10 } 11 i++; 12 }
注意点:
1、我们知道代码段一般都只会设置为可读可执行的,因此需要使用mprotect改变内存页为可读可写可执行;
2、如果执行完这些操作后hook并没有生效,可能是由于缓存的原因,需要使用cacheflush函数对该内存进行操作。
3、获取目标函数的偏移地址,可以通过dlsym得到绝对地址,再减去基址。
我们还是通过执行“Android so注入( inject)和Hook技术学习(一)”文中的inject程序将本程序注入到目标进程进行hook,运行结果如下:
完整代码如下:
1 #include <unistd.h> 2 #include <stdio.h> 3 #include <stdlib.h> 4 #include <android/log.h> 5 #include <EGL/egl.h> 6 #include <GLES/gl.h> 7 #include <elf.h> 8 #include <fcntl.h> 9 #include <dlfcn.h> 10 #include <sys/mman.h> 11 12#define LOG_TAG "INJECT" 13#define LOGD(fmt, args...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, fmt, ##args) 14 15 16int (*old_check_signatures)(); 17int new_check_signatures(){ 18 LOGD("[+] New call check_signatures.\n"); 19if(old_check_signatures == -1){ 20 LOGD("error.\n"); 21 } 22return old_check_signatures(); 23} 24 25void* get_module_base(pid_t pid, constchar* module_name){ 26 FILE* fp; 27long addr = 0; 28char* pch; 29char filename[32]; 30char line[1024]; 31 32// 格式化字符串得到 "/proc/pid/maps" 33if(pid < 0){ 34 snprintf(filename, sizeof(filename), "/proc/self/maps"); 35 }else{ 36 snprintf(filename, sizeof(filename), "/proc/%d/maps", pid); 37 } 38 39// 打开文件/proc/pid/maps,获取指定pid进程加载的内存模块信息 40 fp = fopen(filename, "r"); 41if(fp != NULL){ 42// 每次一行,读取文件 /proc/pid/maps中内容 43while(fgets(line, sizeof(line), fp)){ 44// 查找指定的so模块 45if(strstr(line, module_name)){ 46// 分割字符串 47 pch = strtok(line, "-"); 48// 字符串转长整形 49 addr = strtoul(pch, NULL, 16); 50 51// 特殊内存地址的处理 52if(addr == 0x8000){ 53 addr = 0; 54 } 55break; 56 } 57 } 58 } 59 fclose(fp); 60return (void*)addr; 61} 62 63#define LIB_PATH "/data/app-lib/com.bbk.appstore-2/libvivosgmain.so" 64int hook_check_signatures(){ 65 66// 获取目标pid进程中"/data/app-lib/com.bbk.appstore-2/libvivosgmain.so"模块的加载地址 67void* base_addr = get_module_base(getpid(), LIB_PATH); 68 LOGD("[+] libvivosgmain.so address = %p \n", base_addr); 69 70//计算program header table实际地址 71 Elf32_Ehdr *header = (Elf32_Ehdr*)(base_addr); 72if (memcmp(header->e_ident, "\177ELF", 4) != 0) { 73return0; 74 } 75 76void* handle = dlopen("/data/app-lib/com.bbk.appstore-2/libvivosgmain.so", RTLD_LAZY); 77//获取原函数地址 78void* funcaddr = dlsym(handle, "check_signatures"); 79 LOGD("[+] libvivosgmain.so check_signatures address = %p \n", (int)funcaddr); 80 81int phOffset = header->e_phoff; 82int phNumber = header->e_phnum; 83int phPhyAddr = phOffset + base_addr; 84 LOGD("[+] phOffset : %x", phOffset); 85 LOGD("[+] phNumber : %x", phNumber); 86 LOGD("[+] phPhyAddr : %x", phPhyAddr); 87int i = 0; 88 89 Elf32_Phdr* phdr_table = (Elf32_Phdr*)(base_addr + phOffset); 90if (phdr_table == 0) 91 { 92 LOGD("[+] phdr_table address : 0"); 93return0; 94 } 95 96/* 97 // Program header for ELF32. 98 struct Elf32_Phdr { 99 Elf32_Word p_type; // Type of segment 100 Elf32_Off p_offset; // File offset where segment is located, in bytes 101 Elf32_Addr p_vaddr; // Virtual address of beginning of segment 102 Elf32_Addr p_paddr; // Physical address of beginning of segment (OS-specific) 103 Elf32_Word p_filesz; // Num. of bytes in file image of segment (may be zero) 104 Elf32_Word p_memsz; // Num. of bytes in mem image of segment (may be zero) 105 Elf32_Word p_flags; // Segment flags 106 Elf32_Word p_align; // Segment alignment constraint 107 }; 108*/109//遍历program header table,ptype等于2即为dynamic,获取到p_offset110 unsigned long dynamicAddr = 0; 111 unsigned int dynamicSize = 0; 112113for (i = 0; i < phNumber; i++) 114 { 115if (phdr_table[i].p_type == PT_DYNAMIC) 116 { 117 dynamicAddr = phdr_table[i].p_vaddr + base_addr; 118 dynamicSize = phdr_table[i].p_memsz; 119break; 120 } 121 } 122 LOGD("[+] Dynamic Addr : %x", dynamicAddr); 123 LOGD("[+] Dynamic Size : %x", dynamicSize); 124125/*126 typedef struct dynamic { 127 Elf32_Sword d_tag; 128 union { 129 Elf32_Sword d_val; 130 Elf32_Addr d_ptr; 131 } d_un; 132 } Elf32_Dyn; 133*/134//开始遍历dynamic段结构,d_tag为6即为GOT表地址135int symbolTableAddr = 0; 136 Elf32_Dyn* dynamic_table = (Elf32_Dyn*)(dynamicAddr); 137138for(i=0; i < dynamicSize / 8; i++) 139 { 140int val = dynamic_table[i].d_un.d_val; 141if (dynamic_table[i].d_tag == 6) 142 { 143 symbolTableAddr = val + base_addr; 144break; 145 } 146 } 147 LOGD("Symbol Table Addr : %x", symbolTableAddr); 148149/*150 typedef struct elf32_sym { 151 Elf32_Word st_name; 152 Elf32_Addr st_value; 153 Elf32_Word st_size; 154 unsigned char st_info; 155 unsigned char st_other; 156 Elf32_Half st_shndx; 157 } Elf32_Sym; 158*/159//遍历GOT表,查找GOT表中标记的check_signatures函数地址,替换为new_check_signatures的地址160int giveValuePtr = 0; 161int fakeValuePtr = 0; 162int newFunc = (int)new_check_signatures - (int)base_addr; 163int oldFunc = (int)funcaddr - (int)base_addr; 164 i = 0; 165 LOGD("[+] newFunc Addr : %x", newFunc); 166 LOGD("[+] oldFunc Addr : %x", oldFunc); 167168// 获取当前内存分页的大小169 uint32_t page_size = getpagesize(); 170// 获取内存分页的起始地址(需要内存对齐)171 uint32_t mem_page_start = (uint32_t)(((Elf32_Addr)symbolTableAddr)) & (~(page_size - 1)); 172 LOGD("[+] mem_page_start = %lx, page size = %lx\n", mem_page_start, page_size); 173 mprotect((uint32_t)mem_page_start, page_size, PROT_READ | PROT_WRITE | PROT_EXEC); 174 Elf32_Sym* symTab = (Elf32_Sym*)(symbolTableAddr); 175while(1) 176 { 177//LOGD("[+] func Addr : %x", symTab[i].st_value);178if(symTab[i].st_value == oldFunc) 179 { 180//st_value 保存的是偏移地址181 symTab[i].st_value = newFunc; 182 LOGD("[+] New Give func Addr : %x", symTab[i].st_value); 183break; 184 } 185 i++; 186 } 187 mprotect((uint32_t)mem_page_start, page_size, PROT_READ | PROT_EXEC); 188189return0; 190} 191192int hook_entry(char* a){ 193 LOGD("[+] Start hooking.\n"); 194 hook_check_signatures(); 195return0; 196 }
参考资料:
https://blog.csdn.net/u011247544/article/details/78564564
原文:https://www.cnblogs.com/goodhacker/p/9313297.html
内容总结
以上是互联网集市为您收集整理的Android so注入(inject)和Hook技术学习(三)——Got表hook之导出表hook全部内容,希望文章能够帮你解决Android so注入(inject)和Hook技术学习(三)——Got表hook之导出表hook所遇到的程序开发问题。 如果觉得互联网集市技术教程内容还不错,欢迎将互联网集市网站推荐给程序员好友。
内容备注
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 gblab@vip.qq.com 举报,一经查实,本站将立刻删除。
内容手机端
扫描二维码推送至手机访问。