PolyHook 使用介绍(Code Project)
首先下载 GitHub 上的源码到本地,编译一遍,我这里用的是 vs2019。编译完成后我们找到以下几个文件/文件夹:


之后我们新建一个工程(我的工程名叫 Test_Console),把 Capstone 文件夹放在项目根目录下:

在源目录下创建两个文件夹 Include、Import,分别存放我们需要的头文件和库:



然后来到 Test_Console 的项目属性,改一下 VC++目录中的 包含目录 和 库目录
注意这里的 库目录在编译成 x86 和 x64 时要选择的文件夹是不一样的。
最后添加如下代码到 .cpp 内:
#include <iostream>
#include "CatchUnitTest.h"
#include "PolyHook.hpp"
#pragma region 1
typedef int(__stdcall* tMessageBoxA)(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType);
tMessageBoxA oMessageBoxA;
int __stdcall hkMessageBoxA(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType)
{
return oMessageBoxA(hWnd, "Hooked", lpCaption, uType);
}
#pragma endregion
#pragma region 2
class VirtualTest
{
public:
virtual int NoParamVirt()
{
return 4;
}
virtual int NoParamVirt2()
{
return 1;
}
};
typedef int(__thiscall* tVirtNoParams)(DWORD_PTR pThis);
tVirtNoParams oVirtNoParams;
int __fastcall hkVirtNoParams(DWORD_PTR pThis)
{
return 0;
}
#pragma endregion
#pragma region 3
#pragma endregion
#pragma region 4
#pragma endregion
#pragma region 5
typedef DWORD(__stdcall* tGetCurrentThreadId)();
tGetCurrentThreadId oGetCurrentThreadID;
DWORD __stdcall hkGetCurrentThreadId()
{
return 123456;
}
#pragma endregion
#pragma region 6
typedef int(__stdcall* tVEH)(int intparam);
tVEH oVEHTest;
int __stdcall VEHTest()
{
return 3;
}
std::shared_ptr<PLH::VEHHook> VEHHook_Ex;
int __stdcall hkVEHTest()
{
return 1;
}
#pragma endregion
int main()
{
std::shared_ptr<PLH::Detour> Detour_Ex(new PLH::Detour);
Detour_Ex->SetupHook((BYTE*)&MessageBoxA, (BYTE*)&hkMessageBoxA);
MessageBoxA(NULL, "", "", NULL);
Detour_Ex->Hook();
oMessageBoxA = Detour_Ex->GetOriginal<tMessageBoxA>();
MessageBoxA(NULL, "", "", NULL);
Detour_Ex->UnHook();
MessageBoxA(NULL, "", "", NULL);
std::shared_ptr<VirtualTest> ClassToHook(new VirtualTest);
std::shared_ptr<PLH::VFuncDetour> VFuncDetour_Ex(new PLH::VFuncDetour);
VFuncDetour_Ex->SetupHook(*(BYTE***)ClassToHook.get(), 0, (BYTE*)&hkVirtNoParams);
std::cout << ClassToHook->NoParamVirt() << "," << ClassToHook->NoParamVirt2() << std::endl;
VFuncDetour_Ex->Hook();
std::cout << ClassToHook->NoParamVirt() << "," << ClassToHook->NoParamVirt2() << std::endl;
VFuncDetour_Ex->UnHook();
std::cout << ClassToHook->NoParamVirt() << "," << ClassToHook->NoParamVirt2() << std::endl;
std::shared_ptr<PLH::VTableSwap> VTableSwap_Ex(new PLH::VTableSwap);
VTableSwap_Ex->SetupHook((BYTE*)ClassToHook.get(), 0, (BYTE*)&hkVirtNoParams);
std::cout << ClassToHook->NoParamVirt() << "," << ClassToHook->NoParamVirt2() << std::endl;
VTableSwap_Ex->Hook();
std::cout << ClassToHook->NoParamVirt() << "," << ClassToHook->NoParamVirt2() << std::endl;
VTableSwap_Ex->UnHook();
std::cout << ClassToHook->NoParamVirt() << "," << ClassToHook->NoParamVirt2() << std::endl;
std::shared_ptr<PLH::VFuncSwap> VFuncSwap_Ex(new PLH::VFuncSwap);
VFuncSwap_Ex->SetupHook(*(BYTE***)ClassToHook.get(), 0, (BYTE*)&hkVirtNoParams);
std::cout << ClassToHook->NoParamVirt() << "," << ClassToHook->NoParamVirt2() << std::endl;
VFuncSwap_Ex->Hook();
std::cout << ClassToHook->NoParamVirt() << "," << ClassToHook->NoParamVirt2() << std::endl;
VFuncSwap_Ex->UnHook();
std::cout << ClassToHook->NoParamVirt() << "," << ClassToHook->NoParamVirt2() << std::endl;
std::shared_ptr<PLH::IATHook> IATHook_Ex(new PLH::IATHook);
IATHook_Ex->SetupHook("kernel32.dll", "GetCurrentThreadId", (BYTE*)&hkGetCurrentThreadId);
std::cout << "GetCurrentThreadId = " << GetCurrentThreadId() << std::endl;
IATHook_Ex->Hook();
std::cout << "GetCurrentThreadId = " << GetCurrentThreadId() << std::endl;
IATHook_Ex->UnHook();
std::cout << "GetCurrentThreadId = " << GetCurrentThreadId() << std::endl;
getchar();
return 0;
}
其中包含了一些测试代码,用的时候酌情删除便是。
PolyHook v2 编译
既然出了第二版,那么再用第一版总感觉有点 Low ????,所以花点时间看下第二版相对第一版的改动。
编译方面 v2 改用了 cmake 方式编译,首先打开 git ,输入命令:git clone --recursive https://github.com.cnpmjs.org/stevemk14ebr/PolyHook_2_0.git ,注意这里的网址为什么不是 github.com 而是 github.com.cnpmjs.org 这是因为第二种方式 clone 代码的速度会 快很多 ,具体原因感兴趣的朋友可以自己查阅资料,在这里不再深究。
执行完成之后,我出现了如下错误:

polyhook 框架用到了反汇编引擎 capstone ,而不知道什么原因我们下载 capstone 的时候出错了。这时候我们完全可以再某个地方新建一个目录,再打开一个 git 控制台,输入:git clone https://github.com.cnpmjs.org/aquynh/capstone.git
下载完成后,把内容拷贝到 polyhook 的 capstone 文件夹里:

然后来到我们最开始打开的那个控制台,执行:cd PolyHook_2_0

再输入:git submodule update --init --recursive 检查一下更新,这个项目目前作者仍在持续维护,所以保险起见检查更新的操作是很有必要的:
再输入 cmake -B"./_build" -DCMAKE_INSTALL_PREFIX="./_install/" -DPOLYHOOK_BUILD_SHARED_LIB=ON -G "Visual Studio 16 2019" -A x64 ,使用 cmake 生成 .sln 文件。
如果要编译为 32位项目,只需要把末尾的 -A x64 改为 -A Win32 即可

之后我们可以选择打开 _build 文件夹下的 .sln 来自己编译(用 vs):


或者使用命令编译:cmake --build "./_build" --config Release --target INSTALL

编译完成之后,想要的东西都在 _install 文件夹内:

PolyHook 使用 Demo
#include <iostream>
#include <cstdarg>
#include <memory>
#include <Catch.hpp>
#pragma region Detour
#include "polyhook2/CapstoneDisassembler.hpp"
#include "polyhook2/Detour/x64Detour.hpp"
uint64_t u64_hMessageBoxA = NULL;
NOINLINE int __cdecl hook_MessageBoxA(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType) {
return PLH::FnCast(u64_hMessageBoxA, &MessageBoxA)(NULL, "hook MessageBoxA", "LYSM", NULL);
}
#pragma endregion
#pragma region BreakPointHook
#include "polyhook2/Exceptions/BreakPointHook.hpp"
NOINLINE int hookMe() {
volatile int i = 0;
i += 1;
i += 2;
return i;
}
std::shared_ptr<PLH::BreakPointHook> bpHook;
NOINLINE int hookMeCallback() {
std::cout << "调用 hookMe 前先进入回调函数" << std::endl;
return hookMe();
}
#pragma endregion
#pragma region HWBreakPointHook
#include "polyhook2/Exceptions/HWBreakPointHook.hpp"
NOINLINE int hookMeHWBP() {
volatile int i = 0;
i += 1;
i += 2;
return i;
}
std::shared_ptr<PLH::HWBreakPointHook> hwBpHook;
NOINLINE int hookMeCallbackHWBP() {
std::cout << "调用 hookMeHWBP 前先进入回调函数" << std::endl;
return hookMeHWBP();
}
#pragma endregion
#pragma region IatHook
#include "polyhook2/PE/IatHook.hpp"
uint64_t oGetCurrentThreadID;
NOINLINE DWORD __stdcall hkGetCurrentThreadId() {
return 0;
}
#pragma endregion
#pragma region EatHook
#include "polyhook2/PE/EatHook.hpp"
typedef void(*tEatTestExport)();
uint64_t oEatTestExport;
extern "C" __declspec(dllexport) NOINLINE void EatTestExport() {
std::cout << "EatTestExport" << std::endl;
}
NOINLINE void hkEatTestExport() {
std::cout << "hkEatTestExport" << std::endl;
}
#pragma endregion
#pragma region MemProtector
#include "polyhook2/MemProtector.hpp"
#pragma endregion
#pragma region VTableSwapHook
#include "polyhook2/Virtuals/VTableSwapHook.hpp"
class VirtualTest {
public:
virtual ~VirtualTest() {}
virtual int NoParamVirt() {
return 4;
}
virtual int NoParamVirt2() {
return 7;
}
};
typedef int(__thiscall* tVirtNoParams)(uintptr_t pThis);
PLH::VFuncMap origVFuncs;
NOINLINE int __fastcall hkVirtNoParams(uintptr_t pThis) {
std::cout << "执行虚函数之前先执行 hkVirtNoParams" << std::endl;
return ((tVirtNoParams)origVFuncs.at(1))(pThis);
}
#pragma endregion
#pragma region VFuncSwapHook
#include "polyhook2/Virtuals/VFuncSwapHook.hpp"
class VirtualTest2 {
public:
virtual ~VirtualTest2() {}
virtual int NoParamVirt() {
return 4;
}
virtual int NoParamVirt2() {
return 7;
}
};
typedef int(__thiscall* tVirtNoParams)(uintptr_t pThis);
PLH::VFuncMap origVFuncs2;
NOINLINE int __fastcall hkVirtNoParams2(uintptr_t pThis) {
std::cout << "执行虚函数之前先执行 hkVirtNoParams2" << std::endl;
return ((tVirtNoParams)origVFuncs2.at(1))(pThis);
}
#pragma endregion
int main()
{
getchar();
return 0;
}
PolyHook_2 将 dll 项目改为 lib 项目
修改 PolyHook_2 的项目属性,如下:


重新编译,会生成 lib 而不是 dll。 但此时如果直接使用这个 lib 会在 lib 里报错:无法解析的外部符号 _cs_open … _cs_free 等等,全局搜索了一下,这个引用是在 PolyHook_2 的 cpp 里,其中找不到的这些外部符号来自 Capstoon ,所以只需要在 PolyHook_2 的项目里添加对 Capstoon 的 lib 的引用即可(附加库目录、附加依赖项)