Análisis packer 01 - I

Tamaño de letra:

Introducción, presentación del packer 01

Hoy vamos a estudiar un packer que podríamos definir como complejo. Tiene bastantes dificultades y por este motivo voy a desglosarlo en varios "capítulos". Usaré distintas herramientas aunque principalmente el debugger OllyDBG. Dada su complejidad (algo que fascina porque si no no se aprende) tengo que obviar muchas cosas que considero básicas y que se tratan en tutoriales más básicos.

El packer es uno desconocido que me ha enviado un usuario de la Web de un programa propio gratuito y nos permite su desensamblaje porque quiere aprender a protegerlo mejor. Me dice que sólo se ejecuta durante 30 días sin limitaciones durante esos días.

En conjunto el programa está compilado con Delphi (posiblemente 6 o 7) y después está protegido con este packer. Está bien protegido ya que destruye complementamente el OEP, emula y ofusca funciones, traslada la IAT fuera del ejecutable, tiene stolen bytes. En fin una maravilla y más de un programa en Delphi, ya que veremos cómo construir la INIT TABLE. Digo maravilla porque aprendermos todos bastantes cosas (o eso espero).

Antes de instalarlo y ejecutarlo observo que el instalador ha sido realizado con InnoSetup así que como no quiero instalarlo extraigo todo con InnoSetup Unpacker. Observo el ejecutable (lo voy a llamar a partir de ahora protegido.exe) y veo que para que funcione se necesitan dos librerías que crea en su directorio. Antes de nada... normalmente antes de analizar el ejecutable y ejecutarlo me gusta hacer una copia de las modificaciones del registro y archivos que modifica el programa, así pues uso el programa Regshot y hago una instantánea antes de instalarlo y después, y me encuentro con que protegido.exe crea en el registro la siguiente clave:

HKU \ S-1-5-21-2105599253-1882590444-2130468898-1000 \ Software \ Protector \ DataEspecifica \ 123456BC4BB4B5B6

Es tan curiosa que es la que usa el programa para saber cuándo se ha instalado y así calcula los días de uso. Simplemente borrándola el programa vuelve a tener los 30 días. ¿Qué fácil verdad? Puedes crear un simple archivo .reg.

Como he dicho el packer es desconocido y ningún detector de packers lo reconoce pero realmente esto no me importa nada. Lo analizo estudiandolo por encima y después simplemente hago lo siguiente: lo ejecuto desde OllyDBG simplemente protegiéndo de la API IsDebuggerPresent, dumpeo el programa cargado en OllyDBG y con el dumpeado (que está hecho un destrozo) lo analizo con OllyDBG y con detectores de packers y veo que efectivamente ha sido compilado con Delphi. Este dumpeado también se puede analizar con E2A, con DEDE, con cualquier editor de recursos y nos da muchísima información. Aunque ya sabemos resetear el tiempo, me propongo desempacar este packer tan curioso que ha realizado nuestro amigo.

Parte I: Localizando el OEP

Los programas compilados en Delphi tienen una particularidad y es que si examinas el OEP la primera API que es llamada es GetModuleHandleA pasándole como parámetro un cero (NULL). Por ejemplo este es el OEP de un programa compilado en Delphi 7:

00483DB0 > push ebp ; OEP - Delphi 7
00483DB1 . mov ebp,esp
00483DB3 . add esp,-10
00483DB6 . push ebx
00483DB7 . mov eax,483B70
00483DBC . call 0040645C ; Primera call...

Ahora entro en esa primera call:

0040645C /$ push ebx
0040645D |. mov ebx,eax
0040645F |. xor eax,eax
00406461 |. mov dword ptr ds:[4840A0],eax
00406466 |. push 0
00406468 |. call 00406398 ; <jmp.&kernel32.GetModuleHandleA>
...

Y esto es así en Delphi 3, 3.02, 6, 7, 2010. En otras versiones yo no lo he probado pero seguramente sea igual. Volvamos a nuestro programa. Localizar esta llamada a la API en el programa "protegido.exe" es muy sencillo: en OllyDBG nos vamos a la API GetModuleHandleA y ponemos un Breakpoint condicional (Shift+F2), tal como se muestra a continuación:

BP condicional

Y pulsamos simplemente F9. Lo que quiero que se entienda es saber dónde nos encontramos en el programa "protegido.exe" parado ahí. Estamos en la llamada a GetModuleHandleA después del OEP, por lo tanto, el OEP se encuentra antes. Si donde estoy parado vuelvo al código del programa veo lo siguiente:

00BB3910 push eax
00BB3911 push ecx
00BB3912 push edx
00BB3913 mov eax,esp
00BB3915 mov ecx,13
00BB391A mov edx,dword ptr ds:[eax]
00BB391C add edx,14FB14FB
00BB3922 test edx,edx
...

No es posible verificar de dónde viene el código. La dirección 00BB3910 que ves se crea en ejecución. Analizo un poquito por encima y pienso que el packer ha modificado los primeros bytes del programa, esto es: que tiene stolen bytes.

Después de analizar el packer en su conjunto y hacer pruebas y pruebas descubro una forma muy sencilla de llegar al OEP. Y es la siguiente: Reinicio OllyDBG, pongo directamente un BP en GetModuleHandleA cuento las veces que pasa por aquí antes de llegar al que acabo de mostrar. Veo que son 17 veces hasta la última, así que me paro en la décimo sexta y examino la pila y veo lo siguiente:

 

Pila_OllyDBG

 

A simple vista no se ve nada, pero el packer pushea direcciones en la pila que luego necesitará y donde tengo el cursor es una. Así que pongo un BP en la dirección 00B998AC. Pulso F9:

00B998AC push 5C28CA25
00B998B1 push 1EBC
00B998B6 push 0A9EC
00B998BB push 30000
00B998C0 push dword ptr ds:[BB94D4]
00B998C6 call 00B96B94
00B998CB xor dword ptr ss:[esp],eax
00B998CE mov eax,dword ptr ds:[BB94D4]
00B998D4 add dword ptr ss:[esp],eax
00B998D7 retn

Paso el retn:

Codigo 01

Entro con F7 en la call donde está el cursor (entro con F7 porque si lo hago con F8 el programa se ejecuta y se que estoy cerca del OEP):

00BAC084 add esp,4
00BAC087 call 00B96B94
00BAC08C call 00BAC092
00BAC091 >add dword ptr ds:[ebx+43104C4],1E824

Paso con F8 la primera call (00B96B94) y las siguientes con F7 (observa que la segunda call no lo es realmente, mira la dirección a la que va) hasta llegar a un ret. Nada más pasar el retn quedo aquí:

00BAAE98 call 00BAACE0 ; protegido.00BAACE0
00BAAE9D retn

Entro con F7 en la call 00BAACE0 y me encuentro con lo siguiente:

Codigo 02

Esta subrutina es muy sencilla de analizar y puedes pasarla con F8 hasta casi el final donde verás algo parecido a esto:

Codigo 03

Entro en esa call donde estoy parado y encuentro con este código tan ofuscado:

Codigo 04

Simplemente traceo con F7 hasta llegar un cercano retn:

00BA9EE0 sub eax,C6403BD3
00BA9EE6 lea eax,dword ptr ds:[ecx+1B903DE]
00BA9EED sub eax,ecx
00BA9EEF push eax
00BA9EF0 adc eax,413931FC
00BA9EF6 retn

Llego al retn de 00BA9EF6 y lo paso y llego aquí:

OEP

Bueno, pues ese es el OEP. El OEP está en una dirección que crea el packer. Éste mediante código ofuscado y usando posiblemente una VM ha emulado los stolen bytes y ahora se dispone a ejecutarlos. Todavía no se ha ejecutado ninguna instrucción del programa, hay que entender dónde nos encontramos. Podemos hacer una cosa y es tracear con F7 creando un log y poniendo una condición, por ejemplo que el eip esté entre 400000 y 500000. Para hacer esto comentado:

  • Pulsar Ctr+T -> Condition to pause run trace -> EIP is in range 400000 - 500000
  • Nos vamos al menú View -> Run trace
  • Botón derecho -> Log to file y llamo al archivo: Stolen bytes.txt
  • Trace into (Ctr+F11)

El programa para aquí:

00407ED4 call 01BB0000
00407ED9 db 0C
00407EDA mov eax,eax
00407EDC jmp near dword ptr ds:[76D354] ; kernel32.LocalAlloc
00407EE2 mov eax,eax
00407EE4 jmp near dword ptr ds:[76D350] ; kernel32.TlsGetValue
00407EEA mov eax,eax
00407EEC jmp near dword ptr ds:[76D34C] ; kernel32.TlsSetValue
00407EF2 mov eax,eax

Ahora cerramos el archivo (Close log file) y veremos que ahí tenemos los stolen bytes. Esta es la primera vez que el programa para en su código. Esta zona es muy común en un programa en Delphi y es justo la llamada a la API GetModuleHandleA, mira las dos imágenes: la primera del packer y la segunda de un Delphi 7 cualquiera que acaban de enviar:

 

GetModuleHandleA

 

Esto me hace pensar que esa call 01BB0000 lo que hace es emular a GetModuleHandleA, así que si simplemente redirijo el código a GetModuleHandleA en vez de 01BB0000 veo que el programa se ejecuta correctamente sin rechistar... Ya tenemos el OEP = 01B903DE que es lo que buscábamos en este tutorial.
Los stolen bytes los tenemos en el archivo "Stolen bytes.txt" y vemos que el salto a GetModuleHandleA también lo tenemos localizado en 407ED4 call 01BB0000. Lo tenemos todo.

En el siguiente tutorial veremos cómo analizar los Stolen bytes que pienso que es de las cosas más difíciles.

Edito: Después de analizar los Stolen bytes ya es seguro que se emulan mediante una M. Virtual. Es posible saber cuáles son los stolen bytes (lo mostraré, no es difícil ni tampoco fácil pero es muy entretenido) y los recuperaremos todos. Además de descubrir los stolen bytes emulados los pondremos en el lugar que le corresponde (soy demasiado perfeccionista ¿verdad?), sin embargo, aunque el siguiente tutorial iba a hablar de stolen bytes (que estoy analizando ahora mismo) no va a ser posible y voy a tener que solucionar primeramente la IAT y/o la INIT TABLE ya que para reparar los stolen bytes es necesario un dumpeado y no es cuestión de trabajar con un dumpeado destrozado. Esto es algo lógico, es mejor tener un dumpeado en condiciones. Nos vemos en breve...

Última actualización: Domingo, 20 Noviembre 2011

No tiene privilegios para responder a los comentarios.


 
Visitas: 8551112