1

我在应用程序中做了一个 DLL 挂钩。
像这样绕过一个函数:

typedef void (WINAPI *pUCPackets)(int a1, int a2, char* a3, int a4, int a5);
void WINAPI MyUCPackets(int a1, int a2, char* a3, int a4, int a5);
pUCPackets MyUC2Packets = (pUCPackets)(0x408050);

(...) some irrelevant code (...)

DetourTransactionBegin();
DetourUpdateThread(GetCurrentThread());
DetourAttach(&(PVOID&)MyUC2Packets, MyUCPackets);
if(DetourTransactionCommit() == NO_ERROR)
    cout << "[" << MyUCPackets << "] successfully detoured." << endl;

因此,我尝试通过以下方式显示绕行函数的参数内的值:

 void WINAPI MyUCPackets(int a1, int a2, char* a3, int a4, int a5)
 {
     printf( "%d ", a5 );
     printf("%d\n", a2);
     return MyUC2Packets(a1, a2, a3, a4, a5);
 }

但是,当函数被调用并且我显示参数时,应用程序就会崩溃。
但如果我只是离开这样的功能:

 void WINAPI MyUCPackets(int a1, int a2, char* a3, int a4, int a5)
 {
     //no prints whatsoever
     return MyUC2Packets(a1, a2, a3, a4, a5);
 }

它运行正常。为什么会这样?

OLLY 编码器:

Gate_00408050:                               ;<= Procedure Start

        MOV EDX,DWORD PTR SS:[ESP+0xC]
        PUSH EBP
        PUSH EDI
        MOV EDI,ECX
        XOR EBP,EBP
        MOV CL,BYTE PTR DS:[EDI+0x21C]
        TEST EDX,EDX
        JBE Gate_004080F0
        MOV EAX,DWORD PTR DS:[EDI+0x218]
        PUSH EBX
        PUSH ESI
        MOV DWORD PTR SS:[ESP+0x1C],EDX

Gate_00408074:

        MOV EDX,DWORD PTR SS:[ESP+0x14]
        DEC EAX
        TEST EAX,EAX
        MOV DL,BYTE PTR DS:[EDX]
        JLE Gate_004080A5
        LEA ESI,DWORD PTR DS:[EDI+EBP+0xEC7D]

Gate_00408086:

        MOV BL,BYTE PTR DS:[ESI+EAX]
        CMP BL,DL
        JA Gate_00408091
        SUB DL,BL
        JMP Gate_00408097

Gate_00408091:

        NOT BL
        INC BL
        ADD DL,BL

Gate_00408097:

        MOV BL,BYTE PTR DS:[ESI+EAX+0xFFFF8AD0]
        XOR DL,BL
        DEC EAX
        TEST EAX,EAX
        JG Gate_00408086

Gate_004080A5:

        MOV AL,BYTE PTR DS:[EDI+EBP+0xEC7D]
        CMP AL,DL
        JA Gate_004080B4
        SUB DL,AL
        JMP Gate_004080BA

Gate_004080B4:

        NOT AL
        INC AL
        ADD DL,AL

Gate_004080BA:

        MOV AL,BYTE PTR DS:[EDI+EBP+0x774D]
        MOV EBX,DWORD PTR SS:[ESP+0x14]
        XOR AL,DL
        MOV EDX,DWORD PTR SS:[ESP+0x18]
        XOR AL,CL
        MOV BYTE PTR DS:[EDX],AL
        XOR CL,AL
        MOV EAX,DWORD PTR DS:[EDI+0x218]
        ADD EBP,EAX
        INC EBX
        INC EDX
        MOV DWORD PTR SS:[ESP+0x14],EBX
        MOV DWORD PTR SS:[ESP+0x18],EDX
        MOV EDX,DWORD PTR SS:[ESP+0x1C]
        DEC EDX
        MOV DWORD PTR SS:[ESP+0x1C],EDX
        JNZ Gate_00408074
        POP ESI
        POP EBX

Gate_004080F0:

        POP EDI
        POP EBP
        RETN 0xC                             ;<= Procedure End
4

1 回答 1

3

的签名MyUC2Packets可能不正确。由于函数使用 stdcall 调用约定,因此需要在返回之前清理堆栈。如果您使用错误数量的参数调用这些函数之一,则堆栈指针在返回时将不正确。

删除打印语句时不会发生这种情况的原因是编译器可能会将转发调用优化为单个jmp指令。当包含打印语句时,detour 函数实际上有工作要做,并在返回之前将堆栈调整为不正确的值。如果MyUC2Packets需要 6 个参数但函数签名只需要 5 个参数,这将在任何时候无法优化 detour 函数时导致问题。

下面的代码通过模拟示例中的迂回设置来演示这一点。被挂钩的函数需要 4 个参数,但 detour 只需要 3 个参数。它模拟来自客户端的调用,该调用需要一个需要 4 个参数的函数。

#include <stdio.h>
#include <ios>
#pragma inline_depth(0)

typedef void (WINAPI *Function3)(int, int, int);
typedef void (WINAPI *Function4)(int, int, int, int);

void WINAPI FinalFunction(int x, int y, int z, int q);
void WINAPI DetourFunction(int x, int y, int z);
void WINAPI DetourFunctionPrint(int x, int y, int z);

Function3 callFinalFunction = reinterpret_cast<Function3>(FinalFunction);
Function4 callDetourFunction = reinterpret_cast<Function4>(DetourFunction);
Function4 callDetourFunctionPrint = reinterpret_cast<Function4>(DetourFunctionPrint);


void WINAPI FinalFunction(int x, int y, int z, int q)
{
    std::cout << x << " " << y << " " << z << " " << q << std::endl;
}

void WINAPI DetourFunction(int x, int y, int z)
{
    callFinalFunction(x, y, z); // Optimzed to a single jmp instruction.
}

void WINAPI DetourFunctionPrint(int x, int y, int z)
{
    printf("%d", x);
    printf("%d\n", y);
    callFinalFunction(x, y, z);
}


int main()
{
    // This works
    callDetourFunction(0, 1, 2, -1);

    // This does not
    callDetourFunctionPrint(0, 1, 2, -1);

    return 0;
}
于 2013-04-23T13:34:24.967 回答