
もう1週間経ってしまいましたが、先週は SECCON 東北大会に参加しました。
SECCON といえば、去年は ifconfig というチームでつくば大会に参加して優勝し、全国大会はあえなく撃沈と言った感じでした。
今年は、福島県会津地方で行われた、東北大会に参加しました。宿泊込の、セキュリティトライアスロンです。普通の CTF とは違って、とても面白かったです。ちなみに、上の写真は、猪苗代湖上で遊覧船かめ丸の上で行われた、早押しクイズ大会開催前の様子です...
僕は、開催前日になって熱がでて大変だったのですが、当日は熱も下がったのでアンチウイルスを施して参加しました。あまり体調は万全ではありませんでしたが、いつもの通りエナジードリンク漬けで頑張りました。
結果的には、まあ優勝というわけだったのですが、僕自身よりチーム全員がそれぞれ取り組んだポイントを確実に取っていったところが、結果につながったと思います。とはいえ、いつもどおりくりすの貢献が大きかった気がする。
初めて、アセンブラかるたとバイナリかるたにも挑戦しました。アセンブラかるたはよかったですが、バイナリかるたは僕には無理ゲーでした。
左から、うちのチームのエナジーじドンク消費具合。あくまでセキュリティトライアスロン中のはなしであって、就寝後に追加投入したり、帰宅時に車で参加したので寝ないために追加投入したり、こんなのまだまだ。
中央は、会場の会津藩校日新館からの眺め。会津の盆地に霧がかかっていて幻想的でした。ちなみに、これは朝食の時。ハッカソン2日目が 06:00 - 08:00 でした。
右が、早押しクイズ大会と表彰式が行われた、猪苗代湖。2日目は天候が悪く、紅葉の季節なのに霧がでてあまり綺麗な感じではありませんでしたが、綺麗だったとしても体力が限界を迎える頃で楽しめなかっただろうし、天候なんて関係ないです。クイズ大会は、すごく盛り上がりました。
アセンブラクロスワードパズル write-up
さて、問題の write-up も少しぐらい書かないと怒られそうなので、アセンブラクロスワードパズルだけ、ほぼ僕が解いた問題なので、書いておきましょう。問題はこんなかんじです。(回答が含まれているので注意)
同時に x86 チートシートも配られましたが、あんなもの見ていては解けません。少なくとも、左半分は無理です。
僕は、nasm と ndisasm を駆使しながら、命令表を眺めて手動でバイナリを組み立てていきました。nasm を使う理由は、ELF ではない生のバイナリを直接吐き出せるのと Intel syntax からです。バイナリエディタでの書き換えが楽になります。as でもできますが、いろいろ面倒です。
nasm でアセンブルするときは、bits 32 をファイルに指定してアセンブルします。(指定しないと変なプレフィックスが付きます) ndisasm は -b32 を使うと間違いないでしょう。生バイナリなので、弄りたいときはそのまま ghex などで書き換えやすいです。ちなみに、nasm の吐いた結果を objdump でみたい場合には、objdump -D -m i386 -b binary
命令表はこのサイトが非常に役に立ちます。
http://ref.x86asm.net/coder32.html
手の付け方ですが、わかりやすいところから手を付けると罠に陥るという感じです。ヨコ2の、4byte NOP から手を付けると、間違いなくアウトです。
"x86 4byte nop" とか "x86 multibyte nop" とか検索すると、0f 1f 40 00 が出てくるのが普通だと思いますが、そうなるとタテ2の EAX を3倍にが、うまくいかなくなり手詰まりになります。0f は拡張命令群のプレフィックスなので、その時点で 3byte に収めるのは不可能だと思っていいでしょう、魔法のような命令がない限り。
なので、タテ1とタテ2から埋めてくのが良いです。タテ1は、直後のラベルに call して pop することで、EIP を取得する定番のやつです。
call dummy dummy: pop eax
こういう感じです。ndisasm にかけるとこうなります。
00000000 E800000000 call dword 0x5 00000005 58 pop eax
これができると、ヨコ5も自動的に埋まります。戻り値は eax にぶち込むので、即値を使って 1 を push して、eax に pop すればよいのです。
00000000 6A01 push byte +0x1 00000002 58 pop eax
ヨコ4 も簡単です。ただのメモリからの load だと思いますが、ここ解けなくても別に問題ないです。
00000000 8B00 mov eax,[eax]
次に、タテ2を探しましょう。いろいろ検索していると、アドレッシングの説明で lea を使ってレジスタの内容が実質3倍になる、みたいな命令にたどり着けるかと思います。(Wikibooks の x86 Assenbly のページに乗っていた) または、x86 特有のアドレッシングを使えばできるかも知れない、と閃けば自力でも行けるかも知れません。
00000000 8D0440 lea eax,[eax+eax*2]
さて、ここまで来ると、ヨコ2の先頭バイトが 8d であることが実質決定します。つまり lea を使って nop を組めということです。ここまで来たら実は余裕で、lea eax, [eax] って、実質 nop ですよね?これに、x86 のアドレッシングでディスプレースメントをつけたりすればいけそうだということが、x86 に精通していればわかるのではないかと思います。しかし、普通アセンブラ書いても、思うような結果が出てこないのではないかと思いますので、ハンドアセンブルしましょう。
ちなみに、x86 のアドレッシングは、Mod R/M byte とか SIB byte とかそういうやつを駆使しなければいけないので、これのチートシートが必要です。それも、前述の命令表に含まれています。(http://ref.x86asm.net/coder32.html#modrm_byte_32)
これを元に、destination が eax で、source が [sib]+disp8 をまず探します。これは、これに続く SIB byte で指定したアドレッシングに、ディスプレースメントを追加できる、ということです。なので、まず 44 がでてきます。次に、SIB byte で [foo
00000000 8D442000 lea eax,[eax+0x0]
こうなると、残りはタテ3とヨコ3です。指令を見てみると、タテ3が call で、ヨコ3が push っぽいです。先頭バイトを同じにしなければいけないので、普通の命令じゃないことがわかります。しかもよく見てみると、どちらもオペランドに R/M byte を指定するように思えます。ここで、call と push のオペランドに R/M を取る命令の opcode を探してみると、なんとどちらも ff から始まるではありませんか、これは来たわけです。
--- 余談
が、僕はここで詰まります。問題の日本語の意味をミスって、終わりのない迷宮に迷い込んでしまいました。"第一引数をスタックに詰む"、"引数の指す先を関数呼び出し"、引数はどのような場合の引数を指すのかという問題が...
- ふつーのコンパイラで生成された関数で、ebp からのオフセットを使う場合
- スタックフレームを作らず、渡されたまま esp からのオフセットを使う場合
結局、 この間違いに気づいたのは、回答を何回かダメ元で提出した時に、ebp は使わないと言われてからです。謎が一気にひらめいた瞬間でした。
--- 余談おわり
[追記]
大事なことを忘れていました。ff から始まる2つの命令ですが、これらの Mod R/M Byte を構築する方法です。 call と push の R/M をオペランドに取る命令はこんな感じで表記されてることが多いです。
- push: ff /6
- call: ff /2
あとは、push であれば /digit = 6, [sib]+disp8 の 74 と、SIB byte が esp, none の 24 を組みあわせ、04 のディスプレースメントをつければ完成です。
[追記おわり]
つまり、こんな感じになります。
00000000 FF742404 push dword [esp+0x4] 00000000 FF542404 call dword [esp+0x4]
終わりです。
- タテ1: e8 00 00 00 00 58
- タテ2: 8d 04 40
- タテ3: ff 54 24 04
- ヨコ2: 8d 44 20 00
- ヨコ3: ff 74 24 04
- ヨコ4: 8b 00
- ヨコ5: 8a 01 58
この間違いに一晩悩んでました、悩んでる間に後輩に先越されました、わかった瞬間は割と絶望感漂ってました。賢者モードってやつです。まあ、こういうこともあります。ある答えに行き着くと、周りが見えなくなるの良くないですね... 人間アセンブラ楽しかったです。
全国大会は、更にガチ勢が集まるので、僕が太刀打ちできるような相手ではないかも知れませんが、なんとなく頑張ります。
0 件のコメント:
コメントを投稿