Karpoff Spanish Tutor 2001

Programa: Advanced Grapher 1.61


 

PROTECCION: ASPack 1.08.04 + ASProtect.
Descripcion: Liquidar manualmente la protección.
Dificultad: Avanzado.
DOWNLOAD: http://www.serpik.com/agrapher/
Herramientas: Softice 3.24, FrogsICE, Procdump32, FileinsPEctor 3.5, un Editor Hexadecimal.
CRACKER: Arkanian   FECHA: 09/01/2001

 

 introduccion

"audaces fortuna juvat"
que traducido (muy) libremente quiere decir:
"este jodido programa no va poder conmigo"

la verdad es que no pensaba meterme en semejante follón cuando empecé con este programa pero la curiosidad pudo más que yo. veremos como derrotar manualmente este tipo de protecciones y para ello seguiremos la serie de pasos que detallo a continuación:

  • liquidar manualmente las rutinas antidebugger.
  • saltar por encima del chequeo crc-32.
  • hallar el punto de entrada original (oep) y volcar el programa.
  • conseguir una tabla de importaciónes "limpia".
  • reconstruir el ejecutable obtenido.
  • después de todo esto y para finalizar, ¿que nos queda?

antes de empezar una pequeña recomendación, ¡cuidado al instalar el programa!. antes de finalizar la instalación se produce una comprobación de la presencia de sotfice, por ello te recomiendo que, o bien no tengas activo softice o bien que hagas la instalación, si lo tienes activado, con frogsice funcionando con la opción bulletproof activada. si el programa de instalación detecta softice aparecerá la consabida ventanita de debugger detected y si lo instalas con frogsice funcionando con la opcion default settings saltará softice con un mensaje de error y el programa no se instalará correctamente.

ahora si, dicho esto, vamos alla...

 

 al atake

contacto con el enemigo

una vez instalado el programa utilizamos fileinspector para analizarlo, la pestaña compilador nos devuelve un aspack1.08.04 + asprotect, ya sabemos a que nos enfrentamos. echemos un vistazo a las secciones, todas a c0000040, estupendo..., ¿tres secciones .data y en una de ellas el entry point?, que curioso, ¿no hay sección .aspr?, parece que la cosa se complica.

ejecutamos agrapher.exe con frogsice activado y la opción bulletproof marcada. ventana de unregistered copy, shareware licence, insert register key, etc..., pero esto de momento no nos interesa.vamos a frogsice, concretamente a la opción view log y obtenemos esto:

=> agrapher
** softice detection ** code 0b, at cs:005a118b
attempting to load: siwdebug (string ref at cs:005a1160)

=> agrapher
** softice detection ** code 0b, at cs:005a118b
attempting to load: sice (string ref at cs:005a1154)

=> agrapher
** softice detection ** code 0b, at cs:005a118b
attempting to load: ntice (string ref at cs:005a1148)

=> agrapher
** softice detection ** code 01, at 0177:005a1338
interrupt:03h >eax=00000004h ebx=005a1750h ecx=0078fdech
edx=0078fcb4h esi=0056bbe8h edi=0058ebech

>ebp=4243484bh

seh proc address at cs:005a134c

por lo que parece "meltice" seguido de un "boundschecker" con una int3 (ebp=4243484bh, o sea bchk). de momento asequible.

bueno, cerramos agrapher, iniciamos procdump y solo por probar cambiamos las características de cualquier sección a e0000020, iniciamos de nuevo el objetivo y aparece una ventana de error con un mensaje de file corrupted. una comprobación de integridad de archivo (crc), asequible también.

primeras escaramuzas

1.- liquidar manualmente las rutinas antidebugger.

desactivamos frogsice y hacemos un crtl+d, asignamos las ventanas como siempre y metemos un bpx createfilea, volvemos al windows, ejecutamos el programa, softice salta, pulsamos f5 y veremos en la ventana de datos correspondiente a eax, la cadena \\.\siwdebug, pulsamos f11 para ver de donde venimos y empezamos trazando con f10 hasta ver tres kernel32!getlasterror seguidos, concretamente este código repetido tres veces:

call 0059d9bc
test al, al
jnz 0059db07
call kernel32!getlasterror
cmp eax, 02
jnz 0059db07

recuerda que las direcciones en tu ordenador serán diferentes. ahora puedes desactivar el bp mediante bd * , ya que si no saltará otra vez en el call 59d9bc, sigue trazando despacio con f10.

para \\.\siwdebug el primer getlasterror devuelve en eax 32, puedes cambiarlo manualmente a 02 (que es lo que el programa espera encontrar) o invertir el salto. para \\.\sice el segundo call 59d9bc devuelve en eax 01, cambialo a 00 o invierte el salto, si no el test al, al y el salto posterior nos llevan fuera. con \\.\ntice (¿estas trabajando con windows nt?) haz lo mismo. si no dejalo estar.

personalmente prefiero "nopear" los dos saltos, ya que es posible que el programa vuelva a comprobar, en un momento posterior, la presencia de softice así nos curamos en salud.

habilita el bp de nuevo con be * cuando saltes a un xor eax, eax, sigue despacio con f10 y verás una serie de jmp seguidos que van cambiando cuando pasas por encima de ellos. verás poco después como aparece una instrucción del tipo mov ebp, 4243484b y un poco más abajo como aparece un mov ax, 0004. aquí tienes dos opciones:

  1. modificar ebp para que el valor sea superior a 0x4243484bh. por ejemplo cambia el 42 por un 52. sigues con f10, aparece una int3 y saltas al bp en kernel32!createfilea.
  2. ensamblar la linea mov ax, 0004 a jmp 0, pulsas f5 y aterrizas igualmente en kernel32!createfilea.

he optado por la segunda opción, no vaya a ser que haya una comprobación posterior.

de aquí f11 de nuevo y volvemos a estar en el código de asprotect. sigue con f10.

2.- saltar por encima del chequeo crc-32.

seguimos con f10 durante un rato, observa los valores que devuelven los call en eax, cuando veas un valor extraño (recuerda que has cambiado las características de una sección con procdump), apuntalo, sigue hasta que veas el siguiente código:

mov eax, [ebp-14] --> el valor que has apuntado, 02de855f en mi caso.
cmp eax, [ebp-10] --> el valor bueno, concretamente 79428769.
jz 0059dcc9 --> cambialo por un jmp.

bien, ya hemos saltado, sigue con f10.

inicio de las hostilidades

3.- hallar el punto de entrada original (oep) y volcar el programa.

aquí si que no hay estrategia que valga. he probado soluciones que he encontrado en diferentes ensayos sobre asprotect y no funcionan. supongo que en cada programa serán diferentes, así que aquí tienes lo que he encontrado yo y mi solución para encontrar el punto de entrada real.

  1. sigue con f10 hasta que veas dos jmp's seguidos. parate ahí.
  2. desplaza el código hacia abajo con las teclas del cursor hasta que veas un ret 0004.
  3. anota la dirección del último call antes del ret 0004 y haz un g [dirección].
  4. entra en ese call con f8. sigue con f10 hasta ver dos call seguidos.
  5. entra en el segundo call con f8, verás un jmp kernel32!raiseexception. sigue con f10 y salta.
  6. de kernel32!raiseexception pasamos a kernel32!findclose sigue con f10 hasta un call [ebp+18].
  7. pulsa f8 y vuelve a asprotect.
  8. f10 hasta encontrar, otra vez, dos call seguidos. otra vez al 2º call. la jugada se repite tres veces.
  9. sigue con f10 hasta un setz seguido de dos jmp's. f8 y despacio. observa como cambia el código.
  10. debajo de los jmp's aparece un mov eax, [ebp-08]. en eax está el oep.(4e7368).
  11. encuentras un popad y poco después aparece un ret. alto ahí.

paramos en el ret y ensamblamos la linea a jmp eip. crtl+d vamos procdump32 y hacemos un volcado completo de agrapher.exe. vamos a llamarlo por ejemplo prueba.exe. cambia el entry point (00140001) por el que hemos hallado restando la image base (004e7368 - 00400000 = 000e7368).

si analizamos prueba.exe con fileinspector veremos que la pestaña compilador devuelve borland delphi 3.0 pero la tabla de importaciones (pestaña importaciones/exportaciones) parece estar incompleta. todo es aparentemente normal menos la rama kernel32.dll que tiene ahora tres subramas con tres números (0, 397 y 553) mientras que en el programa original estas subramas están ocupadas por getmodulehandlea, getprocaddress y loadlibrarya.

para subsanar esto hay que copiar manualmente, mediante un editor hexadecimal, la sección .data (¿.aspr camuflada?) que contiene el entry point de agrapher.exe a nuestro volcado prueba.exe. utilizaremos de nuevo fileinspector para volcar la sección ya que procdump32 se muestra ineficaz y devuelve un mensaje de error de lectura/escritura.

una vez copiada la sección analizamos de nuevo prueba.exe con fileinspector, parece que todo esta correcto y cada cosa en su sitio.

retirada estrategica

pero no funciona. si ejecutas prueba.exe veras como salta softice en una dirección cbxxxx con código inválido y el consiguiente cuelge. aquí, después de muchas pruebas, algunas de ellas autenticas tonterias y visto que la cosa no funcionaba, fue necesario documentarse sobre lo que estaba pasando y lo que realmente hace asprotect.

el truco de todo esto es que exite una tabla de importaciones original dentro de la sección .data pero las únicas funciones que usa son las tres señaladas arriba, el resto está puesto como cebo.

de hecho las direcciones de algunas importaciones contenidas en la sección .idata desempacada (ojo .idata, no .data) son correctas pero la mayoría apuntan a una tabla contenida en la dirección cbxxxx (en mi caso) que a su vez puede tener la dirección buena o encriptada. también tenemos unas llamadas indirectas a kernel32!getprocaddress en la citada tabla.

vaya follón...

de nuevo a la carga

la tabla de importaciones

tenemos cuatro casos diferentes en el código de la tabla de importaciones. los voy a enumerar según van apareciendo cuando se crea la sección .idata y pondré algunos ejemplos:

1- la dirección real de la api es relocalizada en la tabla cbxxxx.
edi = 004ec154, eax = 00cb45a4, ecx = bf2cce2e
edi apunta a la dirección de la sección .idata que se está creando. el valor de eax en 00cb45a4 es e9 2e ce 2c bf. es decir, un salto con el desplazamiento contenido en ecx. así que, si cogemos eax le sumamos ecx y le añadimos los 5 bytes correspondientes a la instrucción tendremos la dirección real donde apunta: 00cb45a4 + bf2cce2e + 5 = bff813d7 --> kernel32!getcurrentthreadid.

2- llamada indirecta a getprocaddress
edi = 004ec1a8, eax = 005a1aa8, ecx = 0078fc6b
el valor de ecx es fijo cada vez que la dirección (redirecionada en eax) de kernel32!getprocaddress se incorpora a la sección .idata.. con un d ecx vemos la cadena getprocaddress en la ventana de datos.

3- la llamada a la api está encriptada dentro de la tabla cbxxxx.
edi = 004ec220, eax = 00cb4701, ecx = 00000000
el valor de eax es 68 04 48 cb, o sea que eax+1 contiene 00cb4804 que es la dirección que se copia a la tabla de importación. esta dirección nos manda a 00cb4962, de ahí a cb4a28 y por fin aterrizamos en 680e48cb --> advapi32.dll

4- la dirección de la api es copiada directamente a .idata
edi = 004ec228, eax = 65341610, ecx = 0078fc4c
no hay problema, el valor de eax, 65341610 --> oleaut32.dll, se copia directamente a la tabla. ecx es también fijo en este caso.

volvemos a empezar

acabamos de saltar después de cambiar el salto de comprobación del crc. ahora lo que necesitamos es lo siguiente:

  • la dirección del código que genera la sección .idata.
  • idem para el código que se usa para "desredireccionar" las direcciónes api del caso 3.
  • una rango de direcciones vacio para insertar un pequeño trozo de código.
  • la dirección de kernel32!getprocaddress.

te ahorraré tiempo y molestias:

  • s 0 lffffffffff ac 08 c0 74 e4 para el primer punto.(59a606)
  • s 0 lffffffffff 55 8b ec 81 c4 f8 fe ff ff 53 56 para el segundo. (59a300)
  • buscalo tu mismo, mueve la ventana de código hasta encontrar un montón de add [eax], al seguidos.
  • usa softice, para mi máquina es bff76dac.

la busqueda para los dos primeros puntos hay que realizar así, ya que asprotect utiliza, cada vez que inicias el programa, unas direcciones de memoria diferentes y aparentemente aleatorias. si tienes ganas cambia las caracteristicas de la sección .data donde está el entry point a e0000020 y mete el programa en el loader de softice, si encuentras como lo hace, me llamas. (¿gettickcount tiene algo que ver en todo esto?).

3.- conseguir una tabla de importaciónes "limpia".

bueno a lo que íbamos, el código que crea la sección .idata es el siguiente, hemos aterrizado aquí después de un g [resultado busqueda primer punto]:

:59a606 ac lodsb
:59a607
08c0 or al, al
:59a609
74e4 jz 0059a6ef
:59a60b 4e dec esi
:59a60c 56 push esi
:59a60d 53 push ebx
:59a60e 80f802 cmp al, 02
:59a611 7407 jz 0059a61a
:59a613 0fb64e01 movzx ecx, byte ptr [esi+01]
:59a617 41 inc ecx
:59a618 eb05 jmp 0059a61f
:59a61a b904000000 mov ecx, 00000004
:59a61f 41 inc ecx
:59a620 01ce add esi, ecx
:59a622 e8b5fdffff call 0059a3dc --> cambiamos el call por un jmp a la dirección libre.
:59a627 ab stosd
:59a628 ebdc jmp 0059a606

supongamos que la dirección libre está en 0059b95c y que hemos encontrado la dirección 0059a300 para desenmarañar las relocalizaciones del caso 3 (hay que asegurarse que la dirección encontrada queda dentro del rango de direcciones de asprotect), queda otra cosa por hacer. hay que "nopear" la dirección 0059a300+95 con tres 90, si no el call no regresa adecuadamente y el cuelge es seguro. los bytes a cambiar en 0059a300+95 son 89 45 04 por 90 90 90.

otra cosa, buscando una dirección libre y moviendo la ventana de código con avpag, nueve pantallas después del código de arriba, se encuentra el setz seguido de los dos jmp's, aprovechamos para ponerle un bp y acortar la busqueda del oep.

metemos el siguiente código con softice:

:59b95c call 005a3dc --> la llamada de arriba.
:59b961 cmp ecx, 0078fc6b --> ¿es el caso 2º?
:59b967 jz 0059b983
:59b96c cmp ecx, 00 --> ¿es el caso 3º?
:59b96c jz 0059b98a
:59b96e cmp ecx, 0078fc4c --> ¿es el caso 4º?
:59b974 jz 59b97d --> caso 4º, salta directamente y almacena.
:59b976 add eax, ecx --> caso 1º, dirección relocalizada.
:59b978 add eax, 5
:59b97d stosd --> guarda en la dirección de la tabla apuntada por edi.
:59b97e jmp 0059a606--> vuelve arriba, al código que crea la tabla.
:59b983 mov eax, bff76dac --> caso 2º, dirección de kernel32!getprocaddress.
:59b988 jmp 0059b97d --> salta y guarda.
:59b98a push dword ptr [eax+1] --> caso 3º, valor encriptado.
:59b98d call 0059a300 --> llamada a la rutina de desencriptación.
:59b992 jmp 0059b97d --> salta y guarda.

una vez ensamblado todo esto trazamos un rato con f10 para ver si funciona. una vez que estamos seguros le metemos un f5 para saltar al bp puesto en la instrucción setz. limpiamos los bp's y con f8 seguimos hasta el ret y ensamblamos la linea a jmp eip.

4.- reconstruir el ejecutable obtenido.

esta parte ya la conoces:

  • volcado completo mediante procdump.
  • volcar con fileinspector la sección .data que contiene el entry point del programa original.
  • copiar manualmente con un editor hexadecimal la sección al archivo volcado.
  • con procdump, cambiar el entry point por el que hemos hallado (oep - image base).
  • finalmente, reconstruir la cabecera del archivo.

copiamos el archivo prueba.exe al directorio advanced grapher y probamos, ...felicidades, ¡funciona!.

las últimas defensas

5.- después de todo esto y para finalizar, ¿que nos queda?

pues queda la protección de un programa shareware, el límite de tiempo de 30 días.

bpx getlocaltime, f11 y luego f10 durante bastante rato hasta llegar a un cmp dword ptr [ebp-10], 1e. cambia el salto que sigue a esta instrucción y el progama no caducará pero, seguirá apareciendo la molesta pantalla del inicio del programa recordandote que es shareware, que hay que registrarse, etc... .

¿el programa está registrado? justo encima de la comparación anterior tenemos:

cmp byte ptr [ebp-05], 00
jnz 4e5f7c

invierte el salto y haz desaparecer la molesta "nagscreen" del inicio del programa. el programa piensa ahora que está registrado y funciona perfectamente.

hacer desaparecer las referencias a "unregistered copy" que quedan ya es cosa tuya. ahora puedes modificar el programa a tu antojo.

tu mismo.

buena caza.

"a posteriori nada hay más sencillo
que una utopía realizada."

 

karpoff spanish tutor: página dedicada a la divulgación de información en castellano, sobre ingeniería inversa y programación. email "colabora con tus proyectos"
www.000webhost.com