niwajiang 发表于 2025-5-11 08:36

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 中的内容是注册激活相关操作的提示信息。
完整内容如下所示:这个翻译挺二把刀的&#128516;


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文件

简单粗暴,所有都是全功能&#128516;


2.3.2 使用Java agent进行相关拦截

从其反编译内容来看,FxRegistrationDialog属于 Kotlin 编写的类,所以刚开始还真有点不知道如何下手。一步步的走吧:




1.激活按钮触发的方法
这个“还行”按钮&#128516;触发的方法已经知道了,从执行逻辑来看使用的激活方式是许可证密钥方式,也就是会调用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

pedoc 发表于 2025-6-17 09:16

没想到还有人分析这个软件,我也分享一下
多年前研究了一下,在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,填进去就行

xueyinglantian 发表于 2025-5-11 12:07

respect
膜拜一下

gy001715 发表于 2025-5-13 07:05

看不太懂。

liyaohui0000 发表于 2025-5-13 18:58

厉害,支持一下

pojie小萌新 发表于 2025-5-14 05:22

我也想学,可是好像看不懂:(eew

niwajiang 发表于 2025-6-23 08:43

pedoc 发表于 2025-6-17 09:16
没想到还有人分析这个软件,我也分享一下
多年前研究了一下,在v8以上版本,验证机制就有变化,看了一下最新的 ...

这个方式也不错

幽林梦 发表于 2025-7-2 00:02

楼主可以提供下 jar 包吗?

z94213715 发表于 2025-7-17 15:44

这玩意真是悟性不够呀!佩服!

AAA3GO 发表于 2026-1-16 17:32

pedoc 发表于 2025-6-17 09:16
没想到还有人分析这个软件,我也分享一下
多年前研究了一下,在v8以上版本,验证机制就有变化,看了一下最新的 ...

大佬能提供一个可用的jar么 :lol
页: [1] 2
查看完整版本: DbSchema专业版无限免费使用