吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 15066|回复: 157
收起左侧

[游戏安全] 通杀爆改 Unity FPS 游戏系列-第一章:常规搜索+通杀结构解析

    [复制链接]
lyl610abc 发表于 2023-9-6 12:27
本帖最后由 lyl610abc 于 2023-9-18 20:09 编辑

本篇为 Windows 篇,Android 篇将和 @正己  梦幻联动,敬请期待

索引

通杀爆改 Unity FPS 游戏系列-序章:介绍及游戏下载

通杀爆改 Unity FPS 游戏系列-第二章:HOOK实现全屏改血+秒杀

通杀爆改 Unity FPS 游戏系列-第三章:il2cpp mono 差异

环境准备

作为第一章,需要的准备并不多,只需要修改神器:Cheat Engine 即可

在论坛中可以直接下载:https://down.52pojie.cn/Tools/Debuggers/CheatEngine_v7.5.exe

除此之外就是拿来开刀的 DEMO 程序,可以在序章篇获取

本章内容

本章将先以 windows mono 游戏包为例实现以下功能:

  • 锁血
  • 高跳
  • 移速
  • 无限子弹

先介绍常规修改思路,然后再介绍通杀方法

windows il2cpp 游戏包的修改逻辑类似,主要在原理上会有区别,限于篇幅这篇不展开


功能展示

锁血

image-20230904182756122

高跳


移速

移速


无限子弹

无限子弹


基本思路

在不考虑 CE 修改器的 mono 功能下,基本的修改思路是:

  • 搜索数值
  • 修改数值
  • 查看数值引用情况(可选)
  • 修改赋值代码(可选)
  • 分析数据结构(可选)

锁血

这里以锁血这个功能点为例,演示基本思路的改法

搜索数值

玩家的血量数值就在左下角,直接搜索即可

为照顾部分萌新,这里从打开修改器开始演示,熟悉的小伙伴可以跳过这部分

查看所有进程

image-20230905100856921

打开游戏进程

image-20230905101035543


打开后:

image-20230905101134015


修改搜索类型

image-20230905102644818

将搜索类型改成 Float 浮点数,像玩家血量这种数值通常都是浮点数

在计算机中,浮点数采用的编码格式是 IEEE 754

简单地理解就是在内存中,100.00 被编码为 0x42c80000 (十六进制)

数据本身是不变的,核心在于以什么方式去解读这个数据

打个比方,当你看到 10 这个数据时,如果以十六进制去解读则 0x10 = 16 ,如果以二进制去解读则 binary(10)=2

这里给一个浮点数和十六进制互相转换的网址:https://gregstoll.com/~gregstoll/floattohex/

image-20230905103256130

关于数据类型的内容这里不再展开,有兴趣的可以回顾我以前的帖子:逆向基础笔记十一 汇编C语言基本类型


搜索目标数值

浮点数搜索

修改完搜索类型为 Float 后,填入目标为玩家当前血量即可

image-20230905104830702


十六进制搜索

这里也可以选择类型为 默认的 4 Bytes,然后直接搜索 100 对应的十六进制: 0x42c80000,之后每次搜索也是以十六进制去搜

image-20230905104934405

会发现找到的数据量很明显比直接搜 Float 要少,因为如果是浮点数还有精度问题,这里用十六进制去搜,就相当于锁定了精度


筛选出目标数值

然后等玩家血量变化后,再次搜索数值,最终得到的唯一值就是玩家的血量

浮点数筛选

image-20230905105034449


image-20230905105217346


十六进制筛选

浮点数 90 对应的十六进制为:0x42b40000,在前面提到的:https://gregstoll.com/~gregstoll/floattohex/ 可以转换得到

image-20230905105348987


可以发现通过常规搜浮点数和直接搜十六进制得到的结果的 Address 内存地址是一样的

这里都是 1AFDAAC0,佐证了前面说的,在内存中浮点数是按 IEEE 754 标准存储的


锁定数值

image-20230905110142685


image-20230905110721456


锁定:

image-20230905110451564

锁定这个值就完成锁血功能了


修改数值:

image-20230905110428203


修改类型:

image-20230905110745720


image-20230905110813091


image-20230905110827226


image-20230905110841744

可以看到,将类型修改为 4 Bytes 和 Hexadecimal(十六进制)后,得到的结果和用十六进制搜索是一致的


爆杀思路

  1. 打开 mono 功能
  2. 分析对应程序集
  3. 分析对应类
  4. 分析对应函数
  5. 精准定位,一击必杀

锁血

依旧是以锁血为例子,演示 CE 修改器的 mono 功能

打开 mono 功能

在前面附加完游戏以后,会发现 CE 修改器多了个 mono 选项

image-20230905133718769


点击激活该功能

image-20230905133825663


分析对应程序集

点击 .Net Info 查看程序集信息

image-20230905133944293


点击以后会展示当前游戏的所有程序集,这里可以看到连 unity 引擎提供的程序集都包含在这里面

image-20230905134355278


过滤掉一些默认的程序集:Mono.Security、SystemXXX、UnityXXX 等

可以很轻松地找到游戏里相关逻辑的程序集在:

Assembly-CSharp、fps.XX 中

和源代码的关系(扩展内容)

简单说明一下这里的数据的由来,算是扩展内容,可跳过

Assembly-CSharp

可以看到只有一个类:ProduceEnemy,这个类是我新加的逻辑

为了演示,并没有走原有项目的路径格式,以此来说明源代码和这里的对应关系

ProduceEnemy 默认放在Assets目录下且没有自定义操作,所以最后是走到了 Assembly-CSharp 中

详细的对应关系可参考:Unity关于程序集(Assembly )的那些事

这里我只是稍微提一下,不作为重点

image-20230905135218485

相关的游戏源码为:

using System.Collections;
using System.Collections.Generic;
using Unity.FPS.Game;
using Unity.FPS.Gameplay;
using UnityEngine;
//注意这里并没有声明任何的命名空间,所以在类中直接就是 ProduceEnemy 而不是 XXX.XXX.ProduceEnemy
public class ProduceEnemy : MonoBehaviour
{

    //这里主要说明命名空间对应到 CE 修改器中的程序集,具体逻辑不在这里展示...
}

fps.AI

image-20230905140101845

照样给出相关的游戏源码:

using System.Linq;
using Unity.FPS.Game;
using UnityEngine;
using UnityEngine.Events;

//注意这里的这里的命名空间影响的是类的信息,程序集信息则跟路径和自定义有关系
namespace Unity.FPS.AI
{
    public class DetectionModule : MonoBehaviour
    {
        //省略
    }
}

找寻血量对应程序集

先说思路:

我们的目标是修改我们自己的血量

因此有 2 种找法:

  • 找自己,自己底下肯定有一个血量
  • 找血量,当自己收到伤害时,一定会触发到血量的变动,这个时候就能够定位到自己的血量

因为这个游戏是我自己搞出来的 demo,所以对这些程序集、类都比较熟悉,但是”假装“不知情的情况下

可以通过检索关键词来过滤,通常来说程序员是有一套命名规范的,不会随便给类或方法瞎起名,不利于后期维护

举个例子,一个函数的功能是受到伤害,通常情况下就是对应含义的英文组合:TakeDamage 之类的

如果瞎起名,起个 Abcd,怕不是分分钟被暴打 φ(* ̄0 ̄)

下面给一组比较常用的关键词:

关键词 相关点
Heath 生命值
Damage 伤害
Speed 速度
Character 角色
Player 玩家
Weapon 武器
Pickup 拾取

找自己

找自己,很明显就是找角色,正好可以使用我上面提供的关键词:Character 或 Player

不难通过关键词锁定目标:fps.GamePlay 下的 Unity.FPS.GamePlay.PlayerCharacterController

image-20230905145819634


找到了关键点,接下来要做的就是拿到这个类对应的内存地址,然后再以这个类的结构去解析

不难发现 CE 修改器提供了个很棒的功能:Lookup instances 查找实例,通过这个方法查找到的结果就包含了我们想要的内存地址

因为是 demo 小游戏,所以找出了的结果很少(结果很多时可以用后面找血量的方法),此次演示时只有 2 个:

image-20230905150702227


在结果比较少的下,还是比较好筛选的,就是查看对应的值是否合理

比较好鉴别的就是 System.Single (C# 里的 Float),查看它的数值是否合理

这里以 GravityDownForce 为基准,发现第一个地址解析出来的数值相对合理:20.0

再看下一个地址就是一个乱七八糟的内容,排除:

image-20230905151003157


于是回到第一个地址,找到 m_Health,并点击这个 + 号,展开结构

image-20230905151125969


很快就能锁定 2 个关键数值:

  • MaxHealth 最大生命值
  • CurrentHealth 当前生命值 (这里之所以有个 BackingField 只是因为源代码中将它的作用域设置为 private 私有)

image-20230905151248679


终于到了心心念念的修改环节:

右键→ Browse memory region (查看内存区域)

image-20230905151949896


接下来会弹出一个新的界面:

image-20230905152142127

选中上面选中的部分的开头,右键 Add this address to the list (将地址加入到列表中)

image-20230905152357279


出现了新的窗口:

image-20230905152518197


将类型改成对应的类型,这里是 Float :

image-20230905152559091


然后回到 CE 修改器的主界面:

image-20230905152631627

到这里就和前面的锁定数值是一样的了,不再赘述


找血量

找血量,关键词:Health,不难定位到:fps.Game 下的 Unity.FPS.Game.Health

对于血量,很明显不大好像前面找自己一样直接通过 Lookup instances 来定位

原因很简单:敌人的血量也包含在内,导致其实例会比较多,当然也可以根据玩家和敌人的不同点来筛选,但相对费时费力,不推荐

于是需要新的方法:当自己血量发生变化时,断点,其上下文环境一定包含我们需要的地址

先找到扣血的函数:TakeDamage

image-20230905155511757


双击函数以后,跳转到对应的汇编代码段:

image-20230905155556082


然后选中开头,按快捷键 F5 下断点,第一次下断点会询问需要附加进程,是否继续,点 Yes (是)

或者通过选项卡,Debug → Toggle breakpoint

image-20230905155932556


image-20230905155700834


成功下断点以后,选中的那一行会变绿色

image-20230905160126028


然后回到游戏里,被敌人射一枪 `(>﹏<)′ ,触发断点

image-20230905160320968


这个时候会发现,右边的寄存器全都变红了,变红表示和先前比有了变化

通常来说,我们需要的地址就在寄存器或者堆栈中

寄存器就是这里的 EAX EBX....

而堆栈则需要用 ESP 去查看

这里的 ESP 指向堆栈顶

image-20230905160608321


可以选中下面的第一格,然后快捷键 Ctrl+G 或者右键 Goto address

image-20230905161005359


image-20230905161035410


然后将地址改成 esp 跳转得到:

image-20230905161214462


再修改一下显示类型(Ctrl+5 或者 右键 Display Type > 4 Byte hex):

image-20230905161311889


除开第一个地址是调用完这个函数的返回地址之外,剩下的头几个地址都是函数参数

具体原理可以回顾以前的:逆向基础笔记七 堆栈图(重点)

只想改游戏的萌新可以不用管这个

记住这个规则:在函数开头断点后,关键的数据在 EAX EBX ... EDI 或者 ESP 指向的地址从第二个开始 之中

image-20230905161515263


这个时候已经其实已经可以一个个试哪个地址是我们需要的了

但也可以利用寄存器变化会变红的特性做下筛选,同一把游戏,我们受到伤害时,生命值实例的内存地址是不会变的

因此再让游戏跑起来后,让敌人再攻击我们一次( •̀ ω •́ )✧

点击 Run 按钮让游戏跑起来即可

image-20230905162336310


再次断下来以后得到:

image-20230905162418754

可以看到 只有 EBX ECX EDX 还有堆栈里的某些数据没有变


接下里就可以一个个试了,当然对照函数的参数,其实是可以推测出堆栈中内存地址的含义的

函数原型是 :

image-20230905162646117

一个就是伤害值浮点数,还有一个是伤害来源,但在汇编之中其实还会多出一个参数:这个类实例本身的内存地址

因此对照堆栈里的信息可以得出对应地址的含义:

内容 含义
ESP 0071D3C4 完成调用后要返回的内存地址
ESP+4 1A940410 我们想要的指向 Health 的内存地址
ESP+8 41200000 damage 伤害,这里把这个十六进制按 IEEE 754 去解读,其值就是:10,也就是敌人对我们造成了 10 点伤害
ESP+C 04170640 damageSource 伤害来源

接下来就是验证这个地址是否为我们想要的,可以把地址丢回原本的 .Net Info 里

image-20230905163535687

很明显这个地址就是我们想要的,之后的步骤就同上面找自己一样,这里不再赘述


但丢回 .Net Info 有个问题,就是如果数据对不上不能解析时,可能会报错,不是很方便

再介绍另一个验证的途径:

回到先前的 Memory Viewer 界面,Tools --> Dissect data/structures 或者快捷键 Ctrl + D

image-20230905163852685


在新出来的界面把要解析的内存地址丢进去,然后 Structures --> Define new structure 或者快捷键 Ctrl + N

这一步建议取消断点,然后让游戏跑起来以后再操作,不然容易失败

image-20230905164154612


然后点击后,CE 修改器会自动根据你的内存地址推导出它的结构,因此可以验证数据是否为我们想要的

image-20230905164413336


image-20230905164523847

得以验证确实是该地址就是我们所需的指向我们角色自身血量的


作业

你已经学完 1+1=2 了,是时候来点高等数学了(〃 ̄︶ ̄)( ̄︶ ̄〃)

高跳、移速和无限子弹都是类似的原理,就当作是课后作业了


总结

  • 基本思路中介绍了搜索浮点数数值可以使用十六进制搜索法更精准锁定数值
  • 数值本身不重要,重要的是我们如何去解析这个数值
  • 要善于结合关键词来对游戏逻辑进行分析,关键词可以起到很好的推进作用
  • CE 修改器的 mono 功能主要是帮助我们更方便去解析游戏的结构
  • CE 修改器的 Lookup instances 查找实例更适用于实例少的情况,实例多时则需要动态断点分析

这一期只介绍了 CE 修改器 mono 功能最基础的玩法,还有动态调用游戏函数等内容限于篇幅放在后面

关于 CE 修改器的 mono 功能原理限于篇幅也暂未展开,也放到后面

使用 Visual Studio 编写外挂部分也留到后面

有能力的小伙伴可以试试做出全屏秒杀功能,刚进入游戏就完成击杀并胜利:

5ede2a4e0a9d04760e8cf5512de1320b


最后的最后 @正己  大佬 ,快去催更他出 Android 版

放 2 张 @正己 大佬的图

image-20230905171305365

image-20230906134358778


免费评分

参与人数 89吾爱币 +98 热心值 +85 收起 理由
zcjhnb + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
chenztai + 1 我很赞同!
Puremilk + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
liujing8018 + 1 + 1 用心讨论,共获提升!
FFF全部成为F + 1 + 1 感谢教程
JJ啊 + 1 + 1 谢谢@Thanks!
呆萌爱士 + 1 + 1 用心讨论,共获提升!
shaoniu + 1 + 1 眼睛会了,脑不会!
luckydat + 1 用心讨论,共获提升!
zanzan1 + 1 + 1 我很赞同!
Lightstarx + 1 + 1 我很赞同!
gunxsword + 1 + 1 我很赞同!
iiiiiu + 1 + 1 谢谢@Thanks!
lynxtang + 1 + 1 谢谢@Thanks!
涛之雨 + 7 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
斜雨孤泪 + 1 Unity 程序员表示很赞!!
Capybara + 1 + 1 谢谢@Thanks!
Myself999 + 1 + 1 我很赞同!
apple52 + 1 + 1 热心回复!
ganqingxi + 1 + 1 我很赞同!
MayMayHai8971 + 1 + 1 谢谢@Thanks!
萌宝 + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
da187jk + 1 + 1 谢谢@Thanks!
jay0118 + 1 我很赞同!
luoyunxiaotian + 1 + 1 用心讨论,共获提升!
tioo888 + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
yyx2004 + 1 + 1 谢谢@Thanks!
che_shen + 1 用心讨论,共获提升!
Boly + 1 我很赞同!
guo2xl + 1 + 1 谢谢@Thanks!
zls黑战魔 + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
yixi + 1 + 1 谢谢@Thanks!
叶樱枫 + 1 + 1 谢谢@Thanks!
Sionis + 1 + 1 谢谢@Thanks!
YCK0701 + 1 + 1 谢谢@Thanks!
allspark + 1 + 1 用心讨论,共获提升!
papapo + 1 + 1 用心讨论,共获提升!
fanganer + 1 + 1 我很赞同!
1113519447 + 1 热心回复!
wenzijun + 1 + 1 热心回复!
kun0219kun + 1 + 1 我很赞同!
huangmo1 + 1 + 1 我很赞同!
qiyefeng + 1 + 1 谢谢@Thanks!
厉害呢。。 + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
长得太帅怪我咯^ + 2 + 1 谢谢@Thanks!
道冲渊风 + 1 + 1 热心回复!
theStyx + 2 + 1 谢谢@Thanks!
随风起舞 + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
Bay + 1 + 1 用心讨论,共获提升!
奈空林 + 1 + 1 我很赞同!
muade + 1 + 1 用心讨论,共获提升!
XEROXEON + 1 + 1 热心回复!
Cyude + 1 + 1 谢谢@Thanks!
tarodp + 1 + 1 热心回复!
崛川雷鼓 + 1 + 1 我很赞同!
yxpp + 1 谢谢@Thanks!
cscscscs + 1 + 1 我很赞同!
debug_cat + 2 + 1 用心讨论,共获提升!
zx2000 + 1 + 1 我很赞同!
Mint_Grass + 1 + 1 用心讨论,共获提升!
hikansakuratan + 1 + 1 热心回复!
CrazyNut + 3 + 1 用心讨论,共获提升!
乔帮主 + 1 + 1 谢谢@Thanks!
walykyy + 1 + 1 谢谢@Thanks!
tjh13423573428 + 1 + 1 鼓励转贴优秀软件安全工具和文档!
sorryzzital + 1 + 1 谢谢@Thanks!
eloader + 1 + 1 热心回复!
fengbolee + 2 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
秋总大人 + 1 + 1 我很赞同!
Hulen + 1 + 1 谢谢@Thanks!
忆魂丶天雷 + 2 + 1 用心讨论,共获提升!
lightf + 1 + 1 谢谢@Thanks!
Zhaofeiyan + 1 + 1 用心讨论,共获提升!
w3023185276 + 1 + 1 鼓励转贴优秀软件安全工具和文档!
SerryKl + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
leiguoguo + 1 用心讨论,共获提升!
gaosld + 1 + 1 热心回复!
ouyang12138 + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
tuo哥哥 + 1 + 1 我很赞同!
lushihao + 1 + 1 这么多图新辛苦了
杨辣子 + 1 + 1 热心回复!
是随风啊 + 1 热心回复!
wystudio + 1 + 1 用心讨论,共获提升!
BonnieRan + 1 + 1 谢谢@Thanks!
DEATHTOUCH + 3 + 1 以前也用CE改过一些东西,浮点数用16进制搜确实好思路!
猫捉老虎 + 2 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
zhifubao + 1 + 1 热心回复!
waltzofjack + 1 大佬让我们这些小白刚出来新手村,就直接上打BOSS?
无问且问 + 1 + 1 很牛逼,想问下有没有安卓版本

查看全部评分

本帖被以下淘专辑推荐:

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

 楼主| lyl610abc 发表于 2023-9-7 11:03
本帖最后由 lyl610abc 于 2023-9-7 11:08 编辑
乔帮主 发表于 2023-9-7 10:18
记住这个规则:在函数开头断点后,关键的数据在 EAX EBX ... EDI 或者 ESP 指向的地址从第二个开始 之中

...

在这里的例子中,我们下断点的函数是 Health.TakeDamage
断下来的时机是我们受到伤害的时候
我们想要在这个时机拿到的数据是:我们自己的 Health
我们自己的 Health 肯定是在断下来这一刻的上下文环境里:也就是 寄存器(EAX EBX ... EDI) 或 堆栈(ESP) 里
好比:正己老师在路上走路的时候,突然被人给了一拳,在他被打的这一刻
肯定是能知道是谁打的他(伤害来源 damageSource)
他受了多少伤害(有多痛 damage)
是他自己被打了(指向自己的Health)
乔帮主 发表于 2023-9-7 10:18
记住这个规则:在函数开头断点后,关键的数据在 EAX EBX ... EDI 或者 ESP 指向的地址从第二个开始 之中

这一句没有看懂,能说说吗

免费评分

参与人数 1吾爱币 +1 热心值 +1 收起 理由
lyl610abc + 1 + 1 用心讨论,共获提升!

查看全部评分

 楼主| lyl610abc 发表于 2023-9-7 11:45
乔帮主 发表于 2023-9-7 11:31
感谢解释,是我没有描述清楚, EAX EBX EDI因为断点的时候变红了,说明有变换,你说的伤害 和来源还有收 ...

关键值可能通过寄存器(EAX EBX .. EDI)传递,也可能通过堆栈(esp)来传递
对于初学者来说,可以粗浅地理解从 EAX EBX .. EDI 和 ESP 里找就行
但实际情况可能更复杂,因为不同函数可能会有不同的调用协定,如:stdcall cdecl fastcall
关于调用协定可以参考我以前的帖子:https://www.52pojie.cn/thread-13 ... 8%E5%8D%8F%E5%AE%9A
现在一般都是以 stdcall 居多,本次的 demo 也是 stdcall
具体情况还需要具体分析,我这里想要表达的点是:
在合适的时机做 HOOK ,然后去分析它的上下文环境(寄存器+堆栈),再深入的话就要去分析反汇编代码了
正己 发表于 2023-9-6 12:33
这个系列给个精华,别画饼了,《安卓逆向这档事》都还有好多没更新呢

点评

那是不是加上一篇这个: 通杀爆改 Unity FPS 游戏系列-序章 https://www.52pojie.cn/thread-1829474-1-1.html  详情 回复 发表于 2023-9-6 17:06
Ming520Ying 发表于 2023-9-6 12:48
确实挺细的,但我还是不太懂,害
fa22 发表于 2023-9-6 13:24
  有点意思感谢了!
yaodm2019 发表于 2023-9-6 13:53
我挺期待全屏秒杀的。
平时自己也改游戏,最烦的就是找到关键特征值
gamingnow 发表于 2023-9-6 14:26
特地过来支持了
艾莉希雅 发表于 2023-9-6 14:36
好好好,等后续更新
这章是mono,下一章估计是il2cpp了吧
BonnieRan 发表于 2023-9-6 15:12
最近比较感兴趣il2cpp,能看懂一点点,期待Android的
wystudio 发表于 2023-9-6 15:25
请问楼主这篇文章是由markdown语法写的吗?
 楼主| lyl610abc 发表于 2023-9-6 15:34
wystudio 发表于 2023-9-6 15:25
请问楼主这篇文章是由markdown语法写的吗?

是,用markdown 配合 oss 图床
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则 警告:本版块禁止灌水或回复与主题无关内容,违者重罚!

快速回复 收藏帖子 返回列表 搜索

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-4-28 19:03

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表