TDC’s Remove the NAG – Parche

Intro

Es un crackme realizado en ensamblador y en el que el objetivo es remover la NAG de la forma más limpia posible.

Analizando a la víctima

Abrimos el crackme con Olly y ya a simple vista vemos los mensajes de la Nag y parte del código interesante. Si necesitaramos localizar la Nag podemos mirar en las intermodular calls las típicas subrutinas, en este caso se ve claramente  a MessageBoxA, bastaría con poner un breakpoint para localizar quien llama.

Aquí vemos la implicación de MessageBoxA.

004010A7     |> \6A 40                    PUSH 40                                         ; /Style = MB_OK|MB_ICONASTERISK|MB_APPLMODAL
004010A9     |.  68 61304000              PUSH Nag1.00403061                              ; |Title = "[NAG] Please register this software!"
004010AE     |.  68 86304000              PUSH Nag1.00403086                              ; |Text = "[BULLSHIT] Please register this software for support and you'll receive the full version!"
004010B3     |.  FF75 08                  PUSH [ARG.1]                                    ; |hOwner = 7FFDF000
004010B6     |.  E8 49010000              CALL <JMP.&user32.MessageBoxA>                  ; \MessageBoxA
........
00401137     |.  6A 40                    PUSH 40                                         ; /Style = MB_OK|MB_ICONASTERISK|MB_APPLMODAL
00401139     |.  68 6E324000              PUSH Nag1.0040326E                              ; |Title = "Thank you!"
0040113E     |.  68 79324000              PUSH Nag1.00403279                              ; |Text = "Thank you for registering this software!"
00401143     |.  FF75 08                  PUSH [ARG.1]                                    ; |hOwner = 7FFDF000
00401146     |.  E8 B9000000              CALL <JMP.&user32.MessageBoxA>                  ; \MessageBoxA
........
00401155     |.  6A 40                    PUSH 40                                         ; /Style = MB_OK|MB_ICONASTERISK|MB_APPLMODAL
00401157     |.  68 E0304000              PUSH Nag1.004030E0                              ; |Title = "About"
0040115C     |.  68 E6304000              PUSH Nag1.004030E6                              ; |Text = "Remove the NAG by TDC\r\n\n..: Coded by\t: TDC\t\t\t:..\t\r\n..: Also known as\t: The Dutch Cracker\t:..\t\r\n..: Protection\t: Custom\t\t\t:..\t\r\n..: Contact info\t: tdc123@gmail.com\t:..\t\r\n..: Release date\t: 09-08-2005\t\t:..\t"...
00401161     |.  FF75 08                  PUSH [ARG.1]                                    ; |hOwner = 7FFDF000
00401164     |.  E8 9B000000              CALL <JMP.&user32.MessageBoxA>                  ; \MessageBoxA

 Un poco encima vemos la función SetDlgItemTextA, que nos mostrará el mensaje de que hemos parcheado correctamente.

dlgitem

00401106     |> \68 21304000              PUSH Nag1.00403021                              ; /Text = "Dirty crack! Nag removed not registered!"
0040110B     |.  6A 73                    PUSH 73                                         ; |ControlID = 73 (115.)
0040110D     |.  FF75 08                  PUSH [ARG.1]                                    ; |hWnd = 7FFDF000
00401110     |.  E8 FB000000              CALL <JMP.&user32.SetDlgItemTextA>              ; \SetDlgItemTextA
00401115     |.  EB 36                    JMP SHORT Nag1.0040114D
00401117     |>  68 10304000              PUSH Nag1.00403010                              ; /Text = "Nag not removed!"
0040111C     |.  6A 73                    PUSH 73                                         ; |ControlID = 73 (115.)
0040111E     |.  FF75 08                  PUSH [ARG.1]                                    ; |hWnd = 7FFDF000
00401121     |.  E8 EA000000              CALL <JMP.&user32.SetDlgItemTextA>              ; \SetDlgItemTextA
00401126     |.  EB 25                    JMP SHORT Nag1.0040114D
00401128     |>  68 4A304000              PUSH Nag1.0040304A                              ; /Text = "Clean crack! Good Job!"
0040112D     |.  6A 73                    PUSH 73                                         ; |ControlID = 73 (115.)
0040112F     |.  FF75 08                  PUSH [ARG.1]                                    ; |hWnd = 7FFDF000
00401132     |.  E8 D9000000              CALL <JMP.&user32.SetDlgItemTextA>              ; \SetDlgItemTextA

Encima de SetDlgItemTextA vemos el código que analiza si la Nag tiene que aparecer.

004010E6     |.  E8 C4000000              CALL Nag1.004011AF                              ;  ; Llamada interesante a analizar
004010EB     |.  803D B0324000 03         CMP BYTE PTR DS:[4032B0],3
004010F2     |.  74 12                    JE SHORT Nag1.00401106                          ;  ; Si de la llamada volvemos con un 3 -> Parcheo chapuza
004010F4     |.  803D B0324000 02         CMP BYTE PTR DS:[4032B0],2
004010FB     |.  74 1A                    JE SHORT Nag1.00401117                          ;  ; Si de la llamada volvemos con un 2 -> Sin parchear
004010FD     |.  803D B0324000 01         CMP BYTE PTR DS:[4032B0],1
00401104     |.  74 22                    JE SHORT Nag1.00401128                          ;  ; Si de la llamada volvemos con un 1 -> Buen trabajo Joe!
........
004011AF     /$  68 A2324000              PUSH Nag1.004032A2                              ; /String2 = "Value1"
004011B4     |.  68 A9324000              PUSH Nag1.004032A9                              ; |String1 = "Value2"
004011B9     |.  E8 64000000              CALL <JMP.&kernel32.lstrcmpA>                   ; \lstrcmpA
004011BE     |.  50                       PUSH EAX                                        ;  kernel32.BaseThreadInitThunk
004011BF     |.  85C0                     TEST EAX,EAX                                    ;  kernel32.BaseThreadInitThunk
004011C1     |.  75 10                    JNZ SHORT Nag1.004011D3
004011C3     |.  33C0                     XOR EAX,EAX                                     ;  kernel32.BaseThreadInitThunk
004011C5     |.  58                       POP EAX                                         ;  kernel32.75CDEE1C
004011C6     |.  85C0                     TEST EAX,EAX                                    ;  kernel32.BaseThreadInitThunk
004011C8     |.  74 15                    JE SHORT Nag1.004011DF
004011CA     |.  C605 B0324000 03         MOV BYTE PTR DS:[4032B0],3
004011D1     |.  EB 17                    JMP SHORT Nag1.004011EA
004011D3     |>  58                       POP EAX                                         ;  kernel32.75CDEE1C
004011D4     |.  33C0                     XOR EAX,EAX                                     ;  kernel32.BaseThreadInitThunk
004011D6     |.  C605 B0324000 02         MOV BYTE PTR DS:[4032B0],2
004011DD     |.  EB 0B                    JMP SHORT Nag1.004011EA
004011DF     |>  33C0                     XOR EAX,EAX                                     ;  kernel32.BaseThreadInitThunk
004011E1     |.  C605 B0324000 01         MOV BYTE PTR DS:[4032B0],1
004011E8     |.  EB 00                    JMP SHORT Nag1.004011EA
004011EA     \>  C3                       RETN

 Vemos dentro del Call 4011AF que Compara si Value1 = Value2 y dependiendo de esa comparación guarda en memoria (4032B0), los valores 1, 2 ó 3.

Basta con modificar en un editor hexadecimal la parabra «Value2» por «Value1» y ya tenemos el problema resuelto.

09-09-2014 11-04-03

09-09-2014 11-04-34

09-09-2014 11-05-26

Al pulsar Re-Check

09-09-2014 11-06-01

Notas finales

Se podía haber parcheado un montón de código para obtener el mismo resultado pero fijándonos en el código lo hemos conseguido parcheando un solo byte. Recuerda, cuando halla que parchear, cuantos menos bytes mejor.

 Links


While Crackmes.de returns, I leave a couple of files for practice. Mientras vuelve Crackmes.de, os dejo un par de archivos para practicar.
Warning: This challenge is still active and therefore should not be resolved using this information. Aviso: Este reto sigue en
En el BTM anterior nos remontábamos al año 2006 para ver un pequeño gazapo ocurrido en la serie Dexter. En
Introducción Javascript 1 (Serial a la vista) Javascript 2 (La función charAt()) Javascript 3 (Input) Javascript 4 (Fuerza bruta manual) Javascript

ThisIsLegal.com – Realistic Challenge 2

Warning: This challenge is still active and therefore should not be resolved using this information.
Aviso: Este reto sigue en activo y por lo tanto no se debería resolver utilizando esta información.

Introducción

Realistic Challenge 2: You have heard about people being targeted by a new religion called Egitology. Another hacker infiltrated the group and discovered that the list of people they target is stored on the site but he doesn’t know where.

Break into the site, find the file and remove it. Also leave no evidence that you was ever there so they wont realise until its too late!

El enunciado del reto nos dice que tenemos que localizar la lista de objetivos y eliminarla sin dejar evidencias.

Analizando la seguridad de la víctima

Echamos un vistazo y vemos que tienen un Login para usuarios registrados, este será nuestro primer testeo.
Lo primero que se no viene a la cabeza con un formulario de este tipo es Inyección SQL, probamos varios métodos y tenemos suerte.
User: admin
Pass: ‘ or 1=1–‘;
 
 Vemos que hemos entrado como admin y enseguida nos llama la atención «Back up Database«. Pulsamos a ver que pasa.
 Obtenemos el hash de las claves de los usuarios Admin y SuperAdmin. Por suerte son hashes MD5. Obtenemos la clave de SuperAdmin y nos loguemos.

Solo nos queda borrar la lista de objetivos y nuestras huellas. Para ello borramos los siguientes archivos y reto superado.

Lista de objetivos: root/misc/targets
Logs: root/images/logs

Links

Stego – Bytes en bruto

Toda esta aventura comienza con un archivo llamado pretty_raw, sin extensión. Porque sí. Porque las extensiones son una invención heredada de CP/M, precursor de MS-DOS, que Windows terminó de popularizar. Porque son innecesarias. Y porque echo de menos cuando los archivos se reconocían por sus permisos… y no por cómo se llamaban.

Como iba diciendo, todo esto comienza mediante el análisis de pretty_raw. Mirando debajo de la falda con un editor hexadecimal encontramos unos cuantos bytes aleatorios hasta dar con una cabecera PNG.

Si atendemos a la captura, justo antes de la cabecera PNG tenemos 116.254 bytes (0x1C61E). Tomad nota que este número será relevante más adelante.

Extraemos el PNG, lo visualizamos y lo pasamos por todas las herramientas habidas y por haber. Nada funciona. Volvemos a visualizarlo con atención y vemos que hace referencia a un archivo llamado flag.png con unas dimensiones que no coinciden con la extraída.

Toca centrarse y pensar en que camino tomar. Hemos gastado tiempo con el PNG extraído y quizá lo mejor sea centrarse en los bytes que inicialmente hemos descartado. En concreto se trata de un bloque de 116.254 bytes, pero espera, 1570×74=116.180 bytes. ¡Mierda!, no coincide exactamente con los bytes extraídos. Bueno, da igual. Si suponemos que el PNG que buscamos no tiene compresión y que cada pixel ocupa un byte (escala de grises y 8 bits), su tamaño depende únicamente de la geometría y de cómo se almacenan las filas en memoria. Vamos a procesarlo con Python para salir de dudas.

import numpy as np
from PIL import Image

INPUT_FILE  = "pretty_raw"
OUTPUT_FILE = "pretty_raw_flag.png"

WIDTH  = 1570 # ¿estás seguro?
HEIGHT = 74
DEPTH  = 8  # bits

# Leer archivo como RAW
with open(INPUT_FILE, "rb") as f:
    raw = f.read()

expected_size = WIDTH * HEIGHT
if len(raw) < expected_size:
    raise ValueError("El archivo no tiene suficientes datos")

# Convertir a array numpy (grayscale 8 bits)
img = np.frombuffer(raw[:expected_size], dtype=np.uint8)
img = img.reshape((HEIGHT, WIDTH))

# Crear imagen
image = Image.fromarray(img, mode="L")
image.save(OUTPUT_FILE)

print(f"Imagen generada correctamente: {OUTPUT_FILE}")

El script nos devuelve un PNG válido pero con las letras torcidas. Tras darle vueltas me di cuenta de que si en el script usamos como WIDTH=1571 en lugar de 1570, la imagen resultante es correcta y tiene todo el sentido del mundo ya que 1571×74=116.254, que son exactamente los bytes que se encuentran antes del png señuelo.

Aunque el ancho visible de la imagen es de 1570 píxeles, cada fila ocupa realmente 1571 bytes. Ese byte adicional actúa como relleno (padding) y forma parte del stride o bytes por fila. Ignorar este detalle lleva a un desplazamiento erróneo acumulativo y por eso se ve la imagen torcida. En este caso concreto da igual ya que el texto se aprecia, pero si el reto hubiera sido más exigente no se vería nada.

Solución al PGC Official KeygenMe

Introducción

Siguiendo con los crackmes que contienen RSA, esta vez tenemos un Keygenme del grupo PGC (Pirates Gone Crazy) que incluso servía para ser admitido en el grupo si mandabas la solución. Como veremos usa RSA32 + MD5 y en la parte de RSA ni siquiera usa el descifrado por lo que es de los sencillitos.

Resumen RSA

Parámetros

p = Primer número primo
q = Segundo número primo
e = Exponente público que cumpla MCD(e,(p-1)*(q-1))==1
n = Módulo público siendo n=p*q
d = Exponente privado que cumpla d=e^(-1) mod ((p-1)*(q-1))

De este modo y n son la parte pública de la clave y d y n la parte privada. Los número primos q se utilizan solo para generar los parámetros y de ahí en adelante se pueden desechar.

Funciones de Cifrado/Descifrado

cifrado = descifrado ^ e mod n
descifrado = cifrado ^ d mod n

Debug

En las referencias de texto se ven a simple vista el exponente público e (10001) y el módulo n (8e701a4c793eb8b739166bb23b49e421)

Text strings referenced in RSA32+MD:.text
Address    Disassembly                                                     Text string
00401848   PUSH    RSA32+MD.00404104                                       ASCII "%.8x%.8x%.8x%.8x"
00401A72   PUSH    RSA32+MD.0040429C                                       ASCII "[PGCTRiAL/2oo2]"
00401AEE   PUSH    RSA32+MD.00404275                                       ASCII "10001"
00401AFE   PUSH    RSA32+MD.0040427B                                       ASCII "8e701a4c793eb8b739166bb23b49e421"
00401B43   PUSH    RSA32+MD.00404404                                       ASCII "Name Must Be >= 1 Character."
00401B57   PUSH    RSA32+MD.00404421                                       ASCII "Key Must Be >= 1 Character."
00401B6D   PUSH    RSA32+MD.0040443D                                       ASCII "Congratulations!"
00401B72   PUSH    RSA32+MD.0040444E                                       ASCII "                 You've done it!
Please send your keygen along with
source code to pgc@dangerous-minds.com
if you would like to be considered as
         a new member of PGC."
00401BE7   PUSH    0                                                       (Initial CPU selection)
00401C47   MOV     [DWORD SS:EBP-24],RSA32+MD.00404119                     ASCII "PGCWinClass"
00401C7C   MOV     [DWORD SS:EBP-24],RSA32+MD.0040424E                     ASCII "STATIC"
00401CDB   PUSH    RSA32+MD.00404115                                       ASCII "PGC"
00401CE0   PUSH    RSA32+MD.00404119                                       ASCII "PGCWinClass"
00401D13   PUSH    RSA32+MD.00404125                                       ASCII "EDIT"
00401D46   PUSH    RSA32+MD.00404125                                       ASCII "EDIT"
00401DFB   PUSH    RSA32+MD.00404115                                       ASCII "PGC"
00401E00   PUSH    RSA32+MD.0040424E                                       ASCII "STATIC"

Rutina de comprobación

00401A0E  /$  53            PUSH    EBX
00401A0F  |.  57            PUSH    EDI
00401A10  |.  56            PUSH    ESI
00401A11  |.  6A 11         PUSH    11                             ; /Count = 11 (17.)
00401A13  |.  68 AC424000   PUSH    RSA32+MD.004042AC              ; |Buffer = RSA32+MD.004042AC
00401A18  |.  FF35 94454000 PUSH    [DWORD DS:404594]              ; |hWnd = NULL
00401A1E  |.  E8 49080000   CALL    <JMP.&USER32.GetWindowTextA>   ; \GetWindowTextA
00401A23  |.  83F8 01       CMP     EAX,1
00401A26  |.  0F8C 17010000 JL      RSA32+MD.00401B43
00401A2C  |.  A3 6D424000   MOV     [DWORD DS:40426D],EAX
00401A31  |.  6A 22         PUSH    22                             ; /Count = 22 (34.)
00401A33  |.  68 BD424000   PUSH    RSA32+MD.004042BD              ; |Buffer = RSA32+MD.004042BD
00401A38  |.  FF35 98454000 PUSH    [DWORD DS:404598]              ; |hWnd = NULL
00401A3E  |.  E8 29080000   CALL    <JMP.&USER32.GetWindowTextA>   ; \GetWindowTextA
00401A43  |.  83F8 01       CMP     EAX,1
00401A46  |.  0F8C 0B010000 JL      RSA32+MD.00401B57
00401A4C  |.  A3 71424000   MOV     [DWORD DS:404271],EAX
00401A51  |.  6A 00         PUSH    0
00401A53  |.  E8 C8080000   CALL    RSA32+MD.00402320
00401A58  |.  A3 69424000   MOV     [DWORD DS:404269],EAX
00401A5D  |.  A1 71424000   MOV     EAX,[DWORD DS:404271]
00401A62  |.  FF35 69424000 PUSH    [DWORD DS:404269]              ; /Arg2 = 00000000
00401A68  |.  68 BD424000   PUSH    RSA32+MD.004042BD              ; |Arg1 = 004042BD
00401A6D  |.  E8 510A0000   CALL    RSA32+MD.004024C3              ; \RSA32+MD.004024C3
00401A72  |.  68 9C424000   PUSH    RSA32+MD.0040429C              ; /StringToAdd = "[PGCTRiAL/2oo2]"
00401A77  |.  68 AC424000   PUSH    RSA32+MD.004042AC              ; |ConcatString = ""
00401A7C  |.  E8 51080000   CALL    <JMP.&KERNEL32.lstrcatA>       ; \lstrcatA
00401A81  |.  68 AC424000   PUSH    RSA32+MD.004042AC              ; /String = ""
00401A86  |.  E8 4D080000   CALL    <JMP.&KERNEL32.lstrlenA>       ; \lstrlenA
00401A8B  |.  68 DF424000   PUSH    RSA32+MD.004042DF              ; /Arg4 = 004042DF
00401A90  |.  68 10454000   PUSH    RSA32+MD.00404510              ; |Arg3 = 00404510
00401A95  |.  50            PUSH    EAX                            ; |Arg2
00401A96  |.  68 AC424000   PUSH    RSA32+MD.004042AC              ; |Arg1 = 004042AC
00401A9B  |.  E8 60F5FFFF   CALL    RSA32+MD.00401000              ; \RSA32+MD.00401000
00401AA0  |.  6A 00         PUSH    0
00401AA2  |.  E8 79080000   CALL    RSA32+MD.00402320
00401AA7  |.  A3 5D424000   MOV     [DWORD DS:40425D],EAX
00401AAC  |.  6A 00         PUSH    0
00401AAE  |.  E8 6D080000   CALL    RSA32+MD.00402320
00401AB3  |.  A3 59424000   MOV     [DWORD DS:404259],EAX
00401AB8  |.  6A 00         PUSH    0
00401ABA  |.  E8 61080000   CALL    RSA32+MD.00402320
00401ABF  |.  A3 61424000   MOV     [DWORD DS:404261],EAX
00401AC4  |.  6A 00         PUSH    0
00401AC6  |.  E8 55080000   CALL    RSA32+MD.00402320
00401ACB  |.  A3 65424000   MOV     [DWORD DS:404265],EAX
00401AD0  |.  B8 02000000   MOV     EAX,2
00401AD5  |.  C1E0 04       SHL     EAX,4
00401AD8  |.  FF35 5D424000 PUSH    [DWORD DS:40425D]              ; /Arg2 = 00000000
00401ADE  |.  68 DF424000   PUSH    RSA32+MD.004042DF              ; |Arg1 = 004042DF
00401AE3  |.  E8 DB090000   CALL    RSA32+MD.004024C3              ; \RSA32+MD.004024C3
00401AE8  |.  FF35 65424000 PUSH    [DWORD DS:404265]              ; /Arg2 = 00000000
00401AEE  |.  68 75424000   PUSH    RSA32+MD.00404275              ; |Arg1 = 00404275 ASCII "10001"
00401AF3  |.  E8 CB090000   CALL    RSA32+MD.004024C3              ; \RSA32+MD.004024C3
00401AF8  |.  FF35 61424000 PUSH    [DWORD DS:404261]              ; /Arg2 = 00000000
00401AFE  |.  68 7B424000   PUSH    RSA32+MD.0040427B              ; |Arg1 = 0040427B ASCII "8e701a4c793eb8b739166bb23b49e421"
00401B03  |.  E8 BB090000   CALL    RSA32+MD.004024C3              ; \RSA32+MD.004024C3
00401B08  |.  FF35 59424000 PUSH    [DWORD DS:404259]
00401B0E  |.  FF35 61424000 PUSH    [DWORD DS:404261]
00401B14  |.  FF35 65424000 PUSH    [DWORD DS:404265]
00401B1A  |.  FF35 5D424000 PUSH    [DWORD DS:40425D]
00401B20  |.  E8 87120000   CALL    RSA32+MD.00402DAC
00401B25  |.  FF35 69424000 PUSH    [DWORD DS:404269]
00401B2B  |.  FF35 59424000 PUSH    [DWORD DS:404259]
00401B31  |.  E8 61080000   CALL    RSA32+MD.00402397
00401B36  |.  85C0          TEST    EAX,EAX
00401B38  |.  74 31         JE      SHORT RSA32+MD.00401B6B
00401B3A  |.  E8 85000000   CALL    RSA32+MD.00401BC4
00401B3F  |.  5E            POP     ESI
00401B40  |.  5F            POP     EDI
00401B41  |.  5B            POP     EBX
00401B42  |.  C3            RET
00401B43  |>  68 04444000   PUSH    RSA32+MD.00404404              ; /Text = "Name Must Be >= 1 Character."
00401B48  |.  FF35 98454000 PUSH    [DWORD DS:404598]              ; |hWnd = NULL
00401B4E  |.  E8 5B070000   CALL    <JMP.&USER32.SetWindowTextA>   ; \SetWindowTextA
00401B53  |.  5E            POP     ESI
00401B54  |.  5F            POP     EDI
00401B55  |.  5B            POP     EBX
00401B56  |.  C3            RET
00401B57  |>  68 21444000   PUSH    RSA32+MD.00404421              ; /Text = "Key Must Be >= 1 Character."
00401B5C  |.  FF35 98454000 PUSH    [DWORD DS:404598]              ; |hWnd = NULL
00401B62  |.  E8 47070000   CALL    <JMP.&USER32.SetWindowTextA>   ; \SetWindowTextA
00401B67  |.  5E            POP     ESI
00401B68  |.  5F            POP     EDI
00401B69  |.  5B            POP     EBX
00401B6A  |.  C3            RET
00401B6B  |>  6A 00         PUSH    0                              ; /Style = MB_OK|MB_APPLMODAL
00401B6D  |.  68 3D444000   PUSH    RSA32+MD.0040443D              ; |Title = "Congratulations!"
00401B72  |.  68 4E444000   PUSH    RSA32+MD.0040444E              ; |Text = "                 You've done it!
Please send your keygen along with
source code to pgc@dangerous-minds.com
if you would like to be considered as
         a new member of PGC."
00401B77  |.  FF35 8C454000 PUSH    [DWORD DS:40458C]              ; |hOwner = NULL
00401B7D  |.  E8 02070000   CALL    <JMP.&USER32.MessageBoxA>      ; \MessageBoxA
00401B82  |.  EB 00         JMP     SHORT RSA32+MD.00401B84
00401B84  |>  FF35 5D424000 PUSH    [DWORD DS:40425D]
00401B8A  |.  E8 BE070000   CALL    RSA32+MD.0040234D
00401B8F  |.  FF35 59424000 PUSH    [DWORD DS:404259]
00401B95  |.  E8 B3070000   CALL    RSA32+MD.0040234D
00401B9A  |.  FF35 61424000 PUSH    [DWORD DS:404261]
00401BA0  |.  E8 A8070000   CALL    RSA32+MD.0040234D
00401BA5  |.  FF35 65424000 PUSH    [DWORD DS:404265]
00401BAB  |.  E8 9D070000   CALL    RSA32+MD.0040234D
00401BB0  |.  FF35 69424000 PUSH    [DWORD DS:404269]
00401BB6  |.  E8 92070000   CALL    RSA32+MD.0040234D
00401BBB  |.  E8 04000000   CALL    RSA32+MD.00401BC4
00401BC0  |.  5E            POP     ESI
00401BC1  |.  5F            POP     EDI
00401BC2  |.  5B            POP     EBX
00401BC3  \.  C3            RET

Como vemos comprueba que tanto el nombre como el número de serie tengan al menos un dígito y a continuación comienza el chequeo del serial. El chequeo es muy sencillo ya que ni siquiera tenemos que buscar los números primos p y q y a continuación n, simplemente podemos obtener el número de serie con la parte pública de la clave (par de número e y n). Lo resumimos a continuación:

  1. Concatena nuestro nombre con la cadena «[PGCTRiAL/2oo2]»
  2. Crea el hash MD5 de la cadena concatenada.
  3. Cifra el hash usando el par de números e y n obtenidos en las referencias de texto.
1. deurus[PGCTRiAL/2oo2]
2. md5(deurus[PGCTRiAL/2oo2]) = dc8a39282da8539d11b8a6aec000c45a
3. dc8a39282da8539d11b8a6aec000c45a^10001 mod 8e701a4c793eb8b739166bb23b49e421 = 1FF83ECC5A65334DA2BC93C675A9BA15

Nombre: deurus
Serial: 1FF83ECC5A65334DA2BC93C675A9BA15
X^Y MOD Z para deurus

Keygen

//
// md5(deurus[PGCTRiAL/2oo2]) = dc8a39282da8539d11b8a6aec000c45a
//
var c = BigInt("0xdc8a39282da8539d11b8a6aec000c45a");
var e = BigInt("0x10001");
var n = BigInt("0x8e701a4c793eb8b739166bb23b49e421");
//
var serial = BigInt(0);
serial = powmod(c, e, n);
document.write(serial.toString(16));
//
//POWMOD
//
function powmod(base, exp, modulus) {
  var accum = BigInt("1");
  var i = BigInt("0");
  var basepow2 = BigInt(base);
  while ((BigInt(exp) >> BigInt(i) > BigInt(0))) {
    if (((BigInt(exp) >> BigInt(i)) & BigInt(1)) == BigInt(1)) {
      accum = (BigInt(accum) * BigInt(basepow2)) % BigInt(modulus);
    }
    basepow2 = (BigInt(basepow2) * BigInt(basepow2)) % BigInt(modulus);
    i++;
  }
  return BigInt(accum);
}

Enlaces