这篇文章上次修改于 240 天前,可能其部分内容已经发生变化,如有疑问可询问作者。

前言

有能力还是支持一下作者吧, 毕竟这也是人家一直在维护的东西, 在官网论坛我也看到作者确实是在不停解决各种 "问题".

思来想去还是把标题加上了一个 过程 (这也是最初的标题), 一开始我确实是歪打正着地实现了破解授权, 但现在是彻底搞清楚了整个过程.

关于 files.gallery

官网: files.gallery. 从域名和网站页脚以及源码来看, 它貌似是从 photo.gallery 分离出来的. 很显然这依然还是一个小众的网站, 没听过就对了.

它也只是众多开源网盘中的普通一个罢了, 我看中的是它的轻量和简洁 (仅一个 PHP 文件, 默认采用 CDN 加载需要资源并且支持自托管), 同时缓存也很小,我 700 张缩略图总缓存也才 15M. 虽然在 GitHub 上有这个项目, 但是用户反馈和问题解答基本都是在自己的网站内进行的.
缺点也很明显, 比如不支持多用户和权限控制, 以及付费才能开启文件操作的相关功能. 有点离谱的是不支持递归搜索文件, 后续版本可能会实现吧.

破解

其实这个项目的老版本是有不少破解好的, 网上一搜就能看见了. 但是我之前使用的版本发现在加载大量资源的时候会卡顿, 但是新版的不会, 所以就换上了新版.

可能是因为能直接找到的旧版实在是太老了,我发现的两个都是两年前更新的 0.2.2, 然而官网已经是 0.7.0 的版本了.

思路

代码全部开源, 根据旧版本的使用方法我也知道付费验证的核心只在一个文件里, 那我直接干掉它就好了. 最大的问题还是我没见过授权后返回的是什么东西. 我只知道没授权返回

{"status":0,"msg":"not found"}

实操

由于代码是经过压缩混淆的, 所以我首先要找到的是它请求的验证服务器 URL 所在的函数, 按理来说我直接搜索域名就行了, 然而完全搜不到. 尝试查找 POST 关键字发现有很多, 于是尝试快速浏览代码发现了一个函数参数是形如 aHR0cHM6Ly9hdXRoLnBob3RvLmdhbGxlcnkv 的字符串, 查找后发现是使用 atob() 来进行 base64 加密解密的.

最后查找到验证函数的代码如下:

if (!i || i != _c.qrx && S(i) != t) {
 var a = _c.x3_path && !_c.qrx;
 if (!a || _c[S("dXNlcng=")] !== S("ZnA="))
  return _c.qrx || a || !t || t.includes(".") ?
   !_c.qrx || "string" == typeof _c.qrx && /^[a-f0-9]{32}$/.test(_c.qrx) ?
   void T({
    params: (_c.qrx ? "key=" + _c.qrx + "&" : "") + (a ? "app=1&domain=" : "app=2&host=") +
     encodeURI(t),
    url: S("aHR0cHM6Ly9hdXRoLnBob3RvLmdhbGxlcnkv"),
    json_response: !0,
    complete: function(i, o, l) {
     if (l && i && i.hasOwnProperty("status"))
      return i.status && 301 != i.status ?
       void(a || I.set(e, _c.qrx || btoa(t))) :
       n(_c.qrx)
    }
   }) :
   n(!0) :
   n()
}

虽然我看不懂, 但是这个 status301 肯定是有点东西的.
好了, 在替换 URL 为本地链接并尝试输出 "status":301 后发现好像没什么用, 结果去掉这个键值对后反而通过了验证, 真是莫名其妙(反正过了验证就行了.

可行方案

我一开始是直接 POST 一个静态 JSON 文件的, 但是报了 405, 我也懒得去改服务器配置直接换成 PHP 输出来解决. 设置一个响应标头并输出就行了, 上面的代码用相对路径换成 IP 访问也没问题. 我的代码加上了一个跨域许可, 其实返回什么都无所谓了, 判断逻辑好像是不包含 status 和 301 就能过.

<?php
function cors(){
    if (isset($_SERVER['HTTP_ORIGIN'])) {
        header("Access-Control-Allow-Origin: {$_SERVER['HTTP_ORIGIN']}");
        header('Access-Control-Allow-Credentials: true');
        header('Access-Control-Max-Age: 86400');
    }
    if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') {
        if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_METHOD']))
            header("Access-Control-Allow-Methods: GET, POST, OPTIONS");
        if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS']))
            header("Access-Control-Allow-Headers: {$_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS']}");
        exit(0);
    }
}

$check = $_POST;
header('Content-type:text/json;charset=utf-8');
cors();
if ($check) {
    echo json_encode($check);
} else {
    echo '{"status":301,"msg":"无效字段"}';
}

寻找原因

在完成上述操作后准备起草这篇文章的时候发现原来官网已经提供了"破解方法":

How does license verification work?
When Files app loads in browser, it will attempt to verify your encrypted license_key remotely via non-blocking Javascript. On successful verification, your Files app will be licensed, else it will simply continue in "free" mode. *If connection fails for any reason, Files app is considered licensed.

许可证验证如何工作?
当文件应用程序在浏览器中加载时,它将尝试通过非阻塞Javascript远程验证您的加密许可证密钥。验证成功后,您的“文件”应用程序将获得许可,否则它将以“免费”模式继续运行*如果由于任何原因导致连接失败,“文件”应用程序将被视为已获得许可。

这验证逻辑还真是有点....奇特, 这也是上面莫名其妙能够破解授权的原因所在.

另辟蹊径

上面说过作者主要在自己的网站论坛里解答用户的各种奇怪问题以及合理诉求, 其中诊断问题时可能会要求发送自己网站的链接, 这个过程一般来说是通过电子邮件来进行的, 但也有少数的人不在乎图方便直接公开回复了, 我发现这一点后找到了三个地址, 其中两个是购买了授权的. 最后得到了应该返回的值:

{"status":1,"type":1}

并且这两个网站返回的内容都是相同的, 而且请求地址也是作者提供的服务器, 可以确定这就是我想要的内容. 话说还真是简陋啊~
知道了这一点把上面的 PHP 内容换成这个就行了

<? php
header('Content-type:text/json;charset=utf-8');
echo '{"status":1,"type":1}';

三行搞定~

破解成品

最新版已经上传到 GitHub files.gallery

目前版本是 0.8.0

配置

最后是普通的调教环节, 下载应该是没什么影响的, 上传则会受到服务器和 PHP 的限制. 关于切片上传这一点我也向作者反馈了, 他的观点是切片需要消耗浏览器和服务器的额外资源来进行文件的分割以及拼接, 也就是并不打算做这个功能.

在 php.ini 中找到以下两个配置项, 然后改成需要的值 post_max_size upload_max_filesize, 在 nginx.conf 中找到 client_max_body_size, 使用纯数字则是字节为单位(显然会有很多个 0), 后面加字母则可以写成 128M 这种形式.根据自己的需求配置吧!

至于其他的常规内容,直接看官网的就好了: 传送门

我的站点

目前已经换成了 Alist, 双方各有利弊吧

后记

其实还有只修改 JS 代码的方法, 我尝试提前设置授权参数依然无法避开第一次出现授权弹窗(第二次开始不会, 破解成功并且也不会再次请求服务器). 显然这样做是更不方便的, 所以我还是使用破解 JS 加修改授权链接的做法. 由于代码写的很混乱, 我也不想去搞清楚那一坨屎一样(变量命格式化 + 嵌套三目 + 强转类型 + 字符串加密 + 大量逻辑运算符等等)的东西的运行逻辑, 所以到这就算完成了!