.net程序破解实战之Advanced System Cleane
2010-09-29 18:08
对于我们来说,.net最大的特点就是可以“跨平台”。大多数运行在Windows下的软件都不能很好地支持Linux,也就是说在Windows下能够正常使用的软件在Linux下不能正常运行,这样就很不方便日常的学习和交流。但是有了.net,这一切就发生变化,不管我们的操作系统是什么,只要装有.net,这个系统就可以运行.net的所有语言。 .net包含很多语言,对于破解来说,我们只需要了解.net的开发语言就可以了。.net的开发语言主要包括C#.net、VB.net、C++.net、J#.net等,这些也是最常见的。最重要的一点是,虽然VB和VB.net语言看起来类似,但是其内部运作是完全不同的。这一点一定要注意。关于.net就先介绍到这里,更多的细节大家会慢慢体会到的。 目标篇 我们的目标软件是Advanced System Cleaner,一个功能很强大的系统清理软件,可以方便地清理系统中的垃圾文件和注册表。 按照破解的一般步骤,照例用PEiD检查一下,看到了吗?检查结果是“Microsoft Visual C# / Basic .net”,如图1所示。很明显,这是一个.net系列的软件,一般的破解工具拿它是没有办法的,所以必须有合适的工具才能完成我们的任务。
图1 工具篇 .net程序对于破解而言,可以算是一个崭新的领域,不论从破解的手段还是方法来说,都是一个全新的挑战。破解手段需要改变,破解方法也需要更新。仅从使用工具这个方面来看,我们就可以发现不小的变化。对付普通的软件,主要用OllyDBG或者是W32dASM就可以轻松搞定了;但是对于.net程序,这两个工具就显得力不从心了,基本无从下手。那破解.net程序的工具有哪些呢?破解的第一步依然是检测软件的基本信息,如何对.net程序进行检测呢?这个很简单,就像对待一般的程序一样,用PEiD就可以搞定了,使用的方法也没有任何变化。 要继续破解.net程序,我们就得认识两个新朋友:Reflector和UltraEdit32。Reflector是对付.net程序的杀手锏,它可以轻易地将.net程序进行反编译,就好像是OD或W32dASM一样,帮助我们完成分析程序的任务。当使用Reflector反编译一个程序之后,我们就可以根据反编译的结果进行进一步的分析了,比如说寻找注册信息、定位关键代码等。这里要说明的是,在Reflector中我们是不能修改程序的代码的(不像使用OD可以任意修改程序的代码),这样一来问题就出现了,怎样修改.net程序中的代码呢?这时就要用到UltraEdit32了,其用处就是用来修改关键代码的,与在OD中修改跳转或是标志位来达到破解程序的效果一样。 总的来说,对于.net程序,目前还不能只凭一个工具打天下,必须把不同的工具结合起来使用才能达到目的,正如这里的Reflector与UltraEdit32。如果把破解软件比作是用导弹炸毁一个目标,那么Reflector的作用就是这个导弹的导航系统,用来寻找方向和定位目标,UltraEdit32则是导弹的战斗部,肩负炸毁目标的重任。 操作篇 用Reflector打开我们的目标软件,稍等片刻Reflector就帮我们把程序分析完毕了,但Reflector似乎没有什么明显的反应,如图2所示,没有像OD那样出现一大片的代码。如何在Reflector中看到分析的结果呢?此时注意Reflector主界面上的变化,比打开目标程序之前相比多了一项“ASC”,它是什么呢?它就是我们的目标程序了,只要单击这一项就可以看到Reflector为我们分析的结果了。
图2 打开之后会发现这里有两个选项,一项是“ASC.exe”,另一项是“Resources”。第一项“ASC.exe”中的内容是Reflector为我们分析好的信息,而“Resources”中包含的内容则是程序用到的一些资源文件,比如图标、图片等。我们关心的当然是分析好的程序信息了,所以打开“ASC.exe”一探究竟。很明显,又现了四个不同的子项,这里我们选择“AdvancedSystemCleaner”这一项打开,如图3所示,更多的内容又出现了。在新出现的几个选项里,我们选择“Module2”打开,有没有发现打开这一项后出现的选项里有一个内容与“Register”有关?经过层层的选择,总算出现了与注册有关的内容,当然要提高警惕了,如图4所示。我们打开这一项看看,看到了吗?“GetRegCodeValue”、“GetRegName” 这些非常有用的内容已经摆在我们面前了,如图5所示。选择“Registed”这一项打开,发现并没有出现新的子项,而是在旁边新开了一个窗口,里面有一些很让人迷茫的代码,如图6所示。不过现在我们已经可以肯定了,这些代码与软件的注册必然存在一些关系。既然可以肯定这一点,那么目前的主要问题就是如何去看懂这段代码了,如果可以成功地理解这段代码的含义,那么最少也可以寻找到一些与软件注册有关的信息,幸运的话,也许这段代码就是软件注册的核心代码。若找到了核心代码,剩下的工作就会像在OD中破解软件一样,痛痛快快地修改代码了。
图3 图4
![](//image50.360doc.cn/DownloadImg/2012/02/2923/21928814_5.jpg) 图6 于是,现在的主要问题就是如何去看懂这段代码了。想要看懂这段代码,先得把它转换一下。我们再一次关注Reflector的主界面,看到有一个下拉菜单,目前显示的是“IL”,打开菜单,在选项里选择“C#”,如图7所示。为什么要这样做呢?简单地给大家解释一下。回忆一下用OD破解的过程,当我们用OD载入准备破解的软件之后,OD会把分析的结果显示出来,这个分析的结果是以汇编语言的形式显示的。类似的道理,Reflector也需要通过一种方式把分析结果表示出来,于是我们就在Reflector中看到了上面提到的代码。但是与OD不同的是,OD是通过汇编语言来显示结果的,而Reflector则是用“IL”语言显示出来的。
![](//pubimage.360doc.com/wz/default.gif) 图7 小提示 MSIL是将.Net代码转化为机器语言的一个中间过程,它是一种介于高级语言和基于Intel的汇编语言的伪汇编语言。这里的“IL”语言我们是不容易看懂的,所以就要转换成一种比较容易理解的语言。相比较“IL”而言,“C#”代码更容易被我们接受。 选择“C#”之后会弹出一个对话框,我们不用关心里面的内容,单击“OK”就可以了,如图8所示。之后Reflector窗口中的内容会发生变化,“IL”语言已经被转换成标准的“C#”代码了,清晰多了。到了这一步,我们就可以仔细地分析一下这些代码了。 图8 分析篇 我们就从第一部分代码开始看吧,完整的代码如图9所示。稍微有点编程基础的朋友可以很容易地发现,这是一个“IF条件语句”,我们试着来把这段代码翻译一下。
图9 首先,程序在这里给两个变量赋了值,代码如下。意思是将“GetRegName()”里的值赋给“left”这个变量,将“GetRegCodeValue()”里的值赋给“regCodeValue”。 string left = GetRegName(); string regCodeValue = GetRegCodeValue(); 接着,程序开始对变量的值进行判断,代码如下。意思是如果“regCodeValue”这个变量的值等于0,并且“left”这个变量的值也等于0,那么“blnRegistered”这一项的值就是“false”。有编程基础的朋友可能已经看懂了,可是对于小菜们来说还是很生涩,怎样理解这句代码的含义呢?其实我们可以这样简单的理解。如果注册名的内容等于0,而且注册码的内容也等于0,那么就提示使用者“软件未注册”。 if ((Operators.CompareString(regCodeValue, "", false) == 0) | (Operators.CompareString(left, "", false) == 0)) { Module1.blnRegistered = false; } 小提示 简单理解“Module1.blnRegistered = false”的意思就是,软件的注册状态为“假”,那就是软件“未注册”喽! 理解了这句代码的含义,新的问题又出现了!这段代码里没有具体的注册信息收集过程,也没有明显的算法计算,程序为什么要做这样的一个检验呢?这就要从软件的“注册检验流程”开始说起了(为了方便说明问题,暂且把这个过程叫做软件的“注册检验流程”)。“注册检验流程”是一个怎样的过程呢?我们一起来想象一下这个过程。 首先,软件运行起来了,由于是共享软件,所以在使用上就会有注册版和未注册版之分,两者有很大的区别。比如未注册的版本不能使用软件的全部功能,或者在使用时会有时间限制等情况。于是在刚运行的时候,软件就会判断当前的使用者是不是注册用户。如果是“未注册”的用户,那么就有各种各样的限制;如果是已经“注册”的用户,那么就可以正常的使用软件。到此就出现了两种情况,一是发现使用者是注册用户,就会显示“软件已经注册”等之类的信息;二是发现使用者是未注册用户,就会显示“软件试用期是××天”等这样的信息,并且会要求使用者输入注册名以及相应的注册码来注册软件。 如果是第一种情况,就没有接下来的过程了。如果是第二种情况,在用户输入了注册名及注册码后,程序还会对注册码的有效性进行检验(这就是以前所说的算法过程)。如果注册码合法就注册成功,用户就变成了注册用户,进而可以正常的使用软件。如果注册码不合法就注册失败,用户依然处于未注册状态。 软件的“注册检验流程”简单的来说就是如上所述的一个过程。大家可以很容易地看出,如果软件在运行时发现当前的使用者是一个已经注册的用户,那么后面诸如“提示软件试用期”、“要求输入注册码”就不会出现了。 回到我们所分析的软件中来,大家是不是发现刚才所解释的那一段代码,似乎正是这个作用呢?这段代码的作用就是检验当前的用户是否是注册用户,当然了,这段代码也正是在软件开始运行时就起作用了。这段代码就先分析到这里,我们来看看后面的代码。 else { string text3; string text4; string strValue; string text6; try { text3 = regCodeValue.Substring(1, 5); text4 = regCodeValue.Substring(6, 5); strValue = regCodeValue.Substring(12, 5); text6 = regCodeValue.Substring(0x12, 5); } catch (Exception exception1) { ProjectData.SetProjectError(exception1); Module1.blnRegistered = false; ProjectData.ClearProjectError(); return; ProjectData.ClearProjectError(); } if (Operators.CompareString(text6, GetRegisterCode(text3 + text4 + Module1.ainfo.Version.Substring(0, 4), strValue), false) != 0) { Module1.blnRegistered = false; } else { Module1.blnRegistered = true; } } } 开头的“else”是与上一段的“if”相对应的,我们把注意力放在最后几句的“if”代码上。这段代码的意思就更明显了,有一点编程基础的朋友已经很清楚它的意思了,不过还是给大家翻译一下吧。如果“括号”里面的条件成立,那么软件就注册失败,否则软件就注册成功。 我们可以看出这是一个很经典的注册验证过程,当检验完毕注册码的有效性之后,就根据检验的结果做出不同的反应,如果注册码有效就注册成功,如果注册码错误就注册失败。由于这种注册验证的方法十分容易被破解,所以在一般的软件中已经很难见到了,但由于这是一个.net程序,所以破解起来就会有一定的难度了。如果是一个普通软件,下一步该如何去做,大家肯定都知道了,只需要在OD里修改相应的跳转就可以了。这样一个有着明显的注册判断的程序,最简单的办法就是“暴破”。一般软件的暴破我们都已经很熟悉了,那.net程序又该如何去暴破呢? 修改篇 找到了关键的代码,我们如何去修改它呢?现在还不能直接用UltraEdit32,因为目前我们还不能在UltraEdit32中找到这一段关键的代码,继续注意Reflector中的内容。 还记得开始的时候,我们为了读懂Reflector反编译出来的代码,特意把“反编译选项”中的“IL”改成了“C#”,进而根据代码的提示找到了注册验证的关键部分,但是现在的任务是想办法在UltraEdit32定位这一段关键代码,如何做到这一点呢? 首先要把分析时调整的选项再次调整一下。具体来说就是在“反编译选项”中将“C#”再一次调整为“IL”,这样反编译的代码又发生变化了,就在这代码的海洋里寻找吧!这里就以注册验证的那一段代码为例吧。转换之后的代码如下所示。 L_00b8: stloc.s flag L_00ba: ldloc.s flag L_00bc: brfalse.s L_00c9 L_00be: ldc.i4.0 L_00bf: stsfld bool AdvancedSystemCleaner.Module1::blnRegistered L_00c4: nop L_00c5: br.s L_00d2 L_00c7: br.s L_00d0 L_00c9: nop L_00ca: ldc.i4.1 L_00cb: stsfld bool AdvancedSystemCleaner.Module1::blnRegistered 我们都知道,这里只是代码的表现形式发生了变化,代码的作用和含义是不变的。我们看看这两段截然不同的代码有什么样的联系!找来找去只发现有一个共同的标志“:blnRegistered”。继续仔细观察,在两段代码中“:blnRegistered”都出现了两次,从C#代码可以看出,第一次出现“:blnRegistered”对应的是注册失败的情况,因为“Module1.blnRegistered = false”意思就是注册为“假”;第二处则是“ Module1.blnRegistered = true”,应该就是注册成功了。这段代码经过转换之后,注册成功与否的标志又是什么呢?我们来观察这几句代码。 第一次出现“:blnRegistered”的情况是这样的: L_00be: ldc.i4.0 L_00bf: stsfld bool AdvancedSystemCleaner.Module1::blnRegistered 第二次则是这样的: L_00ca: ldc.i4.1 L_00cb: stsfld bool AdvancedSystemCleaner.Module1::blnRegistered 这两段代码基本相同,不同点就是第一段中“ldc.i4.0”出现的是“0”,而第二个则变成了“ldc.i4.1”。因为代码经过转换表现形式发生了变化,但意思是不会变的,所以我们就可以确定程序是通过不同的标志位赋值来区分注册成功与否的。标志位的值等于“0”则注册失败,等于“1”则注册成功。在这其中,“i4”就充当了一个标志位的作用(相当于一般的EAX、EBX等)。 小知识 什么是标志位?以前提到过这个概念,在暴力破解中还有一种方法叫做“标志位暴破法”。“标志位”可以这样来理解,做一个简单的比喻,大家都经历过无数的考试,60分以上就是及格,60分以下就是挂课。那么60就是挂课与否的一个标志。程序检验软件是否已经注册时会对标志位中的值进行检查,如果标志位中的值为0,那么就显示“软件未注册”;如果标志位中的值等于1,就提示“软件已注册”。好比我们看到了自己的考试成绩,一看是55分,完咯,低于60分,很不幸,挂课了! 在软件的代码中,标志位本身只是一个寄存器,可以是EAX也可以是EBX,还可以是ECX等。简单地说,常用的寄存器都可以被用作“标志位”,具体使用哪个就要看软件作者的选择了。标志位中最核心的内容就是标志位的赋值情况,具体来说就是选择一个什么样的值来作为“标志”。它的重要性就像是老师在选择60分以上算做及格还是50分以上算做及格一样,这个分数线直接关系到我们的命运。同样,标志位的赋值也直接关系到了最后的结果。一般来说,大多数软件都以1和0这两个数作为标志位的值,如果注册成功,标志位的值就自动为1,反之则为0。这里一定要注意,这个值不是一定的,也可以是注册成功时标志位的值等于0,这全看软件作者如何设计了。 现在我们的思路就出来了,只需要修改标志位的值欺骗程序,就可以达到破解软件的目的了。思路有了,下面就要确定修改的位置了,修改哪一段代码能够达到我们的目的呢?重新分析一下这个软件的注册流程,看看哪个部分可以作为“突破口”。 整理一下思路,软件的注册验证流程是这样的:输入注册码,注册码不正确,标志位就赋值为0,并提示注册失败;注册码正确,就给标志位赋值为1,进一步提示注册成功。因为软件在运行时就会对标志位进行检验,所以只要在这里修改标志的值,程序就会一直认为软件已经注册了。没错,就把程序运行时检测的那个“标志位”作为修改的目标。 我们回到Reflector中,找到第一处出现“:blnRegistered”的地方,如图10所示,代码如下。
图10 L_0032: ldc.i4.0 L_0033: stsfld bool AdvancedSystemCleaner.Module1::blnRegistered 很明显,此时标志位的值为0。如何使得程序认为软件已注册了呢?把“ldc.i4.0”修改成“ldc.i4.1”就可以了(改变标志位的值)。现在,修改的地方已经找到了,接下来就要看看如何修改标志位的值了。把鼠标放在“L_0032: ldc.i4.0”这句代码上停顿1秒钟,Reflector就会给我们弹出一个提示“0x0016”;同理,把鼠标放在“L_0033: stsfld bool AdvancedSystemCleaner.Module1::blnRegistered”上的时候,Reflector给出的提示是“0x0080”,也就是说,这两句指令的机器码是16和80。而我们是要让“ldc.i4.0”变成“dc.i4.1”,任意找一句“ldc.i4.1”,按同样的方法,Reflector给出的提示是“0x0017”。现在,我们的任务就更加明确了,就是要把“16”改成“17”,这样对应的“ldc.i4.0”就改为“ldc.i4.1”了。那么如何修改呢? 至此,Reflector的任务就光荣完成了,下面该UltraEdit32出场了。运行UltraEdit32,打开目标软件,会发现UltraEdit32给我们的也是一篇代码的海洋,这时该如何下手呢?还记得上面我们找到的两个代码指令“0x0016”和“0x0080”吗?现在它们可是我们的“指南针”哦。打开UltraEdit32的查找功能(单击“Search->Find”),在“Find what”栏中填写要寻找的内容。我们要寻找什么呢?没错,就是“0x0016”和“0x0080”,所以我们输入“16 80”,如图11所示。
图11 小提示 大家要注意查找时输入的格式。这里要寻找的代码是0x0016,在输入的时候只需要输入16就可以了。依此类推,查找0x0080只用输入80就可以了。虽然我们要找的是0x0016和0x0080,也只用输入16和 80就可以了。 输入完毕后,单击“Find Next”,UltraEdit32就可以帮我们寻找目标了。当找到满足代码为“16 80”的内容时,会自动停在目标代码上,如果当前找到的内容不是我们所需要的,继续单击“Find Next”即可。经过测试,第一次寻找到的代码并不是我们所要的,所以还要寻找,稍后第二处满足条件的代码也被找到了。这里就要提高警惕了,关键的代码很可能就在这里了。仔细观察这一行代码,会发现“16”这个数字出现了两次。不用管它,坚定我们的想法,把这里的两处“16”都改成“17”,如图12所示,保存修改后的文件。
图12 运行我们修改后的程序,把系统的日期调整到一年之后。在正常情况下,软件的试用期只有20天,一年之后肯定过期了。但是经过修改之后再看看,呵呵,软件依然可以使用,而且已经是注册版了。是不是很成功呢? 总结篇 与以往的软件破解相比,.net程序的破解可以说是翻天覆地的变化。从使用的工具到破解的思路,再到破解的方法,都是前所未见的。但是.net的流行只是时间的问题,所以我们必须要时刻面对新事物的挑战才能完善自我。当然,对于.net程序的破解,我也是在摸索中前进的,文章中难免有不准确的地方,还希望大家能够指出。
|
本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请
点击举报。