3

http://lastfrag.com/hotpatching-and-inline-hooking-explained/から、

Q1) コードは上位メモリから下位メモリへ、またはその逆に進みますか?

Q2) さらに重要なことは、置換オフセットの計算中に、関数プリアンブルを差し引かなければならないのはなぜですか? オフセットが命令の先頭ではなく末尾から始まるためですか?

DWORD ReplacementAddressOffset = ReplacementAddress - OriginalAddress - 5;

完全なコード:

void HookAPI(wchar_t *Module, char *API, DWORD Function)
{
    HMODULE hModule = LoadLibrary(Module);
    DWORD OriginalAddress = (DWORD)GetProcAddress(hModule, API);
    DWORD ReplacementAddress = (DWORD)Function;
    DWORD ReplacementAddressOffset = ReplacementAddress - OriginalAddress - 5;
    LPBYTE pOriginalAddress = (LPBYTE)OriginalAddress;
    LPBYTE pReplacementAddressOffset = (LPBYTE)(&ReplacementAddressOffset);

    DWORD OldProtect = 0;
    DWORD NewProtect = PAGE_EXECUTE_READWRITE;

    VirtualProtect((PVOID)OriginalAddress, 5, NewProtect, &OldProtect);

    for (int i = 0; i < 5; i++)
        Store[i] = pOriginalAddress[i];

    pOriginalAddress[0] = (BYTE)0xE9;

    for (int i = 0; i < 4; i++)
        pOriginalAddress[i + 1] = pReplacementAddressOffset[i];

    VirtualProtect((PVOID)OriginalAddress, 5, OldProtect, &NewProtect);

    FlushInstructionCache(GetCurrentProcess(), NULL, NULL);

    FreeLibrary(hModule);
}

Q3) このコードでは、jmp 命令の相対アドレスが置き換えられています。relAddrSet は元の宛先へのポインターです。to は、新しい宛先へのポインターです。to アドレスの計算がわかりません。元の宛先を functionForHook + opcodeOffset に追加する必要があるのはなぜですか?

DWORD *relAddrSet = (DWORD *)(currentOpcode + 1);
DWORD_PTR to = (*relAddrSet) + ((DWORD_PTR)functionForHook + opcodeOffset);
*relAddrSet = (DWORD)(to - ((DWORD_PTR)originalFunction + opcodeOffset));
4

3 に答える 3

1

Q1) プログラムは、下位アドレスから上位アドレスへと実行されます (つまり、プログラム カウンターは、ジャンプ、呼び出し、または ret の場合を除き、各命令のサイズによって増加します)。しかし、私はおそらく質問の要点を見逃しています。

Q2) はい、x86 では、プログラム カウンターがジャンプ命令のサイズ (5 バイト) だけ増加した後にジャンプが実行されます。CPU がプログラム カウンターにジャンプ オフセットを加算してターゲット アドレスを計算するとき、プログラム カウンターは既に 5 増加しています。

Q3) このコードは非常に奇妙ですが、動作する可能性があります。*relAddrset には、最初に originalFunction へのジャンプ オフセットが含まれていると思います (つまり、*relAddSet==originalFunction-relativeOffset)。これが真の場合、最終的な結果は、*reladdrSet に functionFoHook へのジャンプ オフセットが含まれることになります。実際、最後の命令は次のようになります。

*relAddrSet=(originalFunction-relativeOffset)+functionForHook-originalFunction

== functionForHook-relativeOffset

于 2013-09-06T06:48:36.083 に答える
1

はい、相対アドレスは命令の後のオフセットです。そのため、5 を減算する必要があります。

しかし、私の意見では、相対ジャンプの考え方を忘れて、絶対ジャンプを試してみるべきです。
なんで ?はるかに簡単で、x86-64 と互換性があるためです (相対ジャンプは+/-2GBに制限されています)。

絶対ジャンプは (x64) です:

48 b8 ef cd ab 89 67 45 23 01   mov rax, 0x0123456789abcdef
ff e0                           jmp rax

そして x86 の場合:

b8 67 45 23 01   mov eax, 0x01234567
ff e0            jmp eax

変更されたコードは次のとおりです (ローダーは 5 バイトではなく 7 バイトになりました)。

void HookAPI(wchar_t *Module, char *API, DWORD Function)
{
    HMODULE hModule = LoadLibrary(Module);
    DWORD OriginalAddress = (DWORD)GetProcAddress(hModule, API);

    DWORD OldProtect = 0;
    DWORD NewProtect = PAGE_EXECUTE_READWRITE;

    VirtualProtect((PVOID)OriginalAddress, 7, NewProtect, &OldProtect);

    memcpy(Store, OriginalAddress, 7);

    memcpy(OriginalAddress, "\xb8\x00\x00\x00\x00\xff\xe0", 7);
    memcpy(OriginalAddress+1, &ReplacementAddress, sizeof(void*));

    VirtualProtect((PVOID)OriginalAddress, 7, OldProtect, &NewProtect);

    FlushInstructionCache(GetCurrentProcess(), NULL, NULL);

    FreeLibrary(hModule);
}

コードは x64 でも同じですが、次の命令のサイズに一致させるために、最初または最後に2 つのnop ( ) を追加する必要があるため、ローダーは(14 バイト)になります。90"\x48\xb8<8-bytes addr>\xff\xe0\x90\x90"

于 2013-09-05T19:52:40.250 に答える
0

はい、この質問を正しく理解していれば、コードは「順方向」に実行されます。分岐でない場合、1 つの命令が別の命令の後に実行されます。

相対ジャンプ (JMP、CALL) を行う命令は、次の命令の開始に対して相対的にジャンプします。そのため、差から命令の長さ (ここでは 5) を差し引く必要があります。

3 番目の質問にはお答えできません。コンテキストと、コードが何をすべきかを教えてください。

于 2013-09-05T19:49:38.770 に答える