Cover image for the article: Read Hex Dumps: xxd, hexdump and File Signatures

Read Hex Dumps: xxd, hexdump and File Signatures

By Hex to ASCII Editorial Team
Reviewed by Hex to ASCII Editorial Team on

A hex dump displays each byte as two hexadecimal digits, usually beside an offset and an ASCII gutter. It lets you inspect file headers, protocol fields, and hidden text without asking an application to interpret the whole file first.

This guide explains the output formats from xxd, hexdump, and POSIX od, then shows how to reverse or parse them. Use the hex decoder when you have a plain byte column and need to inspect its text interpretation.

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.

Each byte has a value from 0 to 255, or 00 to FF in hex. A typical dump places 16 bytes on each row and shows printable ASCII beside them.

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: is the byte offset from the start of the file. The first byte is at offset 0; the row after 16 bytes begins at hex offset 10.

Many common dump formats display offsets in hexadecimal. In such output, 00000010 means byte 16, 00000020 means byte 32, and 000000FF means byte 255. Tools such as od can display offsets in other bases, so check the selected format.

2. Hex Bytes

4865 6c6c 6f2c 2057 6f72 6c64 210a contains the byte values in hexadecimal, grouped according to the tool's output format. 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!. is the printable ASCII gutter. The dot stands in for the line-feed byte 0A, which is not printable.

Command-Line Tools for Hex Dumps

xxd

xxd is distributed with Vim and is commonly available on Unix-like systems, though minimal installations may not include it.

# 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 reverses an xxd dump after you inspect or edit it. Use a separate output file and compare it with the original before replacing data.

hexdump

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

od (octal dump) is specified by POSIX and is the most portable choice across POSIX environments:

# Portable hex and character views
od -A x -t x1 -t c -v file.bin

# Output:
# 0000000 89 50 4e 47 0d 0a 1a 0a 00 00 00 0d 49 48 44 52
#         211 P  N  G  \r \n 032 \n \0 \0 \0 \r I  H  D  R

# Two-byte hex words
od -A x -t x2 file.bin

# First 48 bytes
od -A x -t x1 -t c -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 first four bytes of the PNG signature (89 followed by PNG in ASCII)
  • 0D 0A 1A 0A → DOS/Unix line ending test bytes (part of the PNG spec)
  • 00 00 00 0D → Chunk length: 13 bytes
  • 49 48 44 52 → Chunk type: IHDR (image header)

Line 2 (offset 0x10):

  • 00 00 01 00 → Image width: 256 pixels
  • 00 00 01 00 → Image height: 256 pixels
  • 08 → Bit depth: 8
  • 02 → Color type: 2 (truecolor RGB)

Line 3 (offset 0x20):

  • 73 52 47 42 → Chunk type: sRGB (color space)

The offset locates each field, the byte column shows the stored values, and the ASCII gutter provides landmarks such as PNG and IHDR. A format specification is still required to interpret field lengths and byte order.

Common File Signatures (Magic Bytes)

Many binary file formats start with recognizable magic bytes or file signatures. They can help identify a format without trusting its extension, although not every format has a unique signature.

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
PDF 25 50 44 46 2D %PDF- 0
ZIP local-file header 50 4B 03 04 PK.. 0
GZIP 1F 8B .. 0
ELF 7F 45 4C 46 .ELF 0
SQLite 3 53 51 4C 69 74 65 20 66 6F 72 6D 61 74 20 33 00 SQLite format 3 + NUL 0
WebAssembly 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 (the complete PNG signature)

# The `file` command does this automatically
file mystery_file
# mystery_file: PNG image data, 256 x 256, 8-bit/color RGB

A matching header supports a format identification, but it does not validate the rest of the file. Parse the complete structure and use a trusted digest when integrity matters.

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):
    """Parse lines emitted by the hex_dump() function above."""
    result = bytearray()
    for line in dump_text.splitlines():
        if not line.strip():
            continue

        match = re.match(r'^[0-9a-f]+:\s+(.*)$', line, re.I)
        if not match:
            raise ValueError(f'Invalid dump line: {line!r}')

        # The generator separates its byte column and ASCII gutter with
        # two or more spaces. Validate every byte token explicitly.
        hex_column = re.split(r'\s{2,}', match.group(1), maxsplit=1)[0]
        tokens = hex_column.split()
        if not tokens or any(
            re.fullmatch(r'[0-9a-f]{2}', token, re.I) is None
            for token in tokens
        ):
            raise ValueError(f'Invalid byte column: {line!r}')

        result.extend(int(token, 16) for token in tokens)
    return bytes(result)

# Identify file type by magic bytes
SIGNATURES = {
    b'\x89PNG\r\n\x1a\n': 'PNG image',
    b'\xff\xd8\xff': 'JPEG image',
    b'%PDF-': 'PDF document',
    b'PK\x03\x04': 'ZIP archive',
    b'\x1f\x8b': 'GZIP stream',
    b'\x7fELF': 'ELF binary',
    b'SQLite format 3\x00': 'SQLite database',
    b'\x00asm': 'WebAssembly module',
}

def identify_file(filepath):
    with open(filepath, 'rb') as f:
        header = f.read(16)
    for sig, name in SIGNATURES.items():
        if header.startswith(sig):
            return f'{name} candidate'
    return 'Unknown signature'

print(identify_file('photo.png'))  # "PNG image candidate"

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, '');
  if (clean.length % 2 !== 0 || !/^[0-9a-f]*$/i.test(clean)) {
    throw new TypeError('Expected an even number of hex digits');
  }
  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 protocol documentation is missing or stale, compare captures of known messages to form and test hypotheses about the byte layout:

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.

Checking File Type (Not Integrity)

A quick header check can show whether a file begins with the signature expected for its claimed format, but it cannot prove that the rest of the file is complete or unmodified:

# Check for the common ZIP local-file header
xxd -l 4 download.zip
# Should show: 504b 0304 → PK..

Use a trusted checksum or cryptographic hash to verify transfer integrity, and parse the complete file with a format-aware tool to detect structural truncation or corruption.

Debugging Encoding Issues

Hex dumps expose the bytes behind mojibake, missing characters, and byte-order marks. For example, é is C3 A9 in UTF-8 and E9 in Latin-1. The text encoding guide explains why a byte pattern still needs format or protocol context.

Related Decoding References

Use these references alongside command-line inspection:

  • Hex decoder: compare ASCII, Latin-1, and UTF-8 interpretations of a plain byte string.
  • RFC 4648 Base64: identify Base64 input and decode it to bytes before creating a hex dump.

Keep the original bytes when testing a possible decoding so you can reverse the operation.

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 familiar hex dump, hexdump -C filename for canonical format, or od -A x -t x1 -t c -v filename for portable hexadecimal and character views from a POSIX-defined utility. Availability and option syntax vary by operating system, so check the local manual. Use the tool's length option 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 byte sequences associated with a file format, often at the beginning of a file. A match is evidence for a format, not proof that the complete file is valid. For example, PNG begins with 89 50 4E 47 0D 0A 1A 0A, PDF begins with %PDF-, and a ZIP local-file header can begin with 50 4B 03 04. The Unix file utility combines signature rules and other checks to classify input.

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 provides a convenient reversible format, hexdump offers custom format strings on systems that include it, and od is defined by POSIX. Choose based on the required output and what is installed rather than assuming all three are present.

Why do hex dumps show dots (.) for some characters?

Dots represent bytes outside printable ASCII 0x20-0x7E, including controls, DEL, and values above 0x7F. Replacing them in the gutter prevents terminal control sequences and invisible characters from changing the display; the hex column still preserves their values.

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.

Sources

Article changelog
  • : Added primary format sources and separated signature checks from integrity verification.

Inspect bytes from a hex dump

Paste plain bytes or common dump output, then inspect ASCII, UTF-8, and control characters in the browser.

Open Hex Decoder