asm2 picoCTF 2019 Solution

Published: April 2, 2026

Description

What does asm2(0x6, 0x24) return? Trace through the x86 assembly with loop logic. Submit the flag as a hexadecimal value.

Download the assembly file.

bash
wget <url>/test.S

Solution

Want to try it yourself first?

The guided walkthrough reveals hints one step at a time.

Walk me through it
  1. Step 1
    Read the assembly
    Observation
    I noticed the challenge provides a .S source file and calls asm2(0x6, 0x24), which suggested reading the raw assembly text to identify the function's structure, argument offsets, and loop boundaries before attempting any simulation.
    Open test.S. The function asm2 is called as asm2(0x6, 0x24): the first argument 0x6 is at [ebp+8] and the second 0x24 is at [ebp+12]. It contains a loop. Identify the loop variable, the loop condition, the loop body operations, and the exit value. Note that the step constant and loop bound are instance-specific, so verify them in your own test.S.
    bash
    cat test.S
    What didn't work first

    Tried: Assume [ebp+8] is the second argument and [ebp+12] is the first argument, reversing the parameter order.

    x86 cdecl calling convention pushes arguments right-to-left, so the first argument lands closest to the return address at [ebp+8] and the second at [ebp+12]. Swapping them gives a wrong initial accumulator and counter, causing the loop to run a different number of iterations and produce an incorrect return value.

    Tried: Use objdump or ndisasm to disassemble test.S directly instead of reading it as source.

    test.S is a raw AT&T or Intel syntax source file, not an object file or binary. Running objdump -d on it or piping it to ndisasm treats the ASCII text bytes as machine code and produces nonsense output. The correct approach is to read it with cat or a text editor since it is already human-readable assembly source.

    Learn more

    Assembly loops use conditional jumps to backward labels. A typical loop structure: initialize a counter, compare it to a limit, do the body, increment the counter, and jump back if the condition still holds.

    The two arguments are stored at fixed offsets from ebp: first argument at [ebp+8], second at [ebp+12]. Local variables are at negative offsets: [ebp-4], [ebp-8], etc.

  2. Step 2
    Simulate the loop on paper
    Observation
    I noticed the assembly contains a loop with a counter and a condition, which suggested manually (or programmatically) stepping through each iteration with the given inputs 0x6 and 0x24 to track eax until the exit condition is met.
    Initialize the local variables with the given arguments. Step through each iteration of the loop, tracking register and memory values, until the exit condition is met. Note the value in eax at the ret instruction.
    Learn more

    Alternatively, translate the assembly to Python for rapid simulation: replace each assembly instruction with equivalent Python operations and print intermediate values to verify.

  3. Step 3
    Submit the return value
    Observation
    I noticed the challenge asks for the return value of asm2(0x6, 0x24) and that asm2 stores its result in eax at the ret instruction, which suggested the final eax value from the simulation is the answer to submit in hex.
    The return value in hex (preceded by 0x) is the answer. Check if it should be wrapped in picoCTF{...}.
    Learn more

    Understanding loop patterns in assembly is essential for reversing cryptographic algorithms, where loops process data byte-by-byte or block-by-block. Recognizing the loop structure quickly is more valuable than tracing every iteration manually.

Interactive tools
  • 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.
  • Number Base ConverterConvert numbers between binary, octal, decimal, and hexadecimal instantly. Enter any value and see all four bases update in real time.

Flag

Reveal flag

picoCTF{0x63}

asm2(0x6, 0x24): the loop adds 0xf each iteration until the counter exceeds arg2 (0x24), and the function returns 0x63.

Key takeaway

Loops in assembly are just backward conditional jumps: a compare instruction checks a counter or accumulator against a bound, and the branch either continues the loop body or falls through to the exit. Recognizing this pattern immediately reduces a complex sequence of instructions to a simple 'what does this loop compute' question, which is far more tractable than tracing every individual instruction. Cryptographic primitives, compression routines, and protocol parsers all rely on the same loop skeletons in compiled binaries, so fluency with assembly loops is directly transferable to real-world reversing and vulnerability research.

Related reading

Want more picoCTF 2019 writeups?

Useful tools for Reverse Engineering

What to try next