前文介绍了导入表 hook, 现在来说下导出表的 hook. 导出表的 hook 的流程如下.
1, 获取动态库基值
- void* get_module_base(pid_t pid, const char* module_name){
- FILE* fp;
- long addr = 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, "-");
- // 字符串转长整形
- addr = strtoul(pch, NULL, 16);
- // 特殊内存地址的处理
- if(addr == 0x8000){
- addr = 0;
- }
- break;
- }
- }
- }
- fclose(fp);
- return (void*)addr;
- }
2, 计算 program header table 实际地址
通过 ELF 文件头获取到程序表头的偏移地址及表头的个数
- Elf32_Ehdr *header = (Elf32_Ehdr*)(base_addr);
- if (memcmp(header->e_ident, "\177ELF", 4) != 0) {
- return 0;
- }
- int phOffset = header->e_phoff;
- int phNumber = header->e_phnum;
- int phPhyAddr = phOffset + base_addr;
- int i = 0;
- Elf32_Phdr* phdr_table = (Elf32_Phdr*)(base_addr + phOffset);
- if (phdr_table == 0)
- {
- LOGD("[+] phdr_table address : 0");
- return 0;
- }
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 段对应的地址:
- for (i = 0; i <phNumber; i++)
- {
- if (phdr_table[i].p_type == PT_DYNAMIC)
- {
- dynamicAddr = phdr_table[i].p_vaddr + base_addr;
- dynamicSize = phdr_table[i].p_memsz;
- break;
- }
- }
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;
遍历方法为:
- for(i=0; i < dynamicSize / 8; i++)
- {
- int val = dynamic_table[i].d_un.d_val;
- if (dynamic_table[i].d_tag == 6)
- {
- symbolTableAddr = val + base_addr;
- break;
- }
- }
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 值, 即偏移地址
- while(1)
- {
- //LOGD("[+] func Addr : %x", symTab[i].st_value);
- if(symTab[i].st_value == oldFunc)
- {
- //st_value 保存的是偏移地址
- symTab[i].st_value = newFunc;
- LOGD("[+] New Give func Addr : %x", symTab[i].st_value);
- break;
- }
- i++;
- }
注意点:
1, 我们知道代码段一般都只会设置为可读可执行的, 因此需要使用 mprotect 改变内存页为可读可写可执行;
2, 如果执行完这些操作后 hook 并没有生效, 可能是由于缓存的原因, 需要使用 cacheflush 函数对该内存进行操作.
3, 获取目标函数的偏移地址, 可以通过 dlsym 得到绝对地址, 再减去基址.
我们还是通过执行 "Android so 注入 ( inject) 和 Hook 技术学习(一)" 文中的 inject 程序将本程序注入到目标进程进行 hook, 运行结果如下:
完整代码如下:
- #include <unistd.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <android/log.h>
- #include <EGL/egl.h>
- #include <GLES/gl.h>
- #include <elf.h>
- #include <fcntl.h>
- #include <dlfcn.h>
- #include <sys/mman.h>
- #define LOG_TAG "INJECT"
- #define LOGD(fmt, args...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, fmt, ##args)
- int (*old_check_signatures)();
- int new_check_signatures(){
- LOGD("[+] New call check_signatures.\n");
- if(old_check_signatures == -1){
- LOGD("error.\n");
- }
- return old_check_signatures();
- }
- void* get_module_base(pid_t pid, const char* module_name){
- FILE* fp;
- long addr = 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, "-");
- // 字符串转长整形
- addr = strtoul(pch, NULL, 16);
- // 特殊内存地址的处理
- if(addr == 0x8000){
- addr = 0;
- }
- break;
- }
- }
- }
- fclose(fp);
- return (void*)addr;
- }
- #define LIB_PATH "/data/app-lib/com.bbk.appstore-2/libvivosgmain.so"
- int hook_check_signatures(){
- // 获取目标 pid 进程中 "/data/app-lib/com.bbk.appstore-2/libvivosgmain.so" 模块的加载地址
- void* base_addr = get_module_base(getpid(), LIB_PATH);
- LOGD("[+] libvivosgmain.so address = %p \n", base_addr);
- // 计算 program header table 实际地址
- Elf32_Ehdr *header = (Elf32_Ehdr*)(base_addr);
- if (memcmp(header->e_ident, "\177ELF", 4) != 0) {
- return 0;
- }
- void* handle = dlopen("/data/app-lib/com.bbk.appstore-2/libvivosgmain.so", RTLD_LAZY);
- // 获取原函数地址
- void* funcaddr = dlsym(handle, "check_signatures");
- LOGD("[+] libvivosgmain.so check_signatures address = %p \n", (int)funcaddr);
- int phOffset = header->e_phoff;
- int phNumber = header->e_phnum;
- int phPhyAddr = phOffset + base_addr;
- LOGD("[+] phOffset : %x", phOffset);
- LOGD("[+] phNumber : %x", phNumber);
- LOGD("[+] phPhyAddr : %x", phPhyAddr);
- int i = 0;
- Elf32_Phdr* phdr_table = (Elf32_Phdr*)(base_addr + phOffset);
- if (phdr_table == 0)
- {
- LOGD("[+] phdr_table address : 0");
- return 0;
- }
- /*
- // Program header for ELF32.
- 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
- };
- */
- // 遍历 program header table,ptype 等于 2 即为 dynamic, 获取到 p_offset
- unsigned long dynamicAddr = 0;
- unsigned int dynamicSize = 0;
- for (i = 0; i < phNumber; i++)
- {
- if (phdr_table[i].p_type == PT_DYNAMIC)
- {
- dynamicAddr = phdr_table[i].p_vaddr + base_addr;
- dynamicSize = phdr_table[i].p_memsz;
- break;
- }
- }
- LOGD("[+] Dynamic Addr : %x", dynamicAddr);
- LOGD("[+] Dynamic Size : %x", dynamicSize);
- /*
- typedef struct dynamic {
- Elf32_Sword d_tag;
- union {
- Elf32_Sword d_val;
- Elf32_Addr d_ptr;
- } d_un;
- } Elf32_Dyn;
- */
- // 开始遍历 dynamic 段结构, d_tag 为 6 即为 GOT 表地址
- int symbolTableAddr = 0;
- Elf32_Dyn* dynamic_table = (Elf32_Dyn*)(dynamicAddr);
- for(i=0; i < dynamicSize / 8; i++)
- {
- int val = dynamic_table[i].d_un.d_val;
- if (dynamic_table[i].d_tag == 6)
- {
- symbolTableAddr = val + base_addr;
- break;
- }
- }
- LOGD("Symbol Table Addr : %x", symbolTableAddr);
- /*
- typedef struct elf32_sym {
- Elf32_Word st_name;
- Elf32_Addr st_value;
- Elf32_Word st_size;
- unsigned char st_info;
- unsigned char st_other;
- Elf32_Half st_shndx;
- } Elf32_Sym;
- */
- // 遍历 GOT 表, 查找 GOT 表中标记的 check_signatures 函数地址, 替换为 new_check_signatures 的地址
- int giveValuePtr = 0;
- int fakeValuePtr = 0;
- int newFunc = (int)new_check_signatures - (int)base_addr;
- int oldFunc = (int)funcaddr - (int)base_addr;
- i = 0;
- LOGD("[+] newFunc Addr : %x", newFunc);
- LOGD("[+] oldFunc Addr : %x", oldFunc);
- // 获取当前内存分页的大小
- uint32_t page_size = getpagesize();
- // 获取内存分页的起始地址(需要内存对齐)
- uint32_t mem_page_start = (uint32_t)(((Elf32_Addr)symbolTableAddr)) & (~(page_size - 1));
- LOGD("[+] mem_page_start = %lx, page size = %lx\n", mem_page_start, page_size);
- mprotect((uint32_t)mem_page_start, page_size, PROT_READ | PROT_WRITE | PROT_EXEC);
- Elf32_Sym* symTab = (Elf32_Sym*)(symbolTableAddr);
- while(1)
- {
- //LOGD("[+] func Addr : %x", symTab[i].st_value);
- if(symTab[i].st_value == oldFunc)
- {
- //st_value 保存的是偏移地址
- symTab[i].st_value = newFunc;
- LOGD("[+] New Give func Addr : %x", symTab[i].st_value);
- break;
- }
- i++;
- }
- mprotect((uint32_t)mem_page_start, page_size, PROT_READ | PROT_EXEC);
- return 0;
- }
- int hook_entry(char* a){
- LOGD("[+] Start hooking.\n");
- hook_check_signatures();
- return 0;
- }
参考资料:
https://blog.csdn.net/u011247544/article/details/78564564
来源: https://www.cnblogs.com/goodhacker/p/9313297.html