Ingeniería inversa de software

Reversando a java y C#

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 la 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 (desde cygwin) para que corrobores el tipo de archivo que es. Según el comando file, ¿qué tipo de archivo es?

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 comando para desensamblar es javap. Le pasaremos varias opciones para que nos devuelva toda la información que pueda. Haz

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

y observa el resultado. Compara el programa inicial con el desensamblado. El “Constant Pool” es parecido al “symbol table” que hemos estudiado. 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;
 System.out.println(a + b);

Compila, corre y desensambla. Muestra las instrucciones que realizan la asignación de valores a y b 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 8 en vez de 7. Borra el archivo HelloWorld.java para que no este 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 FrontEndPlus. Lo puedes descargar aquí: http://bit.ly/4995-fend. Usaremos el FrontEndPlusV1R04. Instálalo y haz File -> BatchDecompile y escoge HelloWorld.class. 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 haremos reversing a un programa un poco más complejo. Fue creado como ejercicio 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 de reversing 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 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 en PasswordVault.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 es puesto en un directorio con nombre info.

  • FrontEndPlusV1R04 permite decompilar múlitples archivos .class a la vez usando la opción File -> Batch Decompile.

Una vez hayas cambiado las instrucciones necesarias en el código descompilado, guardalo como __.java. Debes guardarlo en el mismo directorio donde están los archivos .class y con el mismo nombre que tiene su .class. Por ejemplo, si el archivo que modificaste fue el descompilado de PasswordVault.class debes guardarlo como PasswordVault.java. Para compilarlo debes estar en el directorio superior a info y:

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

Luego para rehacer el jar file

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://bit.ly/4995-jar y hazle reverse engineering para determinar el username y password que requiere.

Descarga el archivo en http://bit.ly/4995-jarobf. Este archivo es el resultado de pasar el jar anterior por un bytecode obfuscator llamado Sandmark (http://sandmark.cs.arizona.edu/). ¿Trata de identificar. 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.