-
Notifications
You must be signed in to change notification settings - Fork 1
/
treeDumper.py
183 lines (147 loc) · 5.9 KB
/
treeDumper.py
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
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
# py3
# requirements:
# pythonnet 3+
# pip install git+https://github.com/pythonnet/pythonnet/
# TypeTreeGenerator
# https://github.com/K0lb3/TypeTreeGenerator
# requires .NET 5.0 SDK
# https://dotnet.microsoft.com/download/dotnet/5.0
#
# pythonnet 2 and TypeTreeGenerator created with net4.8 works on Windows,
# so it can do without pythonnet_init,
# all other systems need pythonnet 3 and either .net 5 or .net core 3 and pythonnet_init
############################
#
# Warning: This example isn't for beginners
#
############################
import os
import UnityPy
from typing import Dict
import json
ROOT = os.getcwd()
TYPETREE_GENERATOR_PATH = os.path.join(ROOT, "l19-EN-patcher","TypeTreeGenerator")
def main():
# dump the trees for all classes in the assembly
dll_folder = os.path.join(ROOT, "hololiveERROR_Data", "il2cpp_dump", "DummyDll")
print(dll_folder)
asset_path = os.path.join(ROOT, "hololiveERROR_Data")
print(asset_path)
tree_path = os.path.join(ROOT, "typetrees.json")
trees = dump_assembly_trees(dll_folder, tree_path)
# by dumping it as json, it can be redistributed,
# so that other people don't have to setup pythonnet3
# People who don't like to share their decrypted dlls could also share the relevant structures this way.
export_monobehaviours(asset_path, trees)
def export_monobehaviours(asset_path: str, trees: dict):
for r, d, fs in os.walk(asset_path):
for f in fs:
try:
env = UnityPy.load(os.path.join(r, f))
except:
continue
for obj in env.objects:
if obj.type == "MonoBehaviour":
d = obj.read()
if obj.serialized_type.nodes:
tree = obj.read_typetree()
else:
if not d.m_Script:
print("Script not found!")
continue
# RIP, no referenced script
# can only dump raw
script = d.m_Script.read()
# on-demand solution without already dumped tree
# nodes = generate_tree(
# g, script.m_AssemblyName, script.m_ClassName, script.m_Namespace
# )
if script.m_ClassName not in trees:
# class not found in known trees,
# might have to add the classes of the other dlls
continue
nodes = FakeNode(**trees[script.m_ClassName])
tree = obj.read_typetree(nodes)
# save tree as json whereever you like
def dump_assembly_trees(dll_folder: str, out_path: str):
# init pythonnet, so that it uses the correct .net for the generator
pythonnet_init()
# create generator
g = create_generator(dll_folder)
# generate a typetree for all existing classes in the Assembly-CSharp
# while this could also be done dynamically for each required class,
# it's faster and easier overall to just fetch all at once
bigJson = {}
for file in os.listdir(dll_folder):
filename = os.fsdecode(file)
if filename.endswith(".dll"):
bigJson[filename] = generate_tree(g, filename, "", "")
trees = generate_tree(g, "Assembly-CSharp.dll", "", "")
# bigJson["Assembly-CSharp.dll"] = trees
if out_path:
with open("typetrees.json", "wt", encoding="utf8") as f:
json.dump(bigJson, f, ensure_ascii=False)
return trees
def pythonnet_init():
"""correctly sets-up pythonnet for the typetree generator"""
# prepare correct runtime
from clr_loader import get_coreclr
from pythonnet import set_runtime
print(os.path.join(TYPETREE_GENERATOR_PATH, "TypeTreeGenerator.runtimeconfig.json"))
rt = get_coreclr(
os.path.join(TYPETREE_GENERATOR_PATH, "TypeTreeGenerator.runtimeconfig.json")
)
set_runtime(rt)
def create_generator(dll_folder: str):
"""Loads TypeTreeGenerator library and returns an instance of the Generator class."""
# temporarily add the typetree generator dir to paths,
# so that pythonnet can find its files
import sys
sys.path.append(TYPETREE_GENERATOR_PATH)
#
import clr
clr.AddReference("TypeTreeGenerator")
# import Generator class from the loaded library
from Generator import Generator
# create an instance of the Generator class
g = Generator()
# load the dll folder into the generator
g.loadFolder(dll_folder)
return g
class FakeNode:
"""A fake/minimal Node class for use in UnityPy."""
def __init__(self, **kwargs):
self.__dict__.update(**kwargs)
def generate_tree(
g: "Generator",
assembly: str,
class_name: str,
namespace: str,
unity_version=[2021, 1, 20, 1],
) -> Dict[str, Dict]:
"""Generates the typetree structure / nodes for the specified class."""
# C# System
from System import Array
unity_version_cs = Array[int](unity_version)
# fetch all type definitions
def_iter = g.getTypeDefs(assembly, class_name, namespace)
# create the nodes
trees = {}
for d in def_iter:
try:
nodes = g.convertToTypeTreeNodes(d, unity_version_cs)
except Exception as e:
# print(d.Name, e)
continue
trees[d.Name] = [
{
"level": node.m_Level,
"type": node.m_Type,
"name": node.m_Name,
"meta_flag": node.m_MetaFlag,
}
for node in nodes
]
return trees
if __name__ == "__main__":
main()