Karpoff Spanish Tutor 1999-2001

Programa:

MEMTURBO II


 

PROTECCION: Empakado, Anti-Sice, CRC-32, Limitación de uso a 30 días.
Objetivo: Fragmentar MemTurbo.
Descripcion: Recuperar RAM ocupada inútilmente por Windows.
Dificultad: Avanzadillo.
DOWNLOAD: http://www.memturbo.com
Herramientas: Softice 3.24, FrogSice, Procdump32, File insPEctor XL, W32dasm 8.9, ExeScope v6.00, un editor Hex.
CRACKER: Arkanian   FECHA: 01/09/2001

 

 INTRODUCCION

Esta nueva versión de MemTurbo trae un montón de novedades en cuanto a nuestro campo de investigación se refiere. Así como la versión anterior, la 1.5, venía a "pelo", parece que los chicos de Shareware OnLine.com , Inc. han espabilado y han intentado hacer las cosas un poco mejor.

Estas son las dificultades que nos vamos a encontrar:

  1. .- Programa empakado/comprimido y anti-dasm.
  2. .- Anti-Sice mediante MeltIce, BoundsChecker y llamadas a la INT3.
    .- Comprobaciones de integridad del archivo aleatorias durante la ejecución del programa.
  3. .- Encriptación de las cadenas de texto "delicadas".

Interesante programa a fe mía, amigo Sancho...

Vamos alla...

"Per aspera ad astra"

 

 AL ATAKE

Instalar el programa y lo primero es lo primero, la talla XL del File insPEctor, Compilador UPX 1.05-1.07, un vistazo a las secciones y a sus características, rápidamente Procdump y cambiamos la UPX0 a E0000020 por si las moscas, de paso nos apuntamos el Entry Point y el Size of Image que nunca viene mal. Le echamos un vistazo a las funciones solo por curiosidad y ejecutamos el memturbo.exe.

PRIMERAS SORPRESAS

Iniciamos el programa y empiezan las sorpresas, pantallazo del Sice, no se qué del Windows requested break point, fallo de página, fallo general de protección y cuelgue de la maquina todo seguido. R- EXIT-C por este orden. Empezamos bien .... |:-(

Al montar FrogSice nos encontramos con otra sorpresa, un aviso de modificaciones en la IDT. Algo ha tocado la Tabla de Descripción de Interrupciones y el Sice no funciona. El Loader dice que está activo pero es incapaz de cargar nada. Reiniciando que es gerundio...

FrogSice y a ver que pasa...

=> Memturbo
** SOFTICE DETECTION ** code 0B, at cs:00404852
Attempting to load: SICE (string ref at cs:0044D404)

Esto de aquí arriba aparece 9 veces seguidas en el log del FrogSice. Tiene pintas de MeltIce. Parece que el programa funciona y que tenemos el Sice convenientemente oculto. Como uno nació desconfiado vamos a ver que ocurre con la opción "a prueba de balas":

=> Memturbo
** SOFTICE DETECTION ** code 0B, at cs:00404852
Attempting to load: SICE (string ref at cs:0044D404)

=> Memturbo
** SOFTICE DETECTION ** code 01, at 0177:0040497C
Interrupt:03h >eax=006C0004h ebx=0000044Ch ecx=006CF954h
edx=BFF76859h esi=0121C290h edi=00000000h >ebp=4243484Bh

SEH proc address at cs:0041A18C

=> Memturbo
** SOFTICE DETECTION ** code 00, at 0177:004049C8
Interrupt:03h eax=0040038Fh ebx=0000044Ch ecx=006CF954h
edx=BFF76859h esi=01211227h edi=00000000h ebp=006CF7DCh

SEH proc address at cs:0041A18C

=> Memturbo
** SOFTICE DETECTION ** code 00, at 0177:004049C8
Interrupt:03h eax=0040038Fh ebx=0000044Ch ecx=006CF954h
edx=00000000h esi=01211227h edi=00000000h ebp=006CF768h

SEH proc address at cs:0041A18C

¡Toma pedazo de log!, todo esto vuelve a aparecer otras nueve veces seguidas. Vamos a analizarlo a ver que sacamos en claro:

.- Attempting to load: SICE --> El truco MeltIce, por lo menos parte de él... ya veremos.
.- > ebp = 4243484Bh --> BCHK. BoundsChecker seguido de una INT3.
.- Interrupt: 03h --> Lo mismo que el anterior, intenta acceder a la INT3 dos veces antes de probar Meltice otra vez. y empezar el bucle.

Nada fuera de nuestras posibilidades. :-)

AL TAJO

Desempakando a pedales

Vamos a probar primero a desempacar el programa, siendo UPX no creo que haya mucha dificultad. Hemos cambiado las características de la sección UPX0, de E0000080 a E0000020 lo cargamos en el Loader y cuando rompa ponemos un BPX GETVERSION.

F5 y F12, hasta aparecer en una dirección 41 y pico, miramos un poco más arriba para encontrar un PUSH EBP - MOV EBP, ESP en 417860. Ya tenemos el Punto de entrada original. borramos el bp y ponemos otro en esa dirección. Salimos, quitamos el FrogSice, volvemos al Loader, F5 y directamente a 417860

Lo de siempre, quitar el bp, apuntar los bytes (558BEC6A), ensamblar a JMP EIP, Procdump, Dump full y Kill task, Hex editor, buscar E9FE, poner los bytes originales (558B), Procdump otra vez, cambiar el Entry Point a 00017860 y reconstruir la cabecera. El XL ya lo identifica como C++ v.6 y Tabla de Importaciones parece correcta...

Eh... ¿Voy muy deprisa?

Anti-debugging a mano

Ya tenemos un ejecutable funcional, hora de cambiar cosas. Vamos a buscar las direcciones "calientes" en 00404852, 0040497C y 004049C8. Como se puede comprobar una vez desensamblado con W32dasm, no hay informaciones de menús, ni de diálogos, ni de funciones ni de nada de nada y las String References parecen un bebedero de patos.

El motivo de esto es parte de las strings del programa vienen en formato ancho en plan VB, ...eso las que no están encriptadas, otras en formato normal y las que quedan no sirven para mucho... Usaremos el ExeScope para ver por donde andamos.

A lo que íbamos, echemos un vistazo a esto:

* Referenced by a CALL at Addresses:
|:0040462C , :00409AFC , :0040BABE , :0040BB81 , :0040BC6C
|:0040BD3E , :0040BDA2 , :0040BED5 , :0040BF5E , :0040BFC9
|:0040C05D , :0040C140 , :0040C1AE
|
:00404813 6A08 push 00000008
--> Número de bytes a desencriptar.
:00404815 59 pop ecx
--> ECX = 8

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0040482B(C)
|
:00404816 8A9104D44400 mov dl, byte ptr [ecx+0044D404]
--> Un valor encriptado.
:0040481C 8D8104D44400 lea eax, dword ptr [ecx+0044D404]
--> Ponemos en EAX 44D404 + 8.
:00404822 F6D2 not dl
--> Poderosa técnica de encriptación.
:00404824 8810 mov byte ptr [eax], dl
--> Lo movemos al valor de EAX.
:00404826 8BC1 mov eax, ecx
--> El contador a EAX.
:00404828 49 dec ecx
--> Decrementa el contador.
:00404829 85C0 test eax, eax
--> Comprobación del contador.
:0040482B 7FE9 jg 00404816
--> Volvemos arriba.

Aquí lo que va haciendo es desencriptar los valores de 44D404+8 hacia atrás. Poco a poco va apareciendo la cadena \ \ . \ SICE. Observad la cantidad de sitios desde donde se llama a este código, la cantidad de comprobaciones que hace, ...inutilmente. Seguimos:

:0040482D 53 push ebx --> Guarda estos valores.
:0040482E 55 push ebp
:0040482F 56 push esi
:00404830 8B3534C24300 mov esi, dword ptr [0043C234]
--> La dirección de CreateFileA.
:00404836 57 push edi
:00404837 BB80000000 mov ebx, 00000080
:0040483C 6A00 push 00000000
--> Nos vamos a haciendo hueco...
:0040483E 53 push ebx
:0040483F 6A03 push 00000003
--> Hum....
:00404841 6A00 push 00000000
--> Más espacio vital...
:00404843 BF000000C0 mov edi, C0000000
--> ¿Y esto?, ¿0028:C0000000?. La cosa se pone caliente.
:00404848 6A03 push 00000003
:0040484A 57 push edi
:0040484B 6803D44400 push 0044D404
--> Guarda la dirección de la cadena \ \ . \ SICE.
:00404850 FFD6 call esi
--> Entramos en CreateFileA.
:00404852 6A08 push 00000008
--> ¿Otra vez?, ¿Encriptamos o desencriptamos?
:00404854 59 pop ecx
--> El contador..

Ahora tendría que venir la comprobación de si ha encontrado el archivo... hay que esperar un poco, la cosa tiene miga:

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0040486B(C)
|
:00404855 8A9104D44400 mov dl, byte ptr [ecx+0044D404]
--> La dirección de antes.
:0040485B 8DA904D44400 lea ebp, dword ptr [ecx+0044D404]
--> Ahora en EBP.
:00404861 F6D2 not dl
--> Encriptamos con la poderosa tecnología NOT.
:00404863 885500 mov byte ptr [ebp+00], dl
--> Y lo guardamos.
:00404866 8BD1 mov edx, ecx
--> El contador [8].
:00404868 49 dec ecx
--> Un byte menos.
:00404869 85D2 test edx, edx
--> Comrpueba si esta todo.
:0040486B 7FE8 jg 00404855
--> Arriba si no es 0.
:0040486D 83F8FF cmp eax, FFFFFFFF
--> ¡Por fin!
:00404870 7403 je 00404875 --> El salto de marras...
:00404872 50 push eax
:00404873 EB4D jmp 004048C2
--> Si llegamos aquí la jodimos...

Esto es un ejemplo de la mayoría de los procesos del programa, desencripta las cadenas de texto que va a utilizar, hace lo que tenga que hacer y antes de acabar las vuelve a encriptar.

Para no aburriros mucho, ya que es duro leerse el código, la siguiente comprobación que hace un poco más abajo, usando este mismo código, la hace con \ \ . \ NICE. ¡Que bonito!.

¿NICE no debería ser NTICE?, ¿Han ajustado la cadena al valor fijo de ECX que es 8?, Me parece que "alguien" ha metido la pata con esto. :-)))

Vamos a liquidar esto de una manera elegante. Nada de cambiar saltos que es lo fácil, pensemos un poco, el resultado después de CreateFileA tiene que ser FFFFFFFF en EAX, la dirección de la cadena \ \ . \ SICE la ha metido en la pila antes de entrar en esa función, (PUSH 44D404), una pequeña modificación nos permitirá salir con el valor de error esperado por el programa casi automáticamente.

El valor de 44D403 es 00 (un byte antes), CreateFileA empieza a procesar una cadena que empieza con 00, como lo hace con un REPNZ salimos con un -1 en EAX rápidamente. Así que cambiamos PUSH 44D404 por PUSH 44D403 (04 x 03). Fácil, ¿No?.

Vamos con la siguiente dificultad, el BCHK:

:0040496C 8965E8 mov dword ptr [ebp-18], esp
:0040496F 8365FC00 and dword ptr [ebp-04], 00000000
:00404973 BD4B484342 mov ebp, 4243484B
--> Valor de la cadena BCHK.
:00404978 66B80400 mov ax, 0004
--> Cambiar a JMP 0.
:0040497C CC int 03
--> La putada.
:0040497D 3C04 cmp al, 04
:0040497F 7515 jne 00404996

Aquí la solución es ensamblar la linea 404978 mov ax, 0004 a JMP 0, de esta manera se produce una excepción y el programa pensará que no hay ningún Debugger que la maneje.

De la misma forma solucionamos la última dificultad de la INT 3 en 4049C8:

:004049B9 8965F0 mov dword ptr [ebp-10], esp
:004049BC 8365FC00 and dword ptr [ebp-04], 00000000
:004049C0 66BE2712 mov si, 1227
:004049C4 66B88F03 mov ax, 038F
--> Ensamblar esto a JMP 0.
:004049C8 CC int 03
:004049C9 EB16 jmp 004049E1

Espero que hayas tomado nota de todos los cambios que hemos realizado, ahora toca ir al editor Hex y realizar las oportunas modificaciones:

DIRECCIÓN OFFSET VALOR CAMBIAR A
:0040484B 00003E48 68 04 D4 44 68 03 D4 44
:00404978 00003F78 66 B8 04 00 CC * E9 83 B6 BF BF
:004049C4 00003FC4 66 B8 8F 03 CC E9 37 B6 BF FF

[*] A mi también me extraña que el desplazamiento sea 83 B6 BF ¿BF? y no FF como el de abajo, pero así lo puso el Sice después de meter a mano el JMP 0. De todas formas funciona que es lo que queríamos.

La fecha de caducidad

Ya hemos "liberado" el programa y ya podemos dedicarnos a investigar tranquilamente cuando y donde hace la comprobación de tiempo transcurrido hasta que caduca.

Sabemos de antemano, por que uno lee y se considera instruido, que el programa calcula el tiempo transcurrido a traves de una serie de comprobaciones en el Registro (vease, Karpoff - MemTurbo v1.5 - WKT Tutorialz Site - 1 de Diciembre de 1999), pero de algún sitio tendrá que coger la fecha actual, digo yo...

Así que que por probar que no quede, le metemos un BPX GETSYSTEMTIME después de cargarlo en el Loader a ver que pasa.

Esto:

:0040BF23 FF1524C34300 call dword ptr [0043C324] --> Llamada a GetSystemTime.
:0040BF29 8D45C8 lea eax, dword ptr [ebp-38]
--> Aparecemos aquí.
:0040BF2C 50 push eax
:0040BF2D 8D45A0 lea eax, dword ptr [ebp-60]
:0040BF30 50 push eax
:0040BF31 FF1528C34300 call dword ptr [0043C328]
--> Llamada a SystemTimeToFileTime
:0040BF37 8D45C8 lea eax, dword ptr [ebp-38]
:0040BF3A 50 push eax
:0040BF3B 8D45C0 lea eax, dword ptr [ebp-40]
:0040BF3E 50 push eax
:0040BF3F FF152CC34300 call dword ptr [0043C32C]
--> LLamada a CompareFileTime.
:0040BF45 85C0 test eax, eax
:0040BF47 7E6B jle 0040BFB4
:0040BF49 833D18DD44001E cmp dword ptr [0044DD18], 0000001E
--> Un cebo... ni caso.
:0040BF50 7407 je 0040BF59
--> ¡No tocar!
:0040BF52 56 push esi
:0040BF53 56 push esi
:0040BF54 E82BB50000 call 00417484
--> A Kernel32!RaiseException. Este es nuevo.
:0040BF59 E84A8AFFFF call 004049A8
--> Comprobación de la INT 3.
:0040BF5E E8B088FFFF call 00404813 -
-> Comprobación del MeltIce.
:0040BF63 85C0 test eax, eax
:0040BF65 7512 jne 0040BF79
............................................ sigue y sigue

Y el código sigue y sigue más o menos igual dando vueltas y más vueltas sobre lo mismo. Podemos encontrar la comparación con 1E y el mismo código de arriba en 40BFBF, 40C048, 40C128, 40C199. En realidad la comparación y el salto posterior no hacen nada más que mandarnos a las rutinas Anti-Sice.

Si invertimos el salto vamos a RaiseException con el consiguiente cuelgue y si lo dejamos vamos donde "estaba" la INT3. Y vuelta para arriba y vuelta para abajo. Como ya hemos liquidado las rutinas Anti-Sice podemos seguir trazando tranquilamente.

Bien, si echamos un vistazo hacia arriba veremos que todo esto empieza en 40BA9E, el código se repite hasta 40C20F que es donde está el RET. Entretanto y más o menos escondido podemos encontrar cosas curiosas como esta:

  • :0040BAFF 8A9030DD4400 mov dl, byte ptr [eax+0044DD30] --> Algo encriptado.
    :0040BB05 32D1 xor dl, cl
    --> Xorea el valor con el número de caracteres [3F].
    :0040BB07 80F272 xor dl, 72
    --> El resultado xoreado con 72.
    :0040BB0A 49 dec ecx
    --> Abajo el contador para el siguiente xoreo...
    :0040BB0B 889020054500 mov byte ptr [eax+00450520], dl
    --> Lo ponemos en su sitio.
    :0040BB11 40 inc eax
    --> Siguiente valor.
    :0040BB12 3BCE cmp ecx, esi
    --> Comprobación del contador.
    :0040BB14 75E9 jne 0040BAFF
    --> Volvemos arriba.

Que es la desencriptación de la siguiente cadena de texto correspondiente a la Clave del Registro:

HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\DataViewSettings

Acto seguido lo completa con -2RC1 y llama a RegOpenKeyExA. Luego la vuelve a encriptar nada más salir de un RegQueryValueExA, hay varias encriptaciones y desencriptaciones de diversas claves del Registro a lo largo de ese fragmento de código. Como cosa curiosa vemos la incorporación de varios valores fijos como son 07, 09 y 13 que tienen su importancia.

La comprobación de la fecha se hace después de una larga manipulación del valor de las cadenas DataViewSettings-2RC1 y DataViewStream-2RC1. Los resultados válidos sólo pueden ser uno de esos tres (07, 09, 13). Dependiendo de cual sea, llegamos al RET, salimos y nos encontramos con esto:

:00409B35 E86EAEFFFF call 004049A8 --> Otra llamadita más a la INT 3, sin problemas...
:00409B3A E85F1F0000 call 0040BA9E
--> Venimos de aquí.
:00409B3F 83F807 cmp eax, 00000007
--> Tiempo OK.
:00409B42 0F8494000000 je 00409BDB --> Este es el bueno.
:00409B48 83F809 cmp eax, 00000009 --> ¡MemTurbo expires soon!. Creo que sale a los 20 días.
:00409B4B 7462 je 00409BAF
--> A la ventana de aviso de "quedan pocos días".
:00409B4D 83F813 cmp eax, 00000013
--> Memturbo ha caducado... Gracias por probar el programa,
:00409B50 7424 je 00409B76
--> A pasar por caja...

Creo que no hay que decir cual hay que cambiar. E9 94 00 00 00 90 es el nuevo valor de 409B42.


CUESTIONES ESTETICAS - DE CAZA POR LA PILA

Hagamos un pequeño paréntesis... Siempre suelo dejar para el final la tarea de maquillar el programa para que parezca la versión integra. Cambiar las referencias a Shareware, cambiar el menú About, etc. Pero en este caso vamos ha hacerlo ahora para hacer más comprensible como cazar la comprobación del CRC.

Si echamos un vistazo al programa con el ExeScope veremos que está TODO. Y con "todo" me refiero a cosas como esta:

32809,MemTurbo II
32810,Licensed Version
32811,Evaluation Copy
32812,%s, Serial No. MT-39601$0AThis program is licensed for use on a single computer.$0APlease do not distribute this copy!$0AThank-you for registering!
32813,%s$0AThis product may be evaluated for 30 days, after which it must be purchased or uninstalled. Continued use beyond this period without a license is illegal.
61707,System registry entries have been removed and the INI file (if any) was deleted.
** This is the LICENSED version of MemTurbo. Please do not illegally redistribute!

Licensed version, Evaluation, un número de Serie, una referencia a un archivo .ini (¿memturbo.ini?), This is the LICENSED ... que viene en el diálogo About pero en el programa no aparece.

Lamentablemente no hay un diálogo en plan "Enter Registration Code", pero esta misma cadena si que aparece por algún lado con el Sice.(en 44DE18 concretamente), ¿?

¿Como modificamos el horrible "Evaluation Copy" de la ventana de inicio del programa?, podríamos cambiar con el editor Hex la referencia a Evaluation y poner algo así como Registered que tiene el mismo número de caracteres y queda bonito, pero ya que tenemos el Licensed vamos a cambiar uno por otro.

¿Donde carga el programa el dichoso Evaluation?. Al Loader de nuevo.

Veamos... C++... a ver.... BPX LOADSTRINGA, ...el primero no, con el segundo ya tenemos la carátula y el nombre del programa, con el tercero la versión y con el cuarto se carga el "Evaluation copy". Después de seguirle la pista que ha ido dejando por la pila te dire que hay un PUSH 0000802B que es el responsable del desaguisado.

Este aparentemente inofensivo valor es el encargado de mostrar la cadena "Evaluation copy". El procedimiento es el siguiente, se guarda 802B en la pila, damos un montón de vueltas arriba y abajo por el código para despistar, al rato entramos en LOADSTRINGA y seguimos avanzando hasta la función Kernel32!ORD_000E:

call Kernel32!LoadResource --> Salimos de aquí con una dirección de la sección rsrc en EAX [48ED48]
test eax,eax
--> Comprobación.
mov [ebp-04], edi
--> Una dirección 006CFXXX.
and dword ptr [ebp+0C], 0F
--> ¡AQUI,! 802B and 0F = B
movzx ecx, word ptr [eax]
mov [ebp-1C], ecx
add eax, 02
--> Ajustamos EAX a la posición en la que aparece el texto.
mov edx, [ebp+0C]
--> Hay que joderse, es un contador... [B]
dec dword ptr, [ebp+0C]
--> Empieza la cuenta...
test edx, edx
--> La comprobación.
jz BFF80C61
--> Saltamos a un loop hasta que EDX sea 0.

Aquí ha metido en EAX una dirección de la sección rsrc, luego mete en [ebp-04] una dirección de memoria 6CFXXX que va ser usada como paso intermedio, obtiene mediante al AND 0F un valor que va ser usado como contador del bucle y vamos sumando 2 a EAX hasta que EDX (B) sea 0, cuando EDX alcanza ese valor la cadena que se va a usar es la que aparece en la dirección que marca EAX (Evaluation Copy).

Si cambiamos el PUSH 0000802B por PUSH 0000802A, el valor que se va a cargar es el inmediatamente superior (EDX = A --> EAX = Licensed version).

La cosa no ha acabado, una vez que salimos al código real del programa tenemos en 6CFXXX la cadena Evaluation copy, la mueve a otra dirección (0126664, este dato es fijo), establece un byte de control y borra el valor de 6CFXXX. ¡Buff!

Si lo hacemos de nuevo vemos que solo paramos una vez en LOADSTRINGA ya que el programa comprueba el byte de control (hay una comprobación con código diferente para cadena de texto que se carga) y si lo encuentra nos manda directamente a la dirección 0126664 sin pasar por el bp que habiamos puesto, ¡Joder!.

Todo lo anterior nos va a servir para cazar la comprobación del CRC que también tiene historia... :-)


Comprobación de la Integridad del Archivo

Esto también tiene su historia, porque la ventana de aviso de archivo corrupto no aparece siempre, te puedes pegar 20 minutos seguidos dandole al botón de Recover now y no pasa nada, otra vez aparece a la primera o a los cinco minutos o incluso no aparece en toda la tarde, ¿?. Pero una vez que aparece el programa deja de funcionar y la ventana sale una y otra vez como una maldición..

Como ya sabemos donde mirar, esperamos que aparezca la ventana, metemos un BPX LOADSTRINGA e intentamos recuperar memoria, esta vez saltamos rápido al SICE. Trazamos , le ponemos un bp a la instrucción AND DWORD PTR [EBP+0C], 0F y vamos tomando nota de los valores que va tomando el [ebp+0C] hasta que se carga la cadena "Your memturbo binary appears corrupt...". El valor es 8018, metemos un comando de búsqueda:

S 0 LFFFFFFFF 68 18 80 00 00
Pattern found at 00406F3B (00406F3B) --> Solo hay uno en el rango de memoria de memturbo.

68 es el valor de PUSH, este es el código donde aparece:

:00406F2D E8E3200200 call 00429015
:00406F32 84DB test bl, bl
:00406F34 5B pop ebx
:00406F35 740E je 00406F45
--> Un hermoso salto.
:00406F37 6AFF push FFFFFFFF
:00406F39 6A00 push 00000000
:00406F3B 6818800000 push 00008018
--> AQUI.
:00406F40 E8B37B0200 call 0042EAF8

La primera reacción es modificar el salto, pero no sirve de nada, ya que lo único que hacemos con esto es que no aparezca la ventana, sin embargo el programa deja igualmente de funcionar.

Sigamos con la caza, este código que incluye PUSH 00008018 empieza en 406EBD y tiene todas las pintas de un call ya que la instrucción inmediatamente superior es un RET. Pero de un call con truco, veamos:

:00407198 53 push ebx --> Inicio del bucle.
:00407199 53 push ebx
:0040719A 53 push ebx
:0040719B 8D45BC lea eax, dword ptr [ebp-44]
:0040719E 53 push ebx
:0040719F 50 push eax
:004071A0 FF1550C54300 call dword ptr [0043C550]
--> Llamada a la función PeekMessage.
:004071A6 85C0 test eax, eax
:004071A8 741A je 004071C4
--> Salto importante.
:004071AA E895E40200 call 00435644
:004071AF 8B4004 mov eax, dword ptr [eax+04]
:004071B2 8BC8 mov ecx, eax
:004071B4 8B10 mov edx, dword ptr [eax]
:004071B6 FF525C call [edx+5C]
--> Al programa...
:004071B9 85C0 test eax, eax
:004071BB 75DB jne 00407198
--> Fin del Bucle.
:004071BD 53 push ebx
:004071BE FF1554C54300 call dword ptr [0043C554]

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:004071A8(C)
|
:004071C4 6800400000 push 00004000
--> Llegamos aquí.
:004071C9 6A08 push 00000008
:004071CB FF75EC push [ebp-14]
:004071CE A130D54400 mov eax, dword ptr [0044D530]
--> ¡Aquí está!.
:004071D3 FFD0 call eax
--> Este es el call maldito.
:004071D5 3BC3 cmp eax, ebx
:004071D7 7431 je 0040720A
:004071D9 8D8800400000 lea ecx, dword ptr [eax+00004000]

Estamos contemplando el "backbone" del programa, espinazo en cristiano, todos los procesos del mismo pasan por aquí. El valor de EAX depués de PeekMessage es el que decide si se salta o no y la dirección de la llamada [call EAX] la dedice el valor de 44D530. Si ese valor es 407005 el programa se desarrolla perfectamente, si el valor es 406EBD vamos al mensaje de error.

Sería demasiado largo explicar como va a parar 406EBD a 44D530 y porqué la aparición de este valor tiene un componente aleatorio, en este proceso intervienen GetTickCount, la fecha del system.ini (creo) ¿?, un montón de valores fijos que trae el programa y una larga serie de operaciones matemáticas que, si todo va mal, nos llevan a este código:

:0040C253 3B45EC cmp eax, dword ptr [ebp-14]
:0040C256 59 pop ecx
:0040C257 59 pop ecx
:0040C258 740A je 0040C264
:0040C25A C70530D54400BD6E4000 mov dword ptr [0044D530], 00406EBD
--> Estamos jodidos.

Podríamos parchear el salto en 40C258 pero por alguna razón esto cuelga la máquina, por lo visto al llegar a ese salto el proceso está tan avanzado que es inevitable que el valor 406EBD vaya a 44D530.

Así que la derecha es hacer que en 4071D3 CALL EAX siempre se llame a 407005. Y esto lo hacemos modificando en 4071CE la instrucción MOV EAX, DWORD PTR [0044D530] por MOV EAX, 00407005.

Estos son los cambios a realizar con el editor Hex:

DIRECCIÓN OFFSET VALOR CAMBIAR A
:00409B42 00009142 0F 84 94 00 00 00 E9 94 00 00 00 90
:0040CFB0 0000C5B0 68 2B 80 68 2A 80
:004071CE 000067CE A1 30 D5 44 B8 05 70 40

Nota

Si "quemas" el programa adelantando el reloj para ver si los cambios funcionan y luego vuelves el reloj a su fecha, aparecerá una molesta ventanita avisando que has cambiado la fecha etc. Esto se soluciona rápidamente borrando las dos Claves del Registro mencionadas arriba. También se le puede meter mano al código. El push fatídico está entre 40BA9E y 40C20F, suerte. :-)


ESTO NO HA TERMINADO

La verdad es que pensaba acabar aquí, pero me jode un montón dejar la ventana de "Cache Tuning" deshabilitada y con el aviso "de esta ventana solo funciona en la versión buena". Así que ponte cómodo que todavía queda un rato.

Habilitando ventanas

Veamos, para que una ventana funcione y con que funcione me refiero a que reciba entradas de teclado o de ratón se utiliza la función EnableWindow que tiene la siguiente forma:

     BOOL EnableWindow 
      HWND  hWnd,                               // el manipulador de ventana
      BOOL  bEnable                              // la bandera para validar o desactivar la entrada

Bien, vamos a ver un pequeño ejemplo, iniciamos el programa, vamos al Sice, ponemos un BPX ENABLEWINDOW y hacemos click, por ejemplo, en la pestaña Options, aterrizamos aquí después de salir de la función:

:00428760 FF742404 push [esp+04] --> El Flag para activar o desactivar la ventana.
:00428764 FF711C push [ecx+1C]
--> El manipulador o handle.
:00428767 FF152CC64300 call dword ptr, [0043C62C]
--> LLamada a EnableWindow.
:0042876D EB0E jmp 004287D
--> Salimos aquí.

El flag lo tenemos fácil, 1 - Activado, 0 - Desactivado. Pero tenemos dos problemas, el primero es identificar la ventana Cache Tuning y el segundo cual es su manipulador.

El primer problema lo solucionamos con ExeScope, vamos al Dialog 106, la ventana que queremos habilitar tiene cuatro controles msctls_trackbar32: Slider1 que son controles deslizantes que definen el tamaño máximo de la caché, el mínimo, el tamaño de los bloques y una cosa rara que se llama ASync IO Buffers. Debemos apuntar también cuales son y cual es el número de los elementos restantes. (Un Button y 19 referencias a Static)

Una vez que sabemos esto vamos al Sice y hacemos un HWND MEMTURBO para buscar donde están esos controles, el manipulador es el valor de la columna de la izquierda cuyo encabezado es Window handle. Ojo que cada vez que se inica el programa son diferentes por eso hemos identificado los elementos característicos de la ventana para saber donde empieza y donde acaba.

Al Loader de nuevo, ¿Cuando se carga la ventana de Cache Tuning? ... Ponemos un BPX ENABLEWINDOW, vamos dandole alegremente al F5 y cada vez que salte el bp metemos un HWND MEMTURBO para ver si ya están cargados los controles msctls_trackbar32 y cuando les toca ser procesados por EnableWindow. Observamos que siempre aparecemos en la dirección del ejemplo de arriba.

Pues no aparecen, por lo menos en el código donde se carga el resto, me temo que vuelve a haber gato encerrado... efectívamente:

:004016F2 6A02 push 00000002 --> Principio.
:004016F4 FF731C push [ebx+1C]
:004016F7 FFD5 call ebp
:004016F9 50 push eax
:004016FA E890420200 call 0042598F
:004016FF 8BD8 mov ebx, eax
:00401701 3BDE cmp ebx, esi
:00401703 740A je 0040170F
--> Este es el responsable.
:00401705 56 push esi
:00401706 8BCB mov ecx, ebx
:00401708 E84C700200 call 00428759
--> Bucle de EnableWindow para cargar las ventanas
:0040170D EBE3 jmp 004016F2
--> Fin.

Lo gracioso es que para cuando llegamos aquí las ventanas del programa ya están cargadas, todas menos la que nos interesa, se puede ver con un HWND. Así que esto o es código redundante o estamos a punto de soltar al gato.

Veamos si con un G 40170F pasa algo, saltamos a 40170F y seguimos avanzando, a la salida de uno de los numerosos call's que encontramos vemos con HWND que ya tenemos cargados los controles, despacio.... hasta aquí:

:0040186B 6A00 push 00000000
:0040186D 6866060000 push 00000666
--> ¿El número de la Bestia?
:00401872 53 push ebx
:00401873 FF701C push [eax+1C]
:00401876 FFD6 call esi
:00401878 8BCF mov ecx, edi
:0040187A E85DFCFFFF call 004014DC
--> Entramos aquí.
:0040187F 6A01 push 00000001

Entramos en call 004014DC y seguimos avanzando despacio, por lo que parece la ventana del Cache Tuning se carga aparte, estamos buscando exáctamente una llamada a EnableWindow y también buscamos cuando el valor de esp+04 se pone a 0.

Después de algunas pruebas, esto no siempre sale a la primera, encontramos que la dirección que marca esp+04 es 006CE908 y que es siempre la misma, así que nos ponemos una ventana de control con esa dirección (DEX 3 6CE908), seguimos y ...

:0040151D 3BF8 cmp edi, eax
:0040151F 74DD je 004014FE
:00401521 6A00 push 00000000 --> ¡El Flag!
:00401523 8BCF mov ecx, edi
:00401525 E82F720200 call 00428759
--> A EnableWindow.
:0040152A EBD2 jmp 004014FE

Pues esto es lo que hay... cambiamos el PUSH 00000000 por PUSH 00000001, entramos en el call 428759 y vamos controlando los handler que el programa va cargando en ECX+1C, comprobando sucesivamente con HWND MEMTURBO y D ECX+1C que efectívamente corresponden a los elementos de nuestra ventana:

:00428760 FF742404 push [esp+04] --> El Flag siempre va a ser 1.
:00428764 FF711C push [ecx+1C]
--> Controlamos que este valor corresponda a nuestra ventana.
:00428767 FF152CC64300 call dword ptr, [0043C62C]
--> LLamada a EnableWindow.
:0042876D EB0E jmp 004287D
--> Nos vamos....

No vamos a tener ningún problema, el valor de ECX+1C siempre corresponde al handler de los elementos de nuestra ventana. El programa carga las otras ventanas en el bucle 4016F2 - 40170D y luego sigue hasta 401521 donde el valor del Flag se pone a 0 para procesar la ventana del Cache Tuning.

El último cambio con el editor Hex:

DIRECCIÓN OFFSET VALOR CAMBIAR A
:00401521 00000B21 6A 00 6A 01

FINAL

Creo que ya está bien por hoy. Hemos desempakado el programa, quitado las rutinas antidebugger, saltado el límite de tiempo, la comprobación de integridad, hemos logrado una presentación atractiva y como propina hemos habilitado la ventana que nos faltaba.

Conclusión, salvo un par de menudencias como son quitar la nota de aviso en la ventana del Cache y habilitar la línea "** This is the LICENSED..." en el menú About, tenemos el programa completo, ¿Que más quieres?.

Se me olvidaba, esto es con propositos educativos..., el autor no se hace responsable..., etc, etc...

Saludos y hasta otra...

 

 

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