Decompiler output comparison

December 20, 2012

JEB is much more than just a Dalvik decompiler and disassembler. The interactivity and flexibility it provides are equally important for reverse engineers. However, you will be interested to see how JEB's decompiler output compares to that of the current standard tools.

This page shows how our decompiler performs versus the two most common combination of tools: dex2jar+JD-GUI and dex2jar+JAD.

Note: JD and JAD being Java class files decompilers, a prerequisite step to show you their output is to convert the Java bytecode to Dalvik bytecode. This step can be destructive, both for bytecode data and metadata. It is also extremely easy to write Dalvik bytecode that will crash dex2jar. One such example of obfuscated code is presented later in this page.

This document is divided into three parts:

  • A comparison of decompiled Java methods implementing various well-known algorithms (RC4, Quicksort, Eratosthenes, LZSS.)
  • A real-world, entire class decompilation output (from the Android Support Framework package.)
  • A highly-obfuscated piece of code. Only JEB output is presented as the other tools simply fail and cannot handle the file.

In these three areas, JEB clearly outperforms its competitors.

In all four cases below, JEB gives almost perfect source code. JD-GUI and JAD, particularly the latter, produce incorrect, overly complex and difficult to read decompiled code.

RC4 encryption

A ubiquitous, standard stream cipher. Algorithmically, nothing fancy: three loops and a couple of bit-wise arithmetic operations.


Quicksort

The most famous divide-and-conquer sorting algorithm. The presence of nested loops and recursion calls makes it algorithmically more complex than RC4.


Eratosthenes sieve

One of the most basic way to generate prime numbers. A standard implementation is based on loops, nested loops, conditionals, arrays and so on.


LZSS decompression

Improved upon LZ77, this Lempel-Ziv compression scheme is the basis of many more compression algorithms. Algorithmically, it is at par with the prime number generator presented above.

Here, we show the decompiled output for android.support.v4.app.BackStackRecord, one of the big classes of the Android Support framework.

As you can see, the code produced by JD and JAD does not appear as unreadable as for the examples above. Still, many routines are incorrectly or poorly rendered by those tools, especially when they contain nested complex loops.

As previously shown, JEB is doing very well. For an example of that, see the method popFromBackStack().


(For enhanced clarity, here are the individual output files : JEB / JD-GUI / JAD)

Obfuscated code and/or modified+repackaged apps are likely to break DEX-to-JAR converters. It is the case here:

$ dex2jar obfuscated.dex
dex2jar obfuscated.dex -> obfuscated-dex2jar.jar
com.googlecode.dex2jar.DexException: while accept method:[LA;.f(I)I]
        at com.googlecode.dex2jar.reader.DexFileReader.acceptMethod(DexFileReader.java:699)
        at com.googlecode.dex2jar.reader.DexFileReader.acceptClass(DexFileReader.java:446)
        at com.googlecode.dex2jar.reader.DexFileReader.accept(DexFileReader.java:328)
        at com.googlecode.dex2jar.v3.Dex2jar.doTranslate(Dex2jar.java:84)
        at com.googlecode.dex2jar.v3.Dex2jar.to(Dex2jar.java:239)
        at com.googlecode.dex2jar.v3.Dex2jar.to(Dex2jar.java:230)
        at com.googlecode.dex2jar.tools.Dex2jarCmd.doCommandLine(Dex2jarCmd.java:107)
        at com.googlecode.dex2jar.tools.BaseCmd.doMain(BaseCmd.java:168)
        at com.googlecode.dex2jar.tools.Dex2jarCmd.main(Dex2jarCmd.java:34)
Caused by: com.googlecode.dex2jar.DexException: while accept code in method:[LA;.f(I)I]
        at com.googlecode.dex2jar.reader.DexFileReader.acceptMethod(DexFileReader.java:689)
        ... 8 more
Caused by: java.lang.ArrayIndexOutOfBoundsException: 15
        at com.googlecode.dex2jar.v3.V3CodeAdapter.visitConstStmt(V3CodeAdapter.java:326)
        at com.googlecode.dex2jar.reader.DexOpcodeAdapter.x1n(DexOpcodeAdapter.java:262)
        at com.googlecode.dex2jar.reader.DexCodeReader.acceptInsn(DexCodeReader.java:411)
        at com.googlecode.dex2jar.reader.DexCodeReader.accept(DexCodeReader.java:330)
        at com.googlecode.dex2jar.reader.DexFileReader.acceptMethod(DexFileReader.java:686)
        ... 8 more
        

For this reason, we can only present JEB's output.

This example is a very simple piece of code that was run through a flow obfuscator and junk insertion program.
JEB reconstructs the flow and removes junk code, giving simplified Java source.

.method public f(I)I
          const/4             v15, 0x7
          move/from16         v6, v15
          move                v7, v15
          and-int/lit8        v6, v15, 0x4
          move/from16         v9, v7
          const               v14, 0x5
          add-int             v9, v9, v15
          mul-int/2addr       v7, v9
          const               v7, 0x5
          sub-int/2addr       v9, v6
          move                v11, v7
          move/16             v13, v6
          goto/16             :180
:30
          return              v0
:36
          goto                :38
:38
          const               v7, 0x6
          move                v15, v7
          const/4             v8, 0x3
          move                v0, v2
          const               v15, 0x2
          const               v3, 0x1
          div-int/lit8        v3, v8, 0x3
          mul-int/lit8        v8, v3, 0x2
          div-int/lit8        v8, v3, 0x2
          div-int/2addr       v7, v8
          const/4             v5, 0x2
          and-int             v8, v8, v5
          const               v5, 0x6
          goto                :6C
:6C
          const               v9, 0x1
          const/16            v6, 0x2
          add-int/lit8        v9, v6, 0x2
          move/from16         v10, v9
          move/from16         v14, v10
          move/16             v5, v6
          const/16            v3, 0x0
          move/from16         v6, v9
          add-int             v6, v6, v14
          add-int/lit8        v0, v0, 0x1
          const               v14, 0x6
          rem-int             v5, v5, v6
          const/4             v10, 0x6
          shr-int/lit8        v10, v14, 0x5
          goto/16             :1EC
:AC
          const               v13, 0x2
          const/4             v9, 0x7
          const/4             v15, 0x1
          const/4             v10, 0x2
          rem-int/lit16       v9, v13, 0x7
          move/from16         v4, v15
          ushr-int/2addr      v15, v10
          const               v5, 0x6
          shl-int             v5, v5, v13
          div-int/lit8        v4, v5, 0x1
          const               v14, 0x6
          xor-int             v15, v15, v14
          const               v7, 0x6
          move                v15, v5
          if-ltz              v15, :12E
:E6
          const/16            v7, 0x4
          const/16            v4, 0x7
          move/16             v14, v5
          xor-int/lit16       v7, v14, 0x2
          move/from16         v9, v15
          div-int             v4, v4, v10
          move                v8, v15
          and-int             v9, v9, v4
          const/16            v4, 0x0
          move/from16         v14, v9
          const               v12, 0x1
          move/16             v4, v12
          mul-int/lit16       v14, v10, 0x0
          move                v7, v10
          div-int             v10, v10, v7
          or-int/lit16        v4, v8, 0x6
          mul-int             v5, v5, v15
          goto                :12E
:12E
          const               v3, 0x7
          move/16             v10, v3
          ushr-int/lit8       v10, v3, 0x0
          and-int/lit8        v10, v3, 0x0
          const/4             v4, 0x4
          xor-int/lit16       v3, v4, 0x3
          move/from16         v6, v4
          xor-int/lit8        v3, v6, 0x3
          sub-int             v10, v10, v4
          goto                :156
:156
          const               v4, 0x2
          const               v6, 0x4
          add-int/lit16       v6, v4, 0x6
          move                v11, v4
          const               v6, 0x5
          move/from16         v10, v11
          move/from16         v3, v4
          and-int             v10, v10, v4
          shr-int             v4, v4, v6
          goto                :1E2
:180
          const               v13, 0x0
          move/from16         v7, v13
          add-int/lit8        v13, v7, 0x1
          const/16            v11, 0x3
          div-int             v7, v7, v11
          xor-int             v13, v13, v11
          if-ge               v7, v13, :36
:19E
          xor-int/2addr       v7, v13
          move                v10, v13
          and-int/lit16       v11, v13, 0x6
          const               v7, 0x5
          const/16            v8, 0x6
          rsub-int            v10, v11, 0x6
          const               v13, 0x5
          const               v5, 0x0
          const               v5, 0x1
          and-int             v10, v10, v5
          rem-int/lit16       v11, v8, 0x7
          move/16             v4, v10
          move                v10, v13
          or-int/2addr        v7, v13
          rem-int/lit16       v5, v13, 0x0
          div-int/2addr       v10, v11
          goto/16             :36
:1E2
          const               v11, 0x1
          goto/16             :30
:1EC
          const               v8, 0x0
          move                v9, v8
          move/from16         v8, v9
          goto/16             :AC
.end method

This Dalvik listing appears unnaturally complex. JEB's deobfuscator chimes in and decompiles it to a simple:

// Decompiled by JEB v0.9.0 alpha

public int f(int arg2) {
    int v0 = arg2 + 1;
    return v0;
}

Try JEB for yourself, download a demo version.