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.
- Java SDK en Windows: Puedes descargarlo de aquí: http://bit.ly/4995-java. Luego debes añadir el directorio bin de su instalación al environment variable PATH. Start->Control Panel->System and Security -> System -> Advanced System Settings -> Environment Variables.
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:
-
El programa queda instalado por defecto en C:\PasswordVaultTrialJava.
-
Si deseas correr el ejecutable desde el command line usa:
java -jar PasswordVault.jar
-
La clase ‘principal’ de este program es la clase
PasswordVault
, incluida enPasswordVault.class
. -
Los archivos tipo
jar
son en realidad directorios comprimidos que contienen los archivos.class
de un programa. Si quieres descomprimir usa:jar xf jar-file
. El resultado de la descompilación cae en un directorio llamadoinfo
. -
JD-GUI
permite decompilar archivosjar
. Una vez descompilasJD-GUI
permite los archivos.java
descompilados en una estructura de directorios que luego permitirá recrear eljar
. Guarda los files, entra al directorio y cambia el contenido del archivoPasswordVault.java
para que permita guardar más de 5 records.
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.