android的got表HOOK实现代码
对于android的so文件的hook根据ELF文件特性分为:Got表hook、Sym表hook和inline hook等。全局符号表(GOT表)hook,它是通过解析SO文件,将待hook函数在got表的地址替换为自己函数的入口地址,这样目标进程每次调用待hook函数时,实际上是执行了我们自己的函数。
Androd so注入和函数Hook(基于got表)的步骤:1.ptrace附加目标pid进程;2.在目标pid进程中,查找内存空间(用于存放被注入的so文件的路径和so中被调用的函数的名称或者shellcode);3.调用目标pid进程中的dlopen、dlsym等函数,用于加载so文件实现Android so的注入和函数的Hook;4.释放附加的目标pid进程和卸载注入的so文件。
具体代码实现以下以fopen函数进行got hook为例。
//获取模块地址功能实现void* getModuleBase(pid_t pid, const char* module_name){ FILE* fp; long address = 0; char* pch; char filename[32]; char line[1024]; // 格式化字符串得到 '/proc/pid/maps' if(pid < 0){snprintf(filename, sizeof(filename), '/proc/self/maps'); }else{snprintf(filename, sizeof(filename), '/proc/%d/maps', pid); } // 打开文件/proc/pid/maps,获取指定pid进程加载的内存模块信息 fp = fopen(filename, 'r'); if(fp != NULL){// 每次一行,读取文件 /proc/pid/maps中内容while(fgets(line, sizeof(line), fp)){ // 查找指定的so模块 if(strstr(line, module_name)){// 分割字符串pch = strtok(line, '-');// 字符串转长整形address = strtoul(pch, NULL, 16); }break; }} } fclose(fp); return (void*)address;}
//hook fopen进行实现//(libxxxx.so文件是ELF32文件)#define LIBPATH '/data/app-lib/com.xxxx/libxxxx.so'int hookFopen(){ // 获取目标pid中'/data/app-lib/com.xxxx/libxxxx.so'模块的加载地址 void* base_addr = getModuleBase(getpid(), LIBPATH ); // 保存Hook目标函数的原始调用地址 old_fopen = fopen; int fd; // 用open打开内存模块文件'/data/app-lib/com.xxxx/libxxxx.so' fd = open(LIB_PATH, O_RDONLY); if(-1 == fd){return -1; } // elf32文件的文件头结构体Elf32_Ehdr Elf32_Ehdr ehdr; // 读取elf32格式的文件'/data/app-lib/com.xxxx/libxxxx.so'的文件头信息 read(fd, &ehdr, sizeof(Elf32_Ehdr)); // elf32文件中节区表信息结构的文件偏移 unsigned long shdr_addr = ehdr.e_shoff; // elf32文件中节区表信息结构的数量 int shnum = ehdr.e_shnum; // elf32文件中每个节区表信息结构中的单个信息结构的大小(描述每个节区的信息的结构体的大小) int shent_size = ehdr.e_shentsize; // elf32文件节区表中每个节区的名称存放的节区名称字符串表,在节区表中的序号index unsigned long stridx = ehdr.e_shstrndx; Elf32_Shdr shdr; lseek(fd, shdr_addr + stridx * shent_size, SEEK_SET); // 读取elf32文件中的描述每个节区的信息的结构体(这里是保存elf32文件的每个节区的名称字符串的) read(fd, &shdr, shent_size); // 为保存elf32文件的所有的节区的名称字符串申请内存空间 char * string_table = (char *)malloc(shdr.sh_size); // 定位到具体存放elf32文件的所有的节区的名称字符串的文件偏移处 lseek(fd, shdr.sh_offset, SEEK_SET); read(fd, string_table, shdr.sh_size); lseek(fd, shdr_addr, SEEK_SET); int i; uint32_t out_addr = 0; uint32_t out_size = 0; uint32_t got_item = 0; int32_t got_found = 0; // 循环遍历elf32文件的节区表(描述每个节区的信息的结构体) for(i = 0; i<shnum; i++){// 依次读取节区表中每个描述节区的信息的结构体read(fd, &shdr, shent_size);// 判断当前节区描述结构体描述的节区是否是SHT_PROGBITS类型//类型为SHT_PROGBITS的.got节区包含全局偏移表if(shdr.sh_type == SHT_PROGBITS){ // 获取节区的名称字符串在保存所有节区的名称字符串段.shstrtab中的序号 int name_idx = shdr.sh_name; // 判断节区的名称是否为'.got.plt'或者'.got' if(strcmp(&(string_table[name_idx]), '.got.plt') == 0|| strcmp(&(string_table[name_idx]), '.got') == 0){// 获取节区'.got'或者'.got.plt'在内存中实际数据存放地址out_addr = base_addr + shdr.sh_addr;// 获取节区'.got'或者'.got.plt'的大小out_size = shdr.sh_size;int j = 0;// 遍历节区'.got'或者'.got.plt'获取保存的全局的函数调用地址for(j = 0; j<out_size; j += 4){ // 获取节区'.got'或者'.got.plt'中的单个函数的调用地址 got_item = *(uint32_t*)(out_addr + j); // 判断节区'.got'或者'.got.plt'中函数调用地址是否是将要被Hook的目标函数地址 if(got_item == old_fopen){got_found = 1;// 获取当前内存分页的大小uint32_t page_size = getpagesize();// 获取内存分页的起始地址(需要内存对齐)uint32_t entry_page_start = (out_addr + j) & (~(page_size - 1)); // 修改内存属性为可读可写可执行if(mprotect((uint32_t*)entry_page_start, page_size, PROT_READ | PROT_WRITE | PROT_EXEC) == -1){ return -1;} // Hook的函数,是我们自己定义的函数got_item = new_fopen; // 进行恢复内存属性为可读可执行if(mprotect((uint32_t*)entry_page_start, page_size, PROT_READ | PROT_EXEC) == -1){ return -1;}break; // 目标函数的调用地址已经被Hook了 }else if(got_item == new_fopen){break; }}// 对目标函数HOOk成功,跳出循环if(got_found) break; }} } free(string_table); close(fd);}
到此这篇关于android的got表HOOK实现代码的文章就介绍到这了,更多相关android HOOK实现got表内容请搜索好吧啦网以前的文章或继续浏览下面的相关文章希望大家以后多多支持好吧啦网!
相关文章:
1. python中requests模拟登录的三种方式(携带cookie/session进行请求网站)2. 网页中img图片使用css实现等比例自动缩放不变形(代码已测试)3. IE6/IE7/IE8/IE9中tbody的innerHTML不能赋值的完美解决方案4. Django显示可视化图表的实践5. python opencv实现目标区域裁剪功能6. JavaScript实现form提交,回车提交URL地址伪静态 原创7. Vue打包部署到Nginx时,css样式不生效的解决方式8. java不解压直接读取压缩包中文件的实现方法9. xml中的空格之完全解说10. ASP常用日期格式化函数 FormatDate()