y los Immortal Descendants presentan
 Un tutorial sobre UPX unpacker





Versión Corregida
 
 

prehistoria:
Acid_Burn me envió un archivo, un archivo empacado con UPX.  No puedo recordar la versión ahora.  Fue muy interesante y al inicio no fui capaz de desempacarlo (aunque no es difícil como lo vi después), lo admito.  Bien, algunos días después que localicé la nueva versión de UPX 0.82.  Descargué el archivo y estudie los docs.  Encontré que es una versión beta y podría tener algunos bugs. 
Tengo que admitir que no encontré ningún desmán del programa.  Para entender mejor el programa, codifiqué mi propio programa de prueba, el cual consistió solo de un MessageBox (explicado después).  Entonces ejecuté upx en él y comencé a debugearlo.  Y en este punto comienza el tut (aunque incluyo el código fuente del programa de prueba aquí. jeje).

tutorial:
La primer pregunta es que necesito para seguir este tut.  La respuesta es: necesitarás:

  1. UPX ver. 0.82  obtenlo de protools.cjb.net
  2. Hview
  3. Softice por supuesto
  4. procdump o cualquier tipo de dumper (volcador o descargador)
  5. Tasm 5.0 o MASM (aunque usaré Tasm)
  6. un poco de conocimiento en asm

Primera aproximación/ el objetivo es conseguir un archivo descargado, desempacado

Primero vamos a crear nuestro pequeño programa de prueba
Este fue el código fuente que usé: 

.386P
jumps
locals

Model Flat, Stdcall

extrn ExitProcess :proc
extrn MessageBoxA :proc

.Data
_cap db "upx-killer",0
_text1 db "regged!",0
_text2 db "unregistered!",0
_flag dd ?

.Code
Start:

mov [_flag],00
mov eax,[_flag]
cmp eax, 00000000
jnz _reg

push 0
push offset _cap
push offset _text2
push 0
call MessageBoxA
jmp _exit
 

_reg:
push 0
push offset _cap
push offset _text1
push 0
call MessageBoxA

_exit:
Call ExitProcess

End Start

 

Guarda el archivo (usé upx1.asm) y lo compile con ayuda de un archivo batch  

tasm32 /mx /m3 /z /q upx1
tlink32 -x /Tpe /aa /c upx1,upx1,, import32.lib
del *.obj
del *.map

Deberías tener un archivo PE corriendo.  Usa procdump para cambiar las características de sección a E0000060 (aplícalo a las 3 secciones UPX0, UPX1 y UPX2, para estar seguro).  Lo cual significa que el archivo es ejecutable y contiene código y datos.  Si no haces esto, el symbol loader no romperá.  Ahora usa el symbol loader u rastrea a través del archivo.  Verás que es muy pequeño.  Ahora es tiempo de ejecutar upx sobre él, así para empacarlo todo.  Usa estos parámetros: upx.exe -7 upx1.exe.  Upx empacará tu archivo como un virus (jeje alguna clase).  Carga tu archivo en el symbol loader y una vez F10 y versa el siguiente código:
 

Lo que vemos aquí, es:
pushad empuja todos los registros en la pila.  Después uno mueve 00405000 (el cual es el entrypoint -80h) en esi.  Así que tipo de código es este entre 00405000 y 00405080? Lo busqué en el hexeditor y encontré que contiene la Tabla de Importación, las cadenas que usé en mi programa (upx1).  Así que no hay necesidad de eso aún. 000405000+FFFFC000= 00401000, y esa es la dword que es movida a edi.  Si has investigado tu propio programa (como te dije antes), verías que este es el OEP de tu archivo.  (Seguro que puede ser diferente debido a las imagebases preferidas ;)).  Y también es la localidad de memoria donde la rutina desempacadota comienza escribiendo nuestro código empacado (desempaquetado).  El salto solo salta algunos nop y entra a la rutina desempaquetadora.  Lo siguiente que hago a menudo es ver la rutina desempaquetadora, como desempaca mi código.  (No sé por qué, así que no me culpen :)).  bpm 00401000 w es el breakpoint correcto.  Después de presionar F5 entrarás en un loop (un sub loop de hecho), el loop desempacador (jej).  Con un dato activado puedes seguir como es desempacado el código y escribir en la localidad en memoria  mientras presionas F10.  Como dije, estás en un sub loop.  Te tomará un poco salir de todos estos loops  (ca. 7 min).  Así que te digo la siguiente instrucción importante, es: 004051B4.lea eax,[esi+eax+00005000] carga la Tabal de Importación en eax.  Ahora estamos un minuto lejos de entrar en nuestro programa desempacado.  Así que se cuidadoso.  Camina con F10 hasta que alcances 0040521D.  Ves que es?
jmp 00401000. (Si tu código es diferente al mío, entonces carga el exe con el symbol loader y busca jmp 00401000, debería estar cerca de 0040521D)
Virgo, o no?
Ahora queremos entrar en un loop infinito para volcar el programa, entonces escribe 'a eip' y luego 'jmp eip' y otra vez presiona enter.  Okas, ahora sal de SICE (F5,g o de alguna forma).  Abre procdump, en el canal de tareas encontrarás upx1.exe.  Dale un clic derecho y selecciona dump(full).  Ahora abre tu nuevo archivo volcado con  pe-editor y cambia el Punto de entrada a 00001000.  arreglado y listo para correr.  Pruébalo!
Ahora puedes aplicarle tu parche.  No debería ser difícil.  Si no puedes resolverlo, no puedo imaginar que entiendas lo que expliqué hasta ahora ;)
 
 

Segunda aproximación/ el objetivo es insertar nuestro código en el archivo empacado (redireccionamiento de código)

Pienso que sería mejor explicar la teoría primero.  Tengo que decir que esta parte del tut no es para principiantes!
Así que, que es redireccinamiento de código?
Para mi y muchos mas en la escena, redireccionamiento de código significa inserta nuestro propio código “dentro” de la rutina desempaquetadora,  para que el programa ejecute por si mismo nuestro código insertado, lo que parchea el exe.  Tengo que admitir que he hecho esto un poco mas complicado.  Encontré que después en...
lo que necesitamos primero es algo de espacio, el cual será usado para insertar nuestro código.  Así que abre el archivo y busca 00 00 00 00....un montón de ellos.  Para me, un buen lugar fue 00405230.  Lleno  de 00's.  Ahora tenemos el espacio, ahora debemos redireccionar algo de código de la rutina desempacadota para que salte a nuestro código insertado (será insertado en 00405230 por supuesto).
#Explicación: queremos que la rutina desempaquetadora desempaque el archivo primero,  y luego saltar a nuestro código y parchar el archivo en memoria.  #Tuve un error, el cual hizo todo un poco mas complicado.  Pero pensé por qué no hacer algo mas complicado, algo que atormente el cerebro :).  El error pasó mientras usaba hview y veía el Punto de Entrada Real del programa en 0040521d.  Hview no mostró jmp 00401000 (El punto de Entrada Real) pero algo más.  Por lo tanto, pensé que la rutina desempacadota calcula el Punto de Entrada Real y sobreescribe 0040521d. (espero que puedas seguirme).  Por lo tanto, no tendría sentido cambiar 0040521d jmp 004ffbc0 (que es lo que hview me mostró) en jmp 00405230, porque se borraría de todas formas.  Por lo tanto busqué un salto para arreglarlo, lo encontré realmente en el principio:
 

:004050A0 60                                   pushad
:004050A1 BE00504000                  mov esi, 00405000
:004050A6 8DBE00C0FFFF            lea edi, dword ptr [esi+FFFFC000]
:004050AC 57                                  push edi
:004050AD 83CDFF                        or ebp, FFFFFFFF
:004050B0 EB10                              jmp 004050C2 -->here

Recuerdas ese salto, o no? Encontré ese salto también en hview, y por lo tanto, lo cambió a jmp 00405230.  algunos podrán gritar ahora porque esto podría cambiar el siguiente código, pero upx tiene una característica virga: después de ese salto, hay un grupo de nops.  Eso hace fácil cambiarlos.  Los opcodes para jmp 00405230 son : E9BFB1BFFF Aplica estos cambios con la ayuda de hview.  Imagínate caminando con SICE (o camina y haz cambios temporales) a través del código alcanzando 004050B0 y saltando también 00405230.  Sí, estas en lo correcto, no hay nada allí (por el momento), solo algunos [eax],al lo cual señaliza nuestros 0000's.
lo siguiente que necesitamos hacer es cambiar este 0040521D jmp 00401000  a jmp 00405260.  te preguntas por qué? Bien después de desempacarlo, quiere saltar al programa real, pero tenemos que ejecutar nuestro parche primero, por lo tanto no queremos saltar a 00401000 pero sí a 00405260 (donde agregaremos nuestro parche).
Cambiando ese salto a jmp 00405260  se ve así:
jmp 00405260 en opcodes es :
EB41
Pero en 00405260 no soporta algo com EB43 para que podamos sobreescribirlo, no algunos bytes mas encontramos allí algo como EB43FF32BC por lo que vamos a parcharlo con
EB41000000 para sobrescribirlo completamente.  En asm, esa rutina de sobrescritura en 00405230 se ve como esto:
 

00405230 66C7051D524000EB41     mov word ptr[0040521d], 41EB
00405239
66C7051F5240000000       mov word ptr[0040521f],  0000
00405242
C6052152400000                mov byte ptr[00405221], 00
00405249
E974FEFFFF                      jmp 004050C2

Okas, espero que entiendas las primeras 3 líneas (recuerdas el orden invertido de la primera línea), si no, ellos parchan el jmp 0040100 a jmp 00405260.  la última línea salta a 00405242.  sube un poco a la tabla amarilla.  00405242 es el inicio de la rutina desempacadota.

Resumen de lo que se ha hecho hasta ahora:
Parchamos un jmp de jmp 00405242 a jmp 00405230
Insertamos nuestro primer código de rutina, el cual parcha el salto al programa real de 00401000 a 00405260

Imagina el programa corriendo: comienza; salta a nuestra primer rutina; parcha el salto; salta de Nuevo a la rutina desempacadota; finalmente alcanza el salto en 0040521d salta a 00405260; y ahora?
ahora agregamos la segunda rutina, la cual parcha el programa en memoria:

Esto es muy fácil, porque el programa que codificamos, puede ser pegado cambiando un jnz a jz.(Solo es un demo, lo recuerdas?)
Alcanzamos 00405260: insertando:
 

00405260 C6051210400074       mov byte ptr[00401012],74
00405267
E974FEFFFF              jmp 00401000

La primera línea parcha el  jnz en un jz y la segunda línea salta al programa real.  Ahora tienes un archivo corriendo empacado / desempacado pero este es el punto en que tienes un archivo parchado.

Después, haciéndolo temporal con SICE, toma los opcodes que marqué en azul e inserta todo lo que necesite en el archivo, con la ayuda de hview.  Ahora es estático!

Saludos y gracias van a:

Volatility
Lord Soth
Lucifer 48
Tornado
Acid_Burn
Risc
Pain
ytc_
LaZaRuS
RevX
knotty
    Warezpup
...........and all I forgot :( sorry


 




www.000webhost.com