MmGetSystemRoutineAddress和MiFindExportedRoutineByName函式的實現程式碼

whatday發表於2013-07-29

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;
}

在模組中定位指定函式名的地址,這個演算法挺不錯的

相關文章