バイナリ列の解答/解説(第5回)

ということで第5回です。
j(ry

簡単だよ!

簡単だよ。
パスを見つけるのすごく簡単。


実行してみて!

ということで、すごく簡単です。
本当に実行するだけです。


え?
"spcamp!"が出るだけだって?
それは実行環境が違うからです。

スタブプログラム

答えに移る前に少し別のことを。
PEファイルにはよく"This program cannot be run in DOS mode."ってのが埋まっているのですが、皆さんはこれが何か知っていますか?
これ、実はある環境でこのファイルを実行したときに表示されるメッセージなのです。

ではその「ある環境」とはなんなのでしょうか。


実はPEファイルの先頭にはMS-DOS用のスタブが埋まっています。
つまりMS-DOSの環境で誤って(?)実行した場合、「MS-DOSでは動かないよ!」ってメッセージを出力して終了するプログラムが通常埋め込まれています。

実行

いろいろ説明する前に実行してみましょう。
以下の画像はVMware Player上のMS-DOSで実行した結果です。


ね、簡単でしょう?


ということでパスは

spcamp_2010_16th_floor

です。

ということで解凍するとなにやら変なファイルができました。
さらに今後解いていきます。
今回はここまで!

プログラムなど

今回のPEファイルを作成するのに作ったプログラムを載せておきます。
ご自由にお使いください。
(使えるようなものであるかは微妙ですが・・・)

PEファイル本体のプログラム
#include <stdio.h>

int main( void )
{
	puts( "spcamp!" );

	return 0;
}

見ての通り"spcamp!"を出力するだけのプログラムです。
なのでいくらこちらを解析しても何もでてきません。 ←

ちなみに容量削減のためコンパイルにはVC++2010EEを使って以下のようにコンパイルしました。

 >cl /O1 /MD spcamp.c

/O1は最適化スイッチで容量を少なくする最適化です。
(意味あるかわからないですが念のため。ちなみによく使われる/O2は速度の最適化ですね)
/MDはランタイムライブラリを使う指定です。

スタブプログラム
CODE	SEGMENT
	ASSUME	DS:CODE,CS:CODE,ES:CODE,SS:CODE
	.186
	ORG	0h

START:
	PUSH	CS
	POP		DS
	MOV		DX, 0Eh
	MOV		AH, 09h
	int		21h

	JMP	DISP

	NOP
	NOP
	NOP

	DB 'This program cannot be run in DOS mode.', 0Dh, 0Dh, 0Ah, '$'
	DB 0B3h, 0DCh, 0B9h, 0BDh, 0F7h, 031h, 09Ah, 0AAh, 0DFh, 038h, 0C4h, 0DFh, 0CBh, 0F1h, 0F0h, 014h, 0E7h, 0BAh, 041h, 00Eh, 059h, 0DDh, 0Dh, 0Dh, 0Ah, '$'
	DB 0C0h, 0ACh, 0DAh, 0DCh, 09Ah, 041h, 0C5h, 098h, 0EFh, 009h, 0F4h, 080h, 0FAh, 0C7h, 084h, 07Ch, 0B8h, 0DCh, 02Dh, 061h, 036h, 0AFh

DISP:
	MOV		CX, 16h
	MOV		BX, 39h
	MOV		SI, 53h

TRANS:
	MOV		AH, BYTE PTR [SI]
	XOR		BYTE PTR CS:[BX], AH
	INC		SI
	INC		BX
	LOOP	TRANS

	MOV		DX, 39h
	MOV		AH, 09h
	int		21h

	MOV		AX, 4C01h
	int		21h

CODE	ENDS
	END	START

最初の部分は"This program〜"を出力するためのプログラムです。
これは一般的なプログラムのスタブと同じプログラムとなっています。
なので比較しても違いがないかと思います。


JMPとNOPは元々のスタブプログラムを書き換えた部分です。
本来ここにはプログラムを終了するためのシステムコールがありました。
それを書き換えて下の部分にジャンプさせています。


DISP以降が今回この為に追加した部分です。
内容としてはデータのXORをとってデータを復元(TRANSラベルからLOOP命令の部分)したあと出力して終了するようなプログラムです。
バイナリエディタで見てもそのような文字列がなかったのに・・・」というのはXORでデータを隠してるためです。
そんなバイナリエディタでパッと見てわかるような埋め込み方なんてするわけないでしょ? ←
逆に言うとXORを取るだけで簡単に隠蔽できたり・・・
#ただし、だからと言って重要な暗号ではXORは危険だからね!
##XORを取る対象が完全にランダムで予測ができないデータであれば逆に解読不可能で最強ではあるんだけどね・・・

少し解説

VC++コンパイラだとリンカオプションで/STUBを使うと任意のMS-DOSプログラムを埋め込めたりします。
逆に一般的には別途スタブプログラムを用意しないためデフォルトのスタブプログラムが使われ、結果"This program〜"ってものを表示して終了するプログラムが埋め込まれるわけです。


今回は自作のスタブプログラムを作ったのですが、うまく作る方法がわからなかったため、スタブを作ったあとバイナリエディタを用いて埋込みを行ないました。
なぜかと言うとMS-DOSのプログラム自体が(なぜか)大きくなり、/STUBで埋めこむと妙にスタブ領域が大きくなってしまったためです。
そのため通常のスタブプログラムの大きさ以内におさめ*1、それを手動で埋め込みました。


ところで気になったのがデフォルトのスタブプログラムでは"This program〜"の以前でプログラムは終了するため、"This program〜"の表示文字列のデータのあとは何も使われないはずなんですよね〜。
なのにそのすぐには本来のPEファイルのデータ("PE\00\00"から始まるPEフォーマット)がこなくて別のよくわからないデータが入っていたのです。
これ、なんなのですかね?
まぁ、今回はその領域を利用してパスを表示するプログラムを埋め込まさせてもらいましたがw

あとがき

ね、簡単だったでしょ? ←
だって実行するだけの簡単な作業ですからね! ←←
解析も何も必要ないです。
そのように作りましたからw


結構ここまではたどりつけてたのにこれがわからなくて次に進めない方が多くいたように感じました。
まぁ一番手をかけたので私としてはほくほくw ←


ちなみに少し裏話。
なぜ"This program〜"を表示するプログラムをわざわざ入れたかというと、「その前のビットマップに埋め込まれているデータがPEファイルであるということが簡単にわかってもらうため」というのが1つですが、もう1つ「MS-DOS用スタブには何も手を加えていない」という先入観を持たせるためですw
極端な話、前者のは"MZ"とか"PE"というのを見ればPEファイルだっていうのは見る人が見ればわかるはずです。
それをあえて"This program〜"を入れたのはよりたくさんの人にわかってもらうためです。
後者のはこの部分があまりにも一般的なものと変わっていた場合、必ず疑うと思うのです。
しかし、このようにしておいたら気づく人は減るんじゃないかなぁ〜と。
ということで、わざわざ一般的なスタブプログラムを残しつつ別のプログラムを埋め込んだのでしたw

*1:結果的には気にしなくてもデフォルトのスタブよりも小さかったですが。