Somewhere in every developer's career, they stare at a wall of hex and think: "What am I looking at?"
Hex dumps are the raw truth of computing. Beneath every file, network packet, and memory address is a sequence of bytes — and hex dumps are how you see them. Whether you're debugging a corrupted file, analyzing a network capture, or reverse-engineering a binary protocol, reading hex dumps is a skill that pays off over and over.
This guide teaches you to read hex dumps fluently. You'll learn the format, master the command-line tools, identify files by their magic bytes, and write code that parses hex data programmatically.
What Is a Hex Dump?
A hex dump is a representation of binary data where each byte is shown as a two-digit hexadecimal number. Instead of showing you the raw binary (which would be unreadable streams of 1s and 0s), hex dumps present data in a compact, human-scannable format.
Every byte in a file is a value from 0 to 255 — or in hex, 00 to FF. A hex dump lines these up in rows, usually 16 bytes per line, with the ASCII interpretation shown alongside.
Anatomy of a Hex Dump
Here's a typical hex dump of a simple text file containing "Hello, World!\n":
00000000: 4865 6c6c 6f2c 2057 6f72 6c64 210a Hello, World!.
Let's break down the three parts:
1. Offset (Address)
00000000: — The byte offset from the start of the file. This tells you where you are. The first byte is at offset 0, the 16th byte at offset 10 (hex for 16), and so on.
Offsets are always in hexadecimal. So 00000010 means byte 16, 00000020 means byte 32, and 000000FF means byte 255.
2. Hex Bytes
4865 6c6c 6f2c 2057 6f72 6c64 210a — The actual byte values in hexadecimal, grouped in pairs or quads depending on the tool. Each pair is one byte:
| Hex | Decimal | ASCII |
|---|---|---|
| 48 | 72 | H |
| 65 | 101 | e |
| 6c | 108 | l |
| 6c | 108 | l |
| 6f | 111 | o |
| 2c | 44 | , |
| 20 | 32 | (space) |
| 57 | 87 | W |
| 6f | 111 | o |
| 72 | 114 | r |
| 6c | 108 | l |
| 64 | 100 | d |
| 21 | 33 | ! |
| 0a | 10 | (newline) |
You can paste these hex values into hextoascii.co and instantly see the decoded text.
3. ASCII Representation
Hello, World!. — The printable ASCII interpretation of the bytes. Non-printable characters (control codes, bytes above 126) are shown as . (dot). This column is your quick visual reference — it's what makes hex dumps readable at a glance.
Command-Line Tools for Hex Dumps
xxd — The Standard
xxd ships with Vim and is available on virtually every Unix system. It's the go-to tool.
# Basic hex dump
xxd file.bin
# Output:
# 00000000: 8950 4e47 0d0a 1a0a 0000 000d 4948 4452 .PNG........IHDR
# Limit to first 64 bytes
xxd -l 64 file.bin
# Show in plain hex (no ASCII column)
xxd -p file.bin
# Set columns per line (default 16)
xxd -c 32 file.bin
# Reverse: convert hex dump back to binary
xxd -r hexdump.txt > restored.bin
# Seek to offset (skip first 100 bytes)
xxd -s 100 file.bin
The -r flag is gold. It means you can edit a hex dump in a text editor and convert it back to binary — poor man's hex editor.
hexdump — The Flexible One
hexdump (or hd) offers more formatting control:
# Canonical format (most readable, same as `hd`)
hexdump -C file.bin
# Output:
# 00000000 89 50 4e 47 0d 0a 1a 0a 00 00 00 0d 49 48 44 52 |.PNG........IHDR|
# First 32 bytes only
hexdump -C -n 32 file.bin
# Skip first 1024 bytes
hexdump -C -s 1024 file.bin
# Custom format: offset + hex bytes
hexdump -e '"%08.8_ax: " 16/1 "%02x " "\n"' file.bin
# Just the hex, no formatting
hexdump -v -e '/1 "%02x "' file.bin
od — The POSIX Classic
od (octal dump) is POSIX standard — available everywhere, even minimal systems:
# Hex output with ASCII
od -A x -t x1z -v file.bin
# Output:
# 000000 89 50 4e 47 0d 0a 1a 0a 00 00 00 0d 49 48 44 52 >.PNG........IHDR<
# Two-byte hex words
od -A x -t x2 file.bin
# First 48 bytes
od -A x -t x1z -N 48 file.bin
Quick Comparison
| Tool | Best For | Reverse? | Everywhere? |
|---|---|---|---|
xxd |
General use, binary editing | ✅ -r flag |
Ships with Vim |
hexdump |
Custom formatting | ❌ | Most Unix |
od |
POSIX compliance | ❌ | POSIX standard |
Reading a Hex Dump: Step-by-Step Walkthrough
Let's read a real hex dump. Here's the first 96 bytes of a PNG image:
00000000: 8950 4e47 0d0a 1a0a 0000 000d 4948 4452 .PNG........IHDR
00000010: 0000 0100 0000 0100 0802 0000 00d3 107e ...............~
00000020: 8f00 0000 0173 5247 4200 aece 1ce9 0000 .....sRGB.......
00000030: 0004 6741 4d41 0000 b18f 0bfc 6105 0000 ..gAMA......a...
00000040: 0009 7048 5973 0000 0e74 0000 0e74 01de ..pHYs...t...t..
00000050: 661f 7800 0020 0049 4441 5478 5ced ddb7 f.x.. .IDATx\...
Line 1 (offset 0x00):
89 50 4E 47→ The PNG signature! (89followed byPNGin ASCII)0D 0A 1A 0A→ DOS/Unix line ending test bytes (part of the PNG spec)00 00 00 0D→ Chunk length: 13 bytes49 48 44 52→ Chunk type:IHDR(image header)
Line 2 (offset 0x10):
00 00 01 00→ Image width: 256 pixels00 00 01 00→ Image height: 256 pixels08→ Bit depth: 802→ Color type: 2 (truecolor RGB)
Line 3 (offset 0x20):
73 52 47 42→ Chunk type:sRGB(color space)
See the pattern? Once you know the file format, hex dumps become a conversation with the binary data. The offset tells you where to look, the hex tells you what's there, and the ASCII column gives you quick landmarks.
Common File Signatures (Magic Bytes)
Every file format starts with specific bytes — called magic bytes or file signatures. These let you identify a file regardless of its extension.
| File Type | Magic Bytes (Hex) | ASCII | Offset |
|---|---|---|---|
| PNG | 89 50 4E 47 0D 0A 1A 0A |
.PNG.... |
0 |
| JPEG | FF D8 FF |
ÿØÿ |
0 |
| GIF87a | 47 49 46 38 37 61 |
GIF87a |
0 |
| GIF89a | 47 49 46 38 39 61 |
GIF89a |
0 |
25 50 44 46 2D |
%PDF- |
0 | |
| ZIP | 50 4B 03 04 |
PK.. |
0 |
| GZIP | 1F 8B |
.. |
0 |
| ELF (Linux binary) | 7F 45 4C 46 |
.ELF |
0 |
| Mach-O (macOS) | FE ED FA CE |
þíúÎ |
0 |
| PE (Windows .exe) | 4D 5A |
MZ |
0 |
| SQLite | 53 51 4C 69 74 65 |
SQLite |
0 |
| MP3 (ID3) | 49 44 33 |
ID3 |
0 |
| MP4 | 66 74 79 70 |
ftyp |
4 |
| WASM | 00 61 73 6D |
.asm |
0 |
Quick File Identification
# Check the first 8 bytes of a mystery file
xxd -l 8 mystery_file
# Output: 89504e47 0d0a1a0a → It's a PNG!
# The `file` command does this automatically
file mystery_file
# mystery_file: PNG image data, 256 x 256, 8-bit/color RGB
This is incredibly useful when dealing with files that have wrong extensions, corrupted downloads, or data extracted from network captures. Knowing magic bytes lets you identify content without trusting metadata.
Converting Hex Dumps Programmatically
Python
# Read a file as hex dump
def hex_dump(filepath, bytes_per_line=16):
with open(filepath, 'rb') as f:
offset = 0
while chunk := f.read(bytes_per_line):
hex_part = ' '.join(f'{b:02x}' for b in chunk)
ascii_part = ''.join(
chr(b) if 32 <= b < 127 else '.' for b in chunk
)
print(f'{offset:08x}: {hex_part:<{bytes_per_line*3}} {ascii_part}')
offset += len(chunk)
hex_dump('example.bin')
# Convert hex string to bytes
hex_str = '48656c6c6f20576f726c64'
data = bytes.fromhex(hex_str)
print(data.decode('utf-8')) # "Hello World"
# Convert bytes to hex string
text = 'Hello World'
hex_output = text.encode('utf-8').hex()
print(hex_output) # "48656c6c6f20576f726c64"
# Parse a hex dump back to binary
import re
def parse_hex_dump(dump_text):
"""Extract raw bytes from a hex dump string."""
result = bytearray()
for line in dump_text.strip().split('\n'):
# Remove offset and ASCII columns, keep hex bytes
match = re.match(r'[0-9a-f]+:\s+((?:[0-9a-f]{2}\s*)+)', line, re.I)
if match:
hex_bytes = match.group(1).strip()
result.extend(bytes.fromhex(hex_bytes.replace(' ', '')))
return bytes(result)
# Identify file type by magic bytes
SIGNATURES = {
b'\x89PNG': 'PNG image',
b'\xff\xd8\xff': 'JPEG image',
b'%PDF': 'PDF document',
b'PK\x03\x04': 'ZIP archive',
b'\x7fELF': 'ELF binary',
b'MZ': 'Windows executable',
b'SQLite': 'SQLite database',
}
def identify_file(filepath):
with open(filepath, 'rb') as f:
header = f.read(8)
for sig, name in SIGNATURES.items():
if header.startswith(sig):
return name
return 'Unknown'
print(identify_file('photo.png')) # "PNG image"
JavaScript (Node.js)
const fs = require('fs');
// Generate a hex dump
function hexDump(buffer, bytesPerLine = 16) {
const lines = [];
for (let i = 0; i < buffer.length; i += bytesPerLine) {
const slice = buffer.slice(i, i + bytesPerLine);
const offset = i.toString(16).padStart(8, '0');
const hex = [...slice]
.map(b => b.toString(16).padStart(2, '0'))
.join(' ');
const ascii = [...slice]
.map(b => (b >= 32 && b < 127) ? String.fromCharCode(b) : '.')
.join('');
lines.push(`${offset}: ${hex.padEnd(bytesPerLine * 3)} ${ascii}`);
}
return lines.join('\n');
}
const buf = fs.readFileSync('example.bin');
console.log(hexDump(buf));
// Hex string to text
function hexToText(hex) {
const clean = hex.replace(/\s+/g, '');
const bytes = clean.match(/.{2}/g).map(b => parseInt(b, 16));
return Buffer.from(bytes).toString('utf-8');
}
console.log(hexToText('48 65 6c 6c 6f')); // "Hello"
// Text to hex string
function textToHex(text) {
return Buffer.from(text, 'utf-8')
.toString('hex')
.match(/.{2}/g)
.join(' ');
}
console.log(textToHex('Hello')); // "48 65 6c 6c 6f"
Bash One-Liners
# Hex string to ASCII text
echo "48656c6c6f" | xxd -r -p
# Output: Hello
# ASCII text to hex string
echo -n "Hello" | xxd -p
# Output: 48656c6c6f
# Compare two binary files at hex level
diff <(xxd file1.bin) <(xxd file2.bin)
# Extract bytes at specific offset (bytes 4-7 of a file)
dd if=file.bin bs=1 skip=4 count=4 2>/dev/null | xxd -p
# Find a hex pattern in a file
xxd file.bin | grep "504b 0304"
# Patch a single byte at offset 0x10 to value 0xFF
printf '\xff' | dd of=file.bin bs=1 seek=16 conv=notrunc
Real-World Use Cases
Debugging Network Packets
When a REST API returns garbled data or a WebSocket connection drops, hex-dumping the raw bytes reveals the truth. Tools like Wireshark show hex dumps of every packet. You can identify:
- Malformed headers
- Encoding mismatches (UTF-8 vs Latin-1)
- Unexpected null bytes in payloads
- Protocol-level framing errors
If you capture raw bytes, paste them into hextoascii.co for quick visual decoding.
Forensics and Incident Response
Digital forensics relies heavily on hex analysis:
- File carving: Finding files embedded in disk images by scanning for magic bytes
- Metadata extraction: Reading EXIF data from images, embedded timestamps
- Malware analysis: Examining PE headers, finding embedded strings
- Data recovery: Identifying file boundaries in corrupted storage
Reverse Engineering Binary Protocols
When documentation doesn't exist (or lies), hex dumps are the source of truth. Compare hex dumps of different messages to spot patterns:
Request 1: 01 00 00 05 48 65 6c 6c 6f → length=5, data="Hello"
Request 2: 01 00 00 03 48 69 21 → length=3, data="Hi!"
The pattern emerges: byte 0 is a type flag, bytes 1-3 are a big-endian length prefix, and the rest is the payload.
Validating File Integrity
After downloading or transferring files, a quick hex check of the header confirms the file isn't truncated or corrupted:
# Check that a downloaded ZIP starts with PK signature
xxd -l 4 download.zip
# Should show: 504b 0304 → PK..
Debugging Encoding Issues
When text looks wrong — mojibake, missing characters, invisible BOM markers — hex dumps show you the actual bytes. Is that é encoded as C3 A9 (UTF-8) or E9 (Latin-1)? The hex dump tells you instantly. Check our guide on text encoding formats for more on fixing encoding bugs.
Online Tools for Hex Analysis
Command-line tools are powerful but not always convenient. For quick conversions:
- hextoascii.co — Convert hex to ASCII text and back. Paste hex bytes and instantly see the decoded output. Supports UTF-8 and multiple formats.
- base64decode.co — When your data is Base64-encoded (common in APIs and email), decode it first, then hex-dump the result.
These are especially useful when you're reading logs, debugging on a machine without your usual tools, or just need a quick sanity check.
FAQ
What is a hex dump?
A hex dump is a display of binary data where each byte is represented as a two-digit hexadecimal number (00–FF). It typically shows three columns: the byte offset, the hex values, and the ASCII interpretation. It's used for inspecting files, debugging protocols, and analyzing binary data.
How do I create a hex dump on Linux or Mac?
Use xxd filename for a standard hex dump, hexdump -C filename for canonical format, or od -A x -t x1z filename for POSIX-compatible output. All three are pre-installed on most systems. Use -l N or -n N to limit output to the first N bytes.
How do I convert a hex dump back to a binary file?
With xxd, use the reverse flag: xxd -r hexdump.txt > output.bin. For plain hex strings (no offsets), use xxd -r -p hex.txt > output.bin. You can also use hextoascii.co for quick online conversion.
What are magic bytes?
Magic bytes (or file signatures) are specific byte sequences at the beginning of a file that identify its format. For example, PNG files start with 89 50 4E 47, PDFs with 25 50 44 46, and ZIP files with 50 4B 03 04. The file command on Unix uses magic bytes to determine file types.
How do I find a specific byte sequence in a file?
Use xxd file.bin | grep "pattern" for a quick search, or grep -boa -P '\x89\x50\x4e\x47' file.bin for binary pattern matching. Python's bytes.find() method works for programmatic searches.
What's the difference between hexdump, xxd, and od?
xxd is the most user-friendly and supports reverse conversion (hex dump to binary). hexdump offers the most formatting flexibility with custom format strings. od is the POSIX standard, guaranteed to exist on any Unix system. For most tasks, xxd is the best default choice.
Why do hex dumps show dots (.) for some characters?
The dots represent non-printable bytes — control characters (0x00–0x1F), DEL (0x7F), and extended bytes (0x80–0xFF). Only bytes in the range 0x20–0x7E (printable ASCII) are shown as their character representation. This prevents terminal escape sequences and invisible characters from breaking your display.
Can I edit a binary file using hex dumps?
Yes. With xxd: dump the file (xxd file.bin > hex.txt), edit hex.txt in any text editor, then reverse it (xxd -r hex.txt > modified.bin). For single-byte patches, use printf '\xNN' | dd of=file.bin bs=1 seek=OFFSET conv=notrunc. For serious hex editing, tools like hexedit or bless provide a proper GUI.