MybatisCodeHelperPro 3.3.5 注册流程逆向分析

查看 120|回复 12
作者:lvbuqing   
首先感谢大佬提供的图床工具,好用的一批,原帖地址:
https://www.52pojie.cn/thread-1965983-1-1.html
正文开始
插件介绍:


1.jpg (108.28 KB, 下载次数: 0)
下载附件
2024-10-12 17:23 上传

阅读完之后你会得到什么:


2.jpg (66.24 KB, 下载次数: 0)
下载附件
2024-10-12 17:23 上传

步骤1:
尝试假激活码,查看报错堆栈:


3.jpg (49.92 KB, 下载次数: 0)
下载附件
2024-10-12 17:23 上传

可疑的地方:


4.jpg (144.04 KB, 下载次数: 0)
下载附件
2024-10-12 17:23 上传

注意看有个doOKAction方法,从字面意思都可以猜出是执行验证OK的动作。
jdax 启动!!


5.jpg (87.77 KB, 下载次数: 0)
下载附件
2024-10-12 17:23 上传

相关代码:
if (this.f1772a.getSelectedComponent() == this.f1776d) {
            if (StringUtils.isNotBlank(this.f1769a.getText())) {
                C0392c c0392c = new C0392c();
                String trim = this.f1769a.getText().trim();
                c0392c.m2039a(trim);
                c0392c.m2042c(C0385a.f1268a);
                String m1880a = C0375s.m1880a();
                c0392c.m2041b(m1880a);
                if (C0626a.m2753a().getInvalidOffLineKey().contains(trim)) {
                    Messages.showErrorDialog(this.f1789a, "激活码不正经", "失败");
                    return;
                }
                try {
                    boolean m2020a = C0389c.m2020a(c0392c);
                    c0392c.m2041b(C0375s.m1881b());
                    boolean m2020a2 = C0389c.m2020a(c0392c);
                    c0392c.m2041b(this.f1767b.getText());
                    boolean m2020a3 = C0389c.m2020a(c0392c);
                    if (m2020a || m2020a2 || m2020a3) {
                        C0626a.m2753a().setValid(true);
                        C0626a.m2753a().setUseFreeVersion(false);
                        Messages.showInfoMessage(this.f1789a, "激活成功", "成功");
                        C0337Q.m1558a();
                        m2641a();
                    } else {
                        Messages.showErrorDialog(this.f1789a, "激活码错误, 激活码为" + trim + "\n 唯一码为:" + m1880a + " 请将信息复制发送给作者", "失败");
                    }
                    return;
                } catch (Exception e) {
                    Messages.showErrorDialog(this.f1789a, "激活码不正常" + ExceptionUtils.getStackTrace(e), "失败");
                    return;
                }
            }
            return;
        }
需要到达 “激活成功” 需要经过的步骤:
1.如图 206 行,得填写激活码
  • 214行 激活码不正经,激活码格式要正确
  • 需要满足其中任意条件,m2020a ,m2020a2,m2020a3
    [/ol]
    boolean m2020a = C0389c.m2020a(c0392c);
    c0392c.m2041b(C0375s.m1881b());
    boolean m2020a2 = C0389c.m2020a(c0392c);
    c0392c.m2041b(this.f1767b.getText());
    boolean m2020a3 = C0389c.m2020a(c0392c);
    // if (m2020a || m2020a2 || m2020a3)
    这里校验都是走的 m2020a ,所以重点分析这个函数。

  • m2020a 方法函数:


    6.jpg (10.05 KB, 下载次数: 0)
    下载附件
    2024-10-12 17:23 上传

    没啥好看的,重点分析mo1993a

  • mo1993a 方法函数:


    7.jpg (75.55 KB, 下载次数: 0)
    下载附件
    2024-10-12 17:23 上传

    [/ol]
    public static C0400f m2156a(String str) {
            byte[] m2154b;
            try {
                m2154b = C0411d.m2154b(C0411d.m2148a(new String(Base64.getDecoder().decode("aaaaaaaaaaaa"), Charsets.UTF_8)), Base64.getDecoder().decode(str));
            } catch (C0409b e) {
                m2154b = C0411d.m2154b(new C0410c(), Base64.getDecoder().decode(str));
            }
            try {
                String str2 = new String(m2154b, "UTF-8");
                try {
                    return (C0400f) f1356a.fromJson(str2, C0400f.class);
                } catch (Exception e2) {
                    throw new RuntimeException("gson catch exception, the json string is" + str2, e2);
                }
            } catch (UnsupportedEncodingException e3) {
                throw new RuntimeException(e3);
            }
        }
    可以看到,取 c0392c 的 值都是通过  m2156a 去校验。而 m2156a
    来自: C0412e.m2156a(m2035a);
    这里需要分析 m2156a 这个方法
  • m2156a 方法函数:


    8.jpg (44.21 KB, 下载次数: 0)
    下载附件
    2024-10-12 17:23 上传

    [/ol]
    看返回出: return (C0400f) f1356a.fromJson(str2, C0400f.class);
    这里返回的是一个json序列化转为 C0400f 对象,而C0400f如图:


    9.jpg (20.27 KB, 下载次数: 0)
    下载附件
    2024-10-12 17:23 上传

    大概这个就是离线验证后的要校验的数据类了。
    回到m2156a 一看能够看出这是一个数据解密字符串的操作,而返回的数据是 m2154b ,
    好巧不巧,m2154b 都是通过 C0411d.m2154b 函数返回的
    正好 第 17 行  可以看出他加载的一个很长的字符串,盲猜就是解密数据相关的东西了。
  • m2154b 方法函数:


    10.jpg (43.24 KB, 下载次数: 0)
    下载附件
    2024-10-12 17:23 上传

    [/ol]
    可知,采用 RSA 的加密方式,图中所示为 公钥解密操作。
    由步骤五 可以逆推出 加密过程。
    还原加密代码流程:
    [ol]
  • 构建自己的RSA 公私钥
  • 由步骤五 可知,传入的 公钥需要经过base64 编码 。
  • 构建 需要加密的json串,大致结构为:{"paidKey":"","valid":true,"userMac":"","validTo":}
    其中,根据字面意思可知,paidKey为购买的激活码,valid验证的结果,userMac机器码,validTo到期时间
  • 私钥加密json串
    [/ol]
    完整代码:
    package org.example;
    import cn.hutool.core.codec.Base64;
    import cn.hutool.crypto.SecureUtil;
    import cn.hutool.crypto.asymmetric.KeyType;
    import cn.hutool.crypto.asymmetric.RSA;
    public class Main2 {
        public static void main(String[] args) {
            String json = "{\"paidKey\":\"[email protected]\",\"valid\":true,\"userMac\":\"实际的机器码\",\"validTo\":4092566400000}";
            RSA rsa = SecureUtil.rsa( "私钥", "公钥");
            String privateKeyBase64 = rsa.getPrivateKeyBase64();
            String publicKeyBase64 = rsa.getPublicKeyBase64();
            System.out.println("privateKeyBase64: " + privateKeyBase64);
            System.out.println("publicKeyBase64: " + publicKeyBase64);
            String encode1 = Base64.encode(publicKeyBase64);
            System.out.println("公钥的base64编码: " + encode1);
                            /**
                             * 私钥加密
                             */
            String encryptBase64 = rsa.encryptBase64(json, KeyType.PrivateKey);
            System.out.println("私钥加密: " + encryptBase64);
            /**
             * 公钥解密
             */
            String decryptStr = rsa.decryptStr(encryptBase64, KeyType.PublicKey);
            System.out.println("公钥解密: " + decryptStr);
        }
    }
    运行结果:


    11.jpg (41.93 KB, 下载次数: 0)
    下载附件
    2024-10-12 17:23 上传

    将 公钥的base64编码 放到程序包里面,将 私钥加密 的数据在程序内激活
    最重要:
    当然是替换插件包里面的 被加密的公钥啦,换成我们自己的。
    jclasslib bytecode viewer  启动!!
    了解classfile 文件结构的同学都知道,像这种写死的字符串,一般都在常量池初始化,如图


    12.jpg (36.31 KB, 下载次数: 0)
    下载附件
    2024-10-12 17:23 上传

    这里是引用类型,需要跳转到 索引第78号的位置,
    然后,就可以了执行如图所示操作:


    13.jpg (58.51 KB, 下载次数: 0)
    下载附件
    2024-10-12 17:23 上传

    当然,我这边已经修改好了,
    下载:
    [color=][backcolor=rgb(238">https://lvbuqing.lanzouw.com/iBe3X2ar9k3c
    密码:hgnb
    至于激活,我得需要知道你的机器码才能帮你激活
    ps:
    我用的公私钥:
    privateKeyBase64: MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAJ7XwlkJ1om75Abw55NiFfqwA+MR9UBhfKAr/EF/jdei219PMWc0AQDYfiCrjunIOdRXkTXXbbwMfH3pD4gv38nmDFrQ89rOD9aW964vzX05zMEbi35PRFQ+TS5wKvXeOGzn1CEvOwbiqVvzty1wRfydfXv1XyqVMWoAdXySL+MFAgMBAAECgYACVYNKkaVwYq1oGLQea0uNYna8KHBlIMmXBO5w+/HWoFL+5IgCTzZQj93SlxLDhqiq4RqGIwM+xyQxKXKL+sAcDmrFNt9vPMfa1mWLckoWquZEknj36MgC3apFCkfGUG+VGr0LOsubj0qSJ6M6YA9sXfk+zOV555dgoSUS8YNmwQJBAOAPY0RnuDFAHLSl0zL6E8fxwUnDsHrOSv5xqJQOTpBjvm1UDpqMRLTO9mFoQW6r1qa+0sXjTnPwzd7Q8HN7cPUCQQC1fGZ/WVTMXxz5WIsD3HY1QxTjtrBjgH9d3GYUQKYvSYF3iWKj4Og4c6peGkhwxm3mXbDbZ/mepuXlHGp37x/RAkA2AB9pliHTZONGOo0LRTBNSRvPnmVDQ8LZTiVWAZi3vgJgMRkP8GyCszq4QTs75Bhouabs4JrA4LGNWQgKnR6dAkBdLEa67rPYUKRhZxHHo7GUWqIo3ivkiZ3aJELL9vzanhQ3uHLJy7es88TtlvTF4Tme4U7g9Zpz1x+D5njKphthAkALsHi9VlzWO1mao3m1H5LvZUy+uu2tsuCOhnU7VsewG5ETd84btS5zpaU4ugLdO4Smeka+37AG/KHx3bVXMrwh
    publicKeyBase64: MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCe18JZCdaJu+QG8OeTYhX6sAPjEfVAYXygK/xBf43XottfTzFnNAEA2H4gq47pyDnUV5E11228DHx96Q+IL9/J5gxa0PPazg/WlveuL819OczBG4t+T0RUPk0ucCr13jhs59QhLzsG4qlb87ctcEX8nX179V8qlTFqAHV8ki/jBQIDAQAB
    完结,撒花!!{:301_987:}
    ==================================
    2024年9月26日19:50:50
    接着分析:
    有人反馈,生成代码后闪退,


    14.jpg (45.09 KB, 下载次数: 0)
    下载附件
    2024-10-12 17:23 上传

    具体问题原因为:


    15.jpg (46.35 KB, 下载次数: 0)
    下载附件
    2024-10-12 17:23 上传

    这里判断成功后会直接弹窗后关闭 IDEA  ,
    其实不难发现 是在取值 getIfUseNewMapping 后判断 是否大于100。然后后面的代码才会执行。
    我们只需要把修改getIfUseNewMapping 函数,让他的值返回小于100以内就可以。
    怎么做呢?
    使用工具:javassist
    具体代码:
        public static void main(String[] args) throws NotFoundException, CannotCompileException, IOException {
            ClassPool pool = ClassPool.getDefault();
            pool.insertClassPath("instrumented-MyBatisCodeHelper-Pro241-3.3.5+2321.jar");
            CtClass driverclass = pool.get("com.ccnode.codegenerator.myconfigurable.Profile");
            driverclass.getDeclaredMethod("getIfUseNewMapping").setBody("{return 1;}");
            driverclass.writeFile("javassistout");
        }
    然后替换 Profile.class 文件就可以啦!!
    效果图:


    16.jpg (22.56 KB, 下载次数: 0)
    下载附件
    2024-10-12 17:23 上传

    这样就OK啦!!
    https://lvbuqing.lanzouw.com/iizzI2ax8pmb
    密码:52pj
    2024年10月16日11:09:58
    MyBatisCodeHelper-Pro-3.3.6_2321-2023.2-2024.3obfuscated.zip
    [/color">https://lvbuqing.lanzouw.com/i1gXB2cnqpba

    密码:52pj

    下载次数, 下载附件

  • lvbuqing
    OP
      

    [i]
    目前有这个问题,晚点修复,
    # 已修复,请下载最新的
    sunlong0717   


    wyakuan 发表于 2024-9-28 03:21
    离线激活的话还有个方案,就是改解码的方法
    然后再修改Profile.java的getIfUseNewMapping方法

    我也是用这种方案处理的,很方便
    tokenian   

    我这里有个疑问,既然能够替换人家的class文件。那直接修改验证逻辑。把验证方法里多余的改成
                            C0626a.m2753a().setValid(true);
                            C0626a.m2753a().setUseFreeVersion(false);
                            Messages.showInfoMessage(this.f1789a, "激活成功", "成功");
                            C0337Q.m1558a();
                            m2641a();
    其他的直接删掉。常见的hook方法,不知道这样子行不?公私钥得自己构造,多麻烦。望楼主解惑
    孤狼微博   

    文件是俩apk正确吗
    water201   

    文件是2个apk包,时间还是2019,是不是上传错误了?
    孤狼微博   
    lhfcsm   

    呵呵,正在使用中
    XD001   

    试试看 也可以
    lvbuqing
    OP
      


    孤狼微博 发表于 2024-9-24 18:41
    文件是俩apk正确吗

    不好意思填错了链接,已经修改了
    您需要登录后才可以回帖 登录 | 立即注册

    返回顶部