출처 : http://blog.naver.com/seamusic00/130009898000

Hooking 을 사용하는 프로그램 내의 구현

int FAR PASCAL InitHooksDll(HWND hMain);
int FAR PASCAL InstallHook();
int FAR PASCAL UnInstallHook();

ON_MESSAGE(WM_KEY_HOOK, OnSetFocus)   //사용자가 지정한 함수 Call

// Hook 사용하기 전  

 static HINSTANCE hinstDLL;
 typedef int (WINAPI *inithook)(HWND command);
 inithook initkbhook;
 hinstDLL = LoadLibrary((LPCTSTR) "Hook.dll");
 initkbhook = (inithook)GetProcAddress(hinstDLL, "InitHooksDll");
 initkbhook(this->m_hWnd);             //Dll Load
 typedef BOOL (CALLBACK *inshook)();
 inshook instkbhook;
 instkbhook = (inshook)GetProcAddress(hinstDLL, "InstallHook");
 instkbhook();                                //Hook Start

 //Hook 사용한 후  (프로그램 종료시)

 static HINSTANCE hinstDLL;
 typedef int (WINAPI *UnInhook)();
 UnInhook UnInkbhook;
 hinstDLL = LoadLibrary((LPCTSTR) "Hook.dll");
 UnInkbhook = (UnInhook)GetProcAddress(hinstDLL, "UnInstallHook");
 UnInkbhook();                                //Hook End

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

 실제 Hook dll

//공유 테이블
#pragma data_seg("SHARDATA")
static HWND hwndMain = NULL;
static HINSTANCE hInstance;
#pragma data_seg()

int FAR PASCAL InitHooksDll(HWND hMain);
//Hook의 init

int FAR PASCAL InstallHook();
//Hook Start

int FAR PASCAL UnInstallHook();
// Hook 해제

LRESULT CALLBACK KeyboardFunc(int nCode,WPARAM wParam,LPARAM lParam);
// 실제 후킹 담당

int CALLBACK InitHooksDll(HWND hMain)
{
      hwndMain = hMain;
      hInstance = theApp.m_hInstance;
      return 1;
}

int CALLBACK InstallHook()
{
      hHook = SetWindowsHookEx(WH_KEYBOARD,(HOOKPROC)KeyboardFunc,hInstance,0);
      //키가 눌리는 것을 후킹
      return 1;
}

  
int CALLBACK UnInstallHook()
{
      UnhookWindowsHookEx(hHook);
      return 1;
}

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

// 실제 하는 작업 부분 (아래는 키보드 Hooking 이다)

LRESULT CALLBACK KeyboardFunc(int nCode,WPARAM wParam,LPARAM lParam)
{
      CString message;
      if(nCode >= 0)
      {
            if(wParam != VK_PROCESSKEY)

           {
                  char szTemp[100];
                  sprintf(szTemp, "wParam=%X, lParam=%X", wParam, lParam);
            }
 
           if(wParam == VK_HANJA)   //한자키를 누르면 Event를 발생시킨다.
           {  
                  HWND ProcB = FindWindow( NULL, "Program Name");
                  UINT uMsg;
                  uMsg = WM_KEY_HOOK;
                  if( ProcB )
                  { 
                        ::PostMessage(ProcB, uMsg, NULL ,NULL);
                  }
           }
     }
      return (CallNextHookEx(hHook,nCode,wParam,lParam));
}


신라호텔 프로그램 작업할 때 했던 Hooking

정보를 찾고 구현을하고 Test를 하면서 무척 재미있었다.. ㅋㅋ

이제서야 정리를 하네... >.<

'C/C++언어 > 후킹' 카테고리의 다른 글

Anti Game hacking 프로그램의 구현.  (0) 2008.01.17
Hooking 을 사용하는 프로그램 내의 구현  (0) 2007.11.11
Global Hooking in Win32  (0) 2007.11.11
[본문스크랩] 메세지 후킹  (0) 2007.09.06
[본문스크랩] Knowledge Base API  (0) 2007.09.06
API Hooking Revealed  (0) 2007.09.06

출처: http://www.javalinux.co.kr/study/windows/

1. Non-MFC DLL

1.1 Basis

1.1.1 Features

n         내부적으로 C++ 클래스를 사용할 수 있고, C 함수 Wrapper만을 Export 할 수 있다. 따라서 내부적인 C++ Class에 대한 변경은 DLL의 호출에 영향을 주지 않는다.

n         MFC를 사용할 수 없으며, 별도의 MFC Library가 필요없다.

n         DLL을 사용하는 Client는 DLL 호출을 지원하는 어떠한 Language로 작성될 수 있다.

n         AppWizard를 이용하여 자동으로 Project를 생성할 수 있다.

1.1.2 Function Export

DLL 내에서 정의된 Function을 export하기 위해서는 __declspec(dllexport) 를 사용한다. __declspec MS만의 C, C++의 확장된 syntax로서, 확장된 storage-class 정보를 정의한다. dllexport storage-class의 한 속성으로, DLL의 Function, Data, Object를 export할 수 있도록 하여준다. 반대로 DLL내의 Function을 import하기 위해서는 dllimport 속성을 사용한다. Win32 환경에서는 Function Export/Import를 위하여 이것을 이용하며, Win32 이전의 Windows 환경에서 사용되던 module-definition file (.DEF)을 사용하지 않는다. 단, VB와 호환가능한 DLL을 제작하는 경우, module-definition file을 사용하도록 한다.

· export / import

함수 export/import를 위하여 아래와 같이 함수를 선언한다. Coding의 편의를 위하여 export선언을 #define으로 간략화시킨다.

#define DLLImport  __declspec(dllimport)

#define DLLExport  __declspec(dllexport)

DLLExport void somefunc();

· export/import Tips

위 방법으로 export/import 함수를 정의하면, DLL 내에서의 함수 정의와 DLL을 사용하는 Client에서의 함수정의를 다르게 해야 하는 불편이 생긴다. DLL과 Client에서 동일한 Header File을 사용할 수 있도록 하기 위하여 아래와 같이 export/import 함수를 정의한다.

#ifdef DLLTEST_EXPORTS

#define DLLFunction  __declspec(dllexport)

#elseif

#define DLLFunction  __declspec(dllimport)

#endif

DLLFunction void somefunc();

DLLTEST3_EXPORTS 은 DLL의 Project Settings에 Preprocessor definitions에 프로젝트명_EXPORTS의 형식으로 정의 되어 있다. 따라서 DLL Project에서는 export로, Client Project에서는 import로 동작한다.

· Adjusting Naming Convention

C++은 C와 다른 Naming Convention을 사용한다. 따라서 export되는 함수명은 Compile시에 기존의 정의한 이름과 다르게 해석된다. 따라서 Naming Convention에 대한 조정과정이 없으면, export된 함수는 C++ 이외의 Language로 작성되는 프로그램에서는 호출될 수 없다. extern C는 함수가 C naming convention을 사용하도록 만들어주며, 이를 통하여 C++로 작성되어 export되는 함수를 다른 Language에서도 사용가능하도록 하여 준다. VC는 기본적으로 프로젝트생성시에 C++을 사용하도록 구성되므로 모든 export함수는 Naming Convention의 조정이 필요하다.

#ifdef _USRDLL

#define DLLFunction  __declspec(dllexport)

#elseif

#define DLLFunction  __declspec(dllimport)

#endif

#ifdef __cplusplus

extern C {

#endif

DLLFunction void somefunc();

#ifdef __cplusplus

}

#endif

· Adjusting Calling Convention

Visual Basic은 C와 다른 Calling Convention을 사용하므로 Visual Basic에서 사용될 DLL을 작성하는 경우, 이를 조정해주어야 한다. VC는 기본적으로 __cdecl 방식을 사용하며, VB는 __stdcall 방식을 사용한다. 따라서 Project Settings에서 C/C++ Tab의 Code Generation Category를 선택하여 Calling Convention을 __stdcall로 설정한다. 또한 module-definition file(.Def)을 작성하여 프로젝트에 포함시킨다. VC로 작성한 DLL을 VB에서 사용하는 경우, Calling Convention이 틀린다는 에러가 발생하거나 Run 시에 다운되는 현상을 자주 볼 수 있는데, 이는 모두 Calling Convention을 조정과정을 거치지 않아서 발생하는 문제이다.

testdll.def

LIBRARY              "TESTDLL.DLL"

DESCRIPTION 'SIMPLEDLL Windows Dynamic Link Library'

EXPORTS

   somefunc   @1

1.2 Making DLL

1.2.1 Object

간단한 DLL을 만들기 위하여 아래와 같은 두가지의 기능만을 가지는 DLL을 만들기로 한다.

n         int 형의 두 숫자를 parameter로 받아 그 합을 return하는 함수

n         두개의 string을 받아 연결된 string을 넘겨주고, 그 총 길이를 return하는 함수

1.2.2 Implementation

n       아래와 같이 Win32 Dynamic-Link Library를 선택하여 Project를 생성한다.

n       App-Wizard에서 아래와 같이 A simple DLL Project를 선택한다. 이는 간단한 DLLMain 함수를 자동으로 생성하여 준다.



n       export할 함수의 정의를 위하여 Header File을 생성한다. 이는 후에 import 측에서도 공용으로 사용될 것이다. SimpleDll.h의 이름으로 Header를 생성하고, 두개의 함수를 위한 함수정의를 만든다.

SimpleDll.h

#ifndef __SIMPLEDLL_H__

#define __SIMPLEDLL_H__

 

#ifdef SIMPLEDLL_EXPORTS

   #define DLLFunction  __declspec(dllexport)

#else

   #define DLLFunction  __declspec(dllimport)

#endif

 

extern "C" {

 

DLLFunction int addint(int n1, int n2);

DLLFunction int addchar(char* s1, char* s2, char* added);

 

}

 

#endif //__SIMPLEDLL_H__

 

n       선언한 함수에 대하여 기능을 작성한다. 두 함수는 단지 int형과 char형의 더하기 만을 지원하므로 아래와 같이 함수를 작성한다.

SimpleDll.cpp

#include "stdafx.h"

#include "stdio.h"

#include "SimpleDll.h"

 

BOOL APIENTRY DllMain( HANDLE hModule,

                       DWORD  ul_reason_for_call,

                       LPVOID lpReserved

                                       )

{

    return TRUE;

}

 

DLLFunction int addint(int n1, int n2)

{

   return n1 + n2;

}

 

DLLFunction int addchar(char* s1, char* s2, char* added)

{

   sprintf(added, "%s%s", s1, s2);

   return strlen(added);

}

 

1.3 Using DLL

위에서 작성한 Code를 Compile하여 SimpleDll.dll 파일을 얻는다. 이것을 Test하기 위하여 VC++ 및 VB에서 DLL내의 함수를 호출하는 기능을 작성한다.

 

1.3.1 Using DLL with VC++ (Link Implicitly)

Dialog-Based Project를 생성하여 아래와 같은 순서로 DLL의 함수를 호출하는 기능을 작성한다.

 

n       SimpleDll.h를 복사하여 프로젝트에 추가한다.

n       Project-Settings 메뉴를 선택하여 Project Settings 화면을 열어, Link Tab의 library modules에 SimpleDll.lib를 추가한다. SimpleDll.lib 파일은 VC++의 환경설정의 Directories에 존재하는 폴더 또는 Project 폴더에 복사하도록 한다.

 

n       Dialog를 다음과 같이 Design하고 각 컨트롤에 멤버변수를 추가한다.

 

 

 

 

 

 

n       DLL 함수를 호출할 Button에 대한 Handler를 작성한다.

SimpleDllTestDlg.cpp

#include "SimpleDll.h"

 

void CSimpleDllTestDlg::OnAddint()

{

   UpdateData(TRUE);

   m_nSumInt = addint(m_nInt1, m_nInt2);

   UpdateData(FALSE);

}

 

void CSimpleDllTestDlg::OnAddchar()

{

   char s1[9];

   char s2[9];

   char sSum[17];

 

   UpdateData(TRUE);

 

   lstrcpy(s1, (LPCTSTR)m_sChar1);

   lstrcpy(s2, (LPCTSTR)m_sChar2);

 

   addchar(s1, s2, sSum);

 

   m_sSumChar = sSum;

   UpdateData(FALSE);

}

 

위의 방법으로 DLL 함수를 호출하는 프로그램을 작성하여 실행하면 각 함수들이 정확하게 호출되고 있음을 확인할 수 있다.

 

 

1.3.2 Using DLL with VC++ (Link Explicitly)

Explicit Link를 사용하여 DLL 함수를 호출하는 경우, Header 파일과 Library 파일은 필요하지 않다. 단지 그 함수의 원형만을 알고 있으면 된다. Explicit Link를 사용하기 위하여 아래와 같은 순서로 DLL 함수를 호출하는 기능을 작성한다.

 

n       1.3.1에서와 같이 Dialog-Based Project를 생성한 후, 같은 모양으로 Dialog를 Design하고 멤버변수를 연결한다.

 

n       DLL 함수호출을 위한 Button의 Handler를 만들고 아래와 같이 코드를 작성한다.

SimpleDllTest2Dlg.cpp

void CSimpleDllTest2Dlg::OnAddint()

{

   int (*lpfnAddInt)(int, int);

   HINSTANCE hLib = LoadLibrary("SimpleDll.dll");

   if(hLib == NULL)

            return;

 

   lpfnAddInt = (int(*)(int, int))GetProcAddress(hLib, "addint");

   if(lpfnAddInt == NULL)

   {

            FreeLibrary(hLib);

            return;

   }

 

   UpdateData(TRUE);

   m_nSumInt = lpfnAddInt(m_nInt1, m_nInt2);

   UpdateData(FALSE);

 

   FreeLibrary(hLib);

}

 

위와 같이 LoadLibrary를 이용하여 Explicit Link로 DLL 함수를 호출하도록 하면, 별도의 Library와 Header가 필요하지 않으며, 실행 시간에 DLL의 Load와 Unload의 시점을 결정할 수 있는 장점이 있다.

 

 

1.3.3 Using DLL with VB

VB에서는 DLL의 함수 호출이 매우 간단하다. 단지 어떤 Library의 어떤 함수를 사용할 것인지에 대한 선언만 정확이 명시하면 된다. 아래와 같이 addintaddchar 함수를 선언한다.

test.mod

Public Declare Function addint

Lib "SimpleDll.dll" (ByVal n1 As Long, ByVal n2 As Long) As Long

Public Declare Function addchar

Lib "SimpleDll.dll" (ByVal s1 As String, ByVal s2 As String, ByVal sum As String) As Long

 

함수선언 후, 실행을 위한 Handler에서 아래와 같이 호출한다.

Private Sub cmdAddStr_Click()

    Dim sum As String * 32

    addchar txtInt1, txtInt2, sum

    txtSum = sum

End Sub

 

Private Sub cmdCall_Click()

    Dim n1, n2, sum As Long

    n1 = CLng(txtInt1)

    n2 = CLng(txtInt2)

   

    sum = addint(n1, n2)

    txtSum = CStr(sum)

End Sub

VB로 작업시 char* Type에 대한 Parameter를 넘길 때, 반드시 그 크기를 지정하여야 한다. VB는 그 값이 지정될 때, 메모리가 할당되므로 함수 호출 이전에 값을 지정하거나 위 코드의 sum 변수에서처럼 그 크기를 미리 지정하여야 한다.

www.codein.co.kr  프로그래머의 놀이터 Code人

 Global Hooking in Win32
 ====================================================

 후크에는 두가지가 있다.
   Local Hook   - 하나의 스레드나 프로세스 안에서의 후킹
   Global Hook  - 전역 모든 윈도우들에 대한 후킹

 후크를 하기 위해서는 기본적으로 두가지 자료형을 알아야 한다.
   HHOOK - 후크 핸들  윈도우 시스템에서 한번 이벤트가
           발생하면 후크 체인의 첫 후크핸들에게 이벤트를
           넘긴다. 각 후크들은 다음 후크를 호출하여 후크체인에
           있는 모든 후크 프로시져를 호출하게 된다.
   HOOKPROC - 후크 프로시져로 후크시 호출되는 프로시져이다.
    LRESULT CALLBACK fnHookProc(int nCode, WPARAM wParam, LPARAM lParam)
    라는 자료형을 갖는다.

 1. 로컬 후킹
   SetWindowHookEx를 이용하면 간단히 구현이 가능하다.
   각 후크는 후크 타입이 있는데 아래와 같다.
      WH_CALLWNDPROC - 윈도우 메세지가 목적지로 전달되기 전에
                       메세지를 후크할때 쓴다 (SendMessage)
                       CallWndProc라는 후크프로시져명의 도움말 참조한다.
      WH_CALLWNDPROCRET - 윈도우 메세지가 목적지에 전달되어 처리
                          된 후 후크가 일어난다
                          CallWndRetProc함수명 도움말 참조
      WH_CBT - computer-based training (CBT) application 에 유용한
               후크 타입 CBTProc 함수 참조
      WH_DEBUG - 디버깅에 유용한 후크 DebugProc 참조
      WH_FOREGROUNDIDLE - Foreground상태있는 윈도우가 idle상태로 들어갈
                          때 생기는 후크 이는 idle시 낮은 우선순위
                          (low priority)를 줄때 유용하다 ForegroundIdleProc
      WH_GETMESSAGE - 메세지가 Post된 후 후크됨 (PostMessage)
                      GetMsgProc 함수 참조
      WH_JOURNALPLAYBACK - WH_JOURNALRECORD에 의해서 Record되기 전에
                           일어나는 후크 JournalPlaybackProc
      WH_JOURNALRECORD - Input message가 시스템 메세지 큐로 들어가는것을
                         Record하는 후크 JournalRecordProc
      WH_KEYBOARD - 등등이 있다.... -_- 도움말 참조..

 
 --------------------- 로컬 후크 예제 ------------------------------
    HHOOK hHook;
    HOOKPROC hProc;
              :
    hProc = CallWndProc;            // CallWndProc 후크 프로시져로 연결
    hHook = ::SetWindowsHookEx(     // 후크를 설치한다. ( 후크체인에 끼워넣는다 )
                 WH_CALLWNDPROC,    // WH_CALLWNDPROC 후크 설치
                 hProc,             // 후크차례가 오면 분기되는 콜백후크 프로시져
                 (HINSTANCE) NULL,  // 전역 후크가 아닌 로컬 후크임을 말한다
                 dwTreadID);        // 특정 스레드를 정한다 0 이면 현재 스레드


    만일 여러 스레드중 한 HWND가 속한 스레드를 얻고 싶으면
    DWORD    dwProcessID = NULL;
    DWORD    dwTreadID = ::GetWindowThreadProcessId( hWnd, &dwProcessID );

    if( dwProcessID )
    {
                 :
         후크 설치코드 맨 마지막인자에 dwThreadID를 넣으면 된다.    
    }

// 자세한 프로시져 도움말을 보면 자세히 알수 있다.
LRESULT WINAPI CallWndProc(int nCode, WPARAM wParam, LPARAM lParam)
{
    CWPSTRUCT* lpWp = (CWPSTRUCT*)lParam;
     //PMSG lpMsg = (PMSG)lParam;    
    if (nCode < 0 && hWnd == lpWp->hwnd )  // do not process message
        return CallNextHookEx(m_stHookCallProc.hHook, nCode, wParam, lParam);

    switch(  lpWp->message  )
    {
         case EM_REPLACESEL :
         TRACE("CallWndProc EM_REPLACESEL %s\r\n ", (char*)lpWp->lParam );
         break;

         default : 
         break;
    }   
    return CallNextHookEx(m_stHookCallProc.hHook, nCode,
        wParam, lParam);
}
 
2 전역 후킹
  전역 후킹을 하기 위해서는 후크 프로시져를 dll안에 넣어야 한다.
 


 --------------------- 전역 후크 예제 --------------------------

  testdll.dll 에서 ............

// dll에서 쓰는 자료는 dll공유를 했다.
// 이 후크 프로시져 dll은 내 프로그램에서 쓰이기도 하지만
// 시스템에 의해서 이 dll이 또 열리게 된다. ( dll의 참조카운트 증가
//  가 일어나지 않고 새로 dll이 생긴다. 따라서 두 dll은 자료가
//  분리되어 있는 셈이다. )
// 그러므로 부득이 하게 자료를 공유자료로 해야 한다.
#pragma data_seg(".shared")
    HHOOK             _hHook = NULL;
    HWND            _hTarget = NULL;
#pragma data_seg()
// 공유 자료로 했을 경우 아래처럼 링커 옵션도 주어야 한다.
#pragma comment(linker, "/SECTION:.shared,RWS")

// 자료 억세스 함수 마련...
extern "C" __declspec(dllexport) void fnSetHook( HHOOK hHook )
{
    _hHook = hHook;
}

extern "C" __declspec(dllexport) void fnSetHWND( HWND hWnd )
{
    _hTarget = hWnd;
    char szBuf[20];
    wsprintf( szBuf, "%lu", (ULONG)_hTarget );
    MessageBox( NULL, szBuf, "fnSetHWND당시의 _hTarget값", MB_OK);
}

extern "C" __declspec(dllexport) HHOOK fnGetHook()
{
    return _hHook;
}

// 콜백을 위한 CALLBACK 콜링컨벤션 키워드를 넣게 되면 나중에 GetAddressProc시
// NULL값을 리턴하므로 그냥 콜링컨벤션을 무시했다
//extern "C" __declspec(dllexport) LRESULT CALLBACK fnCallWndProc(int nCode, WPARAM wParam, LPARAM
lParam)
extern "C" __declspec(dllexport) LRESULT fnCallWndProc(int nCode, WPARAM wParam, LPARAM lParam)
{
    CWPSTRUCT* lpWp = (CWPSTRUCT*)lParam;
     
    if( nCode >= 0  ) //&& _hTarget == lpWp->hwnd ) // do not process message
    { 
        switch(  lpWp->message  )
        {
        case EM_REPLACESEL :
            if( _hTarget == lpWp->hwnd )
            {
                MessageBox( NULL, "EM_REPLACESEL 메세지 발쌩", "메세지 발쌩in
dll", MB_OK);
            }
            break;

        default : 
            break;
        }   
    }
    return CallNextHookEx( _hHook, nCode, wParam, lParam);
}


 내 프로그램에서 .........................
HINSTANCE    hInstDll = NULL
HHOOK        hHook = NULL;

// dll을 연다.
hInstDll = LoadLibrary("TestDll.dll");
if( hInstDll )
{
    // 후크 프로시져를 찾아낸다.
    LRESULT (*hHookDllProc)(int, WPARAM, LPARAM ) = (LRESULT (*)(int, WPARAM, LPARAM ))
GetProcAddress(hInstDll, "fnCallWndProc");
    if( hHookDllProc ) // 있으면 후크를 설치한다.
        hHook = SetWindowsHookEx( WH_CALLWNDPROC, (HOOKPROC)hHookDllProc, hInstDll, 0);
   
        // dll내의 자료를 세팅한다. HHOOK와 HWND 값 세팅
    void (*lpfnSetHook)(HHOOK) = (void (*)(HHOOK))GetProcAddress(hInstDll, "fnSetHook");
    if( lpfnSetHook )
        (*lpfnSetHook)( hHook );
        void (*lpfnSetHWND)(HWND) = (void (*)(HWND))GetProcAddress
(hInstDll, "fnSetHWND");
    if( lpfnSetHWND )
        (*lpfnSetHWND)( m_hTarget );
}



출처 : http://blog.naver.com/heroyik/14775896

'C/C++언어 > 후킹' 카테고리의 다른 글

Anti Game hacking 프로그램의 구현.  (0) 2008.01.17
Hooking 을 사용하는 프로그램 내의 구현  (0) 2007.11.11
Global Hooking in Win32  (0) 2007.11.11
[본문스크랩] 메세지 후킹  (0) 2007.09.06
[본문스크랩] Knowledge Base API  (0) 2007.09.06
API Hooking Revealed  (0) 2007.09.06

따라해보는 후킹

작성자 : 이은규

작성일 : 2003.11.02

홈페이지 : http://unkyulee.net

 

목차

1. 들어가는 글

2. 후킹이란?

3. 후킹 프로시져를 만들어 보자.

4. 후킹 프로시져를 시작, 종료하는 함수

5. 프로시져 내에서 다른 윈도우로 데이터 전송하기

6. 간단한 샘플 프로그램

 


1. 들어가는 글

 

 "그냥 실행되는 걸 보고 싶었다."

 

 예전에 했던 프로젝트의 내용 중에 사용자가 키보드로 입력하는 내용을 얻어와서 처리해야 되는 부분이 있었다. 이러한 기능을 구현하기 위해서는 후킹 이라는 기술을 사용해야 한다. 그래서 관련된 내용을 인터넷에서 찾아봤는데, 왜 이렇게 알아야 되는 내용이 많은지… 또 내용들은 어찌나 어렵던지… 후킹 구현한답시고 한달 내내 문서 읽고 인터넷 뒤지고 엄청 고생 했었다. 결국 간단한 샘플 코드를 구해서 원하는 기능을 구현 했던 기억이 있다.

 

 프로젝트 내에서 그리 중요한 부분도 아니였고, 그냥 호기심에 후킹이라는 걸 실제로 구현해보고 싶었던 것 뿐이였는데, 정말 어렵게 공부했던 것 같다.

 

 이 강좌는 순전히 호기심으로 혹은 그냥(?) 한번 후킹을 실제로 구현해 보고자 하는 사람들을 대상으로 한다. 후킹에 대한 자세한 이론은 대부분 생략할 예정이다. 대신에 이 강좌를 다 읽고 나면 후킹을 사용한, 일단 돌아가는 코드를 작성 할 수 있을 것이다.

 

 일단 돌아가는 코드를 작성하고 나면, 그 외의 내용을 익히는 것은 시간 문제일 뿐이지 않을까 생각한다. ^0^)

 


2. 후킹이란?

 

 "후킹은 도청하는 걸 예로 들면 쉽게 이해할 수 있다."

 

 후킹이란 다른 프로세스에 걸려서(Hooked) 해당 프로세스의 정보를 얻어 오거나, 변경하는 것이 가능한 기술이다. 여기서 프로세스란 좁게 봐서 "윈도우 프로시져" 혹은 "윈도우 메시지" 라고 생각하면 되겠다.

 

 예를 들어 보자. 한 아파트 건물이 있다고 하자. 그리고 이 건물을 도청하고 싶다. 그럼 전화선들이 지나가는 곳(Window Process) 에 도청 장치(후킹 프로시져)를 설치한다. 그럼 도청장치 사이로 전화 내용들이(Window Message) 지나간다. 도청 장치는 그 중 필요한 메시지를 저장하고 있거나, 필요한 곳으로 전송한다.

 

 위의 과정들에 의해서 빌딩은 도청을 당한다. 그럼 실제 후킹의 경우를 살펴보자. 일단 윈도우 메시지(전화 내용)를 후킹 한다고 하면 윈도우 메시지를 발생시키는 곳(전화선들이 지나가는 곳)에 후킹 프로시져(도청 장치)를 설치한다. 그럼 해당 윈도우에서 발생하는 메시지가 후킹 프로시져를 거쳐가게 된다. 그럼 후킹 프로시져는 메시지들을 보고 필요한 내용을 저장하던지 필요한 곳에 전달을 하면 되는 것이다.

 

 그림 한번 보자. 위 그림은 Ivo Ivanov 라는 사람이 쓴 API Hooking Reveal 라는 문서에서 퍼 가지고 왔다. 그림을 보면 Hook Driver 라는 것들이 3개가 보인다. 그림에서 현재 윈도우가 3 개가 떠있는데 각각의 윈도우에 하나씩 Hook Driver 가 붙어 있는 모양이다. 따라서 이 드라이버(후킹 프로시져) 들이 각각의 윈도우에서 오는 메시지들을 받을 수가 있는 것이다.

 

 뭔가 굉장히 많아 보인다. 하지만 결국 개발자가 만들어 주는 것은 후킹 프로시져 하나이다.

 

 여기서 키포인트는 특정 위도우가 받는 메시지를 후킹 프로시져도 받을 수 있다는 것이다. 이런 기술을 바로 후킹이라고 한다.

 

 


3. 후킹 프로시져를 만들어 보자.

 

 "이론은 끝났다. 이제 만들어 보자."

 

 후킹 프로시져… 아까부터 프로시져라는 단어가 계속 나오는데 결국 프로시져는 함수와 그 의미가 비슷하다. 따라서 후킹 프로시져라고 함은 후킹을 하는 함수라고 생각하자. 따라서 후킹 프로시져를 만든다는 것은 함수를 하나 만든다고 생각하면 된다.

 

 후킹 함수를 만들기 위해서는 지켜야 하는 규칙들이 있다.

1. Call Back 함수이여야 한다.

2. 함수의 마지막 부분에서는 CallNextHookEx() 함수를 호출한다.

3. 함수가 받는 인자는 정해져 있다.

4. 후킹 프로시져는 DLL 안에 있어야 한다.

 

Ex)

//--------------------------------------------------------------

// Hook Procedure - Keyboard

//--------------------------------------------------------------

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

{

             if( nCode >= 0 )

             {

            

             }

             // We must pass the all messages on to CallNextHookEx.

           return ::CallNextHookEx( g_Hook , nCode , wParam , lParam );

}

 

 위의 규칙들을 지켜서 만든 후킹 프로시져 함수이다.

 

 4번째 규칙에 의하면 후킹 함수는 DLL 내부에 있어야 한다. Visual C++ 6.0 에서 DLL 프로젝트를 하나 만들어 보자.

 

Step 1. Win32 Dynamic-Link Library 프로젝트 생성

Step 2. Simple 프로젝트 선택 후 Finish

Step 3. cpp 파일에 위의 후킹 함수를 만들어 준다.

 

위의 프로젝트를 컴파일 하면 HookDll.dll 파일이 생성된다.

 

전체 프로젝트 파일 내용

// HookDll.cpp : Defines the entry point for the DLL application.

//

 

#include "stdafx.h"

 

BOOL APIENTRY DllMain( HANDLE hModule,

                       DWORD  ul_reason_for_call,

                       LPVOID lpReserved

                                                                   )

{

    return TRUE;

}

 

//--------------------------------------------------------------

// Hook Procedure - Keyboard

//--------------------------------------------------------------

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

{

             if( nCode >= 0 )

             {

                          

             }

             // We must pass the all messages on to CallNextHookEx.

             return ::CallNextHookEx( g_Hook , nCode , wParam , lParam );

}

 

 

 


4. 후킹 프로시져를 시작, 종료하는 함수

 

 이번 단계에서는 앞서 만든 후킹 프로시져를 설치하고, 제거하는 함수를 만들어 보겠다. 후킹 프로시져는 특정 윈도우에 설치가 되어야 제 역할을 할 수 있게 된다. 이때 후킹 프로시져를 설치해주는 함수가 바로 SetWindowsHookEx() 이다. 그 다음에 설치된 후킹 프로시져를 제거하기 위해서는 UnhookWindowsHookEx() 가 쓰이게 된다. 각각의 사용법을 알아보고 앞에서 만든 프로젝트에 이어서 적용시켜 보자.

 

SetWindowsHookEx()의 사용법

HHOOK SetWindowsHookEx(

    int idHook,

    HOOKPROC lpfn,

    HINSTANCE hMod,

    DWORD dwThreadId

);

 

 첫번째 패러메터는 후킹 필터를 설정한다. 후킹 프로시져가 받은 메시지의 종류를 설정하는 항목이다. 예를 들어 WH_GETMESSAGE로 설정하면 모든 메시지를 받게 되고, WH_KEYBOARD로 설정하게 되면 키보드 관련 메시지만 전달 받게 된다. 더 자세한 내용은 MSDN 을 참고하기 바란다.

 

 두번재 패러메터는 후킹 프로시져의 포인터를 지정해야 한다. 간단하게 후킹 프로시져 함수 이름 써주면 된다.

 

 세번째 패러메터는 DLL 의 핸들을 넘겨줘야 된다. 이 값은 앞에서 만든 프로젝트에서 DLlMain() 함수를 보면 HANDLE hModule 값이 넘어오는데 이걸 저장 해놨다가 넘겨주면 된다.

 

 네번째 패러메터는 후킹 프로시져를 설치할 윈도우 값을 넘겨준다. 이번 예에서는 0 을 넘겨준다. 0 을 넘겨주면 후킹 프로시져가 모든 윈도우에 설치가 된다.

 

 이 함수가 리턴하는 값을 잘 저장 해 놓자. 나중에 후킹을 해제할 때 필요하게 된다.

 

Ex) g_HookKeyboard = SetWindowsHookEx( WH_KEYBOARD , KeyboardProcedure , (HINSTANCE)g_Module , 0 ) ;

 

UnhookWindowsHookEx() 의 사용법

 

BOOL UnhookWindowsHookEx(         

             HHOOK hhk

);

 

 이 함수는 후킹 핸들을 받아서 해당 후킹 프로시져를 해제한다. 이때 받는 핸들은 SetWindowsHookEx() 함수가 리턴한 값을 넣어주면 된다.

 

 

프로젝트를 계속 진행 해보자.

 

1. 필요한 전역 변수를 만들어 준다.

//---------------------------------------------------

// Global Variables

// 공용 메모리

//---------------------------------------------------

#pragma data_seg(".HKT")

HINSTANCE g_Module = NULL ;     // DLL Handle

HHOOK g_Hook = NULL ;  // Hook Handle

HWND g_HwndServer = NULL ;      // Hook Server Window Handle

#pragma data_seg()

 

 

2. SetHook, Remove 이라는 함수를 만든다.

//------------------------------------------------------------------// Set Hook

//------------------------------------------------------------------BOOL     SetHook( HWND hWnd )

{

             g_HwndServer = hWnd ;                // Set Hook Server

             g_Hook = SetWindowsHookEx( WH_KEYBOARD , KeyboardProcedure , (HINSTANCE)g_Module , 0 ) ;

             return false ;

}

 

//------------------------------------------------------------------// Remove Hook

//------------------------------------------------------------------BOOL     RemoveHook()

{

             UnhookWindowsHookEx( g_Hook ) ;

             return false;

}

 

 

// HookDll.def 파일 부분

LIBRARY   HookDll

 

SECTIONS

             .HKT   Read Write Shared

 

EXPORTS

             SetImeWindow                 @1

             SetHook                                        @2

             RemoveHook                                 @3

 

 

 

3.. 위의 함수와 전역 변수의 세팅을 위한 [프로젝트명].def 파일을 만들어 준다.

LIBRARY   [프로젝트명]

 

SECTIONS

             .HKT   Read Write Shared

 

EXPORTS

             SetHook                                        @2

             RemoveHook                                 @3

 

 

4. DllMail 함수에서 핸들을 저장한다.

//------------------------------------------------------------------// DllMain : Entry point

//------------------------------------------------------------------BOOL APIENTRY DllMain( HANDLE hModule,

                       DWORD  ul_reason_for_call,

                       LPVOID lpReserved

                                                                   )

{

             switch (ul_reason_for_call)

             {

             case DLL_PROCESS_ATTACH:

                           g_Module = (HINSTANCE)hModule; // Save Dll Handle

                           break;

 

             case DLL_PROCESS_DETACH:

                           RemoveHook();

                           break;

    }

 

    return TRUE;

}

 

 

전체 프로젝트 파일

 

1. HookDll.cpp

// HookDll.cpp : Defines the entry point for the DLL application.

//

 

#include "stdafx.h"

 

//---------------------------------------------------

// Global Variables

// 공용 메모리

//---------------------------------------------------

#pragma data_seg(".HKT")

HINSTANCE g_Module = NULL ;     // DLL Handle

HHOOK g_Hook = NULL ;  // Hook Handle

HWND g_HwndServer = NULL ;      // Hook Server Window Handle

#pragma data_seg()

 

BOOL    RemoveHook() ;

BOOL    SetHook( HWND hWnd ) ;

 

//------------------------------------------------------------------

// DllMain : Entry point

//------------------------------------------------------------------

BOOL APIENTRY DllMain(

                                                                                HANDLE hModule,

                                                                                DWORD  ul_reason_for_call,

                                                                                LPVOID lpReserved

                                                                  )

{

             switch (ul_reason_for_call)

             {

             case DLL_PROCESS_ATTACH:

                           g_Module = (HINSTANCE)hModule; // Save Dll Handle

                           break;

                          

             case DLL_PROCESS_DETACH:

                           RemoveHook();

                           break;

    }

            

    return TRUE;

}

 

//--------------------------------------------------------------

// Hook Procedure - Keyboard

//--------------------------------------------------------------

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

{

             if( nCode >= 0 )

             {

                          

             }

             // We must pass the all messages on to CallNextHookEx.

             return ::CallNextHookEx( g_Hook , nCode , wParam , lParam );

}

 

//------------------------------------------------------------------

// Set Hook

//------------------------------------------------------------------

BOOL    SetHook( HWND hWnd )

{

             g_HwndServer = hWnd ;                // Set Hook Server

             g_Hook = SetWindowsHookEx( WH_KEYBOARD , KeyboardProcedure , (HINSTANCE)g_Module , 0 ) ;

            

             return false ;

}

 

//------------------------------------------------------------------

// Remove Hook

//------------------------------------------------------------------

BOOL    RemoveHook()

{

             UnhookWindowsHookEx( g_Hook ) ;

             return true ;

}

 

 

2. HookDll.def 파일

LIBRARY   HookDll

 

SECTIONS

.HKT   Read Write Shared

 

EXPORTS

SetHook                                        @2

RemoveHook                                 @3

 

 

프로젝트를 컴파일 하면 HookDll.lib 와 HookDll.dll 파일이 생성이 된다.


5. 프로시져 내에서 다른 윈도우로 데이터 전송하기

 

 이번에는 앞서 만든 후킹 프로시져를 좀더 강화해보기로 하자. 후킹 프로시져 내에서 메시지를 받고 받은 메시지를 복사하여 특정 윈도우에게 보낼 것이다.

 

 이때 WM_COPYDATA 메시지를 생성 할 것이다.

 

1. WM_COPYDATA 로 보낼 데이터의 구조를 정의한다.

// 메시지를 저장하는 구조체

typedef struct

{

             int                       Type ;

             WPARAM            Data ;

             LPARAM              lParam ;

} HEVENT;

 윈도우 메시지의 내용을 저장할 구조체

 

 

2. 현재의 메시지를 복사한다.

COPYDATASTRUCT  CDS;

HEVENT          Event;

 

// Set CDS

CDS.dwData = 0 ;

CDS.cbData = sizeof(Event);

CDS.lpData = &Event;

 

// 메시지의 내용을 저장한다.

Event.Type = 1 ;               // It's WM_KEY..

Event.Data = wParam ;     // Send CharCode

Event.lParam = lParam ;

 

 

3. g_HwndServer 에게로 메시지를 전달한다.

// g_HwndServer  에게로 메시지를 날린다.

// g_HwndServer 는 SetHook 함수 호출시 저장한 윈도우 핸들이다.

::SendMessage( g_HwndServer , WM_COPYDATA , 0 , (LPARAM)(VOID*)&CDS ) ;

 

 

 

프로젝트를 계속 진행 해보자.

 

1. 앞선 프로젝트의 내용 중 KeyboardProcedure() 함수의 내용을 수정한다.

 

//------------------------------------------------------------------

// Hook Procedure - Keyboard

//------------------------------------------------------------------

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

{

             if( nCode >= 0 )

             {

                           // Send To HookServer

                           COPYDATASTRUCT  CDS;

                           HEVENT          Event;

                           // Set CDS

                           CDS.dwData = 0 ;

                           CDS.cbData = sizeof(Event);

                           CDS.lpData = &Event;

                           // Set Variables

                           Event.Type = 1 ;               // It's WM_KEY..

                           Event.Data = wParam ;     // Send CharCode

                           Event.lParam = lParam ;

                          

                            ::SendMessage( g_HwndServer , WM_COPYDATA , 0 , (LPARAM)(VOID*)&CDS ) ;    

             }

 

             // We must pass the all messages on to CallNextHookEx.

             return ::CallNextHookEx( g_Hook , nCode , wParam , lParam );

}

 

 

2. Event 구조체를 정의 해준다.

typedef struct

{

             int                       Type ;

             WPARAM            Data ;

             LPARAM              lParam ;

} HEVENT;


6. 간단한 샘플 프로그램

 

 지금까지 만들어 본 후킹 프로시져를 가지고 실제로 샘플 프로그램을 작성해보자.

 

 

1. 프로젝트를 생성한다.

             - MFC 다이얼로그

 

2. 위의 프로젝트를 컴파일 하면 HookDll.lib 와 HookDll.dll 파일이 생성이 된다. 이 파일을 새롭게 생성할 프로젝트 폴더에 복사한다.

 

3. 프로젝트를 세팅한다.

             - 메뉴에서 Project -> Setting -> Link 탭에서 Object/Modules Library 칸에 HookDll.lib 를 지정한다.

 

 

 

4 . 다이얼로그를 위의 모양같이 만들어 주고

에디트 컨트롤을 생성하고 다음과 같이 변수를 연결해준다..

 

ID : IDC_EDIT_CNT

Value : m_Cnt

Type : int


5. 클래스 위저드로 WM_COPYDATA 메시지 핸들러를 생성한다.

BOOL CHookTestDlg::OnCopyData(CWnd* pWnd, COPYDATASTRUCT* pCopyDataStruct)

{

             // TODO: Add your message handler code here and/or call default

            

             // WM_COPYDATA 메시지가 올때마다 카운트를 증가한다.

             m_Cnt++ ;

             UpdateData( false ) ;

 

             return CDialog::OnCopyData(pWnd, pCopyDataStruct);

}

 

위와 같이 수정해준다.

 

6. DLL 에 있는 함수를 사용하기 위해 헤더 파일에 다음을 추가한다.

BOOL    RemoveHook() ;

BOOL    SetHook( HWND hWnd ) ;

 

 

7. InitDialog() 함수에서 SetHook() 함수를 호출한다.

BOOL CHookTestDlg::OnInitDialog()

{

             CDialog::OnInitDialog();

 

             // Add "About..." menu item to system menu.

 

             // IDM_ABOUTBOX must be in the system command range.

             ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);

             ASSERT(IDM_ABOUTBOX < 0xF000);

 

             CMenu* pSysMenu = GetSystemMenu(FALSE);

             if (pSysMenu != NULL)

             {

                           CString strAboutMenu;

                           strAboutMenu.LoadString(IDS_ABOUTBOX);

                           if (!strAboutMenu.IsEmpty())

                           {

                                        pSysMenu->AppendMenu(MF_SEPARATOR);

                                        pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);

                           }

             }

 

             // Set the icon for this dialog.  The framework does this automatically

             //  when the application's main window is not a dialog

             SetIcon(m_hIcon, TRUE);                           // Set big icon

             SetIcon(m_hIcon, FALSE);                          // Set small icon

            

             // TODO: Add extra initialization here

             SetHook( this->GetSafeHwnd() ) ; // 후킹 프로시져 설치

            

             return TRUE;  // return TRUE  unless you set the focus to a control

}

 

자 이제 실행하면 키보드를 칠 때마다 카운트가 증가하는 프로그램이 완성이 되었다

'C/C++언어 > 후킹' 카테고리의 다른 글

Hooking 을 사용하는 프로그램 내의 구현  (0) 2007.11.11
Global Hooking in Win32  (0) 2007.11.11
[본문스크랩] 메세지 후킹  (0) 2007.09.06
[본문스크랩] Knowledge Base API  (0) 2007.09.06
API Hooking Revealed  (0) 2007.09.06
동적 API 후킹의 구조  (0) 2007.09.06

+ Recent posts