2015年9月20日 星期日

攔截 dynamic library 裡面的函數 - 2

Part 1 中說明了如何使用 LD_PRELOAD
攔截 link stage 階段包進去的 dynamic library
這些 library 可以夠過 ldd 看到
不過有時候 dynamic library 是透過 dlfcn.h 下的 dlopen 實現的

例如說 part 1 之中的 main.c 變成了這樣

Main file a.c

#include <dlfcn.h>

int main() {
  void *handle = dlopen("libtest.so", RTLD_LAZY);
  void (*test)() = dlsym(handle, "test");
  void (*test2)() = dlsym(handle, "test2");
  test();
  test2();
  return 0;
}

dlopen 雖然會讀取 LD_LIBRARY_PATH
不過卻不像使用 LD_PRELOAD 的可以讀取多個檔案
因此就算我們編譯出來的檔案 hook library 名稱是 libtest.so
還是讀取不到 test2(),造成 segmentation fault

$ LD_LIBRARY_PATH=hook/ ./a.out
hook
[1]    5721 segmentation fault  LD_LIBRARY_PATH=hook/ ./a.out

因此我們剩下這個選擇:

「在我們掛上去的 libtest.so 中實作舊有的 libtest.so 的所有界面,要 bypass 的就自己用 dlopen 開啟舊的 libtest.so」

不過這於巨大的 library 來說太麻煩了
我們可不可能把「連結原本的 libtest.so」此一資訊放在「hook 的 libtest.so」裡面呢?
這樣一來 dlopen 開啟的時候便會包還這個資訊

答案是可能的

我們把原本的 libtest.so 改成大寫 libTEST.so
要用來取代的 hook library 叫做 libtest.so
而如果 ldd libtest.so 我們會看到

$ LD_LIBRARY_PATH=hook/ ldd hook/libtest.so
        linux-vdso.so.1 =>  (0x00007fff1dd45000)
        libTEST.so => hook/libTEST.so (0x00007f946ec9d000)
        ...

要到這一個目的
我們先製造一個 hook 資料夾
改名原有 library(沒有修改其中內容)

$ mkdir hook
$ mv libtest.so hook/libTEST.so

接著編譯
$ gcc -Wl,--no-as-needed\
      hook/hook.c\
      -L./hook -lTEST\
      -o hook/libtest.so\
      -shared -fPIC 
這邊我們看到跟之前相比多了兩個參數

  1. -L./hook -lTEST 如前面所說明是為了要連結到舊的 library 所使用
  2. -Wl,--no-as-needed 確保 linker 會把沒用到的 library 放進去
這樣一來便可以正確執行了

$ LD_LIBRARY_PATH=./hook ./a.out
Haha
origin2

沒有留言:

張貼留言