美文网首页游戏外挂
实战帝国时代(罗马复兴)外挂编写(CE找基址、C#实现)

实战帝国时代(罗马复兴)外挂编写(CE找基址、C#实现)

作者: 老胡聊聊天 | 来源:发表于2018-11-02 22:23 被阅读12次

    一、启动游戏

    1、准备好帝国时代游戏、窗口化工具(D3DWindower.exe),运行游戏
    窗口化工具不是必须的,只是为了切换窗口方便。

    二、打开ce,找一下资源地址并修改

    2、打开cheat engine,选择游戏进程


    image.png

    3、先找下食物的吧
    食物初始有200


    image.png

    valuetype选择float,value输入200,然后点击firstscan,找到39个


    image.png

    然后造一个农民,这时候食物就剩150了


    image.png

    ce里面输入150,然后nextscan,就剩一条了,太easy了(有的游戏可能需要搜更多次),双击一下地址添加到地址列表


    image.png

    双击一下下面列表中的value列,修改值为15000


    image.png

    回到游戏发现已经生效了


    image.png

    同样的方式很容易就可以找到其他几个资源。


    image.png

    然后就可以爽一局了,但是也只有一局,因为游戏重新开始后,地址通常就变了,还要再找一次。

    这个问题就要通过基址+偏移的方式来解决了。

    三、找到基址和偏移

    右键食物的地址列,点击“find out what writes to this address”


    image.png

    打开一个列表,只有一条,双击一下,看到一句话“the value of the pointer needed to find this address is probably 11BA4CD0”,就是说我们要找的指针的值可能是11BA4CD0


    image.png

    这句话是说,某个地址的值是可能是11BA4CD0,而11BA4CD0恰好就是食物的地址;反过来说,就是我们如果知道这个地址,取他的值,就是食物的地址了;如果这个地址是个静态地址,那么我们就大功告成了,那么这个地址是多少呢?我们用11BA4CD0这个值来搜索,看看有多少个地址的值是它。

    新开始一个scan,选择hex值,搜索,好巧,只有一个,这个就是我们找的地址,按照刚才说的,每次只要找到了这个地址,读他的值,就是食物的地址了。


    image.png

    然而经过试验发现,这个地址也是每次重启游戏就会变化的。
    其实不用试验,图中可以看到这个地址是黑色的,黑色就代表是动态地址,绿色才是静态地址,我们需要的是一个静态地址。

    失败了吗?没有!继续找。我们现在知道了,有且只有11B737F8这个地址保存了食物的地址,那么游戏开发者想改变食物的值,一样要通过11B737F8这个地址来找,他每次重启游戏是怎么找到11B737F8这个地址的呢?肯定是某个地址存储着这个地址,如下图,一层套一层,某个地址存储着11B737F8,11B737F8又存储着11BA4CD0,甚至更多级,我们要做的就是耐心往上溯源。

    image.png

    搜索一下谁保存了11B737F8这个值,然而并没有(如下图)!!!


    image.png

    那么这个值怎么产生的呢。。。看看谁写了这个地址(如下图),很尴尬,白板。——解释一下,这个地址确实会有人写,但是是在游戏初始化的时候,初始化之后,我们就监控不到了,那么怎么办呢,刚才说到了,初始化之后,游戏开发者想要改变食物的值,仍然需要知道这个地址,那么他虽然不会写这个地址,但是他会读啊,那么我们来看看谁读了这个地址。


    image.png

    看看谁读了,终于有了(如下图),某个地址存储着11B737A8,而不是11B737F8
    请注意看,下面的列表中写着,EAX=11B737A8,上面写着EAX+50,EAX+50刚好是11B737F8,也就是说,游戏开发人员并不是用某个地址存了11B737F8,而是在某个地址存了11B737A8,每次他根据那个地址获取到11B737A8之后,再+50,就得到了11B737F8,哈哈,让我们发现了,这样我们就知道规律了。


    image.png

    来看看哪个地址存着11B737A8,我擦,有15个之多,而且全都不是静态地址,还得往上找,这就相当于往上溯源的过程中出现了岔路,只能看运气了,好在ce比较智能,往往把可能性大的放在上面,从第一个开始尝试吧。当然,如果失败了,就再回到岔路口换下一条路继续尝试就行了。


    image.png

    我把前三个都加到下面列表中一个一个尝试,看看谁读了这些地址。


    image.png

    发现后两个都是白板,没人读这俩地址,第一个打开之后有两行,分别是下面这两个图。
    第一个图又回到了11B737A8这个地址,没啥意义。


    image.png

    第二个图出现了一个新的值1173B390,然后加上ecx*4(ecs是1)得到我们前面的1173B394


    image.png

    搜索1173B394得到新的地址07AF1D08


    image.png

    这个地址依然是一个动态地址,还得往上找。不过我们先总结一下。

    07AF1D08 ==> 1173B390
    1173B390+4 = 1173B394
    1173B394 ==> 11B737A8
    11B737A8+50 = 11B737F8
    11B737F8 ==> 11BA4CD0
    11BA4CD0 ==> 食物的数量
    

    ok,我们手里的王牌是07AF1D08,用上面的方法继续战斗
    07AF1CC8+40 = 07AF1D08


    image.png

    搜索07AF1CC8,我的天!出现了两个绿色的地址,很可能就是基址,2A06B0和3BE7A8


    image.png

    插入一个知识点,静态地址的真正地址并不是2A06B0这样的,双击之后可以看到是进程名+地址,也就是“Empires.exe+2A06B0”,记住就好,后面要用到。


    image.png

    我们再总结一下,现在除了哪个是真的基址不清楚,其他都知道了

    2A06B0或3BE7A8 ==> 07AF1CC8
    07AF1CC8+40 = 07AF1D08
    07AF1D08 ==> 1173B390
    1173B390+4 = 1173B394
    1173B394 ==> 11B737A8
    11B737A8+50 = 11B737F8
    11B737F8 ==> 11BA4CD0
    11BA4CD0 ==> 食物的数量
    

    知道上面的流程之后,我们来写一个公式把上面这些串起来,点击“Add Address Manually”,然后勾选pointer


    image.png

    把上面的公式填写进来,这个是从下往上看的,最下面写基址,右边可以看到基址对应的值是07AF1CC8
    然后倒数第二行,07AF1CC8+40这个地址的值是1173B390
    1173B390+4这个地址的值是11B737A8
    11B737A8+50这个地址的值是11BA4CD0
    11BA4CD0就是食物的地址


    image.png

    看下这两行,值是一样的,都是15050。


    image.png

    说到这里,注意看一下地址列表的前四行,食物、木材、石头、黄金的地址,不难看出一个规律:
    食物地址+4=木材地址
    食物地址+8=石头地址
    食物地址+C=黄金的地址(注意这里是十六进制,12也就是C)

    那么我们可以用同样的方式写出木材的串联,只需要把最上面的格子改成4:


    image.png

    好了,说回到刚才2个基址的问题,基址通常只有一个,怎么确定哪个才是真正的基址,重启游戏即可。
    我们现在来重启游戏,然后用ce重新附加。

    可以看出,最前面的4个地址的值已经是??了,说明已经失效了。但是我们最后添加的两个串联,也就是食物和木材,依然是有效的。


    image.png

    我们把串联中的基址换成另一个,发现同样有效,那么说明这两个基址确实真的都可以。。


    image.png

    我们来改一下食物和木材的值,可以看到,立即就生效了,这样我们就不用每次都找一遍地址了。


    image.png image.png

    四、编写外挂

    ce算是一个开发环境,如果我们想把上面这一堆研究成果分享给小伙伴,不懂编程的他们未必能懂啊,最简单的方式当然是封装一个exe,双击就能开搞。

    这里我们用C#来实现一个(ce也能直接生成一个exe,有兴趣的自行研究一下)
    1、写一个方法,读取Empires进程,然后用上面的公式流程先找到食物的地址foodAddr,然后循环4次依次遍历食物、木材、石头和黄金,如果数值小于5000,就改成10000

    读写内存这里用到了一个MemorySharp框架(github传送门),这个并不是关键,类似的框架有很多,也可以用原生dll

    
            private static void findProcessAndHack_Empires()
            {
                var process = System.Diagnostics.Process.GetProcessesByName("Empires").FirstOrDefault();
                if (process == null)
                {
                    Console.WriteLine("未找到进程...");
                    Thread.Sleep(2000);
                    return;
                }
                var sharp = new MemorySharp(process);
    
    
                IntPtr baseAddr = sharp.Modules.MainModule.BaseAddress;
                int addr1 = sharp.Read<int>(new IntPtr(0x2A06B0));
                int addr2 = sharp.Read<int>(new IntPtr(addr1) + 0x40, false);
                int addr3 = sharp.Read<int>(new IntPtr(addr2) + 0x4, false);
                int foodAddr = sharp.Read<int>(new IntPtr(addr3) + 0x50, false);
    
                //IntPtr foodAddr = baseAddr + 0;
                //IntPtr woodAddr = foodAddr + 4;
                //IntPtr stoneAddr = woodAddr + 4;
                //IntPtr goldAddr = stoneAddr + 4;
                for (int i = 0; i < 4; i++)
                {
                    try
                    {
                        IntPtr addr = new IntPtr(foodAddr) + i * 4;
                        float val = sharp.Read<float>(addr, false);
                        //Console.WriteLine("i={0}, val={1}", i, val);
                        if (val < 5000)
                        {
                            sharp.Write<float>(addr, 10000, false);
                            Console.WriteLine("增加资源");
                        }
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine("error:{0}", ex);
                    }
                }
    
            }
    

    2、main方法里面每隔3秒调用一次上面的方法,加了一个try,这样即使写内存失败,也不会出错。

            static void Main(string[] args)
            {
                Console.WriteLine("hack程序运行成功,开启游戏开搞即可。");
                while (true)
                {
                    try
                    {
                        findProcessAndHack_Empires();
                    }
                    catch (Exception e)
                    {
                        Console.WriteLine(e);
                    }
                    Thread.Sleep(3000);
                }
    

    3、这样我们就实现了,每3秒写一次内存,只要你开着这个程序,四种资源就永远用不完,是不是比输入woodstock这种每次加1000资源的命令更爽?

    五、总结

    写的很啰嗦,如果你能看完,说明你真得是毅力很大,为了尽量写的详细,我把为什么这样操作都写上了。总的来说,分为几个步骤:
    1、根据游戏数值的变化找地址(需要注意的是数值类型的选择,比如有的是4byte,有的是float,这个得尝试了,发现不对就换一个试试)
    2、找到一次之后,就结合谁写/读了你的地址,来一步步跟踪,这是最关键的过程,也是最繁琐的过程,还带着一些运气,可能还需要稍微看一下汇编代码。我这个例子算是很简单了。找的过程中记得把偏移记录下来。
    3、都找到之后就简单了,用你熟悉的语言,按照流程读写内存就可以了。C++、C#、易语言是最常用的,其他的java、nodejs、python也都可以找到对应的读写内存的库。

    就酱,有问题可Q我376665005一起学习,请备注“帝国时代外挂探讨”。

    相关文章

      网友评论

        本文标题:实战帝国时代(罗马复兴)外挂编写(CE找基址、C#实现)

        本文链接:https://www.haomeiwen.com/subject/rnrtaftx.html