1. 선언 부분
#include <windows.h>
#include <gdiplus.h>

using namespace Gdiplus;
#pragma comment(lib, "gdiplus")

GDI +  사용함을 뜻함

2. WinMain 함수 선언 부분에서
// GDI Plus 초기화
 ULONG_PTR gpToken;
 GdiplusStartupInput gpsi;
 if (GdiplusStartup(&gpToken,&gpsi,NULL) != Ok)
 {
      MessageBox(NULL,L"GDI+ 라이브러리를 초기화할 수 없습니다.",L"알림",MB_OK);
      return 0;
 }

3. 사용은 WinProc 함수의 WM_PAINT 부분에서
case WM_PAINT:
      hdc=BeginPaint(hWnd, &ps);
      Game_Proc.OnPaint(hdc);
      EndPaint(hWnd, &ps);
return 0;

4. OnPaint(hdc) 함수로 모든 이미지 처리
class Game : private Image
{
private:
 // 게임에 사용할 이미지 선언 부분
     Image *m_iStage_me;              // 배경
     Image *m_iStage_you;
     Image *m_iChess_Command;   // 스프라이트
     ImageAttributes attr;                // 스프라이트 투명화 지정을 위해 필요

public:
 // 게임 초기화 (이미지 로딩부분...)
 Game()
 {
      m_iStage_me   = Image::FromFile(L"Image\\mystage.bmp");
      m_iStage_you  = Image::FromFile(L"Image\\yourstage.bmp");
      m_iChess_Command = Image::FromFile(L"Image\\Command.bmp");

      Color Trans_Color(0,255,255,255);      // 투명색 지정하되 흰색이 투명색
      attr.SetColorKey(Trans_Color,Trans_Color);
 }
 
 // 게임 그래픽 뿌려주는 영역..
 void OnPaint(HDC hdc)
 {
  Graphics G(hdc);
  // 실제 이미지가 뿌려지는 영역
  {
       G.DrawImage(m_iStage_me,0,0,m_iStage_me->GetWidth(),
                                                  m_iStage_me->GetHeight());
   
       G.DrawImage(m_iStage_you,0,256,m_iStage_you->GetWidth(),
                                                  m_iStage_you->GetHeight());

       G.DrawImage(m_iChess_Command,
   Rect(0,0, m_iChess_Command->GetWidth(), m_iChess_Command->GetHeight()),
                0, 0, m_iChess_Command->GetWidth(), m_iChess_Command->GetHeight(),
                UnitPixel, &this->attr);   // 투명색 지정하면서 그린다
  }

  // NDS상 가려지는 영역
  {
   SolidBrush S(Color(255,255,0));
   G.FillRectangle(&S,0,193,256,62);
   G.FillRectangle(&S,0,448,256,64);
   
   Font Font(L"굴림체",20,FontStyleRegular,UnitPixel);
   SolidBrush Brush_Font(Color(0,0,255));
   G.DrawString(L"Main 화면 밖 영역",-1,&Font,PointF(40,212),&Brush_Font);
   G.DrawString(L"Sub 화면 밖 영역",-1,&Font,PointF(40,468),&Brush_Font);
  }
 }
};

알송의 데스크탑 가사창을 어떻게 구현했을까.

우선 알송의 가사창을 자세히 보면 Alpha Blending으로 윈도우를 반투명 처리 되었지만

마우스 이벤트가 가사창 윈도우에 먹지 않습니다.

클릭을 하면 가사창 아래의 창에 클릭이 되고, 마우스 포인터(보통 화살표)도 가사창 아래의 윈도우에 투영됩니다.

이런것은 어떻게 처리 했을까?

정답은, API 함수의 윈도우 생성 함수중

CreateWindowEx() 함수의 Extended Style 인자에

WS_EX_TRANSPARENT 와 WS_EX_LAYERED 속성을 추가하면 됩니다

각각, 이 윈도우는 투명화 처리, 윈도우 레이어 임의 설정이 됩니다 

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

<이클립스 디버그 방법>  (0) 2008.01.28
printf 문을 만들어보자  (0) 2008.01.28
함수포인터란 ?  (0) 2007.09.06
[본문스크랩] 파일 입.출력  (0) 2007.09.06
[본문스크랩] VC++ Article  (0) 2007.09.06
출처 카페 > 게임 개발자 네트워크 (jz.. / 자존심
원본 http://cafe.naver.com/jzsdn/2284

♧ 유니코드


-싱글바이트와 더블바이트

: strlen을 호출하면 싱글바이트 문자들의 문자열 종결자(제로)배열내 문자들의 수를 리턴한다. 문제는 몇 언어들과 쓰이고 있는 시스템들 (예 : 일본어 kanji)이 싱글바이트가 제공하는 최대 256개의 심볼보다 더 많은 기술을 사용한다는 것이다. 그래서 더블바이트 문자세트는 이런 언어들과 기록시스템을 지원한다. (비주얼 C++런타임 라이브러리 : _mbslen함수)


- 1988년 애플과 제록스 사에 의해 표준화


- 더블바이트 문자세트는 다음 바이트가 같은 문자의 일부인지 새로운 문자인지 구분해야 하는데 유니코드는 그럴 필요가 없어서 CharNext, CharPrev와 같은 함수는 필요가 없다. 또한 16비트 값으로 표현하기 때문에 65000이상의 문자들을 이용할수 있으므로 싱글바이트 문자세트로 256문자들을 이용하는것과는 많은 차이가 난다.


- 현재 29000코드 포인트들이 할당되지 않고 있어서 이것들은 미래의 사용을 위하여 보류된 것이다. (키릴어, 영어, 히브리어, 아라비아어 등등을 표현한다)


- 유티코드를 사용하는 이유

        ? 언어들 사이에서 쉬운 데이터로 변환이 가능

        ? 모든 언어를 지원하는 싱글 바이너리, .exe  또는 DLL파일의 분배가 가능

        ? 애플리케이션의 효율향상


- 윈도우 2000은 유니코드 사용을 근거로 만들어졌다. 즉, 윈도우 함수를 호출하고 그것을 ANSI문자열로 넘기면, 시스템은 처음에 그 문자열을 유니코드로 바꾸고, 그 다음 유니코드 문자열을 운영체제로 넘긴다. 또한 함수로부터 ANSI문자열을 기다린다면, 애플리케이션으로 돌아가기 전에 시스템은 유니코드 문자열을 ANSI문자열로 변환한다. 물론 이런 변환을 수행하기 위해서는 시간과 메모리의 오버헤드가 존재한다.

예를들어, CreateWindowEx를 호출하고 유니코드가 아닌 클래스 이름과 윈도우 캡션을 전달하게 되면, CreateWindowEx는 메모리 블록을 할당해야 하고, 유니코드가 아닌 문자열을 유니코드로 바꿔야 한다. 그리고 그 결과를 할당된 메모리 블록에 저장하고, 유니코드버전 CreateWindowEx를 호출하는 함수를 만들어야 한다. 또한 문자열을 버퍼에 넣는 함수에서는 애플리케이션이 그 문자열을 처리히기 전에 시스템은 반드시 유니코드를 유니코드가 아닌 코드로 바꾸어야 한다. 그러므로 처음부터 유니코드를 사용하는 애플리케이션을 개발함으로써 능률적으로 수행하게 할 수 있다.


- Microsoft Unicode Story

        ? 윈도우 2000은 유니코드와 ANSI를 지원한다. 즉 둘 중 하나로 개발할 수 있다.

        ? 윈도우 98은 ANSI만 지원하므로 ANSI로만 개발해야 한다.

        ? 윈도우 CE는 유니코드만 지원하므로 유니코드로만 개발해야 한다.


- COM : 문자열을 요구하는 모든 COM인터페이스 메소드는 단지 유니코드 문자열만 받도록 되어있다. 왜냐하면 COM은 전형적으로 다른 컴포넌트가 서로 대화할 때 사용되고, 유니코드는 문자열을 전달하는 좋은 방법이기 때문이다. 만일 윈도우 98로 개발하고 COM을 사용한다면 많은 문제가 발생할 것이다.


- 유니코드 소스 작성하는 방법

? typedef unsigned short wchar_t;

wchar_t szBuffer[100]; 이렇게 버퍼를 생성한다. 물론 strcpy, strcat같은 표준 c런타임 문자열함수는 ASNI문자열만 연산한다. 그래서 그에 대응하는 유니코드함수가 있다.

char* strcat(char*, const char*);

wchar_t* wcscat(schar_t*, const wchar_t*);

이렇게 모든 유니코드 함수는 wcs(wide character string)로 시작한다. 그러므로 str을 wcs로 변경하면 된다.


- CreateWindowExW와 CreateWindowExA함수 프로토타입 비교

? PCWSTR : 상수 유니코드 문자열 포인터

? 윈도우 2000에서 CreateWindowExA의 마이크로소프트 소스코드는 단순히 청크(thunking)또는 변환, 즉 ANSI문자열을 유니코드 문자열로 바꾸기 위한 레이어가 된다.

그래서 코드는 바꾼 문자열을 전달하여 CreateWindowExW를 호출한다.


- ANSI와 유니코드를 대비한 애플리케이션 만들기

? 윈도우 문자열 함수 : lstrcat, lstrcmp, lstrcmpi, lstrcpy, lstrlen

: 이들 함수는 소스 모듈이 컴파일될 때 UNICODE가 정의되었는지에 따라 유니코드 버전 함수나 ANSI버전 함수를 호출하는 매크로로 구현된다. 즉, lstrcat는 lstrcatA와 lstrcatW로 확장될 것이다.

? 일반적인 데이터타입(TCHAR과 PTSTR과 같은)을 텍스트문자와 문자열로 사용한다.



- 유니코드는 한마디로 말하면 모든 문자를 2byte로 표현하자는 의미이다.

- 98은 전혀 지원하지 않는다.

- CE은 유니코드만 지원한다.

- 2000은 유니코드와 ANSI를 지원한다.

- 미래 os는 유니코드만 지원하게 될 거 같다.

- 그래서 코드를 재 사용할 때 문자열 표현에 문제가 생긴다.

예)char sz[100] : 미래에는 char보다 short int로 컴파일 해야한다.

그래서 지금 코딩할 때 유니코드 버전을 대비해서 미리 코딩하는 기법을 써야한다.

- 유니코드 쓰는 방법 : short int sz[100];

그런데 아직은 전부 다 이렇게 쓸 수가 없다.

- 유니코드를 쓰는 세 가지 관점

① 변수 선언

② 상수 선언

③ 함수 선언


1) 변수 선언


- char나 short int를 쓸 수 없어서 매크로를 만들어 놨다. : TCHAR

이러면 상황에 따라서 두 가지로 컴파일이 된다. 유니코드는 short int로, ansi는 char로 컴파일이 된다. 그러므로 앞으로는 TCHAR을 쓰는 게 좋다.

윈도우 CE에서 작업할 때 기존의 소스를 포팅하려면 char를 전부 수정해줘야 하는 문제가 생긴다.

- 포인터 변수일 경우는 (정수는 문제가 되지 않기 때문에 char *를 말한다) TCHAR*로 써줘야 한다.  그래서 이것도 다음과 같이 매크로로 만들어 놓았다.

- LPCTSTR : 상수형이다. c가 있으면  TCHAR* 상수형이다

- LPTSTR : TCHAR* 버퍼형이다. 즉 배열을 잡아서 써야한다. 그렇지만 LPCTSTR은 상수형이므로 바로 "HOWON"과 같이 쓸 수 있다.

-LPCSTR, LPSTR : 이것들은 T가 없으므로 여전히 char*로만 컴파일이 된다. 그래서 쓰지 않는다.


2) 상수 선언


- 문자열 상수 "   "를 말한다. "abc"면 \0까지 포함해서 4byte인데 이걸 유니코드에서

컴파일하면 여전히 4바이트이다. 그래서 유니코드 상수로 하여 L"abc"로 하면 8바이트가 된다. 그러나 이렇게 하면 지금 당장 컴파일이 안되므로 역시 매크로를 만들어 왔다.

- _T, TEXT : _T("abc") 이렇게 쓰면 컴파일될 때 현재 상황은 "abc"가 되고 유니코드로 컴파일하면 L"abc"가 된다.

TEXT는 API용이고 _T는 MFC용이다.


3. 함수사용


- strcpy는 char*를 인자로 받는다. 그래서 유니코드에서는 이 함수를 쓰지 못한다.

그래서 유니코드용 문자열 복사 함수를 만들어 놓았다. wcscpy(short int*  )

그런데 지금 코딩할 때는 이걸 쓸 수 없으므로 역시 매크로를 만들어 놓았다. : _tcscpy

이 함수는 지금 컴파일 하면 strcpy가 되고 나중에는 wcscpy가 된다.

strcat, strlen등도 마찬가지이다. 즉, _tcslen, _tcscpy 이렇게 _tcs만 앞에 붙인다.

그런데 atoi함수같은 경우는 _ttoi, sprintf는 _stprintf이다.

즉 간혹 두 번 째에 t가 나올 경우가 있다.

- MSDN찾는 방법

strcpy : 위는 ansi, 두번째는 유니코드용, 밑에 TCHAR Routine에 _tcscpy가 나온다.

- API에 CreateWindow가 있다면 CreateWindowA는 ANSI용이고 CreateWindowW는 유니코드용이다. 이렇게 두개 함수가 있으므로 그냥 사용하면 된다.

- PTSTR과 LPTSTR은 같은 의미이다. 윈3.1때는 포인터에 NearPointer 2바이트가 있었고, 4바이트짜리 포인터가 있었다. 그래서 long형이 아닌 걸 만들어 놓았다.

지금은 세그먼트가 없으므로 LPCTSTR이나 PCTSTR차이가 없다. 그래서 PTSTR은 사용하지 않는다.

- char temp sz[100];

  for(i = 0; i<sizeof(sz); i++ )

이렇게 하면 틀린다. 왜냐하면 유니코드가 아니면 100이므로 괜찮지만 유니코드에서는 short int로 바뀌므로 200이 된다. 그래서 sizeof(sz)/sizeof(TCHAR)로 해줘야 한다.



♣ 윈도우 문자열 함수



- CompareString : 문자열 비교하는 api함수이다. c 런타임함수로 strcmp함수가 있으니까 이걸 쓰거나 _tcscmp를 쓰면 되는데 이 함수는 문법적으로는 유니코드를 지원하지만 논리적으로는 지원하지 않는다.

유니코드는 한글, 알파벳이 한꺼번에 모여있다. 그래서 같은 국가문자들끼리만 비교해주려면 CompareString함수를 써야한다. CompareString함수의 첫 번째 인자가 지역ID를 나타낸다. 즉, 이 인자를 사용해서 문자의 EMt을 검사함으로서 두 문자열을 비교하게 된다. 이런 동작은 단순히 숫자를 비교하는 C런타임함수보다 훨씬 의미가 더 있다. (C런타임함수 strcmp, wcscmp등은 문자열에서 코드 포인트의 값을 비교한다. 즉, 함수는 실제 문자의 의미를 무시하고 단순히 각 문자의 숫자 값을 검사한다.)

- 즉 문자열 대소비교할 때 같은 버퍼 안에 한글, 영문이 섞여있을 때는 이  API함수를 써야한다.

- CharLower, CharUpper함수들은 ansi가 없고 유니코드 전용함수들이다.

- 이런 함수들을 쓸때는 다음과 같이 코딩한다.


#ifdef _UNICODE

        CharLower();

#else

        tolower();

#endif


- IsTextUnicode : 텍스트가 ANSI인지 유니코드인지 결정하는 함수로서 참, 거짓을 반환한다. 즉, 버퍼가 유니코드인지 ANSI인지 판단해주는 함수이다.

PDA는 윈도우CE를 쓴다. 메모장에서 작성한걸 pda에서 쓰려면 먼저 유니코드로 변환해야 하는데 이럴 때 이 함수를 써서 유니코드인지 아닌지를 먼저 판단한다.

- DWORD IsTextUnicode(CONST PVOID pvBuffer, int cb, PINT pResult);

- 함수인자로는 첫 번째 인자는 비교하고자 하는 버퍼주소인데 이때 버퍼문자열이 유니코드 문자열인지 ANSI 문자열인지 알지 못하므로 void포인터형이다.

두 번째 인자는 버퍼 바이트 수인데 역시 버퍼내용을 모르기 때문에 문자 카운트가 아닌 바이트의 카운트가 된다. 세 번째 인자는 비교하는 방법(옵션인데 null을 준다)으로 되어있다.

특히 이 함수는 통계적으로 판단한다. 즉 버퍼에 유니코드가 더 많으면 유니코드라고 판단해준다.


♧ 유니코드와 ANSI간의 문자열 변환


- MultiByteToWideChar : ANSI를 유니코드로 변환

- WideCharToMultiByte : 유니코드를 ANSI로 변환

- com은 os와 상관없이 반드시 유니코드를 써야한다.

- .Net : 실행가능한 개체를 만들자는게 목표이다. 실행가능한 개체란 소스상태가 아니고

컴파일된 상태를 말한다. 그래서 com도 같은 목표를 가지고 있다.


따라해보는 후킹

작성자 : 이은규

작성일 : 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
[본문스크랩] Knowledge Base API  (0) 2007.09.06
API Hooking Revealed  (0) 2007.09.06
동적 API 후킹의 구조  (0) 2007.09.06

+ Recent posts