Otro Keygen para el CrackMe Pythagoras de Spider

Hace 32 años resolví este crackme por fuerza bruta. Tampoco le di mayor importancia, me pareció bien en aquel momento y ni siquiera me plantee darle un enfoque optimizado. También puede ser que me esté haciendo mayor y al revisitar mis soluciones anteriores les quiera dar una vuelta, no sabría deciros.

Si echamos un vistazo a la entrada original, el nombre introducido debe tener al menos seis caracteres y el número de serie debe cumplir con la ecuación de Pitágoras a² + b² + = 0 que tiene un formato como este aabbbbcccc. Además, el crackme impone tres restricciones sobre el serial generado.

  1. El valor a debe estar relacionado con el nombre introducido por el usuario, concretamente dentro del rango SUMNOMBRE ± 4, donde SUMNOMBRE es la suma de los caracteres del nombre reducida a 8 bits.
  2. Los valores a, b y c deben cumplir la relación pitagórica clásica a² + b² + = 0.
  3. La diferencia entre la hipotenusa y uno de los catetos debe ser constante, concretamente c = b + 8.

En conjunto, esto obliga a encontrar una terna pitagórica, es decir, tres enteros positivos específicos y ligados directamente al nombre del usuario.

En resumen, si sustituimos la ecuación c = b + 8 en la ecuación pitagórica clásica y desarrollamos llegamos a esto:

\begin{aligned}
&b = \frac{a^2 - 64}{16}
\end{aligned}

Teniendo posibilidad de calcular directamente b y c todo se reduce a comprobar los candidatos de a válidos para nuestro SUMNOMBRE. Aquí todo queda simplificado a comprobar 9 combinaciones posibles a diferencia de la fuerza bruta en la que debemos comprobar 65536. A decir verdad, con la capacidad de cálculo de hoy día 65536 iteraciones se hacen en milisegundos, pero reducirlo a 9 queda más elegante. Incluso todavía se puede reducir algo más, ya que como necesitamos números enteros, para que a2 cumpla debe ser múltiplo de 4.

Keygen en Javascript

function toHex(value, length) {
    return value.toString(16).toUpperCase().padStart(length, "0");
}

function sumNombre8(nombre) {
    let suma = 0;
    for (let i = 0; i < nombre.length; i++) {
        suma = (suma + nombre.charCodeAt(i)) & 0xFF;
    }
    return suma;
}

function generarSeriales(nombre) {
    const sum8 = sumNombre8(nombre);
    const seriales = [];

    for (let a = sum8 - 4; a <= sum8 + 4; a++) {
        if (a < 0 || a > 0xFF) continue;

        // Para que b sea entero, a debe ser múltiplo de 4
        if (a % 4 !== 0) continue;

        const a2 = a * a;
        const numerador = a2 - 64;

        if (numerador < 0) continue;
        if (numerador % 16 !== 0) continue;

        const b = numerador / 16;
        const c = b + 8;

        // Ajuste a formato aa bbbb cccc
        if (b < 0 || b > 0xFFFF) continue;
        if (c < 0 || c > 0xFFFF) continue;

        const serial =
            toHex(a, 2) +
            toHex(b, 4) +
            toHex(c, 4);

        seriales.push({
            nombre: nombre,
            sum8: toHex(sum8, 2),
            a: toHex(a, 2),
            b: toHex(b, 4),
            c: toHex(c, 4),
            serial: serial
        });
    }

    return seriales;
}

// Ejemplo
const nombre = "deurus2026";
const resultados = generarSeriales(nombre);

if (resultados.length === 0) {
    console.log("No se han encontrado seriales válidos.");
} else {
    console.log(`Nombre: ${nombre}`);
    console.log(`SUMNOMBRE (8 bits): ${resultados[0].sum8}`);
    console.log("");

    for (const r of resultados) {
        console.log(
            `a=${r.a}  b=${r.b}  c=${r.c}  -> Serial: ${r.serial}`
        );
    }
}

Resultado:

Nombre: deurus2026
SUMNOMBRE (8 bits): 62

a=60  b=023C  c=0244  -> Serial: 60023C0244
a=64  b=026D  c=0275  -> Serial: 64026D0275

-------------------------------------------

Nombre: deurus
SUMNOMBRE (8 bits): 98

a=94  b=0555  c=055D  -> Serial: 940555055D
a=98  b=05A0  c=05A8  -> Serial: 9805A005A8
a=9C  b=05ED  c=05F5  -> Serial: 9C05ED05F5

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

24 − 19 =