Análisis Packer 01 - Parte II

Tamaño de letra:

Reparando la IAT - Introducción

En el anterior tutorial vimos cómo llegar al OEP, vimos la primera instrucción ejecutada en el código del programa que algunos suelen llamar supuesto OEP o Virtual OEP, yo prefiero nombrarlo como hemos visto: el salto a la API GetModuleHandleA.Ya adelanto que hace uso de una máquina virtual y veremos en tutoriales posteriores cómo resolver el stolen code, stolen bytes, redirecciones al packer, ofuscación y emulación de funciones. Sin embargo, vamos a centrarnos en este artículo en saber reparar la IAT, todavía no haremos un dumpeado con la IAT arreglada ya que antes repararé todas las cosas posibles que veremos en siguientes artículos (por ej. la INIT TABLE) y después sí que haremos un dumpeado.

Reconociendo la IAT

Como vimos en el artículo anterior, encontramos la primera instrucción ejecutada en el código de "protegido.exe" en la dirección 00407ED4 con una call 01B0000, como se puede apreciar en la siguiente imagen (ver la figura de arriba, la de abajo es un Delphi 7 cualquiera que puse antes como ejemplo):

Primera instrucción ejecutada

Ahora desde OllyDBG vamos a llegar ahí de nuevo poniendo simplemente un Hardware Breakpoint (HBP) en (0047ED4). Como se puede apreciar en un compilado en Delphi 7, estamos en un salto jmp a una API.Si dumpeamos por ejemplo la dirección 76D354 de la API LocalAlloc veremos lo siguiente (en OllyDBG en ventana dumpeado botón derecho -> long -> address):
...

...
0076D354 7C809A1D kernel32.LocalAlloc
0076D358 5DAEC475
0076D35C 00000000
0076D360 77DAD757 advapi32.RegSetValueExW
0076D364 77FA8834 SHLWAPI.77FA8834
0076D368 77DA6FEF advapi32.RegQueryValueExW
0076D36C 92DF4F46
0076D370 3C9D9A52
0076D374 77DA6A9F advapi32.RegOpenKeyExW
0076D378 29B2D066
0076D37C 249099DD
0076D380 77DB5196 advapi32.RegEnumKeyExA
0076D384 E4FBE891
0076D388 AAEF7361
...

Como puedes observar, la IAT está totalmente destrozada y lo que llama la atención a primera vista es que las direcciones que están mal no están redirigidas a ningún sitio, son valores sin sentido... observa la segunda linea del código anterior, esa dirección (5DAEC475) no existe, no puedo ir al mapa de memoria. Esto se corresponde con la función que vimos en la imagen anterior donde la llamada a GetModuleHandleA se hace a través de la call 01B0000. Esta call está ofuscada, usa una VM y finalmente llama a la API GetModuleHandleA,esto es una dificultad añadida ya que si observas, las direcciones de la IAT que están mal no hacen falta, pero ahora no voy a mostrar cómo reparar esas call 01B0000, voy a centrarme en la IAT. Si eres observador puedes intuir que en 76D358 seguramente vaya la API GetModuleHandleA.

Antes de nada vamos a ver el inicio de la IAT:

...
0076D24C 00000000
0076D250 00000000
0076D254 00000000
0076D258 7C92135A ntdll.RtlDeleteCriticalSection
...

Y el final parece ser:

...
0076DDD0 00000000
0076DDD4 77EFD7AE GDI32.GetRandomRgn
0076DDD8 00000000
0076DDDC 7C812B6E kernel32.GetVersionExA
0076DDE0 00000000
...

Por lo tanto ya tenemos que:

  • INICIO IAT: 0076D258
  • FIN IAT:0076DDE0
  • TAMAÑO IAT: B88 bytes
  • DIRECCIÓN VIRTUAL RELATIVA (RVA): 0036D258

Voy a poner un HBP (on write -> dword) en 76D354 que es una API buena, reinicio OllyDBG, observo cuándo se pone la dirección de la API en 76D354 y a partir de ahí a estudiar el código. El momento en que salta el HBP y pone la API en la IAT es aquí:

IAT en 76D354

Después de estudiar detenidamente la subrutina de la imagen llegué a la siguiente conclusión:

00BB61D6 movzx esi,byte ptr ds:[eax]; Carga en esi una constante y
observé que en [eax+3] tenemos la longitud exacta de la API.
...
00BB61EF mov al,byte ptr ds:[ebx+3B]; Carga en al el byte B1
00BB61F2 cmp esi,eax
00BB61F4 jnz short 00BB6254
00BB61F6 => Pone la API correctamente en la IAT
...
00BB6254 => Pone una entrada mala o la deja intacta.
...

Analicé durante un buen rato el código anterior y verifiqué lo siguiente:

  • Cuando se pone la dirección de API correctamente en la IAT el valor de la constante de esi vale: B1
  • Cuando pone una entrada mala el valor de esi vale: 77
  • El valor que toma eax siempre es: B1

Observa la curiosidad de esto. Quiero comentar el problema que me surgió aquí, me equivoqué en un detalle que resultó muy importante. Si la entrada es buena esi = B1 y eax = B1. Como son iguales en 00BB61F4 no salta:

Salto reparar IAT

Observa que el valor de ESI ya no vale para nada ya que el salto jbe se realiza siempre y seguidamente modifica esi. Si quisieramos intentar ahora reparar la IAT ¿Qué pensaría cualquier persona aquí? pues lo que se me ocurrió a mi erronea y lógicamente: nopear el jnz de 00BB61F4 ¿Lógico no? Lo hago y cuando llega a una entrada mala recibo lo siguiente:

Error entrada mala

Umm!! ¡qué extraño! Analicé con F7 toda la subrutina entera, hice logs con "trace into" y tanto la entrada buena como la mala realizan las mismas operaciones. ¡No tiene sentido! El problema es que al xorear el código que tiene el programa encriptado no se desencripta bien la API. En esta zona se desencripta parte de la cadena de texto de la API:

00B9EC44 mov eax,dword ptr ds:[esi+ecx*4]
00B9EC47 xor eax,dword ptr ds:[edx+ecx*4]
00B9EC4A mov dword ptr ds:[edi+ecx*4],eax
00B9EC4D jmp short 00B9EC41

Entiendo por qué sale la ventana y el lector debería también entenderlo. Me podía haber quedado estancado aquí durante mucho tiempo pero recordé un tutorial de un amigo llamado Tena (desde aquí lo saludo) y recuerdo vagamente que comentó algo de que en un packer similar el valor de esi no era una sola constante y que podía tener varios valores distintos. Esto me hizo abrir lo ojos y resolví instantáneamente el problema.

Entonces reinicié OllyDBG, puse un HBP-W en una entrada buena cualquiera de la IAT y puse un HBP en la dirección siguiente:

00BB61D6 movzx esi,byte ptr ds:[eax] ;Que es cuando esi toma el valor

Entonces fui pulsando F9 y observé que esi toma varios posibles valores. Probando encontré, parado en 00BB61D6, lo siguiente:

  • Si esi=B1 o esi=7D se producirá una Entrada buena
  • Si esi=77 se producirá una Entrada mala. Tendremos que obligar a que esi=7D para crear una entrada buena.
  • Si esi=AD o esi=A5 se producirá una Entrada mala con redirección al código del packer. Lo dejaré sin modificar y ya lo resolveré después.

Lo único que voy a hacer es que cuando esi=77 se modifique esi a 7D. Antes te voy a poner una imagen parado en 00BB61D6, observa que la IAT se está rellenando correctamente y llega un momento que esi va a valer AD:

Esi vale ADh

Observa que se va a rellenar la función en 0076D2AC. Pulso F9:

Esi vale AD - entrada mala

Se ha rellenado con una entrada mala que no podemos resolver por ahora y que está redirigida al código del packer. No hay problema, ya adelanto que tendremos 3 entradas malas como esta sin reparar. Mira también que el valor de esi valdrá ahora 77. Simplemente a mano lo modifico y pongo el valor a 7D y pulso F9:

Entrada mala en la IAT

Al poner el valor 7D se ha rellenado la IAT con una entrada buena. Para solucionar toda la IAT y no ir una a una a mano hice un injerto sencillo con el plugin Multiassembler (muy bueno) justo donde está el debugger parado en la imagen anterior, pero este packer hace chequeos de crc y nos descubre incluso con un simple BP y hace saltar ventanas de errores, así que para no modificar el código haré un simplísimo script, el siguiente:

// Script reparador IAT - Packer 01
var inicio_iat //Inicio de la IAT
var primera_instruccion //Esta es la dirección cuando esi ya ha obtenido un valor.
mov inicio_iat, 0BB61D9
mov primera_instruccion, 407ED4
bphws primera_instruccion, "x"
bphws inicio_iat, "x"
Inicio:
run
cmp eip, primera_instruccion
jne sigue
ret
sigue:
cmp esi, 7D
je Inicio
cmp esi, B1
je Inicio
cmp esi, 77
jne Inicio
mov esi, 7D
jmp Inicio

 

Es muy sencillo e indetectable por crc. La IAT se repara totalmente excepto 3 funciones que son los valores cuando esi vale AD, A2 y otra vez AD. Dos de las entradas son muy sencillas ya que corresponden a la API GetProcAddress. Se puede averiguar ya que las subrutinas son muy cortas y únicamente llaman a la API GetProcAddress. Sin embargo la última entrada no válida que nos queda por resolver no es tan sencilla ya que tiene algo más de código pero analizándola por encima descubrí que tras la call realiza un jmp directo a una API, mira este momento. (para llegar aquí me puse en la dirección sin resolver y puse ahí el eip. Observa también la IAT en el dump):

Reparar última API en la IAT

Esto me hace sospechar que la API que queda sin resolver en 076D2FC es RaiseException. Además puedes fijarte que por orden alfabético que es como suele estar la IAT de un Delphi también está bien. No tengo duda, ya tengo toda la IAT reparada que me ha costado tiempo, pero todavía no hago el dumpeado. Veremos en el siguiente tutorial cómo encontrar y restaurar la INIT TABLE característica de un programa en Delphi.

Última actualización: Domingo, 10 Julio 2011
Escribir un comentario
Antes de publicar un comentario, usted debe aceptar nuestras condiciones de uso: Condiciones de uso. Debido al spam, todos los comentarios serán moderados. Normalmente se responde en unos minutos, refresca los comentarios para comprobarlo.



 
Visitas: 8491080