您的位置:首页 > 其它

记录一次.net项目的破解过程

2010-06-30 14:24 246 查看
声明:本CSDN博客中的所有文章均为本人原创 请勿转载

注意:本次演示破解了一个当前网络的商业项目,请务必遵守互联网及软件版权的相关规定。不要对原公司造成侵权,及利益上的侵害。

同时,我也无意侵犯该商业项目的相关权益。下面的内容仅供学习交流。

[如何破解]
破解一个软件,最强大,最直接,最让人情有独钟的非调试莫属。不管怎样,调试方式几乎像一把软件的解剖刀。无所不能。通过调试,能够窥探加密,为程序去壳,解密口令,修改源码,这些关键的操作在许多强大的工具支持下其实并不困难,一些很著名的工具可以帮助我们轻松完成。微软也提供了一些调试工具,如winDbg,虽然它可能不是最强大的,但却是最适应于windows调试的工具。

软件的加密,加壳,证书等手段都是人们总结的能够抵制破解的一些方法。正如人们所说,没有一个软件是完美的,也没有一个软件是不能被破解的。
这次演示中并不打算使用调试,而是一种类似测试中的“白盒”形式来分析并提出方案。专业的建议使用调试。
下面简单聊几句.net的基础知识,这些是我们使用.net的基础。
作为.net应用程序,它依赖于.net的CLR运行,CLR(公共语言运行时)是.net平台的核心,它管理了一个内存区,将其称为托管堆。所以许多人称.net程序为托管应用程序。一般WIN32等程序使用的内存分配直接由操作系统管理,即存储在操作系统的分配的堆栈上。但.net内存由CRL管理。因此,当应用层发生异常时,将不会直接抛向操作系统,而是首先由CLR接管并处理。CLR自身很难出现异常。所以,这样的程序在一定程度上是安全的。CRL内驻留的一个GC(垃圾回收)进程来专门负责对象的回收任务。GC的出现降低了程序开发人员的内存管理负担,同时在一定程度上防止了疏忽造成的内存泄露。
CLR类似于一个虚拟CPU,只不过它面向的数据不是进制码,而是MSIL。MSIL(简称IL),是微软创建的一种中间语言。微软的.net平台的语言无关性就是靠它来体现。在NET平台上,你可以使用多种语言进行开发,最终,将被编译为同一种MSIL语言。你可以将其看做一种低级的类似Win32/64汇编的语言。一句话,.net编译生成的文件exe,Dll等,都是以MSIL码存储的。它只有在运行时才会被JIT即时译为适应于目标机器CPU的语言。而不像C,C++等是直接的机器语言。事实上如JAVA等有平台虚拟机的程序框架都具有如此的特征。JAVA就是被译为JVM可执行的字节码存放。这样的好处是跨平台能力增强。并且具备了一次编译多次运行的较强移植能力。

上面的基础应该足够了,回到正题,我们要破解一个软件,在不通过调试时似乎理解源码是至关重要的,因为我们需要知道软件中在哪些地方进行了限制。但没有源码,就必须有一定的低层知识。由一些MFC,WIN32等直接生成的文件已经成了二进制代码,经过了一系列的链接,和优化,已经和源代码相去甚远了,你可以使用如UltraEdit的工具直接查看甚至修改二进制文件,但这几乎不可行,不过你可以反汇编,汇编语言要好理解多了,想返回源码的想法在Win32/64下最好打消。但在.net下却可以很好的还原到一定的程度。汇编你可能也不太熟悉,但.net生成的是MSIL,所以,我们只需要看MSIL。和汇编一样,MSIL也是由一些助记符构成的,这些指令的含意,你可以在微软的相关技术文档上查阅。我就在此不多延伸。
既然我们看到的DLL都是MSIL,那么我们可以通过VS下的工具箱中的IL DASM来查看MSIL。IL DASM 和IL ASM工具都是微软提供的比较基础和强大的反IL工具。

[寻找突破口]

从演示项目中找到可能使用证书的页面或者事件,逐渐排除一些无关的业务逻辑。首先我们登陆系统。可以发现,当前系统没有注册。



其次找到验证的入口,点击“未授权网站”铵钮。打开如下页面:



可以看到,底部有一个上传证书的文本框。根据WEB程序的特点。这些处理全部集中在下面“确认提交”这个事件中完成的。单击右键-》属性,可以看到该页面的名称,然后在VS项目下找到该页面。但我们从原页面程序中并没有看到该事件的处理函数。Asp.net的机制是后台与页面相分离,通过页面指令来关联后台文件,所以,使用VS环境寻找并打开这一页面源码,可以看到后台地址的映射:



可以看到,后台程序放在了ShopWe 空间下的Admin_ShopWeCert类。

这个名为ShopWe的DLL在Bin目录下。



[顺藤摸瓜 寻找相应的工作模块]
既然锁定了入口的DLL,我们便可以使用VS提供的IL DASM工具反射原程序集,我不想在此说反射是多么强大的一种工具,这全归功于元数据集。
在 程序-》SDK-》工具,打开DASM,然后选择“文件”=-》选择SHOPWE ,即可打开该DLL,如下图所示。



上面清楚的列举了当前模块中的类,方法,字段和属性,还有版本等信息。

对于右侧的三角符号,正方形的含义不清楚的,可以查下IL DASM的用法中有介绍。

蓝色的“集成快”表示类,“集成块”中间有I标识的是接口,向右红三角表示版本,元数据信息,平行四边形表示字段,向上红三角表示属性,正方形是方法。带S的是静态方法。
如果不清楚,请单击IL DSAM 帮助菜单。或者查找 该工具用法。

在页面的提交事件中,我们已经看到,该事件的处理函数为Submit_Click



双击列表中的Submit_Click方法以查看MSIL代码。下面是该方法的MSIL码:
.method family hidebysig instance void Submit_Click(object sender,
class [mscorlib]System.EventArgs e) cil managed
{
// 代码大小 250 (0xfa)
.maxstack 5
.locals init (class [Common]ShopWe.Common.XMLControl V_0)
IL_0000: ldarg.0
IL_0001: ldfld class [System.Web]System.Web.UI.WebControls.FileUpload Admin_WebSetting_Cert::UploadCert
IL_0006: callvirt instance bool [System.Web]System.Web.UI.WebControls.FileUpload::get_HasFile()
IL_000b: brfalse.s IL_0031
IL_000d: br IL_00d9
IL_0012: ret
IL_0013: ldarg.0
IL_0014: ldfld class [System.Web]System.Web.UI.HtmlControls.HtmlInputText Admin_WebSetting_Cert::cert
IL_0019: callvirt instance string [System.Web]System.Web.UI.HtmlControls.HtmlInputControl::get_Value()
IL_001e: ldsfld string [mscorlib]System.String::Empty
IL_0023: call bool [mscorlib]System.String::op_Inequality(string,
string)
IL_0028: brtrue.s IL_009a
IL_002a: ldc.i4 0x1
IL_002f: brfalse.s IL_0062
IL_0031: ldarg.0
IL_0032: callvirt instance class [System.Web]System.Web.UI.Page [System.Web]System.Web.UI.Control::get_Page()
IL_0037: callvirt instance class [System.Web]System.Web.UI.ClientScriptManager [System.Web]System.Web.UI.Page::get_ClientScript()
IL_003c: ldarg.0
IL_003d: call instance class [mscorlib]System.Type [mscorlib]System.Object::GetType()
IL_0042: ldstr ""
IL_0047: ldstr "gnjikabjhphjdapjhpfklpmkmpdldmkldobmloimbopmlognko"
+ "nnljeohjloamcpcnjpgnaaenhailoadmfbmkljpobiafhkbdalpbmjgiildgfehojlmeeoj"
+ "colopnoakhlolfnkcgmndnheehocdfoahlpmboodmnakiaaflaaaeglpdnloeemmelmmdcn"
+ "nhjnkgaoghhokgooogfppgmpgdda"
IL_004c: ldc.i4 0x921899a
IL_0051: call string xb9d8bb5e6df032aa.x1110bdd110cdcea4::_xaacba899487bce8c(string,
int32)
IL_0056: call string [mscorlib]System.String::Intern(string)
IL_005b: callvirt instance void [System.Web]System.Web.UI.ClientScriptManager::RegisterStartupScript(class [mscorlib]System.Type,
string,
string)
IL_0060: br.s IL_0098
IL_0062: newobj instance void [Common]ShopWe.Common.XMLControl::.ctor()
IL_0067: stloc.0
IL_0068: ldloc.0
IL_0069: ldarg.0
IL_006a: call instance class [System.Web]System.Web.HttpServerUtility [System.Web]System.Web.UI.Page::get_Server()
IL_006f: ldstr "~/Setting/ShowSetting.xml"
IL_0074: callvirt instance string [System.Web]System.Web.HttpServerUtility::MapPath(string)
IL_0079: ldstr "root/Cert"
IL_007e: ldarg.0
IL_007f: ldfld class [System.Web]System.Web.UI.HtmlControls.HtmlInputText Admin_WebSetting_Cert::cert
IL_0084: callvirt instance string [System.Web]System.Web.UI.HtmlControls.HtmlInputControl::get_Value()
IL_0089: callvirt instance void [Common]ShopWe.Common.XMLControl::UpdateInnerText(string,
string,
string)
IL_008e: ldc.i4 0xfffffffe
IL_0093: brtrue IL_0012
IL_0098: br.s IL_00f9
IL_009a: ldarg.0
IL_009b: ldstr "~/cert/"
IL_00a0: call instance string [System.Web]System.Web.UI.Page::MapPath(string)
IL_00a5: call bool [mscorlib]System.IO.Directory::Exists(string)
IL_00aa: brfalse.s IL_00de
IL_00ac: ldarg.0
IL_00ad: ldfld class [System.Web]System.Web.UI.WebControls.FileUpload Admin_WebSetting_Cert::UploadCert
IL_00b2: ldarg.0
IL_00b3: call instance class [System.Web]System.Web.HttpServerUtility [System.Web]System.Web.UI.Page::get_Server()
IL_00b8: ldstr "~/cert/"
IL_00bd: callvirt instance string [System.Web]System.Web.HttpServerUtility::MapPath(string)
IL_00c2: ldarg.0
IL_00c3: ldfld class [System.Web]System.Web.UI.WebControls.FileUpload Admin_WebSetting_Cert::UploadCert
IL_00c8: callvirt instance string [System.Web]System.Web.UI.WebControls.FileUpload::get_FileName()
IL_00cd: call string [mscorlib]System.String::Concat(string,
string)
IL_00d2: callvirt instance void [System.Web]System.Web.UI.WebControls.FileUpload::SaveAs(string)
IL_00d7: br.s IL_00f4
IL_00d9: br IL_0013
IL_00de: ldarg.0
IL_00df: ldstr "~/cert/"
IL_00e4: call instance string [System.Web]System.Web.UI.Page::MapPath(string)
IL_00e9: call class [mscorlib]System.IO.DirectoryInfo [mscorlib]System.IO.Directory::CreateDirectory(string)
IL_00ee: pop
IL_00ef: ldc.i4.0
IL_00f0: brtrue.s IL_00ac
IL_00f2: br.s IL_00ac
IL_00f4: br IL_0062
IL_00f9: ret
} // end of method Admin_WebSetting_Cert::Submit_Click

如果你对MSIL还不了解,那么必然会面对上面的代码感到迷惑。不要紧,我只所以刚才将其描述为助记符是有原因的,上面的指令(第二列)表示了一些特定的意义。你也应该发现,这些指令是一些单词的简单形式:如:ret (return)
还有意思类的:.ldstr (load string) ,call (调用静态方法)callvirt(调用实例方法)ldarg(加载参数)
Ld这个前缀预示着要向托管堆分配内存并复制数据。
我们同时看到有熟悉的类库成员,一般使用类库时就会以类似原形的形式出现在代码中。
第一列是什么意思?怎么那么像地址。它就是地址,这种地址是相对地址,用于进行指令跳转,并不是内存中的真正地址。
上面蓝色的部分只是我在第一次接触MSIL时的猜测。之所以写出来是想让别人多一种懒人的办法。MSIL的相关知识建议看国外较为权威的书籍,或者去MSDN上进行指令含义的速查。想学MSIL,我可以推荐几本书,当前引进国内的这一方面的书几乎没有。

[查看DLL的内部,跟踪验证函数的思路,确定修改地点和修改方式]
现在我们分析上面的代码,不论怎样,这个事件当中必然有验证证书的逻辑。我们只需要先定位这段逻辑。
我们发现这段核心的验证代码:



其它的代码很明显是属于一般的IO操作。这一段初始了一个ValidateCert对象并调用它的一个返回BOOL的ValidCert方法。由IF来判断,因此,这一段属于验证判断。是我们需要修改的部分。很明显,我们可以取掉这段IF判断,只让其顺序执行为真的代码即可。

类似于下面的代码:
If(true)
{
//TO DO:合法证书操作
}else{
//TO DO: 不合法证书
}
但是,根据一般编程规律。一个验证逻辑应该被共享。因为如果验证仅仅在该事件被触发时才验证一次,那么当页面加载时,如果判断该软件是否注册?所以,很明显这个验证会在许多地方进行。我们要一一找出并修改,的确是一件很麻烦的事。但假设它们的验证点调用的是一个验证方法呢?事实上前面已经说了,这个逻辑应该被共享,所以,我们在这里不应该去改,而继续追踪 ValidCert方法。
下图为该验证类ValidateCert:



这个方法的代码如下,

.method public hidebysig instance bool ValidCert() cil managed
{
// 代码大小 311 (0x137)
.maxstack 5
.locals init (class [Common]ShopWe.Common.XMLControl V_0,
char[] V_1,
string[] V_2,
bool V_3,
bool V_4)
.try
{
IL_0000: call class [System.Web]System.Web.HttpContext [System.Web]System.Web.HttpContext::get_Current()
IL_0005: callvirt instance class [System.Web]System.Web.HttpRequest [System.Web]System.Web.HttpContext::get_Request()
IL_000a: ldstr "~/Certificate.xml"
IL_000f: callvirt instance string [System.Web]System.Web.HttpRequest::MapPath(string)
IL_0014: call bool [mscorlib]System.IO.File::Exists(string)
IL_0019: brfalse.s IL_0020
IL_001b: br IL_00d7
IL_0020: ldc.i4.0
IL_0021: stloc.3
IL_0022: ldloc V_3
IL_0026: conv.u4
IL_0027: ldloc V_3
IL_002b: conv.u4
IL_002c: add
IL_002d: ldc.i4.0
IL_002e: clt.un
IL_0030: stloc V_4
IL_0034: ldloc V_4
IL_0038: brfalse.s IL_0075
IL_003a: ldc.i4.1
IL_003b: stloc.3
IL_003c: leave IL_0135
IL_0041: br.s IL_006b
IL_0043: call class [System.Web]System.Web.HttpContext [System.Web]System.Web.HttpContext::get_Current()
IL_0048: callvirt instance class [System.Web]System.Web.HttpRequest [System.Web]System.Web.HttpContext::get_Request()
IL_004d: callvirt instance class [System]System.Uri [System.Web]System.Web.HttpRequest::get_Url()
IL_0052: callvirt instance string [System]System.Uri::get_Host()
IL_0057: callvirt instance string [mscorlib]System.String::ToLower()
IL_005c: ldloc.2
IL_005d: ldc.i4.0
IL_005e: ldelem.ref
IL_005f: callvirt instance string [mscorlib]System.String::ToLower()
IL_0064: callvirt instance bool [mscorlib]System.String::Contains(string)
IL_0069: brtrue.s IL_00b1
IL_006b: ldc.i4.0
IL_006c: stloc.3
IL_006d: leave IL_0135
IL_0072: ldc.i4.0
IL_0073: brfalse.s IL_0020
IL_0075: br.s IL_00d0
IL_0077: ldarg.0
IL_0078: ldloc.0
IL_0079: call class [System.Web]System.Web.HttpContext [System.Web]System.Web.HttpContext::get_Current()
IL_007e: callvirt instance class [System.Web]System.Web.HttpRequest [System.Web]System.Web.HttpContext::get_Request()
IL_0083: ldstr "~/Certificate.xml"
IL_0088: callvirt instance string [System.Web]System.Web.HttpRequest::MapPath(string)
IL_008d: ldstr "root/CertCode"
IL_0092: callvirt instance string [Common]ShopWe.Common.XMLControl::GetInnerText(string,
string)
IL_0097: call instance string ValidateCert::xcc381ffa3ede662f(string)
IL_009c: ldc.i4.1
IL_009d: newarr [mscorlib]System.Char
IL_00a2: stloc.1
IL_00a3: ldloc.1
IL_00a4: ldc.i4.0
IL_00a5: ldc.i4.s 124
IL_00a7: stelem.i2
IL_00a8: ldloc.1
IL_00a9: callvirt instance string[] [mscorlib]System.String::Split(char[])
IL_00ae: stloc.2
IL_00af: br.s IL_0043
IL_00b1: call valuetype [mscorlib]System.DateTime [mscorlib]System.DateTime::get_Now()
IL_00b6: ldloc.2
IL_00b7: ldc.i4.1
IL_00b8: ldelem.ref
IL_00b9: call valuetype [mscorlib]System.DateTime [mscorlib]System.DateTime::Parse(string)
IL_00be: call bool [mscorlib]System.DateTime::op_LessThanOrEqual(valuetype [mscorlib]System.DateTime,
valuetype [mscorlib]System.DateTime)
IL_00c3: brfalse.s IL_006b
IL_00c5: ldc.i4.0
IL_00c6: brtrue IL_0043
IL_00cb: br IL_003a
IL_00d0: ldc.i4 0x3
IL_00d5: brtrue.s IL_00fa
IL_00d7: ldloc V_3
IL_00db: conv.u4
IL_00dc: ldloc V_3
IL_00e0: conv.u4
IL_00e1: sub
IL_00e2: ldc.i4.0
IL_00e3: clt.un
IL_00e5: stloc V_4
IL_00e9: ldloc V_4
IL_00ed: brtrue.s IL_00ef
IL_00ef: newobj instance void [Common]ShopWe.Common.XMLControl::.ctor()
IL_00f4: stloc.0
IL_00f5: br IL_0077
IL_00fa: leave.s IL_0135
} // end .try
catch [mscorlib]System.Object
{
IL_00fc: pop
IL_00fd: call class [System.Web]System.Web.HttpContext [System.Web]System.Web.HttpContext::get_Current()
IL_0102: callvirt instance class [System.Web]System.Web.HttpRequest [System.Web]System.Web.HttpContext::get_Request()
IL_0107: ldstr "~/Certificate.xml"
IL_010c: callvirt instance string [System.Web]System.Web.HttpRequest::MapPath(string)
IL_0111: call bool [mscorlib]System.IO.File::Exists(string)
IL_0116: brfalse.s IL_0131
IL_0118: call class [System.Web]System.Web.HttpContext [System.Web]System.Web.HttpContext::get_Current()
IL_011d: callvirt instance class [System.Web]System.Web.HttpRequest [System.Web]System.Web.HttpContext::get_Request()
IL_0122: ldstr "~/Certificate.xml"
IL_0127: callvirt instance string [System.Web]System.Web.HttpRequest::MapPath(string)
IL_012c: call void [mscorlib]System.IO.File::Delete(string)
IL_0131: ldc.i4.0
IL_0132: stloc.3
IL_0133: leave.s IL_0135
} // end handler
IL_0135: ldloc.3
IL_0136: ret
} // end of method ValidateCert::ValidCert

或许和上面的代码一样,你感觉很晕,不要紧,首先这个方法返回的是一个BOOL,我们可以想到,返回真是为验证通过,返回假时为验证不通过。事实上代码中很明显进行了一次“证书加密的字符串比较”
如下面的核心代码。



这段代码进行了一次比较,如果比较结果为真,则表示验证成功,当前方法返回为真。代码中是从XML中读取一个字符串并调用一个方法解密后分隔为一个数组,这段比较了数组第一个项和当前路径的值。

事实上完全没有必要看方法的内部,你既然猜测它返回真假就意味着是否通过验证,那么只要注释该方法内部,加入直接返回真的代码即可。这种情况下,必须是在该方法内部没有和其它验证部分有影响的操作。比如,该方法内部同时写了一个私钥文件。而其它地方却需要使用这个文件。这种情况是不能这样处理的。但是,通过这段代码,并没有发现这类操作。
所以,我们只需要删除该方法内部的代码,编写一个返回为真的语句即可。

确定了修改地点和修改方案,那么接下来我们要将该DLL转储为IL文件,以便修改它。
[反编译DLL为MSIL代码]
有许多工具可以选择,但微软提供的IL DASM工具已经绰绰有余了。
在IL DASM打开 ShopWe 的DLL,然后执行 文件-》转储,在弹出的文件对话框中选择转储地址,输入文件名后确定即可,此时,你会在设置的路径下看到两个文件,一个为IL后缀的MSIL文件和一个RES的资源文件。



[修改MSIL]

现在,我们有了IL文件,接下来需要修改这个IL。用一般的文本编辑器都可以打开编辑。
在该.method public hidebysig instance bool
ValidCert() cil managed
方法体开始加入如下代码,这段代码只是简单的返回了一个真值。

//破解代码
// 代码大小 7 (0x7)
.maxstack 1
.locals init ([0] bool CS$1$0000)
IL_0000: nop
IL_0001: ldc.i4.1
IL_0002: stloc.0
IL_0003: br.s IL_0005
IL_0005: ldloc.0
IL_0006: ret
//破解代码完

注释下面的逻辑,IL同样支持C++的单行多行注释符。
接下来编译修改过的DLL。

[编译IL文件为DLL]
我使用微软的ilasm工具进行编译
在VS2005命令提示符下,输入ilasm filefullname /dll
Filefuname:你的IL文件的路径和文件名,后面的/DLL选项是告诉编译器输出为DLL
回车即可显示编译进度:



最后提示成功。如果没有成功,请仔细查看输出的错误信息。

一般常见的有:你的文件不可访问。这常是由于命令参数不对。或者当前用户缺少权限。
还有,就是你当前目录已经生成了一个DLL,并且由其它程序使用而造成无法覆盖的问题。
同时也要注意你修改的源文件是不是有语法等错误。
不会使用,请参考:http://msdn.microsoft.com/zh-cn/library/496e4ekx(VS.80).aspx

生成成功后,你就可以看到IL文件目录下生成的一个DLL文件。

[偷梁换柱 替换修改后的DLL]
将该DLL替换原网站下的DLL即可。到此,我打开系统并登陆,便可以看到“注册成功”的提示。



事实上,此时你会发现,这原来是如此简单的过程。这个简单的演示中,我们得到的不是一个免费的软件,而是知识。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: