Description
What does asm1(0x345) return? Trace through the provided x86 assembly. Submit the flag as a hexadecimal value.
Setup
Download the assembly file.
wget <url>/test.SSolution
Want to try it yourself first?
The guided walkthrough reveals hints one step at a time.
Step 1Read the assembly and set up the stack frame
ObservationThe challenge gives a raw .S assembly file and asks what the function returns for a given input. Step one is reading it to find where the argument 0x345 lives, which in 32-bit cdecl means on the stack.Open test.S. The function asm1 takes one argument (0x345 = 837). Trace through it manually: set up the stack frame in your head (or on paper), tracking the value of eax and other registers at each instruction.bashcat test.SWhat didn't work first
Tried: Running 'objdump -d test.S' to disassemble and read the function flow.
objdump -d disassembles compiled binaries, not raw .S source. Point it at an uncompiled .S file and you get nothing useful. Just open the file with cat or an editor: AT&T-syntax assembly is already readable.
Tried: Assuming the first argument is in eax or edi (System V AMD64 convention) rather than on the stack.
This is 32-bit x86 cdecl, not the 64-bit System V ABI. In cdecl the arguments are pushed on the stack before the call, so the first one sits at [ebp+8] after the standard prologue. Reading edi or eax instead gives garbage.
Learn more
In x86 calling convention (cdecl), arguments are pushed on the stack right-to-left. The first argument is at
[ebp+8]after the function prologue (push ebp; mov ebp, esp). The return value is ineaxwhen the function returns.Key x86 instructions:
cmp a, bsets flags based on a - b.jgjumps if greater (signed).jljumps if less.jejumps if equal.addandsubmodify a register.movcopies a value.Step 2Trace the function logic
ObservationThe function is a chain of cmp instructions against fixed values followed by conditional jumps (jg, jl, je). Following the one branch that 0x345 actually takes tells you what eax holds at ret.Start with the argument value 0x345 in [ebp+8]. Follow each branch condition (compare the argument to hardcoded values) to determine which branch is taken. Track the final value loaded into eax before ret.Learn more
Compile the assembly and run it to verify your answer:
gcc -m32 -o test test.S -no-pie && python3 -c "import ctypes; lib=ctypes.CDLL('./test'); print(hex(lib.asm1(0x345)))". This is often faster than manual tracing for complex functions.Step 3Submit the return value as the flag
ObservationThe problem statement asks for the result in hexadecimal, so the eax value from the trace is the answer, formatted as picoCTF{0x...}.The return value in hex is the flag. Wrap it in picoCTF{...} if required, or submit it directly as a hex number.Learn more
Reading x86 assembly is a fundamental reversing skill. Automated tools like Ghidra and IDA Pro decompile assembly to C-like pseudocode, but understanding the raw assembly allows you to verify and correct the decompiler output.
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{0x348}
asm1(0x345): the argument 0x345 is compared against 0x37a, found smaller, so the function adds 3 and returns 0x348.