프로그래밍

IopLoadUnloadDriver

VOID NTAPI
IopLoadUnloadDriver(PLOAD_UNLOAD_PARAMS LoadParams)
{
    RTL_QUERY_REGISTRY_TABLE QueryTable[3];
    UNICODE_STRING ImagePath;
    UNICODE_STRING ServiceName;
    NTSTATUS Status;
    ULONG Type;
    PDEVICE_NODE DeviceNode;
    PDRIVER_OBJECT DriverObject;
    PLDR_DATA_TABLE_ENTRY ModuleObject;
    PVOID BaseAddress;
    WCHAR *cur;

    // 언로드 요청인지 아닌지 검사한다.
    if (LoadParams->DriverObject)
    {
        // 드라이버 로드/언로드에 관련된 파라미터안에 드라이버 객체가 있다면 로드 된 드라이버이기 때문에,
        // 해당 드라이버 객체의 언로드 루틴을 실행한다.
        (*LoadParams->DriverObject->DriverUnload)(LoadParams->DriverObject);

        // 성공적으로 언로드를 수행했다는 상태값을 반환하고 이벤트에 시그널을 설정한다.
        LoadParams->Status = STATUS_SUCCESS;
        (VOID)KeSetEvent(&LoadParams->Event, 0, FALSE);
        return;
    }

    //
    // 드라이버에 대한 로드 요청인 것으로 처리
    //

    RtlInitUnicodeString(&ImagePath, NULL);

    //
    // 레지스트리 키 이름으로부터 서비스 이름을 얻는다.
    //

    ASSERT(LoadParams->ServiceName->Length >= sizeof(WCHAR));

    ServiceName = *LoadParams->ServiceName;
    cur = LoadParams->ServiceName->Buffer + (LoadParams->ServiceName->Length / sizeof(WCHAR)) - 1;

    while (LoadParams->ServiceName->Buffer != cur)
    {
        if(*cur == L'\\')
        {
            ServiceName.Buffer = cur + 1;
            ServiceName.Length = LoadParams->ServiceName->Length - (USHORT)((ULONG_PTR)ServiceName.Buffer - (ULONG_PTR)LoadParams->ServiceName->Buffer);
            break;
        }
        cur--;
    }

    //
    // 서비스 타입을 얻는다.
    //

    RtlZeroMemory(&QueryTable, sizeof(QueryTable));

    RtlInitUnicodeString(&ImagePath, NULL);

    QueryTable[0].Name = L"Type";
    QueryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_REQUIRED;
    QueryTable[0].EntryContext = &Type;

    QueryTable[1].Name = L"ImagePath";
    QueryTable[1].Flags = RTL_QUERY_REGISTRY_DIRECT;
    QueryTable[1].EntryContext = &ImagePath;

    Status = RtlQueryRegistryValues(
        RTL_REGISTRY_ABSOLUTE,
        LoadParams->ServiceName->Buffer,
        QueryTable,
        NULL,
        NULL);

    if (!NT_SUCCESS(Status))
    {
        DPRINT("RtlQueryRegistryValues() failed (Status %lx)\n", Status);

        if (ImagePath.Buffer)
            ExFreePool(ImagePath.Buffer);

        LoadParams->Status = Status;
        (VOID)KeSetEvent(&LoadParams->Event, 0, FALSE);
        return;
    }

    //
    // 이후의 모든 처리를 위해서 이미지 경로를 표준으로 통일시킨다.
    //

    Status = IopNormalizeImagePath(&ImagePath, &ServiceName);

    if (!NT_SUCCESS(Status))
    {
        DPRINT("IopNormalizeImagePath() failed (Status %x)\n", Status);

        LoadParams->Status = Status;
        (VOID)KeSetEvent(&LoadParams->Event, 0, FALSE);
        return;
    }

    DPRINT("FullImagePath: '%wZ'\n", &ImagePath);
    DPRINT("Type: %lx\n", Type);

    // 드라이버가 이미 로드되어 있고 초기화 되어 있는 경우에는 기존 DriverObject를 얻는다.

    Status = IopGetDriverObject(
        &DriverObject,
        &ServiceName,
        (Type == 2 || /* SERVICE_FILE_SYSTEM_DRIVER */
        Type == 8 )); /* SERVICE_RECOGNIZER_DRIVER */

    if (!NT_SUCCESS(Status))
    {
        //
        // 드라이버 모듈을 로드한다.
        //
        DPRINT("Loading module from %wZ\n", &ImagePath);

        Status = MmLoadSystemImage(&ImagePath, NULL, NULL, 0, (PVOID)&ModuleObject, &BaseAddress);

        if (!NT_SUCCESS(Status) && Status != STATUS_IMAGE_ALREADY_LOADED)
        {
            DPRINT("MmLoadSystemImage() failed (Status %lx)\n", Status);

            LoadParams->Status = Status;
            (VOID)KeSetEvent(&LoadParams->Event, 0, FALSE);
            return;
        }

        //
        // 만약 처음으로 로드된 드라이버 모듈이라면 초기화를 한다.
        //
        if (Status != STATUS_IMAGE_ALREADY_LOADED)
        {
            Status = IopCreateDeviceNode(IopRootDeviceNode, NULL, &ServiceName, &DeviceNode);

            if (!NT_SUCCESS(Status))
            {
                DPRINT("IopCreateDeviceNode() failed (Status %lx)\n", Status);

                MmUnloadSystemImage(ModuleObject);
                LoadParams->Status = Status;
                (VOID)KeSetEvent(&LoadParams->Event, 0, FALSE);
                return;
            }

            IopDisplayLoadingMessage(&DeviceNode->ServiceName);

            Status = IopInitializeDriverModule(
                DeviceNode,
                ModuleObject,
                &DeviceNode->ServiceName,
                (Type == 2 || /* SERVICE_FILE_SYSTEM_DRIVER */
                Type == 8 ),  /* SERVICE_RECOGNIZER_DRIVER */
                &DriverObject);

            if (!NT_SUCCESS(Status))
            {
                DPRINT("IopInitializeDriver() failed (Status %lx)\n", Status);
                MmUnloadSystemImage(ModuleObject);
                IopFreeDeviceNode(DeviceNode);
                LoadParams->Status = Status;
                (VOID)KeSetEvent(&LoadParams->Event, 0, FALSE);
                return;
            }

            //
            // Initialize and start device
            //
            IopInitializeDevice(DeviceNode, DriverObject);
            Status = IopStartDevice(DeviceNode);
        }
    }
    else
    {
        DPRINT("DriverObject already exist in ObjectManager\n");

        // IopGetDriverObject로 얻은 DriverObject에 대한 참조를 해제한다.
        ObDereferenceObject(DriverObject);
    }

    // 호출자에게 상태값을 넘겨주고 이벤트를 시그널한다.
    LoadParams->Status = Status;
    (VOID)KeSetEvent(&LoadParams->Event, 0, FALSE);
}


'프로그래밍' 카테고리의 다른 글

MSB8022  (0) 2019.02.13
IrpSp->FileObject  (0) 2017.11.16
NtLoadDriver가 STATUS_PRIVILEGE_NOT_HELD(0xC0000061)를 리턴  (0) 2014.12.05
IoCreateDevice와 IoCreateDeviceSecure  (0) 2014.06.23
The funny page table terminology on AMD64  (0) 2014.06.03