Operasi filter driver dalam registri. Berlatih

Halo, Habr!

Ketika saya dihadapkan dengan tugas untuk menulis driver saya sendiri, yang memonitor operasi dalam registri, saya, tentu saja, mencari di Internet untuk setidaknya beberapa informasi tentang ini. Tetapi satu-satunya hal yang keluar atas permintaan "Driver-filter dari registri" adalah aliran artikel tentang menulis driver-filter (tepuk tangan), TETAPI semua artikel ini hanya menyangkut filter sistem file (kesedihan).

Sayangnya, satu-satunya hal yang ditemukan adalah artikel tahun 2003, kode yang tidak akan pernah Anda kumpulkan di VS19 baru Anda.

Untungnya, ada contoh yang bagus dari Microsoft di GitHub (Saya akan langsung membuang tautan ), di mana sebagian besar analisis ini akan dibangun.

Mungkin tautan ke contoh sudah cukup bagi superprogrammer untuk menyelesaikan semuanya dalam 5 menit. Tetapi ada juga pemula, siswa, seperti saya, untuk siapa, kemungkinan besar, artikel ini akan menjadi. Saya harap ini sangat membantu seseorang.

Baiklah Dikejar. Kami membuka contoh. Perhatian! Kami tidak takut pada sejumlah besar file, 80% kami tidak perlu.

Kami melihat 2 folder dalam proyek: exe dan sys. Yang pertama berisi program yang memulai driver, mendaftarkannya di sistem, dan setelah selesai bekerja dengan driver, menghapusnya. Kami akan mulai dengannya.

Buka regctrl.c

Ini hampir semua kode program yang kita butuhkan.

Segera pergi ke fungsi utama. Apa yang kita lihat di sana? Memuat driver dengan fungsi UtilLoadDriver (util.c), dan kemudian instruksi untuk beberapa pengaturan:

printf("\treg add \"HKLM\\SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Debug Print Filter\" /v IHVDRIVER /t REG_DWORD /d 0x8\n\n"); 

Ya, Anda harus memasukkan parameter ke dalam registri di folder yang ditentukan (Anda dapat menggunakan cmd, atau Anda dapat menggunakan pena). Ini diperlukan agar kami dapat melihat lebih banyak pesan dari pengemudi
Omong-omong, jangan lupa mengunduh aplikasi yang memungkinkan Anda melihat informasi debug, saya menggunakan DbgView.

Selanjutnya kita melihat 2 fungsi menarik: DoKernelModeSamples dan DoUserModeSamples - mereka diperlukan untuk menunjukkan operasi driver. Ini yang pertama, misalnya, mengirimkan permintaan ke driver IOCL dengan fungsi DeviceIoControl, driver, pada gilirannya, akan meluncurkan fungsi yang diperlukan menggunakan parameter IOCTL_DO_KERNELMODE_SAMPLES.

Dari deskripsi fungsi DeviceIoControl, kita melihat bahwa ia dapat meneruskan buffer ke driver dan juga menerimanya. Kami akan membutuhkan ini di masa depan. Sementara itu, tidak ada yang menarik bagi kami dalam file ini.

Mari kita buka folder sys, file driver.c

Mari kita mulai dengan fungsi DriverEntry. Di sana, driver menampilkan beberapa jenis informasi debug, kemudian fungsi IoCreateDeviceSecure membuat objek perangkat bernama dan menerapkan parameter keamanan yang ditentukan, sedikit yang menarik menunggu kita lebih lanjut:

 DriverObject->MajorFunction[IRP_MJ_CREATE] = DeviceCreate; DriverObject->MajorFunction[IRP_MJ_CLOSE] = DeviceClose; DriverObject->MajorFunction[IRP_MJ_CLEANUP] = DeviceCleanup; DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DeviceControl; DriverObject->DriverUnload = DeviceUnload; 

Dalam tanda kurung adalah kode fungsi utama untuk IRP. Artinya, ini adalah jenis paket yang akan mendapat perhatian pengemudi kami. Setelah tanda "=", fungsi yang akan memproses paket yang masuk ditunjukkan. Kemudian lagi, sedikit menarik. TAPI. Di sini Anda perlu menambahkan satu fitur menarik. Ingat tempat ini, kami akan kembali ke sini
Jadi, jika semuanya jelas dengan DeviceCreate, DeviceClose, DeviceCleanup dan DeviceUnload, lalu apa yang terjadi di DeviceControl? Dan di sana permintaan program kami akan terbang, yang kami kirim dengan fungsi DeviceIoControl. Kami mengambil permintaan dari tumpukan dan mengambil (dalam contoh ini) hanya parameter kedua yang saya bicarakan:

 IrpStack = IoGetCurrentIrpStackLocation(Irp); Ioctl = IrpStack->Parameters.DeviceIoControl.IoControlCode; 

Berdasarkan pada IoControlCode, driver akan pergi untuk melakukan fungsi tertentu. Saya menyarankan Anda untuk memahami, misalnya, pertimbangkan file pre.c dan cari tahu apa yang terjadi di sana.

Dan kita akan menyelesaikan pertimbangan contoh dengan poin menarik terakhir - tentu saja, fungsi Callback .

Di sinilah notifikasi operasi yang terjadi di registri akan terbang. Ingat tempat yang saya minta untuk diingat? Ini sedikit lebih tinggi. Di sini kita akan meninggalkan CmRegisterCallbackEx. Mereka akan mendeklarasikan fungsi Callback sebagai "tas" di mana paket IRP akan terbang untuk diproses. CallbackCtx-> Ketinggian akan menentukan tingkat driver kami (kami bukan satu-satunya yang memonitor registri), yaitu, berapa tinggi driver kami akan mencegat paket dan melakukan sesuatu dengan mereka (Sekali lagi, di pre.c, cukup jelas apa dan bagaimana hal itu terjadi : Kami mendaftarkan fungsi, melakukan sesuatu dengan registri, semuanya sudah diperbaiki, informasi ditampilkan oleh driver, dan kemudian kami melakukan tindakan sebaliknya - CmUnRegisterCallback - sehingga tidak ada lagi yang datang kepada kami).

Oh ya Jangan panik ketika di DbgView Anda menemukan aliran pesan yang tak ada habisnya dari driver - selalu ada beberapa hangout di registri.

Sebenarnya, dari argumen fungsi CallBack, Anda dapat mengekstrak semua informasi yang diperlukan - baik operasi yang dilakukan pada beberapa tombol (ini hanya dalam kode - NotifyClass), dan nama kuncinya

Sekarang mari kita beralih dari contoh ini. Pertimbangkan apa yang bisa dilakukan dengan menarik.

Tugas semacam itu: biarkan kami memiliki nama program dan kunci registri dalam sebuah file, kami juga menentukan hak akses program ke kunci tertentu (kami membatasi diri untuk sederhana: ia memiliki / tidak memiliki akses).

Program kami (yang ada di folder exe) akan membaca konfigurasi dan mengirimkannya ke driver menggunakan permintaan IOCL. Artinya, dalam fungsi DeviceIoControl sebagai argumen ketiga, kami akan melewati buffer. Anda dapat mentransfer dan mengatur konfigurasi sesuai keinginan.

Pengemudi memperoleh hak-hak ini dan menyimpan dirinya ke beberapa penyangga global. Array input dapat diperoleh dengan cara ini:

 in_buf = Irp->AssociatedIrp.SystemBuffer; 

Sekarang cobalah untuk menolak beberapa akses program ke kunci
Pergi ke fungsi Callback.

Mari kita tunjukkan nama program kami dan kunci yang tidak memiliki akses, masing-masing MyProg dan MyKey.

Kita perlu mengetahui program mana yang saat ini mencoba mengakses kunci dan membandingkan namanya dengan yang terdaftar dalam konfigurasi kita. Nama proses dapat diperoleh dengan cara ini:

 PUNICODE_STRING processName = NULL; GetProcessImageName(PsGetCurrentProcess(), &processName); if (wcsstr(processName->Buffer, MyProg) != NULL) { <>} 

Fungsi GetProcessImageName bukan perpustakaan (tetapi Internet), berbagai variasinya dapat ditemukan di banyak forum. Saya akan meninggalkannya di sini:

 typedef NTSTATUS(*QUERY_INFO_PROCESS) ( __in HANDLE ProcessHandle, __in PROCESSINFOCLASS ProcessInformationClass, __out_bcount(ProcessInformationLength) PVOID ProcessInformation, __in ULONG ProcessInformationLength, __out_opt PULONG ReturnLength ); QUERY_INFO_PROCESS ZwQueryInformationProcess; NTSTATUS GetProcessImageName( PEPROCESS eProcess, PUNICODE_STRING* ProcessImageName ) { NTSTATUS status = STATUS_UNSUCCESSFUL; ULONG returnedLength; HANDLE hProcess = NULL; PAGED_CODE(); // this eliminates the possibility of the IDLE Thread/Process if (eProcess == NULL) { return STATUS_INVALID_PARAMETER_1; } status = ObOpenObjectByPointer(eProcess, 0, NULL, 0, 0, KernelMode, &hProcess); if (!NT_SUCCESS(status)) { DbgPrint("ObOpenObjectByPointer Failed: %08x\n", status); return status; } if (ZwQueryInformationProcess == NULL) { UNICODE_STRING routineName = RTL_CONSTANT_STRING(L"ZwQueryInformationProcess"); ZwQueryInformationProcess = (QUERY_INFO_PROCESS)MmGetSystemRoutineAddress(&routineName); if (ZwQueryInformationProcess == NULL) { DbgPrint("Cannot resolve ZwQueryInformationProcess\n"); status = STATUS_UNSUCCESSFUL; goto cleanUp; } } /* Query the actual size of the process path */ status = ZwQueryInformationProcess(hProcess, ProcessImageFileName, NULL, // buffer 0, // buffer size &returnedLength); if (STATUS_INFO_LENGTH_MISMATCH != status) { DbgPrint("ZwQueryInformationProcess status = %x\n", status); goto cleanUp; } *ProcessImageName = ExAllocatePoolWithTag(NonPagedPoolNx, returnedLength, '2gat'); if (ProcessImageName == NULL) { status = STATUS_INSUFFICIENT_RESOURCES; goto cleanUp; } /* Retrieve the process path from the handle to the process */ status = ZwQueryInformationProcess(hProcess, ProcessImageFileName, *ProcessImageName, returnedLength, &returnedLength); if (!NT_SUCCESS(status)) ExFreePoolWithTag(*ProcessImageName, '2gat'); cleanUp: ZwClose(hProcess); return status; } 

Kami menemukan bahwa saat ini MyProg sedang mengakses registri. Sekarang Anda perlu mencari tahu kunci mana.

Dari argumen kedua, kami mengekstrak informasi tentang kunci yang sedang diakses

 REG_PRE_OPEN_KEY_INFORMATION* pRegPreCreateKey = (REG_PRE_OPEN_KEY_INFORMATION*)Argument2; if (pRegPreCreateKey != NULL) { if (wcscmp(pRegPreCreateKey->CompleteName->Buffer, MyKey) == 0) { if (){// return STATUS_SUCCESS; } else {// return STATUS_ACCESS_DENIED; } } } 

Cukup kembalikan nilai yang mengindikasikan larangan. Dan itu saja.

Artikel ini tidak dimaksudkan untuk memastikan bahwa setiap orang yang membaca setelahnya pergi untuk melihat driver super.

Ini, bisa dikatakan, adalah pengantar jalannya urusan :) Karena biasanya itu benar-benar tidak cukup ketika Anda baru mulai mengerti.

Source: https://habr.com/ru/post/id485606/


All Articles