zoukankan      html  css  js  c++  java
  • 如何破解.net软件

    .net sdk中有不少很强大的工具,可以轻易完成对.net程序的破解,只要你懂得一点IL语言就行。现在以一个 M 软件为例,介绍整个破解过程。

    第零步:用反编译工具分析软件的可执行文件,制订破解逻辑。

    我以"M"来称呼这个软件。首先,要搞明白M的注册原理。M是通过输入注册码来完成注册的,为了破解它,要先搞明白它注册的原理,这就必须用反译工具来分析它。

    我用的反编译工具是.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对象,这个对象的定义如下:

    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-----
    MIIB...pQIDAQAB
    -----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对象了。

    破解的办法有下列几个:

    • 把 VerifyLicense 改掉,直接返回一个true。
      • 这个方法我其实用过了,但是发现了一个问题,就是软件一启动总是会出现一个nullException,原因应该是在启动时软件检查是否注册,然后改过的方法给它返回了个true,但它兴致勃勃地去检查注册的信息时,没料到是个空的。
      • 解决办法就是去修改c:users<用户名>AppDataLocal<产品名><文件名><版本号>user.config文件,把注册信息写进去,但是注册信息还是要符合解密要求才行。所以我不采用这个方式。
    • 写一个注册码生成器,用对应的私钥生成符合程序要求的注册码,这样不用改程序。
      • 问题在于,我们没有这个公钥对应的私钥,没办法做注册信息之后再加密。
      • 解决办法自己生成一个新的RSA密码对,然后把程序的公钥替换了,用对应的私钥写注册机。那么还是要改程序了?意义不大,算了不用这个办法。
    • 改掉Decrypt方法,把解密的过程跳过,让它支持明文注册信息,然后就随我的意输入了。就用这个办法了!

    方案确定,现在开始修改程序。

    第一步:用ildasm来反编译EXE或是DLL文件到IL源代码。

    ildasm是.net自带的反编译IL的工具。我用ildasm打开M.exe文件,看到里面的命名空间、类名什么的了,然后选择菜单里的“转储”,选择一个空的目录——注意,会生成一堆文件的,所以要用空的目录——,确认,IL文件与资源文件就保存到那里了。

    由于我们只要动一点点代码,其他的都不要动的,所以,只需要注意m.il文件就行了。

    第二步:修改il文件。

    用你喜欢的文本编辑器打开m.il文件,找到我们打算要修改的Decrypt方法,哇,这个在C#里只有几行方法,在IL里好长呀!

    再回来看看这个方法的c#版:

    private License Decrypt(string payload)
    {
        RSA rSA = CryptoKey.FromPublicKey(
            "-----BEGIN PUBLIC KEY-----
    MIIB...pQIDAQAB
    -----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-----
    MI...QAB
    -----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来重新编译exe文件。

    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……

    成功!您现在的所有使用限制已被解除。

  • 相关阅读:
    PHP+shell实现多线程的方法
    【JavaScript】一个同步于本地时间的动态时间
    void 0 或者 undefined
    Android Activity间动画跳转
    如何申请微信公众平台接口測试帐号
    hdu 4862 KM算法 最小K路径覆盖的模型
    【Unity 3D】学习笔记三十三:游戏元素——天空盒子
    cocos2d-x-lua基础系列教程五(lua单例)
    技术走向管理一些思考(7)-激励成员
    Runtime.getRuntime().exec()----记录日志案例
  • 原文地址:https://www.cnblogs.com/haoxiaobo/p/4157770.html
Copyright © 2011-2022 走看看