Introduction

 오늘은 Global Hooking 이라 불리는 전역후킹에 대해서 알아보도록 하겠습니다.
아시다시피 Windows 는 Event Driven 방식으로 되어 있습니다.
후킹이라 하면 이벤트가 Windows Message Queue 에서 Application Message Queue 로 넘어갈 때,
메세지를 중간에서 Hook 하는 것을 의미합니다.



Prepare

 전역 후킹을 하는 방법을 간단히 설명드리면 다음과 같습니다.

 [필요한 것]
1. 어떤 메세지를 후킹할 것인가?
2. 메세지를 후킹했을 때, 원하는 작업을 수행할 함수.
3. 그리고 그 함수를 포함한 DLL 파일
4. 이 후킹 작업을 해 줄 프로그램(exe)

 [사용할 API]
1. LoadLibrary
HMODULE WINAPI LoadLibrary(
  __in  LPCTSTR lpFileName
);
 - dll 을 load 할 때 사용


2. GetProcAddress
FARPROC WINAPI GetProcAddress(
  __in  HMODULE hModule,
  __in  LPCSTR lpProcName
);
 - dll  내부에 함수의 포인터를 구할 때 사용


3. CallNextHookEx
LRESULT WINAPI CallNextHookEx(
  __in_opt  HHOOK hhk,
  __in      int nCode,
  __in      WPARAM wParam,
  __in      LPARAM lParam
);
 - 메세지 후킹 후, 메세지를 다음 chain 으로 전달할 때 사용


4. SetWindowsHookEx
HHOOK WINAPI SetWindowsHookEx(
  __in  int idHook,
  __in  HOOKPROC lpfn,
  __in  HINSTANCE hMod,
  __in  DWORD dwThreadId
);
 - Windows Hook 을 할 때 사용


5. UnhookWindowsHookEx
BOOL WINAPI UnhookWindowsHookEx(
  __in  HHOOK hhk
);
 - Hook 을 끝낼 때(chain 에서 빼낼때) 사용



Code

본격적으로 프로그램을 만들어 봅시다.
만들어 볼 프로그램은 다음과 같습니다.
프로그램은 Keyboard 의 메세지를 후킹합니다. 만약 사용자가 키보드에서 'm' 키를 누르면, MessageBox 를 띄웁니다.

간단한 프로그램이므로 Assembly 로 만들어 보겠습니다.
먼저 Hooking 한 메세지를 처리 할 함수와 해당 dll을 만들도록 합시다.


.386 
.MODEL FLAT, STDCALL 
OPTION CASEMAP:NONE 
  
INCLUDE windows.inc 
INCLUDE gdi32.inc 
INCLUDE user32.inc 
INCLUDE kernel32.inc 
  
INCLUDELIB gdi32.lib 
INCLUDELIB user32.lib 
INCLUDELIB kernel32.lib 
  
  
.DATA 
     szCaption db "CrystalCube", 0 
     szMessage db "Don't Press 'M' Key!", 0 
     
.DATA?
     g_hInstance HINSTANCE ? 
     g_hHook HHOOK ? 
  
  
.CODE 
DllEntry proc hinstDLL:HINSTANCE, dwReason:DWORD, lpvReserved:DWORD 
    cmp dwReason, DLL_PROCESS_ATTACH 
    jne EXIT 
    mov ebx, hinstDLL 
    mov g_hInstance, ebx 
EXIT: 
    mov eax, TRUE 
    ret 
DllEntry Endp 
  
  
KeyboardHook proc nCode:DWORD, wParam:WPARAM, lParam:LPARAM 
	push eax
	mov eax, lParam
	or eax, 00FFFFFFh
	.if (nCode == HC_ACTION && eax != 0C0FFFFFh)
		cmp wParam, VK_M
		JNE EXIT
		mov ebx, lParam
		and ebx, 80000000h
		cmp ebx, 0
		JNE EXIT
		push MB_OK 
		push offset szCaption 
		push offset szMessage 
		push NULL 
		call MessageBox 
	.endif
EXIT:
    push lParam 
    push wParam 
    push nCode 
    push g_hHook 
    call CallNextHookEx 
    ret 
KeyboardHook endp 
  
  
InstallHook proc 
    push 0 
    push g_hInstance 
    push KeyboardHook 
    push WH_KEYBOARD 
    call SetWindowsHookEx 
    mov g_hHook, eax 
    ret 
InstallHook endp 
  
  
UninstallHook proc 
    push g_hHook 
    call UnhookWindowsHookEx 
    mov g_hHook, NULL 
    ret 
UninstallHook endp 
  
  
End DllEntry 




물론 Export 시키기 위해서 DEF 파일도 만들어 주어야 합니다.


LIBRARY   KeyboardHook
EXPORTS   InstallHook
EXPORTS   UninstallHook 



이렇게 dll 파일이 완성되었습니다.


그럼 이제 후킹을 걸어줄 Test Application 을 만들어 보죠.


.386
.MODEL FLAT, STDCALL
OPTION CASEMAP:NONE


INCLUDE D:\masm32\include\windows.inc
INCLUDE D:\masm32\include\gdi32.inc
INCLUDE D:\masm32\include\user32.inc
INCLUDE D:\masm32\include\kernel32.inc
INCLUDE D:\masm32\include\msvcrt.inc

INCLUDELIB D:\masm32\lib\gdi32.lib
INCLUDELIB D:\masm32\lib\user32.lib
INCLUDELIB D:\masm32\lib\kernel32.lib
INCLUDELIB D:\masm32\lib\msvcrt.lib


.DATA
	szDllName db "GlobalHook.dll", 0
	szHookInstall db "InstallHook", 0 
	szHookUninstall db "UninstallHook", 0
	szStartMsg db "Hooking Started!", 13, 10, 0
	szHelp db "Press 'q' to quit!", 13, 10, 0
	
.DATA?
	hDll HMODULE ?
	pInstallFunc dd ?
	pUninstallFunc dd ?
	
.CODE
start:
	push offset szDllName
	call LoadLibrary
	mov hDll, eax
	
	push offset szHookInstall
	push hDll
	call GetProcAddress
	mov pInstallFunc, eax
	
	push offset szHookUninstall
	push hDll
	call GetProcAddress
	mov pUninstallFunc, eax
	
	call [pInstallFunc]
	
	push offset szStartMsg
	call crt_printf

	push offset szHelp
	call crt_printf
	
INPUT:
	call crt__getch
	cmp eax, 'q'
	jne INPUT
	
	call [pUninstallFunc]
	
	push hDll
	call FreeLibrary
	
	push NULL
	call ExitProcess
end start 




코드가 정리가 되어있지 않아 조금 복잡해 보일뿐, 자세히 보면 간단합니다.
(사실 주석 달기 귀찮지 않았다는 것은 거짓말이겠지요. =_=)



실행하면 보시는 것 처럼, 콘솔창이 뜨고 'm' 을 누르면 Alert 창이 뜹니다. :)


Eplilogue

항상 사용하는 C/C++ 만 이용하며, 편식하면 나중에 비만이 될 것 같아 편식하지 않으려고 이번 포스팅은 어셈으로 개발하였습니다. :)
그럼 오늘 블로깅은 여기서 마치겠습니다.
아~~~내일은 월요일이네요. OTL