> ## Content Index
> Fetch the complete content index at: https://taiwanding.com/llms.txt
> Use this file to discover other available public pages before exploring further.

# 磁碟底層與資料救援（二）：不用 strings | grep，用鑑識流程解 picoCTF Disk, disk, sleuth!
- URL: https://taiwanding.com/ci-die-di-ceng-yu-zi-liao-jiu-yuan-er-bu-yong-strings-grep-yong-jian-shi-liu-cheng-jie-picoctf-disk-disk-sleuth/
- Published: 2026-09-03T07:31:41.000Z
- Updated: 2026-09-03T07:31:41.000Z
- Author: Kevin Chen
- Tags: 資料救援, 磁碟底層, 檔案系統, #picoCTF

聲明：本文是 picoCTF「Disk, disk, sleuth!」的解題記錄，所有操作都在下載的 image 檔上進行，不會碰到真實硬碟。

## 為什麼第二課是實戰

第一課的碟是自己造的：檔案自己放、inode 自己查、block 自己算，這一課換一顆「別人的碟」：picoCTF 2021 的 Disk, disk, sleuth!，一個小型 Linux 系統的硬碟 image，題目只說裡面有一個 flag，

先講清楚：這題用 `strings image | grep picoCTF` 三秒就出來，但這篇不走那條，走的是鑑識的流程——看結構、列名單、縮範圍、最後才碰內容，flag 是順便的，流程才是要帶走的。

## 環境

工具還是 Sleuth Kit，題目檔下載後，如果是在 WSL，Windows 的下載資料夾要透過 `/mnt/` 拿：

```bash
cp /mnt/d/Download/dds1-alpine.flag.img.gz ~/lab/ && cd ~/lab
gunzip dds1-alpine.flag.img.gz

```

Sleuth Kit 的工具名有規則，看懂就不用背：第一個字母是「看哪一層」，後面是「做什麼」。

1. 前綴：`mm` \= 分割表，`f` \= 檔名，`i` \= inode，`blk` \= block，`j` \= journal
2. 後綴：`ls` \= 列出來，`stat` \= 印詳細資料，`cat` \= 把內容倒出來

所以 `mmls` \= 列分割表，`fls` \= 列檔名，`istat` \= 看一張 inode，`icat` \= 倒一個檔案的內容。

![](https://taiwanding.com/content/images/2026/09/fig5-sleuthkit-naming.jpg)

## Step 1：看結構

```bash
mmls dds1-alpine.flag.img

```

```
DOS Partition Table
Offset Sector: 0
Units are in 512-byte sectors
      Slot      Start        End          Length       Description
000:  Meta      0000000000   0000000000   0000000001   Primary Table (#0)
001:  -------   0000000000   0000002047   0000002048   Unallocated
002:  000:000   0000002048   0000262143   0000260096   Linux (0x83)

```

跟第一課自己切的那顆長得一模一樣：第 0 格是分割表、1 到 2047 是 1 MiB 對齊留白、分割區從 2048 開始，260096 個 sector 約 127 MiB，類型 0x83 Linux。

重點：記住 2048，後面每個指令都要用 `-o 2048` 告訴工具「分割區從這裡開始」，跟第一課 losetup 的 `-o` 是同一件事，只是單位是 sector 不是 byte。

## Step 2：列根目錄

```bash
fls -o 2048 dds1-alpine.flag.img

```

```
d/d 10161:      home
d/d 11: lost+found
r/r 12: .dockerenv
d/d 2033:       bin
d/d 8129:       boot
d/d 6097:       dev
d/d 16257:      etc
d/d 28449:      lib
...
d/d 26418:      root
...
d/d 20324:      var
V/V 32513:      $OrphanFiles

```

`d/d` 是資料夾、`r/r` 是檔案，冒號前是 inode 編號，兩個線索：

1. `.dockerenv` 這不是裝在實體機上的 Linux，是一個 Docker container 被倒成硬碟 image，container 通常很乾淨，多出來的東西會很顯眼。
2. `$OrphanFiles` Sleuth Kit 自己加的虛擬資料夾，放「inode 還在、但名單上已經沒名字」的檔案，刪一半的、被藏的東西會出現在這，真實案子裡也是第一批要看的地方。

以「有人故意藏東西」來想，優先順序：`root`（登入者的家）、`home`（有沒有其他使用者）、`$OrphanFiles`。

## Step 3：一格一格翻，還是整顆倒出來

fls 後面接 inode 編號，就是「列這個資料夾裡面的東西」：

```bash
fls -o 2048 dds1-alpine.flag.img 26418   # root
fls -o 2048 dds1-alpine.flag.img 10161   # home

```

兩個都是空的，空的也是資訊：root 沒被用過、沒有其他使用者。

一格一格翻適合摸清結構，實務上第一步是整顆倒出來：`-r` 遞迴到底、`-p` 印完整路徑，存成檔案，之後所有判斷都在這個清單上做。

```bash
fls -o 2048 -r -p dds1-alpine.flag.img > listing.txt
wc -l listing.txt
grep -c '^\*' listing.txt

```

```
1973 listing.txt
0

```

1973 個項目，0 個開頭有 `*`（被刪掉但名字還在名單上的），所以 flag 是以正常檔案的身分放著的，不用走復原那條路，問題只是「在哪」，

## Step 4：排除「有人新增的目錄」

container 預設幾個目錄應該是空的，從清單裡看它們底下有沒有東西：

```bash
grep -E ': (home|root|tmp|opt|srv|mnt|media)(/|$)' listing.txt

```

只印出目錄本身那幾行，沒有多出來的檔案，所以放 flag 的人沒走「新增檔案」那條懶路，東西藏在現有檔案裡，或塞在不顯眼的路徑。

## Step 5：時間線

換一個真正鑑識會用的角度：時間，image 是一次建好的，兩千個檔案的修改時間會擠在同一段；之後被人動過的，時間會脫隊。

`fls -l` 每行多印四個時間和大小，再用 sort 照修改時間排：

```bash
fls -o 2048 -r -p -l dds1-alpine.flag.img | sort -t$'\t' -k3 | tail -15

```

新手坑：`-l` 的欄位順序是「類型 編號」、「路徑」、然後才是修改時間，所以是第 3 欄不是第 2 欄，我第一次用 `-k2` 排，結果整排都是 `var/` 開頭——那是照路徑排的。

```
r/r 30729:  lib/modules/.../tcp_vegas.ko   2021-02-17 04:55:28 ...
r/r 28871:  lib/modules/.../scsi_transport_fc.ko   2021-02-17 04:55:28 ...
r/r 28598:  lib/modules/.../poly1305_generic.ko   2021-02-17 04:55:28 ...
d/d 8129:   boot                2021-02-17 04:55:29 ...
r/r 8136:   boot/ldlinux.sys    2021-02-17 04:55:29 ...
r/r 8137:   boot/ldlinux.c32    2021-02-17 04:55:29 ...
r/r 8138:   boot/syslinux.cfg   2021-02-17 04:55:29 ...   219

```

全部 04:55:28，只有 `boot` 底下晚一秒，時間線在這題幫不上太多——整顆碟是腳本一秒內產出的，不是人用了很久的機器，container image 都這樣。

但這個「晚一秒」還是能讀：最後被動的是開機設定 `boot/syslinux.cfg`，219 bytes 的純文字，想藏在「看起來是系統檔、但其實是人手寫的」地方，這種小設定檔剛好合適，反正它很小，直接看。

## Step 6：讀內容

```bash
icat -o 2048 dds1-alpine.flag.img 8138

```

```
DEFAULT linux
  SAY Now booting the kernel from SYSLINUX...
  SAY picoCTF{Redacted}
 LABEL linux
  KERNEL /boot/vmlinuz-virt
  APPEND ro root=/dev/sda1 rootfstype=ext3 initrd=/boot/initramfs-virt

```

`SAY` 是開機時印一行字給使用者看的指令，第一行是正常的，第二行不是開機設定該有的東西。

順便看 `APPEND` 那行的 `rootfstype=ext3`：這顆碟是 ext3，跟 ext4 同一家，一樣有 journal，第三課會用到。

## 那暴力搜可不可以？

可以，而且有三種，各有涵蓋不到的地方：

1. `strings image | grep picoCTF` 直接掃整顆碟，連被刪的、journal 裡的舊版、沒被任何檔案佔用的空格子都掃到，範圍最大，缺點：檔案碎掉時字串會斷；flag 被 base64、壓縮、拆成兩半就看不到。
2. `tsk_recover` 照名單把每個檔案 icat 出來、用原本路徑存到資料夾，等於「把每張 inode 都倒一遍」的迴圈，之後用一般 grep、find 去搜，「照地圖走」，每個檔案的邊界都對，碎掉的也會拼好，但只涵蓋名單上有的東西。

```bash
tsk_recover -o 2048 -a dds1-alpine.flag.img out/
grep -r picoCTF out/

```

`-a` 只倒正常檔案，`-e` 連 orphan 和被刪的一起倒，這題倒出 1565 個檔案，比 listing 的 1973 少，差的是資料夾和 symlink，那些沒有內容可倒，

 3\. 縮範圍 就是這篇走的路，慢，但不依賴「知道 flag 長什麼樣」。

![](https://taiwanding.com/content/images/2026/09/fig6-search-coverage.jpg)

關鍵句：真實案子沒有 `picoCTF{`，你要找的可能是一封信、一個帳號、某天晚上發生了什麼，不知道長什麼樣的東西 grep 不到，專業做法是兩條並行：靠假設縮範圍，同時拿已知關鍵字掃整顆碟，互相印證，這篇練的是前者。

## 結語

![](https://taiwanding.com/content/images/2026/09/fig4-investigation-funnel.jpg)

這一課帶走的四件事：

1. 拿到一顆碟的順序：mmls 看結構 → fls 倒名單 → 排除 → 縮範圍 → 最後才 icat 看內容
2. 空的目錄、0 個刪除檔，都是資訊，每一步都在縮小範圍
3. 時間線是鑑識的基本功，這題剛好沒用，但排序前先確認欄位在第幾欄
4. 暴力搜是正規手法，不是作弊，但要知道它漏哪裡

Happy Hunting！