_Use_decl_annotations_ EXTERN_C NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) { UNREFERENCED_PARAMETER(RegistryPath); PAGED_CODE(); auto status = STATUS_UNSUCCESSFUL; DriverObject->DriverUnload = DriverUnload; DBG_BREAK(); status = LogInitialization(LOG_LEVEL, nullptr); if (!NT_SUCCESS(status)) { return status; } // Build the following code as a SYSENTER handler on NonPagedPool // // FF 25 00 00 00 00 jmp cs:jmp_address // FF FF FF FF FF FF FF FF jmp_address dq 0FFFFFFFFFFFFFFFFh const JMP_CODE jmpCode = {{0xff, 0x25}, __readmsr(IA32_LSTAR)}; g_Trampoline = reinterpret_cast<UCHAR*>(ExAllocatePoolWithTag( NonPagedPoolExecute, sizeof(jmpCode), POOL_TAG_NAME)); if (!g_Trampoline) { LogTermination(); return STATUS_MEMORY_NOT_ALLOCATED; } RtlCopyMemory(g_Trampoline, &jmpCode, sizeof(jmpCode)); // Modify MSR UtilForEachProcessor(MsrHookCallback, nullptr); return status; }
_Use_decl_annotations_ EXTERN_C static void VminitpLaunchVM() { size_t errorCode = 0; auto vmxStatus = static_cast<VMX_STATUS>(__vmx_vmread(VM_INSTRUCTION_ERROR, &errorCode)); if (vmxStatus != VMX_OK) { LOG_WARN("VM_INSTRUCTION_ERROR = %p %d", errorCode, vmxStatus); } DBG_BREAK(); vmxStatus = static_cast<VMX_STATUS>(__vmx_vmlaunch()); // Here is not be executed with successful vmlaunch. Instead, the context // jumps to an address specified by GUEST_RIP. if (vmxStatus == VMX_ERROR_WITH_STATUS) { vmxStatus = static_cast<VMX_STATUS>(__vmx_vmread(VM_INSTRUCTION_ERROR, &errorCode)); LOG_ERROR("VM_INSTRUCTION_ERROR = %p %d", errorCode, vmxStatus); } DBG_BREAK(); }
_Use_decl_annotations_ EXTERN_C static void DriverUnload( PDRIVER_OBJECT DriverObject) { UNREFERENCED_PARAMETER(DriverObject); PAGED_CODE(); DBG_BREAK(); // Restore MSR UtilForEachProcessor(MsrHookCallback, nullptr); ExFreePoolWithTag(g_Trampoline, POOL_TAG_NAME); LogTermination(); }
// Switch the current log buffer and save the contents of old buffer to the log // file. This function does not flush the log file, so code should call // LogpWriteMessageToFile() or ZwFlushBuffersFile() later. EXTERN_C static NTSTATUS LogpWriteLogBufferToFile( _In_opt_ LogBufferInfo *Info) { NT_ASSERT(Info); NT_ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL); auto status = STATUS_SUCCESS; // Enter a critical section and acquire a reader lock for Info in order to // write a log file safely. ExEnterCriticalRegionAndAcquireResourceExclusive(&Info->Resource); // Acquire a spin lock for Info.LogBuffer(s) in order to switch its head // safely. const auto irql = KeAcquireSpinLockRaiseToDpc(&Info->SpinLock); auto oldLogBuffer = const_cast<char *>(Info->LogBufferHead); if (oldLogBuffer[0]) { Info->LogBufferHead = (oldLogBuffer == Info->LogBuffer1) ? Info->LogBuffer2 : Info->LogBuffer1; Info->LogBufferHead[0] = '\0'; Info->LogBufferTail = Info->LogBufferHead; } KeReleaseSpinLock(&Info->SpinLock, irql); // Write all log entries in old log buffer. IO_STATUS_BLOCK ioStatus = {}; for (auto currentLogEntry = oldLogBuffer; currentLogEntry[0]; /**/) { const auto currentLogEntryLength = strlen(currentLogEntry); status = ZwWriteFile(Info->LogFileHandle, nullptr, nullptr, nullptr, &ioStatus, currentLogEntry, static_cast<ULONG>(currentLogEntryLength), nullptr, nullptr); if (!NT_SUCCESS(status)) { // It could happen when you did not register IRP_SHUTDOWN and call // LogIrpShutdownHandler() and the system tried to log to a file after // a filesystem was unmounted. DBG_BREAK(); } currentLogEntry += currentLogEntryLength + 1; } oldLogBuffer[0] = '\0'; ExReleaseResourceAndLeaveCriticalRegion(&Info->Resource); return status; }
// Logs the current log entry to and flush the log file. EXTERN_C static NTSTATUS LogpWriteMessageToFile( _In_ const char *Message, _In_ const LogBufferInfo &Info) { NT_ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL); IO_STATUS_BLOCK ioStatus = {}; auto status = ZwWriteFile(Info.LogFileHandle, nullptr, nullptr, nullptr, &ioStatus, const_cast<char *>(Message), static_cast<ULONG>(strlen(Message)), nullptr, nullptr); if (!NT_SUCCESS(status)) { // It could happen when you did not register IRP_SHUTDOWN and call // LogIrpShutdownHandler() and the system tried to log to a file after // a filesystem was unmounted. DBG_BREAK(); } status = ZwFlushBuffersFile(Info.LogFileHandle, &ioStatus); return status; }
EXTERN_C static void LogpFinalizeBufferInfo( _In_opt_ PDEVICE_OBJECT DeviceObject, _In_ LogBufferInfo *Info) { PAGED_CODE(); NT_ASSERT(Info); // Closing the log buffer flush thread. if (Info->BufferFlushThreadHandle) { Info->BufferFlushThreadShouldBeAlive = false; auto status = ZwWaitForSingleObject(Info->BufferFlushThreadHandle, FALSE, nullptr); if (!NT_SUCCESS(status)) { DBG_BREAK(); } ZwClose(Info->BufferFlushThreadHandle); Info->BufferFlushThreadHandle = nullptr; } // Cleaning up other things. if (Info->LogFileHandle) { ZwClose(Info->LogFileHandle); Info->LogFileHandle = nullptr; } if (Info->LogBuffer2) { ExFreePoolWithTag(Info->LogBuffer2, LOGP_POOL_TAG_NAME); Info->LogBuffer2 = nullptr; } if (Info->LogBuffer1) { ExFreePoolWithTag(Info->LogBuffer1, LOGP_POOL_TAG_NAME); Info->LogBuffer1 = nullptr; } if (DeviceObject) { IoUnregisterShutdownNotification(DeviceObject); } if (Info->ResourceInitialized) { ExDeleteResourceLite(&Info->Resource); Info->ResourceInitialized = false; } }
EXTERN_C static void Win8pDetectPatchGuardWorkItem( __inout WORK_QUEUE_ITEM* Item, __in ULONG64 CallerId) // For debugging { PAGED_CODE(); // It should be by the SYSTEM process when it was called from // ExpWorkerThread if (PsGetCurrentProcessId() != reinterpret_cast<HANDLE>(4)) { return; } // And of course, the Work Item should be valid. Be-aware, we are not // filtering DCP routines that may use a non-canonical address. if (Item < MmSystemRangeStart || !Win8pIsAccessibleAddress(Item)) { return; } if (Item->WorkerRoutine < MmSystemRangeStart || !Win8pIsAccessibleAddress(Item->WorkerRoutine)) { return; } // 0 = KiCommitThreadWait, 1 = KiAttemptFastRemovePriQueue // It seems that the value is always 0 though if (CallerId == 1) { DBG_BREAK(); } PgContext_8_1* pgContext = nullptr; // It is a PatchGuard context if the routine is KiScbQueueScanWorker if (reinterpret_cast<ULONG_PTR>(Item->WorkerRoutine) == g_Symbols.KiScbQueueScanWorker) { // Decode parameter by doing the same things as KiScbQueueScanWorker auto* param = reinterpret_cast<Pg_KiScbQueueScanWorkerContext*>( Item->Parameter); ULONG64 routine = param->EncodedRoutine ^ param->XorKey; ULONG64 context = param->EncodedPgContext ^ param->XorKey; DBG_PRINT("[%5Iu:%5Iu] PatchGuard %016llX :" " KiScbQueueScanWorker (%016llX) was dequeued.\n", reinterpret_cast<ULONG_PTR>(PsGetCurrentProcessId()), reinterpret_cast<ULONG_PTR>(PsGetCurrentThreadId()), context, routine); pgContext = reinterpret_cast<PgContext_8_1*>(context); } // It is PatchGuard context if the routine is FsRtlUninitializeSmallMcb. else if (*reinterpret_cast<ULONG_PTR*>(Item->WorkerRoutine) == WIN8_FsRtlUninitializeSmallMcb_PATTERN) { DBG_PRINT("[%5Iu:%5Iu] PatchGuard %p :" " FsRtlUninitializeSmallMcb (%p) was dequeued.\n", reinterpret_cast<ULONG_PTR>(PsGetCurrentProcessId()), reinterpret_cast<ULONG_PTR>(PsGetCurrentThreadId()), Item->Parameter, Item->WorkerRoutine); pgContext = reinterpret_cast<PgContext_8_1*>(Item->Parameter); } else { // Neither KiScbQueueScanWorker nor FsRtlUninitializeSmallMcb. benign. return; } if (Win8pIsMonitoringModeEnabled()) { // Now, we have got a decrypted PatchGuard context that is about to be // fetched by ExpWorkerThread. Win8pPatchPgContext(pgContext); } else { // You can replace the routine with a harmless empty function instead of // modifying the PatchGuard context. Item->WorkerRoutine = [](void*){}; } }