今日、Windows Portable Executable ファイル構造の解析をいじっていると、非常に奇妙な問題に遭遇しました。具体的には、エクスポート テーブルで。
DLL 内のエクスポートされた関数の関数アドレスを解決しようとすると、スタック オーバーフローが発生しました (これが最も適切な QA ボードのように思えました)。
既存のメソッドGetProcAddress
を呼び出すのではなく、手動で解析を行う独自のバージョンを作成しました。GetProcAddress
既存の方法を使用するように言わないでください。GetProcAddress
それは私の現在の状況には適しておらず、これから何かを学びたいと思っています。
私が遭遇するほとんどの状況で、私のバージョンは見事に機能し、問題は発生しませんでした。API-MS-Win-Core-ProcessThreads-L1-1-0.dll
ただし、この関数は(の再帰的解析の一部として)という名前の 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
再帰的に lookup を試みます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);
}
どんな洞察も大歓迎です。