CelestialCosmic / themeblog

blog articles by Celestial_Cosmic,source code by chanshiyucx
0 stars 0 forks source link

unity 游戏调试(二) #46

Open CelestialCosmic opened 1 year ago

CelestialCosmic commented 1 year ago

这一篇是 《dnspy 调试新版 unity》的后续

上一次弄的是 mono 的,但是很快就发现这些对 il2cpp 的不起作用,所以又有了这一篇

获得源代码

首先准备 cpp2il,写个脚本给它方便操作

chcp 65001
cls
set /p gamepath="输入游戏路径"
set /p exefile="输入主程序名字(如果有.exe请删除)"
.\Cpp2IL-2022.0.7-Windows.exe  --game-path="%gamepath%" --exe-name="%exefile%"
pause

这样程序会把东西解压到 cpp2il_out 里面

代码被反编译出来的那一刻,直接收回想法:

// Token: 0x17000063 RID: 99
// (get) Token: 0x0600057B RID: 1403 RVA: 0x00002050 File Offset: 0x00000250
    [Token(Token = "0x17000063")]
    public string Raw
    {
        [Token(Token = "0x600057B")]
        [Address(RVA = "0x5FC090", Offset = "0x5FB090", VA = "0x1805FC090")]
        get
        {
            return null;
        }
    }

加密过了,能看得到的只有函数名

去查,得到这个说法:

  • RVA: (Relative Virtual Address 简称 RVA),RVA 只是内存中的一个简单的相对于 PE 文件装入地址的偏移位置,或称为偏移量。
  • VA:在 PE 用语里,实际的内存地址被称作虚拟地址(Virtual Address)简称VA。
  • 虚拟地址 VA =装入地址(Imagebase) + 相对虚拟地址(RVA)

想起前几天弄的一个 rpgmaker 的,用了什么六代 jsjiami ,然后被开源工具 0.7 秒解密,感慨攻防双方的无尽战争,顺带咒骂加私货还加密它的组织(原文件没有任何加密),这种东西只会拖延人十分钟的进度

尝试解除混淆

尝试 de4dot ,但是它删除了所有的 release

ipwnosx/de4dot-All-Version-2021: GG

这里有之前的 release

一万个 error,没用

对齐地址

这种双重保护的时候就不要想着装插件改代码了,准备磕汇编吧

应该要开 IDA 和 cheatengine 试试看了,不过在此之前需要收集可疑函数

发现它做了自己的类:

using Cpp2IlInjected;
using System;

namespace OHD.Dungeons.Player
{
    // Token: 0x02000751 RID: 1873
    [Token(Token = "0x2000751")]
    public enum PlayerHPState
    {
        // Token: 0x04001C06 RID: 7174
        [Token(Token = "0x4001C06")]
        Normal = 1,
        // Token: 0x04001C07 RID: 7175
        [Token(Token = "0x4001C07")]
        Injury,
        // Token: 0x04001C08 RID: 7176
        [Token(Token = "0x4001C08")]
        SeriousInjury
    }
}

全局搜索 "0x4001C06",发现只是一个类成员变量

所以我逐渐开始理解一切:

于是我锁定了一段代码:

这是一段血量代码

// OHD.Dungeons.Player.BattleRoomPlayer  
// Token: 0x06002CF9 RID: 11513 RVA: 0x00002053 File Offset: 0x00000253  
[Token(Token = "0x6002CF9")]  
[Address(RVA = "0x79C030", Offset = "0x79B030", VA = "0x18079C030", Slot = "34")]  
public override void RecoverHP(int recoverAmount)  
{  
}

根据我的猜测,这个地址是 18079B030

丢进 IDA 里面,起始地址是 180001000

18079B030 得到 call sub_180B83830 18079C030 得到 mov [rsp+arg_0], rbx

因为它应该是个函数的首地址,所以我认为 18079B030 更合适

再看看游戏启动以后的表现

cheat engine 这时候开启 mono 也没用

跳转 gameassembly.dll,这次得到 7FFEF42F0000

再跳转 7FFEF4A8B030 (gameassembly.dll+offset) 得到操作码 cld

7FFEF4A8B030 - cld 
7FFEF4A8B031 - xchg [rsi],edi
7FFEF4A8B033 - add [rax+rax*8-74B7EC8C],al
7FFEF4A8B03A - and al,38
7FFEF4A8B03D - test rcx,rcx
7FFEF4A8B040 - je 7FFEF4A8B084 ; 跳到段内
7FFEF4A8B042 - xor edx,edx
7FFEF4A8B044 - call 7FFEF4CB0F50 ; 去了别处
7FFEF4A8B049 - jmp 7FFEF4A8B023 ; 无用
7FFEF4A8B04B - mov rdx,[7FFEF7055608]
7FFEF4A8B052 - lea rcx,[rsp+28]
7FFEF4A8B057 - call 7FFEF46E8800 ; 跳转到一段空代码
7FFEF4A8B05C - jmp 7FFEF4A8B079 ; 跳到段内
7FFEF4A8B05E - mov rdx,[7FFEF7055608]
7FFEF4A8B065 - lea rcx,[rsp+28]
7FFEF4A8B06A - call 7FFEF46E8800 ; 跳转到一段空代码
7FFEF4A8B06F - mov rcx,[rsp+70]
7FFEF4A8B074 - test rcx,rcx
7FFEF4A8B077 - jne 7FFEF4A8B08A ; 跳转到下面的xor那里
7FFEF4A8B079 - mov rbx,[rsp+78]
7FFEF4A8B07E - add rsp,60
7FFEF4A8B082 - pop rdi
7FFEF4A8B083 - ret 
7FFEF4A8B084 - call 7FFEF46B1F10 ; 另一段跳转
7FFEF4A8B089 - nop 
7FFEF4A8B08A - xor edx,edx
7FFEF4A8B08C - call 7FFEF46B1EE0 ; 无用
7FFEF4A8B091 - int 3 

还有一个值得注意的点:从这里开始,下面有九个跳转

即便打下断点,游戏中的操作也不会触发断点,说明这里只是前期处理的时候使用过,游戏启动以后就完全不被使用了。

我想找的函数大概就藏在里面...

所以寻址大概就是:

VA - RVA + offset
GameAssembly.dll + offset >它们之间没有等号关系,只是同一份代码,同一个 dll 在不同软件中的初始地址不一样罢了 cheat engine 同时发现 `7FFEF481A470` ,操作码 `mov [rcx+10],edx` 控制着血量,算出来大致是 `52A470` 的位置,这个位置只能说近似,查询可以发现多个类里面都有符合条件的 不过还是有点发现的: ```cs // OHD.Card.Achievement402Checker // Token: 0x06003261 RID: 12897 RVA: 0x00002053 File Offset: 0x00000253 [Token(Token = "0x6003261")] [Address(RVA = "0x52A470", Offset = "0x529470", VA = "0x18052A470")] [Attribute(Name = "CompilerGeneratedAttribute", RVA = "0x863B0", Offset = "0x857B0")] public void set_HealAmount(int value) { } ``` 再在 IDA 里面加载 il2cpp 的 python 脚本找对应的函数 就像之前发现跳转代码一样,定位跳转它的代码 IDA 中跑过 il2cpp 脚本后函数的命名与 dnspy 中不同 上面的函数是 `OHD.Card.Achievement402Checker` 中的 `OnCheck`成员函数 `set_HealAmount ` 在 IDA 中是 `OHD_CARD_Achievement402Checker$$OnCheck` ```cpp void __stdcall OHD_Card_Achievement402Checker__OnCheck( OHD_Card_Achievement402Checker_o *this, const MethodInfo *method) { GamePlayer_o *Player; // rax OHD_Players_GamePlayer_Achievement_o *Achievements_k__BackingField; // rcx if ( !this->fields._HealAmount_k__BackingField ) { Player = Game__get_Player(0i64); if ( !Player || (Achievements_k__BackingField = Player->fields._Achievements_k__BackingField) == 0i64 ) sub_1803C1F10(); OHD_Players_GamePlayer_Achievement__MarkAsFinish(Achievements_k__BackingField, 402, 0i64); } } ``` 看明白了逻辑,重点就到了如何把代码重新插回去,但我已经累了,所以先这样吧