深入理解.NET程序的原理 谈一谈破解.NET软件的工具和方法
2013-06-29 22:35
721 查看
深入理解.NET程序的原理谈一谈破解.NET软件的工具和方法
最近一段时间不忙,闲下来的空闲时间,重读了一下CLR的原理,回味一下有关程序集的的知识,顺便练了一下手,学习致用,破解了若干个.NET平台的软件。以此来反观.NET程序开发中,需要注意的一些问题。基本原理
.NET平台的编译格式是依靠MSIL中间语言,运行时即时编译(JIT)成CPU指令,对Win32的PE格式进行了扩展。程序集是自描述的,本身蕴藏了丰富的元数据信息。MSDN中有一段代码例子,请参考下面的程序usingSystem; usingSystem.Reflection; publicclassExample { publicstaticvoidMain() { //Getmethodbodyinformation. MethodInfomi=typeof(Example).GetMethod("MethodBodyExample"); MethodBodymb=mi.GetMethodBody(); Console.WriteLine("\r\nMethod:{0}",mi); //Displaythegeneralinformationincludedinthe //MethodBodyobject. Console.WriteLine("Localvariablesareinitialized:{0}", mb.InitLocals); Console.WriteLine("Maximumnumberofitemsontheoperandstack:{0}", mb.MaxStackSize); //Displayinformationaboutthelocalvariablesinthe //methodbody. Console.WriteLine(); foreach(LocalVariableInfolviinmb.LocalVariables) { Console.WriteLine("Localvariable:{0}",lvi); } //Displayexceptionhandlingclauses. Console.WriteLine(); foreach(ExceptionHandlingClauseehcinmb.ExceptionHandlingClauses) { Console.WriteLine(ehc.Flags.ToString()); //TheFilterOffsetpropertyismeaningfulonlyforFilter //clauses.TheCatchTypepropertyisnotmeaningfulfor //FilterorFinallyclauses. switch(ehc.Flags) { caseExceptionHandlingClauseOptions.Filter: Console.WriteLine("FilterOffset:{0}", ehc.FilterOffset); break; caseExceptionHandlingClauseOptions.Finally: break; default: Console.WriteLine("Typeofexception:{0}", ehc.CatchType); break; } Console.WriteLine("HandlerLength:{0}",ehc.HandlerLength); Console.WriteLine("HandlerOffset:{0}",ehc.HandlerOffset); Console.WriteLine("TryBlockLength:{0}",ehc.TryLength); Console.WriteLine("TryBlockOffset:{0}",ehc.TryOffset); } } //TheMainmethodcontainscodetoanalyzethismethod,using //thepropertiesandmethodsoftheMethodBodyclass. publicvoidMethodBodyExample(objectarg) { //Definesomelocalvariables.Inadditiontothesevariables, //thelocalvariablelistincludesthevariablesscopedto //thecatchclauses. intvar1=42; stringvar2="Forty-two"; try { //Dependingontheinputvalue,throwanArgumentExceptionor //anArgumentNullExceptiontotesttheCatchclauses. if(arg==null) { thrownewArgumentNullException("Theargumentcannotbenull."); } if(arg.GetType()==typeof(string)) { thrownewArgumentException("Theargumentcannotbeastring."); } } //ThereisnoFilterclauseinthiscodeexample.SeetheVisual //BasiccodeforanexampleofaFilterclause. //ThiscatchclausehandlestheArgumentExceptionclass,and //anyotherclassderivedfromException. catch(Exceptionex) { Console.WriteLine("Ordinaryexception-handlingclausecaught:{0}", ex.GetType()); } finally { var1=3033; var2="Anotherstring."; } } } //Thiscodeexampleproducesoutputsimilartothefollowing: // //Method:VoidMethodBodyExample(System.Object) //Localvariablesareinitialized:True //Maximumnumberofitemsontheoperandstack:2 // //Localvariable:System.Int32(0) //Localvariable:System.String(1) //Localvariable:System.Exception(2) //Localvariable:System.Boolean(3) // //Clause //Typeofexception:System.Exception //HandlerLength:21 //HandlerOffset:70 //TryBlockLength:61 //TryBlockOffset:9 //Finally //HandlerLength:14 //HandlerOffset:94 //TryBlockLength:85 //TryBlockOffset:9
重头戏在这一行:MethodBodymb=mi.GetMethodBody();它返回方法的元数据的字节流。说通俗一点,它返回的是方法的源代码。把这个返回的字节流转换成MSIL指令,不是一件难事。MSDN中有描述,CodeProject上面有一篇文章,讲解如何写一个解释方法,把MethodBody传化为方法的MSIL代码,几乎就是一个反编译器的模型。
再去理解那句话:.NET程序集是自描述的,是不是理解更深刻了一些。
基本方法
熟悉MSIL语言。对照文档手册,边读边看边学。我的办法是,想知道高级语言编译时如何翻译成MSIL的,找一本高级语言(C#,VB.NET)的入门手册书,把代码敲进去,编译成程序集,再用反编译器.NETReflector一行一行对比看,进步神速。之所以要找入门书,是因为它会讲解到高级语言的各个特性,反编译时看到的MSIL代码会更全面。两个软件破解的实战经验
软件A软件A采用的保护措施是根据用户购买的许可数量,分成个人版,专业版,企业版。版本越高,能拥有的功能更高级。
比如个人版只能一天只能下载50个文档,专业版一天可以下载1000份文档,企业版则不受限制。
方法:跟踪软件启动时,加载的程序集和执行的动作。.NET程序一般有两个地方放程序文件,一是GAC,另一个是当前目录,或是当前目录的子目录(需要在配置文件中指定)。找到GAC中的文件,先把它拷贝到普通文件夹。
再拿.NETReflector打开看看,如果能打开,看是否有strongname,如打不开,则用ILDASM反编它,看生成的IL文件中,是否有strongname的值。再开一下软件,看看哪些地方会显示注册/未注册,试用期等信息。一般有几个地方会暴露软件的保护方法:
1直接在主窗体的标题栏中显示,“软件已注册”,”软件未注册,还有29天试用“
2在关于对话框中显示软件是否注册,剩余许可天数或次数。
再从IL代码中追查,看它在哪些地方,保存当前的使用天数的数据。以我的追查经验,多半是保存在注册表中。于是开一个注册表写入监控程序,一下就知道它写到什么去了,再来解码就容易很多。
软件B
我的软件注册方式也是这样做的,所以我对这种方式非常熟悉。是运用Xml签名文件,生成一个只读的许可文件,看起来是文本文件,但是你不能修改,一有任何的修改,重新计算Hash值会,会验证失败。这种方式,我的QQ群中的朋友都知道,用下面的代码,重新生成一套密钥匙对,替换程序集中的公匙,再用私匙生成一个注册文件让它验证。
[TestMethod]
publicvoidSolutionValidationTest()
{
stringpublickey=RSACryptionHelper.GeneratePublickKey(false);
stringprivateKey=RSACryptionHelper.GeneratePublickKey(true);
}
基本思路与对策
1替换策略当程序集中有写死一些基本信息,比如strongname的publictoken,xmlsignature的publickey,这时,只有替换程序集中的这些元数据,才能破解成功。因为这些信息是全球唯一的,就像GUID字符串的值一样,全世界再没有任何一台电脑能生成和他一样的数据(publictoken,public/privatekey),应用替换方法。2重签名策略。strongname一般都会配合代码进行检查,让它不会轻易被破解。遇到这种情况,可以考虑移除现在的签名,用本机重新生成的key给它签名。如果能保证程序集和它引用的程序都是一样的签名,则匹配成功。到目前为止,还没有看到一套程序,会有几个不同的strongname同时存在于不同的程序集中。
3rouding-trip策略。根据程序,生成MSIL,修改MSIL,再生成程序集。这里涉及到修改MSIL代码指令,可以让程序直接绕过验证,或是不验证,这种方式威慑力最大。根本不用考虑验证这一回事,直接跳过,把验证方法方法体全部删除,第一句IL代码为nop,或是ret,直接返回。
4代码与反编译结合策略。有时候面对程序集中五花八门的字符,完全不理解它的含义,无从下手。
遇到这种情况,它是应用了字符串加密技术。以其人之道,还其人之身,下面的几行简单的方法,破解文中的不可理解的字符:
Assemblyassembly=Assembly.GetExecutingAssembly();
Typetype=assembly.GetType("Class64");
MethodInfomi=type.GetMethod("smethod_0");
mi.Invoke(null,newobject[]{"ᓏᓒᓕᓎᒹᓊᓝᓑ"});
再把这个方法做成一个GUI程序,依此对照文中乱码字符,全部解码。
5直接编辑策略。程序集文件也是一个文件,编译器以生成目标格式的方式生成这个文件,而我们用到的文本,则是手工敲入字符生成,你可以用十六进制文件去编辑它的值,依照规律即可。.NET反编译时,经常遇到的一个错误是的
ThisassemblydoesnotcontainaCLIheader,ThisassemblyisnotaPE.NETformat。借助于PE格式知识,把添加进去的错误的元数据删除即可。这里有一个典型的例子,VisualStudio本身是不可以生成多Module的程序集,一个程序集只能有一个Module,但是加密程序通常会给它加上多余的Module,对照PE.NET格式标准,删除多余的节即可。
6利用Mono.Ceil.dll主动修改程序集策略。第四个策略中我提到字符串有加密,反其道行之,我把所有调用该方法的地方,再运算一次,重新生成一次,即可破解字符串,再把重新生成的代码写成一个程序集文件。
7应用密码学算法策略。如果应用对称加密,试着给一些随机的错误的序列号给它,看看它是如何验证序列号的,再将此方向反向,导出如何生成它可以验证的字符中。举例说明,有一个软件,它的序列号是这样的
1234567890=》1234567890=>185286120144
序列号1234567890,它把这个序列号以两个为一组,前后相邻的2个,转化为10进制数,再运算一次DES对称算法解密,看是是符合要求的密码。破解它的方法,就是学会如何生成一个字符串,让它通过验证,也就是理解这个流程。
也有应用MD5加密算法的软件。这样,整个软件只有一个序列号可用。把给你的序列号,验证时生成MD5哈希值,与它保存在当前程序中的密码匹配,验证错误则失败,否则通过。这种方式的好处是,你完全没有办法应用密码学知识去破解它,要么用前面提到的,把验证方法改成nop直接返回,别无它法。
8跟踪策略。.NET时代是开创绿色软件时代,一个.NETRuntime,所有程序共用,再调用这个公共类库。但是,我发现几乎所有的加密方法中,都会涉及把注册信息或是机密信息,写到注册表中去。写到注册表中去的键或值,肯定不会是明文,至少也要用个ToBase64把它变成一堆乱码。键值不可读,一般要还原到验证,你要知道自己在哪个地方写入了UserName,哪个地方写入LicenseKey,一般会用可逆的算法,就像第六条中所说的。也有软件应用可不可逆的算法,比如直接用MD5加密,这时,生成键值UserName或LicenseKey的变量,肯定是不变的,否则,无法再次生成键值,去对比验证。找一个合适的注册表跟踪软件,如RegMonitor为你的破解之路添加一线希望。
与此相对应的,ProcessExplorer,DependencyWalker也都应当应用到实际中,以发现珠丝马迹。
要做Web方面的破解,Findder,HttpWatch可以很好的帮忙你分析服务器与IE客户端之间,有哪些数据交互来往。
有的追踪是死路(deadend),比如你想知道在网上买东西,网银付款时,在自己的打开的IE中,输入的钱数,是如何提交到银行,被银行扣走的。但是大部分程序或是网站,没有做到这么高的安全级别,可以考虑尝试。
相关文章推荐
- 深入理解.NET程序的原理 谈一谈破解.NET软件的工具和方法
- 深入理解.NET程序的原理 谈一谈破解.NET软件的工具和方法
- .NET逆向三(深入.NET程序运行原理) by:凉游浅笔深画眉 / Net7Cracker
- 软件源码破解工具De4Dot用法,net破解、反混淆
- .NET应用程序调试―原理、工具、方法
- 软件应用加壳程序防止破解工具Themida免费下载地址
- .NET应用程序调试—原理、工具、方法
- 深入理解软件构建系统原理与最佳实践阅读笔记
- .net程序出现没有对Temporary ASP.NET Files”的写访问权限解决方法
- .NET程序运行原理 (转自http://book.csdn.net)
- [转载]Eziriz .NET Reactor 4.7.0.0 官方原版+破解补丁(强大的代码保护和软件防盗版工具)
- 深入理解软件测试应用(测试用例+测试应用+测试技术及工具+测试等级)
- 不安装.net framework框架运行.Net 程序的方法<收藏>
- RDIFramework.NET ━ .NET快速信息化系统开发框架-第3章 软件安装、配置、运行方法
- C#基础--.net平台的重要组成部分以及.net程序简单的编译原理
- 【软件开发知识积累】深入理解HTTP 原理基础与变迁
- 深入理解iputils网络工具-第5篇 arping:地址解析程序
- .NET应用程序调试—原理、工具、方法
- [原创]推荐一款强大的.NET程序内存分析工具.NET Memory Profiler
- .NET/ASP.NET Routing路由(深入解析路由系统架构原理)