如何使用 C++ 挂钩 Delphi 函数

Posted

技术标签:

【中文标题】如何使用 C++ 挂钩 Delphi 函数【英文标题】:How to hook Delphi function using C++ 【发布时间】:2020-09-30 15:37:22 【问题描述】:

我正在尝试使用 C++ 和 Detours 库挂钩用 Delphi 编写的程序的用户定义函数。 (DLL 注入)

但是,我无法挂钩它,因为 Delphi 和 C++ 的函数调用约定不匹配。

Delphi使用fastcall函数调用约定,C++也提供了fastcall函数调用约定。

然而,Delphi 的 fastcall 将其参数顺序存储在 EAX、EDX、ECX 和堆栈上,而 C++ 的 fastcall 将其参数顺序存储在 ECX、EDX 和堆栈上。 (这是因为fastcall没有标准。)

由于这些差异,我无法获取存储在 EAX 中的参数。

我该如何解决这个问题?

(本文已由谷歌翻译翻译。)


#include "pch.h"

typedef void(__fastcall* ORGFP)(char); //Prototype of function to hook (reverse engineering)
ORGFP originFunc1 = (ORGFP)((DWORD)GetModuleHandle(NULL) + 0x2B2F20); //Image base of target process + offset of function to hook
ORGFP originFunc2 = (ORGFP)((DWORD)GetModuleHandle(NULL) + 0x2B2A20);

DWORD WriteLog(LPCTSTR lpszFormat, ...) 
    TCHAR szLog[512];
    DWORD dwCharsWritten;

    va_list args;
    va_start(args, lpszFormat);
    _vstprintf_s(szLog, 512, lpszFormat, args);
    va_end(args);

    WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE), szLog, _tcslen(szLog), &dwCharsWritten, NULL);

    return dwCharsWritten;


void __fastcall DetourFunc1(char on) 
    WriteLog(TEXT("Function called : BlockInternet(%d)\n"), on);
    return originFunc1(on);


void __fastcall DetourFunc2(char on) 
    WriteLog(TEXT("Function called : BlockInputDevices(%d)\n"), on);
    return originFunc2(on);


BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) 
    if (DetourIsHelperProcess())
        return TRUE;

    switch (ul_reason_for_call) 
    case DLL_PROCESS_ATTACH:
        AllocConsole();
        DetourRestoreAfterWith();
        DetourTransactionBegin();
        DetourUpdateThread(GetCurrentThread());
        DetourAttach(&(PVOID&)originFunc1, DetourFunc1);
        DetourAttach(&(PVOID&)originFunc2, DetourFunc2);
        DetourTransactionCommit();
        break;
    case DLL_PROCESS_DETACH:
        FreeConsole();
        DetourTransactionBegin();
        DetourUpdateThread(GetCurrentThread());
        DetourDetach(&(PVOID&)originFunc1, DetourFunc1);
        DetourDetach(&(PVOID&)originFunc2, DetourFunc2);
        DetourTransactionCommit();
        break;
    

    return TRUE;

#include "pch.h"

#ifndef PCH_H
#define PCH_H

#include "framework.h"
#include <stdio.h>
#include <stdarg.h>
#include <tchar.h>
#include <detours.h>

#endif

#pragma once

#define WIN32_LEAN_AND_MEAN
#include <windows.h>

【问题讨论】:

需要一些组装。 @500 - 内部服务器错误 // 你能解释一下你需要什么程序集吗? 您需要编写代码为汇编中的调用准备参数。您无法让编译器执行此操作。所以你必须承担编译器的工作。那是如果您被限制使用 C++ 代码。你可以做的是使用一个小的 Delphi DLL 来为你处理 register 调用。 这能回答你的问题吗? Hooking an App made on MSVC++ with __fastcall enabled from an injected Delphi dll @Remko // 这不是我想要的,但它帮助很大。谢谢! 【参考方案1】:

我使用__declspec(naked) 解决了这个问题!感谢所有帮助过我的人!

#include <iostream>
#include <Windows.h>
#include <detours.h>
using namespace std;

__declspec(naked) void __fastcall originFunction(char arg) 
    __asm 
        push ebp
        mov ebp, esp
        sub esp, __LOCAL_SIZE
    
    __asm 
        mov byte ptr [arg], al
    
    cout << (int)arg << endl;
    __asm 
        mov esp, ebp
        pop ebp
        ret
    


typedef void(__fastcall* FP)(char);
FP originFunctionPointer = originFunction;

__declspec(naked) void __fastcall detourFunction(char arg) 
    __asm 
        push ebp
        mov ebp, esp
        sub esp, __LOCAL_SIZE
    
    __asm 
        mov byte ptr [arg], al
    
    cout << (int)arg << endl;
    arg = 0;
    __asm 
        mov al, byte ptr [arg]
        call dword ptr [originFunctionPointer]
    
    __asm 
        mov esp, ebp
        pop ebp
        ret
    


int main() 
    DetourRestoreAfterWith();
    DetourTransactionBegin();
    DetourUpdateThread(GetCurrentThread());
    DetourAttach(&(PVOID&)originFunctionPointer, detourFunction);
    DetourTransactionCommit();

    __asm 
        mov al, 1h
        call dword ptr [originFunction]
    

    DetourTransactionBegin();
    DetourUpdateThread(GetCurrentThread());
    DetourDetach(&(PVOID&)originFunctionPointer, detourFunction);
    DetourTransactionCommit();

    return 0;

【讨论】:

【参考方案2】:

根据***:

Borland 注册 从左到右评估参数,它通过 EAX、EDX、ECX 传递三个参数。剩余的参数被压入堆栈,也是从左到右。 [12]它是 Delphi 的 32 位编译器的默认调用约定,在这里它被称为寄存器。这种调用约定也被 Embarcadero 的 C++Builder 使用,它被称为 __fastcall。[13]在这个编译器中,可以使用微软的fastcall作为__msfastcall。[14]

通过使用带有 regparm 函数属性的 __stdcall 或 -mregparm=3 开关,可以使 GCC 和 Clang 使用类似的调用约定。 (堆栈顺序是倒置的。)也可以使用 cdecl 生成调用者清理变体,或者将其扩展为也使用 SSE 寄存器。 [15]自 2.6.20 版(2007 年 2 月发布)以来,i386 上的 Linux 内核使用基于 cdecl 的版本。

https://en.wikipedia.org/wiki/X86_calling_conventions#Borland_register

【讨论】:

以上是关于如何使用 C++ 挂钩 Delphi 函数的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Visual C++ 中使用 Delphi 的寄存器调用约定调用函数?

如何将方法挂钩到 Delphi 7 IDE 中的 Edit 事件?

用delphi查找F12的键盘挂钩

使用啥私有 c++ 函数返回类型来使用 mobilesubstrate 进行挂钩

如何使用 Detours 挂钩 exe 函数?

挂钩内部函数:参数如何?