绕路函数在打印时崩溃

Detoured function crashes on printf

本文关键字:崩溃 打印 函数 绕路      更新时间:2023-10-16

我做了一个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("%dn", 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);
 }

运行正常。为什么会发生这种情况?

奥丽coderipper:

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的签名可能有误。由于函数使用标准调用约定,因此需要在返回之前清理堆栈。如果使用错误的形参数调用其中一个函数,则堆栈指针返回时将出现错误。

当print语句被删除时,它没有发生的原因是因为编译器可能会优化转发调用到单个jmp指令。当包含print语句时,绕道函数实际上有工作要做,并在返回之前根据不正确的值调整堆栈。如果MyUC2Packets期望6个参数,但函数签名只接受5个参数,这将导致任何时候绕行函数无法优化下来的问题。

下面的代码通过模拟示例中的绕道设置来演示这一点。被钩住的函数需要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("%dn", y);
    callFinalFunction(x, y, z);
}

int main()
{
    // This works
    callDetourFunction(0, 1, 2, -1);
    // This does not
    callDetourFunctionPrint(0, 1, 2, -1);
    return 0;
}