前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >LLDB实战之导出Mac微信备份聊天记录的SQLite密码(SQLCipher加密)

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

作者头像
xferris
发布2019-12-30 09:52:05
6.1K1
发布2019-12-30 09:52:05
举报
文章被收录于专栏:慎独

涉及到的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文件里

代码语言:javascript
复制
- (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微信进程

代码语言:javascript
复制
$ 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

代码语言:javascript
复制
(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

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

代码语言:javascript
复制
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出来看看

代码语言:javascript
复制
(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

代码语言:javascript
复制
(lldb) br set -n setCipher
(lldb) c //继续执行

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

代码语言:javascript
复制
(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,获取打开两个加密文件的方法。
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 0x00 准备工作
  • 0x01获取保存目录
  • 0x02 获取sqlite3_key
  • 0x03 Todo
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档