探索黑客技术攻防,实战研究与安全创新

导航菜单

论Net熟肉的正确打开方式

说也奇怪,现在主流的编程语言排行,譬如说TIOBE、.Net平台的编程语言排名都是靠前的,C#更是稳居前五,也就是说,用.Net平台开发的软件不是小众,最近微软的.Net平台开源了几个核心组件,.Net开发的面积很可能还有上升空间,可为什么.Net相关的逆向研究文章不多呢?也许大牛觉得太简单了不屑于动笔,可是会者不难,很多同学其实都并不了解面对.Net编写的程序要如何入手破解,未知总是最可怕的,久了还会产生畏惧感,那我就以一次破解.Net程序为例子,进行相关思路和工具的介绍。

首先随便捡个.Net开发的软件,名叫“Excel分割器”,可以从官网自由下载,但只是共享版,试用次数超了就不能用了。主界面如图1所示。

A57.png

图1

先别急着动手,所谓兵来将挡水来土掩,很多文章一上来就直奔对抗方法去了,但正确确定对抗的对象,才是对症下药的前提基础。.Net逆向也一样,首先要确定是不是.Net程序,毕竟后缀名都是exe。方法很多了,可以用Hex编辑器看看是不是PE32+头结构,或者上查壳工具,这里我只补充一个很容易漏的但又很快捷的方法:看说明。现在很多软件的说明文档都写得比较详细,这其实有利于我们快速把握环境信息,这个Excel分割器的说明里(作者命名为“使用前先阅读我”)就有这么一段话:“xp用户如果您的电脑没法运行该软件,请先安装framework2.0”,基本可以确定它是.Net平台的程序了。

接着上工具。这里先要明确一个.Net程序运行的基本模型结构,.Net平台的程序有点类似Java,是需要依靠高级编程语言虚拟机运行的,名叫.Netframework,从源代码到真正执行,其实经历两大步,第一大步是将源代码编译成MSIL(MicrosoftIntermediateLanguage),Java那边叫做字节码,不妨理解成一种特殊的“机器码”,只不过这种“机器码”仅供对应的高级编程语言虚拟机执行,此虚拟机非彼虚拟机,是个进程虚拟机。第二大步,就是解释执行了,虚拟机.Netframework将执行第一大步编译得到的MSIL,当然现在为了优化性能,还采用了JIT等技术,将MSIL二次编译成真正的机器码,但这只是性能上的调整,整个.Net模型结构还是该只按两大步理解。

说清了这个结构,就可以接着讨论工具的问题。最常见的问题是:OD能不能调.Net程序?前面说了,.Net程序最终是要由“进程虚拟机”执行的,OD可以调试Windows下Ring3层的进程,当然可以调试这个虚拟机,但有个问题:难度太大。Vmprotect之前之所以独霸武林,正是因为它单独搞了一套“机器码”,将x86的机器码编译成vmp的机器码,最终在由vmp的虚拟机解释执行,和.Net的执行模型非常相近,也就不难推断出上OD直接调.Net程序的难度了。

那要怎么逆向.Net程序呢?前面已经说明,第一步是将源代码编译成MSIL,如果有一款神器,能将MSIL直接反编译回源码,那逆向岂不是和阅读源码一样轻松愉快了?还真的有,.NETReflector就是这么一款神器,使用方法非常简单,把.Net编写的主程序拖进去,就直接吐出反编译后的源码,看着就像一个.Net工程(图2),连类名方法名变量名都一一还原列出来:

A58.png

图2

可能会有同学担心,.Net还分C#和VB.Net呢,语法格式完全不同,而且这两款编程语言热度很高,要怎么确定目标程序用哪款语言编写呢?完全不必担心,.Net平台的设计初衷之一就是跨语言,只要功能相同,无论采用哪种语言,都将统一变成等价的MSIL,也就是说,同一个MSIL理论上可以反编译成等价的C#或者VB.Net源码。.NETReflector事实上也实现了这个功能,只需要选择想要的语言即可得到对应的源码,这里选择C#(图3)。

A59.png

图3

好,现在可以直接把源码导出来了(图4)。对,你没看错,导出源码,而且直接导出来就是一个完整的C#工程,逆向工程做到这种地步,不但连源码都有,而且连工程文件都给你建好了(图5),还有没什么好再说的了?要怎么玩耍怎么改,剩下的就交给想象力吧。

A60.png

A61.png

这样就结束了?错了,懒是没有止境的,譬如说我,我就懒得为破解一个共享软件打开VS,MS的IDE启动越来越慢,而且我都看到源码了,就不能直接改吗?抱歉,也许以后会有,现在还真的不行,不过.NETReflector下有一款名为Reflexil的插件提供了修改MSIL的功能,还可以直接保存成exe,算是间接满足了我们的想法。这里就以破解这款Excel分割器完整走一遍流程。

先定位到检测注册的地方,在MainFr.CheckReg,代码如下:


privatevoidCheckReg()
{
DoRegreg=newDoReg();
if(reg.CheckKey())
{
if(Ver.IsCorrectSn(newHardWare().GetCpuID(),reg.GetSetting(sn)))
{
this.Text=this.Text+(已注册);
this.RegBtn.Visible=false;
this.CutBtn.Location
=
new
Point((base.Size.Width
/
2)
-
(this.CutBtn.Size.Width/2),this.CutBtn.Location.Y);
}
else
{
intnum=Convert.ToInt32(reg.GetSetting(c,1));
if((num1)||(num15))
{
MessageBox.Show(该软件试用次数已到!请注册成为正式版!,提
示,MessageBoxButtons.OK,MessageBoxIcon.Asterisk);
newRegFr().ShowDialog();
Application.Exit();
}
else
{
num--;
this.Text=this.Text+string.Format((未注册,您还可以使用{0}次),
num);
reg.SaveSetting(c,num.ToString());
}
}
}
else
{
reg.InitReg();
reg.SaveSetting(c,15);
this.Text=this.Text+string.Format((未注册,您还可以使用{0}次),15);
}
}


基本流程就是通过注册表判断是否已经注册,判断成功就进入“已注册”流程,否则就计算还能试用的次数并给出相应提示。判断的关键在于Ver.IsCorrectSn(newHardWare().GetCpuID(),reg.GetSetting(sn)),现在看看Ver.IsCorrectSn,源码如下:


publicstaticboolIsCorrectSn(stringcid,stringsn)
{
char[]chArray=cid.ToCharArray();
stringstr=;
for(inti=0;ichArray.Length;i++)
{
str=str+(((chArray[i]+i)+2)).ToString();
}
return(str==sn);
}


非常简单,关键就在于最后的return(str==sn),先比较再返回比较结果。让这个返回值永远为真就可以成功破解了。有了思路,现在需要改一下对应的MSIL。使用Reflexil插件完成这项工具。Reflexil不是自带的,需要先通过Addin安装(图6),点“+”:

A62.png

图6

通过菜单栏Tools调出Reflexil,我的是1.8版,文档说最高只支持.NETReflector的8.3版,但我试了最新版同样也支持。回到Ver.IsCorrectSn,现在可以看到Reflexil栏显示出对应的MSIL代码(图7):

A63.png

图7

要修改MSIL,首先就要读懂它,这里没什么捷径好走,掌握了MSIL的基本语法之后,找到return(str==sn)对应的MSIL(图8):

A64.png

.Net的虚拟机采用堆栈机的结构,因此它的指令操作也多是堆栈操作,上述MSIL基本上可以理解成分别将str和sn压栈,然后调用字符串比较函数,最后将(放在堆栈中的)结果ret。只要将两个压栈参数改成一样,就能保证返回值一定为真了。这里选择把stloc.3改成ldloc.1,操作方法是对着stloc.3右键选择Edit,进行修改后点击update即可(图9):


A64.png

图9

最后是保存。对着项目名右键,选择Reflexilv1.8-Saveas...,保存成一个新的exe,破解就完成了。从逆向出源码工程,到直接破解,这篇文章都演示了一遍,相信各位同学对.Net程序如何逆向已经心中有数。不过最后我要泼一下冷水,这款.Net程序这么好破解,是因为它没经过任何保护,但既然逆向工具已经如此成熟,当然对应的盾也该有相当的发展。目前.Net较为主流的保护方式是进行混淆,首先各种名称就不要指望能看得到了,代码的阅读难度也将提升一个数量级,就不能简单套用本篇的方法了。这种混淆过的程序,不妨叫做生肉,生肉先要做成熟肉才能入口,因此,混淆过的程序,也要想办法先去掉混淆保护,变成熟肉,再用本篇的“熟肉正确打开方式”,就能达到逆向的目的了。

(完)

本文内容所提及均为本地测试或经过目标授权同意,旨在提供教育和研究信息,内容已去除关键敏感信息和代码,以防止被恶意利用。文章内提及的漏洞均已修复,作者不鼓励或支持任何形式的非法破解行为。