ESTUDIO COLECTIVO DE DESPROTECCIONES
WKT Tutorialz Site
WKT
Programa Particle Fire screensaver v1.1a W95 / W98 / NT
Descripción Salvapantallas
Tipo Trial de 7 días
Url http://www.longbowdigitalarts.com/particlefire.html
Protección Serial-num
Dificultad 1) Principiante y 2) Amateur, 3) Aficionado, 4) Profesional, 5) Especialista
Herramientas SoftIce v4.00, Regmon, Hiew 6.04 (mASM32 opcional)
Objetivo Parchear la comprobación del S/N o hallar uno válido
Cracker Champion
Fecha 19 de Agosto de 1999

Introducción

El salvapantallas es vistoso, aunque es el primero que veo que pretenden hacerte pagar por él (hay gratis a patadas en internet), aunque bueno, cada cual que haga lo que quiera; si realmente te gusta, creo que solo son unos 10$... Para registrarse, hay que introducir un número en la pantalla de configuración del salvapantallas; si no, al cabo de 7 días, cuando el salvapantallas lleva un par de minutos, comienza con los clásicos mensajitos tipo "regístrate porfa". Nunca he visto un tutorial sobre un screensaver, así que ya que lo conseguí con este, he pensado en contarlo y de paso ilustrar la aplicación del método 'bpx RegQueryValueExA' para programas que interactuan poco o nada con el usuario a la hora de registrarse pero que leen datos tipo s/n desde el registro de Windows (este es su punto de contacto con el mundo 'real'). Dado que me considero un principiante, he tratado de escribir esto a un nivel bastante bajo, aunque he desarrollado dos métodos de cracking, uno para principiantes (parchear) y uno para amateurs (keygen); es por estos motivos que este documento es un poco largo. Sin embargo, la gente más avanzada podrá leerlo de corrido (o leer directamente el 'briefing' (resumen) del final de todo).

Al Atake

1. Familiarizándonos

Bien, nuestro primer punto de partida es familiarizarnos con el programa y su mecanismo de protección. Éste consiste en la introducción de un número en la ventana de configuración del protector de pantalla (podemos acceder desde Panel de control / Propiedades de pantalla / Configurar protector pantalla, o bien desde el menú contextual que aparece al pulsar el botón derecho sobre el fichero de ParticleFire). Si el número no es el correcto, y ya han pasado 7 días desde que lo instalamos, veremos mensajes molestos al cabo de un par de minutos de iniciarse el screen saver (adelantad 10 días el calendario para verlos).

Así que tenemos que solucionar eso... pero para nuestra desdicha, constatamos que, tras introducir un número (por ejemplo 12345) y aceptar, no aparece ningún mensaje que valide o invalide el código introducido. Sin embargo, si volvemos a entrar, nuestro número sigue ahí. Si hacemos algún experimento, probando números y entrando y saliendo de la configuración, llegaremos a la conclusión de que, posiblemente, el serial num debe ser de 10 cifras (o menos) y formado exclusivamente por números (de otro modo, trunca o pone a cero la expresión introducida cuando volvemos a entrar a "configurar").

Ahora que creemos tener una leve orientación sobre lo que buscamos, hacemos pruebas a base de poner break-points en las típicas funciones que capturan el texto de un cuadro de diálogo, comparación de cadenas, etc. Pero... no parecen llevarnos a ninguna parte.

Quizás los autores del Particle Fire quieran echarnos una mano ;) echamos un vistazo al read-me que lo acompaña. Allí encontramos un par de informaciones interesantes:

(1) 'asegúrate de introducir correctamente el serial-num'
(2) 'por favor no digas a nadie tu número de registro'

Traducción:

(1) no hay mensaje tipo 'gracias por registrarte' o 'serial num incorrecto'
(2) el número es independiente de tu nombre, disco duro, etc..

Si bien el punto (2) nos facilita un poco las cosas, el (1) las complica porque significa que, posiblemente, la comprobación del serial num no se realiza cuando lo entramos en 'configurar' si no al correr el salvapantallas. Es decir, la típica relación causa-efecto entre el serial/keyfile/etc y nag screen/reminder/lo que sea, está en cierto modo disociada, separada, retardada.

Sin embargo, tiene que haber un nexo de unión entre ambos. El número que entramos en configurar, el Pfire lo tiene que leer de algún sitio. Yo ya me imagino donde está, pero vamos a correr el Regmon para asegurarnos... ejecutamos el salvapantallas y nos encontramos con que, antes de comenzar, lee una serie de parámetros del registro. Entre ellos, el serial num... si hacemos doble click sobre esa línea desde RegMon, se abrirá el RegEdit en esa rama, y veremos que el Pfire, como buen screensaver, tiene guardaditos ahí sus parámetros, incluido el serial num que le hayamos puesto... estos parámetros los debe de leer el Particle Fire cuando se ejecuta como salvapantallas... que bien... porque hace poco estuve leyendo algo sobre la función API RegQueryValue, que precisamente es la que lee datos del registro. Pero en realidad hay varias parecidas; RegQueryValue, RegQueryValueEx y RegQueryValueA. Para Win32 ansi la 'ExA' es la que nos vale, pero por si tenemos duda en estos casos, abrimos el fichero en el w32 Dasm, y le damos al botón de funciones importadas; allí nos aparecerá, para este caso, la función RegQueryValueExA, junto a otras funciones tipo OpenKey que van "abriendo" el camino para la QueryValue. Creo que el serial num se debe sentir un poco solo, así que vamos a tratar de acompañarle durante su viaje, a ver que hacen con él. Antes de empezar, escogemos un serial que nos haga de referencia, para identificarlo luego cuando lo lea y lo compare o haga lo que sea con él. Basándonos en nuestras pruebas, ponemos 1000000000 (mil millones) en la pantalla de configurar (podemos poner otro, pero recordemos cual), aceptamos y salimos. Tomamos tambien nota del equivalente en hexadecimal de nuestro número, para que no nos cojan desprevenidos; 3B9ACA00. Ahora pondremos el breakpoint y trataremos de subirnos al tren a la vez que lo hace nuestro serial.


2. Manos a la obra: un breakpoint en la lectura del registro

Para ello, ctrl+d, bpx RegQueryValueExA, ctrl+d. Traducción: entramos al SoftIce, ponemos un breakpoint en la función de Windows que lee del registro para que el SoftIce nos avise y salimos otra vez a Windows con ctrl+d. Ahora tenemos dos opciones, ejecutar nosotros mismos el salvapantallas o esperar que salga (previamente le habremos puesto el mínimo tiempo, un minuto). Esto puede parecer una tontería, pero si lo ejecutamos directamente, aparecerán unas 50 llamadas-basura a la función RegQueryValueExA (por lo menos a mi me pasa). Si esperamos que salga el Pfire por si solo, ya casi estaremos en el punto (nota: puede que durante la espera nos salte el breakpoint hasta 10 veces o más -o quizás ninguna-, por accesos rutinarios del sistema al registro; lo ignoramos haciendo ctrl+d para salir otra vez, y seguimos esperando hasta que pase el minuto y nos salga el pfire; para disminuir el riesgo de que pase esto, mejor no tener programas abiertos o solo lo imprescindible, y evidentemente, no tocar ninguna tecla ni mover el ratón).

Al cabo de un minuto, nos sale un breakpoint. Este ya es del propio Pfire, ya que en el borde inferior derecho del SoftIce pondrá 'Particle Fire'. No obstante, las primeras llamadas aún no nos interesan. Si hacemos F12, veremos que se ha llamado a la función a través de un CALL ESI o CALL EBX. Mientras esto suceda (seguramente saldrán 4 llamadas de este tipo en total, dos de cada), vamos haciendo ctrl+d, hasta que llegue un momento en que al salir el breakpoint y apretar F12 veamos que se ha llamado a la función por su nombre, algo así como:

...
PUSH 	ECX
PUSH 	ESI
PUSH 	ESI
PUSH 	004147F8
PUSH 	DWORD PTR [EBP-04]
CALL 	[ADVAPI32!RegQueryValueExA]
TEST 	EAX,EAX
...
(insisto, ten en cuenta que no 'aterrizas' directamente aquí, tienes que haber pulsado F12)

Vamos a comentar la jugada: en azul, los PUSH van pasando uno a uno los parámetros de la función, a la cual se llama con el CALL, en rojo. Concretamente, el penultimo valor contiene la dirección de memoria donde se halla el nombre del valor que se quiere leer (esto lo sabemos si miramos la documentación de referencia de WinAPI32). En este caso, vemos que es 4147F8, así que hacemos un d 4147F8 para que el Softice nos muestre que hay en esa dirección. Veremos en la pantalla de datos (si no la tienes a la vista, se pone/quita con wd) que es 'screensaveusepassword'. Bueno, esto no nos interesa, solo debe estar comprobando si tenemos puesto password con el salvapantallas o no. Así que ctrl+d, y de nuevo ha saltado el breakpoint por otro acceso al registro. Pulsamos F12 para ver quien le ha llamado y como, y tenemos:


015F:00404F7A PUSH	EAX
015F:00404F7B PUSH	ECX
015F:00404F7C MOV	EAX, [ESI+00000400]
015F:00404F82 PUSH	EDX
015F:00404F83 PUSH	00
015F:00404F85 PUSH	EBX
015F:00404F86 PUSH	EAX
015F:00404F87 CALL	[ADVAPI32 ! RegQueryvalueExA]
015F:00404F8D TEST	EAX,EAX
015F:00404F8F JNZ	00404FA7
015F:00404F91 MOV	EAX, (ESP+14]
015F:00404F95 MOV	ECX,ESI
015F:00404F97 MOV	[EDI],EAX
Reconocemos la idea otra vez, ¿no? En azul, son los parámetros que pasamos, en rojo la llamada a la función. Como veríamos si siguieramos saliendo y entrando, este fragmento del código es el que a partir de ahora se repetirá en cada lectura de valores del registro (claro que cada vez se lo montará para usar parámetros distintos, si no siempre haría lo mismo, pero será el mismo trozo de programa), de modo que vamos a borrar nuestro breakpoint con bc 00, y a poner otro justo en el punto donde llama a la función, para que antes de llamarla podamos ver qué pretende hacer; así que ponemos bpx 404f87 (fíjate cual es esta dirección en tu caso; podría ser otra). Este cambio de breakpoint no es 100% imprescindible, pero así nos ahorraremos darle al F12 cada vez y todo será más claro, puesto que pararemos ANTES de entrar en la función RegQueryValueExA. Por cierto, antes de salir hacemos d ebx y vemos : RandomTime, uno de los parámetros. Bueno, aún no es el que nos interesa, pero parece que estamos en el buen camino...

Así que ctrl+d para salir, y boom, ya estamos otra vez en SoftIce, justo antes de llamar otra vez a RegQueryValueExA (no hemos de pulsar F12). Vamos a ver que quiere leer ahora... hacemos d ebx, y nos sale GravityTime... otro parámetro, y justo despues suyo pone RandomTime...mmh, es el anterior... usamos alt+flecha arriba/abajo para desplazarnos por ese listado, y vemos como encima de esos parámetros, están los demás haciendo 'cola', concretamente deberíamos estar viendo RandomColor, ColorScheme, Particles y ... nuestro querido SerialNum! Pues bueno, como es evidente se trata de ir haciendo ctrl+d y comprobando ebx (todo el rato el listado será como el que he puesto más arriba) hasta que estemos en el punto en que va a leer el serial num. Son 4 veces, pero es recomendable ir uno a uno y comprobando ebx hasta que veamos que apunta directamente a SerialNum. Ahora querremos ver que hace con él, así que vamos a ir trazando la función. Estamos situados en el CALL [ADVAPI32 ! RegQueryvalueExA]; pulsamos F10 puesto que no nos interesa seguir a la función API en sí (tanto F8 como F10 ejecutan igual el programa, pero con F10 nos ahorramos ver la ejecución de CALLs como este que no nos interesan). Ahora representa que hemos vuelto de la función que lee el registro, y el valor de nuestro serial num de prueba debe andar en alguna parte. Observemos las lineas siguientes al CALL:

015F:00404F87 CALL [ADVAPI32 ! RegQueryvalueExA]
015F:00404F8D TEST EAX,EAX
015F:00404F8F JNZ 00404FA7
015F:00404F91 MOV EAX,[ESP+14]
015F:00404F95 MOV ECX,ESI
015F:00404F97 MOV [EDI],EAX
015F:00404F99 CALL 00404EC0

El test eax,eax y el jnz parecen una comprobación de rutina a la vuelta de la función. Los movimientos que hay despues parece que sean mas deliberados. Echamos un vistazo a esos valores, teniendo en cuenta que nuestro serial 1000000000 en hexadecimal es 3B9ACA00. Haciendo d esp+14, enseguida encontramos que en esa dirección está nuestro querido serial, que es copiado a EAX (nota: si veis el serial 'al revés' o sea 00CA9A3B, pulsad SHIFT+F3 para cambiar el formato de la ventana data hasta verlo como DWORD - gracias PicsOne!). Pero no le perdamos la pista, porque dos lineas más abajo vuelve a cambiar de vagón: ahora EAX se copia a la dirección apuntada por EDI. Bueno, pues miramos el valor de EDI con ? edi, y en mi caso es 412114. Ahí se sienta mi serial, así que vamos a poner un breakpoint para que cualquier acceso a esa posición de memoria sea detectada; ponemos bpm 412114 (ojo, no bpx si no bpm : no controla la ejecución si no la manipulación de lo que hay en ella). Tambien quitamos el bpx 404f87 que teníamos, puesto que ya nos ha aportado lo que queríamos; bc 00, ctrl+d y salimos del Softice.


3. La rutina de comprobación del serial

Y en una fracción de segundo ya hemos vuelto al Softice. Alguien está metiendo mano a nuestro serial. Vemos lo siguiente:

015F:00402726   MOV         EAX, [00412114]
015F:0040272B   PUSH        EAX
015F:00402720   CALL        00401000
015F:00402731   ADD         ESP,04
015F:00402734   TEST        EAX,EAX
015F:00402736   JNZ         00402A69

En pocas palabras, carga nuestro serial en EAX, lo envía a la pila con un PUSH y llama a algún tipo de rutina en la 401000... ¿quizás la que comprueba si es correcto? Vamos a seguir la rutina; vamos pulsando F8 (ahora si que nos interesa ver qué hace el CALL) hasta que al ejecutar el CALL, llegamos a una rutina que es ya la pieza clave de todo el asunto; veámosla parte a parte:

015F:00401000   MOV         EAX, [ESP+04]
015F:00401004   PUSH        ESI
015F:00401005   CMP         EAX,3B9ACA00 ; compara con 1000000000
015F:0040100A   JBE         00401049     ; si menor o igual, fracaso
015F:0040100C   CMP         EAX,77359400 ; compara con 2000000000
015F:00401011   JAE         00401049     ; si mayor o igual, fracaso

Esta primera parte en azul hace lo siguiente: recupera en EAX el valor de la pila (ESP+04), que como sabemos es nuestro serial, y a continuación comprueba que sea mayor que 3B9ACA00 (mil millones en decimal) y menor que 77359400 (dos mil millones en decimal). Si no es así, salta a la 401049, la cual huele a fracaso porque es más fácil ir a parar a ella que evitarla. No ibamos tan mal encaminados, ahora sabemos el rango concreto en el que se situa el serial. Claro que hay mil millones de números en ese intervalo, y si tenemos planes para el resto de nuestra vida, no vamos a poder probarlos todos. Veremos que más comprueba si el número cumple el requisito anterior. El código que viene ahora lo podemos ver, aunque al ir trazando con F8 no lo ejecutará (saltará a la 401049 tras la primera comparación) ya que nuestro serial no es MAYOR que mil millones. Si quisieramos trazar esta parte del código para ir mirando como cambian los registros, etc, podemos hacer una pirula; situarnos con el ratón en EAX y cambiar su contenido de 3B9ACA00 (nuestro serial) a 3B9ACA01, por ejemplo. Sea como sea, veamos lo que viene ahora, resaltado en rojo:

015F:00401013   MOV         ECX,EAX
015F:00401015   MOV         ESI,EAX
015F:00401017   SHL         ECX,14
015F:0040101A   MOV         EDX,EAX
015F:00401010   SHL         ESI,10
015F:0040101F   AND         ECX,FFF00000
015F:00401025   SHR         EDX,10
015F:00401028   OR          ESI,EDX
015F:0040102A   SUB         EDX,EDX
015F:00401020   XOR         ESI,ECX
015F:0040102E   MOV         ECX,EAX
015F:00401030   SHR         ECX,0C
015F:00401033   XOR         ESI,ECX
015F:00401035   MOV         ECX00002694
015F:0040103A   XOR         EAX,ESI
015F:0040103C   DIV         ECX
015F:0040103E   TEST        EDX,EDX
015F:00401040   JNZ         00401049
015F:00401042   MOV         EAX,00000001
015F:00401047   POP         ESI
015F:00401048   RET
015F:00401049   XOR         EAX,EAX
015F:0040104B   POP         ESI
015F:0040104C   RET

En pocas palabras: el tramo resaltado en rojo hace unas operaciones con nuestro número; el tramo siguiente, en blanco, determina si el número ha "pasado la prueba", y de no ser así, nos envía a la 401049... sí, la que olía a fracaso, aquí de color gris, que es la vía de salida de los perdedores... Pero por suerte nosotros no somos de esos. Nos interesa recorrer todo el tramo blanco, donde vemos que basicamente vuelve copiando un 1 en EAX, que a mi me huele que es una especie de flag tipo 'si, teniente; el chico está limpio', ya que en caso contrario, lo primero que se hace en la 401049 es poner eax a cero con el xor eax,eax así que veamos por donde atacar...

Lo primero que deberíamos pensar, una vez suponemos que este es el punto clave, es que todo ese lío en color rojo no merece que nos rompamos la cabeza; lo más fácil es parchear algo, tipicamente las comparaciones y/o los saltos. Es un principio del cracking elegir la vía más fácil y no calentarse la cabeza gratuitamente. Aunque por otra parte, también se dice que hay que ser lo más elegante posible, entendiendo esto como practicar la mínima intrusión al modificar el código del programa. Y desde luego si queremos aprender algo, habrá que hacer caso de lo que dicen los que saben del tema. Claro que tambien nos advierten de que en la práctica se dan casos como este, donde nos encontramos en una disyuntiva; lo más fácil es parchear, pero lo más elegante es generar un serial bueno sin tocar el programa original. En este caso, decidí que buscaría un serial correcto más por aprender algo que por otra cosa, pero en este tutorial desarrollaré ambas posibilidades; la de parchear es más apropiada para principiante, y la de analizar el código en rojo y encontrar un serial para amateur (requiere un poquito más de esfuerzo). Comencemos por el parcheo.


4. Opción fácil : parchear

Como sabeis, parchear consiste en sustituir unas intrucciones por otras; típicamente se cambian saltos y comparaciones por 'nops' (NOPs = no operation, o sea no hacer nada). También, en casos como este, podíamos haber considerado pasar de todo y 'borrar' los mensajes shareware parcheando con '00', pero por varios motivos no me parece una vía con futuro. El caso es que siempre hay varias opciones, pero en nuestro caso creo que la más simple sería parchear los 3 saltos a la 401049 con nops, de modo que si el número es menor que un millardo o mayor que dos, o si no cumple la condición que luego se aplica, en vez de saltar a la 401049, no hace nada y sigue. Con lo cual el código correrá sin alteraciones en su flujo hasta el final del tramo blanco. Así que ponemos code on, y vemos el valor de los bytes que equivalen a esas instrucciones en el fichero; la primera comparación es 3d00ca9a3b, el primer salto jbe es 7636, la segunda comparación es 3d00943577, el segundo salto jae es 7336, y el último es 7507 (en realidad solo nos interesa el código de los saltos, pero tomamos nota de los que van antes y despues para luego asegurarnos que hemos encontrado el correcto). Ponemos el editor hexa, en mi caso el hiew, cargamos el fichero, pulsamos F4 y elegimos hex, y pulsamos F7 para buscar. Con la tecla tab saltamos al campo 'HEX' y tecleamos 3d00ca9a3b763d3d00943577 (o solo 763d pero luego a ojo miramos que lo de antes y despues es lo que esperábamos, si no seguimos buscando). Así nos aseguramos de estar en el punto correcto comprobando que los códigos anteriores y posteriores coinciden con los que anotamos antes. El hiew nos situará al principio de la localización de la cadena de bytes hallada. Como todo está en la misma zona, localizando el 763d ya vemos a ojo los demás un poco más abajo. Pulsamos F3 para editar, y situándonos en los lugares correctos, cambiamos 763d,7336 y7507 por 9090, 9090 y 9090 (si metemos la pata, con esc deshacemos los cambios y luego empezamos de nuevo). Si esta ok, pulsamos F9 para grabar los cambios en el fichero. Para hacerlo aún más fácil, ahí va una lista de los offset locales, el valor original de cada byte y el valor parcheado:

                File 1:      File 2:      
    Offset:     ORIGINAL     PARCHEADO      
----------------------------------------------
      40Ah      76h          90h       
      40Bh      3Dh          90h       
      411h      73h          90h       
      412h      36h          90h       
      440h      75h          90h       
      441h      07h          90h       

Number of differences: 6

(en el hiew, para cambiar entre vista de offset local y offset global, pulsa alt+f1)

En fin, si despues de esto aún te quedan ganas, puedes buscar un crackmaker y crear tu propio crack a partir del fichero original (del que habrás guardado una copia) y el que acabamos de parchear.


5. Opción no tan fácil: análisis de la rutina de chequeo del serial

Aunque a mi que no tengo ni idea del tema me dió un poco de dolor de cabeza, solo es cuestión de unos minutos y paciencia descifrar el mecanismo que sigue el código que más arriba resalté en rojo. Si nuestro número es "#", el programa realiza la siguiente comprobación:

XOR [ { XOR (OR a,b , AND c,d)  }, e ]

, donde, si "#" es igual al serial introducido:

a = shl (#,10)
b = shr (#,10)
c = shl (#,14)
d = fff00000
e = shr (#,0c)

Una vez calculado el churro en cuestión, llamémosle "F" al resultado, realiza otro xor:

XOR (#,F)

, y sobre este resultado, efectua una división (DIV) por 2694h (la 'h' quiere decir que está en hexadecimal, como sabreis). Si la división tiene resto (módulo), fracaso (también conocido como billete de ida a la 401049). Por ejemplo, para 3B9ACA01 (1000000001 en decimal), el resto es 26f, o lo que es lo mismo, distinto de cero, o lo que es lo mismo, de cabeza a la 401049, o lo que es lo mismo, fracaso, chico malo, etc.

El caso es que integrándolo todo, podemos formularlo como una ecuación. Pero ignoro si es posible solucionarla de modo analítico (reordenando, simplificando, despejando...), ya que para una cosa que si que sé un poco (matemáticas), me encuentro con un tipo de funciones (and, or, xor...) a las que no estoy acostumbrado. Caso de poderse solucionar, como que ha de tener muchas soluciones (no va a haber un único serial), nos quedaría en función de un valor que daríamos nosotros, en plan f(x)=y. Pero como digo, yo no sé solucionarlo así. De modo que la alternativa son los métodos numéricos (o sea, probando hasta que hallemos un número correcto - con la potencia de cálculo de un ordenador, sería cuestión de fracciones de segundo). Yo pretendía probarlo con el solver del Excel (da valores hasta encontrar una solución) pero... no puedo utilizar esas funciones porque no están disponibles en el Excel (alguna lo está, pero que yo sepa solo con resultados booleanos tipo verdadero/falso, no resultados numéricos). De modo que ya que en assembler existen están funciones, quizás pudiéramos programar algo... si, hombre, que nadie se asuste, yo no sé casi nada de assembler pero mira ahí arriba... el 90 % de nuestro keygen ya está hecho. Simplemente hemos de cambiar que, si el número no da resto cero tras la transformación, en vez de "echarnos", aumente una unidad al número inicial y pruebe de nuevo. Yo, la primera vez, hice algo muy simple; una rutina que comienza por 1000000000, y a partir de ahí, hace un cálculo; le suma una, le hace todo el churro de más arriba, y si no da el resto cero, vuelve a sumarle uno, hacer todo el churro... hasta que salga uno bueno. Como no sabía cómo mostrar ese número, se me ocurrió poner un messageboxa una vez sale del cálculo, antes de terminar el programa, para poder poner un bpx y ver que número había salido (hice que cada vez la rutina guardase una copia del número que prueba en EDI, porque el original en EAX queda 'desfigurado' por la comprobación). Así que una vez ensamblado, pongo un bpx messageboxa, y cuando me sale, le doy a F12 para volver a mi programa principal, y escribo ? edi , obteniendo 1000002675 en decimal, esto es, ¡el serial del cliente número uno! (que honor, seguro que lo reservaban para alguien importante). El programa lo ensamblé con el mASM32; como digo no sé assembler más que 4 cosas, por lo cual no puedo entrar en mucha explicación sobre parámetros de ensamblado, etc, simplemente creé el ejecutable. Si no me equivoco, era así:

.386
.model flat, stdcall
option casemap:none
include c:\masm32\include\windows.inc
include c:\masm32\include\kernel32.inc
includelib c:\masm32\lib\kernel32.lib
include c:\masm32\include\user32.inc
includelib c:\masm32\lib\user32.lib
; cosas generales para ensamblar y linkar

.data
MsgBoxCaption  db "SuperCutre's Key-Gen",0
MsgBoxText  db "Bpx en el NOP y el resultado en EDI",0
Serial_Start  dd 3b9aca00h
; reservamos e inicializamos unas variables del msgbox
; y el número de inicio (1000000000)

.code

start:     ; vamos a por ello

Calculo:
inc [Serial_Start]   ; aumentamos en uno el número de partida
mov eax,Serial_Start ; lo pasamos a EAX para hacer el test
mov edi,eax          ; pero edi guarda una copia por si sale bien
mov ecx,eax	     ; y comienza la fiesta...
mov esi,eax
shl ecx,14h
mov edx,eax
shl esi,10h
and ecx,0fff00000h
shr edx,10h
or esi,edx
sub edx,edx
xor esi,ecx
mov ecx,eax
shr ecx,0ch
xor esi,ecx
mov ecx,2694h
xor eax,esi
div ecx
test edx,edx
jnz Calculo ; si no es un serial válido, prueba otra vez
invoke MessageBoxA,NULL,addr MsgBoxText,addr MsgBoxCaption,MB_OK
; este messagebox hace saltar el breakpoint y nos permite mirar
; en EDI el último número probado, es decir el bueno
invoke ExitProcess, NULL
end start

Espero que al menos se entienda la idea. Mi intención es que otros como yo le pierdan un poco el miedo al assembler y vean que, aunque falten conocimientos, con un poco de ingenio a veces se pueden solucionar los problemas.


6. Opción no tan fácil: creación de un keygen semi-decente

Pero como crear un key-gen, nada. Yo al final creé uno, estoy seguro que está fatal programado, que da pena y que quizás solo funcione en mi PC, pero me da números de registro buenos. He de decir que no podría haberlo creado sin la *gran* ayuda de Mr. Crimson que me proporcionó una rutina para convertir de hexadecimal a decimal (para poder sacar el resultado como string en un msgbox). Muchas gracias una vez más, Mr. Crimson. El programa también utiliza la funcion gettickcount para crear un número distinto cada vez, aunque no totalmente aleatorio (cada vez es mayor). No es perfecto ni mucho menos, pero da el pego. Para el que le interese:

.386
.model flat, stdcall
option casemap:none
include c:\masm32\include\windows.inc
include c:\masm32\include\kernel32.inc
includelib c:\masm32\lib\kernel32.lib
include c:\masm32\include\user32.inc

includelib c:\masm32\lib\user32.lib
.data
MsgBoxCaption  db "SuperCutre's Key-Gen",0
Mensaje db "       = (by Champion) =",13,13,"       Se va
a generar un",13,"    número de serie
para",13,"      Particle Fire v1.1a",0
MsgBoxCaption2  db " Su número:",0
Buffer db 10 dup(0),0
Serial_Start  dd 3b9aca00h

.code

start:

invoke MessageBox,NULL,addr Mensaje,addr MsgBoxCaption,MB_OK
invoke GetTickCount
add [Serial_Start],eax

Calculo:
inc [Serial_Start]
mov eax,Serial_Start
mov edi,eax
mov ecx,eax
mov esi,eax
shl ecx,14h
mov edx,eax
shl esi,10h
and ecx,0fff00000h
shr edx,10h
or esi,edx
sub edx,edx
xor esi,ecx
mov ecx,eax
shr ecx,0ch
xor esi,ecx
mov ecx,2694h
xor eax,esi
div ecx
test edx,edx
jnz Calculo
nop

mov eax,edi ; lo preparo para la Hex a Dec
mov esi,offset Buffer
add esi,11
mov byte ptr[esi],00
dec esi

; Hex a Dec proporcionado por Mr. Crimson
mov ecx,00000010 
c20: cmp eax,ecx 
jb c30 
xor edx,edx 
div ecx 
or dl,30h
mov byte ptr[esi],dl
dec esi
jmp c20
c30: or al,30h
mov byte ptr[esi],al

invoke MessageBox,NULL,ESI,addr MsgBoxCaption2, MB_OK
invoke ExitProcess, NULL
end start


7. Conclusiones finales

Para terminar veamos a modo de resumen lo que hemos hecho:

1. Poner bpx en regqueryvalueexa
2. Encontrar donde lee y guarda nuestro serial
3. Encontrar la rutina que luego lo lee y lo verifica
4. Estudiar ese código y ser capaces de crear una clave válida

He de decir por pen-último, que con posterioridad a todo lo explicado, me he encontrado dos patchers en internet para Particle Fire v1.1a, pero ningún keygen, lo cual confirma que lo más fácil es parchear bien y no mirar a quien. Asímismo, de los dos parcheadores, uno no funciona (al menos a mí), y el otro me parece que no tiene mucho estilo, porque lo que hace es dedicarse a sustituir el inicio de todas las cadenas de texto por '00' para evitar que salgan (si recordais, durante el tutorial hemos considerado esta opción pero la descarté porque es poco elegante, tediosa, y no sabes hasta que punto te garantiza resultados). Aún peor, ese patch sustituye *todas* las cadenas de texto, con lo cual se ha cargado no solo los mensajitos shareware si no también la parte de frases aleatorias que forman parte del salvapantallas estés registrado o no, y que pueden anularse con una simple casilla del cuadro 'configurar protector de pantalla', con lo que más que parchear, se ha mutilado el programa innecesariamente.

Ahora sí, por último, pedir disculpas si algo no ha quedado bien explicado, o por los errores que sin duda habré cometido, pero puedo asegurar que con este sistema me he librado de los mensajitos no deseados. Y, bueno, ahora ya puedo volver a mi antiguo salvapantallas con la cabeza bien alta...

= Champion =