Xenocode是国外的一款针对.net代码进行加密混淆保护的软件,其功能强大,使用率比较高。它采用的是自有的字符串加密逻辑,通过一个插入的类对程序集中的字符串进行加密保护,同时还会对程序执行流程进行混淆,并且还会在程序中随机加入一些无效代码,使得要读懂程序逻辑和反编译代码的难度大大增加。一个典型的Xenocode混淆加密后的dll在reflector中的显示图片如下:

可见其中有一个特征性的类,即Xen0code,该类可能保存了加密的类的动态映射表(推测),其详细的生成规则及作用后面再做探讨。这里要说明的是,在较新版本的reflector中可以查看到经Xenocode加密混淆后的代码,但是这些代码经过了字符串的加密和流程的混淆后很难读懂,也基本上不能使用,我们希望通过技术手段尽可能的恢复这些代码的原貌。目前国外的研究者通过还原经Xenocode加密代码的IL文件来达到破译的效果,效率很高,但是兼容性不好(典型的问题是,使用该工具处理加密文件时,很容易出现异常(Exception)而退出,而作者并没有公开破解思路或破解工具的代码)。我们这里是在reflector反编译处理得到C#后再进行处理,这样处理的好处是代码直观,容易理解,缺点是效率较低,也不排除异常情况。一段经reflector反编译后的Xenocode示例代码如下(由于篇幅的关系,仅截取部分代码进行演示):
private void x85601834555fb7d5()
{
ComponentResourceManager manager = new ComponentResourceManager(typeof(x4dc9cb69c65ee315));
if (-2 != 0)
{
this.x14b9c2a9b57534b8 = new Label();
this.x0049a6285a44bbd6 = new TextBox();
this.x792ada3ad34d61e0 = new TextBox();
this.xf8ffb0d954b2c2f6 = new PictureBox();
this.xb115ebd03641a474 = new Button();
if (0 != 0)
{
goto Label_0558;
}
goto Label_05BB;
}
goto Label_031E;
Label_0056:
base.StartPosition = FormStartPosition.CenterParent;
this.Text = "错误";
((ISupportInitialize) this.xf8ffb0d954b2c2f6).EndInit();
base.ResumeLayout(false);
base.PerformLayout();
return;
Label_00AE:
base.Controls.Add(this.xb115ebd03641a474);
base.Controls.Add(this.xd15b9cf5592f8785);
base.Controls.Add(this.xf6d68814b444670e);
if (15 == 0)
{
goto Label_0435;
}
base.Icon = (Icon) manager.GetObject("$this.Icon");
base.MaximizeBox = false;
base.MinimizeBox = false;
this.MinimumSize = new Size(0x138, 0x10d);
if (3 != 0)
{
if (0 == 0)
{
if (1 == 0)
{
return;
}
base.Name = "MessageForm";
base.SizeGripStyle = SizeGripStyle.Show;
goto Label_0056;
}
goto Label_0360;
}
return;
Label_010D:
base.CancelButton = this.xb115ebd03641a474;
base.ClientSize = new Size(0x16a, 0xf6);
base.Controls.Add(this.x14b9c2a9b57534b8);
Label_013F:
base.Controls.Add(this.x0049a6285a44bbd6);
base.Controls.Add(this.x792ada3ad34d61e0);
base.Controls.Add(this.xf8ffb0d954b2c2f6);
if (0 == 0)
{
goto Label_00AE;
}
Label_0219:
this.xf6d68814b444670e.Font = new Font("Microsoft Sans Serif", 8.25f, FontStyle.Bold, GraphicsUnit.Point, 0);
this.xf6d68814b444670e.Location = new Point(0x38, 14);
this.xf6d68814b444670e.Name = "lblMsg";
this.xf6d68814b444670e.Size = new Size(0x126, 40);
this.xf6d68814b444670e.TabIndex = 10;
if (-2147483648 != 0)
{
this.xf6d68814b444670e.Text = "An error occurred";
base.AutoScaleMode = AutoScaleMode.Inherit;
if (0 != 0)
{
goto Label_05D1;
}
if (-2147483648 == 0)
{
goto Label_0254;
}
goto Label_010D;
}
goto Label_031E;
Label_0254:
this.xd15b9cf5592f8785.Size = new Size(0x56, 0x11);
this.xd15b9cf5592f8785.TabIndex = 12;
this.xd15b9cf5592f8785.Text = "错误类型:";
this.xf6d68814b444670e.Anchor = AnchorStyles.Right | AnchorStyles.Left | AnchorStyles.Top;
this.xf6d68814b444670e.FlatStyle = FlatStyle.System;
goto Label_0219;
Label_031E:
this.xb115ebd03641a474.DialogResult = DialogResult.Cancel;
if (15 == 0)
{
goto Label_00AE;
}
this.xb115ebd03641a474.FlatStyle = FlatStyle.Popup;
while (true)
{
this.xb115ebd03641a474.Location = new Point(0x11b, 220);
this.xb115ebd03641a474.Name = "buttonOK";
this.xb115ebd03641a474.Size = new Size(0x43, 0x18);
this.xb115ebd03641a474.TabIndex = 8;
this.xb115ebd03641a474.Text = "OK";
this.xb115ebd03641a474.Click += new EventHandler(this.x38e768d357a8f002);
this.xd15b9cf5592f8785.FlatStyle = FlatStyle.System;
this.xd15b9cf5592f8785.Location = new Point(12, 0x3b);
if (-2147483648 != 0)
{
this.xd15b9cf5592f8785.Name = "labelType";
if (0 != 0)
{
goto Label_05A7;
}
if (0 != 0)
{
goto Label_04D4;
}
goto Label_0254;
}
}
Label_0360:
this.xf8ffb0d954b2c2f6.Paint += new PaintEventHandler(this.x952d2a572666dc32);
this.xb115ebd03641a474.Anchor = AnchorStyles.Right | AnchorStyles.Bottom;
if (4 != 0)
{
goto Label_031E;
}
if (3 != 0)
{
goto Label_03EE;
}
Label_0395:
this.xf8ffb0d954b2c2f6.Location = new Point(12, 12);
this.xf8ffb0d954b2c2f6.Name = "pictIcon";
this.xf8ffb0d954b2c2f6.Size = new Size(0x26, 0x22);
this.xf8ffb0d954b2c2f6.TabIndex = 9;
if (0 != 0)
{
goto Label_0593;
}
this.xf8ffb0d954b2c2f6.TabStop = false;
goto Label_0360;
Label_03EE:
this.x792ada3ad34d61e0.Size = new Size(0x152, 0x15);
this.x792ada3ad34d61e0.TabIndex = 13;
goto Label_0395;
Label_0435:
this.x792ada3ad34d61e0.Name = "textType";
if (0 != 0)
{
goto Label_0523;
}
this.x792ada3ad34d61e0.ReadOnly = true;
if (-2147483648 == 0)
{
goto Label_0056;
}
goto Label_03EE;
Label_04D4:
this.x0049a6285a44bbd6.Location = new Point(12, 0x83);
if (0 != 0)
{
goto Label_013F;
}
this.x0049a6285a44bbd6.Multiline = true;
this.x0049a6285a44bbd6.Name = "textDetails";
this.x0049a6285a44bbd6.ReadOnly = true;
this.x0049a6285a44bbd6.ScrollBars = ScrollBars.Vertical;
this.x0049a6285a44bbd6.Size = new Size(0x152, 0x53);
this.x0049a6285a44bbd6.TabIndex = 15;
this.x792ada3ad34d61e0.Anchor = AnchorStyles.Right | AnchorStyles.Left | AnchorStyles.Top;
this.x792ada3ad34d61e0.BackColor = SystemColors.Info;
this.x792ada3ad34d61e0.BorderStyle = BorderStyle.FixedSingle;
this.x792ada3ad34d61e0.Location = new Point(12, 0x4c);
goto Label_0435;
Label_0523:
this.x14b9c2a9b57534b8.Text = "错误明细:";
this.x0049a6285a44bbd6.Anchor = AnchorStyles.Right | AnchorStyles.Left | AnchorStyles.Bottom | AnchorStyles.Top;
this.x0049a6285a44bbd6.BackColor = SystemColors.Info;
if (0 == 0)
{
this.x0049a6285a44bbd6.BorderStyle = BorderStyle.FixedSingle;
goto Label_04D4;
}
goto Label_05BB;
Label_0558:
this.x14b9c2a9b57534b8.Size = new Size(0x43, 0x11);
this.x14b9c2a9b57534b8.TabIndex = 14;
goto Label_0523;
Label_0593:
this.x14b9c2a9b57534b8.Location = new Point(12, 0x6f);
Label_05A7:
this.x14b9c2a9b57534b8.Name = "labelDetails";
goto Label_0558;
Label_05BB:
this.xd15b9cf5592f8785 = new Label();
this.xf6d68814b444670e = new Label();
Label_05D1:
((ISupportInitialize) this.xf8ffb0d954b2c2f6).BeginInit();
if (0 == 0)
{
base.SuspendLayout();
this.x14b9c2a9b57534b8.FlatStyle = FlatStyle.System;
if (0 != 0)
{
goto Label_010D;
}
goto Label_0593;
}
goto Label_0523;
}
这是一段Xenocode加密过的.net窗口应用程序经reflector反编译后得到的代码。从中可以看到,类名、函数名称及控件名称等均被加密成无意义的字符串。代码中有多处判断和跳转,由于流程的混淆导致reflector不得不在代码中加入很多的标签(Label_****)来实现跳转。程序中也存在很多诸如if (-2147483648 == 0)等无效代码。我们处理的第一步是进行代码清理,即去除无效代码和精简正常代码。Xenocode加密后的代码中,无效代码有很多种类型:比如if (-2147483648 == 0)、if(0!=0)、if(uint.MinValue>13842)、while(!true)等,还有一些判断中涉及到逻辑计算的,比如if((uint.MaxValue+4434)<0)之类,甚至还有采用插入参数判断的,比如int a=5;if(a<0)等,可谓五花八门。另外就是冗余代码,与无效代码的判断类似:比如if(0=0)、if(4!=0)等,还有假的死循环语句如while(true)之类,这类语句中一般都存在return语句,因此也可当作冗余代码处理。之所以会产生这样多种类并且复杂结构的语句,其中很大程度上与reflector的语言解析器有关,如果不是采用reflector反编译生成代码的话,仅从IL代码来看跳转虽然很多,但语句还是相对简洁的。
通过以上分析,可以看出,无论是去除无效代码还是精简冗余代码都需要从语句的判断上入手。其判断语句虽然是随机多样的,但类型有限并且是可操作的,多是数字比较判断,或是数字运算后的比较及恒真恒假条件等。我们采取的方式是正则匹配再解析运算的方法,比如针对纯数字(类似于if(2<10))的比较判断语句可以采用的正则语句是if \((-?\d+ [!>=<]{1,2} -?\d+)\)\n[ ]*{([^{}]*)},这里面针对正负数的大于、等于、小于、不等于的代码块进行匹配。匹配成功后对if语句中的条件判断进行计算,其中的条件语句在程序处理中只是一个字符串而已,而要对其进行计算就需要单独构建一个语言编译器,一个采用C#反射技术实现的语言编译器的核心代码如下:
private void ConstructEvaluator(EvaluatorItem[] items)
{
ICodeCompiler compiler = new CSharpCodeProvider().CreateCompiler();
CompilerParameters options = new CompilerParameters();
options.ReferencedAssemblies.Add("system.dll");
options.ReferencedAssemblies.Add("system.data.dll");
options.ReferencedAssemblies.Add("system.xml.dll");
options.GenerateExecutable = false;
options.GenerateInMemory = true;
StringBuilder builder = new StringBuilder();
builder.Append("using System; \n");
builder.Append("using System.Data; \n");
builder.Append("using System.Data.SqlClient; \n");
builder.Append("using System.Data.OleDb; \n");
builder.Append("using System.Xml; \n");
builder.Append("namespace EvalGuy { \n");
builder.Append(" public class _Evaluator { \n");
foreach (EvaluatorItem item in items)
{
builder.AppendFormat(" public {0} {1}() ", item.ReturnType.Name, item.Name);
builder.Append("{ ");
builder.AppendFormat("return ({0});", item.Expression);
builder.Append("}\n");
}
builder.Append("} }");
CompilerResults results = compiler.CompileAssemblyFromSource(options, builder.ToString());
if (results.Errors.HasErrors)
{
StringBuilder builder2 = new StringBuilder();
builder2.Append("编译有错误的表达式: ");
foreach (CompilerError error in results.Errors)
{
builder2.AppendFormat("{0}\n", error.ErrorText);
}
throw new Exception("编译错误: " + builder2.ToString());
}
this._Compiled = results.CompiledAssembly.CreateInstance("EvalGuy._Evaluator");
}
(注:本文为 [风影网络工作室] 原创文章,未经书面许可,严禁转载和复制本站的任何信息,违者必究)