EBT 道客巴巴的加密与破解 -免费下载器的基础

前一节《EBT 道客巴巴的加密与破解 序章》粗略讲了一些关系DOC88文档下载的一些枝节,主要是通过Chrome等工具来开启VIP模式,打开道客巴巴文档内容复制和打印功能,这一篇开始将进行更深入的研究。

还是以《Soilless Culture- Theory and Practice》为例:http://www.doc88.com/p-362142082976.html

DOC88文档页面中的每一页内容都由JavaScript代码和ActionScript代码两部分功能结合显现的,JavaScript部分主要是由一个称为Viewer的类来实现DOC88档案的加密解密,并产生页面的FLASH嵌入代码,包括了页面内容ebt文件的服务器端地址。而ActionScript部分则主要是通过pv.swf这个FLASH文件来实现页面内容的展示,同时它还需要为文档页的内容进行解密,这就是本文的重点内容。

先通过Chrom浏览器,FireFox也可以,取得网页的一断代码:

<object type="application/x-shockwave-flash" data="http://assets.doc88.com/assets/swf/pv.swf?v=1.7" width="100%" height="100%" id="pageflash_0" style="visibility: visible;">
  <param name="hasPriority" value="true">
  <param name="wmode" value="transparent">
  <param name="swliveconnect" value="true">
  <param name="FlashVars" value="hn=1&ph=http://ebt246.doc88.com/getebt-0rUXzqMX2LkT0qEd1qEd0jPR0jP50qEd0jPR0jP50qEX2q3V0jBApIlVpTsTsqs=.ebt&pk=http://ebt246.doc88.com/getebt-0rUR0Ln50TMQzq3R0jvS1SUV1SUS0LMS0LkR1SUS0LMS0LkR1TP50jsS1l9MGotI1q1p1v==.ebt&ptm=GotoPage&hlm=HeaderLoaded&fn=0&e404m=ViewerError&st=GetSURL&v=0&sp=false">
  <param name="allowScriptAccess" value="always">
</object>

这里取得的代码就是《Soilless Culture- Theory and Practice》的封面页,是一张大图片。这里重要的内容是FlashVars对应的参数,其中包含了两个ebt文件的URL地址,通过这个地址就可以下载到想要的页面了。这只是第一步,后头才是关键。这两个文件分别标记ph和pk,ph指向的这个文件是共用的,每一页内容都会需要它。URL地址中,在getebt-到扩展名之间有一段编码内容,明眼人一看就知道是BASE64编码内容,但其实只对了一半,它是BASE64的一种变体,我在破解中称其为BASE64DOC88编码。现在就通过链接下载这两个ebt 文件:

PH EBT:http://ebt246.doc88.com/getebt-0rUXzqMX2LkT0qEd1qEd0jPR0jP50qEd0jPR0jP50qEX2q3V0jBApIlVpTsTsqs=.ebt

PK EBT:http://ebt246.doc88.com/getebt-0rUR0Ln50TMQzq3R0jvS1SUV1SUS0LMS0LkR1SUS0LMS0LkR1TP50jsS1l9MGotI1q1p1v==.ebt


接下来就是要破解它们,让它们变成可以看的书封。在stackoverflow.com上我找到了一段代码,省了我不少时间,这里贴上修改后的代码,使用FlashDevelop编译后可以得到一个DOC88 EBT破解工具,运行后,会弹出文件选择框,就选择前面下载的两个ebt文件,DOC88破解工具会将生成最后直接可用的SWF页面内容:


	package {

	import flash.display.Sprite;
	import flash.events.Event;
	import flash.net.FileFilter;
	import flash.net.FileReference;
	import flash.text.TextField;
	import flash.utils.*;

	/**
	 * ...
	 * @author Ace 
	 * Modify by Jimbowhy
	 * 
	 * http://stackoverflow.com/questions/12121062/how-to-decompress-a-lzma-compressed-file-using-bytearray-method-in-as3
	 * This is the code that I'am using to compress/decompress files.
	 * 
	 * My problem : This method doesn't decompress LZMA compressed files :(
	 * Can anyone please tell me how to restructure the above code to achieve LZMA decompression
	 * and whether the above compression code is good enough for LZMA-compression?If not, 
	 * please do give an example of it.
	 * 
	 * EDIT : After long hours of searching,I got this but I can't quite understand the 
	 * example code in it :( Some help,anyone?
	 * https://helpx.adobe.com/flash-player/kb/exception-thrown-you-decompress-lzma-compressed.html
	 */

	public class Compressor extends Sprite
	{
		private var ref:FileReference;
		private var txf:TextField;
		private var buffer:ByteArray;
		private var glue:String = "";
		private var dos:String = "Nothing";
		private var MAGIC_ZWS:String = "ZWS";
		private var MAGIC_CWS:String = "CWS";
		private var MAGIC_FWS:String = "FWS";
		private var MAGIC_EBT:String = "YBD";
		private var MAGIC_EBK:String = "EBT_PK";
		private var MAKEUP:Boolean = true;

		public function Compressor()
		{
			txf = new TextField();
			txf.text = "SWF Compressor and Decompressor";
			txf.width = txf.textWidth + 200;
			txf.x = stage.stageWidth / 2 - txf.width / 2;
			txf.y = stage.stageHeight / 2 - txf.height / 2;
			parent.addChild(txf);
			open();
		}
		
		private function open():void
		{
			ref = new FileReference();
			ref.addEventListener(Event.SELECT, load);
			ref.browse([new FileFilter("SWF Files", "*.swf;*.ebt")]);			
		}
		
		private function load(e:Event):void
		{
			ref.addEventListener(Event.COMPLETE, processSWF);
			ref.load();
		}

		private function processSWF(e:Event):void
		{
			var swf:ByteArray;
			dos = ref.data.readMultiByte(3, "us-ascii");
			switch(dos)
			{
				case MAGIC_CWS:
					swf = decompress(ref.data);
					break;
				case MAGIC_ZWS:
					txf.text = "ZWS detected, donothing. SWF 13 and later use LZMA.";
					break;
				case MAGIC_FWS:
					swf = compress(ref.data);
					break;
				case MAGIC_EBT:
					swf = decompressEBT_PH(ref.data);
					break;
				default:
					//throw Error("Not SWF...");
					dos = MAGIC_EBK;
					swf = decompressEBT_PK(ref.data); // ebt is a compress file and light encrypt
					break;
			}
			if ( swf && glue && MAKEUP) {
				txf.text = "Need the 2nd part of ebt to makeup a page.";
				open(); // deal the 2nd part of ebt.
				MAKEUP = false
			}else if (swf && glue && dos!=glue) {
				var b:Boolean = glue == MAGIC_EBT;
				swf = makeup(b?buffer:swf, b?swf:buffer);
				glue = null;
			}
			
			if( (swf && !glue) || dos==glue ){
				txf.text = dos + " Dectected.";
				new FileReference().save(swf);
			}
		}
		
		private function makeup(ebt_ph:ByteArray, ebt_pk:ByteArray):ByteArray
		{
			var buff:ByteArray = new ByteArray();
			ebt_ph.position = 0;
			ebt_pk.position = 0;
			buff.endian = Endian.LITTLE_ENDIAN;
			buff.writeBytes(ebt_ph, 0, ebt_ph.bytesAvailable);
			buff.writeBytes(ebt_pk, 0, ebt_pk.bytesAvailable);
            buff.writeByte(64); // make 4 bytes ending
            buff.writeByte(0);
            buff.writeByte(0);
            buff.writeByte(0);
            buff.position = 4;
            buff.writeUnsignedInt(buff.length); // write file size back to header
            buff.position = 0;
			return buff;
		}
		
		private function decompressEBT_PH(data:ByteArray):ByteArray
		{
			data.position = 40; // 40 bytes bypass
			var buff:ByteArray = new ByteArray();
			buff.endian = Endian.LITTLE_ENDIAN;
			var ebt:ByteArray = new ByteArray();
			ebt.endian = Endian.LITTLE_ENDIAN;
			//ebt.writeBytes(data, 0, data.bytesAvailable); // different below
			data.readBytes(ebt, 0, data.bytesAvailable);
			try {
				ebt.uncompress();
				buff.writeBytes(ebt, 0, ebt.length);
				buff.position = 4;
				buff.writeUnsignedInt(buff.length);
			}catch (e:Error) {
				txf.text = e.message + " at line:" + /\\.+\.as:([0-9]+)]/.exec(e.getStackTrace())[1];
				return null;
			}
			if (!buffer) {
				buffer = buff;
				glue = MAGIC_EBT;
			}
			return buff;
		}
		private function decompressEBT_PK(data:ByteArray):ByteArray
		{
			data.position = 32; // 32bytes bypass in pk ebt
			var ebt:ByteArray = new ByteArray();
			var buff:ByteArray = new ByteArray();
			ebt.endian = Endian.LITTLE_ENDIAN;
			buff.endian = Endian.LITTLE_ENDIAN;
			data.readBytes(buff, 0, data.bytesAvailable);
			try {
				buff.uncompress();
				ebt.writeMultiByte(MAGIC_FWS, "ANSI");
				//ebt.position = 4;
				//ebt.writeUnsignedInt(buff.length+8);
				ebt.writeBytes(buff);
			} catch (e:Error) {
				txf.text = e.message + " at line:" + /\\.+\.as:([0-9]+)]/.exec(e.getStackTrace())[1];
				return null;
			}
			if (!buffer) {
				buffer = buff;
				glue = MAGIC_EBK;
			}
			return ebt;
		}
		
		private function compress(data:ByteArray):ByteArray
		{
			var header:ByteArray = new ByteArray();
			var decompressed:ByteArray = new ByteArray();
			var compressed:ByteArray = new ByteArray();

			header.writeBytes(data, 3, 5); //read the header, excluding the signature
			decompressed.writeBytes(data, 8); //read the rest

			decompressed.compress();

			compressed.writeMultiByte("CWS", "us-ascii"); //mark as compressed
			compressed.writeBytes(header);
			compressed.writeBytes(decompressed);

			return compressed;
		}

		private function decompress(data:ByteArray):ByteArray
		{
			var header:ByteArray = new ByteArray();
			var compressed:ByteArray = new ByteArray();
			var decompressed:ByteArray = new ByteArray();

			header.writeBytes(data, 3, 5); //read the uncompressed header, excluding the signature
			compressed.writeBytes(data, 8); //read the rest, compressed

			compressed.uncompress();

			decompressed.writeMultiByte("FWS", "us-ascii"); //mark as uncompressed
			decompressed.writeBytes(header); //write the header back
			decompressed.writeBytes(compressed); //write the now uncompressed content

			return decompressed;
		}

		
	}
}

编译好的工具可以在CSDN下载中心找到,搜索DOC88_CRACKER_JIMBOWHY就可以得到。工具使用截图如下,得到的SWF文件内容见下图:




注:本人的破解工作中,已经可以直接完成ebt文件的导出,而不必借助工具:



来张《Soilless Culture: Theory and Practice》by Michael Raviv J. Heinrich Lieth超清的封面图,只有PDF电子书才看得到这700*1025这个SIZE的。




刚去 wenku.baidu.com 百度文库去探了下环境,没想到立刻给拉到招聘现场一样!“百度文库招聘靠谱前端开发工程师,有意者请发简历到cuihongpeng#baidu.com。(邮件请注明:来自console)”,看客们用这段内容去百度文库吧,如果在找工作的话,会加分的:)。


阅读器完工,先鉴赏图,后再发贴。有了阅读器《Soilless Culture: Theory and Practice》才能真正称为电子版,唯一的差别就是非PDF,但可以是CHM。阅读器有单页、对开、阵列三种显示模式,还有页面跳转G:





花卉无土栽培技术DOC》DOC88下载破解示范已经上传: http://download.csdn.net/detail/winsenjiansbomber/8938317



破解DOC88文档下载系列未完待续。。。


  • 7
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值