1. 程式人生 > >Android so註入(inject)和Hook技術學習(三)——Got表hook之導出表hook

Android so註入(inject)和Hook技術學習(三)——Got表hook之導出表hook

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