プロセス アドレス空間の分離は、現代のオペレーティング システムの注目すべき機能です。これは、「古い」オペレーティング システムと区別する特徴でもあります。 プロセス アドレス空間の分離とは、メモリが共有されていると宣言されていない限り、プロセス 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 スレーブのデータバックアップ方法のチュートリアル
序文Ahhang が Springboot プロジェクトを開発していたとき、フロントエンドから検証コ...
Linux インストール MySQL ノート1. MySQL データベース サーバーをインストールす...
XML/HTML コードコンテンツをクリップボードにコピー< div style = &quo...
この記事の主な内容は次のとおりです。 1. 閉じるボタン2.キャレット3. フローティングを素早く設...
標準化されたデザインソリューション - マークアップ言語とスタイルマニュアルWeb 標準ソリューショ...
1. MySQLサービスをシャットダウンする# service mysqld stop 2. rpm...
理論的には、MySQL によって使用されるメモリ = グローバル共有メモリ + max_connec...
この記事は主にMybatisでの動的SQL文の解析について紹介します。この記事のサンプルコードは非常...
重複したフォーム送信は、マルチユーザー Web アプリケーションで最も一般的で厄介な問題です。重複送...
天気予報をウェブサイトに挿入すると、次のような効果が得られます。次のコードを挿入する必要があります:...
画像リンク <img src="" /> jsを使用してURLが有効...
新しい用途new の機能は、コンストラクターを通じてインスタンス オブジェクトを作成することです。イ...
今日私が書こうとしている内容では、プログラムは 7 時間近く実行され、データベースに 1,000 万...
この記事では、ネイティブ JS で実装されたデジタル時計エフェクトを紹介します。エフェクトは次のとお...
1. まず、CSS3 のターゲット セレクターを使用し、a タグを使用して id セレクターを指定し...