好友
阅读权限10
听众
最后登录1970-1-1
|
本帖最后由 gzzchh 于 2020-2-16 23:50 编辑
最近为了准备把开发环境搬到Linux上去. 对一些开发工具进行了挑选. 我发现DBeaver-EE效果不错. 于是尝试逆向分析之.本文原文在B站的链接是 https://www.bilibili.com/read/cv4538717
0x01 搜索
在 plugins 文件夹找一些文件名比较奇特的JAR文件,丢进反编译器看结果.
一般来说,包名为org.apache org.eclipse 之类的一看就跟程序本身无关的第三方库就可以跳过去了.
最后发现所有带有关键字 lm 的包都与许可有关(LicenseManager)
0x02 反编译
我用的是Bytecode-Viewer,可以开两种(最多三种)不同的反编译器相互比较.
如果要跟踪上下关系就得用IDEA进行反编译.
Bytecode-Viewer中我们看到了导入许可相关的类
导入
跟踪之~
获取解密密钥
若要解密则必须获取解密Key
从JAR获取
密钥源自JAR中
读加密字符串
要读出来
自定义读取方法
用自己的方法读
此时完成密钥读取.回到之前的位置,开始解密
解密方法如下
解密
这就是标准的RSA解密.
解密方法:读取导入的字符串->Base64解密->用JAR内置Key进行RSA解密(方法RSA/ECB/PKCS1Padding)
解密完成后,得到的字节数组便是密钥.
其格式如下,先贴出读取代码
public LMLicense(@NotNull byte[] encryptedData, @NotNull Key key) throws LMException {
this.encoded = encryptedData;
byte[] data = LMEncryption.decrypt((byte[])encryptedData, (Key)key);
int offset = 0;
this.licenseFormat = LMLicenseFormat.STANDARD;
try {
this.licenseFormat = LMLicenseFormat.valueOf((byte)data[offset]);
}
catch (Exception exception) {
log.warning("Unsupported license format: " + data[offset]);
}
++offset;
if (data.length != this.licenseFormat.getEncryptedLength()) {
throw new LMException("Bad " + (Object)this.licenseFormat + " license length (" + data.length + ")");
}
this.licenseId = new String(data, offset, 16).trim();
this.licenseType = LMLicenseType.valueOf((byte)data[offset += 16]);
this.licenseIssueTime = LMUtils.getDateFromBytes((byte[])data, (int)(++offset));
this.licenseStartTime = LMUtils.getDateFromBytes((byte[])data, (int)(offset += 8));
this.licenseEndTime = LMUtils.getDateFromBytes((byte[])data, (int)(offset += 8));
this.flags = LMUtils.bytesToLong((byte[])data, (int)(offset += 8));
this.productId = new String(data, offset += 8, 16).trim();
this.productVersion = new String(data, offset += 16, 8).trim();
this.ownerId = new String(data, offset += 8, 16).trim();
this.ownerCompany = new String(data, offset += 16, 64).trim();
offset += 64;
if (this.licenseFormat == LMLicenseFormat.STANDARD) {
this.ownerName = new String(data, offset, 64).trim();
offset += 64;
return;
}
this.ownerName = new String(data, offset, 32).trim();
this.ownerEmail = new String(data, offset += 32, 48).trim();
offset += 48;
this.yearsNumber = data[offset++];
if (this.yearsNumber <= 0) {
this.yearsNumber = 1;
}
this.reserved1 = data[offset++];
this.usersNumber = LMUtils.bytesToShort((byte[])data, (int)offset);
if (this.usersNumber <= 0) {
this.usersNumber = 1;
}
offset += 2;
}
原文中使用FernFlower反编译,我写这篇文章的时候用的是CFR(懒得开IDEA) 所以有一些不一样
我整理了一下这段字节数组中,哪些部分是什么意思,如下
格式分析
偏移量 |
项目 |
类型 |
长度 |
注解 |
0 |
LMLicenseFormat |
枚举 |
1 |
见下文 |
1 |
licenseId |
String |
16 |
17 |
licenseType |
LMLicenseType |
1 |
18 |
licenseIssueTime |
Date |
8 |
26 |
licenseStartTime |
Date |
8 |
34 |
licenseEndTime |
Date |
8 |
42 |
flags |
Long |
8 |
50 |
productId |
String |
16 |
66 |
productVersion |
String |
8 |
74 |
ownerId |
String |
16 |
90 |
ownerCompany |
String |
64 |
若许可类型是STANDARD
偏移量 |
项目 |
类型 |
长度 |
注解 |
154 |
ownerName |
String |
64 |
否则
偏移量 |
项目 |
类型 |
长度 |
注解 |
154 |
ownerName |
String |
32 |
186 |
ownerEmail |
String |
48 |
234 |
yearsNumber |
Byte(直接取数组) |
1 |
见下文 |
235 |
reserved1 |
Byte(直接取数组) |
1 |
保留位,未知 |
236 |
usersNumber |
Short |
1 |
见下文 |
最后偏移量再+2
值 |
许可类型 |
说明 |
加密长度 |
0 |
STANDARD |
初始许可格式 |
238 |
1 |
EXTENDED |
带有所有者邮箱的扩展格式 |
218 |
yearsNumber
看上去貌似是直接取了所在下标的值.
若小于等于0,则设定该变量为1
usersNumber
同yearsNumber 但需要进行转换,从Byte 转换到Short
判定规则相同
以上内容来自我自己的笔记程序
简单来说就是把字节数组一部分一部分读出来并进行类型转换.获取许可信息
关于注册原理分析就到此结束了. DBeaver-EE可以说是一款很良心的数据库管理软件了.
如果有dalao想要白嫖EE版,想办法换了JAR的key然后照着上面的原理写一份Keygen就可以了.
至于数组转换 这些方法位于 org.jkiss.lm.LMUtils 包内 文件名是 org.jkiss.lm_xxxxx.jar
最后放上研究手记和一些代码 : https://github.com/gzzchh/DBE-Agent
疫情在家好无聊QAQ~ |
免费评分
-
查看全部评分
|
发帖前要善用【论坛搜索】功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。 |
|
|
|
|