2014年8月18日星期一

内核中的GetProcAddress

#include <ntifs.h>
#include <windef.h>


/*
文件名:MiFindExportedRoutineByName.c

写作目的:
MmGetSystemRoutineAddress这个函数有如下的限制:
It can only be used for routines exported by the kernel or HAL, not for any driver-defined routine. 

有时候获取别的内核模块的函数的地址是一个解决问题的办法,如:WINHV.sys。
有人为此还专门写了函数,当然是解析PE32/PE32+了。

其实系统已经提供了一些函数,只不过导出而没有公开而已。

看WRK知道:MmGetSystemRoutineAddress是通过MiFindExportedRoutineByName实现的。
可是:MiFindExportedRoutineByName没有导出,定位又没有好的稳定的办法。
所以自己实现,还好RtlImageDirectoryEntryToData(RtlImageNtHeader)已经导出。

本文的一些信息摘自:WRK。
不过这也是源码,加入驱动也是可以使用的。

made by correy
made at 2014.08.18
email:kouleguan at hotmail dot com
homepage:http://correy.webs.com
*/


#define IMAGE_DIRECTORY_ENTRY_EXPORT          0   // Export Directory


/*
这个应该兼容PE32+也就是64位的系统。
有待看PE32规范。
*/
typedef struct _IMAGE_EXPORT_DIRECTORY {
    DWORD   Characteristics;
    DWORD   TimeDateStamp;
    WORD    MajorVersion;
    WORD    MinorVersion;
    DWORD   Name;
    DWORD   Base;
    DWORD   NumberOfFunctions;
    DWORD   NumberOfNames;
    DWORD   AddressOfFunctions;     // RVA from base of image
    DWORD   AddressOfNames;         // RVA from base of image
    DWORD   AddressOfNameOrdinals;  // RVA from base of image
} IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;


DRIVER_UNLOAD Unload;
VOID Unload(__in PDRIVER_OBJECT DriverObject)
{

}


PVOID RtlImageDirectoryEntryToData (IN PVOID Base, IN BOOLEAN MappedAsImage, IN USHORT DirectoryEntry, OUT PULONG Size);
/*++
Routine Description:
    This function locates a Directory Entry within the image header and returns either the virtual address or seek address of the data the Directory describes.
Arguments:
    Base - Supplies the base of the image or data file.
    MappedAsImage - FALSE if the file is mapped as a data file.
                  - TRUE if the file is mapped as an image.
    DirectoryEntry - Supplies the directory entry to locate.
    Size - Return the size of the directory.
Return Value:
    NULL - The file does not contain data for the specified directory entry.
    NON-NULL - Returns the address of the raw data the directory describes.
--*/
//{
//    PIMAGE_NT_HEADERS NtHeaders;
//
//    if (LDR_IS_DATAFILE(Base)) {
//        Base = LDR_DATAFILE_TO_VIEW(Base);
//        MappedAsImage = FALSE;
//    }
//
//    NtHeaders = RtlImageNtHeader(Base);
//
//    if (!NtHeaders)
//        return NULL;
//
//    if (NtHeaders->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
//        return (RtlpImageDirectoryEntryToData32(Base, MappedAsImage, DirectoryEntry, Size, (PIMAGE_NT_HEADERS32)NtHeaders));
//    } else if (NtHeaders->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
//        return (RtlpImageDirectoryEntryToData64(Base, MappedAsImage, DirectoryEntry, Size, (PIMAGE_NT_HEADERS64)NtHeaders));
//    } else {
//        return (NULL);
//    }
//}


PVOID MiFindExportedRoutineByName (IN PVOID DllBase, IN PANSI_STRING AnsiImageRoutineName)
/*++
Routine Description:
    This function searches the argument module looking for the requested exported function name.
Arguments:
    DllBase - Supplies the base address of the requested module.
    AnsiImageRoutineName - Supplies the ANSI routine name being searched for.
Return Value:
    The virtual address of the requested routine or NULL if not found.
--*/
{
    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);// Initialize the pointer to the array of RVA-based ansi export strings.    
    NameOrdinalTableBase = (PUSHORT)((PCHAR)DllBase + (ULONG)ExportDirectory->AddressOfNameOrdinals);// Initialize the pointer to the array of USHORT ordinal numbers.    
    Low = 0;
    Middle = 0;
    High = ExportDirectory->NumberOfNames - 1;

    while (High >= Low) // Lookup the desired name in the name table using a binary search.
    {        
        Middle = (Low + High) >> 1;// Compute the next probe index and compare the import name with the export name entry.
        Result = strcmp (AnsiImageRoutineName->Buffer, (PCHAR)DllBase + NameTableBase[Middle]);
        if (Result < 0) {
            High = Middle - 1;
        } else if (Result > 0) {
            Low = Middle + 1;
        } else {
            break;
        }
    }

    // If the high index is less than the low index, then a matching table entry was not found.
    // Otherwise, get the ordinal number from the ordinal table.
    if (High < Low) {
        return NULL;
    }

    OrdinalNumber = NameOrdinalTableBase[Middle];

    // If the OrdinalNumber is not within the Export Address Table,then this image does not implement the function.
    // Return not found.
    if ((ULONG)OrdinalNumber >= ExportDirectory->NumberOfFunctions) {
        return NULL;
    }

    // Index into the array of RVA export addresses by ordinal number.
    Addr = (PULONG)((PCHAR)DllBase + (ULONG)ExportDirectory->AddressOfFunctions);
    FunctionAddress = (PVOID)((PCHAR)DllBase + Addr[OrdinalNumber]);

    // Forwarders are not used by the kernel and HAL to each other.
    ASSERT ((FunctionAddress <= (PVOID)ExportDirectory) || (FunctionAddress >= (PVOID)((PCHAR)ExportDirectory + ExportSize)));

    return FunctionAddress;
}


DRIVER_INITIALIZE DriverEntry;
NTSTATUS DriverEntry( __in struct _DRIVER_OBJECT  * DriverObject, __in PUNICODE_STRING  RegistryPath)
{
    NTSTATUS status = STATUS_UNSUCCESSFUL;    

    KdBreakPoint();

    DriverObject->DriverUnload = Unload;    

    //测试的代码就不写了。

    return status;    
} 

--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

#include <ntifs.h>
#include <windef.h>
#include <Aux_klib.h> //source里面要有TARGETLIBS=$(DDK_LIB_PATH)\Aux_klib.lib


/*
made by correy
made at 2014.08.19
email:kouleguan at hotmail dot com
homepage:http://correy.webs.com
*/


#define TAG  'tset' //test

#define IMAGE_DIRECTORY_ENTRY_EXPORT          0   // Export Directory


DRIVER_UNLOAD Unload;
VOID Unload(__in PDRIVER_OBJECT DriverObject)
{

}


PVOID RtlImageDirectoryEntryToData (IN PVOID Base, IN BOOLEAN MappedAsImage, IN USHORT DirectoryEntry, OUT PULONG Size);
/*++
Routine Description:
    This function locates a Directory Entry within the image header and returns either the virtual address or seek address of the data the Directory describes.
Arguments:
    Base - Supplies the base of the image or data file.
    MappedAsImage - FALSE if the file is mapped as a data file.
                  - TRUE if the file is mapped as an image.
    DirectoryEntry - Supplies the directory entry to locate.
    Size - Return the size of the directory.
Return Value:
    NULL - The file does not contain data for the specified directory entry.
    NON-NULL - Returns the address of the raw data the directory describes.
--*/
//{
//    PIMAGE_NT_HEADERS NtHeaders;
//
//    if (LDR_IS_DATAFILE(Base)) {
//        Base = LDR_DATAFILE_TO_VIEW(Base);
//        MappedAsImage = FALSE;
//    }
//
//    NtHeaders = RtlImageNtHeader(Base);
//
//    if (!NtHeaders)
//        return NULL;
//
//    if (NtHeaders->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
//        return (RtlpImageDirectoryEntryToData32(Base, MappedAsImage, DirectoryEntry, Size, (PIMAGE_NT_HEADERS32)NtHeaders));
//    } else if (NtHeaders->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
//        return (RtlpImageDirectoryEntryToData64(Base, MappedAsImage, DirectoryEntry, Size, (PIMAGE_NT_HEADERS64)NtHeaders));
//    } else {
//        return (NULL);
//    }
//}


PVOID MiFindExportedRoutineByName (IN PVOID DllBase, IN PANSI_STRING AnsiImageRoutineName)
/*++
Routine Description:
    This function searches the argument module looking for the requested exported function name.
Arguments:
    DllBase - Supplies the base address of the requested module.
    AnsiImageRoutineName - Supplies the ANSI routine name being searched for.
Return Value:
    The virtual address of the requested routine or NULL if not found.
--*/
{
    USHORT OrdinalNumber;
    PULONG NameTableBase;
    PUSHORT NameOrdinalTableBase;
    PULONG Addr;
    LONG High;
    LONG Low;
    LONG Middle;
    LONG Result;
    ULONG ExportSize;
    PVOID FunctionAddress = 0;
    PIMAGE_EXPORT_DIRECTORY ExportDirectory;

    PAGED_CODE();

    __try
    {
        FunctionAddress = *(PVOID *)DllBase;
        FunctionAddress = 0;
    }
    __except(EXCEPTION_EXECUTE_HANDLER)
    {
        return FunctionAddress;
    }  

    //确保DllBase可以访问。否则蓝屏。
    ExportDirectory = (PIMAGE_EXPORT_DIRECTORY) RtlImageDirectoryEntryToData (DllBase, TRUE, IMAGE_DIRECTORY_ENTRY_EXPORT, &ExportSize);
    if (ExportDirectory == NULL) {
        return NULL;
    }
    
    NameTableBase = (PULONG)((PCHAR)DllBase + (ULONG)ExportDirectory->AddressOfNames);// Initialize the pointer to the array of RVA-based ansi export strings.    
    NameOrdinalTableBase = (PUSHORT)((PCHAR)DllBase + (ULONG)ExportDirectory->AddressOfNameOrdinals);// Initialize the pointer to the array of USHORT ordinal numbers.    
    Low = 0;
    Middle = 0;
    High = ExportDirectory->NumberOfNames - 1;

    while (High >= Low) // Lookup the desired name in the name table using a binary search.
    {        
        Middle = (Low + High) >> 1;// Compute the next probe index and compare the import name with the export name entry.
        Result = strcmp (AnsiImageRoutineName->Buffer, (PCHAR)DllBase + NameTableBase[Middle]);
        if (Result < 0) {
            High = Middle - 1;
        } else if (Result > 0) {
            Low = Middle + 1;
        } else {
            break;
        }
    }

    // If the high index is less than the low index, then a matching table entry was not found.
    // Otherwise, get the ordinal number from the ordinal table.
    if (High < Low) {
        return NULL;
    }

    OrdinalNumber = NameOrdinalTableBase[Middle];

    // If the OrdinalNumber is not within the Export Address Table,then this image does not implement the function.
    // Return not found.
    if ((ULONG)OrdinalNumber >= ExportDirectory->NumberOfFunctions) {
        return NULL;
    }

    // Index into the array of RVA export addresses by ordinal number.
    Addr = (PULONG)((PCHAR)DllBase + (ULONG)ExportDirectory->AddressOfFunctions);
    FunctionAddress = (PVOID)((PCHAR)DllBase + Addr[OrdinalNumber]);

    // Forwarders are not used by the kernel and HAL to each other.
    ASSERT ((FunctionAddress <= (PVOID)ExportDirectory) || (FunctionAddress >= (PVOID)((PCHAR)ExportDirectory + ExportSize)));

    return FunctionAddress;
}


PVOID get_module_image_base(__in UCHAR * name)
{
    NTSTATUS status = 0;
    ULONG  modulesSize;
    AUX_MODULE_EXTENDED_INFO * modules;
    ULONG  numberOfModules;
    ULONG i;
    PIMAGE_EXPORT_DIRECTORY pied = 0;
    PVOID ImageBase = 0;

    status = AuxKlibInitialize();
    if (!NT_SUCCESS( status )) 
    {
        KdPrint(( "AuxKlibInitialize fail %d\n", status));
        return ImageBase;
    }    
    
    status = AuxKlibQueryModuleInformation(&modulesSize, sizeof(AUX_MODULE_EXTENDED_INFO), NULL);// Get the required array size.
    if (!NT_SUCCESS(status) || modulesSize == 0) {
        return ImageBase;
    }
    
    numberOfModules = modulesSize / sizeof(AUX_MODULE_EXTENDED_INFO);// Calculate the number of modules.
        
    modules = (AUX_MODULE_EXTENDED_INFO*) ExAllocatePoolWithTag(PagedPool, modulesSize, TAG);// Allocate memory to receive data.
    if (modules == NULL) {
        status = STATUS_INSUFFICIENT_RESOURCES;
        return ImageBase;
    }
    RtlZeroMemory(modules, modulesSize);
    
    status = AuxKlibQueryModuleInformation(&modulesSize, sizeof(AUX_MODULE_EXTENDED_INFO), modules);// Obtain the module information.
    if (!NT_SUCCESS(status)) {
        ExFreePoolWithTag(modules,TAG);
        return ImageBase;
    }

    for (i = 0;i<numberOfModules;i++)
    {
        UCHAR * pfilename = modules[i].FullPathName + modules[i].FileNameOffset;

        if (_stricmp(pfilename, name) == 0)
        {
            ImageBase = modules[i].BasicInfo.ImageBase;
            break;
        }
    }

    ExFreePoolWithTag(modules,TAG);

    return ImageBase;
} 


DRIVER_INITIALIZE DriverEntry;
NTSTATUS DriverEntry( __in struct _DRIVER_OBJECT  * DriverObject, __in PUNICODE_STRING  RegistryPath)
{
    NTSTATUS status = STATUS_UNSUCCESSFUL;  
    PVOID ImageBase = 0;
    PVOID FunctionAddress;
    //ANSI_STRING EngCreateBitmap  = RTL_CONSTANT_STRING("EngCreateBitmap");//NT开头的函数竟然一个也没有导出。
    ANSI_STRING EngCreateBitmap  = RTL_CONSTANT_STRING("ArcFilterDprIndicateReceive");

    KdBreakPoint();

    DriverObject->DriverUnload = Unload;    

    //ImageBase = get_module_image_base("WIN32k.sys");//注意这个的环境:有的线程不可以访问。不然地址不可访问。
    ImageBase = get_module_image_base("ndis.sys");
    if (ImageBase == 0)
    {
        return status; 
    }

    FunctionAddress = MiFindExportedRoutineByName (ImageBase, &EngCreateBitmap);
    if (FunctionAddress == 0)
    {
        return status; 
    }

    /*
    验证下:
    1: kd> dd FunctionAddress L1
    f88f6c78  ba5552cd
    1: kd> u ba5552cd
    NDIS!ArcFilterDprIndicateReceive:
    ba5552cd 8bff            mov     edi,edi
    ba5552cf 55              push    ebp
    ba5552d0 8bec            mov     ebp,esp
    ba5552d2 83ec24          sub     esp,24h
    ba5552d5 a11c6353ba      mov     eax,dword ptr [NDIS!__security_cookie (ba53631c)]
    ba5552da 53              push    ebx
    ba5552db 8b5d10          mov     ebx,dword ptr [ebp+10h]
    ba5552de 56              push    esi
    */

    return status;    
} 

没有评论:

发表评论