MmGetSystemRoutineAddress这个函数也是比较有用的,是得到系统导出函数的地址,不过网上都是写了一堆汇编代码在哪里,根本没有可读性,还不如用IDA看呢。
下面的函数是摘自ReactOS项目的代码:
PVOID NTAPI MmGetSystemRoutineAddress(IN PUNICODE_STRING SystemRoutineName) { PVOID ProcAddress = NULL; ANSI_STRING AnsiRoutineName; NTSTATUS Status; PLIST_ENTRY NextEntry; PLDR_DATA_TABLE_ENTRY LdrEntry; BOOLEAN Found = FALSE; UNICODE_STRING KernelName = RTL_CONSTANT_STRING(L"ntoskrnl.exe"); UNICODE_STRING HalName = RTL_CONSTANT_STRING(L"hal.dll"); ULONG Modules = 0; ERESOURCE PsLoadedModuleResource; /* Convert routine to ansi name */ Status = RtlUnicodeStringToAnsiString(&AnsiRoutineName, SystemRoutineName, TRUE); if (!NT_SUCCESS(Status)) return NULL; /* Lock the list */ KeEnterCriticalRegion(); ExAcquireResourceSharedLite(&PsLoadedModuleResource, TRUE); /* Loop the loaded module list */ NextEntry = PsLoadedModuleList.Flink; while (NextEntry != &PsLoadedModuleList) { /* Get the entry */ LdrEntry = CONTAINING_RECORD(NextEntry, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks); /* Check if it's the kernel or HAL */ if (RtlEqualUnicodeString(&KernelName, &LdrEntry->BaseDllName, TRUE)) { /* Found it */ Found = TRUE; Modules++; } else if (RtlEqualUnicodeString(&HalName, &LdrEntry->BaseDllName, TRUE)) { /* Found it */ Found = TRUE; Modules++; } /* Check if we found a valid binary */ if (Found) { /* Find the procedure name */ ProcAddress = MiFindExportedRoutineByName(LdrEntry->DllBase, &AnsiRoutineName); /* Break out if we found it or if we already tried both modules */ if (ProcAddress) break; if (Modules == 2) break; } /* Keep looping */ NextEntry = NextEntry->Flink; } /* Release the lock */ ExReleaseResourceLite(&PsLoadedModuleResource); KeLeaveCriticalRegion(); /* Free the string and return */ RtlFreeAnsiString(&AnsiRoutineName); return ProcAddress; }
MiFindExportedRoutineByName——EAT中定位到指定函数
MmGetSystemRoutineAddress实际调用的MiFindExportedRoutineByName
PVOID MiFindExportedRoutineByName ( IN PVOID DllBase, IN PANSI_STRING AnsiImageRoutineName ) { USHORT OrdinalNumber; PULONG NameTableBase; PUSHORT NameOrdinalTableBase; PULONG Addr; LONG High; LONG Low; LONG Middle; LONG Result; ULONG ExportSize; // 保存表项的大小 PVOID FunctionAddress; PIMAGE_EXPORT_DIRECTORY ExportDirectory; PAGED_CODE(); ExportDirectory = (PIMAGE_EXPORT_DIRECTORY) RtlImageDirectoryEntryToData ( DllBase, TRUE, IMAGE_DIRECTORY_ENTRY_EXPORT, &ExportSize); if (ExportDirectory == NULL) { return NULL; } NameTableBase = (PULONG)((PCHAR)DllBase + (ULONG)ExportDirectory->AddressOfNames); NameOrdinalTableBase = (PUSHORT)((PCHAR)DllBase + (ULONG)ExportDirectory->AddressOfNameOrdinals); //二分查找法 Low = 0; Middle = 0; High = ExportDirectory->NumberOfNames - 1; while (High >= Low) { Middle = (Low + High) >> 1; Result = strcmp (AnsiImageRoutineName->Buffer, (PCHAR)DllBase + NameTableBase[Middle]); if (Result < 0) { High = Middle - 1; } else if (Result > 0) { Low = Middle + 1; } else { break; } } // 如果High < Low,表明没有在EAT中找到这个函数;否则,返回此函数的索引 if (High < Low) { return NULL; } OrdinalNumber = NameOrdinalTableBase[Middle]; // 如果索引值大于EAT中已有的函数数量,则查找失败 if ((ULONG)OrdinalNumber >= ExportDirectory->NumberOfFunctions) { return NULL; } Addr = (PULONG)((PCHAR)DllBase + (ULONG)ExportDirectory->AddressOfFunctions); FunctionAddress = (PVOID)((PCHAR)DllBase + Addr[OrdinalNumber]); ASSERT ((FunctionAddress <= (PVOID)ExportDirectory) || (FunctionAddress >= (PVOID)((PCHAR)ExportDirectory + ExportSize))); return FunctionAddress; }
在模块中定位指定函数名的地址,这个算法挺不错的