hook技術是一種攔截用戶函數調用的技術。經過hook技術能夠實現統計用戶對某些函數的調用次數,對函數注入新的功能的目標。在Linux平臺,Hook技術能夠分紅用戶和內核兩個層面,每一個類比中都存在不一樣的hook技術。本文主要介紹針對動態連接技術的PLT hook。html
其次編寫咱們的main.c 該函數實現了將要替換strcmp函數的my_strcmp,和使hook生效的hook函數。代碼的具體細節linux
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <unistd.h> 4 #include <string.h> 5 #include <inttypes.h> 6 #include <execinfo.h> 7 #include <sys/user.h> 8 #include <sys/mman.h> 9 #include "passwd.h" 10 11 #define PAGE_SHIFT 12 12 #define PAGE_SIZE (_AC(1,UL) << PAGE_SHIFT) 13 #define PAGE_MASK (~(PAGE_SIZE-1)) 14 15 #define __AC(X,Y) (X##Y) 16 #define _AC(X,Y) __AC(X,Y) 17 18 #define PAGE_START(addr) ((addr) & PAGE_MASK) 19 #define PAGE_END(addr) (PAGE_START(addr) + PAGE_SIZE) 20 21 int my_strcmp(const char *s1, const char *s2) 22 { 23 return 0; 24 } 25 26 uintptr_t get_base_addr(char *libname) 27 { 28 FILE *fp; 29 char line[1024]; 30 //char base_addr[1024]; 31 uintptr_t base_addr = 0; 32 33 if (NULL == (fp = fopen("/proc/self/maps", "r"))) { 34 perror("open err"); 35 return -1; 36 } 37 38 while (NULL != fgets(line, sizeof(line), fp)) { 39 if (NULL != strstr(line, libname)) { 40 sscanf(line, "%"PRIxPTR"-%*lx %*4s 00000000", &base_addr); 41 printf("line2:%s, base_addr:%"PRIxPTR"\n", line, base_addr); 42 break; 43 } 44 } 45 fclose(fp); 46 47 return base_addr; 48 } 49 50 void hook() { 51 uintptr_t base_addr; 52 uintptr_t addr; 53 //1. get the base addr of libpasswd.so 54 base_addr = get_base_addr("libpasswd"); 55 if (0 == base_addr) return; 56 addr = base_addr + 0x201020; 57 //2. add the write permisson 58 mprotect((void *)PAGE_START(addr), PAGE_SIZE, PROT_READ|PROT_WRITE); 59 //3. replace our hook func my_strcmp 60 *(void **)addr = my_strcmp; 61 //4. clear the cache 62 __builtin___clear_cache((void *)PAGE_START(addr), 63 (void *)PAGE_END(addr)); 64 } 65 66 int main() 67 { 68 hook(); 69 check_is_authenticated("abcd"); 70 return 0; 71 }
在將libpasswd.so放置到/usr/lib/目錄中以後,使用如下命令編譯main.c緩存
運行以後咱們 能夠看到驗證結果始終是正確的,說明咱們對於libpasswd.so中的strcmp函數的Hook已經生效。函數
怎麼樣,是否是很神奇呀!下面咱們就來看看plt hook究竟是怎麼實現的。post
以上就是第一次調用strcmp函數的流程,能夠看出開銷仍是不小的。在後續的調用中,首先仍是跳轉到strcmp的plt條目中,而後執行jmpq *0x200aaa(%rip)指令,不一樣的是這是strcmp對應的GOT條目已經被寫入了strcmp的運行時地址,因此這條jmp指令直接將程序的執行跳轉到strcmp函數中。那麼是否是隻要在strcmp對應的GOT條目中寫入咱們本身的函數的地址,就能夠將控制跳轉到本身實現的函數中了嘛。本着這樣的思路咱們來看看開始時的代碼。ui
第二步就是獲取libpasswd中調用strcmp對應的GOT條目的地址,爲何是addr = base_addr + 0x201020呢?咱們回到上文的libpasswd的plt段,能夠看到strcmp的plt條目的第一條指令已經寫出了相應的GOT條目的地址就是0x201020。又由於咱們攔截的動態庫的strcmp函數,因此必須加上libpasswd在main中的首地址。spa
3. 由於咱們要寫入目標進程的數據段,因此必須給相應的頁增長寫權限,這裏使用mprotect函數來調整相應頁的權限。命令行
1 void hook() { 2 uintptr_t base_addr; 3 uintptr_t addr; 4 //1. get the base addr of libpasswd.so 5 base_addr = get_base_addr("libpasswd"); 6 if (0 == base_addr) return; 7 addr = base_addr + 0x201020; 8 //2. add the write permisson 9 mprotect((void *)PAGE_START(addr), PAGE_SIZE, PROT_READ|PROT_WRITE); 10 //3. replace our hook func my_strcmp 11 *(void **)addr = my_strcmp; 12 //4. clear the cache 13 __builtin___clear_cache((void *)PAGE_START(addr), 14 (void *)PAGE_END(addr)); 15 }