linux hook機制研究
1 人贊了文章
在研究C++中協程機制時,發現有些實現通過hack掉glibc的read、write等IO操作函數,以達到遷移協程框架時,最小化代碼改動,遂小小研究一下linux下的hook機制。
引子
在linux下調用C庫中的函數,主要是調用得 libc.so.6
這個動態鏈接庫中的函數。 那麼我們有沒有辦法讓應用程序改調其它函數,而應用程序無感知,也就是hack掉應用程序中調用的某些函數。
由於是調用得動態鏈接庫中函數,我們可以通過劫持該函數的方式引入額外處理。 例如通過劫持 malloc
、free
來追蹤內存使用情況等等。
實際操作
so文件
我們先創建一個 my_hook.c
文件,並編寫需要hook的函數實現。
#define _GNU_SOURCE#include <stdio.h>#include <stdint.h>#include <dlfcn.h>#define unlikely(x) __builtin_expect(!!(x), 0)#define TRY_LOAD_HOOK_FUNC(name) if (unlikely(!g_sys_##name)) {g_sys_##name = (sys_##name##_t)dlsym(RTLD_NEXT,#name);}typedef void* (*sys_malloc_t)(size_t size);static sys_malloc_t g_sys_malloc = NULL;void* malloc(size_t size){ TRY_LOAD_HOOK_FUNC(malloc); void *p = g_sys_malloc(size); printf("in malloc hook function ...
"); return p;}typedef void (*sys_free_t)(void *ptr);static sys_free_t g_sys_free = NULL;void free(void *ptr){ TRY_LOAD_HOOK_FUNC(free); g_sys_free(ptr); printf("in free hook function ...
");}
其中使用 RTLD_NEXT
來獲取系統glibc的 malloc
函數地址,由於待會使用 LD_PRELOAD
來優先載入我們創建的 so
文件,因而系統的 libc.so.6
排在第二位,也就是 next
。
編譯該文件生成一個 so
庫: gcc -fPIC -shared -o libmyhook.so my_hook.c -ldl
測試程序
接下來創建測試程序:
#include <stdio.h>#include <malloc.h>#include <stdlib.h>#include <string.h>int main(){ printf("enter main...
"); int *p = (int *)malloc(10); if (!p) { printf("allocation error...
"); exit(1); } printf("returning to main...
"); free(p); if (strcmp("aa", "bb") == 0) { printf("hook strcmp
"); } else { printf("not match
"); } return 0;}
hack測試
對上面的測試程序,直接編譯並運行 gcc -o main main.c
./main
結果如下:
enter main...returning to main...not match
可以清楚的看到,我們創建的 so
中函數並沒有被調用到,也就是說hack失敗!
等等!目前為止我們生成的 so
文件,並沒有與測試程序產生關聯,所以 malloc
、free
函數沒有被hack掉,理所應當。
那麼,如何才能讓兩者產生關聯呢?
LD_PRELOAD這個環境變數,能夠影響程序運行時候動態鏈接庫的載入,可以通過設置其來優先載入某些庫,進而覆蓋掉某些函數。
這裡只需要稍加更改運行方式: LD_PRELOAD=./libmyhook.so ./main
結果如下:
enter main...in malloc hook function ...returning to main...in free hook function ...not match
大功告成,我們自定義的 malloc
、free
被調用到,hack成功!
問題
在嘗試對strcmp
函數進行hack時,按照如上方式並不能hack成功,通過查閱資料,原來編譯器對很多函數進行了內聯優化,並不會調用到 so
庫中的函數,因而通過優先載入自定義動態庫的方式不可行。 不過,可以在編譯測試程序時,添加 -fno-builtin-strcmp
,關閉 strcmp
函數的優化 gcc -o main main.c -fno-builtin-strcmp
以相同的方式運行測試程序: LD_PRELOAD=./libmyhook.so ./main
運行結果:
enter main...in malloc hook function ...returning to main...in free hook function ...in strcmp hook function ...hook strcmp
參考資料
- 警惕UNIX下的LD_PRELOAD環境變數
- 如何hack strcmp
推薦閱讀:
※每個 Linux 新手都應該知道的 10 個命令
※給小白的Ubuntu系統不完全安裝指北
※運用虛擬機搭建本地Hadoop環境
※翻譯 known.met 程序
※centos 7 Minimal設置網路及配置ssh