#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

 

-------------------------------------------------------------------------------------------------------------------------------

 

KeyHook.dll 파일의 소스코드(KeyHook.cpp) 분석

 

모르는 개념


프로시저란 ? -> 함수라고 생각하면된다.


모듈이란 ? (HMODULE)

인스턴스 핸들이란 ? (HINSTANCE ) 


HMODULE 과 HINSTANCE는 16비트 윈도우에서온 잔재이다.
우선 16비트 윈도우 환경에서  HMODULE 과 HINSTANCE 를 간략히 정의하면 다음과 같다.

1. HMODULE
   코드 영역에 대한 핸들

2. HINSTANCE
    데이터 영역에 대한 핸들

16비트 윈도우는 시스템 전체가 단일 메모리 공간을 사용하였다. 이때는 동일 프로세스가 여러번 실행될 경우 메모리 절약을 위해 코드영역은 한번만 로드하고 데이터영역은 각 프로세스별로 할당하였다. 각 프로세스별로 할당한 영역을 가리키는 핸들이 인스턴스핸들이다. 그래서 16비트 윈도우 환경에서는 인스턴스 핸들에 의해서 프로그램이 식별된다.

32비트 윈도우는 프로세스별로 메모리 공간이 독립적이기때문에 코드영역과 데이터영역 모두가 독립된 프로세스 메모리에 할당되므로 인스턴스 핸들과 모듈 핸들의 구분이 의미가 없어졌다. 그래서 32비트 윈도우에서는 모듈 핸들과 인스턴스 핸들이 같은 의미로 사용된다.
32비트 윈도우에서 인스턴스 핸들은 메모리 공간에 할당된 실행 모듈의 베이스주소(0x00400000)를 가리킨다.



모든 실행 파일과 DLL 파일은 프로세스의 메모리 공간 상에 로드될 때 고유의 인스턴스 핸들을 할당 받는다.

이 인스턴스 핸들은 WinMain의 첫번째 매개변수인 hInstance를 통해 전달된다.


많은 애플리케이션에서 WinMain의 hInstance 매개변수를 전역변수로 저장해 두고 사용한다.

아래와 같이 HMODULE형 인자를 요구하는 함수들이 있으며, HMODULE과 HINSTANCE는 완전 동일하다.

(16비트 윈도우에서 구분되었지만 지금은 혼용)

즉 함수마다 요구하는 값은 같지만 자료형을 다르게 해서 사용한다? 는 의미인듯

 

DWORD GetModuleFileName (HMODULE hInstModule, PTSTR pszPath, DWORD cchPath);

 

 

 

 

1. BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD dwReason, LPVOID lpvReserved)

- DLL이 로딩될때 가장 먼저 실행되는 함수

 

BOOL WINAPI DllMain(

HINSTANCE hInst,

DWORD fdwReason,

LPVOID lpRes

);

-> hlnst : DLL의 인스턴스 핸들

-> fdwReason : DLL 호출된 타이밍

-> lpRes : 암시적 호출시 TRUE, 명시적 호출시 FALSE

(함수가 어느 DLL에 있는지 밝히지 않고 사용 -> 암시적 

      어느 DLL에 있는 함수인지를 밝히고 사용 -> 명시적)


 

 

2. SetWindowsHookEx(WH_KEYBOARD, KeyboardProc, g_hInstance, 0);
- 훅을 설치할때 사용하는 함수

- 훅 프로시저를 설치한 후 HHOOK 타입의 훅 핸들 리턴

- 에러 발생시 NULL 리턴

 

HHOOK SetWindowsHookEx(

int idHook,

HOOKPROC lpfn,

HINSTANCE hMod,

DWORD dwThreadId

);

-> idHook : 훅 타입

-> lpfn : 훅 프로시저

-> hMod : 훅 프로시저가 속해 있는 DLL 핸들
-> dwThreadId : 훅을 걸고 싶은 스레드의 ID

 

 

3. BOOL UnhookWindowsHookEx( HHOOK hhk );

- 훅을 해제 하는 함수

- 해제하고자 하는 훅 핸들을 넘겨주면 됨.

- 훅을 설치한 프로그램은 종료되기 전에 반드시 훅 프로시저를 해제해 주어야 한다

 

 

4. LRESULT CALLBACK KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)

- 키보드 관련 메시지가 요청되었을때 어떻게 처리할 것 인지를 결정하는 함수

 

-> int nCode : 훅프로시저에서 이 메시지를 어떻게 처리할 것인가? (음수이면 메시지를 처리하지않고 다음 훅 에게 전달)

-> WPARAM wParam, LPARAM lParam : 전달된 메시지에 대한 추가정보(훅타입에 따라 달라지므로 레퍼런스 참고)

 

 

5. GetModuleFileNameA(NULL, szPath, MAX_PATH);
- 현재 실행되고 있는 프로세스의 이름을 얻어올때 사용하는 함수

 

DWORD WINAPI GetModuleFileName(

HMODULE hModule,

LPTSTR lpFilename,

DWORD nSize

);

-> hModule : 이름을 얻어올 프로세스의 핸들이지만, 보통 자신의 이름을 얻기위함으로 NULL로 사용

-> lpFilename : 해당 모듈의 이름이 입력될 버퍼
-> nSize : 입력한 크기 만큼 저장(오버플로우 방지)


 

6. strrchr(szPath, '\\');

- 스트링에서 문자열의 마지막 표시를 찾는 함수

 

char *strrchr(const char *str, int c)


ex)

char str[10] = "abcdefghi";

char * p = NULL;

p = strrchr(str, 'd');

printf(p);

-> "defghi"

 

 

7. CallNextHookEx(g_hHook, nCode, wParam, lParam);
- 메시지를 훅 체인의 다음 훅 프로시저에게 전달할때 사용하는 함수

- 훅 프로시저가 설치되면 해당 타입의 메시지는 목표 윈도우로 보내지기 전에

  훅 프로시저에게 먼저 전달되는데 훅 프로시저는 메시지를 살펴본 후 특별한 이유가 없으면 

  메시지를 훅 체인의 다음 훅 프로시저에게 전달해 주어야 한다.

 

- 훅 프로시저는 전달 받은 메시지를 다음 훅 프로시저에게 꼭 전달해 주어야 할 의무는 없으며 

  메시지를 아예 없애버리려면 전달하지 않아도 상관없으며 원하는 대로 변경할 수도 있다. 

  물론 이 때는 메시지를 없애버리거나 변경한 후의 효과에 대해 확실히 책임질 수 있어야 한다. 

  이 함수는 훅 체인에서 다음 훅 프로시저를 호출하고 훅 프로시저가 리턴하는 값을 다시 리턴해 주는데 

  현재의 훅 프로시저는 이 리턴값을 또 그대로 리턴해 주어야 한다. 

  그래서 훅 프로시저의 끝은 보통 return CallNextHookEx(...) 호출문이 온다

 

LRESULT CallNextHookEx(

HHOOK hhk,

int nCode,

WPARAM wParam,

LPARAM lParam

);

-> hhk는 현재 처리하고 있는 훅의 핸들인데 SetWindowsHookEx 함수가 리턴한 값이다. 

 

나머지 세 인수는 운영체제가 훅 프로시저에게 전달해준 인수들이다. 

훅 체인에 포함된 훅 프로시저의 목록은 운영체제가 직접 관리하기 때문에 훅을 설치한 응용 프로그램은 다음 훅 프로시저의번지를 따로 저장할 필요없이 이 함수만 호출해 주면 훅 체인을 따라 모든 훅 프로시저가 순서로 호출되며 최종적으로 목표 윈도우로 메시지가 전달 될 것이다.

 

 

8.

__declspec(dllimport) : DLL로부터 제공받을 함수를 선언할때 사용

__declspec(dllexport) : 외부에 export할 함수를 선언할때 사용

 

 

 

 

 

+ Recent posts