Экспортируемая функция перенаправлена ​​на себя?

Сегодня я столкнулся с чрезвычайно странной проблемой, когда возился с анализом структуры файлов исполняемого файла Windows Portable. В частности, в таблице экспорта.

Я обнаружил, что получаю переполнение стека (так что это кажется наиболее подходящей платой контроля качества) при попытке разрешить адрес функции экспортируемой функции в DLL.

Я написал свою собственную версию GetProcAddress который выполняет разбор вручную, а не вызывает существующий GetProcAddress метод. Пожалуйста, не говорите мне, чтобы использовать существующие GetProcAddress метод, он не подходит для моей текущей ситуации, и я хочу чему-то научиться из этого.

Для большинства ситуаций, с которыми я сталкиваюсь, моя версия работает превосходно и не имеет проблем. Однако функция была протестирована с DLL API-MS-Win-Core-ProcessThreads-L1-1-0.dll (как часть рекурсивного анализа Kernel32.dll) и это когда происходит StackOverflow.

Я сузил его до следующей функции, экспортируемой из API-MS-Win-Core-ProcessThreads-L1-1-0.dll:

CreateRemoteThreadEx

Теперь эта экспортированная функция на самом деле является перенаправленным экспортом. Обычно это не беспокоит; Я написал свою функцию так, чтобы она обрабатывала перенаправленный экспорт. Однако эта функция передается

api-ms-win-core-processthreads-l1-1-0.CreateRemoteThreadEx

Кто-нибудь видит проблему здесь? Проходя через код, мой GetProcAddress функция затем вызывает LoadLibrary на api-ms-win-core-processthreads-l1-1-0 а затем пытается рекурсивно искать CreateRemoteThreadEx, Однако на следующей итерации CreateRemoteThreadEx функция снова перенаправлена ​​… в

api-ms-win-core-processthreads-l1-1-0.CreateRemoteThreadEx

И так начинается StackOverflow. После более подробного расследования я обнаружил, что результат звонка

LoadLibraryA("api-ms-win-core-processthreads-l1-1-0");

Возвращает тот же результат, что и

LoadLibraryA("kernel32.dll");

Я в тупике.

Вот мой текущий код:

#include <Windows.h>

#define MKPTR(p1,p2) ((DWORD_PTR)(p1) + (DWORD_PTR)(p2))

INT LookupExport(IMAGE_DOS_HEADER* pDosHd, DWORD* pNames, DWORD nNames, LPCSTR lpProcName)
{
// Do a binary search on the name pointer table
INT start = 0,
index = -1,
middle = -1,
end = nNames - 1,
cmp = 0;

CHAR *pName;

while (start <= end && index == -1)
{
middle = (start + end) >> 1;
pName = (CHAR*)MKPTR(pDosHd, pNames[middle]);

if ((cmp = strcmp(pName, lpProcName)) == 0)
index = middle; // found
else if (cmp < 0)
start = middle + 1;
else
end = middle;
}

return index;
}

FARPROC InternalGetProcAddress(HMODULE hModule, LPCSTR lpProcName)
{
BOOL ordinalSearch = HIWORD(lpProcName) == 0;
WORD ordinal = 0;
IMAGE_DOS_HEADER *pDosHd = (IMAGE_DOS_HEADER*)hModule;

if (pDosHd->e_magic != IMAGE_DOS_SIGNATURE)
return NULL;

IMAGE_NT_HEADERS *pNtHd = (IMAGE_NT_HEADERS*)MKPTR(pDosHd, pDosHd->e_lfanew);
if (pNtHd->Signature != IMAGE_NT_SIGNATURE)
return NULL;

IMAGE_DATA_DIRECTORY directory = pNtHd->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
if (directory.Size == 0 || directory.VirtualAddress == 0)
return NULL;

IMAGE_EXPORT_DIRECTORY *pExports = (IMAGE_EXPORT_DIRECTORY*)MKPTR(pDosHd, directory.VirtualAddress);
if (!ordinalSearch)
{
INT index = LookupExport(pDosHd, (DWORD*)MKPTR(pDosHd, pExports->AddressOfNames), pExports->NumberOfNames, lpProcName);
if (index == -1)
return NULL;
ordinal = ((WORD*)MKPTR(pDosHd, pExports->AddressOfNameOrdinals))[index];
}
else
{
ordinal = LOWORD(lpProcName);
}

INT delta = pExports->Base - 1;
DWORD dwAddress = ((DWORD*)MKPTR(pDosHd, pExports->AddressOfFunctions))[ordinal - delta];
// Check whether forwarded:
if (dwAddress >= directory.VirtualAddress && dwAddress < (directory.VirtualAddress + directory.Size))
{
CHAR pForward[256];
strcpy(pForward, (CHAR*)MKPTR(pDosHd, dwAddress));
CHAR *pFunction = strchr(pForward, '.');
if (pFunction == NULL)
return NULL;

// break into seperate parts and recurse
*pFunction++ = 0;
return InternalGetProcAddress(LoadLibraryA(pForward), pFunction);
}

return (FARPROC)MKPTR(hModule, dwAddress);
}

Любое понимание будет с благодарностью.

5

Решение

Хорошо, после следования совету @ sergmat, я посмотрел документацию по API Set (найдено Вот для всех, кто заинтересован). Теперь я изменил свой код GetProcAddress, чтобы выполнить наивный поиск таблицы Api Set.

#include <Windows.h>

#define MKPTR(p1,p2) ((DWORD_PTR)(p1) + (DWORD_PTR)(p2))

typedef struct _stripped_peb32 {
BYTE    unused1[0x038];
PVOID   ApiSet;
BYTE    unused2[0x1AC];
} PEB32;

typedef struct _stripped_peb64 {
BYTE    unused1[0x068];
PVOID   ApiSet;
BYTE    unused2[0x23C];
} PEB64;

typedef struct _PROCESS_BASIC_INFORMATION {
PVOID       Reserved1;
LPVOID      PebBaseAddress;
PVOID       Reserved2[2];
ULONG_PTR   UniqueProcessId;
PVOID       Reserved3;
} PROCESS_BASIC_INFORMATION;

typedef struct _api_set_host {
DWORD           ImportModuleName;
WORD            ImportModuleNameLength;
DWORD           HostModuleName;
WORD            HostModuleNameLength;
} API_SET_HOST;

typedef struct _api_set_host_descriptor {
DWORD           NumberOfHosts;
API_SET_HOST    Hosts[1];
} API_SET_HOST_DESCRIPTOR;

typedef struct _api_set_entry {
DWORD           Name;
WORD            NameLength;
DWORD           HostDescriptor;
} API_SET_ENTRY;

typedef struct _api_set_header {
DWORD           unknown1;
DWORD           NumberOfEntries;
API_SET_ENTRY   Entries[1];
} API_SET_HEADER;

typedef NTSTATUS (__stdcall *fnNtQueryInformationProcess)(HANDLE ProcessHandle, DWORD ProcessInformationClass, PVOID ProcessInformation, ULONG ProcessInformationLength, PULONG ReturnLength);

API_SET_HEADER *GetApiSetHeader()
{
fnNtQueryInformationProcess NtQueryInformationProcess = (fnNtQueryInformationProcess)GetProcAddress(LoadLibraryW(L"ntdll.dll"), "NtQueryInformationProcess");
if (!NtQueryInformationProcess)
return NULL;

PROCESS_BASIC_INFORMATION info;
if (NtQueryInformationProcess(GetCurrentProcess(), 0, &info, sizeof(info), NULL) != S_OK)
return NULL;

#if defined(_WIN32)
return (API_SET_HEADER*)(((PEB32*)info.PebBaseAddress)->ApiSet);
#elif defined(_WIN64)
return (API_SET_HEADER*)(((PEB64*)info.PebBaseAddress)->ApiSet);
#else
return NULL; // unsupported architecture
#endif
}

HMODULE ResolveImportMap(LPCSTR lpModuleName)
{
API_SET_HEADER *pHeader = GetApiSetHeader();
if (pHeader == NULL)
return NULL;
API_SET_ENTRY *pEntry = pHeader->Entries;
API_SET_HOST_DESCRIPTOR* pDescriptor;
wchar_t module[128];

// First, normalize the LPCSTR, the API Set table doesn't have the API- prefix
if (strnicmp("api-", lpModuleName, 4) == 0)
lpModuleName += 4;

// Next convert the LPCSTR to a unicode string for comparison and remove the extension (if found)
mbstowcs(module, lpModuleName, sizeof(module) / sizeof(wchar_t));
wchar_t *dot = wcsrchr(module, L'.');
if (dot) *dot = L'\0';

// Begin the lookup:
// todo: implement a case-insensitive binary search, not much to be gained for the effort IMO as there's
//          only 35 entries in the current version of Windows 7, but the option is there for performance nuts.
for(unsigned long i = 0; i < pHeader->NumberOfEntries; ++i, ++pEntry)
{
// Check the top-level host map
if (wcsnicmp(module, (const wchar_t*)MKPTR(pHeader, pEntry->Name), pEntry->NameLength) == 0)
{
pDescriptor = (API_SET_HOST_DESCRIPTOR*)MKPTR(pHeader, pEntry->HostDescriptor);
// iterate backwards through the hosts to find the most important one (I think this is naive)
for(unsigned long j = pDescriptor->NumberOfHosts; j > 0; --j)
{
if (pDescriptor->Hosts[j - 1].HostModuleNameLength)
{
memcpy(module, (const void*)MKPTR(pHeader, pDescriptor->Hosts[j - 1].HostModuleName), pDescriptor->Hosts[j - 1].HostModuleNameLength);
module[pDescriptor->Hosts[j - 1].HostModuleNameLength / sizeof(wchar_t)] = L'\0';
return GetModuleHandleW(module); // All the modules should already be loaded, so use GetModuleHandle rather than LoadLibrary
}
}
}
}

return NULL;
}

INT LookupExport(IMAGE_DOS_HEADER* pDosHd, DWORD* pNames, DWORD nNames, LPCSTR lpProcName)
{
// Do a binary search on the name pointer table
INT start = 0,
index = -1,
middle = -1,
end = nNames - 1,
cmp = 0;

CHAR *pName;

while (start <= end && index == -1)
{
middle = (start + end) >> 1;
pName = (CHAR*)MKPTR(pDosHd, pNames[middle]);

if ((cmp = strcmp(pName, lpProcName)) == 0)
index = middle;
else if (cmp < 0)
start = middle + 1;
else
end = middle;
}

return index;
}

FARPROC InternalGetProcAddress(HMODULE hModule, LPCSTR lpProcName)
{
if (hModule == NULL)
return NULL;

BOOL ordinalSearch = HIWORD(lpProcName) == 0;
WORD ordinal = 0;
IMAGE_DOS_HEADER *pDosHd = (IMAGE_DOS_HEADER*)hModule;

if (pDosHd->e_magic != IMAGE_DOS_SIGNATURE)
return NULL;

IMAGE_NT_HEADERS *pNtHd = (IMAGE_NT_HEADERS*)MKPTR(pDosHd, pDosHd->e_lfanew);
if (pNtHd->Signature != IMAGE_NT_SIGNATURE)
return NULL;

IMAGE_DATA_DIRECTORY directory = pNtHd->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
if (directory.Size == 0 || directory.VirtualAddress == 0)
return NULL;

IMAGE_EXPORT_DIRECTORY *pExports = (IMAGE_EXPORT_DIRECTORY*)MKPTR(pDosHd, directory.VirtualAddress);
if (!ordinalSearch)
{
INT index = LookupExport(pDosHd, (DWORD*)MKPTR(pDosHd, pExports->AddressOfNames), pExports->NumberOfNames, lpProcName);
if (index == -1)
return NULL;
ordinal = ((WORD*)MKPTR(pDosHd, pExports->AddressOfNameOrdinals))[index];
}
else
{
ordinal = LOWORD(lpProcName);
}

INT ordbase = pExports->Base - 1;
DWORD dwAddress = ((DWORD*)MKPTR(pDosHd, pExports->AddressOfFunctions))[ordinal - ordbase];
// Check whether forwarded:
if (dwAddress >= directory.VirtualAddress && dwAddress < (directory.VirtualAddress + directory.Size))
{
CHAR pForward[256];
strcpy(pForward, (CHAR*)MKPTR(pDosHd, dwAddress));
CHAR *pFunction = strchr(pForward, '.');
if (pFunction == NULL)
return NULL;

// break into seperate parts and recurse
*pFunction++ = 0;
// check if ordinal-forwarded
if (*pFunction == '#')
pFunction = (PSTR)(unsigned short)(atoi(++pFunction));

HMODULE hDestination = LoadLibraryA(pForward);

// detect an infinite loop, the forward declaration requests the same module handle with
// the same function lookup, this could be an Api Set (Windows7+)
if (hDestination == hModule && (ordinalSearch ? LOWORD(pFunction) == LOWORD(lpProcName) : strcmp(pFunction, lpProcName) == 0))
hDestination = ResolveImportMap(pForward); // ResolveImportMap will return NULL if not an API Set and so avoid infinite recursion

return InternalGetProcAddress(hDestination, pFunction);
}

return (FARPROC)MKPTR(hModule, dwAddress);
}
1

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

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

По вопросам рекламы [email protected]