下载 《电子工业出版社》 开放的 pdf 电子书
用 chome dev tools 调试了下,找到 pdf 是用 AES 加密过的。主要是在这个文件里断点:
https://yd.51zhy.cn/ebook/reader/static/js/0.f4c9028886a7b391c0df.1574068669001.js
搜索 AES.decrypt
跟踪到 pdf 解密流程是根据 this.v.authorKey
来解密的。如何计算 this.v.authorKey
呢?
继续搜索 authorKey
找到
r.authorKey = r.makeKey(r.readData.devicekey, e.Data.Key)
断点调试得到 devicekey
为: LQBhm1oQvo2pDGBO
Data.Key
为 9TWh4BIVxRJMrYnyw1wlx8Huz/eLPe6H0Y5VRHmOl90=
查看 makeKey
的实现如下:
makeKey: function(e, t) {
e = e;
var n = _.a.enc.Utf8.parse(e);
return _.a.AES.decrypt(t, n, {
mode: _.a.mode.ECB,
padding: _.a.pad.Pkcs7
})
},
用 openssl 命令就是这样的:
echo '9TWh4BIVxRJMrYnyw1wlx8Huz/eLPe6H0Y5VRHmOl90=' | openssl enc -d -aes-128-ecb -a -K 4c5142686d316f51766f32704447424f
其中 4c5142686d316f51766f32704447424f
是 LQBhm1oQvo2pDGBO
的 hex 形式。
算出结果为 DzglNMjfYapT9XGy
, 再算出对应的 hex 为 447a676c4e4d6a665961705439584779
然后再从 chrome dev tools 的 network 里找到 pdf 的下载链接,右键拷贝出 curl 命令,然后把页码换成下面的变量 $i
,批量下载。最后用 pdf 工具合并搞定,合并工具我选的 cpdf。
for((i=1;i<=432;i++));do
echo "$i".pdf
# 下载 pdf
curl "https://file.51zhy.cn/files/encryptfiles/b2a/....../"$i".pdf ... -o "$i".pdf.aes
# 解密 pdf
openssl enc -d -aes-128-ecb -K 447a676c4e4d6a665961705439584779 -in "$i".pdf.aes -out "$i".pdf
done
# 合并 pdf
cpdf *.pdf -o ../out.pdf
上面的方法仅用于学习目的,强烈建议不要用来做爬虫。
20200206更新:
上面走了点弯路,用下面的 tampermonkey 脚本方法更方便。
// ==UserScript==
// @name bridge.51zhy.cn
// @namespace http://tampermonkey.net/
// @version 0.1
// @grant GM_xmlhttpRequest
// @description try to take over the world!
// @author You
// @match yd.51zhy.cn/*
// @grant GM_setClipboard
// ==/UserScript==
let allText = {};
(function (open) {
XMLHttpRequest.prototype.open = function () {
if (arguments[1].includes("/content/authorize")) {
this.addEventListener("load", function () {
let responseOBJ = JSON.parse(this.responseText);
allText.Key = responseOBJ.Data.Key;
allText.Url = responseOBJ.Data.Url;
allText.deviceKey = localStorage.deviceKey;
let keybase = CryptoJS.enc.Utf8.parse(allText.deviceKey)
let authorKey = CryptoJS.AES.decrypt(allText.Key, keybase, {
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7
}).toString(CryptoJS.enc.Hex)
allText.authorKey = authorKey;
delete allText.Key;
delete allText.deviceKey;
console.log(allText);
let ret = "authorKey='"+allText.authorKey+"'\n"
+ "url='"+allText.Url+"'\n"
+ "isbn='"+allText.Isbn+"'\n"
+ "title='"+allText.Title+"'\n";
console.log(ret);
GM_setClipboard(ret);
alert("Url copy to clipboard OK!");
}, false);
} else if (arguments[1].includes("Content/Detail")) {
this.addEventListener("load", function () {
let responseOBJ = JSON.parse(this.responseText);
allText = {};
allText.Isbn = responseOBJ.Data.ExtendData.Isbn;
allText.Title = responseOBJ.Data.Title;
}, false);
}
open.apply(this, arguments);
};
})(XMLHttpRequest.prototype.open);
然后粘贴到脚本下载和解密
authorKey='594c4528626731776a79304867436243'
url='https://file.51zhy.cn/files/encryptfiles/59c/40181f28a7f5e76f2974cdb747fec68598844e26.pdf'
isbn='978-7-121-27637-8'
title='游戏中的数学'
curl $url --compressed -o "$title".pdf.aes
openssl enc -d -aes-128-ecb -K $authorKey -in "$title".pdf.aes -out "$title".pdf
202002061714 更新:
这东西见光就死了,现在 js 代码里的字符串被转正 hex 格式了,解密算法应该也修改了,断点调试还是一样的,只是搜索字符串麻烦了点,需要先转成字符串看看要搜的东西是啥,比如下面这个脚本是将 hex 转成字符串的:
# tostr.sh
TESTDATA=$(echo $1 | tr '\\x' '0x ')
echo $TESTDATA
for c in $TESTDATA; do
echo $c | xxd -r
done
sh tostr.sh '\x6d\x6f\x64\x65'