Binary Analysis 09 · ファイルシステム — FAT・LittleFS・SPIFFS を読む

Chapter 09

ファイルシステム — FAT・LittleFS・SPIFFS を読む

この章のゴール.

フラッシュ上のファイルシステムを種類ごとに見分け、 FAT・LittleFS・SPIFFS それぞれから、専用ツールでファイルを取り出せるようになること。 「フラッシュ向けファイルシステム」がなぜ普通の FAT と違う作りなのかを理解すること。

この章で使う既出の用語(定義は各リンク先). エントロピー(01 章 3 節)、binwalk(02 章 3 節)、オフセット(03 章 1 節)、サイズ(04 章 7 節)、テーブル(07 章 6 節)、文字列(07 章 6 節)、バージョン(08 章 3 節)

1. フラッシュとファイルシステムの相性

外付けフラッシュや内蔵フラッシュの一部が、ファイルシステムとして使われることがある(ログ、設定、Web の静的ファイル、音声データ)。 だが、PC の FAT や ext4 をそのまま載せると問題が起きる。フラッシュには 2 つの制約があるからだ。

そこで、書き込みを全域に分散する(ウェアレベリング)、電源断で壊れにくい(給電中断に強い)ように設計された、フラッシュ専用のファイルシステムが使われる。 LittleFS、SPIFFS、JFFS2、UBIFS、YAFFS などである。

フラッシュの制約とファイルシステムフラッシュの 2 つの制約① 消去はブロック単位(4KB/64KB)、書き込みは 1→0 だけ。上書きは「消して書き直す」 ② 書き換え回数に寿命(1万〜10万回)だから専用の FS書き込みを全域に分散(ウェアレベリング)、電源断に強い設計。LittleFS / SPIFFS / JFFS2 / UBIFS見分け: FAT は 55 AA と EB/E9、LittleFS は "littlefs" 文字列、squashfs は hsqsbinwalk / strings / xxd で先頭を見る
フラッシュは消去がブロック単位で寿命がある。だからウェアレベリングと電源断耐性を持つ専用 FS

2. 種類を見分ける

まずダンプの先頭とマジックで当たりをつける。

ファイルシステム目印用途
FAT12/16/32オフセット 0 に EB/E9 のジャンプ、0x1FE に 55 AA、"FAT16 "/"FAT32 " 文字列SD カード、USB、大きめの外付け
LittleFSブロック内に "littlefs" 文字列、superblock の magicマイコンの内蔵/外付け(Zephyr、Mbed、Arduino)
SPIFFS明確なマジックが乏しい。4 バイトのオブジェクト索引が規則的に並ぶESP8266/ESP32 の古い構成
FatFs(ChaN)中身は FATマイコンで最も普及した FAT 実装
JFFS285 19(ノードマジック 0x1985)古い Linux 組み込み(15 章)
UBIFS / UBIUBI#(0x23494255)、31 18 10 06NAND の Linux 機器(15 章)
squashfshsqs/sqsh読み取り専用ルート(15 章)
binwalk fs.bin                          # 既知の FS を検出
strings fs.bin | grep -i littlefs       # LittleFS の痕跡
xxd -l 16 fs.bin                         # FAT なら EB/E9 で始まる

3. FAT を読む

FAT は PC と同じなので、道具が豊富である。

file fs.bin                              # "DOS/MBR boot sector" など
mdir -i fs.bin ::                        # mtools: ルートの一覧(マウント不要)
mcopy -i fs.bin ::/LOG.TXT .             # ファイルを取り出す
7z l fs.bin                              # 7-Zip でも一覧・展開できる
# あるいはループバックマウント(Linux)
sudo mount -o loop,ro fs.bin /mnt

パーティションがある場合(先頭が MBR、Master Boot Record——ディスクの先頭にある区画表)は、まず fdisk -l fs.bin でパーティションの開始オフセットを調べ、その位置から FAT が始まる。mtools は -i fs.bin@@<offset> でオフセットを指定できる。

FAT の構造(ブートセクタ → FAT テーブル → ルートディレクトリ → データ領域)を手で読む必要はめったにないが、壊れた FAT からファイルを救うときは、ディレクトリエントリ(32 バイト固定、ファイル名 + 開始クラスタ + サイズ)を直接拾う。日時はビット詰めの 2+2 バイトである(03 章)。

4. LittleFS を読む

LittleFS(ARM が主導する、電源断に強い小型ファイルシステム)は、マイコンで今最も使われる。 専用の Python ツールで、ダンプからファイルを取り出せる。

pip install littlefs-python
from littlefs import LittleFS
# ブロックサイズと個数はその機器の設定に合わせる(重要)
fs = LittleFS(block_size=4096, block_count=256, mount=False)
with open("lfs.bin","rb") as f:
    fs.context.buffer = bytearray(f.read())
fs.mount()
for root, dirs, files in fs.walk("/"):
    for name in files:
        path = root.rstrip("/") + "/" + name
        data = fs.open(path, "rb").read()
        print(path, len(data))

block_size と block_count が合わないとマウントに失敗する。値は機器のソース(lfs_config)か、フラッシュ容量から推定する(block_count × block_size = 領域サイズ)。 littlefs-fuse を使えば mount して普通のファイルとして扱うこともできる。

LittleFS の内部は、各ブロックがメタデータペア(2 ブロックを交互に使い、片方が壊れてももう片方が残る)と CTZ スキップリスト(Count Trailing Zeros——ブロックを飛び飛びにたどってファイルデータをつなぐ仕組み)でできている。手で追うのは骨だが、「同じ内容が 2 か所にある」「ブロックの先頭に revision カウンタがある」ことを知っておくと、目視でも構造が見える。

LittleFS — マイコンで最も使われる取り出し方littlefs-python でダンプからファイルを取り出す。block_size と block_count を機器の設定に合わせる(合わないとマウント失敗)内部の勘どころメタデータペア(2 ブロックを交互に使い、片方が壊れても残る)+ CTZ スキップリスト。ブロック先頭に revision カウンタblock_count × block_size = 領域サイズ。ソース(lfs_config)・データシート・繰り返し境界から求めるlittlefs-fuse でマウントもできる。SPIFFS は mkspiffs、FAT は mtools / 7z / マウント「同じ内容が 2 か所にある」「revision カウンタ」が目視の手がかり
LittleFS は littlefs-python で取り出す。block_size / block_count を機器に合わせる

5. SPIFFS を読む

SPIFFS(SPI Flash File System)は ESP8266 / 古い ESP32 で使われた。マジックが弱く見分けにくいが、ESP-IDF のツールで生成・展開できる。

# ESP-IDF 付属(バージョンにより名前が違う)
python spiffsgen.py --help
mkspiffs -u out_dir spiffs.bin          # 展開(page/block サイズを機器に合わせる)

SPIFFS は flat(ディレクトリ階層を持たず、パスをファイル名に埋める)で、ページ単位のオブジェクト索引で管理する。新しい設計では LittleFS に置き換えられている。

6. パラメータが分からないとき

フラッシュ向け FS は、ブロックサイズ・ページサイズ・領域の開始オフセットと大きさを知らないと読めない。手がかり:

# 4 KB ごとに先頭 16 バイトを見て、繰り返し構造を探す
python3 -c "
d=open('fs.bin','rb').read()
for off in range(0,len(d),0x1000):
    print(hex(off), d[off:off+16].hex())" | head -40

7. 取り出した後

ファイルを取り出せたら、それ自体がまた解析対象になる。

8. 手を動かす

ファイルシステムを見分ける


この章のポイント