Skip to content

Instantly share code, notes, and snippets.

@notwa
Last active February 15, 2024 21:13
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save notwa/d351084ace490f12c8f657d36c58d61d to your computer and use it in GitHub Desktop.
Save notwa/d351084ace490f12c8f657d36c58d61d to your computer and use it in GitHub Desktop.
Halo CE product key, reverse engineered
/*
most of this code was reverse engineered from PidGen.dll, with hashes:
MD5: 3e275cb6c964ad9e3bffa3f37e1eef1e
SHA-1: 325d4bc9b603075ba96c48bfaf31588cb57026c6
SHA-256: 62361c9b86f7899d736986829d7a8529573e030c6656f04f3244f3b792a6b291
how to debug:
* ensure Halo is not installed OR temporarily rename the following registry key:
HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Microsoft Games\Halo
* run the Halo CE installer off the CD
* enter something into the Product Key field
* add a breakpoint at LaunchGame+6150 (i'm using Cheat Engine for this)
* click Next in the installer
* step into the function being called
note that PidGen.dll is temporal in memory,
but once the aforementioned breakpoint is hit,
it will remain loaded until the key is either accepted or rejected.
note that when using other tools, you may need to enter addresses
as 0x0040xxxx instead of PidGen.dll+xxxx, or something similar.
also be aware that most functions take some arguments in registers
without any clear calling convention. no, it's not fastcall.
some example keys found online:
Product Key | 11 _____ 31 ______________ 62 10 | Validity
G9QK2-22KV6-TFXCR-V3Y9M-2CCBJ | 046 3A4D43C9 0EB4A87E 3AA1A1C8 147 | Valid
M4GHF-MMRWK-MW9BD-B76JK-6P8MJ | 046 1E8B123B 26C42C8D 03870589 242 | Valid
PGQTG-4X4J3-68M87-B28TR-J2CBJ | 046 2CDFA94F 3EF3EF60 FEA2CE11 25B | Valid
RGGD9-BYCC4-6J6FV-VPJJQ-HCXHW | 046 7E1A4E15 315B53CB 3B2EB7A6 2DE | Valid
V84H7-J62Y4-HTMHK-RKH6T-BHXHW | 046 23ADEC66 154C1407 74538B4D 395 | Valid
W4WMQ-BP7J4-YPV8D-9HYK3-KQTVW | 046 73A76D95 13FF4A4D 5342B878 3CE | Valid
WWCXQ-QKQ4V-QRWKQ-YDVGJ-FCRYJ | 046 4E6A8B10 0A5F690E 18803260 3BF | Valid
*/
#include <assert.h>
#include <stdint.h>
#include <stdio.h>
typedef unsigned int uint;
typedef uint64_t u64;
typedef uint32_t u32;
typedef uint16_t u16;
typedef uint8_t u8;
typedef int64_t s64;
typedef int32_t s32;
typedef int16_t s16;
typedef int8_t s8;
typedef struct {
// a 128-bit integer, split into 32-bit integers,
// ordered from least to most significant.
u32 parts[4];
} big128_t;
enum { BIG512_PARTS = 16 };
typedef struct {
// a 512-bit integer, split into 32-bit integers,
// ordered from least to most significant.
u32 parts[BIG512_PARTS];
} big512_t;
typedef struct {
big512_t lo;
big512_t hi;
} pair_t;
typedef struct {
u32 p0; // 11 bits
u32 p1; // 31 bits
u32 p2lo; // 62 bits (lower 32)
u32 p2hi; // 62 bits (upper 30)
u32 p3; // 10 bits
} keysegs_t;
typedef struct {
u8 buf[64];
u32 hash[5];
int pos;
u64 len; // measured in bits
} hashdata_t;
static inline u32 rol32(u32 a, u32 b) {
return b ? (a << (b & 31)) | (a >> (32 - (b & 31))) : a;
}
static inline u32 ror32(u32 a, u32 b) {
return b ? (a >> (b & 31)) | (a << (32 - (b & 31))) : a;
}
static inline u32 bswap32(u32 a) {
return ((a & 0x000000FF) << 24) | ((a & 0x0000FF00) << 8) |
((a & 0x00FF0000) >> 8) | ((a & 0xFF000000) >> 24);
}
static inline char toupper_(char c) {
return c + ((c >= 'a' && c <= 'z') ? ('A' - 'a') : 0);
}
static inline int checkbit(const u32 *a, int bit) {
return (a[bit / 32] >> (bit & 0x1F)) & 1;
}
static u64 prng_state = 1;
static u32 prng() {
prng_state = 3935559000370003845 * prng_state + 1;
return prng_state ^ (prng_state >> 32);
}
static void seed_prng();
static const pair_t magic_a = {
{{
0x2A1CC733, 0x055CC475, 0xB1DA5D5A, 0x267150E9,
0xAFA72628, 0x1A7E75BD, 0x89170561, 0x9472497B,
0x251C72A0, 0x4E61097B, 0x2C689E54, 0xA5D7EB73,
0xCDDB23EF, 0xBD36F0B9, 0x5BD9B393, 0x4356D735,
}}, {{
0x2840B2A2, 0x54400A49, 0xE968200F, 0xA6FB1DCA,
0x034A83A9, 0x2AECC46C, 0x9527F484, 0x1E543830,
0x3D3EC11F, 0x23555455, 0xB2853290, 0x9AABC349,
0xD23BFCC2, 0xA3054661, 0xC3ABF489, 0x8040E062,
}}
};
static const pair_t magic_b = {
{{
0x07CBD665, 0x562E67D5, 0x267C2F03, 0x824D5FC5,
0x1F8B8C82, 0x3BA8F17A, 0xD8070E94, 0x3CD42B35,
0x3FDAD5A8, 0xC5CDF77C, 0x510EE529, 0x4C43E59D,
0xD2E67BC0, 0xDD111427, 0x131CDAA2, 0x1C003CDB,
}}, {{
0x81EBBAA3, 0x1CA626C4, 0x69E30F80, 0x51A5E7E9,
0x238978A1, 0x58B6A363, 0x1172FE28, 0xB8B691DB,
0x49BCE948, 0x7FC7DDFD, 0xB28C3359, 0x0AC512A8,
0xC4E0F38C, 0x6A92E05C, 0x50CD9535, 0x7D1B8C92
}}
};
static const big512_t one = {{
1, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0
}};
static const big512_t zero = {{
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0
}};
static const big512_t divisor = {{
// this is a prime number.
0x9F68B969, 0x10B28CF3, 0x381D84F0, 0x6A466592,
0x08BD8A31, 0x61235B47, 0x579139F7, 0x6D842C22,
0x9FB7FBEF, 0x750F64DB, 0x357E2CC4, 0xAC8A818C,
0x8F34FD09, 0x588AFE31, 0xF563C002, 0xDF1B4644
}};
static const u32 hash_seed[5] = {
// here's what this sequence looks like in little endian bytes:
// 01 23 45 67 89 AB CD EF
// FE DC BA 98 76 54 32 10
// F0 E1 D2 C3
0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0
};
enum { CHARSET_LEN = 24 };
static const char charset[CHARSET_LEN] = "BCDFGHJKMPQRTVWXY2346789";
static int findchar(char c) {
c = toupper_(c);
for (int ind = 0; ind < CHARSET_LEN; ind++) {
if (charset[ind] == c) return ind;
}
return -1;
}
static void printbig(int len, const u32 data[len]) {
for (int i = 0; i < len; i++) {
printf(" %08X", data[i]);
if ((i + 1) % 4 == 0) printf("\n");
}
if (len % 4 != 0) printf("\n");
}
static big128_t big128_fma(big128_t big, u32 mul, u32 add) {
// multiply, then add.
for (int i = 3; i >= 0; i--) {
u64 temp = (u64)big.parts[i] * mul;
if (i == 0) temp += add;
big.parts[i] = (u32)temp;
if (i != 3) big.parts[i + 1] += (u32)(temp >> 32);
}
return big;
}
static big128_t big128_pop(big128_t big, u8 amount, u32 *popped) {
if (amount == 0) {
if (popped != NULL) *popped = 0;
} else if (amount == 32) {
if (popped != NULL) *popped = big.parts[0];
big.parts[0] = big.parts[1];
big.parts[1] = big.parts[2];
big.parts[2] = big.parts[3];
big.parts[3] = 0;
} else {
assert(amount < 32);
u32 mask = (1u << amount) - 1u;
if (popped != NULL) *popped = big.parts[0] & mask;
for (int i = 0; i < 4; i++) {
big.parts[i] >>= amount;
if (i != 3) big.parts[i] |= (big.parts[i + 1] & mask) << (32 - amount);
}
}
return big;
}
static big128_t big128_push(big128_t big, u32 amount, u32 value) {
assert(amount <= 32);
if (amount == 32) {
big.parts[3] = big.parts[2];
big.parts[2] = big.parts[1];
big.parts[1] = big.parts[0];
big.parts[0] = value;
} else if (amount != 0) {
assert(value < (1u << amount));
for (int i = 3; i > 0; i--) {
big.parts[i] <<= amount;
big.parts[i] |= big.parts[i - 1] >> (32 - amount);
}
big.parts[0] = (big.parts[0] << amount) | value;
}
return big;
}
static big128_t decode_to_big128(const char *key) {
big128_t big = {0};
int validated = 0;
for (char c; (c = *key); key++) {
int ind = findchar(c);
if (ind < 0) continue;
big = big128_fma(big, CHARSET_LEN, ind);
validated++;
}
// TODO: return error instead.
if (validated != 25) {
fprintf(stderr, "WARNING: invalid product key detected; incorrect length.\n");
}
assert(validated == 25);
return big;
}
static void encode_from_big128(big128_t big, char out[30]) {
for (int i = 29; i >= 0; i--) {
if (i == 29) {
out[i] = '\0';
} else if (i % 6 == 5) {
out[i] = '-';
} else {
// perform long division.
const u64 denom = CHARSET_LEN;
u64 residual = 0;
for (int j = 3; j >= 0; j--) {
assert(residual + (u64)big.parts[j] >= residual); // no overflow
residual += big.parts[j];
u64 divided = residual / denom;
big.parts[j] = divided;
residual %= denom; // residual -= divided * denom;
if (j != 0) residual <<= 32;
}
u32 ord = residual % denom;
assert(ord < denom);
out[i] = charset[ord];
}
}
}
static inline void big512_copy(big512_t *out, const big512_t inp) {
// based on PidGen.dll+2660
*out = inp;
}
static int big512_cmp(const big512_t a, const big512_t b) {
// based on PidGen.dll+2624
// returns 1 when a < b
// returns -1 when a > b
// returns 0 when a = b
// you can remember this by looking at the direction of the arrow:
// big512_cmp(...) > 0 means the rightmost argument was bigger;
// big512_cmp(...) < 0 means the leftmost argument was bigger.
int result = 0;
for (int i = BIG512_PARTS - 1; i >= 0; i--) {
if (result != 0) return result;
u32 ai = a.parts[i];
u32 bi = b.parts[i];
result = (int)(ai < bi) - (int)(bi < ai);
}
return result;
}
static uint big512_add(big512_t *out, const big512_t a, const big512_t b) {
// based on PidGen.dll+25D8
uint carries = 0;
for (int i = 0; i < BIG512_PARTS; i++) {
u32 bi = b.parts[i] + carries;
u32 ai = a.parts[i] + bi;
out->parts[i] = ai;
carries = (int)(ai < bi) + (int)(bi < carries);
}
return carries;
}
static uint big512_sub(big512_t *out, const big512_t a, const big512_t b) {
// based on PidGen.dll+2674
uint carries = 0;
for (int i = 0; i < BIG512_PARTS; i++) {
u32 ai = a.parts[i];
u32 bi = b.parts[i];
u32 delta = ai - bi;
out->parts[i] = delta - carries;
carries = (int)(ai < bi) + (int)(delta < carries);
}
return carries;
}
static void big512_addmod(big512_t *out, const big512_t a, const big512_t b) {
// based on PidGen.dll+26C9
// writes a plus b, all modulo divisor, to out.
// assumes a and b are already less than divisor.
// out = a + b;
int carries = big512_add(out, a, b);
if (carries == 0) {
// return if divisor > out.
if (big512_cmp(divisor, *out) < 0) return;
}
// out = (a + b) - divisor;
big512_sub(out, *out, divisor);
}
static void big512_submod(big512_t *out, const big512_t a, const big512_t b) {
// based on PidGen.dll+278F
int carries = big512_sub(out, a, b);
if (carries == 0) return;
big512_add(out, divisor, *out);
}
static void big512_mulmod(big512_t *out, const big512_t a, const big512_t b) {
// based on PidGen.dll+2734
big512_copy(out, zero);
for (int bit = BIG512_PARTS * 32 - 1; bit >= 0; bit--) {
big512_addmod(out, *out, *out);
if (checkbit(a.parts, bit)) big512_addmod(out, b, *out);
}
}
static void big512_rormod(big512_t *io) {
// based on PidGen.dll+2700
// if it's odd, make it even so that
// the last bit can then be shifted out without losing information.
int odd = io->parts[0] & 1;
u32 carry = odd ? big512_add(io, divisor, *io) : 0;
for (int i = BIG512_PARTS - 1; i >= 0; i--) {
u32 value = io->parts[i];
// rotate carry onto MSB of io->parts[i].
io->parts[i] = (value >> 1) | (carry << 31);
carry = value & 1;
}
}
static void big512_divmod(big512_t *out, big512_t a, big512_t b) {
// based on PidGen.dll+27B6
// divides a by b. more precisely, it multiplies
// a by the multiplicative inverse of b.
big512_t c = zero;
big512_t d = divisor;
for (;;) {
// only run big512_rormod for even inputs.
if (!(b.parts[0] & 1)) {
big512_rormod(&b);
big512_rormod(&a);
continue;
}
if (!(d.parts[0] & 1)) {
big512_rormod(&d);
big512_rormod(&c);
continue;
}
// NOTE: since d is initially set to the divisor,
// which is larger than the largest modulated value,
// compared will always be 1 the first time around.
int compared = big512_cmp(b, d);
if (compared == 0) break;
big512_t *output;
const big512_t *input;
output = compared > 0 ? &d : &b;
input = compared < 0 ? &d : &b;
big512_sub(output, *output, *input);
output = compared > 0 ? &c : &a;
input = compared < 0 ? &c : &a;
big512_submod(output, *output, *input);
}
big512_copy(out, a);
}
static keysegs_t split_product_key(big128_t big) {
keysegs_t result = {0};
big = big128_pop(big, 11, &result.p0);
big = big128_pop(big, 31, &result.p1);
big = big128_pop(big, 32, &result.p2lo);
big = big128_pop(big, 62 - 32, &result.p2hi);
big = big128_pop(big, 10, &result.p3);
assert(big.parts[0] == 0);
assert(big.parts[1] == 0);
assert(big.parts[2] == 0);
assert(big.parts[3] == 0);
return result;
}
static big128_t join_product_key(keysegs_t key) {
big128_t result = {0};
assert(key.p0 < (1u << 10));
assert(key.p1 < (1u << 31));
assert(key.p2hi < (1u << 30));
assert(key.p3 < (1u << 10));
result = big128_push(result, 10, key.p3);
result = big128_push(result, 62 - 32, key.p2hi);
result = big128_push(result, 32, key.p2lo);
result = big128_push(result, 31, key.p1);
result = big128_push(result, 11, key.p0);
// TODO: assertions on result.
return result;
}
static void perform_hash(u32 hash[5], const u32 array[80]) {
// based on PidGen.dll+874D which is a part of PidGen.dll+86E0
const u32 magics[4] = { 0x5A827999, 0x6ED9EBA1, 0x8F1BBCDC, 0xCA62C1D6 };
u32 a = hash[0], b = hash[1], c = hash[2], d = hash[3], e = hash[4];
// this was not fun to roll back into loops.
int ind = 0;
for (int outer = 0; outer < 4; outer++) {
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 5; j++) {
u32 temp;
switch (outer) {
case 0: temp = ((c ^ d) & b) ^ d; break;
case 2: temp = (b & c) | ((b | c) & d); break;
default: temp = c ^ b ^ d; break;
}
temp += rol32(a, 5) + array[ind] + magics[outer] + e;
ind++;
e = d;
d = c;
c = ror32(b, 2);
b = a;
a = temp;
}
}
}
hash[0] += a;
hash[1] += b;
hash[2] += c;
hash[3] += d;
hash[4] += e;
}
static void ugly_hash(u32 hash[5], const u8 inp[64]) {
// based on PidGen.dll+86E0
// turns out this is SHA-1!
u32 buf[80]; // 16 for input + 64 for schedule
for (int i = 0; i < 16; i++) {
const u8 *t = &inp[i * 4];
buf[i] = (t[0] << 24) | (t[1] << 16) | (t[2] << 8) | t[3];
}
for (int i = 0; i < 64; i += 2) {
u32 a = buf[i + 0];
u32 b = buf[i + 1];
a ^= buf[i + 2];
b ^= buf[i + 3];
a ^= buf[i + 8];
b ^= buf[i + 9];
a ^= buf[i + 13];
b ^= buf[i + 14];
buf[i + 16] = rol32(a, 1);
buf[i + 17] = rol32(b, 1);
}
perform_hash(hash, buf);
}
static inline void hash_push(hashdata_t *hd, u32 value, int len) {
// loosely based on PidGen.dll+8500
assert(len == 0 || len == 1 || len == 2 || len == 4);
if (len == 1) {
assert(value < (1u << 8));
hd->buf[hd->pos++] = (u8)value;
hd->len += 8; // measured in bits
} else if (len == 2) {
assert(value < (1u << 16));
hash_push(hd, (u8)value, 1);
hash_push(hd, (u8)(value >> 8), 1);
return;
} else if (len == 4) {
hash_push(hd, (u8)value, 1);
hash_push(hd, (u8)(value >> 8), 1);
hash_push(hd, (u8)(value >> 16), 1);
hash_push(hd, (u8)(value >> 24), 1);
return;
}
if (hd->pos == 64) {
ugly_hash(hd->hash, hd->buf);
hd->pos = 0;
}
}
static void hash_finish(hashdata_t *hd) {
u32 hi = (u32)(hd->len >> 32);
u32 lo = (u32)hd->len;
hash_push(hd, 0x80, 1); // end-of-message bit
while (hd->pos != 64 - 8) hash_push(hd, 0, 1); // padding
hash_push(hd, bswap32(hi), 4); // message length in bits (upper)
hash_push(hd, bswap32(lo), 4); // message length in bits (lower)
}
static int pair_check(const pair_t pair) {
// based on PidGen.dll+28A4
// return 1 if both are equal to divisor, 0 otherwise.
const big512_t a = pair.lo;
const big512_t b = pair.hi;
if (big512_cmp(divisor, a) != 0) return 0;
if (big512_cmp(divisor, b) != 0) return 0;
return 1;
}
static void pair_reset(pair_t *pair) {
// based on PidGen.dll+28CF
pair->lo = divisor;
pair->hi = divisor;
}
static void pair_something(pair_t *out, const pair_t a, const pair_t b) {
// based on PidGen.dll+299C
// TODO: what is this doing exactly?
big512_t temp_a;
big512_t temp_b;
big512_t temp_c;
if (pair_check(a)) {
big512_copy(&out->lo, b.lo);
big512_copy(&out->hi, b.hi);
return;
}
if (pair_check(b)) {
big512_copy(&out->lo, a.lo);
big512_copy(&out->hi, a.hi);
return;
}
if (big512_cmp(b.lo, a.lo) == 0) {
big512_addmod(&temp_b, b.hi, a.hi);
if (big512_cmp(temp_b, zero) == 0) {
// temp_b can't be zero because it would cause a division by zero.
// this is incredibly rare in practice.
pair_reset(out);
return;
}
// temp_c = 3 * a.lo**2 + 1
big512_mulmod(&temp_a, a.lo, a.lo);
big512_addmod(&temp_c, one, temp_a);
big512_addmod(&temp_c, temp_c, temp_a);
big512_addmod(&temp_c, temp_c, temp_a);
} else {
big512_submod(&temp_c, a.hi, b.hi);
big512_submod(&temp_b, a.lo, b.lo);
}
// if a.lo == b.lo:
// temp_a = (3 * a.lo**2 + 1) / (b.hi + a.hi)
// else:
// temp_a = (a.hi - b.hi) / (a.lo - b.lo)
big512_divmod(&temp_a, temp_c, temp_b);
// &out->lo = temp_a**2 - a.lo - b.lo
big512_mulmod(&temp_b, temp_a, temp_a);
big512_submod(&temp_b, temp_b, a.lo);
big512_submod(&temp_b, temp_b, b.lo);
big512_copy(&out->lo, temp_b);
// &out->hi = temp_a * (a.lo - &out->lo) - a.hi
big512_submod(&temp_c, a.lo, temp_b);
big512_mulmod(&temp_b, temp_a, temp_c);
big512_submod(&out->hi, temp_b, a.hi);
}
static void pair_loop(pair_t *io, const pair_t magic, u32 src[2]) {
// just to reduce code clutter.
for (int bit = 62 - 1; bit >= 0; bit--) {
pair_something(io, *io, *io);
if (checkbit(src, bit)) pair_something(io, *io, magic);
}
}
int validate(keysegs_t data) {
// returns 1 when data is a valid product key, 0 otherwise.
pair_t pairs[3];
hashdata_t hd_a = {0};
hashdata_t hd_b = {0};
for (int i = 0; i < 3; i++) pair_reset(&pairs[i]);
for (int i = 0; i < 5; i++) hd_a.hash[i] = hash_seed[i];
for (int i = 0; i < 5; i++) hd_b.hash[i] = hash_seed[i];
hash_push(&hd_a, 31 + 62, 1); // bits popped from p1 + bits popped from p2
hash_push(&hd_a, data.p0, 2);
hash_push(&hd_a, data.p1, 4);
hash_push(&hd_a, data.p3, 4);
hash_finish(&hd_a);
u32 src_a[2] = { data.p2lo, data.p2hi };
u32 src_b[2] = { bswap32(hd_a.hash[0]), bswap32(hd_a.hash[1]) >> 2 };
pair_loop(&pairs[0], magic_a, src_a);
pair_loop(&pairs[1], magic_b, src_b);
pair_something(&pairs[0], pairs[0], pairs[1]);
pair_loop(&pairs[2], pairs[0], src_a);
hash_push(&hd_b, 0x79, 1); // bits popped, which is 121 here for some reason.
hash_push(&hd_b, data.p0, 2);
for (int i = 0; i < 16; i++) hash_push(&hd_b, pairs[2].lo.parts[i], 4);
for (int i = 0; i < 16; i++) hash_push(&hd_b, pairs[2].hi.parts[i], 4);
hash_finish(&hd_b);
u32 masked = bswap32(hd_b.hash[0]) & ((1u << 31) - 1u);
return masked == data.p1;
}
static void printheader() {
printf(" Product Key | 11 _____ 31 ______________ 62 10 | Validity\n");
fflush(stdout);
}
static int printkey(keysegs_t data) {
// also returns validity.
char recon[30];
big128_t big = join_product_key(data);
encode_from_big128(big, recon);
int validity = validate(data);
printf("%s | %03X %08X %08X %08X %03X | %s\n",
recon, data.p0, data.p1, data.p2hi, data.p2lo, data.p3,
validity ? "Valid" : "Invalid");
fflush(stdout);
return validity;
}
int bruteforce() {
// brute force. returns 1 when at least one key is found, 0 otherwise.
// TODO: make this better. you are very unlikely to find any keys this way.
int found_any = 0;
// NOTE: this seeding function does not collect a lot of entropy.
// NOTE: this RNG is probably not enough for what we're using it for.
seed_prng();
printf("seed: 0x%08X%08X\n", (u32)(prng_state >> 32), (u32)prng_state);
printheader();
keysegs_t data = {0};
data.p0 = 70; // must be 70 or the game rejects it (i think)
data.p2lo = prng();
data.p2hi = prng() & ((1u << 30) - 1u);
for (int tries = 0; tries < 65536; tries++) {
data.p1 = prng() & ((1u << 31) - 1u);
data.p3 = prng() & ((1u << 10) - 1u);
if (printkey(data)) found_any = 1;
}
return found_any;
}
int main(int argc, char **argv) {
if (argc <= 0 || argv == NULL || argv[0] == NULL) {
fprintf(stderr, "You've met with a terrible fate.\n");
return -1;
}
if (argc == 1) return !bruteforce();
int all_valid = 1;
printheader();
for (int i = 1; i < argc; i++) {
const char *key = argv[i];
big128_t big = decode_to_big128(key);
keysegs_t data = split_product_key(big);
if (!printkey(data)) all_valid = 0;
}
return !all_valid;
}
#ifdef _WIN32
typedef struct {
s64 QuadPart;
} hacky__li;
int __stdcall QueryPerformanceFrequency(hacky__li *lpFrequency);
int __stdcall QueryPerformanceCounter(hacky__li *lpPerformanceCount);
static void seed_prng() {
hacky__li time;
QueryPerformanceCounter(&time);
prng_state = time.QuadPart;
}
#else
#include <time.h>
#include <unistd.h>
static void seed_prng() {
prng_state = ((u64)getpid() << 16) ^ (u32)time(NULL);
}
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment