加密方式
对于DLsite Play上的一部作品,DLsite服务器生成一个
AES key,将这部作品的所有图片文件都使用这一个AES key加密,并存储。
用户访问时,在浏览器生成RSA密钥(非对称加密,公钥和私钥),将公钥上报至服务器,服务器使用RSA公钥加密AES key并返回给浏览器。浏览器使用RSA私钥还原AES key,再使用AES key还原图片文件。
一句话概括:浏览器和服务器使用RSA通道传递AES key,AES key用于解密还原图片文件。
UML序列图如下:

解密思路
要想解密图片资源,最直接的办法是获取到AES密钥。
经过分析,发现AES密钥是在主线程还原完成,然后发往Web Worker,在Web Worker中进行图片数据的解密的。
网站源代码调用了Worker.prototype.postMessage向Web Worker发送AES密钥,所以我们可以hook postMessage方法以截获包含AES密钥的数据。同时也可以hookcrypto.subtle.decrypt方法来截获刚刚还原出来的AES密钥。
这里给出示例代码:
const hookXorKey = () => {
return new Promise((resolve, reject) => {
// 方法1:hook Worker.prototype.postMessage
const origPostMessage = Worker.prototype.postMessage;
Worker.prototype.postMessage = function (msg, ...rest) {
if (msg && msg.param) {
if (msg.param.key && msg.param.method === "xor") {
Worker.prototype.postMessage = origPostMessage;
resolve({
key: msg.param.key,
});
}
}
return origPostMessage.call(this, msg, ...rest);
};
// 方法2:hook crypto.subtle.decrypt
const originalDecrypt = crypto.subtle.decrypt;
crypto.subtle.decrypt = async function (algorithm, key, data) {
const result = await originalDecrypt.call(this, algorithm, key, data);
const utf8String = new TextDecoder('utf-8').decode(result);
resolve({
key: utf8String,
})
return result;
};
});
}
当然,你也可以自行生成RSA密钥对,模拟请求服务器来得到使用你自己生成的RSA公钥加密的AES密钥,然后再解密。
依照以上方式实现的DLsite Play作品打包下载油猴脚本:
Github:https://github.com/cpuopt/DLsite-Play-Downloader
Greasyfork:https://greasyfork.org/zh-CN/scripts/480281-dlsite-play-downloader
