破解百词斩单词数据之旅

作为一位英语爱好者,百词斩是我每天都会用的一款APP,这款应用可以自测词汇量,并巩固你的单词量,确实是一款用心的产品。作为一名雅思7分选手,个人觉得里面的发音和例句,对于口语还是有很大的帮助,可以边听边读,做到碎片化的学习。总言而之,推荐大家都体验一下。

再完美的产品也会有瑕疵,我的词汇量在1.3万左右,大多数单词都能比较熟悉,直接斩掉,但也会有零星的生僻词,我会收藏起来,每天专门巩固这部分词汇。用了两年之久,收藏了八九百个单词了,对于我而言,这些单词属于Panic Zone,需要重点强化的。所以希望针对收藏单词列表提供针对性的练习模式,具体是什么模式我就不多想了,专业的人肯定比我有经验。之前跟客服也反馈过一些小问题和建议,而且百词斩和我的办公地点都在同一个办公区,不管有没有走心,起码反馈还比较及时,所以我也跟客服提了这样一个优化收藏列表的需求,而且随着收藏单词的增多,严重怀疑收藏单词的查询性能是有隐患的。

下面画风要转变了,一年多过去了,我提的需求还是没着落,斩家千万不要轻视客户的需求啊,特别是一个程序员的需求,索性自己动手,丰衣足食。

本身百词斩提供离线数据包,而且是Android的应用,假如我能够获取单词的请求格式,同时能够解析每一个单词的音频,图片,例句,再能够解析各个数据库之间的结构关系。理论上讲,这样就能做出来一个PC版的百词斩,也就能满足我的个性化需求,而用户关系这些不太可能是二进制文件的形式,而Android上也就只有sqlite数据库了,这意味着这些数据应该不难解析。初步分析,可行。

首先找到这些数据都存在什么位置了,我对Android系统不熟悉,或许是我眼拙,找了很久都没找到存放路径,就这几个文件夹,怎么就没有baicizhan这样让人眼前一亮的文件夹呢。仿佛当头一棒,看来敌人没这么蠢,靠人肉技术是不行滴。出师不利,只好另辟蹊径。百词斩提供离线数据包,如果可以监控手机的网络请求,那就能知道他下载的是什么内容了。查了一下,在Fiddler->Options中开启代理,如下图。然后重启Fiddler。

在手机端(保证是同一个网段),长摁WiFi信号源,修改网络,显示高级,代理设置为手动选择Fiddler所在电脑的无线IP,端口为8888,和Fiddler里面的端口号一致。

一切就绪,点击下载单词包,yes,we get it!一条条的请求都在Fiddler中获取。请求消息如下图:

如上图,不难猜测,zpk应该就是每一个单词的数据内容,原来单词是保存在文件里面而非数据库中,文件则按照一定的规则来命名。好吧,顺藤摸瓜,看看zpk里面到底是什么玩意。下载下来一个zpk,然后在beyond compare下面以16进制方式打开,上阕如下:

好吧,你应该和我一样不想看下去了,唯一不同的是我能继续忍,继续看到中间,发现看到了里面的单词,音标,例句等ASCII码的内容,终于有点头绪了。再继续向下看,右下角思路是我们人类的语言啊,没错,ASCII码如下:

可以看到,这算是里面的数据清单,包括zpk文件对应的数据和顺序,说明这个数据包括一个jpg,一个aac或者mp3的音频,其中的.是他们的分隔符,对应的是ASCII码的0X00。也就是说,右下角的这一段相当于整个二进制的一个清单,而且也是按照清单中的顺序有前到后排列的。我们先解析这部分,就可以知道该zpk文件中有哪些部分,比如png,jpg,mp3或aac等;每一个文件都有自己的标示头和尾,这样就可以把该二进制文件分解成对应格式的内容,一个zpk就这样迎刃而解。

当然如上都只是猜测而已,还是需要验证,另外对比来看看是否有没有遗漏的字段。比如jpg文件的开头是FF D8的标识,结尾则是FF D9,我们手动把这部分的二进制字段截取出来,保存为jpg格式,果然不出所料。同样,里面还有png图片和aac的音频。都可以如此获取,最麻烦的是mp3,我对这个格式不熟悉,发现它没有固定的头尾标识,也算一个美中不足吧,导致我写的zpkParser解析代码有特殊处理的地方,而且结果还是有瑕疵(主要是mp3文件在第一或最后位置)。

至此,总体而言zpk对我就是明码了,赤裸裸的呈现在我的眼前肆意的享用了。看了一下百词斩的数据库,百词斩总计有六万多个单词(这是后话),找了其中几个zpk运行一下,效果都还可以,因为我并不是真的想要解析出来,点到为止,就没有进一步的优化代码。如下是代码片段,根据当前的arrType类型来获取对应的文件开头和结尾标识符,进而截取对应的二进制流并保存。毕竟这种事情不太厚道,所以刻意截取了一些不痛不痒的代码片段:

switch (arrType[i])
{case zpk_mp3:
    pHeader = mp3Header;
    pEnd = mp3Header2;    break;case  zpk_png:
    pHeader = pngHeader;
    pEnd = pngEnd;    break;case zpk_jpg:
    pHeader = jpgHeader;
    pEnd = jpgEnd;    break;case zpk_aac:
    pHeader = aacHeader;
    pEnd = NULL;    break;default:    break;
}

当然,我之前破解过Google Earth的数据。相比而言,zpk文件并没有加密,也没有压缩,而且还是ASCII码,所以破解这种程度的数据其实并不复杂。而且我只是做到打哪指哪,而想要做到的是指哪打哪。所以,尽管我知道了单词文件的格式,但还是没有找到用户和单词数据之间的映射关系。

继续努力,全局搜索下终于找到了百词斩在手机里面的存放路径。我认为在Android手机上,百词斩也对自己的数据存放位置做了一些隐藏,因为我用百词斩比较久了,早起的版本貌似就在存储卡下baicizhan文件夹,但发现后来他放到了很难找到的位置,在我的华为手机上对应在Android/data/com.jiongci.com这样一个目录下,也是比较隐藏的,里面的zpk文件夹肯定是所有单词的汇总了,把其他数据拷到电脑上,看看里面的逻辑关系。

我的习惯,先按照大小排序,然后找到我想要分析的文件后,然后在按照格式排序,最后在看看里面是什么内容。一些logo,广告图片就掠过,首先最大的文件是baicizhantotal.db,这也太明显了。手机上只能是sqlite嘛。我们在sqliteman软件下打开这个数据库,果然不出所料,在tb_total_topic_resources表中,保存了所有单词的属性信息。音标,中英文示意,例句等,前面还有book_id,topic_id等索引信息。这和zpk里面对应的内容完全一样。我们在建立一个key value的映射上迈出了一小步。

然后对所有db解析,建立各自关联,仿佛你在和百词斩的程序员在进行一次无声的交流,为什么要这样设计,多不方便,哦,为了避免这种情况,为什么会有这么多重复,容易的单词。这是一个漫长,晦涩的过程,也是一个绞尽脑汁的过程。

bookID表,里面统计了不同科目下的id和单词数目等。可见,考研词汇有6k多个,其中bookid就是每一类的一个索引值。

想看看雅思核心中的具体的单词统计,则打开对应的表,如下,topic就是该单词的唯一id,下面则是zpk的路径。

我想要的只是收藏单词的导出功能,所以继续找,你会找到出错单词的统计,当然,还有收藏单词的数据表。如下,这下大家满意了吧。

我也不清楚为什么这里的id怎么就不一样了呢?于是在Fiddler里面反复收藏单词,查看请求,该功能必须要求在联网环境下,估计是避免版本管理问题。然后找到两个已知id的单词,收藏后对比,这里面是是id+N这样一个固定格式,具体N是多少就不说了,于是乎,把id取出来,减掉N,你就可以获取到收藏单词的id,根据id就可以获取该单词的存放路径,在通过zpkparser就可以解析。这样就能满足我的要求了。试了几个单词,基本验证了我的想法。可是还是不能解释为何这里要用这样一个id的行为,或许也是因为这种“多此一举”的行为,导致收藏单词的不变,进而影响了查找性能。

当然,人的欲望是无止境的,现在,我已经不满足于收藏的单词了,何不把所有的单词都搞下来,这也算一份不错的英文单词数据了,毕竟数据到哪里都是最核心的。好吧,于是就有了下面的这个截图,百词斩大部分的单词都在其中,每个数字对应一个bookid,包括新概念,囧记,以及雅思托福考研等主要内容。

前前后后用了两天,也算基本搞清楚了百词斩单词数据部分的相关细节。个人有两个感想,第一,看上去数据解析很简单明了,这是在知道的情况下,其实在破解过程中并不如是,就好比让你蒙眼走路,即使再熟悉的一条路也是有一定的挑战,你不得不凭借你的其他感官,综合判断方向,还要不断的尝试并忍受无劳的失败;另外一个,就是大多数公司对数据安全的重视程度不够,无论如何,数据都是应用的一个基石,没有任何的防范还是略显不足,尽管你以为二进制对人类而言是不可解析的。

时间: 2024-10-15 04:41:23

破解百词斩单词数据之旅的相关文章

艰苦的RAW格式数据恢复之旅

艰苦的RAW格式数据恢复之旅 1.RAW 格式形成原因 2.RAW 格式的解决的方法 经验之谈: 1.RAW 格式形成原因 关于形成的原因,在网上搜索了下,千奇百怪的都有,就不一一诉说了,可是有果必有因. 在网上搜索到正确的说法是硬盘的DBR损坏导致的,个人不懂硬件,就引用前辈的解释了. 2.RAW 格式的解决的方法 写在前面的话,我差点儿能够是说没有一丁点关于硬盘数据恢复方面的经历,所以这次纯粹是摸着石头过河,摸出来的经验,终于给自己摸着攻克了,算是牛年中不幸中的大幸.因为变成RAW格式的硬盘

大数据是什么?华为云学院带你探索大数据之旅

大数据是什么?华为云学院带你探索大数据之旅我们首先从大数据是什么开始讲起,!下面由我来带领大家!展开我们本次的大数据学习之旅!大数据是什么,内容将包括大数据的产生,发展大数据的基本概念.首先我们来追溯一下大数据的产生与发展,大数据的产生和发展主要经历了三个阶段. 第一个阶段,我们称为是萌芽期!自上世纪九十年代至本世纪初,随着数据挖掘理论和数据库技术的逐步成熟,一批商业智能工具和知识的管理技术也开始得到应用,比如数据仓库,专家系统知识管理系统等等.第二阶段我们称为是成熟期.本世纪的前十年Web2.

惊心动魄的数据恢复之旅

站点被攻击了,数据表丢了两张,而且是很重要的明细表,真是魂淡啊!没办法了,必须先恢复数据,平息老板+客户的怒气. 万幸的是,数据库有做差异备份,备份了一天之前的数据.先把这部分数据提取,恢复了,心率和血压顿时下降了一半.最多最多就丢了半天的数据,应该不会卖身也赔不起了. 接下来,才是重头戏,恢复这半天的数据,从早上4点到下午16点,这12个小时的数只能从bin-log上想办法.首先,找到这一天的日志文件(因为这一天日志文件涉及好几个,这边简化过程,只以一个文件来说,暂且命名为bin-log-08

行业软件 加密狗复制 加密狗数据 加密狗备份 加密狗破解 写狗数据 OEM信息

加密狗复制备份.定制写狗程序.算法注册机.OEM信息.二次封装.行业软件破解.酒店客房管理.餐饮娱乐管理.美容美发管理.会员管理.口腔管理.商超POS收银.服装鞋帽.家具生产设计.家具数控.家居设计及销售设计.药店管理.汽车行业管理.财务进销存系统 .OA办公 .服装设计.积分管理.电脑行业管理系统.客户管理.洗浴足浴.等各行业管理系统.批发零售.量大价优.欢迎选购.合作联系QQ:309889372 部分产品分类展示: 家具厂:海迅家具设计拆单管理系统 海迅家具数控生产管理系统 凯恩家美橱衣柜销

[LeetCode] 211. Add and Search Word - Data structure design 添加和查找单词-数据结构设计

Design a data structure that supports the following two operations: void addWord(word) bool search(word) search(word) can search a literal word or a regular expression string containing only letters a-z or .. A . means it can represent any one letter

leetcode 211. 添加与搜索单词 - 数据结构设计 解题报告

设计一个支持以下两种操作的数据结构: void addWord(word) bool search(word) search(word) 可以搜索文字或正则表达式字符串,字符串只包含字母 . 或 a-z . . 可以表示任何一个字母. 示例: addWord("bad") addWord("dad") addWord("mad") search("pad") -> false search("bad"

[LeetCode] Add and Search Word - Data structure design 添加和查找单词-数据结构设计

Design a data structure that supports the following two operations: void addWord(word) bool search(word) search(word) can search a literal word or a regular expression string containing only letters a-z or .. A . means it can represent any one letter

旅行青蛙逆向破解的自我实践之旅

本文作者:aaaasss 来自纯新手的手把手实践经验分享,初次接触逆向,简单记录一下心路历程及一些自认为有趣的东西,大佬们可绕过或者多多指教.== 这里用的是1.04版本,1.01版本基本雷同 == 下面就开始了此次的心路历程: 首先是看到了 https://paper.seebug.org/519/,参照其中 1.下载游戏安装包,修改后缀为zip可用解压工具打开,发现存在 assets\bin\Data\Managed 目录(判断游戏应该为Unity游戏的一个特征).据说此类游戏主要逻辑代码在

leetcode-211-添加与搜索单词-数据结构设计

题目描述: 方法一: class WordDictionary: def __init__(self): """ Initialize your data structure here. """ #from collections import defaultdict self.lookup = {} def addWord(self, word: str) -> None: """ Adds a word i