前言

1
2
3
4
5
6
7
#include <tlhelp32.h>
#include <psapi.h>
#include <windows.h>
#include <vector>
#include <algorithm>
#include <ppl.h>
#include <immintrin.h>

打开进程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
std::pair<HANDLE, DWORD> OpenTargetProcess(const wchar_t* targetProcess) {
DWORD pid = 0;
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (hSnapshot == INVALID_HANDLE_VALUE) {
return { nullptr, 0 }; // 返回空句柄和 0 PID
}

PROCESSENTRY32 pe32 = { sizeof(PROCESSENTRY32) };
for (BOOL success = Process32First(hSnapshot, &pe32); success; success = Process32Next(hSnapshot, &pe32)) {
if (_wcsicmp(pe32.szExeFile, targetProcess) == 0) {
pid = pe32.th32ProcessID;
break;
}
}

CloseHandle(hSnapshot); // 关闭快照句柄

// 如果找到了 PID,则尝试打开进程
HANDLE hProcess = nullptr;
if (pid != 0) {
hProcess = OpenProcess(
PROCESS_QUERY_INFORMATION |
PROCESS_VM_OPERATION |
PROCESS_VM_READ |
PROCESS_VM_WRITE,
FALSE,
pid
);
}

return { hProcess, pid }; // 返回句柄和 PID
}
1
2
3
4
const wchar_t* targetProcess = L"gamemd.exe";
std::pair<HANDLE, DWORD> result = OpenTargetProcess(targetProcess);
HANDLE hProcess = result.first;
DWORD pid = result.second;

偏移数组找地址

1
2
3
4
5
6
7
8
9
10
11
12
13
LPVOID Widget::GetFinalAddress(
HANDLE hProcess,
LPVOID pBaseAddress,
const DWORD* offsets,
int offsetCount)
{
DWORD currentAddress = (DWORD)pBaseAddress;
for (int i = 0; i < offsetCount - 1; i++) {
ReadProcessMemory(hProcess, (LPVOID)(currentAddress + offsets[i]), &currentAddress, 4, NULL);
}
currentAddress = currentAddress + offsets[offsetCount - 1];
return (LPVOID)currentAddress;
}

查找字节数组

逐字节查找,单线程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
LPVOID FindPatternInAllocatedMemory(
HANDLE hProcess,
LPVOID pBaseAddress,
SIZE_T nMemorySize,
const BYTE* pPattern, // 支持通配符的字节模式
SIZE_T nPatternSize,
DWORD nSearchStep, // 默认逐字节搜索
BYTE wildcard // 自定义通配符标识(默认0xCC)
)
{
// 参数有效性检查
if (!hProcess || !pBaseAddress ||
nMemorySize < nPatternSize ||
nPatternSize == 0 ||
nSearchStep == 0)
{
return nullptr;
}

BYTE* buffer = new BYTE[nPatternSize];
const BYTE* endAddress = static_cast<BYTE*>(pBaseAddress) + nMemorySize - nPatternSize;

for (BYTE* current = static_cast<BYTE*>(pBaseAddress);
current <= endAddress;
current += nSearchStep)
{
SIZE_T bytesRead;
if (!ReadProcessMemory(hProcess, current, buffer, nPatternSize, &bytesRead))
continue;

if (bytesRead != nPatternSize)
continue;

bool found = true;
for (SIZE_T i = 0; i < nPatternSize; ++i) {
// 通配符位置跳过匹配检查
if (pPattern[i] == wildcard)
continue;

if (buffer[i] != pPattern[i]) {
found = false;
break;
}
}

if (found) {
delete[] buffer;
return current;
}
}

delete[] buffer;
return nullptr;
}

利用Boyer-Moore算法,多线程查找

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
// Boyer-Moore算法预处理结构
struct BMContext {
size_t badChar[256];
size_t goodSuffix;
const BYTE* pattern;
size_t patternLen;
};

BMContext PreprocessBM(const BYTE* pattern, size_t len) {
BMContext ctx{};
ctx.pattern = pattern;
ctx.patternLen = len;

// 坏字符规则
for(size_t i=0; i<256; ++i) ctx.badChar[i] = len;
for(size_t i=0; i<len-1; ++i)
ctx.badChar[pattern[i]] = len - i -1;

// 好后缀规则(简化版)
ctx.goodSuffix = len;
for(size_t i=len-1; i>0; --i) {
if(pattern[i] == pattern[0]) {
ctx.goodSuffix = len - i -1;
break;
}
}
return ctx;
}

// AVX2加速内存块扫描
const BYTE* AVX2ScanBlock(const BYTE* data, size_t dataLen,
const BMContext& ctx) {
const size_t patternLen = ctx.patternLen;
const __m256i firstChar = _mm256_set1_epi8(ctx.pattern[0]);

for(size_t i=0; i<dataLen - patternLen; ) {
// 首字符向量化匹配
__m256i chunk = _mm256_loadu_si256(
reinterpret_cast<const __m256i*>(data + i));
__m256i cmp = _mm256_cmpeq_epi8(chunk, firstChar);
int mask = _mm256_movemask_epi8(cmp);

while(mask != 0) {
int pos = _tzcnt_u32(mask);
const BYTE* candidate = data + i + pos;

// Boyer-Moore完整匹配
bool match = true;
for(int j=patternLen-1; j>=0; --j) {
if(candidate[j] != ctx.pattern[j] &&
ctx.pattern[j] != 0xCC) { // 0xCC为通配符
match = false;
break;
}
}

if(match) return candidate;

mask = _blsr_u32(mask);
}

// 计算跳转步长
size_t shift = ctx.badChar[data[i + patternLen -1]];
i += (std::max)(shift, ctx.goodSuffix);
}
return nullptr;
}

// 分块并行扫描
LPVOID OptimizedScan(HANDLE hProcess,
const BYTE* pattern,
size_t patternLen) {
const size_t BLOCK_SIZE = 16 * 1024 * 1024; // 16MB块
BMContext ctx = PreprocessBM(pattern, patternLen);
LPVOID foundAddr = nullptr;

// 获取可读内存区域
auto regions = GetProcessMemoryRegions(hProcess);

concurrency::parallel_for_each(regions.begin(), regions.end(),
[&](const MemoryRegion& region) {
if(!IsReadable(region.protect)) return;

ULONG_PTR base = (ULONG_PTR)region.baseAddress;
ULONG_PTR end = base + region.regionSize;

for(ULONG_PTR addr = base; addr < end && !foundAddr;
addr += BLOCK_SIZE)
{
size_t readSize = (std::min)(BLOCK_SIZE, end - addr);
std::unique_ptr<BYTE[]> buffer(new BYTE[readSize]);

SIZE_T bytesRead;
if(ReadProcessMemory(hProcess, (LPCVOID)addr,
buffer.get(), readSize, &bytesRead))
{
if(const BYTE* p = AVX2ScanBlock(buffer.get(), bytesRead, ctx)) {
foundAddr = (LPVOID)(addr + (p - buffer.get()));
}
}
}
},
concurrency::static_partitioner() // 减少线程调度开销
);

return foundAddr;
}

硬编码修改

修改原来的硬编码

1
2
3
4
5
6
7
8
9
10
11
12
void ChangeOriginalCode(
HANDLE hProcess,
LPVOID TargetFunAddr,
BYTE* NewCode,
size_t NewCodeSize
)
{
DWORD OldProtect = 0;
VirtualProtectEx(hProcess, (LPVOID)TargetFunAddr, NewCodeSize, PAGE_READWRITE, &OldProtect);
WriteProcessMemory(hProcess, (LPVOID)TargetFunAddr, NewCode, NewCodeSize, NULL);
VirtualProtectEx(hProcess, (LPVOID)TargetFunAddr, NewCodeSize, OldProtect, &OldProtect);
}

申请并初始化内存

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
bool InitializeHookMemory(HANDLE hProcess)
{
// 1. 分配内存
HookAddr = VirtualAllocEx(
hProcess,
NULL,
4096, // 分配大小
MEM_COMMIT, // 立即提交物理内存
PAGE_EXECUTE_READWRITE // 内存保护属性
);

if (!HookAddr) {
QMessageBox::information(NULL, "错误信息", "请先打开游戏");
return false;
}

// 2. 准备NOP填充数据
std::vector<BYTE> nopBuffer(4096, 0x90); // 4096字节全部填充0x90

// 3. 写入内存
SIZE_T bytesWritten = 0;
if (!WriteProcessMemory(
hProcess,
HookAddr,
nopBuffer.data(),
nopBuffer.size(),
&bytesWritten
)) {
QMessageBox::information(NULL, "警告", "写入内存失败");
VirtualFreeEx(hProcess, HookAddr, 0, MEM_RELEASE); // 释放已分配内存
return false;
}
// 4. 验证写入完整性
if (bytesWritten != nopBuffer.size()) {
QMessageBox::information(NULL, "警告", "未能完整写入内存");
VirtualFreeEx(hProcess, HookAddr, 0, MEM_RELEASE);
return false;
}
return true;
}

添加Hook代码

1
2
3
4
5
6
7
8
9
10
11
12
void AddHookCode(
HANDLE hProcess,
LPVOID TargetFunAddr,
LPVOID HookAddr,
BYTE* HookCode,
size_t NewCodeSize,
size_t HookCodeSize)
{
DWORD OffSet = ((DWORD)TargetFunAddr + (DWORD)NewCodeSize) - (DWORD)HookAddr - (DWORD)(HookCodeSize);//得到偏移值
memcpy(&HookCode[HookCodeSize-4], &OffSet, 4);
WriteProcessMemory(hProcess, HookAddr, HookCode, HookCodeSize, NULL);
}

示例

widget.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>

#include <Windows.h>

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
Q_OBJECT

public:
Widget(QWidget *parent = nullptr);
~Widget();

protected:
BYTE* baseAddr;
SIZE_T moduleSize;
LPVOID HookAddr;
LPVOID DoubleMoney;

bool InitializeHookMemory(HANDLE hProcess);
std::pair<HANDLE, DWORD> OpenTargetProcess(const wchar_t* targetProcess);
LPVOID GetFinalAddress(HANDLE hProcess, LPVOID pBaseAddress, const DWORD* offsets, int offsetCount);
LPVOID FindPatternInAllocatedMemory(
HANDLE hProcess, // 目标进程句柄
LPVOID pBaseAddress, // 内存区域基地址
SIZE_T nMemorySize, // 内存区域大小(字节)
BYTE* pPattern, // 要查找的字节模式
SIZE_T nPatternSize, // 字节模式长度
DWORD nSearchStep = 1 // 搜索步长(默认逐字节搜索)
);
void ChangeOriginalCode(HANDLE hProcess, LPVOID TargetFunAddr, BYTE* HookCode, size_t NewCodeSize);
void AddHookCode(HANDLE hProcess, LPVOID TargetFunAddr, LPVOID HookAddr, BYTE* HookCode, size_t NewCodeSize, size_t HookCodeSize);
void DoubleMoneyFunc(int state);
private slots:


private:
Ui::Widget *ui;
};
#endif // WIDGET_H

widget.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
void widget::DoubleMoneyFunc(int state)
{
if (state == Qt::Checked) {


// 查找进程ID
const wchar_t* targetProcess = L"gamemd.exe";
std::pair<HANDLE, DWORD> result = OpenTargetProcess(targetProcess);

HANDLE hProcess = result.first;
if (hProcess) {
// 调用扫描函数并传入模式
BYTE searchPattern[] = { 0xDA, 0x86, 0x0C, 0x03, 0x00, 0x00 };
DoubleMoney = FindPatternInAllocatedMemory(hProcess, baseAddr, moduleSize, searchPattern, sizeof(searchPattern), 1, 0xCC);

//DWORD offsets[] = { 0xF964E };
//int offsetCount = sizeof(offsets) / sizeof(offsets[0]);
//DoubleMoney = GetFinalAddress(hProcess, baseAddr, offsets, offsetCount);

if (DoubleMoney) {
InitializeHookMemory(hProcess);
BYTE BlankPattern[] = { 0x90, 0x90, 0x90, 0x90 };
LPVOID BlankAddr = FindPatternInAllocatedMemory(hProcess, HookAddr, 4096, BlankPattern, sizeof(BlankPattern), 100, 0xCC);
if (BlankAddr) {
//0xE9,0x00,0x00,0x00,0x00表示jmp xxxxxxxx
BYTE NewCode[] = { 0xE9,0x00,0x00,0x00,0x00,0x90 };//要改变的硬编码,E9表示jmp
DWORD OffSet = (DWORD)BlankAddr - (DWORD)DoubleMoney - 5;//内存到地址的偏移地址
memcpy(&NewCode[1], &OffSet, 4);//拷贝硬编码给NewCode
ChangeOriginalCode(hProcess, DoubleMoney, NewCode, sizeof(NewCode));
BYTE HookCode[] = { 0x3B, 0x35, 0x4C, 0x3D, 0xA8, 0x00, 0x0F, 0x85, 0x02, 0x00, 0x00, 0x00, 0xD8, 0xC0, 0xDA, 0x86, 0x0C, 0x03, 0x00, 0x00, 0xE9, 0x00, 0x00, 0x00, 0x00 };
AddHookCode(hProcess, DoubleMoney, BlankAddr, HookCode, sizeof(NewCode), sizeof(HookCode));

CloseHandle(hProcess);
}
else
{
QMessageBox::information(NULL, "错误", "未找到空白地址");
CloseHandle(hProcess);
}
}
else
{
QMessageBox::information(NULL, "错误", "未找到地址");
CloseHandle(hProcess);
}
}
}
else
{
const wchar_t* targetProcess = L"gamemd.exe";
std::pair<HANDLE, DWORD> result = OpenTargetProcess(targetProcess);
HANDLE hProcess = result.first;
BYTE OriginalCode[] = { 0xDA, 0x86, 0x0C, 0x03, 0x00, 0x00 };
ChangeOriginalCode(hProcess, DoubleMoney, OriginalCode, sizeof(OriginalCode));
CloseHandle(hProcess);
}
}