首页
社区
课程
招聘
[原创]Burp Suite Pro 破解
2022-6-13 08:22 25916

[原创]Burp Suite Pro 破解

2022-6-13 08:22
25916

前言

感谢scz、surferxyz、h3110w0r1d-y等各位老师傅在前面开路,这篇文章只是跟着前面的老师傅脚步走,用了自己的方式(ClassFileTransformer)开刀。

 

基于burpsuite_pro_v2022.3.9.jar

使用工具

  • cfr - 反编译工具

  • jadx - 反编译gui工具,可以查找引用

  • btrace - 运行状态跟踪工具

  • arthas - 用起来比btrace更方便的跟踪工具

  • IDEA

正文

这里直接用h3110w0r1d-y老师傅的jar开看

1
2
3
4
5
6
7
8
文件名称: E:\xxx\burp_latest\BurpLoaderKeygen.jar
文件大小: 18.1 KB (18,580 字节)
修改时间: 20210723日,10:20:48
MD5: B7E345B5594331516F49A709C04A3E41
SHA1: B9F4E9CA60E7493E459E21019441D115D2F43D98
SHA256: 63BE39F1AEEBEA9B86477ACEBBEFEE469A7562216BA8B253A4BB87B0A7A68566
CRC32: BCF7BD4F
计算时间: 0.00s

启动方式:java -javaagent:BurpLoaderKeygen.jar -noverify -jar burpsuite_pro_v2022.3.9.jar

启动解析

  1. -noverify 这个是跳过jvm对class文件的验证,后面详细说为什么要跳过
  2. -javaagent 这个是class加载之前对class进行修改

原理分析

关于javaagent的详细介绍,可以看知道创宇的paper 认识 JavaAgent

破解原理

上jadx,先看看老师傅们都搞了啥.

 

 

这里很明显,找到Class里面包含751a8be34c1a9ed9633d04be3ba075a7的Class文件进行修改,变量p以及p2是负责定位要修改的位置,找到位置后进行patch.

 

先照搬进IDEA看看到底干了啥

 

 

 

这里可以看到,修改的地方其实很相近,那就dump整个class下来看看情况.

 

dump的办法

 

 

然后调用下

 

 

得到两个class文件

 

 

还记得之前的启动参数么,-noverify,由于这里简单粗暴的修改了原class的byte,导致jvm的字节码校验失败,根据这个道理,很容易就能找到师傅们干了什么,直接上图把.

 

用cfr反编译后,直接找empty

 

 

两个if被改成empty了,但是到这里我们还是不知道老师傅这么干的道理,继续往下走把.

查找调用链

根据之前cfr反编译出来的源码,能看到两个超级混淆的办法,void a以及bool b,知道类名跟方法名了,直接写个btrace,把调用链弄出来.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import org.openjdk.btrace.core.annotations.*;
import org.openjdk.btrace.core.BTraceUtils;
 
 
@BTrace(unsafe = true)
public class nls {
 
    @OnMethod(clazz="burp.nls", method="a")
    public static void void_a(Object[] var1, Object var2) {
        BTraceUtils.println("calleded void a");
        BTraceUtils.println("1st arg");
        BTraceUtils.println("arg length: " + var1.length);
        BTraceUtils.printArray(var1);
        byte[] my_var1 = (byte [])var1[0];
        BTraceUtils.println(new String(my_var1));
 
        BTraceUtils.println("2nd arg");
        String var2_str = String.valueOf(var2);
        BTraceUtils.println(var2_str);
        BTraceUtils.println("-----------");
    }
 
 
    @OnMethod(clazz="burp.nls", method="b")
    public static void bool_b(Object[] var1, Object var2) {
        BTraceUtils.println("called boolean b");
        BTraceUtils.println("1st arg");
        BTraceUtils.println("arg length: " + var1.length);
        BTraceUtils.printArray(var1);
 
        BTraceUtils.println("2nd arg");
        String var2_str = String.valueOf(oos);
        BTraceUtils.print(var2_str);
        BTraceUtils.jstack();
        BTraceUtils.println("-----------");
    }
}

过滤后,得到以下输出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
burp.nls.b(Unknown Source)
java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
java.base/java.lang.reflect.Method.invoke(Method.java:566)
burp.pev.a(Unknown Source)
java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
java.base/java.lang.reflect.Method.invoke(Method.java:566)
burp.dcw.b(Unknown Source)
java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
java.base/java.lang.reflect.Method.invoke(Method.java:566)
burp.ib2.b(Unknown Source)
java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
java.base/java.lang.reflect.Method.invoke(Method.java:566)
burp.gw4.a(Unknown Source)
java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
java.base/java.lang.reflect.Method.invoke(Method.java:566)
burp.hcp.b(Unknown Source)
java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
java.base/java.lang.reflect.Method.invoke(Method.java:566)
burp.ciq.b(Unknown Source)
java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
java.base/java.lang.reflect.Method.invoke(Method.java:566)
burp.a_q.a(Unknown Source)
java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
java.base/java.lang.reflect.Method.invoke(Method.java:566)
burp.f09.a(Unknown Source)
java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
java.base/java.lang.reflect.Method.invoke(Method.java:566)
burp.bh5.a(Unknown Source)
java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
java.base/java.lang.reflect.Method.invoke(Method.java:566)
burp.fqs.a(Unknown Source)
burp.e20.b(Unknown Source)
burp.etx.b(Unknown Source)
burp.etx.g(Unknown Source)
burp.etx.<init>(Unknown Source)
burp.epi.d(Unknown Source)
burp.StartBurp.main(Unknown Source)

这里能看出,很多类都是动态加载进去的,想知道怎么调用还是得看看上一个调用类干了什么.

 

还记得之前的javaagent工程里面写了writeModifyClass方法不,把他放到最外层,然后修改下即可dump整个burp加载的class.

 

 

分析后发现...pev.class其实就是把nls.class从string数组变成byte数组然后解密加载

 

 

无实质性进展..尝试硬啃混淆过的验证类

代码分析

首先,nls里面有两个核心方法,一个void a,另外一个boolean b

 

 

这里看到b方法,直接调用了a方法,其中var_9是new的object array,内容是var3_3的byte array,整个传入a方法第一个参数就是object [][],第二个参数就是固定的一串字符串

 

再查找整个nls.a调用,发现整个文件就两次调用,第二次调用也是差不多

 

 

那就可以认定,nls.a的第二个参数是固定的字符串,第一个参数是object [][]类型,具体传入了什么上arthas看看传入

获取验证函数的调用信息

由于是Windows,写个bat放到arthas的目录方便arthas attach到burp的进程

 

这里有个小技巧,如果burp调用太快手速不够,可以进入主界面后,用help->License->Update License来触发验证类的调用

1
2
3
4
5
@echo off
 
for /f "tokens=1" %%a in ('jps ^| findstr /rc:"burpsuite_pro_"') do @set BURP_PID=%%a
 
as.bat %BURP_PID% --ignore-tools

用老师傅的jar先启动,然后运行bat就行

 

这里监控用watch 类名 方法名 -b -s -x 4

 

先输入个错误的license key,看看nls类里面方法a跟b什么情况

 

 

这里输入的是abcd,所以对着的是97,98,99,100,那就知道了其实nls.a传入的是字符串的byte array再放进Object[]

 

再尝试正确的License Key,根据之前代码进行分析得到下面的结论

  • b方法进入时收到的参数是Object[][],第一个数组是int的数组负责控制走哪个流程,第二个是字符串获取到的Byte数组;退出时第一个数组变成了Object数组套着的验证信息,第二数组没变
  • a方法进入时收到的参数是Object[][],第一个参数是字符串获取到的Byte数组,第二个参数是固定的字符串;退时,第一个参数变成验证信息,第二个参数没变

结合注册流程,可以得知

  • 用户输入的License Key,先进入nls.b,然后由nls.b调用nls.a进行解密,解密成功则修改传入的第一个变量作为字符串数组给nls.b进行下一步操作
  • nls.a是解密函数
  • nls.a会被调用两次,两次修改的变量长度不一致,对应License Key解密跟Activation Respond解密

有了以上信息,尝试构造nls.a的调用

分析解密函数

之前有dump过破解跟原版的验证class,使用破解后的class用jar打包

 

jar -cvf burp.jar burp/nls.class

 

作为依赖塞进IDEA的工程,这里注意Run的配置要加上jvm参数-noverify

 

 

这里能成功解密了,现在就是这串License Key是怎么生成的问题了

 

继续用jadx查看Loader的注册机部分

 

 

这里很明显了,DES加密,key在最顶上是祖传的burpr0x!

 

大体流程就是字符串数组然后转成byte array用0作为分隔,再进行DES加密,最后base64

 

那么我们就可以根据这个流程反向写出解密函数

写自己的解密函数

这里没什么好说的,直接跟着原理,先base64解码,然后des解密成byte array,转化成字符串后再用0分割成字符串数组

 

这里注意,解密出来的字符串数组长度会比原生的nls.a解密出来的字符串数组长,需要根据字符串数组长度删减返回的数组长度

 

其中长度是7的是License Key解密,长度10的是Activation Respond解密

 

 

到这里就差不多了,可以开始写自己的javaagent了

编写Javaagent

获取java版本的asm代码

要把上面的java代码覆盖到原来的文件,我用的org.ow2.asm这个库,当然你用其他的库也可以,例如bytebuddy.

 

首先把java方法变成asm库的代码,这里我直接用asm库里面的asmifier(org.ow2.asm:asm-util:9.3)

  1. 下载asm本体以及asm.util两个jar
  2. 把之前写的Main编译成class,什么方式都行
  3. 把class丢到下载的jar同目录
  4. java -cp asm-9.3.jar;asm-util-9.3.jar org.objectweb.asm.util.ASMifier Main.class
  5. 得到java版的asm代码

写ClassFileTransformer

  1. 在manifest定义Premain-Class以及Agent-Class

  2. 实现接口ClassFileTransformer

  3. 覆写激活类的方法

代码部分我就不详说了,有兴趣的可以自己围观

展示

测试版本burpsuite_pro_v2022.5.1.jar

 

结束

自己还是太菜不会用动态debug,只能用这样的笨方法来研究,代码附上,成品附上,欢迎各位大佬指导.

 

在这里感谢各位老师傅的路子,让刀burp不用找具体激活类具体是哪个,方便我这种后来破解的小白.

 

截至写完本文,2022.5.1还能这么刀


[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

最后于 2023-1-16 16:47 被kanxue编辑 ,原因:
上传的附件:
收藏
点赞27
打赏
分享
最新回复 (14)
雪    币: 5722
活跃值: (1443)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
广州李寻欢 2022-6-13 10:54
2
0
感谢分享。
雪    币: 3805
活跃值: (5083)
能力值: ( LV8,RANK:127 )
在线值:
发帖
回帖
粉丝
Ally Switch 1 2022-6-13 14:59
3
0
感谢分享!
雪    币: 1358
活跃值: (1313)
能力值: ( LV6,RANK:99 )
在线值:
发帖
回帖
粉丝
KEEEY 1 2022-6-13 16:21
4
0
真的强!
雪    币: 299
活跃值: (5774)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
wandering 2022-6-14 08:44
5
0
非常感谢,境界高啊!
雪    币: 12768
活跃值: (16302)
能力值: (RANK:730 )
在线值:
发帖
回帖
粉丝
有毒 10 2022-6-14 09:34
6
0
感谢分享宝贵思路和过程,分析过程远比最后的成品重要,支持~
雪    币: 422
活跃值: (2614)
能力值: ( LV4,RANK:45 )
在线值:
发帖
回帖
粉丝
t0hka1 1 2022-6-14 12:33
7
0
mark
雪    币: 373
活跃值: (160360)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
飘零丶 2022-7-7 11:17
8
0
感谢分享
雪    币: 5722
活跃值: (1443)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
广州李寻欢 2022-7-7 15:38
9
0
感谢分享
雪    币: 427
活跃值: (59)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
pwelyn 2022-7-7 16:45
10
0
Mark
雪    币: 694
活跃值: (2341)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
ny0c 2022-11-8 12:33
11
0
2022.11 破解失败了,我需要一些时间看看
雪    币: 345
活跃值: (3402)
能力值: ( LV5,RANK:69 )
在线值:
发帖
回帖
粉丝
小菜鸟一 2022-11-8 13:05
12
0
说是新版有了一丢丢变化
雪    币: 345
活跃值: (3402)
能力值: ( LV5,RANK:69 )
在线值:
发帖
回帖
粉丝
小菜鸟一 2022-11-8 13:06
13
0

重复了

最后于 2022-11-8 13:10 被小菜鸟一编辑 ,原因:
雪    币: 1360
活跃值: (2093)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
xdnice 2022-12-27 10:24
14
0
感谢分享。
雪    币: 192
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
mb_rmgobvua 2022-12-27 11:58
15
1
新版字符串混淆了,代码也混淆了,反编译读不懂,不过也简单,加了几行判断其实就是占位,不做任何事情,不参与代码逻辑,但是会让反编译器摸不着头脑。
字符串混淆就更简单了,不管怎么混淆总是不可能把字符串内容都改了吧?所以一定会在某处用到。java的hook大法,通杀一切。
游客
登录 | 注册 方可回帖
返回