窥探Android内核: Crash
& Treasure�
方家弘 聂森
Keen
Team
Android Kernel ≈ Linux Kernel�
• 我们关⼼心的区别:�
– 没有udev�
– Init负责创建/dev�
– 没有pagefi゙le J �
• 其他区别:�
– 特别的内存管理(ashmem, pmem)�
– 特别的电源管理(wakelock, alarm)�
– ……�
静态分析Android Kernel
静态分析Android Kernel�
• 不是ELF⽂文件�
• 也不存在ELF bundle�
– 区别于iOS kernel�
– 平面结构�
• IDA会尝试分析并区分
其中的函数�
• 效果非常不理想 è �
静态分析Android Kernel�
• /proc/kallsyms可以提供所有的kernel
symbol�
• 早先的⼀一个patch加⼊入/proc/sys/kernel/
kptr_restrict,默认为1,隐藏symbol�
• 对exploit略微增加了⼀一些阻碍�
h)ps://
静态分析Android Kernel�
• /proc/sys/kernel/
kptr_restrict置为0即可
正常输出。�
• 创建⼀一个IDA loader�
• 配合kallsyms输出来创
建函数�
• 效果非常理想è �
设备注册�
Fuzzing设备驱动�
• 为什么从驱动⼊入⼿手?�
• 已知漏洞�
– mmap() logic issue [Framaroot]�
– memory corruption [Qualcomm MSM]�
• Dumb Fuzzing
软柿⼦子�
• Android碎片化严重�
• 芯片厂商代码良莠不齐�
• 驱动难道不是最简单的root⽅方案么?�
已知漏洞 – mmap边界检查�
• Framaroot ,包含多个可root漏洞,
针对以下设备列表.�
/dev/exynos-mem "Sam"
/dev/DspBridge "Gemli"
/dev/s5p-smem "Merry"
/dev/exynos-mem "Frodo"
/dev/video1 "Aragorn"
/dev/graphics/fb "Legolas"
/dev/msm_camera "Gandalf"
/dev/camera-isp "Boromir"
/dev/memalloc "Pippin"
/dev/amjpegdec "Gollum"
/dev/camera-sysr "Faramir"
/dev/Vcodec "Barahir"
下载地址:h)p://-‐
已知漏洞 – mmap边界检查�
• 以/dev/Vcodec为例,mmap具有读写
权限且没有边界检查,导致用户态可
以任意地址读写内核数据.�
/mediatek/plaLorm/mt6582/kernel/drivers/videocodec/
已知漏洞 – mmap边界检查
• Root流程�
mmap�
•・ 调用存在问题的
mmap�
•・ 获取任意读写权
限�
kallsyms�
•・ 查找kallsyms的
格式字串"%pK"�
•・ 替换为正常
的"%p"�
•・ 查找setresuid�
setresuid�
•・ 修改setresuid逻
辑�
•・ 调用获取root�
已知漏洞 – 内存破坏�
• Qualcomm MSM代码存在多个内存破
坏漏洞�
– CVE-2013-2596
– CVE-2013-2597
– CVE-2013-4738
– CVE-2013-4739
– CVE-2013-6123
– …
已知漏洞 – 内存破坏
• CVE-‐2013-‐4738
栈上的四字节变量被覆盖为超长数据,
导致栈溢出。�
已知漏洞 – 内存破坏
• CVE-‐2013-‐6123
读写地址均可由用户态传⼊入数据指定,
导致任意地址写任意数据。�
Dumb Fuzzing�
• 相比iOS,构造更简洁�
• 三个API :�
1. ioctl (fd, cmd, arg)
2. copy_from_user(*to, *from, length)
3. copy_to_user(*to, *from, length)
�
Dumb Fuzzing�
int ioctl(int fd, int cmd, ...)
Dumb Fuzzing�
• copy_from_user() & copy_to_user()�
• ⽆无须处理page fault ☺ �
static inline unsigned long __must_check copy_from_user(void *to, const void __user *from,
unsigned long n)
{
if (access_ok(VERIFY_READ, from, n))
n = __copy_from_user(to, from, n);
return n;
}
static inline unsigned long __must_check copy_to_user(void __user *to, const void *from,
unsigned long n)
{
if (access_ok(VERIFY_WRITE, to, n))
n = __copy_to_user(to, from, n);
return n;
}
#define __copy_from_user(to,from,n) (memcpy(to, (void __force *)from, n), 0)
#define __copy_to_user(to,from,n) (memcpy((void __force *)to, from, n), 0)
Dumb Fuzzing�
• Dumb Fuzzer实现流程�
Crash,更多的Crash�
• Crash太多以⾄至于我们不知道该怎么办�
Dumb Fuzzing的问题�
• Android内核难以调试
– last_kmsg
• Crash过多,反⽽而影响测试效率
• ⼤大量Crash由Pointer
Dereference造成,可用性较低。
Dumb Fuzzing的问题�
“懒惰是科技发展的原动⼒力”�
为了减少⼈人⼯工分析成本:�
• 更精确的识别ioctl cmd�
• 尽可能的还原ioctl中arg的数据类型�
• 确定cmd和arg的对应关系�
HexRaysCodeXplorer�
• HexRaysCodeXplorer�
– 基于Hex-Rays SDK实现,其类型重建功
能可以依据代码中对于指针的引用情况自
动⽣生成对应的结构体类型.�
– 原作者开发此插件用于分析Win32/Gapz
Bootkit[RECon’13、 ZeroNights’13]
�
HexRaysCodeXplorer�
• 为恶意代码分析设计,有其不完善的地⽅方�
• 如何改进它的输出?�
• 利用Hex Rays输出的⼀一些特性,“模糊处理”�
• 继⽽而改进HexRaysCodeXplorer的输出�
Hex-Rays SDK�
• Hex-‐Rays SDK简要介绍
– 函数在反编译过程中,Hex-Rays内部维护
了⼀一个ctree结构,针对此结构的遍历和修
改提供了⼀一系列数据结构和API供插件开
发者使用。�
– ctree的每个节点是citem_t结构,该结构体
包含⼀一个ctype_t的字段,指示当前item的类
型。�
*(DWORD *)(a1 + 12) =
0xEFCDAB89;
Hex-Rays SDK�
Hex-Rays SDK�
• citem_t类型有80+种�
• 类型重建中可能用到的citem_t类型有
以下⼏几种:�
�
enum ctype_t
{
cot_asg = 2, ///< x = y
cot_add = 35, ///< x + y
cot_sub = 36, ///< x – y
cot_cast = 48, ///< (type)x
cot_ptr = 51, ///< *x, access size in 'ptrsize'
cot_call = 57, ///< x(...)
cot_idx = 58, ///< x[y]
cot_memref = 59, ///<
cot_memptr = 60, ///< x->m, access size in 'ptrsize'
};
HexRaysCodeXplorer的不⾜足�
• 存在的问题�
– 没有考虑变量依赖关系�
– 没有充分利用类型转换信息�
– 没有函数间的类型重建能⼒力�
改进⽅方案�
• 问题1:没有考虑变量依赖关系�
v6 = arg;
v8 = _copy_from_user((int)&v209, (void *)v6, 4);
if ( !v8 )
{
_xlog_printk((int)off_C002EFCC, (int)((char
*)off_C002EFCC - 728), v209, v79);
clkmux_sel(1u, v209, (int)off_C002EFD0, v80);
return v8;
}
改进⽅方案�
• 解决⽅方案:处理变量依赖�
1. 对待分析变量的所有赋值操作进⾏行处理�
2. 被赋值变量和待分析变量纳⼊入同⼀一个集
合�
3. 对该集合里所有变量同时进⾏行类型重建,
且所有结果映射到原待分析变量�
改进⽅方案
ISP_ioctl 变量依赖关系�
输⼊入参数�
改进⽅方案
• 问题2:没有充分利用类型转换信息�
• HexRaysCodeXplorer不会利用该表达式获取信息�
• 但根据该表达式可以分析出v4指向的缓冲区长度
⼤大于等于16,且(v4+16)对应⼀一个指针变量。�
v69 = (DWORD*)((char *)v4 + 16);
改进⽅方案
• 解决⽅方案:充分利用类型转换信息�
1. 处理cot_add、cot_sub,获取变量的长度重建信
息�
2. 处理cot_cast,获取变量的类型重建信息�
�case cot_add:
case cot_sub:
{
if (expr->y->op == cot_num)
{
= expr->op==cot_add ?
int32(expr->y->numval()) : 0-int32(expr->y->numval());
}
}
改进⽅方案�
• 问题3:⽆无函数间的类型重建能⼒力�
v4 = arg;
v154 = _copy_from_user((int)&v232, (void *)v4, 44);
if ( !v154 )
{
m4u_query_mva(v232, v233, v234, (int)&v235, v3);
//todo...
}
改进⽅方案�
• 解决⽅方案:函数间类型重建�1. cot_call可以看作变量依赖的特殊情况,即
待分析变量和⼦子函数参数的依赖关系�
2. 对于原函数中的每⼀一个cot_call,判断其参
数是否包含待分析变量,如果包含则建立
映射关系�
3. 对⼦子函数进⾏行分析,若为特定函数如
memcpy、memzero、copy_from_user,则特
殊处理,否则4�
4. 对⼦子函数进⾏行反编译,递归的进⾏行类型重
建操作�
改进⽅方案�
• cot_call处理�
– 从原函数分析获得的变量信息为实参�
– 对⼦子函数反编译得到的是形参�
– 如果实参为待分析变量,则对应的⼦子函数
形参为待分析变量(⼀一种较为特殊的变量
依赖关系)�
改进⽅方案�
• 特殊函数处理,以memcpy为例�
�
Field field = {0};
if (strcmp(s,"memcpy") == 0)
{
carg_t arg = arglist->at(2);
if ( == cot_num)
{
= 0;
= 1;
= int32(());
}
cfield->(field);
}
Q
&
A
谢谢!�