Buenas a todos,

por si alguno de vosotros está lo suficientemente aburrido como para intentar resolver este pequeño keygenme que acabo de programar, aquí os lo dejo:
[Enlace externo eliminado para invitados]

Y como obviamente se puede crackear modificando un único byte, la única solución aceptable es la programación de un keygen, o al menos conseguir una tripla Nombre, Serial1, Serial2 válidas.

Para cualquier duda no dudéis en dejar un mensaje en el hilo o enviarme un MP.

Mucha suerte a todos.

Saludos.
Imagen
Buenas a todos,

pues parece que poca gente lo ha intentado o no sabe cómo empezar. Bueno, decir que obviamente conocía el grado de dificultad del keygenme, y es curioso porque es muy sencillo para alguien que ya haya solucionado algun crackme de aritmética modular con enteros grandes pero puede resultar complicado para alguien que nunca haya visto algo parecido.

Voy a dar unas cuantas pistas, y si en unos días no hay solución, ya publicaré yo la misma.

Bien, los pocos que os habéis decidido a probar el keygenme ya os habréis dado cuenta de quel programa utiliza unos números muy grandes, pero quizás no sabéis qué operaciones realiza en cada momento. Yo para esto suelo utilizar el [Enlace externo eliminado para invitados] con el que compruebo qué operación ha realizado. Una vez entendamos qué hace y veamos que no es la típica aritmética de los keygenes normales (por ejemplo, serial = suma de los asciis del nombre por 1234) es que probablemente se trate de un cifrado de criptografía, así que toca buscar un poco de información en internet hasta encontrar el algoritmo usado (en este caso es el de una "firma digital" bastante conocida). Una vez entendido todo ya sabremos qué nos falta por encontrar y una vez solucionado esto ya será muy sencillo programar el keygen (o al menos dar con una solución válida).

Saludos.
Imagen
Solución al Keygenme:

Abrimos el Keygenme con el Olly y ya apreciamos unas cadenas que parecen grandes números en hexadecimal. De hecho si intentamos buscar todas las cadenas del keygenme sólo encontraremos éstas:

Código: Seleccionar todo

Text strings referenced in KeygenMe:.text
Address    Disassembly                   Text string
0040107D   PUSH DWORD PTR DS:[404205]    (Initial CPU selection)
00401083   PUSH KeygenMe.00404034        ASCII "FA1317A3C3A8CB6F1A771334E80C4C5F"
004010D3   PUSH KeygenMe.00404055        ASCII "8C8AF5013DC471AFADAB3A01F14B9129"
004010E3   PUSH KeygenMe.00404076        ASCII "D9A5EEB2E5A16DF88B0ACD1C207BA91B"
00401208   PUSH KeygenMe.00404000        ASCII "Correcto"
0040120D   PUSH KeygenMe.00404009        ASCII "Muy bien hecho.Ahora programa el keygen."
Vale, al lío. Está claro que el procemiento que nos interesa es lo que sucede tras apretar el botón "Probar" en el programa, que empieza en la línea 0x4010AC, así que introducimos algo como nombre y como seriales y probamos.:

Código: Seleccionar todo

004010AC  |. 6A 64          PUSH 64                              ; /Count = 64 (100.)
004010AE  |. 68 B4404000    PUSH KeygenMe.004040B4               ; |Buffer = KeygenMe.004040B4
004010B3  |. 68 EA030000    PUSH 3EA                             ; |ControlID = 3EA (1002.)
004010B8  |. FF75 08        PUSH DWORD PTR SS:[EBP+8]            ; |hWnd
004010BB  |. E8 C6010000    CALL <JMP.&user32.GetDlgItemTextA>   ; \GetDlgItemTextA
004010C0  |. 0BC0           OR EAX,EAX
004010C2  |. 0F84 52010000  JE KeygenMe.0040121A
004010C8  |. A3 01424000    MOV DWORD PTR DS:[404201],EAX
004010CD  |. FF35 09424000  PUSH DWORD PTR DS:[404209]           ; /Arg2 = 02840840
004010D3  |. 68 55404000    PUSH KeygenMe.00404055               ; |Arg1 = 00404055 ASCII "8C8AF5013DC471AFADAB3A01F14B9129"
004010D8  |. E8 C30B0000    CALL KeygenMe.00401CA0               ; \KeygenMe.00401CA0
004010DD  |. FF35 0D424000  PUSH DWORD PTR DS:[40420D]           ; /Arg2 = 028408A0
004010E3  |. 68 76404000    PUSH KeygenMe.00404076               ; |Arg1 = 00404076 ASCII "D9A5EEB2E5A16DF88B0ACD1C207BA91B"
004010E8  |. E8 B30B0000    CALL KeygenMe.00401CA0               ; \KeygenMe.00401CA0
Vemos que lo primero que hace es leer el Nombre que metamos y en caso de estar vacío ya no continúa con el procedimiento. Y a continuación llama dos veces al procedimiento 0x401CA0 junto con dos de los supuestos grandes números en hexadecimal. Así que al llegar, por ejemplo a la línea 0x4010D8, pulsamos con el botón derecho en la pila encima de la línea del argumento 2, y escogemos la opción "Follow in dump", en mi caso me muestra:

Código: Seleccionar todo

02840840  01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ...............
02840850  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
02840860  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
02840870  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
02840880  00 00 00 00 00 00 00 00 AB AB AB AB AB AB AB AB  ........««««««««
Ejecuto la llamada con F8 (para no entrar en la misma) y veo que ahora en esa posición tengo:

Código: Seleccionar todo

02840840  04 00 00 00 00 00 00 00 29 91 4B F1 01 3A AB AD  .......)‘Kñ:«­
02840850  AF 71 C4 3D 01 F5 8A 8C 00 00 00 00 00 00 00 00  ¯qÄ=õŠŒ........
Vale, está claro que esa función carga el número en memoria. Fijaos que está en Little Endian, por eso está del revés.
Y a continuación lee los seriales introducidos y llama al mismo procedimiento para cargarlos en memoria. Esto pasa si habéis introducido números o letras de la "A" a la "F" como los números en hexadecimal, ya que en caso contrario si el programa no es capaz de entender cualquier serial como un número hex, lo interpreta como un "0". En mi caso va bien, porque he introducido el valor "81818181" como Serial1:

Código: Seleccionar todo

02840900  01 00 00 00 00 00 00 00 81 81 81 81 00 00 00 00  ...........
y "69696969" como Serial2:

Código: Seleccionar todo

02840960  01 00 00 00 00 00 00 00 69 69 69 69 00 00 00 00  .......iiii....
Creo que hasta aquí nadie ha tenido problemas. Así que veamos como continúa el programa:

Código: Seleccionar todo

00401135  |. FF35 0D424000  PUSH DWORD PTR DS:[40420D]           ; /Arg4 = 028408A0
0040113B  |. FF35 05424000  PUSH DWORD PTR DS:[404205]           ; |Arg3 = 028407E0
00401141  |. FF35 11424000  PUSH DWORD PTR DS:[404211]           ; |Arg2 = 02840900
00401147  |. FF35 0D424000  PUSH DWORD PTR DS:[40420D]           ; |Arg1 = 028408A0
0040114D  |. E8 DE0B0000    CALL KeygenMe.00401D30               ; \KeygenMe.00401D30
00401152  |. FF35 11424000  PUSH DWORD PTR DS:[404211]           ; /Arg4 = 02840900
00401158  |. FF35 05424000  PUSH DWORD PTR DS:[404205]           ; |Arg3 = 028407E0
0040115E  |. FF35 15424000  PUSH DWORD PTR DS:[404215]           ; |Arg2 = 02840960
00401164  |. FF35 11424000  PUSH DWORD PTR DS:[404211]           ; |Arg1 = 02840900
0040116A  |. E8 C10B0000    CALL KeygenMe.00401D30               ; \KeygenMe.00401D30
Dos llamadas a 0x401D30 con muchos argumentos. Pues nada, vamos hasta la primera de las llamadas y vemos en la pila los argumentos enviados:

Código: Seleccionar todo

0018FA3C   028408A0  |Arg1 = 028408A0
0018FA40   02840900  |Arg2 = 02840900
0018FA44   028407E0  |Arg3 = 028407E0
0018FA48   028408A0  \Arg4 = 028408A0
Con el botón derecho pulsamos en cada una de esas líneas y escogemos la opción "Follow in dump" para ver qué es cada uno. En mi caso veo que se corresponden a:
0018FA3C 028408A0 |Arg1 = 028408A0 <- D9A5EEB2E5A16DF88B0ACD1C207BA91B
0018FA40 02840900 |Arg2 = 02840900 <- 81818181 (Serial1)
0018FA44 028407E0 |Arg3 = 028407E0 <- FA1317A3C3A8CB6F1A771334E80C4C5F
0018FA48 028408A0 \Arg4 = 028408A0 <- D9A5EEB2E5A16DF88B0ACD1C207BA91B
Pulso F8 para ver que pasa y compruebo que el argumento 2 y el 3 se mantienen igual, pero el 1 y el 4 (que eran el mismo) ha cambiado, así que supongo que es enviado dos veces a la llamada porque una de ellas es para almacenar el resultado. Bueno, pero el asunto es que ahora tengo (en mi caso en 0x028408A0) el valor E0029F08F58C579D6FE46E6D0DE8FA1A y no sé que ha pasado. Aquí es donde recomiendo usar una calculadora de números grandes y yo suelo utilizar el [Enlace externo eliminado para invitados] de nuestro buen amigo Mr Paradox. Introduzco los valores en el mismo orden que en la llamada (no os olvidéis de cambiar el input base del Keygener Assistant a 16):
Prime P <- D9A5EEB2E5A16DF88B0ACD1C207BA91B
Prime Q <- 81818181
Modul N <- FA1317A3C3A8CB6F1A771334E80C4C5F
y pruebo los botoncitos hasta encontrar algo que me guste. Bueno, realmente como veo que hay tres parámetros en lugar de dos, no hay muchas operaciones que los usen así que pruebo la primera de tres parámetros: "P*Q MOD N" y no obtengo premio, pero al probar la otra opción: "P^Q MOD N" obtengo el mismo resultado que genera el Keygenme. ¡Bien!. Ya sabemos que el procedimiento 0x401D30 corresponde a "P^Q MOD N".
Vale, ahora que va a volver a ser llamado, vuelvo a ver qué valores lleva y pruebo en el Keygener Asistant esos valores incluso antes que en el Keygenme:
81818181(Serial1) ^ 69696969(Serial2) MOD FA1317A3C3A8CB6F1A771334E80C4C5F = AFC3FBEEDAA470EA63885B1823C3AE65
y el programita hace lo propio. Esto va bien. A ver como continúa:

Código: Seleccionar todo

0040116F  |. FF35 0D424000  PUSH DWORD PTR DS:[40420D]           ; /Arg3 = 028408A0
00401175  |. FF35 11424000  PUSH DWORD PTR DS:[404211]           ; |Arg2 = 02840900
0040117B  |. FF35 0D424000  PUSH DWORD PTR DS:[40420D]           ; |Arg1 = 028408A0
00401181  |. E8 8A0A0000    CALL KeygenMe.00401C10               ; \KeygenMe.00401C10
00401186  |. FF35 0D424000  PUSH DWORD PTR DS:[40420D]           ; /Arg3 = 028408A0
0040118C  |. FF35 05424000  PUSH DWORD PTR DS:[404205]           ; |Arg2 = 028407E0
00401192  |. FF35 0D424000  PUSH DWORD PTR DS:[40420D]           ; |Arg1 = 028408A0
00401198  |. E8 63090000    CALL KeygenMe.00401B00               ; \KeygenMe.00401B00
Funciones nuevas... pero hacemos lo mismo, vamos hasta la primera llamada, vemos los argumentos enviados y comprobamos el resultado con el de las distintas operaciones que nos permite el Keygener Assistant.
Prime P <- E0029F08F58C579D6FE46E6D0DE8FA1A (el resultado del primer "P^Q MOD N")
Prime Q <- AFC3FBEEDAA470EA63885B1823C3AE65 (el resultado del segundo "P^Q MOD N")
y sí, en cuanto probemos el "P MUL Q" obtenemos el mismo resultado, para mis datos introducidos:
99CD4929D75DBCCA00B2A24CB4C30398532BFB120F279FAF78B4D392E6B65842
Hago lo mismo en la siguiente llamada (la que llama a 0x401B00) y veo que este caso es un "P MOD Q" con el resultado de 566BFA4CD1F39C2094C43AA1B4DDA97B
Pues no parece tan difícil este keygenme. A ver cómo continúa por sin nos sorprende con algo distinto:

Código: Seleccionar todo

0040119D  |. E8 D6060000    CALL KeygenMe.00401878
004011A2  |. FF35 01424000  PUSH DWORD PTR DS:[404201]           ; /Arg2 = 00000009
004011A8  |. 68 B4404000    PUSH KeygenMe.004040B4               ; |Arg1 = 004040B4 ASCII "PeterPunk"
004011AD  |. E8 06070000    CALL KeygenMe.004018B8               ; \KeygenMe.004018B8
004011B2  |. E8 61070000    CALL KeygenMe.00401918
004011B7  |. 68 E0414000    PUSH KeygenMe.004041E0               ; /Arg3 = 004041E0
004011BC  |. 6A 10          PUSH 10                              ; |Arg2 = 00000010
004011BE  |. 50             PUSH EAX                             ; |Arg1
004011BF  |. E8 AC070000    CALL KeygenMe.00401970               ; \KeygenMe.00401970
Esta parte no va de enteros grandes y dependerá un poco de la experiencia del cracker saber que hace, por ejemplo, yo sólo con entrar en la primera llamada y ver las constantes:

Código: Seleccionar todo

00401898  |. C700 01234567  MOV DWORD PTR DS:[EAX],67452301
0040189E  |. C740 04 89ABCD>MOV DWORD PTR DS:[EAX+4],EFCDAB89
004018A5  |. C740 08 FEDCBA>MOV DWORD PTR DS:[EAX+8],98BADCFE
004018AC  |. C740 0C 765432>MOV DWORD PTR DS:[EAX+C],10325476
ya intuyo un hash MD5, así que no entro en las dos siguientes llamadas pero me animo a ver en el dump la dirección a la que apunta eax tras salir de ese tercer call. Así que estando parado en la línea 0x4011B7 veo que EAX me apunta a 0x404260 en donde me encuentro:

Código: Seleccionar todo

00404260  8D 67 B8 EF 33 F2 64 3E 0B 4F 95 0B BB 25 91 9D  g¸ï3òd>O•»%‘
que es el MD5 de "PeterPunk". Para mi muy fácil de reconocer pues lo veo constantemente (aún recuerdo que antiguamente lo que veía constantemente era el CRC32 que en mi caso es muy fácil: 77757999). De todas formas, para eso tenemos el Keygener Assistant, que tiene innumerables funciones de hashes y un analizador, donde le introduces el texto, el hash obtenido y ya te dice que tipo es. Mr Paradox está en todo.
Bueno, nos queda una llamada en el último grupo de ellas que copie del keygenme y suponemos que va a ser que convierta en cadena el valor del MD5. Acertamos.
Continuamos en el Olly:

Código: Seleccionar todo

004011C4  |. FF35 19424000  PUSH DWORD PTR DS:[404219]           ; /Arg2 = 026809C0
004011CA  |. 68 E0414000    PUSH KeygenMe.004041E0               ; |Arg1 = 004041E0 ASCII "8D67B8EF33F2643E0B4F950BBB25919D"
004011CF  |. E8 CC0A0000    CALL KeygenMe.00401CA0               ; \KeygenMe.00401CA0
004011D4  |. FF35 09424000  PUSH DWORD PTR DS:[404209]           ; /Arg4 = 02680840
004011DA  |. FF35 05424000  PUSH DWORD PTR DS:[404205]           ; |Arg3 = 026807E0
004011E0  |. FF35 19424000  PUSH DWORD PTR DS:[404219]           ; |Arg2 = 026809C0
004011E6  |. FF35 09424000  PUSH DWORD PTR DS:[404209]           ; |Arg1 = 02680840
004011EC  |. E8 3F0B0000    CALL KeygenMe.00401D30               ; \KeygenMe.00401D30
y vemos que llama a dos procedimientos que nos son conocidos, pues ya han sido utilizados. El primero es la carga del hash MD5 del nombre como si fuese un número igual que los de antes y luego la operación "P^Q MOD N", en este caso con los siguientes valores:
P <- 8C8AF5013DC471AFADAB3A01F14B9129
Q <- 8D67B8EF33F2643E0B4F950BBB25919D (MD5 del nombre)
N <- FA1317A3C3A8CB6F1A771334E80C4C5F
obteniendo en mi caso C28899B58E7D9BB37F8FD186F6D10D45
Y ya nos queda una única llamada:

Código: Seleccionar todo

004011F1  |. FF35 09424000  PUSH DWORD PTR DS:[404209]           ; /Arg2 = 02680840
004011F7  |. FF35 0D424000  PUSH DWORD PTR DS:[40420D]           ; |Arg1 = 026808A0
004011FD  |. E8 CE070000    CALL KeygenMe.004019D0               ; \KeygenMe.004019D0
00401202  |. 0BC0           OR EAX,EAX
00401204  |. 75 14          JNZ SHORT KeygenMe.0040121A
a la que son enviados como argumentos (en mi caso):
C28899B58E7D9BB37F8FD186F6D10D45 y 566BFA4CD1F39C2094C43AA1B4DDA97B
y como además es seguida por una comprobación del valor de EAX y un salto, estoy seguro de que es simplemente una comparación de ambos valores y si coinciden EAX será 0 y obtendré el mensaje de felicitación.
Pues ya sabemos todo lo que necesitamos del keygenme. Tenemos que solucionar la siguiente ecuación:
(num1 ^ serial1 mod n) * (serial1 ^ serial2 mod n) mod p = num2 ^ MD5(nombre) mod n
teniendo las constantes num1, num2 y p:
num1 <- D9A5EEB2E5A16DF88B0ACD1C207BA91B
num2 <- 8C8AF5013DC471AFADAB3A01F14B9129
n <- FA1317A3C3A8CB6F1A771334E80C4C5F
que sería equivalente a:
num1^serial1 * serial1^serial2 = num2 ^ MD5(nombre) (mod n)
lo primero que pensamos es en darle un valor aleatorio a serial1 y calcular serial2 en base a este. Pero como no parece un cálculo fácil y con él sólo encontrariamos una solución para un nombre específico no podríamos programar el keygen. Así que toca buscar en internet información sobre aritmética modular a ver si encontramos algo que resuelva el caso anterior o por lo menos nos de una idea de a que nos estamos enfrentando. Obviamente aquí dependería de la pericia de cada uno como buscador de información, pero creo que más temprano o más tarde daríamos todos con que se trata de la firma de [Enlace externo eliminado para invitados] ya que si of fijáis en la última premisa de la verificación nos indica:
g^H(m) = y^r * r^s (mod p)
que es lo que nosotros tenemos:

Código: Seleccionar todo

mi notación   Wikipedia
-----------   ---------
    serial1 = r
    serial2 = s
          n = p
       num1 = y
       num2 = g
MD5(nombre) = H(m)
Vale, pues ahora toca generar una firma válida para mi nick PeterPunk. Lo primero que tenemos que hacer según la wikipedia es coger un número aleatorio k tal que "0<k<p-1" y "mcd(k, p-1) = 1".
Esta parte parece fácil:
p-1 = FA1317A3C3A8CB6F1A771334E80C4C5F - 1 = FA1317A3C3A8CB6F1A771334E80C4C5E
y para asegurarme que k es coprimo (que no tengan divisores comunes) con p-1, factorizo p-1 (utilizando otra vez el Keygener Asistant, en Encryption -> Assymetric -> RSA -> RSA Solver, copio el valor de p-1 en "Modulus N" y pulso "Factoring (N)":
FA1317A3C3A8CB6F1A771334E80C4C5E = 02 * 1F * 05FE0880D455 * AC50A24B8A51DF5E712D
Vale, 3 me va a valer como k, así que ya puedo calcular r (serial1):
r = g^k (mod p)
r = 8C8AF5013DC471AFADAB3A01F14B9129 ^ 3 (mod FA1317A3C3A8CB6F1A771334E80C4C5F)
r = 3046002ECC709FE4EAA173B055F8D8C2
Y ahora a por el s (serial 2)... pero... ¿qué es esa x que necesito? Yo no tengo ninguna clave secreta...
Vale, sabemos que esa "x" es la necesaria para cumplir que "y = g ^ x (mod p)". Y este es ni más ni menos que el consabido problema del logaritmo discreto, que a fin de cuentas es en lo que se basan algunos sistemas criptográficos como el de ElGamal ya que para un ordenador es muy sencillo calcular "y" en teniendo "g" y "x", pero hallar esa "x" con los otros valores es dificilísimo. De hecho yo el único algoritmo que sé implementar para calcularlo es el Rho de Pollard y me tarda unos 20 minutos en solucionar uno de 56 bits así que no me quiero ni imaginar cuantos años tardaría con este. Por suerte en internet encontramos de todo, así que la mejor opción es ayudarnos de esta calculadora online: [Enlace externo eliminado para invitados] en la que introduciremos el problema para que lo solucione por nosotros:

Código: Seleccionar todo

p := 0xFA1317A3C3A8CB6F1A771334E80C4C5F;
K := GF(p);
g := K ! 0x8C8AF5013DC471AFADAB3A01F14B9129;
y := K ! 0xD9A5EEB2E5A16DF88B0ACD1C207BA91B;
x :=  Log(g, y);
printf "%h", x;
y en tan sólo 5 segundos obtengo 92F122C353F0F5E1D0796C544650E028 como valor de la clave secreta x.
Ahora sí que tengo todo lo necesario para calcular s (serial2), que según la wiki se haría así:
s = (H(m) - x*r) * k^-1 (mod p-1)
precisamente k tiene que ser coprimo con p-1 porque de lo contrario no podríamos calcular su inversa (k^-1 mod p-1)
s = (8D67B8EF33F2643E0B4F950BBB25919D - B65AFE2C3CB2FEA56E5952176E38783E) * k^-1 (mod p-1)
s = D11FD266BAE83107B76D562934F965BD * A6B76517D7C5DCF4BC4F6223455D883F (mod p-1)
s = 9910F8AE2A3054279B4C231F5F01E609
Ahora pruebo la tripla: PeterPunk, 3046002ECC709FE4EAA173B055F8D8C2, 9910F8AE2A3054279B4C231F5F01E609 en el keygenme.
¡¡¡¡¡BINGO!!!!!
Además como ya conozco el valor de "x" ya no tengo que resolver ningún logaritmo discreto y como existen millones de ks válidas puedo programar un keygen con infinidad de seriales válidos para cada nombre.
Adjunto un rar con los dos ejecutables (keygenme y keygen) y los códigos fuentes de ambos en masm32, por si alguien quiere echarle un vistazo:
[Enlace externo eliminado para invitados]

Saludos.

PD: Tanto la librería Bignum como la Cryptohash usadas en ambos programas fueron creadas por [Enlace externo eliminado para invitados].
PD2: Ya recuerdo porque normalmente no escribo tutoriales. Un programa que soluciono en 5 minutos y tardo más de 3 horas en escribir el tutorial.
Imagen


Excelente !!

Yo no pasé de los primeros pasos

Pero bueno, gracias por el tuto que a muchos nos servirá

PD: Igual y para no escribir mejor hacer un videtotutorial

Soy lo que soy gracias a que ustedes son lo que soN

Skype: bibetto.hax
Lastima que no tuve suficiente tiempo para seguir adelante con el reto! De todos modos gracias por la atención @PeterPunk espero pronto pongas mas de estos!
1337 & culture!
Responder

Volver a “Cracking/Reversing”