API Hooking 실습 첫 번째로 Debugger Attach해서 0xCC로 제어권을 받아서 후킹하는 방법을 해보자.
후킹할 프로그램은 아래와 같이 자신의 pid를 출력하고 아무키나 누르면 MessageBox를 띄워주는 프로그램이다.
- MessageBox APi 원형
int WINAPI MessageBox(
__in_opt HWND hWnd,
__in_opt LPCTSTR lpText,
__in_opt LPCTSTR lpCaption,
__in UINT uType
);
MessageBox의 파라미터는 위와 같은데 후킹을 통해 lpText에 있는 "Hello World ~~" 부분을 "Hello Hooking !"으로 바꿔보도록 하겠다.
[ 실습 소스 ]
#include "windows.h" #include "stdio.h" LPVOID g_pfMessageBox = NULL; LPVOID g_lpChangeStr = NULL; CREATE_PROCESS_DEBUG_INFO g_cpdi; BYTE g_INT3Byte = 0xCC, g_OriginByte = 0; BOOL InstallHook(LPDEBUG_EVENT pde) { g_pfMessageBox = GetProcAddress(LoadLibraryA("User32.dll"), "MessageBoxA"); memcpy(&g_cpdi, &pde->u.CreateProcessInfo, sizeof(CREATE_PROCESS_DEBUG_INFO)); ReadProcessMemory(g_cpdi.hProcess, g_pfMessageBox, &g_OriginByte, sizeof(BYTE), NULL); //원래 byte값 백업 WriteProcessMemory(g_cpdi.hProcess, g_pfMessageBox, &g_INT3Byte, sizeof(BYTE), NULL); //0xcc로 write함으로써 Hook설치 printf("\n[ Install Hook ] 0x%p (MessageBoxA) : 0x%2x -> 0xCC Patched\n", g_pfMessageBox, g_OriginByte); g_lpChangeStr = VirtualAllocEx(g_cpdi.hProcess, NULL, strlen("Hello Hooking ~!"+1, MEM_COMMIT, PAGE_READWRITE); // 후킹대상 프로세스에 메모리 할당 WriteProcessMemory(g_cpdi.hProcess, (LPVOID)g_lpChangeStr, "Hello Hooking ~!", strlen("Hello Hooking ~!"), NULL); // 변조할 문자열 복사 return TRUE; } BOOL HookProc(LPDEBUG_EVENT pde) { CONTEXT ctx; PBYTE lpBuffer = NULL; PEXCEPTION_RECORD per = &pde->u.Exception.ExceptionRecord; if( EXCEPTION_BREAKPOINT == per->ExceptionCode ) //ExceptionCode가 INT3인 경우 { if( g_pfMessageBox == per->ExceptionAddress ) //Exception 발생 주소가 후킹된 API함수의 주소인 경우 { //Unhook WriteProcessMemory(g_cpdi.hProcess, g_pfMessageBox, &g_OriginByte, sizeof(BYTE), NULL); ctx.ContextFlags = CONTEXT_CONTROL; GetThreadContext(g_cpdi.hThread, &ctx); WriteProcessMemory(g_cpdi.hProcess, (LPVOID)(ctx.Esp + 0x8), &g_lpChangeStr, sizeof(LPVOID), NULL); //lpText의 포인터를 바꿔줌 //현재 eip는 0xcc+1에 있음. ctx.Eip = (DWORD)g_pfMessageBox; //eip를 0xcc부분으로 되돌려줌 SetThreadContext(g_cpdi.hThread, &ctx); ContinueDebugEvent(pde->dwProcessId, pde->dwThreadId , DBG_CONTINUE); Sleep(0); //훅이 해제된 상태의 MessageBox가 실행되도록 아래의 WriteProcessMemory를 연기시킴 WriteProcessMemory(g_cpdi.hProcess, g_pfMessageBox, &g_INT3Byte, sizeof(BYTE), NULL); //다시 Hook설치함 return TRUE; } } return FALSE; } void DebugLoop() { DEBUG_EVENT de; DWORD dwContinueStatus; while(WaitForDebugEvent(&de, INFINITE)) { dwContinueStatus = DBG_CONTINUE; if( CREATE_PROCESS_DEBUG_EVENT == de.dwDebugEventCode) { InstallHook(&de); //후킹 시작 } else if( EXCEPTION_DEBUG_EVENT == de.dwDebugEventCode ) //EXCEPTION_BREAKPOINT { if(HookProc(&de)) //후킹을 수행하는 함수 continue; } else if( EXIT_PROCESS_DEBUG_EVENT == de.dwDebugEventCode ) { printf("[ Terminate Hook ]\n"); break; //디버거 종료 } ContinueDebugEvent(de.dwProcessId, de.dwThreadId, dwContinueStatus); //DBG_CONTINUE or DBG_EXCEPTION_NOT_HANDLED } } int main(int argc, char *argv[]) { DWORD dwPID; if(argc != 2) { printf("\nUSAGE : HookPractice.exe [pid] \n"); return 1; } dwPID = atoi(argv[1]); if(!DebugActiveProcess(dwPID)) { printf("DebugActiveProcess(%d) failed!!! \n" "Error code = %d \n", dwPID, GetLastError()); return 1; } DebugLoop(); }
위의 동작은 후킹할 프로세스의 MessageBoxA API함수의 시작 byte를 0xCC로 바꿔서 디버거에 제어권을 가져온 후
디버거에서 원하는 동작을 수행한 후 다시 원래 실행코드로 돌아가는 방식으로 동작한다.
위에서 만든 후킹 프로그램으로 테스트한 결과 "Hello World"에서 "Hello Hooking"으로 메시지 박스의 lpText 파라미터를 변조된 것을 볼 수 있다.
'Programming > Win_API' 카테고리의 다른 글
API Hooking - [3] DLL Injection + Trampoline Code Hooking (0) | 2013.12.07 |
---|---|
API Hooking - [2] DLL Injection + IAT (1) | 2013.11.24 |
API Hooking 정리 (0) | 2013.11.10 |
쓰레드 - 타이머기반 동기화 (0) | 2013.01.03 |
쓰레드 - 이벤트기반 동기화 (0) | 2013.01.03 |