Üye
Giriş
1) Öncelikle driver ve client arasında iletişim kurmanız gerekli (standart ioctl kullanmayın)
Örnek;
2) MmUnloadedDrivers ve PiDDBCachetable gibi izleri temizleyin
Örnek;
3) Poolları allocate etmeyin
ADIM 1: Fonksiyon pointer'ını bulmak
Bu fonksiyon pointer'ı için gerekenler:
1) Usermode'dan çağırılabilir olmalı
2) PatchGuard bulunmamalı
3) Parametreleri olmalı
4) Funciton Call'dan önce en az bir parametrenin temizlenmemiş olması gerekir
İlk olarak guard_dispatch_icall için xref'leri bulmamız gerekiyor. guard_dispatch_icall tüm fonksiyon pointerlarının istenmeyen kodları çalıştırmasını önlemek için control flow korumalı driverlar tarafından çağırılan fonksiyondur. Eğer bunu xref ederseniz bir driverin tüm fonksiyon pointerlarını bulabilirsiniz. Image integrity check'ten sonra RAX'ta bulunan fonksiyonu çağıracaktır.
Bazı sebeplerden dolayı guard dispatch icall bu fonksiyon pointerlarını kullanmamızı engellemiyor, control flow guard hiçbir işe yaramıyor.
Genellikle Syscall'ların önünde "Nt" bulunur, bu yüzden bir "Nt" fonksiyonu arayalım.
Syscall ile çağırılan fonksiyon pointer'ınızı (v8) bulduktan sonra kendi fonksiyonumuza ayarlayabilir, girdiğimiz parametreler ile geçirebiliriz.
Sadece 1 byte parametre alanına ihtiyacınız var! ekstra input / output ile iletişim kurmak için Usermode'da bir buffer ayırabilir ve bu buffer aracılığıyla iletişim kurmak için MmCopyVirtualMemory / KeStackAttachProcess kullanabilirsiniz.
ADIM 2: Usermode'dan çağırmak ve her şeyi bir araya getirmek
Fonksiyonu çağırma şekliniz, ne tür bir fonksiyonu kullandığınıza bağlı, eğer standart bir syscall ile çağırılan ntoskrnl içerisindeki bir fonksiyon pointeri ise
Input'unuzu bu fonksiyonun parametrelerinden geçirin, izleri temizleyin ve read/write ekleyin.
1) Öncelikle driver ve client arasında iletişim kurmanız gerekli (standart ioctl kullanmayın)
Örnek;
C:
#include <ntddk.h>
const WCHAR sc_wszDeviceNameBuffer[] = L"\\Device\\Comm_Test";
const WCHAR sc_wszDeviceSymLinkBuffer[] = L"\\DosDevices\\Comm_Test";
NTSTATUS IRPRead(PDEVICE_OBJECT pDriverObject, PIRP pIrp)
{
UNREFERENCED_PARAMETER(pDriverObject);
char szBuffer[255] = "Test";
strcpy(pIrp->AssociatedIrp.SystemBuffer, szBuffer);
pIrp->IoStatus.Status = STATUS_SUCCESS;
pIrp->IoStatus.Information = strlen(szBuffer);
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
NTSTATUS IRPWrite(PDEVICE_OBJECT pDriverObject, PIRP pIrp)
{
UNREFERENCED_PARAMETER(pDriverObject);
char szBuffer[255] = { 0 };
strcpy(szBuffer, pIrp->AssociatedIrp.SystemBuffer);
pIrp->IoStatus.Status = STATUS_SUCCESS;
pIrp->IoStatus.Information = strlen(szBuffer);
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
NTSTATUS MajorFunctionCall(PDEVICE_OBJECT pDriverObject, PIRP pIrp)
{
PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp);
switch (pStack->MajorFunction)
{
case IRP_MJ_READ:
IRPRead(pDriverObject, pIrp);
break;
case IRP_MJ_WRITE:
IRPWrite(pDriverObject, pIrp);
break;
default:
pIrp->IoStatus.Status = STATUS_SUCCESS;
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
}
return STATUS_SUCCESS;
}
VOID DriverUnload(IN PDRIVER_OBJECT pDriverObject)
{
UNREFERENCED_PARAMETER(pDriverObject);
DbgPrint("Driver unload rutini çağırıldı!\n");
UNICODE_STRING symLink;
RtlInitUnicodeString(&symLink, sc_wszDeviceSymLinkBuffer);
IoDeleteSymbolicLink(&symLink);
if (pDriverObject && pDriverObject->DeviceObject)
{
IoDeleteDevice(pDriverObject->DeviceObject);
}
}
NTSTATUS DriverEntry(IN PDRIVER_OBJECT pDriverObject, IN PUNICODE_STRING pRegistryPath)
{
UNREFERENCED_PARAMETER(pRegistryPath);
if (!pDriverObject)
{
DbgPrint("Comm_Test driver entry null!\n");
return STATUS_FAILED_DRIVER_ENTRY;
}
DbgPrint("Driver yüklendi, system range başlangıcı %p, Entry: %p\n", MmSystemRangeStart, DriverEntry);
pDriverObject->DriverUnload = &OnDriverUnload;
NTSTATUS ntStatus = 0;
UNICODE_STRING deviceNameUnicodeString, deviceSymLinkUnicodeString;
RtlInitUnicodeString(&deviceNameUnicodeString, sc_wszDeviceNameBuffer);
RtlInitUnicodeString(&deviceSymLinkUnicodeString, sc_wszDeviceSymLinkBuffer);
PDEVICE_OBJECT pDeviceObject = NULL;
ntStatus = IoCreateDevice(pDriverObject, 0, &deviceNameUnicodeString, FILE_DEVICE_UNKNOWN, FILE_DEVICE_SECURE_OPEN, FALSE, &pDeviceObject);
if (ntStatus != STATUS_SUCCESS)
{
DbgPrint("Comm_Test IoCreateDevice başarısız oldu! Status: %p\n", ntStatus);
return ntStatus;
}
ntStatus = IoCreateSymbolicLink(&deviceSymLinkUnicodeString, &deviceNameUnicodeString);
if (ntStatus != STATUS_SUCCESS)
{
DbgPrint("Comm_Test IoCreateSymbolicLink başarısız! Status: %p\n", ntStatus);
return ntStatus;
}
for (ULONG t = 0; t <= IRP_MJ_MAXIMUM_FUNCTION; t++)
pDriverObject->MajorFunction[t] = &OnMajorFunctionCall;
pDeviceObject->Flags |= DO_BUFFERED_IO;
return STATUS_SUCCESS;
}
C++:
#include <Windows.h>
#include <string>
#include <iostream>
static const std::string gsc_szSymLink = "\\\\.\\Comm_Test";
static HANDLE gs_hDriver = INVALID_HANDLE_VALUE;
void HataMesaji(const std::string & szError, DWORD dwErrorCode)
{
char szErrorMessage[4096] = { 0 };
auto dwFlags = DWORD(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_MAX_WIDTH_MASK);
FormatMessageA(dwFlags, NULL, dwErrorCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), szErrorMessage, _countof(szErrorMessage), NULL);
printf("%s | Hata: %u - Açıklama: %s\n", szError.c_str(), dwErrorCode, szErrorMessage);
}
bool ReadRoutine()
{
auto dwReadCount = 0UL;
char szBuff[255] = { 0 };
if (ReadFile(gs_hDriver, szBuff, sizeof(szBuff), &dwReadCount, NULL) == FALSE)
{
ShowErrorMessage("ReadFile başarısız!", GetLastError());
return false;
}
printf("Mesaj içeriği: %s Boyut: %u\n", szBuff, dwReadCount);
return true;
}
bool WriteRoutine()
{
printf("Mesajı girin: ");
auto szMessage = std::string("");
std::cin >> szMessage;
auto dwMessageSize = static_cast<DWORD>(szMessage.size());
auto dwWriteCount = 0UL;
if (WriteFile(gs_hDriver, szMessage.c_str(), dwMessageSize + 1, &dwWriteCount, NULL) == FALSE)
{
ShowErrorMessage("WriteFile başarısız!", GetLastError());
return false;
}
printf("Mesaj gönderildi! Boyut: %u İçerik boyutu: %u\n", dwWriteCount, dwMessageSize);
return true;
}
int main()
{
printf("İletişim CLI'ı başladı! Hedef device: %s\n", gsc_szSymLink.c_str());
gs_hDriver = CreateFileA(gsc_szSymLink.c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
if (!gs_hDriver || gs_hDriver == INVALID_HANDLE_VALUE)
{
ShowErrorMessage("CreateFileA başarısız!", GetLastError());
return 1;
}
printf("Handle başarıyla oluşturuldu: %p\n", gs_hDriver);
printf("--------------------------------------\n");
char pInput = '0';
while (pInput != 'x')
{
printf("Lütfen seçin:\n1 --> Mesajı oku\n2 --> Mesaj yaz\nx --> Exit\n");
std::cin >> pInput;
switch (pInput)
{
case '1':
{
ReadRoutine();
} break;
case '2':
{
WriteRoutine();
} break;
case 'x':
return 0;
default:
continue;
}
}
return 0;
}
2) MmUnloadedDrivers ve PiDDBCachetable gibi izleri temizleyin
Örnek;
C++:
bool driver::ClearMmUnloadedDrivers(HANDLE device_handle)
{
ULONG buffer_size = 0;
void* buffer = nullptr;
NTSTATUS status = NtQuerySystemInformation(static_cast<SYSTEM_INFORMATION_CLASS>(nt::SystemExtendedHandleInformation), buffer, buffer_size, &buffer_size);
while (status == nt::STATUS_INFO_LENGTH_MISMATCH)
{
VirtualFree(buffer, 0, MEM_RELEASE);
buffer = VirtualAlloc(nullptr, buffer_size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
status = NtQuerySystemInformation(static_cast<SYSTEM_INFORMATION_CLASS>(nt::SystemExtendedHandleInformation), buffer, buffer_size, &buffer_size);
}
if (!NT_SUCCESS(status))
{
VirtualFree(buffer, 0, MEM_RELEASE);
return false;
}
uint64_t object = 0;
auto system_handle_inforamtion = static_cast<nt::PSYSTEM_HANDLE_INFORMATION_EX>(buffer);
for (auto i = 0u; i < system_handle_inforamtion->HandleCount; ++i)
{
const nt::SYSTEM_HANDLE current_system_handle = system_handle_inforamtion->Handles[i];
if (current_system_handle.UniqueProcessId != reinterpret_cast<HANDLE>(static_cast<uint64_t>(GetCurrentProcessId())))
continue;
if (current_system_handle.HandleValue == device_handle)
{
object = reinterpret_cast<uint64_t>(current_system_handle.Object);
break;
}
}
VirtualFree(buffer, 0, MEM_RELEASE);
if (!object)
return false;
uint64_t device_object = 0;
if (!ReadMemory(device_handle, object + 0x8, &device_object, sizeof(device_object)))
return false;
uint64_t driver_object = 0;
if (!ReadMemory(device_handle, device_object + 0x8, &driver_object, sizeof(driver_object)))
return false;
uint64_t driver_section = 0;
if (!ReadMemory(device_handle, driver_object + 0x28, &driver_section, sizeof(driver_section)))
return false;
UNICODE_STRING us_driver_base_dll_name = { 0 };
if (!ReadMemory(device_handle, driver_section + 0x58, &us_driver_base_dll_name, sizeof(us_driver_base_dll_name)))
return false;
us_driver_base_dll_name.Length = 0;
if (!WriteMemory(device_handle, driver_section + 0x58, &us_driver_base_dll_name, sizeof(us_driver_base_dll_name)))
return false;
return true;
}
C++:
ClearMmUnloadedDrivers(device_handle);
3) Poolları allocate etmeyin
ADIM 1: Fonksiyon pointer'ını bulmak
Bu fonksiyon pointer'ı için gerekenler:
1) Usermode'dan çağırılabilir olmalı
2) PatchGuard bulunmamalı
3) Parametreleri olmalı
4) Funciton Call'dan önce en az bir parametrenin temizlenmemiş olması gerekir
İlk olarak guard_dispatch_icall için xref'leri bulmamız gerekiyor. guard_dispatch_icall tüm fonksiyon pointerlarının istenmeyen kodları çalıştırmasını önlemek için control flow korumalı driverlar tarafından çağırılan fonksiyondur. Eğer bunu xref ederseniz bir driverin tüm fonksiyon pointerlarını bulabilirsiniz. Image integrity check'ten sonra RAX'ta bulunan fonksiyonu çağıracaktır.
Bazı sebeplerden dolayı guard dispatch icall bu fonksiyon pointerlarını kullanmamızı engellemiyor, control flow guard hiçbir işe yaramıyor.
Bağlantıları görmek için lütfen
Giriş Yap
Genellikle Syscall'ların önünde "Nt" bulunur, bu yüzden bir "Nt" fonksiyonu arayalım.
Bağlantıları görmek için lütfen
Giriş Yap
Syscall ile çağırılan fonksiyon pointer'ınızı (v8) bulduktan sonra kendi fonksiyonumuza ayarlayabilir, girdiğimiz parametreler ile geçirebiliriz.
Sadece 1 byte parametre alanına ihtiyacınız var! ekstra input / output ile iletişim kurmak için Usermode'da bir buffer ayırabilir ve bu buffer aracılığıyla iletişim kurmak için MmCopyVirtualMemory / KeStackAttachProcess kullanabilirsiniz.
ADIM 2: Usermode'dan çağırmak ve her şeyi bir araya getirmek
Fonksiyonu çağırma şekliniz, ne tür bir fonksiyonu kullandığınıza bağlı, eğer standart bir syscall ile çağırılan ntoskrnl içerisindeki bir fonksiyon pointeri ise
C++:
HMODULE hModule = LoadLibrary(L"ntdll.dll");
fonksiyon = (FonksiyonPointeri)GetProcAddress(hModule, "");
Input'unuzu bu fonksiyonun parametrelerinden geçirin, izleri temizleyin ve read/write ekleyin.
Son düzenleme: