本記事は生成AIと共同で執筆しています。事実関係は可能な範囲で公式ドキュメント等と照合していますが、誤りが含まれている可能性があります。重要な判断を行う前にご自身でも一次情報をご確認ください。

ボーンデジタル資料の受入で登場する「ディスクイメージ」(メディアを1ビットも変えずに1ファイルへ複製したもの)と、通常の「フォルダのコピー」では、保存できる情報量が異なります。この違いを概念ではなく実際の挙動で確認するため、macOS(Apple Silicon)に The Sleuth KitBrunnhilde を入れ、自作の FAT16 テストイメージに対して各ツールを実行した記録です。

デジタル保存ツール(ArchivematicaBitCurator)のドキュメントには、fiwalktsk_recoverbulk_extractor といったデジタルフォレンジック由来のツールが登場します。これらが通常のフォルダ運用と何が違うのかを、fls / icat / fiwalk / tsk_recover / mactime / mmls、および Brunnhilde(Siegfried + ClamAV + bulk_extractor を統合)を動かして確認します。

確認した環境は次のとおりです。

項目
OSmacOS (Darwin 25.x, Apple Silicon / arm64)
The Sleuth Kit4.15.0(Homebrew bottle、afflib 3.7.22 / libewf 20140816 同梱)
Brunnhildevenv 上の brunnhilde.py
Siegfried (sf) / ClamAV / bulk_extractorHomebrew

「ディスクイメージ」とは何か

記録メディア(USB メモリ・フロッピー・HDD・CD/SD など)の中身を、見えているファイルだけでなく、削除済みファイル・空き領域・ファイルシステムのメタデータ(作成・更新・アクセスの時刻)まで含めて、1ビットも変えずに1ファイルへ複製したものを指します。形式には raw(dd)・E01(EnCase)・AFF などがあります。

通常のファイルコピー(Finder でのドラッグや cp -R)が「見えているファイルの内容」だけを運ぶのに対して、ディスクイメージは「メディアという物体の現在の状態」を運びます。この違いが、以降の実験で観察できます。

graph LR
    M["記録メディア<br/>USB / フロッピー / HDD"]
    M -->|"cp -R / Finder"| F["フォルダコピー"]
    M -->|"dd / イメージャ"| D["ディスクイメージ"]
    F --> F1["見えるファイルの内容だけ"]
    D --> D1["見えるファイル"]
    D --> D2["削除済みファイル"]
    D --> D3["空き領域 / slack space"]
    D --> D4["FSメタデータ・正確な時刻"]

図1: コピーとイメージ化では、同じメディアでも運べる情報量が異なります。

Sleuth Kit を入れる

brew install sleuthkit で導入しました。依存として openssl@3 / afflib / libewf / sqlite が入り、sleuthkit 4.15.0 本体が展開されます。確認しておきたかったのは fiwalk が同梱されているかどうかです。fiwalk はファイルシステムを走査して DFXML(Digital Forensics XML、後述)を出力するツールですが、ビルドによっては含まれないことがあります。

$ fiwalk -V
SleuthKit Version: 4.15.0
AFFLIB Version:    3.7.22
LIBEWF Version:    20140816

今回のビルドには含まれていました。fls mmls icat tsk_recover tsk_gettimes mactime なども /opt/homebrew/opt/sleuthkit/bin に揃います。

テスト用ディスクイメージを自作する

実メディアは使わず、古いメディアの受入を模した FAT16 イメージを作成します。ファイルを4つ書き込み、そのうち1つを削除してからイメージを確定する、という手順です。削除済みファイルがイメージに残るかどうかを後で確認します。

macOS では newfs_msdos が生ファイルを直接フォーマットできず、デバイスを要求して Inappropriate ioctl for device で停止します。そのため、先に hdiutil attach -nomount でデバイス化してからフォーマットします。

# 20MB の空イメージ
dd if=/dev/zero of=OLDMEDIA.img bs=1m count=20

# 生イメージをデバイスとして接続(マウントはしない)
DEV=$(hdiutil attach -imagekey diskimage-class=CRawDiskImage -nomount ./OLDMEDIA.img | awk 'NR==1{print $1}')

# デバイスを FAT16 でフォーマット
newfs_msdos -F 16 -v OLDMEDIA "$DEV"

# マウントして資料ファイルを書き込み、1つを削除
diskutil mount "$DEV"
#   本文.txt / 台帳.csv / 写真/IMG_0001.jpg / 削除予定.txt を作成
#   → 削除予定.txt を rm
diskutil unmount "$DEV"; hdiutil detach "$DEV"

削除予定.txt の内容は、後段の復元と PII(Personally Identifiable Information、個人情報)検出の観察のために「消す前の秘密メモ。連絡先 03-1234-5678」としました。

削除済みファイルが見える・読める

まずイメージの素性を確認します。

$ img_stat OLDMEDIA.img
Image Type: raw
Size in bytes: 20971520

$ fsstat OLDMEDIA.img | head
File System Type: FAT16
Volume Label (Boot Sector): OLDMEDIA

次に全ファイルを列挙します。fls -r(再帰)と -p(フルパス)を付けると、削除済みエントリには * が付きます。

$ fls -r -p OLDMEDIA.img
r/r 7:    本文.txt
r/r 11:   台帳.csv
d/d 15:   写真
r/r 1029: 写真/IMG_0001.jpg
r/r * 19: 削除予定.txt          ← * = 削除済み
r/r * 21: ._削除予定.txt
...

削除予定.txt は Finder 上では消えていますが、ファイルシステムのエントリ(inode 19)としては残っています。FAT の削除は「削除フラグが立つ」のではなく、ディレクトリエントリの先頭バイトが 0xE5(未使用印)で上書きされ、FAT のクラスタチェーンが解放されるだけで、データ本体は上書きされるまで残ります。そのため icat で内容を読み出せます。

削除前  dirエントリ[ 削除予定.txt → cluster 5 ]
        FAT      [5]→[6]→EOF
        data     [cl5: 消す前の…][cl6: …87878]

削除後  dirエントリ[ ?削除予定.txt ]   ← 先頭バイトが 0xE5 に
        FAT      [5] free  [6] free    ← クラスタチェーンを解放
        data     [cl5: 消す前の…][cl6: …87878]  ← 中身は無傷 → icat で復元可能

図2: FAT の削除はエントリの印付けとチェーン解放だけで、データ領域は上書きされるまで残ります。

続いて、削除済みファイルの内容を直接読み出します。

$ icat OLDMEDIA.img 19
消す前の秘密メモ。連絡先 03-1234-5678

読み出せました。フォルダコピーであれば存在しないことになっていた内容が、イメージからは復元できます。資料として救える利点であると同時に、寄贈者が消したつもりの個人情報まで保存・公開してしまう可能性もあります。

一括で取り出す場合は tsk_recover-e で削除分も含める)を使います。

$ tsk_recover -e OLDMEDIA.img recovered
Files Recovered: 12

書き込んだのは4ファイルですが、12 個が出力されます。差分は macOS が FAT に書き込む ._*(AppleDouble。リソースフォークや拡張属性)と .fseventsd/(FSEvents ログ)です。Mac で書いたメディアをイメージ化すると、macOS 由来のメタデータも一緒に取り込まれることが分かります。

DFXML — 保存に使えるメタデータ

fiwalk -X は、各ファイルの時刻・サイズ・ハッシュ・物理位置(格納セクタ)を記録した DFXML(Digital Forensics XML)を生成します。削除済みファイルも含まれます。

$ fiwalk -z -X OLDMEDIA.dfxml OLDMEDIA.img
$ wc -l OLDMEDIA.dfxml
577 OLDMEDIA.dfxml

DFXML は BitCurator や Archivematica で、メディアの状態の記録として保存対象になります。今回は577行・約16KB の XML が出力され、<filename>削除予定.txt</filename> のエントリも含まれていました。

時系列で見る場合は tsk_gettimes | mactime でタイムライン化できます。削除済みファイルは (deleted) と表示されます。

$ tsk_gettimes OLDMEDIA.img | mactime -d | grep 削除
... 削除予定.txt (deleted)

パーティションテーブルのあるイメージ

実メディアの多くはパーティションテーブルを持ちます。30MB のイメージに MBR で FAT16 区画を1つ作成し、mmls で解析します。

$ mmls PARTED.img
DOS Partition Table
      Slot      Start        End          Length       Description
002:  000:000   0000000063   0000061424   0000061362   DOS FAT16 (0x06)

区画がセクタ 63 から始まることが分かります。ファイルシステム系のツールには、このオフセットを -o で渡します。

$ fls -o 63 PARTED.img
r/r 6:  doc.txt

イメージ全体のどこにファイルシステムがあるかを mmls で調べ、そのオフセットを指定して中を見る(fls -o)、という二段構えになっています。

graph TD
    IMG["ディスクイメージ全体<br/>PARTED.img"] --> MMLS["mmls<br/>パーティション解析"]
    MMLS -->|"FAT16 はセクタ63開始"| FS["FAT16 区画"]
    FS --> FLS["fls -o 63<br/>ファイル列挙"]
    FS --> ICAT2["icat -o 63<br/>内容取り出し"]
    FS --> FW2["fiwalk<br/>DFXML生成"]

図3: Sleuth Kit はボリュームシステム層(mmls)とファイルシステム層(fls/icat/fiwalk)に分かれ、後者に -o <開始セクタ> を渡します。

Brunnhilde でまとめて処理する

Brunnhilde はこれらのツールを束ね、1コマンドでレポートを生成します。-d(disk image 入力)を付けると、イメージから直接処理します。

brunnhilde.py -d OLDMEDIA.img brunn_report
graph LR
    IMG["OLDMEDIA.img"] --> TR["tsk_recover<br/>ファイル救出"]
    TR --> FW["fiwalk<br/>DFXML"]
    TR --> CL["ClamAV<br/>ウイルススキャン"]
    TR --> SF["siegfried<br/>形式識別"]
    TR -.->|"-b 指定時"| BE["bulk_extractor<br/>PII抽出"]
    FW --> RPT["report.html<br/>+ csv_reports/"]
    CL --> RPT
    SF --> RPT
    BE -.-> RPT

図4: Brunnhilde は救出したファイル群に形式識別・ウイルススキャン・(任意で)PII 抽出をまとめて適用し、1つのレポートにします。

ログから、内部で順に実行されていることが分かります。

INFO - Attempting to carve files from disk image using tsk_recover.
INFO - File carving successful.
INFO - Attempting to generate DFXML file from disk image using fiwalk.
INFO - DFXML file created.
INFO - Running virus scan. This might take a while...
----------- SCAN SUMMARY -----------
Known viruses: 3627875
Scanned files: 12
Infected files: 0
Time: 17.078 sec
INFO - No viruses found.
INFO - Running Siegfried. This might take a while...
INFO - Brunnhilde characterization complete.

tsk_recover(救出)→ fiwalk(DFXML)→ ClamAV(ウイルススキャン。362万シグネチャ、12 ファイル、0 感染)→ Siegfried(形式識別)が連鎖し、report.htmlcsv_reports/(formats / mimetypes / years / duplicates / unidentified など)が出力されました。イメージを渡すと、受入判断に使える特性評価レポートが生成されます。

Brunnhilde が生成した report.html。Provenance・Statistics・Virus report・File formats・MIME types・Duplicates の各セクションが並ぶ

図5: 生成された report.html。ウイルススキャン結果(0 感染)、Siegfried による形式識別(PUID)、重複ファイル、最終更新年などが1ページにまとまります。

なお、本記事ではウイルス検体(EICAR など)は作成していません。スキャンは無害なファイルに対するもので、結果は 0 感染です。

bulk_extractor は日本の国内表記の電話番号を抽出しなかった

Brunnhilde は -bbulk_extractor(クレジットカード番号・メール・電話番号などの PII を全文から抽出)を統合できます。救出済みファイル群(電話番号を含む 削除予定.txt を含む)に対して実行しました。

$ bulk_extractor -R -o be_out brunn_report/carved_files
$ grep -vc '^#' be_out/telephone.txt
0          ← 電話番号 0 件
$ grep -vc '^#' be_out/pii.txt
0

ファイルには 03-1234-5678 が含まれています。

$ grep -r '03-1234' brunn_report/carved_files/削除予定.txt
消す前の秘密メモ。連絡先 03-1234-5678

それでも telephone は 0 件でした。スキャナの定義(src/scan_accts.flex)を読むと、03-1234-5678 がどの規則にも一致しない理由が分かります。

  • 北米形式の規則は 3-3-4 桁グループを要求します。03-1234-5678 は 2-4-4 のため一致しません。
  • 括弧形式 (xxx) や国際形式は + 国番号を要求します。国内表記には含まれません。
  • 国内表記に一致しうる「0 始まりの規則」は、ハイフンのない連続数字列を要求します。ハイフンで分断されるため一致しません。
  • キーワード前置の規則は、直前に英語の tel / fax / mobile などを要求します。今回の直前は日本語の「連絡先」のため一致しません。

ハイフン区切りと日本語の文脈が、国内表記に一致しうる規則をいずれも外しています。確認した限りでは、bulk_extractor の電話番号スキャナは北米・国際形式を主な対象としており、日本の国内表記(0AB-CDEF-GHIJ)には素直に一致しないようです。

ツールを導入すれば PII 検出が済む、というわけではないことを示す例です。海外で開発されたフォレンジックツールを日本語資料に適用する場合、国内電話番号・郵便番号・和暦を含む個人情報などに合わせた調整や独自実装が必要になります。

どのような場合にこれらのツールが要るか

観察された事実を整理します。

  • ディスクイメージはフォルダコピーを包含します。削除済みファイル・時刻・物理配置まで保存でき、icattsk_recover で削除分の内容も復元できます。
  • ただしこれは、対象が実メディア(または既存のイメージ)であることが前提です。すでにコピー済みのフォルダを入力にする運用では、削除済み領域も元メディアの時刻も存在せず、fiwalktsk_recover の対象がありません。
  • これらのツールが効くのは次の2つの場合です。(a) 物理メディア(古いフロッピー・USB・HDD など)を現物で受け入れ、ライトブロッカーとイメージャ(Guymager など)で自分でイメージ化する場合。(b) 寄贈者がディスクイメージ(.E01 / .dd / .aff)そのものを渡してくる場合。後者はフォルダではなくイメージとして扱う必要があり、フォルダ運用でも本記事のツール群が必要になります。いずれも、ディレクトリを入力にするアプリとは別のワークフローとして置くのが自然です。
  • PII 検出は、ツールの導入だけでは完結しません。bulk_extractor が日本の電話番号を抽出しなかった例のとおり、日本語資料には国内向けの検出ロジックが必要です。

要否を分けるのは、実メディアまたはディスクイメージを受け入れるかどうかの一点でした。すでにコピー済みのフォルダだけを扱う運用では、フォレンジック層の出番はありません。

参考