JAVA的逆向和反混淆 追查Burpsuite的破解原理详解

摘要:

近来,一款名为Burpsuite的商业版安全工具惨遭破解,网上出现了一款名为BurpLoader的恶意软件,该软件破坏了Burpsuite的认证流程,给世界和平带来了严重威胁。本系列文章通过对BurpLoader的几个版本的逆向分析,分析Burpsuite的破解原理,分析Burpsuite认证体系存在的安全漏洞。

JD-GUI的用途与缺陷:

JD-GUI是一款从JAVA字节码中还原JAVA源代码的免费工具,一般情况下使用这款工具做JAVA逆向就足够了,但是由于其原理是从JAVA字节码中按照特定结构来还原对应的JAVA源代码,因此一旦字节码结构被打乱(比如说使用混淆器),那么JD-GUI就会失去它的作用,如图为使用JD-GUI打开Burpsuite时的显示:

 


 

显然,JD-GUI没能还原JAVA源代码出来,因为Burpsuite使用了混淆器打乱了字节码结构 所以,JD-GUI适用于‘没有使用混淆器’的JAVA字节码,而缺陷是一旦字节码结构被打乱,则无法发挥它的作用

字节码分析:

Java的字节码并不像普通的二进制代码在计算机中直接执行,它通过JVM引擎在不同的平台和计算机中运行。

 


 

JVM是一个基于栈结构的虚拟计算机,使用的是JVM操作码(及其助记符),在这一点上和普通二进制反汇编的过程非常相似。 对Java字节码进行反编译其实非常简单,JDK内置的Javap工具即可完成这项任务。

示例:对Javar.class进行反编

 


 

注意javap的-c参数是显示详细代码,否则只显示method,而按照java的老规矩Javar不要加后缀名 同时你也可以使用eclipse的插件Bytecode Visualizer来反编译字节码

 


 

注意右面的流程图,大家在上程序设计导论课时都画过吧,现在发现它的用途了吧,一眼就看出是一个if-else结构,前两句定义i变量,然后取i=2压栈常数1,比对i和1以后就都java.lang.system.out了,一个输出wooyun,一个输出lxj616。

老版本的BurpLoader分析:

随着Burpsuite的更新,BurpLoader也在跟着进行更新,我们从老版本的BurpLoader入手,简要分析一下之前老版本的burpsuite破解原理。 本处选用了1.5.01版本的BurpLoader进行分析 首先试着用JD-GUI载入BurpLoader:

 


 

成功还原了BurpLoader源代码,只可惜由于是对burpsuite的patch,所以burpsuite的混淆在burploader里仍然可读性极差,不过可以推断burploader本身没有使用混淆工具。

<span class="keyword" style="font-weight: bold;">public</span> <span class="keyword" style="font-weight: bold;">static</span> <span class="keyword" style="font-weight: bold;">void</span> main(String[] args)
  {
    <span class="keyword" style="font-weight: bold;">try</span>
    {
      <span class="keyword" style="font-weight: bold;">int</span> ret = JOptionPane.showOptionDialog(<span class="keyword" style="font-weight: bold;">null</span>, <span class="string" style="color: #dd1144;">"This program can not be used for commercial purposes!"</span>, <span class="string" style="color: #dd1144;">"BurpLoader by larry_lau@163.com"</span>, <span class="number" style="color: #009999;">0</span>, <span class="number" style="color: #009999;">2</span>, <span class="keyword" style="font-weight: bold;">null</span>, <span class="keyword" style="font-weight: bold;">new</span> String[] { <span class="string" style="color: #dd1144;">"I Accept"</span>, <span class="string" style="color: #dd1144;">"I Decline"</span> }, <span class="keyword" style="font-weight: bold;">null</span>);
      <span class="comment" style="font-style: italic; color: #999988;">//显示选择对话框:这程序是出于学习目的写的,作者邮箱larry_lau(at)163.com </span>
      <span class="keyword" style="font-weight: bold;">if</span> (ret == <span class="number" style="color: #009999;">0</span>)  <span class="comment" style="font-style: italic; color: #999988;">//选择我同意</span>
      {
        <span class="comment" style="font-style: italic; color: #999988;">//以下用到的是java反射机制,不懂反射请百度</span>
        <span class="keyword" style="font-weight: bold;">for</span> (<span class="keyword" style="font-weight: bold;">int</span> i = <span class="number" style="color: #009999;">0</span>; i < clzzData.length; i++)
        {
          Class clzz = Class.forName(clzzData[i]);
          <span class="comment" style="font-style: italic; color: #999988;">//是burpsuite的静态类(名字被混淆过了,也没必要列出了)</span>
          Field field = clzz.getDeclaredField(fieldData[i]);
         <span class="comment" style="font-style: italic; color: #999988;">//静态类中的变量也被混淆过了,也不必列出了</span>
          field.setAccessible(<span class="keyword" style="font-weight: bold;">true</span>);
        <span class="comment" style="font-style: italic; color: #999988;">//访问private必须先设置这个,不然会报错</span>
 
          field.<span class="keyword" style="font-weight: bold;">set</span>(<span class="keyword" style="font-weight: bold;">null</span>, strData[i]);
        <span class="comment" style="font-style: italic; color: #999988;">//把变量设置成strData(具体那一长串到底是什么暂不讨论)</span>
        }
 
        Preferences prefs = Preferences.userNodeForPackage(StartBurp.<span class="keyword" style="font-weight: bold;">class</span>);
        <span class="comment" style="font-style: italic; color: #999988;">//明显preferences是用来存储设置信息的</span>
        <span class="keyword" style="font-weight: bold;">for</span> (<span class="keyword" style="font-weight: bold;">int</span> i = <span class="number" style="color: #009999;">0</span>; i < keys.length; i++)
        {
          <span class="comment" style="font-style: italic; color: #999988;">// key和val能猜出是什么吧</span>
          String v = prefs.<span class="keyword" style="font-weight: bold;">get</span>(keys[i], <span class="keyword" style="font-weight: bold;">null</span>);
          <span class="keyword" style="font-weight: bold;">if</span> (!vals[i].equals(v))
          {
            prefs.put(keys[i], vals[i]);
          }
        }
        StartBurp.main(args);
      }
    }
    <span class="keyword" style="font-weight: bold;">catch</span> (Exception e)
    {
      JOptionPane.showMessageDialog(<span class="keyword" style="font-weight: bold;">null</span>, <span class="string" style="color: #dd1144;">"This program can only run with burpsuite_pro_v1.5.01.jar"</span>, <span class="string" style="color: #dd1144;">"BurpLoader by larry_lau@163.com"</span>,
        <span class="number" style="color: #009999;">0</span>);
    }
  }
}

因此,BurpLoader的原理就是伪造有效的Key来通过检测,Key的输入是通过preference来注入的,而我猜测它为了固定Key的计算方法,通过反射把一些环境变量固定成常量了

新版本的BurpLoader分析:

以下用1.6beta版的BurpLoader进行分析: 首先用JD-GUI尝试打开BurpLoader:

 


 

看来这个版本的BurpLoader对字节码使用了混淆,这条路走不通了 于是直接读字节码吧!

 


 

大家可以看到这里的字符串都是混淆过的,每一个都jsr到151去解密

 


 

这段解密代码特点非常明显,一个switch走5条路,给221传不同的解密key,这不就是Zelix KlassMaster的算法吗? 简单的异或而已,轻松写出解密机:

<span class="keyword" style="font-weight: bold;">public</span> <span class="class" style="font-weight: bold; color: #445588;"><span class="keyword">class</span> <span class="title">Verify</span> {</span>
<span class="keyword" style="font-weight: bold;">private</span> <span class="keyword" style="font-weight: bold;">static</span> String decrypt(String str) {
<span class="keyword" style="font-weight: bold;">char</span> key[] = <span class="keyword" style="font-weight: bold;">new</span> <span class="keyword" style="font-weight: bold;">char</span>[] {<span class="number" style="color: #009999;">73</span>,<span class="number" style="color: #009999;">25</span>,<span class="number" style="color: #009999;">85</span>,<span class="number" style="color: #009999;">1</span>,<span class="number" style="color: #009999;">29</span>};
<span class="keyword" style="font-weight: bold;">char</span> arr[] = str.toCharArray();
<span class="keyword" style="font-weight: bold;">for</span> (<span class="keyword" style="font-weight: bold;">int</span> i = <span class="number" style="color: #009999;">0</span>; i < arr.length; i++) {
arr[i] ^= key[i % <span class="number" style="color: #009999;">5</span>];
}
<span class="keyword" style="font-weight: bold;">return</span> <span class="keyword" style="font-weight: bold;">new</span> String(arr);
}
 
<span class="keyword" style="font-weight: bold;">public</span> <span class="keyword" style="font-weight: bold;">static</span> <span class="keyword" style="font-weight: bold;">void</span> main (String args[]) {
System.out.println(decrypt(<span class="string" style="color: #dd1144;">"%x'sdgu4t3#x#`egj"hs.7%m|/7;hp+l&/S t7tn5v:j'}_dx%"</span>));
}
}

里面的5个密钥就是上图bipush的传参,别忘了iconst_1的那个1 解密出来是:larry.lau.javax.swing.plaf.nimbus.NimbusLook:4

其实这里解密出字符串没有什么用处,因为我们已经拿到老版本的源代码了,不过在别的软件逆向分析中可能会非常有用

总结&POC

以下为我修改后的BurpLoader,其中的恶意代码我已经去除,并将修改前的原值输出,大家可以在添加burpsuite jar包后编译运行这段代码

<span class="keyword" style="font-weight: bold;">package</span> stratburp;
 
<span class="keyword" style="font-weight: bold;">import</span> burp.StartBurp;
<span class="keyword" style="font-weight: bold;">import</span> java.lang.reflect.Field;
<span class="keyword" style="font-weight: bold;">import</span> java.util.prefs.Preferences;
<span class="keyword" style="font-weight: bold;">import</span> javax.swing.JOptionPane;
 
<span class="keyword" style="font-weight: bold;">public</span> <span class="class" style="font-weight: bold; color: #445588;"><span class="keyword">class</span> <span class="title">startburp</span>
{</span>
 
  <span class="keyword" style="font-weight: bold;">private</span> <span class="keyword" style="font-weight: bold;">static</span> <span class="keyword" style="font-weight: bold;">final</span> String[] clzzData = { <span class="string" style="color: #dd1144;">"burp.ecc"</span>, <span class="string" style="color: #dd1144;">"burp.voc"</span>, <span class="string" style="color: #dd1144;">"burp.jfc"</span>,
    <span class="string" style="color: #dd1144;">"burp.gtc"</span>, <span class="string" style="color: #dd1144;">"burp.zi"</span>, <span class="string" style="color: #dd1144;">"burp.q4c"</span>, <span class="string" style="color: #dd1144;">"burp.pid"</span>, <span class="string" style="color: #dd1144;">"burp.y0b"</span> };
 
  <span class="keyword" style="font-weight: bold;">private</span> <span class="keyword" style="font-weight: bold;">static</span> <span class="keyword" style="font-weight: bold;">final</span> String[] fieldData = { <span class="string" style="color: #dd1144;">"b"</span>, <span class="string" style="color: #dd1144;">"b"</span>, <span class="string" style="color: #dd1144;">"c"</span>, <span class="string" style="color: #dd1144;">"c"</span>, <span class="string" style="color: #dd1144;">"c"</span>, <span class="string" style="color: #dd1144;">"b"</span>, <span class="string" style="color: #dd1144;">"c"</span>, <span class="string" style="color: #dd1144;">"c"</span> };
  <span class="keyword" style="font-weight: bold;">private</span> <span class="keyword" style="font-weight: bold;">static</span> <span class="keyword" style="font-weight: bold;">final</span> String errortip = <span class="string" style="color: #dd1144;">"This program can only run with burpsuite_pro_v1.5.01.jar"</span>;
  <span class="keyword" style="font-weight: bold;">private</span> <span class="keyword" style="font-weight: bold;">static</span> <span class="keyword" style="font-weight: bold;">final</span> String[] keys = { <span class="string" style="color: #dd1144;">"license1"</span>, <span class="string" style="color: #dd1144;">"uG4NTkffOhFN/on7RT1nbw=="</span> };
 
  <span class="keyword" style="font-weight: bold;">public</span> <span class="keyword" style="font-weight: bold;">static</span> <span class="keyword" style="font-weight: bold;">void</span> main(String[] args)
  {
    <span class="keyword" style="font-weight: bold;">try</span>
    {
        <span class="keyword" style="font-weight: bold;">for</span> (<span class="keyword" style="font-weight: bold;">int</span> i = <span class="number" style="color: #009999;">0</span>; i < clzzData.length; i++)
        {
          Class clzz = Class.forName(clzzData[i]);
          Field field = clzz.getDeclaredField(fieldData[i]);
          field.setAccessible(<span class="keyword" style="font-weight: bold;">true</span>);
 
          <span class="comment" style="font-style: italic; color: #999988;">//field.set(null, strData[i]); </span>
          System.out.println(field.get(<span class="keyword" style="font-weight: bold;">null</span>));
        }
 
        Preferences prefs = Preferences.userNodeForPackage(StartBurp.class);
        <span class="keyword" style="font-weight: bold;">for</span> (<span class="keyword" style="font-weight: bold;">int</span> i = <span class="number" style="color: #009999;">0</span>; i < keys.length; i++)
        {
          String v = prefs.get(keys[i], <span class="keyword" style="font-weight: bold;">null</span>);
          System.out.println(prefs.get(keys[i], <span class="keyword" style="font-weight: bold;">null</span>));
        }
        StartBurp.main(args);
    }
    <span class="keyword" style="font-weight: bold;">catch</span> (Exception e)
    {
      JOptionPane.showMessageDialog(<span class="keyword" style="font-weight: bold;">null</span>, <span class="string" style="color: #dd1144;">"This program can only run with burpsuite_pro_v1.5.01.jar"</span>, <span class="string" style="color: #dd1144;">"Notice"</span>,<span class="number" style="color: #009999;">0</span>);
    }
  }
}

其效果如截图所示

 


 

其中前8行输出为之前BurpLoader恶意修改的目标原值(对我的计算机而言),同一台设备运行多少遍都是不变的,后面的key由于我之前运行过BurpLoader因此是恶意修改后的值(但是由于前8行没有修改因此不能通过Burpsuite验证),可见BurpLoader其实是使用了同一个密钥来注册所有不同计算机的,只不过修改并固定了某些参与密钥计算的环境变量而已,这大概就是Burpsuite破解的主要思路了,至于最初能用的license是怎么计算出来的,我们以后再研究

时间: 2024-08-14 02:44:44

JAVA的逆向和反混淆 追查Burpsuite的破解原理详解的相关文章

JAVA逆向&amp;反混淆-追查Burpsuite的破解原理(转)

0x00 摘要: 本系列文章通过对BurpLoader的几个版本的逆向分析,分析Burpsuite的破解原理,分析Burpsuite认证体系存在的安全漏洞. 0x01 JD-GUI的用途与缺陷: JD-GUI是一款从JAVA字节码中还原JAVA源代码的免费工具,一般情况下使用这款工具做JAVA逆向就足够了,但是由于其原理是从JAVA字节码中按照特定结构来还原对应的JAVA源代码,因此一旦字节码结构被打乱(比如说使用混淆器),那么JD-GUI就会失去它的作用,如图为使用JD-GUI打开Burpsu

java RMI原理详解

[本文转载自java RMI原理详解] 定义 RMI(Remote Method Invocation)为远程方法调用,是允许运行在一个Java虚拟机的对象调用运行在另一个Java虚拟机上的对象的方法. 这两个虚拟机可以是运行在相同计算机上的不同进程中,也可以是运行在网络上的不同计算机中. Java RMI:Java远程方法调用,即Java RMI(Java Remote Method Invocation)是Java编程语言里,一种用于实现远程过程调用的应用程序编程接口.它使客户机上运行的程序

Java Spring MVC 上传下载文件配置及controller方法详解_java

下载: 1.在spring-mvc中配置(用于100M以下的文件下载) <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"> <property name="messageConverters"> <list> <!--配置下载返回类型--> <bean class="or

Java中静态类型检查是如何进行的实例思路详解_java

以下内容来自维基百科,关于静态类型检查和动态类型检查的解释: •静态类型检查:基于程序的源代码来验证类型安全的过程: •动态类型检查:在程序运行期间验证类型安全的过程: Java使用静态类型检查在编译期间分析程序,确保没有类型错误.基本的思想是不要让类型错误在运行期间发生. 以下代码是一个例子,理解了他,你会更好的理解Java静态类型检查是如何工作的. 代码示例 假定我们有如下类,A和B,B继承A. class A { A me() { return this; } public void do

Java中的instanceof关键字在Android中的用法实例详解_java

在下面介绍Android中如何使用instanceof关键字开发更方便时,先来温习一下java中instanceof的概念. instanceof大部分的概念是这样定义的:instanceof是Java的一个二元操作符,和==,>,<是同一类东西.由于它是由字母组成的,所以也是Java的保留关键字.它的作用是测试它左边的对象是否是它右边的类的实例,返回boolean类型的数据.举个栗子: String s = "I AM an Object!"; boolean isObj

Java 高并发十: JDK8对并发的新支持详解_java

1. LongAdder 和AtomicLong类似的使用方式,但是性能比AtomicLong更好. LongAdder与AtomicLong都是使用了原子操作来提高性能.但是LongAdder在AtomicLong的基础上进行了热点分离,热点分离类似于有锁操作中的减小锁粒度,将一个锁分离成若干个锁来提高性能.在无锁中,也可以用类似的方式来增加CAS的成功率,从而提高性能. LongAdder原理图: AtomicLong的实现方式是内部有个value 变量,当多线程并发自增,自减时,均通过CA

Java中的日期和时间类以及Calendar类用法详解_java

Java日期和时间类简介 Java 的日期和时间类位于 java.util 包中.利用日期时间类提供的方法,可以获取当前的日期和时间,创建日期和时间参数,计算和比较时间. Date 类 Date 类是 Java 中的日期时间类,其构造方法比较多,下面是常用的两个: Date():使用当前的日期和时间初始化一个对象. Date(long millisec):从1970年01月01日00时(格林威治时间)开始以毫秒计算时间,计算 millisec 毫秒.如果运行 Java 程序的本地时区是北京时区(

Java多线程编程中使用Condition类操作锁的方法详解_java

Condition的作用是对锁进行更精确的控制.Condition中的await()方法相当于Object的wait()方法,Condition中的signal()方法相当于Object的notify()方法,Condition中的signalAll()相当于Object的notifyAll()方法.不同的是,Object中的wait(),notify(),notifyAll()方法是和"同步锁"(synchronized关键字)捆绑使用的:而Condition是需要与"互斥

Java中统计字符个数以及反序非相同字符的方法详解_java

复制代码 代码如下: import java.util.ArrayList;import java.util.List;public class Test2 { /**  * @param args  */ public static void main(String[] args) {  String src = "A B C D E B C";  //替换掉空格  src = src.replaceAll(" ", "") ;   Syste