Nazar-Okruzhko/Model-Researcher-Ultimate

GitHub: Nazar-Okruzhko/Model-Researcher-Ultimate

一款用于逆向研究和提取游戏二进制文件中3D模型数据的工具,通过手动配置偏移量和数据格式参数将未知格式的模型导出为OBJ文件。

Stars: 4 | Forks: 0

# Model-Researcher-Ultimate 大家好,我是 Nazar Okruzhko,也许有些人通过 Reddit 或 Sketchfab 认识我。我是 ID Tech 5 专家,我在逆向工程方面还是个新手/菜鸟,几个月前才开始,但在过去几周取得了一些进展,特别是针对 ID Tech 5 和 6 游戏的…… 我做了一个名为“Model Researcher Ultimate”的工具,用于从任何游戏中提取 3D 模型,它基于最初的“Model Researcher”,而后者又基于“Model Inspector”。 Screenshot (2015) ![Static Badge](https://img.shields.io/badge/Minimal%20Requirements%3A-Windows%2010-default) 在获得一个完全准备好且未/解压缩、未/解密的二进制 3D 模型文件后,该程序可用于找出 3D 数据是如何存储的,包括顶点、UV 坐标和面,支持任何不支持的格式,方法是通过找到顶点、UV 坐标和面的偏移量(入口点),并找出数据的组织方式(分离缓冲区或结构化缓冲区,其中顶点和 UV 坐标通过填充分隔),我们还可以找到顶点和面的数量以及缓冲区头/标记,然后将模型导出为 OBJ 文件。我在 2025 年 12 月 28 日制作了这个 Ultimate 版本的工具。发布版本需要 .Net 6.0 才能运行。 我设法使用 Model Researcher 挖掘出了一个传奇的 BJ Blazkowicz 模型,并且非常喜欢这个程序首次能够从任何专有 3D 模型文件中提取 3D 模型的想法。我非常喜欢它的设计,所以我真的创建了 Model Researcher "Ultimate",我真的很喜欢这个软件,所以我做出了我的终极回应。版本 1.0.0 目前很稳定。 主要功能:\ • 一体化 Windows 套件\ • 专为多任务处理而生\ • 改进的相机导航\ • 舒适的 HEX 查看器\ • 拖放支持\ • 快速复制粘贴 OBJ 文件\ • C# .NET 带来良好的性能\ • F1 F2 F3 F4 - 快捷键技巧\ • 参数文件(加载/保存)\ • Diffuse 纹理 (我也计划为其他 ID Tech 引擎和马里奥游戏制作工具)。 ## 入门指南 https://github.com/user-attachments/assets/e5cbb8a6-0b10-475e-8404-48a3dce611b8 Model Researcher Ultimate 是一个用于逆向工程/研究二进制 3D 模型文件的程序。 该工具允许解析二进制 3D 数据文件的: • Vertex:Offset(偏移)、Count(数量)、Padding(填充)、Format(格式)\ • UVs:Offset、Count、Padding、Format\ • Face:Offset、Count、Padding、Format\ • Normals:Offset、Count、Padding、Format\ (这些是从二进制文件中提取完整 3D 数据的关键要素) 示例“PMR”参数文件: ``` [Big-Endian] ROOT: VB 0x7BFD0, 4672, 20, XZY, Float VTB 0x7BFDC, 4672, 24, UV, Float FB 0xA07D0, 26340, 0, Triangles, Short Mesh 1: VB 0x5B55, 7854, 20, XZY, Float VTB 0x5B61, 7854, 24, UV, Float FB 0x43115, 41588, 0, Triangles, Short Mesh 2: VB 0x167, 418, 20, XZY, Float VTB 0x173, 418, 24, UV, Float FB 0x35A7, 2306, 0, Triangles, Short ``` [快捷键:F1 打印,F2 渲染,F3 查看 UV,F4 翻转 UV]\ [工具支持 Multi-Submesh 系统、Diffuse 纹理、参数文件和脚本]\ [BMD6MODEL 文件的示例脚本作为默认脚本内置在程序中] ## 介绍 但是所有这些模型是如何存储它们的 3D 数据的呢? 嗯,答案很简单,这里没有魔法,所有 3D 模型都只是由 *顶点*、*面*、*顶点 UV 坐标* 和 *顶点法线坐标* 组成的 它们 *一定* 在你的文件中的某个地方(这个地方被称为缓冲区),这里绝对没有额外的魔法。 example.obj: ``` This is how the Vertecies look like: v 1.0 4.0 3.0 <= X, Y, Z matrix coordinates (usually from 0.01 to 1000) v 2.0 3.0 4.0 <= Point values so are usually floats v 6.0 2.0 3.0 <= Usually stable, values don't varry to much in max and min values This is how faces looks like: f 1 2 3 <= Takes all those previous vertecies and makes a triangle out of them This is how UV Vertex coords look lke: vt 0.2 0.3 <= 2D coordinate of the first vertex (usually from 0.1 to 1.0) vt 0.5 0.2 <= Point values so are usually floats vt 0.3 0.1 <= Usually stable, values don't warry to much in max and min values This is how Vertex normals look like: [not so important actually] vn 0.745 0.845 0.360 <= X, Y, Z matriz coordinates (usually from 0.01 to 1) vn 0.320 0.625 0.270 <= Point values so are usually floats, so "v2 x, y, z" vn 0.430 0.320 0.390 <= Usually stable, values don't warry much in max and min values ``` 结果是一个简单的三角形,它也有自己的 UV 贴图。 这就是最简单的 3D 模型格式 OBJ 存储其 3D 模型数据的方式,但是我们可以说是所有的二进制模型都以 OBJ 文件格式存储它们的 3D 数据,只是还有一件事。 ### 缓冲区和二进制数据 二进制格式只有两种方式存储它们的 3D 数据(除了面),即分离方式和结构化方式,这是它的样子: 分离方式: vertex_buffer = [ ``` v1 1.0 4.0 3.0 <= X, Y, Z matrix coordinates (usually from 0.01 to 1000) v2 2.0 3.0 4.0 <= Point values so are usually floats, so "v2 x, y, x" v3 6.0 2.0 3.0 <= Usually stable, values don't varry to much in max and min values ... ``` ] face_buffer = [ ``` f1 1 2 3 <= Takes all those previous vertecies and makes triangle out of them, so "f1 v1, v2, v3" ... ``` ] uv_coords_buffer = [ ``` vt1 0.2 0.3 <= 2D coordinate of the first vertex (usually from 0.1 to 1.0) vt2 0.5 0.2 <= Point values so are usually floats, so "vt2 u, v" vt3 0.3 0.1 <= Usually stable, values don't warry to nuch in max and min values ... ``` ] vertex_normals_buffer = { ``` vn1 0.745 0.845 0.360 <= X, Y, X matrix coordinates (usually from 0.01 to 1) vn2 0.320 0.625 0.270 <= Point values so are usually floats, so "v2, x, y, z" vn3 0.450 0.310 0.390 <= Usually stable, values don't warry much in max and min values ... ``` ] 结构化方式: buffer = [ ``` {v1 1.0 4.0 3.0, vt1 0.2 0.3, vn1 0.745 0.845 0.360} {v2 2.0 3.0 4.0, vt2 0.5 0.2, vn2 0.320 0.625 0.270} {v3 6.0 2.0 3.0, vt3 0.3 0.1, vn3 0.450 0.310 0.390} ... ``` ] ### 二进制数据 每个文件中的数据都可以被视为二进制,无论它之前是可读的、不可读的甚至是空的,以二进制查看它会立即揭示一切。 虽然二进制文件都是一样的,但我们读取它的方式会彻底改变一切!要查看你的二进制文件,你必须从中转储 HEX 或将其加载到 HEX 查看器中: 示例文件: ``` Addres: HEX Bytes: ASCII: 0012BFC0 48 53 68 61 70 65 5F 31 37 00 00 00 00 00 01 00 HShape_17....... <= First line contains ASCII strings 0012BFD0 00 00 0A 00 00 00 22 00 00 10 00 00 00 00 0C 00 ......"......... <= Second line does not contain ASCII strings 0012BFE0 00 00 61 32 76 2E 6F 62 6A 43 6F 6F 72 64 01 00 ..a2v.objCoord.. <= Third line contains ASCII strings 0012BFF0 00 00 FF FF FF FF 02 00 00 00 47 04 00 00 82 56 ..........G....V <= Fourth line contains interesting "00 00 FF FF FF FF" buffer mark 0012C000 F9 40 39 94 59 43 76 26 13 41 BB 61 FB 40 5A A4 .@9.YCv&.A.a.@Z. <= Fifth line starts containg the actual float Vertex coordinates! But looks random in ASCII strings! 0012C010 5B 43 95 B7 00 41 8F 70 CB 40 C1 4A 5B 43 31 08 [C...A.p.@.J[C1. <= Sixth line contains actual float Vertex coordinates! But looks random in ASCII strings! 0012C020 12 41 8A 8E C9 40 E7 5B 59 43 E8 82 1D 41 90 A0 .A...@.[YC...A.. <= Seventh line contains actual flaot Vertex coordinates! But looks still random in ASCII strings! 0012C030 62 40 21 90 58 43 05 DD 1C 41 BC B3 78 40 D7 63 b@!.XC...A..x@.c <= Eight line contains actual float Vertex coordinates! But looks again random in ASCII strings! ``` 但是那些 float、short 和 ASCII 是什么呢? 位是计算机数据的最小单位,它们要么是 0 要么是 1,以此类推。 然而字节是由 8 个位组合而成的,实际上可以开始代表一些数据。这些是范围从 0 到 255 的位,其中 0 也被视为一个重要值(所以是 256 种组合),(我用 HEX 表示它们,0-F 值,所以是 256 种组合) 例如这里有一个字节:10110111 (32 16 8 4 2 1 = 256 位作为总和),将字节组合在一起我们可以创建多种数据类型。 ``` This are all of the data types: Byte/Char => 1 Byte, unsigned/signed (8 Bits) |Example: 48 <= H | ASCII Word/Short => 2 bytes, unsigned/signed (16 Bits) |Example: 48 53 <= HS | ASCII Dword/Int => 4 bytes, unsigned/signed (32 Bits) |Example: 48 53 68 61 <= HShap | ASCII ULONG32/Long => 4 Byte, unsigned/signed (32 Bits) |Example: 48 53 68 61 <= HShap | ASCII ULONG64/Long Long => 8 Byte, unsigned/signed (64 Bits) |Example: 48 53 68 61 70 65 5F 31 <= HShape_17 | ASCII float => 4 bytes, for represnting floating point values (32 Bits) |Example: 48 53 68 61 <= HShap | ASCII double => 8 bytes, for representing more precise floating point values (64 Bits) |Example: 48 53 68 61 70 65 5F 31 <= HShape_17 | ASCII String/Char => A Sequence/Array of Characters terminated by the nulll character |Example: 48 53 68 61 70 65 5F 31 <= HShape_17 | ASCII ``` ### Big-Endian vs Little-Endian: 以 Big-Endian 读取(例如一个 float 字节)会正常读取它,从左到右 48 53 68 61 "HShap",而 Little-endian 以相反的顺序读取字节,从右到左 61 68 53 48 "paSH"。 Big-Endian 主要用于 PS3、Xbox360 和 Wii 平台,而 Little-Endian 主要用于 Windows、PS4、Xbox One、Nintendo Switch。 ### 尝试逆向二进制 3D 格式 但是我们如何将这些信息实际应用到逆向工程二进制 3D 文件格式结构中,甚至将其转换为 OBJ 模型呢。 假设你拥有实际已解压缩/未压缩且已解密/未加密的二进制 3D 模型文件,你实际上可以在实时分析其中的 HEX 的同时可视化 3D 数据几何体! ModelResearcherUltimate 是一个能够实现这些可能性的程序。 首先,级别 1:从顶点数 500 开始,类型:float,在打印值和渲染它的同时小心地尝试不同的偏移量,直到你看到一个连续且非常稳定的输出,没有疯狂大或小的值。(从 0.001 到 1000)。 如果什么都不起作用,尝试不同的 Endianess(字节序),然后尝试不同的类型(可能性不大)。如果网格出现了但也出现了随机顶点,这意味着数据结构是结构化的,你需要尝试不同的填充甚至有时是 Pad inters。 其次,级别 2:从顶点 UV 坐标数 [精确等于顶点数] 开始,类型:float,在打印值和渲染它的同时小心地尝试不同的偏移量,直到你看到一个连续稳定的输出,没有疯狂大或小的值(从 0.0001 到 1)。 如果什么都不起作用,尝试不同的类型,因为你已经知道了 Endianess 和结构。 第三,级别 3:从面开始,它们实际上与顶点非常紧密地相连,所以错误会不断出现,在打印值的同时小心地尝试不同的偏移量,不要渲染它,它通常只会抛出错误。 你需要看到没有浮点数的完整值,它们在输出中非常稳定,没有大和小的值,如果什么都不起作用,尝试不同的类型甚至格式。 第四,级别 4:[老实说我不知道该在这里写什么,法线虽然很没用,你可以在像 Blender 这样的程序中只需点击几下就能翻转和计算它们,非常容易,所以不值得你绞尽脑汁!] ### 实践步骤: 以下是糟糕的数据的样子: good.obj: ``` v -0.0000 -0.0000 -184538016.0000 v -0.0000 15.7924 -158665664.0000 v -0.0000 90990377942005974930976407552.0000 -17551224.0000 v -0.0000 -3386287.2500 -115467744.0000 v -0.0000 15397417210601645679040601784320.0000 -22963316.0000 v -0.0000 15397417210601645679040601784320.0000 -22963316.0000 ... vt 0.0000 1785889664.0000 vt 0.0000 140283808776479363868647227392.0000 vt 0.0000 10997215558668704718782464.0000 vt 0.0000 -516472.2188 vt 0.0000 -0.0000 vt 0.0000 0.0000 ... f 57856 10240 3073 f 3073 64769 57856 f 31744 64768 3072 f 57857 64768 58112 f 57856 58112 58368 f 58112 59136 58368 ... [random, disoriented pattern, extreamly low and extreamly big values occur] ``` 以下是良好的数据的样子: bad.obj: ``` v -0.0733 0.0012 1.6030 v -0.0735 -0.0118 1.6023 v -0.0776 -0.0146 1.5900 v -0.0718 -0.0247 1.6005 v -0.0784 0.0009 1.5913 v -0.0784 0.0009 1.5913 ... vt 0.0008 0.6221 vt 0.0316 0.6229 vt 0.0344 0.6543 vt 0.0628 0.6246 vt 0.0008 0.6539 vt 0.9978 0.6533 ... f 226 296 268 f 268 253 226 f 124 253 268 f 226 253 227 f 226 227 228 f 227 231 228 ... [strong countinous repating pattern, values are pretty much very similiar] ``` ### 最后 更改偏移量(通常)或 Endianess 或类型会立即给出不同的结果,包括糟糕的数据急剧变为良好的数据,所以请记住这一点并尝试那些偏移量。 最后还有一个很小但非常重要的步骤,大多数时候那些二进制文件还会留下像顶点数(UV 坐标和顶点法线数总是与顶点数相同)、面数、缓冲区标记甚至顶点步幅这样的值!(顶点步幅 = 顶点填充 + 12,UV 坐标步幅 = UV 坐标步幅 + 8)。它们本质上位于网格缓冲区的开头,非常容易找到,并且总是以相同的方式放置,然而,这一次我个人建议使用专门的 HEX 查看器来找到它们,我的推荐是 IM Hex,在易用性方面它真的是开源之王。 ### **骨骼支持现已推出!** Screenshot (2316) ### v1.0 版本已添加 DDS、TGA 和 JPG 纹理支持!!!
标签:3D图形处理, 3D模型提取, C# .NET, ID Tech 5, ID Tech 6, OBJ导出, Sktechfab资源, UV坐标, Windows工具, 专有格式解析, 二进制分析, 云安全运维, 云资产清单, 十六进制查看器, 数据偏移分析, 文件格式分析, 模型研究, 游戏数据挖掘, 游戏模组制作, 游戏资源提取, 网格数据, 逆向工程, 顶点数据