MmGetSystemRoutineAddress

来源:互联网 发布:中国银行校园淘宝卡 编辑:程序博客网 时间:2024/05/18 00:33

MmGetSystemRoutineAddress这个函数也是比较有用的,是得到系统导出函数的地址,不过网上都是写了一堆汇编代码在哪里,根本没有可读性,还不如用IDA看呢。

下面的函数是摘自ReactOS项目的代码:

[cpp] view plaincopy
  1. PVOID  
  2. NTAPI  
  3. MmGetSystemRoutineAddress(IN PUNICODE_STRING SystemRoutineName)  
  4. {  
  5.     PVOID ProcAddress = NULL;  
  6.     ANSI_STRING AnsiRoutineName;  
  7.     NTSTATUS Status;  
  8.     PLIST_ENTRY NextEntry;  
  9.     PLDR_DATA_TABLE_ENTRY LdrEntry;  
  10.     BOOLEAN Found = FALSE;  
  11.     UNICODE_STRING KernelName = RTL_CONSTANT_STRING(L"ntoskrnl.exe");  
  12.     UNICODE_STRING HalName = RTL_CONSTANT_STRING(L"hal.dll");  
  13.     ULONG Modules = 0;  
  14.   
  15.     /* Convert routine to ansi name */  
  16.     Status = RtlUnicodeStringToAnsiString(&AnsiRoutineName,  
  17.                                           SystemRoutineName,  
  18.                                           TRUE);  
  19.     if (!NT_SUCCESS(Status)) return NULL;  
  20.   
  21.     /* Lock the list */  
  22.     KeEnterCriticalRegion();  
  23.     ExAcquireResourceSharedLite(&PsLoadedModuleResource, TRUE);  
  24.   
  25.     /* Loop the loaded module list */  
  26.     NextEntry = PsLoadedModuleList.Flink;  
  27.     while (NextEntry != &PsLoadedModuleList)  
  28.     {  
  29.         /* Get the entry */  
  30.         LdrEntry = CONTAINING_RECORD(NextEntry,  
  31.                                      LDR_DATA_TABLE_ENTRY,  
  32.                                      InLoadOrderLinks);  
  33.   
  34.         /* Check if it's the kernel or HAL */  
  35.         if (RtlEqualUnicodeString(&KernelName, &LdrEntry->BaseDllName, TRUE))  
  36.         {  
  37.             /* Found it */  
  38.             Found = TRUE;  
  39.             Modules++;  
  40.         }  
  41.         else if (RtlEqualUnicodeString(&HalName, &LdrEntry->BaseDllName, TRUE))  
  42.         {  
  43.             /* Found it */  
  44.             Found = TRUE;  
  45.             Modules++;  
  46.         }  
  47.   
  48.         /* Check if we found a valid binary */  
  49.         if (Found)  
  50.         {  
  51.             /* Find the procedure name */  
  52.             ProcAddress = MiFindExportedRoutineByName(LdrEntry->DllBase,  
  53.                                                       &AnsiRoutineName);  
  54.   
  55.             /* Break out if we found it or if we already tried both modules */  
  56.             if (ProcAddress) break;  
  57.             if (Modules == 2) break;  
  58.         }  
  59.   
  60.         /* Keep looping */  
  61.         NextEntry = NextEntry->Flink;  
  62.     }  
  63.   
  64.     /* Release the lock */  
  65.     ExReleaseResourceLite(&PsLoadedModuleResource);  
  66.     KeLeaveCriticalRegion();  
  67.   
  68.     /* Free the string and return */  
  69.     RtlFreeAnsiString(&AnsiRoutineName);  
  70.     return ProcAddress;  
  71. }  

函数主要是遍历PsLoadedModuleList查找"ntoskrnl.exe"与"hal.dll"模块基址,然后调用MiFindExportedRoutineByName在两个模块中查找函数地址,查看下MiFindExportedRoutineByName实现

[cpp] view plaincopy
  1. PVOID  
  2. MiFindExportedRoutineByName (  
  3.     IN PVOID DllBase,  
  4.     IN PANSI_STRING AnsiImageRoutineName  
  5.     )  
  6. {  
  7.     USHORT OrdinalNumber;  
  8.     PULONG NameTableBase;  
  9.     PUSHORT NameOrdinalTableBase;  
  10.     PULONG Addr;  
  11.     LONG High;  
  12.     LONG Low;  
  13.     LONG Middle;  
  14.     LONG Result;  
  15.     ULONG ExportSize;   // 保存表项的大小  
  16.     PVOID FunctionAddress;  
  17.     PIMAGE_EXPORT_DIRECTORY ExportDirectory;  
  18.   
  19.     PAGED_CODE();  
  20.   
  21.     ExportDirectory = (PIMAGE_EXPORT_DIRECTORY) RtlImageDirectoryEntryToData (  
  22.                                 DllBase,  
  23.                                 TRUE,  
  24.                                 IMAGE_DIRECTORY_ENTRY_EXPORT,  
  25.                                 &ExportSize);  
  26.   
  27.     if (ExportDirectory == NULL) {  
  28.         return NULL;  
  29.     }  
  30.   
  31.     NameTableBase = (PULONG)((PCHAR)DllBase + (ULONG)ExportDirectory->AddressOfNames);  
  32.     NameOrdinalTableBase = (PUSHORT)((PCHAR)DllBase + (ULONG)ExportDirectory->AddressOfNameOrdinals);  
  33.   
  34.     //二分查找法  
  35.     Low = 0;  
  36.     Middle = 0;  
  37.     High = ExportDirectory->NumberOfNames - 1;  
  38.   
  39.     while (High >= Low) {  
  40.         Middle = (Low + High) >> 1;  
  41.   
  42.         Result = strcmp (AnsiImageRoutineName->Buffer,  
  43.                          (PCHAR)DllBase + NameTableBase[Middle]);  
  44.   
  45.         if (Result < 0) {  
  46.             High = Middle - 1;  
  47.         }  
  48.         else if (Result > 0) {  
  49.             Low = Middle + 1;  
  50.         }  
  51.         else {  
  52.             break;  
  53.         }  
  54.     }  
  55.   
  56.     // 如果High < Low,表明没有在EAT中找到这个函数;否则,返回此函数的索引  
  57.     if (High < Low) {  
  58.         return NULL;  
  59.     }  
  60.   
  61.     OrdinalNumber = NameOrdinalTableBase[Middle];  
  62.   
  63.     // 如果索引值大于EAT中已有的函数数量,则查找失败  
  64.     if ((ULONG)OrdinalNumber >= ExportDirectory->NumberOfFunctions) {  
  65.         return NULL;  
  66.     }  
  67.   
  68.     Addr = (PULONG)((PCHAR)DllBase + (ULONG)ExportDirectory->AddressOfFunctions);  
  69.   
  70.     FunctionAddress = (PVOID)((PCHAR)DllBase + Addr[OrdinalNumber]);  
  71.     ASSERT ((FunctionAddress <= (PVOID)ExportDirectory) ||  
  72.             (FunctionAddress >= (PVOID)((PCHAR)ExportDirectory + ExportSize)));  
  73.   
  74.     return FunctionAddress;  
  75. }  

在函数MiFindExportedRoutineByName中,利用RtlImageDirectoryEntryToData 函数得到模块的导出表地址,然后二分遍历导出表,查看是否有指定的字符串导出,还用了Ordinal值判断(PE解析的内容~)