カーネルモードのプロセス保護ドライバー。ObRegisterCallbacks を使用して、ハンドルからのメモリアクセス許可を剥奪します。基本的な実装であり、ハンドル作成をフックし、不正なアクセス試行から PROCESS_VM_READ/WRITE/OPERATION を削除します。


注: GIFがアニメーションしない場合は、Imgurで直接表示してください。
ドライバーは高度 321123 でOBコールバックを登録し、OB_OPERATION_HANDLE_CREATE および OB_OPERATION_HANDLE_DUPLICATE 操作をインターセプトします。プロセスが保護対象のターゲットへのハンドルを開こうとすると、ソースプロセス名をホワイトリスト(explorer.exe、csrss.exe、svchost.exe、MsMpEng.exe、RuntimeBroker.exe)と照合し、許可されていない場合はアクセス権を剥奪します。
現在の実装は、PsGetProcessImageFileName(非公開API)を使用した名前ベースのマッチングです。保護リストは、スピンロック同期を使用したリンクリストで管理されています。ワーカースレッドは存在しますが、リフレッシュロジックはまだ実装されていません。
ユーザーモードコンポーネントは、ImGui GUIを提供し、保護リストにプロセスを追加するためのインターフェースを提供します。通信は、METHOD_BUFFERED 転送を使用した \\Device\\Aegis シンボリックリンク経由のIOCTLコードを介して行われます。ドライバーは \\DosDevices\\Aegis から \\Device\\Aegis へのシンボリックリンクを作成し、ユーザーモードが \\\\.\\Aegis 経由でアクセスできるようにします。
カーネル(km/):
driver.c - DriverEntry、OB登録、IOCTLディスパッチャー、デバイス/シンボリックリンク作成protect_manager/ - プロセスリスト管理、ワーカースレッド(リフレッシュロジックは未実装)ユーザーモード(um/):
client.cpp - メインループ、ImGui描画、ウィンドウ管理gui/ - DirectX 11 + ImGuiセットアップ、カスタムフォント(Gilroy)service_manager/ - ドライバーのロード/アンロードのためのSCM統合ioctl_manager/ - ドライバーとの通信のためのDeviceIoControlラッパー321123 で登録(サードパーティ標準は321000以上、競合を避けるために高めに設定)PsGetProcessImageFileName を使用 - 非公開API。.exeファイル名のみを返す(最大15文字)KSPIN_LOCK 操作をDISPATCH_LEVEL(IRQL 2)で実行し、競合状態を防止NonPagedPoolはprotect_manager用(常にRAM、高速アクセス、プールは限られている)PagedPoolは個々のプロセス構造用(ページアウト可能)KEVENT を使用したシステムスレッド(非同期処理用、現在はスタブ)METHOD_BUFFERED、デバイスタイプは 0x8000(サードパーティ範囲)SERVICE_DEMAND_START で作成DeviceIoControlと、共有の ioctl.h で定義されたIOCTLコードを使用OBコールバック登録:
// ハンドル作成/複製のためのOBコールバックを登録
OB_CALLBACK_REGISTRATION ob_registration = { 0 };
OB_OPERATION_REGISTRATION op_registration = { 0 };
RtlSecureZeroMemory(&ob_registration, sizeof(ob_registration));
RtlSecureZeroMemory(&op_registration, sizeof(op_registration));
op_registration.ObjectType = PsProcessType;
op_registration.Operations = OB_OPERATION_HANDLE_CREATE | OB_OPERATION_HANDLE_DUPLICATE;
// 複製も対象とする理由: 賢いハッカーやマルウェアは、ハンドルを作成せず、
// 既存のものを複製して疑われないようにすることが多いため
op_registration.PreOperation = pre_open_process;
RtlInitUnicodeString(&ob_registration.Altitude, L"321123");
// 321000 はサードパーティドライバーの標準、競合を避けるため少し高めに設定
status = ObRegisterCallbacks(&ob_registration, ®_handle);
ハンドルアクセス剥奪:
OB_PREOP_CALLBACK_STATUS pre_open_process(PVOID reg_context, POB_PRE_OPERATION_INFORMATION info)
{
if (info->ObjectType == *PsProcessType) {
PEPROCESS proc = (PEPROCESS)info->Object;
char* process_name = (char*)PsGetProcessImageFileName(proc);
// DISPATCH_LEVEL でリストをロックし、BSODを防止
KIRQL irql;
KeAcquireSpinLock(&g_protect_manager->list_lock, &irql);
// プロセスが保護リストに含まれているか確認
for (entry = g_protect_manager->process_list.Flink;
entry != &g_protect_manager->process_list;
entry = entry->Flink) {
pprocess proces = CONTAINING_RECORD(entry, process, entry);
if (strcmp(proces->path, process_name) == 0) {
// ソースプロセスをチェック - システムプロセスは許可
PEPROCESS source_process = IoGetCurrentProcess();
char* source_name = (char*)PsGetProcessImageFileName(source_process);
if (strcmp(source_name, "explorer.exe") == 0 ||
strcmp(source_name, "csrss.exe") == 0 || ...) {
KeReleaseSpinLock(&g_protect_manager->list_lock, irql);
return OB_PREOP_SUCCESS;
}
// VM_READ, VM_WRITE, VM_OPERATION (0x1, 0x10, 0x20) を剥奪
// 0に設定しないのはバグの原因になるため
info->Parameters->CreateHandleInformation.DesiredAccess &= ~(0x1 | 0x10 | 0x20);
KeReleaseSpinLock(&g_protect_manager->list_lock, irql);
return OB_PREOP_SUCCESS;
}
}
KeReleaseSpinLock(&g_protect_manager->list_lock, irql);
}
return OB_PREOP_SUCCESS;
}
プロセスリスト管理:
NTSTATUS protect_manager_protect_process(pprotect_manager self, pprocess process)
{
// KIRQL (カーネル割り込み要求レベル) 0-31 はCPUに割り込みを禁止するよう指示
// 0 = PASSIVE_LEVEL, 2 = DISPATCH_LEVEL, 31 = HIGH_LEVEL
KIRQL irql;
// スピンロックを取得し、IRQLをDISPATCH_LEVEL (2) に引き上げる
// リストをロックし、編集時に変更されないようにする
KeAcquireSpinLock(&self->list_lock, &irql);
InsertTailList(&self->process_list, &process->entry);
KeReleaseSpinLock(&self->list_lock, irql);
// ワーカースレッドにリフレッシュを通知
self->refresh_needed = TRUE;
KeSetEvent(&self->worker_event, 0, FALSE); // FALSE = IRQL自動復帰
return STATUS_SUCCESS;
}
ユーザーモードIOCTL通信:
bool ioctl_manager::check(std::string name)
{
// シンボリックリンク \\\\.\\Aegis -> \\Device\\Aegis 経由でドライバーデバイスを開く
this->driver_object = CreateFile(this->path, GENERIC_READ | GENERIC_WRITE,
0, NULL, OPEN_EXISTING, 0, NULL);
// IOCTL経由でプロセス名をドライバーに送信
result = DeviceIoControl(this->driver_object, IOCTL_PROTECT_PID,
(LPVOID)data.c_str(), data.size() + 1,
NULL, 0, &bytes_returned, NULL);
CloseHandle(this->driver_object); // ハンドルを閉じるのを忘れずに
return result;
}
すべてのコードには、カーネルの概念、IRQLレベル、メモリ管理、潜在的な落とし穴に関する詳細なコメントが含まれています。ソースコードを自由に確認してください。教育的で、よく文書化されています。
bcdedit /set testsigning on)ビルド出力: km.sys(ドライバー)および um.exe(ユーザーモードアプリ)。
OB_OPERATION_HANDLE_CREATE のみ処理、複製攻撃が可能protect_manager.c のリフレッシュロジックが空ExAllocatePool2 ではなく ExAllocatePool を使用PsActiveProcessHead からのリンク解除によるプロセス隠蔽、PsLoadedModuleList からのドライバー隠蔽教育目的のみ。Windowsアップデートで動作しなくなる可能性のある非公開APIを使用しています。実行するにはテスト署名または適切なコード署名が必要です。