c++ - 绕行函数在 printf 上崩溃

标签 c++ dll detours

我在应用程序中创建了一个 DLL Hook 。
像这样绕过一个函数:

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

最佳答案

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

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

下面的代码通过模拟示例中的绕行设置来演示这一点。被 Hook 的函数需要 4 个参数,但绕行只需要 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;
}

关于c++ - 绕行函数在 printf 上崩溃,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16168353/

相关文章:

c++ - 在调用导入之前将钩子(Hook) DLL 注入(inject)到进程中?

c++ - 访问 OpenCV 中的每个单独 channel

c++ - 启动使用附加库的可执行程序

c++ - 在 dll 中导出函数。 C 和 C++

windows - 如何在 DLL 中查找特定指令的内存地址

c++ - 如何获取 ConnectEx() 指针

c++ - OpenMP 积分图像比顺序图像慢

停止执行并启动 Visual Studio 调试器的 C++ 源代码行

c++:如何将字符串分解(不解析)为命令行参数?

c++ - 为什么 DirectX Device Present Hook 无法绕道工作?