2

我正在尝试访问Import Address Table任意进程中延迟加载的函数地址。

我的假设是这样的:

首先,我需要查看它相对于基地址在图像本身中的位置:

DWORD_PTR dwFuncOffset = get_IAT_entry_offset_for_imported_function(
    L"path-to\\TargetProc.exe", "WTSAPI32.dll", "WTSOpenServerW");
wprintf(L"Offset is 0x%p\n", dwFuncOffset);

这是 PE 标头中查找的一些缩写版本。我删除了大多数错误检查以使其可读:

#include <delayimp.h>
#include <Dbghelp.h>
#pragma comment(lib, "Dbghelp.lib")


PIMAGE_SECTION_HEADER getEnclosingSectionHeader(DWORD_PTR rva, PIMAGE_NT_HEADERS pNTHeader)
{
    PIMAGE_SECTION_HEADER section = IMAGE_FIRST_SECTION(pNTHeader);

    for (WORD i = 0 ; i < pNTHeader->FileHeader.NumberOfSections; i++, section++)
    {
        // Is the RVA within this section?
        if((rva >= section->VirtualAddress) && 
                (rva < (section->VirtualAddress + section->Misc.VirtualSize)))
        {
            return section;
        }
    }

    return 0;
}

LPVOID GetPtrFromRVA(DWORD_PTR rva, PIMAGE_NT_HEADERS pNTHeader, DWORD_PTR imageBase)
{
    PIMAGE_SECTION_HEADER pSectionHdr = getEnclosingSectionHeader(rva, pNTHeader);
    if (!pSectionHdr)
        return 0;

    INT_PTR delta = (INT_PTR)(pSectionHdr->VirtualAddress - pSectionHdr->PointerToRawData);
    return (PVOID)(imageBase + rva - delta);
}


DWORD_PTR get_IAT_entry_offset_for_imported_function(LPCTSTR pImageFilePath, LPCSTR pImportDllName, LPCSTR pImportFuncName)
{
    HANDLE hFile = INVALID_HANDLE_VALUE;
    HANDLE hOpenFileMapping = NULL;
    const BYTE* lpBaseAddress = NULL;

    __try
    {
        hFile = CreateFile(pImageFilePath, 
                GENERIC_READ,
                FILE_SHARE_READ, NULL, OPEN_EXISTING,
                FILE_ATTRIBUTE_NORMAL, NULL);
        hOpenFileMapping = ::CreateFileMapping(hFile,
                NULL, PAGE_READONLY, 0, 0, NULL);

        lpBaseAddress = (const BYTE*)::MapViewOfFile(hOpenFileMapping,
                FILE_MAP_READ, 0, 0, 0);

        if(!lpBaseAddress)
            return 0;

        PIMAGE_NT_HEADERS pNtHeader = ::ImageNtHeader((PVOID)lpBaseAddress);

        _ASSERT(pNtHeader->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC);  //32-bit only here

        IMAGE_OPTIONAL_HEADER32* pIOH32 = &reinterpret_cast<PIMAGE_NT_HEADERS32>(pNtHeader)->OptionalHeader;
        PIMAGE_DATA_DIRECTORY pDataDirectories = pDataDirectories = pIOH32->DataDirectory;

        IMAGE_DATA_DIRECTORY* pDLoadTbl = &pDataDirectories[IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT];

        ImgDelayDescr *pImportDescriptor = (ImgDelayDescr*)GetPtrFromRVA(
            pDLoadTbl->VirtualAddress, pNtHeader, (DWORD_PTR)lpBaseAddress);

        //Go through all DLLs
        for(; pImportDescriptor->rvaIAT; pImportDescriptor++)
        {
            //Get DLL name
            LPCSTR pStrDllName = (LPCSTR)GetPtrFromRVA(pImportDescriptor->rvaDLLName,
                pNtHeader, (DWORD_PTR)lpBaseAddress);

            //Look for specific import dll
            if(lstrcmpiA(pStrDllName, pImportDllName) != 0)
                continue;

            IMAGE_THUNK_DATA32 *pITD_IAT = (IMAGE_THUNK_DATA32*)
                GetPtrFromRVA(pImportDescriptor->rvaIAT, pNtHeader, (DWORD_PTR)lpBaseAddress);

            IMAGE_THUNK_DATA32 *pITD_INT = (IMAGE_THUNK_DATA32*)
                GetPtrFromRVA(pImportDescriptor->rvaINT, pNtHeader, (DWORD_PTR)lpBaseAddress);

            //Go through all imported functions from this DLL
            for(; pITD_INT->u1.AddressOfData != 0; pITD_IAT++, pITD_INT++)
            {
                if(IMAGE_SNAP_BY_ORDINAL32(pITD_INT->u1.Ordinal))
                    continue;

                IMAGE_IMPORT_BY_NAME* pIIBY = (IMAGE_IMPORT_BY_NAME*)
                    GetPtrFromRVA(pITD_INT->u1.AddressOfData, pNtHeader, (DWORD_PTR)lpBaseAddress);
                if(!pIIBY)
                    continue;

                //Pick only specific imported function
                if(lstrcmpiA((LPCSTR)pIIBY->Name, pImportFuncName) != 0)
                    continue;

                //Get this function's offset in IAT relative to base address
                return (DWORD_PTR)pITD_IAT - (DWORD_PTR)lpBaseAddress;
            }
        }
    }
    __finally
    {
        ::UnmapViewOfFile(lpBaseAddress);
        ::CloseHandle(hOpenFileMapping);
        ::CloseHandle(hFile);
    }

    return 0;   //failed
}

然后我将其构建TargetProc.exe为一个简单的控制台项目,并WTSAPI32.dll设置延迟加载:

在此处输入图像描述

TargetProc.exe只有这个代码:

#include "stdafx.h"
#include <Windows.h>

#include <Wtsapi32.h>
#pragma comment(lib, "Wtsapi32.lib")


int _tmain(int argc, _TCHAR* argv[])
{
    //Get base address for this image
    void* pBaseAddr = (void*)::GetModuleHandle(NULL);

    ::WTSOpenServerW(NULL);     //Set up for delayed loading

    return 0;
}

然后我运行我的第一个项目,它为我提供了该WTSOpenServerW函数的 IAT 条目偏移量,TargetProc.exe即:

Offset is 0x00007670

我可以用调试器验证:

在此处输入图像描述


然后第二阶段是检查它。

所以如果我TargetProc.exe在 Visual Studio 中运行我的,我可以首先得到它的基地址(恰好是0x890000):

在此处输入图像描述

然后我可以单步执行该WTSOpenServerW函数以查看其 IAT 条目的位置:

在此处输入图像描述

跳过那个跳转,它只在调试器构建中添加到这里。

这就是它实际从指令WTSOpenServerW的 IAT 条目中读取函数地址的地方:jmp

在此处输入图像描述

我在 address 获得了它的 IAT 条目0x008AB070,它恰好位于0x1B070与基地址(即0x008AB070- 0x890000= 0x1B070)的字节偏移处,而不是我在上面计算的预期0x7670

那么我在计算中做错了什么?

PS。PE头结构参考1参考2

4

1 回答 1

0

我想我得到了它的工作。我不确定是否有更好的方法来优化GetPtrFromRVA函数调用,这可能会占用更大的 PE 文件的一些 CPU 时钟。

所以对我最初的问题的简短回答是肯定的,当文件被映射执行时,PE 文件部分可以根据它们在 PE 文件中的位置重新定位在内存中。

这里有两种读取导入地址表的方法(在我的例子中是延迟加载的 DLL):从图像文件和从当前进程内存。(还有第三种方法是通过读取另一个正在运行的进程的虚拟内存来获取它。这个人展示了如何。在这种情况下,我的方法是将我的 DLL 注入一个进程并使用parsePEheader_DelayLoad_FromMem()方法。)

wprintf(L"Memory:----------------------\n");
parsePEheader_DelayLoad_FromMem((void*)::GetModuleHandle(NULL), TRUE);

wprintf(L"File:----------------------\n");
WCHAR buff[MAX_PATH];
::GetModuleFileName(NULL, buff, _countof(buff));
parsePEheader_DelayLoad_FromFile(buff);

然后是代码本身:(请注意,Visual Studio 6.0 使用的还有一种旧式 PE 标头格式,我也必须考虑这一点。)

BOOL parsePEheader_DelayLoad_FromFile(LPCTSTR pImageFilePath)
{
    HANDLE hFile = INVALID_HANDLE_VALUE;
    HANDLE hOpenFileMapping = NULL;
    void* lpBaseAddress = NULL;

    __try
    {
        hFile = CreateFile(pImageFilePath, 
                GENERIC_READ,
                FILE_SHARE_READ, NULL, OPEN_EXISTING,
                FILE_ATTRIBUTE_NORMAL, NULL);
        hOpenFileMapping = ::CreateFileMapping(hFile,
                NULL, PAGE_READONLY, 0, 0, NULL);

        lpBaseAddress = (void*)::MapViewOfFile(hOpenFileMapping,
                FILE_MAP_READ, 0, 0, 0);

        if(!lpBaseAddress)
            return FALSE;

        return parsePEheader_DelayLoad_FromMem(lpBaseAddress, FALSE);
    }
    __finally
    {
        ::UnmapViewOfFile(lpBaseAddress);
        ::CloseHandle(hOpenFileMapping);
        ::CloseHandle(hFile);
    }

    return FALSE;
}


BOOL parsePEheader_DelayLoad_FromMem(void* lpBaseAddress, BOOL bImage)
{
    __try
    {
        PIMAGE_SECTION_HEADER pDummy;
        ULONG uiDummy;
        ImgDelayDescr *pImportDescriptor = (ImgDelayDescr*)ImageDirectoryEntryToDataEx(
            lpBaseAddress, bImage, IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT, &uiDummy, &pDummy);

        if(pImportDescriptor)
        {
            const char* base = (char*)lpBaseAddress;

            IMAGE_NT_HEADERS* pNtHeader = 
                (IMAGE_NT_HEADERS*)((BYTE*)lpBaseAddress + 
                ((IMAGE_DOS_HEADER*)lpBaseAddress)->e_lfanew);

            DWORD_PTR uiPreferredBase = pNtHeader->OptionalHeader.ImageBase;

            //Go thru all DLLs
            while(pImportDescriptor->rvaIAT)
            {
                PIMAGE_THUNK_DATA nameTable = 0;
                PIMAGE_THUNK_DATA funcTable = 0;

                const char* dllName;
                BOOL bOldStyle;

                if(pImportDescriptor->grAttrs & 1)
                {
                    bOldStyle = FALSE;

                    if(!bImage)
                    {
                        nameTable = (PIMAGE_THUNK_DATA)
                            GetPtrFromRVA(pImportDescriptor->rvaINT, pNtHeader, (DWORD_PTR)lpBaseAddress);

                        funcTable = (PIMAGE_THUNK_DATA)
                            GetPtrFromRVA(pImportDescriptor->rvaIAT, pNtHeader, (DWORD_PTR)lpBaseAddress);

                        dllName = (const char*)
                            GetPtrFromRVA(pImportDescriptor->rvaDLLName, pNtHeader, (DWORD_PTR)lpBaseAddress);
                    }
                    else
                    {
                        nameTable = (PIMAGE_THUNK_DATA)(base + pImportDescriptor->rvaINT);
                        funcTable = (PIMAGE_THUNK_DATA)(base + pImportDescriptor->rvaIAT);

                        dllName = base + pImportDescriptor->rvaDLLName;
                    }
                }
                else
                {
                    //Old style PE file, compiled with VC++ 6.0
                    //Cannot be 64-bit
                    _ASSERT(sizeof(DWORD_PTR) == sizeof(DWORD));
                    bOldStyle = TRUE;

                    if(!bImage)
                    {
                        nameTable = (PIMAGE_THUNK_DATA)
                            GetPtrFromRVA((DWORD_PTR)pImportDescriptor->rvaINT - uiPreferredBase, pNtHeader, (DWORD_PTR)lpBaseAddress);

                        funcTable = (PIMAGE_THUNK_DATA)
                            GetPtrFromRVA((DWORD_PTR)pImportDescriptor->rvaIAT - uiPreferredBase, pNtHeader, (DWORD_PTR)lpBaseAddress);

                        dllName = (LPCSTR)((DWORD_PTR)lpBaseAddress + (DWORD_PTR)pImportDescriptor->rvaDLLName - uiPreferredBase);
                    }
                    else
                    {
                        nameTable = (PIMAGE_THUNK_DATA)(base + pImportDescriptor->rvaINT - uiPreferredBase);
                        funcTable = (PIMAGE_THUNK_DATA)(base + pImportDescriptor->rvaIAT - uiPreferredBase);

                        dllName = base + pImportDescriptor->rvaDLLName - uiPreferredBase;
                    }
                }


                printf("Delay Loaded DLL: %s\n", dllName);

                while(nameTable->u1.AddressOfData)
                {
                    // check whether this is imported by ordinal only
                    if(nameTable->u1.Ordinal & IMAGE_ORDINAL_FLAG)
                    {
                        WORD ordinal = static_cast<WORD>(nameTable->u1.Ordinal & (~IMAGE_ORDINAL_FLAG));

                        printf("ordinal: %u", ordinal);
                    }
                    else
                    {
                        PIMAGE_IMPORT_BY_NAME funcNameStruct;
                        if(!bImage)
                        {
                            if(!bOldStyle)
                            {
                                funcNameStruct = (PIMAGE_IMPORT_BY_NAME)
                                    GetPtrFromRVA((DWORD_PTR)nameTable->u1.AddressOfData, pNtHeader, (DWORD_PTR)lpBaseAddress);
                            }
                            else
                            {
                                funcNameStruct = (PIMAGE_IMPORT_BY_NAME)
                                    GetPtrFromRVA((DWORD_PTR)nameTable->u1.AddressOfData - uiPreferredBase, pNtHeader, (DWORD_PTR)lpBaseAddress);
                            }
                        }
                        else
                        {
                            if(!bOldStyle)
                            {
                                funcNameStruct = (PIMAGE_IMPORT_BY_NAME)
                                    (base + (DWORD_PTR)nameTable->u1.AddressOfData);
                            }
                            else
                            {
                                funcNameStruct = (PIMAGE_IMPORT_BY_NAME)
                                    (base + (DWORD_PTR)nameTable->u1.AddressOfData - uiPreferredBase);
                            }
                        }

                        char* pFuncName = (char*)funcNameStruct->Name;

                        printf("func: %s", pFuncName);
                    }

                    printf("\tAddr: [0x%p]=0x%p\n", 
                        funcTable, *(DWORD_PTR*)funcTable);

                    nameTable++;
                    funcTable++;
                }

                pImportDescriptor++;
            }
        }
        else
        {
            wprintf(L"There's no Delay loaded DLLs\n");
        }

        return TRUE;
    }
    __except(1)
    {
        wprintf(L"\nException! Bad address, not a PE file, or something goofed up...\n");
        return FALSE;
    }
}

最后,如果您不想链接到Dbghelp.lib只是为了使用它的几个函数,这些是它们的 C 实现:

PVOID WINAPI ImageDirectoryEntryToDataEx( PVOID base, BOOLEAN image, USHORT dir, PULONG size, PIMAGE_SECTION_HEADER *section )
{
    const IMAGE_NT_HEADERS *nt;
    DWORD addr;

    *size = 0;
    if (section)
        *section = NULL;

    if (!(nt = RtlImageNtHeader( (HMODULE)base ))) 
        return NULL;

    if (dir >= nt->OptionalHeader.NumberOfRvaAndSizes) 
        return NULL;
    if (!(addr = nt->OptionalHeader.DataDirectory[dir].VirtualAddress)) 
        return NULL;

    *size = nt->OptionalHeader.DataDirectory[dir].Size;
    if (image || addr < nt->OptionalHeader.SizeOfHeaders) 
        return (char *)base + addr;

    return RtlImageRvaToVa( nt, (HMODULE)base, addr, section );
}

PVOID WINAPI RtlImageRvaToVa( const IMAGE_NT_HEADERS *nt, HMODULE module, DWORD rva, IMAGE_SECTION_HEADER **section )
{
    IMAGE_SECTION_HEADER *sec;

    if (section && *section)
    {
        sec = *section;
        if ((sec->VirtualAddress <= rva) && (sec->VirtualAddress + sec->SizeOfRawData > rva))
            goto lbl_use_it;
    }

    if (!(sec = RtlImageRvaToSection( nt, module, rva ))) 
        return NULL;

lbl_use_it:

    if (section) 
        *section = sec;

    return (char *)module + sec->PointerToRawData + (rva - sec->VirtualAddress);
}

PIMAGE_SECTION_HEADER WINAPI RtlImageRvaToSection( const IMAGE_NT_HEADERS *nt, HMODULE module, DWORD rva )
{
    int i;
    const IMAGE_SECTION_HEADER *sec;

    sec = (const IMAGE_SECTION_HEADER*)(
        (const char*)&nt->OptionalHeader + nt->FileHeader.SizeOfOptionalHeader);

    for (i = 0; i < nt->FileHeader.NumberOfSections; i++, sec++)
    {
        if ((sec->VirtualAddress <= rva) && (sec->VirtualAddress + sec->SizeOfRawData > rva))
            return (PIMAGE_SECTION_HEADER)sec;
    }

    return NULL;
}

PIMAGE_NT_HEADERS WINAPI RtlImageNtHeader(HMODULE hModule)
{
    IMAGE_NT_HEADERS *ret;

    __try
    {
        IMAGE_DOS_HEADER *dos = (IMAGE_DOS_HEADER *)hModule;

        ret = NULL;
        if (dos->e_magic == IMAGE_DOS_SIGNATURE)
        {
            ret = (IMAGE_NT_HEADERS *)((char *)dos + dos->e_lfanew);
            if (ret->Signature != IMAGE_NT_SIGNATURE) 
                ret = NULL;
        }
    }
    __except(1)
    {
        return NULL;
    }

    return ret;
}
于 2017-12-29T07:27:48.837 回答