| SlimKeyGen |
[Click to reveal]I started from the VM call in main. The program reads the name and three hex values, puts them in a small struct, then passes a pointer to that struct into the VM as R0.
The VM has 16 registers and 84 instructions, with every instruction taking 16 bytes. The opcode, register bytes, and immediate are encrypted differently for each instruction, using the instruction index as part of the key. I recovered that key generation from the dispatcher, dumped and decoded the whole bytecode stream, then labelled the handlers I needed: loads, stores, AND, OR, XOR, NOT, shifts, compare, conditional jumps, and halt.
After lifting the instructions, most of the ugly parts were just obfuscated XOR and addition. For example, (a | b) & ~(a & b) is XOR, and (a ^ b) + 2 * (a & b) is addition. Once those were simplified, the VM turned into a normal 32-bit hash over the name plus two extra mixing steps.
The important part is that the serial order is hash-intermediate-final, not the order the values are calculated
in:
C++
uint32_t h = 0x4D3C2B1A;
for (unsigned char c : name) {
h = rol32(h ^ c, 5);
h += 0x13579BDF;
h ^= h >> 7;
}
uint32_t s1 = rol32(h ^ 0x7319C5AD, 11);
s1 += 0x51ED270B;
s1 ^= s1 >> 13;
uint32_t s2 = (h ^ s1) + 0x9E3779B9;
s2 = rol32(s2, 17);
s2 ^= h >> 3;
// print h-s1-s2 as %08X-%08X-%08X
I verified the order from the comparison block: field 0 is checked against h, field 1 against s1, and field 2 against s2. For example, Alice gives D080EC22-1B397DEA-0D12CE66.
|
2026-08-20 16:31 |