パッカー(packer)とは、実行ファイルを実行できる形式のまま圧縮(暗号化)するツールであり、UPX、FSG、tElockなど様々なものがあります。しかし、種類は違えどパッカーの基本的動作原理はほとんど同じで、「ターゲットとなる実行ファイルを圧縮(or暗号化)して、プログラムの先頭に展開(or復号化)ルーチンを付加する」だけです。こうすることにより、プログラムが実行されたら一番最初に展開ルーチンが実行され、本来の実行ファイルのデータをメモリ上に復元してプログラムが処理されることになります。
それで、今回はそのパッカーを作成してみようというわけですが、パッカー作成に関して大いなる壁になるのは「実行ファイルの先頭に付加する展開コードはアセンブリ言語で書かなければならない」という事態です。
例えば、暗号化処理がビット反転だったなら、アセンブリ言語でも簡単に復号化処理を書くことができますが、LHAやZIP(DESやRSA)のような一般的な圧縮(暗号化)アルゴリズムを使用したい場合、付加する展開コードもさぞかし面倒くさいことになります。まぁCで書いてアセンブラに変換すれば万時解決ですが、それは置いといて。
というわけで、その辺りを考慮しつつ、さらに一般的なパッカーとは少し違う「新しいパッカーを考えてみよう」というのが今回のネタです。
http://ruffnex.oc.to/kenji/crackme/crackme.zip
もし、解析に挑戦したい方は上記のプログラムをダウンロードしてチャレンジしてみてください。ここから先のテキストには少なからずヒントになるようなことが書かれてあるのでご注意ください。
難易度は低く、速ければ10分程度で解くことができますが、一般的なパッカーと比べると少し特殊な構造なので、ハマってしまうと無駄に時間をとられるかもしれません。
もし解けた方はBBSで感想などをくださると有難いです。
ここからは実際の動作原理について解説していきます。といってもソースコードなどは出てきませんが、一応プログラミングに関するテキストなので、読み進めるためにはWindowsAPIと多少の解析スキルを必要とします。
また解析にチャレンジする方は以降のテキストは解析後に読むことをお勧めします。解析する前に読むと、だいたいの動作が分かってしまうため面白さが半減すると思うので。あらかじめご了承ください。
実行ファイルのリソース領域には、本来「ダイアログ」「メニュー」「ビットマップ」などを入れることができますが、カスタムリソースというカタチでバイナリデータを挿入することもできます。よって、この領域に実行ファイルのデータをそのまま格納することができるわけです。
そして、リソース領域は他のプログラムから変更することが可能です。それはPEフォーマットを直接書き換えるカタチではなく、WindowsAPIにより提供されている機能を使うことで実現できます。よって、他のプログラムから「リソース領域の変更と読み込み」を行うことができるわけです。
リソース領域の変更と読み込みを行うための具体的な関数を以下に示します。
----- リソース操作関数 読み込み 書き込み ・FindResource ・BeginUpdateResource ・SizeofResource ・UpdateResource ・LoadResource ・EndUpdateResource ・LockResource -----
これらはWindows2000以降でしか使用することができません。しかし、PEフォーマットの知識を持たなくても実行ファイルのリソース領域にアクセスすることができるため、なかなか面白い使い方ができると思います。
仮にターゲットとなる(パッキングを行う)実行ファイルをtarget.exe、パッキングを行う側のプログラムをpacker.exeとします。
まず、最初にpacker.exeはtarget.exeのデータを読み込み、暗号化します。暗号化アルゴリズムはなんでもよいですが、ここでは分かりやすくビット反転とします。つまり、packer.exeはtarget.exeをビット反転したデータを持つことになります。
次にpacker.exeは雛形となる実行ファイルを新しく作成します。その実行ファイルをmodel.exeとします。
model.exeは自分自身のリソース領域からデータを読み込み、それをビット反転して実行するプログラムにします。つまり、あらかじめ「自分自身のリソース領域からデータを読み込み、ビット反転して実行する」というプログラムが記述してあるmodel.exeを作成しておき、このmodel.exeのデータをpacker.exeに入れておくというわけです。そしてpacker.exeはmodel.exeをそのままのカタチで出力することになります。
packer.exeがmodel.exeを作成したら、今度はmodel.exeのリソース領域を変更します。model.exeは「自分自身のリソース領域からデータを読み込み、ビット反転して実行する」プログラムですが、まだ現時点ではリソース領域には何も入っていないので、実行してもエラーになるだけです。よって、packer.exeがmodel.exeのリソース領域にtarget.exeをビット反転したデータを格納します。
これで、model.exeを実行したら自分自身のリソース領域からビット反転されたtarget.exeのデータを取り出して実行することになり、つまりは、target.exeを暗号化した実行ファイルがmodel.exeということになります。これでpacker.exeの処理は終わります。
----- (プログラムの一連の流れ) +---------------+ +---------------+ | | | | | model.exe | <---------- | packer.exe | | | | | +---------------+ | +-----------+ | | model.exe | +---+-----------+ (1)packer.exeがmodel.exeを作成 +---------------+ +---------------+ | | | target.exe | | model.exe | +---------------+ | | +---------------+ | +------------+ | | | | target.exe | <---------- | packer.exe | +--+------------+ | | | +-----------+ | | model.exe | +---+-----------+ (2)packer.exeがtarget.exeを読み込み、 暗号化してmodel.exeへ挿入 (3)model.exeが暗号化後のtarget.exeとなる (4)model.exeは自分自身のリソース領域(target.exe)を 読み込み、実行するコードを持っている -----
上記がpacker.exeの一連の流れとなります。
model.exeはリソース領域に存在するtarget.exeを取得して実行するわけですから、つまりはローダーの役割を担うことになります。
もちろん、target.exeのバイナリデータを一度実行ファイルとして出力し、それをCreateProcessを使って起動してもよいですが、それだとアンチリバースエンジニアリングという方面から見るとかなり軟弱になってしまうので、やはりメモリ上からそのままロードするカタチがよいでしょう。となると、やはり自前でローダーを用意することになってしまいます。
model.exeは「自分自身のリソース領域からデータを読み込み、ビット反転して実行する」というプログラムと書きましたが、この「実行する」という部分はmodel.exe自身がローダーの役割を果たすという意味に他なりません。しかし、ちゃんとしたローダーを作成するのは少し骨が折れるので、今回はインチキなローダーを作成しました。
まず、何でもよいので適当なプロセスを起動します。まぁcalc.exe(電卓プログラム)などが手頃でしょう。そして、そのプロセスが実行される前に停止させ、プロセス空間内のデータをすべて、target.exeのデータに置きかえます。そしてその停止を解除させることで、生成されたプロセスはtarget.exeとして動作することになります。本当の意味でのローダーの処理は行っていませんが、結果的にローダーと変わらない動作を行うプログラムとなります。
このような処理をあらかじめmodel.exeに組み込んでおくことで、リソース領域内のデータを読み込み、実行させることができます。
以上で、概略的な動作原理の解説は終了です。よってここでは少し解析へのヒントを書くことにします(といってもヒントを書くほど難しくないかもしれませんが^^;)。
まず動作原理が一般的なパッカーとは若干異なるため、セオリー通りに解析していっても解けないかもしれません。アセンブリコードを見ても分かるとおり、コード自体が難読化されているわけではないので、読み進めるのはかなり容易でありその動作原理を解読するのにもさほど時間はかかりません。
そして、原理が分かってしまえばいとも簡単にオリジナルコードにたどり着けるため、どれほど正確にマシン語を追い、動作原理を掴むかが攻略のポイントになると思います。
解析する方法はいろいろとありますが、OllyDbgだけで十分こと足ります。
速ければ10分ほどでパスワードを得られると思いますが、一度ハマってしまうと1、2時間くらいかかってしまう場合もあるかもしれません。しかし、ソースコードは見えてるも同然なので、ポイントさえ抑えれば問題ないでしょう。
さて、いかがだったでしょうか。今回はこれまでと少し変わってテキストベースで進めてみました。個人的には「ソースコードがないと、というか実際に動作しているプログラムがないと本当に実現可能かどうかを証明できないじゃん!」という考えなのですが、ソースコードばっかりのテキストも読むのが辛くなったりするかなぁ、と思いまして、今回はこういったテキスト形式にしてみました。
ただし文章内でソースコードの解説をしていないだけで、crackmeやpackerのソースコードはちゃんと書いているので、テキストが少ない割に結構な労働力だったりしました。なので出来ればcrackmeやソースコードも併せて楽しんでもらえたらと思います。ちなみに今回のテキストの解答編も1ヶ月後くらいに行いますので、お楽しみに〜(^^;。
さて、最後になりましたが、ここまで読んでくれて本当にありがとうございま す。
では、また会う日まで...