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;
"ntoskrnl.exe");
"hal.dll");
ULONG Modules = 0;
/* 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;
}
函數主要是周遊PsLoadedModuleList查找"ntoskrnl.exe"與"hal.dll"子產品基址,然後調用MiFindExportedRoutineByName在兩個子產品中查找函數位址,檢視下MiFindExportedRoutineByName實作
PVOID
MiFindExportedRoutineByName (
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;
}
PULONG)((PCHAR)DllBase + (ULONG)ExportDirectory->AddressOfNames);
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;
}
PULONG)((PCHAR)DllBase + (ULONG)ExportDirectory->AddressOfFunctions);
PVOID)((PCHAR)DllBase + Addr[OrdinalNumber]);
PVOID)ExportDirectory) ||
PVOID)((PCHAR)ExportDirectory + ExportSize)));
return FunctionAddress;
}