某数据库管理软件注册分析
本帖最后由 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中
要读出来
用自己的方法读
此时完成密钥读取.回到之前的位置,开始解密
解密方法如下
这就是标准的RSA解密.
解密方法:读取导入的字符串->Base64解密->用JAR内置Key进行RSA解密(方法RSA/ECB/PKCS1Padding)
解密完成后,得到的字节数组便是密钥.
其格式如下,先贴出读取代码
```java
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);
}
catch (Exception exception) {
log.warning("Unsupported license format: " + data);
}
++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);
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;
if (this.yearsNumber <= 0) {
this.yearsNumber = 1;
}
this.reserved1 = data;
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
## LicenseFormat
|值|许可类型|说明|加密长度|
|-|-|-|-|
|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~ a5228172 发表于 2020-3-2 09:11
""用JAR内置Key进行RSA解密(方法RSA/ECB/PKCS1Padding) ""这个用什么软件可以
自己写一段Java代码来实现 背景是美琴嘛?哈哈。
感谢楼主的分析。 于是尝试逆向分析之 感谢分享,谢谢楼主 膜拜大佬,学习学习 感谢楼主的分析。学习一下 感谢楼主分享,学习了。 厉害,大神啊
感谢楼主的分析。学习一下。
页:
[1]
2