第一作者简介:康艳荣(1980—),女,河北乐亭人,硕士,副研究员,研究方向为电子物证。E-mail:kangyanrong@cifs.gov.cn;
目前关于Android手机动态内存提取技术的研究是在LiME工具基础上,通过编译源内核进行提取。由于Android系统手机开放源代码的不完整性,实际取证工作中很难获取到与目标手机相匹配的内核源码。因此,本文提出一种通过解决未知符号错误实现基于相似内核提取Android手机动态内存的方法。该方法通过分析Linux下ELF格式与内核符号机制,在内核源码中找到未知符号函数定义并取消其在内核中的配置,编译内核时产生不具有指定的符号引用信息的模块,最后将相似内核成功加载至目标手机并提取到动态内存数据。
Live memory can be extracted from Android phone when the sourcing kernel of Android phone is able to be successfully compiled with LiME tool. However, most of the sourcing kernel cannot be obtained during the actual electronic forensics because not all the open source codes of Android phone are offered publicly, and even many of them are difficult to find. In this paper, a method was proposed to use one similar kernel to extract live memory from Android phone by resolving the unknown symbol error. First, an analysis was conducted on the Linux-based ELF format and kernel symboling mechanism so as to find the function definition of unknown symbols from the relevant source codes, and thereby cancel the corresponding configuration. Second, one similar kernel was accordingly compiled to exclude those unknown symbol indexes. At last, the similar kernel has been successfully uploaded to Android phones, making the live memory acquired from most of the tested phones.
Android是基于Linux内核的移动操作系统, 大部分适用于Linux动态内存分析的框架也适用于Android, 如Volatility[1]、Second Look[2]、Rekall[3]等。但基于Android的动态内存提取工具则非常少, 2004年Carrier[4]提出了基于硬件的内存提取框架, 但无法在Android上操作, 目前基于Android的动态内存提取只有软件工具, 其中比较成熟的是2012年由Sylve[5]提出的LiME Forensics。2012年10月Muller等[6]在发表的论文中报道了一种在FROST状态下提取Android手机随机访问内存的方法。FROST技术的实现也是基于LiME工具, 但该文章没有对提取的数据做任何分析, 而且用于试验的手机型号也仅限于Samsung Galaxy Nexus。2013年, Stü ttgen等[7]提出了一种新的PTE物理内存提取技术, 然而使用的PMEM工具仅能在Windows、Linux、Mac OS X运行, 尚不能提取Android手机的物理内存。2014年, Stü ttgen等[8]又提出了一种新的通过重定向技术使注入到目标系统的通用内核模块执行提取内存命令的方法, 但该研究的实验对象是2.6.38至 3.10的Linux发行版, 并没有在Android上进行测试。2014年, Cohen等[9]提出了Rekall Memory ForensicFramework, 是一个完整的开源的动态内存提取与分析解决方案, 该系列工具的支持对象有Windows, OSX 和Linux。在2015年最新发布的版本中, 该框架可以对Android的动态内存进行分析, 然而尚未能提取到Android的动态内存。
在Linux平台上, 基于内核模块的取证工具通常需要针对特定版本内核和配置来进行编译工作, 这使得在实际工作中, 对Linux内存提取操作十分复杂。由于Linux的内核安全机制, 一般会拒绝装载针对非本身版本的模块, 即使通过技术手段绕过内核校验机制, 也无法避免内核中的符号导出问题[10]。使用LiME工具对Android动态内存的提取准备工作中, 需要基于目标系统对应的内核源代码, 而在大部分取证工作中, 由于Android开放源代码的不完全性, 特别是对于厂商定制版的手机型号, 很难获取与目标Android系统完全匹配的内核源代码, 这使得取证工作的开展具有一定困难。本文提出一种通过解决未知符号错误实现基于相似内核提取Android手机动态内存的方法。该方法通过分析Linux下ELF格式与内核符号机制, 在内核源码中找到未知符号函数定义并取消其在内核中的配置, 编译内核时产生不具有指定的符号引用信息的模块, 最后将相似内核成功加载至目标手机并提取到动态内存数据。
Linux下的内核模块实质上是可重定位的ELF(Executable and Linkable Format)目标文件, ELF文件是Linux里一种非常重要的数据形式, 总体可分为ELF头部、Section部分和尾部的Section Header Table部分。内核根据ELF头部确定文件中需要映射到内存中的部分, 在文件的动态链接过程中, 外部符号通过全局偏移表(.got)和过程链接表(.plt)解决运行时的符号引用问题。符号表是可装载模块中描述符号定义的信息组合, 全局偏移表用于映射模块内部引用与外部地址, 若在模块加载过程中, 出现独立编译的外部模块需要本地内核加载的情况, 内核无法解决符号引用问题, 就会出现模块装载错误。
在Linux系统下, 内核模块之间的变量和函数的共享通过符号信息实现, 在底层代码的实现层面上, 内核符号信息的导出以宏声明的方式声明定义。Linux源码下的/module.h中定义了内核符号导出的三种声明方式:
#define EXPORT_SYMBOL(sym)
#define EXPORT_SYMBOL_GPL(sym)
#define EXPORT_SYMBOL_GPL_FUTURE(sym)
编译内核模块时, 遇到EXPORT_SYMBOL的宏定义则在对应模块中导出相应的符号信息, 在对应的ELF视图下可看到对应符号的内存地址。内核完全编译后, 生成存储所有符号信息的System.map文件, 对于指定的模块可通过readelf命令读取ELF中的符号信息。图1为模块中的外部符号变量在汇编视图中的显示。
基于上述原理, 在本地内核中加载外部模块时, 可能会出现模块中内核符号不一致问题导致加载失败, 解决这类问题的关键是在编译阶段防止模块导出特定的符号信息。在编译基于Linux的源码时, 内核配置的具体细节会使模块中的导出符号产生很大差别, 不同的配置选项用于引用不同的函数, 因此在特定系统内核中加载外部模块时, 实质上是由于模块中未知符号引用的函数体在特定内核中未被定义, 目标系统无法识别该模块中的函数, 出现未解决的符号引用错误。在编译前取消对该未知函数体的引用, 可以解决此类问题。
由于Linux内核源码的复杂性与差异性, 基于某一内核版本编译的模块在内核配置上存在很大的不同, 要使得编译的外部模块与目标系统内核中的符号信息相差最小, 需尽量减小依赖编译的内核与目标系统内核的版本差异性, 这样可使内核符号表中的引用的函数体相互匹配。
解决模块因符号错误造成的加载失败, 需要对未解决的符号进行分析, 在相应的内核源码中确定所依赖的函数体, 并分析该符号在模块之间的相互依赖关系。再根据函数定义在内核配置中取消对应的配置, 在编译内核时产生的模块即不具有指定的符号引用信息, 可以使基于内核的取证模块在目标内核中成功装载。
LiME是基于内核编译的可装载模块取证工具, 可以从Android手机中提取动态内存, 是面向Android的易失性取证方面非常有价值的工具。构造LiME模块需要编译对应目标Android手机的设备内核源代码, 而在现实取证工作中, 出于各种原因或无法获得目标Android手机的对应内核源代码, 使用相似内核源码产生的模块在手机上加载时, 会出现内核符号表未知引用的问题。本文以HTC S710d, Android 版本 2.2, 内核版本2.6.3 5为实验对象, 相似内核选择flyer-hc-mr-2.6.35-f4a346d。
Android 手机是USB调试模式并已经获得ROOT权限。操作系统为32位Ubuntu 12.04。
JDK 7.0 配置与软件包安装:
$sudo apt-get install ia32-libs
$sudo apt-get update
$sudo apt-get install openjdk-7-jdk
$sudo apt-get install gitgnupg flex bison gperf build-essential/
zip curl libc6-dev libncurses5-dev:i386 x11proto-core-dev/
libx11-dev:i386 libreadline6-dev:i386 libgl1-mesa-glx:i386/
libgl1-mesa-dev g++-multilib mingw32 tofrodos/
python-markdown libxml2-utils xsltproc zlib1g-dev:i386
$sudoln -s /usr/lib/i386-linux-gnu/mesa/libGL.so.1 /usr/lib/i386-linux-gnu/libGL.so
Android USB 访问权限配置:
#HTC
SUBSYSTEM= =” usb” , SYSFS(idVendor)= =” 0bb4” , MODE=” 0666”
Android SDK 配置:
# Android SDK
export PATH=~/Android/Sdk/platform-tools:$PATH
export PATH=~/Android/Sdk/tools:$PATH
$sudo source /etc/profile
Android NDK 配置:
$chmoda+x android-ndk-r10c-darwin-x86_64.bin
$./android-ndk-r10c-darwin-x86_64.bin
export PATH=/home/liu/android/ndk/toolchains/arm-linux-androideabi-4.6/
prebuilt/linux-x86/bin: $PATH
$sudo source /etc/profile
Build cross-compiling environment
编译器环境配置:(本实验使用arm-eabi-4.4.3 作为编译器)
$sudomkdir /home/liu/cc
$sudochmod 777 /home/liu/cc
$sudo tar -xzvf arm-eabi-4.4.3
$sudogedit ~/.bashrc
# arm-eabi- path
export PATH=/home/liu/cc/ arm-eabi-4.4.3/bin:$PATH
$sudo source ~/.bashrc
$sudo arm-eabi-gcc -v
LiME配置:
$sudogit clone https://github.com/504ensicsLabs/LiME.git
# LiME
export LIME=/home/liu/android/lime/src
$sudo source /etc/profile
make KERNELRELEASE=< UTS_RELEASE> modules_prepare
Makefile配置:
SUBARCH := arm
ARCH:= arm
CROSS_COMPILE:= /home/liu/cc/arm-eabi-4.4.3/bin/arm-eabi-
Lime模块编译:
obj-m := lime.o
lime-objs := main.otcp.odisk.o
KDIR :=< path/to/kernel-source>
PWD := $(shell pwd)
CCPATH :=< path/to/android-ndk/toolchains>
default:
$(MAKE) ARCH=arm CROSS_COMPILE=$(CCPATH)/arm-linux-androideabi- -C $(KDIR_GOLDFISH) EXTRA_CFLAGS=-fno-pic M=$(PWD) modules
将上述生成模块上传至目标Android手机中, 装载该模块后出现如图2所示的错误。
由该错误可知, Android手机装载LiME模块时, 该模块中含有__gnu_mcount_nc符号信息, 但该符号引用在原Android系统中未被定义, 于是出现无法识别该函数的错误提示。根据反馈的提示, 需要在内核源码中分析有关__gnu_mcount_nc符号变量的定义代码, 并寻找与该符号有依赖关系的函数定义。通过在源码中进行遍历操作, 可知该符号由ftrace操作产生, ftrace可以动态地监视Linux内核中的行为, 是基于Linux内核的一个非常重要的内核跟踪调试工具, ftrace定义中关于__gnu_mcount_nc的声明如下:
#ifdef CONFIG_FUNCTION_TRACER
#define MCOUNT_ADDR((unsigned long)(__gnu_mcount_nc))
#define MCOUNT_INSN_SIZE4 /* sizeofmcount call * /
#ifndef __ASSEMBLY__
extern void mcount(void);
extern void __gnu_mcount_nc(void);
……
内核配置里关于ftrace的选项与CONFIG_FUNCTION_TRACER相关, 当启用该内核设置时, 编译内核时就会加入ftrace的所有相关的函数引用, 并向外部导出__gnu_mcount_nc的符号信息。实验所用的Android手机中系统内核并无编译ftrace功能, 加载该模块时就会触发手机内核对该符号的依赖, 但由于手机中未定义该函数, 因此在Android手机加载模块时出现未知符号__gnu_mcount_nc的提示。
确定未知符号由ftrace引起后, 在源码中继续遍历与ftrace相关的所有内核编译选项, 分析其函数间的调用关系, 发现以下设置依赖于__gnu_mcount_nc符号:
CONFIG_FUNCTION_TRACER
CONFIG_DYNAMIC_FTRACE
CONFIG_FUNCTION_GRAPH_TRACER
……
编辑源码下的.config配置文件, 关闭相关配置选项, 重新执行编译操作后的内核不具备ftrace功能。经分析发现, 编译后的底层数据文件还存在__gnu_mcount_nc符号信息, 查找后定位在/kernel/bounds.s与/arch/arm/kernel/asm-offsets.s中, 如图3所示该数据文件以汇编形式存在。
对编译过程进行研究可知, LiME编译外部模块时依赖上述两个文件, 构造的外部模块也会具有__gnu_mcount_nc符号, 需要对符号所在的数据段进行删除。使用gedit定位至该符号所在代码, 如图4所示中的.LCFI2段, 将所有内容删除后直接使用LiME重新编译模块, 读取该模块的ELF信息并未发现__gnu_mcount_nc符号, 将模块上传至Android手机中最终能够成功装载, 提取动态内存, 且内存提取效率与使用源目标内核代码相差很小。继续使用多个型号手机进行内存提取实验, 按照基本原理方法对内核符号进行跟踪调试, 发现模块中出现的未知符号主要与内核中内存管理、虚拟系统等底层架构有关, 如配置了CONFIG_SPARSEMEM_高端内存管理相关选项组后, 模块中会调用mem_section符号信息, 因此装载模块时需要内核对mem_section的依赖, 而定义CONFIG_ARM_PATCH_PHYS_等关于地址管理的配置信息后, 由于内核中需要映射虚拟地址与物理地址的关系, 而导致内核需要引用__pv_phys_offset符号。在编译内核前消除对相关符号的引用, 可以使模块中不含有该符号。实验结果证明该方法编译后的模块可以在相似内核手机上加载并能成功提取动态内存。
本文介绍的方法能够在无法获取目标手机的内核源代码时, 使用相似内核源码进行替代从而有效提取Android手机的动态内存。本实验证明了通过调试内核符号可以使模块在不同版本特征的内核中进行装载, 减少了目标系统对基于内核模块的内存取证工具的影响, 对实际取证工作有一定的积极作用。LiME是有效提取完整的Android手机动态内存的取证工具, 在目前的情况下, 这种取证方法可以大大提高内存提取成功率, 而将对原始证据信息的影响降到最低。
The authors have declared that no competing interests exist.
作者已声明无竞争性利益关系。The authors have declared that no competing interests exist.
[1] |
|
[2] |
|
[3] |
|
[4] |
|
[5] |
|
[6] |
|
[7] |
|
[8] |
|
[9] |
|
[10] |
|
[11] |
|