Skip to content

Make NTFS alternate data streams members of the raw image seeker, and vendor qnxprobe 1.37 - #253

Merged
abrignoni merged 1 commit into
mainfrom
feat/ntfs-alternate-data-streams
Sep 26, 2026
Merged

abrignoni merged 1 commit into
mainfrom
feat/ntfs-alternate-data-streams

Conversation

@abrignoni

Copy link
Copy Markdown
Owner

What this changes

This keeps VLEAPP's copy of the shared raw-image seeker in step with abrignoni/DLEAPP#274, abrignoni/iLEAPP#2281 and abrignoni/ALEAPP#1443. On an NTFS volume in a raw image or E01, each alternate data stream becomes a member named <file>:<stream>. A stream is matched only by a pattern whose last segment contains a :. No VLEAPP artifact pattern contains one, so no VLEAPP artifact sees a difference.

  • scripts/raw_image.py: streams go in a separate stream_list, so name_list and its order are unchanged.
  • scripts/vendor/qnxprobe.py 1.37:
    • a stream is read from its first stored cluster
    • a stream that stores nothing ($BadClus:$Bad) is not listed
    • with an older vendored qnxprobe, there are no stream members

Anything reviewers should know

  • Vendored from an unmerged commit. qnxprobe is pinned at 75164a9, the qnxprobe.py commit in Read NTFS alternate data streams, and version 1.37 qnxprobe#58, which is not merged yet. check_vendored.py passes against that commit.
  • Identical in five repos. raw_image.py, test_raw_image_seeker.py, the fixture README and the new ntfs-streams fixture are byte-identical to the DLEAPP, iLEAPP, ALEAPP and RLEAPP PRs.
  • Tests. The seeker tests gain 12 stream tests against the new fixture. All 28 streams match The Sleuth Kit's reading, and patterns without : never return a stream.
  • Local results.
    • Full admin/test/scripts suite: OK on Python 3.10.
    • lint_changed.py: no new warnings.
    • check_vendored.py: passes.

🤖 Generated with Claude Code

https://claude.ai/code/session_01AHJe4yVn4XBzXfBETDGyuD


Generated by Claude Code

… vendor qnxprobe 1.37

qnxprobe 1.37 gives each alternate data stream a node of its own that
read_file(), entry() and stamps() take, and streams(record) lists them. The
seeker registers each as a member named <file>:<stream> (":<stream>" on the
root), kept in stream_list rather than name_list, and matches them only for
a pattern whose last segment holds a ":". No VLEAPP artifact pattern holds
one, so every artifact is handed exactly what it was; name_list and its
order are unchanged. Staging needs nothing new: the node goes back to
read_file(), and the staged copy's name has the colon sanitised as any path
does, while the report cites "<file>:<stream>".

What a stream holds is the reader's rule: it is read from its first stored
cluster, so $Extend/$UsnJrnl:$J stages at the size of its records rather
than of the hole Windows leaves in front of them, and a stream that stores
nothing ($BadClus:$Bad, recorded as the whole volume) is not a member. With
a vendored qnxprobe older than 1.37 there are no streams and nothing else
changes.

Tests run against a new fixture, ntfs-streams.img.gz, copied from qnxprobe:
all 28 streams match The Sleuth Kit's reading, patterns without a ":" never
return a stream, both walk routes give the same streams, a reader without
streams() leaves the members as they were, and the 1 MiB front of the
$J-shaped stream is not staged.

raw_image.py, the seeker tests and the fixture are byte-identical to the
copies in DLEAPP, iLEAPP and ALEAPP, which carry the same change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AHJe4yVn4XBzXfBETDGyuD
@abrignoni
abrignoni merged commit 898ff08 into main Sep 26, 2026
9 checks passed
@abrignoni
abrignoni deleted the feat/ntfs-alternate-data-streams branch September 26, 2026 17:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants