专栏首页慎独LLDB实战之导出Mac微信备份聊天记录的SQLite密码(SQLCipher加密)

LLDB实战之导出Mac微信备份聊天记录的SQLite密码(SQLCipher加密)

涉及到的LLDB命令

  • br: 设置断点
  • memory read: 读取内存原始值
  • po: 打印变量,也可以执行函数并且获得返回值
  • bt: 打印当前调用栈
  • thread step over/in/out: 单步跳过/进入/跳出
  • register: 寄存器操作
  • next/ni/n/step/si: 同上

参考链接

0x00 准备工作

查看WCDB所用的SQLite的加密方式,直接在WCDB的README里写了:

Encryption Support: WCDB supports database encryption via SQLCipher.

于是查看SQLCipher的API,看到用的是sqlite3_key()sqlite3_key_v2()这2个函数,在源码里搜索,找到调用,一共有两处,在WCTDatabase+Database.mm文件里

- (void)setCipherKey:(NSData *)cipherKey
{
    _database->setCipher(cipherKey.bytes, (int) cipherKey.length);
}

- (void)setCipherKey:(NSData *)cipherKey andCipherPageSize:(int)cipherPageSize
{
    _database->setCipher(cipherKey.bytes, (int) cipherKey.length, cipherPageSize);
}

然后是下面的C寄存器的含义对照表。其中函数参数也可以不用寄存器表示,可以直接$arg1/$arg2/$arg3来表示 。

0x01获取保存目录

开始用LLDB动态调试,首先Hook微信进程

$ ps aux | grep WeChat

// 25132   6.7  1.0  7341704 170876   ??  S    Fri04PM   8:36.19 /Applications/WeChat.app/Contents/MacOS/WeChat  进程id是25132

$ lldb -p 25132 //开始hook进程

(lldb) process attach --pid 25132
Process 25132 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = signal SIGSTOP
    frame #0: 0x00007fff6162722a libsystem_kernel.dylib`mach_msg_trap + 10
libsystem_kernel.dylib`mach_msg_trap:
->  0x7fff6162722a <+10>: retq
    0x7fff6162722b <+11>: nop

libsystem_kernel.dylib`mach_msg_overwrite_trap:
    0x7fff6162722c <+0>:  movq   %rcx, %r10
    0x7fff6162722f <+3>:  movl   $0x1000020, %eax          ; imm = 0x1000020
Target 0: (WeChat) stopped.

Executable module set to "/Applications/WeChat.app/Contents/MacOS/WeChat".
Architecture set to: x86_64h-apple-macosx.
(lldb)

进入LLDB命令行模式 打断点获取微信的数据库目录,看WCDB的初始化接口,[WCTDatabase [alloc] initWithPath:path];我们要获取path

(lldb) br set -n '[WCTDatabase initWithPath:]'
//输出
Breakpoint 1: where = WCDB`-[WCTDatabase(Database) initWithPath:], address = 0x0000000110b38676
(lldb) br list //查看断点
Current breakpoints:
1: names = {'[WCTDatabase initWithPath:]', '[WCTDatabase initWithPath:]'}, locations = 1, resolved = 1, hit count = 0
  1.1: where = WCDB`-[WCTDatabase(Database) initWithPath:], address = 0x0000000110b38676, resolved, hit count = 0

点击微信的备份文件,恢复聊天记录至手机或者管理备份文件来触发断点。

Process 25132 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
    frame #0: 0x0000000110b38676 WCDB`-[WCTDatabase(Database) initWithPath:]
WCDB`-[WCTDatabase(Database) initWithPath:]:
->  0x110b38676 <+0>: pushq  %rbp
    0x110b38677 <+1>: movq   %rsp, %rbp
    0x110b3867a <+4>: pushq  %r15
    0x110b3867c <+6>: pushq  %r14
Target 0: (WeChat) stopped.
(lldb) po $arg3 //或者po $rdx 打印第三个参数
/Users/xxxx/Library/Containers/com.tencent.xinWeChat/Data/Library/Application Support/com.tencent.xinWeChat/2.0b4.0.9/Backup/91f05ea8dc79f929613c0beb267b789a/75563957-B5FD-4CC5-90F4-0F36D91DD190/Backup.db

获取到备份的数据库位置,直接在finder中打开,发现一共有三个文件

  • Backup.db
  • BAK_0_MEDIA
  • BAK_0_TEXT

为什么是第三个参数呢? OC的对象方法调用,实际调用的是底层的objc_msgSend($arg1,$arg2,...),其中$arg1为调用者本身,$arg2为方法名,后面的参数表示传递的实际参数,因此是从$arg3开始的,可以打印整个寄存器和$arg1,$arg2出来看看

(lldb) register read
General Purpose Registers:
       rax = 0x00006000027b98e0
       rbx = 0x00007fff5fd16680  libobjc.A.dylib`objc_msgSend
       rcx = 0x0000000000000000
       rdx = 0x00006000012e63a0
       rdi = 0x00006000027b98e0
       rsi = 0x00007fff337c64f7  "initWithPath:"
       rbp = 0x00007ffee1a3f060
       rsp = 0x00007ffee1a3efb8
        r8 = 0x0000cc851f0d98e0
        r9 = 0x00000000000007fb
       r10 = 0x0000000110c78ee8  (void *)0x001d800110c78ec1
       r11 = 0x00007fcb9badab70
       r12 = 0x00006000037ca9c0
       r13 = 0x0000000000000000
       r14 = 0x00006000027b9a40
       r15 = 0x00006000037ca9c0
       rip = 0x0000000110b38676  WCDB`-[WCTDatabase(Database) initWithPath:]
    rflags = 0x0000000000000246
        cs = 0x000000000000002b
        fs = 0x0000000000000000
        gs = 0x0000000000000000
(lldb) po $arg1 //po $rdi
<WCTDatabase: 0x6000027b98e0>
(lldb) po $arg2 //po $rsi 
140734057178359 //字符串打印出被转成数字了,可以用memory read打印
(lldb) memory read $rsi
0x7fff337c64f7: 69 6e 69 74 57 69 74 68 50 61 74 68 3a 00 73 65  initWithPath:.se
0x7fff337c6507: 74 46 69 6c 65 6e 61 6d 65 3a 00 69 6d 61 67 65  tFilename:.image

因此实际的函数参数会从第三个$arg3开始。

sqlitebrowser打开这个db文件,发现是SQLCipher加密,要输入密码。

0x02 获取sqlite3_key

继续加断点,如果加在sqlite3_key上,会发现拿不到PageSize,查看源码看调用链,pageSize是在void Database::setCipher(const void *key, int keySize, int pageSize)的时候接收的,断点打在setCipher

(lldb) br set -n setCipher
(lldb) c //继续执行

触发到sqlite3_key的断点, 获取key和pageSize

(lldb) memory read $arg2
0x600003f8fa90: 64 64 30 36 33 35 65 63 65 62 35 37 39 35 32 66  dd0635eceb57952f
0x600003f8faa0: 31 62 35 65 63 31 65 64 33 37 38 30 36 65 31 30  1b5ec1ed37806e10

(lldb) po $arg3
32

(lldb) po $arg4
4096

key是dd0635eceb57952f1b5ec1ed37806e10,取32位,也就是整个字符串,页长度是4096.

打开数据库。 分析一下表,发现文本内容存在BAK_0_TEXT,媒体内容存在BAK_0_MEDIA,以偏移量记录某条消息,简单查看一下这2个文件,都是写二进制数据,看来还用了某种加密方式。

0x03 Todo

  • 静态分析WeChat.app,获取打开两个加密文件的方法。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • AVPlayer初体验之视频解纹理

    xferris
  • Python科学计算和绘图入门

    xferris
  • 利用Electron把Web项目打包成桌面应用

    1.Electron是基于Node.js开发的,第一步当然要安装node盒npm了,就不多说了。

    xferris
  • 聊一聊分库分表及它生产的一些概念

    随着近些年信息化大跃进,各行各业无纸化办公产生了大量的数据,而越来越多的数据存入了数据库中。当使用MySQL数据库的时候,单表超出了2000万数据量就会出现性能...

    BuddyYuan
  • 10w定时任务,如何高效触发超时

    一、缘起 很多时候,业务有定时任务或者定时超时的需求,当任务量很大时,可能需要维护大量的timer,或者进行低效的扫描。 例如:58到家APP实时消息通道系统,...

    架构师之路
  • 微软发出Word漏洞警报,网民防范带毒文档

    如果最近有用户收到RTF格式的Word文档,一定不要轻易打开。根据微软的紧急安全公告显示,Word所有版本均存在一个高危漏洞,并已被黑客攻击,W...

    安恒信息
  • 全栈·跨界·生活·技能·自我驱动

    iCooler、Limber、DeCoder、Vsplorer、失声、懿凡、个革马、乘良、旭辉、00、musde、ck、Arthur张晟、王云飞字昙越、harr...

    mixlab
  • 分享:通过Animate 和wow.js 快速制作你的网页特效

    Animate 通过CSS3封装的一个css插件 wow.js 可以链接到CSS动画库 两者配合起来可以快速制作你的网页特效

    Alone88
  • JavaScript里类和私有属性的两种实现方式

    Jerry Wang
  • 3D打印攻破无人车激光雷达,这个奇怪的盒子它看不见

    深度神经网络容易受到某些对抗样本的攻击,比如图像分类网络,只需在图中加入一点微小的扰动,就能让它把熊猫当成长臂猿。

    量子位

扫码关注云+社区

领取腾讯云代金券