LearnLinux FoundationsReading Files

xxd

xxd - a hands-on Linux lab on a real virtual machine.

Crack open a binary file with xxd: a clean hex dump with an offset, byte pairs, and an ASCII gutter, then -l to read only the first bytes and -b to drop to individual bits. Practice on the intercept payload right in the terminal.

Every reading command you have met so far assumes the file is text. cat prints it, head samples the top, wc counts its lines. But some files are not text at all. They are raw bytes: numbers a program understands but a person cannot read. A byte is the basic unit of stored data, a single value from 0 to 255. Point cat at one of these and your terminal fills with beeps and scrambled symbols, because it is trying to read numbers as if they were letters.

You are on a box called the intercept station, in a folder named intercept. Inside binaries sits payload.bin, a 32-byte capture that file reports as data, meaning raw bytes with no text meaning. To look inside it safely you need a tool that shows every byte as a number instead of trying to print it as a character. That tool is xxd, and it is the one command this lesson is about.

The black boxes in this lesson are a practice terminal: a safe sandbox that only checks the one command each step teaches, so you cannot break anything. The real machine, where you read these files for yourself, comes at the end of the module.

xxd reads a file and prints it as hexadecimal, a compact way of writing each byte as two characters drawn from 0-9 and a-f. Two hex characters cover every possible byte, from 00 up to ff. Point plain xxd at the payload and see what a binary file really holds. Type this exact command and press Enter:

xxd ~/intercept/binaries/payload.bin

prompt: student@linuxcamp:~$ answer: xxd ~/intercept/binaries/payload.bin output: 00000000: 0001 0203 0405 0607 0809 0a0b 0c0d 0e0f ................ 00000010: 4142 4344 4546 4748 fffe fdfc fbfa f9f8 ABCDEFGH........ hint: Type xxd, a space, then the path to the file: xxd ~/intercept/binaries/payload.bin

Two tidy rows, every byte accounted for, and not a beep in sight. Where cat would have thrown garbage at your screen, xxd turned the same 32 bytes into a clean, readable grid. That grid has three columns with three different jobs, and the next step reads them one at a time.

Look again at a single row from that dump. Every xxd line has the same three-part shape, left to right:

00000010: 4142 4344 4546 4748 fffe fdfc fbfa f9f8  ABCDEFGH........
^^^^^^^^  ^^^^ ^^^^ ...                             ^^^^^^^^^^^^^^^^
 offset      hex bytes (grouped in pairs)             ASCII gutter

The left column, 00000010, is the offset: how many bytes from the start of the file this row begins, written in hex. The first row starts at 00000000 (the very beginning) and the second at 00000010, which is 16 in hex, because each row holds exactly 16 bytes.

The middle is the bytes themselves, in hex, grouped two bytes to a chunk. The right column, after the double space, is the ASCII gutter: for each byte that happens to be a printable character, xxd shows that character; every non-printable byte becomes a . dot.

That is why the second row reads ABCDEFGH on the right. The bytes 41 42 43 44 45 46 47 48 are the codes for the letters A through H, so xxd prints them plainly in the gutter. The bytes around them, like 00 and ff, are not printable characters, so they show as dots. The gutter is the fast way to spot readable text hiding inside a mostly binary file: your eye jumps straight to the letters.

The colon after the offset and the neat pairs of hex are not an accident. xxd was built to be the readable byte viewer: its plain output already lines up in columns, groups the bytes into tidy two-byte pairs, and never rearranges them. What you see is the file's bytes in the exact order they sit on disk, first byte first.

Prove the point on a smaller file. Next to payload.bin is magic.bin, just four bytes long. Before you run this, decide: four bytes is far less than a 16-byte row, so how many rows do you expect? Dump it:

xxd ~/intercept/binaries/magic.bin

prompt: student@linuxcamp:~$ answer: xxd ~/intercept/binaries/magic.bin output: 00000000: cafe babe .... hint: Same command, point it at magic.bin: xxd ~/intercept/binaries/magic.bin

Four bytes, ca fe ba be, read straight off the file in order: ca, then fe, then ba, then be. xxd grouped them into two clean pairs, cafe babe, and padded the rest of the row with spaces so the ASCII gutter still lines up on the right. All four bytes are non-printable, so the gutter is four dots. This left-to-right, no-surprises order is exactly what makes xxd the friendly choice for reading bytes.

There is an older tool, hexdump, that does the same job. But its default mode rearranges bytes into a confusing swapped order, so you have to run hexdump -C to get a layout as clean as plain xxd gives you for free. That contrast is the whole reason xxd exists.

A real binary file can be huge, and often you only care about the opening bytes, the part that tells you what kind of file it is. xxd can stop early. The -l flag sets a length: xxd -l <count> <file> dumps only the first <count> bytes and then stops, no matter how big the file is.

The payload is 32 bytes. Before you run this, decide: a full row holds 16 bytes, so how many rows should eight bytes fill? Ask for just the first 8:

xxd -l 8 ~/intercept/binaries/payload.bin

prompt: student@linuxcamp:~$ answer: xxd -l 8 ~/intercept/binaries/payload.bin output: 00000000: 0001 0203 0405 0607 ........ hint: Add -l and the number 8 between xxd and the path: xxd -l 8 ~/intercept/binaries/payload.bin

Exactly eight bytes, 00 through 07, then xxd stopped, even though 24 more bytes sat waiting in the file. One short row instead of two, and the ASCII gutter shrank to match. Change the 8 to any number and you change how much of the opening you get: xxd -l 4 shows the first four bytes, xxd -l 16 the first sixteen. When you only need to check a file's opening signature, -l keeps the dump short.

Hex is compact, but sometimes you need to go one level deeper, down to the individual bits: the ones and zeros that make up each byte. Every byte is eight bits, and the -b flag (short for binary) prints each byte as those eight bits instead of as two hex characters.

Before you run this, decide: which of the three columns should change, and which should stay put? Dump the magic file in binary and watch each byte spell itself out:

xxd -b ~/intercept/binaries/magic.bin

prompt: student@linuxcamp:~$ answer: xxd -b ~/intercept/binaries/magic.bin output: 00000000: 11001010 11111110 10111010 10111110 .... hint: Swap in the -b flag: xxd -b ~/intercept/binaries/magic.bin

The same four bytes you saw as cafe babe, now written as bits. ca is 11001010, fe is 11111110, ba is 10111010, and be is 10111110. The offset and the ASCII gutter are unchanged; only the middle switched from hex to binary. -b is the view you reach for when a single bit matters, like reading a hardware flag or a permission mask where each bit means something on its own.

Scaffolding off. No command is shown this time. You have every piece you need.

You want to see just the opening eight bytes of payload.bin, and nothing more. Reach for the flag that sets a length, give it the number 8, and point xxd at the payload.

prompt: student@linuxcamp:~$ answer: xxd -l 8 ~/intercept/binaries/payload.bin output: 00000000: 0001 0203 0405 0607 ........ hint: Think back to the length flag: -l sets how many bytes. Use -l 8, then the payload path.

Eight bytes, 00 through 07, exactly as ordered. You picked -l because you were capping the length, set it to 8, and aimed it at the right file, all from memory. That is the same move the real machine will ask of you: know what you want, pick the flag, type it without a prompt.

One more, still no command shown. This time the target is magic.bin, and hex is not deep enough: you need to see each of its four bytes spelled out as eight individual bits. Recall the flag that switches xxd from hex to binary and point it at the magic file.

prompt: student@linuxcamp:~$ answer: xxd -b ~/intercept/binaries/magic.bin output: 00000000: 11001010 11111110 10111010 10111110 .... hint: The binary-bits flag is -b. Put it between xxd and the path to magic.bin.

There is cafe babe again, written as thirty-two bits. You remembered that -b means binary, the view for when single bits matter, and you aimed it without a shown command. Two challenges, two clean recalls: the flags are yours now.

You earned this cheat sheet. Every row is a form of xxd you have already run:

The one idea to carry away: xxd turns a file's raw bytes into a clean, in-order grid you can actually read. Plain xxd gives you the whole file in hex, -l caps how many bytes you see, and -b drops to the level of single bits.

When you land on an unfamiliar binary file and want to know what it is, reach for xxd -l 16 <file>. Dumping just the first sixteen bytes is a quick way to read its opening signature without pouring out the whole file.

The practice terminal has shown you the shape: plain xxd for a full hex dump, -l to cap the length, and -b to drop to binary bits. The reading-files module ends with one real Linux machine, and that is where you run xxd for real.

In the reading-files capstone mission, a Linux VM boots just for you on this same intercept filesystem, payload.bin and magic.bin included. One of its objectives asks you to hex-dump those bytes with xxd, and any form you learned here counts: plain xxd, -l, or -b. One difference from here: the mission shows no commands. You read the objective, recall the flag, and type it. That recall is what makes it stick.

Finish the other reading-files lessons, then go crack open the binaries for real.

Practice xxd in a real Linux terminal at The Linux Camp. Progress is verified automatically as you type commands on the machine.