| ESTUDIO COLECTIVO DE DESPROTECCIONES Última Actualizacion: 25/10/2001 |
Programa | Particle Fire screensaver v1.1a | W95 / W98 / NT
Descripción | Salvapantallas
Tipo | Trial de 7 días
Tipo de Tutorial | [X]Original, []Adaptación, []Aplicación, []Traducción
Url | http://www.longbowdigitalarts.com/particlefire.html
Protección | Serial
Dificultad | 1) Principiante, 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
INTRODUCCION
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 ATAQUE
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 `CALL`s 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 '`nop`s' (`NOP`s = 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 `nop`s, 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` y`7507` 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 `2694`h (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 =
[ Entrada | Documentos Genéricos | WkT! Web Site ]
[ Todo el ECD | x Tipo de Protección | x Fecha de Publicación | x Orden Alfabético ]
(c) Whiskey Kon Tekila [WkT!] - The Original Spanish Reversers. Si necesitas contactar con nosotros , lee esto antes e infórmate de cómo puedes ayudarnos