前文介绍了导入表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