Description
The asciiftw binary constructs the flag byte-by-byte using movb instructions. Extract those literals from the disassembly and convert them from hex to ASCII.
Setup
Download the PIE binary, mark it executable, and run objdump -d to disassemble main.
Filter for movb instructions-each contains the next byte of the flag.
wget https://artifacts.picoctf.net/c/508/asciiftwchmod +x asciiftwobjdump -d asciiftw | grep movbSolution
Want to try it yourself first?
The guided walkthrough reveals hints one step at a time.
Step 1
Collect the bytesObservationI noticed the description states the flag is constructed byte-by-byte using movb instructions, which suggested filtering the objdump disassembly for those instructions to extract each immediate hex operand in sequence.Pipe objdump's output through grep/cut to isolate the immediate operand passed to movb. Concatenate them into a single hex string.bashobjdump -d asciiftw | grep movb | grep -oE '0x.*, ' | tr -d ',\\n'What didn't work first
Tried: Run 'strings asciiftw' to find the flag directly in the binary.
strings only extracts contiguous sequences of printable characters from the file. Because the flag is never stored as a single sequence - it is assembled one byte at a time via movb instructions - strings prints no useful output and the flag does not appear. The disassembly approach is needed because the bytes are scattered across individual instructions, not laid out contiguously in a data section.
Tried: Use 'grep movb' alone without the second grep -oE filter to extract the operands.
Without the -oE regex filter, grep prints the entire disassembly line for each movb instruction, which includes the instruction address, opcode bytes, and surrounding register operands. The hex immediates are mixed in with unrelated content and cannot be fed directly into xxd. The -oE '0x.*, ' step is needed to isolate just the immediate hex value from each line.
Learn more
objdump is a command-line utility that displays information about object files and executables. The
-dflag disassembles executable sections, converting the raw machine-code bytes back into human-readable assembly mnemonics. This is one of the most fundamental tools in binary reverse engineering.The movb instruction (move byte) copies a single 8-bit value into a memory location or register. When a program constructs a string or flag at runtime by writing one character at a time, each character appears as an immediate operand - a literal hex value embedded directly in the instruction. Filtering the disassembly for
movblines reveals every byte the program is about to write.The pipeline here combines several Unix tools:
grep -oEextracts only the matching portion of each line using a regex, andtr -ddeletes unwanted characters (commas and newlines) to produce a clean hex stream. This kind of shell pipeline - chaining small, focused tools - is a core skill in CTF forensics and everyday systems work.Step 2
Convert from hexObservationI noticed the collected movb operands are hex literals such as 0x70 and 0x69, which are standard ASCII code points, and that suggested piping the concatenated hex stream through xxd -r -p to recover the original ASCII characters.Send the aggregated hex into xxd -p -r to turn it back into ASCII text.bashobjdump -d asciiftw | grep movb | grep -oE '0x.*, ' | xxd -r -pExpected output
picoCTF{ASCII_IS_EASY_8960...}What didn't work first
Tried: Pipe the hex output into 'xxd -r' without the -p flag.
Without -p, xxd expects its input in the traditional formatted hex dump layout (address, hex columns, ASCII sidebar). The raw hex stream produced by grep -oE does not match that format, so xxd -r alone produces garbled or empty output. The -p flag tells xxd to accept plain continuous hex digits with no extra formatting.
Tried: Use Python's bytes.fromhex() directly on the grep output including the '0x' prefix and commas.
bytes.fromhex() expects a clean string of hex digits with no prefix or separators. Passing '0x41, 0x42' raises a ValueError because of the '0x' prefix and the comma. The grep pipeline must strip those characters first (via tr -d or sed) before the hex string is valid input for fromhex().
Learn more
xxd is a hex dump utility that can both produce and consume hexadecimal representations of binary data. When run with
-r(reverse) and-p(plain hex without formatting), it reads a stream of hex digits and outputs the corresponding raw bytes - effectively decoding hex back into ASCII text.The reason this works is that every printable ASCII character has a well-defined hex equivalent (for example, 'A' is
0x41, and 'p' is0x70). Themovbimmediates are just these values stored directly in the binary. By collecting and decoding them in order, you reconstruct the string the program would have placed in memory at runtime.This technique generalizes broadly: any binary that builds a sensitive string (key, flag, password) byte-by-byte through immediate stores is vulnerable to static extraction via disassembly. Real-world malware analysts use the same method to extract hardcoded C2 domains or decryption keys from obfuscated samples.
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.
- Base64 & Base32 DecoderDecode Base64 and Base32 strings with auto-detection. Multi-layer mode unwraps nested encodings automatically.
Flag
Reveal flag
picoCTF{ASCII_IS_EASY_8960...}
Any pipeline that extracts the movb immediates and feeds them into a hex→ASCII converter produces the same flag.