私は、Windowsでのドライバー開発の基礎を学んでいます-RingおよびRingという用語を見つけ続けています。これらは何を指しますか? カーネルモードおよびユーザーモードと同じですか?
Linux x86リング使用の概要
Linuxでリングがどのように使用されるかを理解することで、それらが何のために設計されているかを知ることができます。
X86保護モードでは、CPUは常に4つのリングのいずれかになります。 Linuxカーネルは0と3のみを使用します。
これは、カーネルとユーザーランドの最もハードで高速な定義です。
Linuxがリング1とリング2を使用しない理由 CPU特権リング:リング1とリング2が使用されない理由
現在のリングはどのように決定されますか?
現在のリングは、次の組み合わせで選択されます。
グローバル記述子テーブル:GDTエントリのメモリ内テーブル。各エントリには、リングをエンコードするフィールドPrivl
があります。
LGDT命令は、アドレスを現在の記述子テーブルに設定します。
セグメントはGDTのエントリのインデックスを指すCS、DSなどを登録します。
たとえば、CS = 0
は、GDTの最初のエントリが実行中のコードに対して現在アクティブであることを意味します。
各リングは何ができますか?
CPUチップは、次のように物理的に構築されています。
リング0は何でもできます
リング3は、複数の命令を実行して複数のレジスタに書き込むことはできません。
独自のリングを変更することはできません!そうしないと、リング自体をリング0に設定し、リングを使用できなくなります。
つまり、現在のリングを決定する現在の セグメント記述子 を変更することはできません。
ページテーブルを変更することはできません: x86ページングはどのように機能しますか?
つまり、CR3レジスタを変更できず、ページング自体がページテーブルの変更を妨げます。
これにより、1つのプロセスが他のプロセスのメモリをセキュリティ/プログラミングの容易さの理由で見ることができなくなります。
割り込みハンドラーを登録できません。それらはメモリ位置への書き込みによって設定されますが、これもページングによって防止されます。
ハンドラーはリング0で実行され、セキュリティモデルを破壊します。
つまり、LGDTおよびLIDT命令は使用できません。
in
やout
などのIO命令を実行できないため、任意のハードウェアアクセスが可能です。
そうしないと、たとえば、プログラムがディスクから直接読み取ることができる場合、ファイルのアクセス許可が役に立たなくなります。
正確には Michael Petch のおかげです。実際には、OSがリング3でIO命令を許可することが可能です。これは実際に タスク状態セグメント によって制御されます。
不可能なことは、リング3がそもそも持っていなかった場合に、それを許可することです。
Linuxは常にそれを許可しません。参照: なぜLinuxはTSS経由でハードウェアコンテキストスイッチを使用しないのですか?
プログラムとオペレーティングシステムはリング間でどのように移行しますか?
cPUがオンになると、リング0で初期プログラムの実行が開始されます(それでもいいですが、これはおおよその目安です)。この初期プログラムはカーネルであると考えることができます(ただし、通常はブートローダーであり、リング0のままカーネルを呼び出します)。
ユーザーランドプロセスがカーネルにファイルへの書き込みのような何かをさせたい場合、 int 0x80
またはsyscall
などの割り込みを生成する命令を使用してカーネルに信号を送ります。 x86-64 Linux syscall hello worldの例:
.data
hello_world:
.ascii "hello world\n"
hello_world_len = . - hello_world
.text
.global _start
_start:
/* write */
mov $1, %rax
mov $1, %rdi
mov $hello_world, %rsi
mov $hello_world_len, %rdx
syscall
/* exit */
mov $60, %rax
mov $0, %rdi
syscall
コンパイルして実行:
as -o hello_world.o hello_world.S
ld -o hello_world.out hello_world.o
./hello_world.out
これが発生すると、CPUはブート時にカーネルが登録した割り込みコールバックハンドラーを呼び出します。 ハンドラを登録して使用する具体的なベアメタルの例 です。
このハンドラはリング0で実行され、カーネルがこのアクションを許可し、アクションを実行し、リング3のユーザーランドプログラムを再起動するかどうかを決定します。x86_64
exec
システムコールが使用されるとき(またはカーネル が/init
を開始するとき)、カーネルは新しいユーザーランドプロセスの レジスタとメモリを準備する 、それからエントリポイントにジャンプし、CPUをリング3に切り替えます
プログラムが(ページングのため)禁止されたレジスタまたはメモリアドレスへの書き込みのようないたずらをしようとすると、CPUはリング0でカーネルコールバックハンドラーも呼び出します。
しかし、ユーザーランドはいたずらだったので、カーネルは今回プロセスを強制終了するか、シグナルで警告を出すかもしれません。
カーネルが起動すると、一定の周波数でハードウェアクロックが設定され、定期的に割り込みが生成されます。
このハードウェアクロックは、リング0を実行する割り込みを生成し、起動するユーザーランドプロセスをスケジュールできるようにします。
このように、プロセスがシステムコールを行っていない場合でも、スケジューリングを行うことができます。
複数のリングを持つポイントは何ですか?
カーネルとユーザーランドを分離することには、2つの大きな利点があります。
それをいじる方法は?
リングを直接操作するのに適したベアメタルセットアップを作成しました: https://github.com/cirosantilli/x86-bare-metal-examples
残念ながらユーザーランドの例を作成する忍耐はありませんでしたが、ページングのセットアップまで行ったので、ユーザーランドを実現できるはずです。プルリクエストをご覧ください。
または、Linuxカーネルモジュールはリング0で実行されるため、それらを使用して特権操作を試すことができます。制御レジスタを読み取ります: プログラムから制御レジスタcr0、cr2、cr3にアクセスする方法?セグメンテーション違反の取得
ホストを殺さずに試すための 便利なQEMU + Buildrootセットアップ があります。
カーネルモジュールの欠点は、他のkthreadが実行されており、実験に干渉する可能性があることです。しかし、理論的には、カーネルモジュールですべての割り込みハンドラーを引き継いでシステムを所有することができます。これは実際には興味深いプロジェクトです。
負のリング
ネガティブリングは実際にはIntelのマニュアルでは参照されていませんが、リング0自体よりもさらに機能があるCPUモードが実際に存在するため、「ネガティブリング」の名前に適しています。
1つの例は、仮想化で使用されるハイパーバイザーモードです。
詳細については、以下を参照してください: https://security.stackexchange.com/questions/129098/what-is-protection-ring-1
[〜#〜] arm [〜#〜]
ARMでは、リングは代わりに例外レベルと呼ばれますが、主な考え方は同じです。
ARMv8には4つの例外レベルがあり、一般に次のように使用されます。
EL0:ユーザーランド
EL1:カーネル(ARM用語の「スーパーバイザー」)。
svc
命令(SuperVisor呼び出し)で入力されます。これは、以前はswi
nified Assemblyの前 として知られており、Linuxシステム呼び出しを行うために使用される命令です。 Hello world ARMv8の例:
.text
.global _start
_start:
/* write */
mov x0, 1
ldr x1, =msg
ldr x2, =len
mov x8, 64
svc 0
/* exit */
mov x0, 0
mov x8, 93
svc 0
msg:
.ascii "hello syscall v8\n"
len = . - msg
Ubuntu 16.04でQEMUを使用してテストします。
Sudo apt-get install qemu-user gcc-arm-linux-gnueabihf
arm-linux-gnueabihf-as -o hello.o hello.S
arm-linux-gnueabihf-ld -o hello hello.o
qemu-arm hello
SVCハンドラーを登録し、SVC呼び出しを行う の具体的なベアメタルの例を次に示します。
EL2: hypervisors 、たとえば Xen 。
hvc
命令(HyperVisor呼び出し)で入力します。
ハイパーバイザーはOSに対するものであり、OSはユーザーランドに対するものです。
たとえば、Xenを使用すると、LinuxやWindowsなどの複数のOSを同じシステムで同時に実行でき、Linuxがユーザーランドプログラムで実行するのと同じように、セキュリティとデバッグを容易にするためにOSを互いに分離します。
ハイパーバイザーは、今日のクラウドインフラストラクチャの重要な部分です。複数のサーバーを単一のハードウェアで実行できるため、ハードウェアの使用率を常に100%近くに保ち、多くのお金を節約できます。
たとえば、AWSは KVMへの移行がニュースになった まで2017年までXenを使用していました。
EL3:さらに別のレベル。 TODOの例。
smc
命令で入力(セキュアモードコール)
ARMv8アーキテクチャリファレンスモデルDDI 0487C.a -第D1章-AArch64システムレベルプログラマモデル-図D1-1は、これを美しく示しています。
おそらく後知恵の恩恵によるARMの特権レベルの命名規則は、負のレベルを必要とせずにx86よりも優れていることに注意してください。0が最低で、3が最高です。高いレベルは、低いレベルよりも頻繁に作成される傾向があります。
現在のELは、MRS
命令で照会できます: 現在の実行モード/例外レベルなどは?
ARMでは、チップ領域を節約する機能を必要としない実装を可能にするために、すべての例外レベルが存在する必要はありません。 ARMv8の「例外レベル」には次のように記載されています。
実装には、すべての例外レベルが含まれない場合があります。すべての実装には、EL0とEL1を含める必要があります。 EL2およびEL3はオプションです。
たとえば、QEMUのデフォルトはEL1ですが、EL2とEL3はコマンドラインオプションで有効にできます: ae power upをエミュレートするときにel1を入力するqemu-system-aarch64
Ubuntu 18.10でテストされたコードスニペット.
Intelプロセッサ(x86など)では、アプリケーションの電力を制限できます。 IO、メモリ、ポートなどの重要なリソースを制限(保護)するために、OS(この場合はWindows)と連携するCPUは、カーネルモードとユーザーモードにそれぞれマップする特権レベル(0が最高特権、3が最低)を提供します。
したがって、OSはリング0でカーネルコードを実行します-CPUによって提供される最高の特権レベル(0)-とリング3でユーザーコード.
詳細については、 http://duartes.org/gustavo/blog/post/cpu-rings-privilege-and-protection/ を参照してください
まあ、それは非常に広範な質問です。ただし、Googleを使用するか、Wikipediaでこれらのことについての記事を読むだけで、最初の概要を把握できます。