yellowtail 发表于 2023-2-11 17:14

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等

但是结果是加密的,无法直接阅读



这个请求会一直发到服务器,猜测官方也是通过这个请求来实现反作弊的,但是因为不知道怎么解密,所以看不到明文,进行不下去了

yellowtail 发表于 2023-2-12 11:01

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

Arcticlyc 发表于 2023-2-12 10:17

厉害,学习了

Arcticlyc 发表于 2023-2-12 10:24

想请问一下,v2clash是什么,具体怎么操作的

a3284595 发表于 2023-2-12 10:27

厉害感谢分享学习

liushuaijie123 发表于 2023-2-12 10:41

金钱和血量是加密的,分析那个比较有意思。我去年玩这个游戏还用CE脚本写过解密过程,不知道后来加解密方式改变了没有。

chen1860906 发表于 2023-2-12 11:11

感谢分享,

不搭落俗笑忘书 发表于 2023-2-12 11:20

学习一下,感觉不错

wxxbc 发表于 2023-2-12 11:54

感谢分享

yellowtail 发表于 2023-2-12 11:56

liushuaijie123 发表于 2023-2-12 10:41
金钱和血量是加密的,分析那个比较有意思。我去年玩这个游戏还用CE脚本写过解密过程,不知道后来加解密方式 ...

大佬还记得加密大概的思路么?

我分析的结果是 循环字节加减,不知道方向对不对
页: [1] 2 3 4 5 6 7 8 9 10
查看完整版本: steam某塔防游戏破解