|
一接触到新的东西,总想看看背后的原理是怎样的,xlua也不例外。于是试着写了一下,算是了解底层的实现原理,以后不用xlua也能有借鉴的地方。
xlua的热修复原理实际上是在 C# 编译成中间语言的时候,进行代码的插入这部分用到了 Mono.Ceil 库来操作,当然还有其他很多的库也可以实现。 因为是在IL的部分插入,因此直接支持IL2CPP
直接进入主题
已知有一个类
1
2
3
4
5
6
7
8
9
10
| publicclass InputTest {
void Start (){
Hello ();
}
privatevoid Hello (){
Debug. Log("hello");
Debug. Log("666"):
}
}
|
这个类在被Unity调用的时候会输出 “Hello”
那么如果我们想修改Hello函数该怎么做呢
1
2
3
4
| string injectPath = @ "./Library\ScriptAssemblies\Assembly-CSharp.dll";
AssemblyDefinition assemblyDefinition = null ;
var readerParameters =new ReaderParameters { ReadSymbols =true};
assemblyDefinition = AssemblyDefinition. ReadAssembly(injectPath, readerParameters );
|
第一步 是要将当前代码的 Assembly 读出来, U3d有3个Assembly。 一个是项目代码叫 Assembly-CSharp.dll 一个是编辑器代码 Assembly-Editor-CSharp.dll.
还有一个是插件 Assembly-Plugin-CSharp.dll. 因为 InputTest是项目代码部分,所以读取 Assembly-CSharp.dll即可
读取成功后,所有的数据都在 assemblyDefinition 中,只需要遍历一下找到要修改的类即可
1
2
3
4
5
6
7
8
9
10
| foreach (Mono. Cecil. TypeDefinition item in assemblyDefinition. MainModule. Types){
if(item. FullName=="InputTest"){
foreach (MethodDefinition method in item. Methods){
if(method. Name. Equals("Hello")){
}
}
}
}
|
第二步 通过遍历类型定义找到我们的类 “InputTest” 然后在 类定义中遍历所有的函数定义,找到我们要修改的 “Hello”函数
找到函数后,就可以正式做函数修改了。
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
| var ins = method. Body. Instructions. First();
var worker = method. Body. GetILProcessor();
var logRef = assemblyDefinition. MainModule. Import(typeof (Debug ). GetMethod("Log", new Type []{ typeof (string )}));
worker. InsertBefore(ins, worker. Create(OpCodes. Ldstr, "Fuck Off"));
worker. InsertBefore(ins, worker. Create(OpCodes. Call, logRef ));
worker. InsertBefore(ins, worker. Create(OpCodes. Ldstr, "Fuck On"));
worker. InsertBefore(ins, worker. Create(OpCodes. Call, logRef ));
Type type = typeof (InjectTest );
if(null != type ){
MethodInfo subMethod = type. GetMethod("SayFuck");
if(null != subMethod ){
Debug. Log("Find Method: "+ subMethod );
var sayRef = assemblyDefinition. MainModule. Import(subMethod );
worker. InsertBefore(ins, worker. Create(OpCodes. Call, sayRef ));
}
}
var writerParameters =new WriterParameters { WriteSymbols =true};
assemblyDefinition. Write(injectPath, new WriterParameters ());
|
第三步 做了3件事情, 绑定了2个UnityEngine的Log函数,打印了 “Fuck Off”, “Fuck On” 之后再绑定一个类 “InjectTest”中的静态函数 SayFuck()
这样原本的 Hello()函数就会在 打印”Hello”之前先打印 “Fuck Off”, “Fuck On” 调用 InjectTest.SayFuck().
最后就是将执行的修改进行保存 assemblyDefinition.Write
最后的最后用C#反编译软件打开 Assembly-CSharp.dll 看看修改后的Hello()函数
可以看到已经成功的修改啦。 |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有账号?立即注册
×
|