普通の Linux 環境で手軽にバイナリファイルを編集する (dd, vim)

# テスト用ファイル
ちょうど手元にあった PPPoE の pcap ファイルから CHAP の部分を抜粋したもの

![pppoe.png](https://qiita-image-store.s3.amazonaws.com/0/44206/04e909d0-3e81-32ff-7de0-57cc7105e5e1.png)

PPPoE のユーザ名 (`hogehoge@one.ocn.ne.jp`) をマスクする目的で書き換えてみる。
hogehoge の部分を xxxxxxxx のようにどうでもいい文字列で埋める。

編集したいファイルが大きい場合、Vim を使う方法ではメモリ消費が激しいので避ける。

# dd でバイナリ編集

編集する場所を確認するため、一度 hexdump で中身を調べる

```console:
$ hexdump -C pppoe.cap
00000000  0a 0d 0d 0a 34 00 00 00  4d 3c 2b 1a 01 00 00 00  |....4...M<+.....|
00000010  ff ff ff ff ff ff ff ff  04 00 0e 00 45 64 69 74  |............Edit|
00000020  63 61 70 20 31 2e 31 30  2e 33 00 00 00 00 00 00  |cap 1.10.3......|
00000030  34 00 00 00 01 00 00 00  20 00 00 00 01 00 00 00  |4....... .......|
00000040  ff ff 00 00 09 00 01 00  06 00 00 00 00 00 00 00  |................|
00000050  20 00 00 00 06 00 00 00  5c 00 00 00 00 00 00 00  | .......\.......|
00000060  c8 11 05 00 2e 6f d1 55  3c 00 00 00 3c 00 00 00  |.....o.U<...<...|
00000070  00 16 3e 5a be 76 00 12  e2 70 c9 3e 88 64 11 00  |..>Z.v...p.>.d..|
00000080  47 55 00 1a c2 23 01 01  00 18 10 bc 71 90 18 fe  |GU...#......q...|
00000090  6e 3f bd 12 07 89 66 85  68 3b 69 42 41 53 00 00  |n?....f.h;iBAS..|
000000a0  00 00 00 00 00 00 00 00  00 00 00 00 5c 00 00 00  |............\...|
000000b0  06 00 00 00 64 00 00 00  00 00 00 00 c8 11 05 00  |....d...........|
000000c0  1c 70 d1 55 41 00 00 00  41 00 00 00 00 12 e2 70  |.p.UA...A......p|
000000d0  c9 3e 00 16 3e 5a be 76  88 64 11 00 47 55 00 2d  |.>..>Z.v.d..GU.-|
000000e0  c2 23 02 01 00 2b 10 65  02 01 45 0e fd dd 19 6e  |.#...+.e..E....n|
000000f0  a1 13 da 3c c8 bf 8f 68  6f 67 65 68 6f 67 65 40  |...<...hogehoge@|
00000100  6f 6e 65 2e 6f 63 6e 2e  6e 65 2e 6a 70 00 00 00  |one.ocn.ne.jp...|
00000110  64 00 00 00 06 00 00 00  5c 00 00 00 00 00 00 00  |d.......\.......|
00000120  c8 11 05 00 10 5a d3 55  3c 00 00 00 3c 00 00 00  |.....Z.U<...<...|
00000130  00 16 3e 5a be 76 00 12  e2 70 c9 3e 88 64 11 00  |..>Z.v...p.>.d..|
00000140  47 55 00 06 c2 23 03 01  00 04 00 00 00 00 00 00  |GU...#..........|
00000150  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000160  00 00 00 00 00 00 00 00  00 00 00 00 5c 00 00 00  |............\...|
00000170
```

書き換え対象の hogehoge は 0xf7 バイトオフセット、つまり 247 バイトを読み進めたところから 8 バイトのデータを書けばよい。

dd で書き換える時は `conv=notrunc` オプションを設定することが重要となる。
これが付いていないと、書き換えたデータがファイルの末尾となり、以降に存在していたデータは削除されてしまう。( ftruncate(2) )
また、`seek=nbytes` を設定することで先頭から書き換え対象までの間は変更せずスキップさせる。

```console:ddによる書き換え
$ echo -n "xxxxxxxx" | dd of=pppoe.cap bs=1 seek=247 conv=notrunc
8+0 records in
8+0 records out
8 bytes (8 B) copied, 7.7162e-05 s, 104 kB/s
```

```console:書き換え後のデータ確認
$ hexdump -C pppoe.cap
00000000  0a 0d 0d 0a 34 00 00 00  4d 3c 2b 1a 01 00 00 00  |....4...M<+.....|
00000010  ff ff ff ff ff ff ff ff  04 00 0e 00 45 64 69 74  |............Edit|
00000020  63 61 70 20 31 2e 31 30  2e 33 00 00 00 00 00 00  |cap 1.10.3......|
00000030  34 00 00 00 01 00 00 00  20 00 00 00 01 00 00 00  |4....... .......|
00000040  ff ff 00 00 09 00 01 00  06 00 00 00 00 00 00 00  |................|
00000050  20 00 00 00 06 00 00 00  5c 00 00 00 00 00 00 00  | .......\.......|
00000060  c8 11 05 00 2e 6f d1 55  3c 00 00 00 3c 00 00 00  |.....o.U<...<...|
00000070  00 16 3e 5a be 76 00 12  e2 70 c9 3e 88 64 11 00  |..>Z.v...p.>.d..|
00000080  47 55 00 1a c2 23 01 01  00 18 10 bc 71 90 18 fe  |GU...#......q...|
00000090  6e 3f bd 12 07 89 66 85  68 3b 69 42 41 53 00 00  |n?....f.h;iBAS..|
000000a0  00 00 00 00 00 00 00 00  00 00 00 00 5c 00 00 00  |............\...|
000000b0  06 00 00 00 64 00 00 00  00 00 00 00 c8 11 05 00  |....d...........|
000000c0  1c 70 d1 55 41 00 00 00  41 00 00 00 00 12 e2 70  |.p.UA...A......p|
000000d0  c9 3e 00 16 3e 5a be 76  88 64 11 00 47 55 00 2d  |.>..>Z.v.d..GU.-|
000000e0  c2 23 02 01 00 2b 10 65  02 01 45 0e fd dd 19 6e  |.#...+.e..E....n|
000000f0  a1 13 da 3c c8 bf 8f 78  78 78 78 78 78 78 78 40  |...<...xxxxxxxx@|
00000100  6f 6e 65 2e 6f 63 6e 2e  6e 65 2e 6a 70 00 00 00  |one.ocn.ne.jp...|
00000110  64 00 00 00 06 00 00 00  5c 00 00 00 00 00 00 00  |d.......\.......|
00000120  c8 11 05 00 10 5a d3 55  3c 00 00 00 3c 00 00 00  |.....Z.U<...<...|
00000130  00 16 3e 5a be 76 00 12  e2 70 c9 3e 88 64 11 00  |..>Z.v...p.>.d..|
00000140  47 55 00 06 c2 23 03 01  00 04 00 00 00 00 00 00  |GU...#..........|
00000150  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000160  00 00 00 00 00 00 00 00  00 00 00 00 5c 00 00 00  |............\...|
00000170
```

[編集後のファイル](#編集後のファイル) は Vim の場合も dd の場合も同じ結果になる。

# Vim でバイナリ編集

おおまかに言って

* バイナリモードで開いて
* `:%!xxd` を打って
* 編集して
* `:%!xxd -r` を打って
* 保存終了

すればよい。

まず `vi -b pppoe.cap` のようにしてバイナリモードでファイルを開く

![vi-b.png](https://qiita-image-store.s3.amazonaws.com/0/44206/132d0001-ae19-4510-3c7e-1563157597cd.png)

次に、Vim 上で `:%!xxd` を実行して hex dump に変換する
(今回くらい簡単なら変換せずに書き換えられるが)

![vi-b-xxd.png](https://qiita-image-store.s3.amazonaws.com/0/44206/ced9e46c-3657-315f-30c7-1b7fc02e5cda.png)

データを変換しただけで Vim なので、通常の操作で
hogehoge に該当する 0x0f0 バイトオフセットの行の `68 6f67 6568 6f67 65` の部分を書き換える。
右側の ASCII 表示はファイルの内容に反映されないのでそのままでよい。
今回は `78 7878 7878 7878 78` (xxxxxxxx) に書き換える。

![vi-b-xxd-edit.png](https://qiita-image-store.s3.amazonaws.com/0/44206/c98152d7-b150-cb6a-4224-8f690bde332b.png)

このまま保存するとテキストファイルになってしまうので、`:%!xxd -r` でバイナリに戻して保存する

![vi-b-xxd-edit-xxd-r.png](https://qiita-image-store.s3.amazonaws.com/0/44206/653fdf97-8530-5184-7fcd-905fa63e5033.png)

[編集後のファイル](#編集後のファイル) は Vim の場合も dd の場合も同じ結果になる。

# 編集後のファイル

![pppoe-edit.png](https://qiita-image-store.s3.amazonaws.com/0/44206/869be5db-b753-1afd-15b3-25cd57ed4ae9.png)