首先感谢大佬提供的图床工具,好用的一批,原帖地址:
https://www.52pojie.cn/thread-1965983-1-1.html
正文开始
插件介绍:
阅读完之后你会得到什么:
步骤1:
尝试假激活码,查看报错堆栈:
可疑的地方:
注意看有个doOKAction方法,从字面意思都可以猜出是执行验证OK的动作。
jdax 启动!!
相关代码:
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
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 方法函数:
没啥好看的,重点分析mo1993a
-
mo1993a 方法函数:
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 方法函数:
看返回出: return (C0400f) f1356a.fromJson(str2, C0400f.class);
这里返回的是一个json序列化转为 C0400f 对象,而C0400f如图:
大概这个就是离线验证后的要校验的数据类了。
回到m2156a 一看能够看出这是一个数据解密字符串的操作,而返回的数据是 m2154b ,
好巧不巧,m2154b 都是通过 C0411d.m2154b 函数返回的
正好 第 17 行 可以看出他加载的一个很长的字符串,盲猜就是解密数据相关的东西了。
- m2154b 方法函数:
可知,采用 RSA 的加密方式,图中所示为 公钥解密操作。
由步骤五 可以逆推出 加密过程。
还原加密代码流程:
- 构建自己的RSA 公私钥
- 由步骤五 可知,传入的 公钥需要经过base64 编码 。
- 构建 需要加密的json串,大致结构为:{"paidKey":"","valid":true,"userMac":"","validTo":}
其中,根据字面意思可知,paidKey为购买的激活码,valid验证的结果,userMac机器码,validTo到期时间
- 私钥加密json串
完整代码:
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\":\"lvbuqing@52pojie.cn\",\"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);
}
}
运行结果:
将 公钥的base64编码 放到程序包里面,将 私钥加密 的数据在程序内激活
最重要:
当然是替换插件包里面的 被加密的公钥啦,换成我们自己的。
jclasslib bytecode viewer 启动!!
了解classfile 文件结构的同学都知道,像这种写死的字符串,一般都在常量池初始化,如图
这里是引用类型,需要跳转到 索引第78号的位置,
然后,就可以了执行如图所示操作:
当然,我这边已经修改好了,
下载:[backcolor=rgb(238">https://lvbuqing.lanzouw.com/iBe3X2ar9k3c[backcolor=rgb(238, 238, 238)] 密码: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
接着分析:
有人反馈,生成代码后闪退,
具体问题原因为:
这里判断成功后会直接弹窗后关闭 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 文件就可以啦!!
效果图:
这样就OK啦!!
密码:52pj