Android so注入(inject)和Hook技术学习(三)——Got表hook之导出表hook

前文介绍了导入表hook,现在来说下导出表的hook。导出表的hook的流程如下。
1、获取动态库基值   

 1 void* get_module_base(pid_t pid, const char* module_name){ 2 FILE* fp; 3 long addr = 0; 4 char* pch; 5 char filename[32]; 6 char line[1024]; 7  8 // 格式化字符串得到 "/proc/pid/maps" 9 if(pid < 0){10 snprintf(filename, sizeof(filename), "/proc/self/maps");11 }else{12 snprintf(filename, sizeof(filename), "/proc/%d/maps", pid);13  }14 15 // 打开文件/proc/pid/maps,获取指定pid进程加载的内存模块信息16 fp = fopen(filename, "r");17 if(fp != NULL){18 // 每次一行,读取文件 /proc/pid/maps中内容19 while(fgets(line, sizeof(line), fp)){20 // 查找指定的so模块21 if(strstr(line, module_name)){22 // 分割字符串23 pch = strtok(line, "-");24 // 字符串转长整形25 addr = strtoul(pch, NULL, 16);26 27 // 特殊内存地址的处理28 if(addr == 0x8000){29 addr = 0;30  }31 break;32  }33  }34  }35  fclose(fp);36 return (void*)addr;37 }

2、计算program header table实际地址 

通过ELF文件头获取到程序表头的偏移地址及表头的个数

 1 Elf32_Ehdr *header = (Elf32_Ehdr*)(base_addr); 2 if (memcmp(header->e_ident, "\177ELF", 4) != 0) { 3 return 0; 4  } 5 int phOffset = header->e_phoff; 6 int phNumber = header->e_phnum; 7 int phPhyAddr = phOffset + base_addr; 8  9 int i = 0;10 11 Elf32_Phdr* phdr_table = (Elf32_Phdr*)(base_addr + phOffset);12 if (phdr_table == 0)13  {14 LOGD("[+] phdr_table address : 0");15 return 0;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 {3 if (phdr_table[i].p_type == PT_DYNAMIC)4  {5 dynamicAddr = phdr_table[i].p_vaddr + base_addr;6 dynamicSize = phdr_table[i].p_memsz;7 break;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 {3 int val = dynamic_table[i].d_un.d_val;4 if (dynamic_table[i].d_tag == 6)5  {6 symbolTableAddr = val + base_addr;7 break;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); 4 if(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); 9 break;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  16 int (*old_check_signatures)(); 17 int new_check_signatures(){ 18 LOGD("[+] New call check_signatures.\n"); 19 if(old_check_signatures == -1){ 20 LOGD("error.\n"); 21  } 22 return old_check_signatures(); 23 } 24  25 void* get_module_base(pid_t pid, const char* module_name){ 26 FILE* fp; 27 long addr = 0; 28 char* pch; 29 char filename[32]; 30 char line[1024]; 31  32 // 格式化字符串得到 "/proc/pid/maps" 33 if(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"); 41 if(fp != NULL){ 42 // 每次一行,读取文件 /proc/pid/maps中内容 43 while(fgets(line, sizeof(line), fp)){ 44 // 查找指定的so模块 45 if(strstr(line, module_name)){ 46 // 分割字符串 47 pch = strtok(line, "-"); 48 // 字符串转长整形 49 addr = strtoul(pch, NULL, 16); 50  51 // 特殊内存地址的处理 52 if(addr == 0x8000){ 53 addr = 0; 54  } 55 break; 56  } 57  } 58  } 59  fclose(fp); 60 return (void*)addr; 61 } 62  63 #define LIB_PATH "/data/app-lib/com.bbk.appstore-2/libvivosgmain.so" 64 int hook_check_signatures(){ 65  66 // 获取目标pid进程中"/data/app-lib/com.bbk.appstore-2/libvivosgmain.so"模块的加载地址 67 void* 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); 72 if (memcmp(header->e_ident, "\177ELF", 4) != 0) { 73 return 0; 74  } 75  76 void* handle = dlopen("/data/app-lib/com.bbk.appstore-2/libvivosgmain.so", RTLD_LAZY); 77 //获取原函数地址 78 void* funcaddr = dlsym(handle, "check_signatures"); 79 LOGD("[+] libvivosgmain.so check_signatures address = %p \n", (int)funcaddr); 80  81 int phOffset = header->e_phoff; 82 int phNumber = header->e_phnum; 83 int phPhyAddr = phOffset + base_addr; 84 LOGD("[+] phOffset : %x", phOffset); 85 LOGD("[+] phNumber : %x", phNumber); 86 LOGD("[+] phPhyAddr : %x", phPhyAddr); 87 int i = 0; 88  89 Elf32_Phdr* phdr_table = (Elf32_Phdr*)(base_addr + phOffset); 90 if (phdr_table == 0) 91  { 92 LOGD("[+] phdr_table address : 0"); 93 return 0; 94  } 95  96 /* 97  // Program header for ELF32. 98  struct Elf32_Phdr { 99  Elf32_Word p_type; // Type of segment100  Elf32_Off p_offset; // File offset where segment is located, in bytes101  Elf32_Addr p_vaddr; // Virtual address of beginning of segment102  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 flags106  Elf32_Word p_align; // Segment alignment constraint107  };108 */109 //遍历program header table,ptype等于2即为dynamic,获取到p_offset110 unsigned long dynamicAddr = 0;111 unsigned int dynamicSize = 0;112 113 for (i = 0; i < phNumber; i++)114  {115 if (phdr_table[i].p_type == PT_DYNAMIC)116  {117 dynamicAddr = phdr_table[i].p_vaddr + base_addr;118 dynamicSize = phdr_table[i].p_memsz;119 break;120  }121  }122 LOGD("[+] Dynamic Addr : %x", dynamicAddr);123 LOGD("[+] Dynamic Size : %x", dynamicSize);124 125 /*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表地址135 int symbolTableAddr = 0;136 Elf32_Dyn* dynamic_table = (Elf32_Dyn*)(dynamicAddr);137 138 for(i=0; i < dynamicSize / 8; i++)139  {140 int val = dynamic_table[i].d_un.d_val;141 if (dynamic_table[i].d_tag == 6)142  {143 symbolTableAddr = val + base_addr;144 break;145  }146  }147 LOGD("Symbol Table Addr : %x", symbolTableAddr);148 149 /*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的地址160 int giveValuePtr = 0;161 int fakeValuePtr = 0;162 int newFunc = (int)new_check_signatures - (int)base_addr;163 int oldFunc = (int)funcaddr - (int)base_addr;164 i = 0;165 LOGD("[+] newFunc Addr : %x", newFunc);166 LOGD("[+] oldFunc Addr : %x", oldFunc);167 168 // 获取当前内存分页的大小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);175 while(1)176  {177 //LOGD("[+] func Addr : %x", symTab[i].st_value);178 if(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);183 break;184  }185 i++;186  }187 mprotect((uint32_t)mem_page_start, page_size, PROT_READ | PROT_EXEC);188 189 return 0;190 }191 192 int hook_entry(char* a){193 LOGD("[+] Start hooking.\n");194  hook_check_signatures();195 return 0;196 }

参考资料:

https://blog.csdn.net/u011247544/article/details/78564564

相关文章