关于网站建设的论坛,wordpress添加侧栏广告,如何自己开发手机app,注册地址最简单的Hook
刚开始学的时候#xff0c;用的hook都是最基础的5字节hook#xff0c;也不会使用hook框架#xff0c;hook流程如下#xff1a;
构建一个jmp指令跳转到你的函数(函数需定义为裸函数)保存被hook地址的至少5字节机器码#xff0c;然后写入构建的jmp指令接着在…最简单的Hook
刚开始学的时候用的hook都是最基础的5字节hook也不会使用hook框架hook流程如下
构建一个jmp指令跳转到你的函数(函数需定义为裸函数)保存被hook地址的至少5字节机器码然后写入构建的jmp指令接着在你的代码里做你想要的操作以内联汇编的形式执行被hook地址5字节机器码对应的汇编指令跳转回被hook的地址下一条指令
这样操作比较繁琐每次hook都要定义一堆东西还得自己补充hook地址被修改的汇编指令最重要的是这种hook无法扩展到Python里使用。
加入反汇编和汇编引擎
csdn有一篇文章说了可以通过引入汇编和反汇编引擎来去掉第二步和第四步也就是不需要关心hook地址的汇编是什么。
文章中用的汇编引擎是XEDParse我试了下用vs2017编译不通过看了文档和issue必须得使用vs2013及以下的版本才能编译成功所以就放弃了改成使用keystone。想编译keystone和Beaengine可以看另一篇文章keystone和beaengine的编译
我也对文章中的代码进行了一些小优化这也是为了方便引入到Python中使用。
开始写代码
下面的说明可能会啰嗦一些对每行代码都做了解释。你也可以去看c 源码也对每行代码做了注释。
定义一个hook函数, 参数有四个返回值是被修改的字节数
hookAddress 要hook的地址hookFunc: hook的回调函数hookOldCode保存被修改的字节hookOldSizehookOldCode的缓冲区大小
size_t HookAnyAddress(__in DWORD hookAddress, __in AnyHookFunc hookFunc, __out BYTE* hookOldCode, __in size_t hookOldSize)
AnyHookFunc的函数指针定义
typedef void(_stdcall * AnyHookFunc)(RegisterContext*);
RegisterContext结构体的定义
struct RegisterContext
{DWORD EFLAGS;DWORD EDI;DWORD ESI;DWORD EBP;DWORD ESP;DWORD EBX;DWORD EDX;DWORD ECX;DWORD EAX;
};
首先定义一个内存的shellcode用来存放裸函数里的指令
BYTE ShellCode[0x40] {0x60, //pushad0x9C, //pushfd0x54, //push esp0xB8, 0x90, 0x90, 0x90, 0x90, //mov eax,hookFunc0xFF, 0xD0, //call eax0x9D, //popfd0x61, //popad
};
这里的4个0x90是存放hook回调函数的地址接着写入回调函数地址
memcpy(ShellCode[0x4], hookFunc, 4);
分配一块可执行的内存, 用于存放这段shellcode
DWORD shellcodeMemAddr (DWORD)VirtualAlloc(NULL, 0x100, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if (shellcodeMemAddr 0) {return 0;
}
因为shellcode已经写了0xC个字节所以后面的指令从0xC开始写
DWORD shellcodeMemAddrStart shellcodeMemAddr 0xC;
定义反汇编引擎和汇编引擎keystone也是老朋友了之前x86发消息的时候就已经用过了
// 定义反汇编引擎
DISASM MyDisasm;
memset(MyDisasm, 0, sizeof(DISASM));
MyDisasm.EIP (UIntPtr)hookAddress;
// 设置为32位x86平台
MyDisasm.Archi 32;
MyDisasm.Options PrefixedNumeral ShowSegmentRegs;
// PrefixedNumeral: 数值前加0xShowSegmentRegs: 显示段寄存器的值// 定义汇编引擎
ks_engine *ks;
ks_err err ks_open(KS_ARCH_X86, KS_MODE_32, ks);
if (err ! KS_ERR_OK) {return 0;
}
开始计算hook地址的指令并将指令写到shellcodeMemAddr里
// 保存返回hook地址下一条指令的地址
DWORD hookRetAddr 0;
// 记录被修改的指令长度
size_t hookSize 0;
// 开始循环反汇编直到满足5个字节
while (true) {// 开始反汇编每次反汇编一条指令返回这条指令的长度int DisasmCodeSize Disasm(MyDisasm);if (DisasmCodeSize 1) {return 0;}// hook的地址不能包含ret指令if (MyDisasm.Instruction.BranchType RetType){return 0;}hookSize DisasmCodeSize;// 保存汇编指令条数size_t encodingCount;// 保存汇编后的指令unsigned char *encodingCode;// 保存汇编后的指令长度size_t encodingSize;// 利用keystone将反汇编后的指令再转为机器码这么操作可以自动处理相对地址// 前三个参数是输入参数第二个参数是反汇编会的指令第三个参数是指令所在的内存地址(用于计算相对偏移)// 后三个参数为输出参数见定义处if (ks_asm(ks, MyDisasm.CompleteInstr, shellcodeMemAddrStart, encodingCode, encodingSize, encodingCount) ! KS_ERR_OK) {return 0;}// 将汇编后的机器码写到shellcodememcpy(ShellCode[shellcodeMemAddrStart - shellcodeMemAddr], encodingCode, encodingSize);ks_free(encodingCode);// 注意: 反汇编和汇编的机器码和长度可能是不一样的shellcodeMemAddrStart encodingSize;// 开始下一条指令的反汇编和汇编MyDisasm.EIP DisasmCodeSize;// 如果指令达到5个字节就结束if (hookSize 5){hookRetAddr MyDisasm.EIP;break;}
}
ks_close(ks);
开始构建跳转指令跳转回hook地址的下一条指令的位置
// 保存原始内存属性值
DWORD dwOldProtect 0;
// 给hook的地址赋予可写权限
BOOL bRet VirtualProtect((LPVOID)hookAddress, 0x20, PAGE_EXECUTE_READWRITE, dwOldProtect);
if (!bRet) {return 0;
}
// 保存被覆盖的机器码
memcpy(hookOldCode, (LPVOID)hookAddress, hookSize);
// 构建跳转指令
BYTE pushRetCode[6] {0x68, 0x90, 0x90, 0x90, 0x90, // push hookRetAddr0xC3 // ret
};
memcpy(pushRetCode[1], hookRetAddr, 4);
将构架的跳转指令写入到shellcode里并将shellcode写到申请的内存shellcodeMemAddr里
memcpy(ShellCode[shellcodeMemAddrStart - shellcodeMemAddr], pushRetCode, sizeof(pushRetCode));
// 将shellcode写入申请的内存地址
memcpy((LPVOID)shellcodeMemAddr, ShellCode, sizeof(ShellCode));
开始修改hook地址的机器码跳转到申请的内存地址shellcodeMemAddr
BYTE jmpCode[5] { 0xE9, 0xFF, 0xFF, 0xFF, 0xFF };
*(DWORD*)(jmpCode 1) shellcodeMemAddr - (DWORD)hookAddress - 5;
memcpy((LPVOID)hookAddress, jmpCode, 5);
BYTE nopCode[2] { 0x90,0x90};
如果被修改的指令超过了五个字节其他字节用nop填充
if (hookSize 5) {memset((LPVOID)(hookAddress 5), 0x90, hookSize - 5);
}
最后还原内存属性返回被修改的指令长度
VirtualProtect((LPVOID)hookAddress, 0x20, dwOldProtect, dwOldProtect);
return hookSize;
取消hook只需要将保存的机器码还原
DWORD UnHookAnyAddress(__in DWORD hookAddress, __in BYTE* hookOldCode, __in size_t hookOldSize) {DWORD dwOldProtect 0;VirtualProtect((LPVOID)hookAddress, 0x20, PAGE_EXECUTE_READWRITE, dwOldProtect);memcpy((LPVOID)hookAddress, hookOldCode, hookOldSize);VirtualProtect((LPVOID)hookAddress, 0x20, dwOldProtect, dwOldProtect);return 0;
}
Python中使用
将这个编译成dll就能在Python里加载了不过dll只能用于hook当前进程这是因为函数不能跨进程调用你创建的回调函数其他进程无法调用。
解决这个问题也很简单可以在目标进程申请一块可执行的内存用汇编引擎和反汇编引擎将回调函数写到这块内存里。
不过我的使用场景是将Python注入到了进程Python作为线程在目标进程里运行不用这么繁琐。使用案例看另一篇文章封装32位和64位hook框架实战hook日志
参考
https://blog.csdn.net/sunflover454/article/details/49029615