Karpoff Spanish Tutor 1999-2002

Programa:
Power Guard v1.0 (Con Armadillo 2.60)


 

PROTECCION: Armadillo 2.60
Objetivo: Desempakar
Descripcion:
Protege archivos hechos en PowerPoint mediante contraseñas.
Dificultad: No apto para corazones débiles (de todas formas depende del Cracker, de su experiencia)
DOWNLOAD: hhttp://www.moonlight-software.com
Herramientas: SofIce, IceDump, Revirgin, PEiD, LordPE, Hex WorkShop, API Spy 32 y un bueno bocadillo de jamón.
CRACKER:
AUSPEX
  FECHA: 01/10/2002

 

 INTRODUCCION

 

Un saludo a toda persona que esté leyendo este tute.

Lo primero de todo deciros que esta va a ser mi nueva plantilla para escribir mis tutoriales, espero que os guste, a mi me gusta =). El logo me lo ha diseñado mi buen amigo el Pr@fesor X, muchas gracias, queda muy bién.

Bueno, aquí os presento a este bichito, Armadillo, en su última versión, 2.60, y cuyo desempakado me ha llevado 4 o 5 días de pura dedicación, estudio y bastantes broncas por llegar tarde a los sitios, =P. La verdad es que está bastante bien esta protección, aunque volvemos a demostrar a sus programadores que han vuelto a cagarla (siento ser tan duro, pero la realidad es esa), un intento inútil por mejorar la eficiencia de Armadillo. De todas formas reconozco que esta nueva versión se las trae en paquete (y nunca mejor dicho, jajaaa), pero sólo llevo unos meses en este mundo del cracking y he conseguido desarmarla, un experto lo habría hecho en cuestión de pocas horas.

Bueno, vayamos ahora a lo realmente interesante: el Desempakado de Armadillo 2.60

 

 AL ATAKE

  

Para comenzar hagamos un estudio previo del ejecutable. Para ello utilizaremos el PEiD (últimamente me ha detectado muchas protecciones bastante recientes):

Como vemos se trata de Armadillo, pero la versión de este no me la concreta. De todas formas os puedo decir con bastante seguridad que se trata de la versión 2.60, os diré por que: versiones anteriores me las detecta el PEiD, hasta la 2.52 y además de eso, he empakado el NotePad con la versión 2.60 de Armadillo y al analizarlo con el PEiD me da el mismo resultado. Por otra parte estudiando el funcionamiento del NotePad empakado he observado un comportamiento similar, por lo que deduzco que se trata de la misma versión.

Ahora pasaré a explicaros de la manera más clara posible como funciona esta protección. Sólo os comentaré lo más importante y dejaré a un lado lo que considere menos interesante de la protección.

Los Gemelos golpean dos veces

Pronto entendereis el por qué de este título.

Cuando nosotros arrancamos el programa, y una vez cargado en memoria y ejecutándose, este crea un nuevo proceso con la función CreateThread. Sorprendentemente el proceso que crea se trata de ella misma, es decir, se vuelve a crear otro proceso similar, pero en modo suspendido. Por lo tanto nos encontramos con dos PowerGuard cargados en memoria, uno de ellos se encuentra detenido (para nosotros PW), el otro está ejecutando código(será PW(A)).

NOTA: De ahora en adelante llamaremos PW(A) al programa que manipula y PW al manipulado, ¿ok?, será más sencillo de entender (PW(A) ---> PowerGuard(Armadillo)).

Después de crear el proceso, PW(A) hace una serie de tonterías como por ejemplo crear un nuevo hilo de ejecución adicional con CreateThread, que ejecutará de forma paralela una función cuya dirección le pasa como parámetro. Al principio le di muchas vueltas a esta función, pensé que aquí estaba la clave, que había algo en esta función que era importante. Más tarde me di cuenta de que no servía para nada, al menos a mi no me valía.

A continuación PW(A) hace una lectura sobre el espacio de memoria de PW mediante la API ReadProcessMemory. Lee los dos bytes que se encuentran en el EntryPoint del programa y que son 55 8B. Es el comienzo de un Push EBP, Mov EBP, ESP. Los guarda en una zona de memoria y continua con un WriteProcessMemory, la escritura de dos nuevos bytes en el mismo lugar, de manera que sobreescribe el EntryPoint. Coloca EB FE que equivale a Jmp EIP. Como vemos pretende dejar colgado en un bucle infinito el programa, aunque por ahora se encuentra suspendido.

Le sigue una serie de operaciones que no comentaré, sólo os diré que vuelve a correr PW, ya sabemos que en un loop infinito, durante un tiempo determinado hasta que, de nuevo, lo suspende. PW(A) continua haciendo operaciones...

Ahora biene lo realmente interesante de este Armadillo. LLama a la función ResumeThread con la que reanudará la ejecución de PW, y seguidamente PW(A) engancha a PW como proceso de depuración, es decir, que Armadillo quiere depurar el programa original, hace una llamada a la función DebugActiveProcess, ¿para qué?, ahora os lo explico: Primero os diré que Windows posee una serie de API's para depurar programas, y la manera de hacerlo será mediante eventos que provoque el proceso que está siendo depurado, un ejemplo podría ser que PW(A) quisiera sacar por pantalla una ventanita con el mensaje "Hasta Luego" cuando PW terminase con la ejecución. Si PW finaliza este envía un evento, EXIT_PROCESS_DEBUG_EVENT, al depurador el cual lo recivirá y actuará según sea, en este ejemplo, sacar mensajito. Pués lo dicho, Armadillo se ha anexado a PW para depurarlo, y ahora vuelve a suspender el proceso con SuspendThread (la ostia, parece que no lo va a dejar correr trankilo al pobre, xD).

NOTA: Para que no os perdais, el proceso depurador es PW(A), y el depurando es PW

Ahora nos volvemos a encontrar con la API WriteProcessMemory, de forma que vuelve a escribir los dos bytes que machacó y que había guardado en una dirección, 55 8B. De esta manera eliminamos el Jmp EIP.

Para continuar os explicaré un poco para que sirven dos funciones importantísimas en los depurados (ambas son llamadas por el proceso depurador, PW(A)):

1- WaitForDebugEvent: El proceso depurador se queda esperando hasta que el depurando suelte un evento. Pudiera ser que nunca soltará el evento, pero para eso se le pasa otro parámetro que es, digamos, un tiempo de expiración. Una vez se sale de esta llamada, el proceso depurador continuará con su ejecución, tratando así el resultado de salida, y el proceso depurando se queda suspendido.

2- ContinueDebugEvent: El depurador lo utiliza para que el proceso depurando continue con su ejecución. Es de entender que nos lo encontraremos un poco después del WaitForDebugEvent, cuando termine de tratar el evento.

Como podeis imaginar esto estará metido en un bucle que irá tratando todos los eventos que envíe el depurando, en nuestro caso, PW. El primer evento que se envía siempre es CREATE_PROCESS_DEBUG_EVENT, en nuestro caso, cuando PW(A) engancha a PW para su depurado.

Si seguimos traceando vemos que por fín Armadillo deja correr la aplicación con un ResumeThread. PW(A) entra en el bucle para tratar eventos. Si vamos observando las salidas de WaitForDebugEvent no daremos cuenta de que todas son LOAD_DLL_DEBUG_EVENT, PW está cargando todas las dll's que le hacen falta, está llamando a la función LoadLibraryA.

Bueno, lo que sigue ahora es un poco complicado, pero lo intentaré explicar lo mejor posible. Vereis, depués de cargar todas las dll's, la salida de WaitForDebugEvent es EXCEPTION_DEBUG_EVENT, ocurre una excepción en el proceso en depuración. Os diré que este evento se envía justo antes de que el programa depurando comience a ejecutarse con su primera instrucción. Y ustedes os preguntareis, pues entonces, ¿significa que ya va a correr el programa original?, no, de hecho recordad que PW(A) y PW son hasta ahora el mismo ejecutable, y PW comenzará a ejecutarse en el mismo EntryPoint que PW(A). Y a lo mejor os preguntareis, entonces ¿va a volver a ejecutarse todo de nuevo?, menudo lio ¿verdad?, imaginaos, se convertiría en un caos hasta que el ordenador reventase, sería un bucle infinito, jajajaaa. Supongo que en algún momento PW comprobará si él es el programa depurador o el depurando. Además si observais en la memoria donde está cargado PW, está casi vacía de código ejecutable, algo anormal, por lo tanto aún no esta desempakado.

NOTA IMPORTANTE: ¿Cómo se mira en la memoria de otro proceso con el Sice?, muy sencillo, existe un comando, proc, con el que podemos ver los procesos que está en memroria, el que tiene el asterísco (*), es el proceso que estamos depurando nosotros con el sice, en este caso PW(A). Para adentrarnos en el espacio de memoria de PW sólo hemos de apuntarnos el campo Context que nos lo proporciona el comando proc y con este otro comando, addr CONTEXT, nos adentramos en el código de PW. En este desempakado, si no fuera por estos dos comandos no se como hubiera conseguido sacar el OEP, luego lo veremos.

De repente, nos aparece la Nag típica de Armadillo:

A todo esto, PW parece que está de nuevo suspendido, no estoy seguro, pero PW(A) ya no llama a la función ContinueDebugEvent, y las salidas de WaitForDebugEvent son fallidas, lo que significa que termina su tiempo de expiración. PW(A) está esperando a ver lo que hacemos.

Pulsamos ok, y de repente Armadillo vuelve a llamar a la función ContinueDebugEvent, reanuda la ejecución de PW. Fijaos ahora, la salida de WaitForDebugEvent es EXCEPTION_DEBUG_EVENT, de nuevo va a comenzar la ejecución de PW, supongo que con otro EntryPoint, pero no os confundais, no es el OEP, ahora veremos por qué. Si esperais al siguiente ContinueDebugEevnt - WaitForDebugEvent y volvemos a echar un vistazo al espacio de memoria de PW observamos como ahora si que tenemos el programa totalmente desempakado. Lo que ha sucedido es que cuando reanudó de nuevo la ejecución con otro EntryPoint lo que hizo PW fue desempakarse el solito, jejee, así que ya lo hemos pillado, pero ¿y el OEP, donde está, cómo lo detectamos, cuándo empieza a ejecutarse relamente el programa original?. Si habeis leido otros tutoriales mios, a lo mejor sabeis la respuesta, vamos pensad un poco como lo hariais...

Ya tenemos el código totalemente desempakado y va a comenzar su ejecución, a ver, ¿os acordais de nuestro comando favorito?, bpr INICIO FIN r if(eip>=INICIO)&&(eip<=FIN), pués ya está, vamos a ver donde empieza a ejecutar código nuestro PW. Nos adentramos de nuevo en su código (ya sabeis, con addr). Ahora con el comando map32 PowerGuard vamos a ver que secciones tiene. Esto es un poco de lógica y suerte, es decir, la primera sección que nos encontramos se llama CODE y va desde 401000 y 494000, es probable que OEP esté ahí, así que pongamos el comando bpr 401000 494000 r if(eip>=401000)&&(eip<=494000) y pulsemos intro a ver que sucede... Sice revienta donde nosotros queríamos, ya estamos en el OEP, ¿y como sabes que es el OEP?, sencillo, fijaos lo que me encuentro:

-----Push EBP <--- Caemos aquí

-----Mov EBP, ESP

-----..... (más código) .....

-----Call 461168

-----Call 404594 <--- Si nos metemos en esta llamada no volvemos a salir

-----Add [EAX], AL <--- Basura

-----INVALID <--- Ya no hay nada

Tiene toda la pinta de ser el OEP, además si traceais durante largo rato observareis como no salta a direcciones de memoria que no sean las del PW. Pues ya está, ahora sólo tenemos que colgar el programa y dumpearlo. Para ello ensamblamos un Jmp EIP en el OEP, mediante el comando a eip (intro), jmp eip (intro)(intro). Por supuesto, recordad los dos bytes que estais sobreescribiendo, que son 55 8B, pues luego tendremos que volver a dejarlo como estaba. Ahora usemos el LordPE para volcar el proceso, pero cuidado, tenemos dos PowerGuard ejecutandose, ¿cuál es el que hemos de elegir?. Se me ocurre una idea, si os fijais en el LordPE, las tareas que se están ejecutando, al lado de cada una teneis un número en hexadecimal, una especie de identificador que se llama PID, este es el processID o identificador del proceso. Sólo hemos de volver a meternos en el Sice, poner el comando proc y ahí encontramos un campo que se llama ProcessID, copiad el del PowerGuard que hemos dejado colgado (ahora será el que tenga el asterísco *), es el nuestro, y ahora mirais de nuevo en la lista de tareas del LordPE, teneis que volcar el que tiene el mismo identificador, pues es el que nos interesa, el depurando.

Bueno, con el mismo LordPE cambiemosle el OEP, en este programa es 4933E0. Ahora habramos un editor hexadecimal y volvamos a dejar el OEP como estaba. buscad el offset 4933E0 - 400000 = 933E0 y donde pone EB FE poned 55 8B. Ahora hemos dejado medio listo nuestro volcado, ¿que falta?, jeje, supongo que no tendremos una IAT decente, así que arreglemosla y construyamos una nueva Tabla de Importaciones para nuestro nuevo programa sin Armadillo.

Reparando lo irreparable

Pués si señores, esta IAT estaba bastante destrozada, pero eso no es todo, repararla no era cosa fácil y ahora vereis por que.

Bien, arrancamos nuestro programa, abrimos el Revirgin, elegimos nuestro proceso (hay dos, ya sabeis como elegir el bueno), colocamos el OEP y pulsamos Fetch IAT, nos saldrán estos datos: IAT RVA = 98140, Length = 6BC, pero como siempre digo, teneis que comprobar esto, no os fieis, de hecho el Length correcto era 6AC. Una forma de comprobarlo es ir variando los rangos en el mismo Revirgin y pulsar de nuevo IAT Resolver.

Si observais la IAT tenemos 413 entradas de las cuales 23 no están resueltas, ¡vaya, 23 entradas! habrá que tener paciencia e ir reparandolas una a una. Pero lo malo no es que sean 23, lo realmente jodido de esto es que cada entrada apunta a una dirección donde no sólo no hay API sino que además tenemos verdaderos trozos de código que cualquiera sabe lo que hacen. He de reconocer que cuando vi esto me asusté muchisimo, pensé que sería imposible reparar aquello, pero ahora vereis como no era para tanto. Nos os voy a poner los cachos de código que había en cada entrada por que se haría demasiado largo esto, pero si os pondré uno como ejemplo para que os hagais una idea de como eran, no os asusteis, jejee:

NOTA: Las direcciones están cambiadas, pero es por el tema de volcar sólo ese trozo de código y mostrarlo con el W32Dasm, como veis empieza por 00000000 y los saltos son todos relativos a esta dirección.

-----00000000 55 push ebp <--- Dirección a donde apunta una de las entradas
-----00000001 8BEC mov ebp, esp
-----00000003 53 push ebx
-----00000004 56 push esi
-----00000005 57 push edi
-----00000006 FF7508 push [ebp+08]
-----00000009 E8BBFFFFFF call FFFFFFC9 <--- Cuidaito con esto, la API puede estar aquí dentro. En este caso no había nada
-----0000000E 84C0 test al, al
-----00000010 59 pop ecx
-----00000011 0F8487000000 je 0000009E
-----00000017 8B5D10 mov ebx, dword ptr [ebp+10]
-----0000001A 33FF xor edi, edi
-----0000001C 3BDF cmp ebx, edi
-----0000001E 7406 je 00000026
-----00000020 393B cmp dword ptr [ebx], edi
-----00000022 7402 je 00000026
-----00000024 893B mov dword ptr [ebx], edi
-----00000026 FF7508 push [ebp+08]
-----00000029 E8ACFFFFFF call FFFFFFDA <--- Aquí tampoco había nada, sólo basura y más basura
-----0000002E 8D3440 lea esi, dword ptr [eax+2*eax]
-----00000031 59 pop ecx
-----00000032 C1E602 shl esi, 02
-----00000035 39BE48F4A200 cmp dword ptr [esi+00A2F448], edi
-----0000003B 7F05 jg 00000042
-----0000003D 83C8FF or eax, FFFFFFFF
-----00000040 EB6E jmp 000000B0
-----00000042 397D14 cmp dword ptr [ebp+14], edi
-----00000045 8B864CF4A200 mov eax, dword ptr [esi+00A2F44C]
-----0000004B 7505 jne 00000052
-----0000004D 8B450C mov eax, dword ptr [ebp+0C]
-----00000050 EB1C jmp 0000006E
-----00000052 837D1401 cmp dword ptr [ebp+14], 00000001
-----00000056 7413 je 0000006B
-----00000058 837D1402 cmp dword ptr [ebp+14], 00000002
-----0000005C 75DF jne 0000003D
-----0000005E A144F4A200 mov eax, dword ptr [00A2F444]
-----00000063 8B0D4800A300 mov ecx, dword ptr [00A30048]
-----00000069 03C1 add eax, ecx
-----0000006B 03450C add eax, dword ptr [ebp+0C]
-----0000006E 3BC7 cmp eax, edi
-----00000070 7D0D jge 0000007F
-----00000072 6883000000 push 00000083
-----00000077 FF151C91A200 call dword ptr [00A2911C] <--- Si buscas aquí te encontrarás una referencia a SetLastError
-----0000007D EBBE jmp 0000003D
-----0000007F 3B0540F4A200 cmp eax, dword ptr [00A2F440]
-----00000085 730F jnb 00000096
-----00000087 57 push edi
-----00000088 53 push ebx
-----00000089 50 push eax
-----0000008A FFB650F4A200 push dword ptr [esi+00A2F450]
-----00000090 FF156891A200 call dword ptr [00A29168] <--- Aquí dentro encontré una referencia a SetFilePointer
-----00000096 89864CF4A200 mov dword ptr [esi+00A2F44C], eax
-----0000009C EB12 jmp 000000B0
-----0000009E FF7514 push [ebp+14]
-----000000A1 FF7510 push [ebp+10]
-----000000A4 FF750C push [ebp+0C]
-----000000A7 FF7508 push [ebp+08]
-----000000AA FF156891A200 call dword ptr [00A29168] <--- Y por aquí aparece una a SetFilePointer también
-----000000B0 5F pop edi
-----000000B1 5E pop esi
-----000000B2 5B pop ebx
-----000000B3 5D pop ebp
-----000000B4 C21000 ret 0010

¡Qué, a que acojona ver esto!, ¿eh?, pues imaginaos ver cosas parecidas en cada una de las entradas sin resolver de la IAT. Bueno, jejeee, he de reconocer que me busqué el ejemplo mas cabrón, sólo os quería asustar, las demás no tenían trozos tan grandes, pero si que nos encontrabamos varias referencias a diferentes API's, como podeis ver en esta, o incluso ninguna referencia, es decir, que estaban ocultas en otros CALL (una verdadera putada). Y ahora ¿a cuál hacemos caso?. Pués esto se puede hacer de dos maneras, al menos fueron las que se me ocurrieron a mi. Una de ellas podría ser poner un bpx al comienzo de cada entrada de la IAT (las direcciones donde apuntan) e ir mirando que API's se llaman y cuales no, ¡ojo! cada vez que resolvais una eliminad el bpx de esa, pués es posible que se llame muchas otras veces y no acabareis nunca. Otra forma más rápida pero menos eficaz sería hacerlo a ojo, por ejemplo, en el código de arriba ¿cual eligiríais ustedes?... fijaos que hay dos llamadas a la misma API, no sé, veo más lógico que sea SetFilePointer, ¿no?. Yo preferí hacerlo de esta segunda manera, fue mucho más rápido.

NOTA IMPORTANTE: Tened mucho cuidado con los otros CALL, los que no hacen llamadas a direcciones relativas, pues al principio no les eché cuenta y me encontré con códigos donde tuve problemas por no mirar dentro de dichas llamadas, ¡muy, muy imoprtante!, ¿eh?, la función API puede que esté ahí dentro.

Bueno, pues ya está. Fui resolviendo la IAT en memoria con el programa detenido en el OEP y cuando acabé, después de un largo rato, continué con la ejecución del programa y todo funcionó a las mil maravillas, no hubo ningún problema. Estuve probando las funciones de PowerGuard y parecía que no había fallos. Ahora había que construir la nueva Tabla de Importaciones y para eso con el programa corriendo y las entradas resueltas en memoria volvemos a realizar las mismas operaciones con el Revirgin. Esta vez nos aparecen todas las API's, pues ya las habíamos resuelto en memoria. Ahora pulsamos generate!, elegimos nuestro volcado y se acabó lo que se daba.

Fin de la historia

Pues si señores, esto realmente se acabó, si probais nuestro programa volcado y arreglado observareis que funciona perfectamente, pero ya no está siendo depurado por nadie, su ejecución es totalmente libre. Hemos eliminado la presencia de Armadillo, y podemos usar el programa todo lo que queramos..

Notas finales:

¡ Vaya !, se supone que ahora debería de decir: "Pero si realmente os gusta el programa, cómprenlo, los programadores os lo agradecerán", pués me niego a decir eso, simplemente por que el programa cuesta 40 dolares o lo que es lo mismo 46 Euros o lo que es lo mismo, cerca de 8000 pesetas. Sinceramente, una barbaridad para lo que realmente hace el programa, así que yo no soy el típico cracker que dice esas tonterías de frasesitas que lo hacen quedar bien, pero que luego utiliza todos los programas que crackean para su uso personal, sin pagar ni un duro.

Vamos a ver, se perfectamente que los programadores de estas aplicaciones hacen un buen trabajo, y no merecen que haya distribuciones ilegales rulando por ahí, después de haberse dejado la piel programandolas, pero pienso que también se violan los derechos del consumidor al intentar vendernos sus productos cuyos precios no están al alcance de todos, sólo de las grandes empresas. Además creo rotundamente que no hay nada de ilegal en descargarte un software de internet y manipularlo a tu antojo. Cada uno puede hacer con su disco duro lo que quiera, y el código está en el disco duro del ordenador. Yo puedo desensamblar zonas de mi disco duro, por que para eso lo he comprado y para eso es mio. Si en dicha zona del disco hay código que otra persona ha programado, es problema de la persona el que dicho código haya ido a parar a mi ordenador, pues estaba como libre distribución por internet, nadie lo ha robado. Y si quieren hacer propaganda de sus productos, que distribuyan copias no completas, o mejoren sus protecciones. Sinceramente, es un tema que me mosquea muchísimo, que haya gente que piense que lo que hacemos es completamente ilegal

Así que ya sabeis, si realmente os gusta el programa, busquen el crack por internet y revientenle la protección, a ver si así se dan cuenta estas empresas de que deben de bajar el precio de sus productos para que desaparezca la piratería.

Después de este discursito, deciros que espero que os haya gustado el tuto. Se que es un poco largo, quizas algo lioso para el que aún sea novatillo. He intentado explicarlo lo mejor que he podido, pero hay aspectos en los que no he podido o no he querido profundizar más por temas de tiempo. Ya he comentado que podeis escribirme para cualquier cosa, duda o critica, tanto buena como mala, me gustaría que todo el mundo entendiera los métodos que he usado para este Unpacker.

Quiero dar las gracias a un compañero que me pasó el Empakador Armadillo 2.60 con el que pude practicar, y detectar así sus puntos débiles. Sin él hubiera tardado más en conseguir desarmar al bicho. Gracias Silver Storm. También, como no, al Profesor X por publicar este tuto por sus animos y por la enorme información que distribuye con sus compilaciones, que ojalá no deje nunca el proyecto. A Karpoff pos su pedazo de Web a la que todavía visito para buscar información además de otras cosas (por cierto, es una lastima que tu web se quede parada, aunque ya se que se trata por falta de tiempo y problemas con el servidor).

Un saludo a todos y hasta el proximo tuto.

auspexxx@hotmail.com

 

 

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