核心代码:Devcpp足够;
对话框界面和资源文件的添加:CodeBlocks
资源编辑器:ResEdit
格式转换工具:格式工厂、Spy++
必备工具:CheatEngine
1.打过CheatEngineTutorial第8关“多级指针”,
2.会修改静态地址下的数据
================================================================================================
打开CE,打开植物大战僵尸
精确数值搜索找到阳光地址(这都是基本操作,我就不说了),
把阳光数值地址加到下方列表,并找出是什么修改了这个地址
记下第一个偏移量:0x5560
搜索框搜索EAX的数值,也就是CE提醒你的数值:0x176599C8。
出来一大堆对吧,多点几次再次次扫描,把数值不停地变的地址排除掉。
还剩下这么多地址,最初到这里我就头疼了,到底找哪个?后来看了一个比较有经验的人的说法,CE会把比较靠谱的几个数据地址给你放在最前面,而且一些前缀相同的、重复的地址大概率不是。
按照这位大佬的经验指导,我选择前4个地址,把他们全部加到下方列表,依次点找出是是谁访问了这个地址,
这是可能1的情况,只有push、pop,没有我们期待的 [寄存器]之类的,而且阳光变化的时候,这里并没有变化,果断排除!
为了让大家更透彻,我就把错误的一个一个排掉,其实可能2就是我们要找的一级指针。
可能3前两个数据一打开,计数就一直在变,很让人怀疑不是和阳光数据有关的。
而第三个数据,我原本发现每次点一个阳光有会加1,刚开始真是个大坑,浪费我好久世界。
后来我发现,其实你在游戏框里每点一次鼠标,不过点什么,第三个数据计数都会加一,所以我判断这只是一个检测鼠标事件的代码。
可能3排除!
来看可能4:
这还用思考吗?阳光变动,这个地址一点反应都没有。
果断排除!
下来看可能2,也就是真正的指针。
可能2的代码是真的多,这真让人讨厌,而且数据非常诡异,好多计数在游戏一启动就不停地上涨,而且下方不断有新的代码跳出来。
不过别慌,我们慢慢仔细地观察。
发现:
1.游戏启动的时候检测可能3,游戏会变得很卡。
------我猜测这可能是可能3这里有很多和游戏运作有关东西,比如冷却时间、僵尸产生什么什么的。
2.过一段时间后,可能3处的逐渐平稳了,不会再有新的代码跳出来了。
------这是我们希望看到的,代码越少越容易缩小范围
3.仔细观察数据,有几行数据会随着你的操作加1,
![在这里插入图片描述](https://img-blog.csdnimg.cn/20210613192111620.png
这里分析一下,随着时间累积,上面那些计数不停变的数据,最后计数会远远大于其他地址,很可能和我们的阳光无关,当你捡起了二三十次阳光后,那些变化很小的个位数也可以排除了。
所以我们的目标就是那些和操作次数比较接近的代码,看看图中76这个数字,这个数字就是之前提到过的那种,每当在游戏界面点击一次鼠标,它就会加一,所以排除。
那么仔细观察下来,其实也没有其他数据了,图中只有25、16、16这三个代码了
我也没数自己到底点了多少次阳光,也许是快20次,也许是20多次,因为监测期间游戏太卡了,延迟很高,我不方便观察。
不过当我们点开这三行数据:
其实都是一样的,所以这里就偷个懒不深究了,不过我猜测一下,可能一个是总的阳光,一个是天上掉落的或自己产出的阳光。
记录第二个偏移量:0x768
搜索框搜索这个EDX (ECX),
终于看到了心动的绿色基址,这里已经无法判断哪一个是了,不过好在只有4个,全部加到下面框里,一个个的合成最终指针。(其实实践中发现四个基址都可以)
然后点这个重新计算新的地址,得到基址,我这里是0X006AA00C
发现了什么?四个指针最后的基址一模一样,都是006AA00C
至于最后那个“以前找到的基址”,我是真的懵逼
这个是以前找到的基址0X0019FD2C,这次找的基址不一样,但是居然也拿来用,不同的基址写出来的程序都能运行,我是真的懵逼!。
CE部分到此结束,这里需要的就是我们找到的那三个数据:
offset1=0x768
offset2=0x5560
base_addr=0x6AA00C
#include
#include
int main(void)
{
DWORD base=0x0019FD2C;
//DWORD base=0x006AA00C;
DWORD offset1=0x768,offset2=0x5560;
DWORD temp_addr=0;
HWND WindHandle;
DWORD pid;
HANDLE ProcessHandle;
LPVOID toChangeAddr=NULL;
int PrevValue;
int toWriteValue=9999;
WindHandle=FindWindow("MainWindow","植物大战僵尸汉化版");
if(!WindHandle)
{
printf("游戏未启动!");
exit(0);
}
GetWindowThreadProcessId(WindHandle,&pid);
ProcessHandle=OpenProcess(PROCESS_ALL_ACCESS,FALSE,pid);
ReadProcessMemory(ProcessHandle,(LPVOID)base,&temp_addr,sizeof(DWORD),NULL);
//printf("%X\n",temp_addr);
ReadProcessMemory(ProcessHandle,(LPVOID)(temp_addr+offset1),&temp_addr,sizeof(DWORD),NULL);
//printf("%X\n",temp_addr);
toChangeAddr=(LPVOID)(temp_addr+offset2);
//printf("%X\n",toChangeAddr);
//ReadProcessMemory(ProcessHandle,toChangeAddr,&PrevValue,sizeof(DWORD),0);
//printf("%d",PrevValue);
WriteProcessMemory(ProcessHandle,toChangeAddr,&toWriteValue,sizeof(DWORD),NULL);
system("pause");
}
WinAPI都是看名字就懂什么意思了,MSDN一查、参数一填就好了。
FinwWindow里的两个参数,参数一是窗口类名,参数二是窗口名,这个为了确保正确,还是拿Spy++打开看了一下:
类名MainWindow,窗口标题真就是“植物大战僵尸汉化版”。
之所以多这么一个部分,主要是想把自己平时的一些操作系统地总结一下
所以这里会很啰嗦,也是为了给自己看。
对话框
DevCpp也可以搞对话框,不过DevCpp的资源调用不太会,暂时没把握,所以CodeBlocks,而且CodeBlocks可以配置资源编辑器ResEdit。
为什么不用VS2019,因为不想过于依赖外物,会阻碍自己成长,而且VS会有一些莫名奇妙的错误,代码量不多划不来用。
#include
#include
#include
#pragma comment(lib,"winmm.lib")
#include "resource.h"
#include
HINSTANCE hInst;
int toWriteValue=9999;
BOOL CALLBACK DlgMain(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
DWORD base=0x0019FD2C;
DWORD offset1=0x768,offset2=0x5560;
DWORD temp_addr=0;
HWND WindHandle;
DWORD pid;
HANDLE ProcessHandle;
LPVOID toChangeAddr=NULL;
int PrevValue;
int toWriteValue=9999;
WindHandle=FindWindow("MainWindow","植物大战僵尸汉化版");
GetWindowThreadProcessId(WindHandle,&pid);
ProcessHandle=OpenProcess(PROCESS_ALL_ACCESS,FALSE,pid);
ReadProcessMemory(ProcessHandle,(LPVOID)base,&temp_addr,sizeof(DWORD),NULL);
ReadProcessMemory(ProcessHandle,(LPVOID)(temp_addr+offset1),&temp_addr,sizeof(DWORD),NULL);
toChangeAddr=(LPVOID)(temp_addr+offset2);
switch(uMsg)
{
case WM_INITDIALOG:
{
}
return TRUE;
case WM_CLOSE:
{
EndDialog(hwndDlg, 0);
}
return TRUE;
case WM_COMMAND:
{
switch(LOWORD(wParam))
{
case IDOK:
WriteProcessMemory(ProcessHandle,toChangeAddr,&toWriteValue,sizeof(DWORD),NULL);
PlaySoundA((LPCSTR)IDR_WAVE1,GetModuleHandle(NULL),SND_RESOURCE|SND_ASYNC);
}
}
return TRUE;
}
return FALSE;
}
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
hInst=hInstance;
InitCommonControls();
if(!FindWindow("MainWindow","植物大战僵尸汉化版"))
{
MessageBeep(MB_ICONHAND);
return MessageBox(NULL,"游戏未启动!","错误:",MB_OK);
}
return DialogBox(hInst, MAKEINTRESOURCE(IDD_DIALOG1), NULL, (DLGPROC)DlgMain);
}
对话框代码不用自己写,直接新建项目,DialogBased自动生成模板,把之前的核心代码放在合适位置即可
另外猜测会有人遇到的问题:
1.undefined refernce impXXX
2.会有控制台出现。
对于1,请看我的另一篇博客 部分undefined reference to `__imp_XXXX‘错误解决方案
对于2,是因为你的生成目标是Debug选项,改成Release就会有控制台跳出来了。
资源文件
加图标:
在记事本里写上:MAINICON ICON “Icon_1.ico”
然后重命名为XXX.rc,点击项目—>添加文件,把XXX.rc加进去,然后把图标“icon_1.ico”放进去,编译即可。
.wav文件的嵌入:
这个要用资源编辑器添加,因为资源文件有自己的语法,如果前后定义不兼容会编译失败,就是通过也不能正常运行。所以尽量用同一个工具。
我比较推荐ResEdit,很靠谱,只有1M多,就是界面有点丑。
占位符:“CodeBlocks配置ResEdit”,有空再写一篇
CodeBlocks对话框项目会自动生成一个对话框,在ResEdit中把它去掉,然后用ResEdit重新添加一个对话框,不然会失败,我猜测是编译器和资源编辑器对资源文件的语法不同,总之要做到统一。
个性图标
这个我是直接在线艺术字生成,然后截图下来
然后格式工厂转格式成.ico就OK了
我不是男同,只是觉得这个图标比较有亮点。
2021/6/14更新:
植物冷却的基址已经找到了,等有空再写下一篇博客,最近考试有点多。
冷却基址是真的不好找,看了两篇其他博客,都找的是变动地址,这不能满足我们制作外挂的要求。
简单说下,冷却基址直接搜——反正在我的版本是搜不到地址的,要找到可疑汇编代码后去内存里看。我觉得原因可能是数据和代码写到一个内存块了,然后直接搜会不被当数值而排除,也许吧,我也不太懂。
(如果没记错,植物冷却基址和这次的阳光基址是一样的。)