Frida
5.24大比武
P.S.本文不涉及任何与案情相关内容 完全是以记录frida的综合应用为主 并且只截取了部分题目 侵删
Hook
题目要我们干三件事情
1.app加密用户聊天记录的数据库文件的加密方式
2.app加密用户聊天记录的数据库文件使用的私钥字符串
3.记录用户聊天记己录的数据库表名
题目提供的镜像是vmdk格式文件 我们雷电模拟器仿真起来 很显然这个数据库被加密了(题目告诉我们的) 意味着一般的数据库取证套路都失败了 我们祭出frida
frida简单来说是一个支持java和python语言的hook动态调试框架 我们首先要先让模拟器跑起来这个框架
1 | adb push D:\Work\网安十百千\frida环境\frida-server-15.0.13-android-x86\frida /data/local/tmp |
传输成功之后 还要赋予他777
1 | chmod 777 frida |
进入root之后 在tmp目录下执行该文件
1 | ./frida |
这个框架目前就运行在了模拟器上 接下来回到咱电脑 模拟器那边的框架建立好了 我们需要在本机上运行frida.exe 建立连接 个人因为环境原因 将frida.exe 放在了python3/script里 进去之后
1 | ./frida.exe --version |
出现版本则代表frida版本暂时没问题 接下来可以进行hook 一个通用脚本是进行hook看看他在运行的时候读取了哪些文件 这样说不定可以找到他的数据库文件
hook_open_files.js
1 | Java.perform(function () { |
基本上这个脚本不用改 我们给他跑起来
1 | ./frida.exe -UF -l hook_open_files.js |
注意 -UF的意思是hook当前正在运行的程序 所以我们事先得先把程序跑起来 然后再执行脚本 而且注意 这个.js脚本得和frida.exe放在一个文件夹里 我的环境是这样
1 | message: {'type': 'send', 'payload': '路径:/data/data/cn.keke.chat/cache/w9wIwIoo.session'} data: None |
我们虽然读取到了他读取的文件 但是我们发现这都是.session文件 如果我们去看一眼也会发现 这个.session是没有加密的 他只是将文本按一定格式进行了储存 那就不对了 说明很可能这个程序有多个进程 目前我们看到的聊天的这个进程他是没有读取数据库的 我们还需要另一个进程的数据 这个时候就需要多进程hook了 由于这个脚本是python的 所以我们需要开放一下27042端口才行 27042是默认的通信端口
1 | adb forward tcp:27042 tcp:27042 |
打开了之后 我们在进行hook 我们用hook_thread_open_file.py 用的时候需要改一下包名
1 | # -*- coding: utf-8 -*- |
这个脚本是python的 所以我们得在python.exe路径下运行 并且我们还需要安装frida-tools库
1 | pip install firda-toolspython hook_thread_open_file.py |
1 | Enabled spawn gating[*] Attach Application id: 2324spawn_added: Spawn(pid=2351, identifier="cn.keke.chat:marsservice")spawn_added: Spawn(pid=2377, identifier="cn.keke.chat:pushservice")[*] 操作文件: 0xc3bdb690[*] 路径:/data/app/cn.keke.chat-1/base.apk[*] 加载文件: libandroidfw.so[*] 操作文件: 0xc3bdb690[*] 路径:/data/app/cn.keke.chat-1/base.apk[*] 加载文件: libandroidfw.so[*] 路径:/data/app/cn.keke.chat-1/lib/x86/libstlport_shared.so[*] 加载文件: libjavacore.so[*] 路径:/data/app/cn.keke.chat-1/base.apk[*] 加载文件: libopenjdkjvm.so[*] 路径:/data/app/cn.keke.chat-1/lib/x86/libmarsxlog.so[*] 加载文件: libjavacore.so[*] 路径:/data/app/cn.keke.chat-1/lib/x86/libmarsstn.so[*] 加载文件: libjavacore.so[*] 路径:/data/app/cn.keke.chat-1/lib/x86/libstlport_shared.so[*] 加载文件: libjavacore.so[*] 路径:/data/app/cn.keke.chat-1/lib/x86/libmarsxlog.so[*] 加载文件: libjavacore.so[*] 路径:/data/app/cn.keke.chat-1/lib/x86/libmarsstn.so[*] 加载文件: libjavacore.so[*] 路径:/data/user/0/cn.keke.chat/files/mipush_region.lock[*] 加载文件: libjavacore.so[*] 路径:/data/user/0/cn.keke.chat/files/mipush_region[*] 加载文件: libopenjdkjvm.so[*] 路径:/data/user/0/cn.keke.chat/shared_prefs/mipush_extra.xml[*] 加载文件: libopenjdkjvm.so[*] 路径:/data/user/0/cn.keke.chat/files/BLBwBwMM/host/ipportrecords2.xml[*] 加载文件: libc.so[*] 路径:/data/user/0/cn.keke.chat/files/BLBwBwMM/Heartbeat.ini[*] 加载文件: libc.so[*] 路径:/data/user/0/cn.keke.chat/databases/geofencing.db[*] 加载文件: libsqlite.so[*] 路径:/data/user/0/cn.keke.chat/shared_prefs/sp_client_report_status.xml[*] 加载文件: libopenjdkjvm.so[*] 路径:/data/user/0/cn.keke.chat/databases/geofencing.db-journal[*] 加载文件: libsqlite.so[*] 路径:/data/user/0/cn.keke.chat/databases/geofencing.db-journal[*] 加载文件: libsqlite.so[*] 路径:/data/user/0/cn.keke.chat/databases/geofencing.db-journal[*] 加载文件: libsqlite.so[*] 路径:/data/user/0/cn.keke.chat/databases/geofencing.db-journal[*] 加载文件: libsqlite.so[*] 路径:/data/user/0/cn.keke.chat/databases/geofencing.db-journal[*] 加载文件: libsqlite.so[*] 路径:/data/user/0/cn.keke.chat/shared_prefs/mipush_extra.xml[*] 加载文件: libopenjdkjvm.so[*] 路径:/data/user/0/cn.keke.chat/shared_prefs/cn.keke.chat_preferences.xml[*] 加载文件: libopenjdkjvm.so[*] 路径:/data/user/0/cn.keke.chat/files/BLBwBwMM/169c2404d30b18c81620889828374/data[*] 加载文件: libmarsstn.so[*] 路径:/data/misc/keychain/pubkey_blacklist.txt[*] 加载文件: libjavacore.so[*] 路径:/data/misc/keychain/serial_blacklist.txt[*] 加载文件: libjavacore.so[*] 路径:/data/misc/keychain/pins[*] 加载文件: libjavacore.so |
1 | [*] 路径:/data/user/0/cn.keke.chat/files/BLBwBwMM/169c2404d30b18c81620889828374/data[*] 加载文件: libmarsstn.so |
经确认 这个data就是被加密的数据库了 但是我们要怎么获取私钥key呢 我们进IDA分析一下这个apk是怎么读取数据库的 我们要注意 是这个libmarsstn.so文件读取的数据库文件 我们分析的时候就先分析这个文件 不过注意是x86的 不清楚就都看一下 我们字符串搜索 查看sqlite 发现他是sqlite3的数据库 然后我们找到了一个很有价值的字符串:SQLite format 3
1 | .rodata:0032363E aSqliteFormat3 db 'SQLite format 3',0 ; DATA XREF: sub_23E940+158↑o |
更重要的是后面的函数 sub_23E940 我们跟进 查看伪代码 这里就需要思考了 就算这个函数就是加密的函数 那么我们要怎么解密 如果直接硬刚肯定是不现实的 咱没这个实力
1 | https://www.cnblogs.com/qxxnxxFight/p/4096074.html |
这个大佬的博客里面写着一些SQL加密的 可以说是源码
1 | 1 void sqlite3pager_free_codecarg(void *pArg); |
列出的总共是14种 我们看看IDA里面的加密函数
1 | char *__cdecl sub_23E940(_DWORD *a1, char *a2, int a3, int a4) |
cdecl 有点像第12个 毕竟你要看他传入的是4个参数 这个可能只是个子函数 我们看看谁调用的他 看看调用它的是调用了几个参数 跟进sub_23E940 看看谁调用的他
1 | int __usercall sub_20F0E0@<eax>(int a1@<eax>, int a2@<edx>, int a3, int a4) |
还是4个的 而且你看名字也有点像12 暂定是他吧 然后我们看第12个 对应的第三个参数就是我们需要的私钥 而这个私钥在调用时会被存放在内存里 上脚本
1 | # -*- coding: utf-8 -*- |
相比上一个改的不多 就是改了一下jscode 注意 我们的地址都是偏移地址 base.apk的基地址+函数的地址=偏移地址 只不过还有一个要注意的地方
根据数据库密钥附加的源码分析 判断左数第三个变量即为私钥 又因为传参的顺序从右向左 则有如下对应关系 为args[0]:nKeyLen, args[1]:pKey; args[2]:nDb; args[3] *db 所以args[1]为私钥pKey 咱hook一下
1 | Enabled spawn gating[*] Attach Application id: 2150spawn_added: Spawn(pid=2178, identifier="cn.keke.chat:marsservice")spawn_added: Spawn(pid=2204, identifier="cn.keke.chat:pushservice")[*] key:216bf0c8-75a3-4bc4-841e-87cabf6bac9aspawn_added: Spawn(pid=2270, identifier="cn.keke.chat:marsservice")[*] key:216bf0c8-75a3-4bc4-841e-87cabf6bac9aspawn_added: Spawn(pid=2319, identifier="cn.keke.chat:marsservice")[*] key:216bf0c8-75a3-4bc4-841e-87cabf6bac9aspawn_added: Spawn(pid=2359, identifier="cn.keke.chat:marsservice")[*] key:216bf0c8-75a3-4bc4-841e-87cabf6bac9aspawn_added: Spawn(pid=2401, identifier="cn.keke.chat:marsservice")[*] key:216bf0c8-75a3-4bc4-841e-87cabf6bac9aspawn_added: Spawn(pid=2441, identifier="cn.keke.chat:marsservice")[*] key:216bf0c8-75a3-4bc4-841e-87cabf6bac9a |
Jackpot! 然后就可以解密数据库了