banner
yfsec

yfsec

一个会一点点渗透,会一点点开发,会一点点Windows内核的Fw
github

VTドライバ開発

VT 技術(VT フレームワークの作成)#

1.VT 技術の紹介#

1. 技術紹介#

1.VT 技術#

VT 技術は Intel が提供する仮想化技術で、正式名称は Intel Virtualization Technology です。これは、仮想化環境の性能、信頼性、および安全性を向上させることを目的としたハードウェアとソフトウェアのソリューションのセットです。VT 技術により、1 台の物理コンピュータ上で複数の仮想マシンを同時に実行でき、それぞれの仮想マシンは異なるオペレーティングシステムやアプリケーションを実行できます。

Intel VT(Intel Virtualization Technology)は、単一の CPU が仮想化環境で複数の論理プロセッサ(Virtual CPU)をシミュレートできるようにし、複数のオペレーティングシステムを同時に実行する能力を実現します。

VT 技術は、ソフトウェア仮想化、コンテナ仮想化、仮想化層の翻訳に分かれます。

  1. ソフトウェア仮想化(Software Virtualization):この仮想化技術はソフトウェアに基づいて実現されており、ホストオペレーティングシステム上で仮想化ソフトウェア(Vmware Workstation、Virtual PC など)を実行し、ハードウェア環境をシミュレートして仮想マシンを作成および管理します。各仮想マシンで実行されるオペレーティングシステムやアプリケーションは変更する必要がありません。
  2. コンテナ仮想化(Container Virtualization):コンテナ仮想化は軽量の仮想化技術で、Linux コンテナ(LXC)や Docker コンテナなどのオペレーティングシステムカーネルの特性を使用してアプリケーションとその実行環境を隔離します。コンテナはホストオペレーティングシステムのカーネルを共有するため、完全なオペレーティングシステムの仮想化は必要ありません。

2. 抽象的な「Ring-1 層」#

従来のコンピュータアーキテクチャには、明確に定義された「Ring -1」層はありません。通常、コンピュータアーキテクチャの「Ring」階層は、システムリソースへのアクセスを制御するための特権レベルまたは権限レベルを指します。一般的な階層には Ring 0(カーネルモード)と Ring 3(ユーザーモード)が含まれます。

しかし、「VT 技術」Intel の仮想化技術(Virtualization Technology)は、1 台の物理コンピュータ上で複数の仮想マシンを同時に実行できるようにします。仮想化技術は、仮想マシンの作成、実行、およびリソース割り当てを管理するために新しいソフトウェアおよびハードウェアの階層を導入しました。

仮想化技術では、ホストオペレーティングシステムを Ring 0 と見なし、仮想マシンのオペレーティングシステムを Ring 3 と見なすことができます。この場合、仮想化技術は仮想マシンの作成とリソース割り当てを管理するための抽象的な「Ring -1」層を導入したと考えることができます。この抽象的な階層は、ホストオペレーティングシステムと仮想マシンオペレーティングシステムの間に位置し、仮想化管理の階層として見ることができます。

注意が必要なのは、「Ring -1」は仮想化技術における階層関係を説明するための抽象的な概念であり、従来のコンピュータアーキテクチャにおける標準用語ではないということです。実際には、異なる仮想化技術が異なる階層構造や用語を採用する可能性があり、具体的な状況は使用される仮想化プラットフォームや技術によって異なります。

2. キーワード紹介#

1.VMM(Virtual Machine Monitor)#

仮想マシンモニターとは、コンピュータ上のソフトウェア、ファームウェア、またはハードウェアを指し、仮想マシンを構築および実行するために使用されます。

2.VMX(Virtual Machine Extensions)#

プロセッサによる仮想化のサポートは、VMX Opreation と呼ばれるプロセッサ操作形式によって提供されます。VMX Opreation には 2 種類あり、VMX Root Opreation と VMX Non-root Opreation があります。一般的に、VMM は VMX Root Opreation で実行され、クライアントソフトウェアは VMX Non-root Opreation で実行されます。

3.VM(Virtual Machine)#

VM は Virtual Machine(仮想マシン)を指します。

4.VMCS(Virtual Machine Control Structures)#

論理プロセッサが VMX 操作を実行する際、仮想マシン制御データ構造(VMCSs)を使用します。これらの操作は、VMX Non-root Opreation への変換(VM エントリーと VM エグジット)の管理や、VMX 非根操作中のプロセッサの動作を管理します。この構造は、新しい命令 VMCLEAR、VMPTRLD、VMREAD、VMWRITE によって操作されます。

5.VMX Root Opreation#

通常、VMM はこのモードで実行されます。

6.VMX Non-root Opreation#

通常、クライアントソフトウェア(仮想マシン)はこの環境で実行されます。2 つの操作タイプ間の変換は VMX 変換と呼ばれ、ルート操作モードから非ルート操作モードへの変換は VMX エントリー(VMX Entry)と呼ばれ、逆に非ルート操作モードからルート操作モードへの変換は VMX エグジット(VMX Exit)と呼ばれます。

7.Guest software#

各仮想マシン(VM)は、クライアントソフトウェアの実行環境です。

3.VMM ソフトウェアのライフサイクル#

img

上記の図は VMM ソフトウェアのライフサイクルを示しています。

  • VMX の有効化:VMX 操作のライフサイクルは、VMX 拡張を有効にすることから始まります。コンピュータの起動時に、仮想マシンモニター(VMM)はプロセッサの制御レジスタを設定して VMX 拡張を有効にします。このプロセスは通常、オペレーティングシステムのブート中または仮想化ソフトウェアのロード時に行われます。
  • VMX 操作モードへの移行:VMX が有効になると、プロセッサは VMX 操作モードに入ります。このモードでは、プロセッサは仮想マシンと仮想マシンモニター間の切り替えをサポートします。VMM は仮想マシンの作成、構成、および実行を管理します。
  • 仮想マシンの作成と実行:VMX 操作モードでは、VMM は仮想マシンインスタンスを作成し、仮想マシンのリソースを構成できます。仮想マシンモニターは VMCS(Virtual Machine Control Structure)を通じて仮想マシンの状態と制御情報を管理します。VMM は仮想マシンと仮想マシンモニター間で切り替えを行い、仮想マシンが物理プロセッサ上で実行できるようにします。
  • 仮想マシンの監視と制御:仮想マシンが実行されている間、仮想マシンモニターは仮想マシンの動作を監視し、必要な制御を提供します。仮想マシンの割り込み、例外、および特権命令を処理し、仮想マシン間および仮想マシンと物理マシン間のリソースの隔離を確保します。
  • VMX 操作モードからの退出:仮想マシンの実行が完了するか、特定のイベントが発生した場合、仮想マシンモニターは制御を仮想マシンから仮想マシンモニターに切り替えます。このプロセスは VMX エグジットと呼ばれます。退出プロセス中に、仮想マシンモニターは仮想マシンの状態情報を読み取り、更新し、必要な処理を行うことができます。
  • VMX の無効化:VMX 操作のライフサイクルは、仮想化環境がもはや必要でなくなったときに終了します。仮想化ソフトウェアを終了するか、コンピュータをシャットダウンすると、プロセッサの VMX 拡張は無効になり、プロセッサは通常の非仮想化モードに戻ります。

全体として、VMX Operation のライフサイクルは、VMX 拡張の有効化、VMX 操作モードへの移行、仮想マシンの作成と実行、仮想マシンの監視と制御、VMX 操作モードからの退出、および VMX 拡張の無効化などの重要な段階をカバーし、ハードウェア仮想化のサポートと管理を実現します。

2.VT 技術(2)CPU サポートの検出#

1.CPUID 命令を使用して CPU が仮想化をサポートしているかを検出#

VMX Opreation に入る前に、CPU が仮想化技術をサポートしているかを判断するために、CPU に対して一連の検査を行う必要があります。

CPUID 命令は Ring3 でも使用でき、Ring0 層に入る必要はありません。以下は CPUID 命令の使用に関する詳細な説明です。

1.CPUID 命令使用説明#

  1. 構文

CPUID を実行する際には、必要なクエリ番号を eax レジスタに渡す必要があります。

xor rax,rax 
cpuid
  1. 機能

CPUID 命令は、プロセッサの情報と機能サポートを返します。これには、プロセッサのベンダー、プロセッサシリーズ、機能ビット、キャッシュ構成、サポートされている拡張機能などが含まれます。

  1. レジスタの使用
  • 入力:EAX レジスタは、クエリの機能番号を渡すために使用されます。
  • 出力:EAX、EBX、ECX、EDX レジスタは、プロセッサの情報と機能を返すために使用されます。

2.CPUID 命令パラメータ説明#

  • プロセッサベンダー情報のクエリ:EAX を 0 に設定して CPUID 命令を実行すると、プロセッサベンダーの識別子が EBX、EDX、ECX レジスタに ASCII コード形式で返されます。
  • 拡張機能サポートのクエリ:EAX を 7 に設定して CPUID 命令を実行すると、プロセッサがサポートする拡張機能情報が EBX、ECX、EDX レジスタに返されます。
  • キャッシュ構成のクエリ:EAX を 2 に設定して CPUID 命令を実行すると、プロセッサのキャッシュ構成情報が EAX、EBX、ECX、EDX レジスタに返されます。
  • CPUID 最大サポート機能番号のクエリ:EAX を 0x80000000 に設定して CPUID 命令を実行すると、最大サポート機能番号が EAX レジスタに返されます。

上記は一般的に使用されるいくつかの命令パラメータを簡単に示したもので、詳細なパラメータを確認するには、Intel のホワイトペーパー第 2 巻第 3 章(命令 A-L)の CPUID セクションを参照してください(書籍内で詳細に説明されています)。


3.CPU が VT 技術をサポートしているかを確認#

この記事で基づいているシステムは x64 ビットオペレーティングシステムであり、VS コンパイラのコンパイラはデフォルトでインラインアセンブリをサポートしています(もちろん asm ファイルを書くこともできます)。しかし、コードの互換性を考慮して、コード内では VS に付属の組み込み関数を使用しています。以下に VS の組み込み関数のクエリ表を示します。

VS x64 組み込み関数大全

VMX 命令関数の紹介

アセンブリで実行される命令は次のようになります。

mov rax,1
cpuid

実行が終了した後、ECX の 5 番目のビットが 1 に設定されているかを確認します。1 に設定されている場合、現在の CPU は仮想化技術をサポートしています。

コード

EXTERN_C BOOLEAN Check_CPUID() {
	int Ecx[4];
	__cpuid(Ecx,1);
	return (Ecx[2] >> 5) & 1;	//Ecxの6番目のビットが1かどうか
}

2.MSR フィールドを読み取って BIOS が VT 技術を有効にしているかを確認#

MSR(Model Specific Register)は、プロセッサのモデル特有の情報と構成パラメータを含む特殊なタイプのレジスタです。MSR レジスタはプロセッサアーキテクチャの一部であり、プロセッサメーカーによって定義および実装されています。

各プロセッサは異なる MSR レジスタセットを持つ可能性があり、これらのレジスタは特定の機能や構成オプションに対応しています。MSR レジスタは通常、プロセッサの特定の機能、性能調整、電源管理、仮想化サポートなどを制御するために使用されます。

一般的なレジスタ(汎用レジスタなど)とは異なり、MSR レジスタには直接アクセスできず、特別な命令(RDMSR および WRMSR)を介して読み取りおよび書き込みを行います。これらの命令は、MSR のアドレスを指定されたレジスタ(ECX など)にロードし、その後、対応する操作を実行します。

MSR_IA32_FEATURE_CONTROLフィールドを読み取ることで、得られた値を検査し、0 ビットが 0 であるかどうかを確認します。0 である場合、VMX は保護例外に入り、VMX Opreation に入るための命令を直接実行できず、BIOS で VT 技術を有効にする必要があります。

1.RDMSR/WRMSR 命令#

この 2 つの命令は MSR レジスタを操作し、MSR レジスタの読み書きを行います。

RDMSR

mov eax,msr_index
rdmsr

この命令は MSR の値を読み取るために使用され、MSR インデックス番号を eax レジスタに置き、命令を実行すると、MSR の上位 32 ビットの値が EDX レジスタに、下位 32 ビットの値が EAX レジスタに格納されます。

WRMSR

mov eax,values
mov ecx,msr_index
wrmsr

この命令は MSR レジスタへの書き込み操作を担当し、書き込むデータを eax に置き、msr レジスタのインデックス番号を ecx に置いて命令を実行します。


C 言語コード

検証する結果が 2 進数であることを考慮すると、奇数の 0 ビットは常に 1 であり、偶数の 0 ビットは常に 0 であるため、奇数性を判断するだけで済みます。

BOOLEAN Check_CPUID() {
	int Ecx[4];
	__cpuid(Ecx,1);
	return (Ecx[2] >> 5) & 1;	//Ecxの6番目のビットが1かどうか
}

3.CR0 および CR4 を読み取って VMX に正常に入ったかを確認#

上記の作業が完了した場合、ハードウェアレベルで VMX に入ることがサポートされていることを示しますが、CR4 レジスタのフィールドロックを有効にして VT を実行できるようにする必要があります。このロックは VMX に入った後は変更できず、そうでないと直接ブルースクリーンが発生します。VMX を終了するまで続きます。

ここでは、6 つの CR(Control Register)レジスタの役割を紹介しますが、実際には 5 つしかありません。CR1 レジスタは実際の状況では使用されません。

  1. CR0:制御と保護モード、実モード、ページング、およびその他のシステム操作に関連する設定を制御します。
  2. CR1:予約されており使用されません。
  3. CR2:ページエラー例外を引き起こす線形アドレスを格納します。
  4. CR3:アドレス変換とページングメカニズムに使用されるページテーブルの物理アドレスを格納します。
  5. CR4:ページング拡張、物理アドレス拡張など、プロセッサの特定の機能と拡張を制御します。
  6. CR8:中断と例外処理の優先順位を制御します(64 ビットオペレーティングシステムでのみ使用)。

CR レジスタに興味がある方は、こちらの大御所のこの記事を参照してください。CR レジスタのビット紹介


実際には、CR レジスタの各ビットにはそれぞれの名前があり、VMX に正常に入ったかを制御するビットは CR4 レジスタの VMXE ビットです。VT ドライバコードを実行する前に、VT に入ったかどうかを確認することができます。VT に入った場合は、いくつかの不必要な操作を避ける必要があります。そうでないと、ホストがブルースクリーンになります。

3.VMXON に入る#

CPU のサポートチェックに関する 2 の作業が完了した後、VMX に入るコードの作成を正式に開始できます。

1.VMXON および VMXOFF 命令#

VMX Opreation モードに入る方法は、VMXON 命令を実行することです。退出するための命令は VMXOFF です。

VMXON

この命令を実行する前に、自然に整列した 1KB のサイズのメモリを初期化して確保する必要があります。このメモリは「VMX_Region」と呼ばれます。

mov ecx,VMX_Region_Physical
vmxon ecx

この命令を実行するための要件は、VMX_Region に対応する物理メモリアドレスをレジスタに置いてから命令を実行することです。

PVOID VMX_Region = ExAllocatePoolWithTag(NonePagePool,0x1000,'VMX');
ULONG_PTR VMX_Region_Phy = MmGetPhysicalAddress(VMX_Region).QuadPart;
//VMX Regionを設定する必要があります
__vmx_on(&VMX_Region_Phy);

VMXOFF

VMXON 命令は、VMX を終了する際に直接実行されます。

これらの 2 つの命令は、VS コンパイラで使用する場合、__vmx_off ()、__vmx_on () を使用できます。

2.VMX_Region の設定#

このメモリは NonePagePool タイプのメモリとして割り当てる必要があります。

このメモリの最初の 4 バイトには、VMCS_ID(MSR インデックス 0x480)を書き込む必要があります。

__readmsr () を使用して読み取り、その値をこのメモリに書き込みます。

* (ULONG*)VMX_Region = __readmsr(0x480);

エラー位置の確認#

__vmx_on () を実行した後、Eflags レジスタの関連ビットを検査し、eflags レジスタの CF フィールドが 1 に設定されているかどうかを確認します。1 に設定されている場合、VMX に入るのに失敗したことを示します。

*(ULONG_PTR*)(&eflags) = __readeflags();
if (eflags.fields.cf != 0) {
	DbgPrint("[CPU:%d]VMXON ON Failed", index);
}

4.VMCS フィールドの設定#

この章を読む前に、第五章の仮想化に入るを先に読んでください。

VMX 環境に正常に入った後、VMCS(Virtual Machine Control Structure)メモリが必要です。これを「VMCS_Region」と呼び、このメモリは仮想マシンの実行を制御するための情報を保存および管理するために主に使用されます。

1.vmwrite および vmread 命令#

vmwrite 命令

mov ecx,VMCS_Fields
mov eax,VMCS_Data
vmwrite ecx,eax

この命令は、書き込む必要がある VMCS_Fields を ecx に置き、書き込むデータを eax に置いて命令を実行し、成功すれば必要なデータが VMCS_Fields に書き込まれます。

vmread 命令

mov ecx,VMCS_Fields
vmread eax,ecx

読み取る必要がある VMCS_Fields を ecx に置き、命令を実行して読み取ったデータを ecx に格納します。


VMCS に主に書き込む必要があるのは Guest_State Host_State VM Execute State です。

1.VM Execute State の設定#
  1. Pin Base

仮想マシンの IA32_VMX_PINBASED_CTLS を設定します。このレジスタは、ピンに基づく仮想マシン実行制御の許可設定の大部分を制御します。レジスタのビット 31:0 は、これらの制御の許可の 0 設定を示します。MSR のビット X が 0 にクリアされている場合、VM エントリーは制御 X(ピンに基づく仮想マシン実行制御の第 X ビット)を 0 として許可します。MSR のビット X が 1 に設定されている場合、制御 X が 0 であれば、VM エントリーは失敗します。

  1. CPU Base

IA32_VMX_PROCBASED_CTLS を設定するために使用されます。このレジスタは、主プロセッサに基づく仮想マシン実行制御の許可設定の大部分を制御します。各ビットの制御を具体的に確認したい場合は、Intel のホワイトペーパー第 3 巻第 24 章第 6 節第 2 点を参照してください。

  1. VM Exit Controls

IA32_VMX_EXIT_CTLS レジスタを設定し、大部分の VM Exit の許可制御を制御します。詳細は第 3 巻第 24 節第 7 節第 1 点を参照してください。

  1. VM Entry Controls

IA32_VMX_ENTRY_CTLS を設定し、大部分の VM Entry の許可設定を記録します。詳細は第 3 巻第 24 節第 8 節第 1 点を参照してください。

  1. Secondary Processor-Based

IA32_VMX_PROCBASED_CTLS2 を設定し、主にセカンダリプロセッサを設定し、APIC EPT などを構成します。詳細は第 3 巻第 24 節第 6 節第 2 点を参照してください。

2.Guest_State の設定#
  1. レジスタ部分

設定する必要があるのは、セグメントレジスタ、セグメントセレクタ、セグメント制限、AR、セグメントベースアドレスなどで、ES、CS、DS、FS、GS、FS、TR、GDTR、IDTR、LDTR、RIP、RSP などが含まれます。具体的な設定パラメータは文書で提供されます。

  1. 非レジスタ

VMCS リンクポインタ。

3.Host_State の設定#

詳細は文末の文書を参照してください。

上記のフィールドに対応するインデックス番号は、Intel のホワイトペーパー第 3 巻付録 B で調べることができます。

5. 仮想化に入る#

上記の VMCS 内容を設定する前に、vmclear および vmptrld の 2 つの命令を実行する大まかな流れをコードで示します。

__vmx_vmclear(&VMCS_Phy);
__vmx_vmptrld(&VMCS_Phy);
SetupVmcs();	//VMCSを設定
__vmx_vmlaunch(); //仮想化に入る
DbgPrint(”Vmlaunch Failed“); //vmlaunchが成功した場合、dbgprint関数は実行されません

__vmx_vmlaunch を実行した後、プログラムに異常が発生しなければ、直接 GUEST モードに入ります。RIP/RSP は GUEST_RIP/GUEST_RSP アドレスに変化し、VMX-Exit イベントが発生すると例外が発生し、RIP/RSP は HOST_RIP/HOST_RSP が指す位置に変わります。

DbgPrint 関数が正常に実行された場合、VM_INSTRUCTION_ERROR のエラー番号を確認してください。これは Intel のホワイトペーパー第 3 巻第 30 章第 4 節で確認できます。

6.VM Exit の処理#

GUEST に正常に入った後、RIP は GUEST_RIP 位置にジャンプし、コードの実行を続けます。

この間に大量の VMX-Exit イベントが発生し、VM-Exit が発生すると仮想マシンは HOST_RIP 位置にジャンプします。したがって、Host_RIP には VMExithandler と呼ばれるコールバック関数を設定する必要があります。

VMExithandler 関数内で、VM_EXIT_REASON のエラー番号を検出して、エラーの発生したコードを判断し、GUEST_RIP を読み取ってコード実行エラーの位置を特定し、コードの文脈に基づいてエラーを排除します。

void ExitHandler(){
    DWORD64 ExitReason = __readmsr(VM_EXIT_REASON);
    DWORD64 GUEST_RIP =  __readmsr(GUEST_RIP);
    __DebugBreak();
}

ブレークポイントを設定してプログラムを停止し、ExitReason を取得してエラー番号に基づいて排除します(ExitReason エラー番号の説明は第 3 巻付録 B を参照)。

完全な流れは、hostrip の関数を asm ファイルに書き出し、レジスタ情報をスタックに保存し、コール命令を通じてポインタを Exithandler に渡すことです。

EXTERN_C ExitHandler:PROC //他のファイルから関数をインポート

PUSHAQ MACRO
    push    rax
    push    rcx
    push    rdx
    push    rbx
    push    -1    
    push    rbp
    push    rsi
    push    rdi
    push    r8
    push    r9
    push    r10
    push    r11
    push    r12
    push    r13
    push    r14
    push    r15
ENDM

POPAQ MACRO
    pop     r15
    pop     r14
    pop     r13
    pop     r12
    pop     r11
    pop     r10
    pop     r9
    pop     r8
    pop     rdi
    pop     rsi
    pop     rbp
    add     rsp, 8    
    pop     rbx
    pop     rdx
    pop     rcx
    pop     rax
ENDM

ExithandlerEntry PROC
	pushaq
	mov rcx,rsp
	sub rsp,50h
	call ExitHandler
	····//後続処理raxを通じて次の流れを制御
	popaq
	resume //GUESTに戻る
ExithandlerEntry ENDP

ExitReason に基づいて switch case プログラムを設定し、Host モードで GUEST モードで実行できない命令を設定します。

次に、VM_EXIT_INSTRUCTION_LEN を読み取って、GUEST_RIP が指す現在の命令の長さを取得し、GUEST_RIP + VM_EXIT_INSTRUCTION_LEN で GUEST_RIP を再設定します。

void CpuidError(//GUESTレジスタ情報を受け取る){
    //HostモードでGUESTが実行できないまたは例外を引き起こす命令を処理
}
void NextCode(){
    DWORD64 GUESTrip =  __readmsr(GUEST_RIP);
    DWORD64 VM_EXIT_INSTRUCTION =  __readmsr(VM_EXIT_INSTRUCTION_LEN);
    DWORD64 NextCode = GUESTrip + VM_EXIT_INSTRUCTION;
    __vmx_vmwrite(GUEST_RIP, NextCode);
}

EXTERN_C BOOLEAN ExitHandler(//GUESTレジスタ情報を取得する構造体を設定){
    DWORD64 ExitReason = __readmsr(VM_EXIT_REASON);
    DWORD64 GUESTrip =  __readmsr(GUEST_RIP);
    
    switch(ExitReason):
    case CPUIDError:  //関数にヒット
    {
    	CpuidError();
    	NextCode();
    	break;
    }
    default:
    	DbgPrint("Exit Reason %p\n", ExitReason); // 未処理のExitイベントを出力
    	__DebugBreak()// int ブレーク 
    	break;
   	return TURE; // ブール値を返してraxが0であるかどうかを確認
}

以上の流れで VMXExitHandler を設定し、vmexit イベントを処理します。

7. 仮想化からの退出#

VT コードはドライバとして Windows にロードされるため、VT を終了する必要がある場合は、ドライバをアンロードする関数を実行する必要があります。したがって、プログラムが正常に DriverLoad 関数の Return 部分に実行されることを保証する必要があります。GUEST に入る前に、スタック情報とレジスタ情報を保存して、GUEST に正常に入った後にスタックとレジスタ情報を復元し、プログラムが正常に実行されるようにします。これにより、ドライバのアンロード関数が正常に実行されることが保証されます。

GUEST は VMCALL 命令を実行でき、直接 GUEST モードを終了します。UnloadVT 関数内でこのコードを実行し、Exithandler で vmcall エラーをヒットさせます。vmcall はパラメータを提供して実行することも、提供せずに実行することもできます。コールバック関数を通じて rax が設定された特別なパラメータであるかどうかを確認し、そうであれば vmxon 命令を実行して VT を終了します。

EXTERN_C void __fastcall AsmVmcall(ULONG_PTR num, ULONG_PTR param);

void UnloadVt(){
	asmcall(vmxexit,0)	
}

asm 内で ExitHandler 関数の戻り値を検査します。

ExithandlerEntry PROC
	pushaq
	mov rcx,rsp
	sub rsp,50h
	call ExitHandler
	····//後続処理raxを通じて次の流れを制御
	test al,al // 戻り値が0であるかどうかを確認
	jz ExitVT:
	popaq // 修正されたレジスタを戻す
	resume // GUESTに戻る
	jmp Error
ExitVT:
	popaq
	vmxon
	jz Error
	jc Error
	push rax
	popfq                // スタックを復元
    mov rsp, rdx            
    push rcx
    ret  
Error:
	int 3
ExithandlerEntry ENDP

ExitHandler 内で設定します。

VT CloseVT(){
    //いくつかのセグメント属性、制限、ベースなどを復元
}
BOOLEAN vmcallhandler(GUESTレジスタ情報){
    //raxが設定された予想値である場合
    CloseVt();
    return FALSE;
    //そうでない場合は、通常のエラー処理を行います
    return TRUE;
}
EXTERN_C BOOLEAN ExitHandler(//GUESTレジスタ情報を取得する構造体を設定){
    DWORD64 ExitReason = __readmsr(VM_EXIT_REASON);
    DWORD64 GUESTrip =  __readmsr(GUEST_RIP);
    ret = TRUE;
    switch(ExitReason):
    case vmcall:  //関数にヒット
    {
        ret = vmcallhandler();
    	break;
    }
    default:
    	DbgPrint("Exit Reason %p\n", ExitReason); // 未処理のExitイベントを出力
    	__DebugBreak()// int ブレーク 
    	break;
   	return ret; // ブール値を返してraxが0であるかどうかを確認
}

まとめ#

コードはすでに GitHub リポジトリにアップロードされています。

皆さんの批評を歓迎します。良いと思った方はスターを付けてください。

記事内のコードにはいくつかの問題があるかもしれませんので、GitHub プロジェクトのソースコードを参考にしてください。


記事に必要な文書はここにあります!!!

Intel ホワイトペーパー全巻&VMCS 設定参考文書:

リンク:https://pan.baidu.com/s/1cmTCIKwaT_eGlnmpO178ZQ
提取码:yftx

読み込み中...
文章は、創作者によって署名され、ブロックチェーンに安全に保存されています。