Dpc異步調用:
#include <ntifs.h>
#include <ntddk.h>
EXTERN_C{
NTSTATUS
DriverEntry(
_In_ PDRIVER_OBJECT DriverObject,
_In_ PUNICODE_STRING RegistryPath
);
VOID
DriverUnload(
_In_ PDRIVER_OBJECT DriverObject
);
}
typedef struct _DPC_WAIT_EVENT
{
KWAIT_BLOCK WaitBlock;
PKDPC Dpc;
PVOID Object;
} DPC_WAIT_EVENT, *PDPC_WAIT_EVENT;
NTSTATUS
ExCreateDpcEvent(
_Out_ PDPC_WAIT_EVENT* DpcWaitEvent,
_Out_ PKEVENT* Event,
_In_ PKDPC Dpc
);
NTSTATUS
ExQueueDpcEventWait(
_In_ PDPC_WAIT_EVENT DpcWait,
_In_ BOOLEAN WaitIfNotSignaled
);
NTSTATUS
ExCancelDpcEventWait(
_In_ PDPC_WAIT_EVENT DpcWait
);
NTSTATUS
ExDeleteDpcEvent(
_In_ PDPC_WAIT_EVENT DpcWait
);
static KDEFERRED_ROUTINE DpcRoutine;
PDPC_WAIT_EVENT g_DpcWait;
KDPC g_Dpc;
PKEVENT g_Event;
decltype(ExCreateDpcEvent)* g_ExCreateDpcEventPtr = nullptr;
decltype(ExDeleteDpcEvent)* g_ExDeleteDpcEventPtr = nullptr;
decltype(ExQueueDpcEventWait)* g_ExQueueDpcEventWaitPtr = nullptr;
decltype(ExCancelDpcEventWait)* g_ExCancelDpcEventWaitPtr = nullptr;
static
void
DpcRoutine(
PKDPC Dpc,
PVOID DeferredContext,
PVOID SystemArgument1,
PVOID SystemArgument2
)
{
UNREFERENCED_PARAMETER(Dpc);
UNREFERENCED_PARAMETER(DeferredContext);
UNREFERENCED_PARAMETER(SystemArgument1);
UNREFERENCED_PARAMETER(SystemArgument2);
DbgPrintEx(77, 0, "Process terminated\n");
}
void CreateProcessNotifyRoutineEx (
PEPROCESS Process,
HANDLE ProcessId,
PPS_CREATE_NOTIFY_INFO CreateInfo
)
{
UNREFERENCED_PARAMETER(ProcessId);
NTSTATUS status;
DECLARE_CONST_UNICODE_STRING(cmdString, L"cmd.exe");
//
// If process name is cmd.exe, create a dpc
// that will wait for the process to terminate
//
if ((!CreateInfo) || (!RtlSuffixUnicodeString(&cmdString, CreateInfo->ImageFileName, FALSE)))
{
return;
}
//
// Only wait on one process
//
if (g_DpcWait == nullptr)
{
KeInitializeDpc(&g_Dpc, DpcRoutine, &g_Dpc);
status = g_ExCreateDpcEventPtr(&g_DpcWait, &g_Event, &g_Dpc);
if (!NT_SUCCESS(status))
{
DbgPrintEx(77, 0, "ExCreateDpcEvent failed with status: 0x%x\n", status);
return;
}
g_DpcWait->Object = (PVOID)Process;
g_ExQueueDpcEventWaitPtr(g_DpcWait, 1);
}
}
VOID
DriverUnload(
_In_ PDRIVER_OBJECT DriverObject
)
{
UNREFERENCED_PARAMETER(DriverObject);
PsSetCreateProcessNotifyRoutineEx(&CreateProcessNotifyRoutineEx, TRUE);
//
// Change the DPC_WAIT_EVENT structure to point back to the event,
// cancel the wait and destroy the structure
//
if (g_DpcWait != nullptr)
{
g_DpcWait->Object = g_Event;
g_ExCancelDpcEventWaitPtr(g_DpcWait);
g_ExDeleteDpcEventPtr(g_DpcWait);
}
}
NTSTATUS
DriverEntry(
_In_ PDRIVER_OBJECT DriverObject,
_In_ PUNICODE_STRING RegistryPath
)
{
UNREFERENCED_PARAMETER(RegistryPath);
DECLARE_CONST_UNICODE_STRING(createDpcEvent, L"ExCreateDpcEvent");
DECLARE_CONST_UNICODE_STRING(deleteDpcEvent, L"ExDeleteDpcEvent");
DECLARE_CONST_UNICODE_STRING(queueDpcEventWait, L"ExQueueDpcEventWait");
DECLARE_CONST_UNICODE_STRING(cancelDpcEventWait, L"ExCancelDpcEventWait");
DriverObject->DriverUnload = DriverUnload;
g_ExCreateDpcEventPtr = (decltype(ExCreateDpcEvent)*)(MmGetSystemRoutineAddress((PUNICODE_STRING)&createDpcEvent));
g_ExDeleteDpcEventPtr = (decltype(ExDeleteDpcEvent)*)(MmGetSystemRoutineAddress((PUNICODE_STRING)&deleteDpcEvent));
g_ExQueueDpcEventWaitPtr = (decltype(ExQueueDpcEventWait)*)(MmGetSystemRoutineAddress((PUNICODE_STRING)&queueDpcEventWait));
g_ExCancelDpcEventWaitPtr = (decltype(ExCancelDpcEventWait)*)(MmGetSystemRoutineAddress((PUNICODE_STRING)&cancelDpcEventWait));
if ((g_ExCreateDpcEventPtr == nullptr) ||
(g_ExDeleteDpcEventPtr == nullptr) ||
(g_ExQueueDpcEventWaitPtr == nullptr) ||
(g_ExCancelDpcEventWaitPtr == nullptr))
{
return STATUS_UNSUCCESSFUL;
}
return PsSetCreateProcessNotifyRoutineEx(&CreateProcessNotifyRoutineEx, FALSE);
}
WorkItem
WorkItem的方式,windows系統會執行一個Worker線程,該線程會不斷從Worker隊列中取出任務然後執行,執行是在PASSIVE_LEVEL下的,所以我們可以將我們要進行的文件操作作爲一個WorkerItem放入Worker隊列中,以下是我在IoControl中執行設置EDID的操作,該操作需要在PASSIVE_LEVEL下執行。
DoIoControl是Worker線程要執行的任務,pIrp是傳遞給DoIoControl的一個參數,該參數類型可自定義。看下DoIoControl的實現:
上述代碼加了一個自旋鎖,是由於多了一個worker線程,爲防止在DoIoControl中先將IRP完成了,然後再在HandleIoControl中執行IoMarkIrpPending(pIrp);造成藍屏,所以當前的邏輯確保在HandleIoControl中執行IoMarkIrpPending(pIrp);後再執行Worker隊列中的任務
static NTSTATUS HandleIoControl(IN PDEVICE_OBJECT pDevObj, IN PIRP pIrp)
{
PIO_STACK_LOCATION irpsp = IoGetCurrentIrpStackLocation(pIrp);
KIRQL OldIrql;
if (irpsp->MajorFunction != IRP_MJ_DEVICE_CONTROL) {
pIrp->IoStatus.Information = 0;
pIrp->IoStatus.Status = STATUS_SUCCESS;
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
PIO_WORKITEM pIoWorkItem = IoAllocateWorkItem(pDevObj);
if (pIoWorkItem == nullptr) {
pIrp->IoStatus.Information = 0;
pIrp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
return STATUS_INSUFFICIENT_RESOURCES;
}
KeAcquireSpinLock(&g_lock, &OldIrql);
IoInitializeWorkItem(pDevObj, pIoWorkItem);
IoQueueWorkItemEx(pIoWorkItem, (PIO_WORKITEM_ROUTINE_EX)DoIocontrol, DelayedWorkQueue, pIrp);
IoMarkIrpPending(pIrp);
KeReleaseSpinLock(&g_lock, OldIrql);
return STATUS_PENDING;
}
static VOID DoIocontrol(IN PDEVICE_OBJECT DeviceObject, PIRP pIrp, PIO_WORKITEM IoWorkItem)
{
UNREFERENCED_PARAMETER(DeviceObject);
KIRQL OldIrql;
KeAcquireSpinLock(&g_lock, &OldIrql);
NTSTATUS status = STATUS_SUCCESS;
PIO_STACK_LOCATION irpsp = IoGetCurrentIrpStackLocation(pIrp);
pIrp->IoStatus.Information = 0;
pIrp->IoStatus.Status = status;
switch (irpsp->Parameters.DeviceIoControl.IoControlCode)
{
case IOCTL_SET_CUSTOM_EDID:
{
ULONG uInputLength = irpsp->Parameters.DeviceIoControl.InputBufferLength;
BYTE *inputBuffer = (BYTE *)pIrp->AssociatedIrp.SystemBuffer;
if (uInputLength >= MIN_EDID_LENGTH
&& pHwDeviceExtension != nullptr
&& pHwDeviceExtension->pMonitor != nullptr) {
pHwDeviceExtension->pMonitor->SetEdid(inputBuffer, uInputLength);
}
else {
status = STATUS_INVALID_VARIANT;
}
break;
}
default:
break;
}
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
IoUninitializeWorkItem(IoWorkItem);
IoFreeWorkItem(IoWorkItem);
KeReleaseSpinLock(&g_lock, OldIrql);
}
強制降權
void CvMonitor::SetEdid(const BYTE *byEdid, ULONG nLength)
{
/* 需要將中斷權限降低,否則無法操作文件 */
KIRQL oldirql = KeGetCurrentIrql();
if (oldirql >= DISPATCH_LEVEL) {
KeLowerIrql(PASSIVE_LEVEL);
}
if (memcmp(byEdid, m_edid, min(sizeof(m_edid), nLength)) != 0) {
memcpy(m_edid, byEdid, min(sizeof(m_edid), nLength));
if (!EdidValid()) {
memcpy(m_edid, s_defaultEdid, sizeof(m_edid));
}
else {
SyncEdidToFile();
}
/* 模擬將顯示器插拔,插拔後系統重新讀取edid */
Reset();
}
KfRaiseIrql(oldirql);
}