// 07 intermediário CRC-8 · XOR · dois campos

Keygen com
Checksum

A licença tem dois campos que se verificam mutuamente. Entender um não basta — você precisa dos dois.

Sistema de licença corporativa. Formato PRODCODE:CHECKSUM — 8 hex chars, dois pontos, 2 hex chars.

O PRODCODE pode ser qualquer valor de 32 bits. O CHECKSUM deve ser exatamente o CRC-8 do PRODCODE, XORado com uma constante mágica escondida no binário.
Você vai reconstruir o CRC-8 e encontrar a constante.

~/cracklab/07-keygen-checksum
$ ./challenge
Licença: DEADBEEF:00
✗ Checksum inválido. Esperado: 14, recebido: 00
$ ./challenge
Licença: DEADBEEF:14
✓ LICENÇA VÁLIDA — Módulo corporativo desbloqueado.
// conceitos necessários
CRC-8
Cyclic Redundancy Check de 8 bits. Processa cada byte bit a bit com o polinômio 0x07. O resultado é um checksum de 1 byte — frequente em protocolos de comunicação.
XOR COM CONSTANTE
O programa faz crc8(prod) ^ MAGIC onde MAGIC = 0xDE. O XOR com uma constante é um padrão clássico pra ofuscar checksums — mas a constante está hardcoded no binário.
BIG-ENDIAN NO INPUT
O PRODCODE é lido como string hex e convertido por strtoul. DEADBEEF vira o inteiro 0xDEADBEEF — depois o CRC processa os bytes do mais significativo ao menos.
STRINGS + GHIDRA
A constante 0xDE aparece como imediato num xor ou mov. objdump | grep 0xde ou a view de constantes do Ghidra acha em segundos.

Olha o disassembly de expected_checksum.
Depois do call crc8, tem uma instrução com um operando de 1 byte.
Esse byte é o MAGIC. Com ele em mãos, o keygen tem duas linhas.

// solução passo a passo
01
Encontrar a constante MAGIC
bash
objdump -d ./challenge | grep -A 10 "<expected_checksum>:"
Você vai ver:
asm
401262 <expected_checksum>:
  401276:  e8 7b ff ff ff   call  4011f6 <crc8>
  40127b:  34 de            xor   $0xde,%al         ← MAGIC = 0xDE

34 de é o opcode de xor al, 0xde.
O byte imediato 0xDE é a constante MAGIC. Com ela e com a lógica do CRC-8, você tem tudo.

02
Reconstruir o CRC-8 em Python
O loop dentro de crc8 processa 4 bytes do inteiro, do mais significativo ao menos:
asm — loop interno de crc8
; para cada byte (i de 3 a 0):
;   para cada bit (7 a 0):
;     if (crc ^ byte) & 0x80: crc = (crc << 1) ^ 0x07
;     else:                    crc = (crc << 1)
;     byte <<= 1
python3 — keygen.py
#!/usr/bin/env python3
# keygen.py — CRACKLAB #07
import sys

MAGIC = 0xDE

def crc8(value: int) -> int:
    crc = 0
    for i in range(3, -1, -1):
        byte = (value >> (i * 8)) & 0xFF
        for _ in range(8):
            if (crc ^ byte) & 0x80:
                crc = ((crc << 1) ^ 0x07) & 0xFF
            else:
                crc = (crc << 1) & 0xFF
            byte = (byte << 1) & 0xFF
    return crc

def keygen(prod: int) -> str:
    checksum = crc8(prod) ^ MAGIC
    return f"{prod:08X}:{checksum:02X}"

# testa com alguns valores
for prod in [0xDEADBEEF, 0x12345678, 0xCAFEBABE]:
    print(keygen(prod))
~/cracklab/07-keygen-checksum
$ python3 keygen.py
DEADBEEF:14
12345678:C2
CAFEBABE:56
03
Verificar
bash
printf "CAFEBABE:56\n" | ./challenge
# ✓ LICENÇA VÁLIDA
// variações pra explorar
variaçãoo que faz
patch do checksumTrocar o jne final em 0x40141e por NOP. Aceita qualquer entrada. Mais rápido, mas não entende nada.
keygen corretoReproduz CRC-8 + XOR. Funciona pra qualquer PRODCODE de 32 bits — ~4 bilhões de licenças válidas.
brutar MAGICCom um par (prod, check) conhecido — que o programa revela no output de erro — você pode deduzir MAGIC em 256 tentativas. O programa entrega ele de graça.

O programa exibe o checksum esperado quando você erra: "Esperado: 14, recebido: 00". Isso torna o keygen completamente desnecessário — uma única tentativa errada já te dá a resposta. Que lição de design de segurança isso demonstra?