yyy

CTFつよくなりたい

画像のバイナリを読んでみた話

この記事は Aizu Advent Calendar 2016 13日目の記事です。

前の人は、@je_je_laさん、次の人は@kobadさんです。

はじめに

SECCON2016お疲れ様でしたって感じで、SECCONが終わったばかりなのでそのWriteupを載せてもいいかなとか少し考えていたのですが、2問しか解いていないのでやめました。

元々ALTで話したQRコードの復元について書くつもりでいたのですが、計算ミスしているのか完全復元ができていない状態なので、他になにかないかなーと思っていたところ、ふと思い浮かんだのが「画像のバイナリを読む」っていうやつ。

読んでみた形式

PNG

BMP

用意した画像

上の2つの形式でそれぞれ100 x 100ピクセル、一面真っ白な画像。

PNG

とりあえず開いてみる。

vim -b white.png

f:id:ywkw1717:20161213043231p:plain

vimバイナリモードで開いた状態。

このままでは流石に読めないので、%!xxdで16進ダンプする。

f:id:ywkw1717:20161213043319p:plain

これなら読める

早速読んでいく。

"8950 4e47 0d0a 1a0a" の部分がシグネチャ

"0000 000d" がIHDRチャンクのlength(13bytes)

"4948 4452" IHDRであることを示すChunk Type

"0000 0064 0000 0064 0806 0000 00" がdata部分

"70 e295 54" がIHDRチャンクのCRC

"00 0000 06" がlength(6bytes)(6は、カラータイプがトゥルーカラーであることを表している)

"62 4b47 44" がbKGDチャンク(背景色、設置は任意、複数の設置はダメ、場所はPLTEの後、IDATの前)であることを示すChunk Type

"00 ff00 ff00 ff" がdata部分 (背景として使われるRGBカラーが示されていて、それぞれ左から2bytesずつR,G,Bの値)

"a0 bda7 93" がbKGDチャンクのCRC

"00 0000 09" length(9bytes)(常に9)

"70 4859 73" pHYsチャンク(物理的なピクセル寸法を示す、設置は任意、複数はダメ、場所はIDATの前)

"00 000b 11" X軸上の単位あたりのピクセル

"00 000b 11" Y軸上の単位あたりのピクセル

"01" 単位指示子(1の場合はメートル)

"7f64 5f91" がpHYsチャンクのCRC

"0000 0007" length(7bytes)(常に7)

"7449 4d45" tIMEチャンク(イメージの最終更新時間、設置は任意、複数はダメ、場所はIHDRとIENDの間ならどこでもよい)

"07e0" 年 (2016)

"0c" 月 (12)

"09" 日 (9)

"0d" 時 (13)

"03" 分 (3)

"11" 秒 (17)

"2b 2df3 cb" tIMEチャンクのCRC

"00 0000 a2" length(162bytes)

"49 4441 54" IDATチャンク

"78 daed d101 0d00 0008 c330 c0bf e763 8390 4ec2 da49 523a d358 0044 4080 0808 1001 0122 2040 0444 4080 0808 1001 0122 2040 0444 4080 0808 1001 0122 2040 0808 1001 0122 2040 0444 4080 0808 1001 0122 2040 0444 4080 0808 1001 0122 2040 0444 4080 0808 1001 0122 2040 0444 4080 0808 1001 0122 2040 0444 4080 0808 1001 0122 2040 0404 8880 0808 1001 0122 20df 5b08 6004 c4be f58b 07" data部分

"00 0000 00" length(0byte)

"49 454e 44" IENDチャンク(場所は一番最後)

"ae 4260 82" CRC

シグネチャと3つの必須チャンクで構成すれば、それがPNG画像の最小構成になるのですが、用意した画像にはどうやら"bKGDチャンク"と"pHYsチャンク"、"tIMEチャンク"が含まれていたようです。

BMP

vim -b white.bmp

f:id:ywkw1717:20161213043844p:plain

16進ダンプ。

f:id:ywkw1717:20161213044012p:plain

ここまではPNGと一緒です。

なにか違うことをやってみたいと思います。

BMPPNGと違って無圧縮です。

よって、このバイナリをビットマップ表示させればそのまま表示できるはずです。

しかし・・・他のバイナリエディタであればこのような機能を実装している場合があるのですが、できれば使い慣れたVimを使いたい・・・!

でもVimテキストエディタ・・・そんなことができたらいいなと夢見つつ・・・呼んでみましょう

暗黒美夢王さん、タスケテー

github.com

vinariseドーーーーーーン!!!!

なんと、ビットマップ機能が付いたバイナリエディタプラグインが既に作られていたらしいです。

有り難や・・・と感謝しつつ、早速

:Vinarise

からの B でビットマップ表示。

f:id:ywkw1717:20161213043019p:plain

素晴らしいですね。

ファイルヘッダや情報ヘッダ部分以外を抜きにして、しっかりと白くビットマップ表示されています。

試しに、用意していた黒い画像のほうも。

vim -b black.bmp
:Vinarise
B

f:id:ywkw1717:20161213045507p:plain

しっかりと黒く表示されています。

休憩はこのくらいにして、本題に戻ります。

先ほどの16進ダンプした結果である、

f:id:ywkw1717:20161213044012p:plain

このバイナリを読んでいきます。

ファイルヘッダ(14bytes)

"424d" BMというビットマップのシグネチャ(2bytes)

"aa75 0000" ファイルサイズ(リトルエンディアンなので、30122bytes)

"0000" 予約1(将来の拡張用に予約されている)(2bytes)

"0000" 予約2(2bytes)

"7a00 0000" イメージデータオフセット(BMPファイルの最初からイメージデータの先頭までのバイト単位でのオフセット)(4bytes)(122)

情報ヘッダ

"6c00 0000" ヘッダサイズが108なので、V4かV5タイプ(INFOタイプを拡張した情報ヘッダ形式)(108)

"6400 0000" 幅(100)

"6400 0000" 高さ(100)

"0100" プレーン数(常に1)

"1800" ピクセルごとのビット数(1画素辺りのデータサイズ)(ex. 256色BMP = 8)

"0000 0000" 圧縮タイプ

"3075 0000" イメージデータサイズ(画像データ部のサイズ)(30000)

"120b 0000" 水平解像度

"120b 0000" 垂直解像度

"0000 0000" カラーインデックス数(格納されているパレット数、使用している色の数)

"0000 0000" 重要インデックス数(重要なパレットのインデックス)

"4247 5273" 色空間のタイプ(sRGBがリトルエンディアンで格納されて、BGRs)

・・・・・・・

7aからデータ部分が始まる。

画像データは左下から右上に向かって記録されています。

よって、最初の数バイトを0で埋めて書き換えたりすると、当然左下の数バイトが黒くなったりします。

f:id:ywkw1717:20161213050326p:plain

まとめ

JPGも資料見てみたりしたら、マーカーとかで区切られていて、さらに複数のセグメントで構成されているらしく(必須のセグメントもあるらしい)PNGのチャンクっぽいとか思った。

個人的には一番PNGが読みやすいなと感じた。

バイナリを読んでいるとき、「あっ、こんな風に格納されているのか・・・」と感動する時がある。

楽しい。

参照

http://hoshi-sano.hatenablog.com/entry/2013/08/18/112550

http://www.setsuki.com/hsp/ext/png.htm

https://www.ruche-home.net/program/bmp/struct#info-header-v4-compression