Challenge 1: sub_140001010
__int64 __fastcall verify_key(unsigned int a1, unsigned int a2) { if ( (3 * a1 + 2 * a2 != 180) ) return 0LL; if ( (a1 - a2 != 10) ) return 0LL; return 1LL; }
Skills: Systems of linear equations. Model `a1` and `a2` as Z3 integers.
Use `a1, a2 = Ints('a1 a2')` in Python Z3, add the two equations, and output the model results.
from z3 import * a1, a2 = Ints('a1 a2') s = Solver() s.add(3 * a1 + 2 * a2 == 180) s.add(a1 - a2 == 10) if s.check() == sat: print(s.model())
Challenge 2: sub_1400011C0
bool __fastcall validate_serial(int input_val) { int calculated; // [rsp+18h] [rbp-4h] calculated = input_val * input_val - 40 * input_val; return input_val > 0 && calculated == 1200; }
Skills: Quadratic equations and inequalities in Z3.
Define a single variable `x = Int('x')`. Add constraints `x > 0` and `x * x - 40 * x == 1200`.
from z3 import * x = Int('x') s = Solver() s.add(x > 0) s.add(x * x - 40 * x == 1200) if s.check() == sat: print(s.model())
Challenge 3: sub_1400012A0
__int64 __fastcall check_byte(__int8 key) { __int8 temporary; temporary = 7 * key + 12; return temporary == -46; // Watch out for signed 8-bit wrap around! }
Skills: Modeling overflow using Bit-Vectors in Z3. Return the unsigned value (0-255).
Model `key` as an 8-bit Bit-Vector: `key = BitVec('key', 8)`. Use equation `7 * key + 12 == -46`. Retrieve the integer output in Python.
from z3 import * key = BitVec('key', 8) s = Solver() s.add(7 * key + 12 == -46) if s.check() == sat: print(s.model()[key].as_long())
Challenge 4: sub_140001350
bool __fastcall verify_str(char *str) { if ( str[0] != 'c' ) return false; if ( str[1] + str[0] != 210 ) return false; if ( (str[2] ^ str[1]) != 13 ) return false; return str[3] == 0; }
Skills: Character calculations. Solve for a 3-character string.
Model indices `str[0]`, `str[1]`, `str[2]` as 8-bit bitvectors or integers. Convert solved values back to ASCII characters.
from z3 import * chars = [BitVec(f'c_{i}', 8) for i in range(3)] s = Solver() s.add(chars[0] == ord('c')) s.add(chars[1] + chars[0] == 210) s.add(chars[2] ^ chars[1] == 13) if s.check() == sat: m = s.model() print("".join(chr(m[c].as_long()) for c in chars))
Challenge 5: sub_1400015F0
__int64 __fastcall bit_kernel(unsigned __int16 input) { if ( (input & 0xFF00) != 0xDE00 ) return 0LL; if ( (input ^ 0x00FF) != 0xDEAD ) return 0LL; return 1LL; }
Skills: 16-bit Bit-Vector constraints (AND, XOR).
Create `input = BitVec('input', 16)`. Add the two constraints and solve. Output the base-10 representation.
from z3 import * input_val = BitVec('input_val', 16) s = Solver() s.add(input_val & 0xFF00 == 0xDE00) s.add(input_val ^ 0x00FF == 0xDEAD) if s.check() == sat: print(s.model()[input_val].as_long())
Challenge 6: sub_140001700
bool __fastcall array_checker(unsigned int *arr) { // arr contains 3 elements if ( arr[0] + arr[1] != 100 ) return false; if ( arr[1] * arr[2] != 1200 ) return false; if ( arr[2] - arr[0] != 10 ) return false; return true; }
Skills: Modeling arrays of integers. Format: `x,y,z` for `arr[0],arr[1],arr[2]`.
Model three Z3 variables: `x, y, z = Ints('x y z')`. Solve and input as comma-separated integers.
from z3 import * arr = [Int(f'arr_{i}') for i in range(3)] s = Solver() s.add(arr[0] + arr[1] == 100) s.add(arr[1] * arr[2] == 1200) s.add(arr[2] - arr[0] == 10) if s.check() == sat: print(s.model())
Challenge 7: sub_140001850
__int64 __fastcall modulo_lock(int secret) { if ( secret <= 1000 ) return 0LL; if ( secret % 11 != 5 ) return 0LL; if ( secret % 13 != 3 ) return 0LL; return secret < 1150; }
Skills: Multi-modulo boundary constraints.
Create `secret = Int('secret')`. Add constraints: `secret > 1000`, `secret < 1150`, `secret % 11 == 5`, `secret % 13 == 3`.
from z3 import * secret = Int('secret') s = Solver() s.add(secret > 1000) s.add(secret < 1150) s.add(secret % 11 == 5) s.add(secret % 13 == 3) if s.check() == sat: print(s.model())
Challenge 8: sub_1400019C0
bool __fastcall shift_check(unsigned int flag) { unsigned int v1; unsigned int v2; v1 = flag >> 4; v2 = flag << 3; return (v1 == 0x123) && ((v2 & 0xFF) == 0x28); }
Skills: Left and right shift simulation in Z3 Bit-Vectors.
Model `flag` as a 32-bit Bit-Vector: `flag = BitVec('flag', 32)`. Add constraints using Z3 shift operators `>>` and `<<`.
from z3 import * flag = BitVec('flag', 32) s = Solver() v1 = flag >> 4; v2 = flag << 3; s.add(v1 == 0x123) s.add((v2 & 0xFF) == 0x28) if s.check() == sat: print(s.model()[flag].as_long())
Challenge 9: sub_140001AF0
__int64 __fastcall multi_check(int x, int y, int z) { if ( 5 * x - 3 * y + 2 * z != 29 ) return 0LL; if ( 2 * x + 4 * y - 5 * z != 5 ) return 0LL; if ( 3 * x - 2 * y + 4 * z != 34 ) return 0LL; return 1LL; }
Skills: 3-variable system of linear equations. Format: `x,y,z`.
Define `x, y, z = Ints('x y z')`. Add the 3 linear checks, check status, and retrieve values.
from z3 import * x, y, z = Ints('x y z') s = Solver() s.add(5 * x - 3 * y + 2 * z == 29) s.add(2 * x + 4 * y - 5 * z == 5) s.add(3 * x - 2 * y + 4 * z == 34) if s.check() == sat: print(s.model())
Challenge 10: sub_140001C50
bool __fastcall final_boss(unsigned int key) { unsigned int transform; transform = key ^ 0x55555555; transform = (transform >> 16) | (transform << 16); // 16-bit block rotate transform += 0x1337; return transform == 0xDEADBEEF; }
Skills: Mixed bitwise rotations, XOR, and arithmetic additions.
Create `key = BitVec('key', 32)`. Replicate the rotate operation using `((transform >> 16) | (transform << 16))` inside Z3. Beware of shift boundaries in 32-bit.
from z3 import * key = BitVec('key', 32) s = Solver() transform = key ^ 0x55555555 transform = RotateRight(transform, 16) s.add(transform + 0x1337 == 0xDEADBEEF) if s.check() == sat: print(s.model()[key].as_long())
Challenge 11: sub_140002100
bool __fastcall check_matrix(unsigned char *a) { if ( (5*a[0] + 3*a[1] + 8*a[2] + 2*a[3]) % 256 != 189 ) return false; if ( (2*a[0] + 9*a[1] + a[2] + 4*a[3]) % 256 != 23 ) return false; if ( ( a[0] + 4*a[1] + 7*a[2] + 5*a[3]) % 256 != 107 ) return false; if ( (8*a[0] + 6*a[1] + 3*a[2] + 9*a[3]) % 256 != 148 ) return false; return true; }
Skills: Linear systems over modular algebra. Model bytes using `BitVec(..., 8)` or `Int` with modulo constraints. Format: `a[0],a[1],a[2],a[3]`.
Define 4 variables as `BitVecs('a b c d', 8)`. Set up equations using `+` and `*` operators. Note: the third equation is `4*a + b + 7*c + 5*d == 107` (IDA decompiler optimization details: `4 * a[0] + a[1]` is optimized as `a[1] + 4 * a[0]`).
from z3 import * a = [BitVec(f'a_{i}', 8) for i in range(4)] s = Solver() s.add((5*a[0] + 3*a[1] + 8*a[2] + 2*a[3]) == 189) s.add((2*a[0] + 9*a[1] + a[2] + 4*a[3]) == 23) s.add((4*a[0] + a[1] + 7*a[2] + 5*a[3]) == 107) s.add((8*a[0] + 6*a[1] + 3*a[2] + 9*a[3]) == 148) if s.check() == sat: print(s.model())
Challenge 12: sub_140002200
bool __fastcall validate_keys(unsigned int x, unsigned int y) { unsigned int combined_xor = x ^ y; unsigned int combined_sum = x + y; return (combined_xor == 0x5599B9F9) && (combined_sum == 0x9999BE01) && ((x & 0xFFFF0000) == 0x32100000); }
Skills: Reversing mixed arithmetic (addition) and bitwise (XOR) operators. Hand analysis is highly difficult due to carry propagation. Format: `x,y`.
Model `x` and `y` as 32-bit Bit-Vectors (`BitVec('x', 32)`). Add the three constraints. The carry bits will be solved automatically by Z3's bit-blasting engine.
from z3 import * x, y = BitVecs('x y', 32) s = Solver() s.add(x ^ y == 0x5599B9F9) s.add(x + y == 0x9999BE01) s.add(x & 0xFFFF0000 == 0x32100000) if s.check() == sat: print(s.model())
Challenge 13: sub_140002300
bool __fastcall verify_hash(const char *str) { unsigned int hash = 5381; int i; for ( i = 0; i < 6; ++i ) { if ( str[i] < 32 || str[i] > 126 ) return false; hash = 33 * hash + str[i]; } return hash == 0x284FC66A; }
Skills: Loop modeling, hash preimages, character boundaries. Find a 6-character printable string. (Multiple inputs may collide and be valid!)
Use Z3 `Int` for each of the 6 characters to prevent slow bitvector division. Model the hash step as: `hash = (hash * 33 + chars[i]) % 4294967296`. Add bounds `chars[i] >= 32` and `chars[i] <= 126`. One solution is `'z3rule'`.
from z3 import * chars = [Int(f'c_{i}') for i in range(6)] s = Solver() for c in chars: s.add(c >= 32, c <= 126) h = 5381 for c in chars: h = (h * 33 + c) % 4294967296 s.add(h == 0x284FC66A) if s.check() == sat: m = s.model() print("".join(chr(m[c].as_long()) for c in chars))
Challenge 14: sub_140002400
bool __fastcall check_nonlinear(unsigned __int16 a, unsigned __int16 b, unsigned __int16 c) { if ( a <= 1000 || b <= 1000 || c <= 1000 ) return false; if ( (a * b + c) % 65536 != 3312 ) return false; if ( (b * c + a) % 65536 != 53290 ) return false; if ( ((a ^ b) << 4) % 65536 != 0x2FC0 ) return false; return true; }
Skills: Non-linear multiplication mixed with bitwise XOR on 16-bit ranges. Format: `a,b,c`.
Model variables as 16-bit Bit-Vectors (`BitVec('a', 16)`). Let multiplication wrap around naturally as it does in 16-bit arithmetic. Z3 can solve non-linear multiplication over finite fields very quickly.
from z3 import * a, b, c = BitVecs('a b c', 16) s = Solver() s.add(a > 1000, b > 1000, c > 1000) s.add(a * b + c == 3312) s.add(b * c + a == 53290) s.add(((a ^ b) << 4) == 0x2FC0) if s.check() == sat: print(s.model())
Challenge 15: sub_140002500
bool __fastcall verify_array(unsigned int *arr) { unsigned int temp[4]; temp[0] = arr[2] + 15; temp[1] = arr[0] ^ arr[3]; temp[2] = arr[1] * 3; temp[3] = arr[3] - arr[2]; return (temp[0] == 100) && (temp[1] == 1337) && (temp[2] == 999) && (temp[3] == 500) && (arr[0] + arr[1] + arr[2] + arr[3] == 2843); }
Skills: Tracking shuffled variables and multi-variable dependencies. Format: `arr[0],arr[1],arr[2],arr[3]`.
Create 4 variables `arr = [BitVec(f'a_{i}', 32) for i in range(4)]`. Set up the temporary variables exactly as shown, add the constraints, and output the results.
from z3 import * arr = [BitVec(f'arr_{i}', 32) for i in range(4)] s = Solver() temp0 = arr[2] + 15 temp1 = arr[0] ^ arr[3] temp2 = arr[1] * 3 temp3 = arr[3] - arr[2] s.add(temp0 == 100) s.add(temp1 == 1337) s.add(temp2 == 999) s.add(temp3 == 500) s.add(arr[0] + arr[1] + arr[2] + arr[3] == 2843) if s.check() == sat: print(s.model())
Challenge 16: sub_140003100
bool __fastcall mix_registers(unsigned int k1, unsigned int k2, unsigned int k3) { unsigned int r0 = k1; unsigned int r1 = k2; unsigned int r2 = k3; r0 = (r0 ^ r1) + r2; r1 = (r1 + r2) ^ r0; r2 = (r2 ^ r0) + r1; r0 = (r0 << 5) | (r0 >> 27); // ROL 5 r1 = (r1 >> 3) | (r1 << 29); // ROR 3 r2 ^= 0x90ABCDEF; return (r0 == 0x12345678) && (r1 == 0x87654321) && (r2 == 0xABCDEF01); }
Skills: Modeling state transitions, bitwise rotations, and mixed addition/XOR diffusion. Format: `k1,k2,k3`.
Use 32-bit Bit-Vectors. Replicate rotations using `RotateLeft(r0, 5)` and `RotateRight(r1, 3)` from the `z3` Python library. Add the final equality checks and output the base-10 values.
from z3 import * k1, k2, k3 = BitVecs('k1 k2 k3', 32) s = Solver() r0 = (k1 ^ k2) + k3 r1 = (k2 + k3) ^ r0 r2 = (k3 ^ r0) + r1 r0 = RotateLeft(r0, 5) r1 = RotateRight(r1, 3) r2 ^= 0x90ABCDEF s.add(r0 == 0x12345678) s.add(r1 == 0x87654321) s.add(r2 == 0xABCDEF01) if s.check() == sat: print(s.model())
Challenge 17: sub_140003200
bool __fastcall sbox_layer(unsigned char *key) { const unsigned char sbox[16] = { 0xC, 0x5, 0x6, 0xB, 0x9, 0x0, 0xA, 0xD, 0x3, 0xE, 0xF, 0x8, 0x4, 0x7, 0x1, 0x2 }; unsigned char a, b, c, d; a = (sbox[key[0] >> 4] << 4) | sbox[key[0] & 0xF]; b = (sbox[key[1] >> 4] << 4) | sbox[key[1] & 0xF]; c = (sbox[key[2] >> 4] << 4) | sbox[key[2] & 0xF]; d = (sbox[key[3] >> 4] << 4) | sbox[key[3] & 0xF]; return (a ^ b ^ c == 0xAA) && (b ^ c ^ d == 0xBB) && (c ^ d ^ a == 0xCC) && (d ^ a ^ b == 0xDD); }
Skills: Modeling lookup tables and substitution layers in Z3. Format: `key[0],key[1],key[2],key[3]`.
Model lookup using Z3 nested `If` constraints. For a 4-bit value `val`, do `res = If(val == 0, 12, If(val == 1, 5, ...))`. Perform this lookup on both high and low nibbles of each key byte, then enforce the diffusion XOR equations.
from z3 import * key = [BitVec(f'key_{i}', 8) for i in range(4)] s = Solver() sbox_vals = [0xC, 0x5, 0x6, 0xB, 0x9, 0x0, 0xA, 0xD, 0x3, 0xE, 0xF, 0x8, 0x4, 0x7, 0x1, 0x2] def sbox_lookup(val): res = BitVecVal(0, 4) for i in range(16): res = If(val == i, BitVecVal(sbox_vals[i], 4), res) return res def sbox_byte(b): return Concat(sbox_lookup((b >> 4) & 0xF), sbox_lookup(b & 0xF)) a = sbox_byte(key[0]) b = sbox_byte(key[1]) c = sbox_byte(key[2]) d = sbox_byte(key[3]) s.add(a ^ b ^ c == 0xAA) s.add(b ^ c ^ d == 0xBB) s.add(c ^ d ^ a == 0xCC) s.add(d ^ a ^ b == 0xDD) if s.check() == sat: print(s.model())
Challenge 18: sub_140003300
bool __fastcall lfsr_check(unsigned int seed) { unsigned int state = seed; unsigned int bit; int i; for ( i = 0; i < 16; ++i ) { bit = (state ^ (state >> 2) ^ (state >> 3) ^ (state >> 5)) & 1; state = (state >> 1) | (bit << 31); } return state == 0x1B7E1337; }
Skills: Modeling loops and bit-level feedback structures. Hand-reversing is highly tedious due to shifting overlaps.
Define an array of 17 variables `states = [BitVec(f'state_{i}', 32) for i in range(17)]` to prevent exponential growth in symbolic expressions. Enforce `states[i+1] == (states[i] >> 1) | (ZeroExt(31, bit) << 31)` with the appropriate XOR taps extracted using `Extract(bit_pos, bit_pos, states[i])`.
from z3 import * seed = BitVec('seed', 32) states = [BitVec(f'state_{i}', 32) for i in range(17)] s = Solver() s.add(states[0] == seed) for i in range(16): bit = (Extract(0, 0, states[i]) ^ Extract(2, 2, states[i]) ^ Extract(3, 3, states[i]) ^ Extract(5, 5, states[i])) s.add(states[i+1] == (states[i] >> 1) | (ZeroExt(31, bit) << 31)) s.add(states[16] == 0x1B7E1337) if s.check() == sat: print(s.model()[seed].as_long())
Challenge 19: sub_140003400
bool __fastcall diophantine_check(unsigned int x, unsigned int y) { if ( x + y != 500000 ) return false; if ( (x / 1337) + (y % 999) != 301 ) return false; if ( (y / 1337) + (x % 999) != 572 ) return false; return true; }
Skills: Modeling integer division and modulus operations. SMT solvers solve this via arithmetic domain reductions. Format: `x,y`.
Use mathematical integers `x, y = Ints('x y')` in Z3 rather than Bit-Vectors. Division `/` and remainder `%` on Z3 `Int` are extremely optimized compared to `BitVec` divisions, solving in milliseconds.
from z3 import * x, y = Ints('x y') s = Solver() s.add(x >= 0, y >= 0) s.add(x + y == 500000) s.add(x / 1337 + y % 999 == 301) s.add(y / 1337 + x % 999 == 572) if s.check() == sat: print(s.model())
Challenge 20: sub_140003500
bool __fastcall tea_round(unsigned int *k) { unsigned int v0 = 0x01234567; unsigned int v1 = 0x89ABCDEF; unsigned int sum = 0x9E3779B9; v0 += ((v1 << 4) + k[0]) ^ (v1 + sum) ^ ((v1 >> 5) + k[1]); v1 += ((v0 << 4) + k[2]) ^ (v0 + sum) ^ ((v0 >> 5) + k[3]); return (v0 == 0xDEADBEEF) && (v1 == 0xCAFEBABE); }
Skills: Decrypting cipher schedules. Solve for the 4-word key `k[0],k[1],k[2],k[3]`. Z3 handles cryptographic constraint systems cleanly.
Define 4 variables as 32-bit Bit-Vectors. Substitute constants `v0, v1, sum` exactly as they are defined. Set the final constraints and print the values in decimal.
from z3 import * k = [BitVec(f'k_{i}', 32) for i in range(4)] s = Solver() v0 = BitVecVal(0x01234567, 32) v1 = BitVecVal(0x89ABCDEF, 32) sum_val = BitVecVal(0x9E3779B9, 32) v0 += ((v1 << 4) + k[0]) ^ (v1 + sum_val) ^ ((v1 >> 5) + k[1]) v1 += ((v0 << 4) + k[2]) ^ (v0 + sum_val) ^ ((v0 >> 5) + k[3]) s.add(v0 == 0xDEADBEEF) s.add(v1 == 0xCAFEBABE) if s.check() == sat: print(s.model())