// FLARE-On 13 #4 ToxicMiner - brute-force the 32-bit volume serial number.
// Mirrors FUN_140134ca0 + FUN_140134d70 of ToxicMiner.exe, where the
// "sha256_transform" is the modified 32-round FUN_140135240 (tools/toxic_transform.h,
// generated from the Ghidra decompilation by tools/ghidra2c.py):
//   h1  = toxic(IV, {serial, 0x0b501e7e, 0x80000000, 0.., 0x40})
//   h2  = toxic(IV, {h1[0..7], 0x80000000, 0.., 0x100})
//   key = first 16 bytes of h2 as stored in memory (little-endian words)
//   pt  = RC4(key, ct[0x33])  -> accept if it contains "@flare-on.com"
// Build: gcc -O3 -march=native -fopenmp -fwrapv brute_serial.c -o brute_serial
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#include "tools/toxic_transform.h"
static const uint32_t IV[8] = {0x6a09e667,0xbb67ae85,0x3c6ef372,0xa54ff53a,
                               0x510e527f,0x9b05688c,0x1f83d9ab,0x5be0cd19};
#include "tools/tox_tab.h"   /* uint8_t TOX_TAB[256] = bytes at 0x1401bd270 */
static const uint8_t CT[0x33] = {
    0xf4,0xf9,0x65,0x9f,0x31,0xf3,0xda,0x5c,0xf1,0x37,0xa1,0xb8,0xcf,0x90,0xa1,0x58,0x1e,
    0xeb,0xe4,0x99,0xa7,0xc1,0x1b,0x15,0x02,0xaf,0x74,0x63,0xd3,0xb2,0x19,0x28,0x66,0xde,
    0x3d,0xe9,0x71,0x63,0xe7,0x75,0xdf,0xe4,0xab,0x61,0x6f,0xb9,0x0c,0x27,0x89,0xfc,0x87};

static void compress(uint32_t out[8], const uint32_t blk[16]) {
    memcpy(out, IV, 32);
    toxic_transform(out, blk);   /* FUN_140135240(state=IV, block, swap=0) */
}

static int try_serial(uint32_t serial, uint8_t pt[0x34]) {
    uint32_t blk[16] = {0}, h1[8], h2[8];
    blk[0] = serial; blk[1] = 0x0b501e7e; blk[2] = 0x80000000; blk[15] = 0x40;
    compress(h1, blk);
    memset(blk, 0, sizeof blk);
    memcpy(blk, h1, 32); blk[8] = 0x80000000; blk[15] = 0x100;
    compress(h2, blk);
    uint8_t key[16], S[256];
    memcpy(key, h2, 16);                       // little-endian host == x64 target
    for (int i = 0; i < 256; i++) S[i] = (uint8_t)i;
    for (int i = 0, j = 0; i < 256; i++) {
        j = (j + S[i] + key[i & 15]) & 0xff;
        uint8_t t = S[i]; S[i] = S[j]; S[j] = t;
    }
    for (int k = 0, i = 0, j = 0; k < 0x33; k++) {
        i = (i + 1) & 0xff; j = (j + S[i]) & 0xff;
        uint8_t t = S[i]; S[i] = S[j]; S[j] = t;
        pt[k] = CT[k] ^ S[(S[i] + S[j]) & 0xff];
    }
    pt[0x33] = 0;
    return memmem(pt, 0x33, "@flare-on.com", 13) != NULL;
}

int main(int argc, char **argv) {
    if (argc > 1) {                     /* check a single serial: ./brute_serial <hex> */
        uint8_t pt[0x34];
        uint32_t s = (uint32_t)strtoul(argv[1], NULL, 16);
        int ok = try_serial(s, pt);
        printf("serial=%08x ok=%d pt=", s, ok);
        for (int k = 0; k < 0x33; k++) printf("%02x", pt[k]);
        printf("\n");
        return ok ? 0 : 1;
    }
    volatile int found = 0;
    #pragma omp parallel for schedule(dynamic, 1 << 16)
    for (int64_t s = 0; s <= 0xffffffffLL; s++) {
        if (found) continue;
        uint8_t pt[0x34];
        if (try_serial((uint32_t)s, pt)) {
            #pragma omp critical
            { printf("serial=%08x flag=%s\n", (uint32_t)s, pt); fflush(stdout); found = 1; }
        }
    }
    return found ? 0 : 1;
}
