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² + c² = 0 que tiene un formato como este aabbbbcccc. Además, el crackme impone tres restricciones sobre el serial generado.
- 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.
- Los valores a, b y c deben cumplir la relación pitagórica clásica a² + b² + c² = 0.
- 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.
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
