Hooking 후킹 – SetWindowsHookEx

메시지 후킹은 앞서 말했듯이 운영체제가 사전에 정해놓은 특정 이벤트(키 입력, 윈도우 크기 변경 등과 같은 작업)를 프로그램이 운영체제에 요청하고 처리 결과를 프로그램에 통보하게 되는데, 이 지점을 가로채기, 즉 후킹하여 메시지를 엿보거나 조작할 수 있다.

이런 메시지 훅은 운영체제에서 제공하는 기본 기능(여기서 소개할 이외 후킹들도 운영체제가 지원하는 기능들을 이용한 것이다)으로 하나의 메시지에 여러 개의 훅을 설치할 수도 있는데 이를 훅 체인(Hook chain)이라고 한다.

이 메시지 훅에 사용되는 API은 SetWindowsHookEx로 다음과 같은 구조체를 가진다.

HHOOK WINAPI SetWindowsHookEx(

__in  int idHook, // 후킹 타입

__in  HOOKPROC lpfn, // 후킹 프로시저

__in  HINSTANCE hMod, // 앞서 지정한 후킹 프로시저가 속해있는 핸들

__in  DWORD dwThreadId // 후킹 설치할 스래드 ID지정, 0으로 지정하면 글로벌로 전체를 후킹한다.

);

SetWindowsHookEx 함수의 각 파라미터중 idHook은 아래의 값을 어떤 종류를 후킹을 할 건지 지정하는 값들이다.

정의 내용
02 WH_KEYBOARD 키보드 입력 모니터링
03 WH_GETMESSAGE PostMessage 를 통해서 메시지가 메시지 큐에 들어가는 것을 모니터링
04 WH_CALLWNDPROC SendMessage 프로시저가 처리되기 직전 시점을 모니터링
05 WH_CBT CBT 기반 프로그램의 윈도우 생성/소멸/활성화 등의 통지를 모니터링
06 WH_SYSMSGFILTER 다이얼로그 박스, 메뉴, 스크롤 바 등에서 생성되는 입력 메시지를 모니터링
07 WH_MOUSE 마우스 입력 모니터링
08 WH_HARDWARE 현재 Win32에서 구현되어 있지 않음
09 WH_DEBUG 다른 후킹 프로시저를 디버깅
10 (A) WH_SHELL 쉘 기반 프로그램에 상황 정보를 모니터링
11 (B) WH_FOREGROUNDIDLE 현재 활성화된 윈도우 스레드가 유휴 상태가 되는 시점을 모니터링
12 I WH_CALLWNDPROCRET  SendMessage 가 처리되고 리턴 되는 시점을 모니터링
13 (D) WH_KEYBOARD_LL WH_KEYBOARD 보다 저 수준에서 키보드 입력

내용을 모니터링

14 (E) WH_MOUSE_LL WH_MOUSE 보다 저 수준에서 마우스 입력

내용을 모니터링

[표] idHook 지정 값

그럼 먼저 Dll을 실행시킬 파일을 만들어 보도록 하자.

여기서도 다시 C++ 코드를 작성하였다(후킹과 관련 글은 C++을 이용하여 작성될 예정이다. 그 이유에 대해서는 2부 내용을 살펴보기 바란다). C#은 디버깅에도 문제가 있지만, 결정적으로 글로벌 후크가 C#이 사용하는 .NET Framework에서 지원되지 않는다는 단점이 있어, 역분석 언어로는 C++이 좋다.

그럼 메시지 후킹을 로드할 로드 프로그램을 만들어 보자. 아래 프로그램은 Visual C++ 2010에서 만들었다.

messagehook.cpp
#include
<windows.h>#include
<conio.h>

#include
<stdio.h>

typedef BOOL (WINAPI *InstHook)();

typedef BOOL (WINAPI *UninstHook)();

void main(int argc, char *argv[])

{

HINSTANCE inst = NULL;

if(argc != 2)

{

printf(“Usage : messagehooking <Filename>\n”);

return;

}

inst = LoadLibrary(argv[1]);

InstHook InstallHook = (InstHook) GetProcAddress(inst, “InstallHook”);

UninstHook UninstallHook = (UninstHook) GetProcAddress(inst, “UninstallHook”);

//메시지 후킹을 설정한다.

InstallHook();

printf(“Press ‘q’ key to Uninstall hook\n”);

while(_getch() != ‘q’);

//메시지 후킹을 해제한다.

UninstallHook();

FreeLibrary(inst);

return;

}

[예제] 메시지 후킹 로더 C++코드

이러한 후킹 로더 프로그램이 필요한 이유는 다른 프로그램을 후킹하기 위해서는 반드시 Dll 내부에 훅 프로시저가 존재해야 한다. 그래야 운영체제가 후킹을 시키고자 하는 Dll을 다른 프로세스에서 로드시켜서 후킹 프로시저를 호출할 수 있다. 따라서 후킹에 필요한 기능들을 Dll에 넣고, 위 내용과 같이 후킹 Dll을 로드시켜주는 프로그램을 작성해야 한다(위 프로그램을 통해 다른 Dll들도 로드할 수 있다).

keyhook.cpp
#include
“stdio.h”#include
“windows.h”

#include
“strsafe.h”

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)

{

if(nCode >= 0)

{

TCHAR buf[80];

StringCbPrintf(buf, sizeof(buf), TEXT(“%c”), wParam);

OutputDebugString(buf);

}

return CallNextHookEx(NULL, nCode, wParam, lParam);

}

extern
“C”
__declspec(dllexport) void InstallHook()

{

g_hHook = SetWindowsHookEx(WH_KEYBOARD, KeyboardProc, g_hInstance, 0);

}

extern
“C”     __declspec(dllexport) void UninstallHook()

{

UnhookWindowsHookEx(g_hHook);

g_hHook = NULL;

}

[예제] 키보드 메시지 후킹 예제

위 프로그램을 컴파일한 후, 다음과 같이 실행해보자.

Messagehook keyhook.dll

그리고 Dbgview를 실행하고, 키보드를 입력해 보면, 아래 화면과 같이 다른 윈도우에서 입력한 내용들이 Dbgview에서 나타나는 것을 확인할 수 있다.

후킹 Hooking - SetWindowsHookEx

[그림] 키보드 입력 메시지를 후킹하여 OutputDebugString으로 출력한다

Process explorer를 이용하여 어떤 프로세스에 로드되어 있는지도 확인할 수 있다.

[그림] 새로 실행한 모든 프로그램에 메시지 훅이 걸리는 걸 알 수 있다

Keyhook.dll은 모든 프로세스에 적용되는 것이 아닌 운영체제에서 키보드 이벤트를 받게 되는 프로세스에 대해 Keyhook.dll을 인젝션(Injection) 시키는 것을 알 수 있다. 그럼 이 프로그램을 간단히 디버깅 해보도록 하자.

앞서 배운 PE 파일 분석 방법을 통해 분석을 진행해보자. 먼저 PEiD를 통해 프로그램 작성에 사용된 코드를 확인하자.

[그림] C++로 개발되었고 패킹이 되어 있지 않음을 예측할 수 있다.

PEiD값을 통해 분석 흐름의 기초인 개발 언어를 확인할 수 있다. 물론 이 정보는 안티 디버깅을 통해 시그니쳐를 변경할 수 있으므로 100% 신뢰할 수 없다. 그럼 계속해서, 프로그램 내의 문자열 정보를 확인해 보자. Ollydbg를 실행하고, 프로그램을 로드하자. 그리고 단축 아이콘 “R“을 누르거나, 마우스 오른쪽 버튼 클릭 후, Scan for
à
All referenced text strings를 선택하자.

[그림] 프로그램에서 사용되는 스트링 분석

프로그램 내에 사용된 문자열 중 의심이 될 만한 문자열을 확인할 수 있다. Unknown Filename을 선택하고 더블 클릭하면, 해당 문자열이 사용된 코드로 이동하게 되는데, 본 로더 프로그램의 실행 초기 코드로(이외에도 API를 분석하여 코드 흐름을 파악할 수도 있다), 대략적인 흐름을 이해할 수 있게 된다.

코드가 간단해서 우리가 프로그래밍한 코드와 대조해 보면 일치함을 쉽게 확인할 수 있다. 해당 문자열을 입력해 이동해 보면, [ARG.1]과 값을 비교하여 2가 아니면, 프로그램이 종료되는 것을 알 수 있다(즉 입력한 구문이 2개일때만 프로그램이 실행된다).

00FB13AC |. F3:AB REP STOS DWORD PTR ES:[EDI]

//아래는 메시지 후킹을 진행할 파일을 받는 실행 인자로써 해당 주소에 브레이크 포인트를 설정하자.

00FB13AE |. C745 F8 00000000 MOV [LOCAL.2],0

00FB13B5 |. 837D 08 02 CMP [ARG.1],2

00FB13B9 |. 74 1E JE SHORT MessageH.00FB13D9

00FB13BB |. 8BF4 MOV ESI,ESP

00FB13BD |. 68 8457FB00 PUSH MessageH.00FB5784 ; /format = “press usage : Messagehook <Filename>\n”

00FB13C2 |. FF15 B082FB00 CALL DWORD PTR DS:[<&MSVCR100D.pr>; \printf

00FB13C8 |. 83C4 04 ADD ESP,4

00FB13CB |. 3BF4 CMP ESI,ESP

00FB13CD |. E8 69FDFFFF CALL MessageH.00FB113B

00FB13D2 |. 33C0 XOR EAX,EAX ; kernel32.BaseThreadInitThunk

[실습] Ollydbg로 확인한 실행 인자 비교

디버깅 진행을 위해서는 추가 인자를 입력해야 한다. 이를 이해 MessageHook.exe를 Ollydbg 에서 File
à
Fileopen시 Keyhook.dll의 인자 값을 입력해 진행하자.

[그림] 실행파일 오픈시 인자값을 추가할 수 있다

이제 다시 조금 전 분석하였던 비교 문장으로 이동하여, 해당 00FB13AE 코드에 브레이크 포인트(F2키)를 설정하고, 프로그램을 실행하면 해당 코드 지점에서 실행이 일시 정지된다.

이를 Step Over(F8키)로 진행하면서 코드를 분석하여 보자. 쉽게 이해할 수 있으리라 믿는다. 더욱이 현재 PDB파일(심볼 파일)이 함께 존재하므로, 분석이 한층 수월할 것이다(앞서 여러 번 경험해보았을 것이다).

[그림] 소드코드를 해당 어셈블리와 코드를 함께 매칭하여 준다

분석 중 우리가 여기서 주목해야 할 부분은 GetProcAddress이다 이 API가 어떠한 용도로 사용되는지 Win32 Programmer’s Reference(Ollydbg에서 Ctrl+F1키를 통해 바로 이용할 수 있다)를 이용하여 확인해 보면 다음과 같다.

[그림] 역분석시 API의 정보를 확인할 수 있다

정리하면 위 함수를 이용하여, 로드한 Dll의 Export 함수의 주소를 저장하는 역할을 한다. 이후 InstallHook()을 호출하는 함수까지 디버깅을 진행해보며 코드를 익히기 바란다.

그럼 계속해서 InstallHook()이 호출하는 Keyhook.dll를 확인하자.

Dll 자체를 바로 Ollydbg와 연결하면, 프로그램 실행 대한 정적 분석은 가능할 수 있지만, 다른 프로세스에 로드시킬 수 있는 로더가 없기 때문에 동적 분석이 불가능하다. 하지만, Ollydbg에서 제공하는 옵션을 이용하면 해당 내용을 분석할 수 있다. 그리고 Ollydbg를 실행하여, File
à
Open 메뉴를 선택하여, 메모장을 디버거로 실행한다.

연결하였다면, Debugging Options(Alt+O키)를 선택하여, 새로운 Dll이 로드될 때 멈추도록 이벤트를 선택하고, 디버거를 실행(F9키)한다.

[그림] Ollydbg의 이벤트 발생에 따른 정지 포인트 설정

그리고 Messagehook을 실행하고 노트패드에 입력을 하는 순간 Dll이 로드되면서 Ollydbg가 일시 정지하며 로드된 모듈창(Alt+E키)을 보여준다. 단축아이콘 바의 ‘C’를 눌려 호출 스택 정보도 함께 확인해 보면, Keyhook.dll을 로드하는 시점에 일시 정지된 것을 확인할 수 있다.

[그림] 새로운 Dll 로드시 일시 정지된다

KeyHook.dll이 마우스로 더블 클릭 후 Keyhook.dll의 Entry 포인트로 Ctrl+G키를 눌려 이동하면 아래 와 같이 실제 메인 코드를 확인할 수 있다. 그림에서는 00C08E10으로 이곳으로 다시 이동후 브레이크 포인트(F2키)를 설정하고 실행하자.

[그림] KeyHook의 메시지 후킹 코드 위치

이제 차근차근 분석해 보면 다음과 같은 코드를 만나게 되는데 해당 코드의 주요 코드인 KeyboardProc의 내용은 다음과 같이 분석할 수 있다.

//KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)
//스택 프레임 생성

00C082A0 /> 55 PUSH EBP

00C082A1 |. 8BEC MOV EBP,ESP

00C082A3 |. 81EC 1C010000 SUB ESP,11C

…중략

00C082C5 |. 8945 FC MOV DWORD PTR SS:[EBP-4],EAX

00C082C8 |. 837D 08 00 CMP DWORD PTR SS:[EBP+8],0 ; ß Code가 0이 아닌경우

00C082CC |. 7C 2A JL SHORT KeyHook.00C082F8 ; ß 00C082F8로 점프

//StringCbPrintf(buf, sizeof(buf), TEXT(“%c”), wParam);

00C082CE |. 8B45 0C MOV EAX,DWORD PTR SS:[EBP+C]

00C082D1 |. 50 PUSH EAX ; wParam

00C082D2 |. 68 6C3BC500 PUSH KeyHook.00C53B6C ; ASCII “%c”

00C082D7 |. 6A 50 PUSH 50 ; buf [80]

00C082D9 |. 8D4D A8 LEA ECX,DWORD PTR SS:[EBP-58] ;

00C082DC |. 51 PUSH ECX

00C082DD |. E8 02E5FFFF CALL KeyHook.00C067E4 ; ß StringCbPrintf 호출

00C082E2 |. 83C4 10 ADD ESP,10

00C082E5 |. 8BF4 MOV ESI,ESP

00C082E7 |. 8D45 A8 LEA EAX,DWORD PTR SS:[EBP-58] ; ß StringCbPrintf통해 변경된 buf값 EAX 복사

00C082EA |. 50 PUSH EAX ; ß 변환된 키보드 입력값이 저장된 buf

00C082EB |. FF15 2472C600 CALL DWORD PTR DS:[<&KERNEL32.OutputDebu>; \OutputDebugStringA

00C082F1 |. 3BF4 CMP ESI,ESP

00C082F3 |. E8 E3E8FFFF CALL KeyHook.00C06BDB

//return CallNextHookEx(NULL, nCode, wParam, lParam);

00C082F8 |> 8BF4 MOV ESI,ESP

00C082FA |. 8B45 10 MOV EAX,DWORD PTR SS:[EBP+10]

00C082FD |. 50 PUSH EAX ; /lParam

00C082FE |. 8B4D 0C MOV ECX,DWORD PTR SS:[EBP+C]

00C08301 |. 51 PUSH ECX ; |wParam

00C08302 |. 8B55 08 MOV EDX,DWORD PTR SS:[EBP+8]

00C08305 |. 52 PUSH EDX ; |HookCode

00C08306 |. 6A 00 PUSH 0 ; |hHook = NULL

00C08308 |. FF15 D873C600 CALL DWORD PTR DS:[<&USER32.CallNextHook>; \CallNextHookEx ß 키보드 메시지를 다음 후킹으로 전달

…중략

//함수 복귀

00C08340 |. 8BE5 MOV ESP,EBP

00C08342 |. 5D POP EBP

00C08343 \. C2 0C00 RETN 0C

[실습] 메시지 후킹 분석 내용

Facebook Comments

Leave A Reply

이메일은 공개되지 않습니다. 필수 입력창은 * 로 표시되어 있습니다.