Reversando a java

Los programas hechos en lenguajes de programación que compilan a bytecode, como Java y C#, son más fáciles de reversar que los que compilan a assembly de procesador (físico). Esto se debe en parte a que las arquitecturas para las cuales compilan, i.e. su virtual machine, tienden a ser menos complejas que la 80x86 o AMD64.

Si abrimos un ‘ejecutable’ de Java o C# notaremos que su contenido son instrucciones en binario para la máquina virtual correspondiente. Existen herramientas, algunas provistas por Oracle y Microsoft, que permiten desensamblar y hasta descompilar los ejecutables y generar archivos de código fuente casi iguales al código original.

Herramientas de software.

El mecanismo de compilación de java

Los proyectos de software de java se componen de múltiples archivos con extensión .java. Por si no lo has visto, aquí va un HelloWord.java:

public class HelloWorld {
 public static void main(String[] args) {
 // Prints "Hello, World" to the terminal window.
 System.out.println("Hello, World");
 }
}

Nota que java es object oriented “hasta el hueso”: aún el programa más simple consiste de la declaración de un clase (HelloWorld) y al menos un método (main). Para compilar, se utiliza el comando javac:

javac HelloWorld.java

Si no hay errores, se generará un archivo binario llamado HelloWorld.class. Puedes correr el comando file para que corrobores el tipo de archivo que es. Según el comando file , ¿qué tipo de archivo es HelloWorld.class?

Para ejecutar el archivo escribes:

java HelloWorld

sin la extensión .class ni .java. Esto correrá el bytecode en la máquina virtual de java y te mostrará los resultados.

El Software Development Kit de java incluye un desensamblador llamado javap. Vamos a usarlo para desensamblar a HelloWorld.class. Le especificaremos varias opciones para que nos devuelva toda la información que pueda:

javap -p -c -constants -verbose ./HelloWorld.class

Observa el resultado. Compara el programa inicial con el desensamblado. El “Constant Pool” es parecido al “symbol table” que hemos estudiado en los ELF y los PE's, i.e. sirve como un mapping de símbolos que comienzan con signo de número a datos o referencias de funciones.


¿Qué número del constant pool le corresponde al string “Hello, World”?


Las instrucciones del disassembly son presentadas usando un formato como el siguiente:

0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #3 // String Hello, World
5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: return

El primer número es el offset de esta instrucción a partir del comienzo del método. Le sigue el mnemónico de la instrucción y cualquier argumento (casi siempre uno de los símbolos del Constant Pool). Le sigue un comentario introducido por disassembler para ayudar al lector.


¿Qué hace la instrucción ldc? (busca en una referencia de instrucciones de assembly de java)


La máquina virtual de java utiliza un paradigma orientado a stacks para llevar a cabo su procesamiento. Esto significa que todo dato sobre el que se vaya a operar y todo resultado que se obtenga se pondrá en un stack. Si quieres sumar 3 + 4, debes poner 3 en el stack, 4 en el stack y luego invocar la función de suma. La función de suma hace dos pops, realiza la operación y coloca el resultado en el stack. Esto contrasta con la arquitectura 80x86 que utiliza el stack mayormente para llevar cuenta de la invocación de funciones y almacenar las variables locales de las funciones.

Añade las siguientes instrucciones a HelloWorld.java (dentro del método main):

 int a = 3;
 int b = 4;
 int c = 10;
 System.out.println(a + b * c);

Compila, corre y desensambla. Muestra las instrucciones que realizan la asignación de valores a, b, y c y las instrucciones que realizan la suma y la imprimen.


Supón que recibiste el archivo HelloWorld.class y deseas hacerle reverse engineering para que imprima 42 en vez de 43. Borra el archivo HelloWorld.java para que no ests tentado a pecar ¿Tendrías que recurrir a cambiar el binario de HelloWorld.class? Resulta que no! Dado la sencillez del virtual machine, las instrucciones que se generan en binario pueden ser utilizadas para descompilar el programa y obtener el código fuente casi como el original. Existen descompiladores para el commandline como JAD (http://varaneckas.com/jad/ ). Hoy usaremos un descompilador con GUI llamado JD-GUI disponible en http://jd.benow.ca/. Descarga la versión para tu sistema operativo e instalalá. Luego corre JD-GUI y abre HelloWorld.class para descompilarlo.

Boom! Ahí está el código descompilado, casi igual al código original.

Si haces cambios a ese código y lo guardas como HelloWorld.java es como si hubieras hecho un patching al programa original. Compila el HelloWorld.java que acabas de guardar. Córrelo y maravíllate al ver el programa patcheado.

Parte A - Reverse engineering y patching a una aplicación en java

Ahora analizaremos un programa un poco más complejo. Fue creado por Teodoro Cipresso como parte de su tesis de maestría en Reverse Engineering Education. Puedes descargar el código aquí: http://bit.ly/4995-jrev . El zip contiene un instalador del programa PasswordVaultTrial. La descripción del ejercicio es la siguiente:

Imagine that you have just implemented a Java version of a console application called “Password Vault” that helps computer users create and manage their passwords in a secure and convenient way. Before releasing a limited trial version of the application on your company’s Web site, you would like to understand how difficult it would be for a reverse engineer to circumvent a limitation in the trial version that exists to encourage purchases of the full version; the trial version of the application limits the number of password records that a user may create to five.

En otras palabras, debes encontrar como patchear el programa para que esquive la limitación del trial version y te deje guardar tantos records como gustes.

Hints:

Para compilarlo debes estar en el directorio superior a info y:

 javac .\info\reversingproject\passwordvault\PasswordVault.java

Luego para rehacer el jar file debes tener el jar file viejo en el directorio superior a info y hacer:

jar uvf PasswordVault.jar info\reversingproject\passwordvault

Corre de nuevo el programa y verifica que funciona como esperado.

Parte B - El efecto ofuscador

Descarga el archivo en http://rarceresearch.fun/misc/Login.jar y hazle reverse engineering para determinar el username y password que requiere.

Descarga el archivo en http://rarceresearch.fun/misc/LoginObf01.jar. Este archivo es el resultado de pasar el programa Login.jar por un bytecode obfuscator llamado Sandmark (http://sandmark.cs.arizona.edu/). Prueba a ver si el password que deduciste en el paso anterior sirve con este programa. Descoprime el jar y descompila usando FrontEnd. Observa el efecto que tuvo el ofuscador en el código descompilado. Explica al menos las comparaciones que realiza el código ofuscado para determinar la validez del primer y segundo caracter.