shared mapping - lock free interweaving io

This commit is contained in:
lhodges1 2024-01-25 22:09:16 +11:00
parent 67fe04c2e2
commit d743f49bd3
16 changed files with 632 additions and 234 deletions

View file

@ -1,4 +1,131 @@
---
BasedOnStyle: LLVM
Language: Cpp
BasedOnStyle: webkit
AccessModifierOffset: -4
...
AlignAfterOpenBracket: Align
AlignConsecutiveAssignments: true
AlignConsecutiveDeclarations: true
AlignConsecutiveMacros: true
AlignEscapedNewlines: Left
AlignOperands: true
AlignTrailingComments: true
AllowAllArgumentsOnNextLine: true
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortBlocksOnASingleLine: true
AllowShortCaseLabelsOnASingleLine: true
AllowShortFunctionsOnASingleLine: false
AllowShortIfStatementsOnASingleLine: false
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterReturnType: TopLevel
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: true #false
BinPackArguments: false
BinPackParameters: false
ExperimentalAutoDetectBinPacking: false
AllowAllParametersOfDeclarationOnNextLine: true
BreakBeforeBraces: Custom
BraceWrapping:
AfterCaseLabel: true
AfterClass: true
AfterControlStatement: true
AfterEnum: true
AfterFunction: true
AfterNamespace: false
AfterStruct: true
AfterUnion: true
AfterExternBlock: false
BeforeCatch: true
BeforeElse: true
BreakBeforeBinaryOperators: None
BreakBeforeTernaryOperators: true
BreakConstructorInitializers: AfterColon
BreakStringLiterals: false
ColumnLimit: 100
CommentPragmas: '^begin_wpp|^end_wpp|^FUNC |^USESUFFIX |^USESUFFIX '
ConstructorInitializerAllOnOneLineOrOnePerLine: true
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
DerivePointerAlignment: false
ExperimentalAutoDetectBinPacking: false
IndentCaseLabels: false
IndentPPDirectives: AfterHash
IndentWidth: 8
KeepEmptyLinesAtTheStartOfBlocks: false
Language: Cpp
MacroBlockBegin: '^BEGIN_MODULE$|^BEGIN_TEST_CLASS$|^BEGIN_TEST_METHOD$'
MacroBlockEnd: '^END_MODULE$|^END_TEST_CLASS$|^END_TEST_METHOD$'
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None #All
PointerAlignment: Left
ReflowComments: true
SortIncludes: false
SpaceAfterCStyleCast: false
SpaceBeforeAssignmentOperators: true
SpaceBeforeCtorInitializerColon: true
SpaceBeforeCtorInitializerColon: true
SpaceBeforeParens: ControlStatements
SpaceBeforeRangeBasedForLoopColon: true
SpaceInEmptyParentheses: false
SpacesInAngles: false
SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
Standard: Cpp11
StatementMacros: [
'EXTERN_C',
'PAGED',
'PAGEDX',
'NONPAGED',
'PNPCODE',
'INITCODE',
'_At_',
'_When_',
'_Success_',
'_Check_return_',
'_Must_inspect_result_',
'_IRQL_requires_same_',
'_IRQL_requires_',
'_IRQL_requires_max_',
'_IRQL_requires_min_',
'_IRQL_saves_',
'_IRQL_restores_',
'_IRQL_saves_global_',
'_IRQL_restores_global_',
'_IRQL_raises_',
'_IRQL_lowers_',
'_Acquires_lock_',
'_Releases_lock_',
'_Acquires_exclusive_lock_',
'_Releases_exclusive_lock_',
'_Acquires_shared_lock_',
'_Releases_shared_lock_',
'_Requires_lock_held_',
'_Use_decl_annotations_',
'_Guarded_by_',
'__drv_preferredFunction',
'__drv_allocatesMem',
'__drv_freesMem',
]
TabWidth: '8'
UseTab: Never

View file

@ -1,131 +0,0 @@
Language: Cpp
BasedOnStyle: webkit
AccessModifierOffset: -4
AlignAfterOpenBracket: Align
AlignConsecutiveAssignments: true
AlignConsecutiveDeclarations: true
AlignConsecutiveMacros: true
AlignEscapedNewlines: Left
AlignOperands: true
AlignTrailingComments: true
AllowAllArgumentsOnNextLine: true
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortBlocksOnASingleLine: true
AllowShortCaseLabelsOnASingleLine: true
AllowShortFunctionsOnASingleLine: false
AllowShortIfStatementsOnASingleLine: false
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterReturnType: TopLevel
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: true #false
BinPackArguments: false
BinPackParameters: false
ExperimentalAutoDetectBinPacking: false
AllowAllParametersOfDeclarationOnNextLine: true
BreakBeforeBraces: Custom
BraceWrapping:
AfterCaseLabel: true
AfterClass: true
AfterControlStatement: true
AfterEnum: true
AfterFunction: true
AfterNamespace: false
AfterStruct: true
AfterUnion: true
AfterExternBlock: false
BeforeCatch: true
BeforeElse: true
BreakBeforeBinaryOperators: None
BreakBeforeTernaryOperators: true
BreakConstructorInitializers: AfterColon
BreakStringLiterals: false
ColumnLimit: 100
CommentPragmas: '^begin_wpp|^end_wpp|^FUNC |^USESUFFIX |^USESUFFIX '
ConstructorInitializerAllOnOneLineOrOnePerLine: true
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
DerivePointerAlignment: false
ExperimentalAutoDetectBinPacking: false
IndentCaseLabels: false
IndentPPDirectives: AfterHash
IndentWidth: 8
KeepEmptyLinesAtTheStartOfBlocks: false
Language: Cpp
MacroBlockBegin: '^BEGIN_MODULE$|^BEGIN_TEST_CLASS$|^BEGIN_TEST_METHOD$'
MacroBlockEnd: '^END_MODULE$|^END_TEST_CLASS$|^END_TEST_METHOD$'
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None #All
PointerAlignment: Left
ReflowComments: true
SortIncludes: false
SpaceAfterCStyleCast: false
SpaceBeforeAssignmentOperators: true
SpaceBeforeCtorInitializerColon: true
SpaceBeforeCtorInitializerColon: true
SpaceBeforeParens: ControlStatements
SpaceBeforeRangeBasedForLoopColon: true
SpaceInEmptyParentheses: false
SpacesInAngles: false
SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
Standard: Cpp11
StatementMacros: [
'EXTERN_C',
'PAGED',
'PAGEDX',
'NONPAGED',
'PNPCODE',
'INITCODE',
'_At_',
'_When_',
'_Success_',
'_Check_return_',
'_Must_inspect_result_',
'_IRQL_requires_same_',
'_IRQL_requires_',
'_IRQL_requires_max_',
'_IRQL_requires_min_',
'_IRQL_saves_',
'_IRQL_restores_',
'_IRQL_saves_global_',
'_IRQL_restores_global_',
'_IRQL_raises_',
'_IRQL_lowers_',
'_Acquires_lock_',
'_Releases_lock_',
'_Acquires_exclusive_lock_',
'_Releases_exclusive_lock_',
'_Acquires_shared_lock_',
'_Releases_shared_lock_',
'_Requires_lock_held_',
'_Use_decl_annotations_',
'_Guarded_by_',
'__drv_preferredFunction',
'__drv_allocatesMem',
'__drv_freesMem',
]
TabWidth: '8'
UseTab: Never

4
.clang-format-cpp Normal file
View file

@ -0,0 +1,4 @@
---
BasedOnStyle: LLVM
...

View file

@ -91,6 +91,7 @@ DrvLoadInitialiseDriverConfig(_In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_ST
typedef struct _DRIVER_CONFIG
{
volatile LONG nmi_status;
UNICODE_STRING unicode_driver_name;
ANSI_STRING ansi_driver_name;
UNICODE_STRING device_name;
@ -110,6 +111,7 @@ typedef struct _DRIVER_CONFIG
THREAD_LIST_HEAD thread_list;
DRIVER_LIST_HEAD driver_list;
PROCESS_LIST_HEAD process_list;
SHARED_MAPPING mapping;
} DRIVER_CONFIG, *PDRIVER_CONFIG;
@ -125,6 +127,26 @@ PDRIVER_CONFIG g_DriverConfig = NULL;
#define POOL_TAG_CONFIG 'conf'
VOID
UnsetNmiInProgressFlag()
{
InterlockedDecrement(&g_DriverConfig->nmi_status);
}
BOOLEAN
IsNmiInProgress()
{
/* if the initial value is true, we dont own the lock hence return false */
return InterlockedCompareExchange(&g_DriverConfig->nmi_status, TRUE, FALSE) == 0 ? FALSE
: TRUE;
}
PSHARED_MAPPING
GetSharedMappingConfig()
{
return &g_DriverConfig->mapping;
}
VOID
AcquireDriverConfigLock()
{
@ -899,6 +921,7 @@ DrvLoadInitialiseDriverConfig(_In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_ST
return status;
}
/* when this function failed, we bugcheck in freeconfigstrings todo: fix */
status = DrvLoadGatherSystemEnvironmentSettings();
if (!NT_SUCCESS(status))
@ -981,7 +1004,7 @@ DriverEntry(_In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath)
if (!NT_SUCCESS(status))
{
DEBUG_ERROR("ResolveDynamicImports failed with status %x", status);
ImpIoDeleteDevice(DriverObject->DeviceObject);
// ImpIoDeleteDevice(DriverObject->DeviceObject);
return status;
}

View file

@ -88,4 +88,13 @@ IsDriverUnloading();
PPROCESS_CONFIG
GetProcessConfig();
PSHARED_MAPPING
GetSharedMappingConfig();
VOID
UnsetNmiInProgressFlag();
BOOLEAN
IsNmiInProgress();
#endif

View file

@ -243,6 +243,13 @@ StoreModuleExecutableRegionsInBuffer(_Outptr_result_bytebuffer_(*BytesWritten) P
*/
dos_header = (PIMAGE_DOS_HEADER)ModuleBase;
if (!MmIsAddressValid(dos_header))
{
ImpExFreePoolWithTag(*Buffer, POOL_TAG_INTEGRITY);
*Buffer = NULL;
return STATUS_INVALID_ADDRESS;
}
/*
* The IMAGE_DOS_HEADER.e_lfanew stores the offset of the IMAGE_NT_HEADER from the base
* of the image.

View file

@ -62,6 +62,8 @@ DispatchApcOperation(_In_ PAPC_OPERATION_ID Operation);
CTL_CODE(FILE_DEVICE_UNKNOWN, 0x20021, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define IOCTL_QUERY_DEFERRED_REPORTS \
CTL_CODE(FILE_DEVICE_UNKNOWN, 0x20022, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define IOCTL_INITIATE_SHARED_MAPPING \
CTL_CODE(FILE_DEVICE_UNKNOWN, 0x20023, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define APC_OPERATION_STACKWALK 0x1
@ -162,7 +164,7 @@ IrpQueueQueryPendingReports(_In_ PIRP Irp)
VOID
IrpQueueInsert(_In_ PIO_CSQ Csq, _In_ PIRP Irp)
{
PIRP_QUEUE_HEAD queue = GetIrpQueueHead();
PIRP_QUEUE_HEAD queue = GetIrpQueueHead();
InsertTailList(&queue->queue, &Irp->Tail.Overlay.ListEntry);
queue->count++;
}
@ -282,6 +284,263 @@ IrpQueueInitialise()
return status;
}
VOID
SharedMappingWorkRoutine(_In_ PDEVICE_OBJECT DeviceObject, _In_opt_ PVOID Context)
{
NTSTATUS status = STATUS_UNSUCCESSFUL;
HANDLE handle = NULL;
PSHARED_MAPPING state = (PSHARED_MAPPING)Context;
InterlockedIncrement(&state->work_item_status);
DEBUG_VERBOSE("SharedMapping work routine called. OperationId: %lx",
state->kernel_buffer->operation_id);
switch (state->kernel_buffer->operation_id)
{
case ssRunNmiCallbacks:
DEBUG_INFO("SHARED_STATE_OPERATION_ID: RunNmiCallbacks Received.");
status = HandleNmiIOCTL();
if (!NT_SUCCESS(status))
DEBUG_ERROR("RunNmiCallbacks failed with status %lx", status);
break;
case ssValidateDriverObjects:
DEBUG_INFO("SHARED_STATE_OPERATION_ID: ValidateDriverObjects Received.");
status = ImpPsCreateSystemThread(&handle,
PROCESS_ALL_ACCESS,
NULL,
NULL,
NULL,
HandleValidateDriversIOCTL,
NULL);
if (!NT_SUCCESS(status))
{
DEBUG_ERROR("PsCreateSystemThread failed with status %x", status);
goto end;
}
ImpZwClose(handle);
break;
case ssEnumerateHandleTables:
DEBUG_INFO("SHARED_STATE_OPERATION_ID: EnumerateHandleTables Received");
/* can maybe implement this better so we can extract a status value */
EnumerateProcessListWithCallbackRoutine(EnumerateProcessHandles, NULL);
break;
case ssScanForUnlinkedProcesses:
DEBUG_INFO("SHARED_STATE_OPERATION_ID: ScanForUnlinkedProcesses Received");
status = FindUnlinkedProcesses();
if (!NT_SUCCESS(status))
DEBUG_ERROR("FindUnlinkedProcesses failed with status %x", status);
break;
case ssPerformModuleIntegrityCheck:
DEBUG_INFO("SHARED_STATE_OPERATION_ID: PerformIntegrityCheck Received");
status = ValidateOurDriverImage();
if (!NT_SUCCESS(status))
DEBUG_ERROR("VerifyInMemoryImageVsDiskImage failed with status %x", status);
break;
case ssScanForAttachedThreads:
DEBUG_INFO("SHARED_STATE_OPERATION_ID: ScanForAttachedThreads Received");
DetectThreadsAttachedToProtectedProcess();
break;
case ssScanForEptHooks:
DEBUG_INFO("SHARED_STATE_OPERATION_ID: ScanForEptHooks Received");
status = DetectEptHooksInKeyFunctions();
if (!NT_SUCCESS(status))
DEBUG_ERROR("DetectEpthooksInKeyFunctions failed with status %x", status);
break;
case ssInitiateDpcStackwalk:
DEBUG_INFO("SHARED_STATE_OPERATION_ID Received");
status = DispatchStackwalkToEachCpuViaDpc();
if (!NT_SUCCESS(status))
DEBUG_ERROR("DispatchStackwalkToEachCpuViaDpc failed with status %x",
status);
break;
case ssValidateSystemModules:
DEBUG_INFO("SHARED_STATE_OPERATION_ID: ValidateSystemModules Received");
status = SystemModuleVerificationDispatcher();
if (!NT_SUCCESS(status))
DEBUG_ERROR("ValidateSystemModules failed with status %x", status);
break;
default: DEBUG_ERROR("Invalid SHARED_STATE_OPERATION_ID Received");
}
end:
InterlockedDecrement(&state->work_item_status);
}
/* again, we want to run our routine at apc level not dispatch level */
VOID
SharedMappingDpcRoutine(_In_ PKDPC Dpc,
_In_opt_ PVOID DeferredContext,
_In_opt_ PVOID SystemArgument1,
_In_opt_ PVOID SystemArgument2)
{
PSHARED_MAPPING mapping = (PSHARED_MAPPING)DeferredContext;
if (!mapping->active || mapping->work_item_status)
return;
IoQueueWorkItem(mapping->work_item, SharedMappingWorkRoutine, NormalWorkQueue, mapping);
}
#define REPEAT_TIME_15_SEC 30000
VOID
SharedMappingTerminate()
{
PSHARED_MAPPING mapping = GetSharedMappingConfig();
while (mapping->work_item_status)
YieldProcessor();
mapping->active = FALSE;
mapping->user_buffer = NULL;
mapping->size = 0;
KeCancelTimer(&mapping->timer);
IoFreeWorkItem(mapping->work_item);
IoFreeMdl(mapping->mdl);
ExFreePoolWithTag(mapping->kernel_buffer, POOL_TAG_INTEGRITY);
RtlZeroMemory(mapping, sizeof(SHARED_MAPPING));
}
STATIC
NTSTATUS
SharedMappingInitialiseTimer(_In_ PSHARED_MAPPING Mapping)
{
LARGE_INTEGER due_time = {0};
LONG period = 0;
due_time.QuadPart = ABSOLUTE(SECONDS(30));
Mapping->work_item = IoAllocateWorkItem(GetDriverDeviceObject());
if (!Mapping->work_item)
{
DEBUG_ERROR("IoAllocateWorkItem failed with no status.");
return STATUS_INSUFFICIENT_RESOURCES;
}
KeInitializeDpc(&Mapping->timer_dpc, SharedMappingDpcRoutine, Mapping);
KeInitializeTimer(&Mapping->timer);
KeSetTimerEx(&Mapping->timer, due_time, REPEAT_TIME_15_SEC, &Mapping->timer_dpc);
DEBUG_VERBOSE("Initialised shared mapping event timer.");
return STATUS_SUCCESS;
}
STATIC
NTSTATUS
SharedMappingInitialise(_In_ PIRP Irp)
{
NTSTATUS status = STATUS_UNSUCCESSFUL;
PMDL mdl = NULL;
PSHARED_MAPPING mapping = NULL;
PSHARED_MAPPING_INIT mapping_init = NULL;
PEPROCESS process = NULL;
PVOID buffer = NULL;
PVOID user_buffer = NULL;
mapping = GetSharedMappingConfig();
status = ValidateIrpOutputBuffer(Irp, sizeof(SHARED_MAPPING_INIT));
if (!NT_SUCCESS(status))
{
DEBUG_ERROR("ValidateIrpOutputBuffer failed with status %x", status);
return status;
}
/* remember that ExAllocatePool2 zeroes the allocation, so no need to zero */
buffer = ExAllocatePool2(POOL_FLAG_NON_PAGED, PAGE_SIZE, POOL_TAG_INTEGRITY);
if (!buffer)
return STATUS_INSUFFICIENT_RESOURCES;
mdl = IoAllocateMdl(buffer, PAGE_SIZE, FALSE, FALSE, NULL);
if (!mdl)
{
DEBUG_ERROR("IoAllocateMdl failed with no status");
ExFreePoolWithTag(buffer, POOL_TAG_INTEGRITY);
return STATUS_INSUFFICIENT_RESOURCES;
}
MmBuildMdlForNonPagedPool(mdl);
__try
{
user_buffer = MmMapLockedPagesSpecifyCache(
mdl, UserMode, MmCached, NULL, FALSE, NormalPagePriority | MdlMappingNoExecute);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
status = GetExceptionCode();
DEBUG_ERROR("MmMapLockedPagesSpecifyCache failed with status %x", status);
IoFreeMdl(mdl);
ExFreePoolWithTag(buffer, POOL_TAG_INTEGRITY);
return status;
}
mapping->kernel_buffer = (PSHARED_STATE)buffer;
mapping->user_buffer = user_buffer;
mapping->mdl = mdl;
mapping->size = PAGE_SIZE;
mapping->active = TRUE;
mapping->work_item_status = FALSE;
SharedMappingInitialiseTimer(mapping);
mapping_init = (PSHARED_MAPPING_INIT)Irp->AssociatedIrp.SystemBuffer;
mapping_init->buffer = user_buffer;
mapping_init->size = PAGE_SIZE;
return status;
}
STATIC
NTSTATUS
DispatchApcOperation(_In_ PAPC_OPERATION_ID Operation)
@ -640,7 +899,7 @@ DeviceControl(_In_ PDEVICE_OBJECT DeviceObject, _Inout_ PIRP Irp)
DEBUG_INFO("IOCTL_VALIDATE_SYSTEM_MODULES Received");
status = SystemModuleVerificationDispatcher();
if (!NT_SUCCESS(status))
DEBUG_ERROR("ValidateSystemModules failed with status %x", status);
@ -660,7 +919,7 @@ DeviceControl(_In_ PDEVICE_OBJECT DeviceObject, _Inout_ PIRP Irp)
case IOCTL_INSERT_IRP_INTO_QUEUE:;
//DEBUG_INFO("IOCTL_INSERT_IRP_INTO_QUEUE Received");
// DEBUG_INFO("IOCTL_INSERT_IRP_INTO_QUEUE Received");
PIRP_QUEUE_HEAD queue = GetIrpQueueHead();
@ -670,8 +929,6 @@ DeviceControl(_In_ PDEVICE_OBJECT DeviceObject, _Inout_ PIRP Irp)
* into the queue. The reason for this is the cancel-safe queue will automically
* mark the irp as pending, so if we then use that irp to return a deferred report
* and return success here verifier has a lil cry.
*
* TODO: some issue with using an incoming irp meant for the queue and finishing a deferred report.
*/
/* before we queue our IRP, check if we can complete a deferred report */
@ -690,6 +947,15 @@ DeviceControl(_In_ PDEVICE_OBJECT DeviceObject, _Inout_ PIRP Irp)
return STATUS_SUCCESS;
case IOCTL_INITIATE_SHARED_MAPPING:
status = SharedMappingInitialise(Irp);
if (!NT_SUCCESS(status))
DEBUG_ERROR("SharedMappingInitialise failed with status %x", status);
break;
default:
DEBUG_WARNING("Invalid IOCTL passed to driver: %lx",
stack_location->Parameters.DeviceIoControl.IoControlCode);
@ -716,6 +982,7 @@ DeviceClose(_In_ PDEVICE_OBJECT DeviceObject, _Inout_ PIRP Irp)
/* we also lose reports here, so sohuld pass em into the irp before freeing */
ProcCloseClearProcessConfiguration();
UnregisterProcessObCallbacks();
SharedMappingTerminate();
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return Irp->IoStatus.Status;

View file

@ -12,6 +12,48 @@ typedef struct _DRIVER_INITIATION_INFORMATION
} DRIVER_INITIATION_INFORMATION, *PDRIVER_INITIATION_INFORMATION;
typedef struct _SHARED_MAPPING_INIT
{
PVOID buffer;
SIZE_T size;
} SHARED_MAPPING_INIT, *PSHARED_MAPPING_INIT;
typedef enum _SHARED_STATE_OPERATION_ID
{
ssRunNmiCallbacks = 0,
ssValidateDriverObjects,
ssEnumerateHandleTables,
ssScanForUnlinkedProcesses,
ssPerformModuleIntegrityCheck,
ssScanForAttachedThreads,
ssScanForEptHooks,
ssInitiateDpcStackwalk,
ssValidateSystemModules,
} SHARED_STATE_OPERATION_ID;
typedef struct _SHARED_STATE
{
volatile UINT32 status;
volatile UINT16 operation_id;
} SHARED_STATE, *PSHARED_STATE;
typedef struct _SHARED_MAPPING
{
volatile LONG work_item_status;
PVOID user_buffer;
PSHARED_STATE kernel_buffer;
PMDL mdl;
SIZE_T size;
volatile BOOLEAN active;
KTIMER timer;
KDPC timer_dpc;
PIO_WORKITEM work_item;
} SHARED_MAPPING, *PSHARED_MAPPING;
NTSTATUS
DeviceControl(_In_ PDEVICE_OBJECT DeviceObject, _Inout_ PIRP Irp);

View file

@ -135,7 +135,7 @@ ValidateDriverObjects(_In_ PSYSTEM_MODULES SystemModules,
STATIC
NTSTATUS
AnalyseNmiData(_In_ PNMI_CONTEXT NmiContext, _In_ PSYSTEM_MODULES SystemModules, _Inout_ PIRP Irp);
AnalyseNmiData(_In_ PNMI_CONTEXT NmiContext, _In_ PSYSTEM_MODULES SystemModules);
STATIC
NTSTATUS
@ -795,7 +795,7 @@ IsInstructionPointerInsideModule(_In_ UINT64 Rip,
*/
STATIC
NTSTATUS
AnalyseNmiData(_In_ PNMI_CONTEXT NmiContext, _In_ PSYSTEM_MODULES SystemModules, _Inout_ PIRP Irp)
AnalyseNmiData(_In_ PNMI_CONTEXT NmiContext, _In_ PSYSTEM_MODULES SystemModules)
{
PAGED_CODE();
@ -810,27 +810,25 @@ AnalyseNmiData(_In_ PNMI_CONTEXT NmiContext, _In_ PSYSTEM_MODULES SystemModules,
/* Make sure our NMIs were run */
if (!NmiContext[core].callback_count)
{
NTSTATUS status =
ValidateIrpOutputBuffer(Irp, sizeof(NMI_CALLBACK_FAILURE));
PNMI_CALLBACK_FAILURE report = ImpExAllocatePool2(
POOL_FLAG_NON_PAGED, sizeof(NMI_CALLBACK_FAILURE), REPORT_POOL_TAG);
if (!report)
return STATUS_INSUFFICIENT_RESOURCES;
report->report_code = REPORT_NMI_CALLBACK_FAILURE;
report->kthread_address = NULL;
report->invalid_rip = NULL;
report->were_nmis_disabled = TRUE;
status = IrpQueueCompleteIrp(report, sizeof(NMI_CALLBACK_FAILURE));
if (!NT_SUCCESS(status))
{
DEBUG_ERROR("ValidateIrpOutputBuffer failed with status %x",
status);
continue;
DEBUG_ERROR("IrpQueueCompleteIrp failed with status %x", status);
return status;
}
NMI_CALLBACK_FAILURE report = {0};
report.report_code = REPORT_NMI_CALLBACK_FAILURE;
report.kthread_address = NULL;
report.invalid_rip = NULL;
report.were_nmis_disabled = TRUE;
Irp->IoStatus.Information = sizeof(NMI_CALLBACK_FAILURE);
RtlCopyMemory(
Irp->AssociatedIrp.SystemBuffer, &report, sizeof(NMI_CALLBACK_FAILURE));
return STATUS_SUCCESS;
}
@ -905,36 +903,24 @@ AnalyseNmiData(_In_ PNMI_CONTEXT NmiContext, _In_ PSYSTEM_MODULES SystemModules,
if (!flag)
{
status = ValidateIrpOutputBuffer(Irp, sizeof(NMI_CALLBACK_FAILURE));
PNMI_CALLBACK_FAILURE report =
ImpExAllocatePool2(POOL_FLAG_NON_PAGED,
sizeof(HIDDEN_SYSTEM_THREAD_REPORT),
REPORT_POOL_TAG);
report->report_code = REPORT_NMI_CALLBACK_FAILURE;
report->kthread_address = NmiContext[core].kthread;
report->invalid_rip = NmiContext[core].interrupted_rip;
report->were_nmis_disabled = FALSE;
status = IrpQueueCompleteIrp(report, sizeof(HIDDEN_SYSTEM_THREAD_REPORT));
if (!NT_SUCCESS(status))
{
DEBUG_ERROR("ValidateIrpOutputBuffer failed with status %x",
status);
DEBUG_ERROR("IrpQueueCompleteIrp failed with status %x", status);
return status;
}
/*
* Note: for now, we only handle 1 report at a time so we stop the
* analysis once we receive a report since we only send a buffer
* large enough for 1 report. In the future this should be changed
* to a buffer that can hold atleast 4 reports (since the chance we
* get 4 reports with a single NMI would be impossible) so we can
* continue parsing the rest of the stack frames after receiving a
* single report.
*/
NMI_CALLBACK_FAILURE report = {0};
report.report_code = REPORT_NMI_CALLBACK_FAILURE;
report.kthread_address = NmiContext[core].kthread;
report.invalid_rip = NmiContext[core].interrupted_rip;
report.were_nmis_disabled = FALSE;
Irp->IoStatus.Information = sizeof(NMI_CALLBACK_FAILURE);
RtlCopyMemory(
Irp->AssociatedIrp.SystemBuffer, &report, sizeof(NMI_CALLBACK_FAILURE));
return STATUS_SUCCESS;
}
}
@ -1024,7 +1010,7 @@ LaunchNonMaskableInterrupt()
}
NTSTATUS
HandleNmiIOCTL(_Inout_ PIRP Irp)
HandleNmiIOCTL()
{
PAGED_CODE();
@ -1033,6 +1019,9 @@ HandleNmiIOCTL(_Inout_ PIRP Irp)
SYSTEM_MODULES system_modules = {0};
PNMI_CONTEXT nmi_context = NULL;
if (IsNmiInProgress())
return STATUS_ALREADY_COMMITTED;
status = ValidateHalDispatchTables();
/* do we continue ? probably. */
@ -1084,7 +1073,7 @@ HandleNmiIOCTL(_Inout_ PIRP Irp)
return status;
}
status = AnalyseNmiData(nmi_context, &system_modules, Irp);
status = AnalyseNmiData(nmi_context, &system_modules);
if (!NT_SUCCESS(status))
DEBUG_ERROR("Error analysing nmi data");
@ -1093,6 +1082,7 @@ HandleNmiIOCTL(_Inout_ PIRP Irp)
ImpExFreePoolWithTag(nmi_context, NMI_CONTEXT_POOL);
ImpKeDeregisterNmiCallback(callback_handle);
UnsetNmiInProgressFlag();
return status;
}

View file

@ -49,7 +49,7 @@ PRTL_MODULE_EXTENDED_INFO
FindSystemModuleByName(_In_ LPCSTR ModuleName, _In_ PSYSTEM_MODULES SystemModules);
NTSTATUS
HandleNmiIOCTL(_Inout_ PIRP Irp);
HandleNmiIOCTL();
BOOLEAN
FreeApcContextStructure(_Inout_ PAPC_CONTEXT_HEADER Context);

View file

@ -10,19 +10,23 @@ dispatcher::dispatcher::dispatcher(LPCWSTR driver_name,
: thread_pool(DISPATCHER_THREAD_COUNT),
k_interface(driver_name, message_queue) {}
void dispatcher::dispatcher::timer_test_callback() {
LOG_INFO("Timer callback invoked from dispatcher class!!");
void dispatcher::dispatcher::write_shared_mapping_operation() {
int operation =
helper::generate_rand_int(kernel_interface::SHARED_STATE_OPERATION_COUNT);
LOG_INFO("Shared mapping operation callback received. operation: %lx",
operation);
this->k_interface.write_shared_mapping_operation(
*reinterpret_cast<kernel_interface::shared_state_operation_id *>(
&operation));
}
void dispatcher::dispatcher::init_timer_callbacks() {
/* we want to offset when our driver routines are called */
this->k_interface.initiate_shared_mapping();
std::optional<HANDLE> result = this->timers.insert_callback(
std::bind(&dispatcher::dispatcher::timer_test_callback, this), 5, 5);
this->timers.insert_callback(
std::bind(&dispatcher::dispatcher::timer_test_callback, this), 7, 7);
this->timers.insert_callback(
std::bind(&dispatcher::dispatcher::timer_test_callback, this), 10, 10);
this->timers.remove_callback(result.value());
std::bind(&dispatcher::dispatcher::write_shared_mapping_operation, this),
WRITE_SHARED_MAPPING_DUE_TIME, WRITE_SHARED_MAPPING_PERIOD);
helper::sleep_thread(TIMER_CALLBACK_DELAY);
}
void dispatcher::dispatcher::run_timer_thread() {
@ -38,10 +42,10 @@ void dispatcher::dispatcher::run() {
this->init_timer_callbacks();
this->run_timer_thread();
this->run_io_port_thread();
//thread_pool.queue_job([this]() { k_interface.run_completion_port(); });
thread_pool.queue_job([this]() { k_interface.run_completion_port(); });
while (true) {
//this->issue_kernel_job();
//helper::sleep_thread(DISPATCH_LOOP_SLEEP_TIME);
this->issue_kernel_job();
helper::sleep_thread(DISPATCH_LOOP_SLEEP_TIME);
}
}

View file

@ -7,9 +7,12 @@
namespace dispatcher {
static const int DISPATCH_LOOP_SLEEP_TIME = 10;
static const int KERNEL_DISPATCH_FUNCTION_COUNT = 11;
static const int DISPATCHER_THREAD_COUNT = 4;
constexpr int DISPATCH_LOOP_SLEEP_TIME = 30;
constexpr int KERNEL_DISPATCH_FUNCTION_COUNT = 11;
constexpr int DISPATCHER_THREAD_COUNT = 4;
constexpr int TIMER_CALLBACK_DELAY = 15;
constexpr int WRITE_SHARED_MAPPING_PERIOD = 30;
constexpr int WRITE_SHARED_MAPPING_DUE_TIME = 30;
class dispatcher {
timer timers;
@ -17,7 +20,7 @@ class dispatcher {
kernel_interface::kernel_interface k_interface;
void issue_kernel_job();
void timer_test_callback();
void write_shared_mapping_operation();
void init_timer_callbacks();
void run_timer_thread();
void run_io_port_thread();

View file

@ -85,8 +85,8 @@ void dispatcher::timer::close_handle_entry(HANDLE handle) {
if (this->handles[entry] == handle) {
CloseHandle(handle);
this->handles[entry] = INVALID_HANDLE_VALUE;
/* ordering doesnt matter, aslong as the valid handles are at the front of
* the array and are contiguous */
/* ordering doesnt matter, as long as the valid handles are at the front
* of the array and are contiguous */
std::sort(this->handles.begin(), this->handles.end());
this->active_callbacks--;
return;
@ -110,10 +110,14 @@ void dispatcher::timer::query_removal_queue() {
HANDLE entry = callbacks_to_remove.front();
this->close_handle_entry(entry);
this->callbacks_to_remove.pop();
this->active_callbacks--;
}
}
/* todo: maybe have an event object that we can wait on whilst no events are queued, then when we do insert an event alert the event object ? though this isnt urgent for our use case...*/
void dispatcher::timer::run_timer_thread() {
if (this->active_callbacks == 0)
return;
while (true) {
unsigned long index = WaitForMultipleObjects(
this->active_callbacks, reinterpret_cast<HANDLE *>(&handles), false,
@ -122,6 +126,9 @@ void dispatcher::timer::run_timer_thread() {
std::lock_guard<std::mutex> lock(this->lock);
this->dispatch_callback_for_index(index);
this->query_removal_queue();
/* maybe we should have some default event ? */
if (this->active_callbacks == 0)
return;
}
}
}

View file

@ -6,9 +6,9 @@
#include <functional>
#include <mutex>
#include <optional>
#include <vector>
#include <unordered_map>
#include <queue>
#include <unordered_map>
#include <vector>
/*
* array of handles which we pass to WaitForMultipleEvents
@ -17,16 +17,11 @@
* cos u cant just take a pointer to a member function for some reason lol like
* tf
*
* this returns an index into the array
*
* setup a vector containing the job routine for the associated handle, then
* call that job.
*
* If we activate another handle in the handles array, the next time the current
* event is signalled and we are invoked, only then should we insert new
* requests. This means we should implement some queue where new timer objects
* are inserted into the queue, then when the current even is invoked, we can
* recall WaitForMultipleObjects with the updated array count.
* map maps a handle to a callback object, this object contains various
* information bust most important the callback routine. When the event is
* signaled, it returns a handle, use that handle to index the map and run the
* callback routine. This has to be done as the handles needs to be in a
* contiguous array, so we can use an array of callback objects.
*/
namespace dispatcher {
@ -65,8 +60,9 @@ public:
timer();
~timer();
std::optional<HANDLE> insert_callback(std::function<void()> routine, int due_time_seconds,
int period_seconds);
std::optional<HANDLE> insert_callback(std::function<void()> routine,
int due_time_seconds,
int period_seconds);
void remove_callback(HANDLE handle);
void run_timer_thread();
};

View file

@ -273,18 +273,38 @@ void kernel_interface::kernel_interface::send_pending_irp() {
LOG_ERROR("failed to insert irp into irp queue %x", status);
}
//void kernel_interface::kernel_interface::query_deferred_reports() {
// unsigned long bytes_returned = 0;
// void *buffer = malloc(MAXIMUM_REPORT_BUFFER_SIZE);
// if (!buffer)
// return;
// for (int i = 0; i < QUERY_DEFERRED_REPORT_COUNT; i++) {
// unsigned int status =
// generic_driver_call_output(ioctl_code::QueryDeferredReports, buffer,
// MAXIMUM_REPORT_BUFFER_SIZE, &bytes_returned);
// if (status && bytes_returned > 0)
// helper::print_kernel_report(buffer);
// memset(buffer, 0, MAXIMUM_REPORT_BUFFER_SIZE);
// }
// free(buffer);
//}
// void kernel_interface::kernel_interface::query_deferred_reports() {
// unsigned long bytes_returned = 0;
// void *buffer = malloc(MAXIMUM_REPORT_BUFFER_SIZE);
// if (!buffer)
// return;
// for (int i = 0; i < QUERY_DEFERRED_REPORT_COUNT; i++) {
// unsigned int status =
// generic_driver_call_output(ioctl_code::QueryDeferredReports, buffer,
// MAXIMUM_REPORT_BUFFER_SIZE,
// &bytes_returned);
// if (status && bytes_returned > 0)
// helper::print_kernel_report(buffer);
// memset(buffer, 0, MAXIMUM_REPORT_BUFFER_SIZE);
// }
// free(buffer);
// }
void kernel_interface::kernel_interface::write_shared_mapping_operation(
shared_state_operation_id operation_id) {
InterlockedExchange16(
reinterpret_cast<SHORT *>(&this->mapping.buffer->operation_id),
operation_id);
}
void kernel_interface::kernel_interface::initiate_shared_mapping() {
LOG_INFO("Initialising shared memory buffer!");
unsigned long bytes_returned = 0;
unsigned long result = this->generic_driver_call_output(
ioctl_code::InitiateSharedMapping, &this->mapping,
sizeof(kernel_interface::shared_mapping), &bytes_returned);
if (!result) {
LOG_ERROR("DeviceIoControl failed with status %x", GetLastError());
return;
}
}

View file

@ -136,8 +136,25 @@ enum ioctl_code
InitiateDpcStackwalk = CTL_CODE(FILE_DEVICE_UNKNOWN, 0x20019, METHOD_BUFFERED, FILE_ANY_ACCESS),
ValidateSystemModules = CTL_CODE(FILE_DEVICE_UNKNOWN, 0x20020, METHOD_BUFFERED, FILE_ANY_ACCESS),
InsertIrpIntoIrpQueue = CTL_CODE(FILE_DEVICE_UNKNOWN, 0x20021, METHOD_BUFFERED, FILE_ANY_ACCESS),
QueryDeferredReports = CTL_CODE(FILE_DEVICE_UNKNOWN, 0x20022, METHOD_BUFFERED, FILE_ANY_ACCESS)
QueryDeferredReports = CTL_CODE(FILE_DEVICE_UNKNOWN, 0x20022, METHOD_BUFFERED, FILE_ANY_ACCESS),
InitiateSharedMapping = CTL_CODE(FILE_DEVICE_UNKNOWN, 0x20023, METHOD_BUFFERED, FILE_ANY_ACCESS)
};
constexpr int SHARED_STATE_OPERATION_COUNT = 9;
enum shared_state_operation_id
{
ssRunNmiCallbacks = 0,
ssValidateDriverObjects,
ssEnumerateHandleTables,
ssScanForUnlinkedProcesses,
ssPerformModuleIntegrityCheck,
ssScanForAttachedThreads,
ssScanForEptHooks,
ssInitiateDpcStackwalk,
ssValidateSystemModules,
};
// clang-format on
struct event_dispatcher {
@ -181,6 +198,18 @@ class kernel_interface {
std::mutex lock;
std::vector<event_dispatcher> events;
struct shared_data {
unsigned __int32 status;
unsigned __int16 operation_id;
};
struct shared_mapping {
shared_data *buffer;
size_t size;
};
shared_mapping mapping;
void initiate_completion_port();
void terminate_completion_port();
event_dispatcher *get_free_event_entry();
@ -216,6 +245,7 @@ public:
void verify_process_module_executable_regions();
void initiate_apc_stackwalk();
void send_pending_irp();
void query_deferred_reports();
void write_shared_mapping_operation(shared_state_operation_id operation_id);
void initiate_shared_mapping();
};
} // namespace kernel_interface