2012년 11월 13일 화요일

<펌> Hook 에 대한 또 다는 설명


Hooking?
Hooking는 처리될 윈도우 메시지를 도중에 가로채는 것을 의미한다. 윈도우 메시지는 처리과정에서 어떤 식으로든 시스템의 도움을 받아야 한다. 윈도우 시스템에서 메시지를 전달하기 전에 가로채 재처리를 할 수 있는 기회가 있는데 이것이 Hooking이다. 사실 Hooking은 가로챈 메시지를 처리하기 위한 Hook Procedure를 설치함으로써 간단하게 사용할 수 있지만 시스템이나 프로세스에 많은 부하를 줄 뿐만 아니라 시스템에 심대한 영향을 줄 수도 있는 위험한 기능이다. 그래서 윈도우에 대해 전반적인 이해가 필요하다.

Hooking의 종류
Hooking은 Hooking할 대상의 범위와 메시지의 종류에 따라 나눠 볼 수 있다. Hooking 대상의 범위는 Global Hook과 Thread Hook이 있다. 그리고 Hooking할 메시지의 종류에 따라 여러 가지를 선택할 수 있는데 이 Hooking Type에 따라 시스템의 성능이나 안정성에 심대한 영향을 줄 수 있으며, 대상으로 하는 메시지에 따라 적절하게 선택해야 한다. 특히 Global Hook은 System을 대상으로 하는 것으로 시스템 전체에 많은 영향을 줄 것이다.

System Hook (Global Hook)
시스템 전체에 대한 메시지를 Hooking한다. 시스템 전체에 대해서 Hooking하려면 Hook Procedure의 위치가 중요하다. 윈도우 플랫폼에서는 다른 프로세스의 주소 영역을 침범할 수 없음을 잘 알고 있을 것이다. 또한 각 프로세스마다 논리적으로 같은 실행 영역을 가지고 있음도 알고 있을 것이다. 그렇다면 Hook Procedure의 위치가 왜 중요한지 생각해 보면, Hooking을 시도하는 어플리케이션의 그 실행 영역에 Hook Procedure가 있다면, 시스템에서 그 Procedure를 호출하는 것은 위와 같은 이유로 쉬운 일이 아닐 것이다. 사실 윈도우에서는 시스템에서 서로 공통으로 사용할 수 있는 실행 영역이 따로 존재한다. 이 영역은 DLL과 같은 공통으로 사용할 수 있는 코드를 적재하기 위한 공간이다. 비록 어플리케이션이 실행될 때 어플리케이션 영역은 모두 논리적으로 고유한 공간을 확보하고 있지만 시스템을 위한 공간만큼은 공통으로 사용하게 된다. 다시 말하면, 시스템에 있는 주소 영역은 어느 어플리케이션이나 물리적으로 같은 것을 가리킨다. 정리하면, Global Hook을 위해서는 Hook Procedure가 어플리케이션의 영역이 아닌 DLL이 적재되는 영역에 있어야 한다.

다른 문제는 DLL 내에 존재할 수 있는 Hook의 Handle이 공유되어야 한다는 것이다. 왜냐면, CallNextHookEx에서 Hook의 Handle을 패스해야 하지만 이 Handle이 다른 어플리케이션에서 유효하지 않다. DLL은 코드만 공유할 뿐 그 데이터는 공유하지 않는다. 그래서 Hook Handle은 모든 프로세스에서 공유할 수 있도록 방법을 찾아야 한다. 프로세스간 데이터를 공유하는 방법은 여러 가지가 있다. 쉽게 생각할 수 있는 방법이 공유메모리와 Memory Map IO 등이다.

Thread Hook
특정 Thread에 대해서만 Hooking한다. System Hook (Global Hook)과는 다르게 같은 프로세스의 한 Thread만을 대상으로 하기 때문에 Hook Procedure의 위치는 DLL에 있든, 어플리케이션 실행 영역에 있든 상관 없다.

Hook Chain
위에서 설명한 것처럼 Hook Type은 여러 종류가 있다. 또한 여러 개가 설치될 수 있을 것이며, 그 Type도 여러 개일 수 있다. 그래서 시스템에서는 이들 Hook을 리스트로 관리한다. 메시지가 발생 하여 리스트에 설치된 Hook이 발생된 메시지와 일치하면 그 메시지를 Hooking하게 된다. 그리고 메시지가 Hooking되면 그 메시지는 다른 Hook에서 처리할 수 있도록 메시지를 전달해야 할 것이다.

Win32 API for Hooking
Hook은 SetWindowsHookEx, UnHookWindowsHookEx, CallNextHookEx, Hook Procedure 등 이용한다. 이중 Hook Procedure는 각 Hook Type에 맞는 Procedure가 따로 정의되어 있다. 그 Hook Procedure Prototype에 맞게 Hook Procedure를 새롭게 작성하면 된다.

SetWindowsHookEx
Hook을 설치한다.
function SetWindowsHookEx(idHook: Integer; lpfn: TFNHookProc; hmod: HINST; dwThreadId: DWORD): HHOOK; stdcall;
두 번째 Argument는 Hooking될 때 호출될 Hook Procedure이다. 마지막 Argument는 Global Hook을 사용할 때는 0을, Thread Hook을 사용할 때는 그 Thread ID를 넘겨준다. Hook Type에는 사용할 Type에 따라 Global Hook과 Thread Hook을 구분해야 할 것이다.

UnHookWindowsHookEx
Hook을 제거한다.
function UnhookWindowsHookEx(hhk: HHOOK): BOOL; stdcall;

CallNextHookEx
Hooking된 메시지를 다음 Hook으로 패스한다.
function CallNextHookEx(hhk: HHOOK; nCode: Integer; wParam: WPARAM; lParam: LPARAM): LongInt; stdcall;

Hook Procedure
Hook Procedure에서 주의할 점
Thread Hook의 경우 좀 덜하겠지만 Global Hook의 경우 시스템 영향이 상당하다. MSDN에도 보면 디버깅 용도 이외에는 가능하면 자제할 것을 권고하고 있다. 그러므로 Hook Procedure는 가능하면 간단하게 작성되어야 한다.

메시지를 Hooking하고 나면 그 다음 Hook으로 그 메시지를 넘겨야 한다. 그래서 CallNextHookEx를 호출해야 하는데 처음에 받은 nCode, wParam, lParam을 그대로 넘겨주면 된다. 만약 메시지를 수정하거나 CallNextHookEx를 호출하지 않더라도 상황에 따라 별 문제는 생기지 않지만 어떤 동작을 할지 보장할 수 없게 된다. 왜냐면, Hook Chain은 시스템이 관리하므로 메시지가 어떤 순서로 처리되고 있는지 보장할 수 없다. 그리고 정상적인 경우 Hook Procedure의 결과 값은 CallNextHookEx의 결과 값을 리턴한다. nCode는 0보다 작을 경우 메시지를 처리하지 말고 바로 CallNextHookEx로 호출하여 메시지를 넘겨야 한다.


WH_CALLWNDPROC 쓰레드 또는 시스템 윈도우 관련 메시지들이 처리되기 전에 동작하는 훅
WH_CALLWNDPROCRET 쓰레드 또는 시스템 윈도우 관련 메시지들이 처리된 후 동작하는 훅
WH_CBT 쓰레드 또는 시스템 윈도우의 생성, 소멸, 포커스 등의 이벤트가 발생할 때 동작하는 훅
WH_DEBUG 쓰레드 또는 시스템 다른 훅의 사용을 디버그할 수 있는 훅
WH_FOREGROUNDIDLE 쓰레드 또는 시스템 애플리케이션 맨 앞의 윈도우가 아이들 상태일 때 동작하는 훅
WH_GETMESSAGE 쓰레드 또는 시스템 메시지들이 메시지 큐로 들어올 때 동작하는 훅
WH_JOURNALPLAYBACK 시스템만 저널 레코드 훅에 의해 저장된 키보드나 마우스 이벤트를 재생시킬 수 있는 훅. 일반적으로 이 훅이 동작하는 동안 키보드나 마우스 사용은 정지된다.
WH_JOURNALRECORD 시스템만 모든 키보드와 마우스 동작을 저장 할 수 있는 훅. 키 매크로 등을 제작 할 때 아주 유용하다.
WH_KEYBOARD 쓰레드 또는 시스템 키보드 메시지 발생시 동작하는 훅
WH_MOUSE 쓰레드 또는 시스템 마우스 메시지 발생시 동작하는 훅
WH_MSGFILTER 쓰레드만 특정 애플리케이션에서 만들어낸 메뉴, 다이얼로그 박스, 스크롤 박스 등에서 메시지가 발생할 때 동작하
는 훅
WH_SHELL 쓰레드 또는 시스템 윈도우 쉘에 관계된 메시지를 대상으로 하는 훅
WH_SYSMSGFILTER 시스템만 모든 애플리케이션이 만들어낸 메뉴, 다이얼로그 박스, 스크롤 박스 등에서 메시지가 발생할 때 동작하는 훅


댓글 없음:

델파이 12.1이냐 11.3이냐?

 델파이가 12.1이 나왔습니다. 혹시 11.3버전의 커뮤니티버전이 필요하시는분이 있을 수 있을 것 같아 https://altd.embarcadero.com/.../RADStudio_11_3_esd_28... 와 이것 찾느랴 엄청고생함.