我正在尝试访问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;
}
然后我运行我的第一个项目,它给了我qazxsw poi函数AT入口偏离qazxsw poi的基数是:
WTSOpenServerW
我可以用调试器验证:
TargetProc.exe
然后第二阶段是检查它。
因此,如果我在Visual Studio中运行我的Offset is 0x00007670
,我可以先得到它的基地址(恰好是):
TargetProc.exe
然后我可以进入0x890000
函数来查看其IAT条目的位置:
跳过那个跳转,它只在调试器版本中添加。
这就是它实际上从WTSOpenServerW
指令的IAT条目中读取函数的地址:
WTSOpenServerW
我在地址jmp
得到它的IAT条目,它恰好在字节偏离基地址(即
0x008AB070
- 0x1B070
= 0x008AB070
),而不是我上面计算的我预期的0x890000
。
那么我在计算中做错了什么?
PS。 PE头结构0x1B070
和0x7670
。
我想我搞定了。我不确定是否有更好的方法来优化reference 1函数调用,这可能会占用一些较大的PE文件的CPU时钟。
因此,对原始问题的简短回答是肯定的,当文件被映射执行时,PE文件部分可以相对于它们在PE文件中的位置重新定位在内存中。
这里有两种方法可以读取导入地址表(在我的情况下是延迟加载的DLL):来自图像文件和当前进程内存。 (还有第三种方法是通过读取其虚拟内存从另一个正在运行的进程中获取它.reference 2。在这种情况下,我的方法是将我的DLL注入进程并使用GetPtrFromRVA
方法。)
This guy shows how
然后代码本身:(注意,还有一种旧式PE头格式,由Visual Studio 6.0使用,我也必须考虑。)
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);
,这些是它们的C实现:
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;
}
}