本帖最后由 chomosuke 于 2020-4-23 19:08 编辑
声明:以下资料仅供交流学习,严禁将以下内容用于商业或者非法用途,否则一切后果用户自负
以下为不完整的C++ DLL伪代码,仅供交流学习:
Inline hook可以用于程序侦错用途,因法律问题请只用于侦错自己开发的软件
[C++] 纯文本查看 复制代码
#include "pch.h"
// 出处:http://www.cplusplus.com/forum/general/202725/#msg964447
std::vector<const void*> scan_memory(void* address_low, const std::size_t& nbytes, const std::vector<BYTE>& bytes_to_find) {
std::vector<const void*> addresses_found;
// all readable pages: adjust this as required
const DWORD pmask{ PAGE_READONLY | PAGE_READWRITE | PAGE_WRITECOPY | PAGE_EXECUTE | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY };
::MEMORY_BASIC_INFORMATION mbi{};
BYTE* address{ static_cast<BYTE*>(address_low) };
BYTE* address_high{ address + nbytes };
while (address < address_high && ::VirtualQuery(address, std::addressof(mbi), sizeof(mbi))) {
// committed memory, readable, wont raise exception guard page
//if ((mbi.State == MEM_COMMIT) && (mbi.Protect | pmask) && !(mbi.Protect & PAGE_GUARD)) {
if ((mbi.State == MEM_COMMIT) && (mbi.Protect & pmask) && !(mbi.Protect & PAGE_GUARD)) {
const BYTE* begin{ static_cast<const BYTE*>(mbi.BaseAddress) };
const BYTE* end{ begin + mbi.RegionSize };
const BYTE* found{ std::search(begin, end, bytes_to_find.begin(), bytes_to_find.end()) };
while (found != end) {
addresses_found.push_back(found);
found = std::search(found + 1, end, bytes_to_find.begin(), bytes_to_find.end());
}
}
address += mbi.RegionSize;
mbi = {};
}
return addresses_found;
}
std::vector<const void*> scan_memory(const std::vector<BYTE>& bytes_to_find) {
auto base{ ::GetModuleHandleW(NULL) }; // NULL为本体exe,如有需要可以参考出处写法,理论上可以查找其他dll或exe的内存部分
if (base == nullptr) return {};
MODULEINFO minfo{};
::GetModuleInformation(GetCurrentProcess(), base, std::addressof(minfo), sizeof(minfo));
return scan_memory(base, minfo.SizeOfImage, bytes_to_find);
}
VOID hook_util(PVOID addressToHook, PVOID functionToInject, UINT length) {
if (length < 5) return; // 至少要五字元才能放一个JMP
// 内存读写权限属性
DWORD original_protection;
DWORD placeholder;
// 内存读写提权
while (VirtualProtect(addressToHook, length, PAGE_EXECUTE_READWRITE, &original_protection) == 0);
memset(addressToHook, 0x90, length); // 多余指令设定为无效果的NOP,减少运行出错机会
// 于指定addressToHook位置放置JMP运算元
*reinterpret_cast<BYTE*>(addressToHook) = 0xE9;
// 于指定addressToHook位置放置JMP运算子
*reinterpret_cast<DWORD_PTR*>(reinterpret_cast<DWORD_PTR>(addressToHook) + 1) = reinterpret_cast<DWORD_PTR>(functionToInject) - reinterpret_cast<DWORD_PTR>(addressToHook) - 5;
// 内存读写降权
while (VirtualProtect(addressToHook, length, original_protection, &placeholder) == 0);
}
/*
结构体例子:
FFFFFF00 80C30000 00CE0000 00010000
00F00FF0 12F0EC12
提示:注意位元組順序!
dumpSize为0x100 (256字元)
dumpBuffer为0x12ECF012
*/
struct myStruct {
UINT32 padding_0; // 4字元填充(unsigned int 32-bit = 32/8->4字元),用于对位
UINT64 padding_1; // 8字元填充
DWORD dumpSize;
UINT32 padding_2; // 4字元填充
LPCVOID dumpBuffer;
};
myStruct* param;
DWORD_PTR param_reference;
std::wstring my_string;
VOID myFunc() {
if (!param) return; // 空指针检测
HANDLE& myHandle{ reinterpret_cast<HANDLE&>(param_reference) }; // 直接还原HANDLE reference / 引用
// 调用param里的参数,写入文件?(WriteFile)
if (!param->dumpBuffer) return;
if (param->dumpSize <= 0) return;
}
DWORD_PTR original_func;
/*
_asm部分解释:
先保存原有寄存器跟标志位到堆栈,运行你的代码后还原(push/pop ad/fd)
mov会拷贝eax到param值中,以防止后面的void myFunc()意外改动
对于有些x86汇编禁止mov的操作,可以先推进堆栈再立即推出
call会调用你写的void myFunc()
然后可以在void myFunc()里调用param(原有eax)的数值、指针或引用
最后的jmp跳回原来的程序位置,继续运行
*/
VOID __declspec(naked) wrapper() {
_asm {
/* 这里补上原本有的指令 */
pushad
pushfd
mov param, eax
push dword ptr ss : [ecx + 0x30]
pop param_reference
push dword ptr ss : [eax + 0x60]
pop my_string
call dword ptr[myFunc]
popfd
popad
/* 或者这里补上原本有的指令 */
jmp dword ptr[original_func]
}
}
VOID hook() {
const std::vector<BYTE> hook_signature{ 0x6A, 0x00, 0x50, 0x10, 0xFF, 0x75, 0x0C, 0xFF, 0x08 }; // 需要hook的特征码
const DWORD_PTR hook_address{ reinterpret_cast<DWORD_PTR>(scan_memory(hook_signature).at(0)) }; // 需要hook的特征码位置,.at(0)会提取第一个记录
const UINT hook_length{ 0x5 }; // 必须是一个或者几个完整的指令的长度,至少要5字元才能放1个jmp,并在_asm部分补上此处被覆盖的指令。
original_func = hook_address + hook_length;
hook_util(reinterpret_cast<PVOID>(hook_address), wrapper, hook_length);
}
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) {
switch (ul_reason_for_call) {
case DLL_PROCESS_ATTACH:
hook(); // 载入dll时hook
break;
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
|