DbSchema专业版无限免费使用
本帖最后由 niwajiang 于 2025-6-24 11:48 编辑简介
DbSchema 是个还不错的数据库设计与管理工具,分为社区版本和专业版。这里对 专业版9.7.2 做个破解过程分析。
https://static.52pojie.cn/static/image/hrline/1.gif
步骤一
操作入口一般是从激活、试用等关键词开始,Java的实现模式大都是放在properties文件里。这里直接看它的内容是进行了编码处理,所以需要先对其进行解码。
它这里就是一个Base64的编码,解析结果properties文件的最终结果定位了License.properties、FxRegistrationDialog_zh_CN. properties等关键文件。
完整的解析过程如下所示:
这里以License.properties为例,原文件内容如下:
读取文件,并对其进行解析,处理结果如下:
1.1 Dictionary字典类
com.wisecoders.dbs.cli.command.Dictionary 是装载配置的字典类。
处理逻辑:
1. 静态代码块调用方法处理resource下的properties文件
2. 读取文件流
3. 在`Px.a`中处理文件流
public class Dictionary {
public static final List a = new ArrayList();
public static final List b = new ArrayList();
public Dictionary() {
}
private static void a(String var0, boolean var1) {
Properties var2 = new Properties();
//2.读取文件流
try (InputStream var3 = AbstractConsole.class.getResourceAsStream(var0)) {
if (var3 == null) {
throw new IOException("Missing commands.properties file");
}
//3.处理文件流
Px.a(var2, var3);
}
}
//1.静态代码块调用方法处理resource下的properties文件
static {
try {
a("connectivity.properties", true);
a("schema.properties", true);
a("data.properties", true);
a("scripting.properties", true);
a("system.properties", true);
a("sql.properties", false);
a("license.properties", false);
} catch (IOException var1) {
Log.a("Error opening commands.properties file. ", var1);
System.out.println("Error opening commands.properties file. " + var1.toString());
}
}
}
1.2 装载properties定义信息
com.wisecoders.dbs.sys.Px
1. 逐行读取properties
2. 使用`SimpleEncrypt.b`对行内容进行解码
3. 将解码内容装置到Properties里
public class Px {
public static void a(Properties var0, InputStream var1) {
if (var1 != null) {
try (InputStreamReader var2 = new InputStreamReader(var1, StandardCharsets.UTF_8)) {
StringBuilder var3 = new StringBuilder();
String var5;
try (BufferedReader var4 = new BufferedReader(var2)) {
//1.逐行读取properties
//2.对行内容进行解码
//3.将解码内容装置到Properties里
for(; (var5 = var4.readLine()) != null; var3.append(SimpleEncrypt.b(var5))) {
if (!var3.isEmpty()) {
var3.append("\n");
}
}
}
try (StringReader var13 = new StringReader(var3.toString())) {
var0.load(var13);
}
}
}
}
}
1.3 编解器
com.wisecoders.dbs.project.store.SimpleEncrypt
这是个自己实现的规则就是Base64编解码,核心方法如下
public class SimpleEncrypt {
public SimpleEncrypt() {
}
public static String b(String var0) {
if (var0 == null) {
return null;
} else if (var0.isEmpty()) {
return "";
} else {
int var1 = 0;
for (int var2 = var0.length() - 1; var0.charAt(var2) == '='; --var2) {
++var1;
}
int var8 = var0.length() * 6 / 8 - var1;
byte[] var3 = new byte;
int var4 = 0;
for (int var5 = 0; var5 < var0.length(); var5 += 4) {
int var6 = (a(var0.charAt(var5)) << 18) + (a(var0.charAt(var5 + 1)) << 12) + (a(var0.charAt(var5 + 2)) << 6) + a(var0.charAt(var5 + 3));
for (int var7 = 0; var7 < 3 && var4 + var7 < var3.length; ++var7) {
var3 = (byte) (var6 >> 8 * (2 - var7) & 255);
}
var4 += 3;
}
return new String(var3, StandardCharsets.UTF_8);
}
}
private static int a(char var0) {
if (var0 >= 'A' && var0 <= 'Z') {
return var0 - 65;
} else if (var0 >= 'a' && var0 <= 'z') {
return var0 - 97 + 26;
} else if (var0 >= '0' && var0 <= '9') {
return var0 - 48 + 52;
} else if (var0 == '+') {
return 62;
} else if (var0 == '/') {
return 63;
} else {
return var0 == '=' ? 0 : -1;
}
}
}
1.4 实现解析
public class ScriptTest {
public static void main(String[] args) {
String path = ScriptTest.class.getClassLoader().getResource("").getPath();//注意getResource("")里面是空字符串
String filePath = path + "License_zh_CN.properties";
// String filePath = path + "FxWelcome_zh_CN.properties";
try (InputStream inputStream = new FileInputStream(filePath)) {
InputStreamReader inputStreamReader = new InputStreamReader(inputStream, StandardCharsets.UTF_8);
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
String line;
while ((line = bufferedReader.readLine()) != null) {
// 方式一 调用SimpleEncrypt中实现的代码
// String b = b(line);
// System.out.println(b);
// 方式二 按照Base64进行测试
System.out.println(new String(Base64.getDecoder().decode(line)));
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
https://static.52pojie.cn/static/image/hrline/1.gif
步骤二
2.1 查找激活信息
文件 /com/wisecoders/dbs/dialogs/system/resources/FxRegistrationDialog_zh_CN.properties 中的内容是注册激活相关操作的提示信息。
完整内容如下所示:这个翻译挺二把刀的😄
activate.text=还行
cancel.text=取消
cancel.tooltip=取消
chooseSeatDialog.header.text=选择要使用的许可证席位
chooseSeatDialog.title.text=选择座位
contactSupport.text=技术支持
customerAreaURL.text=https\://cc.payproglobal.com/Customer/Account/Login
dialog.header.text=购买后,您将收到来自 PayProGlobal.com 或 2Checkout.com 的每封电子邮件的许可证密钥。\n许可证是多行 UTF-8 文本,以 '\#\#\#' 开头和结尾。\n从 dbschema.com 支持/客户帐户管理许可证。
dialog.title.text=注册对话框
failed.text=<html>注册失败。请复制/粘贴许可证密钥,包括第一行和最后一行注释。文本应采用 UTF-8 编码。<br>如果许可证比 DbSchema 发布日期早一年,则需要续订许可证。
fillFields.content.text=请输入您的姓名和许可证密钥
fillFields.title.text=错误信息
fillFlsFields.content.text=请输入您的姓名、服务器主机和端口
fillFlsFields.title.text=错误信息
flsEnable.text=使
hostField.promptText.text=浮动许可证服务器主机
licenseField.promptText.text=在此处复制许可证密钥多行文本。文本应采用 UTF-8 编码。
licenseKey.text=许可证密钥
licenseLabel.text=许可证密钥
licenseLabel.tooltip=购买后,您将收到来自 PayProGlobal.com 或 2Checkout.com 的每封电子邮件的许可证密钥。\n请在此处复制许可证密钥文本,包括 \#\#\# 行。
loadFromFile.text=从文件加载
manageLicenses.text=客户账户
manyComputers.text=许可证密钥已在 2 台以上的计算机上使用。请在另一台机器上执行 Help / Unregister 操作。
portField.promptText.text=浮动许可证服务器端口
purchase.text=购买许可证
purchase.tooltip=购买许可证或续订
regSucceed.text=注册成功。
register.text=还行
register.tooltip=还行
renew.text=<html><h2>续订您的软件升级和支持</h2>所有许可证包括 1 年免费升级到新发布的版本。<br>1 年后,更新到最新版本需要续订许可证,费用约为 6 美元/月(商业)或 4 美元(个人)。<br><br>从网站 Support / Customer Account 管理许可证。<br>目前,将激活 DbSchema Free。
saveFailed.text=请检查系统权限。DbSchema 无法在当前 OS 用户下保存许可数据。
serverHostLabel.text=主机
serverHostLabel.tooltip=浮动许可证服务器的主机名或 IP。\n许可证服务器是使用命令“license server”启动的 DbSchema CLI。\n有关详细信息,请使用 'license server -h' 。
serverPortLabel.text=港口
serverPortLabel.tooltip=服务器端口,默认为 8080。
serverTab.text=浮动许可证服务器
serverTabHeader.text=使用“license server”命令从 DbSchemaCLI 启动浮动许可证服务器。
trialActive.text=PRO 试用版处于活动状态。
userNameField.promptText.text=输入您的姓名。
userNameLabel.text=用户名
userNameLabel.tooltip=您的姓名,用于将您标识为此许可证密钥的用户
validationFailed.text=无法验证许可证。
2.2 定位激活入口类
根据activate.text=还行 这个属性配置,在反编译的内容进行搜索定位到相关class文件
com.wisecoders.dbs.dialogs.system.FxRegistrationDialog
com.wisecoders.dbs.sys.License
com.wisecoders.dbs.sys.Features
简单说License 就是凭证信息的实体模型,Features是不同的功能特性,FxRegistrationDialog是进行注册展示的提示框。
2.3 操作处理
主要分析工作结束,下面是操作了。
操作方式上采取了修改替换class和Java agent两种方式。先看下结果吧:
还有一些小的瑕疵,还是有续订的提示、还有烦人的升级提示,那都是小菜了。
2.3.1 修改Features的class文件
简单粗暴,所有都是全功能😄
2.3.2 使用Java agent进行相关拦截
从其反编译内容来看,FxRegistrationDialog属于 Kotlin 编写的类,所以刚开始还真有点不知道如何下手。一步步的走吧:
1.激活按钮触发的方法
这个“还行”按钮😄触发的方法已经知道了,从执行逻辑来看使用的激活方式是许可证密钥方式,也就是会调用this.b这个方法。
代码如下:
@Action(
b = "flagHasText"
)
@Nullable
public final Task activate() {
if (this.j.isSelected()) {
return this.a();
} else {
this.i.getSelectionModel().select(0);
Keys.g.c();
Keys.h.c();
Keys.i.c();
return this.b();
}
}
2.在this.b 这个方法里, 又是调用 License.b(),又是各种验证 还挺麻烦。
都可以不管 ,就看这10行就够了,首先会调用License的a()方法,然后调用License的g()方法得到一个Features实例,然后进行switch判断是否有效。因此只要这个方法返回一个有效的Features就行了。
3. 实现Java agent 拦截返回有效的Features
public class DBSchemaAgent {
public static void premain(String args, Instrumentation inst) {
inst.addTransformer(new ClassFileTransformer() {
@Override
public byte[] transform(ClassLoader loader, String className,
Class<?> classBeingRedefined,
ProtectionDomain protectionDomain,
byte[] classfileBuffer) throws IllegalClassFormatException {
try {
if (className.equals("com/wisecoders/dbs/sys/License")) {
ClassPool pool = ClassPool.getDefault();
CtClass cc = pool.makeClass(new ByteArrayInputStream(classfileBuffer));
// 找到目标方法
CtMethod[] declaredMethods = cc.getDeclaredMethods();
for (CtMethod method : declaredMethods) {
if ("a".equals(method.getName()) && method.getReturnType().getName().equals("boolean")) {
method.setBody("{ return true; }");
}
if ("g".equals(method.getName())){
method.setBody("{ return com.wisecoders.dbs.sys.Features.a; }");
}
}
return cc.toBytecode();
}
} catch (Exception e) {
e.printStackTrace();
}
return classfileBuffer;
}
});
}
}
到此就结束了,打包配置下Java agent就可以愉快的使用了。
https://static.52pojie.cn/static/image/hrline/1.gif
没想到还有人分析这个软件,我也分享一下
多年前研究了一下,在v8以上版本,验证机制就有变化,看了一下最新的是9.7.5,但是逻辑还是可用
对于v9,核心验证逻辑位于 com.wisecoders.dbs.sys.License.class, 其中,对于license的签名校验逻辑
private static boolean a(byte[] paramArrayOfbyte1, byte[] paramArrayOfbyte2, String paramString) {
X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(Base64.decodeBase64(paramString));
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
Signature signature = Signature.getInstance("SHA256withRSA");
signature.initVerify(publicKey);
signature.update(paramArrayOfbyte1);
return signature.verify(paramArrayOfbyte2);
}
所以对于v8.x以上版本来说,核心思想就是替换publickey,然后生成符合要求的license即可
对于v9,多了一个额外的离线验证机制,不过这个url服务器没做什么校验,直接访问就可以拿到所谓的Response Code,填进去就行 respect
膜拜一下 看不太懂。 厉害,支持一下 我也想学,可是好像看不懂:(eew pedoc 发表于 2025-6-17 09:16
没想到还有人分析这个软件,我也分享一下
多年前研究了一下,在v8以上版本,验证机制就有变化,看了一下最新的 ...
这个方式也不错 楼主可以提供下 jar 包吗? 这玩意真是悟性不够呀!佩服! pedoc 发表于 2025-6-17 09:16
没想到还有人分析这个软件,我也分享一下
多年前研究了一下,在v8以上版本,验证机制就有变化,看了一下最新的 ...
大佬能提供一个可用的jar么 :lol
页:
[1]
2