steam某塔防游戏破解
本帖最后由 yellowtail 于 2023-2-11 17:34 编辑## 概述
最近在玩Steam某款塔防游戏,玩了1000多小时,难度低的地图都过去了,点击模式过不去,竞速模式因为手残成绩不太理想,所以萌生了破解、逆向的念头,研究一下
## 省流
部分功能破解成功,但是被官方抓住了,封号了,无法联机(也就是长草,但是本地模式还是可以玩的
所以大家练习的时候,记得开小号尝试,大号被封了就很emo
游戏版本是 `34.3.6116`
## unity游戏破解思路
打开游戏安装目录,发现是 unity 游戏
unity游戏破解思路比较固定
运行时有两种: `mono` 或者`il2cpp`
`mono`步骤简单点,就是 dnspy 分析代码+修改il字节码(步骤较少,难度不一定)
`il2cpp` 步骤多点,如下所示
1. `Il2CppDumper` 提取c#代码结构,得到 `Assembly-CSharp.dll`、`script.json`、`stringliteral.json`和其他文件
2. `dnspy` 分析`Assembly-CSharp.dll`找到关键方法(函数)
3. `ida` 分析 `libil2cpp.so`(安卓)`GameAssembly.dll`(Windows)
4. `ida` 加载`script.json`、`stringliteral.json`
5. 找到关键修改点,用汇编转16进制网站,得到修改后的16进制,用`010`修改、保存(记得备份)
## 分析
步骤1、2、3、4,我之前的帖子已经描述过了,这里就不再赘述
[某unity游戏逆向破解](https://www.52pojie.cn/thread-1631028-1-1.html)
这里直接进入第五步
首先玩一下游戏,得到一些概念
|概念|关键字|
|--|---|
|防御塔|`Tower``Hero`|
|生命值|`Hp`|
|伤害|`Damage`|
|buff|`buff`|
|技能|`skill`|
|攻击范围|`Range` `Distance`|
|升级|`Upgrade`|
|金币|`gold``cost` `gain`(获得金币)|
|飞艇|`ddt` `moab`|
|折扣|`discount`(猴村的折扣)|
找了很多地方,尝试了一些修改,但是效果不太理想,这里放1个有效果的
## 升级折扣
`dnspy` 搜索 `GetTowerUpgradeCost`
(为什么搜索这个?因为我用关键字找了很多的地方,其它地方没有效果,只有这里有效果,所以才贴出来:keai )
看到签名如下
```
public float GetTowerUpgradeCost(Tower tower, int path, int tier, float overrideBaseCost = -1f, bool isParagonUpgrade = false)
{
return default(float);
}
```
可知:方法返回一个 float浮点数,表示当前防御塔升级所需要消耗的金币
用ida看一下实现
看红色区域
```cpp
LABEL_45:
v41 = *(_QWORD *)(a1 + 24);
if ( !v41 )
goto LABEL_50;
LOBYTE(v16) = FreeUpgrade == 0;
if ( (float)((float)((float)v16 * v14)
* (float)(1.0
- Assets_Scripts_Simulation_Simulation__GetSimulationBehaviorDiscount(// 得到折扣值
v41,
a2,
a3,
a4,
LODWORD(v34)).m128_f32)) < 0.0 )
return 0.0;
else
return (float)(int)Assets_Scripts_Simulation_SMath_Math__RoundToNearestInt(v42, 5);// 得到浮点数临近的int(floor)
```
大概的意思是:
1. 得到折扣系数
2. 如果折扣小于0(我实在想不到怎样可以小于0,那只能是程序员防御编程了,避免出bug,严谨!),那么就返回0.0
3. 否则用cost*discount, 再floor一下返回(RoundToNearestInt的作用)
其实,这一个方法里面有很多的修改点
1. 最后一行的v42就是折扣系数,5应该是原始价格(这里可能是ida识别错误,毕竟找不到v42的来演),那么可以修改v42或5
2. 如果折扣值小于0,就返回0,可以修改为去掉判断,直接返回0(jmp)
3. GetAreaDiscount得到折扣区域,我们可以加大这个返回值
4. GetDiscountMultiplier得到折扣系数,我们可以加大这个返回值
修改返回值,还比较费劲,如果代码返回立即数还好说,汇编也能改,如果返回寄存器或者需要寻址,就费劲了,所以我选择了最简单的,修改跳转逻辑,也就是2
可以看到,ida里的if语句,在汇编里是
```
il2cpp:0000000180B5A0AE 0F 5B D2 cvtdq2ps xmm2, xmm2
il2cpp:0000000180B5A0B1 F3 41 0F 59 D0 mulss xmm2, xmm8
il2cpp:0000000180B5A0B6 F3 0F 10 05 82 9C A1 01 movss xmm0, cs:Y
il2cpp:0000000180B5A0BE F3 0F 5C C1 subss xmm0, xmm1
il2cpp:0000000180B5A0C2 F3 0F 59 D0 mulss xmm2, xmm0
il2cpp:0000000180B5A0C6 0F 2F F2 comissxmm6, xmm2
il2cpp:0000000180B5A0C9 77 3F ja short loc_180B5A10A
```
看到`ja`就应该能想到`jmp`
`ja`是满足某种条件之后才跳转,跳到返回0的地方,我们改为无脑跳转`jmp`就好了
首先看一下调整偏移量
`77 3f` 是 `ja 0x41`
我们改为`jmp 0x41`就好
可以看到,`77`改为`eb`就行
转换网站是 (https://shell-storm.org/online/Online-Assembler-and-Disassembler/?inst=jmp+0x41&arch=x86-64&as_format=hex#assembly)
ida改完看一下效果
看起来是有效果的,那么上 010 修改一下
010偏移和ida有所差异(不知道为啥),我是通过搜索16进制找到的
搜索 `F3 0F 59 D0 0F 2F F2 77 3F` 找到了唯一的一个地方(如果出现多个,那么就继续多复制几个字节,直到只有一个为止)
修改`77`为`eb`
直接进游戏吧
有效果的
效果是:塔的升级都是免费的;放置塔要花钱,无法免费升级到模范塔
## 作弊和反作弊
玩了几局,效果可以的
当天也没有封号
但是第二天再登录游戏,就长草、封号了
猜测是游戏的时候,上传的那些数据,游戏服务器会做离线计算,发现不对,就封号
后面再开了一个小号,等封号之前赶紧进行竞速比赛,发现最后无法提交成绩
看来提交成绩的时候,还会再做一次数据校验,于是抓包分析了一下
以下是步骤和分析结果,结论就是:本人太菜,反作弊失败
## 抓包
安卓上抓包,很简单,wifi 设置代{过}{滤}理,或者用potsern 把流量导到电脑上,或者骚气的,直接用 eCapture 抓包
电脑上,需要一定的思路和技巧
第一招: hosts
1. wireshark 抓dns包,得到域名
2. 修改hosts为本地 https服务
3. https服务流量转到charles上
4. charles通过设置 dns解析服务器,把流量发到正确的服务器上
比较费劲的是第三步,需要自己写代码实现,费了好大劲才搞出来
第二招,v2clash
这个也是偶然想到的,之前破解 `羊了个羊`小程序的时候,就是用这招抓电脑版微信小程序的流量,这次试了一下,对电脑版游戏也适用
v2clash可以安装虚拟网卡,通过设置规则,让指定进程流量发到抓包软件上,就可以抓到游戏的包了(仅限http/https类型的包,如果游戏是tcp的,那得想别的办法)
以下贴几个请求和样例吧,因为有签名和加密,加密逻辑没有逆向出来,就不多写了
### DNS 查询
```
A static-api.nkstatic.com A 104.18.16.97 A 104.18.17.97
A api.ninjakiwi.com A 104.19.235.38 A 104.19.234.38
A bfb.ninjakiwi.com A 104.19.234.38 A 104.19.235.38
A analytics.ninjakiwi.com A 104.19.234.38 A 104.19.235.38
A fast-static-api.nkstatic.com A 104.18.16.97 A 104.18.17.97
```
可以发现ip都是 `104.19` 或者 `104.18`
这些都是 cloudflare 的地址
### /unity/time
得到服务器时间
```
{
"error": null,
"data": "{\"date\":{\"time\":1675178987230}}",
"sig": "392ae763cc5171fd893a2ef1640d6a26373120e5"
}
```
### 杰哥买东西
每在商店买一次东西,都会发起请求一次
连续买两次,那么在延迟几秒之后,只会发起一个请求,数量是2
比如下面买了三个辣椒
兔子是 PetRabbit
```
POST /btd6/mobile/geraldoshopdata HTTP/1.1
Host: bfb.ninjakiwi.com
Accept: */*
Accept-Encoding: deflate, gzip
Cookie: xxxx
user-agent: btd6-windowsplayer-34.3.6116
donottrack: 0
Content-Type: application/json
signature: 01f261aa3a490fbe4b5a385a5fcc6ad2
X-Unity-Version: 2021.3.12f1
Content-Length: 745
{
"game_id": "5158",
"attempt_id": "0",
"round_id": "78",
"geraldoshopitem_ShootyTurret": "0",
"geraldoshopitem_StackOfOldNails": "0",
"geraldoshopitem_CreepyIdol": "0",
"geraldoshopitem_JarOfPickles": "0",
"geraldoshopitem_RareQuincyActionFigure": "0",
"geraldoshopitem_SeeInvisibilityPotion": "0",
"geraldoshopitem_TubeOfAmazoGlue": "0",
"geraldoshopitem_SharpeningStone": "0",
"geraldoshopitem_WornHerosCape": "0",
"geraldoshopitem_BladeTrap": "0",
"geraldoshopitem_BottleHotSauce": "3", //辣椒
"geraldoshopitem_Fertilizer": "0",
"geraldoshopitem_PetRabbit": "0",// 兔子
"geraldoshopitem_RejuvPotion": "0",
"geraldoshopitem_GenieBottle": "2",
"geraldoshopitem_ParagonPowerTotem": "0",
"userId": "xxx",
"created_at": "2023-02-01 12:21:21",
"session_id": "1675252467",
"_delta": "5"
}
```
### 换英雄 selectHero
```
POST /btd6/mobile/selectHero HTTP/1.1
Host: bfb.ninjakiwi.com
Accept: */*
Accept-Encoding: deflate, gzip
Cookie: xxx
user-agent: btd6-windowsplayer-34.3.6116
donottrack: 0
Content-Type: application/json
signature: f05362162af86562455edee0cae4081d
X-Unity-Version: 2021.3.12f1
Content-Length: 133
{
"hero_name": "Geraldo",
"userId": "zzz",
"created_at": "2023-02-01 12:02:32",
"session_id": "1675252467",
"_delta": "6"
}
```
响应
```
{
"send": true,
"error": ""
}
```
### 开始游戏 startTrack
```
POST /btd6/mobile/startTrack HTTP/1.1
Host: bfb.ninjakiwi.com
Accept: */*
Accept-Encoding: deflate, gzip
Cookie: xxxx
user-agent: btd6-windowsplayer-34.3.6116
donottrack: 0
Content-Type: application/json
signature: 5f4397120971317beb34496ee198f965
X-Unity-Version: 2021.3.12f1
Content-Length: 409
{
"game_mode": "MagicOnly", //仅魔法
"resumed_game": "no",
"coop_mode": "no",
"map_name": "Carved", // 地图
"difficulty": "Hard", // 困难模式
"overwrote_save": "1",
"monkey_money": "52204", // 猴钞数量
"knowledge_points": "0",
"game_id": "5158",
"attempt_id": "0",
"hero_selected": "Geraldo", // 英雄
"hero_skin": "Geraldo", // 英雄皮肤
"event_id": "",
"rank": "155",
"is_restart": "false",
"userId": "xxxx",
"created_at": "2023-02-01 12:03:00",
"session_id": "1675252467",
"_delta": "1"
}
```
响应
```
{
"send": true,
"error": ""
}
```
### 改变目标 changeTargetingPrio
```
POST /btd6/mobile/changeTargetingPrio HTTP/1.1
Host: bfb.ninjakiwi.com
Accept: */*
Accept-Encoding: deflate, gzip
Cookie: xxx
user-agent: btd6-windowsplayer-34.3.6116
donottrack: 0
Content-Type: application/json
signature: 001c45a0dc7437980986af166483205c
X-Unity-Version: 2021.3.12f1
Content-Length: 159
{
"towerName": "Alchemist-220", // 炼金220
"prevOrNext": "Prev",
"userId": "xxx",
"created_at": "2023-02-01 12:14:14",
"session_id": "1675252467",
"_delta": "3"
}
```
```
{
"send": false,
"error": "disabled"
}
```
### 游戏结束之后 新增猴钞
```
POST /bank/balances HTTP/1.1
Host: api.ninjakiwi.com
Accept-Encoding: deflate, gzip
Cookie: xxx
User-Agent: btd6-windowsplayer-34.3
Content-Type: application/json
Accept: application/json
X-Unity-Version: 2021.3.12f1
Content-Length: 294
{
"data": "{\"accountHolder\":\"xxx\",\"wallets\":[\"NK_ACCDATA\"]}",
"auth": {
"session": "xxx",
"appID": 11,
"skuID": 35,
"device": "no_linkxxxx"
},
"sig": "95ae9e69bfbea2b049df7261a51e4328",
"nonce": "2998913795344284556"
}
```
>> 可以看到这里的响应体里出现了 `sig` ,说明这个响应体被签名了, 直接进行中间人攻击是没有用的,得找到验签逻辑,破解掉才行
### 特殊 /storage/static/multi
游戏启动的时候,会触发这个请求,猜测是 获取每日活动之类的,竞速、boss等
但是结果是加密的,无法直接阅读
这个请求会一直发到服务器,猜测官方也是通过这个请求来实现反作弊的,但是因为不知道怎么解密,所以看不到明文,进行不下去了 Arcticlyc 发表于 2023-2-12 10:24
想请问一下,v2clash是什么,具体怎么操作的
说错了,软件名是 Clash for Windows
1. 安装虚拟网卡
2. 开启 Tun Mode
3. 编写配置文件
Fiddler 或者 charles 就可以抓包了。
配置文件如下
proxies:
- name: charles
type: socks5
server: 127.0.0.1
port: 8889
rules:
- PROCESS-NAME,BloonsTD6.exe,charles
- MATCH,DIRECT 厉害,学习了 想请问一下,v2clash是什么,具体怎么操作的 厉害感谢分享学习 金钱和血量是加密的,分析那个比较有意思。我去年玩这个游戏还用CE脚本写过解密过程,不知道后来加解密方式改变了没有。 感谢分享, 学习一下,感觉不错 感谢分享 liushuaijie123 发表于 2023-2-12 10:41
金钱和血量是加密的,分析那个比较有意思。我去年玩这个游戏还用CE脚本写过解密过程,不知道后来加解密方式 ...
大佬还记得加密大概的思路么?
我分析的结果是 循环字节加减,不知道方向对不对