Linux 中的 LD_PRELOAD 技巧是什麼?


簡介

LD_PRELOAD 是 Linux 動態連結器中一個強大且高階的功能,它允許使用者將共享物件檔案預載入到程序的地址空間(在程序開始執行之前)。這可以用於用自定義實現覆蓋程序中的某些函式,或在執行時將額外的程式碼注入到程序中。LD_PRELOAD 通常用於除錯和測試目的,但它也可能用於惡意目的,例如將惡意軟體注入到程序中。

LD_PRELOAD 如何工作?

LD_PRELOAD 環境變數指定了一個共享物件檔案列表,動態連結器應該在任何其他共享物件檔案之前載入這些檔案。這些共享物件檔案被稱為“預載入庫”。當執行一個程序時,動態連結器會搜尋LD_PRELOAD中指定的共享物件檔案,並將它們載入到程序的地址空間。程序發出的任何函式呼叫都將指向預載入庫中的實現,而不是系統庫或其他共享物件檔案中的實現。

示例

例如,考慮這個簡單的“C”程式,它呼叫來自stdio.h 庫的printf 函式 -

#include <stdio.h>
int main() {
   printf("Hello, Earth!
"); return 0; }

輸出

Hello, Earth

如果我們編譯此程式並正常執行它,則printf 函式將從libc.so 庫中呼叫,libc.so 是大多數 Linux 系統上的標準“C”庫。但是,如果我們將LD_PRELOAD環境變數設定為指向包含printf自定義實現的共享物件檔案,則動態連結器將載入該共享物件檔案而不是libc.so,並且當程式執行時將呼叫printf的自定義實現。

要設定LD_PRELOAD環境變數,我們可以在終端中使用export 命令 -

$ export LD_PRELOAD=/path/to/custom_printf.so

然後,當我們執行程式時,將呼叫“printf”的自定義實現,而不是libc.so 中的實現 -

$ ./a.out
Hello, Earth!

LD_PRELOAD 的使用案例

LD_PRELOAD 有幾個常見的使用案例 -

除錯和測試

LD_PRELOAD 最常見的用途之一是覆蓋程式中的函式,以進行除錯和測試。例如,我們可能希望編寫printf的自定義實現,該實現記錄對該函式的所有呼叫,或者編寫malloc的自定義實現,該實現檢查記憶體洩漏。透過使用LD_PRELOAD,我們可以輕鬆地將這些自定義實現插入到程式中,而無需修改原始碼。

動態連結

LD_PRELOAD 也可用於將程式動態連結到編譯時未連結的共享物件檔案。如果我們想使用系統上未安裝的庫,或者想使用系統上安裝的庫的較新版本,這將非常有用。

惡意軟體注入

不幸的是,LD_PRELOAD 也可能用於惡意目的,例如將惡意軟體注入到程序中。因此,在使用LD_PRELOAD時務必謹慎,並且只使用可信的共享物件檔案。

LD_PRELOAD 的限制

在使用LD_PRELOAD時,需要考慮一些限制 -

僅覆蓋函式

LD_PRELOAD 只能覆蓋透過動態連結器呼叫的函式。這意味著它不能覆蓋透過程式程式碼直接呼叫的函式,或者在靜態連結庫中實現的函式。

共享物件依賴項

預載入庫必須是自包含的,不能依賴於其他共享物件檔案。如果預載入庫依賴於另一個共享物件檔案,則動態連結器將無法載入它。

預載入庫的順序

LD_PRELOAD中指定預載入庫的順序很重要。如果兩個預載入庫都為同一個函式提供了實現,則將使用LD_PRELOAD中首先指定的庫中的實現。

安全隱患

如前所述,LD_PRELOAD 可用於惡意目的,例如將惡意軟體注入到程序中。在使用LD_PRELOAD時務必謹慎,並且只使用可信的共享物件檔案。

示例:覆蓋 printf

為了演示LD_PRELOAD的工作原理,讓我們建立一個簡單的共享物件檔案,該檔案提供printf函式的自定義實現。我們將首先建立一個名為custom_printf.c的檔案,內容如下 -

#include <studio.h>
int printf(const char *format, ...) {
   va_list args;
   va_start(args, format);
   vprintf("Custom printf: ", args);
   va_end(args);
   return 0;
}

printf 的此實現只是將字串“Custom printf:”新增到輸出的開頭。接下來,我們將使用gcc編譯器將custom_printf.c編譯成共享物件檔案 -

$ gcc -fPIC -shared -o custom_printf.so custom_printf.c

現在,我們可以使用LD_PRELOAD覆蓋程式中的printf函式。首先,我們將LD_PRELOAD環境變數設定為指向custom_printf.so -

$ export LD_PRELOAD=/path/to/custom_printf.so

示例

然後,我們將執行一個簡單的“C”程式,該程式呼叫printf -

#include <stdio.h>
int main() {
   printf("Hello, Earth!
"); return 0; }

輸出

Hello, Earth!

當我們執行程式時,輸出將是“Custom printf: Hello, world!” -

$ ./a.out
Custom printf: Hello, Earth!

正如我們所看到的,custom_printf.so中提供的printf的自定義實現被呼叫,而不是libc.so中的實現。

結論

總的來說,LD_PRELOAD是 Linux 動態連結器中一個強大的功能,它允許使用者在程序開始執行之前將共享物件檔案預載入到程序的地址空間。這可以用於各種目的,例如除錯和測試、動態連結和惡意軟體注入。但是,在使用LD_PRELOAD時務必謹慎,並且只使用可信的共享物件檔案。LD_PRELOAD有一些限制,例如無法覆蓋靜態連結的函式以及預載入庫必須是自包含的。瞭解LD_PRELOAD的工作原理及其侷限性可以幫助使用者有效地利用此功能開展工作。

更新於: 2023年1月4日

5K+ 次檢視

開啟你的 職業生涯

透過完成課程獲得認證

開始學習
廣告

© . All rights reserved.