逆向篇-源码修复
摘要
反编译后的代码存在许多问题,这个章节我们将对出现的问题进行修复,让其能够顺利编译通过
导入 Assembly-CSharp
将 Assets\Scripts\Assembly-CSharp\Assembly-CSharp.csproj
覆盖到根目录
编辑 csproj
文件,所有源文件的路径是错误的,对所有字符串进行统一替换,<Compile Include="
替换为 <Compile Include="Assets\Scripts\Assemble-CSharp\
1
2
3
4
5
6
| <Compile Include="AssemblyInfo.cs" />
<Compile Include="Global.cs" />
<Compile Include="C2TDemo.cs" />
<Compile Include="SampleTable.cs" />
<Compile Include="CsvParser.cs" />
<Compile Include="CsvParser2.cs" />
|
替换后路径如下
1
2
3
4
5
| <Compile Include="Assets\Scripts\Assemble-CSharp\AssemblyInfo.cs" />
<Compile Include="Assets\Scripts\Assemble-CSharp\Global.cs" />
<Compile Include="Assets\Scripts\Assemble-CSharp\C2TDemo.cs" />
<Compile Include="Assets\Scripts\Assemble-CSharp\SampleTable.cs" />
<Compile Include="Assets\Scripts\Assemble-CSharp\CsvParser.cs" />
|
重新打开 Visual Studio
,发现已经可以正确识别 Assembly-CSharp
内的源码了
第三方库修复
第三方引用是以源码的形式写入的
此处我们将 Assembly-CSharp-firstpass
和 Unity
系列的换成工程换成 dll
形式
打开游戏目录,将 TWOKFDEMO\TheWorldOfKongFu_Data\Managed
目录下的 Unity.Analytics.DataPrivacy.dll
和 Unity.TextMeshPro.dll
拷贝到工程 Library\ScriptAssemblies
目录
编辑 Assembly-CSharp.csproj
文件,将以上三个目录下的 cs 引用删除
1
2
3
4
5
6
| <Compile Include="Assets\Scripts\Unity.TextMeshPro\TMPro\ColorMode.cs" />
<Compile Include="Assets\Scripts\Unity.TextMeshPro\TMPro\FaceInfo.cs" />
...
<Compile Include="Assets\Scripts\Assembly-CSharp-firstpass\Steamworks\CSteamID.cs" />
<Compile Include="Assets\Scripts\Assembly-CSharp-firstpass\Steamworks\CallbackDispatcher.cs" />
...
|
Assembly-CSharp-firstpass.dll
是 steam
的 api
库,我们可以将其移除
同时,修改 Assets\Scripts\Assembly-CSharp\TitleController.cs
文件,将初始化逻辑删除
1
2
3
4
5
6
7
8
| private void Start()
{
// 注释steam初始化代码
//if (SteamManager.Initialized)
//{
// Debug.Log(SteamFriends.GetPersonaName());
//}
}
|
导入 DOTween
DOTween 是开源在 Github
的一个强大的动画插件,我们可以直接从 Github 上下载源码覆盖进去
首先需要确认 DOTween
的版本号,打开 Reflector
反编译工具,将 DOTween.dll
文件拖进去,版本号记录在 DG.Tweening.DOTween.Version
中
可以看到版本号为 1.2.280,接下来就可以将 DOTWeen1.2.280 整个工程下载下来,里面目录较多,我们只需要将 _DOTween.Assembly\DOTween\
下的所有文件覆盖到 Assets\Scripts\DOTween\DG\Tweening
目录即可
导入 spine-unity
spine
是一个强大的骨骼动画工具,官方提供的插件源码在 Spine-Runtime,只需要下载对应版本代码覆盖进去即可
确定版本号可以通过 Reflect
工具对代码进行反编译,或者与 Github
上的源码进行比对,最终确认版本为 spine-runtimes-spine-libgdx-3.8.55.1
下载地址为 spine-libgdx-3.8.55.1,下载成功后,搜索对应的类名,将文件覆盖到 Assets\Scripts\Assembly-CSharp\Spine\
和 Assets\Scripts\spine-unity
目录
导入 SuperTiled2Unity
SuperTiled2Unity 是一个开源的转换工具,支持将 Tiled Map Editor 创建的地图导入到 Unity
中使用,与上面的方式处理相同,将代码下载后,覆盖到同名文件
注意
.meta
文件不能覆盖,里面存放着 cs 文件的 id 信息,替换后 id 变化会导致资源引用失败
源码修复
yield 修复
C#
中使用 yield
关键字的源码
1
2
3
4
5
6
7
8
9
10
11
12
13
| IEnumerator AutoActionWait_CallBack(Vector3Int _attackPos) {
yield return new WaitForSeconds(0.25f);
if (current.race == "player")
{
m_Flow = BattleController.Flow.PlayerAction;
}
else if (current.race == "enemy")
{
m_Flow = BattleController.Flow.EnemyAction;
}
current.Attack(_attackPos, true);
}
|
编译后会变成如下代码,使用了状态机进行条件转换
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
| [IteratorStateMachine((Type) typeof(<AutoActionWait_CallBack>d__54))]
private IEnumerator AutoActionWait_CallBack(Vector3Int _attackPos) =>
new <AutoActionWait_CallBack>d__54(0) {
<>4__this = this,
_attackPos = _attackPos
};
[CompilerGenerated]
private sealed class <AutoActionWait_CallBack>d__54 : IEnumerator<object>, IEnumerator, IDisposable
{
private int <>1__state;
private object <>2__current;
public BattleController <>4__this;
public Vector3Int _attackPos;
[DebuggerHidden]
public <AutoActionWait_CallBack>d__54(int <>1__state)
{
this.<>1__state = <>1__state;
}
private bool MoveNext()
{
int num = this.<>1__state;
BattleController controller = this.<>4__this;
switch (num)
{
case 0:
this.<>1__state = -1;
this.<>2__current = new WaitForSeconds(0.25f);
this.<>1__state = 1;
return true;
case 1:
this.<>1__state = -1;
if (controller.current.race == "player")
{
controller.m_Flow = BattleController.Flow.PlayerAction;
}
else if (controller.current.race == "enemy")
{
controller.m_Flow = BattleController.Flow.EnemyAction;
}
controller.current.Attack(this._attackPos, true);
break;
}
return false;
}
[DebuggerHidden]
void IEnumerator.Reset()
{
throw new NotSupportedException();
}
[DebuggerHidden]
void IDisposable.Dispose()
{
}
object IEnumerator<object>.Current =>
this.<>2__current;
object IEnumerator.Current =>
this.<>2__current;
}
|
此处只能手动将代码中所有的 yield
逻辑进行替换
Object 不明确引用
提示 Object
不明确引用

C#标准库
和 Unity
中都有定义 Ojbect
类,需要指定为 UnityEngine.Object
还有
-
Random
修改为 UnityEngine.Random
bool 修复
所有 bool
类型编译后都被转换成了 0 和 1,需要手动转换成 false
和 true
get/set
DateTime.get_UtcNow
提示无法显示调用运算符或访问器
修改为
同理,将以下 get 函数还原成直接读取属性
DateTime.get_Now()
修改为 DateTime.Now
-
componentInChildren.get_color();
修改为 componentInChildren.color;
-
componentInChildren.set_color(color);
修改为 componentInChildren.color = color;
-
go.GetComponent<Image>().set_sprite(this.SelectedSprite);
修改为 go.GetComponent<Image>().sprite = this.SelectedSprite;
还有一种形式,编译后的 set
如下,多了一个()
1
2
3
4
5
6
7
| public float MoveThreshold
{
get =>
this.moveThreshold;
set =>
(this.moveThreshold = Mathf.Abs(value));
}
|
需要修改为
1
2
3
4
5
6
7
| public float MoveThreshold
{
get =>
this.moveThreshold;
set =>
this.moveThreshold = Mathf.Abs(value);
}
|
switch
源码 switch
的结构如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| switch (type)
{
case "debuffATK":
{
original = SharedData.Instance(false).Buff_debuffATK_Prefab;
break;
}
case "burn":
{
original = SharedData.Instance(false).Buff_burn_Prefab;
break;
}
}
|
编译后 C#
为了提高性能,会优化成判断字符串的 hash
值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| switch (<PrivateImplementationDetails>.ComputeStringHash(type))
{
case 0x312d5c4b:
if (type == "debuffATK")
{
original = SharedData.Instance(false).Buff_debuffATK_Prefab;
break;
}
break;
case 0x51f91098:
if (type == "burn")
{
original = SharedData.Instance(false).Buff_burn_Prefab;
break;
}
break;
}
|
需要将所有涉及 switch
语句的代码进行还原
int/float/string 转换
编译后的代码,部分赋值不符合函数参数格式,比如 int
和 string
需要进行转换
1
2
3
4
5
6
7
8
9
10
11
12
13
| public Row Find_LV(string find) =>
this.rowList.Find(delegate (Row x) {
return x.LV == find;
});
public class CharaData
{
public int m_Level = 1;
}
SharedData.Instance(false).a05.Find_LV( ( (int) this.charadata.m_Level) ).EXP )
base.transform.Find("Panel/Status/Power/Text").GetComponent<Text>().text = ((float) num);
|
修改为
1
2
3
| SharedData.Instance(false).a05.Find_LV( this.charadata.m_Level.ToString() ).EXP )
base.transform.Find("Panel/Status/Power/Text").GetComponent<Text>().text = num.ToString();
|
goto
for
循环中,会使用 goto
进行跳转
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| foreach (char ch in str)
{
if (ch != '"')
{
if (ch != ',')
{
goto Label_003F;
}
state = state.Comma(context);
}
else
{
state = state.Quote(context);
}
continue;
Label_003F:
state = state.AnyChar(ch, context);
}
|
可以修复为
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| foreach (char ch in str)
{
if (ch != '"')
{
if (ch != ',')
{
state = state.AnyChar(ch, context);
continue;
}
state = state.Comma(context);
}
else
{
state = state.Quote(context);
}
}
|
修复完成
以上所有代码修改完成后,代码可以正常编译,没有报错信息了
要修复的种类并不多,但是代码量比较大,需要一些耐心逐个进行修复