【问题标题】:Detouring DrawString in dll doesn't work在 dll 中绕行 DrawString 不起作用
【发布时间】:2015-01-02 12:58:10
【问题描述】:

我正在尝试 Detours。

首先,我有一个 ForHook.cpp,它使用 gdiplus DrawString 打印一个“Hello”。

VOID OnPaint(HDC hdc)
{
    Graphics graphics(hdc);
    FontFamily  fontFamily(L"Times New Roman");
    Font        font(&fontFamily, 24, FontStyleRegular, UnitPixel);
    PointF      pointF(30.0f, 10.0f);
    SolidBrush  solidBrush(Color(255, 0, 0, 255));
    graphics.DrawString(L"Hello", -1, &font, pointF, &solidBrush);
}

我在ForHook.cpp中编写了绕行代码,它拦截DrawString命令,将“哈哈”写入窗口而不是“Hello”。它有效。 用于替换gdiplus DrawString的代码:

class CDetour {
public:
    static Gdiplus::Status(CDetour::*pDrawString)(const WCHAR* s, INT len, const Gdiplus::Font *f, const Gdiplus::PointF& org, const Gdiplus::Brush* b);
    Gdiplus::Status MyDrawString(WCHAR* s, INT len, const Gdiplus::Font *f, const Gdiplus::PointF& org, const Gdiplus::Brush* b);
};
Gdiplus::Status CDetour::MyDrawString(WCHAR* s, INT len, const Gdiplus::Font *f, const Gdiplus::PointF& org, const Gdiplus::Brush* b)
{
    WCHAR* a = L"Haha!";
    return (this->*pDrawString)(a, len, f, org, b);
}

Gdiplus::Status(CDetour::* CDetour::pDrawString)(const WCHAR* s, INT len, const Gdiplus::Font *f, const Gdiplus::PointF& org, const Gdiplus::Brush* b) = (Gdiplus::Status(CDetour::*)(const WCHAR* s, INT len, const Gdiplus::Font *f, const Gdiplus::PointF& org, const Gdiplus::Brush* b))&Graphics::DrawString;

绕行代码:

Gdiplus::Status(CDetour::* pMyDrawString)(WCHAR* s, INT len, const Gdiplus::Font *f, const Gdiplus::PointF& org, const Gdiplus::Brush* b) = &CDetour::MyDrawString;
DetourTransactionBegin();
DetourUpdateThread(GetCurrentThread());
DetourAttach(&(PVOID&)(CDetour::pDrawString), *(PBYTE*)&pMyDrawString);
DetourTransactionCommit();

然后,我将它们移动到 dll 中,并在 ForHook.cpp 中加载 dll。 在我习惯绕行代码的地方:

LoadLibrary(TEXT("Mydll.dll"));

绕行代码放在DllMain下:

case DLL_PROCESS_ATTACH:
    //detours
    MessageBox(0, L"Process Attach", L"Info", MB_OK);
    DetourTransactionBegin();
    DetourUpdateThread(GetCurrentThread());
    DetourAttach(&(PVOID&)(CDetour::pDrawString), *(PBYTE*)&pMyDrawString);
    if (DetourTransactionCommit() == NO_ERROR)
    { 

        //WCHAR buffer[15];
        //_ultow_s(GetCurrentThreadId(), buffer, 15, 10);
        MessageBox(0, L"Dll successfully injected.", L"Info", MB_OK);
        //MessageBox(0, buffer, L"Info", MB_OK);
    }
    break;

我在 dll 分离时使用 detourdetach:

case DLL_PROCESS_DETACH:
    MessageBox(0, L"Process Detach", L"Info", MB_OK);
    DetourTransactionBegin();
    DetourUpdateThread(GetCurrentThread());
    DetourDetach(&(PVOID&)(CDetour::pDrawString), *(PBYTE*)&pMyDrawString);
    DetourTransactionCommit();
    break;

由于在 ForHook.cpp 中不会调用 detours API,所以我也删除了 #include <detours.h>#pragma comment(lib, "detours.lib)

棘手的是,附加了 dll,我使用 Listdlls.exe 对其进行了验证。该 dll 位于 ForHook.exe 进程 ID 下。但是程序仍然打印“Hello”而不是“Haha”。

为什么它不起作用?我该如何改变呢?

dllmain.cpp:

// dllmain.cpp : Defines the entry point for the DLL application.
#include "stdafx.h"
#include <detours.h>
#include <gdiplus.h>
#include <windows.h>
#include <objidl.h>

using namespace Gdiplus;
#pragma comment (lib,"Gdiplus.lib")
#pragma comment(lib,"detours.lib")

//class __declspec(dllexport) CDetour
class CDetour
{

public:
    static Gdiplus::Status(CDetour::*pDrawString)(const WCHAR* s, INT len, const Gdiplus::Font *f, const Gdiplus::PointF& org, const Gdiplus::Brush* b);
    Gdiplus::Status  MyDrawString(WCHAR* s, INT len, const Gdiplus::Font *f, const Gdiplus::PointF& org, const Gdiplus::Brush* b);

};

Gdiplus::Status CDetour::MyDrawString(WCHAR* s, INT len, const Gdiplus::Font *f, const Gdiplus::PointF& org, const Gdiplus::Brush* b)
{
    WCHAR* a = L"Haha!";
    return (this->*pDrawString)(a, len, f, org, b);
}

DWORD GetMainThreadId(DWORD dwPid)
{
    LPVOID lpTid;

    _asm
    {
        mov eax, fs:[18h]
            add eax, 36
            mov[lpTid], eax
    }

    HANDLE hProcess = OpenProcess(PROCESS_VM_READ, FALSE, dwPid);
    if (hProcess == NULL)
        return NULL;

    DWORD dwTid;
    if (ReadProcessMemory(hProcess, lpTid, &dwTid, sizeof(dwTid), NULL) == FALSE)
    {
        CloseHandle(hProcess);
        return NULL;
    }

    CloseHandle(hProcess);

    return dwTid;
}

Gdiplus::Status(CDetour::* CDetour::pDrawString)(const WCHAR* s, INT len, const Gdiplus::Font *f, const Gdiplus::PointF& org, const Gdiplus::Brush* b) = (Gdiplus::Status(CDetour::*)(const WCHAR* s, INT len, const Gdiplus::Font *f, const Gdiplus::PointF& org, const Gdiplus::Brush* b))&Graphics::DrawString;


BOOL APIENTRY DllMain( HMODULE hModule,
                   DWORD  ul_reason_for_call,
                   LPVOID lpReserved
                 )
{
    //MessageBox(0, TEXT("Why?"), TEXT("Info"), MB_OK);

    Gdiplus::Status(CDetour:: *pMyDrawString)(WCHAR* s, INT len, const Gdiplus::Font *f, const Gdiplus::PointF& org, const Gdiplus::Brush* b) = &CDetour::MyDrawString;
    //WCHAR buf[15];
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
        //MessageBox(0, L"Process Attach", L"Info", MB_OK);
        //_ultow_s(GetMainThreadId(GetCurrentProcessId()), buf, 15, 10);
        //MessageBox(0, buf, L"Info", MB_OK);
        DetourTransactionBegin();
        DetourUpdateThread(GetCurrentThread());
        DetourAttach(&(PVOID&)(CDetour::pDrawString), *(PBYTE*)&pMyDrawString);
        if (DetourTransactionCommit() == NO_ERROR)
        {

            WCHAR buffer[15];
            _ultow_s(GetCurrentThreadId(), buffer, 15, 10);
            //MessageBox(0, L"Dll successfully injected.", L"Info", MB_OK);
            MessageBox(0, buffer, L"Info", MB_OK);
        }
        break;
    case DLL_THREAD_ATTACH:
        //detours
        //MessageBox(0, L"Thread Attach", L"Info", MB_OK);
        //_ultow_s(GetMainThreadId(GetCurrentProcessId()), buf, 15, 10);
        //MessageBox(0, buf, L"Info", MB_OK);
        DetourTransactionBegin();
        DetourUpdateThread(GetCurrentThread());
        DetourAttach(&(PVOID&)(CDetour::pDrawString), *(PBYTE*)&pMyDrawString);
        if (DetourTransactionCommit() == NO_ERROR)
        {

            WCHAR buffer[15];
            _ultow_s(GetCurrentThreadId(), buffer, 15, 10);
            //MessageBox(0, L"Dll successfully injected.", L"Info", MB_OK);
            MessageBox(0, buffer, L"Info", MB_OK);
        }
        break;
    case DLL_THREAD_DETACH:
        //MessageBox(0, L"Thread Detach", L"Info", MB_OK);
        DetourTransactionBegin();
        DetourUpdateThread(GetCurrentThread());
        DetourDetach(&(PVOID&)(CDetour::pDrawString), *(PBYTE*)&pMyDrawString);
        DetourTransactionCommit();
        break;
    case DLL_PROCESS_DETACH:
        //MessageBox(0, L"Proccess Detach", L"Info", MB_OK);
        DetourTransactionBegin();
        DetourUpdateThread(GetCurrentThread());
        DetourDetach(&(PVOID&)(CDetour::pDrawString), *(PBYTE*)&pMyDrawString);
        DetourTransactionCommit();
        break;
    }

    return TRUE;
}

【问题讨论】:

    标签: c++ dll gdi+ detours


    【解决方案1】:

    首先,您要尽量避免在 DllMain() 中执行任何操作。其次,当您实际上可能想在 DLL_THREAD_ATTACH 中调用它时,您在 PROCESS attach 中进行了设置。如果您在代码中放置某种视觉标记,您会发现 Windows 在加载 DLL 时会做一些 WHACKY 的事情。你会看到一个 ProcessAttach,(好吧,当然……),然后你会看到一个 ThreadAttach()……好吧……但是你会看到一个 ThreadDetatch(),当你的应用程序关闭时,你'将得到一个 PROCESS_DETATCH,但不是一个 THREAD_DETATCH...

    您在 PROCESS_ATTACH 中设置的线程信息很可能不是实际执行工作的线程...将您的代码移动到 THREAD_ATTACH,它可能会工作。就 DLL 而言,代码很可能使用 TLS,这是另一种可怕的技术......

    【讨论】:

    • 感谢您的快速回答。
    • 我认为它也可能不是同一个线程。所以你可以看到在我的 DLL_PROCESS_ATTACH 中,我试图输出当前线程 ID。而且我还让在 ForHook.cpp 中加载 dll 之后放置相同的代码。线程 ID 相同。
    • 我听取了您的建议,将代码移至 DLL_THREAD_ATTACH,但没有成功。
    • 另外,DLL_THREAD_ATTACH 将 dll 附加到除进程主线程之外的所有线程。但就目前而言,我在 ForHook.cpp 中的 OnPaint() 在主线程中运行,DLL_THREAD_ATTACH 应该不起作用。
    • 等一下,你的 CDetour 类是在哪里声明的?我在想静态 Gdiplus::Status 声明可能与此有关?它可能在 DLL 加载和挂钩之前很久就被声明了,到这个时候,为时已晚,并且指向错误的东西,或者是 NULL?我必须看到更多的代码。顺便说一句,你把它放在什么样的DLL中?如果您使用的是 MFC dll,那么修改附加点会更加棘手和复杂。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-02-09
    • 1970-01-01
    相关资源
    最近更新 更多