LearnLinux FoundationsReading Files

hexdump

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

Read any file one byte at a time with hexdump. Learn the canonical -C layout (offset, 16 hex bytes, and an ASCII gutter), then meet the byte-swapped default form so its scrambled output never fools you. Practice on the intercept binaries right in the terminal.

Every reading command you have met so far assumes a file is made of text: letters, digits, and punctuation you can read. Point one at the wrong kind of file and you get gibberish, because the file was never text to begin with. It is raw bytes: pure numbers a program understands, with no readable meaning for a person.

A byte is the smallest unit of stored data, a single number from 0 to 255. Text files are just bytes that happen to line up with printable characters. Plenty of files are not: a photo, a program, a saved game. To look inside those you need a tool that ignores the idea of "characters" and shows you every byte as a number. That tool is hexdump.

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. It replays the real captured output here. At the end of the module you launch a live Linux machine and run hexdump for real on these same files.

You are in the binaries folder of a captured intercept, and inside it sits payload.bin: 32 bytes of raw data, no text at all. hexdump shows bytes in hex (short for hexadecimal, the base-16 number system that writes each byte as two compact digits 00 to ff). Its clearest format is the -C flag, called canonical. Type this exact command and press Enter:

hexdump -C payload.bin

prompt: student@linuxcamp:~/intercept/binaries$ answer: hexdump -C payload.bin output: 00000000 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f |................| 00000010 41 42 43 44 45 46 47 48 ff fe fd fc fb fa f9 f8 |ABCDEFGH........| 00000020 hint: Type hexdump, a space, then -C for canonical, then the file: hexdump -C payload.bin

There it is: 32 bytes laid out as numbers instead of characters. It looks dense, but it is only three columns, and the next few steps take them one at a time. The -C you added is what makes it this readable; the very same command without it prints something much stranger, which you will see later in this lesson. For now, -C is the form to know.

Look only at the far-left column of the dump you just ran: 00000000 on the first row, 00000010 on the second. That is the offset: how many bytes from the start of the file this row begins, written in hex.

The first row starts at offset 00000000, the very beginning. Each -C row holds exactly 16 bytes, so the second row starts 16 bytes in. Sixteen in hex is 10, which is why the second offset reads 00000010 and not 00000016. The offset is your ruler: it tells you where in the file any byte lives, so you can say "the trouble is at offset 10" and mean an exact spot.

Hex offsets climb by 10 per row, not by 16, because 10 in hex already means sixteen. If a third row existed it would start at 00000020, which is thirty-two in plain counting: exactly two full rows of 16 bytes behind it.

The wide middle column is the heart of the dump: the actual bytes, each written as a two-digit hex number. Count the pairs on the first row and there are sixteen of them, 00 through 0f, one per byte.

There is one detail people miss. hexdump -C splits the sixteen bytes into two groups of eight with a wider gap in the middle. So 00 01 02 03 04 05 06 07 sits a little apart from 08 09 0a 0b 0c 0d 0e 0f. That extra space is a reading aid, nothing more; it just helps your eye find the eighth byte without counting from one. The bytes run in file order, left to right, top to bottom.

Those bytes climb 00 01 02 ... 0e 0f because payload.bin was built as a neat counting sequence, perfect for learning the layout. A real binary file would show a jumble of values here, but the format is identical: sixteen hex bytes a row, two groups of eight, in the order they appear on disk.

The part between the two | bars on the right is the ASCII gutter. ASCII is the standard table that pairs each byte value with a character: byte 41 is A, byte 42 is B, and so on. The gutter shows what each byte would look like if it were read as text, so you can spot readable strings hiding inside binary data at a glance.

Compare the two rows in the dump you ran. The first row is bytes 00 through 0f, none of which map to a printable character, so the gutter is all dots: |................|. The second row opens with 41 42 43 44 45 46 47 48, and the gutter shows ABCDEFGH, because those byte values are exactly the codes for the letters A through H.

A dot in the gutter does not mean the byte is a literal period. It means "this byte is not a printable character," a placeholder so the columns stay aligned. The bytes ff fe fd fc at the end of row two are not text, so they show as dots too, even though they are perfectly real bytes in the file.

The other file in binaries is magic.bin, and it holds just 4 bytes. Dumping it shows what a partial row looks like, and it makes one more piece of the layout obvious. Run:

hexdump -C magic.bin

prompt: student@linuxcamp:~/intercept/binaries$ answer: hexdump -C magic.bin output: 00000000 ca fe ba be |....| 00000004 hint: Same command as before, pointed at the other file: hexdump -C magic.bin

Four bytes, ca fe ba be, then a long stretch of blank space where the other twelve bytes of a full row would go. hexdump pads the row so the gutter still lines up. None of those four values is a printable character, so the gutter is four dots.

Now look at the last line: 00000004. Every hexdump ends with a final offset line, and it is simply the total size of the file: four bytes here, so the file ends at offset 00000004. In the earlier dump it read 00000020, which is thirty-two, the exact size of payload.bin. That closing offset is not a byte in the file; it is hexdump telling you where the file stops.

Here is the surprise the first step promised. Everything so far used -C. Drop it, run plain hexdump payload.bin, and the same 32 bytes come back looking scrambled. This is the single most confusing thing about the command, and seeing it once is how you never get caught by it:

hexdump payload.bin

prompt: student@linuxcamp:~/intercept/binaries$ answer: hexdump payload.bin output: 0000000 0100 0302 0504 0706 0908 0b0a 0d0c 0f0e 0000010 4241 4443 4645 4847 feff fcfd fafb f8f9 0000020 hint: This is the default form: just hexdump and the file, no -C: hexdump payload.bin

The bytes did not change. Their order did. Without -C, hexdump groups the bytes into 2-byte pairs and prints each pair with its two bytes swapped. Look at the very first group: the file starts with byte 00 then byte 01, but the dump shows 0100. The 01 and the 00 traded places. Every pair does the same: 03 02 becomes 0302, and 41 42 (which was AB) becomes 4241.

This is not a bug. On common Intel and AMD processors the machine stores 2-byte numbers with the low byte first, and the default hexdump format shows them the machine's way. It is almost never what you want to read, and it fools nearly everyone the first time. The cure is one flag.

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

You want to see the 4 bytes of magic.bin in the clean, readable layout: one byte per hex pair, in true file order, offset on the left and ASCII gutter on the right. That means the canonical form, not the scrambled default. Pick the flag that turns it on and point hexdump at the file.

prompt: student@linuxcamp:~/intercept/binaries$ answer: hexdump -C magic.bin output: 00000000 ca fe ba be |....| 00000004 hint: The readable layout is the canonical one. Use the -C flag with the file name: hexdump -C magic.bin

ca fe ba be, in true order, one byte per pair, the way you actually want to read bytes. You reached for -C from memory because you now know the default form swaps them. That is the whole habit this lesson is trying to build: when you dump a file, add -C, every time.

One more, still no command shown.

The intercept team suspects payload.bin has readable text buried among its raw bytes. Dump the file in the layout that puts an ASCII gutter on the right, then read the gutter to find the hidden letters and note the offset of the row they sit in.

prompt: student@linuxcamp:~/intercept/binaries$ answer: hexdump -C payload.bin output: 00000000 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f |................| 00000010 41 42 43 44 45 46 47 48 ff fe fd fc fb fa f9 f8 |ABCDEFGH........| 00000020 hint: The layout with the ASCII gutter is the canonical one: hexdump -C payload.bin

There it is in the gutter of the second row: ABCDEFGH, sitting at offset 00000010. The bytes 41 through 48 are the ASCII codes for those letters, and the gutter turned them back into text for you. That is the real working use of hexdump -C: scanning the right-hand column for strings hiding inside a file that is otherwise pure numbers.

You earned this cheat sheet. Every row is a form of hexdump you just ran on the binaries files:

Reading one -C row is three columns. The left column is the offset: how far into the file, in hex. The middle is the 16 bytes as hex, split into two groups of eight. The right, between the | bars, is the ASCII gutter: each byte as a character, a dot for anything not printable. The final offset line on its own is just the file's total size.

The one habit to carry away: reach for hexdump -C, not bare hexdump. The default form byte-swaps every pair and will have you reading 0100 when the file actually starts with 00 01. Add the -C and the bytes read in true order.

There is a close cousin worth a single line: xxd does the same job as hexdump -C with an even cleaner default and no byte-swapping ever. You meet xxd in its own lesson; hexdump -C is the one this lesson makes automatic.

The practice terminal has shown you the shape: hexdump -C for a readable, byte-in-order dump, and plain hexdump so the byte-swapped default can never surprise you. You typed both yourself and watched the same 32 bytes appear two very different ways.

The Reading Files module ends with one real Linux machine, booted just for you on this same binaries folder. Its objectives use exactly what you just ran: hexdump -C to read a file byte by byte, the offset to name an exact spot, the ASCII gutter to catch hidden text. 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 read the raw bytes for real.

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