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