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"); }(注:本文为 [风影网络工作室] 原创文章,未经书面许可,严禁转载和复制本站的任何信息,违者必究)