Linuxで大きめのファイルの扱えるバイナリエディタ

2Gほどのバイナリファイルを開こうと思ったらエラーになったり読み込むのに時間が掛かったりして苦労したことがあったので、そうゆう時のために大きめのファイルの扱えるバイナリエディタを調べてみました。ちなみにその時はHexcurse + dd + catで目的は達成できたように記憶しています。

今回使用した環境はLubuntu16.04（64bit）、メモリ4Gです。/dev/urandomを使って作成した8Gくらいのファイルを指定してストレスなく起動するかどうかで判断しています。エディタは標準パッケージからインストールできるもののみを対象としています。

## 大きめのファイルが扱えたバイナリエディタ
- Bless Hex Editor - A full featured hexadecimal editor（注１）
- DHEX - ncurses based hex editor with diff mode
- Hexcurse - Ncurses-based hex editor with many features
- hexedit - view and edit files in hexadecimal or in ASCII
- lfhex - large file hex editor
- Ncurses Hexedit - Edit files/disks in hex, ASCII and EBCDIC
- shed - simple hex editor with a pico-style interface
- Tweak - Efficient text-mode hex editor（注２）
- wxHexEditor - hexadecimal editor for massive files

（注１）Bless Hex Editorはファイルを開くことはできましたが表示がうまくされずエディタとして使用することができませんでした。
（注２）Tweakはファイルは開けますが保存に時間がかかります。

## それぞれの特徴
特徴というほどでもないのですが使ってみて思ったところを書きます。ディスクの読み書きについてはファイル名として/dev/sdb1のように指定することで読み書きができるものもありますが、ここではヘルプ等で明記されているものだけを書いています。

変更すると即保存されてしまうコマンドもありますので、ちょっと試す場合でもバックアップを取ってから行う事をおすすめいたします。

### Bless Hex Editor
アプリの種類: GTK2.0
インストールコマンド: `sudo apt install bless`
起動コマンド: `bless [file]...`
ファイルは開けましたが0x00-0x19と0xe6-0xffあたりの16進数が表示されないという不思議な現象が起きてエディタとして使うことができませんでした。Ask Ubuntuでも同様の書き込みがありましたので私だけではないようです。参考URL）https://askubuntu.com/questions/818477/problem-when-displaying-hex-bytes-in-bless

### DHEX
アプリの種類: コンソール
インストールコマンド: `sudo apt install dhex`
起動コマンド: `dhex [options] file...`
終了キー: F10（変更可）
保存キー: F10（変更可、終了時にSaveを選択する事で保存）
ファイル保存といった操作はファンクションキーで行いショートカットキーは使えません。最初の起動時にキーの割当を設定する画面が表示されるのがちょっと面倒ですがLubuntuデフォルトではF10でファイルメニューが開いてしまうため別のキーに割り当てるといったことも可能です。１）オフセット移動（16進数のみ）。２）計算機能。３）ファイルの比較。４）-o*でオプションで起動時のオフセット指定。といった機能があります。

※今回使用したバージョンは0.68でしたが0.7では最初の起動時でもキーの割当をする画面は表示されませんでした。ファンクションキー以外にもShift+*に対応したため')'（JISキーボードの場合Shift+9）で終了できます。

### Hexcurse
アプリの種類: コンソール
インストールコマンド: `sudo apt install hexcurse`
起動コマンド: `hexcurse [options] file`
終了キー: CTRL+q 又は F8
保存キー: CTRL+s 又は F2
画面をぱっと見た感じはファンクションキー操作オンリーに見えますが、実際にはショートカットキーも使えます。アドレス表示を16進数と10進数に切り替えることができまF1でヘルプ表示できます。

### hexedit
アプリの種類: コンソール
インストールコマンド: `sudo apt install hexedit`
起動コマンド: `hexedit [options] file`
終了キー: CTRL+c
保存キー: CTRL+w 又は F2
F1でヘルプ表示できます。コピーペーストや選択した範囲を別のファイルへの書き出すといった機能があります。

### lfhex
アプリの種類: Qt4
インストールコマンド: `sudo apt install lfhex`
起動コマンド: `lfhex [options] file...`
かなりシンプルなバイナリエディタです。HEXとASCIIを同時に表示することができないかわりに画面いっぱいのHEX表示や画面いっぱいのASCII表示といったことができます。Qtを使っている関係上Lubuntuの場合だとインストールサイズが大きめになります。

### Ncurses Hexedit
アプリの種類: コンソール
インストールコマンド: `sudo apt install ncurses-hexedit`
起動コマンド: `hexeditor [options] file`
終了キー: CTRL+c
保存キー: CTRL+o
CTRL-gでヘルプ表示。１）オフセット移動（16進数のみ）。２）追加・削除（起動オプションで-bを指定しメモリーに読み込む必要があります）。３）ディスク読み書き（起動オプションで-dを指定、書き込みもする場合は-fも指定）。

### shed
アプリの種類: コンソール
インストールコマンド: `sudo apt install shed`
起動コマンド: `shed [options] file...`
終了キー: X
保存キー: なし（変更すると即保存）
変更すると即保存されてしまうので注意が必要です。またjump toで0x100000000を指定しても0x0にジャンプしてしまうようです、それ以降にジャンプしたい場合は4Gのように単位指定すればジャンプできます。

### Tweak
アプリの種類: コンソール
インストールコマンド: `sudo apt install tweak`
起動コマンド: `tweak [options] file`
終了キー: CTRL+x CTRL+c（２ストローク）
保存キー: CTRL+x CTRL+s（２ストローク）
追加・削除が行える数少ないエディタですがその代償として保存は遅いです。操作キーはemacsライクらしく、F1でヘルプ表示やファンクションキーによる操作、CTRL+Cでの終了といったことはできません。事前にヘルプを読んでおいた方が良いかなと思います。

### wxHexEditor
アプリの種類: GTK3.0
インストールコマンド: `sudo apt install wxhexeditor`
起動コマンド: `wxHexEditor [file]`
hexのhとeditorのeが大文字というのがちょっと違和感がありますがコマンド名はwxHexEditorで正しいです。50MBを超えるファイルを読み込むと読み取り専用（Read-Only）となりますが、Option → File ModeからWritebleを選択すれば保存できるようになります。１）オフセット移動。２）ファイルの比較。３）ディスクからの読み取り（WritableやDirect Writeを選択しても私の環境では書き込みができませんでした）。４）追加・削除。等々、今回調べたエディタの中では最も機能が豊富ですが、GTK関係のエラーが出るところが気になるところです。

## おまけ
バイナリエディタは内容を表示したりするのは便利なんですが、編集したり計算したりするはコマンドの方が便利というか、慣れているので、そのメモです。

### ddでバイナリファイルから指定範囲内を切り出す。
ddコマンドを使えばバイナリファイルから必要な部分を取り出すことができます。

```console:16バイト目から256バイト分取り出す
$ dd if=in.img of=out.img bs=1 skip=16 count=256
```

16進数や計算式で指定することもできます。

```console:16進数や計算式で指定する
$ dd if=in.img of=out.img bs=1 skip=$((0x10)) count=$((0x10*16))
```

### catでバイナリファイルを結合する。
catコマンドはテキストファイルだけでなくバイナリファイルも連結することができます。

```console:in-1.imgとin-2.imgを結合したout.imgを作成する
$ cat in-1.img in-2.img > out.img
```

### ddでバイナリファイルの一部を書き換える
元のファイルを書き換えるということはあまりしないのですが、一箇所だけ書き換えたい時とか、別のファイルで書き換えたい時とかに使えます。countは省略可能ですがつけた方が安心かなと思います。またconv=notruncを忘れるとファイルが切り詰められてしまうので注意が必要です。

```console:16バイト目から2バイトを\x00\xffに書き換える
$ printf "\x00\xff" | dd of=out.img seek=16 bs=1 count=2 conv=notrunc
```

```console:out.imgの16バイト目から256バイトをin.imgで書き換える
$ dd if=in.img of=out.img seek=16 bs=1 count=256 conv=notrunc
```

### メディアをイメージファイルに変換して編集する
メディアを直接読み書きできるエディタもありますが、可能ならイメージファイルに変換して編集する方が安全に作業できるかなと思います。

```console:メディアをイメージファイルに変換
$ sudo dd if=/dev/sdb of=usb.img
```

イメージファイルに変換してしまえばバックアップをとったり、解析したり、編集したりするのがかなり楽になります。イメージファイルはループバックデバイスを使用すればハードディスクのようにマウントすることができますが、今回のようにディスクを丸ごと変換している場合はoffsetオプションを使用してパーティションを指定する必要があります。

```console:パーティション1を/mntにマウント
$ fdisk -l usb.img 
Disk usb.img: 1.9 GiB, 2004877312 bytes, 3915776 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x68f7c20e

Device     Boot Start     End Sectors   Size Id Type
usb.img1         2048 1002047 1000000 488.3M  c W95 FAT32 (LBA)

$ sudo mount -o loop,offset=$((2048*512)) usb.img /mnt
```

またイメージファイルはVDI形式に変換すればVirtualBoxで使用することもできますので、編集作業の全てをVirtualBox上で行うことも可能です。

```console:イメージイメージをVDI形式に変換
$ VBoxManage convertfromraw usb.img usb.vdi --format VDI
```

VDI形式を元のイメージファイル形式に戻す事もできます。実行時にdoes not match the value {dc61fe63-...}みたいなエラーが表示された場合は、VirtualBoxを起動してFile → Virtual Media Managerで該当のvdiファイルを削除するとエラーがでなくなります。

```console:VDI形式をイメージイメージに変換
$ VBoxManage clonehd usb.vdi usb-fromvdi.img --format RAW
```
### 10進数や16進数の相互変換や計算

エディタによっては計算機能のあるものもありますが、やっぱり慣れているコマンドの方が楽なのでそのメモです。

printfを使って16進数を10進数に変換、10進数を16進数に変換。

```console:printfを使って16進数を10進数に変換
$ printf "%#x\n" 100 #10進数→16進数
0x64
$ printf "%#d\n" 0x100 #16進数→10進数
256
$ printf "%#d\n" $((0x100)) #16進数→10進数
256
```

bashの計算式とprintfを組み合わせることもできます。

```console:計算式＋printf
$ printf "%#x\n" $((0x100*2+100*4)) #256*2 + 100*4
0x390
$ printf "%#d\n" $((0x100*2+100*4)) #256*2 + 100*4
912
```

大きな数値（64bit）も扱えます。OSのbit数には依存しないので32bitのLubuntuでも同じ結果になります。

```console:大きな数
$ printf "%#x\n" -1
0xffffffffffffffff
$ printf "%#d\n" $((0xffffffffffffffff))
-1
$ printf "%#u\n" $((0xffffffffffffffff))
18446744073709551615
$ printf "%#d\n" $((0x7fffffffffffffff))
9223372036854775807
$ printf "%#d\n" $((0x8000000000000000+1))
-9223372036854775807
$ printf "%#u\n" $((0x8000000000000000+1))
9223372036854775809
```

エディタ画面等から16進数をコピー・ペーストして10進数に変換する際のメモです。値が32bitリトルエンディアンで格納されている場合とかに使えます。

```console:
$ echo "11 22 33 44" | xxd -p -r | od -An -td4 --endian=little
  1144201745
$ echo $((0x44332211)) #念のため同じなのかを確認
  1144201745
$ echo "55 66 77 88" | xxd -p -r | od -An -td4 --endian=little
 -2005440939
$ echo "55 66 77 88" | xxd -p -r | od -An -tu4 --endian=little
 2289526357
$ echo "55 66 77 88" | xxd -p -r | od -An -tu2 --endian=little
 26197 34935
$ echo "55 66 77 88" | xxd -p -r | od -An -td2 --endian=little
  26197 -30601
```

変数に代入できますので計算したりすることもできます。

```console:変数に代入して計算
$ n=$(echo "11 22 33 44" | xxd -p -r | od -An -td4 --endian=little)
$ echo $n
1144201745
$ echo $((n + 1024))
1144202769
$ echo $((n + 0x400))
1144202769
```

ファイルから取り出して計算する事もできます。

```console:ファイルから直接変換して計算
$ od -tx1 -N 16 data.img
0000000 00 00 11 22 33 44 00 00 00 00 00 00 00 00 00 00
$ n=$(dd if=data.img skip=2 bs=1 count=4 2>/dev/null | od -An -td4 --endian=little)
$ echo $n
1144201745
$ echo $((n + 1024))
1144202769
$ echo $((n + 0x400))
1144202769
```