また、この記事の単体のHTMLファイルがあります。
単体のHTMLファイルの方がスタイルが良い感じになっているので、出来ればそちらを読んでください。
東京工業大学の公認サークル「ロボット技術研究会」のブログです。 当サークルの日々の活動の様子を皆さんにお伝えしていきます。たくさんの人に気軽に読んでもらえると嬉しいです。
新歓特設ページ ロボット技術研究会 HP ロボット技術研究会 twitter公式アカウント
さて今回書く内容についてですが、最近ロ技研ではLinux が流行っているらしいので、あえて
Windows
に関するちょっとテクニカルなお話をしたいと思います。
Windowsといえばおそらく大多数の人が初めて触れたOSであると思いますが、技術的な視点から触れる機会は少ないのではないでしょうか。今回はそんなWindowsに普段とは違った一面を感じていただければと思います。
今回紹介する内容は一言でいうと上の見出しのようになるのですが、「なんのこっちゃ」と感じる人がほとんどだと思うので1つずつ説明することにします。
みなさんがこのブログを見る際に使っているブラウザやメールソフトなど、様々なアプリは「プロセス」という単位でPC上を動いています。こういったプロセスに、自分の書いたソースコードをDLL (ダイナミックリンクライブラリ) という形にして注入 (インジェクション) する技術のことをDLLインジェクションと呼びます。こうすることで対象のプロセス上で、自作した任意のコードが動かせることになり、事実上アプリを乗っ取ることができます。コンピューターウイルスなどがよく用いる手法だったりします。
プログラム上でwindows特有の機能を使う場合には、必ずWindowsAPI と呼ばれる関数群を介することになりますが、このAPIの処理をこっそり独自の処理にすり替えてしまう技術がAPIフックと呼ばれるものです。これによって、例えばアプリがファイルを開くためにOpenFileといったWindows APIの関数を呼び出そうとしても、APIフックにより自前の処理に誘導してしまうことが可能になるわけです。
要するに、これらはいわゆる「ハッキング」に使われるような仕組みですね。次はこれらの詳細な仕組みや利用方法を実際のプログラムを通して見てみることにします。
今回対象とするプログラムは、簡単のために次のように「進捗どうですか」をメッセージボックスで3回表示させるようなもの (TestEXE.exe)です。
このプログラムにDLLインジェクションとAPIフックを行い、「進捗どうですか」のメッセージを改変させてみようと思います。
必要なファイルは次の2つです。
以下では2つのプログラムをソースコードを交えて解説していきます。
WindowsAPIにはLoadLibraryという、引数としてDLLのファイルパスを指定するとDLLを読み込んでくれる関数があります。対象プロセスがこの関数を実行してくれればDLLインジェクションが成功して万事解決というわけです。したがって、この実行ファイルの目的は
対象プロセスに注入したいDLLのファイルパスを書き込んでLoadLibraryを実行させる
ということになります。手順としては以下のような感じです。
...以下淡々とコードの説明となるので、飛ばしたい方はこちらをクリックしてください
ようやくプロセス内にDLLが入り込めたので、続いての作業はDLL自身が担うこととなります。このDLLが行う仕事はAPIフックを行ってメッセージボックスを呼び出す関数 (MessageBoxW)の処理をすり替えることです。APIの関数のアドレスは、対象プログラムのexeファイルに存在するインポートセクションという領域で管理されているので、これを書き換えてしまえばAPIフックが実現できます。
ここでの手順は次のようになります。
...この後延々とコードの説明が続くので、飛ばしたい方はこちらをクリックしてください。
まず、置き換え先の関数Hook_MessageBoxWの定義は、本来のMessageBoxWの定義に似せて、次のようにします。
ざっくりと説明すると、Hook_MessageBoxWでは、メッセージボックスの本文だけを「ぽぽぽぽぽぽぽぽ」に変えたMessageBoxWを実行しています。その際、return文の箇所を
のようにMessageBoxWをそのまま書いてしまうと、MessageBoxWをHook_MessageBoxWに置き換える処理のために無限ループが生じてしまうため、GetProcAddressを用いて、置き換える前のMessageBoxWを使用しています。
さて、DLLがプロセスに読み込まれた際、DLLにおけるmain関数に相当するDllmain関数が実行され、CreateThread関数によりapiHook関数が実行されるのですが、この関数の中でAPIフックに関する一連の処理が行われています。
apiHook関数では、まず対象プログラムのインポートセクションにアクセスすることから始めるのですが、その際にImageDirectoryEntryToDataというAPIを用いると便利です。ここではインポートされているモジュール名に、MessageBoxWを取り扱うモジュールであるuser32.dllが含まれているかを探しています。
モジュール名が見つかった後は、それぞれAPIの名前とアドレスを扱うインポートネームテーブルとインポートアドレステーブルを探し、MessageBoxWという文字列がインポートネームテーブルに存在するかを調べます。もし見つかれば対応するインポートアドレステーブルをHook_MessageBoxWのアドレスに書き換えてしまえばよいのですが、この領域は読み取り専用となっており、書き換えが不可能なので、VirtualProtectを用いて領域のアクセス権をいじってやればよいです。
これにて、MessageBoxWの処理がすべてHook_MessageBoxWに置き換わることとなったので、予想通り行けばすべてのメッセージボックスの本文が「ぽぽぽぽぽぽぽぽ」に書き換えられます。
1つ目のダイアログが「進捗どうですか」と表示されている間にInjectEXE.exeを実行すると、InjectDLL.dllが対象プロセスに注入され、APIフックによりMessageBoxWの処理がHook_MessageBoxWに置き換わります。その結果、2つ目以降のダイアログは次のようになります。
やったね。ちゃんとテキストが置き換わったね。
さて、長々とした説明となってしまいましたが、いかがでしたでしょうか。
今回はメッセージボックスの本文を変えるという単純なサンプルであったわけですが、注入するコード次第で様々なことができるのはとても面白いのではないでしょうか。例えば、いわゆる「ウイルス対策ソフト」がPCを監視するための方法にもこういった手法が使われていたりするそうです。
いずれにせよ、この記事を読んでくれた方が多少なりとも興味をもってもらえればと思います。
最後までお読みくださりありがとうございます!
※効果には個人差があります。
どうも。らりおです。
この記事は rogy Advent Calendar 2015 の2日目です。
さてこの度、 rogy Advent Calendar 2015
におきまして、「シェルスクリプトは救い」と題した記事を書くことを予定しておりました。
というのも、なかなか私の要求を満たす静的サイトジェネレータが見付からず、
ならば自力でシェルスクリプトで書こうか、などと考えていたためでございます。
しかしその目論みは崩れ、 nanoc に救われてしまったため、
急遽予定を変更し、
シェルスクリプトがあなたを救う、その理由
シェルスクリプト成果物 自慢大会 紹介
の二本立てでお送りいたします。
あと、この記事は†強い†人向けのものではありません、念の為。
Rubyに圧倒的感謝。