转载自我的小站:原文地址

通过Mono.Cecil我们可以通过Emit的方式将代码注入到已有的dll中,以实现AOP等高级功能。
Unity的代码在修改之后会自动编译到Library\ScriptAssemblies下的两个Assembly中,所以我会尝试着将代码注入到其中。

public class Test : MonoBehaviour{void Start()
{InjectMod();
}void InjectMod () {Debug.Log("Heihei asdasd");
}
}

将Test绑定到场景物体上,运行后我们会发现输出“Heihei asdasd”,就像我们预期的一样。
然后我们尝试着将代码注入到该函数中。

private static bool hasGen = false;
[PostProcessBuild(1000)]
private static void OnPostprocessBuildPlayer(BuildTarget buildTarget, string buildPath)
{hasGen = false;
}[PostProcessScene]
public static void TestInjectMothodOnPost()
{if (hasGen == true) return;hasGen = true;TestInjectMothod();
}
[InitializeOnLoadMethod]
public static void TestInjectMothod()
{var assembly = AssemblyDefinition.ReadAssembly(@"D:\Documents\Unity5Projects\UnityDllInjector\Library\ScriptAssemblies\Assembly-CSharp.dll");var types = assembly.MainModule.GetTypes();foreach(var type in types){foreach(var Method in type.Methods){if(Method.Name == "InjectMod"){InjectMethod(Method, assembly);}}}var writerParameters = new WriterParameters { WriteSymbols = true };assembly.Write(@"D:\Documents\Unity5Projects\UnityDllInjector\Library\ScriptAssemblies\Assembly-CSharp.dll", new WriterParameters());
}

我们首先看TestInjectMothod,这是我们在编辑器下进行注入的函数,这里我们需要注意的是,每当我们修改代码之后我们注入的结果会被覆盖掉,所以我们在每次修改代码之后都需要进行注入,所以我们这里添加了标签:InitializeOnLoadMethod
这个标签的意思是,当初始化的时候都进行执行,所以编译完成之后就会自动执行。

然后我们看前面两个函数,这两个函数是为了在打包时进行注入而存在的,其中hasGen是为了防止重复注入而定义的flag。

然后我们查看一下我们的注入方法:

private static void InjectMethod(MethodDefinition method, AssemblyDefinition assembly)
{var firstIns = method.Body.Instructions.First();var worker = method.Body.GetILProcessor();//获取Debug.Log方法引用var hasPatchRef = assembly.MainModule.Import(typeof(Debug).GetMethod("Log", new Type[] { typeof(string) }));//插入函数var current = InsertBefore(worker, firstIns, worker.Create(OpCodes.Ldstr, "Inject"));current = InsertBefore(worker, firstIns, worker.Create(OpCodes.Call, hasPatchRef));//计算OffsetComputeOffsets(method.Body);
}

在这个函数中我们可以看到,我们首先将我们所需要的函数导入,然后插入到方法的最前端。

会用到的一些工具函数

/// <summary>
/// 语句前插入Instruction, 并返回当前语句
/// </summary>
private static Instruction InsertBefore(ILProcessor worker, Instruction target, Instruction instruction)
{worker.InsertBefore(target, instruction);return instruction;
}/// <summary>
/// 语句后插入Instruction, 并返回当前语句
/// </summary>
private static Instruction InsertAfter(ILProcessor worker, Instruction target, Instruction instruction)
{worker.InsertAfter(target, instruction);return instruction;
}
//计算注入后的函数偏移值
private static void ComputeOffsets(MethodBody body)
{var offset = 0;foreach (var instruction in body.Instructions){instruction.Offset = offset;offset += instruction.GetSize();}
}

等待编译完成,并且运行程序,我们发现在输出原来的语句之前多了一句“Inject”
可是我们在查看代码的时候并没有发生任何改变,这是因为我们只修改了dll而并非修改源代码。

通过反编译软件ILSpy我们可以通过IL来反编译出我们的dll中的语句。

代码变为:

public class Test : MonoBehaviour
{private void Start(){this.InjectMod();}private void InjectMod(){Debug.Log("Inject");Debug.Log("Heihei asdasd");}
}


注入成功,也达成了我们的目的。

这个东西到底有什么用呢?
之前在看知乎上的一篇文章,slua的作者分析了一下腾讯最近xlua的思路,也就是luapatch。
大概就是在每一个需要热补丁的函数前面加上一个[hotfix]就可以通过热更新lua代码来进行热补丁。
这是一种非侵入式的方法来为我们的框架添加额外的功能,这类似于AOP。
例如,原来我们的代码是

[hotfix]
void Test()
{//DoSomething
}

在注入之后就变成了

[hotfix]
void Test(){//如果存在热补丁if(hasPatch()){//加载luaPatch并且执行return;}//DoSomething
}

也就是通过lua完全替代了原本的函数。

如果是手动添加这些代码的话想必是一个不小的工作量,但是如果使用了我们以上所写的方式来做这个东西则会轻松非常多。

也就是不侵入代码的情况下自动注入我们想要的额外代码。

或许在不久的将来会出现不少基于此类的框架,就像Java中的Spring等等。虽然C#的代码生成方式比起Java来说要麻烦不少,但是也是可以做的!

在公司的IOS版本中,我也想加入这样的方法来进行框架的构筑,而并非热更新而已。

以上。

本文章参考了:
http://www.jianshu.com/p/481994e8b7df
https://www.zhihu.com/question/54344452/answer/138990189

感谢大大们的分享,让我这样的小透明也可以不断学习到新的技术。

顺便给大家拜个晚年吧!

在Unity中利用Mono.Cecil将代码注入到Dll中相关推荐

  1. 免杀新姿势:利用线程将恶意代码注入到内存中

    本文讲的是免杀新姿势:利用线程将恶意代码注入到内存中, 产生存放远程攻击线程的进程 在这篇文章中我不想一步一步解释我编写的C#代码,但是我会展示下它能够绕过杀毒软件,并且操作非常简单,而且实用. 首先 ...

  2. Mono.Cecil C#代码注入

    先标记下 有时间再写 转载于:https://www.cnblogs.com/docomo/archive/2013/06/06/3122157.html

  3. 利用Mono.Cecil动态修改程序集来破解商业组件(仅用于研究学习)

    原文:利用Mono.Cecil动态修改程序集来破解商业组件(仅用于研究学习) Mono.Cecil是一个强大的MSIL的注入工具,利用它可以实现动态创建程序集,也可以实现拦截器横向切入动态方法,甚至还 ...

  4. java学习中,DVD管理系统纯代码(java 学习中的小记录)

    java学习中,DVD管理系统纯代码(java 学习中的小记录)作者:王可利(Star·星星) class DvdMain{public static void main (String[] args ...

  5. 远程代码注入及DLL注入教程(InlineHook)---植物大战僵尸为例

    远程代码注入及DLL注入教程 说明 ​ 本人刚开始学习逆向,不知道有没有动力学下深去,这一块也没有详细的实战教学,学多少就上传多少,希望能给想学的朋友一点帮助吧,本教程想通过植物大战僵尸这一经典游戏来 ...

  6. 获取C#中方法的执行时间及其代码注入

    在优化C#代码或对比某些API的效率时,通常需要测试某个方法的运行时间,可以通过DateTime来统计指定方法的执行时间,也可以使用命名空间System.Diagnostics中封装了高精度计时器Qu ...

  7. java jeditorpane 自动换行_JDIC 中利用WebBrowser内置浏览器到java application中 | 学步园...

    JDIC简介: JDesktop Integration Components (JDIC),是一个开源的项目,目的是构建消除本机应用程序和 Java 等价物之间差距的组件.项目组长是个中国人.该项目 ...

  8. 在python中、对于函数定义代码的理解_python中如何理解装饰器代码?

    长文预警,[最浅显易懂的装饰器讲解] 能不能专业地复制题目?配上代码,问题分段. 我来给提主配上问题的代码. 正式回答: 1:如何理解return一个函数,它与return一个值得用法区别在哪? 敲黑 ...

  9. spring boot中利用mybatis-generator插件生成代码

    使用Idea在spring boot中集成mybatis-generator,自动生成mapper.xml  model  dao 文件 一.配置 pom.xml 在pom.xml的<plugi ...

  10. 在Keil中利用AStyle插件格式化代码

    平时在用keil调试程序的时候,代码默认情况下不能自动调整格式.需要手动调整格式,比较麻烦.通过AStyle插件可以很方便的自动调整代码格式.下面总结一个AStyle插件的使用方法. 首先在官网上下载 ...

最新文章

  1. Git 常用命令总结
  2. 关于SAP Router连接不稳定的改良
  3. Java并发编程—Synchronized底层优化(偏向锁、轻量级锁)
  4. 四参数坐标转换c++_GPSRTK坐标转换及四参数、七参数适用条件
  5. C++ STL list的大小
  6. 解决webstorm中vue语法没有提示
  7. 【LeetCode】剑指 Offer 54. 二叉搜索树的第k大节点
  8. OpenShift 4 之Istio-Tutorial (6) 服务恢复能力(重试、超时、断路器)
  9. 给一个div innerhtml 后 没有内容显示的问题_实战:仅用18行JavaScript构建一个倒数计时器...
  10. mysql之分页_MySQL之分页查询(DQL)
  11. SQL Server调优系列进阶篇(查询优化器的运行方式)
  12. 2012.4.20总结
  13. Java补缺补漏—基本数据类型与引用数据类型
  14. 联想开机按f2怎么修复系统图解_联想笔记本开机f2修复电脑步骤 - 卡饭网
  15. 【SonicUI】 VS2008 SP1 编译错误处理。
  16. 程序员工作几年的一些感悟
  17. Data requirement
  18. 华为服务器故障灯不开机_华为手机开不了机指示灯亮,怎么办
  19. 解决sublime text2字体显示模糊问题
  20. codeforcesf382D-厉害的我的哥(德巴赫)

热门文章

  1. 微信小程序自定义地址选择器
  2. R-S编码译码-缩短码(10,6)
  3. 如何锻炼提高自己的逻辑思维?这里给你7个方法!
  4. C#网络编程 (二) 数据流的类型和应用
  5. 利用条形码生成器在Word 2013中轻松制作条形码的方法
  6. 一招秒杀常见网页基本排版布局
  7. 【Git】工作区、暂存区与版本库
  8. 结算系统功能详解-上篇
  9. 剪刀石头布java流程图_青岛能源所基于“剪刀石头布”策略实现快速多轮基因编辑...
  10. RobotStudio动态夹具的创建