小物を設計する〜割り込みハンドラを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+00EBPの内容
esp+04C言語ハンドラの次の実行位置オフセット
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モードにはいる時など重宝します。 一歩間違えば、発見しにくいバグを作りますが・・・


無作為研究所トップページに戻る

copyright(c)2007 by MUSAKUI-LABO