horizonLd 发表于 2026-4-17 03:44:44

【经验记录】在解包HAS游戏CG时的一些过程记录

本帖最后由 horizonLd 于 2026-4-17 03:44 编辑

写在前面

去年的时候解包了“HAS:我用催眠APP把全校男生变成了我的X奴”这个游戏,而最近也在解一些其他的,突然想起来之前解HAS的经历,感觉蛮有趣的,遂发个帖记录一下过程。

指路一下当时的资源帖,当时还不会编辑帖子,现在也已经翻新惹~

还是想不出来帖子放在哪个分区合适,只是个人向的心得记录帖,最后还是感觉在CODE板块里合适点()

分析游戏

结合游玩过程和游戏目录的文件架构,基本可以确定是RPG Maker MV构建出的产物,因为根目录里有nw或node相关的dll库,所以应该是RPG Maker MV构建出的NW.js程序。



NW.js感觉有点像Electron,就是用写网页的方式写软件,那根目录下的content文件夹内就是这个“网页”的内容了。



那就可以一眼锁定到其中的“data.pak”,因为它文件体积最大,至此便找到了资源包的位置。

解包资源

我的第一反应是想,它是不是通用的压缩格式,只不过改了个后缀而已?所以我就直接尝试用7-zip打开它,居然是可以正常打开的,那它就是通用压缩格式。



尝试解压的时候提示需要密码,那密码肯定是会存在游戏中的,而且是在尝试加载资源包的时候会用到这个密码。既然本质是网页项目,那么加载资源包的逻辑大概会存在main.js里的,顺藤摸瓜接着找就好了

然后在翻阅main.js的时候我先看到了这两个函数:

function stringEncrypter(str) {
    if (typeof str != 'string') {
      return str;
    };
    var strEncrypted = '';
    var ccode = 0;
    for (var i = 0; i < str.length; i++) {
      ccode = str.charCodeAt(i);
      strEncrypted += (String(ccode).length - 1 + String(ccode));
    };
    return strEncrypted;
};
这是一个针对字符串的加密函数,下面还有一个对应的解密函数我省略掉了,那么我要找的密码可能和这两个函数有关。

稍微分析一下这个加密,加密过程是对输入的字符串逐字符遍历,取每一个字符的Unicode编码,计算其编码的长度减1,然后再与这个Unicode编码拼在一起,就是单个字符的密文。比如A的Unicode编码是“65”,“65”长度为2,减1为1,所以“A”加密就是“165”

再往下是这样一个函数(有点长我就不直接展开了):
function loadPackageData(targetobj, filepath, options) {
    if (RPGMakerMV.xhrSucceeded) {
      if (!isObject(options)) {
            options = {};
      };
      var xhr = new XMLHttpRequest();
      xhr.responseType = 'blob';
      xhr.open('GET', filepath);
      xhr.overrideMimeType('application/zip');
      xhr.onload = () => {
            if (xhr.status < 400) {
                var zipReader = new zip.ZipReader(new zip.BlobReader(xhr.response));
                zipReader.getEntries().then(entries => {
                  if (entries && entries.length) {
                        for (var i = 0; i < entries.length; i++) {
                            if (!entries.directory) {
                              targetobj.filename] = entries;
                              targetobj.filename] = RPGMakerMV.compileKey(options);
                            };
                        };
                  };
                }).finally(() => {
                  zipReader.close();
                  if (typeof options.onLoad === 'function') {
                        options.onLoad();
                  };
                });
            } else {
                if (typeof options.onError === 'function') {
                  options.onError();
                };
            }
      };
      xhr.onerror = () => {
            if (typeof options.onError === 'function') {
                options.onError();
            };
      };
      xhr.send();
    } else {
      var message = 'Your browser does not allow to read local files.';
      RPGMakerMV.printError('Error', message);
    };
};
那很明显了,资源包“data.pak”就是一个有密码的zip压缩包,中间读取资源那段用到的“options”也就是要找的密码相关内容了

然后我就搜索“options”,定位到:

options = RPGMakerMV.decompileKey(RPGMakerMV.packageKey)
继续搜索“decompileKey”,找到了:

RPGMakerMV.decompileKey = function name(key) {
    key = key || '';
    if (key == '') {
      return '';
    };
    return stringDecrypter(window.atob(window.atob(key)));
};
核心其实就是“stringDecrypter(window.atob(window.atob(key)))”,然后继续搜索刚刚的“packageKey”,找到:

RPGMakerMV.packageKey = window.packageKey || 'TWpFeU1qSXhNVEl5TVRBNQ==';
那至此密文和解密方法都找到了,所以只需要用他写好的解密方法运行一下就ok了

把如下代码丢给浏览器的F12控制台:

function stringDecrypter(str) {
    if (typeof str != 'string') {
      return str;
    };
    var strDecrypted = '';
    var ccode = 0;
    var clength = 0;
    for (var i = 0; i < str.length; i++) {
      clength = Number(str) + 1;
      ccode = Number(str.substring(i + 1, i + clength + 1));
      strDecrypted += (String.fromCharCode(ccode));
      i += (clength);
    };
    return strDecrypted;
};

stringDecrypter(window.atob(window.atob('TWpFeU1qSXhNVEl5TVRBNQ==')));
控制台就会返回运行结果:



那这个“zpm”应该就是压缩包密码了,最后成功解压

最后の碎碎念

感觉搞到最后有点像解谜小游戏()但是最后稍微有点失望,因为作者为了省空间,只分别存了CG的差分部分,如下:



原来还要自己拼.jpg
我是强迫症晚期()准备找个时间把这些全拼一起,不然看着太难受了

求大家点点评分点点追随!求求惹求求惹!

Rg26 发表于 2026-4-17 03:47:09

好厉害,专业的程序佬,自己学了这么久的程序完全不懂怎么弄这些东西qq

you9632587 发表于 2026-4-17 07:14:10

没想到这游戏解包最麻烦的地方在于找密码,看上去难度真是很高的样子了

凯诺斯 发表于 2026-4-17 08:36:19

很详细的解包过程呀,尤其是找密码的这个步骤{:6_165:}

whitemoss 发表于 2026-4-17 08:56:39

哇塞感觉楼主的分析特别专业,好强的能力,小白根本看不懂

jasperch 发表于 2026-4-17 08:59:09

楼主好有技术,整个过程看着就像推理小说一样,涨知识了呢{:6_165:}

Sloanx 发表于 2026-4-17 09:04:53

楼主牛逼呀 反正我是不会的 还没那么深造 and一看楼主就知道 楼主喜欢这个虎(忘记名字了)

寂寥样子 发表于 2026-4-17 09:09:13

好厉害,我一看到编程就头疼,羡慕楼主的执行能力

Dodie 发表于 2026-4-17 09:19:47

我属于是跟着步骤来可能都有点问题的那种,lz好厉害

scfy 发表于 2026-4-17 09:34:49

虽然需要自己拼挺麻烦的,但是确实是个省空间的好办法,大家都不想自己的空间被像微信一样的玩意给吃完

志城 发表于 2026-4-17 10:05:22

没有完全懂,但是思路很清晰,感谢分享!

hdac666 发表于 2026-4-17 10:08:17

感谢分享!以后遇到类似的情况可以参考了

可靠的头孢 发表于 2026-4-17 10:12:09

很详细的过程了,感觉有时间可以去别的游戏尝试一下

娱乐法师火布偶 发表于 2026-4-17 10:12:26

一步步查找密码的过程非常有条理了

MemesMak 发表于 2026-4-17 10:13:33

好強大的解包能力好有技術

heluhong0210 发表于 2026-4-17 10:14:07

从上个帖子来的。 会看代码真的很羡慕了

Lightsnack 发表于 2026-4-17 10:19:23

好厉害啊,差分作为防止包体过大也是一种方法了

mozhu 发表于 2026-4-17 10:22:53

他里面的背景不是同一个图片叠加氛围,或者说渲染条,而是单纯的晚上一个图片,早上一个图片,属于是我没想到的解决逻辑了(笑)

錾稠 发表于 2026-4-17 10:38:23

原来还要自己拼.jpg
懒狗看到加密之后估计就不会拆了,不过倒是提供了思路

我的肉 发表于 2026-4-17 10:45:39

大佬好厉害~解包这个东西没接触过看上去也不是很简单的样子
页: [1] 2 3 4
查看完整版本: 【经验记录】在解包HAS游戏CG时的一些过程记录