Reflexi简明教程

本文是Assembly Manipulation and C# / VB.NET Code Injection的翻译,已得到作者授权。该教程作者(亦工具作者)现在在微软开发UnityVS…

本文使用的老版本界面与最新版本有所不同,而且内容覆盖的功能比较全,但介绍的比较笼统;之后我会结合Unity写一些具体的例子。
就我的使用经验来说,直接操作IL code还是比较丧心病狂的,利用后面的C#/VB.NET注入功能会方便很多,而且修改作用域、属性等功能其实用的不多。

ps. 图片来自codeproject,可能载入速度较慢,见谅

最新版本下载

最新版本的Reflexi可以在官网下载。

简介

ReflectorJustDecompile都是能用来对各类程序集进行深度检查的工具,他们同时也都能对.NET的IL code进行反汇编。但是这两个工具都无法修改对应程序集的结构或IL code。在Jb EVAIN实现的强大Mono.Cecil帮助下,Reflexil达到了这个目标。作为一个专门用来处理IL code的插件,Reflexil实现了一个完整的指令编辑器,并允许直接注入C#/VB.NET代码。下面将用两个例子具体说明。

Demo应用

让我们先实现一个简单的求和应用。

using System;
using System.Windows.Forms;

namespace ReflexilDemo
{
public partial class DemoForm : Form
{
public DemoForm()
{
InitializeComponent();
}

private void ComputeAndDisplay(decimal x, decimal y)
{
MessageBox.Show(String.Format("{0}+{1}={2}", x, y, x + y));
}

private void DisplayResultButton_Click(object sender, EventArgs e)
{
ComputeAndDisplay(LeftOperand.Value, RightOperand.Value);
}
}
}


使用指令编辑器

通过指令编辑器,我们可以在ComputeAndDisplay方法内修改对MessageBox.Show方法的调用,增加一个调用参数title(因为这个函数有多个重载形式),从而在显示结果的窗口上添加标题。

为了达成这个目标,首先需要在栈里增加一个ldstr操作码,来存放一个string参数。


接下来,通过修改函数里调用MessageBox.Show方法的指令,将刚才创建的string作为参数传进去。



最后保存我们刚才的修改,然后测试一下新的程序集。

![](https://www.codeproject.com/KB/msil/reflexil/modsave.png $}

可以看到保存过的程序能正常显示标题,说明它使用了新的重载函数,并将正确的参数传进去了。

指令编辑器功能特性

指令编辑器支持Mono.Cecil中定义的所有操作码,如下所示:

  • 基本数据类型: byte, sbyte, int32, int64, single, double
  • 字符串
  • 指令引用
  • 多指令引用(switch)
  • 参数或类型引用
  • 内部泛型引用
  • 类型、属性或方法引用(Reflector实现了一个浏览器,从中选择合适的即可)

需要注意的是,ReflectorReflexil的对象更新不是同步的:对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.TypeMono.Cecil.TypeDefinition分别独立实现了.NET类型概念,两者之间并没有联系。因此如果我们希望通过写程序(而不是直接修改IL code)的方式完成第一个例子(重载Show来修改窗口标题栏),在Mono.Cecil帮助下也能很方便的完成。

翻译后记

这是我第一次翻译一些内容,因为除了汉语之外只看得懂一些英文,所以每次想动手但又觉得没啥必要…最近受Trace激励,觉得还是得动手整点,不管有没有人看~

一遍下来还是挺耗费精力的,而且有些细节也不是拿捏的非常清楚:譬如opcodes和operand区别,我参考了Opcodes and Operands但不知道翻译成操作符和操作码是否精确;C#相关的一些术语我也不确认翻译的是否精确,也许有些地方直接写英文反而更好。

总之,如果有什么意见和勘误请在下方留言:D