GDB baby step 3 picoGym Exclusive Solution

Published: March 5, 2024

Description

Debugger0_c stores 0x2262c96b on the stack. Examine the bytes exactly as they appear in memory and wrap them (in order) inside picoCTF{...}.

Memory inspectionDownload debugger0_c

Launch the binary under gdb and switch to layout asm to see where the MOV that stores 0x2262c96b executes.

Set a breakpoint right afterward (main+25) so the stack contents can be inspected before anything is overwritten.

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

Solution

Want to try it yourself first?

The guided walkthrough reveals hints one step at a time.

Walk me through it
  1. Step 1
    Break after the store
    Observation
    I noticed the challenge says 0x2262c96b is stored on the stack by a MOV instruction, which suggested I needed to halt execution immediately after that store so the bytes are in place and not yet overwritten by subsequent instructions.
    Place b *(main+25) so execution halts immediately after the constant is written to the stack. Run the program to hit that breakpoint.
    bash
    b *(main+25)
    bash
    run
    What didn't work first

    Tried: Breaking at main instead of main+25 to inspect the stack slot early.

    At main+0 the stack frame has not been set up yet and the MOV that writes 0x2262c96b has not executed, so rbp-4 holds whatever garbage was already there. The breakpoint must be placed after main+25, the instruction immediately following the store, so the four bytes are actually in place when you examine them.

    Tried: Using 'break main' and then stepping through with 'next' until reaching the store instruction.

    Single-stepping through a function with source-level 'next' can skip over individual assembly instructions because GDB maps multiple instructions to a single source line. If the store happens inside a line that 'next' completes in one step, you may land past it and see a later value. Using 'b *(main+25)' targets the exact assembly offset and guarantees you stop at the right instruction boundary.

    Learn more

    This challenge introduces memory inspection: reading raw bytes from a specific address rather than just reading a register value. After the MOV instruction writes 0x2262c96b to [rbp-0x4], the four bytes are stored in RAM at that stack address. Breaking immediately after the store lets you inspect those bytes before anything overwrites them.

    The key concept is that the CPU stores multi-byte integers in little-endian byte order on x86. Little-endian means the least significant byte is stored at the lowest memory address. So the 32-bit value 0x2262c96b is stored in memory as the byte sequence 6b c9 62 22(lowest byte first). When the challenge asks you to report the bytes "in order," it means in the order they appear in memory - which is the reversed byte order compared to how you read the constant in the assembly listing.

    Understanding endianness is critical in networking (network byte order is big-endian), binary exploitation (shellcode must account for byte order when encoding addresses), and file format parsing (file formats vary by endianness - JPEG is big-endian, most x86 executables are little-endian). Getting endianness wrong is one of the most common sources of bugs in low-level code.

  2. Step 2
    Dump four bytes from the stack
    Observation
    I noticed the flag description asks for the bytes 'in order' as they appear in memory, which suggested using GDB's x/4xb command to read each byte individually from the [rbp-0x4] stack slot and reveal the little-endian byte sequence rather than reading the constant as written in the assembly listing.
    Use x/4xb $rbp-4 to view the byte order as stored in memory. The address $rbp-4 matches the [rbp-0x4] stack slot where the assembly stored the constant; the little-endian byte order means the bytes appear reversed compared to reading 0x2262c96b left-to-right. Concatenate those bytes, preserving order, to form the hexadecimal inside the flag.
    bash
    x/4xb $rbp-4

    Expected output

    0x...: 0x6b 0xc9 0x62 0x22
    What didn't work first

    Tried: Reading the flag bytes directly from the constant 0x2262c96b as printed in the assembly listing, giving '2262c96b'.

    The assembly listing shows the value in logical (big-endian) notation, but x86 stores it in little-endian order so the bytes appear reversed in memory. The correct sequence to concatenate is the one x/4xb prints - '6b c9 62 22' - not the left-to-right reading of the immediate. Submitting '2262c96b' is the most common wrong answer for this challenge.

    Tried: Using x/1xw $rbp-4 to print the whole 32-bit word at once instead of examining individual bytes.

    GDB's word-sized examine command (x/1xw) prints the value as a 32-bit integer and re-applies the little-endian-to-host conversion, showing '0x2262c96b' - the same number you already saw in the assembly. This hides the byte-level reversal rather than revealing it. You need x/4xb to see each byte individually in the order it sits in memory.

    Learn more

    The GDB examine command (x) reads memory at an arbitrary address and displays it in a chosen format. The syntax x/4xb $rbp-4 means: examine 4 units, formatted as hex, each unit being one byte, starting at the address $rbp - 4. GDB prints each byte individually, separated by spaces, which makes it easy to read the byte sequence in memory order.

    To build the flag, concatenate the four bytes exactly as GDB prints them. Because x86 is little-endian, the byte GDB prints first is the least-significant byte of the original 32-bit value - so the output will appear "reversed" compared to 0x2262c96b. The challenge is testing whether you understand this reversal and report the bytes in memory order rather than the logical order.

    The x command is invaluable in real debugging and exploitation work. When inspecting a buffer overflow, you might use x/32xb $rsp to view 32 bytes of stack data. When verifying a shellcode injection, x/10i $rip disassembles ten instructions starting at the instruction pointer. Learning the format specifiers (x=hex, d=decimal, s=string, i=instruction, b=byte, h=halfword, w=word, g=giant/8-byte) is worth the small investment.

Flag

Reveal flag

picoCTF{0x6b......22}

Your exact byte sequence may differ; always copy the four bytes exactly as x/4xb prints them.

Key takeaway

x86 stores multi-byte integers in little-endian order, meaning the least significant byte sits at the lowest memory address. This means a 32-bit constant like 0x2262c96b appears in memory as 6b c9 62 22, the reverse of how you read it in assembly. GDB's examine command (x/Nxb) lets you inspect raw bytes at any address, a skill that transfers directly to buffer overflow analysis, shellcode verification, and firmware forensics.

Related reading

Want more picoGym Exclusive writeups?

Useful tools for Reverse Engineering

What to try next