zoukankan      html  css  js  c++  java
  • DLL的导出函数重定向机制

    曾经,调试时跟进HeapAlloc,结果发现直接进入到ntdll的RtlAllocateHeap中,感到很有趣,就使用Dependency Walker查看kernel32.dll的导出函数,结果发现HeapAlloc的地址直接显示的就是NTDLL.RtlAllocateHeap。于是反汇编查看kernel32.dll文件,发现本以为是汇编代码的HeapAlloc的函数体就是字符串NTDLL.RtlAllocateHeap。

    想想以前也曾经自己实现过GetProcAddress,就是直接从导出表获取地址返回而已。照这样来看这样实现肯定是不完善的。这到底是如何设计的?查阅了一下《Windows PE权威指南》,在导出表一章没有找到相关说明。又看了一下微软的PE COFF格式文档,也没有找到相关信息。于是决定自己来研究一下。

    先仔细的看了一下导出表相关各结构体的定义,没有觉得有哪个字段标明某个函数是真实的代码还是重定向字符串。就自己来分析PE文件,在分析文件时注意到PE文件中所有重定向字符串和IMAGE_EXPORT_DIRECTORY结构的位置布局,感觉这些字符串应该是位于数据目录IMAGE_DIRECTORY_ENTRY_EXPORT包括的地址范围内,也就是说如果导出函数地址位于此范围,就是重定向函数,因为数据目录IMAGE_DIRECTORY_ENTRY_EXPORT不应该包含任何可执行代码的。

    于是照此思路编写代码测试了一下,结果与猜想一致。但是这毕竟只是推测,还没有找到官方证实。想到对导出函数重定向的支持代码Ldr里肯定会有,就从LdrGetProcedureAddress跟到LdrpSnapThunk去分析,最终在LdrpSnapThunk里找到了相关的代码,主要逻辑就是先从导出表中找到对应函数的地址,然后判断函数地址是否在数据目录IMAGE_DIRECTORY_ENTRY_EXPORT所指的地址范围内,如果不在则是真实地址,在此范围则需要进一步重定向。

    例如,用如下代码遍历kernel32.dll中的重定向导出函数:

    #include <tchar.h>
    #include <stdio.h>
    #include <stddef.h>
    #include <Windows.h>
    
    VOID ListRedirects(HMODULE hModule)
    {
        if(NULL != hModule)
        {
            PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)hModule;
            PIMAGE_OPTIONAL_HEADER pOptionalHeader = (PIMAGE_OPTIONAL_HEADER)((PBYTE)hModule + pDosHeader->e_lfanew + offsetof(IMAGE_NT_HEADERS, OptionalHeader));
            PIMAGE_EXPORT_DIRECTORY pExportDirectory = (PIMAGE_EXPORT_DIRECTORY)((PBYTE)hModule + pOptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
            LPCSTR lpstrLibraryName = (LPCSTR)hModule + pExportDirectory->Name;
            PDWORD aryAddressOfFunctions = (PDWORD)((PBYTE)hModule + pExportDirectory->AddressOfFunctions);
            PDWORD aryAddressOfNames = (PDWORD)((PBYTE)hModule + pExportDirectory->AddressOfNames);
            LPWORD aryAddressOfNameOrdinals = (LPWORD)((PBYTE)hModule + pExportDirectory->AddressOfNameOrdinals);
            DWORD dwIndex = 0;
            while(dwIndex < pExportDirectory->NumberOfNames)
            {
                PCSTR pstrFunctionName = (PCSTR)hModule + aryAddressOfNames[dwIndex];
                PVOID pFunctionAddress = (PBYTE)hModule + aryAddressOfFunctions[aryAddressOfNameOrdinals[dwIndex]];
                if((PBYTE)pFunctionAddress > (PBYTE)pExportDirectory && (PBYTE)pFunctionAddress < (PBYTE)pExportDirectory + pOptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size) //LdrpSnapThunk中也是如此判断
                {
                    printf("%s.%s -> %s
    ", lpstrLibraryName, pstrFunctionName, (PCSTR)pFunctionAddress);
                }
                ++dwIndex;
            }
        }
    }
    
    int _tmain(int argc, _TCHAR* argv[])
    {
        HMODULE hModule = LoadLibraryW(L"kernel32.dll");
        ListRedirects(hModule);
        return 0;
    }

    在VC中,使用#pragma预处理指令可以制作包含重定向函数的DLL:

    #pragma comment(linker,"/export:HeapAlloc=NTDLL.RtlAllocateHeap,@2000")
    #pragma comment(linker,"/export:HeapFree=NTDLL.RtlFreeHeap,@2001")
    #pragma comment(linker,"/export:HeapReAlloc=NTDLL.RtlReAllocateHeap,@2002")
    #pragma comment(linker,"/export:HeapSize=NTDLL.RtlSizeHeap,@2003")

    @后面跟的是导出序号。

    这样把一个导出函数重定向到另一个DLL中的某个函数,与通过导入表引入另一个DLL中的某个函数是不同的。重定向函数只要不被其他模块引入是不会被解析的,哪怕是重定向到一个根本不存在的DLL中或者指向某个根本不存在的函数,也不会影响当前模块的正常加载。直到这个函数真正被使用,Ldr才会真正去定位它的真实地址,因为重定向的目标函数不会出现在当前模块的导入表中。但是通过导入表引入的某个模块或者函数不存在的话,在加载时就会报错。

    看了一下Windows NT 4.0的kernel32.dll,HeapAlloc的重定向已经有了,应该是从NT最初版本(手头没有)堆分配函数就已经被转移到ntdll.dll中了。估计这种重定向技术在NT开发时作为向下兼容的一种手段在PE格式设计就被设计出来了。

    Ok. That's all.

  • 相关阅读:
    httpcontext in asp.net unit test
    initialize or clean up your unittest within .net unit test
    Load a script file in sencha, supports both asynchronous and synchronous approaches
    classes system in sencha touch
    ASP.NET MVC got 405 error on HTTP DELETE request
    how to run demo city bars using sencha architect
    sencha touch mvc
    sencha touch json store
    sencha touch jsonp
    51Nod 1344:走格子(贪心)
  • 原文地址:https://www.cnblogs.com/youlin/p/dll_export_function_redirect.html
Copyright © 2011-2022 走看看