본문 바로가기
Programming/Win_API

API Hooking - [2] DLL Injection + IAT

by bbolmin 2013. 11. 24.



이번에는 IAT Hooking으로 실행흐름에서 제어권을 가져오고 DLL Injection으로 원하는 동작을 하는 코드를 실행시키는 방법으로 실습해보겠다. 


대상 프로그램은 "API Hooking - [1] Debugger Attach + 0xCC" 에서 실습한 MessageBox를 띄워주는 프로그램을 가지고 동일하게 "Hello World" 문자열을 "Hello Hooking" 문자열로 띄워지도록 후킹을 해보겠다.




IAT 후킹 코드는 아래의 IAT에 접근하는 방법을 보면 이해하기 쉬울 것이다.



IMAGE_DIRECTORY_ENTRY_IMPORT

 

|-------------------|
|     VirtualAddress   |
|-------------------|
|           Size           | 

|-------------------|


VirtualAddress + ImageBase가 IMAGE_IMPORT_DESCRIPTOR의 가상주소를 가진다.

 

 


IMAGE_IMPORT_DESCRIPTOR (임포트 테이블)

|----------------------|
|    OriginalFirstThunk    |  -> INT(IMPORT_NAME_TABLE) 주소
|----------------------|
|     TimeDateStamp       | 
|----------------------|
|     ForwarderChain     |
|----------------------|
|            Name             |  -> DLL name 주소
|----------------------|
|          FirstThunk        |  -> IAT(IMPORT_ADDRESS_TABLE) 주소
|----------------------|



1. IID테이블의 Name으로 DLL을 먼저 탐색한다.

2. 검색한 DLL의 IID테이블에서 FirstThunk를 탐색하여 후킹할 API의 주소를 탐색한다.

3. 이전 과정에서 구한 IAT 영역을 후킹한다.




 


[실습 소스]



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

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

FARPROC g_pOriginFunc = NULL;


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

	return ((PFMessageBox)g_pOriginFunc)(hWnd, (LPCTSTR)ChangeStr, lpCaption, uType);
}


BOOL Change_iat(LPCSTR szDllName, PROC pfnOrg, PROC pfnNew)
{
	HMODULE hMod;
	LPCSTR szLibName;
	PIMAGE_IMPORT_DESCRIPTOR pImportDesc;
	PIMAGE_THUNK_DATA pThunk;
	DWORD dwOldProtect, dwRVA;
	PBYTE pAddr;

	hMod = GetModuleHandle(NULL);

	pAddr = (PBYTE)hMod;				//IMAGE_DOS_HEADER
	pAddr += *((DWORD*)&pAddr[0x3C]);	//[e_lfanew] -> IMAGE_NT_HEADERS
	dwRVA = *((DWORD*)&pAddr[0x80]);	//IMAGE_IMPORT_DESCRIPTOR Table의 RVA

	pImportDesc = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD)hMod+dwRVA);	//IMAGE_IMPORT_DESCRIPTOR Table의 VA
	//pImportDesc는 IMAGE_IMPORT_DESCRIPTOR임. -> [OriginalFirstThunk, TimeDateStamp, ForwarderChain, Name, FirstThunk]


	/*
	(1). 후킹할 API함수가 있는 DLL이름을 비교하여 찾는다.
	(2). 후킹할 API함수의 주소와 IAT에 있는 주소가 일치한 곳을 구한다. 
	(3). (2)에서 얻은 IAT 부분을 후킹함수로 바꿔준다.
	*/
	for(; pImportDesc->Name; pImportDesc++)
	{
		szLibName = (LPCSTR)((DWORD)hMod + pImportDesc->Name);

		//(1). DLL을 찾기
		if( !stricmp(szLibName, szDllName) )	 //stricmp : 대소문자 구분없이 비교
		{
			pThunk = (PIMAGE_THUNK_DATA)((DWORD)hMod + pImportDesc->FirstThunk); //IAT의 시작부분을 가리킴.
			
			//(2) API함수가 있는 IAT부분 찾기
			for(; pThunk->u1.Function; pThunk++)
			{
				if( pThunk->u1.Function == (DWORD)pfnOrg)	//후킹할 API의 IAT 영역을 찾음
				{
					VirtualProtect((LPVOID)&pThunk->u1.Function, 4, PAGE_EXECUTE_READWRITE, &dwOldProtect);	// 메모리 속성 변경
					pThunk->u1.Function = (DWORD)pfnNew;			// IAT 후킹
					VirtualProtect((LPVOID)&pThunk->u1.Function, 4, dwOldProtect, &dwOldProtect);	// 메모리 속성 복원

					return TRUE;
				}
			}
		}
	}

	return FALSE;
}


BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
	switch( fdwReason )
	{
		case DLL_PROCESS_ATTACH:
			g_pOriginFunc = GetProcAddress(GetModuleHandle(L"User32.dll"), "MessageBoxA");

			Change_iat("User32.dll", g_pOriginFunc, (PROC)MyMessageBox);	// IAT상에 있는 "1번째 인자의 주소"를 "2번째 인자의 주소"로 변경
			break;
		case DLL_PROCESS_DETACH:
			Change_iat("User32.dll", (PROC)MyMessageBox, g_pOriginFunc);
			break;
	}	

	return TRUE;
}






DLL Injection은 http://reversecore.com/에서 받은 Inject.exe프로그램을 사용한다.


InjectDll.exe


프로그램 사용은 InjectDll.exe <pid> <dll_path>와 같이 해준다. 그럼 위에서 만든 IAT 후킹하는 DLL파일을 인젝션 시키면 

원하는 대로 후킹이 수행되어 "Hello Hooking"으로 조작시킬 수 있다.







Ollydbg로 MessageBox를 Call하는 부분을 비교해 보면 아래와 같다.



[ 원래의 API Call ]




[ 후킹되서 조작된 API Call ]