记录一次.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即可。到此,我打开系统并登陆,便可以看到"注册成功"的提示。
  
  事实上,此时你会发现,这原来是如此简单的过程。这个简单的演示中,我们得到的不是一个免费的软件,而是知识。

猜你喜欢

转载自juq876mk.iteye.com/blog/1361260