Можно ли перемещать разделы PE-файлов в памяти относительно базового адреса при загрузке изображения?

Я пытаюсь получить доступ к задержанному адресу загруженной функции в 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 инструкция:

введите описание изображения здесь

Я получаю запись IAT по адресу 0x008AB070, который случается в 0x1B070 смещение байта от базового адреса (т.е. 0x008AB0700x890000 знак равно 0x1B070) вместо моего ожидаемого 0x7670 что я рассчитал выше.

Так что же я не так делаю в своих расчетах?

PS. PE структура заголовка ссылка 1 а также ссылка 2.

2

Решение

Я думаю, что я получил это работает. Я не уверен, что есть лучший способ оптимизировать GetPtrFromRVA однако вызовы функций могут поглотить некоторые тактовые частоты процессора для большего PE-файла.

Таким образом, краткий ответ на мой первоначальный вопрос — да, разделы 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);

А потом сам код:
(Обратите внимание, что существует также формат заголовка PE старого стиля, используемый Visual Studio 6.0, который я также должен был учитывать.)

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;
}
0

Другие решения

Других решений пока нет …

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector