內核散筆代碼-DISPATCH_LEVEL函數調用

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);
    }

參考:https://blog.csdn.net/dailongjian2008

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章