4

さて、今日私は奇妙に遭遇しました。リモートプロセスから関数アドレスを取得するために、少し前に自分のバージョンの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」を返します。これが私の混乱の原因でした。

4

2 に答える 2

5

LZReadアスキーを使用する前に辞書式順序でありLeaveCriticalSectionます。大文字と小文字を区別しないようにしてください。うまくいくようです。


ドキュメントに関する興味深い観察。

ポインタはそれぞれ32ビットです...バイナリ検索を可能にするために字句的に順序付けられています

(シンボル名ではなく)ポインターの二分探索を行う理由を理解するのは難しいですが、それが言われていることです。

于 2012-06-19T18:31:10.703 に答える
3

大文字の序数は、小文字の序数の前にあります。

于 2012-06-19T18:31:01.740 に答える