温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

android中如何实现got表HOOK

发布时间:2021-08-17 09:00:12 来源:亿速云 阅读:301 作者:小新 栏目:开发技术

这篇文章给大家分享的是有关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”这篇文章就分享到这里了,希望以上内容可以对大家有一定的帮助,让大家可以学到更多知识,如果觉得文章不错,可以把它分享出去让更多的人看到吧!

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI