Cracking obfuscated Java Code – Adwind 3

Introduction

As the title suggestes, this post will feature a practical example of cracking obfuscated Java code, namely Allatori 4.7. For the sake of example I’ve chosen an astonishing Java RAT (Remote ‘Administration’ Tool) worth every of the $100 It got sold for, but I will come back to this later. To start, let’s compare the usual workflow of cracking Java programs and then find out why it won’t work for obfuscated code.The ‘usual’ way to crack Java programs is by decompiling, modifying and recompiling the source code.  To understand what this means you need to know that Java source code, unlike e.g. C++,  doesn’t  get directly translated to low-level machine-code interpreted by the processor. Instead, it  gets distributed in a more abstracted code set called java bytecode. This code then gets interpreted by a Java Virtual Machine similar to the .NET Common Language Runtime (CLR)  on execution.
This offers the ability to distribute programs in a platform independent format (only the VM needs to get adjusted), but as everything, it comes with a downside. Since bytecode has such an abstracted, diverse Instruction set it turns out to be vulnerable to so-called  decompiling, which basically means ‘reversing’ the process of compilation by guessing how the original source code might have looked like. Since this is a massive problem of not only application security but also intellectual property there are some efforts to prevent this by obfuscating the code in different ways.
One of the results of these efforts is Alatori, a commercial obfuscator starting at $290. It comes with all the features you would except from a commercial obfuscator, most important for us string obfuscation, flow obfuscation and inserting ‘invalid’ bytecode (bytcode ignored by the VM but crashing/confusing known decompilers/deobfuscators).

Adwind 3 – Overview

Adwind 3 is a Java based RAT supporting not only common desktop operation systems but also Android. It originated from a spain Proof of Concept called Frutas and got recently rebranded and sold as UNRECOM. It’s protected both by a custom login system with serials and hardware identification. Adwind is using the Swing API to draw its GUI. This means that it defines some user input elements (buttons, text boxes, drop downs..) which offer user triggered ‘ActionEvents’ (button click..)  and connects them to ActionListenern performing the desired action/method. Our first goal is the ‘Login’ button of the window below.

adwind_login1 adwind_login2

Adwind gets distributed as an executable Java Archive (JAR) containing not only the bytecode in form of .class files corresponding to the different Java classes but also resources like images and sounds and a Manifest file pointing to the main class function (the one which get’s called on execution of the Jar).

Manifest-Version: 1.0
Ant-Version: Apache Ant 1.9.1
Created-By: 1.7.0_25-b17 (Oracle Corporation)
Class-Path: lib/JTattoo-1.6.9.jar
X-COMMENT: Main-Class will be added automatically by build
Main-Class: cliente.Login

When trying to decompile this main class using JD-GUI, we get just get an ‘// INTERNAL ERROR //‘ instead of the class functions. Of course we could look for a way to fix this, either by hunting down the locations causing them and either patching the decompiler or the class file. But even then, obfuscation techniques like flow obfuscation would result into a source code not really usable.

Messing with bytecode

Today I want to show you another approach – why going through the whole mess of decompiling and recompiling of we could patch the code directly? There are several bytecode editors to make this easier, my favorite one being dirtyJOE, which is written in C++ and supports python-scripts to perform different manipulations. Since dirtyJOE can’t handle Jars we have to unpack the .class file in question first using any zip-utility.  You should really pay attention at this point since zip-archives support case-sensitive filenames (for example cG.class, cg.class), but some operating systems like windows don’t. Since Allatori is utilizing this, it may lead to class files getting overwritten without any notice when unpacking the whole archive to one directory.

This looks better.

This looks better.

dirtyJOE welcomes us with some basic information about the class if available, for example it’s parent/super-class, the number of constants (constant_pool_count) and methods. constants are basically all hardcoded references to other functions and strings used by the bytecode. You can find out more about the ClassFile structure here: http://www.javaworld.com/article/2077233/core-java/bytecode-basics.html.

If we switch to the ‘Methods’-tab, we directly see the main-Method – but before digging into it, we should take a short break – Before using any method of a class we need to initialize it to an object, and so does the JVM. In fact, it even needs to initialize the class. This is happening by invoking (object initialization) and (class initialization).

In our case, nothing spectacular is happening in , since the Login class inherits by ‘javax/swing/JFrame’ (User Interface) it initializes the basic Login form and creates the basic form elements:

 00000000 : new                 javax.swing.JComboBox
 00000003 : dup
 00000004 : invokespecial       void javax.swing.JComboBox.()
 00000007 : putstatic           javax.swing.JComboBox cliente.Login.listalicencias
 0000000A : new                 javax.swing.JPasswordField
 0000000D : dup
 0000000E : invokespecial       void javax.swing.JPasswordField.()
 00000011 : putstatic           javax.swing.JPasswordField cliente.Login.password
 00000014 : new                 javax.swing.JTextField
 00000017 : dup
 00000018 : invokespecial       void javax.swing.JTextField.()
 0000001B : putstatic           javax.swing.JTextField cliente.Login.user
 0000001E : return

To understand what’s going on, you need a basic understanding of the bytecode instruction set and how it gets interpreted. Going through all of this would exceed the scope of this post, and there are people explaining it way better than I could: http://www.javaworld.com/article/2077233/core-java/bytecode-basics.html

This isn’t the button you’re looking for

With the above basics it should be relatively easy to guess what’s going on. Basically, four Swing GUI  elements get created, initialized and its references stored in the corresponding fields of the Login-class.

  • javax.swing.JComboBox -> cliente.Login.listalicencias ( drop down list of available licenses)
  • javax.swing.JTextField -> cliente.Login.user (username text-box)
  • javax.swing.JPasswordField -> cliente.Login.password (masked password text-box)

But hey! Those aren’t all fields, are they? Where are all those buttons, graphics – where is the Login-button we’re looking for?  Let’s check the <init> method:

00000000 : aconst_null
00000001 : aload_0
00000002 : dup
00000003 : dup_x2
00000004 : aload_0
00000005 : invokespecial       void javax.swing.JFrame.()
00000008 : invokespecial       void cliente.Login.B()
0000000B : invokespecial       void cliente.Login.ALLATORI_DEMO()
0000000E : invokevirtual       void cliente.Login.setLocationRelativeTo(java.awt.Component)
00000011 : return

At first glance, this doesn’t  look way better, only a single ‘javax.swing’ is in sight. But let’s take a closer look and figure out what’s going on.

If you don’t know the meaning of an instruction, you can look ut up in Oracles JVM Instruction Set Reference

First, null gets pushed on the stack (aconst_null). Then aload_0 get’s executed. This loads the first (pos 00) local variable onto the stack, which is always a reference to the object itself, similar to the .this directive in Java.The top value of the stack gets duplicated (dup) and the new top-value gets duplicated two values down the stack. Finally, the object reference get’s loaded another time. At this point, the stack should look like this:

this, this, this, this, null

220px-Lifo.svg
You could theoretically get the same result by doing 1 aconst_0 and four aload_0 s, but that would be way to simple!  Those this references are then used to invokespecial (call) 4 methods and in the end null gets returned – Remember the stack is based on the LIFO (last in, first out) principle. All of those functions are void methods, which means that they don’t return anything to the stack. At this pont, you also see another annoying feature of Allatori, the renaming of foreign methods and classes –  B() doesn’t ring any bells, does it? And it gets even worse… If we look for the B() method, we notice that there is not just one, but three of them! This is possible because the JVM allows methods of the same name with different descriptors. Those are the three B() method in question:

  • static synthetic void B(cliente.Login, java.awt.event.ActionEvent)
  • private synthetic void B()
  • private synthetic void B(java.awt.event.ActionEvent)

Since we know that no parameters got pushed to the B() method when it was called above we have to look at the second method, void B(). I won’t post all 381 lines but point out some important aspects.

0000004E : new                 javax.swing.JButton
00000051 : dup
00000052 : invokespecial       void javax.swing.JButton.()
00000055 : putfield            javax.swing.JButton cliente.Login.g
00000058 : new                 javax.swing.JPanel

Aha! A button. This leads us in the right direction, but since there are multiple buttons and ‘cliente.Login.g’ isn’t telling us much, we need to look for another way to identify it. How about the text of the button shown to the user? It has to get set somewhere, does it?

[...]
00000052 : invokespecial       void javax.swing.JButton.()
00000055 : putfield            javax.swing.JButton cliente.Login.g
00000058 : new                 javax.swing.JPanel
[...]
00000105 : getfield            javax.swing.JButton cliente.Login.g
00000108 : ldc_w               " F0@9"
0000010B : invokestatic        java.lang.String Extras.MD5.ALLATORI_DEMO(java.lang.String)
0000010E : invokevirtual       void javax.swing.JButton.setText(java.lang.String)
00000111 : getfield            javax.swing.JButton cliente.Login.g

String deobfuscation – the lazy way

Well, this part definitively sets some text on the button, and it also loads a string, but the string looks kinda strange, and there is another function in between of the loading of the string (ldc_w ” F0@9″ and the text-setting (invokevirtual .setText(java.lang.String) which also takes a string as its parameter. This means that the mysterious ALLATORI_DEMO(String) function probably takes ‘garbage’ as input and returns a more useful string which then gets used to perform whatever operation. In Java, it might look like this:

g.setText(Extras.MD5.ALLATORI_DEMO(" F0@9"));

This happens for every single usage of strings, only with different methods, which means that we just figured out where strings get deobfuscated. The way Allatori deobfuscates strings got analyzed before, actually it’s pretty simple. The method iterates through the strings characters backwards and performs XOR and AND bit-operations on them – the result gets stored in a array which then gets converted and returned as a string. Due to it’s nature, it’s possible to revert the obfuscation by applying the same method again.  However the program which we are looking at is using Allatori 4.7, and here things changed a little. The actual obfuscation is more or less the same, but the key used in it is not simply pushed to the stack but calculated using different stack operations. This, and the fact that Allatori 4.7 uses multiple string obfuscation methods (with different keys) in the same method make it harder to deobfuscate the program by going through all strings automatically.

But there is a good message – we don’t even need to get our hands dirty with all of this! Since class file functions can easily used in other Java programs and Allatori didn’t bother to include some asymmetric encryption method we can import the class file (or the whole Jar) to a new Java program and call the method like any other to both de- and encrypt strings.

System.out.println(Extras.MD5.ALLATORI_DEMO("F0@9")); -> Login
System.out.println(Extras.MD5.ALLATORI_DEMO("Login")); ->  F0@9

Because some encrypted strings contain line breaks and Java doesn’t support raw strings you might encounter some problems – you can partially fix this by enabling automated escaping when pasting text between string literals in Eclipse (Preferences -> Java -> Editor -> Typing.
As we can see thanks to the deobfuscated string, the button text gets indeed set to ‘Login’. Now we just have to find out which EventHandler get’s associated to it.

00000111 : getfield            javax.swing.JButton cliente.Login.g
00000114 : new                 cliente.i
00000117 : dup
00000118 : aload_0
00000119 : invokespecial       void cliente.i.(cliente.Login)
0000011C : invokevirtual       void javax.swing.JButton.addActionListener(java.awt.event.ActionListener)

Here we go.  A new instance of class i gets created, initialized and ‘added’ to the JButton as an ActionListener. When the button gets clicked, the instances method ‘ActionPerformed’ gets called…

00000000 : aload_0
00000001 : getfield            cliente.Login cliente.i.ALLATORI_DEMO
00000004 : aload_1
00000005 : invokestatic        void cliente.Login.ALLATORI_DEMO(cliente.Login, java.awt.event.ActionEvent)
00000008 : return

..which calls ALLATORI_DEMO(cliente.Login, java.awt.event.ActionEvent) in our Login-Class…

00000000 : aload_0
00000001 : aload_1
00000002 : invokespecial       void cliente.Login.B(java.awt.event.ActionEvent)
00000005 : return

which finally calls cliente.Login.B(ActionEvent). What a ride!

// check if Username-Textfield is empty
00000000 : getstatic           javax.swing.JTextField cliente.Login.user 
00000003 : invokevirtual       java.lang.String javax.swing.JTextField.getText()
00000006 : invokevirtual       boolean java.lang.String.isEmpty() // returns boolean 
00000009 : ifeq                pos.0000000E // if result is 0 (Field is NOT empty -> jump
0000000C : return // avoid
0000000D : pop
// check if Password-Field is empty
0000000E : new                 java.lang.String // create new string
00000011 : dup
00000012 : getstatic           javax.swing.JPasswordField cliente.Login.password
00000015 : invokevirtual       char[] javax.swing.JPasswordField.getPassword() 
00000018 : invokespecial       void java.lang.String.(char[])
0000001B : invokevirtual       boolean java.lang.String.isEmpty()
0000001E : ifeq                pos.00000023
00000021 : return // avoid again
// check if any license got selected in ComboBox (Index is not -1 / m1
00000022 : iconst_0
00000023 : getstatic           javax.swing.JComboBox cliente.Login.listalicencias
00000026 : invokevirtual       int javax.swing.JComboBox.getSelectedIndex()
00000029 : iconst_ml
0000002A : if_icmpne           pos.0000002E
0000002D : return // meh.
0000002E : aload_0 // :)
// 'hide' the login-window and call Adwind.main(null) -> the main window?
0000002F : invokevirtual       void cliente.Login.dispose()
00000032 : aconst_null
00000033 : invokestatic        void cliente.Adwind.main(java.lang.String[])
00000036 : return

I already made some comments so it should be pretty clear what’s going on here – some checks if there is any input – but no _real_ checks, no web request. If we patch the return above the main(null)call  (triggered by no license being selected) to nop (do nothing / 00), run the program and enter some garbage, the progress calls the main method and then closes. This means that the login/license check is not in the Login-class at all but in another castle, somewhere in that / or main() method of Adwind.main

Another castle

I will cut things down a bit at this point since  finding the method isn’t really anything new – after some minutes you should have found an Adwind.B() – call at the top of Adwind.<init> which begins with this lovely VM-Check..

00000000 : invokestatic        boolean Extras.Utils.isVMWARE()
00000003 : ifne                pos.0000000C                        // jump if isVMWARE == 1
00000006 : invokestatic        boolean Extras.Utils.isVMWARE()
00000009 : ifeq                pos.00000010                        // jump if isVMWARE == 0

0000000C : iconst_ml
0000000D : invokestatic        void java.lang.System.exit(int)    // we don't want to land here

..and also the reason why Adwind closed without any warning when entering wrong credentials:

// Store InetAdress "65.99.225.111" in var1
00000033 : ldc_w               "0<+7+4"
00000036 : invokestatic        java.lang.String cliente.NoIpService.ALLATORI_DEMO(java.lang.String)
00000039 : invokestatic        java.net.InetAddress java.net.InetAddress.getByName(java.lang.String)
0000003C : astore_1
// Store InetAdress "adwind.com.mx" -> IP in var 2
0000003D : ldc_w               "S(E%\(/]!!J"
00000040 : invokestatic        java.lang.String plugins.CargadorPlugins.ALLATORI_DEMO(java.lang.String)
00000043 : invokestatic        java.net.InetAddress java.net.InetAddress.getByName(java.lang.String)
00000046 : astore_2
// Load and compare those InetAdresses
00000047 : aload_1
00000048 : aload_2
00000049 : invokevirtual       boolean java.net.InetAddress.equals(java.lang.Object)
0000004C : ifeq                pos.00000060    // if not equal (If adwind.com.mx doesn't resolve to 65.99.225.111) jump

As you can see, the method checks if ‘adwind.com.mx’ resolves to a hardcoded IP adress, probably to detect DNS-manipulation. Since the domain isn’t up anymore, the request will fail, even for valid customers (great!)
The method also generates a kind of hwid and issues a web request to the login server using a horrible case switch taking about 200 lines, but I will spare you that. Instead, why don’t we just try to nop the invoke of B() in the first place? Since it only checks conditions to exit, this actually works. If we also change the Main-Class in the Jar-manifest, we can even skip the whole login-window.adwin_main

http://www.mediafire.com/download/bzwv870uovbujci/Adwind3.zip

PS: Found this in the Login.main class:

0000000C    ldc_w               "s9V%F#@5q9W?<^-K��[?F"
                                // AuditoryCues.playlist   <- Java Swing-Ebook CopyPasta
0000000F    invokestatic        java.lang.String plugins.CargadorPlugins.ALLATORI_DEMO(java.lang.String)
00000012    ldc_w               "h"M>]8[.j"L$6E;h"M>]8[.j"L$"
                                // AuditoryCues.allAuditoryCues        <- Srsly? Why I am even cracking this...
// initialize java swing UI-Manager in an absolute innovative way
00000015    invokestatic        java.lang.String Extras.MD5.ALLATORI_DEMO(java.lang.String)
00000018    invokestatic        java.lang.Object javax.swing.UIManager.get(java.lang.Object)
0000001B    invokestatic        java.lang.Object javax.swing.UIManager.put(java.lang.Object, java.lang.Object)

You might wonder what’s the connection of Auditory, a music editor and Adwind, a ‘Premium’RAT. Answer: Complete Copy Pasta.
http://www.ibm.com/developerworks/library/j-mer0730/

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s