1. 훅
Hook, 갈고리
(군사적으로 사용되는) 훅의 개념
"훅(hook)을 건다/설치한다 " or "후킹(hooking)한다"
→ 중간에 오고가는 정보를 엿보거나 가로채기 위해 검문/검색을 하는 "초소"를 설치한다.
2. 메시지 훅
Windows 운영 체제 (GUI 기반): Event Driven 방식
① 이벤트 발생 (ex. 키 보드 입력 이벤트)
→ WM_KEYDOWN 메시지가 OS message queue에 추가된다.
② OS는 이벤트가 발생한 응용 프로그램을 파악하고,
OS message queue에서 메시지를 읽어와서
해당 응용 프로그램의 message queue에 추가한다.
③ 응용 프로그램은 message queue에서 메시지를 읽어와서
해당 이벤트에 대응되는 event handler를 호출하여 이벤트를 처리한다.
3. SetWindowsHookEx()
an application-defined hook procedure 을 훅 체인에 추가
parameters:
① idHook: 추가하려는 hook procedure (Ipfn)의 hook type
✓ 예: WH_KEYBOARD
② Ipfn: hook procedur의 함수 포인터
③ hMod: hook procedur가 정의된 DLL의 handler
④ dwThreadld: hook procedur에 연결 시키려는 thread (응용프로그램).
이 값이 0이면, 실행되고 있는 모든 thread (용용프로그램)에 연결된다.
HHOOK WINAPI SetWindowsHookEx( _In_ int _In_ HOOKPROC Ipfn, _In_ HINSTANCE hMod, _In_ DWORD dwThreadld idHook, ·
사용 예: SetWindowsHookEx(WH_ KEYBOARD, keyHook, pDLLhandler, 0)
→ pDLLHandler가 가르키는 DLL에 정의된 keyHook을 통하여 동작하고 있는 모든 어플리케이션의 키보드 메시지를 후킹하도록 설정한다 (즉, 훅 체인에 등록한다)
4. 키보드 메시지 후킹 실습
실습 시나리오
① 키 보드 메시지 후킹을 위하여 KeyHook.dll과 HookMain.cpp를 작성한다.
KeyHook.dll
#include "stdio.h"
#include "windows.h"
#define DEF_PROCESS_NAME "notepad.exe"
HINSTANCE g_hInstance = NULL;
HHOOK g_hHook = NULL;
HWND g_hWnd = NULL;
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD dwReason, LPVOID lpvReserved)
{
switch( dwReason )
{
case DLL_PROCESS_ATTACH:
g_hInstance = hinstDLL;
break;
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
LRESULT CALLBACK KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)
{
char szPath[MAX_PATH] = {0,};
char *p = NULL;
if( nCode >= 0 )
{
// bit 31 : 0 => press, 1 => release
if( !(lParam & 0x80000000) )
{
GetModuleFileNameA(NULL, szPath, MAX_PATH);
p = strrchr(szPath, '\\');
// 현재 프로세스 이름을 비교해서 만약 notepad.exe 라면 0 아닌 값을 리턴함
// => 0 아닌 값을 리턴하면 메시지는 다음으로 전달되지 않음
if( !_stricmp(p + 1, DEF_PROCESS_NAME) )
return 1;
}
}
// 일반적인 경우에는 CallNextHookEx() 를 호출하여
// 응용프로그램 (혹은 다음 훅) 으로 메시지를 전달함
return CallNextHookEx(g_hHook, nCode, wParam, lParam);
}
#ifdef __cplusplus
extern "C" {
#endif
__declspec(dllexport) void HookStart()
{
g_hHook = SetWindowsHookEx(WH_KEYBOARD, KeyboardProc, g_hInstance, 0);
}
__declspec(dllexport) void HookStop()
{
if( g_hHook )
{
UnhookWindowsHookEx(g_hHook);
g_hHook = NULL;
}
}
#ifdef __cplusplus
}
#endif
HookMain.cpp
#define DEF_DLL_NAME "KeyHook.dll"
#define DEF_HOOKSTART "HookStart"
#define DEF_HOOKSTOP "HookStop"
typedef void (*PFN_HOOKSTART)();
typedef void (*PFN_HOOKSTOP)();
void main()
{
HMODULE hDll = NULL;
PFN_HOOKSTART HookStart = NULL;
PFN_HOOKSTOP HookStop = NULL;
char ch = 0;
// KeyHook.dll 로딩
hDll = LoadLibraryA(DEF_DLL_NAME);
if( hDll == NULL )
{
printf("LoadLibrary(%s) failed!!! [%d]", DEF_DLL_NAME, GetLastError());
return;
}
// export 함수 주소 얻기
HookStart = (PFN_HOOKSTART)GetProcAddress(hDll, DEF_HOOKSTART);
HookStop = (PFN_HOOKSTOP)GetProcAddress(hDll, DEF_HOOKSTOP);
// 후킹 시작
HookStart();
// 사용자가 'q' 를 입력할 때까지 대기
printf("press 'q' to quit!\n");
while( _getch() != 'q' ) ;
// 후킹 종료
HookStop();
// KeyHook.dll 언로딩
FreeLibrary(hDll);
}
② HookMain.exe를 실행한다.
a. KeyHook.dll을 로딩한다.
b. SetWindowsHookEx()함수를 이용하여 KeyboardProc()를 훅체인에 등록한다.
c. OS는 실행 중인 응용 프로그램들에 KeyHook.dll을 인젝션한다.
③ 응용프로그램에서 키 보드 입력 이벤트가 발생한다.
④ KeyboardProc()가 해당 이벤트 메시지를 먼저 받아 처리한다
4.1 실습 예제: HookMain.exe
① HookMain.exe 실행
② notepad.exe 실행
→ 키 보드 입력
→ 노트패드의 프로세스 확인
HookMain.exe를 실행하면 키보드 입력 받는 프로그램들이 먹통이 된다.
App.이 notepad.exe 이면 프로시져(KeyboardProc)가 바로 리턴하여 App에게 이벤트를 전달하나, 그 외의 App이면 훅체인의 다음 프로시져를 콜하기 때문이다.
4.2 소스코드 분석: KeyHook.cpp (KeyHook.dll)
다음과 같은 함수들로 구성된다.
DllMain
KeyboardProc
HookStart
HookStop
5.1 HookMain.exe 디버깅 - 문자열 검색
메인함수 분석
HookStart() 함수 분석
5.2 Notepad.exe 프로세스 내의 KeyHook.dll 디버깅
1) OllyDbg 실행 -> 옵션 설정
2) HookMain.exe를 실행
3) notepad.exe에 키보드 입력
OllyDbg가 멈추면서 'Executable Modules' 창이 뜬다