Runlib32 - Linux 的 Rundll32.exe 實做
rundll.exe (Win16 / Windows 3.x) 與 rundll32.exe (Win32) 是 Microsoft Windows 提供一個特別的機制,允許直接找出 .DLL 或 .EXE 中 function entry,並給定參數後作呼叫,使用方法如下:
Rundll32.exe DllFileName FuncName
如果我們設計了一個 MyDLL.dll,並在其中定義了一個 MyFunc 的函式,於是透過以下指令即可呼叫該函式的實做部份:
Rundll32.exe MyDLL.dll MyFunc
而不需要額外的應用程式去呼叫,這也讓我們想到,如果某個套裝軟體安裝後,在某個 .dll 提供了惡意的 function,是不是有機會以 shell 去觸發 RunDll32.exe 去執行惡意行為呢?是的,的確有這樣的方式,也有許多變形。這裡不探討作壞事的技術細節 (小弟金盆洗手很久了 :P),詳細 Rundll 的應用方式可參考 [
Using Rundll],熟悉 Win32 底層技術的開發者,切換到 UNIX 系統時,不免會有些疑惑:是否有類似 Rundll32.exe 的工具去驗證 shared objects 呢?
[
izik] 創作了 [
runlib] 這個軟體專案,允許在 Linux 上施行類似 Win32 Rundll32.exe 的行為,並提供更強大的功能,這裡展示 "Hello World" 的 Runlib 版本:
Runlib32$ ./runlib -v -x printf-out libc.so.6,puts \""Hello World"\"
------------------------------------------------------------
puts[<0xb7ed8610>]@libc.so.6[]
------------------------------------------------------------
* Stack Generated (1 parameters, 4 bytes)
-----------------------------------------
Generated Assembly
------------------
* pushl $0xbfce7c9a
* call 0xb7ed8610
Streams Buffers
---------------
* Standart Output (STDOUT) : 15 bytes
* Standart Error (STDERR) : 0 bytes
Function Result
---------------
* Pointer: No
* Value: 12
Runlib32$ cat printf-out
Hello World
首先,我們試著去尋找 glibc (libc.so.6,數字 6 表示 ABI version) 中 Standard C Library 的 puts 函式進入點,產生 x86 stack push / call 的動作,執行的結果導入 stdout,並獲得一個 Function result,我們將過程所產生的 streams buffers 印出即得到 "Hello World",[
runlib] 提供的文件 RUNLIB-HOWTO 介紹更複雜的使用方式,可作為參考。
稍早在「深入淺出 Hello World」系列演講提到 function / system call invocation 的細節,[
runlib] 就是一個很好的示範,整個程式最核心的部份就是 src/lib.c,而呼叫 syscall 的程式碼片段如下:
if (ptr->stack) {
for (j = 0; j < ptr->stack->stack_items; j++) {
__asm__ __volatile__ (\
"pushl %0 \n" \
: \
: "r" (ptr->stack->stack[j]) \
: "%eax" \
);
}
}
ret = (unsigned long) ptr->fcn_handler();
if (ptr->stack) {
ptr->stack->stack_items *= sizeof(long);
__asm__ __volatile__ (\
"addl %0, %%esp \n" \
: \
: "r" (ptr->stack->stack_items) \
: "%esp" \
);
ptr->stack->stack_items /= sizeof(long);
}
s_errno = errno;
signal(SIGSEGV, SIG_DFL);
首先我們必須處理 x86 stack 中 function parameter 的順序與返回狀態的議題,上述兩個 inline assembly 片段則負責這個工作,至於 ptr->fcn_handler,可參考載入 shared object 並查詢 symbol 的部份:
int lib_load(libptr *ptr) {
ptr->lib_handler = __lib_dlaction(dlopen(ptr->lib_name, RTLD_LAZY));
ptr->fcn_handler = __lib_dlaction(dlsym(ptr->lib_handler, ptr->fcn_name));
if (!ptr->lib_handler || !ptr->fcn_handler) {
return 0;
}
[
runlib] 也特別處理了 Stream I/O 的部份,也給我們許多新的啟發,比方說實做一個 ELF sandbox,對 Unit test 也派得上用場,特定的 Test case 則可驗證單獨 function,搭配 PIE (Position-Independent Execution) 或許可創造更多變化。
由 jserv 發表於 July 25, 2006 10:37 PM