mirror-ac/driver/queue.c

417 lines
13 KiB
C
Raw Normal View History

2023-08-20 16:12:04 +02:00
#include "queue.h"
2023-09-02 10:54:04 +02:00
#include "callbacks.h"
#include "driver.h"
#include "queue.h"
#include "pool.h"
#include "thread.h"
2023-11-09 08:30:59 +01:00
#include "ioctl.h"
2023-08-30 18:29:44 +02:00
#include "common.h"
#include "imports.h"
2023-08-30 18:29:44 +02:00
2023-09-02 10:54:04 +02:00
/*
2023-12-13 05:06:27 +01:00
* This mutex is to prevent a new item being pushed to the queue
* while the HandlePeriodicCallbackReportQueue is iterating through
* the objects. This can be an issue because the spinlock is released
* after each report is placed in the IRP buffer which means a new report
* can be pushed into the queue before the next iteration can take ownership
* of the spinlock.
*/
2023-09-02 10:54:04 +02:00
typedef struct _REPORT_QUEUE_CONFIGURATION
{
2023-12-13 05:06:27 +01:00
QUEUE_HEAD head;
volatile BOOLEAN is_driver_unloading;
KGUARDED_MUTEX lock;
2023-09-02 10:54:04 +02:00
2023-12-13 05:06:27 +01:00
} REPORT_QUEUE_CONFIGURATION, *PREPORT_QUEUE_CONFIGURATION;
2023-09-02 10:54:04 +02:00
2023-12-13 05:06:27 +01:00
REPORT_QUEUE_CONFIGURATION report_queue_config = {0};
2023-09-02 10:54:04 +02:00
2023-10-05 08:27:17 +02:00
VOID
2023-12-13 05:06:27 +01:00
InitialiseGlobalReportQueue(_Out_ PBOOLEAN Status)
2023-09-02 10:54:04 +02:00
{
2023-12-13 05:06:27 +01:00
report_queue_config.head.start = NULL;
report_queue_config.head.end = NULL;
report_queue_config.head.entries = 0;
report_queue_config.is_driver_unloading = FALSE;
2023-09-02 10:54:04 +02:00
ImpKeInitializeGuardedMutex(&report_queue_config.head.lock);
ImpKeInitializeGuardedMutex(&report_queue_config.lock);
2023-09-02 10:54:04 +02:00
2023-12-13 05:06:27 +01:00
*Status = TRUE;
2023-09-02 10:54:04 +02:00
}
2023-12-13 05:06:27 +01:00
// PQUEUE_HEAD QueueCreate()
2023-08-21 11:45:00 +02:00
//{
2023-12-13 05:06:27 +01:00
// PQUEUE_HEAD head = ExAllocatePool2( POOL_FLAG_NON_PAGED, sizeof( QUEUE_HEAD ),
2023-12-23 19:52:55 +01:00
// QUEUE_POOL_TAG );
2023-08-21 11:45:00 +02:00
//
// if ( !head )
// return NULL;
//
// head->end = NULL;
// head->start = NULL;
// head->entries = 0;
//
// KeInitializeSpinLock( &head->lock );
//
// return head;
2023-12-13 05:06:27 +01:00
// }
2023-08-20 16:12:04 +02:00
2023-10-05 08:27:17 +02:00
VOID
2023-12-13 05:06:27 +01:00
QueuePush(_Inout_ PQUEUE_HEAD Head, _In_ PVOID Data)
2023-08-20 16:12:04 +02:00
{
ImpKeAcquireGuardedMutex(&Head->lock);
2023-08-20 16:12:04 +02:00
2023-12-13 05:06:27 +01:00
PQUEUE_NODE temp = ExAllocatePool2(POOL_FLAG_NON_PAGED, sizeof(QUEUE_NODE), QUEUE_POOL_TAG);
2023-08-20 16:12:04 +02:00
2023-12-13 05:06:27 +01:00
if (!temp)
goto end;
2023-08-20 16:12:04 +02:00
2023-12-13 05:06:27 +01:00
Head->entries += 1;
2023-08-20 17:04:53 +02:00
2023-12-13 05:06:27 +01:00
temp->data = Data;
2023-08-20 16:12:04 +02:00
2023-12-13 05:06:27 +01:00
if (Head->end != NULL)
Head->end->next = temp;
2023-08-20 16:12:04 +02:00
2023-12-13 05:06:27 +01:00
Head->end = temp;
2023-08-20 16:12:04 +02:00
2023-12-13 05:06:27 +01:00
if (Head->start == NULL)
Head->start = temp;
2023-08-20 16:12:04 +02:00
end:
ImpKeReleaseGuardedMutex(&Head->lock);
2023-08-20 16:12:04 +02:00
}
2023-10-05 08:27:17 +02:00
PVOID
2023-12-13 05:06:27 +01:00
QueuePop(_Inout_ PQUEUE_HEAD Head)
2023-08-20 16:12:04 +02:00
{
ImpKeAcquireGuardedMutex(&Head->lock);
2023-08-20 16:12:04 +02:00
2023-12-13 05:06:27 +01:00
PVOID data = NULL;
PQUEUE_NODE temp = Head->start;
2023-08-20 16:12:04 +02:00
2023-12-13 05:06:27 +01:00
if (temp == NULL)
goto end;
2023-08-20 16:12:04 +02:00
2023-12-13 05:06:27 +01:00
Head->entries = Head->entries - 1;
2023-08-20 17:04:53 +02:00
2023-12-13 05:06:27 +01:00
data = temp->data;
Head->start = temp->next;
2023-08-20 16:12:04 +02:00
2023-12-13 05:06:27 +01:00
if (Head->end == temp)
Head->end = NULL;
2023-08-20 16:12:04 +02:00
ImpExFreePoolWithTag(temp, QUEUE_POOL_TAG);
2023-08-20 16:12:04 +02:00
end:
ImpKeReleaseGuardedMutex(&Head->lock);
2023-12-13 05:06:27 +01:00
return data;
2023-08-20 16:12:04 +02:00
}
2023-09-02 10:54:04 +02:00
2023-10-05 08:27:17 +02:00
VOID
2023-12-13 05:06:27 +01:00
InsertReportToQueue(_In_ PVOID Report)
2023-09-02 10:54:04 +02:00
{
2023-12-13 05:06:27 +01:00
if (InterlockedExchange(&report_queue_config.is_driver_unloading,
report_queue_config.is_driver_unloading))
return;
2023-10-11 18:05:29 +02:00
ImpKeAcquireGuardedMutex(&report_queue_config.lock);
2023-12-13 05:06:27 +01:00
QueuePush(&report_queue_config.head, Report);
ImpKeReleaseGuardedMutex(&report_queue_config.lock);
2023-09-02 10:54:04 +02:00
}
2023-10-05 08:27:17 +02:00
VOID
2023-09-27 06:22:14 +02:00
FreeGlobalReportQueueObjects()
2023-09-02 10:54:04 +02:00
{
2023-12-13 05:06:27 +01:00
InterlockedExchange(&report_queue_config.is_driver_unloading, TRUE);
ImpKeAcquireGuardedMutex(&report_queue_config.lock);
2023-09-02 10:54:04 +02:00
2023-12-13 05:06:27 +01:00
PVOID report = QueuePop(&report_queue_config.head);
2023-09-02 10:54:04 +02:00
2023-12-13 05:06:27 +01:00
while (report != NULL)
{
ImpExFreePoolWithTag(report, REPORT_POOL_TAG);
2023-12-13 05:06:27 +01:00
report = QueuePop(&report_queue_config.head);
2023-12-23 19:52:55 +01:00
DEBUG_VERBOSE("Unloading report queue. Entries remaining: %i",
report_queue_config.head.entries);
2023-12-13 05:06:27 +01:00
}
2023-09-02 10:54:04 +02:00
end:
ImpKeReleaseGuardedMutex(&report_queue_config.lock);
2023-09-02 10:54:04 +02:00
}
/*
2023-12-13 05:06:27 +01:00
* This function handles sending all the pending reports in the global report
* queue to the usermode application. This function is called periodically by the
* usermode application. The reason I have implemented this is because as this application
* expanded, it became apparent that some of the driver functions will generate multiple
* reports as a result of a single usermode request and hence it makes dealing with
* reports generated from ObRegisterCallbacks for example much easier.
*/
2023-10-05 08:27:17 +02:00
NTSTATUS
2023-12-13 05:06:27 +01:00
HandlePeriodicGlobalReportQueueQuery(_Inout_ PIRP Irp)
2023-09-02 10:54:04 +02:00
{
2023-12-13 05:06:27 +01:00
INT count = 0;
2023-12-23 19:52:55 +01:00
NTSTATUS status = STATUS_UNSUCCESSFUL;
2023-12-13 05:06:27 +01:00
PVOID report = NULL;
SIZE_T total_size = 0;
PVOID report_buffer = NULL;
ULONG report_buffer_size = 0;
PREPORT_HEADER report_header = NULL;
GLOBAL_REPORT_QUEUE_HEADER header = {0};
2023-09-02 10:54:04 +02:00
ImpKeAcquireGuardedMutex(&report_queue_config.lock);
2023-10-11 18:05:29 +02:00
2023-12-13 05:06:27 +01:00
report_buffer_size = sizeof(INVALID_PROCESS_ALLOCATION_REPORT) * MAX_REPORTS_PER_IRP +
sizeof(GLOBAL_REPORT_QUEUE_HEADER);
2023-09-02 10:54:04 +02:00
2023-12-13 05:06:27 +01:00
status = ValidateIrpOutputBuffer(Irp, report_buffer_size);
2023-11-09 08:30:59 +01:00
2023-12-13 05:06:27 +01:00
if (!NT_SUCCESS(status))
{
2023-12-23 19:52:55 +01:00
DEBUG_ERROR("ValidateIrpOutputBuffer failed with status %x", status);
ImpKeReleaseGuardedMutex(&report_queue_config.lock);
2023-12-13 05:06:27 +01:00
return status;
}
2023-11-09 08:30:59 +01:00
report_buffer = ImpExAllocatePool2(
POOL_FLAG_NON_PAGED, report_buffer_size, REPORT_QUEUE_TEMP_BUFFER_TAG);
2023-09-02 10:54:04 +02:00
2023-12-13 05:06:27 +01:00
if (!report_buffer)
{
ImpKeReleaseGuardedMutex(&report_queue_config.lock);
2023-12-13 05:06:27 +01:00
return STATUS_MEMORY_NOT_ALLOCATED;
}
2023-09-02 10:54:04 +02:00
2023-12-13 05:06:27 +01:00
report = QueuePop(&report_queue_config.head);
2023-11-09 08:30:59 +01:00
2023-12-13 05:06:27 +01:00
if (report == NULL)
{
2023-12-23 19:52:55 +01:00
DEBUG_VERBOSE("Callback report queue is empty. No reports to be sent to usermode.");
2023-12-13 05:06:27 +01:00
goto end;
}
2023-09-02 10:54:04 +02:00
2023-12-13 05:06:27 +01:00
while (report != NULL)
{
if (count >= MAX_REPORTS_PER_IRP)
goto end;
2023-09-02 10:54:04 +02:00
2023-12-13 05:06:27 +01:00
report_header = (PREPORT_HEADER)report;
2023-09-02 10:54:04 +02:00
2023-12-13 05:06:27 +01:00
switch (report_header->report_id)
{
case REPORT_ILLEGAL_HANDLE_OPERATION:
2023-09-02 10:54:04 +02:00
2023-12-13 05:06:27 +01:00
RtlCopyMemory((UINT64)report_buffer + sizeof(GLOBAL_REPORT_QUEUE_HEADER) +
total_size,
report,
sizeof(OPEN_HANDLE_FAILURE_REPORT));
2023-09-02 10:54:04 +02:00
2023-12-13 05:06:27 +01:00
total_size += sizeof(OPEN_HANDLE_FAILURE_REPORT);
break;
2023-09-02 10:54:04 +02:00
2023-12-13 05:06:27 +01:00
case REPORT_ILLEGAL_ATTACH_PROCESS:
2023-09-02 10:54:04 +02:00
2023-12-13 05:06:27 +01:00
RtlCopyMemory((UINT64)report_buffer + sizeof(GLOBAL_REPORT_QUEUE_HEADER) +
total_size,
report,
sizeof(ATTACH_PROCESS_REPORT));
2023-09-02 10:54:04 +02:00
2023-12-13 05:06:27 +01:00
total_size += sizeof(ATTACH_PROCESS_REPORT);
break;
2023-09-18 05:15:26 +02:00
2023-12-13 05:06:27 +01:00
case REPORT_INVALID_PROCESS_ALLOCATION:
2023-09-18 05:15:26 +02:00
2023-12-13 05:06:27 +01:00
RtlCopyMemory((UINT64)report_buffer + sizeof(GLOBAL_REPORT_QUEUE_HEADER) +
total_size,
report,
sizeof(INVALID_PROCESS_ALLOCATION_REPORT));
2023-09-18 05:15:26 +02:00
2023-12-13 05:06:27 +01:00
total_size += sizeof(INVALID_PROCESS_ALLOCATION_REPORT);
break;
2023-09-28 15:56:07 +02:00
2023-12-13 05:06:27 +01:00
case REPORT_APC_STACKWALK:
2023-09-28 15:56:07 +02:00
2023-12-13 05:06:27 +01:00
RtlCopyMemory((UINT64)report_buffer + sizeof(GLOBAL_REPORT_QUEUE_HEADER) +
total_size,
report,
sizeof(APC_STACKWALK_REPORT));
2023-09-28 15:56:07 +02:00
2023-12-13 05:06:27 +01:00
total_size += sizeof(APC_STACKWALK_REPORT);
break;
2023-09-28 15:56:07 +02:00
2023-12-13 05:06:27 +01:00
case REPORT_HIDDEN_SYSTEM_THREAD:
2023-10-06 07:47:01 +02:00
2023-12-13 05:06:27 +01:00
RtlCopyMemory((UINT64)report_buffer + sizeof(GLOBAL_REPORT_QUEUE_HEADER) +
total_size,
report,
sizeof(HIDDEN_SYSTEM_THREAD_REPORT));
2023-10-06 07:47:01 +02:00
2023-12-13 05:06:27 +01:00
total_size += sizeof(HIDDEN_SYSTEM_THREAD_REPORT);
break;
2023-12-29 17:20:32 +01:00
case REPORT_DPC_STACKWALK:
RtlCopyMemory((UINT64)report_buffer + sizeof(GLOBAL_REPORT_QUEUE_HEADER) +
total_size,
report,
sizeof(DPC_STACKWALK_REPORT));
total_size += sizeof(DPC_STACKWALK_REPORT);
break;
2024-01-01 17:45:40 +01:00
case REPORT_DATA_TABLE_ROUTINE:
RtlCopyMemory((UINT64)report_buffer + sizeof(GLOBAL_REPORT_QUEUE_HEADER) +
total_size,
report,
sizeof(DATA_TABLE_ROUTINE_REPORT));
total_size += sizeof(DATA_TABLE_ROUTINE_REPORT);
break;
2023-12-13 05:06:27 +01:00
}
2023-10-06 07:47:01 +02:00
2023-12-13 05:06:27 +01:00
/* QueuePop frees the node, but we still need to free the returned data */
ImpExFreePoolWithTag(report, REPORT_POOL_TAG);
2023-09-02 10:54:04 +02:00
2023-12-13 05:06:27 +01:00
report = QueuePop(&report_queue_config.head);
count += 1;
}
2023-09-02 10:54:04 +02:00
end:
ImpKeReleaseGuardedMutex(&report_queue_config.lock);
2023-09-03 19:33:27 +02:00
2023-12-13 05:06:27 +01:00
Irp->IoStatus.Information = sizeof(GLOBAL_REPORT_QUEUE_HEADER) + total_size;
2024-01-11 10:16:55 +01:00
header.count = count;
2023-09-02 10:54:04 +02:00
2023-12-13 05:06:27 +01:00
RtlCopyMemory(report_buffer, &header, sizeof(GLOBAL_REPORT_QUEUE_HEADER));
RtlCopyMemory(Irp->AssociatedIrp.SystemBuffer,
report_buffer,
sizeof(GLOBAL_REPORT_QUEUE_HEADER) + total_size);
2023-09-02 10:54:04 +02:00
2023-12-13 05:06:27 +01:00
if (report_buffer)
ImpExFreePoolWithTag(report_buffer, REPORT_QUEUE_TEMP_BUFFER_TAG);
2023-09-02 10:54:04 +02:00
2023-12-23 19:52:55 +01:00
DEBUG_VERBOSE("All reports moved into the IRP, sending to usermode.");
2023-12-13 05:06:27 +01:00
return STATUS_SUCCESS;
2023-09-02 10:54:04 +02:00
}
2023-09-25 17:41:38 +02:00
2023-10-12 13:27:40 +02:00
/*
2023-12-13 05:06:27 +01:00
* Simple thread safe linked list implementation. All structures should begin
* with a SINGLE_LIST_ENTRY structure provided by the windows API. for example:
*
* typedef struct _LIST_ENTRY_STRUCTURE
* {
* SINGLE_LIST_ENTRY list;
* PVOID address;
* UINT32 data;
* ...
* };
*
* This common structure layout allows us to pass in a callback routine when freeing
* allowing immense flexibility to ensure we can free and/or deference any objects
* that are referenced in said object.
*
* I've opted to use a mutex rather then a spinlock since there are many times we
* enumerate the list for extended periods aswell as queue up many insertions at
* once.
*/
2023-10-05 08:27:17 +02:00
VOID
2023-12-13 05:06:27 +01:00
ListInit(_Inout_ PSINGLE_LIST_ENTRY Head, _Inout_ PKGUARDED_MUTEX Lock)
2023-09-25 17:41:38 +02:00
{
ImpKeInitializeGuardedMutex(Lock);
2023-12-13 05:06:27 +01:00
Head->Next = NULL;
2023-09-25 17:41:38 +02:00
}
2023-10-08 16:07:49 +02:00
VOID
2023-12-13 05:06:27 +01:00
ListInsert(_Inout_ PSINGLE_LIST_ENTRY Head,
_Inout_ PSINGLE_LIST_ENTRY NewEntry,
_In_ PKGUARDED_MUTEX Lock)
2023-09-25 17:41:38 +02:00
{
ImpKeAcquireGuardedMutex(Lock);
2023-09-25 17:41:38 +02:00
2023-12-13 05:06:27 +01:00
PSINGLE_LIST_ENTRY old_entry = Head->Next;
2023-09-25 17:41:38 +02:00
2023-12-13 05:06:27 +01:00
Head->Next = NewEntry;
NewEntry->Next = old_entry;
2023-09-25 17:41:38 +02:00
ImpKeReleaseGuardedMutex(Lock);
2023-09-25 17:41:38 +02:00
}
2023-10-12 13:27:40 +02:00
/*
2023-12-13 05:06:27 +01:00
* Assuming the SINGLE_LIST_ENTRY is the first item in the structure, we
* can pass a callback routine to be called before the free occurs. This
* allows us to dereference/free structure specific items whilst still allowing
* the list to remain flexible.
*/
2023-10-08 16:07:49 +02:00
BOOLEAN
2023-12-13 05:06:27 +01:00
ListFreeFirstEntry(_Inout_ PSINGLE_LIST_ENTRY Head,
_In_ PKGUARDED_MUTEX Lock,
_In_opt_ PVOID CallbackRoutine)
2023-09-25 17:41:38 +02:00
{
2023-12-13 05:06:27 +01:00
BOOLEAN result = FALSE;
ImpKeAcquireGuardedMutex(Lock);
2023-09-25 17:41:38 +02:00
2023-12-13 05:06:27 +01:00
if (Head->Next)
{
PSINGLE_LIST_ENTRY entry = Head->Next;
2023-10-12 13:27:40 +02:00
if (CallbackRoutine)
{
VOID (*callback_function_ptr)(PVOID) = CallbackRoutine;
(*callback_function_ptr)(entry);
}
2023-10-12 13:27:40 +02:00
2023-12-13 05:06:27 +01:00
Head->Next = Head->Next->Next;
ImpExFreePoolWithTag(entry, POOL_TAG_THREAD_LIST);
2023-12-13 05:06:27 +01:00
result = TRUE;
}
2023-09-25 17:41:38 +02:00
ImpKeReleaseGuardedMutex(Lock);
2023-12-13 05:06:27 +01:00
return result;
2023-09-25 17:41:38 +02:00
}
2023-10-12 13:27:40 +02:00
/*
2023-12-13 05:06:27 +01:00
* If we are removing a specific entry, its assumed we have freed and/or dereferenced
* any fields in the structure.
*/
2023-10-08 16:07:49 +02:00
VOID
2023-12-13 05:06:27 +01:00
ListRemoveEntry(_Inout_ PSINGLE_LIST_ENTRY Head,
_Inout_ PSINGLE_LIST_ENTRY Entry,
_In_ PKGUARDED_MUTEX Lock)
2023-09-25 17:41:38 +02:00
{
ImpKeAcquireGuardedMutex(Lock);
2023-09-25 17:41:38 +02:00
2023-12-13 05:06:27 +01:00
PSINGLE_LIST_ENTRY entry = Head->Next;
2023-09-25 17:41:38 +02:00
2023-12-13 05:06:27 +01:00
if (!entry)
goto unlock;
2023-09-25 17:41:38 +02:00
2023-12-13 05:06:27 +01:00
if (entry == Entry)
{
Head->Next = entry->Next;
ImpExFreePoolWithTag(Entry, POOL_TAG_THREAD_LIST);
2023-12-13 05:06:27 +01:00
goto unlock;
}
2023-09-25 17:41:38 +02:00
2023-12-13 05:06:27 +01:00
while (entry->Next)
{
if (entry->Next == Entry)
{
entry->Next = Entry->Next;
ImpExFreePoolWithTag(Entry, POOL_TAG_THREAD_LIST);
2023-12-13 05:06:27 +01:00
goto unlock;
}
2023-09-25 17:41:38 +02:00
2023-12-13 05:06:27 +01:00
entry = entry->Next;
}
2023-09-25 17:41:38 +02:00
unlock:
ImpKeReleaseGuardedMutex(Lock);
2023-09-25 17:41:38 +02:00
}