道客巴巴破解记录
起因
中考后想提取试卷,发现道客巴巴的资源较多,并且可以直接看文档,然而这个网站下载文档要付费,即使是预览文档也被特殊方式加密。
试着在网上查找相关工具,发现大多数都是提取图片,顶多利用稻壳阅读器解锁打印功能把文档提取下来,于是我开始自己研究
尝试提取文件
首先肯定是抓包了,发现在滚动时会不断地出现这些文件,说明是我们要的文档数据文件:
看起来文件名像base64,但解码不出来
文件内容好像也没有特征
再看看js文件,从中找出了生成链接的代码:
很明显链接的一部分经过了某种方式加密,循着这个加密函数,从中找到了像是密钥的东西:
很像base64的码表,查找资料后发现这就是base64的变体
把这些代码完整地剥离下来,于是我们就得到了加密程序,至于解密,其实在生成相关代码中出现过了,直接用就行
然后把这几个文件名都解密下:
1 | 1-0-455235-63-20110912-20110912175543_7SOoC87j |
这些文件id数字如此地规律…
发现除第一个文件id外,用-分开的倒数第二个数字一直在递增,很明显是页码
再看最后一个,和网址中最后的数字一模一样,很明显是文档id
前几个像是日期,然后最前面的就完全不知道是什么
再仔细看看js文件,发现文件id的源头在这里:
我们看一下Config是什么内容:
没错,就是文档的基本信息,那个像是日期部分也都在p_swf里
看起来pageInfo保存着文档页面的各个id,尝试用之前剥离下来的程序解码,结果是乱码
再转回来看密钥部分,发现下面还有一个差不多的密钥,我们替换一下,解码成功:
答案出来了,就是在这里储存的文件有关id
那么,Config又是从哪来的呢?
于是我在一堆js文件里找,发现怎么找也找不到,正当一筹莫展的时候,偶然间在网页中撇到了一长串的代码:
看起来m_main.init的内容很像是base64,极有可能和之前的加密一样,我们直接用解工具解出来:
没错,这就是Config的内容
到现在,我们就可以做出下载流程了:
1 | 输入URL->从网页中解析配置->获取文档页数->下载头文件->下载每页的文件 |
文件解密
下载出的文件作用不太清楚,只有第一个文件的头文件是YBD,其他的完全没有规律,于是在网上查找资料,偶然间发现了这几篇文章:
于是我们得知,这其实是flash实现的文档查看功能,利用头文件(PH)和页数据文件(PK)就可以合成出一个flash文件,至于如何合成,文章也给出了相应代码,可是原工具是ActionScript写的,于是我费劲周折,找出ActionScript的相关编译工具,然而最终还是因为一个兼容性问题放弃了
最后在网上查找时,不知道从哪找到个度盘链接,然后就下到了原作者做的工具包,现在再去找已经找不到了,也许已经成为一个失传媒体了,于是传到互联网博物馆了(dda_doc88_cracker),直链下载:dda_doc88_cracker.zip
不过总之,还是拿到了解密工具,我们运行下:
很明显,要先选择PK文件,对吗?显示检测到YBD头部,看起来很对,再选择PH文件,也检测到了,再让我们保存文件,就这样成功了吗?不对,转出来的文件没有任何内容:
再换个顺序试试?
成功了,原来是程序本身的BUG,既然能提取,那么就放心了,直接右键打印到PDF,便可导出PDF文件,很简单不是吗?
但是如果一个个都这么搞的话,那就太麻烦了,于是我着手编写解密程序,我用的是python
然而,怎么用python去自动化这个过程呢?
这个解密程序是古老的ActionScript语言,根本没有人会把这玩意转到python里去,但是在GitHub上有转到js的工具:
as3js
于是我们得到js文件,理论上可以直接转到py,但是里面有些用于GUI的方法不能用,有没有一种可以自动适配的转换器呢?
所以我直接用AI转换出来了,那么就有人问,既然都用AI了,为什么不直接从as转过去呢?答案是:AI看不懂ActionScript的一些方法
AI把GUI换成CLI操作了,于是用这个解密程序输入PK文件,PH文件,导出路径,得到swf文件,一顿操作下来没有任何错误,是吗?
然而,是损坏的,于是我再去调教AI更正这个错误,无果,用十六进制查看,发现多了一个FWS头文件,破案了,原来AI错误把FWS头文件重复地附加上,把相关代码删掉就能用了
于是就得到解密程序
仔细看一下解密是如何进行的:先输入PH文件,跳过前40字节,用zlib解压,再输入PK文件,跳过前32字节,解压,将两次结果合并,得到swf文件
所以说,这个文件实质上并没有加密,仅仅只是把一个swf文件分割成两部分然后分别压缩罢了
文件转换
文件解密程序已经有了,其实有flash播放器就可以直接用了,也可以打印出高清文档,可是只能一个个打开并提取,太麻烦了,怎么自动化呢?
于是我查找了一下swf到pdf的转换工具,有些是python写的,然而根本不能用,再找反编译工具,发现ffdec本身就能把swf转到pdf,于是按照帮助文档我试了下转换,结果是:转换出来的只有图片和部分形状,文字全部消失
不过帮助文档中还有个转换到svg的,结果出乎意料,文字形状图片全部保留,但是无法复制文字,与用flash导出pdf的一样,不过够了
但是我们需要的是pdf,于是在网上搜索svg2pdf,很快就找到了一个名为cairosvg的库,利用它很快就把pdf给转出来了,再用pypdf库把文件合并为一个,一个文档就被完整地提取出来了
到这里似乎就已经把提取器做好了,果真如此吗?
当我尝试提取另一个文档时,问题出现了:ffdec没有转换出任何文件,没有任何提示哪里有问题
然后我敏锐地察觉到应该是swf的头部信息有问题,用ffdec打开后,果然发现帧数量居然为0,修改后便能正常转换
但是提取出来的部分文档仍有许多瑕疵,并且还有个重大bug,50页以后的文档全部解密失败
文档转换优化
大部分文档都能成功通过这个方式转换,但是有些就有问题了,要么只显示部分要么就全白了,然而用flash播放器打开却没有任何问题,很明显是ffdec的问题
经过一段时间研究,发现是有些形状在ffdec中被错误解析,导致大片空白覆盖,删掉就恢复正常
怎么自动化这个过程呢?什么?要我自动找出错误形状并自动删除?很可惜,这东西连AI都办不到
那只好先放着,反正这种情况不太常见
不过后来,我又发现ffdec里的高级设置中的font-face功能居然可以开启文本转换,转换出来的SVG文件直接可以复制文本,但是除了有文本,问题一大堆,错位、乱码、字体消失层出不穷,甚至cairosvg遇到这种文件直接开始报错,只能转极个别页面
好了,知道是ffdec问题就该反馈给这个工具的原作者,结果作者在一天之内推了5个commit,把我提到的bug都修好了,我去这神仙效率:
(如果有条件的话可以去打赏这个作者,毕竟我的转换器功劳最大的就是这个工具了,一年才被打赏几百美元也太可怜了)
看到这个的时候非常震惊,可是当时还在上信息课,没法更新我的工具,只能叹为观止
现在放假回来马上更新了工具,本以为只给作者发了两个我认为典型的swf文件,可能不太适用于其他页面,出乎意料的是,除了转到SVG确实会出现如作者回复所说的间隔问题,其他BUG不再复现,更令人意外的是,直接转到PDF完全没问题了,找了十几个文档,全部正常转换,全部显示正常文本(但是用edge预览时会出点渲染问题,所以edge就是个垃圾),也能正常复制文本甚至是他们自己的文字识别贴上去的全透明文本也可以
所以干掉了cairosvg转换方式,全面使用ffdec(其实cairosvg方案还保留着,只是作为一个备选方案在配置文件里)
到目前为止,这个工具除了在速度上太慢了就已经超越了网上100%的提取工具
深入研究
51页后下载出的文件是损坏的,无法解压,那么问题就出现在下载程序,很容易就能想到这是给多页文档分层了,又发现我们把文件id的第一个数字设定成了1,于是很容易想到这是每50页层数+1,再次抓包验证,看一下51页后的文件名,解码结果是:
1 | 2-0-453032-63-20110912-20110912175543_7SOoC87j |
猜想没错,修改程序,恢复正常
到这里就完了吗?
再试试别的文档,又报错,原来有些文档并不完全是按照这个逻辑来分割的,有些上百页的文档却始终只有一个头文件,而且分割的页数也不定
我们再次审视pageInfo的内容
1 | 1-595-841-455235-3936 |
发现第一个数字已经把层数已经定好了,根本不用去手动计算,直接根据这个数字找出对应的头文件就完事了(然鹅就这我修了好几个版本才弄好
再看看其他数字的意义,595-841是固定的,而且在构造文件名时没用上,我们就把它当成第二个文档id吧
再看后面两个数字,第一个数字总是上一页的第二个数字加上上一页的第一个数字,看起来有戏,那么最后一个数字有规律吗?喂给AI,没有结果
可能是文件大小?把下载的文件属性一看,好家伙,一模一样
那第一页的倒数第二个数字是从哪来的?再看看对应的头文件,455235字节,完全一致
就这样解开了?
诶不对,第二层的倒数第二个数字怎么倒退了,哦原来是换了个头文件重新开始计算
文件id差不多弄清楚了,再来探索下有没有别的东西
道客巴巴提供了一个任务池的东西,可以把提出任务让别人寻找这个文档,别人发的文件虽然可以预览,但是只有1页
然后把预览网址贴到我的程序,兼容性很强,直接完美提取
再来试试别的,诶好像不对,pageCount的数字怎么和pageInfo的数量对不上,哦原来是对于分层文档,每个头文件的第一页都被解析了,通过这个这可以解析出那些原本看不到的文档了
但是只能解析每层的第一页,那么如何强制解析额外页面呢?
所以我试了下修改获取文件的长度,从0到1024000,可以下载,但是内容包含多个连续文件(从0开始的PH到这层的最后一页或者达到长度要求)
既然可以获取额外数据,那如何分离呢?
zlib压缩数据的文件头是固定的,所以只要得到第一个文件的文件头,就能通过查找文件头的方式分离
但是没有这么简单,文件头数据只有两字节,极有可能找到错误位置,好在数据尾部有4字节的校验,可以直接用zlib做解压测试,只要解压成功就直接合成出页面
这样一来,只要能显示一页,整个文档就能提取出来
差不多到此为止了
后续计划
1. 提取那些没有被解析出来的额外页面(已实现)
2. 完善缓存和配置文件
3. GUI
说些什么吧!