本教程面向有C\C++基础的人,最好还要懂一些Windows编程知识
代码一律用Visual Studio 2013编译,如果你还在用VC6请趁早丢掉它...
写这个教程只是为了让玩家更好地体验所爱的单机游戏,顺便学到些逆向知识,我不会用网络游戏做示范,请自重
上一章讲了用CE读写内存,本章讲如何自己编程实现
用到的API:
BOOL WINAPI ReadProcessMemory(
LPCVOID lpBaseAddress,
_Out_ LPVOID
_Out_ SIZE_T
*lpNumberOfBytesRead
BOOL WINAPI WriteProcessMemory(
lpBaseAddress,
LPCVOID lpBuffer,
_Out_ SIZE_T
*lpNumberOfBytesWritten
// 打开进程
HANDLE WINAPI OpenProcess(
& _In_&DWORD dwDesiredAccess,
& _In_&BOOL &bInheritHandle,
& _In_&DWORD dwProcessId
本章开始最好学习汇编知识了,也不用太深,能做逆向工程就行了
&这篇文章讲得不错
另外VS2013(我就不告诉你VC6也有)调试时在菜单-调试-窗口-反汇编可以看到C/C++代码的对应汇编代码,多看看就熟悉了
本章以制作东方辉针城修改器的实战讲解读写内存
首先分析一下目标进程的内存
用CE搜索一下HP地址,找到0x004F5864,然后用分析数据/结构分析一下它附近的内存(其实这些变量并不在一个struct或class内,但看看附近的内存总会有惊喜)
这是一个指向资源信息的指针
然后是关于游戏数据的
然后提取出有用的数据
要读写一个进程的内存首先要打开进程,打开进程需要进程ID(PID)
一般有两种方式获取PID,第一种通过窗口句柄:
HWND hwnd = FindWindow(_T(&BASE&), NULL);
GetWindowThreadProcessId(hwnd, &pid);
第二种通过进程名:
#include &tlhelp32.h&
DWORD GetPid(LPCTSTR name)
DWORD pid = 0;
HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
PROCESSE***Y32 processE
processEntry.dwSize = sizeof(PROCESSE***Y32);
// 枚举进程
BOOL hasNext = Process32First(snapshot, &processEntry);
while (hasNext)
// 比较进程名
if (_tcsicmp(processEntry.szExeFile, name) == 0)
pid = processEntry.th32ProcessID;
hasNext = Process32Next(snapshot, &processEntry);
CloseHandle(snapshot);
DWORD pid = GetPid(_T(&th14.exe&));
没什么好讲的,读内存需要PROCESS_VM_READ权限,写内存需要PROCESS_VM_WRITE和PROCESS_VM_OPERATION权限
HANDLE process = OpenProcess(/*PROCESS_ALL_ACCESS*/ PROCESS_VM_WRITE | PROCESS_VM_OPERATION, FALSE, pid);
需要注意的是静态地址也不是不变的,准确来说应该用模块基址+偏移量来表示,因为模块基址可能会变,如果模块基址会变的话还要取模块基址(其实就是模块句柄)
不过大部分exe模块的基址是不变的(32位默认0x位默认0x)
WriteProcessMemory(m_process, (LPVOID)0x004F5864, &(buffer = 8), sizeof(DWORD), NULL);
东方辉针城修改器
然后我们就可以实现这个修改器了,依然用到了MFC(为了少写UI代码)
// 处理定时器
void CTH14CheatDlg::OnTimer(UINT_PTR nIDEvent)
HWND hwnd = ::FindWindow(_T(&BASE&), NULL);
if (hwnd == NULL) // 进程已关闭
if (m_process != NULL)
// 释放句柄
CloseHandle(m_process);
m_process = NULL;
// 打开进程
if (m_process == NULL)
GetWindowThreadProcessId(hwnd, &pid);
m_process = OpenProcess(/*PROCESS_ALL_ACCESS*/ PROCESS_VM_WRITE | PROCESS_VM_OPERATION, FALSE, pid);
if (m_process == NULL)
msg.Format(_T(&打开进程失败,错误代码:%u&), GetLastError());
MessageBox(msg, NULL, MB_ICONERROR);
CDialogEx::OnTimer(nIDEvent);
if (m_lockHp)
WriteProcessMemory(m_process, (LPVOID)0x004F5864, &(buffer = 8), sizeof(DWORD), NULL);
if (m_lockBomb)
WriteProcessMemory(m_process, (LPVOID)0x004F5870, &(buffer = 8), sizeof(DWORD), NULL);
if (m_lockPower)
WriteProcessMemory(m_process, (LPVOID)0x004F5858, &(buffer = 400), sizeof(DWORD), NULL);
CDialogEx::OnTimer(nIDEvent);
东方辉针城修改器V2
每秒钟写内存的方法看上去太蠢了,而且会影响性能,一劳永逸的方法就是修改代码
首先找出减少残机数的指令
地址是0x,机器码A3 64 58 4F 00,把它全部改成90(nop指令)
减少bomb的指令
地址0x0041218A,机器码A3 70 58 4F 00,改成nop
然后是判断bomb够不够用的指令
要修改的是下面的jle指令(小于或等于时跳转),把它改成nop
地址0x0044DD68,机器码7E 0E
然后是游戏刚开始时赋值灵力的
直接改这条指令长度会变长,改上面的mov eax吧,改成赋
地址0x00435DAF,原机器码A3 58 58 4F 00,修改成B8 90 01 00 00
死亡后赋值灵力的
这堆代码的意思是把灵力读到ecx寄存器,减少后写回内存,改成赋吧
地址0x0044DDB8,原机器码03 C8 3B CE 0F 4C CE,修改成B9 90 01 00 00 90 90
实现代码(完整源码地址同上):
// 修改关于残机的代码
void CTH14CheatDlg::modifyHpCode()
static const BYTE originalCode[] = { 0xA3, 0x64, 0x58, 0x4F, 0x00 };
static const BYTE modifiedCode[] = { 0x90, 0x90, 0x90, 0x90, 0x90 };
if (m_process != NULL)
WriteProcessMemory(m_process, (LPVOID)0x, m_lockHp ? modifiedCode : originalCode, sizeof(originalCode), NULL);
// 修改关于炸弹的代码
void CTH14CheatDlg::modifyBombCode()
static const BYTE originalCode1[] = { 0xA3, 0x70, 0x58, 0x4F, 0x00 };
static const BYTE modifiedCode1[] = { 0x90, 0x90, 0x90, 0x90, 0x90 };
static const BYTE originalCode2[] = { 0x7E, 0x0E };
static const BYTE modifiedCode2[] = { 0x90, 0x90 };
if (m_process != NULL)
WriteProcessMemory(m_process, (LPVOID)0x0041218A, m_lockBomb ? modifiedCode1 : originalCode1, sizeof(originalCode1), NULL);
WriteProcessMemory(m_process, (LPVOID)0x0044DD68, m_lockBomb ? modifiedCode2 : originalCode2, sizeof(originalCode2), NULL);
// 修改关于灵力的代码
void CTH14CheatDlg::modifyPowerCode()
static const BYTE originalCode1[] = { 0xA3, 0x58, 0x58, 0x4F, 0x00 };
static const BYTE modifiedCode1[] = { 0xB8, 0x90, 0x01, 0x00, 0x00 };
static const BYTE originalCode2[] = { 0x03, 0xC8, 0x3B, 0xCE, 0x0F, 0x4C, 0xCE };
static const BYTE modifiedCode2[] = { 0xB9, 0x90, 0x01, 0x00, 0x00, 0x90, 0x90 };
if (m_process != NULL)
WriteProcessMemory(m_process, (LPVOID)0x00435DAF, m_lockPower ? modifiedCode1 : originalCode1, sizeof(originalCode1), NULL);
WriteProcessMemory(m_process, (LPVOID)0x0044DDB8, m_lockPower ? modifiedCode2 : originalCode2, sizeof(originalCode2), NULL);
if (m_lockPower)
WriteProcessMemory(m_process, (LPVOID)0x004F5858, &(buffer = 400), sizeof(DWORD), NULL);
本文已收录于以下专栏:
相关文章推荐
这一课 将用CE来找出对对碰游戏 坐位号基址,棋盘数组基址并把它读出来
教学目的:学会用API函数读出内存数据
1、预备知识
4种数据类型
字节 Byte=00-FF ...
windows内存管理知识:
1.分段或分页内存管理
2.物理地址和虚拟地址,虚拟地址空间.
3.虚拟内存布局,内存分工,堆,栈.
4.内存存取权限.
5.标准C内存管理函数与windows...
人生苦短,都说必须python,那么我分享下我是如何从小白成为Python资深开发者的吧。2014年我大学刚毕业..
记得我的博客第一篇文章是写的《仙剑奇侠传3 外传》存档文件修改器(请参看:《[vb6]仙剑3外传的存档修改器》),当时是用vb6写的。
想想也是十多年过去了。
十多年过去已经很少在电脑上玩游戏了,还好...
计算机程序所有的变量、代码都是储存在内存里的,包括游戏里那些HP、MP、金钱等,那么只要能修改内存就能自由改变玩家的HP、金钱了(当然对于网游是没用的,这些数据都储存在服务器,客户端里的只是一个副本)...
在第二章介绍了hook机制,本章要自己实现hook,实现调用某个函数时我们能截获、修改、取消这次调用
CE也不是专门用来调试的,本章将介绍几款调试工具,并且完善上一章的东方辉针城修改器
本章介绍怎么hook D3D函数,实现在游戏画面中显示自己的文字
注入的代码就是目标进程的一部分了,可以直接用指针读写目标进程内存,还可以hook目标进程的函数
先从最简单的模拟操作讲起
模拟键盘鼠标有很多方法,我大体分为消息模拟、API模拟、驱动模拟
对于网页的话还可以用JavaScript模拟,虽然这不在本教程范围
他的最新文章
讲师: 许鹏
讲师:董付国
您举报文章:
举报原因:
原文地址:
原因补充:
(最多只允许输入30个字)