#声明本文章只用于学习与研究,请勿用于犯法!
· WriteProcessMemory与ReadProcessMemory
引言 · 相信每个热爱逆向的你最开始都是从植物大战僵尸开始的,先找数值再找偏移测试用ReadProcessMemory读取在用WriteProcessMemory进行修改,可学到后面可就不是简简单单的修改数值了,有些功能需要通过汇编注入的形式来实现。
如果你能搜到此篇文章就代表你已经掌握通过ce的自动汇编来修改,先上代码
#include <windows.h>
#include <tlhelp32.h>
#include <vector>
#include <string>
#include <stdexcept>
#include <sstream>
#include <cwchar>
class MusicBox_RemoteMemoryPatcher {
private:
// 核心成员变量
DWORD m_dwProcessId; // PID
std::wstring m_wsModuleName; // 目标模块名
DWORD_PTR m_dwModuleOffset; // 模块内偏移地址
std::vector<BYTE> m_vPatchData; // 注入数据缓冲区
std::vector<BYTE> m_vOriginalData; // 原始数据缓冲区
HANDLE m_hProcess; // 进程句柄
DWORD_PTR m_dwModuleBase; // 模块基址
DWORD m_dwOldProtect; // 原始内存保护属性缓存
DWORD_PTR GetRemoteModuleBase() {
HANDLE hSnapshot = CreateToolhelp32Snapshot(
TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32,
m_dwProcessId
);
if (hSnapshot == INVALID_HANDLE_VALUE) {
throw std::runtime_error(
"CreateToolhelp32Snapshot 失败: " + std::to_string(GetLastError())
);
}
MODULEENTRY32W stModuleInfo = { 0 };
stModuleInfo.dwSize = sizeof(MODULEENTRY32W);
DWORD_PTR dwModuleBase = 0;
if (Module32FirstW(hSnapshot, &stModuleInfo)) {
do {
if (_wcsicmp(stModuleInfo.szModule, m_wsModuleName.c_str()) == 0) {
dwModuleBase = reinterpret_cast<DWORD_PTR>(stModuleInfo.modBaseAddr);
break;
}
} while (Module32NextW(hSnapshot, &stModuleInfo));
}
CloseHandle(hSnapshot);
if (dwModuleBase == 0) {
std::string sModuleName(m_wsModuleName.begin(), m_wsModuleName.end());
throw std::runtime_error("未找到模块->" + sModuleName);
}
return dwModuleBase;
}
void OpenTargetProcess() {
m_hProcess = OpenProcess(
PROCESS_ALL_ACCESS,
FALSE,
m_dwProcessId
);
if (m_hProcess == NULL) {
throw std::runtime_error(
"OpenProcess 失败: " + std::to_string(GetLastError())
);
}
}
void ModifyMemoryProtect(DWORD_PTR dwAddress, SIZE_T dwSize, DWORD flNewProtect) {
if (!VirtualProtectEx(
m_hProcess,
reinterpret_cast<LPVOID>(dwAddress),
dwSize,
flNewProtect,
&m_dwOldProtect
)) {
throw std::runtime_error(
"VirtualProtectEx 失败: " + std::to_string(GetLastError())
);
}
}
void RestoreMemoryProtect(DWORD_PTR dwAddress, SIZE_T dwSize) noexcept {
VirtualProtectEx(
m_hProcess,
reinterpret_cast<LPVOID>(dwAddress),
dwSize,
m_dwOldProtect,
&m_dwOldProtect
);
}
void WriteRemoteMemory(DWORD_PTR dwAddress, const std::vector<BYTE>& vData) {
SIZE_T dwWritten = 0;
if (!WriteProcessMemory(
m_hProcess,
reinterpret_cast<LPVOID>(dwAddress),
vData.data(),
vData.size(),
&dwWritten
)) {
throw std::runtime_error(
"WriteProcessMemory 失败: " + std::to_string(GetLastError())
);
}
// 验证写入长度
if (dwWritten != vData.size()) {
std::stringstream ss;
throw std::runtime_error(ss.str());
}
}
public:
MusicBox_RemoteMemoryPatcher(
DWORD dwProcessId,
const std::wstring& wsModuleName,
DWORD_PTR dwModuleOffset,
const BYTE* pPatchData,
SIZE_T dwPatchSize,
const BYTE* pOriginalData,
SIZE_T dwOriginalSize
) : m_dwProcessId(dwProcessId),
m_wsModuleName(wsModuleName),
m_dwModuleOffset(dwModuleOffset),
m_hProcess(NULL),
m_dwModuleBase(0),
m_dwOldProtect(0) {
// 验证数据有效性
if (pPatchData == nullptr || dwPatchSize == 0) {
throw std::invalid_argument("补丁数据不能为空");
}
if (pOriginalData == nullptr || dwOriginalSize == 0) {
throw std::invalid_argument("原始数据不能为空");
}
// 初始化数据缓冲区
m_vPatchData.assign(pPatchData, pPatchData + dwPatchSize);
m_vOriginalData.assign(pOriginalData, pOriginalData + dwOriginalSize);
// 打开目标进程
OpenTargetProcess();
// 获取模块基址
m_dwModuleBase = GetRemoteModuleBase();
}
~MusicBox_RemoteMemoryPatcher() noexcept {
if (m_hProcess != NULL) {
CloseHandle(m_hProcess);
m_hProcess = NULL;
}
}
void EnablePatch() {
DWORD_PTR dwTargetAddr = m_dwModuleBase + m_dwModuleOffset;
try {
ModifyMemoryProtect(dwTargetAddr, m_vPatchData.size(), PAGE_EXECUTE_READWRITE);
WriteRemoteMemory(dwTargetAddr, m_vPatchData);
RestoreMemoryProtect(dwTargetAddr, m_vPatchData.size());
}
catch (const std::exception& e) {
RestoreMemoryProtect(dwTargetAddr, m_vPatchData.size());
throw;
}
}
void DisablePatch() {
DWORD_PTR dwTargetAddr = m_dwModuleBase + m_dwModuleOffset;
try {
ModifyMemoryProtect(dwTargetAddr, m_vOriginalData.size(), PAGE_EXECUTE_READWRITE);
WriteRemoteMemory(dwTargetAddr, m_vOriginalData);
RestoreMemoryProtect(dwTargetAddr, m_vOriginalData.size());
}
catch (const std::exception& e) {
RestoreMemoryProtect(dwTargetAddr, m_vOriginalData.size());
throw;
}
}
DWORD_PTR GetModuleBase() const noexcept {
return m_dwModuleBase;
}
DWORD_PTR GetTargetAddress() const noexcept {
return m_dwModuleBase + m_dwModuleOffset;
}
DWORD GetProcessId() const noexcept {
return m_dwProcessId;
}
};
实用实例
BYTE patch[] = { 0xE9, 0xE7, 0x00, 0x00, 0x00, 0x90 };
BYTE original[] = { 0x0F, 0x8E, 0xE6, 0x00, 0x00, 0x00, 0x8B, 0x43, 0x08, 0x3B, 0x46 };
MusicBox_RemoteMemoryPatcher patcher(
g_miniWorldPid, // 进程ID
L"libSandboxEngine.dll", // 目标模块名
0x5842BB, // 模块内偏移
patch, sizeof(patch), // 注入数据+长度
original, sizeof(original) // 原始数据+长度
);
//启用注入
patcher.EnablePatch();
//恢复
patcher.DisablePatch();
好让我们来解析一下调用
patch[]列表储存这是这个->![]()
original列表储存这是这个->![]()
被 MusicBox_RemoteMemoryPatcher定义的patcher具有两个功能函数,一个是.EnablePatch()为开启(也就是将patch注入到进程中) 一个是.DisablePatch()他是用于恢复(也就是达到关闭的效果将原本的original重新注入回去)
结语:不管是汇编注入还是简单的修改内存本质上都是对游戏创作者心血的亵渎与诋毁所以我本人不倡导、但是还是热爱逆向,如果这篇题材对你有用不妨留下你的痕迹。
1万+

被折叠的 条评论
为什么被折叠?



