吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 1380|回复: 15
收起左侧

[原创] DBeaver Ultimate Edition License验证分析

  [复制链接]
pengsx01 发表于 2022-7-29 18:55
本帖最后由 pengsx01 于 2022-8-2 17:48 编辑

前言
看了论坛大佬写的两篇帖子,
DBeaver Ultimate edition JAVA程序逆向分析
DBeaver Ultimate edition 算法分析/本地服务器搭建

研究了一波DBeaver的源码,在这里分享一下我的过程与方法,补充一种绕过网络验证的方法。


测试环境
操作系统:Windows10
Java版本:JDK11

软件版本:DBeaver Ultimate 22.1.0

License解密分析
下载安装后打开,弹窗提示如下:
图片1.png

点击ImportLicense,随便填写一串字符串,界面会出现以下报错信息:
图片2.png

查看运行日志,日志路径:C:\Users\Niclas\AppData\Roaming\DBeaverData\workspace6\.metadata\dbeaver-debug.log能看到报错信息:
图片3.png

以上能看出导入License调用的方法在:com.dbeaver.lm.embedded.LicenseServiceEmbedded.importProductLicense
根据此处线索去安装目录下找对应的jar包,查找到的jar为安装目录下:DBeaverUltimate\plugins\com.dbeaver.lm.core_2.0.112.202206121739.jar
反编译jar包之后能找到对应的方法源码如下:
图片4.png

分析方法内代码,发现调用了方法:org.jkiss.lm.LMLicenseManager#importLicense(org.jkiss.lm.LMProduct,byte[])
查找到对应的jar包为:DBeaverUltimate\plugins\org.jkiss.lm_1.0.136.202206121739.jar
导入项目中,反编译之后能看到如下代码:
图片5.png

分析此处获取解密Key的方法,可以看到如下源码:
图片6.png

这里可以看出密钥来自于某一个jar包根目录下的keys文件夹中,文件名是以-public.key结尾。
在安装目录中查找后,发现该文件在:plugins\com.dbeaver.app.ultimate_22.1.0.202206121739.jar
如图:
图片7.png

此处也能推出String id =product.getId();中获取的id是:dbeaver-ue
回到方法org.jkiss.lm.LMLicenseManager#importLicense(org.jkiss.lm.LMProduct,byte[])
继续分析 LMLicense license = new LMLicense(licenseData,decryptionKey);,
查找其解密方法,如图:[size=14.6667px]
图片8.png

可以发现其加解密方式为:RSA/ECB/PKCS1Padding
jar包中找到的dbeaver-ue-public.key文件为RAS解密需要的公钥。

License校验分析
根据上述解密过程分析,生成相应的LicenseLicense的生成方式见下面破解步骤)导入激活,出现如下弹窗:
[size=14.6667px]
图片9.png

查看日志文件dbeaver-debug.log,异常信息如下:[size=14.6667px]
图片10.png

从日志可以看出验证的方法在:com.dbeaver.lm.validate.PublicLicenseValidator.validateLicense
根据路径查看该方法的代码,如下:[size=14.6667px]
图片11.png

分析其逻辑,验证的步骤在:result =LMPublicAPI.checkLicenseStatus(monitor,clientId, licenseManager, license, product);
查看其代码如下:
图片12.png [size=14.6667px]
分析代码,发现StringlicenseStatusText = client.checkLicenseStatus(license, product);
返回的licenseStatusText如果为空,那么该方法将会直接返回LMLicenseStatus.VALID的校验状态(即有效的状态),
继续分析调用的checkLicenseStatus方法,代码如下:
图片13.png

这里能看出请求了网络接口,调用的请求方法为父类中的方法,在安装目录下找到PublicServiceClient的父类在com.dbeaver.remote.client_1.0.2.202206121739.jar中,
在类com.dbeaver.remote.client.AbstractRemoteClient中能看到HTTP请求的客户端构造代码如下:
图片14.png

返回类com.dbeaver.lm.validate.PublicServiceClient中,能看到其构造客户端的代码如下:
图片15.png

能分析出PUBLIC_SERVICE_URL为接口的请求地址,继续追溯该变量的来源,其逻辑如下:
图片16.png

回到获取校验结果StringlicenseStatusText = client.checkLicenseStatus(license, product);的代码,
请求的路径无法访问时,那么得到的licenseStatusText就为空值了,就能直接得到有效的验证结果。
从上述代码中,能看出DEBUG_MODEtrue或者是https://dbeaver.com/lmp/无法访问时,请求结果将会返回空值。
[size=14.6667px]
由此得到了两种通过网络校验的方法:
1.       hosts文件中配置dbeaver.comIP127.0.0.1
2.       在配置文件中设置lm.debug.mode=true
[size=14.6667px]
破解思路
1.       生成一对RAS加解密使用的公钥和私钥;
2.       com.dbeaver.app.ultimate_22.1.0.202206121739.jar中的公钥替换成自己生成的公钥;
3.       用自己的私钥生成一个License文件导入到DBEaver中;
4.       绕过License网络校验。
[size=14.6667px]
破解步骤
1.       查看org.jkiss.lm_1.0.136.202206121739.jar内的代码,发现类org.jkiss.lm.LMMain中已经实现了生成密钥、生成License、解密License、导入License的测试代码,只需稍作修改就可以直接使用;
2.       导入项目需要依赖的jar包,需要依赖的jar包如下图:
图片17.png
3.       创建一个新的类,修改org.jkiss.lm.LMMain中的代码,得到如下代码:
[Java] 纯文本查看 复制代码
import org.jkiss.lm.*;
import org.jkiss.utils.Base64;

import java.io.*;
import java.security.Key;
import java.security.KeyPair;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.Date;


public class DBeaverLicenseActiveMain {
    private static final LMProduct TEST_PRODUCT;

    static {
        TEST_PRODUCT = new LMProduct(
                "dbeaver-ue",
                "DB",
                "DBeaver Ultimate",
                "DBeaver Ultimate Edition",
                "22.1",
                LMProductType.DESKTOP,
                new Date(),
                new String[0]
        );
    }


    public static void main(String[] args) throws Exception {
        System.out.println("LM 2.0");
        // 生成RAS密钥对
        if (args.length > 0 && args[0].equals("gen-keys")) {
            System.out.println("Test key generation");
            generateKeyPair();
        // 生成加密License
        } else if (args.length > 0 && args[0].equals("encrypt-license")) {
            System.out.println("Encrypt license");
            encryptLicense();
        // 解密License
        } else if (args.length > 0 && args[0].equals("decrypt-license")) {
            System.out.println("Decrypt license");
            decryptLicense();
        // 导入License测试
        } else if (args.length > 0 && args[0].equals("import-license")) {
            System.out.println("Import license");
            importLicense();
        // 生成明文License
        } else {
            System.out.println("Test license generation");
            generateLicense();
        }
    }

    private static void encryptLicense() throws Exception {
        PrivateKey privateKey = readPrivateKey();
        BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
        String licenseID = LMUtils.generateLicenseId(TEST_PRODUCT);
        System.out.println("License ID: " + licenseID);
        System.out.println("Product ID (" + TEST_PRODUCT.getId() + "):");
        String productID = in.readLine();
        if (productID.isEmpty()) {
            productID = TEST_PRODUCT.getId();
        }

        System.out.println("Product version (" + TEST_PRODUCT.getVersion() + "):");
        String productVersion = in.readLine();
        if (productVersion.isEmpty()) {
            productVersion = TEST_PRODUCT.getVersion();
        }

        System.out.println("Owner ID (10000):");
        String ownerID = in.readLine();
        if (ownerID.isEmpty()) {
            ownerID = "10000";
        }

        System.out.println("Owner company (JKISS):");
        String ownerCompany = in.readLine();
        if (ownerCompany.isEmpty()) {
            ownerCompany = "JKISS";
        }

        System.out.println("Owner name (Serge Rider):");
        String ownerName = in.readLine();
        if (ownerName.isEmpty()) {
            ownerName = "Serge Rider";
        }

        System.out.println("Owner email (serge@jkiss.org):");
        String ownerEmail = in.readLine();
        if (ownerEmail.isEmpty()) {
            ownerEmail = "serge@jkiss.org";
        }

        LMLicense license = new LMLicense(
                licenseID,
                LMLicenseType.ULTIMATE,
                new Date(),
                new Date(),
                (Date)null,
                LMLicense.FLAG_UNLIMITED_SERVERS,
                productID,
                productVersion,
                ownerID,
                ownerCompany,
                ownerName,
                ownerEmail);
        byte[] licenseData = license.getData();
        byte[] licenseEncrypted = LMEncryption.encrypt(licenseData, privateKey);
        System.out.println("--- LICENSE ---");
        System.out.println(Base64.splitLines(Base64.encode(licenseEncrypted), 76));
    }

    private static void decryptLicense() throws Exception {
        PublicKey publicKey = readPublicKey();
        System.out.println("License:");
        byte[] encryptedLicense = LMUtils.readEncryptedString(System.in);
        LMLicense license = new LMLicense(encryptedLicense, publicKey);
        System.out.println(license);
    }

    private static void importLicense() throws Exception {
        final PrivateKey privateKey = readPrivateKey();
        final PublicKey publicKey = readPublicKey();
        System.out.println("License:");
        byte[] encryptedLicense = LMUtils.readEncryptedString(System.in);
        LMLicenseManager lm = new LMLicenseManager(new LMKeyProvider() {
            public Key getEncryptionKey(LMProduct product) {
                return privateKey;
            }

            public Key getDecryptionKey(LMProduct product) {
                return publicKey;
            }
        }, (LMLicenseValidator)null);
        lm.importLicense(TEST_PRODUCT, encryptedLicense);
    }

    private static void generateKeyPair() throws LMException {
        KeyPair keyPair = LMEncryption.generateKeyPair(2048);
        PublicKey publicKey = keyPair.getPublic();
        PrivateKey privateKey = keyPair.getPrivate();
        System.out.println("--- PUBLIC KEY ---");
        System.out.println(Base64.splitLines(Base64.encode(publicKey.getEncoded()), 76));
        System.out.println("--- PRIVATE KEY ---");
        System.out.println(Base64.splitLines(Base64.encode(privateKey.getEncoded()), 76));
    }

    private static void generateLicense() throws LMException {
        System.out.println("Gen keys");
        KeyPair keyPair = LMEncryption.generateKeyPair(2048);
        PublicKey publicKey = keyPair.getPublic();
        PrivateKey privateKey = keyPair.getPrivate();
        System.out.println("Gen Test license");
        String licenseID = LMUtils.generateLicenseId(TEST_PRODUCT);
        LMLicense license = new LMLicense(
                licenseID,
                LMLicenseType.ULTIMATE,
                new Date(),
                new Date(),
                (Date)null,
                LMLicense.FLAG_UNLIMITED_SERVERS,
                TEST_PRODUCT.getId(),
                TEST_PRODUCT.getVersion(),
                "10000",
                "JKISS",
                "Serge Rider",
                "serge@jkiss.org");
        byte[] subData = license.getData();
        byte[] encrypted = LMEncryption.encrypt(subData, privateKey);
        String encodedBase64 = Base64.splitLines(Base64.encode(encrypted), 76);
        byte[] encodedBinary = Base64.decode(encodedBase64);
        LMLicense licenseCopy = new LMLicense(encodedBinary, publicKey);
        System.out.println(licenseCopy);
        System.out.println("Gen subscription");
        LMSubscription subscription = new LMSubscription(
                licenseID,
                LMSubscriptionPeriod.MONTH,
                new Date(),
                new Date(),
                1,
                true);
        subData = LMEncryption.encrypt(subscription.getData(), privateKey);
        String subBase64 = Base64.splitLines(Base64.encode(subData), 76);
        byte[] subBinary = Base64.decode(subBase64);
        LMSubscription subCopy = new LMSubscription(subBinary, publicKey);
        System.out.println(subCopy);
    }

    private static PrivateKey readPrivateKey() throws LMException {
        File keyFile = new File(new File(System.getProperty("user.home"), ".jkiss-lm"), "private-key.txt");
        if (!keyFile.exists()) {
            throw new LMException("Cannot find private key file (" + keyFile.getAbsolutePath() + ")");
        } else {
            try {
                Throwable var1 = null;
                Object var2 = null;

                try {
                    InputStream keyStream = new FileInputStream(keyFile);

                    PrivateKey var10000;
                    try {
                        byte[] privateKeyData = LMUtils.readEncryptedString(keyStream);
                        var10000 = LMEncryption.generatePrivateKey(privateKeyData);
                    } finally {
                        if (keyStream != null) {
                            keyStream.close();
                        }

                    }

                    return var10000;
                } catch (Throwable var12) {
                    if (var1 == null) {
                        var1 = var12;
                    } else if (var1 != var12) {
                        var1.addSuppressed(var12);
                    }
                    throw var1;
                }
            } catch (Throwable var13) {
                throw new LMException(var13);
            }
        }
    }

    private static PublicKey readPublicKey() throws LMException {
        File keyFile = new File(new File(System.getProperty("user.home"), ".jkiss-lm"), "public-key.txt");
        if (!keyFile.exists()) {
            throw new LMException("Cannot find public key file (" + keyFile.getAbsolutePath() + ")");
        } else {
            try {
                Throwable var1 = null;
                try {
                    InputStream keyStream = new FileInputStream(keyFile);

                    PublicKey var10000;
                    try {
                        byte[] keyData = LMUtils.readEncryptedString(keyStream);
                        var10000 = LMEncryption.generatePublicKey(keyData);
                    } finally {
                        if (keyStream != null) {
                            keyStream.close();
                        }

                    }

                    return var10000;
                } catch (Throwable var12) {
                    if (var1 == null) {
                        var1 = var12;
                    } else if (var1 != var12) {
                        var1.addSuppressed(var12);
                    }

                    throw var1;
                }
            } catch (Throwable var13) {
                throw new LMException(var13);
            }
        }
    }
}

4.       上述代码传入参数gen-keys生成密钥对如下:
  Plain  Text
  
--- PUBLIC KEY ---
  MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAhXxuid2ALfSF68yUrH0H3e2eJ0S9MfJu
  Vil1r03M/JXTu5FgjTyUf1Q5KJnXSLaxiibtHjAJUXRk10C9iks7FFNeOP8diDL2KcbPTSdy7iap
  CYAaxZilIRIq0Ab/Zhi+HwPh9wuJk/6XEgoUIC1eiKj0aVMz25G5udXHtIx8OjavH2eoFdlOpDwm
  6ZMj1ZSbkY50cnOb6eJv38oKF8fL4b3i3J0vv5lk90NYpVSGFwPs9S2ANsWT1CXXfl3wQOuBHOfL
  KToJN9V5fqMgiAoPNT8BvO5NMMJ8d9MKQD4rtqq3S40E7iIZ/fN51h/kQ0ynCHXdsdze48jQ+quS
  sifHTwIDAQAB
  --- PRIVATE KEY ---
  MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCFfG6J3YAt9IXrzJSsfQfd7Z4n
  RL0x8m5WKXWvTcz8ldO7kWCNPJR/VDkomddItrGKJu0eMAlRdGTXQL2KSzsUU144/x2IMvYpxs9N
  J3LuJqkJgBrFmKUhEirQBv9mGL4fA+H3C4mT/pcSChQgLV6IqPRpUzPbkbm51ce0jHw6Nq8fZ6gV
  2U6kPCbpkyPVlJuRjnRyc5vp4m/fygoXx8vhveLcnS+/mWT3Q1ilVIYXA+z1LYA2xZPUJdd+XfBA
  64Ec58spOgk31Xl+oyCICg81PwG87k0wwnx30wpAPiu2qrdLjQTuIhn983nWH+RDTKcIdd2x3N7j
  yND6q5KyJ8dPAgMBAAECggEATJu5JM5GfhlTspxaxxOKrEdu+MJugnfL8w8gR1ezSVMDjSZF70jR
  QLIpi6+e6lBPXCYy95xB/Ml8Bj1VikTaxzOBY9ymKkB1HkzHNFRrlVoCsT0gID8WpgAzKeiaMxII
  KuyjhpDMiG8YbHX0TvM6yduNSdVCccUUfh6+2lO2CAH1fRT+FJqEI8tUGbuB16YvM6t/mNjtbOo1
  dSsRacc/7fV3vPP7a3kqc0PHpIDAyuKcLWn1HwEzBeAgp/TlX9J1bU8WcijKQBLcrxYmxAqDZOPD
  imcV0XfKs6I2JUHEePUHiAoG59BhVGA/rJnkyQEpaD3mKkFImIIKm6poLvzboQKBgQD1iLWE4gKe
  g/5TUAFO/aMhiMQG3vP410eBoWHZnvQt72VKX6hlgGwvZld1UF7hqljK1ICvvwFe2aGWDJAk5Zz5
  0ipPj1UVkk5FsLoi/YT3Pj8tsNT0xJrilXDlpYAEsecbqvBs5QBGBKH+4QkFoCvCb6qWuDWQMNYJ
  Ja/Su97nLQKBgQCLLQtqRutmw96XzkfV9yuqQ9nzk7Z7CfM00O4l0jP0tKTXomuSW1nRiYc3UfZJ
  cJfPR91y40N4v7A8DaiReH5T5F6Mt6gnnSmRt/J6vE5tz3lODnoaUVOmjEpj7ytQdtr2xUYNOnEu
  oRH2lPkhUdIBM35EfySpKBu7yWfc3ap16wKBgQDGZzKububI6kWvUp3ME34nUdl859njASph4GMu
  M5iCKckCgSuU4WIKJzuSq2AQH9NiCrb1zHUyDM/abMppVjUzVZUk9uA87x1aiQTP02YHV4A7zoE2
  TEwPvcwddU9t+8eQ/t8KTz2aVpIEYBknN5dEpXEGG1IE8sFxYMejlHX4/QKBgECo/NSzfkqQVapR
  vC48V50TSP9RcUZYqRWwu/P2ZQ0boDpOy4uDxYcETj31ZmdYWC+FQ+1MiNxgspA0CE0NniN7xjG6
  YfWFnvqEa7N6KTX7XnBVaYUwo5yNMUKcq5MGpVRg8trSfCMd0iqtq9E/IkJMmi1YpL+yUrA8MnT6
  x2dhAoGBALSWWmS3fBhUi55YwnmxMGcZKRw3SR4qvgfMVsXx/wraLg6HdHYv5eugQ5JimqlAw6Bv
  B6EIBnhrWT41s1uLkVrD3bYvOT5dKlgHmNUuosZdTkAZQAptiPq0tNnXJ5N++4NIf4vmTgbC4OhG
  b5eH0TuNW8cBSErqYoCf/tVrjVTq
  
5.       创建文件public-key.txtprivate-key.txtdbeaver-ue-public.key,将私钥复制到private-key.txt中,将公钥复制到public-key.txtdbeaver-ue-public.key中;[size=14.6667px]
6.       public-key.txtprivate-key.txt文件放到用户目录下的.jkiss-lm文件夹中,dbeaver-ue-public.key文件替换com.dbeaver.app.ultimate_22.1.0.202206121739.jar内的keys\dbeaver-ue-public.key文件
7.       Java代码中传入参数encrypt-license生成License字符串,如下:
  Plain  Text
  
ReJalXttbssteHQ63re1sGcW2XbP4emJiJxLGaa/JEXXcRiuUzwfwAHh5gUWD+blYrIVKy4Ibx9k
  xm6XcIlg4X1eWI4UrlTep+D5l2cKSqw63Hzi8hyau8H9OFfPe9PHig1Kla+u4fQCkn7AidqZPkV7
  QVK2F++ZIUcPmc+qEkm3suOEtRgEKKBfsZpHTg+CUrUb37DlEz6qWnOI+5hy95B3z892TTJzkARc
  HOhSdFM/1/q4WMUtfjcVcav7x7qiFDIfuWQsMNASVMxBJtbKsKv9tqc+2ExGV+9bZBHls7JehGOW
  bG7YOpoZA49Ha6DnWlhoWAeFizL6zFLbH8+tZQ==
  
8.       修改配置文件dbeaver.ini,增加lm.debug.mode=true配置,如下:
  Plain  Text
  
-startup
  plugins/org.eclipse.equinox.launcher_1.6.400.v20210924-0641.jar
  --launcher.library
  plugins/org.eclipse.equinox.launcher.win32.win32.x86_64_1.2.400.v20211117-0650
  -vmargs
  -XX:+IgnoreUnrecognizedVMOptions
  --add-modules=ALL-SYSTEM
  -Dosgi.requiredJavaVersion=11
  -Xms128m
  -Xmx2048m
  -Djavax.net.ssl.trustStoreType=WINDOWS-ROOT
  -Ddbeaver.distribution.type=exe
  -Dlm.debug.mode=true
  

9.       打开DBeaver导入License,如图:
图片18.png
图片19.png

后记
仅供研究学习使用,请勿用于非法用途,请支持正版!

免费评分

参与人数 6威望 +2 吾爱币 +106 热心值 +6 收起 理由
gaosld + 1 + 1 热心回复!
AlohaRE + 1 + 1 热心回复!
splic2012 + 1 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
小朋友呢 + 2 + 1 热心回复!
tianyulouzhu + 1 + 1 感谢分享,收纳了
云在天 + 2 + 100 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

星空漫步 发表于 2022-8-10 15:35
尝试了一下可以,找key的包的时候找了半天,到最后才发现我下载的是DBeaver EE ,按照楼主的思路来是可以破解的,终于有导出excel功能了,顺便问一下,怎么设置导出excel的默认文件夹
 楼主| pengsx01 发表于 2022-8-5 11:38
lijingboforever 发表于 2022-8-5 11:14
学习了,那是不是 全家桶都可以按照这个思路进行反编译和破解了

那个文件太多了,难度高了一大截
WuAiKeLe 发表于 2022-8-2 08:05
很详细,每天都在用社区版.真没想过要研究那么深,顶楼主的研究精神.
daiqing 发表于 2022-8-2 08:27
Dlm.debug.mode=true 好,https验证一直没成功
blacksails 发表于 2022-8-2 09:43
感谢楼主分享
yuwan1994 发表于 2022-8-2 10:51
这个思路不错,可惜不会java,要不然可以研究下
tianyulouzhu 发表于 2022-8-3 11:21
不会java ..... 按照教程成功了,免费评分冷却ing...
goastship 发表于 2022-8-3 21:49
看了楼主的讲解,明白了两种通过网络校验的方法的来由,非常感谢.
Airs_Lau 发表于 2022-8-4 10:04
感谢大佬的分析
AwesomeOne 发表于 2022-8-5 10:39
感谢楼主分享!
lijingboforever 发表于 2022-8-5 11:14
学习了,那是不是 全家桶都可以按照这个思路进行反编译和破解了
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则 警告:本版块禁止灌水或回复与主题无关内容,违者重罚!

快速回复 收藏帖子 返回列表 搜索

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2022-8-16 18:57

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表