さて、今日私は奇妙に遭遇しました。リモートプロセスから関数アドレスを取得するために、少し前に自分のバージョンのGetProcAddressを作成しました。私は明らかに、これに取り組むための最良の方法を見つけるために、PEアーキテクチャについて読むことにかなりの時間を費やしました。
PECOFF v8仕様(私が思うに、これは最新の公式仕様です)から、次の表記がありますExport Name Pointer Table
。
エクスポート名ポインターテーブルは、エクスポート名テーブルへのアドレス(RVA)の配列です。ポインタはそれぞれ32ビットで、イメージベースを基準にしています。ポインタは、バイナリ検索を可能にするために字句的に順序付けられています。
そのため、GetProcAddressのバージョンを作成するときに、これを考慮に入れました。明らかに、線形検索よりもバイナリ検索を使用して、たとえば... KERNEL32.dll(1300以上のエクスポートされた関数)でエクスポートテーブルをウォークすることで、効率が大幅に向上します。
これは、私が奇妙な問題に遭遇した今日までしばらくの間機能していました。Kernel32でエクスポートされた関数の一部は、実際には字句的に順序付けられていないようで、これが私のバイナリ検索をスローしていました。以下は、以下に投稿する関数を使用した、エクスポートされたDLLダンプからの抜粋です。
Ordinal: 810 Name: K32QueryWorkingSetEx
Ordinal: 811 Name: LCIDToLocaleName
Ordinal: 812 Name: LCMapStringA
Ordinal: 813 Name: LCMapStringEx
Ordinal: 814 Name: LCMapStringW
Ordinal: 815 Name: LZClose
Ordinal: 816 Name: LZCloseFile
Ordinal: 817 Name: LZCopy
Ordinal: 818 Name: LZCreateFileW
Ordinal: 819 Name: LZDone
Ordinal: 820 Name: LZInit
Ordinal: 821 Name: LZOpenFileA
Ordinal: 822 Name: LZOpenFileW
Ordinal: 823 Name: LZRead
Ordinal: 824 Name: LZSeek
Ordinal: 825 Name: LZStart
Ordinal: 826 Name: LeaveCriticalSection
Ordinal: 827 Name: LeaveCriticalSectionWhenCallbackReturns
Ordinal: 828 Name: LoadAppInitDlls
Ordinal: 829 Name: LoadLibraryA
Ordinal: 830 Name: LoadLibraryExA
ここで問題を見つけた人はいますか?エクスポートテーブルが字句的に順序付けられていると主張しているドキュメントにもかかわらず、LZReadはLeaveCriticalSectionの前にリストされています。
文字列を処理するとき、私は常に辞書式順序をアルファベット順と同義であると考えてきましたが、ここで間違っているのでしょうか、それともKernel32のエクスポートテーブルに奇妙な問題がありますか?
エクスポートをダンプするために使用される関数:
void DumpExports(PBYTE pBase)
{
freopen("B:\\PeDump.txt", "wb", stdout);
IMAGE_DOS_HEADER *pDosHd = (IMAGE_DOS_HEADER*)pBase;
IMAGE_NT_HEADERS *pNtHd = (IMAGE_NT_HEADERS*)(pBase + pDosHd->e_lfanew);
IMAGE_DATA_DIRECTORY expDir = pNtHd->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
if (expDir.Size)
{
IMAGE_EXPORT_DIRECTORY *pExpDir = (IMAGE_EXPORT_DIRECTORY*)(pBase + expDir.VirtualAddress);
WORD *pOrds = (WORD*)(pBase + pExpDir->AddressOfNameOrdinals);
DWORD *pNames = (DWORD*)(pBase + pExpDir->AddressOfNames);
for(unsigned long i = 0; i < pExpDir->NumberOfNames; i++, pOrds++, pNames++)
printf("Ordinal: %d\tName: %s\n", *pOrds, (char*)(pBase + *pNames));
}
else
{
printf("No functions are exported from this image.\n");
}
fflush(stdout);
freopen("CON", "w", stdout);
}
編集:私はばかです。もちろん、「Z」は「o」の前にあります。午前3時で、私の脳は機能していません。本当にごめんなさい。
編集編集:さて、私は完全に正気ではありません。問題の半分は、明らかにC#のstring.CompareTo拡張子が字句的に比較されないことでした。
例えば
"LoadLibraryW".CompareTo("LZRead");
「-1」を返します。これが私の混乱の原因でした。