Course outline

Digital Forensics / Basic: scope, integrity, and interpretation / Lesson 4

Read bytes, encodings, and offsets precisely

Interpret small synthetic byte sequences without confusing display text, byte positions, and numeric layout.

About 4 min readContent reviewed
On this page

Lesson goals

  • Distinguish bytes from decoded characters and preserve decoding failures.
  • Interpret declared byte order and bounds in a tiny custom format.

Treat a display as one interpretation of bytes

An artifact begins as bytes, while a tool may display text, hexadecimal, integers, or a decoded structure. These views can answer different questions. Hexadecimal is a representation of bytes, not an encoding that automatically reveals meaning. Record the offset convention, length, and selected view. A byte offset is not necessarily a character index in UTF-8. In the workshop fixture, the Arabic title contains characters that occupy more than one byte. Treat the original byte stream and any decoded text as related but distinct analytical objects.

Declare the codec and error policy

Decode with a supported, explicitly chosen codec and preserve what happens when it fails. Ignoring invalid bytes can remove meaningful material; replacement characters can obscure which original bytes were present. The first program round-trips a known UTF-8 fixture and separately catches a deliberate invalid byte. The invalid case is handled by the complete program rather than leaving a crashing fragment. It demonstrates decoder behavior only. A real artifact’s encoding must be supported by its format or context instead of guessed from one readable word or silently forced to UTF-8.

python Check exact decoding with a handled invalid case
text = "ورشة"
raw = text.encode("utf-8")
assert raw.decode("utf-8") == text
assert len(raw) > len(text)
try:
    b"\xff".decode("utf-8", errors="strict")
except UnicodeDecodeError:
    invalid_rejected = True
else:
    invalid_rejected = False
assert invalid_rejected
print("UTF-8 round trip and invalid-byte rejection checked")
Expected output
UTF-8 round trip and invalid-byte rejection checked

The complete program uses known public text and one inert invalid byte in memory. It checks UTF-8 behavior without reading a file or repairing any artifact. Arabic text is preserved exactly in both language versions of the code.

Make byte order and field length explicit

A multi-byte integer needs a specified width and byte order. Python struct can decode a declared format, while its native default may depend on platform layout. The second program uses explicit little-endian and big-endian unsigned sixteen-bit interpretations of the same two bytes. Both calculations are valid under their own assumptions; only the artifact specification can choose the relevant one. Do not pick whichever value fits the favored timeline. Bounds checks must precede extraction so truncated material is identified rather than padded with invented bytes or read at an unintended offset.

python Compare two declared byte-order interpretations
import struct
raw = bytes.fromhex("01 02")
assert len(raw) == 2
little = struct.unpack("<H", raw)[0]
big = struct.unpack(">H", raw)[0]
assert little == 513
assert big == 258
print("little=513 big=258 for the same two bytes")
Expected output
little=513 big=258 for the same two bytes

The complete program decodes a two-byte synthetic value with explicit layouts. It is not a parser for a filesystem, packet capture, memory image, or real evidence format. No device, file, or network is accessed.

Preserve extraction boundaries and original values

When extracting a field, record start offset, byte length, original hex, and the decoding rule. Distinguish absolute offsets in the source fixture from offsets relative to a selected record. A parser that skips a header changes the reference point, so the report should state that convention. Keep original values alongside normalized fields, especially when malformed input cannot be decoded under the chosen rule. Do not relabel an offset as a sector, block, or character position without the relevant format definition. Unit confusion can create a convincing but incorrect artifact association.

Validate interpretations against known fixtures

A useful parser fixture includes a known valid value, a boundary length, a truncated case, and an unsupported encoding where relevant. Compare expected offsets and values independently from the implementation under review. A program that runs without an exception can still interpret the wrong byte order or silently discard bytes. Preserve failure categories and the exact input for replay. This course’s small calculations teach interpretation discipline; they do not validate a commercial forensic tool or establish that an unknown real-world format has been decoded correctly.

Common errors and fixes

Character index is reported as byte offset

Why it happens
Decoded text positions are confused with source bytes.
How to fix it
Track byte boundaries before decoding and label units.
Prevent it next time
Include multibyte fixtures in parser validation.

Invalid bytes are ignored silently

Why it happens
The decoder removes material without a recorded decision.
How to fix it
Preserve original bytes and report the decoding failure.
Prevent it next time
Make codec and error policy explicit.

Native layout is assumed portable

Why it happens
Byte order and alignment are left to runtime defaults.
How to fix it
Use the artifact’s declared field layout and bounds.
Prevent it next time
Test known byte sequences with independent expected values.

Check your understanding

1. The same bytes decode as 258 and 513. Which result belongs in the report?

Show worked solution

State the original bytes and both assumptions until the format definition establishes the correct byte order. If the format specifies big-endian unsigned sixteen-bit, 258 is the relevant interpretation. Do not select the value merely because it matches a preferred explanation.

2. Why preserve invalid UTF-8 bytes instead of replacing the source with readable text?

Show worked solution

Replacement changes the analytical object and can hide the original sequence. Keep the source intact, record strict-decoding failure, and create a clearly labeled derivative if a display policy is needed. Report what the derivative can and cannot establish about the original.

Key takeaways

  • Bytes, characters, and display formats are different representations.
  • Explicit layouts and bounds prevent ambiguous numeric extraction.
  • Preserve decoding failures and original values.

Official references