.net sdk中有很多很强大的工具,能够轻易完成对.net程序的破解,只要你懂得一点IL语言就行。如今以一个 M 软件为例,介绍整个破解过程。json
我以"M"来称呼这个软件。首先,要搞明白M的注册原理。M是经过输入注册码来完成注册的,为了破解它,要先搞明白它注册的原理,这就必须用反译工具来分析它。c#
我用的反编译工具是.net reflactor,能够用它打开.net的可执行文件,看到C#源代码。打开以后,通过一番寻找,发现的注册窗口的事件处理过程所在,而后一直找到了它的注册校验程序。以下:编辑器
MarkdownPad2.Licensing.LicenseEngine.VerifyLicense(String, String) : Boolean public bool VerifyLicense(string licenseKey, string email) { if (string.IsNullOrEmpty(licenseKey) || string.IsNullOrEmpty(email)) { return false; } try { this.License = this.Decrypt(licenseKey); this.LicenseProcessed = true; } catch (Exception exception3) { ... return false; } if (((this.License == null) || (this.License.Email == null)) || (this.License.Product == null)) { return false; } bool flag = this.License.Email.Equals(email, StringComparison.OrdinalIgnoreCase); bool flag2 = this.License.Product == "MarkdownPad2"; return (flag && flag2); }
在窗口中输入的email和key都被送到这里,其中Key经过Decrypt方法进行解密,直接生成了一个License对象,这个对象的定义以下:ide
namespace MarkdownPad2.Licensing { using System; using System.Runtime.CompilerServices; public class License { public DateTime CreationDate { get; set; } public string Email { get; set; } public int LicenseTypeId { get; set; } public string Name { get; set; } public string Product { get; set; } } }
看来就是注册信息的内存对象了。那么,再看看生成这个对象的那个Decrypt方法:工具
private License Decrypt(string payload) { RSA rSA = CryptoKey.FromPublicKey( "-----BEGIN PUBLIC KEY-----\nMIIB...pQIDAQAB\n-----END PUBLIC KEY-----", "2QJmLPD5ktxIrFkr") .GetRSA(); byte[] msg = Convert.FromBase64String(payload); byte[] bytes = rSA.PublicDecrypt(msg, RSA.Padding.PKCS1); string str = Encoding.Default.GetString(bytes); IRestResponse response = new RestResponse { Content = str }; License license = new JsonDeserializer().Deserialize<License>(response); rSA.Dispose(); return license; }
研究一下就能明白,这个程序就是经过一个指定公钥的rsa解密器,对传入的Base64字符进行解密,解密以后的内容是一个Json格式的文本符串,这个文本串直接反序列化就是License对象了。ui
破解的办法有下列几个:this
方案肯定,如今开始修改程序。加密
ildasm是.net自带的反编译IL的工具。我用ildasm打开M.exe文件,看到里面的命名空间、类名什么的了,而后选择菜单里的“转储”,选择一个空的目录——注意,会生成一堆文件的,因此要用空的目录——,确认,IL文件与资源文件就保存到那里了。spa
因为咱们只要动一点点代码,其余的都不要动的,因此,只须要注意m.il文件就好了。.net
用你喜欢的文本编辑器打开m.il文件,找到咱们打算要修改的Decrypt方法,哇,这个在C#里只有几行方法,在IL里好长呀!
再回来看看这个方法的c#版:
private License Decrypt(string payload) { RSA rSA = CryptoKey.FromPublicKey( "-----BEGIN PUBLIC KEY-----\nMIIB...pQIDAQAB\n-----END PUBLIC KEY-----", "2QJmLPD5ktxIrFkr") .GetRSA(); byte[] msg = Convert.FromBase64String(payload); byte[] bytes = rSA.PublicDecrypt(msg, RSA.Padding.PKCS1); string str = Encoding.Default.GetString(bytes); IRestResponse response = new RestResponse { Content = str }; License license = new JsonDeserializer().Deserialize<License>(response); rSA.Dispose(); return license; }
咱们要把它改成这样:
private License Decrypt(string payload) { IRestResponse response = new RestResponse { Content = payload }; JsonDeserializer deserializer = new JsonDeserializer(); return deserializer.Deserialize<License>(response); }
如今,硬起头皮,看看IL代码,发现并不太难,好比提及头这段:
.locals init ( class [ManagedOpenSsl]OpenSSL.Crypto.CryptoKey V_0, class [ManagedOpenSsl]OpenSSL.Crypto.RSA V_1, uint8[] V_2, uint8[] V_3, string V_4, class [RestSharp]RestSharp.IRestResponse V_5, class [RestSharp]RestSharp.Deserializers.JsonDeserializer V_6, class MarkdownPad2.Licensing.License V_7)
这就是局部变量的定义啦,它们按位置编号,从0开始。
而后对着c#看,就是几个ldXXX指令(ldstr:压栈字串常量;ldloc:压栈局部变量,ldarg:压栈本方法参数)准备参数,一个call调用方法,若有返回值,就用stloc保存到第N号变量里。
IL_0000: ldstr "-----BEGIN PUBLIC KEY-----\nMI...QAB\n-----END PUBLIC KEY-----" IL_0005: ldstr "2QJmLPD5ktxIrFkr" IL_000a: call class [ManagedOpenSsl]OpenSSL.Crypto.CryptoKey [ManagedOpenSsl]OpenSSL.Crypto.CryptoKey::FromPublicKey(string, string) IL_000f: stloc.0
看懂了吧?后面的也都同样。咱们一直找到new RestResponse的地方,
IL_0034: newobj instance void [RestSharp]RestSharp.RestResponse::.ctor() IL_0039: stloc.s V_5 //new 的返回值赋值给V_5局部变量。 IL_003b: ldloc.s V_5 //第0个参数是this指针。 IL_003d: ldloc.s V_4 //set方法的参数,如今取的是V_4,也就是str局部变量 IL_003f: callvirt instance void [RestSharp]RestSharp.IRestResponse::set_Content(string)
咱们只须要把 IL_003d: ldloc.s V_4
改成 IL_003d: ldarg.1
,就把payload参数直接给了response.Content了。
IL_0034: newobj instance void [RestSharp]RestSharp.RestResponse::.ctor() IL_0039: stloc.s V_5 IL_003b: ldloc.s V_5 IL_003d: ldarg.1 IL_003f: callvirt instance void [RestSharp]RestSharp.IRestResponse::set_Content(string)
前面的RSA解密代码若是不删除,它们会对将放进来的明文进行解密,会引发异常,咱们就把它们都注释了吧!也就是初始化局部变量以后到IL_0034以前的代码都所有删除。而最后还有一个RSA.dispose(),它对应的IL代码以下,也一并删除:
IL_0056: ldloc.1 IL_0057: callvirt instance void [ManagedOpenSsl]OpenSSL.Core.Base::Dispose()
还有,因为有4个局部变量没有被使用,因此也要在init里把它们删除,否则会形成程序运行时的堆栈出错。
最后检查程序里的变量引用序号是否是须要调整。最后的程序是这样:
.method private hidebysig instance class MarkdownPad2.Licensing.License Decrypt(string payload) cil managed { // 代码大小 95 (0x5f) .maxstack 3 .locals init ( class [RestSharp]RestSharp.IRestResponse V_5, class [RestSharp]RestSharp.Deserializers.JsonDeserializer V_6, class MarkdownPad2.Licensing.License V_7) IL_0034: newobj instance void [RestSharp]RestSharp.RestResponse::.ctor() IL_0039: stloc.s V_5 IL_003b: ldloc.s V_5 IL_003d: ldarg.1 IL_003f: callvirt instance void [RestSharp]RestSharp.IRestResponse::set_Content(string) IL_0044: newobj instance void [RestSharp]RestSharp.Deserializers.JsonDeserializer::.ctor() IL_0049: stloc.s V_6 IL_004b: ldloc.s V_6 IL_004d: ldloc.s V_5 IL_004f: callvirt instance !!0 [RestSharp]RestSharp.Deserializers.JsonDeserializer::Deserialize<class MarkdownPad2.Licensing.License>(class [RestSharp]RestSharp.IRestResponse) IL_0054: stloc.s V_7 IL_005c: ldloc.s V_7 IL_005e: ret } // end of method LicenseEngine::Decrypt
ilasm是一个.net sdk里的命令行工具,用来编译il文件。在命令行里执行下面的命令,M.exe就被从新生成了:
ilasm /exe /resource=M.res /output=M.exe M.il
注意,M.exe里的程序还有不少图片之类的资源,在反编译时,这些资源文件也会被抽出来放到这里,全部的资源都会在m.res文件里描述。在从新编译时,只须要如上面例子里的那样/resource=M.res就好了。
好,把破解过的M.exe放回到原来的目录里,替换原版的程序,而后执行之。
选择注册,在注册码框里输入json格式的明文:
{"CreationDate":"2014-12-11T06:06:54.0068849Z", "Email":"***@qq.com", "LicenseTypeId":1,"Name":"myname", "Product":"Msoftware"}
这个JSON数据是根据License对象的成员而编写的。如今咱们知道这个字串会被直接反序列化为一个妥妥的License对象。
一按OK……
成功!您如今的全部使用限制已被解除。