本帖最后由 JuncoJet 于 2019-4-26 09:33 编辑
前言
什么红警95 98,VR特警……没有这些游戏的网吧还能叫网吧?老板换台机,这机子什么都没有玩毛啊!
红警作为一代经典,深入人心,带给我们许多快乐的时光。但如今,3D游戏横行的年代,一款平面游戏所能给的激情,似乎少了那么一点。
那么我们就对游戏做一点小小的修改,玩出不一样的花样,呵呵哒。本帖主体内容如下:
原理剖析
在红警的规则里有一条潜规则,也是第一大定律,那就是所有建筑爆了无论你有多少兵都立即判输。我们根据此规则下手,进行研究,看能不能在游戏的任何时间秒对手个措手不及,噗。
虽然有点小邪恶,但是想想还是挺激动的。我们接下来使出我们的游戏分析利器CE(Cheat Engine)和GE(Game Expert),两款修改器各有优缺,CE的专业地位无可动摇,但是部分游戏下切换窗口很不方便,并且有崩溃游戏的可能,很不幸红警就是其中之一…… GE作为第二候选,由于使用注入游戏,在游戏内部打开搜索窗口,有众多优势,所以本帖就由两款编辑器同时开工。
给GE设置好一个适合自己的热键,开始一场遭遇战,然后试试看能不能呼出编辑器窗口,然后我们根据自己的建筑数量搜索单字节整数。下面我就不一一截图了,切换窗口很容易崩溃游戏。
大概4次左右,我们就能找出约4个地址,然后我们卖掉点房子,或者再建造房子,不要搜索值,看值的变化。
由于GE使用注入的方法,所以内存数据会被输入的内容污染,我们使用这个技巧来排除掉污染数据。然后我们可以确定有效数据两个,如下图,再接下来我们逐个验证下。
也许有人问为什么这里使用单字节搜索,那么我们说说单字节整数,双字节(字)以及四字节(双字)的区别
单字节整数,汇编中我们叫做BYTE,C语言中的char是一样的
我们画一个图,来讲解下BYTE(字节)、WORD(字)、DWORD(双字)、QWORD(四字)的区别和内存分布
可以看到8字节共用了两个4字节,4字节又共用了两个2字节,2字节又又共用了两个单字节,下面的例子就是展示4个字节组成一个4字节整形
[C] 纯文本查看 复制代码 char b[4]={1,0,0,0};
int *d=(int*)b;
printf("%d",*d);
[Asm] 纯文本查看 复制代码 bb db 1,0,0,0
_main:
mov eax,DWORD ptr[bb];MASM
;mov eax,[bb];NASM/FASM 使用BIT32修饰
ret
所以,我们在搜索游戏数值的时候用单字节搜索,是一个保守的搜索方式,确保能搜索出来,同时兼容双字节4字节的一种搜索方式。附一个ASM、C、VB类型对照表
前面的例子使用到了指针,指针是什么,指针就是个地址引用比如说b是一个4字节数组,d是个整形指针他引用地址是b,d就是b的内存地址。在32位程序中,指针的大小是32位也就是4字节,64位程序的指针是64位8字节。有些人可能会涉及到8位单片机,比如51、AVR和STM8之类的,里面如果使用指针,那么指针大小就是8位1个字节。(此处说法不确切,经过考究猜测应该是16位2字节,因为还需要段地址)
也许你们中学电脑课是这么上的,构成数据的最小单位是比特(bit),一个字节(BYTE)是8个比特,一个英文字母占一个字节,一个汉字占一个字(WORD)。没错的,但是老湿没说有UTF8,UNICODE的存在?略略略…… 跑偏了,回来回来
我们验证完地址之后,怎么验证?输入0,然后爆炸,游戏结束的就是。然后,我们用CE这三个很好用的功能,我们就用第二个吧。
之后会有一个统计,统计出访问次数最多的,点开他,看一下这个代码的所在位置
代码访问时的值,ECX+0x78相当于一个指针,读取0AB0E818地址中的值。
CE能够很方便的找出偏移
下面又到了科普环节,啥是佩奇,不对啥是基址,啥是偏移?
对于EXE来说,基址基本是固定的,DLL的话基址会由系统分配,一般DLL建议使用动态获取
[C] 纯文本查看 复制代码 GetModuleHandle("DLL名");
//或者
LoadLibrary('DLL名');
EXE的话写死了就行,咩哈哈哈(误,如果开启了DEP,那么也可能由系统分配基址,保守做法还是建议动态获取
此处要请出OllyICE/OllyDBG来验证,先打开游戏附加Game
我们先对地址下一个条件记录吧,然后输入监控访问的地址,玩一把游戏看看记录下来的地址有没有变化。
我假设,敌人和我方都使用一个方法(过程)来判断是否死亡,那么对一个地址下断能获取到对方的地址(猜
下断后获取到了地址,我们先改改数字看,看看房子会不会爆 Boom!
改完发现,然而并不能??什么鬼,然后发现好像我们找错了…… 别,别打我
我们重新按照前面步骤,再来一次。找到建筑的数目,然后再找出访问地址,游戏跑起来
如图我们得到了6个地址,一个一个改代码。爆破!当改到第三个代码时建筑爆炸了Boom!哎呀这次终于找对了,有没有惊喜
爆破对了的话建筑会一秒全爆炸。游戏重启了,再开会秒赢画面不怎么美,下面视频仅供参考
链接:https://pan.baidu.com/s/1T2jT_y6ILS_MwdqXiosipw 提取码:n2sl
链接:https://pan.baidu.com/s/1a5kEFsLcwsZ29FpEvjoUFw 提取码:n6l0 (没啥好看的)
基本已完成代码的定位,下面我们就是写修改器了
福利环节,修改此处,只要把出总部收起来,就会爆炸…… 哎,还是忍不住出IDA了,OD实在是太长了没法看。
链接: https://pan.baidu.com/s/1SbSB8cg-8zeowHIvh0uxIg 提取码: bw2v 超帅小视频
制作修改器
CE/GE生成修改器
CE生成修改器
打开CE,添加进程Game.exe,然后我们在地址栏里添加一些我们已经验证过了的4个地址(底部教程里),并且为他们设置热键B,值为0
然后我们在文件菜单中有一个“从列表中生成修改器”的选项
在左侧框里可以看到刚才我们设置好的4个地址,里面的其他信息可以根据自己需要自己修改,确认无误点击1、2按钮生成我们的修改器。
最终我们得到了一个这个样子的修改器,打开游戏按B就能击败一个国家。这边我个人的分析,小地图一般都有4个国家,大地图有8个,大家可以自己尝试添加其他的国家。
ra2cheat.zip
(3.32 MB, 下载次数: 269)
GE生成修改器
Game Trainer.zip
(6.97 KB, 下载次数: 104)
用编程语言编写修改器
自己编写修改器(或者俗称WG),常见的有人E语言写、VB写、VC写、ASM写……或者Python,基本上能调用系统API,就能写的成外挂。
所以不强求用什么语言实现,我这边什么顺手就随便用了,争取每个例子都写一次,哈哈。
涉及到的基本知识点
注入
消息钩子(如果要写热键的话会很方便)
远程线程(大众玩法)
用户APC(高端玩法)
计算地址
获取基址(必学)
计算虚拟地址(必学)
指针大法(常用,高端大气,功能强)
Hook(能不用尽量不用,易检测易发现,不稳定不通用)
inline hook(必学)
eat/iat hook(基本用不到)
消息钩子,SetWindowsHookEx,有很多种钩子主要分两类拿键盘钩子举个栗子
一类是WH_KEYBOARD(不带LL)另一类是WH_KEYBOARD_LL(带LL)。LL全称 LOW LEVEL,是种全局钩子,这种钩子是系统负责维护的,简单说就是注入到了系统。你的系统是32位你就需要写32位的LL钩子,你的系统是64位你就需要写64位的LL钩子。不带LL的,就是普通钩子。普通的钩子运行原理就是注入到目标程序,也就是你的钩子和目标程序是要相同位的。注入过程由系统负责,一个API搞定,属于最简单的注入,但缺点么就是只能注入有窗口的程序。
远程线程,CreateRemoteThread,这种就是网上最普遍被玩坏了的方法,是个杀毒软件就会报毒(我猜的
先OpenProcess或者CreateProcess,获取进程句柄,然后VirtualAllocEx申请一段内存,WriteProcessMemory写入内存shellcode数据或者指定的栈数据,最后CreateRemoteThread创建线程。
用户APC,这种的话内核玩家用的比较多,病毒中比较常见,日常的话基本没有软件用这个方法。Ring0、Ring3都可以实现,但Ring3就有点多此一举,下面网址自己阅读吧
https://bbs.pediy.com/thread-222923.htm
获取基址,这个前面有说过,就是楼层的高度哪个楼层以上可以住人…… GetModuleHandle/LoadLibrary(笑
计算虚拟地址,分页内存这个东西我猜测大概是WinNT之后发明的吧,Win9X/ME还是很容易奔溃的,大概是没有(我只是无端猜测,没有根据的大家略过,略略略……
写过DOS驻留程序的话,强调一下驻留,用DEBUG装载你的程序并运行之后,你再装载一次程序,此时你的程序在DEBUG里显示的内存地址是不同的,这个就是早期没有分页内存,程序空间不独立。每个程序都是在一个平坦内存上(不确定这个叫法对不对,叫物理内存貌似也不对),改变段寄存器DS:,程序可以操控整个内存。这种做法不安全,且容易出错,奔溃系统也是他的诟病。之后大家普遍的做法就是分页内存,包括Linux和Windows都是这么做的,把物理内存通过算法分配给每个进程,存在一个分配表类似数据库但应该只是简单的结构,这个表叫做页表,存储的这个文件叫做分页文件,这个表里面就是你的程序的内存和物理内存的一个映射关系,和部分分页内存中的数据。而分页内存,每个程序里面的内存地址,可以重复使用,比如程序映射的基址一般为0x00400000,每个EXE起始的地址都是0x00400000,难道内存不会冲突?不会,那就是因为这个分页内存,基址的话就是分页内存的映射地址。是不是很通俗易懂(噗
虚拟地址,=基址+偏移。举个栗子,就很容易懂了,这个楼房40楼上面我能租给你,请问你要租几楼啊。那我租42楼吧,那这个40就是一个基准,偏移是2。简单通俗。
指针,上面有说过,自己复习。
inline hook,内联钩子,就是在你的代码内实现的钩子,C语言中能用__asm/asm()标签来内联汇编,是一个道理,只不过一个实现的钩子。啥是钩子?钩子就是钓鱼的那个,掉到了就把他放到自己的鱼篓子里,带回家。如果想吃就吃掉他,如果不想吃就可以放回去放生。
eat/iat hook,导入表导出表hook,Ring3下实现意义不大,实现就暴力搜索内存就行,程序运行后有一个重定向表里面有API地址篡改他就行。Ring0下实现的话,就能被全局hook上,原因很简单,内核始终在那里只被加载一次,并且一旦被修改就不会改回来。所以……被hook的话,鱼会都往你的篓子跑。
那我们接下来编写第一个修改器吧,使用的是SetWindowsHookEx,游戏的分析如下图。
我们要实现的,就是注入到目标程序,然后按下按键B,炸掉所有建筑结束游戏。
ra2cheat.zip
(15.54 KB, 下载次数: 61)
是时候展现真正的技术了,我们稍微改写了下代码,确定自己不会爆炸(噗,前面发的代码自己会爆炸,为了效果好看。要改的话可以参照下面,改下 i 的起始值
[C] 纯文本查看 复制代码 for(int i=1;i<4;i++){//第一个是自己,我们跳过
long pp=p;
pp+=i*4;
dbg(2,pp);
__asm{
pushad
mov esi,[pp]
mov esi,[esi]
xor eax,eax
mov [esi+0x230],eax//开启快速游戏只需要改这个
mov [esi+0x238],eax
mov [esi+0x234],eax
mov [esi+0x228],eax
popad
}
}
我们也能够直接call爆炸的过程,或者结束的过程,这也是常用的方法
[C] 纯文本查看 复制代码 for(int i=1;i<4;i++){//第一个是自己,我们跳过
long pp=p;
pp+=i*4;
dbg(2,pp);
__asm{
pushad
mov esi,[pp]
mov esi,[esi]
mov ecx,esi
mov eax,0x4e71f0//爆炸
//mov eax,0x4e6c90//结束
call eax
popad
}
}
当然,我们还能通过指针来实现这个操作。主要原因,GCC中的汇编(GAS)超难写,格式简直了…… 下面就是使用MinGW编译的例子。
ra2cheat.zip
(12.78 KB, 下载次数: 244)
我们再优化一下算法,让程序能够自适应通杀2个到8个国家,算法大概就是下面这个样子
[C] 纯文本查看 复制代码 HMODULE baseAddr=GetModuleHandle(NULL);
long *p=*(long**)((long)baseAddr+0x006A871C);
for(int i=1;i<8;i++){//第一个是自己,我们跳过
long pp=p[i];
if(pp){//保证指针非空,不然崩溃程序
*(long*)(pp+0x230)=0;
*(long*)(pp+0x238)=0;
*(long*)(pp+0x234)=0;
*(long*)(pp+0x228)=0;
}
}
我们的小视频环节(没有开启快速游戏嗷!!!) 链接:https://pan.baidu.com/s/1FSdffvhtc6uBqqWJzsVRcA 提取码:fafi
|