Microsoft Word - hook3.doc

Similar documents
Microsoft PowerPoint - 09-CE-5-윈도우 핸들

Microsoft PowerPoint - chap02-C프로그램시작하기.pptx

개요 1. 후킹이란? 후킹의정의.. 2 후킹의종류 2 앞으로 후킹프로그램을위한사전지식들 Window 에서 data 입력과정.. 3 DLL ( Dynamic Link Library ).. 4 메시지후킹을위해필요한지식들 5 3. 후킹프로그램제작에필요한 API

Microsoft Word - hook5.doc

Microsoft Word - hook4.doc

1장 윈도우 프로그래밍 들어가기

Microsoft Word - hook8.doc

Microsoft PowerPoint - 04windows.ppt

Microsoft Word - hook7.doc

Microsoft Word - hook1.doc

PowerPoint 프레젠테이션

슬라이드 1

Microsoft PowerPoint - chap06-2pointer.ppt

04장 메시지 처리 유형

MFC 프로그래밍

API 매뉴얼

A Hierarchical Approach to Interactive Motion Editing for Human-like Figures

윈도우즈 프로그래밍

Lab 3. 실습문제 (Single linked list)_해답.hwp

<4D F736F F F696E74202D20C1A63034B0AD202D20C7C1B7B9C0D3B8AEBDBAB3CABFCD20B9ABB9F6C6DBC0D4B7C2>

11장 포인터

PowerPoint 프레젠테이션

<4D F736F F F696E74202D203031C0E520C0A9B5B5BFEC20C7C1B7CEB1D7B7A1B9D620B1E2C3CA5FBFB5B3B2C0CCB0F8B4EB205BC8A3C8AF20B8F0B5E55D>

Chapter #01 Subject

Microsoft PowerPoint - ch09 - 연결형리스트, Stack, Queue와 응용 pm0100

chap 5: Trees

<4D F736F F F696E74202D B3E22032C7D0B1E220C0A9B5B5BFECB0D4C0D3C7C1B7CEB1D7B7A1B9D620C1A638B0AD202D20C7C1B7B9C0D320BCD3B5B5C0C720C1B6C0FD>

Lab 4. 실습문제 (Circular singly linked list)_해답.hwp

1

슬라이드 1

[ 마이크로프로세서 1] 2 주차 3 차시. 포인터와구조체 2 주차 3 차시포인터와구조체 학습목표 1. C 언어에서가장어려운포인터와구조체를설명할수있다. 2. Call By Value 와 Call By Reference 를구분할수있다. 학습내용 1 : 함수 (Functi

UI TASK & KEY EVENT

Microsoft PowerPoint - chap13-입출력라이브러리.pptx

금오공대 컴퓨터공학전공 강의자료

임베디드시스템설계강의자료 6 system call 2/2 (2014 년도 1 학기 ) 김영진 아주대학교전자공학과

BMP 파일 처리

The Pocket Guide to TCP/IP Sockets: C Version

Microsoft PowerPoint - chap10-함수의활용.pptx

IS119_Message.Hooking_이성재.hwp

Microsoft PowerPoint - chap06-5 [호환 모드]

Microsoft PowerPoint - chap11-포인터의활용.pptx

Chapter 4. LISTS

아이콘의 정의 본 사용자 설명서에서는 다음 아이콘을 사용합니다. 참고 참고는 발생할 수 있는 상황에 대처하는 방법을 알려 주거나 다른 기능과 함께 작동하는 방법에 대한 요령을 제공합니다. 상표 Brother 로고는 Brother Industries, Ltd.의 등록 상

금오공대 컴퓨터공학전공 강의자료

Microsoft PowerPoint Android-SDK설치.HelloAndroid(1.0h).pptx

이번장에서학습할내용 동적메모리란? malloc() 와 calloc() 연결리스트 파일을이용하면보다많은데이터를유용하고지속적으로사용및관리할수있습니다. 2

Microsoft Word - src.doc

<322EBCF8C8AF28BFACBDC0B9AEC1A6292E687770>

윈도우시스템프로그래밍

경우 1) 80GB( 원본 ) => 2TB( 복사본 ), 원본 80GB 는 MBR 로디스크초기화하고 NTFS 로포맷한경우 복사본 HDD 도 MBR 로디스크초기화되고 80GB 만큼포맷되고나머지영역 (80GB~ 나머지부분 ) 은할당되지않음 으로나온다. A. Window P

JAVA 프로그래밍실습 실습 1) 실습목표 - 메소드개념이해하기 - 매개변수이해하기 - 새메소드만들기 - Math 클래스의기존메소드이용하기 ( ) 문제 - 직사각형모양의땅이있다. 이땅의둘레, 면적과대각

Microsoft Word - ntasFrameBuilderInstallGuide2.5.doc

03_queue

Microsoft PowerPoint - ch07 - 포인터 pm0415

윈도우 프로그래밍의 개념

제1장 Unix란 무엇인가?

제 14 장포인터활용 유준범 (JUNBEOM YOO) Ver 본강의자료는생능출판사의 PPT 강의자료 를기반으로제작되었습니다.

Microsoft PowerPoint - Lecture_Note_7.ppt [Compatibility Mode]

쉽게 풀어쓴 C 프로그래밍

C++ Programming

Microsoft PowerPoint - Lecture_Note_5.ppt [Compatibility Mode]

윤성우의 열혈 TCP/IP 소켓 프로그래밍

UI TASK & KEY EVENT

4S 1차년도 평가 발표자료

Install stm32cubemx and st-link utility

2009년 상반기 사업계획

vi 사용법

Microsoft PowerPoint - hci2-lecture1.ppt

Microsoft PowerPoint - [2009] 02.pptx

슬라이드 1

Microsoft PowerPoint - chap01-C언어개요.pptx

PowerPoint 프레젠테이션

Microsoft PowerPoint - C++ 5 .pptx

View Licenses and Services (customer)

C++ Programming

API 매뉴얼

제8장 자바 GUI 프로그래밍 II

<4D F736F F F696E74202D20C1A63137C0E520B5BFC0FBB8DEB8F0B8AEBFCD20BFACB0E1B8AEBDBAC6AE>

12 강. 문자출력 Direct3D 에서는문자를출력하기위해서 LPD3DXFONT 객체를사용한다 LPD3DXFONT 객체생성과초기화 LPD3DXFONT 객체를생성하고초기화하는함수로 D3DXCreateFont() 가있다. HRESULT D3DXCreateFont

The Pocket Guide to TCP/IP Sockets: C Version

A Dynamic Grid Services Deployment Mechanism for On-Demand Resource Provisioning

<443A5C4C C4B48555C B3E25C32C7D0B1E25CBCB3B0E8C7C1B7CEC1A7C6AE425CC0E7B0EDB0FCB8AE5C53746F636B5F4D616E D656E74732E637070>

좀비프로세스 2

Microsoft PowerPoint - chap06-1Array.ppt

Microsoft Word - windows server 2003 수동설치_non pro support_.doc

OCW_C언어 기초

Microsoft PowerPoint - CSharp-10-예외처리

Microsoft PowerPoint - chap03-변수와데이터형.pptx

5장 MFC기반 다지기

Microsoft Word - hook9.doc

Microsoft PowerPoint - hci2-lecture5-messagemap.ppt

설계란 무엇인가?


목차 윈도우드라이버 1. 매뉴얼안내 운영체제 (OS) 환경 윈도우드라이버준비 윈도우드라이버설치 Windows XP/Server 2003 에서설치 Serial 또는 Parallel 포트의경우.

Studuino소프트웨어 설치

PowerPoint 프레젠테이션

(Microsoft Word - \301\337\260\243\260\355\273\347.docx)

Microsoft PowerPoint UI-Event.Notification(1.5h).pptx

목차 1. 시스템요구사항 암호및힌트설정 ( 윈도우 ) JetFlash Vault 시작하기 ( 윈도우 ) JetFlash Vault 옵션 ( 윈도우 )... 9 JetFlash Vault 설정... 9 JetFlash Vault

1장. 유닉스 시스템 프로그래밍 개요

Transcription:

개발자를위한윈도우후킹테크닉 메시지훅이용한 Spy++ 흉내내기 우리는지난강좌에서후킹을하는기초적인방법과사용되는 API 들을비롯해서후킹함수가호출되는컨텍스트에대해서배웠다. 이번강좌에서는 WH_GETMESSAGE 훅을통해서 Spy++ 과유사한프로그램을제작할것이다. 이과정에서 Windows 애플리케이션이메시지를처리하는과정과윈도우를열거하는방법에대해서알아보도록하자. 목차 목차...1 필자소개...1 연재가이드...2 연재순서...2 필자메모...2 메시지는어떻게처리될까?...3 특수한메시지...3 WM_COPYDATA...4 Spy++ 은무엇에쓰는물건인고?...5 WH_GETMESSAGE 훅...9 윈도우핸들을열거하는방법... 10 메시지를후킹해보자... 14 Spy... 16 도전과제... 19 참고자료... 19 필자소개 신영진 pop@jiniya.net 부산대학교정보, 컴퓨터공학부 4 학년에재학중이다. 모자란학점을다채워서졸업하는것이꿈이되버린소박한괴짜프로그래머. 병역특례기간을포함해서최근까지다수의보안프로그램개발에참여했으며, 최근에는모짜르트에심취해있다.

연재가이드 운영체제 : 윈도우 2000/XP 개발도구 : 마이크로소프트비주얼스튜디오 2003 기초지식 : C/C++, Win32 프로그래밍응용분야 : 메시지모니터링프로그램 연재순서 2006. 05 키보드모니터링프로그램만들기 2006. 06 마우스훅을통한화면캡쳐프로그램제작 2006. 07 메시지훅이용한 Spy++ 흉내내기내기 2006. 08 SendMessage 후킹하기 2006. 09 Spy++ 클론 imspy 제작하기 2006. 10 저널훅을사용한매크로제작 2006. 11 WH_SHELL 훅을사용해다른프로세스윈도우서브클래싱하기 2006. 12 WH_DEBUG 훅을이용한훅탐지방법 2007. 01 OutputDebugString 의동작원리 필자메모 이제껏우리는후킹함수내에서실행프로그램으로정보를교환하기위해서 SendMessageTimeout 함수를사용했었다. 하지만전달되는정보에포인터가포함된것도아니기때문에 PostMessage 를사용해도될것이란의문은누구나품었을것이다. 지난 5 월호에연재되었던키보드모니터링프로그램의경우더더욱그렇다. 아마도 PostMessage 로변경해서테스트해보신분들은알겠지만 PostMessage 로해당훅코드를수행할경우이상한동작을한다. 테스트를안해봤다면지금한번해보도록하자. 훅코드를 PostMessage 로변경한다음훅을시작시키고모니터링프로그램에서키보드를막눌러보자. 아마도심하게몇번누르다보면어느순간화면에누르지도않았는데키보드정보가쉬지않고올라오는것을볼수있을것이다. 그원인을얼마전에알수있었다. 원인은다름아닌훅코드에서비교하는 code 변수값과관계가있었다. 통상적으로우리는 code 가 0 이상인경우수행하도록했었다. 그것이문제가된것이다. code 값이 HC_ACTION 인경우에만처리하도록하면그러한문제가없어진다. 2/20 페이지

메시지는어떻게처리될까? API 프로그래밍을한번도해보지않은독자분들이라면아마도애플리케이션에서메시지를어떻게처리하는지궁금할것이다. 실제로이부분은간단한메시지루프를통해서이루어진다. < 리스트 1> 은가장단순한형태의메시지루프다. MFC 프로그램같은경우에도프로그래머가작성한부분에메시지루프가없다고하더라도 MFC 내부적으로 < 리스트 1> 과같은형태의메시지루프를가지고있다. 리스트 1 간단한메시지루프 while(getmessage(&msg, 0, 0, 0)) TranslateMessage(&msg); DispatchMessage(&msg); 메시지루프가하는일은매우간단하다. 큐에서메시지를꺼내서, 적당히가공한다음해당 메시지를받을윈도우의메시지핸들러를호출한다. 이것이메시지처리의전부다. GetMessage 가하는역할은메시지큐에서메시지를하나꺼내는일이다. 메시지가없는경우는메시지가들어올때까지대기한다. 원활하게메시지를처리하기위해서 GetMessage 는메시지에우선순위를두어서처리한다. 즉, 먼저들어왔다고항상먼저처리하는것은아니다. TranslateMessage 가하는일은 WM_KEYDOWN, WM_SYSKEYDOWN, WM_KEYUP, WM_SYSKEYUP 메시지를적절한형태의 WM_CHAR, WM_SYSCHAR, WM_DEADCHAR, WM_SYSDEADCHAR 등의메시지로변환할수있는경우에, 변환된메시지를메시지큐에추가한다. 기본적으로 Windows 시스템의경우키보드를눌렀다떼면단순히 WM_KEYDOWN, WM_KEYUP 메시지만발생한다. DispatchMessage 가하는일은가져온메시지를실제로처리하는일이다. 가져온메시지에 해당하는윈도우의메시지핸들러를호출하는역할을한다고생각하면된다. 특수한메시지 지난강좌에서 Windows 에서실행되는모든프로그램은분리된메모리공간에서실행된다고배웠다. 하지만몇몇특수한메시지는이러한사실이마치거짓말인것처럼만든다. 그대표적인예가 WM_GETTEXT 와 WM_SETTEXT 메시지다. WM_GETTEXT 의경우윈도우텍스트를구해서저장할버퍼의배열을, WM_SETTEXT 의경우윈도우텍스트를 3/20 페이지

설정할문자열포인터를인자로받아들인다. 하지만이두메시지모두다른프로세스에존재하는윈도우에사용해도제대로동작한다. 지난시간에배웠던이론대로라면전달한포인터는다른프로세스로넘어가게되면엉뚱한번지가되고잘못된메모리참조오류가나야정상일것같지만그렇지않은것이다. 왜그럴까? 그비밀은 Windows 내부에있다. Win16 시절에는모든프로세스가동일한메모리공간을공유한다고말했었다. WM_GETTEXT 와 WM_SETTEXT 의경우그시절부터존재하던아주기초적인메시지였다. 따라서그시절프로그램들의경우모두같은공간에있기때문에다른프로세스에대해서도이러한메시지를보내는일이빈번했을것이다. Win32 가되면서더이상이러한일은불법적인것이된다. 하지만 Microsoft 의입장에서볼때그렇게바꾸게되면기존의 Win16 프로그램중많은프로그램에서오류가발생할것이뻔해보였다. 그러한이유로 Microsoft 는 Win32 로넘어오면서기존에사용하던메시지에대해서는포인터처리를자동으로해주자는방침을세운다. 이러한이유로 Win16 부터존재하던대다수의메시지는지금도여전히다른프로세스로포인터를전달해도아무런제약없이잘동작하는것이다. 그내부에서는공유메모리를할당하고다른프로세스로그메모리포인터를전달하는등의복잡한일이일어나고있다. WM_COPYDATA 그렇다면앞에서소개한 Win16 호환메시지를제외한메시지들은다른프로세스로포인터를전송할수없을까? 그렇다. 하지만 Microsoft 에서는다른프로세스로데이터를전달하는목적에사용할수있는범용메시지를하나만들어두었다. 그것이바로 WM_COPYDATA 이다. WM_COPYDATA 의 WPARAM 으로는메시지를보내는윈도우의핸들이지정된다. 받을윈도우가아니라보내는윈도우라는점에주의하도록하자. 만약보내는윈도우핸들이없는경우 0 으로지정하면된다. LPARAM 으로는 COPYDATASTRUCT 의포인터가전달되어야한다. COPYDATASTRUCT 는아래와같이정의되어있다 ( 각필드의의미는 < 표 1> 참고 ). typedef struct tagcopydatastruct ULONG_PTR dwdata; DWORD cbdata; PVOID lpdata; COPYDATASTRUCT, *PCOPYDATASTRUCT; 표 1 COPYDATASTRUCT 구조체필드별의미 4/20 페이지

필드명 의미 dwdata 다른프로세스로전달할정수데이터. cbdata lpdata 가가리키는내용의데이터크기. lpdata 다른프로세스로전달할데이터의포인터. 일반적으로 dwdata 에는현재보내는데이터에대한식별자가들어간다. 앞서말했듯이 WM_COPYDATA 는범용적으로설계된메시지다. 따라서이를통해서여러데이터가전송될수있기때문에각각을구분할무엇인가가필요하다. 그것을위한용도로보통 dwdata 를사용한다. lpdata 에는전달할데이터의포인터를, cbdata 에는해당데이터의크기를넣어주면된다. src 윈도우에서 dest 윈도우로 "hello, world" 라는문자열을전송하는경우라면 < 리스트 2> 과같이작성할수있다. 리스트 2 WM_COPYDATA 를통해서 "hello, world" 를전송하는예제 LPCTSTR data = "hello, world"; COPYDATASTRUCT cds; cds.dwdata = 1; // 주고받는쪽에서약속된임의의번호 cds.lpdata = data; cds.cbdata = strlen(data); SendMessage(dest, WM_COPYDATA, (WPARAM) src, (LPARAM) &cds); Spy++ 은무엇에쓰는물건인고? Spy++ 은굉장히유용한유틸리티이다. 그럼에도이것이설명된문서가잘없기에처음윈도우프로그램개발을시작하는분들은잘모르는분들이더러있다. 몇가지자주사용하는기능만간단히살펴보도록하자. 화면 1 Spy++ 시작화면 5/20 페이지

Spy++ 을실행시키면 < 화면 1> 과같은윈도우가나타난다. 여기에나타난목록은현재생성된모든윈도우의목록이다. 연하게표시된것은화면에표시되지않는숨김윈도우이고, 진하게표시된것은화면에표시되고있는윈도우다. 트리를통해서자식윈도우와부모윈도우의관계를손쉽게파악할수있다. 화면에나타난윈도우가굉장히많기때문에특정윈도우를찾는것이쉽지않다. 이러한 작업을쉽게하기위해서검색도구가제공된다. ALT + F3 을눌러보자. 그러면 < 화면 2> 와 같은윈도우가나타날것이다. 화면 2 Spy++ 찾기화면 위윈도우에서찾기도구오른쪽의아이콘을드래그하면해당아이콘이포함된윈도우를 찾아서영역을표시해준다. 자신이찾고싶은윈도우로드래그한다음놓으면된다. 노트패드를실행시킨후해당윈도우를찾아보자. 드래그가끝난다음확인을누르면된다. 6/20 페이지

화면 3 Spy++ 에서노트패드를찾은화면 < 화면 3> 은 Spy++ 에서노트패드를찾은화면이다. 트리를열어보면어떤자식윈도우로구성되어있는지알수있다. 위에서선택된윈도우에서오른쪽마우스버튼을누른다음속성을선택하면 < 화면 4> 와같은속성윈도우가뜬다. 이곳에서해당윈도우의각종정보를알수있다. 화면 4 Spy++ 에서윈도우속성을확인하는화면 7/20 페이지

화면 5 Spy++ 메시지후킹화면 Spy++ 의가장강력한기능은메시지후킹기능이다. Ctrl + M 을누르면 < 화면 5> 와같은 메시지창이뜬다. 여기서메시지후킹할윈도우를지정한다음메시지탭에서검사할 메시지를선택한다. 우리는 WM_KEYDOWN 메시지를선택했다. 그런다음확인을눌러보자. 화면 6 Spy++ 을통해서노트패드로전달되는키보드메시지를살펴본화면 < 화면 6> 은위에서설정한윈도우에서 a,b,c 키를누른화면이다. WM_KEYDOWN 으로 A,B,C 가발생한것을알수있다. 위에서소개한세가지기능이 Spy++ 에서가장많이 사용되는기능이다. 각종윈도우를찾아보고발생하는메시지를살펴보도록하자. 8/20 페이지

WH_GETMESSAGE 훅 WH_GETMESSAGE 훅은특정쓰레드로전달된메시지가처리되는것을후킹한다. 이훅의후킹프로시저는쓰레드로전달된메시지가 GetMessage 나 PeekMessage 에의해서제거되거나참조될때호출된다. 따라서후킹된쓰레드의메시지큐로들어가서처리되는모든메시지를살펴볼수있다. LRESULT CALLBACK GetMsgProc(int code, WPARAM wparam, LPARAM lparam); code [ 입력 ] code 값이 HC_ACTION 인경우훅프로시저를수행하고, 0 보다작은경우에는훅프로시저를수행하지않고 CallNextHookEx 를호출한다음리턴해야한다. wparam [ 입력 ] 메시지가메시지큐에서제거되었는지아닌지를나타낸다. 이값의의미는 < 표 2> 을참고하자. 표 2 code 값의미 값 의미 메시지가메시지큐에서제거되지않고참조된경우다. PM_NOREMOVE PeekMessage 를 PM_NOREMOVE 플래그를설정해서호출한 경우다. 메시지가메시지큐에서제거된경우다. GetMessage 나 PM_REMOVE PeekMessage 를 PM_REMOVE 플래그를설정해서호출한 경우다. lparam [ 입력 ] 발생한메시지정보를담고있는 MSG 구조체의포인터를저장하고있다. MSG 구조체는아래와같은형태를하고있다. MSG 구조체의필드별의미는 < 표 3> 를 참고하자. typedef struct HWND hwnd; UINT message; WPARAM wparam; LPARAM lparam; DWORD time; POINT pt; MSG, *PMSG; 표 3 MSG 구조체필드별의미 필드명 의미 hwnd 메시지를받을윈도우핸들. 9/20 페이지

message wparam lparam 발생한메시지 ID. 메시지 wparam. 메시지 lparam. time 메시지가발생한시간. pt 메시지가발생한마우스포인터. 리턴값 : code 가 0 보다작은경우에는 CallNextHookEx 의리턴값을그대로리턴해야 한다. 그렇지않은경우에도 CallNextHookEx 의리턴값을그대로사용하는것이좋다. CallNextHookEx 를통해서다음훅체인을호출하지않은경우엔 0 을리턴해야한다. 윈도우핸들을열거하는방법 Spy++ 과같은프로그램을만들기위해서우리가가장먼저해야할일을윈도우핸들을열거하는것이다. < 화면 1> 을보면현재시스템에동작하는모든윈도우가열거되어있는것을볼수있다. 이러한기능을구현하는데핵심역할을하는 API 두개를살펴보자. BOOL EnumWindows(WNDENUMPROC lpenumfunc, LPARAM lparam); EnumWindows 는현재시스템에동작중인 top 윈도우를열거하는역할을한다. top 윈도우는 < 화면 2> 에서루트에존재하는데스크탑윈도우 ( 바탕화면 ) 의직접적인자식윈도우들을말한다. 첫번째파라미터로열거를수행할콜백함수를, 두번째인자로해당콜백함수에전달될파라미터를넣어주면된다. 성공한경우에 0 이아닌값을리턴하고, 실패한경우에 0 을리턴한다. 콜백함수 lpenumfunc 에서 0 을리턴한경우에도 0 을리턴한다. BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lparam); EnumWindows 의첫번째인자로넘어가는콜백함수는위와같은원형을가지고있다. 첫번째파라미터로현재열거된윈도우의핸들이, 두번째인자로 EnumWindows 로프로그래머가전달한파라미터가넘어온다. TRUE 를리턴하면열거작업이계속진행되고, FALSE 를리턴하면열거작업이중단되고 EnumWindows 함수가 0 을리턴한다. < 리스트 3> 에 EnumWindows 를사용해서 top 윈도우를열거하는코드가나와있다. 리스트 3 top 윈도우를열거하는코드 #define WIN32_LEAN_AND_MEAN #include <windows.h> #include <stdio.h> 10/20 페이지

BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lparam) TCHAR windowname[max_path] = 0,; TCHAR classname[max_path] = 0,; if(!getwindowtext(hwnd, windowname, sizeof(windowname)) && GetLastError()!= ERROR_SUCCESS) return FALSE; if(!getclassname(hwnd, classname, sizeof(classname)) && GetLastError()!= ERROR_SUCCESS) return FALSE; printf( "Window name = \"%s\"\n" " Class name = \"%s\"\n", windowname, classname ); return TRUE; int main(int argc, char* argv[]) SetLastError(0); EnumWindows(EnumWindowsProc, 0); return 0; BOOL EnumChildWindows(HWND hwndparent, WNDENUMPROC lpenumfunc, LPARAM lparam); EnumChildWindows 는특정윈도우의자식윈도우를열거하는역할을하는함수다. 첫번째파라미터로열거할부모윈도우의핸들을, 두번째파라미터로콜백함수포인터를, 세번째인자로콜백함수로전달될파라미터를넣어주면된다. 성공한경우 TRUE, 실패한경우 FALSE 를리턴한다. 이함수의첫번째인자를 NULL 로주고호출하면위에서소개한 EnumWindows 와동일한역할을수행한다. < 리스트 4> 는 < 리스트 3> 의콜백부분을수정해서자식윈도우까지모두출력하도록만든것이다. 수행결과가 < 화면 7> 에나와있다. 리스트 4 EnumChildWindows 를사용해서자식윈도우를열거하는콜백함수 BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lparam) TCHAR windowname[max_path] = 0,; TCHAR classname[max_path] = 0,; if(!getwindowtext(hwnd, windowname, sizeof(windowname)) && 11/20 페이지

GetLastError()!= ERROR_SUCCESS) return FALSE; if(!getclassname(hwnd, classname, sizeof(classname)) && GetLastError()!= ERROR_SUCCESS) return FALSE; printf( "%*swindow name = \"%s\"" " Class name = \"%s\"\n", lparam, "", windowname, classname ); EnumChildWindows(hwnd, EnumWindowsProc, lparam+4); return TRUE; 화면 7 자식윈도우열거화면 12/20 페이지

박스 1 응용프로그램을열거하는방법 화면 8 작업관리자에나타난응용프로그램목록 EnumWindows 를사용하면작업표시줄에나타나는응용프로그램을열거할수있다. 작업관리자는화면에표시되는 top 윈도우중에서다음속성을만족하는것을열거한다. WS_EX_APPWINDOW 속성을가지고있다. WS_EX_TOOLWINDOW 속성을가지고있지않으면서, 소유주 (owner) 윈도우가없다. 위의두가지조건을체크하는코드를만들어보면다음과같다. DWORD exstyle = GetWindowLong(hwnd, GWL_EXSTYLE); BOOL isvisible = IsWindowVisible(hwnd); BOOL istoolwindow = (exstyle & WS_EX_TOOLWINDOW); BOOL isappwindow = (exstyle & WS_EX_APPWINDOW); BOOL isowned = GetWindow(hwnd, GW_OWNER)? TRUE : FALSE; if(isvisible && (isappwindow (!istoolwindow &&!isowned))) // 응용프로그램 위에서 if 문을통과한 top 윈도우를열거해보면작업관리자의응용프로그램에나타난 윈도우목록과동일함을알수있다. 13/20 페이지

메시지를후킹해보자. 메시지를후킹하는기본적인함수들은지난시간에소개한것과모두동일하기때문에별도로설명하지않겠다. 새롭게추가된부분만살펴보도록하자. < 리스트 5> 에 InstallHookEx 함수의코드가나와있다. 이함수는기존에사용하던 InstallHook 과동일한역할을한다. 하지만다른점은기존의 InstallHook 의경우훅을전역으로설치했었다. 반면에 InstallHookEx 의경우인자로받아들인쓰레드 ID 에해당하는쓰레드만후킹한다. 리스트 5 InstallHookEx 코드 MSGHK_API BOOL WINAPI InstallHookEx(DWORD tid) BOOL ret = FALSE; if(!g_hhook) g_hhook = SetWindowsHookEx(WH_GETMESSAGE, GetMessageProc, g_hinst, tid); if(g_hhook) ret = TRUE; return ret; GetMessage 와 PeekMessage 모두모든윈도우에서기본적으로가지고있는메시지루프의일부분이기때문에호출빈도가매우높기때문에전역으로후킹할경우시스템성능이상당히저하될수있다. 따라서메시지를살펴보고싶은윈도우를생성한쓰레드만후킹하는것이바람직하다. < 리스트 6> 는 InstallHookEx 를사용해서특정윈도우에해당하는쓰레드만후킹하는방법을보여주고있다. 리스트 6 InstallHookEx 를사용해서후킹하는부분 void CspyDlg::OnBnClickedHook() // 선택한항목의핸들을얻어온다. HTREEITEM item = m_treewindows.getselecteditem(); if(!item) AfxMessageBox(" 훅을설치할윈도우를선택해주세요 "); return; // 해당핸들에대한윈도우핸들을구한다. HWND hwnd = (HWND) m_treewindows.getitemdata(item); DWORD tid = GetWindowThreadProcessId(hwnd, NULL); 14/20 페이지

// 윈도우를생성한쓰레드를후킹한다. if(!installhookex(tid)) AfxMessageBox(" 훅설치에실패하였습니다."); return; // 후킹할메시지맵생성 m_hookmsgs.clear(); for(int i=0; i<g_msgscnt; ++i) if(m_lstmsgs.getsel(i) > 0) m_hookmsgs.insert(make_pair(g_msgs[i].id, g_msgs[i].desc)); // 리스트초기화 m_lsthookmsg.deleteallitems(); WH_GETMESSAGE 훅프로시저는 < 리스트 7> 에나와있다. WPARAM 값이 PM_REMOVE 인경우만체크해서메시지가실제로메시지큐에서꺼내지는시점에만메시지를보내도록되어있다. 메시지정보를다른프로세스로보내기위해서 WM_COPYDATA 를사용하고있다. 리스트 7 WH_GETMESSAGE 훅프로시저 LRESULT CALLBACK GetMessageProc(int code, WPARAM w, LPARAM l) if(code == HC_ACTION && w == PM_REMOVE) PMSG msg = (PMSG) l; if(iswindow(g_targetwnd)) COPYDATASTRUCT cds; cds.cbdata = sizeof(msg); cds.lpdata = msg; cds.dwdata = g_callbackmsg; SendMessageTimeout( g_targetwnd, WM_COPYDATA, 0, (LPARAM) &cds, SMTO_BLOCK SMTO_ABORTIFHUNG, 50, NULL ); 15/20 페이지

return CallNextHookEx(NULL, code, w, l); Spy 이번강좌에서우리가제작해볼프로그램은 Spy++ 의변종 Spy 프로그램이다. Spy 의실행화면이 < 화면 9> 에나와있다. 화면은전체적으로세부분으로나뉘어져있다. 왼쪽위에나오는부분이윈도우목록을열거하는부분이다. 그옆으로있는리스트는후킹해서검사할메시지목록을나타내고있다. 지금은네개의메시지만등록되어있다. 아래쪽에있는목록은후킹한윈도우에서발생한메시지를나타낸다. 훅일시중지버튼이선택되면훅이설치된상태라도훅메시지가올라오지않는다. 체크가해제되면다시메시지가올라온다. 전체삭제버튼을누르면지금까지추가되었던후킹메시지가모두제거된다. 윈도우목록갱신버튼은윈도우목록을최신의상태로업데이트시키는역할을한다. 훅설치버튼은윈도우목록에서선택한윈도우에메시지목록에해당하는메시지를후킹하는훅프로시저를설치하는역할을한다. 훅제거버튼은설치된훅을제거하는역할을한다. 화면 9 WH_GETMESSAGE 훅을사용한 Spy 프로그램실행화면 16/20 페이지

윈도우목록을트리로표현하는것과관련된코드가 < 리스트 8> 에나타나있다. 앞서소개한 EnumWindows 와 EnumChildWindows 함수를사용하고있다. 트리에표시하기위해서트리컨트롤의핸들과추가할아이템노드를콜백함수의파라미터로전달하고있다. 트리에뭔가를재귀적으로추가하는경우에많이사용되는코드이므로한번찬찬히살펴보도록하자. 리스트 8 윈도우목록을구해서트리로만드는코드 // FillTreeProc 콜백파라미터구조체 typedef struct _FTCBPARAM CTreeCtrl *ptree; // 트리핸들 HTREEITEM parent; // 부모아이템핸들 FTCBPARAM, *PFTCBPARAM; BOOL CALLBACK FillTreeProc(HWND hwnd, LPARAM p) FTCBPARAM np; PFTCBPARAM param = (PFTCBPARAM) p; TCHAR windowname[1024]; TCHAR classname[1024]; TCHAR title[1024]; // 윈도우이름과클래스이름을구한다음추가할문자열을만든다. GetWindowText(hwnd, windowname, sizeof(windowname)); GetClassName(hwnd, classname, sizeof(classname)); StringCbPrintf(title, sizeof(title), "%08X \"%s\" \"%s\"", hwnd, windowname, classname); // 아이템을추가한다음자식을열거한다. np.ptree = param->ptree; np.parent = param->ptree->insertitem(title, 0, 0, param->parent, TVI_LAST); param->ptree->setitemdata(np.parent, (DWORD_PTR) hwnd); EnumChildWindows(hwnd, FillTreeProc, (LPARAM) &np); return TRUE; void CspyDlg::RefreshWindowList() FTCBPARAM param; param.ptree = &m_treewindows; param.parent = TVI_ROOT; m_treewindows.lockwindowupdate(); m_treewindows.deleteallitems(); EnumWindows(FillTreeProc, (LPARAM) &param); 17/20 페이지

m_treewindows.unlockwindowupdate(); 실제로후킹된메시지를리스트에표시하는메시지핸들러는 < 리스트 9> 에코드가나와있다. 앞서살펴보았듯이이번강좌에사용된메시지후킹의경우정보전달을위해서 WM_COPYDATA 를사용했었다. 따라서후킹메시지도 WM_COPYDATA 에서처리되어야한다. 훅의일시중지상태와콜백메시지여부를확인한다음처리하고있다. 우리가모니터링하고싶은메시지인지를빠르게확인하기위해서맵을사용했다. 리스트 9 후킹메시지핸들러 BOOL CspyDlg::OnCopyData(CWnd* pwnd, COPYDATASTRUCT* pcopydatastruct) if(!m_hookpaused && pcopydatastruct->dwdata == WM_MSGHOOKNOTIFY) CString buf; int id; PMSG msg = (PMSG) pcopydatastruct->lpdata; if(msg->hwnd == m_hookwnd && m_hookmsgs.find(msg->message)!= m_hookmsgs.end()) id = m_lsthookmsg.getitemcount(); buf.format("%d", id+1); m_lsthookmsg.insertitem(lvif_text, id, buf, 0, 0, 0, 0); buf.format("%08x", msg->hwnd); m_lsthookmsg.setitemtext(id, 1, buf); m_lsthookmsg.setitemtext(id, 2, "POSTED"); m_lsthookmsg.setitemtext(id, 3, m_hookmsgs[msg->message]); buf.format("%08x", msg->wparam); m_lsthookmsg.setitemtext(id, 4, buf); buf.format("%08x", msg->lparam); m_lsthookmsg.setitemtext(id, 5, buf); return CDialog::OnCopyData(pWnd, pcopydatastruct); 메시지맵과관련된코드가 < 리스트 10> 에나와있다. 기본적으로화면에보이는메시지 목록은배열을통해서미리저장해둔것을출력한다. 그중에사용자가선택한메시지만 맵으로들어가게된다. 맵에메시지를추가하는부분은 < 리스트 6> 코드를참고하자. 18/20 페이지

리스트 10 메시지맵과관련된코드 // 메시지목록배열구조체부분 typedef struct _MSGDESC UINT id; LPCTSTR desc; MSGDESC, *PMSGDESC; MSGDESC g_msgs[] = WM_KEYDOWN, "WM_KEYDOWN", WM_CHAR, "WM_CHAR", WM_KEYUP, "WM_KEYUP", WM_LBUTTONUP, "WM_LBUTTONUP" ; const int g_msgscnt = sizeof(g_msgs) / sizeof(msgdesc); // 사용자가선택한목록만저장할맵 typedef std::map<uint, LPCTSTR> MsgMap; typedef std::map<uint, LPCTSTR>::iterator MsgMIt; MsgMap m_hookmsgs; // 후킹할메시지목록 도전과제 이번달에만든예제프로그램도부족한부분이너무도많다. 좀더그럴듯하게만들어보도록하자. 우리가추가한네개의메시지보다많은수의메시지를손쉽게추가할수있는방법을생각해보자. 그리고개별메시지마다파라미터를보기좋게출력하는방법을연구해보자. Spy++ 의경우 WPARAM 과 LPARAM 의형태로나타내는것이아니라, 각각이의미하는바를정리해서보여준다. 다음달은 WH_CALLWNDPROC 과 WH_CALLWNDPROCRET 를사용해서 SendMessage 를후킹해서보여주는기능을 Spy 프로그램에추가하는것을배울것이다. 여러개의윈도우를후킹했을때그것들을관리하는방법과 WM_COPYDATA 외의다른자료교환방법에대해서한번생각해보도록하자. 참고자료 참고자료 1. Jeffrey Richter. <<Programming Applications for Microsoft Windows (4/E)>> Microsoft Press 19/20 페이지

참고자료 2. 김상형, <<Windows API 정복 >> 가남사 참고자료 3. 김성우, << 해킹 / 파괴의광학 >> 와이미디어 참고자료 4. Spy++ 매뉴얼 - http://www.winapi.co.kr/toollec/spy/spy.htm 20/20 페이지