Mike与猴子深入探索三星Gallery3d应用垃圾数据
这一切始于Michael Lacombe在2021年11月初在Physical and RAW Mobile Forensics Google群组上发布的一个帖子。该帖子涉及一个案例,三星手机用户声称收到了特定图片,但在访问后立即被删除。Mike被问及是否有可能确定这一点。由于不知道这个问题的直接答案,他开始分析三星Android 9设备。
Mike发现了一些在local.db SQLite数据库中可见的三星Gallery3d应用删除痕迹。具体来说,他在"log"表中看到了各种带时间戳的日志条目,这些条目与编码字符串相关联。在论坛成员"Tony"提示这些字符串是base64编码后,Mike开始了更深入的研究。
在研究过程中,他发现了Cheeky4n6monkey在2016年发布的这篇帖子。将该信息与他当前的案例数据进行比较后,他发现这些年来情况发生了很大变化,但这足以促使他进行更深入的挖掘。Mike询问这只猴子是否想一起参与,于是冒险开始了…
研究过程中的发现
总有新事物需要研究
三星Gallery3d应用已经存在多年,根据Google Play,它最后一次更新是在2019年,版本为5.4.11.0。从测试设备的Gallery3d Android包(APK)中打开AndroidManifest.xml文件显示:
|
|
根据Android开发者文档,versionName显示给用户,而versionCode是一个正整数,随着每个版本发布而增加,可用于防止降级到早期版本。
这个应用更新频繁。在搜索测试数据时,我们发现几乎我们查看的每个设备都包含不同版本的应用,这反过来导致应用程序文件夹和数据库本身存储的信息也不同。
挖掘应用痕迹可以发现当前未被解析的额外信息
据我们确定,目前没有商业或非商业的取证工具处理三星Gallery3d应用数据库以获取删除痕迹。
我们用于分析数据和APK的一些开源工具包括:
数据分析:
- DB Browser for SQLite - 查看/导出SQLite数据库
- Cyberchef - base64解码字符串
- Base64 Decode and Encode网站 - base64解码字符串
- Epochconverter - 确认时间戳类型
- Android Studio
APK逆向:
- dex2jar - 将APK的classes.dex转换为Java .jar
- JD-GUI - 从.jar文件查看源代码
- JADX - 直接从APK文件查看源代码
我们还编写了自己的Python3脚本来协助批量转换base64编码字符串并输出到制表符分隔变量(TSV)格式。
三星Gallery3d应用的观察
这是一个安装在三星设备上的原生应用。它具有属于三星Android框架的库依赖项。因此,似乎没有简单的方法(如果有的话)在非三星设备上安装该应用程序。
三星Gallery3d应用位于用户数据分区:
/data/com.sec.android.gallery3d
从应用中发送到垃圾箱的文件位于:
/media/0/Android/data/com.sec.android.gallery3d
由于应用程序每个版本的差异以及研究由Mike的案例驱动,我们决定将本博客重点放在该应用程序版本(10.2.00.21)上。
在/data/com.sec.android.gallery3d目录中,有一个cache目录和一个databases目录。
缓存目录
在data/com.sec.android.gallery3d/cache/中包含多个缓存子目录。在这种情况下,/0文件夹包含较大的缩略图图像,宽度范围225-512像素,高度范围256-656像素,而/1文件夹有较小的缩略图,宽度范围51-175像素,高度范围63-177像素。还有/2、/3和/4文件夹。/2和/3为空,/4有一个大小为320x320的单个缩略图。
除了缩略图本身,这里似乎没有其他有用的信息。缩略图的名称似乎使用哈希算法生成。
数据库目录
/data/com.sec.android.gallery3d/cache/databases/中包含local.db SQLite数据库。此数据库包含各种信息,包括:
- 相册中的专辑(“album"表)
- 记录与应用相关的各种操作的日志(“log"表),例如移动到垃圾箱、清空垃圾箱
- 当前在垃圾箱中的项目(“trash"表)
在后期版本中,我们注意到另一个名为"filesystem_monitor"的表。它包含时间戳、应用包名称(例如com.sec.android.gallery3d)和base64编码的文件路径。但是,由于此表不在Mike的案例数据中,我们不确定是什么触发了这些记录,需要进一步研究。
表观察
“album"表
以下是"album"表模式:
|
|
感兴趣的"album"表字段:
| 字段名 | 描述 |
|---|---|
| _bucketID | 通过对专辑的完整路径("_abspath”)调用Java hashcode算法生成 |
| _abspath | 专辑的路径。示例:/storage/emulated/0/DCIM/Screenshots |
| default_cover_path | 与相应专辑关联的图像。示例:/storage/emulated/0/DCIM/Screenshots/Screenshot_20200530-054103_One UI Home.jpg |
| album_count | 当前存储在专辑中的文件数量。示例:14 |
由于文件路径保持不变,已发现_bucketID值在设备间是一致的。这有助于显示是否存在/曾经创建的自定义专辑,以及应用程序特定的专辑,如Facebook、Snapchat等。恢复此处删除的记录可以显示已删除的专辑和曾经用作专辑封面的已删除图像的名称。封面路径信息可以显示潜在感兴趣的文件名,其中许多通常在文件名中包含时间戳信息。这可能有助于将特定应用程序的使用与特定时间联系起来。
没有为"album"表编写提取脚本,因为可以直接使用DB Browser for SQLite来复制/粘贴专辑数据。
“log"表
以下是"log"表模式:
|
|
感兴趣的"log"表字段:
| 字段名 | 描述 |
|---|---|
| _timestamp | 时间戳文本字符串(格式为YYYY-MM-DD HH:MM:SS,本地时间),当特定日志条目发生时。示例:2020-01-09 16:17:14 |
| _log | 专有格式的文本字符串,列出执行的"操作”(见下表)和相关文件的base64编码路径。示例:[MOVE_TO_TRASH_SINGLE][1][0][location://timeline?position=6&mediaItem=data%3A%2F%2FmediaItem%2F-1566891466&from_expand=false][oKHi/x4pePL+KXj3N0b3Lil49h4pePZ2XimIUvZW3imIV14pePbOKYhWHil4904pePZeKYhWTimIUv4pePMC9E4piFQ0nimIVNL+KYhUZh4pePY+KYhWXil49ib2/imIVr4pePL+KXj0ZCX+KYhUnil49N4pePR1/imIUx4piFNeKYhTfimIU4NOKYhTnimIUw4piFNzTimIU1N+KXjzPimIUy4piFLuKXj2rimIVwZw==ST1puy1] |
观察到的日志"操作"包括:
| 日志操作 | 描述 |
|---|---|
| MOUNTED | 未知何时触发。它告诉当前垃圾箱中有多少文件。示例:[MOUNTED][10][0][0][/storage/emulated/0] |
| MOVE_TO_TRASH_SINGLE | 当用户从时间线或图库视图将单个文件移动到垃圾箱时发生。示例:[MOVE_TO_TRASH_SINGLE][1][0][location://timeline?position=0&mediaItem=data%3A%2F%2FmediaItem%2F2000598506&from_expand=false][2sx0p44piFL3N04pePb+KXj3Lil49h4pePZ+KYhWUv4pePZeKXj23imIV14piFbOKXj2Hil4904pePZWTimIUv4pePMOKYhS/imIVE4piFQ0lNL+KXj1Nj4pePcmXil49l4pePbnPil49ob+KYhXTimIVzL+KXj1Pil49j4pePcuKXj2Xil49lbnPil49o4pePb+KYhXRf4piFMjDil48y4piFMDDil482MeKYhTgt4piFMOKYhTjil48xMOKXjzPil4854piFX+KXj09uZeKYhSBV4pePSeKYhSDimIVIb23il49lLuKYhWril49w4pePZw==7nsjIZn] |
| MOVE_TO_TRASH_MULTIPLE | 当用户从时间线或图库视图将多个文件移动到垃圾箱时发生。每个垃圾箱条目都用括号括起来。 |
| EMPTY_SINGLE | 当手动清空垃圾箱且当时垃圾箱中有一个文件时发生。 |
| EMPTY_MULTIPLE | 当手动清空垃圾箱且包含多个文件时发生。每个清空条目都用括号括起来。 |
| EMPTY_EXPIRED | 当文件在垃圾箱中停留预定时间后自动删除时发生,如应用程序设置中所述。示例:[EMPTY_EXPIRED][22][22][2020-05-14 00:00:00] |
其他在我们的数据中未观察到但在源代码中声明的操作(见TrashHelper类,DeleteType枚举):DELETE_MULTIPLE DELETE_SINGLE
手动解码base64编码字符串的示例:
原始值为:
[MOVE_TO_TRASH_SINGLE][1][0][location://timeline?position=9&mediaItem=data%3A%2F%2FmediaItem%2F-575841975&from_expand=false][eTgcy4piFL3Pil4904piFb+KXj3LimIVh4pePZ2Uv4pePZeKXj2114pePbOKYhWHimIV0ZeKXj2TimIUvMOKYhS9EQ+KYhUlN4piFL1PimIVj4piFcmXil49l4pePbuKXj3Pil49ob3TimIVzL1PimIVj4piFcmXil49lbnNo4pePb3TimIVf4piFMjAxOTHimIUy4pePM+KXjzEtMeKXjznimIUyMeKXjzXil4844piFX+KXj1Nu4pePYeKYhXBjaGF0LmrimIVw4pePZw==bakWlla]
我们复制由[ ]括起来的base64字符串(黄色高亮):
eTgcy4piFL3Pil4904piFb+KXj3LimIVh4pePZ2Uv4pePZeKXj2114pePbOKYhWHimIV0ZeKXj2TimIUvMOKYhS9EQ+KYhUlN4piFL1PimIVj4piFcmXil49l4pePbuKXj3Pil49ob3TimIVzL1PimIVj4piFcmXil49lbnNo4pePb3TimIVf4piFMjAxOTHimIUy4pePM+KXjzEtMeKXjznimIUyMeKXjzXil4844piFX+KXj1Nu4pePYeKYhXBjaGF0LmrimIVw4pePZw==bakWlla
调整到正确长度进行解码需要:
- 移除最后7个字符,即"bakWlla”(上面红色高亮)
- 从字符串开头移除3到6个字符,直到长度是4的倍数。即移除"eTgcy”(上面绿色高亮)
然后我们:
- Base64解码字符串
- 移除填充字符,如Black Star和Black Circle
对于我们上面的示例,我们将base64字符串调整为:
4piFL3Pil4904piFb+KXj3LimIVh4pePZ2Uv4pePZeKXj2114pePbOKYhWHimIV0ZeKXj2TimIUvMOKYhS9EQ+KYhUlN4piFL1PimIVj4piFcmXil49l4pePbuKXj3Pil49ob3TimIVzL1PimIVj4piFcmXil49lbnNo4pePb3TimIVf4piFMjAxOTHimIUy4pePM+KXjzEtMeKXjznimIUyMeKXjzXil4844piFX+KXj1Nu4pePYeKYhXBjaGF0LmrimIVw4pePZw==
通过CyberChef或base64decode.org解码为:
★/s●t★o●r★a●ge/●e●mu●l★a★te●d★/0★/DC★IM★/S★c★re●e●n●s●hot★s/S★c★re●ensh●ot★_★20191★2●3●1-1●9★21●5●8★_●Sn●a★pchat.j★p●g
然后我们可以手动移除以下随机添加的填充字符:
- Unicode代码点U+2605 = “Black Star”
- Unicode代码点U+25CF = “Black Circle”
- Unicode代码点U+25C6 = “Black Diamond”
日志表的”__log"字段格式根据APK版本而变化。我们只查看了版本v10.0.21.5、v10.2.00.21(主要焦点)和v11.5.05.1。因此,编写了两个版本的"log"表解析脚本:samsung_gallery3d_log_parser_v10.py和samsung_gallery3d_log_parser_v11.py。
“trash"表
以下是"trash"表模式:
|
|
在这个示例表中只有10个条目。此表中的条目对应于.Trash目录中的实时文件。位于.Trash中的所有其他文件是具有”_Title"文件名的覆盖文件,但没有日期/时间信息。
感兴趣的trash表字段:
| 字段名 | 描述 |
|---|---|
| __absPath | 已删除文件的当前路径和文件名。示例:/storage/emulated/0/Android/data/com.sec.android.gallery3d/files/.Trash/135138193438761664 |
| __Title | 已删除文件的当前文件名。示例:135138193438761664 |
| __originPath | 原始路径和文件名。示例:/storage/emulated/0/Download/unnamed.jpg |
| __originTitle | 原始文件名。示例:unnamed.jpg |
| __deleteTime | UNIX毫秒时间(UTC)。示例:1592678711438 |
| __restorExtra | JSON格式,包含各种元数据,如:"__dateTaken"(本地时间的UNIX毫秒时间)、"__latitude"、"__longitude"。示例:{"__is360Video":false,"__isDrm":false,"__isFavourite":false,"__cloudOriginalSize":0,"__cloudRevision":-1,"__fileDuration":0,"__recordingMode":0,"__sefFileSubType":0,"__sefFileType":-1,"__cloudTimestamp":1592678711350,"__dateTaken":1592669230000,"__size":98526,"__latitude":0,"__longitude":0,"__capturedAPP":"","__capturedURL":"","__cloudServerPath":"","__hash":"","__mimeType":“image/jpeg”,"__resolution":"","__recordingType":0,"__isHdr10Video":false} |
“__Title"值(在”__absPath"中看到)是通过对"__originPath"值调用专有的Crc::getCrc64Long函数派生的。注意:此值是通过与专辑表的"__bucketID"字段不同的方法生成的。
编写了一个脚本来解析"trash"表:samsung_gallery3d_trash_parser_v10.py
local.db中还有其他表,但由于时间限制和可用的测试数据,我们专注于"log"和"trash"表。
在一些后期的应用版本中,我们注意到一个"filesystem_monitor"表,它列出了诸如package、date_event_occurred(疑似自1970年1月1日以来的毫秒数)、__data(base64编码的文件名)、event_type(含义目前未知)等字段。此表需要进一步研究。
脚本编写
编写了一些初始的Python 3脚本来解析"log"和"trash"表。没有为"album"表编写提取脚本,因为可以直接使用DB Browser for SQLite来复制/粘贴专辑数据。
由于观察到的"__log"字段格式不同,为"log"表编写了两个版本:samsung_gallery3d_log_parser_v10.py和samsung_gallery3d_log_parser_v11.py。这两个脚本都从"log"表中提取各种字段,并对我们在数据中观察到的任何编码路径名称进行base64解码。v11版本是为了处理不同格式的"__log"字段值而编写的。
samsung_gallery3d_log_parser_v10.py的帮助文本:
|
|
使用示例:
|
|
samsung_gallery3d_trash_parser_v10.py的帮助文本:
|
|
使用示例:
|
|
GitHub仓库中还编写并包含了一些其他脚本(需要进一步测试):
samsung_gallery3d_log_parser_v11.pysamsung_gallery3d_filesysmon_parser_v11.py
这些脚本是为解析应用程序版本11.5.05.1的测试数据而编写的(与我们目标应用程序版本10.2.00.21不同)。版本11脚本已包含在GitHub仓库中,但在此帖子中不再进一步描述。
脚本方面,最有趣的部分是自动化编码路径解码。使用诸如dex2jar、JD-GUI和JADX等工具,我们能够找到负责路径编码的代码(见Logger.class::getEncodedString方法),并编写了相应的Python函数来base64解码编码的路径字符串。
如前所述,路径解码过程是:
- 移除最后7个base64编码字符
- 从编码字符串开头移除3-6个字符,直到有效的base64长度(4字节的倍数)
- 执行base64解码
- 移除任何特殊的填充字符,例如Black Star、Black Circle
因此,“log"表脚本(samsung_gallery3d_log_parser_v10.py)的基本过程是:
- 查询数据库,例如"SELECT _id, __category, __timestamp, __log FROM log ORDER BY __timestamp ASC;”
- 对于每一行:
- 提取"__log"和"timestamp"字段
- 解码base64编码的路径(每个日志项可能有多个路径)
- 将提取的字段和解码的路径字段打印到TSV
“trash"表脚本(samsung_gallery3d_trash_parser_v10.py)的过程是:
- 查询数据库,例如"SELECT __absID, __absPath, __Title, __originPath, __originTitle, __deleteTime, __restoreExtra FROM trash ORDER BY __deleteTime ASC;”
- 对于每一行:
- 提取__absPath、__originPath、__deleteTime和__restoreExtra(元数据)字段
- 将提取的字段打印到TSV(不需要路径解码)
总结
所有这些研究导致了对逆向工程Android应用、新的和独特的哈希码算法以及不同编码技术的更深入理解。进一步研究可能/可能不被现有工具解析的应用数据库仍然可以发现新的信息、感兴趣的文件夹、日志文件等。您可能会发现Android或特定应用的新版本中引入的新数据。
对于Mike的案例,使用本帖的研究和脚本显示,用户习惯于截屏或下载图像,然后在短时间内删除并清空垃圾箱。网络浏览器用于访问有问题的图像,但删除网络历史记录也是一个频繁的过程。恢复的截屏名称显示用户在特定时间使用了网络浏览器。不幸的是,这些日期和时间与问题中的时间不匹配,但这确实导致了在其他解析数据中未发现的其他调查时间。
与Mike一起研究这篇帖子让猴子学到了更多关于三星Gallery应用的知识,获得了逆向工程Android APK的进一步经验,并保持了他的Python技能新鲜。就像任何语言一样,流利度会因缺乏使用而下降。
编写了各种Python 3脚本来协助解析三星Gallery3d应用(v10.2.00.21)的"log"和"trash"表。这些表可能存储有关从三星Gallery3d应用中执行的图像删除信息,例如时间戳和原始文件路径。
本帖还展示了协作研究如何能够导致增加的产出/新工具。例如,将Mike的测试观察与猴子的脚本编写相结合。与具有不同知识、技能、经验和新鲜视角的其他人合作的机会是无价的。利用这种经验可能与参加培训课程或网络研讨会一样好,甚至更好。