プロセス アドレス空間の分離は、現代のオペレーティング システムの注目すべき機能です。これは、「古い」オペレーティング システムと区別する特徴でもあります。 プロセス アドレス空間の分離とは、メモリが共有されていると宣言されていない限り、プロセス P1 がプロセス P2 のメモリに任意の方法でアクセスできないことを意味します。 これは非常に理解しやすいです。例を挙げてみましょう。 原始的な野蛮社会には家族という概念がないことは周知の事実です。すべての資源は部族内で共有され、すべての野蛮人はいつでも、どんな形でも他の野蛮人と交流することができます。これは、メモリ アドレス空間が分離されていない DOS などのオペレーティング システムの場合です。プロセスは他のプロセスのメモリに自由にアクセスできます。 その後、家族という概念が出現し、家族の資源は隔離され、他人の家に侵入できなくなりました。所有者の許可がない限り、他人の家に入り込んで他人の物を勝手に持ち去ることはできなくなりました。オペレーティング システムがモダン モードに移行した後、プロセスにもファミリーに似た概念が生まれました。 しかし、家族という概念は仮想的なものであり、人々は合意に従うだけで、他人の家族を破壊しないのです。家は家族を守る物理的なインフラストラクチャとして機能します。オペレーティング システムでは、ホームは仮想アドレス空間に類似しており、ハウスはページ テーブルです。 隣人はあなたの家に侵入できませんが、警察はできますし、正当な理由があれば政府当局も侵入できます。いわゆる特権管理機関は、正当な理由がある限り、一般人の家に立ち入り、家族の所有物に触れることができる。オペレーティング システムの場合、これはカーネルが実行できることであり、カーネルは任意のプロセスのアドレス空間にアクセスできます。 もちろん、警察が理由もなく他人の家に侵入しないのと同じように、カーネルも理由もなく他人の家に侵入することはありません。 ただし、カーネルに意図的にこれを実行させて、不正な操作を実行させることもできます。 まずは試しにプログラムを見てみましょう: //テスト.c // gcc テスト.c -o テスト #include <stdio.h> #include <stdlib.h> #include <文字列.h> #include <unistd.h> #include <sys/mman.h> int メイン() { char* addr = mmap(NULL, 1024, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); strcpy(addr, "浙江省温州市ピクシーシ"); printf("addr: %lu pid:%d\n", addr, getpid()); printf("before:%s \n", addr); getchar(); printf("after:%s\n", addr); 0を返します。 } このプログラムの出力は非常にシンプルです。前後ともに「浙江温州妖精市」が出力されます。しかし、この文を変更したいのですが、どうすればよいでしょうか?当然ですが、テスト プロセス自体が変更しない場合は、私たちにできることは何もありません... しかし、民家に侵入するのと同じように、カーネルに強制的に変更させることはできます。 次にカーネルモジュールを記述します。 //テスト.c // make -C /lib/modules/`uname -r`/build SUBDIRS=`pwd` モジュール #include <linux/mm.h> #include <linux/sched.h> #include <linux/module.h> 静的int pid = 1; モジュールパラメータ(pid, int, 0644); 静的符号なしロングアドレス = 0; module_param(アドレス、long、0644); // 仮想アドレスに基づいてプロセスのページ テーブルを見つけることは、家族の家の住所を見つけて侵入することと同じです。 静的 pte_t* get_pte(構造体 task_struct *タスク、符号なしロングアドレス) { pgd_t* pgd; pud_t* プッド; pmd_t* pmd; pte_t* pte; 構造体 mm_struct *mm = task->mm; pgd = pgd_offset(mm, アドレス); pgd_none(*pgd) の場合 || pgd_bad(*pgd)) NULL を返します。 pud = pud_offset(pgd、アドレス); if(pud_none(*pud) || pud_bad(*pud)) NULL を返します。 pmd = pmd_offset(pud、アドレス); pmd_none(*pmd) の場合 || pmd_bad(*pmd)) NULL を返します。 pte = pte_offset_kernel(pmd、アドレス); (pte_none(*pte) の場合) NULL を返します。 pte を返します。 } 静的 int test_init(void) { 構造体task_struct *タスク; pte_t* pte; 構造体ページ*ページ; // このファミリを検索 task = pid_task(find_pid_ns(pid, &init_pid_ns), PIDTYPE_PID); // この家族がどこに住んでいるか調べる if(!(pte = get_pte(task, addr))) -1 を返します。 ページ = pte_page(*pte); // 強制的に侵入 addr = page_address(page); //sdajgdoiewhgikwnsviwgvwgvw strcpy(addr, (char *)"雨で水が溢れても太りません!"); // 作業が完了したら、実績と名声を隠して退出します。 return 0; } 静的void test_exit(void) { } モジュールを初期化します。 モジュールを終了(テスト終了)します。 MODULE_LICENSE("GPL"); さあ、試してみましょう: [ルート@10 ページ置換]# ./test アドレス: 140338535763968 pid:9912 前:浙江省温州市ピクシーシ この時点でカーネルモジュールtest.koをロードします。 [root@10 テスト]# insmod test.ko pid=9912 addr=140338535763968 [root@10 テスト]# テストプロセスで Enter キーを押します。 [ルート@10 ページ置換]# ./test アドレス: 140338535763968 pid:9912 前:浙江省温州市ピクシーシ after:雨で洪水になっても水は増えません! [ルート@10 ページ置換]# どうやら、「浙江省温州市では革靴が濡れる」が「雨が降ってびしょ濡れになると太らない」に変わったようだ。 上記のカーネルモジュールの get_pte 関数をよく見てください。この関数を正しく記述するには、破壊したいプロセスが配置されているマシンの MMU について、32 ビットシステムか 64 ビットシステムか、3 レベルページテーブルか 4 レベルページテーブルか 5 レベルページテーブルかなど、ある程度理解している必要があります。これ… Linux の楽しさは、自分で行うことも、他の人にやってもらうこともできるという点にあります。たとえば、プロセスの仮想アドレスのページ テーブル エントリによって示される物理ページを直接取得できます。 そのようなAPIはありますか?はい、すべてがファイルであることを忘れないでください。proc ファイル システムには、次のようなファイルがあります。 このファイルを読み取ると、プロセスの仮想アドレスのページ テーブル エントリが取得されます。次の図はカーネル ドキュメントから引用したものです。 仮想アドレス空間はプロセスごとに存在しますが、物理アドレス空間はすべてのプロセスで共有されます。つまり、物理アドレスはグローバルです。 ここで、Documentation/vm/pagemap.txt の説明に従って、任意のプロセスの任意の仮想アドレスのグローバル物理アドレスを取得するプログラムを作成します。 // getphys.c // gcc getphys -o getphys #include <fcntl.h> #include <stdio.h> #include <stdlib.h> int main(int argc, char **argv) { 整数データ; 整数 pid; 符号なしロング pte; 符号なしロングアドレス; 符号なしロング phy_addr; char procbuf[64] = {0}; pid = atoi(argv[1]); 引数はatol[2]です。 sprintf(procbuf, "/proc/%d/pagemap", pid); fd = open(procbuf, O_RDONLY); size_t オフセット = (addr/4096) * sizeof(unsigned long); lseek(fd, オフセット, SEEK_SET); 読み取り(fd、&pte、sizeof(unsigned long)); phy_addr = (pte & ((((unsigned long)1) << 55) - 1))*4096 + addr%4096; printf("phy addr:%lu\n", phy_addr); 0を返します。 } 次に、カーネル モジュールを変更します。 #include <linux/module.h> 静的符号なしロングアドレス = 0; module_param(アドレス、long、0644); 静的 int test_init(void) { strcpy(phys_to_virt(addr), (char *)"雨で水が溢れても太りません!"); 0を返します。 } 静的void test_exit(void) { } モジュールを初期化します。 モジュールを終了(テスト終了)します。 MODULE_LICENSE("GPL"); 最初に test を実行し、次に test の出力を getphys の入力として使用し、次に getphys の出力をカーネル モジュール test.ko の入力として使用すれば完了です。覚えていますか?これは複数のプログラムをパイプするスタイルではないでしょうか? 実際の住所を入力して変更するだけです。仮想アドレスを介してページ テーブルを取得する操作は、ユーザー モードでページマップ ファイルを読み取って解析することに置き換えられました。 以上がこの記事の全内容です。皆様の勉強のお役に立てれば幸いです。また、123WORDPRESS.COM を応援していただければ幸いです。 以下もご興味があるかもしれません:
|
<<: Vue は PDF ファイルのオンライン プレビューを実装します (pdf.js/iframe/embed を使用)
>>: MySQL マルチマスターと 1 スレーブのデータバックアップ方法のチュートリアル
この記事の例では、円形の水のアニメーションを実現するためのキャンバスの具体的なコードを参考までに共有...
序文この記事では、DBA がいないチームが参考にできるように、MySQL の一般的な使用に関するヒン...
VMware ツールは VMware の使用に非常に便利です。そのため、VMware ツールをインス...
pymysqlをインストールするpip install pymysql 2|0pymysqlの使用2...
最近の勉強で、GitHub でレイアウトの練習をいくつか見つけたのですが、レイアウトにまったく慣れて...
この記事では、シンプルなカルーセルを実装するためのJavaScriptの具体的なコードを参考までに紹...
例示するフロントエンド開発では、セルの幅を制限し、コンテンツが制限を超える部分に省略記号を表示する必...
スタイル ガイドとは何でしょうか? 簡単に言えば、ストーリーを伝える方法を説明するドキュメントです。...
1つ。序文<br />この種の質問は、どの専門掲示板でも見かけます。Google で検索...
1. 仮想マシンにLinuxシステムをインストールし、仮想マシンを起動し、rootとパスワードを入力...
ページにDOCTYPEを追加するブラウザによってタグやスタイルシートの解釈が異なるため、さまざまなブ...
目次1. 実験環境2. Dockerソースをインストールする3. Dockerをインストールする4....
遅いログクエリ機能スロー ログ クエリの主な機能は、設定された時間しきい値を超える SQL ステート...
Nginx のインストールCentOS 6.x yum にはデフォルトで nginx ソフトウェア ...
オンラインで検索して重複データを削除し、ID が最小のデータだけを残します。方法は次のとおりです。 ...