Rolaand-Jayz/RE-of-FSR-4.1.0-Upscaling

GitHub: Rolaand-Jayz/RE-of-FSR-4.1.0-Upscaling

该项目是对 AMD FSR 4.1.0 神经网络放大器进行的静态逆向工程研究,记录了 DXIL 着色器目录、权重提取、流水线调度分析和数据布局重建。

Stars: 4 | Forks: 0

# FSR 4.1.0 — 静态逆向工程笔记 本仓库记录了对 FSR 4.1.0 时序放大器的静态结构分析:提取的权重 blob、shader/pass 目录、数据布局重建,以及基于 Ghidra、DXIL 和原始 x86-64 反汇编的 provider-DLL 调度分析。 ## 快速链接 - **[RELEASE_NOTES.md](RELEASE_NOTES.md)** — 本版本包含和不包含的内容。 - **[HOSTILE_REVIEWER_START_HERE.md](HOSTILE_REVIEWER_START_HERE.md)** — 如果您认为本仓库存在过度声明,请从这里开始看。 - **[CURRENT_STATUS.md](CURRENT_STATUS.md)** — 一目了然的真值表。 - **[REPRODUCING.md](REPRODUCING.md)** — 如何验证结果。 ## 声明上限 本版本仅证明**静态提取和静态结构分析。** 它**不**证明: - runtime pass 顺序 - runtime descriptor 绑定 - runtime CBV 值 - runtime tensor offset 使用 - 与 AMD 的 DLL 的功能等效性 - 作为替代实现的可部署性 AMD 的 FidelityFX Super Resolution 4.1.0 以编译后的 Windows DLL 形式发布,其中包含一个神经网络放大器。网络的权重是不透明的二进制 blob。流水线架构未公开。shader 调度序列隐藏在多层 API 抽象之后。 包含的内容: 1. **数据 DLL 研究** — 重建的 C 源代码和嵌入的提取权重数据。历史上用于复制原始 PE 区域的 post-link patcher 已从证明路径中移除;复制原始字节后的 MD5 相等性不被声称作为独立的重建证据。 使用 `rebuild/compare_sections.py` 作为当前节区比较工具,它报告哈希和差异而不修改重建输出。有关比较能证明什么和不能证明什么,请参见 [`rebuild/section-comparison-explainer.md`](rebuild/section-comparison-explainer.md)。 2. **Provider DLL** — 反汇编了 Ghidra 无法反编译的 PSO 创建函数 (`FUN_180025990`)。解码了跳转表、标志索引表和 pass descriptor 表。将 30 个唯一的 shader blob(通过 MD5 哈希确认)映射到 pass 索引。从 LLVM IR 中提取了资源绑定布局。 本仓库是一份研究记录:分析、死胡同、方法论、提取的神经网络权重、流水线规范和工具。Runtime 执行仍未经验证;确切的每 pass 算术、buffer-address 推导和 weight-index 映射仍然是研究空白,除非在特定文档中另有明确说明。 ## 简短版本 | 发现 | 细节 | 状态 | 证据 | 声明 ID | |:---|:---|:---|:---|:---| | **编目并分类了 602 个 DXBC shader blob** | 对每个嵌入式 compute shader 的完整枚举 | ✅ STATIC-REPRODUCIBLE | 自动提取 + 人工审核 | — | | **27 个模型家族 entrypoint + 3 个可选的 host slot** | DXIL 清单包含 `prepass`, `pass1..pass12`, `pass0_post..pass12_post`, `postpass`;host descriptor 表还有可选的 `rcas`, `spd_autoexposure`, `debug_view` slot | ✅ STATIC-REPRODUCIBLE | 二进制哈希比较 + descriptor-table 分析 | `dxil_entrypoints` | | **提取了 6 个权重 blob,每个 131,072 字节** | 5 个相同 (quality/balanced/performance/ultraperf/native),1 个唯一 (DRS) | ✅ STATIC-REPRODUCIBLE | MD5 哈希比较 | `blob_extraction` | | **解码了权重容器格式** | 7,208B FP16 bias → 122,880B 类 FP8/uint8 权重 → 888B 额外数据(222 个 FP32 输出 bias) → 96B 填充 | ✅ STATIC-REPRODUCIBLE | 解析了容器,验证了数值,所有 blob 的 offset 均匹配 | `extra_fp32_region` | | **静态 provider 调度结构:27 次循环 + 可选的 SPD/RCAS/Debug** | 在 27 次 pass 循环之前可选地执行 SPD AutoExposure,然后在其后可选地执行 RCAS + Debug View;descriptor-slot 顺序是分开的 | 二进制控制流 STATIC-REPRODUCIBLE;每帧顺序 RUNTIME-NOT-OBSERVED | 调度函数 + host-cbuffer 分析 | `pipeline_dispatch_order` | | **所有 pass 使用 (32,1,1) 线程组** | Wavefront 宽度的 1D 调度 | ✅ STATIC-REPRODUCIBLE | LLVM IR `!dx.numthreads` 元数据 | — | | **静态资源绑定映射** | 识别出 9 个具有拟议语义含义的寄存器空间 | ⚠️ STATIC-INFERRED | 跨 LLVM IR blob 的 createHandle 分析;未捕获 runtime descriptor 绑定 | — | | **解码了常量缓冲区布局** | 每个 pass 5-6 个寄存器 × 4 个浮点数(80-96 字节);奇偶配对模式 | ✅ STATIC-REPRODUCIBLE | 来自 LLVM IR 的 cbufferLoadLegacy 寄存器索引 | — | | **静态推断拓扑** | 4.0.2 的 Pre/Body/Post 似乎被 Encoder/Orchestration/Body/Decoder 流水线取代 | ⚠️ STATIC-INFERRED | 从 shader 分类 + offset 映射推断得出 | `architecture_topology` | | **Runtime pass 顺序** | GPU 上实际的每帧调度序列 | ❌ RUNTIME-NOT-OBSERVED | 无原生 D3D12 捕获;Proton 阻止了所有尝试 | `runtime_pass_order` | | **Runtime CBV 值** | 执行期间每个 pass 的常量缓冲区值 | ❌ RUNTIME-NOT-OBSERVED | 无原生 D3D12 捕获 | `runtime_cbv_values` | | **Runtime descriptor 绑定** | 每次调度实际使用的 GPU descriptor-table 绑定 | ❌ RUNTIME-NOT-OBSERVED | 无原生 D3D12 捕获 | `runtime_descriptor_bindings` | | **Runtime tensor-offset 使用** | cbuffer 加载的 offset 在 runtime 是否与静态推断匹配 | ❌ RUNTIME-NOT-OBSERVED(静态 offset:PLAUSIBILITY-CHECK) | 无 runtime 捕获;来自 4.0.2 HLSL 源码的静态 offset | `runtime_tensor_offsets` | | **权重加载方式已更改** | 4.0.2 将权重嵌入 shader;4.1.0 使用 InitializerBuffer 进行动态加载 | ✅ STATIC-REPRODUCIBLE | 与基于 MIT 许可证的 4.0.2 源码进行结构比较 | — | | **数据节区重建工具** | 重建的 C 源码 → MinGW 交叉编译 → 节区比较 | BOUNDED-REBUILD | 节区哈希单独报告;复制字节的 MD5 相等性不用作证明 | `data_dll_reconstruction` | | **解码了 PSO 创建函数** | FUN_180025990 通过原始 x86-64 反汇编解码:跳转表 + 标志索引表 + 30 项 descriptor 表 | ✅ STATIC-REPRODUCIBLE | objdump 反汇编 + PE 节区解析 | — | ### 证据标签 - ✅ **STATIC-REPRODUCIBLE** — 可从提交的静态工件重现(哈希匹配、二进制分析、交叉引用) - ⚠️ **STATIC-INFERRED** — 从反汇编/反编译推断;未通过 runtime instrumentation 确认 ## 主 DLL:流水线调度分析 主 provider DLL(`amd_fidelityfx_upscaler_dx12.dll`,15.6MB)是系统的另一半。`fsr_data.dll` 存储权重的地方,由 provider DLL 运行神经网络。我们通过 Ghidra 反编译和原始 x86-64 反汇编追踪了静态调度流水线。 ### 调度架构 核心调度函数 (`FUN_18000d5b0`) 运行一个硬编码的 27 次迭代循环: ``` // Decompiled from Ghidra lVar24 = 0x1b; // 27 do { dispatch_per_pass(context, pass_state, threadsX, threadsY); lVar24--; } while (lVar24 != 0); ``` Descriptor-slot 名称和 runtime 调度顺序是不同的。在 `FUN_18000d5b0` 中,静态调度顺序为: - 当启用 auto exposure 时,在 27 次 pass 循环之前执行 **SPD AutoExposure** - **27 个模型循环 pass**(`pass_0` .. `pass_26`) - 当启用锐化时,在循环之后执行 **RCAS** - 当启用 debug 标志时,在循环之后执行 **Debug View** 有关 descriptor-index 分类与 DXIL entrypoint 分类的对比,请参见 [`docs/pass-index-to-entrypoint-map.md`](docs/pass-index-to-entrypoint-map.md)。 ### PSO 创建:FUN_180025990 Ghidra 未能反编译此函数(反编译了 344 个函数;此函数不在其中)。我们从原始二进制文件中对其进行了反汇编并解码了: | 结构 | RVA | 详情 | |:---|:---|:---| | 跳转表 | `0x2acac` | 30 个项,分发到各 pass 代码 | | 标志索引表 | `0x1bfc50` | 64 个项,将配置标志映射到 descriptor 索引 (0-29) | | Pass descriptor 表 | `0x115cf0` | 30 个项 × 296 字节 (`imul rcx, rax, 0x128`) | 每个 descriptor 条目包含:shader blob 大小 (DWORD)、DXBC shader 指针 (QWORD),然后是 7 个绑定组,每组 40 字节。 ### 30 个唯一的 shader blob 所有 30 个 pass shader 都是唯一的,由 MD5 哈希确认: | Pass | 名称 | 大小 | MD5 | |------|------|------|-----| | 0 | pass_0 | 19,820 | fa813b1501c7 | | 1 | pass_1 | 19,884 | 98e3a4b72437 | | 2 | pass_2 | 21,648 | d99b11628f3b | | 3 | pass_3 | 21,584 | 76fdac8ca3c1 | | 4 | pass_4 | 23,336 | abb9cec4c47d | | 5 | pass_5 | 26,276 | cb3ad940c94a | | 6 | pass_6 | 23,404 | 37998fc070d8 | | 7 | pass_7 | 26,208 | 6859bda414df | | 8 | pass_8 | 21,740 | a52c653d1f5c | | 9 | pass_9 | 21,676 | 564efec2abd2 | | 10 | pass_10 | 24,856 | 4a85f5300bf4 | | 11 | pass_11 | 24,788 | 0114b73c945c | | 12 | pass_12 | 21,984 | 33e2e5c24e0d | | 13 | pass_13 | 21,920 | c287b4976479 | | 14 | pass_14 | 18,500 | 8b335e65c091 | | 15 | pass_15 | 18,436 | 3829ef86bf37 | | 16 | pass_16 | 20,456 | 3fbb8b9bd3ad | | 17 | pass_17 | 20,388 | e144590c77dd | | 18 | pass_18 | 20,552 | 7756f6dd0508 | | 19 | pass_19 | 20,480 | aeaf1b44d0b2 | | 20 | pass_20 | 26,276 | 4d61c10addc6 | | 21 | pass_21 | 26,208 | c12ca5dfa20c | | 22 | pass_22 | 23,404 | c6930a995aab | | 23 | pass_23 | 19,884 | 5943b34bd00e | | 24 | pass_24 | 19,820 | a00a487f3be2 | | 25 | pass_25 | 23,336 | 7e95f96376a7 | | 26 | pass_26 | 21,648 | f1079dce7c99 | | 27 | rcas | 21,584 | 5c6397e93889 | |28 | spd_autoexposure | 21,740 | 1f4a33f858b4 | | 29 | debug_view | 21,676 | 4bf4d0a6886c | **唯一:30/30,重复:0** ### 资源绑定映射 每个 pass 都使用相同的 UAV 和 CBV 布局。SRV 数量因 pass 类型而异 (5-8)。 **通用资源(所有 30 个 pass):** | 类型 | 寄存器 | 空间 | 用途 | |------|----------|-------|---------| | SRV | 0 | 18 | 神经网络权重(来自 fsr_data.dll) | | SRV | 1 | 0 | 输入颜色 | | SRV | 2 | 1 | 运动向量 / 重投影 | | SRV | 4 | 3 | 历史 / 时序数据 | | SRV | 5 | 4 | 曝光 / luma mip | | UAV | 0 | 11 | Scratch buffer(依赖于分辨率) | | UAV | 1 | 0 | 输出颜色 | | UAV | 2 | 3 | Temp / recurrent 输出 | | CBV | 0 | 1 | 常量缓冲区(80-96 字节) | | Sampler | 0 | 0 | Point clamp | **条件资源(特定 pass):** | 类型 | 寄存器 | 空间 | 存在于 | |------|----------|-------|------------| | SRV | 3 | 2 | Luma/mask(除 pass 10-15 外的所有 pass) | | SRV | 6 | 6 | 额外特征图 (pass 8, 9, 18, 19, 28, 29) | | SRV | top | 17 | 中间数据(具有 7+ 个 SRV 的 pass) | ### 常量缓冲区布局 每个 pass 从单个 CBV(寄存器 0,空间 1)读取 5 或 6 个寄存器。每个寄存器为 4 × float32 = 16 字节。 | 寄存器 | 偶数 pass(“pre”) | 奇数 pass(“post”) | |-----|---------------------|---------------------| | 0 | 调度维度 / 逆分辨率 | 调度维度 / 逆分辨率 | | 1 | 抖动 / 子像素 offset | 抖动 / 子像素 offset | | 2 | 缩放 / 变换参数 | 缩放 / 变换参数 | | 3 | — (未访问) | 额外参数 | | 4 | 附加配置 | 附加配置 | | 5 | 附加配置 | 附加配置 | - 偶数 pass:5 个寄存器 × 16B = **80 字节** - 奇数 pass:6 个寄存器 × 16B = **96 字节** ### Pass 配对结构 27 个主要 pass 形成 13 个 pre/post 对 + 1 个独立 pass: | 对 | Pre Pass | Post Pass | SRV | |------|----------|-----------|------| | 0 | pass_0 (偶数) | pass_1 (奇数) | 6 | | 1 | pass_2 | pass_3 | 7 | | 2 | pass_4 | pass_5 | 6 | | 3 | pass_6 | pass_7 | 6 | | 4 | pass_8 | pass_9 | 8 | | 5 | pass_10 | pass_11 | 5 | | 6 | pass_12 | pass_13 | 5 | | 7 | pass_14 | pass_15 | 5 | | 8 | pass_16 | pass_17 | 6 | | 9 | pass_18 | pass_19 | 7 | | 10 | pass_20 | pass_21 | 6 | | 11 | pass_22 | pass_23 | 6 | | 12 | pass_24 | pass_25 | 6 | | 独立 | pass_26 | — | 7 | 奇偶拆分对应于 AMD 的命名约定:`pass0` + `pass0_post`、`pass1` + `pass1_post` 等。 ### Pass 对称性 相同的 blob 大小暗示了自编码器对称性(编码器/解码器镜像): - pass_0 ↔ pass_24(各 19,820 字节) - pass_1 ↔ pass_23(各 19,884 字节) - pass_4 ↔ pass_25(各 23,336 字节) - pass_5 ↔ pass_20(各 26,276 字节) - pass_6 ↔ pass_22(各 23,404 字节) - pass_7 ↔ pass_21(各 26,208 字节) 完整分析:[`reports/main_dll_analysis.md`](reports/main_dll_analysis.md)。机器可读规范:[`spec/pipeline_spec.json`](spec/pipeline_spec.json)。 ## Runtime 捕获:三次尝试,没有银弹 Runtime 捕获——通过实时观察放大器执行来确认我们的静态分析——是一个主要目标,我们为此投入了大量的工程精力。构建并部署了三种不同的方法,在运行于 CachyOS 并通过 Proton/VKD3D 运行 FF7 Rebirth 的系统上进行测试。这三种方法都遇到了阻碍。 这不是一个失败记录。它记录了真实的工程工作,即使主要目标(在 runtime 确认 27 次 pass 的调度序列)未能实现,这些工作也产生了有用的工具。这些工具存在、可以编译,并且都在仓库中——如果你能解决 Proton 层次结构的问题,它们就能工作。 ### 尝试 1:FFX 代理 DLL (`ffx_capture_proxy.c`) **方法:** 用一个代理替换 AMD 的 `amd_fidelityfx_upscaler_dx12.dll`,该代理拦截 5 个导出的 FFX API 函数(`ffxConfigure`、`ffxCreateContext`、`ffxDispatch`、`ffxDestroyContext`、`ffxQuery`)。在 API 级别记录参数和原始 descriptor 字节。 **构建方式:** 在 Linux 上通过 MinGW 交叉编译 → `ffx_proxy.dll` **状态:** ⚠️ 已编写并编译。未部署用于实时捕获(需要重命名原始 DLL 并将代理放在游戏目录中,我们在分析阶段没有尝试这样做)。 该代理捕获 API 级别的参数——调度 descriptor、画质预设、上下文创建——但不捕获实际的 GPU 资源绑定。它可以确认发生了*多少次*调度,但不能确认每次调度访问了*什么数据*。 ### 尝试 2:Vulkan LD_PRELOAD Shim (`fsr4_capture.c`) **方法:** 一个轻量级的 `.so`,通过 `LD_PRELOAD` 挂钩 `vkCmdDispatch`、`vkCmdBindDescriptorSets` 和 `vkCmdBindPipeline`。没有帧捕获,也没有 RenderDoc 开销——只有一个调度日志。专门为 FSR4 的 27+ 个 compute pass 设计,因为完整捕获会导致游戏冻结。 **构建方式:** 在 CachyOS 上执行 `gcc -shared -fPIC -O2 -o fsr4_capture.so fsr4_capture.c -ldl` **本地路径示例:** `/runtime-capture/fsr4_capture.so` **启动选项:** ``` LD_PRELOAD=/runtime-capture/fsr4_capture.so PROTON_FSR4_UPGRADE=1 DXIL_SPIRV_CONFIG=wmma_rdna3_workaround WINEDLLOVERRIDES=version=n,b %command% ``` **结果:** ❌ 该 shim 已加载(由 `dispatch_log.txt` 中的 `[INIT] FSR4 capture shim loaded` 确认),但未生成调度数据。`LD_PRELOAD` 挂钩传播到了 Proton 的 wine 进程中,但 `vkCmdDispatch` 拦截从未触发。 可能的原因:VKD3D-Proton 的 Vulkan 调度所经过的代码路径并未路由通过标准的 `vkCmdDispatch` 符号,或者该挂钩的 `dlsym` 解析在 Proton 加载器内部失败。 **经验教训:** `LD_PRELOAD` 确实可以存活并进入 Proton 的 wine 进程(init 消息为此提供了记录证据),但通过 VKD3D 转换来挂钩 Vulkan 调用是不可靠的。该挂钩在宿主进程中触发,但不一定在 wine Vulkan 包装器内部触发。 ### 尝试 3:RenderDoc 完整捕获 **方法:** 通过隐式层进行标准的 RenderDoc Vulkan 捕获。需要 `ENABLE_VULKAN_RENDERDOC_CAPTURE=1`(不是 `ENABLE_VULKAN=1`——那是一个死胡同,耗费了一次调试时间)。 **启动选项:** ``` ENABLE_VULKAN_RENDERDOC_CAPTURE=1 RENDERDOC_CAPTUREFILE=/runtime-capture/fsr4_ff7r VKD3D_DEBUG=trace VKD3D_SHADER_DUMP_PATH=/runtime-capture/vkd3d-shaders %command% ``` **结果:** ❌ RenderDoc 通过 VKD3D 挂钩每一个 Vulkan 调用。FSR4 每帧调度 27+ 个 compute pass。捕获文件变得极其庞大,游戏会冻结,最终在 120 秒超时后崩溃。完整的 RenderDoc 捕获与 FSR4 的调度密度不兼容。 **经验教训:** FSR4 的神经网络推理对于完整帧捕获工具来说负担太重。轻量级方法(shim、日志)是唯一可行的路径。 ### 附带发现:VKD3D Shader Dump 在 RenderDoc 和 shim 尝试期间设置 `VKD3D_SHADER_DUMP_PATH` 产生了 shader dump——但它们是**辅助后处理 shader**(类似 RCAS 的锐化、深度/运动自适应 pass),而不是 FSR4 神经网络核心。 核心模型 shader 通过 VKD3D 不会 dump 的另一条路径编译,或者它们是直接嵌入在 DLL 中的预编译 SPIR-V。 ### 诊断工具 编写了一个完整的诊断脚本 (`scripts/capture/diagnose_capture.sh`),用于检查所有先决条件:RenderDoc 库、Vulkan 层注册、shim 编译、代理 DLL 状态、FF7R DLL 检测和 OptiScaler 存在状态。它还会打印所有三种方法的更正后的捕获说明。 ### 这对项目意味着什么 Runtime 捕获的空白是客观存在的。我们的流水线分析现在来自于: - ✅ STATIC-REPRODUCIBLE:C++ 调度函数的 Ghidra 反编译(确认了 27 次迭代循环) - ✅ STATIC-REPRODUCIBLE:PSO 创建函数的原始 x86-64 反汇编(Ghidra 无法将其反编译) - ✅ STATIC-REPRODUCIBLE:30 个唯一的 shader blob 通过 MD5 哈希映射到 pass 索引 - ✅ STATIC-REPRODUCIBLE:从 LLVM IR 提取的完整资源绑定布局 - ✅ STATIC-REPRODUCIBLE:从 cbufferLoadLegacy 模式提取的常量缓冲区寄存器映射 - ⚠️ RUNTIME-NOT-OBSERVED:Runtime 调度序列未确认(Proton 阻止了所有捕获方法) 数据 DLL 的重建和逐节区的比较支持我们的**数据提取/布局**声明,但并不能证明完整的二进制重建。二进制分析支持已发布的**流水线结构**声明。 尚未确认的部分在于实际的 runtime 执行是否与静态分析相符——而 Proton 阻止了迄今为止的捕获尝试。 ## 我们没有做的事情 诚实的文档意味着展示这些空白: 0. **没有帧生成分析。** FSR 4.1.0 同时附带了时序放大器和帧生成功能。这份 RE(逆向工程)**仅涵盖放大器**。帧生成是一个独立的流水线,拥有自己的 shader、自己的调度逻辑和自己的资源管理。我们没有对其进行分析。 这个范围是刻意为之的:放大器是机器学习组件,理解它是我们的目标。 1. **没有 runtime 验证。** 游戏需要 Proton。Proton 的 VKD3D 转换层吸收了所有的挂钩、shim 和捕获工具。我们无法通过观察代码执行来确认静态分析。这是目前最大的可信度空白。 构建并部署了三种独立的捕获方法——全部被 Proton 的 Vulkan 转换层阻挡。**需要具有原生 Windows + D3D12 访问权限的人进行 runtime 验证,以填补这一空白。** 请参阅下方的[验证状态](#validation-status--call-for-collaborators)。 2. **没有位级完全相同的 provider DLL 重建。** 这个 15.6MB 的 provider DLL 包含已编译的 C++ 代码、链接库和 DXBC shader 容器。要逐位重现它,需要 AMD 确切的构建环境、编译器版本和 shader 编译器。这里的证据是结构分析,而不是 provider 二进制文件的重建。 3. **CBV 字段语义部分未知。** 我们知道访问了哪些寄存器以及提取了哪些组件 (x/y/z/w)。我们不知道每个浮点值的确切语义含义。 4. **Root signature 二进制格式未完全解码。** Descriptor 条目中的绑定组是 FFX 内部结构,而不是原始的 D3D12_ROOT_PARAMETER 结构。我们改为解码了 shader 级别的资源绑定。 5. **逐指令的推理操作未完全解码。** 我们解码了激活函数(ReLU,通过 DXIL IR 中的 FMax 实现)、FP8 权重解码机制( coherent atomic buffer/O)、pass 复杂度层级以及时序状态流(历史缓冲区反馈)。 我们**没有**在单个指令级别解码每一次矩阵乘法或卷积。高层架构已被理解;每个 pass 内部的逐像素算术是从 IR 模式推断出来的,而不是逐条指令追踪的。 ``` fsr-re/ ├── README.md You are here. Narrative + findings. ├── LEGAL.md RE methodology, AMD history, legal positioning. ├── LICENSE MIT License. ├── VALIDATION_STATUS.md Honest assessment of what is proven vs inferred. ├── verification-report.json Machine-readable verification results. ├── .gitignore │ ├── docs/ Technical documentation. │ ├── IMPLEMENTATION_GUIDE.md Implementation research notes for the neural upscaler. │ ├── activation-lut-analysis.md FP8 decode and activation function analysis. │ ├── adversarial-review-2.md Adversarial review — challenges assumptions. │ ├── architecture.md Network topology, layer details, channel flow. │ ├── extra-params-analysis.md Analysis of the 222 extra FP32 output parameters. │ ├── methodology.md Full methodology narrative — including dead ends. │ ├── offset-mapping.md Complete tensor offset table. │ ├── pipeline-dispatch.md Provider DLL dispatch analysis. │ ├── shader-internals.md Neural architecture + FP8 decode analysis. │ ├── static-analysis.md Ghidra decompilation findings. │ └── weight-extraction.md How weights were found and extracted. │ ├── spec/ Machine-readable specifications. │ ├── tensor-map.json Complete tensor offset table (78 tensors). │ ├── blob-format.json Binary layout specification. │ ├── pipeline_spec.json Full 30-pass pipeline spec. │ └── shader_analysis.json 28-pass shader analysis. │ ├── reports/ Analysis reports and data. │ ├── 00-final-re-report.md Complete RE report — the authoritative document. │ ├── main_dll_analysis.md Provider DLL dispatch analysis. │ ├── architecture-map-v410.md Architecture mapping. │ ├── provider-diff-report.md Provider layer diff (4.0.2 vs 4.1.0). │ ├── ml2code-runtime-diff.md Operator comparison. │ ├── tensor-verification-report.md Offset-map verification status. │ ├── pass-catalog.json Full pass catalog (machine-readable). │ └── v410_independent_offsets.json Independent offset map. │ ├── rebuild/ Data DLL rebuild and section comparison. │ ├── README.md Build instructions and bounded verification notes. │ ├── fsr_data.c Reconstructed C source from disassembly. │ ├── fsr_data.def PE export definitions. │ ├── compare_sections.py Section comparison tool; reports per-region hashes. │ ├── pe_patcher.py Deprecated; exits immediately. Use compare_sections.py. │ ├── build.sh Full build + verify script. │ ├── fsr_data_prepatch.dll Independently rebuilt DLL (893,019 bytes). │ └── run_rebuild_section_comparison.sh Compile + compare wrapper (renamed from full_rebuild_proof.sh). │ ├── extracted/ Weight blobs — the neural network data. │ ├── v410_initializers/ 6 blobs × 131,072 bytes (4.1.0 weights). │ ├── v402_initializers/ 7 blobs (4.0.2 weights for comparison). │ ├── fp8_initializers/ Raw FP8 initializer blobs. │ └── fp8_weights/ Per-tensor extracted weights (v402 + v410). │ ├── scripts/ Analysis and verification tools. │ ├── dll_analysis.py DLL structure enumeration. │ ├── extract_blobs.py Weight blob extraction from PE sections. │ ├── weight_encoding.py FP8/FP16 encoding analysis. │ ├── fp8_extract.py FP8 weight extraction and validation. │ ├── parse_weights.py Weight blob parsing and display. │ ├── weight_compare.py Cross-blob comparison and hashing. │ ├── parse_offsets.py Tensor offset extraction. │ ├── layer_diff.py Layer-by-layer diff between blobs. │ ├── parse_v410_dxil.py DXIL shader parsing for 4.1.0. │ ├── trace_cbuffer.py Constant buffer layout tracing. │ ├── verify.py Verification suite. │ ├── verify_tensor_offsets.py Tensor offset validation. │ ├── disasm_all_dxil.py Mass DXBC → DXIL disassembly. │ └── capture/ Runtime capture scripts. │ ├── capture-tools/ Runtime capture tooling. │ ├── analyze_capture.py Post-capture analysis pipeline. │ ├── extract_dispatches.py Dispatch extraction from RenderDoc captures. │ └── capture-guide.md Capture method documentation. │ ├── runtime-capture/ Capture artifacts from runtime attempts. │ ├── dispatch_log.txt Vulkan LD_PRELOAD shim log. │ └── fsr4_capture.so Compiled dispatch shim. │ ├── tools/ Capture tools (written, deployed, not fully successful). │ ├── README.md Setup and usage guide. │ ├── ffx_capture_proxy.c FFX API capture proxy. │ ├── ffx_d3d12_capture.c D3D12 command capture. │ ├── fsr4_capture.c Vulkan dispatch shim for Proton/Linux. │ └── setup_capture.sh Build + inject script. │ └── Not in public repo (excluded via .gitignore — see LEGAL.md): ├── build/ DXBC blobs + LLVM IR (1187 .ll files). Derived │ from proprietary shaders. ├── ghidra-decompile/ 344 decompiled C functions. Proprietary data. └── ghidra-project/ Ghidra project database. Proprietary data. ``` ## 验证状态与呼吁合作者 本项目是一项实质性的静态分析,但并非完整的 runtime 验证。关于权重、pass 结构和静态资源绑定的声明都有本仓库中的二进制分析证据作为支持;runtime 调度顺序、CBV 值和实际的 descriptor 绑定仍然需要原生的 D3D12 确认。 但是**静态分析并不等于 runtime 证明**。27 次 pass 的调度序列、常量缓冲区值以及执行期间实际的 GPU 资源绑定从未被实时观察过。我们尝试了三种捕获方法(FFX 代理 DLL、Vulkan LD_PRELOAD shim、RenderDoc 完整捕获)——全部在 Linux 上被 Proton 的 VKD3D 转换层阻挡。 ### 需要什么 在**原生 Windows 环境下使用 D3D12** 进行 runtime 验证将填补这一空白。具体来说: 1. **捕获 27 次 pass 的调度序列** — 在原生 Windows(无 Proton 层)上使用 PIX、RenderDoc 或 D3D12 捕获工具确认循环计数和 pass 顺序。 2. **验证常量缓冲区内容** — 确认每个 pass 的 CBV 值与静态分析相匹配。 3. **确认权重缓冲区绑定** — 验证 InitializerBuffer 是否按预期进行了绑定。 这不是一项巨大的工作——对于拥有合适硬件和访问权限的人来说,只需几个小时的捕获工作。但这需要一台配备 AMD GPU 的 Windows 机器,以及一款运行 FSR 4.1.0 的游戏。 ### 我们需要的人 少数的**贡献者**(2-3 人),具备以下条件: - 原生 Windows + AMD RDNA 3/4 系统 - 一款运行 FSR 4.1.0 的游戏(FF7 Rebirth,或任何使用独立放大器 DLL 的游戏) - 熟悉在 D3D12 上使用 RenderDoc 或 PIX - 愿意分享捕获数据(不需要源代码——只需要调度日志和 cbuffer dump) 如果您符合条件,请提交一个 issue。这项工作值得被验证,而验证会让它变得更有说服力。 ## FSR 4.0.2 源码参考 我们对 FSR 4.1.0 的逆向工程使用了基于 MIT 许可证的 **FSR 4.0.2 源代码**作为结构参考。AMD 在 [GPUOpen](https://gpuopen.com/) 上以 MIT 许可证发布了此代码——明确允许将其用于任何用途,包括分析和互操作性研究。 4.0.2 源码在以下方面至关重要: - 理解 tensor schema(从 HLSL 源码映射的 78 个 tensor) - 识别神经网络架构(encoder → bottleneck → decoder) - 验证权重 blob 布局和 FP8 量化格式 - 确立 4.1.0 继承的 provider-layer 契约 **仓库:** [fsr4-sdk-402-source](https://github.com/rolaandjayz/fsr4-sdk-402-source) — AMD FidelityFX SDK 2.0.0,FSR 4.0.2 (ML-Upscaler),基于 MIT 许可证。 ## 法律声明与免责声明 本项目在既定的逆向工程原则下运作: - **FSR 4.0.2** 由 AMD 在 [GPUOpen](https://gpuopen.com/fidelityfx-superresolution/) 上以 MIT 许可证发布。我们将其用作结构参考,这是 MIT 许可证明确允许的。 - **FSR 4.1.0** 的分析是通过对分发的二进制文件进行静态分析(Ghidra 反编译、DXIL 反汇编、PE 检查、原始 x86-64 反汇编)来执行的。没有违反任何许可协议。没有接受任何 EULA。该二进制文件是在传输过程中、在线状态下按原样分析的。 - **提取的权重**是 AMD 训练流水线生成的数值参数。此处复制它们是出于研究和互操作性的目的。 - **AMD 自己的创业史就是逆向工程。** AMD 花了五年时间对 Intel 的 386 处理器进行逆向工程。他们在法庭上赢了。我不知道 AMD 有没有针对 GPU 逆向工程项目发起过公开的 DMCA 行动,尽管这并不是法律许可。 AMD 已经在开源许可证下发布了主要的 Linux GPU 驱动组件和 GPUOpen 材料,并且他们以 MIT 许可证发布了之前的 FSR 代次。我们将这种以互操作性为首的传统应用到了他们的最新产品上。 有关完整的法律分析、AMD 历史和诚实的风险评估,请参见 [`LEGAL.md`](LEGAL.md)。 本项目在 **MIT License** 下发布——与 AMD 为 FSR 4.0.2 选择的许可证相同。我们相信知识应该是免费的。AMD 显然曾经同意过这一点。 ## Shader 内部:神经网络架构 对所有 602 个 DXBC blob 的全面反汇编揭示了 **27 个唯一的 FSR4 compute shader**(名为 `fsr4_model_v07_fp8_no_scale_*`)。其余的 575 个 blob 是非 FSR 的实用 shader 和参数化变体。 ### 架构:fsr4_model_v07_fp8_no_scale 该名称证实了: - **模型版本 7** — AMD 的内部迭代编号 - **FP8 权重,无 per-tensor 缩放** — 大多为固定的共享指数量化;4.1.0 的 initializer 布局仍然包含一个小的额外 scale-factor 区域 - 所有画质预设共享**相同的神经网络架构**,仅在权重 offset 上有所不同 ### 权重 Blob 分析 所有 5 个标准预设(Quality、Balanced、Performance、Ultra-Performance、Native)都是**字节相同**的(MD5 `6ccdb68fc828e0bef93fa32fd144c4f6`)。画质选择纯粹是一个调度/分辨率参数——而不是一个不同的网络。 DRS(动态分辨率缩放)blob 是一个**完全重新训练的网络**: - 96.1% 的字节与标准版不同(MD5 `8e5c042e0c14cca83d56ed13df5f02dd`) - 0 个匹配的 4KB 数据块 - 相同的架构(128KB,相同的 FP8 值分布) - 应用于不同训练参数的相同量化方案 ### 通过 Atomics 解码 FP8 权重 Shader 通过 ScratchBuffer 中的 **256 项 atomicCompareExchange 表**解码 FP8 权重,通过 `atomicCompareExchange` 作为无副作用的表读取进行访问: 1. 从权重 SRV(空间 0,寄存器 18)读取 FP8 字节 2. 在 UAV(空间 1,寄存器 0)中使用固定 offset 的 LUT 查找 3. 条目之间的间距为 256 字节 (0x100) — 每个 FP8 值对应一个条目 4. 每个字节通过 LUT 解码为 8 个 FP16 值 5. 完全在整数寄存器中累加 这避免了分支,并习惯性地将 GPU atomics 用作 LUT 读取。 ### 层架构 | 层级 | Pass | 角色 | IR 行数 | LUT Ops | Kernel | |------|--------|------|----------|---------|--------| | Input | prepass | 特征提取 + 双线性采样 | 2,267 | 206 | N/A | | Small | pass1, pass2, pass12 | 3x3 卷积 | ~4,900 | 1,989 | 3x3 | | Medium | pass4, pass5, pass10 | 4x4 卷积 | ~8,000 | 3,296 | 4x4 | | Large | pass7, pass8 | 5x4 卷积 (最深) | ~20,750 | 9,088 | 5x4 | | Special | pass3, pass6, pass9, pass11 | 独特角色 | varies | varies | varies | | Output | postpass | ML + 常规复合 | 2,675 | 1,580 | mixed | | Scatter | pass*_post (x12) | 仅数据重排 | ~125 | 0 | N/A | **Post pass 非常简单** — 2-5 次 `rawBufferStore` 调用,没有 ML 计算。它们将累加的结果从 scratch buffer 散射到输出平面。 ### 数据流 ``` Input Color + Motion + Depth | v Prepass (bilinear sampling -> 3 feature planes) | v Pass1 (3x3 conv) -> Scatter -> Pass2 (3x3 conv) -> Scatter -> ... | (progressively deeper kernels) Pass7/8 (5x4 conv, deepest layers) -> Scatter | v Pass12 (3x3 conv) -> Scatter | v Postpass (ML composite + conventional math -> 7 output planes) ``` 该架构具有**带有对称 pass 布局的瓶颈结构**:特征通过逐渐加深的层进行处理,然后重建。 ### CBV 寄存器语义 | 寄存器 | 类型 | 用法 | |----------|------|-------| | 0 | f32/i32 | 调度维度(宽、高) | | 1 | f32 | 抖动 offset XY | | 2 | f32/i32 | 权重索引步长 / 输出缩放 | | 4 | i32 | 配置标志(字段 3) | | 5 | f32/i32 | 缓冲区步长(字段 0)+ 混合/曝光(字段 1) | | 7 | i32 | 扩展调度参数(专用 pass) | ### 仍未知的内容 - **确切的每 pass MAC 算术** — 已识别出整数乘加模式,但确切的 weight-index 与 input-feature 的映射关系尚未完全追踪 - **Runtime cbuffer 值** — offset 计算是从静态 IR 推断出来的,未在 runtime 捕获 - **时序状态流** — 第 N-1 帧如何反馈到第 N 帧(未在 runtime 捕获) - **跳跃连接** — 未确认;pass 对称性与瓶颈自编码器(decoder 镜像 encoder)一致,不需要跳跃连接 - **注意力机制** — 未发现 softmax/QKV 模式;可能是纯卷积 ### 已解决的问题 - **激活函数** — 通过 `FMax(x, 0.0)` 实现的 ReLU,在 DXIL + SPIR-V 中进行了交叉验证(见 docs/activation-lut-analysis.md) - **额外参数** — postpass 消耗了 222 个 FP32 输出组合 bias(见 docs/extra-params-analysis.md) - **“LUT 机制”** — 用于跨线程组通信的 coherent atomic buffer I/O,而不是激活 LUT 完整详情:[docs/shader-internals.md](docs/shader-internals.md)。机器可读分析:[spec/shader_analysis.json](spec/shader_analysis.json)。 *由 Rolaand Jayz 构建 — The Shadow Librarian。* *如果这项工作帮助您理解了某些东西,请将其传递下去。知识在分享时会不断增长。*
标签:Gophish, 云安全监控, 代码重构, 图形图像, 机器学习模型提取, 神经网络, 逆向分析, 逆向工具, 静态分析