news 2026/10/4 13:47:15

深入解析 gbe 项目的构建资源体系:resources 目录、版本资源模拟与 DOS Stub 改写

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
深入解析 gbe 项目的构建资源体系:resources 目录、版本资源模拟与 DOS Stub 改写
  • 游戏开发
  • 逆向工程

【免费下载链接】gbe_fork

Fork of https://gitlab.com/Mr_Goldberg/goldberg_emulator

项目地址:https://gitcode.com/gh_mirrors/gbe/gbe_fork
点击查看免费下载

本文围绕 gbe(Goldberg 模拟器 fork)仓库中resources/目录展开,系统讲解该项目在 Windows 构建过程中如何利用 Microsoft 资源编译器rc.exe为生成的steam_api(64).dll、steamclient(64).dll、steam.exe、GameOverlayRenderer(64).dll等二进制注入版本信息与模仿原始二进制中的附加资源,并揭示构建后 DOS Stub 改写的原理与实现。读完本文,你将掌握该仓库资源文件的组织方式、VERSIONINFO与WEVT_TEMPLATE等资源的编写规范、premake5.lua中winrsrc/dosstub选项的启用方法,以及file_dos_stub工具对 PE 文件头部偏移写入的底层细节。

一、resources/目录在构建流程中的定位

根据 resources/README.md 的说明,resources/目录存放的是“构建期间使用的附加资源”(additional resources used during build)。这些资源并非运行时逻辑代码,而是构建产物在二进制层面与原版 Steam 文件“外观”保持一致的关键部分。

该目录下只有一个子目录win/,其作用明确:把要添加到.dll/.exe二进制中的资源收集在一起,具体包含两类内容:

  • 版本信息(version info):通过VERSIONINFO资源描述文件声明,用于让生成的模拟器 DLL/EXE 在资源管理器中展示与原始 Steam 组件一致的版本属性;
  • 对原始二进制中“任何附加资源”的模仿(an immitation of any extra resources found in the original .dll/.exe):例如steamclient.dll与GameOverlayRenderer.dll中存在的 Windows 事件跟踪模板WEVT_TEMPLATE资源。

一个值得注意的措辞是 README 使用了“immitation”(模仿)而非“copy”(复制),从源码结构看,这些资源是项目开发者根据对原始 Steam 二进制中资源结构的观察,用资源脚本重新编写出来的等价物,而非从原文件直接抽取的字节拷贝。

二、资源构建链路:rc.exe→*.res→cl.exe

README 明确描述了完整的资源编译流程,这也是 Windows 平台标准的资源编译链路:

  1. 编译资源脚本:构建过程中调用微软资源编译器rc.exe编译resources/win/**/*.rc文件;
  2. 产出中间文件:编译产物为*.res文件,统一存放在build\tmp\win\rsrc目录下;
  3. 参与链接:生成的.res文件随后被当作普通.cpp/.c源文件一样,直接传给编译器cl.exe参与最终链接。

README 给出了示例命令:

cl.exe myfile.cpp myres.res -o myout.exe

这条命令说明.res文件可以直接作为输入参数与源码一同编译链接,最终把资源段合并进输出的 PE 二进制中。在 gbe 仓库中,这一流程的实际触发点位于 premake5.lua 中定义的三个 Windows 专用构建选项(见下文“构建集成”小节),各项目的源文件列表中会按 x32 / x64 平台分别加入对应的resources.rc。

三、win/子目录逐项解析

README 将win/下的资源按目标二进制分为五组,下面结合仓库中的实际文件逐一说明。

3.1api/:模拟steam_api(64).dll的资源

目录 resources/win/api/ 下按 32/64 位分平台存放resources.rc,用于模拟steam_api.dll(32 位)与steam_api64.dll(64 位)中的资源。

以 resources/win/api/64/resources.rc 为例,其核心结构包含:

  • 第一段中性语言块(LANGUAGE 0, 0,语言 = Neutral,子语言 = Neutral):
    • FILEVERSION 8,33,9,23、FileVersion "08.33.09.23":模仿 Steam 客户端 API 的版本号 8.33.9.23;
    • InternalName "GSE Client API (win64)"、OriginalFilename "steam_api.dll":声明内部名称与原始文件名,注意 64 位与 32 位(resources/win/api/32/resources.rc 中为GSE Client API (win32))在此处区分;
    • FILEOS 0x00000004L(VOS__WINDOWS32)、FILETYPE 0x2L(VFT_DLL):声明目标系统为 Windows 32 位家族、文件类型为 DLL;
    • Source Control ID "8330923":由SOURCE_CONTROL_ID指令从../SOURCE_CONTROL_ID.txt文件(即 resources/win/api/SOURCE_CONTROL_ID.txt)读取;
  • 第二段美式英语块(LANGUAGE 0x09, 0x01):提供一份精简的VERSIONINFO,字段值与第一段一致但统一为1, 0, 0, 1;
  • VarFileInfo块:Translation 0x0409, 0x04B0,声明语言为英语(0x409)、字符集为 Windows ANSI 代码页 1252(0x04B0)。

3.2client/:模拟steamclient(64).dll的资源

目录 resources/win/client/ 用于模拟steamclient.dll与steamclient64.dll的资源,相比api/多出一项特殊资源。以 resources/win/client/64/resources.rc 为例:

  • 版本信息:FILEVERSION 8,56,38,63、OriginalFilename "Steamclient.dll"、Source Control ID "8563863"(来自 resources/win/client/SOURCE_CONTROL_ID.txt);

  • 附加资源模仿:文件末尾出现

    LANGUAGE 0x33, 0x04 1 WEVT_TEMPLATE "../WEVT_TEMPLATE1_1.bin"

    这是 Windows 事件跟踪(Event Tracing for Windows)的模板资源,内容直接引用同目录下的二进制文件 resources/win/client/WEVT_TEMPLATE1_1.bin。该资源的存在说明原版steamclient.dll中带有事件模板,模拟器构建时通过.rc引用原样嵌入该.bin数据。语言代码0x33, 0x04(即 4147,Slovak / Slovak)与前面两段不同,同样是模仿原始二进制中的语言块划分。

3.3launcher/:模拟steam.exe的资源

目录 resources/win/launcher/ 用于模拟steam.exe(Steam 启动器)的资源。以 resources/win/launcher/64/resources.rc 为例,其与 DLL 资源的关键差异在于:

  • FILETYPE 0x00000001L(VFT_APP):文件类型声明为应用程序(EXE)而非 DLL;
  • 版本信息:FILEVERSION 8,56,38,63、OriginalFilename "steam.exe"、ProductVersion "01.00.00.02"、Copyright (C) 2021 GSE。

3.4game_overlay_renderer/:模拟GameOverlayRenderer(64).dll的资源

目录 resources/win/game_overlay_renderer/ 用于模拟GameOverlayRenderer.dll(32 位)与GameOverlayRenderer64.dll(64 位)的资源。以 resources/win/game_overlay_renderer/64/resources.rc 为例:

  • 版本信息:FILEVERSION 8,74,80,95、OriginalFilename "GameOverlayRenderer.dll"、Source Control ID "8748095"(来自 resources/win/game_overlay_renderer/SOURCE_CONTROL_ID.txt);
  • 与client/相同,末尾同样带有一段LANGUAGE 0x33, 0x04的1 WEVT_TEMPLATE "../WEVT_TEMPLATE1_1.bin",即 resources/win/game_overlay_renderer/WEVT_TEMPLATE1_1.bin,说明原版GameOverlayRenderer.dll也含有事件跟踪模板资源。

3.5file_dos_stub/:构建后的 DOS Stub 改写

目录 resources/win/file_dos_stub/ 与前四组不同,它不包含.rc资源脚本,而是包含一个独立的命令行工具及其预编译产物:

  • resources/win/file_dos_stub/file_dos_stub.cpp:工具的源码;
  • file_dos_stub_x32.exe与file_dos_stub_x64.exe:两个预编译好的可执行文件。

README 将其定位为“对 DOS Stub 在构建后进行操作的模仿”。DOS Stub 是 PE 文件格式中位于 DOS 头与 PE 头之间的遗留小程序段;gbe 在此处通过改写 DOS Stub 中的特定字节,使模拟器产物在结构上更接近原版二进制(详见第六节源码剖析)。

下表汇总五组资源与目标二进制的对应关系:

目录目标二进制(x32 / x64)资源内容要点
resources/win/api/steam_api.dll/steam_api64.dllVERSIONINFO(8.33.9.23)、Source Control ID
resources/win/client/steamclient.dll/steamclient64.dllVERSIONINFO(8.56.38.63)、WEVT_TEMPLATE事件模板
resources/win/launcher/steam.exeVERSIONINFO(VFT_APP类型,8.56.38.63)
resources/win/game_overlay_renderer/GameOverlayRenderer.dll/GameOverlayRenderer64.dllVERSIONINFO(8.74.80.95)、WEVT_TEMPLATE事件模板
resources/win/file_dos_stub/全部 Windows 构建产物构建后 DOS Stub 改写工具

四、VERSIONINFO资源脚本的写法要点

gbe 的resources.rc是理解 Windows 版本资源脚本的极佳范例。综合 resources/win/api/64/resources.rc 等文件,其编写规范可归纳为:

  1. 语言块声明:每个语言块前用LANGUAGE primary, sublanguage指定,例如LANGUAGE 0, 0(Neutral)与LANGUAGE 0x09, 0x01(English - US,十进制 1033);
  2. SOURCE_CONTROL_ID SCID "路径":自定义指令,用于把外部文本文件(SOURCE_CONTROL_ID.txt)的内容嵌入为资源字符串,避免在多个.rc中重复维护版本号;
  3. VERSIONINFO块:包含FILEVERSION/PRODUCTVERSION(以逗号分隔的 4 段数值)、FILEFLAGSMASK 0x17、FILEFLAGS 0x0、FILEOS 0x00000004L、FILETYPE 0x2L/0x1L、FILESUBTYPE 0x0L等固定字段;
  4. StringFileInfo块:子块"040904b0"为语言(0409)+ 字符集(04B0)的复合键,内部用VALUE "字段名", "字符串"声明CompanyName、FileDescription、FileVersion、InternalName、LegalCopyright、OriginalFilename、ProductName、ProductVersion等可读字段;
  5. VarFileInfo块:VALUE "Translation", 0x0409, 0x04B0声明翻译表。

在 32 位版本的 resources/win/api/32/resources.rc 中,还保留了关于语言 ID 位结构的注释:

* +-------------------------+-------------------------+ * | SubLanguage ID | Primary Language ID | * +-------------------------+-------------------------+ * 15 10 9 0

并注明1033 = English - United States = 0x0409 = 0000 0100 0000 1001,方便后续维护者理解语言 ID 的二进制布局。

五、构建集成:premake5.lua中的资源选项

资源脚本并不会无条件参与所有构建,而是通过 premake5.lua 中定义的 Windows 专用选项按需启用(定义于第 159–179 行):

选项触发开关作用
dosstub--dosstub构建后改写 Windows 产物的 DOS Stub(调用file_dos_stub工具)
winsign--winsign用假证书对 Windows 产物进行签名(调用third-party/build/win/cert/sign_helper.bat)
winrsrc--winrsrc将resources/win/**/resources.rc加入各项目并编译进产物

资源文件的接入方式(以api_regular项目为例,见 premake5.lua):

filter { "system:windows", "platforms:x32", "options:winrsrc", } files { "resources/win/api/32/resources.rc" } filter { "system:windows", "platforms:x64", "options:winrsrc", } files { "resources/win/api/64/resources.rc" }

即:只有system:windows且显式传入--winrsrc时才编译资源脚本;Linux 构建(libsteam_api)则完全不会引入这些.rc文件。同样的模式还出现在client、launcher、game_overlay_renderer以及实验性 steamclient loader 等项目中([premake5.lua](https://link.gitcode.com/i/eec8626b18b65329b8778e944f10ed24#L997-L1002、L1097-L1104、L1221-L1228 等位置)。

DOS Stub 改写的后置命令定义在第 680–683 行:

filter { "system:windows", "options:dosstub", } postbuildcommands { '"' .. dos_stub_exe .. '" %[%{!cfg.buildtarget.abspath}]', }

其中dos_stub_exe指向按平台展开的预编译工具路径:

local dos_stub_exe = path.translate( path.getabsolute('resources/win/file_dos_stub/file_dos_stub_%{cfg.platform}.exe', _MAIN_SCRIPT_DIR), '\\')

由此可见,resources/win/file_dos_stub/下的两个预编译 EXE 正是为这条后置命令准备的,其源码对应的 Premake 项目tool_file_dos_stub_changer定义在 premake5.lua,产物名称为file_dos_stub_%{cfg.platform}。

六、源码剖析:file_dos_stub如何改写 DOS Stub

resources/win/file_dos_stub/file_dos_stub.cpp 是一个独立的命令行小工具,逻辑非常清晰,完整展示了在二进制层面改写 DOS Stub 的过程。

6.1 关键常量

constexpr static size_t DOS_STUB_OOFSET = offsetof(IMAGE_DOS_HEADER, e_lfanew) + sizeof(IMAGE_DOS_HEADER::e_lfanew); constexpr static const char DOS_STUB_SIGNATURE[] = "VLV"; constexpr static uint32_t DOS_STUB_ONE = 1; // what is this?
  • 写入位置:DOS Stub 内容从IMAGE_DOS_HEADER中e_lfanew字段之后开始。e_lfanew是 DOS 头中指向 PE 头偏移的 4 字节字段,紧随其后的区域(自文件头起 0x40 处)正是经典 DOS Stub(如 “This program cannot be run in DOS mode” 消息)的存放位置,因此把改写位置固定在这个偏移是符合 PE 格式布局的;
  • 写入内容:依次写入 4 字节签名'V' 'L' 'V' '\0'、一个固定值1(源码注释为 “what is this?”,即该字段含义未完全确认,只是模仿原版二进制的既有值)、以及 PE 镜像大小。

6.2 主流程

  1. 参数解析:工具接受一个或多个二进制文件路径(argc < 2时报错退出);

  2. 读取文件:以二进制读写模式(in | out | binary)打开文件,并通过std::filesystem::u8path处理 Unicode 路径;

  3. 获取 PE 大小:借助pe_helpers::get_pe_size(来自 helpers/pe_helpers/pe_helpers.hpp,源码见 helpers/pe_helpers.cpp)解析 PE 头得到镜像大小;出于性能考虑,仅读取文件前 1MB(注释 “1MB is enough”)用于解析;

  4. 定位与写入:file.seekp(DOS_STUB_OOFSET, std::ios::beg)定位到 DOS Stub 起始偏移,依次写入:

    // 4 bytes: 'V' 'L' 'V' '\0' file.write(DOS_STUB_SIGNATURE, sizeof(DOS_STUB_SIGNATURE)); // 4 bytes: (uint32_t)1 file.write((const char *)&DOS_STUB_ONE, sizeof(DOS_STUB_ONE)); // 4 bytes: PE image size file.write((const char *)&pe_size, sizeof(pe_size));
  5. 多文件循环:对所有传入参数依次处理,任何一步失败(打不开、取大小失败、解析 PE 失败)都会立即报错并返回 1。

从源码结构可以推断,这套改写的目的是让模拟器产物的 DOS Stub 区域带有与原始 Steam 二进制一致的标记字节序列,从而在更深层的结构检查中不被轻易区分——但具体每个字段的业务含义,仓库源码并未完整注释(如DOS_STUB_ONE处保留了疑问注释),本文不做超出源码的断言。

七、验证与自查:如何确认资源已生效

构建完成后,可以从两个层面验证资源注入结果:

  1. 查看产物文件版本信息:在 Windows 资源管理器中右键构建出的steam_api64.dll等文件 → “属性” → “详细信息”,若已带--winrsrc构建,应能看到与resources.rc声明一致的“文件版本”“公司”等字段(如 08.33.09.23 / GSE / Steamclient.dll 等);
  2. 检查中间文件:按 README 所述,rc.exe的编译产物应位于build\tmp\win\rsrc目录下,文件为*.res;确认这些.res存在即说明资源编译环节正常执行;
  3. 检查 DOS Stub:可用十六进制查看器(或pe_helpers类解析工具)读取产物偏移 0x40 处的内容,确认存在VLV签名、01 00 00 00与 4 字节 PE 大小——这是--dosstub后置命令成功执行的标志。

八、适用前提与注意事项

  • 平台前提:resources/全部内容只服务于 Windows 构建(os.target() == "windows"分支),Linux / SteamOS 构建产物(libsteam_api)不涉及资源注入,无需关注本节内容;
  • 选项前提:资源脚本仅在传递--winrsrc时参与编译,DOS Stub 改写仅在传递--dosstub时执行,二者互不依赖、可单独启用;
  • 工具链依赖:资源编译依赖 Windows 环境中的微软工具链(rc.exe、cl.exe),跨平台(如 Linux 上用 wine 交叉构建)场景下未在 README 中说明相应替代方案;
  • 信息边界:VERSIONINFO中的版本号、WEVT_TEMPLATE内容、DOS Stub 中的VLV标记与固定值1,均来自对原始 Steam 二进制的模仿与仓库内现有数据,本文未对超出源码可确认范围的字段含义作进一步推测。

结语

resources/目录虽然体量不大,却是 gbe 模拟器在 Windows 端“形似”原版 Steam 组件的关键一环:通过VERSIONINFO资源脚本维持版本信息一致、通过WEVT_TEMPLATE与SOURCE_CONTROL_ID补齐附加资源、再通过file_dos_stub工具在构建后改写 DOS Stub 标记。结合 resources/README.md、各resources.rc与 premake5.lua 中winrsrc/dosstub选项的对照阅读,即可完整复现并理解这套 Windows 构建资源流水线。

  • 游戏开发
  • 逆向工程

【免费下载链接】gbe_fork

Fork of https://gitlab.com/Mr_Goldberg/goldberg_emulator

项目地址:https://gitcode.com/gh_mirrors/gbe/gbe_fork
点击查看免费下载

相关推荐

上一篇:LizzieYzy:围棋AI分析工具,让每个棋手都拥有职业级复盘教练
下一篇:免费聚合六大音乐平台:洛雪音乐桌面版跨平台使用指南

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/4 13:45:58

MRAM与PIC18F4525工业存储方案:掉电保护与高频写入实践

1. 项目缘起与方案选型思考工业现场的数据存储有个很尴尬的处境&#xff1a;用 EEPROM 吧&#xff0c;写入速度慢、擦写寿命也就百万次级别&#xff0c;高频采集场景下没几个月就写废了&#xff1b;用 SRAM 加后备电池吧&#xff0c;电池在高温高湿的车间里撑不了几年&#xff…

作者头像 李华
网站建设 2026/10/4 13:45:17

脑电软件BrainVision Recorder与Analyzer 2.2安装避坑指南

做脑电实验的人应该都懂&#xff0c;BrainProducts 这套软件几乎是绕不开的标配。BrainVision Recorder 负责把放大器采到的脑电信号实时收进来&#xff0c;BrainVision Analyzer 2.2 则是离线分析的主力工具&#xff0c;从滤波、分段到 ICA 去伪迹&#xff0c;绝大多数常规流程…

作者头像 李华
网站建设 2026/10/4 13:44:21

一秒学会!!!2025离线安装vscode插件 vsix 全流程避坑指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/4 13:43:13

STM32上跑MQTT:五款嵌入式客户端实现横向对比与选型指南

MQTT 在 STM32 这类资源受限的 MCU 上跑&#xff0c;从来不是"能不能跑"的问题&#xff0c;而是"跑哪个、怎么跑、跑完还剩多少 RAM 和 Flash"的问题。我前后在 STM32F103、F407、F429、H743 这几块板子上折腾过至少五种 MQTT 客户端实现&#xff0c;从最早…

作者头像 李华
网站建设 2026/10/4 13:42:10

AI工程从零开始:提示词、RAG与Agent的工程化落地指南

这几年AI领域最明显的变化&#xff0c;就是“会调API”和“能做AI工程”之间的鸿沟被迅速拉大了。尤其当大模型本身变得唾手可得之后&#xff0c;真正的瓶颈已经不是模型能力&#xff0c;而是围绕模型构建系统的那套工程方法——这正是“ai-engineering-from-scratch”这个主题…

作者头像 李华