小物を設計する〜割り込みハンドラをCで
もともと私の仕事は、ライブラリ屋(小道具屋?)。 いろんなシステムハウス・ソフトハウスに、ライブラリやオブジェクト、資料なんかを提供しています。 今回は、プロテクトモード・モニタをC言語で作るときに使えそうな小物について紹介します。 かなりトリッキーなモノも含まれていますので、注意してくださいね。
割り込みハンドラとC言語
割り込みベクタ(ゲート)に登録するルーチンは、アセンブラで、必要数だけ用意し、 そこからC言語を呼び出すのが、ポピュラーな方法でしょう。 でも、アセンブラの量が増えてイマイチです。 そこで、こんなコードを考えてみました。
; ; C言語関数で割り込みを処理するための補助処理 ; global _intr_magic _intr_magic: push ebp mov ebp,esp push ds push es pusha mov ax,0x10 mov ds,ax mov es,ax mov eax,[ebp+4] ;呼び出し元アドレス call eax popa pop es pop ds pop ebp add esp,4 ;呼び出し元アドレス(オリジナル)破棄 sti iret
このコードは、検証が済んでいません。コンパイラの種類や、状況によっては使えないかもしません。
まず、C言語で、なぜ、割り込みハンドラが記述できないか、あるいは、なぜVisualCなどで、interupt と関数に指定しなければならないかについて説明します。
関数の呼び出しは call命令、関数からの復帰はret命令が生成されます。
ところが、割り込みでの呼び出しには、スタック上にフラグ(eflags)も積み上げられるために、専用の復帰命令 iret が使用されます。割り込みなのに、ret命令で戻ると、スタックに4バイトほどゴミが残ります。当然、暴走します。
あと、割り込み時には、内部で使用する、あらゆるレジスタを保存する必要があります。
これは、一般的な関数では行われません。少なくともeaxは戻り値として使用されるため、壊したい放題です。
問題点をまとめますと、
・関数からiret命令で戻れない。
・レジスタを壊す可能性がある。
といった理由で、割り込みハンドラとして登録できないわけです。そして、VisualCなどについているinterupt型は、
このような処理を行ってくれるのです。
これをなんとかC言語の関数をハンドラにできないかと思って作ったのが上記のプロシジャーです。
かなり怪しい動きをします。
この intr_magic() 関数を、割り込みハンドラの先頭で呼び出してください。
このプロシジャーが呼び出され、EBPをPUSHした時点でのスタックは、次のようになっています。 (一般保護違反による割り込みを除きます。また仮想86モードからも除きます)
esp+00 | EBPの内容 |
esp+04 | C言語ハンドラの次の実行位置オフセット |
esp+08 | 割り込みされたプログラム実行位置オフセット |
esp+12 | 割り込みされたプログラム実行位置オフセット |
esp+16 | 割り込みされたプログラムCSセレクタ値 |
esp+20 | 割り込みされたプログラムEFLAGS状態 |
上の表で、esp+4(ebp+4でも同じです)には、C言語割り込みハンドラが、
このintr_magic()を実行した後、動作すべきアドレスが記載されています。
このプロシジャーがcall文で呼び出されているので、当たり前の話です。
そこで、レジスタを全部スタックにつんで、データのセレクタを調整しておいてから、
(この時点でスタックは上の図よりだいぶ成長します)このアドレスを逆コールします。
コールされた関数の戻り位置を逆にコールするのです。
すると、これは、割り込みの発生で移動してきたものではないので、
通常のret命令で戻ってくることができますし、その直前に全部のレジスタを退避しているので、
レジスタ破壊の問題もありません。
あるとすれば、C言語ハンドラが、なんらかプロローグコード的なものを生成して、
これによって、レジスタ破壊が起こるケースですが、今のところ大丈夫そうです。
プロシジャー内のcallを抜ければ、本来C言語で動作させたかった作業は全部終わっている
状態ですから、退避していたレジスタを全部元に戻し、一番最初に、プロシジャーを呼び出してきた、
C言語からアセンブラプロシジャーをcallしたときのアドレス部分のスタックを破棄します。
これで、最初のcallは、なかったことになります。あとは、iret すればOKです。
これは、スタック操作の一歩で、モニタなどのプログラムでは、 スタックをダマすことは結構よく使うテクニックです。 特に仮想86モードにはいる時など重宝します。 一歩間違えば、発見しにくいバグを作りますが・・・
無作為研究所トップページに戻る