Desempacado Manual

Descomprimiendo/Desempacando Manualmente Un archivo Comprimido/Empacado

por ytc_ [tNO '99]
5 de Marzo, 1999
Traducción de ^[G]oLe[E]^
25 de Enero, 2001


INTRODUCTION

     Los objetivos empacados o comprimidos usualmente son ejecutables (*.exe) o dynamic link libraries (*.dll) cuyos opcodes han sido comprimidos o empacados por medio de manipulación de bytes y algoritmos para obtener archivos mas pequeños. Estos archivos, al ser desensamblados usando Windows Disassembler (W32Dasm) o Interactive Disassembler (IDA), darán resultados nulos o basura con algunos opcodes desensamblados. Hay desempacadores para ciertos empacadores en la web, pero algunas veces no trabajan porque los programadores encontraron formas de vencerlos, de allí la necesidad del desempacado manual.

     Antes de continuar, debo agradecerle a Iczelion por ayudarme a desempacar mi primer ejecutable. Él me explico un monton acerca de los PE y las tablas de importación y me dio instrucciones y pasos para desempacar un archivo. MUCHAS GRACIAS!! ;-)


OBJETIVO

     El objetivo en el que estaba trabajando era Powerstrip v2.35.01, disponible en Entech Taiwan, empacado con ASPack, también de la misma compania. Pero, cualquier objetivo, media vez esté empacado con él puede ser usado, aunque la rutina de desencripción puede diferir un poco.


HERRAMIENTAS USADAS

     Todas las herramientas mencionadas están disponibles en el Site de Iczelion o en el mio, pero los que están en el mio no están muy actualizados. Estaré asumiendo que sabes como usar tus herramientas y las has configurado correctamente.


ENSAYO

     Como todos sabemos, los empacadores comprimen los ejecutable y dll's para obtener archivos mas pequeños en tamaño y tambien para proteger los objetivos de los crackers casuales. Podrás encontrar el lugar que quieres parchear en memoria (usando Softice por supuesto), pero debido a que el objetivo está empacado, no puedes encontrar el lugar. Ahora, hay una posibilidad acerca de los exes y dll's comprimidos -- *NO* pueden ejecutarse en su estado compreso. *DEBEN* estar desempacados en memoria. *PRIMERO*, antes de saltar a las instrucciones reales. Usando este conocimiento, seremos capaces de trabajar a nuestro modo a traves de esto.

     No explicare como obtener la rutina principal de desempaque. Usando softice e IDA, *deberias* ser capaz de saber donde está la rutina. Si no puedes, entonces te sugiero primero ganar mas experiencia. Aquí está un snippet de la rutina principal. Lo copié de IDA y lo edite un poco para que se viera como lo que verías en SoftICE.

005B0000 60		      pusha	 
005B0001 E8 00 00 00 00	      call	 005B0006 ; llamando al siguiente opcode
005B0006 5D		      pop	 ebp ; obteniendo la direccion actual
005B0007 81 ED AE 98 43 00    sub	 ebp, 004398AE ; [hace algunos calculos
005B000D B8 A8 98 43 00	      mov	 eax, 004398A8 ; para obtener la ImageBase
005B0012 03 C5		      add	 eax, ebp      ; del exe a eax]
005B0014 2B 85 12 9D 43 00    sub	 eax, [00439D12+ebp] ; image base (00400000)
005B001A 89 85 1E 9D 43 00    mov	 [00439D1E+ebp], eax ; guarda el image base
005B0020 80 BD 08 9D 43 00 00 cmp	 byte ptr [00439D08+ebp], 0 ; compara el byte con 0
005B0027 75 15		      jnz	 005B003E ; salta a los calls magicos si no es 0
005B0029 FE 85 08 9D 43 00    inc	 byte ptr [00439D08+ebp] ; incrementa el valor en 
								    la localidad
005B002F E8 1D 00 00 00	      call	 005B0051 ; Call Magico 1
005B0034 E8 73 02 00 00	      call	 005B02AC ; Call Magico 2
005B0039 E8 0A 03 00 00	      call	 005B0348 ; Call Magico 3
005B003E 8B 85 0A 9D 43 00    mov	 eax, [00439D0A+ebp]
005B0044 03 85 1E 9D 43 00    add	 eax, [00439D1E+ebp] ;obtiene la DirecciónDelEP real
005B004A 89 44 24 1C	      mov	 [esp+1C], eax ; lo guarda
005B004E 61		      popa	 
005B004F FF E0		      jmp	 eax ; salta al Punto de Entrada real

     Con mis comentarios de arriba, cualquier tonto puede adivinar que los 3 CALLS magicos son el corazon de esta rutina de desempaque. Entonces, miremos dentro del primer CALL Magico.

005B0051 80 BD 2F 9E 43 00 00 cmp	 byte ptr [00439E2F+ebp], 0
005B0058 74 1D		      jz	 005B0077 ; viendo en Softice, este salto es tomado
...
005B0077 8D B5 26 9D 43 00    lea	 esi, [00439D26+ebp]
005B007D 83 3E 00	      cmp        [esi], 0 ; revisa las secciones comprimidas
005B0080 0F 84 EE 00 00 00    jz	 005B0174 ; salta si no hay mas
005B0086 8D 85 86 9D 43 00    lea	 eax, [00439D86+ebp] ; saca el offset de "kernel32.dll"
005B008C 50		      push       eax 
005B008D FF 95 74 9E 43 00    call       [00439E74+ebp] ; llama a GetModuleHandle
005B0093 8B F8		      mov	 edi, eax ; mueve el manejador de kernel32.dll a edi
005B0095 8D 9D 93 9D 43 00    lea	 ebx, [00439D93+ebp] ; obtiene el offset de "VirtualAlloc"
005B009B 53		      push       ebx
005B009C 50		      push       eax
005B009D FF 95 70 9E 43 00    call       [00439E70+ebp] ; llama a GetProcAddress
005B00A3 89 85 76 9D 43 00    mov	 [00439D76+ebp], eax ; guarda la dirección
005B00A9 8D 9D A0 9D 43 00    lea	 ebx, [00439DA0+ebp] ; obtiene el offset de "VirtualFree"
005B00AF 53		      push       ebx
005B00B0 57		      push       edi
005B00B1 FF 95 70 9E 43 00    call       [00439E70+ebp] ; llama a GetProcAddress
005B00B7 89 85 7A 9D 43 00    mov	 [00439D7A+ebp], eax ; guarda la dirección
005B00BD 8D B5 26 9D 43 00    lea	 esi, [00439D26+ebp]
005B00C3 8B 46 04             mov	 eax, [esi+4] ; saca el TamañoVirtual de la sección
005B00C6 6A 04		      push       4
005B00C8 68 00 10 00 00	      push       1000
005B00CD 50		      push       eax
005B00CE 6A 00		      push       0
005B00D0 FF 95 76 9D 43 00    call       [00439D76+ebp] ; llama a VirtualAlloc
005B00D6 89 85 22 9D 43 00    mov	 [00439D22+ebp], eax
005B00DC 56		      push       esi
005B00DD 8B 1E		      mov        ebx, [esi]
005B00DF 03 9D 1E 9D 43 00    add	 ebx, [00439D1E+ebp]
005B00E5 50		      push       eax
005B00E6 53		      push       ebx
005B00E7 E8 89 00 00 00	      call       005B0175 ; [desempaca las secciones al espacio asignado
005B00EC 83 C4 08             add        esp, 8   ; por VirtualAlloc]
005B00EF 3B 46 04	      cmp	 eax, [esi+4] ; es correcto el tamaño de los datos desempacados?
005B00F2 74 0B		      jz	 005B00FF ; salta si sí
...
005B00FF 80 BD 09 9D 43 00 00 cmp	 [00439D09+ebp], 0
005B0106 75 39		      jnz	 005B0141 ; salto no tomado cuando es desempacada la 1er sección
005B0108 FE 85 09 9D 43 00    inc	 byte ptr [00439D09+ebp]
005B010E 50		      push       eax
005B010F 51		      push       ecx
005B0110 56		      push       esi
005B0111 53		      push       ebx
005B0112 8B C8		      mov	 ecx, eax
005B0114 83 E9 05	      sub	 ecx, 5
005B0117 8B B5 22 9D 43 00    mov	 esi, [00439D22+ebp]
005B011D 33 DB		      xor	 ebx, ebx
005B011F		      or	 ecx, ecx ; [las linea de 005B011F a 005B013B
005B0121 74 1A		      jz	 005B013D ; hacen mas calculos
005B0123 AC		      lodsb               ; en la primer sección que
005B0124 3C E8		      cmp        al, E8   ; está siendo desempacada]
005B0126 74 08		      jz	 005B0130
005B0128 3C E9		      cmp	 al, E9
005B012A 74 04		      jz	 005B0130
005B012C 43		      inc	 ebx
005B012D 49		      dec	 ecx
005B012E EB EF		      jmp        005B011F
005B0130 29 1E                sub	 [esi], ebx
005B0132 83 C3 05	      add	 ebx, 5
005B0135 83 C6 04	      add	 esi, 4
005B0138 83 E9 05	      sub	 ecx, 5
005B013B EB E2		      jmp	 005B011F
005B013D 5B		      pop        ebx
005B013E 5E		      pop	 esi
005B013F 59		      pop	 ecx
005B0140 58		      pop	 eax
005B0141 8B C8		      mov        ecx, eax
005B0143 8B 3E		      mov	 edi, [esi]
005B0145 03 BD 1E 9D 43 00    add	 edi, [00439D1E+ebp]
005B014B 8B B5 22 9D 43 00    mov	 esi, [00439D22+ebp]
005B0151 F3 A4		      repe movsb ; copia datos desempacados al espacio del exe
005B0153 5E		      pop	 esi
005B0154 8B 85 22 9D 43 00    mov	 eax, [00439D22+ebp]
005B015A 68 00 80 00 00	      push       8000
005B015F 6A 00		      push       0
005B0161 50		      push       eax
005B0162 FF 95 7A 9D 43 00    call       [00439D7A+ebp] ; llama a VirtualFree
005B0168 83 C6 08	      add	 esi, 8
005B016B 83 3E 00	      cmp	 [esi], 0 ; mas secciones a desempacar?
005B016E 0F 85 4F FF FF FF    jnz	 005B00C3 ; salta si hay
005B0174 C3		      retn    

     Larga, pero franca sección de código. Primero, las direcciones para funciones VirtualAlloc y VirtualFree son recuperadas desde la función GetProcAddress. Entonces, el programa asigna una porción de memoria de un area especifica de memoria en su propia dirección por una llamada a VirtualAlloc, entonces la sección empacada en esta dirección. Si esta sección es la primera, mas calculos son hechos para el. Después, los datos desempacados son movidos a la dirección actual del exe, y luego la nueva dirección es liberada por una llamada a VirtualFree. Esta rutina hace un loop 4 veces para las 4 secciones empacadas, que son CODE, DATA, .idata y .rsrc si estás usando PowerStrip como objetivo. Los nombres de las secciones son obtenidos al ver el titulo de la ventana de código de Softice cuando las secciones desempacadas son movidas en la línea 005B0151.

     Luego, miremos en el segundo CALL Mágico.

005B02AC 8B 95 1E 9D 43 00    mov	 edx, [00439D1E+ebp] ; dirección del image base actual
005B02B2 8B 85 0E 9D 43 00    mov	 eax, [00439D0E+ebp] ; image base del exe antes del empacado
005B02B8 2B D0		      sub	 edx, eax ; los compara
005B02BA 74 75		      jz	 005B0331 ; salta si son iguales
...
005B0331 C3                   retn

     Este segundo Call Mágico me rompió la cabeza por un momento. El image base especificado en el encabezado PE es solo la dirección PREFERIDA de la carga. Esto es, el cargador PE puede cargar el archivo donde quiera que sea necesario. Esta llamada revisa si el image base actual es el mismo que el preferido. Si no lo es, según yo, es que lo relocalizará. Pero esto le pasaría normalmente a un dll.

005B0348 8B 95 1E 9D 43 00    mov	 edx, [00439D1E+ebp] ; Image base
005B034E 8B B5 27 9E 43 00    mov	 esi, [00439E27+ebp] 
005B0354 8B BD 23 9E 43 00    mov	 edi, [00439E23+ebp]
005B035A 03 F2		      add	 esi, edx
005B035C 03 FA		      add	 edi, edx
005B035E 8B 46 0C	      mov	 eax, [esi+0C]
005B0361 85 C0		      test       eax, eax
005B0363 0F 84 F6 00 00 00    jz	 005B045F
005B0369 03 C2		      add	 eax, edx ; obtiene el offset del nombre de la dll
005B036B 8B D8		      mov	 ebx, eax
005B036D 50		      push       eax
005B036E FF 95 74 9E 43 00    call       [00439E7E+ebp] ; llama a GetModuleHandle
005B0374 85 C0		      test       eax, eax ; prueba si la llamada fue exitosa
005B0376 75 67		      jnz	 005B03DF ; salta si sí
...
005B03DF 89 85 1F 9E 43 00    mov	 [00439E1F+ebp], eax ; guarda el manejador
005B03E5 C7 85 2B 9E 43 00 00+mov	 [00439E2B+ebp], 0
005B03EF 8B 95 1E 9D 43 00    mov	 edx, [00439D1E+ebp]
005B03F5 8B 06		      mov	 eax, [esi]
005B03F7 85 C0		      test       eax, eax
005B03F9 75 03		      jnz	 005B03FE
005B03FB 8B 46 10	      mov	 eax, [esi+10]
005B03FE 03 C2		      add	 eax, edx
005B0400 03 85 2B 9E 43 00    add	 eax, [00439E2B+ebp]
005B0406 8B 18		      mov	 ebx, [eax]
005B0408 8B 7E 10	      mov	 edi, [esi+10h]
005B040B 03 FA		      add	 edi, edx
005B040D 03 BD 2B 9E 43 00    add	 edi, [00439E2B+ebp] ; Apunta a la Tabla de Importación
005B0413 85 DB		      test       ebx, ebx
005B0415 74 3A		      jz	 005B0451
005B0417 F7 C3 00 00 00 80    test       ebx, 80000000h
005B041D 75 04		      jnz	 005B0423
005B041F 03 DA		      add	 ebx, edx
005B0421 43		      inc	 ebx
005B0422 43		      inc	 ebx
005B0423 81 E3 FF FF FF 7F    and	 ebx, 7FFFFFFFh
005B0429 53		      push       ebx ; apunta al nombre de una función dll
005B042A FF B5 1F 9E 43 00    push       [00439E1F+ebp]
005B0430 FF 95 70 9E 43 00    call       [00439E70+ebp] ; llama a GetProcAddress
005B0436 85 C0		      test       eax, eax ; fue exitosa la llamada?
005B0438 75 0C		      jnz	 005B0446 ; salta si sí
...
005B0446 89 07		      mov	 [edi], eax ; guarda la dirección de la función en la Tabla de Importación
005B0448 83 85 2B 9E 43 00 04 add	 [00439E2B+ebp], 4
005B044F EB 9E		      jmp	 005B03EF
005B0451 83 C6 14	      add	 esi, 14h
005B0454 8B 95 1E 9D 43 00    mov	 edx, [00439D1E+ebp]
005B045A E9 FF FE FF FF	      jmp	 005B035E
005B045F C3		      retn    

     De aquí, podemos ver que este terecer CALL Mágico actualmente se comporta como el cargador de la DLL. Y de la definición de la Tabla de Importación de abajo, podemos concluir que solo volcaremos las secciones resultantes del primer CALL Mágico. Ahora, concentremonos en que necesitamos saber antes de volcar las secciones, esto es, el archivo PE.

     Primero aprenderemos acerca de los encabezados de secciones. Carga Procdump y usa la función PE Editor, mira las secciones de tu objetivo. Deberías ver algo como esto. Estoy usando Pstrip.exe como ejemplo.

Nombre Tamaño Virtual Offset Virtual   Raw Size  Raw Offset  Caracteristicas
----------------------------------------------------------------------------
CODE     000CB000      00001000        0004B200  00000400    C0000040
DATA     00002000      000CC000        00001A00  0004B600    C0000040
BSS      00005000      000CE000        00000000  0004D000    C0000040
.idata   00003000      000D3000        00001000  0004D000    C0000040
.tls     00001000      000D6000        00000000  0004E000    C0000040
.rdata   00001000      000D7000        00000200  0004E000    C0000040
.reloc   0000D000      000D8000        00000000  0004E200    C0000040
.rsrc    000CB000      000E5000        00024000  0004E200    C0000040
.data    00002000      001B0000        00001400  00072000    C0000040

     Como podemos ver aquí, hay 9 secciones aquí. Cada sección consiste de un encabezado y un cuerpo (los datos raw en el exe). La sección tabla tiene una longitud de 40 bytes, y está definido como sigue en el archivo WINNT.H

#define IMAGE_SIZEOF_SHORT_NAME 8

typedef struct _IMAGE_SECTION_HEADER {
     UCHAR   Name[IMAGE_SIZEOF_SHORT_NAME];
     union {
             ULONG   PhysicalAddress;
             ULONG   VirtualSize;
     } Misc;
     ULONG   VirtualAddress;
     ULONG   SizeOfRawData;
     ULONG   PointerToRawData;
     ULONG   PointerToRelocations;
     ULONG   PointerToLineNumbers;
     USHORT  NumberOfRelocations;
     USHORT  NumberOfLinenumbers;
     ULONG   Characteristics;
} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;

     Solo tocare las partes importantes que necesitas saber para nuestro trabajo de volcado. Name es un campo de 8 bytes almacenando el nombre de nuestras secciones, por ejemplo CODE, DATA, .idata y .rsrc. VirtualSize es el tamaño de esta seccion cuando el exe es asociado en memoria cuando se carga. VirtualAddress (abreviatura, VA) es la dirección inicial de la sección cuando es asociada en memoria. SizeOfRawData es el tamaño fisico de la sección en el exe. PointerToRawData es el offset de la sección en el exe.

     Antes de continuar, creemos un indice de que debemos hacer para nuestro proceso de volcado del exe.

  1. Juntar información vital de nuestro exe para nuestro porceso y tambien activar nuestro exe para correr despues de ser creado. La información es el nuevo Punto de Entrada para nuestro exe (ya explicado), nombres de las secciones que queremos volcar (ya explicado), Virtual Sizes (tamaños virtuales) y Virtual Addresses (direcciones virtuales) de las secciones que queremos volcar (usando Procdump), Offsets Raw y Tamaños Raw de CADA sección en el exe (usando Procdump también) y la nueva dirección de nuestra Tabla de Importación.
  2. Comenzar a volcar las secciones, usando los Tamaños Virtuales y Relative Virtual Addresses (Direcciones Virtuales Relativas) para cada sección. Relative Virtual Address es la suma del Image Base (400000 para Pstrip.exe) y VA.
  3. Poner las secciones juntas usando un buen hexeditor que permita copiar y pegar en hex, comenzando desde el encabezado PE, siguiendo por las secciones en orden corrcto. Ultraedit provee estás funciones.
  4. Modificar el encabezado PE para reflejar el nuevo Punto de Entrada, tabla de importación, offsets raw y tamaños raw del nuevo exe. Esto puede ser hecho facilmente usando procdump, o quiza quieras leer mas acerca del encabezado PE y modificarlos tu mismo con un hexeditor.

     Ahora, puedes comenzar a reunir la información que queremos para nuestro proceso de volcado. Solo necesitaras saber que secciones volcar (ya hicimos esto antes, esto es CODE, DATA, .idata y .rsrc), La Dirección Virtual Relativa (Relative Virtual Address, RVA para abreviar) de las secciones (esto es Image Base + VA) y el tamaño virtual (Virtual Size) de las secciones. Usando Procdump, facilmente podemos determinar estos valores que necesitamos. Así que comencemos a volcar las secciones en archivos usando la fantastica herramienta de Owl, Icedump (o si prefieres, la de Quine, Softdump) en diferentes archivos. El tamaño de cada secciones será el Virtual Size (Tamaño Virtual) de ella, y el RVA para las secciones será el Image Base (que es 400000) + VA.

     Siguiente, debemos 'pegar' estas secciones juntas usando las caracteristicas de copiado & pegado de Ultraedit. Pero, antes de esto, necesitamos saber algunas cosas más. Necesitaremos el Raw Size (Tamaño Crudo) y el Raw Offset (Desplazamiento bruto) de CADA sección. Con esta información, podemos encontrar donde pueden ser encontradas las secciones desempacadas en el exe, y cuales son sus tamaños. Esta información también puede ser encontrada usando Procdump.

     Ahora, comienza copiando el encabezado PE del exe empacado a un nuevo archivo hasta el Raw Offset de la primer sección, que es 400h para Pstrip.exe, en la sección CODE. Luego, copia la sección CODE volcada en este nuevo archivo, comenzando del offset 400h. Toma nota del nuevo Raw Size de esta sección, y el nuevo Raw Offset de la siguiente sección, que es DATA. Has lo mismo para todas las otras secciones. Recuerda tomar nota de los nuevos Raw Sizes y Raw Offsets para cada sección, luego reemplaza las viejas usando Procdump. ESTE PASO ES MUY IMPORTANTE.

     Ahora, modifica el encabezado PE para que refleje el nuevo Punto de Entrada y tabla de importación. El problema ahora es, que no sabemos donde está la tabla de importación. Afortunadamente, la solución es muy simple. Pero, necesitamos saber como está estructurado el directorio de importación. Por Favor toma nota de que los valores de entrada de la Tabla de Importación está apuntando a la estructura VA del Directoria de Importación. Aquí está la estructura del Directorio de Importación, como es dado por Randy Kath del Grupo de Tecnologia de Desarrollo de Redes de Microsoft.

typedef struct tagImportDirectory
     {
     DWORD    dwRVAFunctionNameList;
     DWORD    dwUseless1;
     DWORD    dwUseless2;
     DWORD    dwRVAModuleName;
     DWORD    dwRVAFunctionAddressList;
     }IMAGE_IMPORT_MODULE_DIRECTORY,
      * PIMAGE_IMPORT_MODULE_DIRECTORY;

     No explicaré que siginifican los campos, ya que es muy obvio debido a sus nombres. Aquí, solo hay 2 campos que juegan un role importante determinando las direcciones de las funciones, que son los 2 ultimos campos, dwRVAModuleName y dwRVAFunctionAddressList. dwRVAModuleName apunta al nombre de la dll a cargar, por ejemplo, kernel32.dll. El campo dwRVAFunctionAddressList apunta a un array de direcciones de funciones, y estas direcciones de funciones apuntan a los nombres de las funciones encontradas en la dll. Bien, no hay nombres ahora, pero a los ordinales de las funciones, que son los 2 bytes precediendo a los nombres de las funciones. Si hay pocas dlls siendo usadas, entonces tendrás tantas estructuras tagImportDirectory. Como mencioné antes, el valor de la Tabla de Importacióndebería apuntar a la primer estructura. Pero donde está la estructura?? Con la información que te he dado arriba, estoy seguro que serás capaz de pensar en una forma de encontrarla por ti mismo. ;-).

     Despues de hacer estas modificaciones, graba tu nuevo exe, manten los dedos crusados, ora mucho, y ejecutalo. ;-) Si has hecho las modificaciones al archivo PE correctamente, deberías ser capaz de ejecutar el exe y cuando lo desensambles, el desensamblador debería ser capaz de localizar los nombres de las funciones tambien.


NOTAS FINALES

     En mi opinion, la habilidad del desempacado manual debería ser dominada por todo cracker, en lugar de depender de desempacadores, tanto genericos como especificos, para desempacar exe's o dll's. Esto se debe a que hay muchos tipos de empacadores de la misma versión y esto puede engañar al desempacador muy facilmente. Actualmente me inspire en el metodo 'restauración de la virginidad' de Marigold cuando leia sus ensayos sobre los esquemas de protección VBox.

     También, este metodo pede ser usado generalmente con encriptadores también, no es limitado para los desempacadores.


DEFINICIONES

ImageBase. Dirección Base preferida en el espacio de dirección de un proceso para trazar la imagen del ejecutable.

Dirección del EP. Indica la ubicacion del punto de entrada para la aplicación.

VirtualSize. Indica el tamaño de la sección cuando es trazada en memoria.

Tabla de Importación. Una Tabla de direcciones que apunta a nombres de funciones dll. Esta tabla es reemplazada por direcciones de las funciones antes de cargar el exe en el Punto de Entrada.

Secciones. Tiene el contenido del archivo, incluyendo código, datos, recursos, y otra información del ejecutable.

www.000webhost.com