Binary Analysis 14 · 圧縮と暗号化の見分け — エントロピーで領域を分類する

Chapter 14

圧縮と暗号化の見分け — エントロピーで領域を分類する

この章のゴール.

エントロピーを使って、ダンプの各領域をコード・データ・空き・圧縮・暗号化に分類できるようになること。 「高エントロピーだが圧縮か暗号か」を、ヘッダとヒストグラムで見分けられること。

この章で使う既出の用語(定義は各リンク先). メモリマップ(01 章 3 節)、binwalk(02 章 3 節)、unblob(02 章 3 節)、オフセット(03 章 1 節)、文字列(07 章 6 節)、MCUboot(08 章 1 節)、TLV(08 章 4 節)、メタデータ(08 章 1 節)、squashfs(09 章 2 節)、ファイルシステム(09 章 1 節)

1. エントロピーとは何か

エントロピー(情報量。ここではシャノンエントロピー)は、「バイト列がどれだけランダムに見えるか」を 0〜8 の数値で表したものである。 ある区間の各バイト値 0〜255 の出現確率を $p_i$ として、

\[ H = -\sum_{i=0}^{255} p_i \log_2 p_i \quad (\text{ビット/バイト、} 0 \le H \le 8) \]

つまり、区間ごとに H を計算すれば、中身の種類の当たりがつく。

エントロピー H(0〜8)= ランダムさH空き ≈ 0テキスト ≈ 4コード ≈ 6圧縮 / 暗号 ≈ 880区間ごとに H を測ると、中身の種類(コード・データ・空き・圧縮/暗号)と境界が見える
H は 0〜8 のランダムさ。空き0・テキスト4・コード6・圧縮/暗号8。区間ごとに測り領域を分類

2. 区間ごとに測る

binwalk -E dump.bin              # 一定窓ごとの H をグラフに(matplotlib 必須)
ent dump.bin                     # 全体の H・カイ二乗・圧縮可能性

自分で計算するのも簡単で、境界を細かく調整できる。

import math
def entropy(b):
    if not b: return 0.0
    from collections import Counter
    c = Counter(b); n = len(b)
    return -sum((v/n)*math.log2(v/n) for v in c.values())

d = open("dump.bin","rb").read()
W = 4096
for off in range(0, len(d), W):
    h = entropy(d[off:off+W])
    bar = "#" * int(h*6)
    print(f"{off:#08x}  {h:4.2f}  {bar}")

出力の「H が段になって変わる位置」が、領域の境界である。これを 01 章のメモリマップに反映する。

0x000000  5.9  ###################             ← コード
0x008000  6.1  ####################
0x010000  4.2  #############                   ← 文字列・データ
0x018000  0.0                                  ← 空き(0xFF)
0x020000  7.99 ################################  ← 圧縮 or 暗号

3. 高エントロピーを「圧縮」と「暗号」に分ける

H ≈ 8 の区間は、圧縮データ・暗号化データ・鍵のどれか。ここからが見分けである。

手がかり 1: ヘッダ / マジック

圧縮には形式があり、先頭にマジックがある(03 章)。暗号化データには通常マジックがない(あってもラッパのヘッダだけ)。

先頭バイト形式
1F 8Bgzip
28 B5 2F FDzstd
FD 37 7A 58 5Axz
42 5A 68bzip2
5D 00 00lzma(生)
04 22 4D 18lz4
ヘッダなしで H≈8暗号化の可能性が高い
binwalk dump.bin                 # 圧縮形式を検出してオフセットを示す
# 見つけた圧縮を試しに展開
dd if=dump.bin bs=1 skip=$((0x20000)) | gzip -dc 2>/dev/null | head -c 64 | xxd

手がかり 2: ヒストグラムとカイ二乗

暗号化データは全バイト値がほぼ均一に出る(カイ二乗が理想値に近い)。 圧縮データもランダムに近いが、形式によっては微妙な偏り(ブロックヘッダ、辞書の痕跡)がある。ent のカイ二乗値と「ランダムを超える確率」を見る。

ent dump_region.bin
# Chi square distribution ... would exceed this value 50.00% ... ← 均一に近い=暗号らしい

手がかり 3: 展開できるか

圧縮なら展開すれば意味のあるデータが出る。暗号なら鍵がなければゴミのまま。 候補のオフセットから各展開ツールを試す(binwalk -e / unblob が自動でやってくれる)。展開に成功して中に文字列やコードが現れれば圧縮、どうやっても出なければ暗号(または未知の圧縮)。

H ≈ 8 を「圧縮」と「暗号」に分ける手がかり1: ヘッダ / マジック圧縮には形式があり先頭にマジック(1F 8B gzip、28 B5 2F FD zstd…)。暗号にはマジックがない手がかり2: ヒストグラム暗号は全バイト値がほぼ均一(カイ二乗が理想に近い)。圧縮は微妙な偏りが残ることも。ent で見る手がかり3: 展開できるか圧縮なら展開すれば意味のあるデータ。暗号は鍵なしでゴミのまま。binwalk -e / unblob が自動なぜ大事か圧縮なら展開して中身を解析できる。暗号なら鍵なしでは踏み込めない(時間を無駄にしない判断)。「暗号化されている=一定の保護がある」という設計の読み取りにもなる(13 章の逆)ヘッダは低エントロピーで手がかりが残る。区間を細かく測るのが効く
マジックがあれば圧縮、なければ暗号。展開できれば圧縮。カイ二乗の均一さも手がかり

4. なぜ見分けが大事か

5. 部分的に圧縮・暗号された構造

実際のイメージは、低エントロピーのヘッダ + 高エントロピーの本体という組み合わせが多い。

だから「区間を細かく測る」ことが効く。ヘッダの低エントロピー部分に形式の手がかりが残る。

6. 差分でも使える

同じ機器の 2 つのダンプ(更新前後、正常と異常)を比べるとき、エントロピーのプロファイルを並べるとどの領域が変わったかが一目で分かる(18 章)。

7. 手を動かす

エントロピーで領域を塗り分ける


この章のポイント