首页 / 资讯中心 / 文章详情

NeoX引擎NPK资源包逆向实战:用neox-tools拆解与重打包

NeoX引擎NPK资源包逆向实战:用neox-tools拆解与重打包 ★ FEATURED ARTICLE
简介面向游戏逆向与 Mod 制作者这是一套针对网易 NeoX 引擎 NPK 资源文件的解析工具由 Rust 编写当前适配 2020 年初《星战前夜无烬星河》所用格式支持读取文件列表并用 LZ4 算法解包。资源包共 15 个文件以 Rust 工程源码、Python 脚本及说明文档为主附带的脚本覆盖 PYC 解密、反编译与重定向等处理压缩包仅 24KB目录结构简单便于快速下载与入门学习。工具导出时会自动识别文件类型并补全扩展名方便后续查看与使用项目还规划了 RC4 加密、简单加密及重新打包 NPK 功能可通过 Cargo 直接编译运行。已有约五千人浏览学习对想理解网易 NeoX 引擎资源组织方式、批量提取游戏资源或开发类似二进制解析工具的开发者来说这是一份小巧完整、可直接运行的参考实现很适合作为学习资源解析的起点。1. 从 EVE Echoes 的 NPK 说起neox-tools 到底能拆出什么neox-tools 是一套专门对付网易 NeoX 引擎 NPK 资源包的 Python 工具。我第一次翻出它是想从《星战前夜无烬星河》里提舰船贴图做材质参考。动笔前我以为 NPK 就是个压缩包结果 010 Editor 打开一看头部既没有 PK 也没有 ZIP header索引区文件名全是乱码直接懵了。后来才明白NeoX 的 NPK 是自带版本号和偏移表的二进制容器文件名做过异或混淆数据段还可能被切开分段压缩通用解压工具根本识别不了。neox-tools 干的事就是把黑匣子打开读版本、解索引、按偏移提取、按原结构重新打包。适合三类人想提取 NeoX 游戏美术资源做参考的做汉化和轻量 Mod 的刚接触游戏资源逆向想拿真实项目练手的。2. 摸清 NeoX NPK 的家底文件头、索引表与压缩标记2.1 文件头不是固定魔数版本号决定后续偏移怎么读NPK 开头的结构通常不是一锤子买卖它一般是一个版本号字段加一个头部长度字段然后才是索引数量、索引表偏移、数据区偏移。不同游戏版本之间字段顺序和偏移量会差几个字节所以 neox-tools 的 info 子命令第一个任务就是识别 NeoX 小版本再决定后续字段的读取位置。我见过有人拿旧版工具去开新版包的一上来就报 Unknown magic就是因为魔数或版本号变了。python -m neox_tools info assets.npk命令会输出 NPK 的版本号、索引条目数量、索引表偏移量、数据区起始偏移和文件总数。拿到这些数字之后你才能判断这个包是“正常结构”还是“被加密过”。我一般会先跑 info 而不是直接 list原因很简单如果版本号识别错了list 出来的文件名全是乱码浪费一次调试时间。2.2 索引条目是命根子文件名、偏移、长度和标志位索引表里每个条目是一段固定长度的二进制记录常见布局是先写文件名可能带路径、再写数据偏移、数据长度、时间戳和压缩标志。neox-tools 在 list 模式下会把这段二进制解析成可读的表格文件名、大小、修改时间一次摆出来。这里有个容易忽略的点文件名常常被做过异或或位移混淆直接按字符串读是乱码工具内部会依据版本号匹配对应的混淆 key 来还原。python -m neox_tools list assets.npk | head -50head -50 只是让你先看前 50 条。这时候应该重点看扩展名.xml、.pvr、.ktx、.ogg、.prefab 之类。看到 .pvr 或 .ktx 就说明贴图资源在里面看到 .xml 说明配置表也在里面后续改包就有方向了。如果 list 出来的都是 .bin 或 .dat那这个包大概率是逻辑资源美术资源在另一个包里。2.3 数据段是分段压缩还是整块压缩NeoX 对数据段有两种常见处理小文件直接存原文不压缩大文件按固定大小分段每段独立 zlib 压缩。分段的好处是随机读取一个资源时不用把整个包读进内存代价是文件头里要带每段的压缩前后长度表。neox-tools 的 extract 处理的就是这个它根据索引条目里的偏移和长度先读到这一条对应的分段表再逐个解压拼接回原始文件。python -m neox_tools extract assets.npk text/strings.xml ./out单文件提取时工具会把该条目对应的分段长度先读出来再去数据区按偏移读取并拼接。注意参数里的路径要跟 list 输出里的名字完全一致包括大小写和斜杠方向。我经常在 Windows 上写反斜杠导致匹配不上换成 list 里原样输出的正斜杠就好了。2.4 为什么不用现成的 zipfile 或 7z网易的 NPK 不是 zip头部结构、索引表、压缩段标记都不一样更别说文件名混淆这一层。就算你把扩展名改成 .zip 让 7z 自动识别也只会得到一堆带乱码名的文件碎片因为 7z 根本不认 NeoX 的索引表。所以这类包只能先拆容器再谈内容工具的核心价值就是把“拆 NPK 容器”这一套流程固定下来不让你每次换个游戏版本都重新摸一遍格式。3. 跑通 neox-tools环境准备、三条命令与一次完整解包3.1 准备 Python 环境与依赖neox-tools 是用 Python 写的依赖里一般会有 zlib标准库和一个简单的二进制解析库就够了不必装 numpy 那种重量级的东西。我习惯用 venv 隔离mkdir neox-work cd neox-work python -m venv .venv source .venv/bin/activate # Windows 上换 .venv\Scripts\activate pip install neox-tools pip freeze requirements.txt用 Python 3.9 稳妥一点旧版本对 bytes 和 memoryview 的操作会慢几倍。把依赖固定到 requirements.txt 是给自己留后悔药万一系统 Python 升级后某个包不兼容还能按这份清单恢复。3.2 先看头再列表info 和 list 两个前置动作解包最忌一上来就 extract。先跑 info 确认版本识别正确再跑 list 确认文件名还原正常这两步过了后面的提取才有意义。以我这次的包为例info 显示 NeoX 版本是 v1.14.3共 18642 条索引索引偏移 128数据偏移 0x1C24A0。python -m neox_tools info assets.npk python -m neox_tools list assets.npk npk_files.txt wc -l npk_files.txtwc -l 会显示文件条目数正常应该和 info 输出的索引数量一致。如果差很多可能是某个文件条目解析失败先回头改读取模式。把 list 结果重定向到文本文件是为了下一步用 grep 检索资源路径不用一遍遍翻终端输出。3.3 按路径提取与批量导出拿到清单后具体提取就简单了。单文件提取我在上一章给了示例这里说批量当你只是想快速翻一下美术资源可以直接解开整个包注意磁盘空间和内存python -m neox_tools extract_all assets.npk ./unpackedextract_all 会遍历索引表按每条索引的数据偏移和数据段标记依次解压写出到 unpacked 目录保持原始相对路径。它会边解包边打印进度解到 1 个多 GB 的包时建议把输出重定向到日志避免终端被刷爆。参数上如果包很大可以加 --filter *.xml 只导出 XML 配置比我一开始傻乎乎全量提取快得多。3.4 提取出来的文件长什么样解开之后目录结构一般跟游戏内资源路径一致。以 EVE Echoes 这种美术管线很重的项目为例textures 下是 .pvr/.ktx 贴图ui 下是布局 XMLaudio 下是 .ogg。XML 大部分是明文可以直接打开看配置贴图需要 pvrtextool 或 PVRTexTool 转 PNG 才能预览不是拿个看图软件就能点开。所以清单里看到 .pvr 不用慌工具只负责拆包转格式是另一条流水线的事。4. 改完 XML 再封回去打包参数与索引重写要同步4.1 解包后可改的常见文件类型最常见的修改对象是配置 XML比如修改商店文案、初始资源、UI 布局参数。这类文件解出来就是明文 UTF-8文本编辑器直接改。另一类是二进制配置.bin 或 .bytes里面可能是 Lua 字节码或 Protobuf 数据改起来就要配套工具了。音频贴图这种资源一般不建议动因为格式和压缩参数匹配不上封回去容易整体翻车。我一般会在改之前把原始文件留一份副本路径名加 .bak 后缀。不要只在本地存一份万一打包逻辑有 bug原包又被覆盖连对比的对象都没有。4.2 重新打包为什么不能拿 zip 替代NeoX 在运行时是按固定偏移去索引表里找文件的如果你改完文件后直接拿 zip 或 tar 重新打包游戏根本不会读因为它要找的是 NPK 头部里的索引表而不是 zip 的 central directory。neox-tools 的 pack 子命令会做三件事读旧包的索引结构、把改动过的文件按新长度重新写进数据区、更新索引表里的长度和偏移字段并重写头部。这一套必须原子完成不能只替换文件。python -m neox_tools pack ./unpacked ./output.npk --version 1.14.3--version 指定 NeoX 版本pack 会按该版本的索引结构生成新的头部。参数上如果没有给 --version工具会尝试从输入目录里的 meta.json 读原版本信息所以我解包时会顺手把 meta.json 保留在 unpacked 根目录下面。打包完成后我会再解一次包对比文件内容和原版差异确认索引表一致再拿去测试。4.3 打包时哪些字段不能乱动有三处不能乱改一是头部预留字段通常是 0 或固定值改成别的 NeoX 直接拒绝加载二是索引条目里的时间戳游戏可能会拿它做增量更新判断三是压缩标志位。如果你改了一个本来没压缩的 XML然后打包时把它标成 zlib 压缩但没同步改索引运行时读出来就是乱码。neox-tools 的 pack 会自动根据文件的真实存储方式重写标志位关键是别手动改索引表二进制。4.4 改完怎么验证打包是否正确验证方式很朴素重新解包一次。如果解出来的文件跟你放进去的 unpacked 目录一模一样至少说明容器层面没问题。再进一步可以用 diff 对比改过的文件内容确认修改真的生效了。最后才是放到客户端里跑一遍看启动日志有没有报索引或者资源读取错误。我自己是习惯先跑一次命令行验证不直接上游戏原因很简单游戏崩溃日志不好定位命令行能直接告诉你哪个偏移对不上。5. 避坑手册解包报错、乱码文件名与重打包翻车的救法5.1 乱码文件名到底是谁的锅现象list 出来全是 \x??、\xfd 开头的乱码字符串根本不是可读路径。原因NeoX 对索引里的文件名做过混淆通常是异或或者字节位移而且不同版本用的 key 不一样。工具没识别对版本时直接按原始字节输出。解决先跑 info 看识别到的版本号再去配置里手动指定版本做二次解析。如果 info 都报 Unknown magic先把文件头 32 字节 dump 出来拿到 hex 再跟同版本正常包对比确认是不是魔数被改了。python -m neox_tools info assets.npk --force-version 1.14.3--force-version 是我常用的参数工具识别不到时可以直接指定版本号绕过自动识别。注意这只是临时规避如果换了另一个版本号的包还是要先确认版本。5.2 解包报 CRC 失败先查分段标志现象批量 extract 时某个文件解到一半报 CRC 或 zlib error后面条目全部中断。原因你以为文件是按整块压缩的实际上 NeoX 按 1 MB 分段压缩每一段有独立的压缩长度表。工具只读到了第一段的解压结果就开始拼接后面段次长度对不上自然报错。解决确认你用的 neox-tools 是支持分段标记的版本。老版本脚本只处理单段遇到分段包就崩。可以在 extract 之后加 --chunked 选项强制按分段处理并且打印当前段次方便定位到底哪一段坏了。5.3 重打包闪退的九成原因是长度没同步现象pack 成功文件也能解回来但客户端加载资源时报错或直接闪退。原因八成是索引条目里长度字段没同步。你改的 XML 比原文件长了几百字节但索引表里存的还是旧长度运行时会按旧长度去读读到一半截断数据错位。解决不要自己手动拼包。用工具的 pack 自动重写索引表pack 之后再用 extract 单文件验证长度是否一致。我遇到过一次是原包里有硬编码的路径哈希改了文件名导致哈希对不上后来查询确认文件名不能改只改内容就好了。5.4 大包解压内存爆掉是读取方式不对现象解包 2 GB 以上的大包时Python 进程内存飙到 4 GB 甚至直接 OOM 被杀。原因脚本先把整个数据区读进内存再逐段解压。1 GB 的压缩数据解压后可能膨胀到 3 GB内存自然爆炸。解决改用流式读取。按索引条目的偏移只读当前文件所需的分段到内存解完一个文件释放一个。neox-tools 的 extract 单文件模式就是这么做的不要用 extract_all 一次性全开。真要大包批量处理按目录分批每批次结束后 gc.collect()。import gc from neox_tools import NPKArchive npk NPKArchive.open(assets.npk) for i, entry in enumerate(npk.entries()): # 只读当前条目所需的数据段避免整个包驻留内存 data npk.read_entry(entry) with open(f./unpacked/{entry.path}, wb) as f: f.write(data) if i % 500 0: gc.collect()这段代码的关键点是 npk.open 只解析头部和索引表不加载数据区read_entry 按 offset 读取当前条目的分段数据处理完就丢引用。每隔 500 个文件手动 gc.collect() 一次把这个习惯写进脚本里大包解完基本不会爆内存。5.5 老工具开新包魔数识别失败现象换了新版本客户端包文件还是 .npk但 info 直接报 Unknown magic。原因NeoX 每个大版本可能改魔数或头部字段偏移。工具内置的魔数表是上一版游戏时代的对新包不识别。解决先自己 dump 头部 32 字节跟旧版对比哪里变了。常见变化是版本号从 0x143 升到 0x144或者头部长度从 16 变 32。确认结构后在工具的解析器里补一个 case 分支或者在调用时指定自定义头偏移。这种问题我给作者提过 issue一般几天内就能补上遇到就先本地打补丁不必等上游。6. 进阶批量处理一批 NPK 与文件级校验的习惯到了这一步单包的 info、list、extract、pack 你都能跑通了。日常里更常见的场景是一个客户端目录下有十几二十个 NPK我一般写一个脚本统一处理把所有包解到以包名命名的子目录下再在解出来的 XML 里做关键字检索比如搜 price、item_name 这些字段快速定位数值配置在哪个包、哪个文件里。python batch_unpack.py ./client_dir ./extracted python -m neox_tools search ./extracted price --ext xmlsearch 子命令会递归扫目录把包含关键字的文件和行号打印出来。这在排查“哪个文件控制了这个数值”时特别高效比一个个包手动试快得多。要注意的是如果客户端目录下同时有 .npk 和 .mpk 之类其他格式脚本里要先把扩展名过滤一遍避免把不相干的文件喂给 neox-tools 报错。改包前的习惯我建议你也固定下来先把原始 NPK 做一份 sha1 校验和存起来然后才允许自己动手改内容。为什么这么做因为很多改包翻车不是当场报错而是过了两天才在某个隐藏商城界面里炸开那时候原始包可能已经覆盖了没有校验和连回滚都无从谈起。另一种极端是改包改到一半想回到原版。我自己的经验是备份不是备份原包这一个文件而是连 meta.json、版本号、文件清单一起备份。这样出问题后用 neox-tools 重新打包也能恢复到接近原始状态而不只是一个少了头部信息的裸文件。从那以后我每次拿到新版本客户端都会强制走一遍 info、list、extract 三步把 meta.json 和文件清单归档到单独目录——这个习惯至少帮我避免了三次重包彻底翻车。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?
咨询建站