Linux下的LD_PRELOAD環境變量與庫打樁
LD_PRELOAD是Linux系統的一個環境變量,它可以影響程序的運行時的鏈接(Runtime linker),它允許你定義在程序運行前優先加載的動態鏈接庫,一方面,我們可以以此功能來使用自己的或是更好的函數(比如,你可以使用Google開發的tcmalloc來提升效率),而另一方面,我們也可以向別人的程序注入程序,從而達到特定的目的。
我們下面以一個 foepn()
函數的例子來展示一下如何實現運行時庫打樁。
正常庫函數調用
首先,我們建立一個測試目錄并在該目錄下創建一個文本文件:
mkdir demo && cd demo
touch test.txt
然后,我們在測試目錄下準備一個測試程序,在其中用 foepn()
函數打開我們剛剛創建的文本文件,這個過程應該是成功的,因為我們確實有這樣一個文本文件:
// perload_test.c#include <stdio.h>int main(){printf("Calling the fopen() function \n");FILE *fd = fopen("test.txt", "r");if (!fd) {printf("fopen() returned NULL\n");return 1;}printf("fopen() suceeded\n");return 0;
}
我們編譯運行這個 preload_test.c
:
gcc preload_test.c -o preload_test
./preload_test
# 輸出:
# Calling the fopen() function
# fopen() suceeded
如我們所預期的,打開文件成功。
運行時庫打樁
我們首先準備我們自己想要庫打樁的函數 foepn()
,在這個函數中無論怎么樣都會返回錯誤:
// myfopen.c#include <stdio.h>FILE *fopen(const char *path, const char *node) {printf("Always failing fopen in myfopen()\n");return NULL;
}
將它編譯成一個共享庫 myfopen.so
:
gcc -shared -fpic myfopen.c -o myfopen.so
然后將其添加到 LD_PRELOAD
環境變量:
export LD_PRELOAD=/home/song/demo/myfopem.so
現在我們在來運行一下剛才編譯的 preload
程序(無需重新編譯):
./preload
# 輸出:
# Calling the fopen() function
# Always failing fopen in myfopen()
# fopen() returned NULL
雖然目錄下確實有這個文本文件可以被打開,但是由于執行的是我們自己的 fopen()
函數,無論如何都會返回錯誤。這樣就成功地使用我們自己的 fopen()
函數完成了運行時庫打樁,在運行時,會優先執行環境變量 LD_PRELOAD
中指定的庫打樁函數。
警告:測試完之后記得 unset LD_PRELOAD
,否則你會發現你什么文件也打不開了,因為系統級的 fopen()
函數已經被我們打樁替換掉了,不信的話可以在unset之前隨便vim一個文本試一下。所以,使用庫打樁機制時一定要慎重,否則可能造成不堪設想的后果。
庫打樁的應用
庫打樁機制有很多有意思的應用場景
- 通過添加某些語句,可以追蹤自己的程序對某些庫函數的調用情況
- 我們可以以此功能來使用自己的或是更好的函數(比如,你可以使用Google開發的tcmalloc來提升效率)
- 可以向別人的程序注入程序,從而達到特定的目的
- …
Ref:
https://fengmuzi2003.gitbook.io/csapp3e/di-07-zhang-lian-jie
https://www.bilibili.com/video/BV1RK4y1R7Kf?p=7