吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 354|回复: 12
收起左侧

[C&C++ 原创] 新版QT6 IDA Hook 汉化项目,后续会继续补 Win32 Hook

[复制链接]
XiangXin229 发表于 2026-4-28 20:37
本帖最后由 XiangXin229 于 2026-4-29 00:45 编辑

各位老师好,最近一直在玩 Hook 汉化,截至上一次发布源码已过去很长时间,最近颇有心得,随即利用 AI 编程 手搓 Hook 汉化框架源码!

同时配套更新一些 Hook 汉化视频讲解。

这个项目目前主要针对 Qt/Win32 程序的文本 Hook,核心目标是拦截界面中的字符串,配合本地字典实现文本替换和翻译。现在已经支持 DLL 代理加载、Qt QString 提取、文本替换、翻译字典管理,以及简单的通配匹配。

当前源码主要包含:

- DLL 代理劫持入口,目前支持 version / winmm
- Qt QString 文本提取与重新构造
- QPainter / QLabel / QWindow / QFileDialog 等文本相关 Hook
- 翻译字典读取、自动写入未翻译文本
- `{*}` 通配翻译匹配
- 模式搜索工具,支持 `??` 通配字节
- x86 / x64 构建
- CMake 多目标构建结构

源码目录结构:
common/        公共基础库,日志、编码、字符串处理、地址解析、模式搜索、翻译管理等
hooks/         Hook 项目目录,目前主要是 Qt Hook 示例
hooks/ida/     当前 Qt 文本 Hook 的主要实现
proxy/         DLL 代理源码,包含 version / winmm 的 x86、x64 代理代码
third_party/   第三方库,目前使用 Detours
CMakeLists.txt 项目主构建脚本

整体用 CMake 组织,可以一次生成不同代理 DLL 目标,方便后续继续扩展 Win32 Hook 或其他 Qt Hook 示例。

这份源码主要用于学习 Qt Hook、文本拦截和本地化处理相关思路。后续我会继续整理 Win32 Hook 相关内容,也会逐步补充更多示例。

源码地址:

https://github.com/XiangXin229/L10nHook

视频教程:

此源码为第19集视频教程
链接:https://pan.baidu.com/s/1esLfCRPzyv1A8yxQJLKXkA?pwd=6666 提取码:6666

项目还在持续完善中,欢迎交流和提建议。

d1d80b7c-7c57-4e82-8a6d-e18a92f514a3.png 3fd71e2e-b598-4dc2-8bd9-ac95c18e7b93.png
[C++] 纯文本查看 复制代码
#include "Hook.h"

#include <Windows.h>
#include <detours.h>

#include "AddressResolver.h"
#include "Logger.h"
#include "TranslationManager.h"

#include <cstddef>
#include <cwchar>
#include <cstring>
#include <string>

using QPainterDrawText3_t = void (*)(void* pThis, void* pointF, const void* text, int textFlags, int justificationPadding);
using QPainterDrawText4_t = void (*)(void* pThis, void* rect, int flags, const void* text, void* boundingRect);
using QPainterDrawText5_t = void (*)(void* pThis, void* rectangle, const void* text, void* option);
using QPainterDrawText6_t = void (*)(void* pThis, void* rectangle, int flags, const void* text, void* boundingRect);

QPainterDrawText3_t OriginalDrawText3 = nullptr;
QPainterDrawText4_t OriginalDrawText4 = nullptr;
QPainterDrawText5_t OriginalDrawText5 = nullptr;
QPainterDrawText6_t OriginalDrawText6 = nullptr;

void HookedDrawText3(void* pThis, void* pointF, const void* text, int textFlags, int justificationPadding);
void HookedDrawText4(void* pThis, void* rect, int flags, const void* text, void* boundingRect);
void HookedDrawText5(void* pThis, void* rectangle, const void* text, void* option);
void HookedDrawText6(void* pThis, void* rectangle, int flags, const void* text, void* boundingRect);


using QLabelSetText_t = void (*)(void* pThis, const void* text);
using QWindowSetTitle_t = void (*)(void* pThis, const void* text);
using QFileDialogGetOpenFileName_t = void* (*)(void* outQString, void* parent, const void* caption, const void* dir, const void* filter, void* selectedFilter, unsigned int options);
using QFileDialogGetExistingDirectory_t = void* (*)(void* outQString, void* parent, const void* caption, const void* dir, unsigned int options);

QLabelSetText_t OriginalQLabelSetText = nullptr;
QWindowSetTitle_t OriginalQWindowSetTitle = nullptr;
QFileDialogGetOpenFileName_t OriginalQFileDialogGetOpenFileName = nullptr;
QFileDialogGetExistingDirectory_t OriginalQFileDialogGetExistingDirectory = nullptr;

void HookedQLabelSetText(void* pThis, const void* text);
void HookedQWindowSetTitle(void* pThis, const void* text);
void* HookedQFileDialogGetOpenFileName(void* outQString, void* parent, const void* caption, const void* dir, const void* filter, void* selectedFilter, unsigned int options);
void* HookedQFileDialogGetExistingDirectory(void* outQString, void* parent, const void* caption, const void* dir, unsigned int options);


using QFontMetricsSize_t = void* (*)(void* pThis, void* result, int flags, void* text, int tabStops, int* tabArray);
QFontMetricsSize_t OriginalQFontMetricsSize = nullptr;
void* HookedFontMetricsSize(void* pThis, void* result, int flags, void* text, int tabStops, int* tabArray);

using QStringUtf16_t = const wchar_t* (*)(void* pThis);
using QStringSize_t = long long (*)(void* pThis);
using QStringCtor_t = void* (*)(void* pThis, const wchar_t* text, long long length);
using QStringDtor_t = void (*)(void* pThis);
QStringUtf16_t QStringUtf16 = nullptr;
QStringSize_t QStringSize = nullptr;
QStringCtor_t QStringCtor = nullptr;
QStringDtor_t QStringDtor = nullptr;

bool TranslationReady = false;
constexpr size_t kQStringObjectSize = 24;

const wchar_t* ExtractQStringText(void* qString, long long& length);
class QStringMemory;
bool CreateQStringObject(const wchar_t* text, long long length, QStringMemory& qString);
bool CreateTranslatedQStringObject(const void* text, bool writeUntranslated, QStringMemory& qString);

class QStringMemory {
public:
    QStringMemory() = default;
    QStringMemory(const QStringMemory&) = delete;
    QStringMemory& operator=(const QStringMemory&) = delete;

    ~QStringMemory() {
        Destroy();
    }

    void* Data() {
        return memory_;
    }

    void MarkConstructed() {
        constructed_ = true;
    }

    void Destroy() {
        if (constructed_ && QStringDtor != nullptr) {
            QStringDtor(memory_);
        }
        constructed_ = false;
    }

private:
    alignas(std::max_align_t) unsigned char memory_[kQStringObjectSize] = {};
    bool constructed_ = false;
};

bool Hook::Initialize() {

    OriginalDrawText3 = reinterpret_cast<QPainterDrawText3_t>(AddressResolver::GetFunctionAddress(L"Qt6Gui.dll", "?drawText@QPainter@QT@@QEAAXAEBVQPointF@2@AEBVQString@2@HH@Z"));
    OriginalDrawText4 = reinterpret_cast<QPainterDrawText4_t>(AddressResolver::GetFunctionAddress(L"Qt6Gui.dll", "?drawText@QPainter@QT@@QEAAXAEBVQRect@2@HAEBVQString@2@PEAV32@@Z"));
    OriginalDrawText5 = reinterpret_cast<QPainterDrawText5_t>(AddressResolver::GetFunctionAddress(L"Qt6Gui.dll", "?drawText@QPainter@QT@@QEAAXAEBVQRectF@2@AEBVQString@2@AEBVQTextOption@2@@Z"));
    OriginalDrawText6 = reinterpret_cast<QPainterDrawText6_t>(AddressResolver::GetFunctionAddress(L"Qt6Gui.dll", "?drawText@QPainter@QT@@QEAAXAEBVQRectF@2@HAEBVQString@2@PEAV32@@Z"));
    OriginalQLabelSetText = reinterpret_cast<QLabelSetText_t>(AddressResolver::GetFunctionAddress(L"Qt6Widgets.dll", "?setText@QLabel@QT@@QEAAXAEBVQString@2@@Z"));
    OriginalQWindowSetTitle = reinterpret_cast<QWindowSetTitle_t>(AddressResolver::GetFunctionAddress(L"Qt6Gui.dll", "?setTitle@QWindow@QT@@QEAAXAEBVQString@2@@Z"));
    OriginalQFileDialogGetOpenFileName = reinterpret_cast<QFileDialogGetOpenFileName_t>(AddressResolver::GetFunctionAddress(L"Qt6Widgets.dll", "?getOpenFileName@QFileDialog@QT@@SA?AVQString@2@PEAVQWidget@2@AEBV32@11PEAV32@V?$QFlags@W4Option@QFileDialog@QT@@@2@@Z"));
    OriginalQFileDialogGetExistingDirectory = reinterpret_cast<QFileDialogGetExistingDirectory_t>(AddressResolver::GetFunctionAddress(L"Qt6Widgets.dll", "?getExistingDirectory@QFileDialog@QT@@SA?AVQString@2@PEAVQWidget@2@AEBV32@1V?$QFlags@W4Option@QFileDialog@QT@@@2@@Z"));
    OriginalQFontMetricsSize = reinterpret_cast<QFontMetricsSize_t>(AddressResolver::GetFunctionAddress(L"Qt6Gui.dll", "?size@QFontMetrics@QT@@QEBA?AVQSize@2@HAEBVQString@2@HPEAH@Z"));
    QStringUtf16 = reinterpret_cast<QStringUtf16_t>(AddressResolver::GetFunctionAddress(L"Qt6Core.dll", "?utf16@QString@QT@@QEBAPEBGXZ"));
    QStringSize = reinterpret_cast<QStringSize_t>(AddressResolver::GetFunctionAddress(L"Qt6Core.dll", "?size@QString@QT@@QEBA_JXZ"));
    QStringCtor = reinterpret_cast<QStringCtor_t>(AddressResolver::GetFunctionAddress(L"Qt6Core.dll", "??0QString@QT@@QEAA@PEBVQChar@1@_J@Z"));
    QStringDtor = reinterpret_cast<QStringDtor_t>(AddressResolver::GetFunctionAddress(L"Qt6Core.dll", "??1QString@QT@@QEAA@XZ"));
    
    //std::string PatternDrawText3 = "48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 48 89 7C 24 ?? 41 56 48 81 EC ?? ?? ?? ?? 48 8B 19 49 8B F9 41 8B E8 48 8B F2 4C 8B F1 48 83 BB ?? ?? ?? ?? ?? 0F 84 ?? ?? ?? ?? 49 83 79 ?? ?? 0F 84 ?? ?? ?? ?? 48 8B 4B ?? 48 83 C1 ?? E8 ?? ?? ?? ?? 85 C0 0F 84 ?? ?? ?? ?? 48 83 BB ?? ?? ?? ?? ?? 75 ?? 48 8B 53 ?? 48 8B CB E8 ?? ?? ?? ?? 0F 57 C0 48 8D 4C 24 ?? 0F 57 C9 48 8B D6 0F 11 44 24 ?? 0F 11 8C 24 ?? ?? ?? ?? FF 15 ?? ?? ?? ?? 48 8B 4B ?? 48 8D 44 24 ?? 48 8B B4 24 ?? ?? ?? ?? 33 D2 4C 89 74 24 ?? 48 85 F6 89 54 24 ?? 44 8B C5 48 0F 44 C2 48 89 54 24 ?? 89 54 24 ?? 48 83 C1 ?? 48 89 44 24 ?? 48 8D 54 24 ?? 45 33 C9 48 89 7C 24 ?? E8 ?? ?? ?? ?? 48 85 F6 74 ?? 48 8D 54 24 ?? 48 8D 4C 24 ?? FF 15 ?? ?? ?? ?? 0F 10 00 0F 11 06 4C 8D 9C 24 ?? ?? ?? ?? 49 8B 5B ?? 49 8B 6B ?? 49 8B 73 ?? 49 8B 7B ?? 49 8B E3 41 5E C3";
    //void* address = AddressResolver::FindPattern(L"Qt6Gui.dll", PatternDrawText3.c_str());
    //Logger::Write(u8"模式解析地址:%p", address);

    if (QStringUtf16 == nullptr || QStringSize == nullptr) {
        Logger::Write(u8"[Hook] 解析 QString 文本提取函数失败。utf16=%p size=%p", reinterpret_cast<void*>(QStringUtf16), reinterpret_cast<void*>(QStringSize));
        return false;
    }
    if (QStringCtor == nullptr || QStringDtor == nullptr) {
        Logger::Write(u8"[Hook] 解析 QString 构造/析构函数失败。ctor=%p dtor=%p", reinterpret_cast<void*>(QStringCtor), reinterpret_cast<void*>(QStringDtor));
        return false;
    }

    if (OriginalDrawText3 == nullptr || OriginalDrawText4 == nullptr ||
        OriginalDrawText5 == nullptr || OriginalDrawText6 == nullptr) {
        Logger::Write(u8"[Hook] 解析 QPainter::drawText 重载失败。dt3=%p dt4=%p dt5=%p dt6=%p",
                                reinterpret_cast<void*>(OriginalDrawText3),
                                reinterpret_cast<void*>(OriginalDrawText4),
                                reinterpret_cast<void*>(OriginalDrawText5),
                                reinterpret_cast<void*>(OriginalDrawText6));
        return false;
    }
    if (OriginalQFontMetricsSize == nullptr) {
        Logger::Write(u8"[Hook] 解析 QFontMetrics::size 失败");
        return false;
    }
    if (OriginalQLabelSetText == nullptr || OriginalQWindowSetTitle == nullptr) {
        Logger::Write(u8"[Hook] 解析 setText/setTitle 失败。label=%p window=%p", reinterpret_cast<void*>(OriginalQLabelSetText), reinterpret_cast<void*>(OriginalQWindowSetTitle));
        return false;
    }
    if (OriginalQFileDialogGetOpenFileName == nullptr ||
        OriginalQFileDialogGetExistingDirectory == nullptr) {
        Logger::Write(u8"[Hook] 解析 QFileDialog 静态函数失败。openFileName=%p existingDirectory=%p", reinterpret_cast<void*>(OriginalQFileDialogGetOpenFileName), reinterpret_cast<void*>(OriginalQFileDialogGetExistingDirectory));
        return false;
    }


    TranslationManagerConfig translationConfig;
    translationConfig.filterChineseSourceWrites = true;
    TranslationManager::Configure(translationConfig);
    TranslationReady = TranslationManager::Initialize(L"Dictionaries/translations.txt");
    Logger::Write(L"[Hook] translation dictionary=Dictionaries/translations.txt ready=%d", TranslationReady ? 1 : 0);

    DetourRestoreAfterWith();

    LONG error = DetourTransactionBegin();
    if (error == NO_ERROR) {
        error = DetourUpdateThread(GetCurrentThread());
    }
    if (error == NO_ERROR) {
        error = DetourAttach(reinterpret_cast<void**>(&OriginalDrawText3), reinterpret_cast<void*>(HookedDrawText3));
    }
    if (error == NO_ERROR) {
        error = DetourAttach(reinterpret_cast<void**>(&OriginalDrawText4), reinterpret_cast<void*>(HookedDrawText4));
    }
    if (error == NO_ERROR) {
        error = DetourAttach(reinterpret_cast<void**>(&OriginalDrawText5), reinterpret_cast<void*>(HookedDrawText5));
    }
    if (error == NO_ERROR) {
        error = DetourAttach(reinterpret_cast<void**>(&OriginalDrawText6), reinterpret_cast<void*>(HookedDrawText6));
    }
    if (error == NO_ERROR) {
        error = DetourAttach(reinterpret_cast<void**>(&OriginalQFontMetricsSize), reinterpret_cast<void*>(HookedFontMetricsSize));
    }
    if (error == NO_ERROR) {
        error = DetourAttach(reinterpret_cast<void**>(&OriginalQLabelSetText), reinterpret_cast<void*>(HookedQLabelSetText));
    }
    if (error == NO_ERROR) {
        error = DetourAttach(reinterpret_cast<void**>(&OriginalQWindowSetTitle), reinterpret_cast<void*>(HookedQWindowSetTitle));
    }
    if (error == NO_ERROR) {
        error = DetourAttach(reinterpret_cast<void**>(&OriginalQFileDialogGetOpenFileName), reinterpret_cast<void*>(HookedQFileDialogGetOpenFileName));
    }
    if (error == NO_ERROR) {
        error = DetourAttach(reinterpret_cast<void**>(&OriginalQFileDialogGetExistingDirectory), reinterpret_cast<void*>(HookedQFileDialogGetExistingDirectory));
    }
    if (error == NO_ERROR) {
        error = DetourTransactionCommit();
    } else {
        DetourTransactionAbort();
    }

    if (error != NO_ERROR) {
        Logger::Write(L"[Hook] 安装 QPainter::drawText hook 失败。error=%ld", error);
        OriginalDrawText3 = nullptr;
        OriginalDrawText4 = nullptr;
        OriginalDrawText5 = nullptr;
        OriginalDrawText6 = nullptr;
        OriginalQLabelSetText = nullptr;
        OriginalQWindowSetTitle = nullptr;
        OriginalQFileDialogGetOpenFileName = nullptr;
        OriginalQFileDialogGetExistingDirectory = nullptr;
        OriginalQFontMetricsSize = nullptr;
        QStringUtf16 = nullptr;
        QStringSize = nullptr;
        QStringCtor = nullptr;
        QStringDtor = nullptr;
        TranslationReady = false;
        TranslationManager::Clear();
        return false;
    }

    Logger::Write(L"[Hook] 已安装 Hook: drawText3=%p drawText4=%p drawText5=%p drawText6=%p labelSetText=%p windowSetTitle=%p fileOpen=%p existingDir=%p fontMetricsSize=%p",
                        reinterpret_cast<void*>(OriginalDrawText3),
                        reinterpret_cast<void*>(OriginalDrawText4),
                        reinterpret_cast<void*>(OriginalDrawText5),
                        reinterpret_cast<void*>(OriginalDrawText6),
                        reinterpret_cast<void*>(OriginalQLabelSetText),
                        reinterpret_cast<void*>(OriginalQWindowSetTitle),
                        reinterpret_cast<void*>(OriginalQFileDialogGetOpenFileName),
                        reinterpret_cast<void*>(OriginalQFileDialogGetExistingDirectory),
                        reinterpret_cast<void*>(OriginalQFontMetricsSize));
    return true;
}

void Hook::Uninitialize() {
    LONG error = DetourTransactionBegin();
    if (error == NO_ERROR) {
        error = DetourUpdateThread(GetCurrentThread());
    }
    if (error == NO_ERROR) {
        error = DetourDetach(reinterpret_cast<void**>(&OriginalDrawText3), reinterpret_cast<void*>(HookedDrawText3));
    }
    if (error == NO_ERROR) {
        error = DetourDetach(reinterpret_cast<void**>(&OriginalDrawText4), reinterpret_cast<void*>(HookedDrawText4));
    }
    if (error == NO_ERROR) {
        error = DetourDetach(reinterpret_cast<void**>(&OriginalDrawText5), reinterpret_cast<void*>(HookedDrawText5));
    }
    if (error == NO_ERROR) {
        error = DetourDetach(reinterpret_cast<void**>(&OriginalDrawText6), reinterpret_cast<void*>(HookedDrawText6));
    }
    if (error == NO_ERROR) {
        error = DetourDetach(reinterpret_cast<void**>(&OriginalQFontMetricsSize), reinterpret_cast<void*>(HookedFontMetricsSize));
    }
    if (error == NO_ERROR) {
        error = DetourDetach(reinterpret_cast<void**>(&OriginalQLabelSetText), reinterpret_cast<void*>(HookedQLabelSetText));
    }
    if (error == NO_ERROR) {
        error = DetourDetach(reinterpret_cast<void**>(&OriginalQWindowSetTitle), reinterpret_cast<void*>(HookedQWindowSetTitle));
    }
    if (error == NO_ERROR) {
        error = DetourDetach(reinterpret_cast<void**>(&OriginalQFileDialogGetOpenFileName), reinterpret_cast<void*>(HookedQFileDialogGetOpenFileName));
    }
    if (error == NO_ERROR) {
        error = DetourDetach(reinterpret_cast<void**>(&OriginalQFileDialogGetExistingDirectory), reinterpret_cast<void*>(HookedQFileDialogGetExistingDirectory));
    }
    if (error == NO_ERROR) {
        error = DetourTransactionCommit();
    } else {
        DetourTransactionAbort();
    }

    if (error != NO_ERROR) {
        Logger::Write(L"[Hook] 卸载 QPainter::drawText hook 失败。error=%ld", error);
        return;
    }

    OriginalDrawText3 = nullptr;
    OriginalDrawText4 = nullptr;
    OriginalDrawText5 = nullptr;
    OriginalDrawText6 = nullptr;
    OriginalQLabelSetText = nullptr;
    OriginalQWindowSetTitle = nullptr;
    OriginalQFileDialogGetOpenFileName = nullptr;
    OriginalQFileDialogGetExistingDirectory = nullptr;
    OriginalQFontMetricsSize = nullptr;
    QStringUtf16 = nullptr;
    QStringSize = nullptr;
    QStringCtor = nullptr;
    QStringDtor = nullptr;
    TranslationReady = false;
    TranslationManager::Clear();
    Logger::Write(L"[Hook] 已卸载 QPainter::drawText hook。");
}


void HookedDrawText3(void* pThis, void* pointF, const void* text, int textFlags, int justificationPadding) {
    QStringMemory translatedQString;
    if (CreateTranslatedQStringObject(text, true, translatedQString)) {
        OriginalDrawText3(pThis, pointF, translatedQString.Data(), textFlags, justificationPadding);
        return;
    }

    OriginalDrawText3(pThis, pointF, text, textFlags, justificationPadding);
}

void HookedDrawText4(void* pThis, void* rect, int flags, const void* text, void* boundingRect) {
    QStringMemory translatedQString;
    if (CreateTranslatedQStringObject(text, true, translatedQString)) {
        OriginalDrawText4(pThis, rect, flags, translatedQString.Data(), boundingRect);
        return;
    }

    OriginalDrawText4(pThis, rect, flags, text, boundingRect);
}

void HookedDrawText5(void* pThis, void* rectangle, const void* text, void* option) {
    QStringMemory translatedQString;
    if (CreateTranslatedQStringObject(text, true, translatedQString)) {
        OriginalDrawText5(pThis, rectangle, translatedQString.Data(), option);
        return;
    }

    OriginalDrawText5(pThis, rectangle, text, option);
}

void HookedDrawText6(void* pThis, void* rectangle, int flags, const void* text, void* boundingRect) {
    QStringMemory translatedQString;
    if (CreateTranslatedQStringObject(text, true, translatedQString)) {
        OriginalDrawText6(pThis, rectangle, flags, translatedQString.Data(), boundingRect);
        return;
    }

    OriginalDrawText6(pThis, rectangle, flags, text, boundingRect);
}

void HookedQLabelSetText(void* pThis, const void* text) {
    QStringMemory translatedQString;
    if (CreateTranslatedQStringObject(text, true, translatedQString)) {
        OriginalQLabelSetText(pThis, translatedQString.Data());
        return;
    }

    OriginalQLabelSetText(pThis, text);
}

void HookedQWindowSetTitle(void* pThis, const void* text) {
    QStringMemory translatedQString;
    if (CreateTranslatedQStringObject(text, true, translatedQString)) {
        OriginalQWindowSetTitle(pThis, translatedQString.Data());
        return;
    }

    OriginalQWindowSetTitle(pThis, text);
}

void* HookedQFileDialogGetOpenFileName(void* outQString,
                                       void* parent,
                                       const void* caption,
                                       const void* dir,
                                       const void* filter,
                                       void* selectedFilter,
                                       unsigned int options) {
    QStringMemory translatedCaption;
    QStringMemory translatedFilter;
    const bool hasTranslatedCaption = CreateTranslatedQStringObject(caption, true, translatedCaption);
    const bool hasTranslatedFilter = CreateTranslatedQStringObject(filter, true, translatedFilter);

    void* result = OriginalQFileDialogGetOpenFileName(
        outQString,
        parent,
        hasTranslatedCaption ? translatedCaption.Data() : caption,
        dir,
        hasTranslatedFilter ? translatedFilter.Data() : filter,
        selectedFilter,
        options
    );

    return result;
}

void* HookedQFileDialogGetExistingDirectory(void* outQString,
                                            void* parent,
                                            const void* caption,
                                            const void* dir,
                                            unsigned int options) {
    QStringMemory translatedCaption;
    const bool hasTranslatedCaption = CreateTranslatedQStringObject(caption, true, translatedCaption);
    void* result = OriginalQFileDialogGetExistingDirectory(
        outQString,
        parent,
        hasTranslatedCaption ? translatedCaption.Data() : caption,
        dir,
        options
    );

    return result;
}

void* HookedFontMetricsSize(void* pThis, void* result, int flags, void* text, int tabStops, int* tabArray) {
    long long length = 0;
    const wchar_t* value = ExtractQStringText(text, length);
    if (value != nullptr && TranslationReady) {
        std::wstring source(value, static_cast<size_t>(length));
        wchar_t* translated = source.empty() ? nullptr : TranslationManager::Translate(source.c_str(), false);
        if (translated != nullptr) {
            QStringMemory translatedQString;
            if (CreateQStringObject(
                translated,
                static_cast<long long>(std::wcslen(translated)),
                translatedQString
            )) {
                void* sizeResult = OriginalQFontMetricsSize(pThis, result, flags, translatedQString.Data(), tabStops, tabArray);
                //Logger::Write(L"[Hook] QFontMetrics::size text=%.*s translated=%s",
                //                    static_cast<int>(length),
                //                    value,
                //                    translated);
                return sizeResult;
            }

            Logger::Write(L"[Hook] 创建 QFontMetrics::size 翻译 QString 失败,回退原始文本。");
        }
    }

    return OriginalQFontMetricsSize(pThis, result, flags, text, tabStops, tabArray);
}

const wchar_t* ExtractQStringText(void* qString, long long& length) {
    length = 0;
    if (qString == nullptr || QStringUtf16 == nullptr || QStringSize == nullptr) {
        return nullptr;
    }

    length = QStringSize(qString);
    if (length <= 0) {
        length = 0;
        return L"";
    }

    return QStringUtf16(qString);
}

bool CreateQStringObject(const wchar_t* text, long long length, QStringMemory& qString) {
    if (text == nullptr || length < 0 || QStringCtor == nullptr) {
        return false;
    }

    std::memset(qString.Data(), 0, kQStringObjectSize);
    QStringCtor(qString.Data(), text, length);
    qString.MarkConstructed();
    return true;
}

bool CreateTranslatedQStringObject(const void* text, bool writeUntranslated, QStringMemory& qString) {
    long long length = 0;
    const wchar_t* value = ExtractQStringText(const_cast<void*>(text), length);
    if (value == nullptr || !TranslationReady || length <= 0) {
        return false;
    }

    const std::wstring source(value, static_cast<size_t>(length));
    wchar_t* translated = TranslationManager::Translate(source.c_str(), writeUntranslated);
    if (translated == nullptr) {
        return false;
    }

    //Logger::Write(L"[Hook] QString translate text=%.*s translated=%s length=%lld",
    //                    static_cast<int>(length),
    //                    value,
    //                    translated,
    //                    length);
    return CreateQStringObject(translated, static_cast<long long>(std::wcslen(translated)), qString);
}

免费评分

参与人数 2吾爱币 +8 热心值 +2 收起 理由
苏紫方璇 + 7 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
xlln + 1 + 1 我很赞同!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

混世魔猫 发表于 2026-5-2 21:11
XiangXin229 发表于 2026-5-2 19:33
支持qt 5/6,其中qt 5需要自己起符号

bool Hook::Initialize() {
    // 1. 运行时检测当前是 Qt5 还是 Qt6 环境
    bool isQt6 = (GetModuleHandleW(L"Qt6Core.dll") != nullptr);
    bool isQt5 = (GetModuleHandleW(L"Qt5Core.dll") != nullptr);

    if (!isQt6 && !isQt5) {
        Logger::Write(u8"[Hook] 未检测到 Qt5 或 Qt6 环境");
        return false;
    }

    // 2. 根据版本设置 DLL 名称
    const wchar_t* coreDll = isQt6 ? L"Qt6Core.dll" : L"Qt5Core.dll";
    const wchar_t* guiDll = isQt6 ? L"Qt6Gui.dll" : L"Qt5Gui.dll";
    const wchar_t* widgetsDll = isQt6 ? L"Qt6Widgets.dll" : L"Qt5Widgets.dll";

    // 3. 根据版本设置符号名 (Qt6有 @QT@@ 命名空间修饰,Qt5没有;且部分参数类型 int 和 qsizetype 不同)
    const char* drawText3 = isQt6 ? "?drawText@QPainter@QT@@QEAAXAEBVQPointF@2@AEBVQString@2@HH@Z"
                                  : "?drawText@QPainter@@QEAAXAEBVQPointF@AEBVQString@HH@Z";
    const char* drawText4 = isQt6 ? "?drawText@QPainter@QT@@QEAAXAEBVQRect@2@HAEBVQString@2@PEAV32@@Z"
                                  : "?drawText@QPainter@@QEAAXAEBVQRect@HAEBVQString@PEAV2@@Z";
    const char* drawText5 = isQt6 ? "?drawText@QPainter@QT@@QEAAXAEBVQRectF@2@AEBVQString@2@AEBVQTextOption@2@@Z"
                                  : "?drawText@QPainter@@QEAAXAEBVQRectF@AEBVQString@AEBVQTextOption@@Z";
    const char* drawText6 = isQt6 ? "?drawText@QPainter@QT@@QEAAXAEBVQRectF@2@HAEBVQString@2@PEAV32@@Z"
                                  : "?drawText@QPainter@@QEAAXAEBVQRectF@HAEBVQString@PEAV2@@Z";
    const char* labelSetText = isQt6 ? "?setText@QLabel@QT@@QEAAXAEBVQString@2@@Z"
                                     : "?setText@QLabel@@QEAAXAEBVQString@@Z";
    const char* windowSetTitle = isQt6 ? "?setTitle@QWindow@QT@@QEAAXAEBVQString@2@@Z"
                                       : "?setTitle@QWindow@@QEAAXAEBVQString@@Z";
    const char* fileDialogOpen = isQt6 ? "?getOpenFileName@QFileDialog@QT@@SA?AVQString@2@PEAVQWidget@2@AEBV32@11PEAV32@V?$QFlags@W4Option@QFileDialog@QT@@@2@@Z"
                                       : "?getOpenFileName@QFileDialog@@SA?AVQString@PEAVQWidget@@AEBV2@1PEAV2@V?$QFlags@W4Option@QFileDialog@@@@Z";
    const char* fileDialogDir = isQt6 ? "?getExistingDirectory@QFileDialog@QT@@SA?AVQString@2@PEAVQWidget@2@AEBV32@1V?$QFlags@W4Option@QFileDialog@QT@@@2@@Z"
                                      : "?getExistingDirectory@QFileDialog@@SA?AVQString@PEAVQWidget@@AEBV2@V?$QFlags@W4Option@QFileDialog@@@@Z";
    const char* fontMetricsSize = isQt6 ? "?size@QFontMetrics@QT@@QEBA?AVQSize@2@HAEBVQString@2@HPEAH@Z"
                                        : "?size@QFontMetrics@@QEBA?AVQSize@HAEBVQString@HPEAH@Z";
    const char* qstringUtf16 = isQt6 ? "?utf16@QString@QT@@QEBAPEBGXZ"
                                     : "?utf16@QString@@QEBAPEBGXZ";
   
    // 注意:Qt5的size返回int(H),Qt6的size返回qsizetype(_J)
    const char* qstringSize = isQt6 ? "?size@QString@QT@@QEBA_JXZ"
                                    : "?size@QString@@QEBAHXZ";
   
    // 注意:Qt5的Ctor接收int(H),Qt6接收qsizetype(_J)
    const char* qstringCtor = isQt6 ? "??0QString@QT@@QEAA@PEBVQChar@1@_J@Z"
                                    : "??0QString@@QEAA@PEBVQChar@H@Z";
    const char* qstringDtor = isQt6 ? "??1QString@QT@@QEAA@XZ"
                                    : "??1QString@@QEAA@XZ";

    Logger::Write(u8"[Hook] 检测到 %s 环境,开始解析符号...", isQt6 ? "Qt6" : "Qt5");

    // 4. 解析函数地址
    OriginalDrawText3 = reinterpret_cast<QPainterDrawText3_t>(AddressResolver::GetFunctionAddress(guiDll, drawText3));
    OriginalDrawText4 = reinterpret_cast<QPainterDrawText4_t>(AddressResolver::GetFunctionAddress(guiDll, drawText4));
    OriginalDrawText5 = reinterpret_cast<QPainterDrawText5_t>(AddressResolver::GetFunctionAddress(guiDll, drawText5));
    OriginalDrawText6 = reinterpret_cast<QPainterDrawText6_t>(AddressResolver::GetFunctionAddress(guiDll, drawText6));
    OriginalQLabelSetText = reinterpret_cast<QLabelSetText_t>(AddressResolver::GetFunctionAddress(widgetsDll, labelSetText));
    OriginalQWindowSetTitle = reinterpret_cast<QWindowSetTitle_t>(AddressResolver::GetFunctionAddress(guiDll, windowSetTitle));
    OriginalQFileDialogGetOpenFileName = reinterpret_cast<QFileDialogGetOpenFileName_t>(AddressResolver::GetFunctionAddress(widgetsDll, fileDialogOpen));
    OriginalQFileDialogGetExistingDirectory = reinterpret_cast<QFileDialogGetExistingDirectory_t>(AddressResolver::GetFunctionAddress(widgetsDll, fileDialogDir));
    OriginalQFontMetricsSize = reinterpret_cast<QFontMetricsSize_t>(AddressResolver::GetFunctionAddress(guiDll, fontMetricsSize));
    QStringUtf16 = reinterpret_cast<QStringUtf16_t>(AddressResolver::GetFunctionAddress(coreDll, qstringUtf16));
    QStringSize = reinterpret_cast<QStringSize_t>(AddressResolver::GetFunctionAddress(coreDll, qstringSize));
    QStringCtor = reinterpret_cast<QStringCtor_t>(AddressResolver::GetFunctionAddress(coreDll, qstringCtor));
    QStringDtor = reinterpret_cast<QStringDtor_t>(AddressResolver::GetFunctionAddress(coreDll, qstringDtor));
     
    // 5. 错误检查 (保持原有的严谨性)
    if (QStringUtf16 == nullptr || QStringSize == nullptr) {
        Logger::Write(u8"[Hook] 解析 QString 文本提取函数失败。utf16=%p size=%p", reinterpret_cast<void*>(QStringUtf16), reinterpret_cast<void*>(QStringSize));
        return false;
    }
    if (QStringCtor == nullptr || QStringDtor == nullptr) {
        Logger::Write(u8"[Hook] 解析 QString 构造/析构函数失败。ctor=%p dtor=%p", reinterpret_cast<void*>(QStringCtor), reinterpret_cast<void*>(QStringDtor));
        return false;
    }
    if (OriginalDrawText3 == nullptr || OriginalDrawText4 == nullptr ||
        OriginalDrawText5 == nullptr || OriginalDrawText6 == nullptr) {
        Logger::Write(u8"[Hook] 解析 QPainter::drawText 重载失败。dt3=%p dt4=%p dt5=%p dt6=%p",
                                reinterpret_cast<void*>(OriginalDrawText3),
                                reinterpret_cast<void*>(OriginalDrawText4),
                                reinterpret_cast<void*>(OriginalDrawText5),
                                reinterpret_cast<void*>(OriginalDrawText6));
        return false;
    }
    if (OriginalQFontMetricsSize == nullptr) {
        Logger::Write(u8"[Hook] 解析 QFontMetrics::size 失败");
        return false;
    }
    if (OriginalQLabelSetText == nullptr || OriginalQWindowSetTitle == nullptr) {
        Logger::Write(u8"[Hook] 解析 setText/setTitle 失败。label=%p window=%p", reinterpret_cast<void*>(OriginalQLabelSetText), reinterpret_cast<void*>(OriginalQWindowSetTitle));
        return false;
    }
    if (OriginalQFileDialogGetOpenFileName == nullptr ||
        OriginalQFileDialogGetExistingDirectory == nullptr) {
        Logger::Write(u8"[Hook] 解析 QFileDialog 静态函数失败。openFileName=%p existingDirectory=%p", reinterpret_cast<void*>(OriginalQFileDialogGetOpenFileName), reinterpret_cast<void*>(OriginalQFileDialogGetExistingDirectory));
        return false;
    }

    TranslationManagerConfig translationConfig;
    translationConfig.filterChineseSourceWrites = true;
    TranslationManager::Configure(translationConfig);
    TranslationReady = TranslationManager::Initialize(L"Dictionaries/translations.txt");
    Logger::Write(L"[Hook] translation dictionary=Dictionaries/translations.txt ready=%d", TranslationReady ? 1 : 0);

    // 6. 安装 Detours Hook (逻辑完全不变)
    DetourRestoreAfterWith();

    LONG error = DetourTransactionBegin();
    if (error == NO_ERROR) error = DetourUpdateThread(GetCurrentThread());
    if (error == NO_ERROR) error = DetourAttach(reinterpret_cast<void**>(&OriginalDrawText3), reinterpret_cast<void*>(HookedDrawText3));
    if (error == NO_ERROR) error = DetourAttach(reinterpret_cast<void**>(&OriginalDrawText4), reinterpret_cast<void*>(HookedDrawText4));
    if (error == NO_ERROR) error = DetourAttach(reinterpret_cast<void**>(&OriginalDrawText5), reinterpret_cast<void*>(HookedDrawText5));
    if (error == NO_ERROR) error = DetourAttach(reinterpret_cast<void**>(&OriginalDrawText6), reinterpret_cast<void*>(HookedDrawText6));
    if (error == NO_ERROR) error = DetourAttach(reinterpret_cast<void**>(&OriginalQFontMetricsSize), reinterpret_cast<void*>(HookedFontMetricsSize));
    if (error == NO_ERROR) error = DetourAttach(reinterpret_cast<void**>(&OriginalQLabelSetText), reinterpret_cast<void*>(HookedQLabelSetText));
    if (error == NO_ERROR) error = DetourAttach(reinterpret_cast<void**>(&OriginalQWindowSetTitle), reinterpret_cast<void*>(HookedQWindowSetTitle));
    if (error == NO_ERROR) error = DetourAttach(reinterpret_cast<void**>(&OriginalQFileDialogGetOpenFileName), reinterpret_cast<void*>(HookedQFileDialogGetOpenFileName));
    if (error == NO_ERROR) error = DetourAttach(reinterpret_cast<void**>(&OriginalQFileDialogGetExistingDirectory), reinterpret_cast<void*>(HookedQFileDialogGetExistingDirectory));
   
    if (error == NO_ERROR) {
        error = DetourTransactionCommit();
    } else {
        DetourTransactionAbort();
    }

    if (error != NO_ERROR) {
        Logger::Write(L"[Hook] 安装 Hook 失败。error=%ld", error);
        // 清理指针
        OriginalDrawText3 = OriginalDrawText4 = OriginalDrawText5 = OriginalDrawText6 = nullptr;
        OriginalQLabelSetText = OriginalQWindowSetTitle = nullptr;
        OriginalQFileDialogGetOpenFileName = OriginalQFileDialogGetExistingDirectory = nullptr;
        OriginalQFontMetricsSize = QStringUtf16 = QStringSize = QStringCtor = QStringDtor = nullptr;
        TranslationReady = false;
        TranslationManager::Clear();
        return false;
    }

    Logger::Write(L"[Hook] 已安装 Hook (%s): drawText3=%p drawText4=%p drawText5=%p drawText6=%p labelSetText=%p windowSetTitle=%p fileOpen=%p existingDir=%p fontMetricsSize=%p",
                        isQt6 ? L"Qt6" : L"Qt5",
                        reinterpret_cast<void*>(OriginalDrawText3),
                        reinterpret_cast<void*>(OriginalDrawText4),
                        reinterpret_cast<void*>(OriginalDrawText5),
                        reinterpret_cast<void*>(OriginalDrawText6),
                        reinterpret_cast<void*>(OriginalQLabelSetText),
                        reinterpret_cast<void*>(OriginalQWindowSetTitle),
                        reinterpret_cast<void*>(OriginalQFileDialogGetOpenFileName),
                        reinterpret_cast<void*>(OriginalQFileDialogGetExistingDirectory),
                        reinterpret_cast<void*>(OriginalQFontMetricsSize));
    return true;
}
改成这样?
苏紫方璇 发表于 2026-4-29 00:05
请在帖子中插入部分关键代码
本版块仅限分享编程技术和源码相关内容,发布帖子必须带上关键代码和具体功能介绍
 楼主| XiangXin229 发表于 2026-4-29 00:45
苏紫方璇 发表于 2026-4-29 00:05
请在帖子中插入部分关键代码
本版块仅限分享编程技术和源码相关内容,发布帖子必须带上关键代码和具体功能 ...

插入了源码
SONGXINGJING520 发表于 2026-4-30 11:09
必须支持大佬
混世魔猫 发表于 2026-5-2 18:15
支持大佬 有支持Qt5的HOOK吗
 楼主| XiangXin229 发表于 2026-5-2 19:33
混世魔猫 发表于 2026-5-2 18:15
支持大佬 有支持Qt5的HOOK吗

支持qt 5/6,其中qt 5需要自己起符号
 楼主| XiangXin229 发表于 2026-5-2 22:15
混世魔猫 发表于 2026-5-2 21:11
bool Hook::Initialize() {
    // 1. 运行时检测当前是 Qt5 还是 Qt6 环境
    bool isQt6 = (GetModu ...

稍等几分钟,我做一个多版本的,一会我回复你,你看github
 楼主| XiangXin229 发表于 2026-5-2 23:34
混世魔猫 发表于 2026-5-2 21:11
bool Hook::Initialize() {
    // 1. 运行时检测当前是 Qt5 还是 Qt6 环境
    bool isQt6 = (GetModu ...

可以了你看下github
chnhc 发表于 2026-5-3 09:49
必须支持
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - 52pojie.cn ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2026-5-6 20:10

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表