사실 Windows 메시지 후킹 방법이랑 동일하다.
1) 마우스 이벤트에 훅을 걸고
2) calc.exe에서 우클릭 발생 시 "https://web2.0calc.com/" 가 크롬으로 열리는 식으로 구현
// ClickHook.dll
#include "stdio.h"
#include "windows.h"
#define DEF_PROC_NAME "calc.exe"
#define CHROME_PATH "C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe"
#define CALC_SITE "https://web2.0calc.com/"
HINSTANCE g_hInstance = NULL;
HHOOK g_hHook = NULL;
BOOL WINAPI DllMain(HINSTANCE hinstDll, DWORD fdwReason, LPVOID lpvReserved) {
switch(fdwReason) {
case DLL_PROCESS_ATTACH:
g_hInstance = hinstDll;
}
// dll load success
return TRUE;
}
LRESULT CALLBACK MouseProc(int nCode, WPARAM wParam, LPARAM lParam) {
if(nCode>=0) { // mouse message occurs
if(wParam == WM_RBUTTONDOWN) { // mouse right click
char szPath[MAX_PATH] = {0,};
char* p = NULL;
GetModuleFileNameA(NULL, szPath, MAX_PATH); // get current process' path
p = strrchr(szPath, '\\'); // szPath에서 '\'의 마지막 표시에 대한 포인터 반환
if( p!=NULL && !_stricmp(p+1,DEF_PROC_NAME)) {
char szCmd[MAX_PATH] = {0,};
sprintf(szCmd, "%s %s", CHROME_PATH, CALC_SITE);
STARTUPINFOA si = {0,};
PROCESS_INFORMATION pi = {0,};
si.cb = sizeof(STARTUPINFO);
// CreateProcess는 두번째인자 LPTSTR이므로
// CreateProcessA 사용, 두번째 인자 LPSTR, 9번째 인자 STARTUPINFOA
if(!CreateProcessA(NULL, szCmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) {
MessageBoxA(NULL, "Error in Opening Chrome", "ERROR", MB_ICONERROR | MB_OK);
} else {
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
}
}
}
}
return CallNextHookEx(g_hHook, nCode, wParam, lParam);
}
extern "C" __declspec(dllexport) void HookStart() {
g_hHook = SetWindowsHookEx(WH_MOUSE, MouseProc, g_hInstance, 0);
}
extern "C" __declspec(dllexport) void HookStop() {
if(g_hHook) {
UnhookWindowsHookEx(g_hHook);
g_hHook = NULL;
}
}
szCmd로 "%s --incognito %s" 하면 시크릿모드로 열림
// MouseHook.exe
#include "windows.h"
#include "stdio.h"
#include "conio.h"
#define DFN_DLL_NAME "ClickHook.dll"
#define DFN_HOOK_START "HookStart"
#define DFN_HOOK_STOP "HookStop"
typedef void(*PFN_HOOK_START)();
typedef void(*PFN_HOOK_STOP)();
void main() {
HMODULE hDll = NULL;
PFN_HOOK_START HookStart = NULL;
PFN_HOOK_STOP HookStop= NULL;
// LoadLibraryA는 인자로 LPCSTR, LoadLibraryW는 인자로 LPCWSTR
hDll = LoadLibraryA(DFN_DLL_NAME);
// GetProcAddress 두번째 인자는 LPCSTR(char *)이다, LPCTSTR(w_char *) 아님
// 함수명은 항상 영어,숫자,일부 특수문자로 구성되므로
HookStart = (PFN_HOOK_START)GetProcAddress(hDll, DFN_HOOK_START);
HookStop = (PFN_HOOK_STOP)GetProcAddress(hDll, DFN_HOOK_STOP);
HookStart();
printf("press 'q' to quit!\n");
while(_getch() != 'q');
HookStop();
FreeLibrary(hDll);
}
실행 결과 마우스 이벤트가 발생하는 모든 프로세스들에 ClickHook.dll이 inject 됨을 확인할 수 있었고,
그 중 calc.exe에서 우클릭이 발생할 때마다 새로운 탭이 열림을 확인했다.
1. WH_MOUSE vs. WH_MOUSE_LL 후크
=> WH_MOUSE_LL로 바꾸면 MouseHook.exe에만 dll inject되고 나머지 프로세스들에는 inject 안 됨
WH_MOUSE
- used when you want to monitor mouse events within the context of a specific process
- 후킹을 설치한 process의 context 내에서 마우스 이벤트를 모니터링합니다.
- 후킹 프로시저(MouseProc)가 DLL에 있어야 하며 프로세스에 주입되어야 합니다.
- 목표 프로세스("calc.exe"인 경우)에 주입되어야 올바르게 작동합니다.
WH_MOUSE_LL (LOW LEVEL)
- used when you want to monitor mouse events globally (across all processes)
- 모든 프로세스에서 전역적으로 마우스 이벤트를 모니터링합니다.
- 후킹 프로시저(MouseProc)가 DLL에 있거나 프로세스에 주입될 필요가 없습니다.
- 어떤 프로세스에서도 사용할 수 있으며 모든 프로세스의 마우스 이벤트를 수신합니다.
=> ClickHook.dll이 MouseHook.exe에 주입되고 훅이 셋팅된 이후에는 전역적으로 모든 이벤트에서 마우스 이벤트를 수신하기 때문에 마우스 이벤트가 발생하는 프로세스에 DLL이 주입될 필요가 없다
=> 특정 프로세스에서 마우스 이벤트가 발생하더라도 GetModuleFileNameA()의 반환값은 MouseHook.exe의 경로일 것이므로 이후 코드가 실행되지 않는다.
=> MouseProc() 콜백함수는 후크를 설치한 프로세스의 컨텍스트에서 실행된다 (다른 프로세스의 컨텍스트에선 x)
2. MAX_PATH vs. _MAX_PATH
_MAX_PATH는 C 런타임 헤더인 "stdlib.h"에 정의되어 있다, "stdio.h" include했을 때 사용할 수 있다
#define _MAX_PATH 260
MAX_PATH는 윈도우 헤더 파일인 "WinDef.h"에 정의되어 있다, "windows.h" include 했을 때 사용 가능하다
#define MAX_PATH 260
3. WH_MOUSE에 대한 콜백함수 MouseProc()
LRESULT CALLBACK MouseProc(
_In_ int nCode,
_In_ WPARAM wParam,
_In_ LPARAM lParam
);
- nCode: ncode가 0보다 작으면 hook procedure는 CallNextHook을 이용해 메시지를 넘겨야 함
0 [HC_ACTION] | The wParam and lParam parameters contain information about a mouse message |
3 [HC_NOREMOVE] | The wParam and lParam parameters contain information about a mouse message, and the mouse message has not been removed from the message queue |
- wParam: 마우스 메시지를 나타냄
WM_LBUTTONDOWN[UP], WM_RBUTTONDOWN[UP], WM_MOUSEWHEEL 등
https://learn.microsoft.com/ko-kr/windows/win32/inputdev/mouse-input-notifications
- lParam: MOUSEHOOKSTRUCT 구조체 포인터
4. _stricmp vs. strcmp
_stricmp와 stricmp는 이름의 차이만 있을 뿐 기능은 동일한 함수이다 (대소문자를 구분하지 않는 문자열 비교를 수행)
* _stricmp는 Microsoft Visual C++ 런타임 라이브러리에 속한 함수
* stricmp는 표준화된 이름이 아님
=> 표준화된 이름이 아닌 함수(예: stricmp)를 사용하면 이식성 문제가 발생할 수 있으므로 _stricmp와 같은 표준화된 이름을 사용하는 것이 좋습니다.
=> strcmp(대소문자 구분하여 비교), strcasecmp(_stricmp과 동인한 기능), _wcsicmp(대소문자 구분하지 않는 유니코드 문자열 비교)
=> strrchr 버전이 없는 이유는 strrchr이 이미 표준 C 라이브러리에서 제공하는 함수이므로
5. char *strstr(const char *, const char *); 사용
- 표준 C 라이브러리에서 제공하는 함수
- str1에서 str2 첫 번째 표시 시작 위치에 대한 포인터를 반환, str2가 나타나지 않으면 NULL 리턴
- DLL inject된 프로세스의 이름 가져올 때 유용하겠다
LRESULT CALLBACK MouseProc(int nCode, WPARAM wParam, LPARAM lParam) {
if(nCode>=0) { // mouse message occurs
if(wParam == WM_RBUTTONDOWN) { // mouse right click
char szPath[MAX_PATH] = {0,};
GetModuleFileNameA(NULL, szPath, MAX_PATH); // get current process' path
if(strstr(szPath, DEF_PROC_NAME)!=NULL) {
// if current process is calc.exe
6. MessageBox()
사용자에게 아래 함수인 MessageBox 함수를 사용하여 메시지를 보여줄 수 있습니다.
int MessageBox(HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType);
hWnd: MessageBox를 소유할 윈도우 핸들, NULL이면 owner handle 없음
lpText : 사용자에게 보여줄 메시지 문자열
lpCaption : MessageBox 창의 타이틀 문자열
uType : MessageBox의 type을 설정 (아이콘/버튼 종류)
https://rudalskim.tistory.com/307
7. Unicode를 사용하도록 ClickHook.dll 구현해보면?
Windows API 호환성, 문자 인코딩 일관성 등의 이유로 유니코드 문자열이 멀티바이트 문자열보다 안전
// ClickHook.dll
LRESULT CALLBACK MouseProc(int nCode, WPARAM wParam, LPARAM lParam) {
if(nCode>=0) { // mouse message occurs
if(wParam == WM_RBUTTONDOWN) { // mouse right click
wchar_t szPath[MAX_PATH] = {0,};
GetModuleFileName(NULL, szPath, MAX_PATH); // get current process' path
if(wcsstr(szPath, DEF_PROC_NAME)!=NULL) {
wchar_t szCmd[MAX_PATH] = {0,};
wsprintf(szCmd, L"%s %s", CHROME_PATH, CALC_SITE);
STARTUPINFO si = {0,};
PROCESS_INFORMATION pi = {0,};
si.cb = sizeof(STARTUPINFO);
if(!CreateProcess(NULL, szCmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) {
MessageBox(NULL, L"Error in Opening Chrome", L"ERROR", MB_ICONERROR | MB_OK);
} else {
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
}
}
}
}
return CallNextHookEx(g_hHook, nCode, wParam, lParam);
}
'security > 리버싱핵심원리' 카테고리의 다른 글
PE 패치를 이용한 DLL Injection (0) | 2023.03.20 |
---|---|
EjectDll.exe 구현 (0) | 2023.03.20 |
Registry를 이용한 DLL Injection (0) | 2023.03.18 |
CreateRemoteThread()를 이용한 DLL Injection (0) | 2023.03.18 |
HookMain.exe / KeyHook.dll 디버깅 (0) | 2023.03.17 |