Android so註入(inject)和Hook技術學習(三)——Got表hook之導出表hook
阿新 • • 發佈:2018-07-15
open bin fopen store 模塊 技術學習 個數 where detail
前文介紹了導入表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 segment 100 Elf32_Off p_offset; // File offset where segment is located, in bytes 101 Elf32_Addr p_vaddr; // Virtual address of beginning of segment 102 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 flags 106 Elf32_Word p_align; // Segment alignment constraint 107 }; 108 */ 109 //遍歷program header table,ptype等於2即為dynamic,獲取到p_offset 110 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
Android so註入(inject)和Hook技術學習(三)——Got表hook之導出表hook