Karpoff Spanish Tutor 1999-2002

Programa: Advanced PDF Password Recovery  


 

PROTECCION: Asprotect 1.2x
Objetivo: Desempakado Manual con OllyDbg
Descripcion:
Programa que permite desencriptar los archivos con formato PDF. Es decir nos permite desproteger los archivos en PDF
Dificultad: Al principio fue muy dificil, pero una vez que se le coge el truco
DOWNLOAD: hhttp://www.elcomsoft.com
Herramientas: PEID 0.8, PEDITOR 1.7, Olly DBG 1.06, Memory Dumper v1.0, Revirgin y Topo
CRACKER:
DGrojo
  FECHA: 05/10/2002

 

 INTRODUCCION

 Bueno pues aquí estamos de nuevo con otro desempacado de Asprotect 1.2x, en este caso he considerado hacer este programa porque es ligeramente distinto a los demás, y es que ya me parecen muy sencillos, ahora que se de qué va el tema claro, xD.

En el caso de este he tenido que recurrir a Topo para resolver una molesta entrada, que no afecta al funcionamiento del programa, pero si afecta a la introducción de la clave de registro.

Sin más confiar que estos tutoriales os sean de gran ayuda, cuando debais enfrentaros a algún Asprotect.

 

 

 AL ATAKE

  

Antes que nada sería conveniente saber la base de la imagen y su largo, así que abrimos Peditor por ejemplo y cargamos el programa original:

Bueno pues anotamos lo siguiente:

Base de la imagen: 400000

Tamaño de la imagen: 138000

Como siempre en estos casos lo que deberemos hacer es analizar la víctima a ver si nos enteramos con qué está comprimido, el compilador, o su Punto Original de entrada del programa u OEP.

Abrimos PEID 0.8, y esto es justamente lo que nos va a mostrar:

Vemos que está empacado con Asprotect de mi amigo Alexey :D.

Pulsamos sobre el botón menú y lego pulsamos en OEP module para hallar el OEP, nos mostrará esto:

Como siempre he dicho, no os fieis, puede ser que sea bueno o no, eso si siempre es una referencia para saber por donde van los tiros.

Vamos a compararlo con el que obtengamos en Olly, así que abrimos Olly y cargamos el programa original en el, ejecutamos con F9, ya vamos ir encontrarndo una serie de excepciones que iremos superando con Mayusculas+F7, como unas 20 o así, hasta que llegue una en la cual se carga el programa original, es decir que ya no para. Bueno pues hacemos otra vez lo mismo hasta llegar a esta última dirección que será esta:

Momento en el cual ponemos un Break Point On Memory Access en Olly, a la sección donde esté el códigop ejecutable:

Ejecutamos una vez más con Mayusculas+F7 y F9, y estaremos en el OEP del programa:

Vale pues como vemos este coincide con el de PEID, bién vayamos a volcar a disco ahora mismo, así que no voy a volver a explicar el tema de porqué no funciona LordPE, ProcDump y demás porque ya lo he explicado en el tutorial de RAM Idle, así que directamente abrimos el Memory Dumper v1.0 que trabaja muy bién con Olly y ponemos esto:

Es decir seleccionamos el proceso de APDFPR que es el ejecutable original, seleccionamos para File Name, la localización y el nombre de nuestro archivo volcado, en Start Location ponemos la Base de la imagen y en Byte Count ponemos el largo de la misma, y lo hacemos como siempre como 0x porque está en hexadecimal. Tras un ratillo ya estará volcado el archivo.

Este archivo aún hay que arreglaro porque al tomarlo de la memoria tiene los offsets de disco y memoria descuadrados, así que como siempre abrimos el Peditor y cargar el archivo que acabamos de crear, vamos a seccions y con el botón derecho pulsamos en Dumpfixer, y estará ya arreglado en cuanto a offsets se refiere.

Bién, lo de siempre la famosa IAT, ya sabeis que Asprotect la destroza.

Ejecutamos el programa original, abrimos Revirgin (qué fantástica utilidad, aunque alguna mejora no le vendría nada mal, jeje) y sellecionamos el proceso relativo al apdfpr, que es este ejecutable original, vamos a OEP y ponemos el del programa 401000, pulsamos en Fetch IAT y nos saca algo como esto:

Y aquí me mosqueé cuando vi que sólo sacaba las funciones importadas del módulo Kernel32, uh uh algo no va bién, así que como siempre vamos a ver que pasa en Olly, es decir busquemos la IAT a mano. Pues entonces cargamos el programa en Olly llegamos al OEP, trazamos un poquito con F7, hasta que llegamos a lo que parece el salto a una función importada, es decir a esto:

Pulsamos con el botón derecho del ratón en la instrucción según se indica y ejecutamos Follow in Dump, Memory Address, en la ventana de datos nos aparecerá lo siguiente:

Entonces en esta ventana nos desplazamos hacia arriba, hasta encontrar no un bloque largo de ceros no os confundais sino, un bloque largo de ceros, y la primera palabra que esté escrita en esta sección, es decir hasta llegar a:

veis de lo que estaba hablando, que no os podeis descuidar, fijaos como debajo de eso que está escrito hay un bloque de ceros. Bién pues la dirección 4F1130 es el comienzo de la IAT, ahora sólo nos queda el largo, así que nos desplazamos hacia abajo para encontrar lo mismo, un bloque de ceros y antes la última palabra que haya escrita, es decir esto:

Así que el final de la IAT está en 4F2080, así que si restamos 4F2080 - 4F1130 nos da F50 que es el largo de la IAT.

Nos vamos entonces a Revirgin, y con el OEP tal cual estaba en 401000, el comienzo de la IAT, como offset F1130 y el largo F50, damos sobre el botón IAT Resolver, y luego a Resolve Again, y obtendremos esto:

Ahora ya tiene mejor pinta ya que saca funciones importadas de todo tipo, vaaaaaaale que me enrollo demasiado, damos a Show Unresolved y nos muestra lo siguiente:

Umm algo distinto de los otros que he hecho, y aquí mucho cuidado!, nos ha mostrado estas pero como siempre compruebo todo, si lo poneis en Show All y os desplazais para buscar estas entradas sin resolver casualmente os encontrareis con esta:

que casualmente no salió en las lista de las unresolved y no se porqué, ;)

Bueno en este caso no hay estrategia que valga, es decir no hay grupos iguales, no hay cosas que ya nos suenen, pero la experiencia cuenta y de antemano se que habrá entradas falsas, y que seguro que encontraremos las entradas de siempre GetModuleHandlea, GetprocAddress, GetCommandLineA y GetVersion (y que ya me huele que el resto siempre son falsas, y sólo hay que fijarse en cómo acaba la función si en RETN o RETN4), y que las detectaremos por mi especie de regla. Vamos a enumerarlas de todos modos y a ver que sacamos:

Entradas sin resolver:

(1) 11CE95C 4F13F0

(2) 11CE93C 4F13FC

(3) 11CE930 4F1404

(4) 11CE8DC 4F144C

(5) 11CE48C 4F145C

(6) 11CE8F8 4F1494

(7) 11CE94C 4F1504

(8) 11CE97C 4F1D68

Entrada 1:

Venga lo de siempre, con el programa cargado en Olly al haber llegado ya al OEP, hacemos Crtl+G en la ventana de código y ponemos el offset de la 1, que es 4F13F0, que lo hago así para ver el código y para ver desde donde se llama porque es un dato que puede hacer falta, y vemos con Crtl+R desde donde es llamado, pulso Enter ahí y vamos al final al siguiente código:

Es llamada en la dirección 4C8E7A, es decir tenemos:

004C8E7A FF25 F0134F00 JMP DWORD PTR DS:[4F13F0]

pulsamos Enter aquí y llegamos a esto:

011CE95C 55 PUSH EBP
011CE95D 8BEC MOV EBP,ESP
011CE95F E8 6467FFFF CALL 011C50C8
011CE964 5D POP EBP
011CE965 C2 0400 RETN 4

Esta entrada por experiencia ya me huele a que es falsa, pero de todos modos si quereis saber que es falsa poneos encima del Call y con la barra espaciadora, ensamblais la instrucción a un nop. Por cierto para saber donde va ese call os colocais encima pulsais Enter como un par de veces y va a GetVersion, y digo que se que es falsa porque GetVersion nunca lo hace así, siempre es con un Push cierto valor que devuelve a EAX.

Ya sabeis para esta entrada en Hexworkshop cargais el archivo volcado y con Crtl+G vais al offset de donde es llamada esta entrada, os acordais que la hallamos y es C8E7A, y cambias todos los bytes que aparecen en la sentencia del jmp por un retn4 es decir por c20400 y rellenais con 90 (nops). Bién esta ya estaría.

Entrada 2:

Más de lo mismo, Crtl+G a 4F13FC, luego Crtl+R y vemos esto:

004C8E7A FF25 F0134F00 JMP DWORD PTR DS:[4F13F0] pulsamos Enter aquí y llegamos al código de la entrada que es:

011CE93C 6A 00 PUSH 0
011CE93E E8 6D67FFFF CALL 011C50B0
011CE943 FF35 14671D01 PUSH DWORD PTR DS:[11D6714]
011CE949 58 POP EAX
011CE94A C3 RETN

vereis ya no me hace ni falta poner un breakpoint allí os lo voy a explicar, aunque con las reglas que os di se ve claro, si entrais en el Call con Enter un par de veces vereis que llama a GetModuleHandleA (falso) ya que luego carga EAX con el contenido de 11D6714, esto sin ejecutar ya vereis. Damos un Follow in Dump Memory Addres en la instrucción del Push, veremos que contiene esto:

011D6714 E0 BD A9 81 ཀྵ

es decir que al revés es 819ABDE0, que para vosotros será distinto, pues bién es el resultado de GetCommandLineA, es decir en Revirgin debereis de solucionar esta entrada con:

Module: Kernel32.dll Name: GetCommandLineA Address:BFF8C5DA (para hallar esta dir en SICE podeis hacer u GetCommandLineA)

Damos a Resolve Again y entrada 2 resuelta.

Entrada 3:

Igual Crtl+G en la ventana de código a 4F1404, luego Crtl+R y os vais a la instrucción del Jmp que es:

004C8E98 FF25 04144F00 JMP DWORD PTR DS:[4F1404]

pulsamos Enter aquí y estamos en el código de la entrada:

011CE930 E8 9367FFFF CALL 011C50C8
011CE935 A1 10671D01 MOV EAX,DWORD PTR DS:[11D6710]
011CE93A C3 RETN

No hace falta ni poner un break point, el call va a GetVersion, pero después carga EAX con el valor (ya sabeis como hallarlo):

011D6710 D1 90 FC FF ѐüÿ, que es FFFC90D1, pues bién lo hew comentado otras veces, cuando veais que EAX tiene un valor así que no nos dice nada, es que es una entrada falsa, la experiencia ya me lo hace hacer así, supongo que hasta que cambie Asproect.

Entonces en el hexadecimal del archivo volcado deberemos de ir al offset C8E98 y sustituir los bytes del jmp por un retn que es C3 y luego nopear hasta ajustar la memoria, o sea quedaría como C39090909090.

Ya está reseulto.

Entrada 4:

Crtl+G a 4F144C, luego Crtl+R y llegamos a :

004C8F04 FF25 4C144F00 JMP DWORD PTR DS:[4F144C]

pulsamos Enter aquí y tenemos:

011CE8DC 55 PUSH EBP
011CE8DD 8BEC MOV EBP,ESP
011CE8DF 8B45 08 MOV EAX,DWORD PTR SS:[EBP+8]
011CE8E2 85C0 TEST EAX,EAX
011CE8E4 75 07 JNZ SHORT 011CE8ED
011CE8E6 A1 24661D01 MOV EAX,DWORD PTR DS:[11D6624]
011CE8EB EB 06 JMP SHORT 011CE8F3
011CE8ED 50 PUSH EAX
011CE8EE E8 BD67FFFF CALL 011C50B0
011CE8F3 5D POP EBP
011CE8F4 C2 0400 RETN 4

Nos metemos en el call y resulta ser que es GetModuleHandleA, esta entrada si es verdadera, la experiencia ya me lo dice, de todas formas siempre comprobar a nopear el call, o la entrada completa en el salto con el RETN o RETN4 del final del código.

Vamos a Revirgin, y para esta entrada ponemos:

Module: Kernel32.dll Name: GetModuleHandleA Address: BFF77716

Damos Resolve Agsin para fijar ya este cambio, y entrada 4 resuelta.

Entrada 5:

Crtl+G en 4F145C y Crtl+R y vemos:

004C8F1C FF25 5C144F00 JMP DWORD PTR DS:[4F145C]

Damos Enter, y vemos:

011CE48C 55 PUSH EBP
011CE48D 8BEC MOV EBP,ESP
011CE48F 8B55 0C MOV EDX,DWORD PTR SS:[EBP+C]
011CE492 8B45 08 MOV EAX,DWORD PTR SS:[EBP+8]
011CE495 8B0D B8541D01 MOV ECX,DWORD PTR DS:[11D54B8]
011CE49B 8B09 MOV ECX,DWORD PTR DS:[ECX]
011CE49D 3BC8 CMP ECX,EAX
011CE49F 75 09 JNZ SHORT 011CE4AA
011CE4A1 8B0495 14531D01 MOV EAX,DWORD PTR DS:[EDX*4+11D5314]
011CE4A8 EB 07 JMP SHORT 011CE4B1
011CE4AA 52 PUSH EDX
011CE4AB 50 PUSH EAX
011CE4AC E8 076CFFFF CALL 011C50B8
011CE4B1 5D POP EBP
011CE4B2 C2 0800 RETN 8

Lo mismo la experiencia me dice que es correcta, podeis probar lo de la anterior, que así es como se debiese de hacer para saberlo.

El call llama a GetProcAddress, así que al Revirgin con:

Module: Kernel32.dll Name: GetProcAddress, Address: BFF76DA8.

Resolve Again y ya está.

Entrada 6:

Crtl+G en 4F1494 y luego Crtl+R, veremos esto:

004C8F70 FF25 94144F00 JMP DWORD PTR DS:[4F1494]

Enter aquí y el código es:

011CE8F8 6A 00 PUSH 0
011CE8FA E8 B167FFFF CALL 011C50B0
011CE8FF FF35 04671D01 PUSH DWORD PTR DS:[11D6704]
011CE905 58 POP EAX
011CE906 C3 RETN

Sucede lo mismo que con la entrada 2, pero si hacemos un Follow in Dump, memory address en el push, nos damos cuenta que vale;

011D6704 04 0A 00 C0 ..À, es decir C0000A04, que es el resultado de GetVerion.

Y más Revirgin, para esta entrada necesitamos:

Module: Kernel32.dll Name: GetVersion Address: BFF92F1B

Resolve Again.

Entrada 7:

Crtl+G 4f1504 y Crtl+R:

004C9018 FF25 04154F00 JMP DWORD PTR DS:[4F1504], Enter:

011CE94C 55 PUSH EBP
011CE94D 8BEC MOV EBP,ESP
011CE94F E8 2467FFFF CALL 011C5078
011CE954 8B45 08 MOV EAX,DWORD PTR SS:[EBP+8]
011CE957 5D POP EBP
011CE958 C2 0400 RETN 4

Bién como siempre la experiencia, pero comprobar a nopear el call o el salto hasta aquí con un Retn4, esta llamada es falsa, así que en el hexadecimal del archivo volcado deberemos ir al offset c9018 y sustituir el salto por un retn 4, es decir sustituir ff2504154f00 por c20400909090.

Bién si no nos dimos cuenta en Show Unresolved, en principio no pasaría nada, el programa ya se ejecuta con las funciones falsas parcheadas en el volcado y las resoluciones perfectamente, sólo que el botón de la llavecita que sirve para registrarnos no funciona, y da un error precisamente en la dirección de la entrada 8, claro porque no lo hemos resuelto.

En principio lo que podemos resolver en revirgin debería quedar así:

Entrada 8 y ufff que entrada, lo nunca visto hasta ahora:

Bién hasta aquí seguimos despiertos no, porque buena falta va a hacer. No se como lo habrán hecho los chicos de ElCom creadores de este fantástico programa, o Alexey Sholovnikov creador de Asprotect, pero esta entrada llama a todo un recurso, es decir que no es una simple entrada verdadera o falsa y ya está. Repito que el programa funcionaría perfectamente salvo el botón de la llave para registrarnos que ya es bastante logro (a mi ahora porque tengo experiencia pero este programa me ha costado lo mio, de hecho por el empecé jejejeje)

Hagamos Crtl+G a 4F1D68 y luego Crtl+R:

004C94E0 FF25 681D4F00 JMP DWORD PTR DS:[4F1D68]

Damos a Enter en la instrucción y atención veremos esto:

011CE97C 55 PUSH EBP
011CE97D 8BEC MOV EBP,ESP
011CE97F 53 PUSH EBX
011CE980 8B5D 08 MOV EBX,DWORD PTR SS:[EBP+8]
011CE983 8B45 18 MOV EAX,DWORD PTR SS:[EBP+18]
011CE986 50 PUSH EAX
011CE987 8B45 14 MOV EAX,DWORD PTR SS:[EBP+14]
011CE98A 50 PUSH EAX
011CE98B 8B45 10 MOV EAX,DWORD PTR SS:[EBP+10]
011CE98E 50 PUSH EAX
011CE98F 6A 05 PUSH 5
011CE991 8B45 0C MOV EAX,DWORD PTR SS:[EBP+C]
011CE994 50 PUSH EAX
011CE995 53 PUSH EBX
011CE996 E8 C566FFFF CALL 011C5060
011CE99B 50 PUSH EAX
011CE99C 53 PUSH EBX
011CE99D E8 3E67FFFF CALL 011C50E0
011CE9A2 50 PUSH EAX
011CE9A3 E8 4067FFFF CALL 011C50E8
011CE9A8 50 PUSH EAX
011CE9A9 53 PUSH EBX
011CE9AA E8 5967FFFF CALL 011C5108
011CE9AF 5B POP EBX
011CE9B0 5D POP EBP
011CE9B1 C2 1400 RETN 14

Impresionante no? hay 4 llamadas a APIS, ufff ufff un cafecito? jejeje

Tenemos que la call 011C5060 si pulsamos Enter ahí 2 veces nos lleva a

86EB2CC8 68 2A0AF8BF PUSH KERNEL32.FindResourceA

La call 011C50E0 a 86EB2BD8 68 220BF8BF PUSH KERNEL32.LoadResource

Call 011C50E8 a 86EB2BC8 68 482FF9BF PUSH KERNEL32.LockResource, y por último:

call 011C5108 a 86EB2DA8 68 5112C0BF PUSH USER32.DialogBoxIndirectParamA

Aquí he probado de todo, desde nopear cada call, una a una, hasta en el jmp poner un retn 14, nada, el botón llave no funcionaba, lo único que no he probado es a hacer combinaciones de nops con los call, aunque creo q es inutil, ya que si nopeas uno y falla, me imagino que nopeando 2 fallará más.

Mi única solución fue Topo, así que lo abrí con mi archivo volcado, ya reparadas las entradas y parcheadas las entradas falsas y tenemos:

Le decimos qque queremos una nueva sección, luego en bytes to be added debemos darle los bytes de la entrada.

Bueno contamos todos los bytes de esta entrada desde el comienzo hasta el retn14 incluido y son unos 56 bytes, así que en topo, en Bytes to be added se lo indicamos el 56, que lo pide en decimal, pulsamos sobre Do it! y saldrá algo como esto:

Es decir que ha creado una nueva sección de 56 bytes a partir de 53B000 rellenada con 90 que en el hexadaecimal se ven clarisimos en el offset que indicó Topo, bién bién, vamos a extraerlos para que nos sea más fácil y no hacerlo a pedales, ya se os ocurre como no?, si pues ese memory Dumper v1.0, en Star Code Location le poneis 0x11CE97C y en Bytes Count 0x38 (en decimal 56), le podeis decir que el archivo para no liaros q lo vuelque a 11ce97c.hex.

Así q tenemos el tramo y la sección, pues nos vamos al editor hexadecimal y cargamos nuestro archivo volcado de apdfpr, y en el offset que indicó Topo es decir haciendo Crtl+G 13AC00 veremos los 56 nops que metió, así que ahora los seleccionamos todos, abrimos nuestro 11ce97c.hex, seleccionamos también todos estos, los copiamos y los reemplazamos en nuestro volcado del apdfpr donde los teníamos seleccionados.

Ah y se me olvidaba casi, la dirección a la que apunta 4F1D68 que es precisamente 11ce97c hay que cambiarla como es lógico por 53B000, así que haceis Crtl+G F1D68 y allí sustutuis 7ce91c01 por 00b05300 ok?.

Ya está todo?, nooo, si ejecutais el programa que ya funcionaba y la llave, vereis que sigue sin funcionar, claro porque nos falta ajustar las calls para que apunten a las funciones concretas, así que para ver cómo se hace nos vamos a Olly y cargamos el volcado de apdfpr, y hacemos Crtl+G 53B000, que es la dirección de nuestra sección, o también Crtl+G 4F1D68 y Crtl+R + Enter:

Antes de cualquier cambio, fijaos en los offsets de las call o mejor dicho anotaos dichos offsets y los bytes que hay, pora luego cambiarlos por los que vamos a meter a continuación.

Si os fijasteis arriba cuando puse que era cada call teníamos la dir de la call pero al revés, es decir teníamos como direcciones estas:

Find ResourceA-----------------BFF80A2A

LoadResourceA-----------------BFF80B22

LockResourceA-----------------BFF92F48

DialogBoxIndirectparamA--------BFC01251

pues bién vamos Call a Call, nos ponemos encima y ensamblamos la instrucción:

En la primera la Call 5316e4, deberemos poner call 0xBFF80A2A

Call 531764, deberemos ponerla como call 0xBFF80B22

Call 53176C, como Call 0xBFF92F48, y por último la:

Call 53178C como call 0xBFC01251

Se pone como 0x porque sino no reconoce la dirección, y nos debería quedar como esto:

Como tenemos los offsets, los bytes que se deben cambiar y por cuales se deben cambiar, mirar las dos imágenes, nos vamos al editor hexadecimal, le cargamos el programa volcado del apdfpr y lo cambiamos estos bytes en estos offsets, podeis usar una calculadora de offsets, ya que son direcciones como la de comienzo 13AC00.

Bién espero que lo consiguieseis igual que yo y que os funcione el programa. Ha sido un placer.

Confío en no equivocarme mucho, al menos lo he intentado, de todas maneras si conseguís una solución mejor que la mia por favor háganmelo saber, les estaría muy agradecido.

 

 

CONSIDERACIONES FINALES

Este ha sido el Asprotect más dificil que me he encontrado hasta la fecha, espero que aunque ahora resulte más o menos fácil a mi me ha costado mucho tiempo dar con la clave del problema.

Quisiera agradecer como siempre a Ricardo Narvaja sus enseñanzas, porque es un verdadero maestro, a Auspex por ser el primero en resolver uno al menos en español, jeje y ser un buén amigo y como siempre a todos los chicos de Crackslatinos, Red_Hawk, FLIPI, Danielf, XYzero (algún día se resolverá todo jeje), Mr_Gandlaf entre otros.

Gracias a todos. Seguiremos en la lucha.

DGrojo

 

 

Karpoff Spanish Tutor: Pagina dedicada a la divulgacion de informacion en Castellano, sobre Ingenieria Inversa y Programacion. Email "Colabora con tus Proyectos"
www.000webhost.com