Microsoft MVP성태의 닷넷 이야기
VC++: 6. Win32 API Hook - 소스는 "공개소스"에있습니다. [링크 복사], [링크+제목 복사]
조회: 22645
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일
 

오늘 자료실에 올린, Win32 API Hook의 압축 파일에 넣어둔 "설명서.txt" 파일의 내용입니다.
보시고, 흥미롭다고 판단되면, 소스를 다운로드 받으셔서 분석해 보시는 것도 좋을 것 같습니다.

/////////////////////////////////////////////////////////////////////



압축을 풀면, 다음과 같은 폴더가 나옵니다.

Bin
class
IBizHook
techlib
TestATL

실제적으로 컴파일해야 하는 프로젝트가 있는 폴더는 단 2개입니다.
IBizHook과 TestATL입니다.

- Bin 폴더는 IBizHook과 TestATL 프로젝트를 컴파일하면, 최종 DLL이 놓여지는 폴더이고, TestSock.htm이라는 예제파일이 있습니다.

- class 폴더는 IAT.cpp, IAT.h 파일이 있습니다. 이 소스는 MSDN 웹 사이트에서 구한 것입니다.

- IBizHook 폴더는 실제로 API Hook을 설치하고, 해제하는 루틴이 들어 있는 COM 객체입니다. 재사용이 가능하도록 되어 있지는 않습니다.
  현재의 IBizHook 모듈안의 소스는 TestATL.DLL을 위해서 제작된 것입니다.

- TestATL 폴더는 소켓 함수를 쓰는 간단한 MsgBox라는 메서드만을 포함하고 있습니다.

- techlib 폴더는 IAT에 대한 설명을 담고 있는 MSDN 사이트 URL입니다.


///////////////////////////////////////////////////////////////////////////////////////


동작 방법.
1 . IBizHook을 컴파일 합니다.
2. TestATL을 컴파일 합니다.
3. Internet Explorer를 실행시켜서 Bin 폴더 아래에 있는 TestSock.htm을 실행시킵니다.
4. "메시지" 버튼이 IE 화면에 나오는데, 클릭합니다.
5. "TEST" 메시지 박스가 뜨는 것이 보입니다.

///////////////////////////////////////////////////////////////////////////////////////

소스 추적
1. TestSock.htm을 먼저 설명드리겠습니다.
  ㄱ. IBizHook.DLL 안에 있는 IBizHook.FuncSock 객체를 생성합니다.
  ㄴ. 생성 후, StartHook 메서드를 호출하는 것을 볼 수 있습니다.
  ㄷ. VC++에서 IBizHook 프로젝트를 열어서, 해당 메서드를 살펴봅니다.
  ㄹ. `Func.cpp 안에 StartHook 메서드를 보면, 간단하다는 것을 알 수 있습니다.
       처음 라인부터 살펴보면,
      m_hookFunc.dwOrdinal = 16; // Ws2_32.dll을 "Depends" 툴로 확인하면,
// recv 함수의 경우, 함수 이름이 export 되어 있지 않고,
// 단지 Ordinal만 나오는 것을 확인할 수 있습니다.
// MSDN 웹사이트의 예제는 함수 이름이 없는 경우는,
// 훅킹할 수 있는 방법이 없었는데, 제가 IAT.cpp를 약간
// 수정해서 "recv" 함수처럼 Ordinal만 있는 경우를 위해서
// 작동이 되도록 했습니다.
// 단지, 주의할 것은 함수명이 있는데도, Ordinal을 지정하면 안되는 경우가 있습니다.
// 그러니, 반드시 함수명이 제공되는 경우는 그냥
// m_hookFunc.szFunc = "recv";
// 처럼 적어주시면 됩니다.

m_hookFunc.pProc = (PROC)hook_recv; // 가로채기를 위해 제작한 함수
// 즉, API StartHook 메서드가 실행된 이후에는
// TestATL.dll에서 불려지는 모든 recv 함수는
// 우선, hook_recv를 불려지게 됩니다.
// hook_recv 안에서는 조건에 따라서 실제 recv 함수를
// 부를 수도 있고, 그렇지 않을 수도 있습니다.

HMODULE hModule = GetModuleHandle( 'TestATL.dll" );
// TestATL.dll의 Import Address Table을 조작하기 위해서
// 해당 Instance Handle을 얻어놓습니다.
// 여기에서 알 수 있듯이, DLL API 가로채기는 특정 DLL
// 을 지정해야만 됩니다.

// 제가 이 예제를 IBizHook과 TestATL로 분리시켜 놓은 것은
// 그것을 명백히 보여드리기 위해서였습니다.
// 위와같이 "TestATL.dll"로 지정을 해놓고,
// IBizHook.DLL 안에서 호출되어지는 "Recv"는 가로챌 수가 없다는 것입니다.
// 왜냐면, DLL들에 대한 API 주소를 담고 있는 Import Address Table은
// PE 파일의 구조에 따라서 각각 하나의 DLL마다 포함하고 있기 때문입니다.

BOOL bRet = HookImportedFunctionsByName( ... );
// 이 함수는 MSDN 웹사이트의 BugSlayer 관련한 기사에 포함된 소스이며,
// 제가 Ordinal만 나타나는 함수의 훅킹을 위한 약간의 수정을 했습니다.
// 수정 부분에 대해서는 "techlib" 폴더의 IAT.html을 방문하시면 나오는
// 페이지에서 제공되는 소스와 비교해서 살펴보시면 됩니다.

// 이 함수는 m_hookFunc에 지정된 DLL API를 함수의 인자로 들어온
// m_pfnOrgRecv 주소로 교체를 합니다.
// 따라서, 이후의 모든 recv 함수 호출은 m_pfnOrgRecv 주소로 우회하게 됩니다.


ㅁ. 위와 같은 규격만 따라 준다면 어떠한 API든지 훅킹이 가능합니다.
ㅂ. 다시, HTML로 돌어가서, StartHook을 부르는 것으로 window.onload는 끝나게 됩니다.

ㅅ. 그 이후에, 사용자가 "메시지" 버튼을 누르게 되면, "TestATL.dll" 안에 구현된 COM 객체의 MsgBox()라는
간단한 메서드를 호출하게 됩니다.
명백히 하기 위해서 VC++ IDE에서 TestATL 프로젝트를 열어서 TestSock.cpp를 확인합니다.
ㅈ. MsgBox 메서드 안에는

SOCKET hSocket = socket( ... );
recv( ... );
closesocket( ... );

위와 같이 있는데, "IBizHook"에서의 FucnSock.cpp 안에 있는 StartHook 메서드에 의해서 recv의
주소를 교체를 했으므로, 위의 코드에서의 recv 함수 호출은 FuncSock.cpp 안에 있는
hook_recv( ... );
함수를 호출하게 됩니다.

그럼, 이제 VC++ IDE에서 FuncSock.cpp 파일을 열어봅니다.

hook_recv 함수의 구현을 보면,

int iRet = m_pfnOrgRecv( s, buf, len, flags );
::MessageBox( ... );
return iRet;

와 같이 되어 있는 것을 확인할수 있습니다.
물론, 위의 경우는 테스트를 위한 경우이기 때문에 간단합니다.
하고자 한다면, 조건에 따라서 recv 함수를 호출하지 않게도 할 수 있거나,
모든 소켓 입력에 대해서 따로 로그 파일에 남겨둘 수 도 있습니다.


ㅊ. window.onunload에 보면 EndHook이 있습니다.
즉, 설치해 놓은 주소를 다시 제거하는 것입니다. 만약, Application이 정말 이와 같이
종료를 수반한다면, 굳이 EndHook을 할 필요는 없을 것입니다. 하지만,
설정/해제의 쌍을 맞추는 것은 좋은 습관일 것입니다.





///////////////////////////////////////////////////////////////////////////////////////


못다한 얘기.

1. 원래의 MSDN 소스에는 함수 이름만을 이용해서 API Hook을 하도록 되어 있었습니다.
  원저작자가 Ordinal만 있는 경우 API Hook을 못하게 한 것은 실력이 없어서가 아님을
  저는 분명히 확신합니다.
  어쨌든, 제 편의를 위해서 저는 그 소스를 Ordinal로도 가능하게 바꾸었습니다.

  나중에 테스트를 해보니, CoCreateInstance의 경우, 이름으로는 API Hook이 되는데,
  Ordinal로는 안되었습니다.

  그런데, 제가 이 소스를 배운 이후에 국내의 "마이크로 소프트웨어" 잡지 1999.12월 호를
  우연한 기회에 보게 되었습니다. 거기 보면, "네 이웃의 메모리를 탐하지 말라"라는 제목으로
  DLL API Hook에 대한 설명이 나옵니다. 그 기사의 저자는 "김성우(한국 통신 무료전화 개발팀장)",
  "김상민(고려대 전자공학과 하나와영 7기)" 두 분이었습니다.
  중요한 것은 그 예제를 보고는, "아차!" 했습니다. Ordinal도, Export 함수 이름도 필요 없이,
  그냥 GetProcAddress로 기존 recv 함수의 주소를 알아내고, IAT를 열람하면서 그 주소와 똑같은
  엔트리를 변경시켜 주면 그만이기 때문입니다.

  즉, 실제 GetProcAddress 함수의 역할이 HookImportedFunctionsByName에 있는 것과 동일하다는 것입니다.
  내부적으로 GetProcAddress 역시, IAT를 돌면서 인자로 들어온 함수 이름과 똑같은 Entry를 찾기 때문입니다.

  단지, 그 기사에서는 다소간의 Assembly와 기계어 구조를 알아야 하는 설명이 첨가되어 있었습니다.
  종합해 보면, 그 두 가지 기사 간에는 반쪽씩 장/단점이 있는 것입니다.

  제가 그 2가지를 시간이 나면 합쳐보려고 했는데, 사실, ordinal과 함수 이름만으로도 잘 동작이
  되는 제 소스완성으로 이렇게 시간만 끌게 되었네요. 상당히 간단한(!) 문제인데도, 교체를 하기가
  상당히(!) 귀찮네요.
  혹시나, 이 글을 읽고 계시는 분들이 잠깐의 실습겸 시간을 내서 고쳐보시면 이해에 한층 더 도움이
  되시리라 확신합니다.

2. 기회가 되시면 techlib 폴더에 있는 IAT.html을 꼭 방문해보시고, 기사를 읽어보시는 것도 좋을 것입니다.
  물론, "마이크로 소프트웨어" 잡지 1999.12월 호를 구해서 보는 것이 가장 좋은 방법일 것입니다.
  그렇긴 하지만, ^^ IAT.html은 무료이고,,, "마소" 잡지는 유료이기 때문에.

3. 전체적으로, 소스가 간결합니다.
  VC++을 하시는 분이라면, 다른 언어로는 근접하기 어려운 이런 유의 기술을 습득하시는 것이,
  자신을 남들과 차별화시키는 한 방법일 것 같습니다.
  사실, VC++로 UI를 만드는 것은 정말이지, 인생의 낭비가 아닐까 싶네요.

4. 응용 분야. 요즘 들어서 Plug-In을 지원하는 Application이 많이 늘고 있습니다.
  즉, DLL을 해당 프로세스 경계 안으로 들어갈 수 있도록 하는 것이죠. 결과적으로, 그것으로 인해
  우리가 만든 DLL이 원래 EXE의 무제한에 가까운 제어를 할 수 있는 가능성이 생기는 것입니다.
  COM 객체를 띄워주는 Internet Explorer는 그 좋은 실험 대상일 것입니다.

5. 이 예제에서는 반드시 "EXE"가 DLL을 불러줄 수 있는 상황이어야 합니다.
  하지만, 모든 프로세스를 대상으로 제어를 하고 싶다면, "마이크로 소프트웨어" 잡지의 1999.12월 호 기사의
  이전 기사를 참고하시면 좋을 것 같습니다. 그 기사 외에도 "Programming Applications for Micorosoft Windows, 4th Edition"
  에서도 DLL Injection에 대해서 자세히 설명이 되어 있습니다. 며칠 전에 이 책의 번역서가
  나왔다는 반가운 소식이 있네요. 꼭 한번 보시길 바랍니다.


///////////////////////////////////////////////////////////////////////////////////////

참고 서적 및 방향

1. 사실, 급하게 API Hook을 써야 할 상황이라면, 이 예제 정도로만 이해하시고 써도 크게 무리는 없습니다.
  하지만, 말려도 말려도 무엇인가를 하고자 하시는 분들. 그런 분들께 대강의 길을 제시합니다.

  ㄱ. 우선, PE 파일의 구조 이해가 선행되어야 합니다.
  PE 파일은 Win32 응용 프로그램이 디스크에 저장되는 바로 그 EXE 파일의 구조입니다.
  MS는 가상 메모리 Swap 파일의 크기 및 운용 효율성을 위해서 실행 파일의 경우에는,
  따로 요구가 없는 한, 코드 및 데이터를 가상 메모리에 두지 않고, 그냥 디스크에 보관된
  내용을 Memory Mapped 기법으로 사용을 합니다.
  그것이 바로, EXE 프로그램이 실행되었을 때, 해당 EXE 파일이 "잠겨지는" 원인인 것입니다.


  이것을 위해서,
  MSDN Magazine의 기사 "An In-Depth Look into the Win32 Portable Executable File Format"을
  보시면 도움이 되실 것입니다. 두 달에 걸채서 연재되었습니다. 기사는 아래의 URL에서
  볼 수 있습니다.

  https://docs.microsoft.com/en-us/archive/msdn-magazine/2002/february/inside-windows-win32-portable-executable-file-format-in-detail
  https://docs.microsoft.com/en-us/archive/msdn-magazine/2002/march/inside-windows-an-in-depth-look-into-the-win32-portable-executable-file-format-part-2

ㄴ. Windows Loader에 대한 이해도 아주 "조금은" 하시는 것도 좋습니다.
  물론, 이 부분은 건너 뛰셔도 좋습니다. 하지만, "지치지 않는 열정"을 가지신 분들. 보시려면...
  보십시오. ^^

  Win32 Loader에 대해서 Pseudo 코드로 기능을 설명하고 있습니다.
  http://www.microsoft.com/msj/defaultframe.asp?page=/msj/0999/hood/hood0999.htm
  

  아래의 기사는 별로 도움이 되지 않을 것 같지만, 그래도 추천을 해봅니다.
  MSDN Magazine의 기사 "What Goes on Inside Windows 2000 : Solving the Mysteries of the Loader"
  를 아래의 URL에서 확인할 수 있습니다.
  https://docs.microsoft.com/en-us/archive/msdn-magazine/2002/march/windows-2000-loader-what-goes-on-inside-windows-2000-solving-the-mysteries-of-the-loader

ㄷ. 위에서 이미 한 번씩 언급을 해드렸는데, 다시 한번 적습니다.
  책 : "Programming Applications for Micorosoft Windows, 4th Edition"
  잡지 : "마이크로 소프트웨어 1999.12월 호"
  









[최초 등록일: ]
[최종 수정일: 6/27/2021]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer at outlook.com

비밀번호

댓글 작성자
 



2008-03-21 05시19분
kevin25
2014-10-07 05시15분
정성태

[1]  2  3  4  5  6  7  8  9  10  11  12  13  14  15  ...
NoWriterDateCnt.TitleFile(s)
13608정성태4/26/2024222닷넷: 2249. C# - 부모의 필드/프로퍼티에 대해 서로 다른 자식 클래스 간에 Reflection 접근이 동작할까요?파일 다운로드1
13607정성태4/25/2024219닷넷: 2248. C# - 인터페이스 타입의 다중 포인터를 인자로 갖는 C/C++ 함수 연동
13606정성태4/24/2024246닷넷: 2247. C# - tensorflow 연동 (MNIST 예제)파일 다운로드1
13605정성태4/23/2024545닷넷: 2246. C# - Python.NET을 이용한 파이썬 소스코드 연동파일 다운로드1
13604정성태4/22/2024599오류 유형: 901. Visual Studio - Unable to set the next statement. Set next statement cannot be used in '[Exception]' call stack frames.
13603정성태4/21/2024793닷넷: 2245. C# - IronPython을 이용한 파이썬 소스코드 연동파일 다운로드1
13602정성태4/20/2024855닷넷: 2244. C# - PCM 오디오 데이터를 연속(Streaming) 재생 (Windows Multimedia)파일 다운로드1
13601정성태4/19/2024890닷넷: 2243. C# - PCM 사운드 재생(NAudio)파일 다운로드1
13600정성태4/18/2024911닷넷: 2242. C# - 관리 스레드와 비관리 스레드
13599정성태4/17/2024878닷넷: 2241. C# - WAV 파일의 PCM 사운드 재생(Windows Multimedia)파일 다운로드1
13598정성태4/16/2024913닷넷: 2240. C# - WAV 파일 포맷 + LIST 헤더파일 다운로드2
13597정성태4/15/2024896닷넷: 2239. C# - WAV 파일의 PCM 데이터 생성 및 출력파일 다운로드1
13596정성태4/14/20241077닷넷: 2238. C# - WAV 기본 파일 포맷파일 다운로드1
13595정성태4/13/20241061닷넷: 2237. C# - Audio 장치 열기 (Windows Multimedia, NAudio)파일 다운로드1
13594정성태4/12/20241079닷넷: 2236. C# - Audio 장치 열람 (Windows Multimedia, NAudio)파일 다운로드1
13593정성태4/8/20241089닷넷: 2235. MSBuild - AccelerateBuildsInVisualStudio 옵션
13592정성태4/2/20241226C/C++: 165. CLion으로 만든 Rust Win32 DLL을 C#과 연동
13591정성태4/2/20241201닷넷: 2234. C# - WPF 응용 프로그램에 Blazor App 통합파일 다운로드1
13590정성태3/31/20241081Linux: 70. Python - uwsgi 응용 프로그램이 k8s 환경에서 OOM 발생하는 문제
13589정성태3/29/20241158닷넷: 2233. C# - 프로세스 CPU 사용량을 나타내는 성능 카운터와 Win32 API파일 다운로드1
13588정성태3/28/20241274닷넷: 2232. C# - Unity + 닷넷 App(WinForms/WPF) 간의 Named Pipe 통신 [2]파일 다운로드1
13587정성태3/27/20241358오류 유형: 900. Windows Update 오류 - 8024402C, 80070643
13586정성태3/27/20241532Windows: 263. Windows - 복구 파티션(Recovery Partition) 용량을 늘리는 방법
13585정성태3/26/20241313Windows: 262. PerformanceCounter의 InstanceName에 pid를 추가한 "Process V2"
13584정성태3/26/20241273개발 환경 구성: 708. Unity3D - C# Windows Forms / WPF Application에 통합하는 방법파일 다운로드1
[1]  2  3  4  5  6  7  8  9  10  11  12  13  14  15  ...