공부해요/리버싱_핵심원리

21. Windows 메시지 후킹

yenas0 2024. 11. 4. 14:29
반응형

훅(Hook)
훅은 원하는 정보를 낚아채기 위해 사용하는 도구로, 특정 정보를 엿보거나 가로채기 위해 사용됨. 확장된 의미로 시스템이나 프로그램의 동작 흐름에 개입하여 필요한 정보를 얻거나, 데이터를 조작하는 목적에서도 사용됨
예시

  • 어떤 집단에 검문 절차가 3단계로 구성되어 있었는데, 스파이가 검문 절차 하나를 추가해 4단계로 만듦
  • 집단 내부의 사람들은 별다른 의심 없이 이 절차를 따르게 되고, 스파이는 이를 통해 필요한 정보를 쉽게 얻을 수 있게 됨
  • 이 과정에서 스파이가 추가한 검문 초소 설치 작업을 ‘훅을 건다’ 또는 ‘훅을 설치한다’고 표현함

후킹(Hooking)
후킹은 훅을 통해 실제로 정보를 엿보고 조작하는 행위 자체를 의미함. 훅이 설치된 후 후킹을 통해 중간에서 정보를 가로채거나 데이터를 변경할 수 있음
 
메시지 훅
메시지 훅은 시스템의 메시지 흐름에 개입하여 중간에서 메시지를 엿보거나 필요 시 조작하는 방식임.

메시지 후킹 동작 원리

위에서처럼 키보드 메시지 훅이 설치되었으면 OS 메시지 큐와 응용프로그램 메시지 큐 사이에 설치된 훅 체인에 있는 키보드 메시지 훅들이 응용 프로그램보다 먼저 메시지 확인 가능 or 메시지 변경 or 메시지 가로채 아래로 못가도록 함
 
 
 
SetWindowsHookEx()
메시지 훅은 SetWindowsHookEx() API로 구현이 가능함

HHOOK SetWindowsHookEx(
	int idHook,		//hook type
    HOOKPROC lpfn,		//hook procedure
    HINSTANCE hMod,		//위 hook procedure가 속해 있는 DLL 핸들
    DWORD dwThreadID		//hook을 걸고 싶은 thread의 ID
);

 
 
키보드 메시지 후킹 실습
키보드 메시지 후킹은 특정 DLL을 통해 키보드 입력 이벤트를 중간에서 가로채고 엿보는 방법임. 이 실습에서는 KeyHook.dll 파일과 HookMain.exe 프로그램을 사용하여 키보드 메시지 후킹을 설정함

  • KeyHook.dll: 훅 프로시저가 정의된 DLL 파일로, 키보드 메시지를 감지하고 가로채는 기능을 수행함
  • HookMain.exe: KeyHook.dll을 로딩하고 키보드 훅을 설치하는 프로그램으로, 이 파일이 DLL을 로드한 후 훅을 설정함

동작 과정

  1. HookMain.exe가 실행되면서 KeyHook.dll을 로딩함
  2. SetWindowsHookEx() API를 호출하여 키보드 메시지 훅을 설치함
  3. 키 입력 이벤트가 발생할 때마다 운영체제는 KeyHook.dll을 키 입력 발생 프로세스에 강제로 로딩하여 이벤트를 훅 체인에 연결된 훅 프로시저에서 먼저 처리하도록 함

 
 
SetWindowsHookEx()
 
메시지 훅은 SetWindowsHookEx() API로 구현이 가능함

HHOOK SetWindowsHookEx(
    int idHook,         // 훅 타입 (WH_KEYBOARD_LL 등)
    HOOKPROC lpfn,      // 훅 프로시저
    HINSTANCE hMod,     // 훅 프로시저가 속한 DLL 핸들
    DWORD dwThreadID    // 훅을 걸고 싶은 스레드의 ID (0일 경우 전체 적용)
);

 

메시지 훅과 후킹 동작 원리

키보드 메시지 훅이 설정되면, OS 메시지 큐와 애플리케이션 메시지 큐 사이에 설치된 훅 체인이 동작함. 이 체인에 의해 키보드 메시지가 애플리케이션에 전달되기 전에 훅 프로시저에서 먼저 확인, 수정, 또는 차단할 수 있음. 메시지 훅은 애플리케이션보다 먼저 메시지를 확인할 수 있으며, 필요시 메시지를 변경하거나 전달을 차단함으로써 후킹을 수행함

후킹(Hooking)

후킹은 훅을 통해 실제로 정보를 엿보거나 조작하는 행위임. 예를 들어 메시지 후킹은 메시지를 중간에서 가로채거나 조작하는 것을 의미하며, 키보드 메시지 후킹은 키 입력을 중간에서 가로채 키 입력 데이터를 엿보거나 기록하는 것을 의미함
 
https://github.com/reversecore/book/tree/master/%EC%8B%A4%EC%8A%B5%EC%98%88%EC%A0%9C/03_DLL_Injection/21_Windows_%EB%A9%94%EC%8B%9C%EC%A7%80_%ED%9B%84%ED%82%B9/bin

book/실습예제/03_DLL_Injection/21_Windows_메시지_후킹/bin at master · reversecore/book

리버싱 핵심원리 - 소스 코드 및 실습 예제. Contribute to reversecore/book development by creating an account on GitHub.

github.com

실습 코드는 위에서 다운로드 받으면 된다
 
실습 방법
1. HookMain.exe 실행 - 키보드 훅 설치
2. Notepad.exe 실행
: 사용자의 키도 입력 무시함. 프로세스 익스플로러로 노트패드의 프로세스 보면 KeyHook.dll 로딩된 것 확인 가능하다.
3. HookMain.exe 종료 - 키보드 훅 제거
 
 
 
HookMain.exe파일의 소스코드

// HookMain.cpp

#include "stdio.h"
#include "conio.h"
#include "windows.h"

#define DEF_DLL_NAME "KeyHook.dll"
#define DEF_H00KSTART “HookStart"
#define DEF_H00KST0P "HookStop"

typedef void(*PFN_H00KSTART)();
typedef void(*PFN_H00KST0P)();

void main() {
	HMODULE hDU = NULL;
	PFN_H00KSTART HookStart = NULL;
	PFNiHOOKSTOP HookStop = NULL; 
    char ch = 0;
    
	// KeyHook.dU 로딩
    hDU = LoadLibraryA(DEF_DLL_NAME);
	
    // export 함수주소얻기
	HookStart =(PFN_H00KSTART)GetProcAddress(hDll, DEF_H00KSTART); 
    HookStop =(PFN_H00KST0P)GetProcAddress(hDU, DEF_H00KST0P);

	// 후킹 시작 
    HookStart();
	
    // 사용자가 ‘q'를입력할때까지 대기 
    printf("press 'q' to quit!\n");
	while( _getch() != 'q' ) ;
	
    // 후킹 종료 
    HookStop();
	
    // KeyHook.dll 언로딩 
    FreeLibrary(hDU);
}

Keyhook.dll파일 로딩해 HookStart()함수를 호출하면 후킹 시작
HookStop()함수를 호출하면 후킹이 종료
 
 
DLL 소스코드이다

// KeyHook.cpp

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

#define DEF_PROCESS_NAME "notepad.exe"

HINSTANCE g_hlnstance = NULL;
HHOOK g_hHook = NULL;
HWND g_hWnd = NULL;

BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD dwReason, LPVOID IpvReserved)
{
	switch( dwReason )
	{
		case DLL_PROCESS_ATTACH:
			g_hlnstance = hinstDLL;
			break;
		case DLL_PROCESS_DETACH:
			break;
	}
	return TRUE;
}

LRESULT CALLBACK KeyboardProc(int nCode, WPARAM wParam, LPARAM 1Param)
{
	char szPath[MAX_PATH] = {0,};
	char *p = NULL;

	if( nCode >= 0 )
	{
		// bit 31: 0 = key press, 1 = key release
		if( ! (IParam & 0x80000000) ) // 키보드가눌렀다 떨어질 때
		{
			GetModuleFileNameA(NULL, szPath, MAX_PATH);
			p = strrchr(szPath, AV);

			// 현재 프로세스이름을비교해서 만약 notepad.exe라면, 
            // 메시지는 응용 프로그램 (혹은 다음 훅)으로 전달되지 않음 
            if( !_stricmp(p + 1, DEF_PROCESS_NAME) )
				return 1;
		}
	}

	// 일반적인경우에는 CallNextHookEx()를호출하여
	// 응용 프로그램 (혹은 다음 훅)으로 메시지를 전달함
	return CallNextHookEx(ghHook, nCode, wParam, IParam);
}

#ifdef _cplusplus
extern "C" {
#endif
_declspec(dllexport) void HookStart()
{
	ghHook = SetWindowsHookEx(WHKEYBOARD, KeyboardProc, g_hlnstance, 0);
}
_declspec(dllexport) void HookStop()
{
	if( ghHook )
	{
		UnhookWindowsHookEx(ghHook);
		g_hHook = NULL;
	}
}
#ifdef _cplusplus
}
#endif

 
익스포트 함수인 HookStart()함수 호출 시 SetWindowsHookEx()에 의해 키모드 훅 체인에 KeyboardProc() 추가

반응형