메시지 훅(Message Hook)

메시지 훅(Message Hook)

 

프로그램에서 발생하는 메시지를 감시하면서 특정 메시지가 처리되기 전에 해당 메시지를 가로채어 별도로 정의된 기능을 수행하도록 지원하는 프로그래밍 기법을 의미합니다.

이러한 매커니즘을 이용하여 다른 프로그램의 처리가 일어나기 전에 원하는 기능을 수행하도록 구현할 수 있습니다.

1. 훅의 종류

1) 전역 훅

윈도우 전체 영역에 발생하는 메시지를 감시

별도의 dll 파일로 존재해야 합니다.

2) 로컬 훅

현재 프로그램에서 발생하는 메시지를 감시

2. 로컬 훅 사용 방법

1) 훅 프로시저 작성

LRESULT CALLBACK HookProc(int nCode, WPARAM wParam, LPARAM lParam)

nCode는 훅 프로시저가 처리해야 할 작업을 식별하기 위한 코드가 지정되고 wParam, lParam에는 nCode값에 따르는 부가 정보가 전달됩니다.

이 때 nCode값이 0보다 작다면 더 이상 처리하지 않고 다음 훅 프로시저에게로 제어를 넘겨야 합니다.

예)

LRESULT CALLBACK KeyboardHookProc(int nCode, WPARAM wParam, LPARAM lParam)

{

HDC hdc;

TCHAR s[256];

if(nCode < 0)

{

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

}

hdc = GetDC(ghWnd);

wsprintf(s, “Hooked Key Code = %3d.”, wParam);

TextOut(hdc, 50, 50, s, lstrlen(s));

ReleaseDC(ghWnd, hdc);

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

// 모든 처리가 종료되면 다음 훅 프로시저에게 전달

}

 

2) 훅 프로시저를 훅 체인에 등록

HHOOK SetWindowsHookEx(int idHook, HOOKPROC lpfn, HINSTANCE hMod, DWORD dwThreadId);

설치 된 후 훅 프로시저의 핸들을 반환하며 첫 번째 인자는 설치될 훅 프로시저의 타입을 정의하는 식별자가 되며 아래와 같은 값들이 있습니다.

WH_KEYBOARD: 키보드 메시지를 모니터링

WH_MOUSE: 마우스 메시지를 모니터링

WH_GETMESSAGE: 메시지 큐로 보내는 메시지를 모니터링

WH_CALLWNDPROC: 윈도우에게 보내지는 메시지를 모니터링

두 번째 인자는 훅 프로시저의 주소가 전달되어야 하고 세 번째 인자는 훅 프로시저가 존재하는 dll 핸들을 지정하게 됩니다.

마지막 인자는 스레드 ID를 지정합니다.

샘플)

hKeyboardHook = SetWindowsHookEx(WH_KEYBOARD, KeyboardHookProc, NULL, GetCurrentThreadId());

3) 훅 프로시저를 훅 체인에서 해제

UnhookWindowsHookEx(HHOOK hhk)

Hhk는 SetWindowsHookEx가 반환하는 프로시저의 핸들을 지정하면 됩니다.

 

예제)

1. MFC 프로젝트 생성(SDI 옵션으로 CHookTest)

2. APP 클래스에 전역부에 함수와 변수 선언

HHOOK hKeyboardHook;

HHOOK hMouseHook;

LRESULT CALLBACK KeyboardHookProc(int nCode, WPARAM wParam, LPARAM lParam)

{

TCHAR s[256];

CMainFrame *pMain = (CMainFrame *)AfxGetMainWnd();

CCHookTestView *pView = (CCHookTestView *)pMain->GetActiveView();

CClientDC dc(pView);

if(nCode < 0)

{

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

}

wsprintf(s, TEXT(“Key Code = %3d.”), wParam);

dc.TextOut(50, 50, s, lstrlen(s));

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

}

 

LRESULT CALLBACK MouseHookProc(int nCode, WPARAM wParam, LPARAM lParam)

{

CMainFrame *pMain = (CMainFrame *)AfxGetMainWnd();

CCHookTestView *pView = (CCHookTestView *)pMain->GetActiveView();

CClientDC dc(pView);

TCHAR s[256];

if(nCode < 0)

{

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

}

wsprintf(s, TEXT(“Mouse의wParam = [%d]lParam = [%d]”),

wParam, lParam);

dc.TextOut(50, 100, s, lstrlen(s));

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

}

3. App 클래스의 InitInstance에 작성

hKeyboardHook = SetWindowsHookEx(WH_KEYBOARD, KeyboardHookProc,

NULL, GetCurrentThreadId());

hMouseHook = SetWindowsHookEx(WH_MOUSE, MouseHookProc,

NULL, GetCurrentThreadId());

 

3. 전역 훅

예제)

1. Win32 DLL 프로젝트 생성(GlobalHookProc) – 정적으로 생성

2. 3개의 함수 선언

#pragmadata_seg(“.ShareData”)

HHOOK hook = NULL;

HWNDhWnd = NULL;

#pragmadata_seg()

#pragmacomment(linker, “/section:.ShareData,rws”)

HINSTANCE hInstance = NULL;

#define WM_HOOKEDWM_USER+1

extern”C”__declspec(dllexport) LRESULT CALLBACK HookProc(int nCode, WPARAM wParam, LPARAM lParam)

{

if(nCode < 0)

{

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

}

if((lParam & 0x80000000) == 0x00000000)

{

hWnd = FindWindow(NULL, _T(“GlobalHookApp”));

if(hWnd == NULL)

{

MessageBox(NULL, _T(“GlobalHookApp 윈도우를찾을수없습니다.”), _T(“경고”), MB_OK);

}

PostMessage(hWnd, WM_HOOKED, wParam, lParam);

}

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

}

 

extern”C”__declspec(dllexport) void InstallHookProc(HWND hwnd)

{

hWnd = hwnd;

hook = SetWindowsHookEx(WH_KEYBOARD, HookProc, hInstance, 0);

if(hook == NULL)

{

MessageBox(NULL, _T(“Hook Procedure 를설치할수없습니다.”), _T(“오류”), MB_OK);

}

}

extern”C”__declspec(dllexport) void UnInstallHookProc()

{

UnhookWindowsHookEx(hook);

}

3. 프로젝트를 빌드하고 컴파일

 

4. 위의 DLL을 사용할 프로젝트 생성(대화상자 기반 – GlobalHookApp)

5. 대화상자 디자인

리스트 박스에 변수 연결 – Control Type: m_ListBox

후킹 시작 버튼에 변수 연결 – Control Type: m_Button

6. 다이얼로그 클래스에 변수 선언

HINSTANCE hDllInstance;

int m_ListBoxLoc;

BOOL IsHooked;

7. Dlg 클래스에 매크로 정의

#define WM_HOOKEDWM_USER+1

 

8. Dialog 클래스의 OnInitDialg에 작성

hDllInstance = LoadLibrary(_T(“GlobalHookProc.dll”));

if(hDllInstance == NULL)

{

AfxMessageBox(_T(“GlobalHookProc.dll 파일로드실패.”));

}

IsHooked = FALSE;

9. 다이얼로그 클래스에 훅 프로시저를 로드하기 위한 함수 작성

BOOL InstallHookProc(void)

{

void (*pFunc)(HWND);

if(hDllInstance == NULL)

{

AfxMessageBox(_T(“DLL 파일이로드되지않았습니다.”));

return FALSE;

}

pFunc = (void (*)(HWND))GetProcAddress(hDllInstance, “InstallHookProc”);

if(pFunc == NULL)

{

AfxMessageBox(_T(“InstallHookProc() 함수검색실패.”));

FreeLibrary(hDllInstance);

return FALSE;

}

(*pFunc)(this->GetSafeHwnd());

return TRUE;

}

 

10. 다이얼로그 클래스에 훅 프로시저를 해제하기 위한 함수 작성

BOOL UnInstallHookProc(void)

{

void (*pFunc)();

if(hDllInstance == NULL)

{

AfxMessageBox(_T(“DLL 파일이로드되지않았습니다.”));

return FALSE;

}

pFunc = (void (*)())GetProcAddress(hDllInstance, “UnInstallHookProc”);

if(pFunc == NULL)

{

AfxMessageBox(_T(“UnInstallHookProc() 함수검색실패.”));

FreeLibrary(hDllInstance);

return FALSE;

}

(*pFunc)();

return TRUE;

}

 

11. Dialog 클래스에 메시지 처리 함수 작성

LRESULT CGlobalHookAppDlg::HookedMessage(WPARAM wParam, LPARAM lParam)

{

TCHAR KeyName[30];

static CString Buffer;

GetKeyNameText(lParam, KeyName, 30);

if(lstrcmp(KeyName, _T(“Enter”)) == 0)

{

m_ListBox.AddString(Buffer);

Buffer.Empty();

}

else

{

Buffer.Append(KeyName);

}

return 0L;

}

12. Dialog 클래스의 BEGIN_MESSAGE_MAP에 메시지 연결

ON_MESSAGE(WM_HOOKED, HookedMessage)

 

13. 후킹 시작 버튼의 BN_CLICKED 메시지에 작성

if(IsHooked)

{

if(UnInstallHookProc())

{

m_Button.SetWindowText(_T(“후킹시작”));

IsHooked = FALSE;

}

}

else

{

if(InstallHookProc())

{

m_Button.SetWindowText(_T(“후킹중단”));

IsHooked = TRUE;

}

}

14. DLL 파일을 복사하고 실행