
开发工具逆向工程桌面应用【免费下载链接】TextractorExtracts text from video games and visual novels. Highly extensible.项目地址https://gitcode.com/gh_mirrors/te/Textractor点击查看免费下载Textractor又名 NextHooker是一款面向 Windows/Wine 的开源 x86/x64 视频游戏文本后勾hooker工具它基于早期项目 ITHVNR 发展而来能在不修改游戏文件的前提下实时截获游戏输出到屏幕的对话框文本。本文以 Textractor 项目说明文档为主线系统讲解它的核心特性、/H、/R两种钩子代码语法、host 注入 texthook 双命名管道的项目架构以及扩展功能的编写接口与编译流程读完后你将能够理解并复现其 IPC 机制、为特定游戏手写 hook code、基于OnNewSentence接口开发自己的扩展、在 Visual Studio 中完整编译整个项目。Textractor 是什么Textractor 是一个开源的游戏文本后勾工具运行于 Windows 7 及以上系统或 Wine 环境。它的典型应用场景是视觉小说Galgame与 RPG 类游戏的本地化汉化、剧情文本收集以及实时翻译。工具由两部分组成host 进程GUI 主程序位于 host 与 GUI 目录负责附加/启动目标游戏进程、注入钩子、接收文本并展示texthook.dll由 texthook 目录构建被注入到游戏进程内部真正执行函数 hook 与文本拦截。官方建议通过 教程文档 配套的视频快速上手该文档还说明了一个后续变更自动查找 hook 现在通过界面上的Search for hooks按钮完成而视频中演示的旧方法对应Search for specific text选项这是阅读老教程时需要注意的差异。核心特性项目说明文档列出的四大特性每一条都能在源码中找到对应实现高扩展性与可定制性GUI 在显示文本前会先把每句话派发给所有已加载的扩展extension扩展可以改写、丢弃或旁路输出。文本派发点在 GUI/mainwindow.cpp 的SentenceReceived中完成先调用DispatchSentenceToExtensions处理扩展返回 false 即丢弃该句。自动 hook 大量游戏引擎texthook 注入后会调用Engine::Hijack()识别并接管游戏引擎的文本输出路径。从源码结构看texthook/engine 下分为三套实现native/面向 Windows 原生 GDI 引擎pchooks.h 声明了hookGDIFunctions()、hookGDIPlusFunctions()、hookD3DXFunctions(HMODULE)、hookOtherPcFunctions()四个入口mono/针对基于 .NET/Mono 虚拟机的引擎如部分 Unity 游戏ppsspp/针对 PPSSPP 模拟器的 hook 信息定义。/H hook代码手动挂钩兼容大部分 AGTHAdvanced Game Text Hooker代码格式解析器见下文。/R read代码直接读取不挂钩函数而是周期性/按调用从固定内存地址直接读出文本适合文本驻留在已知内存位置的场合。/H 与 /R 代码语法详解代码解析与生成逻辑集中在 host/hookcode.cpp这是理解整套 hook 系统最关键的单文件。入口函数HookCode::Parse会接受带/前缀或不带的代码并按首字母R/H分派hookcode.cpp#L277-L285。/R读码格式ParseRCodehookcode.cpp#L6-L49/ R {S|Q|V|M} [null_length] [codepage#] address组成部分含义R直接读码标记SANSI 字符串读取QUnicodeUTF-16字符串读取VUTF-8 字符串读取MUnicode 十六进制 dump 模式null_length可选字符串最大长度以 null 结尾前的上限codepage#可选指定代码页如65001为 UTF-8、932为 Shift-JISaddress必选十六进制内存地址源码内置的解析断言hookcode.cpp#L292-L303给出了合法样例/RS65001#44表示用代码页 65001 从地址 0x44 读取 ANSI 字符串。而/RW44、/HWG33会被判为非法——R码不支持W开头W属于 H 码H 码则没有G这种类型字符。/H钩码格式ParseHCodehookcode.cpp#L51-L183/H {A|B|W|H|S|Q|V|M} [F] [null_length] [N] [X] [codepage#] [padding] offset [*deref] [:split [*deref2]] address [:module [:function]]组成部分含义A大端序字节char数据B小端序字节数据带长度头WUnicode 带长度数据HUnicode 十六进制 dump 数据S/Q/V/M分别对应 C 字符串 / Unicode 字符串 / UTF-8 字符串 / Unicode hex dump 字符串F字符串类型后跟F表示FULL_STRING全量字符串null_length可选字符串最大长度NNO_CONTEXT文本不区分调用线程上下文X自定义 text/length/filter/hook 函数AGTH 无对应项Textractor 独有扩展codepage#可选代码页padding可选十六进制 padding 值offset十六进制寄存器偏移支持负数*deref数据指针间接寻址后跟索引偏移:split [*deref2]split 参数第二上下文偏移可再加间接寻址address目标函数地址:module地址为模块基址的相对偏移MODULE_OFFSET:function地址为函数入口的相对偏移FUNCTION_OFFSET两个值得注意的实现细节ITH 寄存器偏移兼容解析末尾有一行修正if (hp.offset 0) hp.offset - 4;hookcode.cpp#L178-L180注释说明 ITH 的寄存器偏移比 AGTH 多 4解析时补 -4 以保证 AGTH 代码原样可用。模块相对地址自动生成GenerateHCode在生成代码时会尝试用VirtualQueryEx查询目标地址所属模块hookcode.cpp#L256-L265把绝对地址改写为offset:module.dll形式使保存的 hook 代码在模块被重定位后仍然有效。源码中的测试用例/HQN936#-c*C:C*1C4AA:gdi.dll:GetTextOutA即完整展示了负偏移 双间接寻址 模块函数相对地址的复合写法末尾的/KF会被按 legacy/AGTH 兼容规则截断。这些类型标志的定义在 include/const.h 的HookParamType枚举中每个标志都附有注释例如USING_STRING 0x1数据为char*或wchar_t*且带长度、DIRECT_READ 0x1000/R读码区别于经典/H钩码等。项目架构双命名管道 共享内存项目说明文档用一段话描述了整体架构此处结合源码逐层展开host 把 texthook.dll 注入目标进程通过两个命名管道与之连接host 写 hostPipetexthook 写 hookPipetexthook 等待管道连接后向若干文本输出函数如 TextOut、GetGlyphOutline注入指令使其输入经管道回传钩子的附加信息通过共享内存交换host 收到管道文本稍作处理后分发给 GUI最后 GUI 在显示前把文本派发给扩展。注入流程入口是 host/host.cpp 的Host::InjectProcess先创建名为VNR_HOOKMAN_pid的互斥体防重复注入已存在则提示 ALREADY_INJECTED64 位 host 下用IsWow64Process检查目标是否为 32 位进程不匹配会给出 NEED_32_BIT 提示反之 32 位 host 遇到 64 位目标时CreateRemoteThread会因 ERROR_ACCESS_DENIED 失败提示 NEED_64_BIT将texthook.dll的完整路径写入目标进程远端内存VirtualAllocExWriteProcessMemory再CreateRemoteThread启动一个以LoadLibraryW为入口的远端线程完成加载。texthook 端DllMain 与 Pipe 线程texthook 被加载后texthook/main.cc 的DllMain在DLL_PROCESS_ATTACH时依次创建带 PID 后缀的互斥体防重入、CreateFileMappingW建立名为VNR_SECTION_pid的共享内存段并映射出TextHook hooks[MAX_HOOK]数组host 端以只读方式映射同一区域读取钩子状态、初始化 MinHook 库、最后启动Pipe线程源码注释特别指出此处不能用std::thread否则会死锁。Pipe线程texthook/main.cc#L29-L88是 texthook 的主循环通过PIPE_AVAILABLE_EVENT事件等待 host 侧管道就绪随后打开\\.\pipe\TEXTRACTOR_HOST读与\\.\pipe\TEXTRACTOR_HOOK写——两个管道名及共享内存段名都定义在 include/defs.h先向 hookPipe 写入自己的 PID 完成握手然后调用Engine::Hijack()执行引擎自动挂钩进入命令循环按HostCommandType定义于 include/const.h分派HOST_COMMAND_NEW_HOOK用NewHook插入用户钩子上限MAX_HOOK超限输出 TOO_MANY_HOOKS、HOST_COMMAND_REMOVE_HOOK移除、HOST_COMMAND_FIND_HOOK触发SearchForText/SearchForHooks对应界面的Search for specific text与Search for hooks、HOST_COMMAND_DETACH结束。文本回传路径为被 hook 的 GDI 函数触发后调用TextOutputtexthook/main.cc#L90-L95把ThreadParam进程 ID、钩子地址、ctx、ctx2作为消息头加上原始字节写入 hookPipe诊断信息则经ConsoleOutput/NotifyHookFound以结构化通知写入同一管道。host 端管道读取与文本线程host 侧CreatePipehost/host.cpp#L79-L147以消息模式创建两个管道并递归保持监听。收到握手 PID 后建立ProcessRecord内含共享内存只读视图供按地址查询钩子参数随后按通知类型处理HOST_NOTIFICATION_FOUND_HOOK处理自动搜索命中会用原代码页与 UTF-8 各转码一次再回调HOST_NOTIFICATION_RMVHOOK清理对应线程默认分支按ThreadParam去重创建/复用TextThread并Push新文本。此外 host 还维护了console与clipboard两个虚拟文本线程并用全局WH_GETMESSAGE钩子监听WM_CLIPBOARDUPDATE把系统剪贴板内容如游戏内 CtrlC 复制的文本一并收编进提取流host/host.cpp#L167-L189。GUI 端扩展派发与显示TextThread输出最终汇入 GUI/mainwindow.cpp 的SentenceReceived先把\r\n中的\r替换为 0x200B线分隔符避免重复换行再调用DispatchSentenceToExtensions让全部扩展依次处理扩展放行后追加换行并插入到ui.textOutput文本控件且仅当滚动条已接近底部时自动滚动。GUI 启动时还会从gamesave.txt/hooksave.txt恢复历史进程与 hook 代码ProcessConnected中按进程名匹配并重新Host::InsertHook见 GUI/mainwindow.cpp#L545-L565实现上次 hook 过的游戏下次自动重挂。扩展功能Extensions项目文档指出扩展功能的写法可参考 Example Extension 项目外部仓库而 extensions 文件夹本身就是现成的范例集合。当前仓库 extensions/CMakeLists.txt 中以MODULE库的形式构建的扩展包括Bing Translate、Copy to Clipboard、DeepL Translate、DevTools DeepL/Papago/Systran Translate、Extra Newlines、Extra Window、Google Translate、Lua、Regex Filter、Regex Replacer、Remove Repeated Characters、Remove Repeated Phrases1/2、Remove 30 Repeated Sentences、Replacer、Styler、Thread Linker——覆盖翻译、去重、正则过滤、Lua 脚本、线程关联等典型需求。扩展的加载机制扩展加载实现在 GUI/extenwindow.cpp 的Load函数规则很明确加载.dll时会先复制一份.xdll副本再加载防止扩展 DLL 与 Textractor 本体的依赖冲突会校验文件内容确实包含OnNewSentence字符串再用GetProcAddress取出该导出函数两者缺一即拒绝加载默认加载顺序写死为Remove Repeated CharactersRegex FilterCopy to ClipboardGoogle TranslateExtra WindowExtra Newlinesextenwindow.cpp#L20-L21用户可在扩展窗口中增删、拖拽排序顺序持久化到SavedExtensions.txt。扩展的编程接口扩展只需导出一个 C 函数。接口结构体定义在 extensions/extension.hstruct InfoForExtension { const char* name; int64_t value; }; struct SentenceInfo { const InfoForExtension* infoArray; int64_t operator[](std::string_view propertyName); // 按属性名查值查不到触发访问违例便于排查 }; struct SKIP {}; inline void Skip() { throw SKIP(); } // 抛出即丢弃本句配套的 extensions/extensionimpl.cpp 是官方实现的骨架注释把契约写得非常清楚wchar_t* OnNewSentence(wchar_t* sentence, const InfoForExtension* sentenceInfo)中sentence为 UTF-16 句子缓冲区HeapAlloc分配扩容量用HeapReAlloc可直接改写返回实际使用的缓冲区指针sentenceInfo是名字-值属性数组以name nullptr项结尾该函数可能并发调用自身必须自行保证线程安全但不会与DllMain并发。host GUI 侧传给扩展的属性数组见 GUI/mainwindow.cpp#L110-L139 的GetSentenceInfo提供的键包括current select是否当前查看的线程、process id、hook address、text handle、text name以及两个以 C 风格字符串和名字签名双键暴露的回调void (*AddSentence)(int64_t number, const wchar_t* sentence)与void (*AddText)(int64_t number, const wchar_t* text)可向任意文本线程注入文本Lua 扩展即以此驱动脚本还有DWORD (*GetSelectedProcessId)()。以 Lua 扩展为例extensions/lua.cpp 就基于这套属性表把回调注册为 Lua 全局函数实现每句触发一段用户脚本。编译指南按项目说明文档的编译前提对应仓库 CMakeLists.txt 的强制要求安装Visual Studio须带 CMake 支持与Qt 5.13需 Core、Widgets、WinExtras、WebSockets 模块根 CMakeLists 通过 cmake/QtUtils.cmake 的find_qt5从注册表定位源码含子模块texthook/minhook为 MinHook 库需git submodule update --init初始化之后用 Visual Studio 打开文件夹直接构建即可。CMake 侧的关键约束CMakeLists.txt最低 CMake 3.16且强制 MSVCif (NOT MSVC) message(FATAL_ERROR ...)统一以/std:c17、/MP多线程编译编译输出按位宽落入builds/配置_x64或builds/配置_x86并分别链接 x64libs/x86libs 下预置的lua53.lib构建目标共五层host、texthook、GUI、extensions19 个扩展 MODULE 库、testassets/*LoaderDll.dll、LocaleEmulator.dll 等会被复制到输出目录其中 LocaleEmulator 用于让 64 位宿主配合 32 位场景做本地化模拟。注意 host/texthook 的位宽必须与目标游戏一致64 位 Textractor 无法用CreateRemoteThread注入 32 位进程源码直接以 NEED_32_BIT/NEED_64_BIT 提示拦截见 host/host.cpp#L203-L213因此 32 位游戏请使用 x86 构建。支持与贡献项目文档README明确了两条协作渠道支持遇到 bug、难以 hook 的游戏或功能建议请提交 issue 说明若 hook 失败作者希望能通过免费下载链接或 Steam 赠礼方式获得该游戏以便调试贡献欢迎一切贡献代码库疑问可邮件联系维护者akashmozumdargmail.com贡献翻译同样容易——text.cpp 集中了所有待翻译的界面文本字符串各语言 README 与教程文稿的翻译也受欢迎。贡献流程遵循标准 fork → branch → commit → PR 模式。开发者名单见 docs/CREDITS.md。小结Textractor 的设计可以浓缩为三条主线语法层/H、/R两套与 AGTH 兼容的 hook code解析器单一且带内置断言、传输层host/texthook 经\\.\pipe\TEXTRACTOR_HOST与\\.\pipe\TEXTRACTOR_HOOK双向通信钩子状态经VNR_SECTION_pid共享内存只读镜像给 host、呈现层GUI 按线程聚合文本并在显示前交给可排序、可持久化的扩展链处理。理解这三层后无论是为某个游戏手写/HQN936#-c*C:C*1C......:module.dll:Function这样的复合钩码还是编写一个导出OnNewSentence的自定义扩展都只是在既有架构上按约定行事——这也是它高扩展性承诺在源码层面的真实含义。赞分享开发工具逆向工程桌面应用【免费下载链接】TextractorExtracts text from video games and visual novels. Highly extensible.项目地址https://gitcode.com/gh_mirrors/te/Textractor点击查看免费下载相关推荐Textractor 源码解析游戏文本 Hook 捕获、双管道 IPC 通信与扩展插件架构Textractor 源码解析游戏文本 Hook 捕获、双管道 IPC 通信与扩展插件架构 Textractor又名 NextHooker是一个面向 Wi开发工具逆向工程桌面应用Textractor 源码解析跨进程游戏文本钩子的架构、Hook Code 与扩展体系Textractor 源码解析跨进程游戏文本钩子的架构、Hook Code 与扩展体系 Textractor又名 NextHooker是一款面向 Wind开发工具逆向工程桌面应用Textractor游戏文本提取工具完全指南项目简介 Textractor又名NextHooker是一款开源的x86/x64游戏文本钩子工具专为Windows 7及以上版本设计同时也支持Wine环开发工具逆向工程桌面应用上一篇Server Survival 游戏平衡数学模型全解析从容量阈值到最优扩容策略的量化指南下一篇为什么你的Windows和Office总提示未激活终极一键激活工具指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考