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;
}
在模块中定位指定函数名的地址,这个算法挺不错的