一条命令解开网易游戏的 NPK 资源包:unnpk 解包工具原理与实战
2026/8/21 21:25:44 网站建设 项目流程

一条命令解开网易游戏的 NPK 资源包:unnpk 解包工具原理与实战

【免费下载链接】unnpk解包网易游戏NeoX引擎NPK文件,如阴阳师、魔法禁书目录。项目地址: https://gitcode.com/gh_mirrors/un/unnpk

从游戏客户端里拷出的资源包,file命令只回你一个冰冷的data,hexdump 前几百字节毫无规律,翻遍网络也找不到格式文档——这是每个想做 MOD、搞逆向研究的人都会撞上的第一堵墙。unnpk 就是为网易 NeoX 引擎 NPK 文件而生的解包工具,阴阳师、魔法禁书目录这些游戏打包的资源,一条命令就能拆回原形。这篇文章不罗列 API,而是带你把 NPK 的格式谜题当案件来破,最后用三步把阴阳师的加密脚本还原成可读的 Python 源码。

破解之旅:一份没有文档的二进制格式

拿到一个未知格式的二进制文件,第一步永远是"找目录"。书的目录通常在开头或结尾,游戏资源包也一样。用十六进制编辑器扫过 NPK 的文件头,你会发现在 0x14 这个固定偏移处,存着一个 4 字节整数,它指向的正是索引表(作者称之为 map)的起始位置:

// 读取map偏移量 fseek(npk, 0x14, SEEK_SET); uint32_t map_offset; fread(&map_offset, 4, 1, npk);

为什么是固定偏移?这是引擎的取舍:加载资源时只需读一次文件头,就能以 O(1) 的代价拿到整张索引表的入口,不必扫描全文件去猜结构。对一款要高频加载资源的游戏来说,这个"固定入口"把定位成本压到了最低。

线索一:28 字节一条的索引记录

拿到 map_offset 之后,unnpk 从那里开始,每隔 28 字节(7 个 uint32)读一条记录,一直读到文件末尾——也就是说,这张索引表铺满了文件尾部一整块区域,数据区在前,索引区在后:

for (int file_offset = map_offset; file_offset < npk_size; file_offset += 7 * 4) { fseek(npk, file_offset, SEEK_SET); fread(&file_info, 4, 7, npk); // ... }

每条记录的信息密度相当高:

字段含义
file_info[0]索引号(解包后用作文件名)
file_info[1]数据在文件中的偏移
file_info[2]压缩后大小
file_info[3]解压后大小
file_info[4] / [5]两个校验值
file_info[6]是否压缩的标志

注意到没有:索引表里根本没有文件名。游戏运行时靠索引号直接寻址资源,而不是靠路径字符串——省空间、省字符串比较,还天然防窥探。代价就是解包之后你得到的是清一色的 8 位十六进制编号(比如FB54F059),unnpk 输出文件的命名就是这么来的。

线索二:压缩判定与"宁可错杀"的容错哲学

有了偏移和大小,理论上就能"切"数据了。但解包工具很快撞上第二个问题:数据不一定是原始字节。unnpk 的判断很朴素——标志位为真,或者压缩前后大小对不上,就按 zlib 解压:

// file_info[2] 和 file_info[3] 大小不匹配说明数据压缩了 if (file_info[6] || file_info[2] != file_info[3])

更体现作者风格的,是解压失败时的处理:遇到Z_DATA_ERROR(数据根本不是 zlib)或Z_BUF_ERROR时,它不报错退出,而是把原始字节原样写出去,只在控制台留一句警告。这个设计很聪明——索引表可能因游戏版本差异而有偏差,硬解压只会让整个任务崩掉;先把原始数据给你,至少还能用别的工具继续分析,不至于一无所获。

线索三:没有名字的文件,怎么知道它是什么?

这是整个解包体验的关键。资源包里贴着图、装着模型、塞着 shader、躺着脚本和配置 XML……没有文件名和扩展名,谁来认领它们?unnpk 的答案是双保险:先用 libmagic 按魔数识别出 MIME 类型,再用内容特征签名兜底。

magic_t cookie = magic_open(MAGIC_MIME_TYPE); magic_load(cookie, NULL); file_out_type = (char *)magic_buffer(cookie, file_out_buf, file_info[3]);

MIME 能认出 png、jpg、mp4、zip、ttf、xls,却认不出游戏特有的东西。于是代码里还藏着一串"指纹"匹配:魔数KTXRGISPKM这类移动端 GPU 纹理格式;<NeoX<FxGroup<SceneConfig这类引擎自定义 XML;靠关键字vec4tex2D判定的 GLSL shader;靠v / vt / f行判定的 OBJ 模型。最终结果按 MIME 类型分文件夹存放,文件名带上类型缩写,大致长这样:

extracted/image/png/12345678.png extracted/text/plain/87654321.txt

项目背后:一场从 script.npk 开始的逆向长跑

unnpk 不是实验室里的格式研究玩具,它的出生点非常具体:解开阴阳师的script.npk。项目 README 记录了完整战斗过程,几个细节很能说明问题。

网易的加密脚本不是简单异或。tools/script_redirect.py还原出的算法是三层叠加:先用 Python 2 的rotor流加密(密钥由asdf_dnasdf_dtasdf_df三段拼接而成),再 zlib 压缩,最后字节反转并异或 154。更妙的是,解密所需的关键文件redirect.pyc本身就藏在script.npk里(2018-03-27 的 3.0.3(1) 版本对应索引FB54F059)——这把"加密锁"的钥匙,居然就放在锁旁边。

随后是字节码层面。pyc_decryptor.py的核心是一张 opcode 映射表,把被混淆的指令逐一换回 Python 2.7 的标准指令,再靠自研的pymarshal.py(Python marshal 模块的增强版)重新序列化出合法的 .pyc 文件,最后交给 uncompyle2 反编译。

作者选择用纯 C 实现主工具,依赖只有 zlib 和 libmagic——两个源文件、一个 Makefile,干净利落。这种"够用就好"的克制,恰恰是逆向工具最稀缺的品质:依赖越少,坑越少,在别人的机器上越容易跑起来。

上手体验:编译、解包、解密,三步跑通

一条命令完成解包

先装依赖(macOS 用brew install libmagic,CentOS 用yum install file-libs file-devel),然后:

git clone https://gitcode.com/gh_mirrors/un/unnpk cd unnpk make

编译产出两个可执行文件:unnpkmapnpk。解包只要一条命令:

./unnpk script.npk extracted

终端会打印一张表格,列出每个文件的索引、偏移、大小、是否压缩、MIME 类型和扩展名,解包结果自动按类型归类。

从加密字节到可读源码:阴阳师脚本三步解密

拿解包出的某个加密脚本文件(README 里以0A0D60DC为例)走完三步:

# 第一步:rotor 解密 → zlib 解压 → 反转异或 ./tools/script_redirect.py 0A0D60DC > 0A0D60DC.out # 第二步:纠正被混淆的 opcode,还原出标准 .pyc ./tools/pyc_decryptor.py 0A0D60DC.out 0A0D60DC.pyc # 第三步:反编译成可读 Python 源码 uncompyle2 -o 0A0D60DC.py 0A0D60DC.pyc

注意script_redirect.pypyc_decryptor.py依赖 Python 2 生态(rotor库、uncompyle2),这是网易当年技术栈决定的,属于历史包袱,别拿 Python 3 硬跑。

把 NPK 结构看个通透:mapnpk

如果只想先"看懂"一个包再决定怎么解,用配套的结构分析工具 mapnpk 更合适:

./mapnpk -f csv -t int script.npk > structure.csv

-f选 markdown 或 csv 输出格式,-t选 hex、int 或 original(原始字节序)展示数值,-o可指定输出文件。对逆向分析来说,csv 格式直接喂给脚本做批量处理非常顺手。

避坑指南:你大概率会踩的坑

  • Python 2 依赖是最大的坑rotor是 Python 2 专属库,uncompyle2也只认 2.7 的字节码。先装好 Python 2.7 环境再跑解密链,别在 Python 3 上浪费时间。
  • asdf_dn/asdf_dt/asdf_df可能因游戏而异。README 明确提醒:网易其他游戏的这三个参数"可能不同(也有可能相同)"。解别的游戏失败时,先怀疑密钥,而不是怀疑工具。
  • opcode 映射表有版本限制pyc_decryptor.py里的映射针对特定 Python 2.7 变体,换游戏、换客户端版本,指令集可能对不上,需要自己重新整理映射关系。
  • 索引表可能不准。看到Z_DATA_ERROR警告别慌,这是 unnpk 故意为之的容错——版本差异会让大小字段对不上,但它会输出原始字节,往往还能救回来。
  • 别在 Windows 上折腾。README 的原话是"不建议使用 Windows 太折腾了",libmagic 的安装、路径处理都够喝一壶,macOS / Linux 顺畅得多。

还能怎么玩:从解包到再造

游戏 MOD 与本地化是最常见的玩法:把 XML 配置、文本资源解出来修改再回填。解包只是第一步,回包通常要自己写工具,此时 mapnpk 输出的 csv 结构表就是最好的参考。

资源格式研究同样大有可为。KTX、RGIS、PKM 这些移动端纹理格式,NeoX 自定义 XML、GLSL shader,解出来就是一份现成的"格式标本库",研究手游渲染管线会非常顺手。

说到底,unnpk 教给我们的不是"怎么抄一个解包器",而是一套可迁移的思路:任何没有文档的二进制格式,都可以从"找目录"开始。下次再遇到打不开的资源包,先别急着放弃——hexdump 扫一遍,找找那个指向索引表的固定偏移,说不定下一个解包工具就是你写的。

【免费下载链接】unnpk解包网易游戏NeoX引擎NPK文件,如阴阳师、魔法禁书目录。项目地址: https://gitcode.com/gh_mirrors/un/unnpk

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询