July 06, 2009

隱藏式取得動態函式庫的 C 函式實做

前文 [dlopen 的 _init 與 _fini] 與 [LD_PRELOAD 的應用] 提過 GNU/Linux 下,動態函式庫操作的技巧,可看到 dlopen(3), dlsym 搭配 _init/_fini 或自訂函式,達到攔截函式進入點的功能。那麼,是否能提供「隱藏式」的機制,讓原始程式碼不甚明顯,但執行時期也有同等功效呢?本文就要探討這樣的技巧。

實驗的目標是動態載入 libm.so 裡頭的 exp() 函式,並計算 e 的值,也就是呼叫 exp(1.0),這部份實做於 lookup(),有趣的是以下程式碼列表的 do_magic() 函式: (findsym.c)
#include <stdio.h>
#include <dlfcn.h>

extern void payload();
typedef double (*proto)(double);
proto _exp = (proto) (void *) &payload;

/* once no exp found, use this */
static double failback(double x) { return 0.0; }

static void lookup()
{
	void *handle = dlopen("libm.so", RTLD_LAZY);
	if (!handle) {
		fprintf(stderr, "E: unable to load libm\n" );
		_exp = (proto) (void *) &failback;
		return;
	}
	_exp = (proto) dlsym(handle, "exp");
	if (!_exp) {
		dlclose(handle);
		_exp = (proto) (void *) &failback;
		fprintf(stderr, "E: unable to find exp\n");
	}
}

static void do_magic() {
	__asm__ __volatile__ (
		"	.section .bss\n"
		"	.align  4\n"
	"external_ebp:	.type   external_ebp, @object\n"
		"	.size   external_ebp, 4\n"
		"	.text\n"
	"payload:\n"
		"	mov     %ebp, external_ebp\n"
		"	mov     %esp, %ebp\n"
		"	call    lookup\n"
		"	mov     external_ebp, %ebp\n"
		"	jmp     *_exp\n"
); }

int main()
{
	printf("e = %f\n", _exp(1.0));
	return 0;
}
透過調整 IA32 stack 的 %ebp,將變數 _exp 指向由 dlopen + dlsym 找出的 libm::exp() 位址,進一步跳躍到函式進入點,達到所設立的目標。參考的編譯與執行輸出:
$ gcc -o findsym{,.c} -O0 -ldl && ./findsym
e = 2.718282
由 jserv 發表於 July 6, 2009 05:43 PM
迴響

人黑真好

aguai 發表於 July 31, 2009 04:46 AM

其實可以不用維護 ebp 的值呀~

若為了方便 call stack 重建而保存 ebp, 應該要 push 進 stack 唷~

Nono 發表於 August 25, 2009 01:33 AM
發表迴響









記住我的資訊?