电子取证Android篇之App数据
沙盒机制
Android的沙盒机制简单来说就是给每一个app创建一个独立且封闭的运行环境,限制app只能访问自身或者被授权的资源,防止app之前或者与系统产生未经允许的恶意干扰。
Android基于Linux内核,这意味着在安装app的时候会被分配一个独一无二的用户id,在运行的时候因为Linux的文件系统权限机制会阻止这类跨用户的访问。
每个应用通常都运行在自己的独立进程内,Android系统中应用进程是由Zgoyte进程fork而来,每开启一个app都会fork出一个子进程也就是所谓的用户进程。
并且因为沙盒机制是完全封闭的原因,所以无法获得类似拍照,访问通讯录等功能,如果想要使用这些功能就必须在AndroidManifest.xml文件中对权限进行声明,当然使用动态获取权限的方法也可疑获取权限。
App内部存储目录
内部存储目录是Android系统分配给app的始终可用的存储空间,在代码中使用getFilesDir方法获取到根目录,路径表现为/data/data/PackagesName/,该目录下通常有files目录(存放应用持久化文件)、cache目录(app临时缓存文件)、database目录(Sqlite数据库文件)、shard_prefs(SharePreferences文件 xml格式 存放键值对的)、code_cache(代码缓存),这个内部存储目录默认是私有的,只有app自身可以访问,当应用卸载之后这个目录会被系统一并清除,包括保存的数据库文件,所以在app中的数据只要不是存储在云端的数据卸载app之后都会丢失并且无法找回。部分截图如下所示:
App外部存储目录
外部存储通常分为两个部分,一个部分是应用的专属目录,表现形式为/storage/emulated/0/Android/data/PackagesName/,这个目录下通常有files目录(应用外部持久化文件)、cache(应用外部缓存文件)、media(应用媒体文件),该目录在代码中使用getExternalFilesDir方法进行获取,一个部分是公用的目录(可以被所有应用访问的目录),表现形式为/storage/emulated/0/下的目录包括Pictures、Movies、Music等,该目录在代码中使用getExternalStoragePublicDirectory方法进行获取。
在app取证的过程中想要获取用户的数据,那么首先要提取的就是app的内部存储目录,但是这个内部存储目录因为安全机制的原因无法通过常规手段获取,如果想要完整的获取到内部存储目录那么我们就需要root权限才能访问或者提取出这个目录中的数据,部分截图如下所示
Apk本体存放位置
这个所谓的本体指的是apk文件,该文件通常的存放位置是data/app/PackageName-[随机字符串]/base.apk,这个目录同样是需要root权限才可以查看,在这种情况下我们可以通过取证软件对文件系统进行提取如“盘古石手机取证分析系统”,通过获取这个app我们可以进行反编译查看app的运行逻辑,如果是伪造的app我们还可以通过比对签名确定真假。
Android应用包管理器
这个文件存放与/data/system/目录下,文件名为“packages.xml”,这个文件记载了当前设备中安装的app的元数据,比如安装时间、更新时间、所需权限、UID、签名哈希与版本好,利用好这个文件内容可以构建一段时间线,证明在某件事发生之前,当前设备是否安装或者更新了数据,部分截图如下所示:
取证实例
需求:有一个直播类型的apk,让我们找到apk在运行过程中手机产生的数据库的文件名以及该数据库的初始密码为多少。
需求1,“获取产生的数据库文件名”如上所述,app运行产生的数据库文件位于app的内部存储目录,该目录需要有root权限才能够访问,所以我们放弃使用真机去安装apk,转而使用模拟器,模拟器可以一键root提供了很大的便利,获取root权限后直接进入私有目录下查看文件名即可。
需求2,“获取数据库的初始密码”,数据库因为要和app内容进行交互所以通常是直接写在代码中的,使用jadx进行反编译,分析代码获取到密码后,使用adb将数据库导出再使用分析出的密码进行验证即可。
我们将app托到模拟器中安装运行,等待安装成功
安装成后运行一下app后,使用adb连接模拟器,并获取管理员权限,成功获取到之后进入app的私有目录,由jadx我们得到该app的包名“plus.H5B8E45D3”,根据前面所说那么完成的目录为/data/data/plus.H5B8E45D3/,进入目录查看数据库文件名为:test.db,部分截图如下所示:
获取到正确的数据库文件名之后我们继续分析数据库密码,因为知道数据库文件名,我们可以使用jdax的全局搜索功能尝试找到创建数据库的位置,根据代码分析可以得出,数据库的密码是由数组(byte[] key = {97, 98, 99, 100, 101, 102})转换为16进制字符串,再去1-2个字符(.substring(1, 3)),之后对结果进行MD5进行加密作为最终密码(getMD5),逆推可以得出密码为:c74d97b01eae257e44aa9d5bade97baf,将数据库文件导出并进行密码验证,导出使用的adb pull的命令,需要注意的是,当前已经不在模拟器的系统当中,所以我们无法通过su获取管理员权限,这里我们使用adb root获取管理员权限之后再进行导出,部分截图如下所示: