Many http://www.grenvillecollege.co.uk/ payday loan uk relief from debt for several folks! . * OSECPUはどのくらいセキュアか -(by [[K]], 2013.03.12) ** 基本的な考え方 -OSECPUでは原則として以下の対策をしません。する必要がないと思っているからです。 --ASLR(アドレス空間配置のランダム化) --DEP(データ実行防止) -そんなわけでOSECPUにおいては、さまざまなモジュールやデータがメモリの各所に配置されると思いますが、そのアドレスは(条件が同じなら)毎回ほぼ一緒です。 -そればかりか、アプリケーションが任意のデータを与えて、それをOSECPUのバイトコードとしてJITコンパイルさせ、即座に実行させることも許します。また特定のアドレス空間に対してDEPを有効にするようなことはしません(こういう特定のCPUでしか有効ではない機能には頼らないということ)。 -つまり、もし「狙ったアドレスに分岐する方法」があれば、OSECPUは一発で倒されるということになります。もしくは「狙ったアドレスのデータを上書きする」ことができれば、システムを大混乱させることもできるかもしれません。 --それにもかかわらずASLRもDEPもしないと言っているのは、要するに「やれるものならやってみろ!」と言っている訳です(笑)。 -なお、OSECPUのバイトコードではないプログラムを実行する機能はありません。 --つまり各CPUのネイティブコードはOSECPU内では実行できません。 ** 分岐命令 -OSECPUの分岐命令では2つの形式があります。即値を指定する形式では、同じ翻訳単位の中しか指定できません。つまりアプリではアプリとスタティックリンクしたライブラリの範囲内しか指定できません(それ以外の指定をしてもJITコンパイラがエラーを出して翻訳できない)。また指定は必ずラベル番号を経由して行うため、命令コードの途中に分岐するようなこともできません。 //--もしかしたら特定のラベル番号は翻訳単位の外を指すかもしれません(システムコールなど)。でもそれはOSの予期した分岐先であって、攻撃には役立たないと思われます。 //--ということで、この形式を使っての攻撃はうまくいきません。 //↑この機能はなくなったので混乱を防ぐためにコメントアウト(by K, 2013.07.08) -分岐先としてレジスタを指定する形式では、翻訳単位に対する制約はありません。そのレジスタの中身が有効な分岐先であるとマークされていれば、OSECPUは素直に分岐してしまいます。したがってこの形式は攻撃に使える可能性があります!・・・しかし以下のような理由で無理です。 --分岐先として指定できるのはポインタレジスタだけですが、ポインタレジスタに対してできる操作は非常に限定されています。自由な値を設定できるわけではありません。 ---許されている操作: ---他のポインタレジスタの値をコピーする ---ラベル番号で示されるアドレスを格納する ---許されている範囲で加算したり減算したりする(プログラムポインタに関しては実質この命令は使えません。可動範囲が0になっているので) ---メモリから読み込む ---(つまり直接値を代入する手段はありません) --この中で不正なポインタを作る方法として可能性がありそうなのは、「メモリから読み込む」の方法だけです。ここへの対策を以下に示します。 ---なお、ポインタレジスタは可動範囲のように多くの情報を含むので、32bitや64bitではなく、256bitくらいあります。 ** メモリ上のポインタを保護するために -OSECPUではメモリ上のデータを保護しています。ここを突破されると、脆弱性になると考えています。 -まずメモリ上のデータには全て型情報が関連付けられており、これはオブジェクトがnewされたときに設定されます。deleteすれば消えます。 -そしてポインタを生成するときは、この型情報と矛盾しないポインタのみを生成します。 --これによりポインタ型のメモリが他の型のアクセスで上書きされることを防いでいます。 -すべてのポインタには可動範囲が設定されており、これにより配列の外へポインタが移動してしまうことを防ぎます。 --配列ではないオブジェクトに対しては、可動範囲0と設定されます。 --配列内のオブジェクトであっても、その一箇所のみを可動範囲0で指すことは可能です。 --可動域を狭める命令はアプリが自由に利用できます。アクセス可能範囲を減らす方向のキャストも自由に利用できます。 -またOSECPUでは、ポインタレジスタが読み書きできるメモリは、ポインタ型のメモリの部分だけです。さらに整数レジスタで読み書きできるメモリは、整数型のメモリの部分だけです。つまりメモリを経由しても、整数レジスタの内容をポインタレジスタへ読み込ませることはできません。 --ちなみに整数レジスタについては、そのbit数で表現可能な数値であるなら、どんな値も好きなように代入できます。 ** use-free-after脆弱性対策 -あるオブジェクトをfreeしたあと、適当なオブジェクトをnewすれば、同じアドレスに割り当てられるかもしれません。仮にそうだとすると、こんな攻撃が考えられます。 // a = ポインタ型のメモリのポインタ; // delete(a); // b = new 整数型の配列で256bitくらいありそうなもの; // b[..] = ..; をいくつか書いて望みの値を代入。 // Pxx = *a; /* ポインタレジスタに*aの値を取得!!(これでPxxには不正なポインタが!) */ // PJMP(Pxx); /* さあ分岐だ! */ // ↑このコードは古いのでもっと現実の仕様に沿ったサンプルに変更(by K, 2013.07.08) P01 = malloc(T_VPtr, 1); free(P01); P02 = malloc(T_UINT8, 32); // 合計256bitなので、P01の領域をリサイクルしている可能性あり R00 = ??; R01 = ??; PASMEM0(R00, T_UINT8, P02, R01); // これを複数実行して、都合のよいポインタレジスタのイメージを構築 PJMP(P01); // さあ分岐だ! -これが成立したらまずいです。ということでこれに対する対策があります。 -ポインタレジスタには可動域のほかに、「自分の指している先に今もまだ期待したオブジェクトが存在しているのか?」を確認するためのポインタとシグネチャコードがあります。 --このポインタは、「newされたときに作られる、オブジェクトの死活を確認するための構造体」を指しており、そこにはnewされたときに決定されたシグネチャコードが書かれています(シグネチャコードは乱数で決めます)。 --OSECPUではメモリアクセスに際して、ポインタレジスタ内に保持されたシグネチャコードと死活確認構造体の中のシグネチャを比較します。一致しなければ、オブジェクトはfreeされたあとなので(もしくはreallocで移動した後なので)、このポインタをその後も使い続けたのは攻撃もしくはバグであり、エラー終了してしまいます。 //--つまりPxx = *a;の部分がエラーになって止まるようになっています。 --つまりPJMP(P01);の部分がエラーになって止まるようになっています。 --型を確認するのではなくシグネチャ方式なので、仮に同じ型で違う内容の新オブジェクトが同じアドレスに配置されても、正しく検出できます。 --死活確認構造体は、管理しているオブジェクトがfreeされれば基本的にはfreeされます。そして新しいオブジェクトを作れば同じ部分が使われる可能性は高いです。しかしこのときに新しいシグネチャが乱数で生成されるので、以前のポインタのものとはシグネチャが一致しません。 ** ちなみに・・・ -osecpu011aでは上記はほとんど未実装です・・・これからせっせと作ります。 ** 関連リンク -[[page0006]] ** こめんと欄 -このページにこめんと欄はありません。このページの内容にコメントしたいときは[[impressions]]にお願いします。