雍正为什么能当皇帝:常见电子书格式及其反编译思路

来源:百度文库 编辑:九乡新闻网 时间:2024/04/28 02:39:57

常见电子书格式及其反编译思路


1. 前言
2. 常见电子书格式及其反编译思路
   2.1 PDF格式
   2.2 基于IE内核的电子书
       2.2.1 CHM格式
       2.2.2 EXE格式
             2.2.2.1 Web Compiler 1.67
             2.2.2.2 Caislabs eBook Pack Express 1.6
             2.2.2.3 通用反编译思路
   2.3 HLP格式
   2.4 小说网/小说世界(ebx/XReader)
3. 结论
附录 基于IE内核电子书的实现方式探讨

1. 前言

本文所描述的电子书,指的是将原始的、可编辑的HTML、TXT、RTF、图像文件等,打包成一个独立的EXE,或其它只有专用浏览器才能读取的文件,打包后的文件通常不可用常规工具进行编辑、全文检索。

本文所描述的电子书反编译,指的是将电子书中的内容提取出来,还原或转换成标准的、可编辑的HTML、TXT、RTF及图像文件等。

就像世间其它事物一样,电子书编译器和反编译器的出现也都不是偶然的,都有其必然性。

在电子书编译器这一方来说,大概从有电子文档那天开始,就有人琢磨着要对电子文档打包了。我个人认为这主要是从以下几个方面进行考虑:

  1. 便于阅读、管理。当年在DOS下阅读文本文件,尤其是中文文件比较麻烦,因此出现了自带中文字库、自带基本浏览(翻页、滚动)功能的DOS电子书;由于需要在不同OS平台上获得相同的阅读效果,因此产生了跨平台的PDF格式电子书;随着互联网络的发展,大量信息以HTML格式出现,但是面对一大堆HTML文件,并不是每个人都知道该去双击index.htm或default.htm的,而且文件太多,管理也成问题,因此出现了CHM格式和各种基于IE内核的EXE格式电子书。
  2. 便于保护知识产权、商业机密。这个问题的重要性相信大家现在都能理解了,不要说那些包含核心商业机密的东西,就算是区区一本小说,都会有些卑鄙小人把原始的HTML、TXT文件拿去加LOGO、打包,然后声称是自己“辛苦扫校的成果”,再堂而皇之地收取所谓“VIP费用”。因此PDF一直将文档安全性作为卖点之一,国内的各种独门格式电子书也以防反编译、防内容复制为首要目标。

而反对将通用格式打包成独门格式的人,当然也有自己的道理:

  1. 便于全文检索。如前所述,电子书一般不可用通常的检索工具进行全文检索,这就为资料的有效利用设置了障碍。我个人认为,藏书量在几十本、上百本的时候,手工建立摘要、索引可能还可以接受;再多以后,我想要的就只是一个快速的全文检索工具,就好像在互联网环境下,对google的依赖一样。
  2. 便于修改。俗话说:“金无足赤,人无完人”,电子书也是人做的,有时难免会出点什么错,或者因为资讯的发展,需要对原有内容加以修正、补充,这个时候如果面对的是一个不可编辑的EXE,您会有什么感想?
  3. 节省时间和耐心。Windows在显示文件列表的时候,需要读取文件信息,EXE文件还要读取ICON等,如果装有反病毒软件,进入文件夹的时候,反病毒软件一般还会自动对文件夹中的EXE文件进行自动检查,而电子书大小一般都在MB级,因此打开包含EXE格式电子书的时候,感觉速度巨慢,比较令人反感。
  4. 节省空间。一般EXE格式电子书的标准架构是:可执行体+内容+TOC。可执行体指的是电子书的执行代码部分,包括程序代码、插件代码、界面资源等。内容指的是电子书中真正包含的文本、图像内容,一般使用某种压缩、加密算法进行处理。TOC(Table Of Content)相当于目录索引,作用是加速对内容的访问。因此相对于直接用WinZip、WinRAR对原始内容进行压缩,每一本EXE格式的电子书都会浪费一部分磁盘空间,以存储执行体部分。电子书的软件界面越花哨,这种浪费一般也越大,我见过最夸张的电子书比原始内容足足多出 1 MB多的东西。
  5. 避免垃圾。对于某些基于IE内核的电子书来说,由于实现技术的限制,可能会在注册表和系统目录下留下垃圾。
  6. 安全。如果说如今的网络社会是一个充满恶意、毫无诚信的环境,可能有点夸张了,不过确实有人不知“做人要厚道”为何物。老实说,每次拿到一个来路不明的EXE格式的电子书的时候,我都在怀疑里面有没有什么木马、病毒,实在难受。
  7. 便于平台转换,包括转换到手持设备。EXE格式的电子书看起来可能很爽,但是毕竟只能在Windows下看,如果想在其它系统下看,尤其是在手持设备上看,唯一的出路就是反编译了它。

当然,在反编译后,也必须寻找合适的替代品,以继续满足原先的需要:

  1. 打包工具。建议选择Winzip或WinRAR,不仅使用方便,而且打包后文件也小,进入目录还快。
  2. 阅读工具。现在可以不解包就直接阅读zip/rar文件内容的软件不少,一搜一大把,我自己都做过一个MyReader,不仅可以直接从ZIP/RAR中读取内容,还有自动定位index.htm、书签、现场保护、资源浏览器右键菜单扩展、zip/rar密码自动记忆等功能。
  3. 全文检索工具。可以直接在zip/rar中全文检索的软件也有不少,我自己也做过一个FindStr,支持加密zip/rar,这个工具还可以与MyReader集成,搜索结果可以直接用MyReader直接打开,不需解包。另外它还支持批量文本替换,所以也经常被我用来整理下载到的或反编译出来的小说,包括去除广告链接、绝对URL改成相对URL等。
  4. 对劳动成果的保护。这个直接用zip/rar的密码保护就好。

2. 常见电子书格式及其反编译思路

2.1 PDF格式

PDF格式是Adobe公司推出的一种跨平台电子文档格式,现已被接纳为ISO标准,用户可以在不同平台下获得相同的阅读效果。

PDF文件如果没有其他限制,用Adobe Acrobat Professional等软件可以直接另存为Word,或导出纯文本、图像。这样的结果可能是某些人不希望看到的,因此市面上也就有了各种PDF文档保护方案。目前比较常见的PDF保护手段包括:

  • 用口令对PDF进行保护,限制编辑、打印、复制、另存等操作。对于这样的PDF,目前有Advanced PDF Password Recovery等软件可以去除保护。
  • 基于数字证书的加密。通常认为数字证书比口令更安全,但据可靠消息,目前国内已经有人能够利用Acrobat本身的漏洞,破解Acrobat 6.x~9.0的证书保护。
  • 基于安全插件的加密。不同的插件具有不同的安全性。

以上PDF文档保护方法在使用上有一些限制:

  • 基于口令保护的PDF可以设置用户口令(user password)或管理口令(owner password)。如果设置了用户口令,则每次打开PDF均会提示输入口令,用户体验不太好。而如果没有用户口令,只有管理口令,则用Advanced PDF Password Recovery等软件可以轻松去除保护。
  • 基于数字证书的加密对PDF阅读器有要求,不是所有阅读器都支持的。
  • 基于安全插件加密的PDF通常只有在安装特定客户端软件的机器上才能阅读。

为了避免上述麻烦,同时又达到避免被人导出、复制PDF中的文本内容的目的,也有人采用了一些变通的办法。这些办法力求采用最简单、最标准的PDF技术,以降低对PDF阅读软件的要求,但又让人“看得到,拿不到”,至少是“不容易拿到”。这样的保护手段包括:

  • 文字转换成图像。 转换以后,自然就不存在任何可以导出、复制的文本信息。代价是PDF文档将不能搜索,放大显示时可能会出现锯齿或模糊,文件尺寸也可能变大。
  • 文字转换成路径(path)。 转换后同样不存在任何可以导出、复制的文本信息,放大显示时不会出现锯齿或模糊,文件长度通常要比转换成图像小。缺点也是不能搜索。
  • 采用特殊内嵌字体。 如果字体的字符映射方式与标准Unicode不同,复制、导出的文本看起来就是一堆乱码。这种方式的PDF文件通常文件长度要比以上两种小,显示效果看起来和常规文本PDF无异,只是不能搜索,或者说搜索出来的结果不对。

以上这几种保护手段有一个终极性质的破解手段:OCR。如果需要OCR的内容就是屏幕上看得到的内容,推荐用“慧视小灵鼠”,否则还是用其他OCR软件吧,如Office 2003的MODI、汉王OCR等。而批量OCR之前需要先获得PDF图片,虽然现在有很多软件都可以做这件事,但是PdfToy专门做了一些加强。

除了上述内容保护手段外,有时为了表示“版权所有”,还会在PDF中加入水印。目前常用的水印技术包括:

  • 图像水印。即用图像对象做背景水印。如果图像对象被人抹掉,水印自然也就消失了。
  • 文字水印。文字通常不用单独的对象表示,而是在页面描述中绘制出来。但是文字水印中的文字比较明显,容易被人搜索、定位,然后抹掉。
  • 路径(path)水印。这种方法是以上两种的变种,即把水印图案或文字改成用路径绘制,因此定位相对麻烦一些,不太容易被抹掉。
  • 裁剪水印。这种水印更进一步,把水印图案或文字转成路径,然后用路径作为裁剪蒙版,对一大堆细如鱼刺的小区域进行裁剪,裁剪的结果就是显示出来的水印。

理论上说,所有水印都可以被抹掉:

  • 如果熟读Adobe公司出版的《PDF Reference》,平时都习惯用UltraEdit32来浏览PDF文件,那么只要把水印的定义或绘制代码抹掉,自然水印就没了。
  • 如果只会用“所见即所得”的PDF编辑软件,推荐使用PitStop、Foxit PDF Editor等。Foxit PDF Editor使用简单,但是不支持批量,页码或文件一多就不行了。PitStop是Acrobat的一个插件,使用稍微复杂一点,但是支持批量,网上也可以找到教程。

当然,在实际操作过程中,能否真的把水印抹掉,就要看运气和技术了。

2.2 基于IE内核的电子书

随着互联网的发展,现在越来越多的网络文档内容是以HTML格式提供的,而微软本身又以控件的形式提供了IE浏览器的内核,可以很方便地被几乎所有Windows下的编程工具所调用,因此目前基于IE内核的电子书似乎占据了主流位置。

2.2.1 CHM格式

CHM(发音为“chum”)的原意是Compiled HTML help file,是微软作为HLP格式(16位Windows下的标准帮助文件格式)的替代格式提出的,因此微软自己不仅随4.01以上版本的IE一起提供免费的浏览器,而且免费提供制作工具Microsoft HTML Help Workshop。

CHM文件内部使用ITS格式,这是一种非常优秀的压缩格式,感觉压缩比要比zip、rar大。

由于ITS格式的开放性,国外早就有人做出了CHM格式的独立编译、反编译工具,并且公开了全部源代码,需要的人可以到这里看:

http://bonedaddy.net/pabs3/hhm/

这个网站除了提供CHM编译、反编译工具及其源代码外,还提供CHM格式的详细说明,当然是英文的。我做的UnEBook在开始的时候,就使用了其中chmdeco的源代码,实现批量反编译CHM的功能。如果这个网站不幸登录不了,google一下chmdeco就好,有很多备份站点的。chmdeco内部使用的是chmlib的源代码,这份源代码很有名,除chmdeco外,chmtools用的也是它。

不过在使用了一段时间后,我发现这份代码在反编译某些CHM文件的时候,会出现数组越界错误。这种错误出现的概率虽然不大,但是出现后还是比较心烦,因此最终放弃了这份代码。

现在UnEBook使用的CHM反编译代码是从这里改出来的:

http://www.codeproject.com/winhelp/htmlhelp.asp

这份代码使用了微软未公开的ITS文件访问接口,直接对文件进行操作。由于使用的都是微软的东西,不仅目标码比较小,兼容性也好得多,目前还没有遇到反编译不出来的CHM文件(唯一的一次例外,是那个CHM文件本身就打不开),内存漏洞什么的也没有发现。看来微软的东西还是要由微软来对付,方为王道。

另外某些人制作CHM电子书的时候,为了省事,没有制作index.htm,而是单纯依赖左侧的目录树进行导航。对于这样的电子书,在反编译后,一般还需要根据生成的hcc文件,自动生成一个索引页,以免看的时候不方便。hcc文件结构大致如下:

  1. 多级目录通过
      控制,见到
        的时候往下走一级目录,
      往回走一级。
    • 目录项以开始,以结束。以存放项名称,存放项链接。
    • 某些目录项可能只有名称,没有链接。

在UnEbook中,不仅能够根据hcc文件自动生成索引页,还能自动生成框架页,将索引页和显示页嵌入框架中,以最大限度模仿CHM中的目录效果。如果要完全模仿能够动态伸缩的树形目录效果,则需要增加图片、js、css等文件,实在得不偿失。

2.2.2 EXE格式

除了CHM格式外,大量基于IE内核的电子书是以EXE格式提供的。制作EXE格式的电子书工具现在似乎已成为一个产业,养活了大批的程序员。虽然很多人认为这种格式的电子书很酷:一个文件就可以执行,界面也可以做得很漂亮,还可以带密码保护。但是我个人对这种格式的电子书是最最痛恨的:除了前面说到的安全性、速度、空间、检索等问题外,我最心烦的一点是目前的EXE电子书都没有好用的书签功能,尤其是没有能够定位到页面中任意位置的书签功能,看长文档看到一半的时候被打断会很麻烦,所以自从MyReader实现了书签功能后,我就下定决心一定要解决反编译问题。

2.2.2.1 Web Compiler 1.67

这种格式的电子书,因为其制作工具在国内出现得比较早,而且有非常彻底的汉化解密版,所以曾经比较流行,E书时空提供的很多电子书都是这种格式。不过也正因为它的流行,导致想反编译它的人也多,引出了各种反编译工具,所以现在用的人似乎已经不多了。

反编译工具里,收费的就不去说它了,国内RMH和Fbilo还联合推出过免费的unwebcompiler,并且提供全套的Delphi源代码,有需要的到google或百度搜索一下unwebcompiler就有了。不过可能国内大多数软件网站的管理员都不是开发人员出身,对源代码不感兴趣,所以收藏的都是212 KB的EXE,有源代码的不多,需要仔细找一下。

在unwebcompiler的源代码里,RMH和Fbilo对Web Compiler 1.67生成的电子书的文件格式进行了详细描述,在这里我就不做无聊的重复,有兴趣就自己去看吧。我做的UnEBook也使用了他们提供的源代码,实现对Web Compiler 1.67生成的电子书的批量反编译,不过被我将代码从Delphi改成了C,似乎长度缩短了一些(原代码中有一段在字符串和十六进制数之间转换来、转换去,看起来比较怪异,被我省了),不过LHA解压缩部分改起来实在太麻烦,我直接在网上找了一段现成的C代码来用。

2.2.2.2 Caislabs eBook Pack Express 1.6

这个电子书制作工具也出过汉化版,所以在国内也有一定影响,不过这种影响似乎还没有大到足以使反编译工具满天飞的程度,嘿嘿……

在分析这种格式的电子书的时候,我没有使用任何反汇编工具,用UltraEdit32和系统监视工具就猜出来了:

  1. 文件标识:以十六进制串 00 F8 03 00 结尾。这个似乎是一种惯例,差不多所有EXE格式的电子书都有自己特殊的文件结尾。
  2. 目录块起始地址指针:0003F81C
  3. 目录块中目录项结构:以0字符结尾的文件名+4字节起始地址,文件名起始字节为FF则目录块结束。
  4. 如果文件存放在子目录里,则文件名首字符:02=../,01:第一个00变成/,直到遇到02。
  5. 文件内容实际起始地址:目录项里的4字节起始地址+9
  6. 文件内容长度:目录项里4字节起始地址所指内容,DWORD。

在分析出目录结构后,我曾经想通过调试工具,分析文件加密算法,再反编译出具体的文件内容,但是很快我就发现那样干太累了,实在是得不偿失。

不过在经过几次尝试后,我还是找到了一个偷懒的办法:

  1. 通过安装hook的方法,往电子书的进程空间注入一个DLL。
  2. 在这个DLL里,用Windows标准的API函数URLDownloadToFile,就可以下载到指定的文件。文件的URL可以按前面说的方法,从目录项得到相对路径,再加上一个固定前缀("file://Z:\\com_caislabs_ebk\\")构成绝对路径。

UnEbook在批量反编译这种格式的电子书的时候,就是按照上面的分析结果实现的。

不过到了更高版本的Caislabs eBook Pack Express的时候,似乎Caislabs公司也开始意识到文件内容保护的重要性,因此不仅对文件内容采用更强的加密算法,杜绝了可以用URLDownloadToFile下载的漏洞,连目录块的加密强度都强到足够使我不想去分析了。幸好这个时候我已经有了更好的反编译思路--与具体文件格式无关的,专门针对使用IE内核的电子书的通用反编译思想。

2.2.2.3 通用反编译思路

在分析过几种电子书格式后,我开始领悟到一个真理:电子书内部文件结构的变化是无穷的,而我的时间和精力是有限的;把有限的时间和精力投入到对抗无穷的变数中去,早晚会有累死的一天。

有此认识后,我开始思考有没有什么通用的方法,可以解决大部分电子书的反编译问题(我还没有幼稚到相信这世上会有万能药的程度)。按照惯例(不可救药的职业病),第一步当然是市场调查、产品定位,结论是目前大多数电子书都是基于IE内核的,但是根据我在开发MyReader时对IE内核的了解,这里面明显存在一个误区:微软以控件的形式提供IE内核,其目的就是希望通过控件接口的开放性、方便性,吸引更多的人加入微软的标准阵营,如果想在此基础上添加加密、保护等等内容,恐怕与微软的初衷不合(我说的是当时,以后微软改主意了也说不定)。因此我相信IE内核一定有后门可走!经过一番努力,果然没有令我失望。

1、基本原理

针对IE内核电子书的通用破解技术实现起来可能需要一些技术和技巧,但是原理却很简单,几句话就可以说清楚:不论电子书在存储的时候如何对内容进行加密,在将内容传递给IE内核进行显示的时候,一定要将内容转换成IE内核能够识别的标准格式--HTML格式。而IE内核为了便于显示、刷新,在对HTML代码进行解析后,并不是立刻就把这些HTML代码抛弃,而是在内存里保存了一份备份。因此只要将这份备份从IE内核里搞出来,就得到了解码后的内容,也就是反编译想得到的内容。

至于网页中的其它内容,包括图片、css、js、Flash文件等,就更简单了:模拟IE内核,直接找电子书要就好。如果电子书分辨不出请求是来自IE内核还是来自其它地方,自然会乖乖把我们需要的东西双手奉上!

虽然反编译的原理几句话就可以说清,但是要加以实现,还需要经过艰苦的探索和试验,我自己就经过了长期的努力,IE内核的源代码都翻来覆去看了好几遍(吹的,别当真!)。而我思想的发展也大概经历了两个阶段:第一个阶段是在得到某份传说中的源代码(没错,就是那份展开后近700MB,被国内主流媒体形容为噱头、无足轻重、充满无聊垃圾的东西)之前,完全立足于微软公开的IE内核接口。当时我考虑将电子书内容按照HTML、图像等分类,分别解决获取问题。第二个阶段是在得到那份源代码之后,我突然发现其实对于所有文件,我都可以直接找电子书要,只要假装是IE内核在要就行了。

由于某些东西比较敏感,因此下面叙述的主要是我第一个阶段的想法,其中有些属于基础性的东西。第二个阶段的实现恕我不便奉告。

2、获取HTML源代码的方法

从IE内核获取HTML源代码的方法不仅我一个人在想,从国内到国外,从CSDN(CSDN的VC/MFC区有一个栏目专门讨论IE内核编程)到MSDN,早就有很多人讨论过了,归纳起来,一般认为可以通过下列步骤实现:

  1. 不管是通过鼠标点击也好,通过EnumChildWindow也好,总之先找到IE内核的显示窗口,也就是电子书显示网页内容的那个窗口。
  2. 通过这个窗口的句柄(HWND),取得这个窗口对应的IE内核文档接口IHTMLDocument2的接口指针。取得的方法目前认为有两种,我个人认为这两种需要结合使用,否则总有一些电子书会搞不定:一个是通过MSAA,一个是通过WM_HTML_GETOBJECT消息。至于具体的实现代码,在CSDN上都快被讨论烂了,因此此处从略,有需要的自己到CSDN上找。不过这两种方法都对平台有要求:XP下是完全没有问题,2000下可能需要装IE 6,98/Me/NT就不要想了。
  3. 在得到IHTMLDocument2接口指针后,按照这个接口提供的标准方法,即可获得文档的HTML代码。具体实现代码见CSDN中的例子。

除了上面这种方法外,我自己还尝试过一种方法:使用MIME Filter。

对于搞过网页在线翻译、网页内容过滤的人来说,MIME Filter可是吃饭的本钱,它的作用和实现机理应该早就烂熟于心,但是对于其它人来说,可能还不是很熟,所以这里简单介绍一下:为了便于对IE内核的功能进行扩展,微软规定在IE内核显示某种标准格式(HTML、TEXT等)的内容之前,会先将要显示的内容传递给这种格式的过滤器,即MIME Filter,由它先对内容进行预处理(如将英文翻译成中文,将下流文字替换成星号等),然后再显示。

按照这个原理,如果实现一个针对HTML格式的MIME Filter,即可拦截到最原汁原味的HTML代码。可惜,经过我的尝试,这招对IE本身是灵的,对某些电子书也有效,但是对另一些无效。再加上使用IHTMLDocument2接口指针的方法要比这种方法简单得多,也可靠得多,所以后来在我开发的反编译工具KillEBook、IECracker和CtrlN里就没有使用这种方法。不过这种方法也有一个好处:与平台无关,我在98/Me/2000/XP下都试过,当然都是在虚拟机下试的啦。

MIME Filter的作用机理、实现方法在MSDN里有详细说明,并提供了详细的实例代码,有需要的可以到MSDN上搜“MIME Filter”。

3、获取图像的方法

与HTML代码相似,IE内核对图像的处理也有一个“下载->解码->显示”的过程。考虑到显示代码的抽象性,原来各种各样的图像格式,包括JPG、GIF、PNG、TIFF等,在解码后都被统一表示成位图格式,而原有格式数据在解码后即被从内存中释放,只在IE的cache中留有文件备份。如果指定不允许保存本地cache,则连这个备份都没有。在IE中通过右键菜单选“图片另存为...”的时候,其实就是将cache中的文件备份拷贝一份出来,如果cache中已经没有备份,就只能保存内存中的位图(*.bmp)了。现在明白为什么有些图片明明是jpg格式,但是用IE却只能保存为“无标题.bmp”了吧?

因此,获取图像文件要比获取HTML文件难得多。而且在MSDN里说得很清楚,用IHTMLDocument2接口只能得到图像的链接,用MIME Filter也不能搞到网页里的图像数据,因此需要另想办法。我想过、试过的包括:

  1. 先将图像复制到剪贴板,再从剪贴板里获取图像数据,然后根据图像文件扩展名(可以从图像元素的URL里解析),编码成原始图像格式,包括jpg、png、gif、tiff等。这个方法实现比较简单,到MSDN KB里搜索Q293125,拷贝图像到剪贴板的现成源代码就有了,图像编码的源代码则可以参考cximage,这个也是google一下就有的。不过这个方法远非完美无缺:a). 对于png、gif等允许带透明背景的格式,用这种方法处理后就不透明了。b). gif动画处理后就动不起来了,只能显示其中的某一帧。c). 对于jpg这样的有损压缩格式来说,每压缩一次就损失一次,多压缩几次可能就没法看了。d). 在电子书里,可以通过标准的Windows API函数,使剪贴板失效。
  2. 将IE内核导航到图片,然后通过IViewObject接口获取图片的拷贝。这个方法与上面的方法基本相同,不过不通过剪贴板,可以防止因为剪贴板被封锁而搞不到图像。
  3. 使用IE图像解码插件。IE内核在下载到某种格式的图像文件后,会调用对应的解码器,对图像进行解码(类似于MIME Filter)。为了便于扩充,解码器是做成插件形式的。如果自己做一个图像解码器插件,对解码请求进行拦截,即可获得解码前的原始图像格式数据。解码器的接口、实现方法在微软公开文档中没有任何蛛丝马迹,但是在那份传说中的源代码里,不仅有详细的接口规范,而且有好几个内嵌图像解码器的实现代码,可供借鉴。奇怪的是,虽然在MSDN中找不到,但是我在google上搜的时候,却发现有一个日本人在自己的个人网站上,早就给出了详细的图像解码器插件实现方法,一步一步说得很清楚,而且落款时间是2002年12月!看来这份源代码的泄漏时间可能比想象的要早。当然这个日本人也可能本来就在微软工作,或与微软有合作关系,可以光明正大地查看解码器源代码也说不定。

4、通用反编译器的实现

在解决了HTML、页面元素等的获取方法后,通用反编译器KillEBook的实现就很简单了,其算法可以描述如下:

  1. 打开电子书。
  2. 定位电子书的显示窗口。
  3. 获取当前显示页面的HTML代码。
  4. 解析页面HTML代码,得到其中的所有链接。
  5. 获取页面上的所有元素内容,包括图片等。
  6. 引导IE内核依次加载HTML链接页面。
  7. 重复步骤3~6,直到所有页面及其中的元素都已获取到。

5、进一步讨论

在完成KillEBook后,我发现其实对它扩展一下,就可以成为一种新的离线浏览器,解决传统离线浏览器(Offline Explorer Pro、Webzip等)面临的一个问题:传统离线浏览器多半与IE内核没什么瓜葛,因此在抓静态网页的时候都没有什么问题,但是在抓用session维持的动态网页时,都有点问题,更不用说抓需要PKI证书验证的HTTPS网站。

因此我考虑可以实现这样一个离线浏览器:

  1. 提供一个地址栏供用户输入起始URL。
  2. 内嵌一个微软web browser控件(IE内核),供用户交互,包括在网页上输入用户名/密码、从IE证书库中选择证书。
  3. 用户登录成功、进入需要开始抓取的网页后,设定递归深度、URL过滤条件,点“开始”按钮开始抓取。
  4. 离线浏览器自动引导web browser进入每个页面,每进入一个页面,都通过web browser控件获取客户端HTML源代码及页面元素,包括图片、css、js、flash等。

采用这种方法实现的离线浏览器,由于使用web browser控件,因此可以维持客户端session,抓取到动态网页。虽然网页抓取下来就成了静态的,但是对离线浏览来说应该不成问题,对付收费的网上教育等网站正好合适。

2.3 HLP格式

这种格式出现得比较早,在16位Windows(Windows 95以前的各Windows版本)下曾是标准的帮助文件格式,因此大概也算是Windows下出现得最早的电子书格式之一了。

由于这种格式比较流行,国外研究的也比较多,不过公开源代码的我似乎只见过一个HELPDECO v2.1。这个软件是一个控制台程序,因此有人做了一个GUI外壳DuffOS对它进行封装。国内有人对HELPDECO进行过汉化,到汉化新世纪搜索一下就可以找到,包括全部源代码。

在UnEBook中使用了HELPDECO的源代码,实现对HLP文件的批量反编译。不过从我使用的情况看,原版HELPDECO有一个小小的不足:反编译出来的RTF文件没有指定字符集。这对英文RTF来说没有任何影响,但是对中文RTF来说,其影响足够强到使您打开RTF后看到的是一堆乱码。它的修正方法有两个:

  1. 用文本编辑器打开反编译出来的RTF文件,手工指定中文字符集。这个是一种比较累的方法。
  2. 修改HELPDECO源代码,加上字符集修正,这个是一劳永逸的办法。但是不知道为什么,在汉化新世纪推出的汉化版上,我看到的还是原版的HELPDECO。看来汉化者只是用它反编译过英文HLP,没有反编译过中文HLP。

另外这份源代码还有一个不知道算不算是严重的问题:变量没有统一初始化、释放,因此不仅在程序退出的时候,VC++会报告有内存漏洞,而且就象当年的DOS内核一样,几乎没有可重入性。我曾经试图修复这个bug,但是在经过一个下午的奋斗后,有两处泄漏死活找不到。最后我还是决定向DuffOS学习:将HELPDECO代码封装成一个独立的DLL,每反编译一个HLP文件,都动态加载、释放一次DLL。这样一方面可以利用Windows本身的DLL管理机制,弥补HELPDECO产生的内存漏洞,一方面解决不可重入问题。收费的“耶书制造”软件提供的HLP反编译功能也是用DLL文件实现的,因此我严重怀疑它的作者可能也曾遇到过相同的烦恼,嘿嘿嘿……

从HLP文件反编译出来的RTF文件,一般包含大量书签、分页符等与实际文本内容无关的东西,有必要转换成纯文本格式。这个实现倒是比较简单:

  1. 创建一个Windows标准的RichEdit控件,当然没有必要在用户界面上显示出来。
  2. 按SF_RTF格式,StreamIn原RTF文件内容。
  3. 按SF_TEXT格式,StreamOut文本内容。

UnEBook提供的从RTF到TXT的批量转换功能,就是按照上面的方法实现的。

2.4 小说网/小说世界(ebx/XReader)

这两家网站提供的电子书使用的是同一个阅读器,只不过小说网出现得比较早,提供的电子书多半不需要验证码,而小说世界出现得比较晚,提供的电子书多半需要输入验证码。

这种电子书分两种:ebx和EXE格式。ebx格式的电子书需要用专用浏览器XReader才能浏览,EXE文件的内容其实就是XReader + ebx包构成。

国内Cyu曾经推出过反编译这种EXE格式的工具--xReader Unpacker。从我试用的情况来看,这个工具的实现应该是基于对EXE文件格式的辛苦分析,果然勤劳善良的中国人什么时候都有啊!不过从我试用的结果看,这个工具也存在下列问题:

  1. 一次只能反编译一个文件,不能批量反编译,使用起来略有不便。
  2. 反编译出来的文件用左侧目录树中对应的节点命名,完全失去了文件的先后顺序。
  3. 在反编译某些文件,如《血酬定律--中国历史中的生存游戏》的时候,会出错退出。我个人猜想可能是因为对书中多级目录处理不当。
  4. 奇怪得很,只能对EXE文件进行反编译,不能对ebx文件反编译,其实这两种文件本是两位一体的。

当然,我试用的只是最初版本的xReader Unpacker,后来听说作者又进行了更新,这些问题都解决了也说不定。

在考虑反编译这种格式的电子书的时候,因为我已经在思考针对IE内核的通用反编译方法,因此从一开始我就没打算对文件格式进行分析,而是打算从界面元素入手,看看有没有什么后面可走:

  1. 先用IECracker抓一下窗口,发现根本就不是基于IE内核的东西。这个时候首先想到的就是:软件作者会不会向起点中文网学习,将内容转换成图片,然后再显示?但是很快就否定了这个可能,一方面是因为XReader提供了文字放大、缩小功能,另一方面是因为启动金山词霸后,将光标往窗口上一放,词霸显示出了抓词内容。这个时候脑袋里一闪念间,也曾出现过一个反编译方案:干脆向金山词霸学习,做一个API hook,抓它的显示内容算了,哈哈……
  2. 在确定XReader显示的东西不是图片后,我就启动SPY++,打算看看XReader的显示窗口用的是什么东西。但是查看的结果令人惊奇:每启动一次XReader,显示窗口的class name就会变化一次,是一个完全随机的字符串,从上面根本看不出这个窗口使用了什么控件。
  3. 再多看几本电子书后,我发现所有电子书都有一个特点:完全没有图片,清一色都是纯文本,但是鼠标放到窗口上的时候,光标不会变成通常文本窗口的插入光标(一条竖线),还是箭头光标。到这个时候,我已经开始准备相信软件作者完全继承了国人勤劳善良的光荣传统,自己写了一个文本输出控件了。……且慢,为什么在打开这个大文件的时候光标会闪一下,从竖线变成箭头?再前后动动鼠标滚轮看看,每次不多不少,正好滚动3行,这个不是RichEdit控件的特性之一吗?!
  4. 立刻启动SPY++,这次不看class name了,改看消息流。果然每次点击左侧目录树,都会向右侧显示窗口发送一堆RichEdit控件的消息:EM_SETBKGNDCOLOR(设置窗口背景色)、EM_SETCHARFORMAT(设置光标形状)、EM_SETMARGINS(设置左右页边距)、EM_STREAMIN(导入显示内容)。
既然已经确定右侧显示区用的是一个标准的RichEdit控件,而左侧目录树是一个标准的TreeCtrl控件,那么反编译方案其实也就出来了:周游左侧目录树,依次选中每个节点,然后拦截右侧RichEdit控件的输出,写入文件即可。

不过在搞清楚XReader的原理后,我也产生了一个疑问:RichEdit控件本身是可以同时显示文本、图片的(RTF格式),但是为什么XReader只显示纯文本,不显示图片呢?要知道这样可是会使做出来的电子书增色不少。开始我以为是为了保密,象我自己一开始不也差点误入歧途?如果不是偶然看到光标闪烁,再动动鼠标滚轮,可能我一时也想不起来他用的是标准RichEdit控件。后来在看到早期版本的XReader后,我想更大的可能是为了兼容:早期版本用WM_SETTEXT传递显示信息,只能显示纯文本,后来才改用EM_STREAMIN的。

总结一下,XReader中采取了下列措施防拷贝、防反编译:

  1. 随机更改RichEdit控件的class name,防止被人识破。
  2. 对光标形状进行设置,一方面防止被人识破使用的是RichEdit,一方面避免用鼠标选择、复制内容。
  3. 对WM_COPY、WM_GETTEXT、EM_STREAMOUT等等消息进行了过滤,因此直接从窗口获得文本内容就不要想了。

可惜,微软提供的RichEdit控件是用于开放环境的,一旦被识破,用微软本身提供的接口就足以搞到所需的内容了。

后来看到小说网早期放出来的EXE格式电子书,才发现XReader这个软件也是不断发展的,而版本升级的目的主要就是为了加强安全性,ebx格式本身却没有什么变化,一直很稳定,新的ebx文件也可以用老的XReader打开:

  1. 早期版本的XReader支持用命令行参数的方式,传入需要打开的ebx文件路径,这样容易被人利用,实现文件自动打开。后来版本的XReader就只能通过菜单或工具条,点“打开电子书”才能打开文件。当然这个限制也不是不可以突破,不过毕竟没有用命令行参数传递这么方便。
  2. 早期版本的XReader其实就使用WM_SETTEXT消息显示文本。如果早点看到这个版本的电子书,说不定我还可以少费点周折。后来版本改用EM_STREAMIN,估计一方面是为了保密,另一方面是为了速度和性能:在显示大文件的时候,EM_STREAMIN比WM_SETTEXT快得多;EM_STREAMIN可以显示RTF文件,而WM_SETTEXT只能显示文本文件;EM_STREAMIN可以显示大型文件,而WM_SETTEXT支持的文件长度是有限的。

3. 结论

  1. 就像信息安全中的攻与防一样,电子书的编译与反编译之间的斗争也将是一个永无止境的死循环。我相信不论电子书反编译技术如何发展,都不会导致电子书的绝迹,毕竟有实际的需要。但是本文的发表,毫无疑问将会刺激电子书制作软件和制作技术的新一轮升级。那么我的文章和软件会不会随之升级呢?我自己是没什么自信啦,毕竟我的自由时间越来越少,而如果没有其他人愿意象我这样研究反编译技术和软件(收费的免谈),我想最终胜利的一定是有商业利益支撑的电子书制作软件。
  2. 先分析电子书的详细文件格式,再有针对性推出专用反编译器的方法,在初期确实是一个不错的方法,但是随着电子书格式的增多,如果每一种都要去分析一遍,早晚会累死。
  3. 电子书制作软件其实也是人开发的,开发者当然也会有人类的通病——懒!只要有现成的东西可用,很少有人会再花力气去修练自己的独门功夫。而目前Windows下的东西,开放性的考虑要比安全性的考虑更多一些,如果能够找到这些东西的突破口,即可突破同一类使用这些东西的电子书。
  4. 利用现成控件的接口或漏洞,实现通用电子书反编译,这其实也是程序员懒惰的一种体现。这种方法虽然比老老实实分析、跟踪电子书简单许多,但是也有其天然缺陷:只能反编译显示到控件中的内容。通俗一点说,如果电子书是加密码保护的,那么这种方法并不能在不知道密码的情况下,反编译出电子书的内容。

附录 基于IE内核电子书的实现方式探讨

电子书看多了,有时候我也会想,如果是我自己做一个电子书制作工具,我会采用什么样的技术加以实现?考虑到现在HTML格式文档的普遍性,在有人开放出新的HTML render之前,我的想法还是只能围绕IE内核打转。下面就是我想到的一些思路。

1、基于res协议

res协议是IE内核提供的一种非常简单的协议,允许将需要浏览的页面存放在EXE或DLL的资源(resource)中,IE根据URL定位EXE或DLL,装载其中的资源。下面这个URL就是这种协议的一个例子:

res://C:\WINNT\system32\shdoclc.dll/http_404.htm

如果您在IE中要浏览的页面不存在,IE就会通过这个URL,打开C:\WINNT\system32\shdoclc.dll,查找其中名为http_404.htm的资源,找到后提取、显示出来,您看到的就是一个提示页面不存在的网页。

从上面这个页面的源代码可以看到,除HTML代码外,res协议还允许在页面中包含图片等内容,如上面这个页面就显示了一个名为pagerror.gif的图片,其绝对URL为res://C:\WINNT\system32\shdoclc.dll/pagerror.gif。

虽然res协议非常简单,基本上不需要额外的编程,但是我目前还没有看到有人用它做电子书,最多只看到有人用它显示软件的About信息。仔细想想,可能是因为这种协议太不保密了:随便找一个资源编辑器,就可以直接获取、替换资源内容了。

2、基于文件方式

这种方式的思路其实非常简单:需要显示网页的时候,先将网页解压缩到临时目录,然后用IE控件显示,退出的时候删除临时文件。

这种方式我早就知道,但是因为它实在是太简单了,所以连我自己都不相信有人真的会用它做电子书,直到我见到雄风网的电子书:这个网站早期发行的电子书,虽然要求用户输入密码进行验证,但是在密码输对以后,就会把全部内容解压缩到temp目录下,然后用IE控件打开文件进行浏览。虽然temp目录下的文件属性被设置为隐藏,但是这点小伎俩实在不值一提,所以只要破解了认证密码,电子书本身就已经提供了完整的反编译功能了。

该网站后来发行的电子书虽然经过升级,但还是延续了这种模式,只不过在temp目录里存放的是加过密的HTML文件,但是图像文件却是不加密的,因此我猜测他们可能改用MIME Filter技术了。

3、基于流或document.write方法

用流往IE控件中写入内容的方法,在MSDN和CSDN中都有详细的讨论,连源代码都有。有需要的到MSDN搜索“Loading HTML content from a Stream”即可。

document.write在动态网页中比较常用,很多网页加密工具都是使用这招来实现网页源代码的隐藏。对于VC、Delphi等来说,这招不过是换成了IHTMLDocument2::write,效果是一样的。

使用这种方法做电子书的虽然不多,不过毕竟还是有的,我见过的就是读写网。由于打开这个网站的电子书后,IE主页就会自动设置为这个网站的URL,所以在这里就不给出这个网站的URL了,以免各位受到意外伤害。破解这种电子书的收费验证的方法,已经有人在紫宸殿网络论坛的技术区贴出来过,有兴趣的可以去看看。

在MSDN中对这种基于流的方法的局限性说得很清楚:

  1. 页面不能太复杂,如果页面包含的tag太多,显示出来的就不是解析后生成的页面,而是原始的HTML代码。大概就是因为这个原因,所以读写网放出来的电子书清一色都只有纯文本,加背景色。
  2. 当前页面的URL永远不变(读写网的永远都是about:blank),因此IE内核没有办法从相对URL自动构造出绝对URL。就是因为这个原因,读写网早期的电子书在页面中使用jpg文件作为背景,就只能将这个背景图片写到temp目录下,然后在网页中使用绝对URL引用这个图片。也正是因为这个原因,所以在页面中不能包含“上一页”、“下一页”、“回目录”等链接,只能自己在左侧放一棵目录树,让用户一页、一页去点。

由于这种电子书的页面没有自己的URL,因此不能用KillEBook进行反编译,只能用IECracker或CtrlN,一页、一页手工抓取。

4、采用MIME Filter

与基于流的方法相比,这种方法不仅支持包含众多tag的复杂HTML页面,而且可以从相对URL构造绝对URL,因此支持页面之间的链接,实现也不复杂,MSDN上就有现成的例子可供参考。

不过这种方法的缺点也很明显:不能对图像等内容进行加密处理。下面说的协议插件方法就比这种方法强些。

5、基于web服务器

对于不懂行的人来说,“web服务器”听起来可能是一个很了不起的东东,但是对于懂行的人来说,实现其实很简单:

  1. 起一个监听线程,对本地80或任何一个指定的端口进行监听。
  2. 每监听到一个连接请求,起一个服务线程,根据请求内容,按照HTTP协议,返回内容。

在codeguru和codeproject上,有很多现成的web server代码,直接拿来用就好,自己只要考虑怎么填写返回内容即可。VC 6自带的MSDN光盘上,也带了一个名为HTTPSVR的例子,说明如何用MFC和WinSock创建web server。

使用这种方法虽然简单、直截了当,而且只要愿意,差不多能够模拟一个真正web server的功能(就算想实现app server也并非不可能,不过要花点功夫),但是也有问题:

  1. 基本上没有什么保密性可言,服务器起来后,本机其它进程很轻松就能下载到需要的内容。
  2. 如果本机上其它进程也提供TCP/IP服务,可能会产生端口冲突。

6、协议插件(Asynchronous Pluggable Protocols)

这个是微软专门为IE扩展的东西。

在互联网上,常见的应用层协议包括http、FTP等。出于种种原因,微软允许用户在标准的应用层协议之外,扩展自己的协议,称为Asynchronous Pluggable Protocol。到MSDN、codeguru和codeproject上搜索这几个关键字,从理论到源代码都能找出一堆,在这里我就不罗嗦了。

Asynchronous Pluggable Protocol可以指定对所有进程有效,这个在注册表的HKEY_CLASSES_ROOT\PROTOCOLS\Handler下注册一下就好;也可以指定只在某个进程内有效,以增加保密性,不过这个时候微软就不叫它Asynchronous Pluggable Protocol了,而是Pluggable Namespace Handler。

由于Asynchronous Pluggable Protocol具有一定的保密性,实现起来又有例子可参考,而且差不多与架设web server一样,能够对网页显示提供全面的支持,因此在电子书中得到了广泛的应用,我见过的就有mk(chm)、ada99(eBook Workshop)、wc2p(Web Compiler 2000)、ic32pp(Web Compiler 2000—exe防反编译格式)、e-book(E-Book Creator)、mec(E-ditor eBook Compiler)等。不过这种技术如果使用不好,可能会在注册表中产生垃圾,或产生垃圾文件(插件本身是一个COM控件,一般用DLL实现,使用前必须在注册表中注册)。

7、最后一招

即使使用Asynchronous Pluggable Protocol,由于在IE内核中还存在可显示的HTML源代码,因此还是存在被导出的可能,这个就是上面正文里讨论了半天的东西。

我想到的最后一招制作防反编译的电子书的办法就是:在制作的时候,将所有页面内容全部转换成图片,然后再打包。将网页转换成图片的源代码参见这里:

http://www.codeproject.com/internet/htmlimagecapture.asp

使用这种方法,在拿到一本制作好的电子书后,想得到原始文本信息的方法大概只有两个:OCR和key in。这个也可以用起点中文网的方法来对付:使用手写体,加水印,故意增加错别字或替换标点符号等。据传说,起点就是根据用户ID,生成错别字和错误标点的,因此如果是原样key in或OCR,就可能被查出来。

但是回头一想,如果哪个电子书制作工具真的走到了这一步,大概也就离消亡不远了,用户还不如直接去做PDF:

  1. 所有动态效果全部没有,页面上的链接也全部失效,大概又只能靠在左侧放一棵目录树才能导航了。
  2. 页面大小、字符大小基本固定,显示的时候很难放大、缩小,尤其是放大的时候,要么速度比较慢,要么必须忍受难看的锯齿。
  3. 文件尺寸大增。对于以收藏为目的的电子书来说,这是一个必须以严肃的态度,认真地加以考虑的问题。