遍历 IMAGE_EXPORT_DIRECTORY 结构的 AddressOfNames 成员时出现问题

Problems iterating through AddressOfNames member of IMAGE_EXPORT_DIRECTORY structure

我在 kernel32.dll 中枚举函数名称时遇到问题。我检索了它的 IMAGE_EXPORT_DIRECTORY 结构并存储了指向每个函数名称的 char 数组的指针数组:char** name_table = (char**)(image+pExp_dir->AddressOfNames); //pExp_dir is a pointer to the IMAGE_EXPORT_DIRECTORY structure。我现在正在尝试遍历函数名称并将它们与包含我需要的 RVA 的函数名称的字符串相匹配。

for(int i=0;i<pExp_dir->NumberOfNames;i++) //until i is 1 less than how many names there are to iterate through elements
    {
        printf("%s ", (char*)(image+(DWORD)(uintptr_t)name_table[i])); //print the name of each function iterated through, I went back and read through these names and didn't see GetProcAddress anywhere
        if(proc_name == image+(DWORD)(uintptr_t)name_table[i]) //if(strcmp(proc_name, (const char*)image+(DWORD)(intptr_t)name_table[i]) == 0) //Is it the function we're looking for?
        {
            address = (DWORD)(uintptr_t)func_table[ord_table[i]];//If so convert the address of the function into a DWORD(hexadecimal)
            system("pause"); 
            system("CLS"); //Clear the screen
            return address; //return the address of the function
        }

但是如果它没有找到该函数,那么程序就会崩溃。在 DBG 调试器中查看内存转储后,我可以看到 name_tables 包含所有函数名称,包括我正在寻找的函数,但我的程序似乎跳过了几个元素,即使我正在遍历它一次一个元素。用户 stijn suggested 我不应该使用 intptr_tchar* 转换为 DWORD 以用于指针运算。所以我的问题实际上是关于迭代 name_table 的正确方法,因为这似乎是一个指针算术问题。下面是获取文件图片的函数和实际获取RVA的函数:

void* GetFileImage(char path[]) //Get maps the image of the file into memory and returns the beginning virtual address of the file in memory
{
    HANDLE hFile = CreateFile(path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL);//Get a handle to the dll with read rights
    if(hFile == INVALID_HANDLE_VALUE){printf("Error getting file handle: %d", (int)GetLastError());return NULL;} //Check whether or not CreateFile succeeded

    HANDLE file_map = CreateFileMapping(hFile, NULL, PAGE_READONLY|SEC_IMAGE, 0, 0, "KernelMap"); //Create file map
    if(file_map == INVALID_HANDLE_VALUE){printf("Error mapping file: %d", (int)GetLastError());return NULL;} //Did it succeed

    LPVOID file_image = MapViewOfFile(file_map, FILE_MAP_READ, 0, 0, 0); //Map it into the virtual address space of my program
    if(file_image == 0){printf("Error getting mapped view: %d", (int)GetLastError());return NULL;} //Did it succeed

    return file_image; //return the base address of the image
}

 DWORD RVAddress(char* image, const char* proc_name) //Gets the relative virtual address of the function and returns a DWORD to be cast to void*.
{
    DWORD address = 0xFFFFFFFF;

    PIMAGE_DOS_HEADER pDos_hdr = (PIMAGE_DOS_HEADER)image; //Get dos header
    PIMAGE_NT_HEADERS pNt_hdr = (PIMAGE_NT_HEADERS)(image+pDos_hdr->e_lfanew); //Get PE header by using the offset in dos header + the base address of the file image
    IMAGE_OPTIONAL_HEADER opt_hdr = pNt_hdr->OptionalHeader; //Get the optional header
    IMAGE_DATA_DIRECTORY exp_entry = opt_hdr.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
    PIMAGE_EXPORT_DIRECTORY pExp_dir = (PIMAGE_EXPORT_DIRECTORY)(image+exp_entry.VirtualAddress); //Get a pointer to the export directory

    void** func_table = (void**)(image+pExp_dir->AddressOfFunctions); //Get an array of pointers to the functions
    WORD* ord_table = (WORD*)(image+pExp_dir->AddressOfNameOrdinals); //Get an array of ordinals
    char** name_table = (char**)(image+pExp_dir->AddressOfNames); //Get an array of function names

    for(int i=0;i<pExp_dir->NumberOfNames;i++) //until i is 1 less than how many names there are to iterate through elements
    {
        printf("%s ", (char*)(image+(DWORD)(uintptr_t)name_table[i])); //print the name of each function iterated through, I went back and read through these names and didn't see GetProcAddress anywhere
        if(proc_name == image+(DWORD)(uintptr_t)name_table[i]) //if(strcmp(proc_name, (const char*)image+(DWORD)(intptr_t)name_table[i]) == 0) //Is it the function we're looking for?
        {
            address = (DWORD)(uintptr_t)func_table[ord_table[i]];//If so convert the address of the function into a DWORD(hexadecimal)
            system("pause");
            system("CLS"); //Clear the screen
            return address; //return the address of the function
        }
    }

    return (DWORD)0; //Other wise return 0
}

如有任何帮助,我们将不胜感激!

Docs (Section 6.3)接下来说一下AddressOfNamestable

The Export Name Pointer Table is an array of addresses (RVAs) into the Export Name Table. The pointers are 32 bits each and are relative to the Image Base. The pointers are ordered lexically to allow binary searches.

关于AddressOfFunctions

Each entry in the Export Address Table is a field that uses one of two formats, ... If the address specified is not within the export section (as defined by the address and length indicated in the Optional Header), the field is an Export RVA: an actual address in code or data. Otherwise, the field is a Forwarder RVA, which names a symbol in another DLL.

您的变量不是 void**char**,但实际上都是 DWORD*,因为这些 table 持有 RVA。尝试下一个代码:

    DWORD* func_table = (DWORD*)(image+pExp_dir->AddressOfFunctions); //Get an array of pointers to the functions
    WORD* ord_table = (WORD*)(image+pExp_dir->AddressOfNameOrdinals); //Get an array of ordinals
    DWORD* name_table = (DWORD*)(image+pExp_dir->AddressOfNames); //Get an array of function names

    for(int i=0;i<pExp_dir->NumberOfNames;i++) //until i is 1 less than how many names there are to iterate through elements
    {
        printf("%s ", (char*)(image+name_table[i])); //print the name of each function iterated through, I went back and read through these names and didn't see GetProcAddress anywhere
        if(strcmp(proc_name, (const char*)(image+name_table[i])) == 0) //Is it the function we're looking for?
        {
            // TODO should we distinguish between normal and forwarded exports?
            WORD ordinal_base = 1; // TODO read it from export directory
            address = func_table[ord_table[i] - ordinal_base];//If so convert the address of the function into a DWORD(hexadecimal)
            system("pause");
            system("CLS"); //Clear the screen
            return address; //return the address of the function
        }
    }

因此,当您的代码在 32 位机器上运行时,无论 var 类型不正确,它都应该可以正常工作,但如果您在 64 位机器上 - 指针比 DWORD 长两倍,并且它将跳过奇数条目在 table 秒内超出数组边界,这可能会导致崩溃。

P.S。名称 table 是有序的,因此可以使用二进制搜索。