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

记录一次.net项目的破解过程
2010年06月30日
  声明:本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.HtmlInputTe xt Admin_WebSetting_Cert::cert
  IL_0019: callvirt instance string [System.Web]System.Web.UI.HtmlControls.HtmlInputCo ntrol::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 "gnjikabjhphjdapjhpfklpmkmpdldmkldobmloimbopmlognk o"
  + "nnljeohjloamcpcnjpgnaaenhailoadmfbmkljpobiafhkbda lpbmjgiildgfehojlmeeoj"
  + "colopnoakhlolfnkcgmndnheehocdfoahlpmboodmnakiaafl aaaeglpdnloeemmelmmdcn"
  + "nhjnkgaoghhokgooogfppgmpgdda"
  IL_004c: ldc.i4 0x921899a
  IL_0051: call string xb9d8bb5e6df032aa.x1110bdd110cdcea4::_xaacba899487 bce8c(string,
  int32)
  IL_0056: call string [mscorlib]System.String::Intern(string) IL_005b: callvirt instance void [System.Web]System.Web.UI.ClientScriptManager::Reg isterStartupScript(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.HtmlInputTe xt Admin_WebSetting_Cert::cert IL_0084: callvirt instance string [System.Web]System.Web.UI.HtmlControls.HtmlInputCo ntrol::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(str ing) 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(str ing, 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(valu etype [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(V S.80).aspx
  生成成功后,你就可以看到IL文件目录下生成的一个DLL文件。
  [偷梁换柱替换修改后的DLL]
  将该DLL替换原网站下的DLL即可。到此,我打开系统并登陆,便可以看到"注册成功"的提示。
  
  事实上,此时你会发现,这原来是如此简单的过程。这个简单的演示中,我们得到的不是一个免费的软件,而是知识。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值