API Hooking - 64비트를 고려해야 한다면? EasyHook!
지난번에 "Trampoline API Hooking" 방식을 알아봤는데요.
Win32 API 후킹 - Trampoline API Hooking
; https://www.sysnet.pe.kr/2/0/1231
이 방법의 단점은, 아쉽게도 x64 지원이 안된다는 것입니다. 물론, 이래 저래 뜯어고치면 되겠지만 '안정성'을 확보할 수 있는 실력이 안된다면 ^^ 차선책을 고려해볼 수 있습니다. 그렇게 선택될 수 있는 첫 번째가 그 누구도 아닌 '마이크로소프트' 스스로 개발한 후킹 라이브러리가 있으니, 바로 "Detours"가 그것입니다.
Detours 라이브러리를 이용한 Win32 API - Sleep 호출 가로채기
; https://www.sysnet.pe.kr/2/0/631
(
MIT 라이선스로 무료 공개된 Detours API 후킹 라이브러리)
음... 그런데, 역시 단점이 없지 않습니다. 32비트/64비트 모두 지원하긴 하지만, 무료 버전은 32비트만 지원하고 64비트까지 지원하려면 반드시 유료 버전을 구매해야 합니다.
저 같은 평범한 개발자가 $9,999를 지불하기에는 좀 그러하니, 다음으로 생각해 볼 것이 무료 중에 제법 안정적인 라이브러리가 없나 찾아보게 됩니다. 다행히, 이번 달(2012-02) 마이크로소프트웨어 잡지에서 소개된 "EasyHook"이 괜찮은 후보가 될 것 같습니다.
EasyHook - The reinvention of Windows API Hooking
; http://easyhook.codeplex.com/
; https://github.com/EasyHook/EasyHook/
LGPL 라이선스이기 때문에 상용에도 쓸 수 있고 DLL로 링크해서 사용하면 아무런 문제가 없습니다. 그러다 만약 버그가 있다면 소스 코드를 다운로드해 버그를 수정한 후 원 저작자에게 알리면 버그 패치된 버전이 나올 것이고, 다시 그 DLL을 링크해서 사용하면 됩니다.
'마이크로소프트웨어'에 기사를 쓴 '권용휘' 님은 "Powered by EasyHook" 로고를 반드시 포함해야 한다고 했는데... 약간 오해가 있지 않나 싶습니다. 원 저작자는 이에 대해 다음과 같이 설명하고 있는데요.
I don't remember if this is covered by the LGPL but I would appreciate some kind of "Powered By" Banner/Icon/Notice. You will find an example at the bottom of this page.
그래줬으면 감사하다는 것이지, 강제 사항은 아닙니다. 사실, 이걸 강제 사항으로 넣으면 또 이상할 수도 있는데요. 가령 예를 들어서, UI가 없는 Library 개발자라면 쓰지 말라는 것이나 다름없으니까요. 게다가 이렇게 라이브러리를 공개한 경우 '감시' 수단이 현실적으로 적절하지 않습니다. 아래의 Q&A에서 원 저작자는,
Licensing question
; http://easyhook.codeplex.com/discussions/236073
you just directly link with the c files it seems fine to me. But it is still a breach with the LGPL but I wouldn't mind in this particular case, as long as the product you are creating is not something like EasyHook.
소스 코드를 가져다 쓰는 것 조차도, EasyHook과 같은 라이브러리를 만들려는 것이 아니라면 자신은 괜찮다는 입장을 취하고 있습니다. (물론, 공식 문서화된 답변은 아니므로 이런 내용에 기대어 오버 해석하는 것은 좋지 않습니다. ^^)
어쨌든, "Powered by" 로고에 대해서는 제가 질문을 올려놨는데 어떤 답이 오는지 한번 지켜보는 것도 재미있을 것 같습니다. ^^
About "Powered By" logo.
; http://easyhook.codeplex.com/discussions/330548
그건 그렇고, 이제 라이브러리를 사용해 봐야겠지요. ^^
오늘 날짜 기준으로 안정화된 버전이라고 알려진 2.6을 다운로드/압축을 풀어놓고 자신의 C/C++ 프로젝트에서 EasyHook32.dll(또는 EasyHook64.dll)을 링크한 후, 기본 소스를 다음과 같이 작성할 수 있습니다.
#include "stdafx.h"
#include "Win32Wrapper.h"
#include "..\Libraries\easyhook.h"
#if defined(_AMD64_)
#pragma comment(lib, "..\\Libraries\\EasyHook64.lib")
#else
#pragma comment(lib, "..\\Libraries\\EasyHook32.lib")
#endif
WIN32WRAPPER_API void fnWin32Wrapper_Hook(void)
{
...
}
WIN32WRAPPER_API void fnWin32Wrapper_Unhook(void)
{
...
}
그럼,
지난번에 했던 CoGetClassObject 후킹을 EasyHook 버전으로 한번 바꿔볼까요? ^^ 다행히 "마이크로소프트웨어" 잡지에 실린 기사에 자세한 설명이 되어 있어서 쉽게 다음과 같이 포팅을 할 수 있었습니다.
DWORD ACLEntries_CoGetClassObject[1] = { (DWORD)-1 };
HOOK_TRACE_INFO g_h_CoGetClassObject= {NULL};
HRESULT WINAPI New_CoGetClassObject(REFCLSID rclsid, DWORD dwClsContext, COSERVERINFO *pServerInfo, REFIID riid, LPVOID *ppv)
{
if (CanBeActivated(rclsid) == FALSE)
{
return REGDB_E_CLASSNOTREG;
}
return CoGetClassObject(rclsid, dwClsContext, pServerInfo, riid, ppv);
}
WIN32WRAPPER_API void fnWin32Wrapper_Hook(void)
{
HMODULE hModule = ::LoadLibrary(L"Ole32.dll");
FARPROC farProc = ::GetProcAddress(hModule, "CoGetClassObject");
if (farProc == NULL)
{
return;
}
LhInstallHook(farProc, New_CoGetClassObject,
NULL, &g_h_CoGetClassObject);
LhSetExclusiveACL(ACLEntries_CoGetClassObject, 1, &g_h_CoGetClassObject);
}
WIN32WRAPPER_API void fnWin32Wrapper_Unhook(void)
{
LhUninstallAllHooks();
}
이전과 다른 점이 있다면, 소스 코드가 훨씬 더 간결해졌다는 점을 들 수 있겠고, 무엇보다도 x64/x86 모두 동작한다는 장점이 있습니다. (이렇게 잘 만들어진 라이브러리를 가져다 쓰는 것은 너무너무 행복한 일입니다. ^^)
이번에도 역시, 첨부한 소스 코드는 "닷넷"에서 C/C++ DLL 함수를 불러서 처리하게 했고 다음과 같이 Hook/Unhook 버튼을 두어 실행 시에 테스트가 가능하도록 했습니다.
첨부된 파일은 위의 코드를 포함한 예제 프로젝트입니다.
[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]