逆向WeChat(七)
上篇介绍了如何通过嗅探MojoIPC抓包小程序的HTTPS数据。
本篇在博客园地址https://www.cnblogs.com/bbqzsl/p/18423518
微信PC客户端有两种主要的数据存储类型,一种是基于sqlcipher,另一种是基于protobuf。除了这两种外还有别的,不在本篇内容。
它们是对应两个主要的类,StorageBase跟ConfigInfoStorage。StorageBase使用sqlcipher作为存储手段,ConfigInfoStorage则使用protobuf存储KeyValue。
StorageBase是一个单表操作的封装类,它包含了数据库名跟表名等信息。它封装了打开,查询等底层操作。它的StorageBase::init方法完成了打开数据库以及必要的设置,包括CipherAndKey的设置。逆向分析到现在,我才发现微信是有用混肴的代码段来做代码保护,这个段位于WeChatWin.dll的末尾。StorageBase::init调用DBFactory::openDBbyName方法完成全部打开工作。这个DBFactory::openDBbyName方法,它的日志信息全部字符串都经过混肴处理,显然是不想让人知道。并且DBFactory::openDBbyName的主执行体逻辑被编辑在混肴代码段。混肴代码的目的,不单是让人变白痴,更加重要是让逆向的工具变白痴,包括调用器。微信使用的混肴,有个特点,就是满天的call指令。只要你清楚call指令只是jmp&push的等价物,就明白它的恶心了。调用器的调用栈帧功能,只能用于分析ebp, eip这种中规中举的调用,混肴的call根本不在于call&ret,如果你认为它会在call的下一指令,ret回来执行,你可能等不到它执行。包括逆向工具的代码分析功能也同样被打成白痴。call等同jmp并push eip。这时的eip不是为了ret,而为了开辟esp,并将eip作为后面这个栈位置的内容的解码因子。或者这个栈位置的内容后面是被直接替换而丢弃的。不单代码混肴,连栈结构都被混肴。
所以碰到混肴保护的代码,Mother Mary comes to me Speaking words of wisdom let it be。虽然我假设,微信可能在这里将数据库的KEY,还在KEY的计算方法保护了起来。我承认我还停留在8年那个安卓版的魔法MD5(imei+uin)。想必PC的计算方法是Foo(myPCInfo, uin),存在于客户端代码某处。既然它们都用混肴代码保护起来了,就 so i listened to Mother Mary,let it be。正当我是这么在想的时候,一处AccountService::setDBKey却赫然在目。唉?不对吧。于是我赶紧windbg跟踪。咦?这参数里面不就有我的数据库的KEY吗?前面我还在说,微信煞有介事地用混肴代码将sqlcipher打开数据库的一连串操作,包括KEY设定,通通都保护起来。现在却明目张胆地露出来。这是在打我脸,还是在打它自个的脸。这戏到底在唱的是哪一出,我完全看不懂。它拼命地将那里遮起来不让别人知道,但用来遮住那里的东西却有一张照片是裸露那里的。这是在玩彩蛋。直到看了它们的技术post才明白,”安全性。基于不怕被破解,但也不能任何人都能破解的原则“,https://cloud.tencent.com/developer/article/1005575。这词令有点耳熟,好像在哪里听过类似的格式的短句。微信开发团队为大家指明了方向,欢迎来破解,就怕你不会。(原来它们的数据库还有一个正名WCDB, https://cloud.tencent.com/developer/article/2406614。我却一直将它当sqlcipher在处理。WCDB是用在移动终端的,PC端的应该不是。)虽然市面上关于这个的破解已经写到烂大街,只要在github上搜wecaht Db crack关键词,就有一大堆的repos。我就简单介绍我的分析,还有方法,为观众多一种角度。AccountService有三个成员变量,DBKey,RSA公钥,RSA私钥。它们都是std::string类型。并且有已知固定的长度。最lucky的是,它们之间的相对位置经历了这么多年还有版本都没有变化过。搜查的步骤,先通过RSA私钥字符串的地址,再将地址值结合字符串的长度值找到RSA私钥成员变量的位置,然后相对偏移后得到DBKey成员变量的位置。就如我们熟悉的几何定理三点定面一样,锁定位置。只要三条指令,就可以用windbg实现目标。
x86
s-a 0 L10000000 "-----BEGIN RSA PRIVATE KEY-----"
* theRes
s-d wechatwin L2000000 theRes 0 0 0 377 37f
* theRes2
da poi(theRes2 - 18)
* check if "-----BEGIN PUBLIC KEY-----"
db poi(theRes2 - a8) Lpoi(theRes2 - a4)
x64
s-a 0 L10000000 "-----BEGIN RSA PRIVATE KEY-----"
* theRes
s-q wechatwin L2000000 theRes 0 377 37f
* theRes2
da poi(theRes2 - 20)
* check if "-----BEGIN PUBLIC KEY-----"
db poi(theRes2 - f8) Lpoi(theRes2 - f0)
补充一下,windbg没有像gdb那样,可以赋值变量,但可以用alias代替。将theRes跟theRes2别名成结果地址, 或者手动替换指令中的theRes跟theRes2。另外一次最多只能搜索地址空间0x10000000,因些在第一个地址段找不到,请用下面命令扫描地址空间的全部用户空间。
s-a 0 L10000000 "-----BEGIN RSA PRIVATE KEY-----"
s-a 10000000 L10000000 "-----BEGIN RSA PRIVATE KEY-----"
s-a 20000000 L10000000 "-----BEGIN RSA PRIVATE KEY-----"
s-a 30000000 L10000000 "-----BEGIN RSA PRIVATE KEY-----"
s-a 40000000 L10000000 "-----BEGIN RSA PRIVATE KEY-----"
s-a 50000000 L10000000 "-----BEGIN RSA PRIVATE KEY-----"
s-a 60000000 L10000000 "-----BEGIN RSA PRIVATE KEY-----"
s-a 70000000 L10000000 "-----BEGIN RSA PRIVATE KEY-----"
我也跟跟风,写一个https://github.com/bbqz007/CrackMicroMsgDBKey关于如何用windbg看密钥。
找到DBKey后,当然就想知道计算Key的代码。开始我以为会在混肴代码段,但在发现AccountService::setDBKey后,再去跟踪才真相大白。DBKey由服务器返回,计算不在客户端,而在服务器。
有需要的请自行探究,与Auth相关的类。
加密解密两因素,一是Key,二是Cipher。多年没碰微信数据库,我也忘了cipher的设定。随便找个最新的sqlcipher来用,key对了还是死活打不开数据库。因为每个sqlcipher版本默认的cipher不同,或者分发软件的发行者编译的选项不同,默认的cipher不同。cipher的设置必须也要匹配。我们可以直接通过微信直接查询它打开的数据库。一共有kdf_iter,cipher_page_size, cipher_use_hmac, cipher_plaintext_header_size, cipher_hmac_algorithm,cipher_kdf_algorithm六项。
微信将sqlcipher的访问封装成一个单表操作的类。StorageBase。它包含了数据库名,表名,还有最重要的数据库句柄。毕竟还是sqlite3。我们只要找到api表就可以使用所有c接口了。上图的示例就是通过api表的c接口访问StorageBase打开的数据库句柄。即使StorageBase虽然将打开数据库的流程通通用混肴代码保护起来。
得到capi表后,sqlite3_exec可以操作数据库,如上图演示,sqlite3_prapare可以跟踪sql,如下图演示。
我已经在我的KTL工具ver0.9.1(gitee仓库)添加了patch,方便有需要的人,只要在AlgoDataTool.cpp设置你的破解出来的DBKey,就可以浏览你的微信数据库。
数据库文件目录在”我的文档“下,”WeChat Files\wxid_????????\Msg"。如果想知道当前登陆的wxid,可以查看”WeChat Files\config\AccInfo.dat",字符串类型数组中编号4的数值,而编号10则是昵称。如何查看,就是接下来要介绍的另一种数据库类型,我同样也在KTL添加了patch,提供对应查看工具。在本篇,数据库是一个广泛的,包括各种格式的数据文件。比如,类mongodb同样可以用plaintext的方式作为底层存储。一种格式的文件也可以通过mysql存储引擎接口做成自定义存储引擎被mysql使用。
另一种数据库基于protobuf,类型名称为ConfigInfoStorage。它的主要数据成员是micromsg::KeyValueSetting,这是由proto文件生成的类。虽然没有KeyValueSetting的proto文件,但是由逆向内容可以分析根结构。
根级结构主要有5个成员,它们都是数组,每个数组元素皆为一个key-value对。key是整形。知道总的框架结构,在没有proto文件情况下,也可以对protobuf进行大概的分析。虽然没有proto定义文件,但仍然可以用protoc工具解码,只是不知道成员的名称,所有名称都是整形的位置号。但是有了上面的根级结构就可以开展分析。根1号位是32位整形的value数组,每个value都有一个名称对应的编号。数组每个元素是一个intkey-value的proto,所以元素的1号位是intkey,2号位是value。后面的数组以同样的方式。根2号位是buffer类型的数组,这里的buffer有些是经序列化的protobuf。可以认为是对象类型的数组。根3号位是字符串数组,所有字符串的value都在这个数组。根4号位是64位的整形数组。根5号位是浮点形数组。
protoc工具不能在没有proto文件的情况下,解码成JSON格式,只能用TextFormat。但是这个TextFormat是真的独特,似DICT或JSON,但又不同。不能方便使用python或json浏览器进行分析。所以我用我的KTL添加了一个功能,可以将protoc工具解码出来的TextFormat转换成JSON,并提供可视视图。有了转换后的JSON,可以粘贴到任意你喜欢的可视工具进行分析,最简单的就是使用chrome浏览器的devtools,粘贴到console执行。
数据库文件目录在”我的文档“下,”WeChat Files\config\“。其中AccInfo.dat对应AccoutService类,aconfig.dat对应AccoutStorageMgr类。
本篇相继介绍了微信如何使用sqlcipher,AccoutService类,基于protobuf的key-value数据库,ConfigInfoStorage类。数据库存储在我们的电脑磁盘,而类运行在我们的内存。通过扫描这些东西,就可以搜集个人信息。AccoutService类还包含了绑定的手机号。又例如,单纯扫描磁盘,不扫描微信进程,不扫描sqlcipher数据库。也可以得到当前登陆的微信号,这个微信号使用了哪些小程序,这些小程序是哪些知名的小程序应用,是哪种类型的小程序应用,这个微信号有着一些使用习惯喜好以及需求,等等。(每个小程序有一个唯一的AppID,还有绑定的公众号)。配合扫描微信进程的DBKey,就可以直接窥探更加多内容。
在我的KTL工具ver0.9.1(gitee仓库)添加了两个patch,patch.sqlcipher查看sqlcipher数据库,patch.protobuf查看protobuf数据(文件)。
另外直得一提。wechatwin.dll就像一个Library,静态编译进了不计其数的开源库。sqlcipher也是其中之一。我们也可以直接wechatwin.dll。
本篇到这里,下一篇再见。
逆向WeChat(七,查找sqlcipher的DBKey,查看protobuf文件)
逆向WeChat(六,通过嗅探mojo抓包小程序https,打开小程序devtool)
逆向通达信 x 逆向微信 x 逆向Qt (趣味逆向,你未曾见过的signal-slot用法)
逆向WeChat(三, EventCenter, 所有功能模块的事件中心)
逆向WeChat (二, WeUIEngine, UI引擎)
我还有逆向通达信系列。
我还有一个K线技术工具项目KTL,可以用C++14进行公式,QT,数据分析等开发。你的代码JustInTime运行。
逆向WeChat(七)的更多相关文章
- XCTF IgniteMe
一.查壳 结论: 1.用vc++编译的. 2.无壳,毕竟是一分的题 二.点击运行,发现不是爆破,而是找出注册机,汇编功力还在提升中,只能拖入ida来静态调试了 具体的见注释: 二.1点击进入关键函数 ...
- 【腾讯Bugly干货分享】移动App入侵与逆向破解技术-iOS篇
本文来自于腾讯bugly开发者社区,非经作者同意,请勿转载,原文地址:http://dev.qq.com/topic/577e0acc896e9ebb6865f321 如果您有耐心看完这篇文章,您将懂 ...
- Reverse Core 第一部分 代码逆向技术基础
@date: 2016/10/14 <逆向工程核心原理>笔记 记录书中较重要的知识,方便回顾 ps. 因有一些逆向基础,所以我本来就比较熟悉的知识并未详细记录 第一章 关于逆向工程 目标, ...
- 10天学会phpWeChat——第七天:创建一个自适应PC网站+H5移动端的模块
本教程基于phpWeChat核心框架1.1.0+版本.下载地址:http://s.phpwechat.com/app_38026ed22fc1a91d92b5d2ef93540f20 通过前面六讲的系 ...
- QiniuUpload- 一个方便用七牛做图床然后插入markdown的小工具
最近一段时间有用markdown做笔记,其他都好,但是markdown插入图片挺麻烦的,特别是想截图之后直接插入的时候.需要首先把图片保存了,然后还要上传到一个地方生成链接才能插入.如果有个工具可以直 ...
- Mybatis(七) mybatis的逆向工程的配置详解
还是觉得看书学习有意思~嘿嘿.今天把mybatis给结束掉. --WH 一.什么是逆向工程? 简单点说,就是通过数据库中的单表,自动生成java代码. Mybatis官方提供了逆向工程,可以针对单表自 ...
- Android逆向分析(2) APK的打包与安装背后的故事
前言 上一次我们反编译了手Q,并遇到了Apktool反编译直接crash的问题,虽然笔者很想在这次解决这个问题,但在解决途中,发现该保护依赖于很多知识,所以本次先插入一下,正所谓知其然知其所以然,授之 ...
- 【从零开始搭建自己的.NET Core Api框架】(七)授权认证进阶篇
系列目录 一. 创建项目并集成swagger 1.1 创建 1.2 完善 二. 搭建项目整体架构 三. 集成轻量级ORM框架——SqlSugar 3.1 搭建环境 3.2 实战篇:利用SqlSuga ...
- Mybatis学习(七)————— mybatis的逆向工程的配置详解
一.什么是逆向工程? 简单点说,就是通过数据库中的单表,自动生成java代码. Mybatis官方提供了逆向工程,可以针对单表自动生成mybatis代码(mapper.java\mapper.xml\ ...
- [工控安全]西门子S7-400 PLC固件逆向分析(一)
不算前言的前言:拖了这么久,才发现这个专题没有想象中的简单,学习的路径大致是Step7->S7comm->MC7 code->firmware,我会用尽量简短的语言把前两部分讲清楚, ...
随机推荐
- c语言模拟Python的命名参数
最近在书里看到的,让c语言去模拟其他语言里有的命名函数参数.觉得比较有意思所以记录一下. 目标 众所周知c语言里是没有命名函数参数这种东西的,形式参数虽然有自己的名字,但传递的时候并不能通过这个名字来 ...
- Gradle的安装和创建java项目(idea)
安装 Gradle下载地址:http://services.gradle.org/distributions/ 下载后解压. 解压后的目录结结构如下: 新增环境变量 在path环境变量中添加以下内容: ...
- tcp粘包与udp丢包的原因
tcp粘包与udp丢包的原因 一,什么是tcp粘包与udp丢包 TCP是面向流的, 流要说明就像河水一样, 只要有水, 就会一直流向低处, 不会间断. TCP为了提高传输效率, 发送数据的时候, 并不 ...
- 【DataBase】MySQL 28 流程控制
一.分支结构 1.IF函数 语法: IF(表达式1, 表达式2, 表达式3) 类似三元运算符,表达式1返回True Or False True执行表达式2,False执行表达式3 IF实现多分枝结构 ...
- 【Zookeeper】02 文件系统 & 监听机制
官方文档上这么解释zookeeper,它是一个分布式服务框架,是Apache Hadoop 的一个子项目, 它主要是用来解决分布式应用中经常遇到的一些数据管理问题, 如:统一命名服务.状态同步服务.集 ...
- 【IDEA】使用Maven骨架创建JavaWeb项目
IDEA版本:2020.1 骨架选项名称: org.apache.maven.archetypes:maven-archetype-webapp 本项目的Maven坐标设置: 设置优先从本地获取骨架: ...
- FFmpeg开发笔记(四十三)使用SRS开启SRT协议的视频直播服务
<FFmpeg开发实战:从零基础到短视频上线>一书在第10章介绍了轻量级流媒体服务器MediaMTX,通过该工具可以测试RTSP/RTMP等流媒体协议的推拉流.不过MediaMTX的功能 ...
- Google主打的机器学习计算框架——jax的升级包
相关: 机器学习洞察 | 一文带你"讲透" JAX Jax的主要应用场景: 深度学习 (Deep Learning):JAX 在深度学习场景下应用很广泛,很多团队基于 JAX 开发 ...
- python多进程multiprocessing模块的变量传递问题:父进程中的numpy.array对象隐式序列化到子进程后的inplace操作的问题
参考: https://docs.python.org/zh-cn/3/library/multiprocessing.html cloudpickle -- Python分布式序列化的专用模块 ...
- ApplicationRunner的讲解
在开发中可能会有这样的情景.需要在容器启动的时候执行一些内容.比如读取配置文件,数据库连接之类的.SpringBoot给我们提供了两个接口来帮助我们实现这种需求.这两个接口分别为CommandLine ...