Skip to main content

GDB baby step 4 picoGym Exclusive Solution

Use a debugger to trace a binary's execution and identify a key arithmetic value tied to the flag.

Published: March 5, 2024Updated: August 25, 2026

Description

Debugger0_d jumps into func1 where EAX is multiplied by a constant. Convert that constant to decimal for the final flag.

GDB control flowDownload debugger0_d

Launch the binary under gdb and examine main until it calls func1.

Inside func1, watch for the IMUL instruction that scales EAX; its immediate operand is the flag.

bash
wget https://artifacts.picoctf.net/c/532/debugger0_d
bash
chmod +x debugger0_d
bash
gdb --args ./debugger0_d

Solution

Want to try it yourself first?

The guided walkthrough reveals hints one step at a time.

Walk me through it
  1. Step 1Step into func1
    Observation
    main ends in a call to func1 before any real arithmetic appears, so the constant lives inside that function. Step into the call rather than over it.
    Break at the call site, b *(main+38), then stepi into the callee. Within func1 the IMUL at offset +14 (b *(func1+14)) multiplies EAX by 0x3269.
    Learn more

    This challenge introduces control flow tracing across function boundaries. Main calls func1 using a CALL instruction, which pushes the return address onto the stack and transfers control to the called function. In GDB, stepi (step one instruction, entering calls) follows execution into func1, while nexti (step over) would execute the entire call and return as a single step.

    In real reverse engineering, following call graphs is essential. Complex software can have hundreds of nested function calls, and understanding the data flow requires knowing which functions are called in what order and what they do to the arguments. GDB's backtrace command (alias bt) shows the current call stack, which helps you understand where you are in the call hierarchy at any breakpoint.

    The IMUL instruction at func1+14 is the key. Setting a breakpoint at that exact offset (b *(func1+14)) would land execution right before the multiply, letting you inspect EAX before and after to observe the transformation. Alternatively, breaking after the IMUL and reading the assembly listing statically reveals the multiplier without needing to run anything.

  2. Step 2Convert the constant
    Observation
    The multiply inside func1 uses 0x3269 as its immediate operand. That constant, converted to decimal, is the answer, not any runtime register value.
    Translate 0x3269 into decimal and wrap it with picoCTF{...}. Any method works: printf, python, or bc all give 12905.
    python
    python3 - <<'PY'
    print(0x3269)
    PY
    bash
    printf "picoCTF{%d}\n" 0x3269

    Expected output

    picoCTF{...}
    What didn't work first

    Tried: Run the binary, let func1 execute, then read EAX after the IMUL to get the flag value.

    Before the multiply, EAX holds whatever main passed in, so the product depends on runtime input and is not constant at all. The question asks for the multiplier, the immediate operand, which you read off the disassembly rather than out of a register.

    Tried: Interpret 0x3269 as ASCII by treating the two bytes 0x32 and 0x69 as characters.

    Read as ASCII those two bytes spell '2i', which is not a flag. The challenge asks for the decimal value of the hex constant, 12905, wrapped in the flag format. Convert; do not decode.

    Learn more

    0x3269 converts to decimal 12905. The challenge specifically asks for the multiplier - the immediate operand of the IMUL instruction - rather than the result of the multiplication. This distinction matters: the multiplier is a constant visible in the disassembly, while the result depends on whatever value EAX held before the IMUL, which could vary across runs.

    This challenge wraps up the GDB Baby Step series by combining all the skills introduced in the previous three: disassembling a binary, tracing data flow across instructions, following a function call, and converting a constant from hex to decimal. Each step was simple in isolation, but combining them is the actual skill being assessed.

    The real-world application is binary auditing for hardcoded constants. License keys, encryption round counts, magic numbers for validation, and protocol version numbers all appear as constants in binaries. Being able to locate and extract them using GDB or static disassembly tools is a core reverse engineering skill used in vulnerability research, malware analysis, and software interoperability work.

Interactive tools
  • Cyclic Pattern GeneratorGenerate de Bruijn cyclic patterns and find buffer overflow offsets. The browser equivalent of pwntools cyclic and cyclic_find.
  • pwntools Payload BuilderPack integers into little-endian bytes (p32 / p64), unpack bytes back to integers, and build flat ROP payloads with offset-based insertion.
  • Hex ViewerView text or raw hex bytes as a xxd-style hex dump with byte offset, hex columns, and ASCII sidebar. Highlights printable characters and null bytes.

Flag

Reveal flag

picoCTF{...}

Only the multiplier matters; the rest of the function simply returns EAX after scaling.

Key takeaway

A disassembler exposes the immediate operands baked into arithmetic instructions, and those constants often carry the program's logic. Choosing between stepping into a call and stepping over it is a basic skill that scales from a small CTF binary to thousands of nested calls in malware or firmware. Magic numbers, round counts, and validation thresholds sit in nearly every compiled binary, and pulling them out is routine in vulnerability research and license-check work.

Related reading

Useful tools for Reverse Engineering

Where to go next