본문 바로가기
Programming/Win_API

API Hooking - [3] DLL Injection + Trampoline Code Hooking

by bbolmin 2013. 12. 7.


Trampoline Code Hooking은 후킹할 함수의 첫 부분에 jmp 코드로 후킹 함수로 실행 흐름이 이동하도록 한다. 

이 때 jmp 코드로 덮어진 부분은 저장해 두었다가 후킹 함수에서 원래 함수를 정상적으로 처리할 수 있도록 해준다.



아래에 실습할 MessageBox를 조작하는 방법은 아래와 같다.


1. 원본코드의 첫 5바이트에 hook 코드로 점프하도록 패치

2. hook코드로 넘어오면 원본코드 언훅 후 파라미터를 조작해서 호출

3. 다시 원본코드에 후킹 설치




#include "stdio.h"
#include "wchar.h"
#include "windows.h"


#define DEF_USER32	"User32.dll"
#define DEF_MBOX	"MessageBoxA"


typedef BOOL (WINAPI *PFMessageBox)(HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType);

// global variable
BYTE g_pOrgBytes[5] = {0,};


BOOL hook_code(LPCSTR szDllName, LPCSTR szFuncName, PROC pfNew)
{
    FARPROC pfOrg;
    DWORD dwOldProtect, dwAddress;
    BYTE pBuf[5] = {0xE9, 0, };
    PBYTE pByte;

    // 후킹 대상 API 주소를 구한다
    pfOrg = (FARPROC)GetProcAddress(GetModuleHandleA(szDllName), szFuncName);
    pByte = (PBYTE)pfOrg;

    // 만약 이미 후킹 되어 있다면 return FALSE
    if( pByte[0] == 0xE9 )
        return FALSE;

    // 5 byte 패치를 위하여 메모리에 WRITE 속성 추가
    VirtualProtect((LPVOID)pfOrg, 5, PAGE_EXECUTE_READWRITE, &dwOldProtect);

    // 기존 코드 (5 byte) 백업
    memcpy(g_pOrgBytes, pfOrg, 5);

    // JMP 주소 계산 (E9 XXXX)
    // => XXXX = pfNew - pfOrg - 5
    dwAddress = (DWORD)pfNew - (DWORD)pfOrg - 5;
    memcpy(&pBuf[1], &dwAddress, 4);

    // Hook - 5 byte 패치 (JMP XXXX)
    memcpy(pfOrg, pBuf, 5);

    // 메모리 속성 복원
    VirtualProtect((LPVOID)pfOrg, 5, dwOldProtect, &dwOldProtect);
    
    return TRUE;
}

BOOL unhook_code(LPCSTR szDllName, LPCSTR szFuncName)
{
    FARPROC pFunc;
    DWORD dwOldProtect;
    PBYTE pByte;

    // API 주소 구한다
    pFunc = GetProcAddress(GetModuleHandleA(szDllName), szFuncName);
    pByte = (PBYTE)pFunc;

    // 만약 이미 언후킹 되어 있다면 return FALSE
    if( pByte[0] != 0xE9 )
        return FALSE;

    // 원래 코드(5 byte)를 덮어쓰기 위해 메모리에 WRITE 속성 추가
    VirtualProtect((LPVOID)pFunc, 5, PAGE_EXECUTE_READWRITE, &dwOldProtect);

    // Unhook
    memcpy(pFunc, g_pOrgBytes, 5);

    // 메모리 속성 복원
    VirtualProtect((LPVOID)pFunc, 5, dwOldProtect, &dwOldProtect);

    return TRUE;
}

BOOL WINAPI MyMessageBox(HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType)
{
	char ChangeStr[20] = "Hello Hooking ~~!";
	FARPROC pforg_mbox;
	DWORD return_val;

	// 작업 전에 unhook
    unhook_code(DEF_USER32, DEF_MBOX);

	pforg_mbox = GetProcAddress(GetModuleHandleA(DEF_USER32), DEF_MBOX);
	return_val = ((PFMessageBox)pforg_mbox)(hWnd, (LPCTSTR)ChangeStr, lpCaption, uType);

	hook_code(DEF_USER32, DEF_MBOX, (PROC)MyMessageBox);

	return return_val;
}


BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
	switch( fdwReason )
	{
		case DLL_PROCESS_ATTACH:
			hook_code(DEF_USER32, DEF_MBOX, (PROC)MyMessageBox);	// MessageBoxA를 MyMessageBox로 jmp하도록 패치
			break;

		case DLL_PROCESS_DETACH:
			unhook_code(DEF_USER32, DEF_MBOX);
			break;
	}	

	return TRUE;
}






위에서 만든 DLL을 인젝션 시켜주면 아래와 같이 후킹이 성공한 것을 확인할 수 있다.






디버거로 MessageBoxA를 따라 들어가보면  아래와 같이 "jmp  100110c3"으로 패치되어 후킹된 함수로 점프하는 것을 확인할 수 있다.








후킹 함수에서는 "Hello Hooking ~~!"으로 파라미터를 변경한 후 언훅된 MessageBox를 호출한다.