리버싱 10
앙 귬귬띠
DLL 인젝션
- 훅(Hook) 이란 ?
중간에서 오고가는 정보를 엿보거나 가로채기 위해 초소를 설치함. (갈고리를 건다)
이중에서 메세지 훅에 대해서 알아봅시다.
- 메세지 훅 ?
Window는 GUI를 제공하고 Event Driven으로 동작한다고 핟나. 키보드/마우스 등 어떠한 행위를 할 때 이벤트가 발생하고 Window는 미리 정의된 메세지를 해당 응용 프로그램으로 통보합니다. 아래 그림을 살펴봅시다.
만약에 키보드에 입력 이벤트가 발생하면 WM_KEYDOWN 메세지가 OS queue에 추가됩니다. OS는 어느 응용 프로그램에서 이벤트가 발생한지 확인 후에 큐에서 꺼내서 Application message queue에 추가합니다. 응용 프로그램(메모장)은 자신의 app 큐를 모니터링 하다가 WM_KEYDOWN 메세지를 확인한 다음에 해당 event handler를 호출하게 됩니다.
위 그림과 같이 훅이 설치되어있으면 OS와 APP 큐 사잉 ㅔ설치된 훅체인을 통해 키보드 메세지 훅들을 응용 프로그램보다 먼저볼 수 있습니다.
-> 저부분에서 가로채는것 뿐만 아니라 메세지 자체의 변경도 가능하며, 아래로 안내려 보낼수도 있다!
메세지 훅같은 경우에는 SetWindowsHookEx() API를 사용하면 간단하게 구현할 수 있다고 합니다.
HHOOK SetWindowsHookExA(
int idHook, // hook type
HOOKPROC lpfn, // hook procedure
HINSTANCE hmod, // 프로시저가 속해있는 DLL 핸들
DWORD dwThreadId // hook을 걸고 싶은 thread ID
);
msdn - api 정의
훅 프로시저는 OS가 호출해주는 콜백 함수이며 훅을 걸때 DLL 내부에 존재해야 한다.
4번째 변수 dwThreadID를 0으로 줄경우 전역(Global) 훅이 설치되며 실행중인 모든 프로세스에 영향이 간다.
키보드 메세지 후킹 실습
- chrome.exe 프로세스의 키보드 메세지를 가로채서 입력을 받지 못하도록 하는 실습
HookMain.exe를 실행하면 다음과 같은 창이 뜬다.
이 상태로 chrome.exe를 실행하여 키보드 입력을 해봅니다.
-> 되지 않음
chrome.exe의 메모리에 keyhook.dll이 인젝션 된것이 보인다. 키보드 이벤트 발생시 chrome.exe는 아무것도 받지 못하기 때문에 검색창에 입력이 되지않는다.
Hookmain.exe 디버깅
먼저 우리가 봤었던 press 'q'를 찾아봅시다. (문자열 검색기능 이용)
40104D에서 찾을 수 있습니다. 여기가 main()의 안이겠죠 ?
401000에 BP를 설치한 후에 디버깅을 위부터 차례대로 시작합니다.
401006의 주소에서 Loadlibrary("keyhook.dll")을 호출합니다.
이후에 40104B의 call ebx에서 keyhook.hookstart() 함수가 호출됨을 알 수 있습니다.
call 안으로 들어가봅시다.
지금 보이는 그림은 HookMain.exe 프로세스에 로딩된 Keyhook.dll의 HookStart() 함수 입니다. 여기서 SetWindowsHookEx의 첫 번째와 두 번째 인자값들을 알 수 있습니다.
먼저 API의 첫 번째 파라미터 값은 WH_KEYBOARD(2)입니다. 두 번째 파라미터(lpfn)의 값은 10001020 이며, 이 값이 바로 훅 프로시저의 주소라고 합니다. main()의 나머지 부분들은 'q'를 입력시 종료받는 부분입니다. (디버깅해보기)
이제 notepad.exe를 실행 시킨다음에 HookMain.exe를 실행시키고 키보드 입력을 받습니다.
(디버그 dll 로드 on)
다음과 같이 10000000의 주소에 keyhook.dll이 로드되는 걸 볼 수 있습니다. 해당 EP로 가봅시다.
아까 봤던 훅 프로시저의 주소 (10001020)에 BP를 걸면 키보드 입력 이벤트가 발생할 때마다 이곳에서 멈춰집니다.
Hookmain.cpp과 keyhook.cpp 소스코드를 첨부합니다.
출처 - reversecore
// HookMain.cpp 출처 - reversecore
/dll 로더
#include "stdio.h"
#include "conio.h"
#include "windows.h"
#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 = LoadLibrary("KeyHook.dll");
// HookStrat, HookStop 함수의 주소를 얻어온다.
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);
}
//keyhook.cpp
#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