x86バイナリ → OSECPUバイトコード
(0) はじめに
- OSECPUはアプリがライブラリが十分にそろっているとは言えない状態です。それでみなさんいろいろな言語を開発してくれていて本当に助かります。また現状のASKAだけで頑張ってアプリを作ってくれている人にも感謝感激です。
- ところでOSECPUアプリを増やす手っ取り早い方法として、x86のコードをOSECPUバイトコードにしたらどうだろうという「安易な」アイデアがあります。そうすればアプリは一気に増やせるだろうと。確かにそうですね、うまくいけば。
- そしてこの手の開発をかなり得意としているのは実はK自身なので、今から適当に設計して、それで少々考察してみようと思います。
(1) メモリアクセス
- まずx86では、ポインタレジスタと整数レジスタの区別はなく、すべて汎用レジスタとして扱われます。しかもメモリに型はありません。x86上の各言語は型をもっていますが、x86の機械語になった後では、その型を推測することは容易ではありません。さらにあるメモリに対して32ビットでアクセスしたり、はたまた同じところを8ビットでアクセスしたりすることが許されていて、そういうことをやってしまうコードもたくさん存在します。
- となれば、これらをすべて解消できる手法は以下の通りだと思います。
- (1) Win32やPOSIXではセグメンテーションを使ってないので、とりあえずセグメンテーションのことは気にしないことにする。ページングでエイリアスとかやっているかもしれないけど、それも真面目に考えると疲れるのでひとまず無視する。
- (2) EAX, ECX, EDX, EBX, ESP, EBP, ESI, EDIの8レジスタはすべて整数レジスタとする。
- (3) メモリアクセスはすべて8ビットとして、32ビットアクセスがあった場合は4回のメモリアクセスに分解し、シフトしてORする。
- もはやこれ以外にエンディアンの影響を受けない確実な方法がない。
- メモリアクセスが4倍遅くなるけど、もうそれも気にしない。
- この方法なら型が分からないという問題も起きない。
- (4) 全アドレス区間は P01 にすべて押し込む。で、EBXでのメモリリードアクセスがあれば、PALMEM0(?, T_UINT8, P01, EBX); みたいにして読み込む。ライトも同様。
(2) フラグ類
- キャリーフラグとかゼロフラグとかそういうのはどうするかというと、それぞれレジスタに割り振るのが簡単でいいと思う。これは遅そうだなあ。・・・まあでももう遅くなることは気にしないことにしよう。
(3) パーシャルレジスタ
- たとえばALに代入すると、EAXやAXの下位ビットも変わらなければいけない。ええいめんどうだ、ALに関する演算があったら、EAXから8ビットを切り出して、それに対して演算したのち、EAXの下位8ビットに戻してやろう。
(4) 自己書き換え
- 自己書き換えをするようなコードは捨てる。これをまじめにやると、本当にしんどい。書き足されるだけならまだしも、上書きとかされるかもしれないから。
(5) ポインタによるジャンプ
- 定数ジャンプはいい。それは2パスにすれば何とかなる。しかし間接ジャンプはどうしよう。これはやばいぞ。うまい方法が思いつかない!
- よし、間接ジャンプ命令を使っているところがあったら、give-upしてしまうことにしよう。つまり変換できない。・・・うわー、これは相当にコンバート可能なコードが減りそうだな・・・。
- 関数呼び出しも、スタックいじってリターンアドレスを変更したらアウトだな・・・。
(6) ここまで書いて思ったこと
- 本当にOSECPUはよくできている。フラグ方式なんて、CPUが違ったらエミュレーションするのがこれほど大変だということだ。パーシャルレジスタもエミュレーションがすごく手間。メモリアクセスもunion的なアクセスを許してしまうからこんな面倒なことになる。
- それらが一切ないOSECPUは本当にさまざまなCPUにすんなりと適合できる。
- jitcのAPIも本当によくできている。いつ書き換えられるかわからない「自己書き換え」とはえらい違いだ。
- OSECPUはポインタレジスタの分離とラベル命令のおかげで、間接ジャンプの問題が起きない!
(7) セキュリティはどうなったか
- まずメモリアクセスに関するセキュリティは、NULLくらいなら検出できる。他のものについては内部のバグは見つけられない。
- P01をmallocしたときにP01--;を実行すればいい。そうすれば、P01[0]はアクセス可能域ではないので、エラーになる。マイナス1じゃなくて256とか4096とかでもいい。
- x86のアプリは結局みんなP01を使ってメモリアクセスをするので、中でポインタアクセスでへまをやっても気づけない。しかしそれでも他のOSECPUバイトコードが、x86由来のバイトコードのバグによって、やられてしまうことはない。
- 間接ジャンプはないので、結果的に危険な分岐はなくなったかもしれない。
- 無限ループ問題はOSECPU化によって自動で守られる。
- double-freeとかuse-after-freeなどの問題は、基本的にP01の中を自前の簡易mallocで切り分けているだけなので、検出できない。
こめんと欄