code patch hook
code patch hook
今天在逆向分析一个程序的时候接触到了code patch hook,其实这个hook技术我在接触逆向之初就已经知道了,但是今天遇到的有点特殊
code patch hook
原理是通过修改api的前5个字节,jmp到自己的函数
当用户调用api时,会跳转到自己的函数
- 脱钩
- 调用原始api
- 脱钩
为正常调用原API,需要先1. 脱钩
(若不脱钩
,2. 调用原始Api
就会陷入无限循环)。然后再钩取函数,在退出函数之前再次3.挂钩
,使之进入钩取状态。也就是在每次调用原api时都必须反复进行脱钩
/挂钩
操作,不仅会造成整体性能低下,更严重的是在多线程环境运行下产生错误,这是由脱钩
/挂钩
操作要对原API的前5个字节进行修改(覆写)引起的。
一种特殊的code patch hook
如何避免呢,其实只要避免反复进行脱钩
/挂钩
操作就行了
- 获取
原函数前
面几个指令的总长度
,一直到字节数>5为止 - 创建
总长度
+5的内存pMem
- 将原函数前面
总长度
的指令复制到内存中pMem的前总长度
字节中 - 计算
pMem
+总长度
+ 5 到原函数 +总长度
的偏移,并将 E9 XXXXXXXX(偏移
)复制到pMem
+总长度
的位置 - 计算
原函数
+ 5 到自己的函数
的偏移,并将E9 XXXXXXXX(偏移
)复制到原函数
的前面5个字节 - 将
pMem
返回并保存起来,这个就是调用原函数
正常功能的函数地址
这样用户调用API时,会转到我们的函数,而我们调用原函数时只需调用pMem
即可,避免了反复进行脱钩
/挂钩
操作。
具体实现
下面是我的实现代码,其实还不完善,第一点我并没有计算原函数前面几个字节的长度,而是默认为5,因为Windows API的前面几个指令的字节长度刚好是5,所以下面的代码只能用于x86 Windows api
#include <Windows.h>
#include <iostream>
using namespace std;
typedef DWORD(__stdcall *MyGetFileAttributes)(LPCWSTR lpFileName);
MyGetFileAttributes originMyGetFileAttributes;
DWORD __stdcall __GetFileAttributes(LPCWSTR lpFileName) {
cout << "__GetFileAttributes" << endl;
return originMyGetFileAttributes(lpFileName);;
}
int main() {
originMyGetFileAttributes = hookCodePatch<MyGetFileAttributes>(::GetFileAttributes, __GetFileAttributes);
::GetFileAttributes(LR"(C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Accessories\Notepad.lnk)");
}
template<typename T>
T hookCodePatch(T originFun, T hookFuntion)
{
const int patchSize = 10; // 假设需要 patch 的字节数
const int pageSize = 5;
unsigned char* patchedMemory = new unsigned char[patchSize];
unsigned char* originFunPtr = reinterpret_cast<unsigned char*>(originFun);
// 如有必要, 将原函数转到实际的函数地址
DWORD oldProtect;
VirtualProtect(patchedMemory, patchSize, PAGE_EXECUTE_READWRITE, &oldProtect);
if (originFunPtr[0] == 0xFF &&originFunPtr[1] == 0x25) {
originFunPtr = reinterpret_cast<unsigned char*>(**(int **)(originFunPtr + 2));
}
if (originFunPtr[0] == 0xEB ) {
originFunPtr = originFunPtr + originFunPtr[1] + 2;
if (originFunPtr[0] == 0xFF && originFunPtr[1] == 0x25) {
originFunPtr = reinterpret_cast<unsigned char*>(* (int*)(originFunPtr + 2));
}
if (originFunPtr[0] == 0xE9) {
originFunPtr = originFunPtr +*(int*)(originFunPtr + 1);
}
}
// 复制 originFun 的前五个字节到 patchedMemory
memcpy(patchedMemory, originFunPtr, 5);
intptr_t hookOffset = (reinterpret_cast<intptr_t>(originFunPtr) + 5) - reinterpret_cast<intptr_t>(patchedMemory + 0xA);
// 修改 patchedMemory 的后五个字节为跳转到 originFun + 5 的指令
patchedMemory[5] = 0xE9; // x86 跳转指令的操作码
memcpy(patchedMemory + 6, &hookOffset, sizeof(hookOffset));
// 计算跳转偏移量
hookOffset = reinterpret_cast<intptr_t>(hookFuntion) - (reinterpret_cast<intptr_t>(originFunPtr) + 5);
// 修改 originFun 的前五个字节为跳转到 hookFunction 的指令
VirtualProtect(originFunPtr, pageSize, PAGE_EXECUTE_READWRITE, &oldProtect);
originFunPtr[0] = 0xE9; // x86 跳转指令的操作码
memcpy(originFunPtr + 1, &hookOffset, sizeof(hookOffset));
VirtualProtect(originFunPtr, pageSize, oldProtect, &oldProtect);
return (T)patchedMemory;
}