EZ LearnEZ Learn
Courses
EZ Board
Dashboard
All articles
python-for-cyber-securitypythondigital-forensicshashing

Forensic Searching and Indexing

You cannot find a book in a library with no catalogue. Before searching a disk image for anything meaningful, build a catalogue of it, and judge every file by its content, never its name.

Ashish Revar17 October 20267 min read
1 video lesson 6:09 total
Sign in to track your progress

Video lesson

An investigator with a disk image of a thousand files does not read them one at a time, hoping to notice something. The very first script run against new evidence is almost always one that catalogues it: what exists, what type each file really is, and a hash proving the state each was found in.

Analogy: The library catalogue

You cannot find a book in a library with no catalogue; you would walk every shelf. Before an investigator can search a disk image for anything meaningful, they build a catalogue of it: what files exist, their type, their size, and a hash to prove the state each was found in.

Building the index

build_index.py

# build_index.py: walk a directory and catalogue every file
import os
import hashlib
import json
import magic
from forensic_log import log_action

def hash_file(path, algorithm="sha256", chunk_size=65536):
    hasher = hashlib.new(algorithm)
    with open(path, "rb") as f:
        while chunk := f.read(chunk_size):
            hasher.update(chunk)
    return hasher.hexdigest()

def build_index(root_dir, logfile="forensic_run.log"):
    index = []
    detector = magic.Magic(mime=True)
    for dirpath, _, filenames in os.walk(root_dir):
        for name in filenames:
            path = os.path.join(dirpath, name)
            try:
                record = {
                    "path": path,
                    "size_bytes": os.path.getsize(path),
                    "mime_type": detector.from_file(path),
                    "sha256": hash_file(path),
                }
                index.append(record)
                log_action(logfile, f"Indexed {path}")
            except (PermissionError, OSError) as exc:
                log_action(logfile, f"Could not read {path}: {exc}")
    return index

if __name__ == "__main__":
    index = build_index("./evidence_copy")
    with open("file_index.json", "w") as f:
        json.dump(index, f, indent=2)
    print(f"Indexed {len(index)} files")

Sample output, against a small evidence folder of six files

[2026-10-17T20:01:44.435] Indexed ./evidence_copy/History
[2026-10-17T20:01:44.440] Indexed ./evidence_copy/holiday.txt
[2026-10-17T20:01:44.443] Indexed ./evidence_copy/holiday_renamed.txt
[2026-10-17T20:01:44.445] Indexed ./evidence_copy/notes_invoice.pdf
[2026-10-17T20:01:44.446] Indexed ./evidence_copy/photo_001.jpg
[2026-10-17T20:01:44.448] Indexed ./evidence_copy/report.pdf
Indexed 6 files

Notice the log line for each file: that timestamped record is the first brick of the chain of custody, written automatically as the script works, using the logger from the last article. os.walk visits every folder under root_dir; for each file it records a path, a size, a true type and a hash, then appends that record to the growing index.

Python concept: The walrus operator :=

while chunk := f.read(chunk_size) both reads a chunk and tests it in one step. It reads a fixed-size piece of the file, assigns it to chunk, and the loop continues while that piece is non-empty. This hashes a multi-gigabyte file without ever loading the whole thing into memory, which an investigator's workstation will thank you for. You met the same shape of streaming read in Unit 1's file-handling article; this is the same idea put to forensic use.

Judging content, not the cover

Analogy: Judging content, not the cover

A file named holiday.jpg is not necessarily an image; a suspect can rename an executable to hide it. python-magic judges a file by its actual content, the signature bytes at its start, not by its extension, the way a reader judges a book by reading a page rather than trusting the title on the spine. This single habit catches one of the oldest and simplest evasions.

The evidence folder above includes holiday_renamed.txt, a real JPEG photograph saved with a .txt name, exactly the trick a suspect might try. Watch what the index records for it.

Searching the index

Once an index exists, searching it is a matter of filtering the list, which is far faster than walking the disk again for every question.

search_index.py

# search_index.py
import json

def search_by_type(index, mime_type):
    return [r for r in index if r["mime_type"] == mime_type]

def search_by_keyword(index, keyword):
    keyword = keyword.lower()
    return [r for r in index if keyword in r["path"].lower()]

with open("file_index.json") as f:
    index = json.load(f)

print(f"PDF files: {len(search_by_type(index, 'application/pdf'))}")
print(f"Files mentioning 'invoice': {len(search_by_keyword(index, 'invoice'))}")

# the renamed file: does python-magic see through the .txt extension?
images = search_by_type(index, 'image/jpeg')
print("Files identified as JPEG despite their name:")
for r in images:
    print(" ", r["path"])

Sample output

PDF files: 2
Files mentioning 'invoice': 1
Files identified as JPEG despite their name:
  ./evidence_copy/holiday_renamed.txt
  ./evidence_copy/photo_001.jpg

holiday_renamed.txt shows up in the JPEG list despite its name, because detector.from_file() read the file's actual signature bytes, not its extension. A search by extension, or a search that trusted the file's name, would have missed it completely. This is the oldest and simplest evasion in digital forensics, and it is defeated by three lines of code.

Caution: Path separators differ by operating system

os.path.join and os.walk write paths using the operating system's own separator, a backslash on Windows and a forward slash on Linux and macOS. The index and search code above work identically either way; only the text of the path itself differs, which is worth remembering when you compare output between your own machine and someone else's.

Check your understanding

Question 1: Why does build_index.py identify a file's type with python-magic rather than by its extension? What evasion does this catch?

An extension is just text in the file's name, which anyone can change. python-magic reads the actual signature bytes at the start of the file's content, which do not change when the name does. This catches a suspect renaming a file, such as an image or an executable, to hide what it really is.

Question 2: What does hashing a file at collection time prove that simply recording its name and size does not?

A hash is a fingerprint of the file's exact content. Recording a name and size only shows that something with that name and size existed; two different files can easily share both. A hash proves the file's content has not changed since it was collected, which a name and size alone can never prove.

Question 3: Why does hash_file read the file in fixed-size chunks instead of reading it all at once?

Reading the whole file into memory at once would fail or become impractically slow for a very large file, such as a multi-gigabyte disk image. Reading fixed-size chunks and updating the hash incrementally processes the file a little at a time, so memory use stays small no matter how large the file is.

Key takeaway: Key takeaway

An index built once turns every later search into a filter over a list instead of a fresh walk of the disk. Hash every file as you index it, and judge its type by content, never by name: that one habit is what catches a suspect's oldest trick, the renamed file.

Assessments

Unit Assessment

Digital Forensics with Python

20 questions30 minPass: 60%

Start exam →

Comprehensive Assessment

Python for Cyber Security: Complete Assessment

100 questions120 minPass: 60%

Start final exam →

Related to this topic

Continue learning

Reference material

Cheatsheet

Python for Cyber Security — Cheatsheet

v1.0

Open resource

MCQ Bank

Python for Cyber Security — MCQ Bank

v1.0

Open resource

Question Bank

Python for Cyber Security — Question Bank

v1.0

Open resource

eBook

Python for Cyber Security — eBook

v1.0

Open resource

Discussion

Loading comments…
More articlesTest your knowledge
EZ MentorAI · Free

Forensic Searching and Indexing

Hi! I'm EZ Mentor

Your AI tutor for
Forensic Searching and Indexing

Sign in to ask, it's free.

EZ Mentor is AI-powered · Answers may not be perfect · 10 free questions / day

EZ LearnEZ Learn

Structured learning across Reverse Engineering, Cloud Security, and Research Methodology, plus games and practice tools in EZ Arena. Free, and open to anyone.

EZ Learn

  • Courses
  • EZ Articles
  • EZ Watch
  • EZ Talks
  • EZ Bytes
  • Cyber Hygiene

EZ Library

  • All Resources
  • eBooks & PDFs
  • Question Banks
  • MCQ Banks
  • Cheatsheets

EZ Exams

  • All Exams
  • REMA
  • Cloud Security
  • Research Methodology
  • Python for Cyber Security
  • Cyber Law & IPR
  • Community
  • EZ Board

EZ Arena

  • EZ Games
  • Live Quiz
  • ML Challenges
  • दिशा - Hygiene
  • सजग - Awareness
  • अभ्यास - Practice

Platform

  • About
  • Contact
  • Terms of Use
  • Disclaimer
  • Verify Certificate
  • Events

© 2026 EZ Learn by EpochZero. Educational content for learning purposes.

Platform Lead: Ashish Revar