← Back to Writeups
CUSTOMEasyReversing

JustExperiment

XESXOR9/10/20263 min read
#reversing#custom#easy#auto_vectorization_recognition#instruction_level_emulation#register_liveness_tracing#windows

JustExperiment

Platform: spbctf | Category: Reversing | Type: Challenge | Difficulty: Easy | OS: Windows | Author: XESXOR | Date: 2026-06-24 | Status: Solved Techniques: auto_vectorization_recognition, instruction_level_emulation, register_liveness_tracing

Summary

Task: 32-bit MSVC crackme that asks for an integer password and prints a flag on success. Solution: emulate the password-validation loop and the flag-computation helper at instruction level, noting the flag is the helper's return value (uninitialized ecx carried from main = password+1337), not the password itself.

JustExperiment — spbctf (forkbomb rev-kids)

Description

Pass the checks, and app will print the flag

A 32-bit PE32 console executable. It reads an integer password via scanf, validates it against a value derived from a loop, and on success prints a flag. The goal is to recover the printed flag value.

Analysis

file reports: PE32 executable, Intel 80386, MS Windows, MSVC-built. A leaked PDB path confirms the toolchain:

C:\Users\dsnos\Desktop\...\CTF\binary05\Tasks\Release\Tasks.pdb

‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Notable strings:

  • This is secure app, you need integer password to use it!
  • You are welcome! The flag is: %d
  • Go out of here!

main() — password validation (0x00401110)

scanf("%d", &password) reads the integer. Then a loop computes a constant in edi, seeded with edi = 0x111b25 (1121061):

edi = 0x111b25
for (esi = 0; esi < 0x63 (99); ) {
    eax = esi;
    ecx = esi + 7;
    eax = eax * edi;      // imul
    esi++;                // inc before idiv
    edx = eax % ecx;      // signed remainder (cdq; idiv ecx)
    edi ^= edx;
}

‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

The loop yields edi = 1121044. The check is:

ecx = password + 0x539 (1337)
cmp edi, ecx

So the required password is 1121044 - 1337 = 1119707.

The trap

A naive reading assumes the flag equals the password (1119707), because the %d print follows the success branch. This is wrong. The value pushed to printf is eax = call fcn.00401080, the return of a helper — not the password. Always verify the operand order before concluding.

fcn.00401080 — flag computation

This is a compiler auto-vectorized loop with two equivalent paths selected by a runtime guard (cmp dword [0x403364], 2; jl 0x4010fd). Both compute the same result. Critically, ecx is not initialized inside the function — it carries over from main, where after cmp edi, ecx we have ecx = password + 1337 = 1121044. The intervening pop edi; pop esi do not clobber ecx. ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Scalar path (0x4010fd):

eax = 0
ecx = incoming_ecx        // = password+1337 = 1121044
do {
    ecx ^= eax;
    eax += 7;
} while (eax < 0x1ab0);    // 6832
return ecx;

The SSE path broadcasts edx, computes (edx + {0,1,2,3}) * 7, XOR-accumulates into two xmm registers over edx < 0x3d0 (976), then horizontally XOR-folds and xor eax, ecx. It produces the identical value.

Both paths return REDACTED.

Solution

Emulate both loops at instruction level. The flag is the helper return value. ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

python
#!/usr/bin/env python3
import ctypes
def u32(x): return x & 0xffffffff
def s32(x): return ctypes.c_int32(x).value

# --- password validation loop -> edi ---
edi = 0x111b25
esi = 0
while True:
    eax = s32(esi)
    ecx = s32(esi + 7)
    eax = s32(eax * edi)        # imul
    esi = s32(esi + 1)          # inc before idiv
    q = int(eax / ecx)          # truncate toward zero (signed idiv)
    edx = s32(eax - q * ecx)
    edi ^= edx
    edi = s32(edi)
    if not (esi < 0x63):
        break
# edi == 1121044; required password = edi - 1337 = 1119707
incoming_ecx = edi              # = password + 1337 at the call to 0x401080
‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

# --- flag helper (scalar path) ---
eax = 0
ecx = incoming_ecx
while True:
    ecx = u32(ecx ^ eax)
    eax = s32(eax + 7)
    if not (eax < 0x1ab0):      # 6832
        break

print("required password:", edi - 1337)   # 1119707
print("flag:", s32(ecx))                   # REDACTED

Output:

required password: 1119707
flag: REDACTED

Running the binary with password 1119707 prints You are welcome! The flag is: REDACTED. ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍