Reflexi简明教程
本文是Assembly Manipulation and C# / VB.NET Code Injection的翻译,已得到作者授权。该教程作者(亦工具作者)现在在微软开发UnityVS…
本文使用的老版本界面与最新版本有所不同,而且内容覆盖的功能比较全,但介绍的比较笼统;之后我会结合Unity写一些具体的例子。
就我的使用经验来说,直接操作IL code还是比较丧心病狂的,利用后面的C#/VB.NET注入功能会方便很多,而且修改作用域、属性等功能其实用的不多。
ps. 图片来自codeproject,可能载入速度较慢,见谅
最新版本下载
最新版本的Reflexi可以在官网下载。
简介
Reflector和JustDecompile都是能用来对各类程序集进行深度检查的工具,他们同时也都能对.NET的IL code进行反汇编。但是这两个工具都无法修改对应程序集的结构或IL code。在Jb EVAIN实现的强大**Mono.Cecil**帮助下,Reflexil达到了这个目标。作为一个专门用来处理IL code的插件,Reflexil实现了一个完整的指令编辑器,并允许直接注入C#/VB.NET代码。下面将用两个例子具体说明。
Demo应用
让我们先实现一个简单的求和应用。
using System; |
使用指令编辑器
通过指令编辑器,我们可以在ComputeAndDisplay
方法内修改对MessageBox.Show
方法的调用,增加一个调用参数title(因为这个函数有多个重载形式),从而在显示结果的窗口上添加标题。
为了达成这个目标,首先需要在栈里增加一个ldstr
操作码,来存放一个string参数。
接下来,通过修改函数里调用MessageBox.Show
方法的指令,将刚才创建的string作为参数传进去。
最后保存我们刚才的修改,然后测试一下新的程序集。
![](https://www.codeproject.com/KB/msil/reflexil/modsave.jpg}
可以看到保存过的程序能正常显示标题,说明它使用了新的重载函数,并将正确的参数传进去了。
指令编辑器功能特性
指令编辑器支持Mono.Cecil中定义的所有操作码,如下所示:
- 基本数据类型: byte, sbyte, int32, int64, single, double
- 字符串
- 指令引用
- 多指令引用(switch)
- 参数或类型引用
- 内部泛型引用
- 类型、属性或方法引用(Reflector实现了一个浏览器,从中选择合适的即可)
需要注意的是,Reflector和Reflexil的对象更新不是同步的:对IL code进行的修改并不会影响Reflector的反汇编窗口,同时Mono.Cecil不会对生成的代码进行检查。在两者之间唯一的保证是对于程序集中同一个操作码(opcode),其对应的操作符(operand type)是一致的。如果你觉得直接修改IL比较麻烦的话,下面将展示如何直接更新一个函数本身的方法。
使用C#/VB.NET代码注入
通过下图所示配置表,你可以选择适合的诸如语言以及输入、显示的进制设置:
下面使用”替换所有代码”来修改ComputeAndDisplay
函数主体:
在弹出的编译窗口中,我们能直接查看到结果IL code;在这个窗口中提供了基本的智能感知:
当然,我们也能通过VB.NET语言进行修改。注意在这个简单例子中,我们在两种语言下得到了完全相同的IL code(但这个并不一定始终成立):
最后我们保存、测试一下修改结果:
C#/VB.NET代码注入功能特性
修改的代码是使用System.CodeDom
在一个单独的AppDomain
下编译。一旦编译成功,生成的指令被剥离出来,然后塞入原来的函数体中。参数、变量、方法、作用域等被调整适应于原作用方法中。当然代码注入也有一定的限制:无法引用到其定义在父类中的属性或方法。此外可以指定编译进程中的目标框架。
方法属性编辑器
通过这个编辑器能够很轻松的修改方法签名或可见范围,也可以修改返回类型:
方法参数及变量也是可编辑的。Reflexil能够载入(MDB、PDB等文件提供的)符号信息,来显示原始变量名:
异常处理
Reflexil能够在方法体内增加、修改、删除异常处理,包括以下类型:
Catch
Filter
Finally
Fault
类型标签编辑器
类似方法,变量的可见域也是可以修改的,譬如说将一个private
变量暴露出来:
操作成员
Reflexil能够对类、接口、结构体、枚举、事件、方法、构造函数、属性或引用进行重命名、删除或插入操作。
智能插入:插入一个属性(property)会生成一个字段(field)及对应的getter/setter方法。
资源编辑器
可以修改或插入内嵌资源、链接资源和程序集链接资源,并提供了一个完整的十六进制编辑器来更新、导入或导出文件。
自定义标签编辑器
完全支持自定义标签
程序集及引用编辑器
可修改程序入口
也可以更新关于认证的所有信息:版本号,公钥,名字和区域信息。当然你也可以修改引用的资源从而使用不同版本内容:
模块编辑器
可用来修改应用类型,譬如从可执行程序变成DLL库:
程序集签名支持
当原程序集自带签名时,保存出来的修改后程序集是”延迟签名”状态。Reflexil能调用SDK工具来修复这个问题。
Reflexil能自动移除程序集强命名或更新其引用资源。当然你也可以手动完成:移除公钥后将HasPublicKey
设为false
即可:
由于这块实在没用过,所以这段翻译的可信度较低…逃
反混淆支持
Reflexil使用了de4dot进行反混淆:
支持Babel NET, CliSecure, CodeFort, CodeVeil, CodeWall, CryptoObfuscator, DeepSea, Dotfuscator, dotNET Reactor, Eazfuscator NET, Goliath NET, ILProtector, MaxtoCode, MPRESS, Rummage, Skater NET, SmartAssembly, Spices Net和Xenocode。
程序集验证
通过使用.NET SDK自带的peverify.exe
,可以检查生成的IL code和对应原信息是否满足类型安全的需要。
总结
Reflexil完全基于Mono.Cecil。核心在于Mono.Cecil能够脱离运行时环境载入程序集,所以操作起来不需要对应的资源限制、能在孤立的AppDOmain
里完成。另外System.Type
和Mono.Cecil.TypeDefinition
分别独立实现了.NET类型概念,两者之间并没有联系。因此如果我们希望通过写程序(而不是直接修改IL code)的方式完成第一个例子(重载Show
来修改窗口标题栏),在Mono.Cecil帮助下也能很方便的完成。
翻译后记
这是我第一次翻译一些内容,因为除了汉语之外只看得懂一些英文,所以每次想动手但又觉得没啥必要…最近受Trace激励,觉得还是得动手整点,不管有没有人看~
一遍下来还是挺耗费精力的,而且有些细节也不是拿捏的非常清楚:譬如opcodes和operand区别,我参考了Opcodes and Operands但不知道翻译成操作符和操作码是否精确;C#相关的一些术语我也不确认翻译的是否精确,也许有些地方直接写英文反而更好。
总之,如果有什么意见和勘误请在下方留言:D