论坛还没有关于传奇私服外挂开发的文章,于是把我前段时间开发过的一款外挂分享出来。传奇私服如火如荼,经过我们统计,有1000多个传奇私服登陆器。本文只针对一款特定的私服做外挂开发,但原理都是一样的。本文有基础介绍,有难点分析,有针对玩家挂机体验做优化,有源码(见附件)。但文章只做研究用,请勿用来做商业用途,破坏游戏的平衡性。
下图是外挂挂机的截图:

界面、功能虽简单,但外挂主要功能都有。且不会被反外挂检测到,这款私服的反外挂是ntprotect:

有人会问,传奇私服大都有内挂:

为什么还要做私服的外挂?因为内挂是要花钱的, 一个账号要花xx RMB。而且本文的外挂是基于内存挂,不是像大漠插件的图色+键鼠模拟外挂,是可以多开的。多开的意义不用我说。
好了,进入主题吧:
上文提到了,本文是基于内存的Call挂,即注入DLL到游戏,调用游戏内的功能函数实现挂机。除了内存挂,还有图色挂和脱机挂。
图色外挂就是识别游戏的图片,来判断怪物,怪物在什么地方,一般模拟键鼠来挂机,优点是开发快速,缺点是一台机器只能挂一个游戏,不能实现多开来挂机。因为键鼠分不了身啊。
脱机挂是分析清除了游戏的传输数据协议,直接收发包来实现挂机。
这款挂是基于C++加内嵌汇编开发的,常用的开发语言还有易语言。
CheatEngine、X32dbg、IDA、VT虚拟化调试器。由于游戏有反外挂,会检测调试器,所以要上VT。VT现在好多群里在买,一个月300左右。

当然有实力也可以自己开发一款,这里给出一篇看雪的文章:https://bbs.kanxue.com/thread-214916.htm
这个步骤很关键,因为是内存Call挂,自然需要逆向分析出各个关键数据存放的地址。不过大家放心,私服是非常少,或者不会更新里面数据的基地址的,所以找一遍地址就行。下面介绍关键数据和Call的找法:
发现直接搜索血量能搜索到,但CheatEngine查找谁访问的基地址,怎么找都是找不到的。于是想到游戏或者反外挂做了手脚,因为这是对外挂最需要的数据。
后来搜减少、减少的数值(类型为2字节),逐步缩小范围后找到了基地址:
[[[[F74388]+834]+4]+x*4]+582
x为0代表自己的血量,x为其它数值代表怪物的血量。
这个CE搜索人物的坐标,然后查找谁访问的基地址,可以找到:
X坐标: [[[[F74388]+834]+4]+x*4]+908
Y坐标: [[[[F74388]+834]+4]+x*4]+A00
x为0代表人物的坐标,但也有例外,有很小几率x是0怪物的坐标。
这里科普下为什么搜索到人物的坐标后,要CE再查找谁访问的,因为搜索到人物的坐标是个堆栈地址,每次程序重启后,都不一样,所以要找出基地址。

上图右边的CE绿色的就是基地址。一般要查找几轮才能到达基地址,如下图:

具体可以看网上的教程,这里不再详述。
按上面基地址的公式,观察内存中的数据可以得到:
[[[[F74388]+834]+8
这个不能CE的查找谁访问找到,定位方式是:先用x32dbg找到怪物基地址。查找的方法可以借鉴网上的方法,但一般是对某个和人物有关的功能下断点,比如骑马、下马,bp Send后,栈回溯,Go到栈中的函数,可以看到上下文汇编代码有出现人物的基地址。
找到怪物基地址后,用CE搜怪物名字如”稻草人”得到地址A。再用CE搜A是哪个地址访问的值,得到地址B。最后B减去怪物基地址得到偏移。所以怪物名字基地址:
[[[[F74388]+834]+4]+x*4]+C24
选择一个怪物攻击,CE搜1,停止攻击怪,CE搜0,反复几次,然后查找谁访问或写入的,得出基地址:
F7A8A4
方法同上,略:
[[0091bb2c]]+8
这个比较难用上面的方法搜索到,使用CE工具里面的Structure dissect看的,在里面输入物品结构体的地址:0091bb2c。然后逐级展开结构体:

最终确认地面物品坐标:
[[[[[[0091bb2c]]+4]+x*4]+4]+0]+8 //X坐标
[[[[[[0091bb2c]]+4]+x*4]+4]+0]+C //Y坐标
x为第几个物品。
CE里还有个工具也很好用,若搜到数据后,不想查找对应的基地址,可以用指针扫描器:

填入扫描的地址,开始扫描后,多重启几次游戏,用这个工具对比,即可找到基地址。
查找的方法是下断发包函数(bp send),然后分析栈中的函数,找到如下call:

然后查找eax的基地址,之后调用0x701620就行了。0x701620是因为游戏exe的基地址是0x400000,加上0x301620就是了。
这里给出捡物品的代码:

同样是下断发包函数(bp send),然后分析栈中的函数,找到如下call:

传奇游戏有走路和跑步call,且有8个方向,只是参数不一样。参数的确定,可以分别向8个方向走路或跑步,代码如下:

我们点击小地图上的点时,是可以自动寻路的。于是想去找这个Call。但是并不顺利,这个call查找花了我好多功夫,一开始查找的call,是需要开启小地图,且寻路后鼠标会黏在小地图上。
然后想到使用A*或者B*算法去寻路,但需要提前制作好二值化地图数据,所有地图都需要制作,而且以后地图更新了,我们程序也得更新。
经过研究,找到完美call的方法: 先找到寻路状态的地址,寻路为1,不寻路为0。然后发现目的坐标写在后面,对其下硬件写入断点,断下函数逐层栈函数进行IDA分析。得到的call如下:

为了代码稳定起见,部分代码改成了C++实现:

为什么部分代码改为C++写就稳定了呢,因为比如遍历怪物数组时,怪物刷新了,导致访问数组就会崩溃。再比如A指向一块内存B,访问B的某偏移获得怪物某属性,而B此时释放了,怎么办?
解决方法是:先读取是否是有效的内存,再进行操作,比如使用ReadProcessMemory并判断返回值。
这个也是比较难定位的,就用自己写的程序(OffsetFind,见附件)去比较数组,得出来角色\怪物的类型。
原理是对比游戏内存中两个怪物数组的数据,设置好过滤条件,然后找出不同的地方:

比较出来的结果例如下图:

找出来的怪物类型如下图:

分析略。见附件代码:

找法:CE搜索背包的第一格物品名称,并下断。然后把第一个物品移动到其它格子。会在mov ecx,00000426断下:

再根据上面的代码推导出公式:
[[0091BCDC]]+(0xC72+0*0x213)*8+1
[注意]看雪招聘,专注安全领域的专业人才平台!
最后于 2025-2-21 15:29
被yirucandy编辑
,原因: