Por lo general, los empaquetadores/encriptadores de ficheros operan
según una idea común.
El código util está cifrado de manera que desensamblando
no se puede analizar su comportamiento. En el momento de la ejecución
entra en juego una rutina que escribe en memoria la versión no-encriptada
y a partir de ese momento todo transcurre como si de un ejecutable normal
se tratara.
La forma de atacar a estos programas tan poco comunicativos se basa
en cargadores o loaders.
Un cargador es un programa capaz de cargar en memoria otros ejecutables
y detenerse antes de comenzar su ejecución de manera que puede aprovecharse
la ocasión para realizar los oportunos parcheos. Sin embargo son
fáciles de engañar y tienen problemas para "seguir la pista"
a nuevos threads (al menos los que he podido examinar).
ToPo no tiene este tipo de problemas porque genera un "enemigo interior"
que puede actuar justo después del desencriptado del código
cualquiera que sea el algoritmo. Como ejemplo veremos como funciona con
un par de empaquetadores de élite.
(a) UPX v0.84
Intentamos repetir la gracia anterior con una versión de Notepad.exe
empaquetada con UPX.
para ello tecleamos en la línea de comandos del DOS:
C:\> upx -9 notepad.exe
y ya está. Pfff....reducido a la mitad de tamaño!. Me
encanta este packer :o)
Ahora debemos añadir nuestra burbuja pero esta vez NO queremos
redirección del entrypoint ya que este hace referencia al comienzo
de la rutina de descompresión y no
al código del notepad.
Ok, tenemos el Notepad.exe, empaquetado con UPX e inoculado con una
sección topo de 100 bytes (no necesitamos tantos en realidad...).
Trazamos un poco para ver como respira el asunto poniendo un BPM en 401000
(el entrypoint original de notepad). Dejamos que fluya el código
con F5 y... reaparecemos en :40C080. Se está escribiendo el código
desempaquetado en su ubicación original... es decir que estamos
en la rutina desempaquetadora. Trazamos un poco hacia el final para comprobar
que la cosa acaba con un JMP al entrypoint del programa empaquetado (:401000).
Esta rutina está perfectamente accesible por lo que desde UltraEdit
modificamos el salto al inicio de nuestra sección ".topo" (JMP 40E000).
A partir de aqui todo se reduce al caso anterior. Disponemos del código
desempaquetado en memoria ANTES de comenzar la ejecución. Añadimos,
como antes, nuestras "strings", los pushes y la llamada a messagebox (no
copiar directamente porque las direcciones relativas no son las mismas).
Por ultimo cerramos con un JMP al entrypoint de notepad :401000.
(b) ASPack v1.08.03
Este packer presenta algunas diferencias con el anterior. La compresión
no es tan buena pero a cambio establece "potentes" medios de protección
del código empaquetado. Siguiendo el ejmplo anterior intentaremos
ejecutar nuestra lamer-splash al comienzo de la ejecución del notepad.
Con las pautas ya explicadas (BPM a la primera instruccion del notepad,
:401000) intentamos cazar el final de la rutina de descompresión
para saltar desde allí a nuestra fortaleza de código en blanco.
Y facilmente la encontramos en:
.........................
:40C554 MOV [ESP+1C],EAX
:40C558 POPAD
:40C559 JNZ 40C563
:40C55B MOV EAX,1
:40C560 RET 000C
:40C561 PUSH EAX
:40C561 RET <------ Esto nos lleva al comienzo de "notepad"
Cuando abrimos "notepad.exe" con Uedit para buscar este trozo de código
descubrimos que NO ESTA. Impresionante. La rutina que desencripta esta
también encriptada.
No problemo. Buscaremos el código que desempaqueta al desempaquetador
siguiendo la misma tecnica de los breakpoints en memoria. Así llegamos
a:
:40C0AE REPZ MOVSD
:40C0B0 MOV ECX,EAX
:40C0B2 AND ECX,03
:40C0B5 REPZ MOVSB <-----Copia el unpacker unpacked!
:40C0B7 MOV EAX,[EBP+4450B5]
Este código SI esta accesible (bueno estaría que no) y
perfectamente editable. Vamos pues con Uedit y modificamos la instrucción
en :40C0b7 según:
:40C0B7 JMP 40F000 <---salto a la sección ".topo"
No debemos preocuparnos por las modificaciones que hacemos para acceder
al ".topo" porque antes de regresar restauraremos todo.
Al comienzo del ".topo" codificamos lo siguiente (ayudandonos de (A)ssembly
en Winice):
;Parcheamos el codigo en :40C554 por "jmp
40f032"
;Es un salto a la mitad de la nueva sección
donde
;pondremos nuestra messagebox
:0040F000 mov dword ptr [0040C554], 002AD9E9
:0040F00A mov byte ptr [0040C558], 00
;Restauramos el contenido en :40c0b7 a su
;código original "mov eax,[ebp+4450b5]"
:0040F011 mov dword ptr [0040C0B7], 50B5858B
:0040F01B mov word ptr [0040C0BB], 0044
;regresamos a :40c0b7 como si nada hubiera
;pasado...
:0040F024 jmp 0040C0B7
Perdidos...? Recapitulemos: hemos parcheado con Uedit la rutina que
desempaqueta al desempaquetador del codigo. El objeto del parche es establecer
un acceso a ".topo" que
contendrá un nuevo parche en memoria. Este parche modifica el
desempaquetador final para que salte de nuevo a ".topo" (hacia la mitad
de la sección) ANTES de lanzar el Notepad.
Creo que lo realmente lioso es intentar contarlo...hacerlo es muy fácil.
La segunda mitad de ".topo" contiene el siguiente codigo más
familiar:
:0040F032 60
pushad ;por seguridad
:0040F033 EB3B
jmp 0040F070 ;saltamos las strings
; strings usadas para la ventana messagebox
:0040F035 54
push esp
:0040F036 6F
outsd
:0040F037 6D
insd
.......................................
:0040F069 7061
jo 0040F0CC
:0040F06B 636B20
arpl dword ptr [ebx+20], ebp
:0040F06E 2000
and byte ptr [eax], al
:0040F070 6A00
push 00000000 ;estilo 0
:0040F072 685FF04000
push 0040F05F ;string titulo
:0040F077 6835F04000
push 0040F035 ;string mensaje
:0040F07C 6A00
push 00000000 ;Padre/Hwnd
* Reference To: USER32.MessageBoxA
|
:0040F07E FF1530744000
call dword ptr [00407430]
;Restauramos las instrucciones que habia antes
del salto
;"40C554 mov [esp+1c],eax"
;"40C558 popad"
:0040F084 C70554C540008944241C mov dword ptr [0040C554],
1C244489
:0040F08E C60558C5400061
mov byte ptr [0040C558], 61
:0040F095 61
popad
:0040F096 E9BAD4FFFF
jmp 0040C554
Y así acaba esta historia.
De manera muy similar se pueden parchear hasta la saciedad todos los
packers/crypters. Al menos así ha sido con los que he probado.
|