Author: sir_richard Date: Wed Apr 21 16:14:45 2010 New Revision: 46978
URL: http://svn.reactos.org/svn/reactos?rev=46978&view=rev Log: [NTOS]: Support unload of system modules by parsing the LoadedImports (implement MiDereferenceImports which was just a stub) and calling MiCallDllUnloadAndUnloadDll. [NTOS]: Fix a bug in MiClearImports.
Modified: trunk/reactos/ntoskrnl/mm/sysldr.c
Modified: trunk/reactos/ntoskrnl/mm/sysldr.c URL: http://svn.reactos.org/svn/reactos/trunk/reactos/ntoskrnl/mm/sysldr.c?rev=46... ============================================================================== --- trunk/reactos/ntoskrnl/mm/sysldr.c [iso-8859-1] (original) +++ trunk/reactos/ntoskrnl/mm/sysldr.c [iso-8859-1] Wed Apr 21 16:14:45 2010 @@ -191,53 +191,10 @@ return Status; }
-NTSTATUS -NTAPI -MiDereferenceImports(IN PLOAD_IMPORTS ImportList) -{ - SIZE_T i; - - /* Check if there's no imports or if we're a boot driver */ - if ((ImportList == (PVOID)-1) || - (ImportList == (PVOID)-2) || - (ImportList->Count == 0)) - { - /* Then there's nothing to do */ - return STATUS_SUCCESS; - } - - /* Otherwise, FIXME */ - DPRINT1("%u imports not dereferenced!\n", ImportList->Count); - for (i = 0; i < ImportList->Count; i++) - { - DPRINT1("%wZ <%wZ>\n", &ImportList->Entry[i]->FullDllName, &ImportList->Entry[i]->BaseDllName); - } - return STATUS_UNSUCCESSFUL; -} - -VOID -NTAPI -MiClearImports(IN PLDR_DATA_TABLE_ENTRY LdrEntry) -{ - PAGED_CODE(); - - /* Check if there's no imports or we're a boot driver or only one entry */ - if ((LdrEntry->LoadedImports == (PVOID)-1) || - (LdrEntry->LoadedImports == (PVOID)-2) || - ((ULONG_PTR)LdrEntry->LoadedImports & 1)) - { - /* Nothing to do */ - return; - } - - /* Otherwise, free the import list */ - ExFreePool(LdrEntry->LoadedImports); -} - PVOID NTAPI -MiFindExportedRoutineByName(IN PVOID DllBase, - IN PANSI_STRING ExportName) +MiLocateExportName(IN PVOID DllBase, + IN PCHAR ExportName) { PULONG NameTable; PUSHORT OrdinalTable; @@ -270,7 +227,7 @@ Mid = (Low + High) >> 1;
/* Compare name */ - Ret = strcmp(ExportName->Buffer, (PCHAR)DllBase + NameTable[Mid]); + Ret = strcmp(ExportName, (PCHAR)DllBase + NameTable[Mid]); if (Ret < 0) { /* Update high */ @@ -299,16 +256,203 @@ ExportDirectory->AddressOfFunctions); Function = (PVOID)((ULONG_PTR)DllBase + ExportTable[Ordinal]);
- /* We found it! */ - ASSERT(!(Function > (PVOID)ExportDirectory) && - (Function < (PVOID)((ULONG_PTR)ExportDirectory + ExportSize))); + /* Check if the function is actually a forwarder */ + if (((ULONG_PTR)Function > (ULONG_PTR)ExportDirectory) && + ((ULONG_PTR)Function < ((ULONG_PTR)ExportDirectory + ExportSize))) + { + /* It is, fail */ + return NULL; + } + + /* We found it */ return Function; +} + +NTSTATUS +NTAPI +MmCallDllInitialize(IN PLDR_DATA_TABLE_ENTRY LdrEntry, + IN PLIST_ENTRY ListHead) +{ + UNICODE_STRING ServicesKeyName = RTL_CONSTANT_STRING( + L"\Registry\Machine\System\CurrentControlSet\Services\"); + PMM_DLL_INITIALIZE DllInit; + UNICODE_STRING RegPath, ImportName; + NTSTATUS Status; + + /* Try to see if the image exports a DllInitialize routine */ + DllInit = (PMM_DLL_INITIALIZE)MiLocateExportName(LdrEntry->DllBase, + "DllInitialize"); + if (!DllInit) return STATUS_SUCCESS; + + /* Do a temporary copy of BaseDllName called ImportName + * because we'll alter the length of the string + */ + ImportName.Length = LdrEntry->BaseDllName.Length; + ImportName.MaximumLength = LdrEntry->BaseDllName.MaximumLength; + ImportName.Buffer = LdrEntry->BaseDllName.Buffer; + + /* Obtain the path to this dll's service in the registry */ + RegPath.MaximumLength = ServicesKeyName.Length + + ImportName.Length + sizeof(UNICODE_NULL); + RegPath.Buffer = ExAllocatePoolWithTag(NonPagedPool, + RegPath.MaximumLength, + TAG_LDR_WSTR); + + /* Check if this allocation was unsuccessful */ + if (!RegPath.Buffer) return STATUS_INSUFFICIENT_RESOURCES; + + /* Build and append the service name itself */ + RegPath.Length = ServicesKeyName.Length; + RtlCopyMemory(RegPath.Buffer, + ServicesKeyName.Buffer, + ServicesKeyName.Length); + + /* Check if there is a dot in the filename */ + if (wcschr(ImportName.Buffer, L'.')) + { + /* Remove the extension */ + ImportName.Length = (wcschr(ImportName.Buffer, L'.') - + ImportName.Buffer) * sizeof(WCHAR); + } + + /* Append service name (the basename without extension) */ + RtlAppendUnicodeStringToString(&RegPath, &ImportName); + + /* Now call the DllInit func */ + DPRINT("Calling DllInit(%wZ)\n", &RegPath); + Status = DllInit(&RegPath); + + /* Clean up */ + ExFreePool(RegPath.Buffer); + + /* Return status value which DllInitialize returned */ + return Status; +} + +BOOLEAN +NTAPI +MiCallDllUnloadAndUnloadDll(IN PLDR_DATA_TABLE_ENTRY LdrEntry) +{ + NTSTATUS Status; + PMM_DLL_UNLOAD Func; + PAGED_CODE(); + + /* Get the unload routine */ + Func = (PMM_DLL_UNLOAD)MiLocateExportName(LdrEntry->DllBase, "DllUnload"); + if (!Func) return FALSE; + + /* Call it and check for success */ + Status = Func(); + if (!NT_SUCCESS(Status)) return FALSE; + + /* Lie about the load count so we can unload the image */ + ASSERT(LdrEntry->LoadCount == 0); + LdrEntry->LoadCount = 1; + + /* Unload it and return true */ + MmUnloadSystemImage(LdrEntry); + return TRUE; +} + +NTSTATUS +NTAPI +MiDereferenceImports(IN PLOAD_IMPORTS ImportList) +{ + SIZE_T i; + LOAD_IMPORTS SingleEntry; + PLDR_DATA_TABLE_ENTRY LdrEntry; + PVOID CurrentImports; + PAGED_CODE(); + + /* Check if there's no imports or if we're a boot driver */ + if ((ImportList == MM_SYSLDR_NO_IMPORTS) || + (ImportList == MM_SYSLDR_BOOT_LOADED) || + (ImportList->Count == 0)) + { + /* Then there's nothing to do */ + return STATUS_SUCCESS; + } + + /* Check for single-entry */ + if ((ULONG_PTR)ImportList & MM_SYSLDR_SINGLE_ENTRY) + { + /* Set it up */ + SingleEntry.Count = 1; + SingleEntry.Entry[0] = (PVOID)((ULONG_PTR)ImportList &~ MM_SYSLDR_SINGLE_ENTRY); + + /* Use this as the import list */ + ImportList = &SingleEntry; + } + + /* Loop the import list */ + for (i = 0; (i < ImportList->Count) && (ImportList->Entry[i]); i++) + { + /* Get the entry */ + LdrEntry = ImportList->Entry[i]; + DPRINT1("%wZ <%wZ>\n", &LdrEntry->FullDllName, &LdrEntry->BaseDllName); + + /* Skip boot loaded images */ + if (LdrEntry->LoadedImports == MM_SYSLDR_BOOT_LOADED) continue; + + /* Dereference the entry */ + ASSERT(LdrEntry->LoadCount >= 1); + if (!--LdrEntry->LoadCount) + { + /* Save the import data in case unload fails */ + CurrentImports = LdrEntry->LoadedImports; + + /* This is the last entry */ + LdrEntry->LoadedImports = MM_SYSLDR_NO_IMPORTS; + if (MiCallDllUnloadAndUnloadDll(LdrEntry)) + { + /* Unloading worked, parse this DLL's imports too */ + MiDereferenceImports(CurrentImports); + + /* Check if we had valid imports */ + if ((CurrentImports != MM_SYSLDR_BOOT_LOADED) || + (CurrentImports != MM_SYSLDR_NO_IMPORTS) || + !((ULONG_PTR)LdrEntry->LoadedImports & MM_SYSLDR_SINGLE_ENTRY)) + { + /* Free them */ + ExFreePool(CurrentImports); + } + } + else + { + /* Unload failed, restore imports */ + LdrEntry->LoadedImports = CurrentImports; + } + } + } + + /* Done */ + return STATUS_SUCCESS; +} + +VOID +NTAPI +MiClearImports(IN PLDR_DATA_TABLE_ENTRY LdrEntry) +{ + PAGED_CODE(); + + /* Check if there's no imports or we're a boot driver or only one entry */ + if ((LdrEntry->LoadedImports == MM_SYSLDR_BOOT_LOADED) || + (LdrEntry->LoadedImports == MM_SYSLDR_NO_IMPORTS) || + ((ULONG_PTR)LdrEntry->LoadedImports & MM_SYSLDR_SINGLE_ENTRY)) + { + /* Nothing to do */ + return; + } + + /* Otherwise, free the import list */ + ExFreePool(LdrEntry->LoadedImports); + LdrEntry->LoadedImports = MM_SYSLDR_BOOT_LOADED; }
PVOID NTAPI -MiLocateExportName(IN PVOID DllBase, - IN PCHAR ExportName) +MiFindExportedRoutineByName(IN PVOID DllBase, + IN PANSI_STRING ExportName) { PULONG NameTable; PUSHORT OrdinalTable; @@ -341,7 +485,7 @@ Mid = (Low + High) >> 1;
/* Compare name */ - Ret = strcmp(ExportName, (PCHAR)DllBase + NameTable[Mid]); + Ret = strcmp(ExportName->Buffer, (PCHAR)DllBase + NameTable[Mid]); if (Ret < 0) { /* Update high */ @@ -370,77 +514,10 @@ ExportDirectory->AddressOfFunctions); Function = (PVOID)((ULONG_PTR)DllBase + ExportTable[Ordinal]);
- /* Check if the function is actually a forwarder */ - if (((ULONG_PTR)Function > (ULONG_PTR)ExportDirectory) && - ((ULONG_PTR)Function < ((ULONG_PTR)ExportDirectory + ExportSize))) - { - /* It is, fail */ - return NULL; - } - - /* We found it */ + /* We found it! */ + ASSERT(!(Function > (PVOID)ExportDirectory) && + (Function < (PVOID)((ULONG_PTR)ExportDirectory + ExportSize))); return Function; -} - -NTSTATUS -NTAPI -MmCallDllInitialize(IN PLDR_DATA_TABLE_ENTRY LdrEntry, - IN PLIST_ENTRY ListHead) -{ - UNICODE_STRING ServicesKeyName = RTL_CONSTANT_STRING( - L"\Registry\Machine\System\CurrentControlSet\Services\"); - PMM_DLL_INITIALIZE DllInit; - UNICODE_STRING RegPath, ImportName; - NTSTATUS Status; - - /* Try to see if the image exports a DllInitialize routine */ - DllInit = (PMM_DLL_INITIALIZE)MiLocateExportName(LdrEntry->DllBase, - "DllInitialize"); - if (!DllInit) return STATUS_SUCCESS; - - /* Do a temporary copy of BaseDllName called ImportName - * because we'll alter the length of the string - */ - ImportName.Length = LdrEntry->BaseDllName.Length; - ImportName.MaximumLength = LdrEntry->BaseDllName.MaximumLength; - ImportName.Buffer = LdrEntry->BaseDllName.Buffer; - - /* Obtain the path to this dll's service in the registry */ - RegPath.MaximumLength = ServicesKeyName.Length + - ImportName.Length + sizeof(UNICODE_NULL); - RegPath.Buffer = ExAllocatePoolWithTag(NonPagedPool, - RegPath.MaximumLength, - TAG_LDR_WSTR); - - /* Check if this allocation was unsuccessful */ - if (!RegPath.Buffer) return STATUS_INSUFFICIENT_RESOURCES; - - /* Build and append the service name itself */ - RegPath.Length = ServicesKeyName.Length; - RtlCopyMemory(RegPath.Buffer, - ServicesKeyName.Buffer, - ServicesKeyName.Length); - - /* Check if there is a dot in the filename */ - if (wcschr(ImportName.Buffer, L'.')) - { - /* Remove the extension */ - ImportName.Length = (wcschr(ImportName.Buffer, L'.') - - ImportName.Buffer) * sizeof(WCHAR); - } - - /* Append service name (the basename without extension) */ - RtlAppendUnicodeStringToString(&RegPath, &ImportName); - - /* Now call the DllInit func */ - DPRINT("Calling DllInit(%wZ)\n", &RegPath); - Status = DllInit(&RegPath); - - /* Clean up */ - ExFreePool(RegPath.Buffer); - - /* Return status value which DllInitialize returned */ - return Status; }
VOID