吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 669|回复: 14
上一主题 下一主题
收起左侧

[原创] jprofiler最新版逆向分析

[复制链接]
跳转到指定楼层
楼主
swiftR 发表于 2024-12-21 17:51 回帖奖励
本帖最后由 swiftR 于 2024-12-22 14:56 编辑

定位密钥校验逻辑

根据Jprofiler安装目录有Jvm相关的dll知道了界面逻辑都是java写的,那么java相关的工具也是可以使用的。
基于awt的gui应用组件的监听回调都会继承ActionListener,这里直接hook这个接口的actionPerformed方法。
通过arthas去hook到按钮点击后的回调方法

watch java.awt.event.ActionListener actionPerformed "{params,target}" -x 3 -m 600


然后输入许可证点击确定

Affect(class count: 480 , method count: 288) cost in 409 ms, listenerId: 9
method=com.ejt.framework.f.c.actionPerformed location=AtExit
ts=2024-12-21 16:02:05.678; [cost=21.1043ms] result=@ArrayList[
    @Object[][
        @ActionEvent[

可以确定com.ejt.framework.f.c.actionPerformed是点击后的校验逻辑,代码逻辑在jprofiler.jar文件中

  public void actionPerformed(ActionEvent actionEvent) {
        Object source = actionEvent.getSource();//获取事件来源 一个是确定按钮的一个是取消按钮的
        if (source == this.f17255d) { 
            m27227o();
        } else if (source == this.f17256e) {
            m27234f();
        }
    }

注册逻辑分析

确定按钮的逻辑在 com.ejt.framework.f.c.o

/* renamed from: o */
    private void m27227o() {
        this.f17257f = false;
        if (this.f17252a.validateAndSave()) {
            mo12243i();
            setVisible(false);
            this.f17252a.getLicenseInfo().m27276p();
        }
    }

validateAndSave实际就是校验和保存密钥的逻辑
由于静态分析太费时间了并且java类都是被混淆处理过的先动静结合分析下,jprofiler启动时会从bin/options/中读取jvm参数,我这里在/bin/options/base.options中添加

-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005

然后把jar复制到idea断点调试,断点来到com.ejt.framework.f.d#a这里,不关键的代码我先去掉了,这里会根据var7的值判断错误类型,所有var7的值决定了这个key能否使用,继续单步进入var6.a方法

 private boolean a(String var1, String var2, String var3, boolean var4, b var5) {
        com.ejt.framework.f.b var6 = this.getLicenseInfo();
        int var7;
        if ((var5 != d.b.b || var6.B() != null && !var6.r()) && !com.ejt.framework.f.b.f(var3)) {
            com.ejt.framework.f.a.g();
            var7 = var6.a(var3, var1, var2, this.d());
        } else {

        }
        if (var4) {
            this.acceptLicense(var1, var2, var3);
            return true;
        } else {
            switch (var7) {
                case -18:
                    this.showWarningMessage(com.ejt.framework.f.q.a("license.server.ssl.error", new Object[]{"https://license.ej-technologies.net/license/v2/"}));
                    this.c();
                    return false;
                case -17:
                    this.showWarningMessage(com.ejt.framework.f.q.a("license.server.not.encrypted", new Object[0]));
                    this.c();
                    return false;
                case -16:
                    this.showWarningMessage(com.ejt.framework.f.q.a("license.server.not.authenticated", new Object[0]));
                    this.c();

继续看var6.a,这个方法用来校验key并返回还有多少天可用(实际是通过当前时间减去key中的时间),所以这里只要返回的是一个正数就算成功,这里只需要满足a(var5, var2.a() + var4, 83, 52, 3)返回的是true就能返回var2.e()也就是剩余多少天

    public int d(String var1) {
        if (var1.startsWith("F-")) {
            return -11;
        } else {
            o var2 = new o(var1);
            int var3 = var2.e();//剩余多少天
            if (var3 != -3 && var3 != -2) {
                String var4 = var2.f();
                String var5 = var2.g();
                if (var4 != null && var5 != null) {//a(var2)是校验key中的版本
                    return !a(var2) && (a(var5, var2.a() + var4, 83, 52, 3) || a(var5, var2.a() + var4, 36, 86, 26)) ? var2.e() : this.c(var1);
                } else {
                    return -3;
                }
            } else {
                return var3;
            }
        }
    }
var1 = "E-J14-AUTO#123456-2024.12.28-10-y763ch33fxm45q#12345"
var2 (slot_2) = {o@12914} 
 a = "E-J14-AUTO#123456-2024.12.28-10-y763ch33fxm45q#12345"
 b = "E-J14-AUTO#123456-2024.12.28-10-"
 c = "y763ch33fxm45q#12345"
 e = 6
var3 (slot_3) = 6
var4 (slot_4) = "y763ch33fxm45q"
var5 (slot_5) = "12345"

a(var5, var2.a() + var4, 83, 52, 3),这里面的逻辑很简单,通过#符号把key分割成两部分,然后把第一部分计算后的结果和第二部分对比是不是一样的

    protected static boolean a(String var0, String var1, int var2, int var3, int var4) {
        if (var1 == null) {
            return false;
        } else {
            char[] var5 = var1.toCharArray();
            int var6 = 0;
            char[] var7 = var5;
            int var8 = var5.length;

            for(int var9 = 0; var9 < var8; ++var9) {
                char var10 = var7[var9];
                var6 += var10;
            }

            String var10000 = String.valueOf(var6 % var2);
            String var11 = var10000 + var6 % var3 + var6 % var4;
            return var0.equals(var11) || var0.equals(var11.replace('0', 'a').replace('1', 'b'));
        }
    }
var0 = "12345"
var1 = "E-J14-AUTO#123456-2024.12.28-10-y763ch33fxm45q"
var2 = 83
var3 = 52
var4 = 3

试用结束的时间是写在key里面的,这里是不是把key的时间改成2099年就可以无限使用了,把校验计算的逻辑复制出来试试看

    public static String encode(String var1){
        int var2 = 83;
        int var3 = 52;
        int var4 = 3;
        char[] var5 = var1.toCharArray();
        int var6 = 0;
        char[] var7 = var5;
        int var8 = var5.length;
        for(int var9 = 0; var9 < var8; ++var9) {
            char var10 = var7[var9];
            var6 += var10;
        }
        String var10000 = String.valueOf(var6 % var2);
        String var11 = var10000 + var6 % var3 + var6 % var4;
        return var1+"#"+var11;
    }
    public static void main(String[] args) {
        System.out.println(encode("E-J14-AUTO#123456-2099.12.28-10-y763ch33fxm45q"));
    }

得到E-J14-AUTO#123456-2099.12.28-10-y763ch33fxm45q#4572,然后输入注册码成功,但是在使用的过程中发现事情没这么简单,当我使用jprofiler去attach到我要分析的java程序的过程中把我的程序强制关闭了。
看控制台打印可以知道应该是在attach完成后重新校验了key

JProfiler> Time measurement: elapsed time
JProfiler> CPU profiling enabled
JProfiler> Initializing configuration.
JProfiler> Retransforming 119 class files.
JProfiler> Configuration updated.
JProfiler> ERROR: Invalid license key. Aborting. 
JProfiler> Killing process

在jadx中搜索了Aborting相关的字符串也是一无所获,转变思路想查下attach后加载了哪个agent然后去搜索VirtualMachine.attach字符串也是一无所获。通过观察attach后多了一个jprofiler的线程,找到了加载的类

native校验

可以看到initDescriptions是一个jni的native方法,搜索了一下加载的dll定位到jprofiler.dll,在dll中查到字符串Aborting,sub_7FF84D145B50应该就是校验函数了

.text:00007FF84D127176                                         ; DATA XREF: sub_7FF84D1270A0:jpt_7FF84D127174↓o
.text:00007FF84D127176                 mov     rdx, [rdi+28h]  ; jumptable 00007FF84D127174 case 1
.text:00007FF84D12717A                 lea     r8, [rdi+40h]
.text:00007FF84D12717E                 mov     rcx, [rdi+38h]
.text:00007FF84D127182                 call    sub_7FF84D145B50
.text:00007FF84D127187                 test    al, al
.text:00007FF84D127189                 jnz     short loc_7FF84D12719D
.text:00007FF84D12718B                 lea     rcx, aErrorInvalidLi ; "ERROR: Invalid license key. Aborting."
.text:00007FF84D127192                 call    sub_7FF84D151470
.text:00007FF84D127197                 mov     word ptr [rdi+8], 101h

接下来会调用到sub_7FF84D115770,这里会校验key是不是合法的,

.text:00007FF84D115538                 mov     rcx, rdi  //rcx就是我们的key
.text:00007FF84D11553B                 lea     rdx, aLjavaLangStrin ; "(Ljava/lang/String;)V"
.text:00007FF84D115542                 call    sub_7FF84D115770

key计算完成后得到的值会在7FF84D115AA8处调用strncmp进行比较直接在这里进程断点,通过ida附加在java进程上,然后jprofiler也去attach到那个java进程

.text:00007FF84D115A97                 inc     r8              ; 比较的字符数量数量
.text:00007FF84D115A9A                 cmp     [rax+r8], sil
.text:00007FF84D115A9E                 jnz     short loc_7FF84D115A97
.text:00007FF84D115AA0                 lea     rdx, [rbp+57h+Str2] ; Str2
.text:00007FF84D115AA4                 lea     rcx, [r13+1]    ; Str1
.text:00007FF84D115AA8                 call    strncmp

用ida打印下字符串

Python>print(idaapi.get_bytes(ida_dbg.get_reg_val("rcx"), 100))
b'y763ch33fxm45q#4572\x00\x00\x00\x00\x00x\xa0\x1c[\x00\x9a\x01\x91nacos-grpc-client-executor-192.168.254.253-279\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00D\xa0\x18[\x00\x9b\x01\x88\x00\x00\x00\x00'
Python>print(idaapi.get_bytes(ida_dbg.get_reg_val("rdx"), 100))
b'3hk2zxe3e5qmbq\x00\x00\x01\x01\x9cB\x00\x00\x00\x00\x00`\x9cB\x00\x00\x00\x00qbmq5e3\x00\xd0s\x9bB\x00\x00\x00\x00\xd0s\x9bB\x00\x00\x00\x00\x00\x00\x81\x00\x00\x00\x00\x00\xc8\xec\x06\x1c\x8dG\x00\x00\xca\xfe\x18M\xf8\x7f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00 \x1f\xa5\x00'

rdx中正确的计算的值,我们直接替换上得到E-J14-AUTO#123456-2099.12.28-10-3hk2zxe3e5qmbq#4572
这样直接使用是不行的还得考虑到java层的校验,通过上面的encode代码计算下#号后面的值得到3791
替换上面的4572

最后完美收工

注:该贴仅学习探讨用

免费评分

参与人数 3吾爱币 +2 热心值 +3 收起 理由
feishibudong + 1 用心讨论,共获提升!
ycycn + 1 + 1 谢谢@Thanks!
hualili + 1 + 1 我很赞同!

查看全部评分

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

推荐
Hmily 发表于 2024-12-21 20:54
看下这个帮助介绍了解一下怎么贴图,然后编辑一下主题。https://www.52pojie.cn/forum.php ... ;page=1#pid51478900
沙发
hbu126 发表于 2024-12-21 18:52
3#
wangjichuan 发表于 2024-12-21 19:10
4#
666888tzq 发表于 2024-12-21 20:25
厉害了,大佬,太复杂了。
5#
三滑稽甲苯 发表于 2024-12-21 20:29
图片好像有问题
7#
 楼主| swiftR 发表于 2024-12-21 21:02 |楼主
Hmily 发表于 2024-12-21 20:54
看下这个帮助介绍了解一下怎么贴图,然后编辑一下主题。https://www.52pojie.cn/forum.php?mod=viewthread& ...

现在看着图片没问题了

点评

有问题,你清空下浏览器缓存就没了,你贴图方式不对,不然我也不会给你发,看下演示吧。  详情 回复 发表于 2024-12-21 21:03
8#
Hmily 发表于 2024-12-21 21:03
swiftR 发表于 2024-12-21 21:02
现在看着图片没问题了

有问题,你清空下浏览器缓存就没了,你贴图方式不对,不然我也不会给你发,看下演示吧。
9#
 楼主| swiftR 发表于 2024-12-21 21:11 |楼主
Hmily 发表于 2024-12-21 21:03
有问题,你清空下浏览器缓存就没了,你贴图方式不对,不然我也不会给你发,看下演示吧。

看了下图片有了,用手机打开看图片也是有的
10#
JESSEDD 发表于 2024-12-21 22:57
牛人!赞一个
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

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

GMT+8, 2024-12-23 00:57

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

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