CVE-2015-1805 iovyroot find absolute kernel addresses
CVE-2015-1805 iovyroot 查找绝对内核地址
1. 提取zImage
1.1 下载rom,找到boot.img
1.2. 使用unpackbootimg或者其他工具解压boot.img
unpackbootimg -i boot.img -o out
1.3. out目录下文件如下:
drwxrwxr-x 2 android android 4096 1月 6 17:45 .
drwxrwxr-x 4 android android 4096 1月 6 17:45 ..
-rw-rw-r-- 1 android android 9 1月 6 17:45 boot.img-base
-rw-rw-r-- 1 android android 99 1月 6 17:45 boot.img-cmdline
-rw-rw-r-- 1 android android 0 1月 6 17:45 boot.img-dt
-rw-rw-r-- 1 android android 5 1月 6 17:45 boot.img-pagesize
-rw-rw-r-- 1 android android 1969875 1月 6 17:45 boot.img-ramdisk.gz
-rw-rw-r-- 1 android android 9 1月 6 17:45 boot.img-ramdisk_offset
-rw-rw-r-- 1 android android 0 1月 6 17:45 boot.img-second
-rw-rw-r-- 1 android android 9 1月 6 17:45 boot.img-second_offset
-rw-rw-r-- 1 android android 9 1月 6 17:45 boot.img-tags_offset
-rw-rw-r-- 1 android android 6097624 1月 6 17:45 boot.img-zImage
1.4. binwalk boot.img-zImage -e提取被gzip压缩的zImage
DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
15851 0x3DEB gzip compressed data, maximum compression, from Unix, NULL date (1970-01-01 00:00:00)
binwalk会自动生成一个_boot.img-zImage.extracted目录,里面有个文件名为3DEB的文件,文件名为data的十六进制起始地址
2. 32位rom获取符号的内核地址
查看iovyroot源码中会知道有个offset结构体,可以看到32位的设备需要获取5个符号的地址
struct offsets {
char* devname; //ro.product.model
char* kernelver; // /proc/version
union {
void* fsync; //ptmx_fops -> fsync
void* check_flags; //ptmx_fops -> check_flags
};
#if (__LP64__)
void* joploc; //gadget location, see getroot.c
void* jopret; //return to setfl after check_flags() (fcntl.c), usually inlined in sys_fcntl
#endif
void* sidtab; //optional, for selinux contenxt
void* policydb; //optional, for selinux context
void* selinux_enabled;
void* selinux_enforcing;
};
分别为ptmx_fops, sidtab, policydb, selinux_enabled, selinux_enforcing
2.1 使用kallsymsprint可以获取以上5个符号的地址
kallsymsprint.x86是获取32位zImage的工具
kallsymsprint.x64是获取64位zImage的工具
$kallsymsprint.x86 3DEB | grep -E "ptmx_fops|sidtab|policydb|selinux_enabled|selinux_enforcing"
[+]mmap
mem=f67c0000 length=00d8c18c offset=c9848000
[+]kallsyms_addresses=c08e6030
count=000135d4
[+]kallsyms_num_syms=000135d4
[+]kallsyms_names=c0933790
[+]kallsyms_markers=c0a1f6b0
[+]kallsyms_token_table=c0a1fb90
[+]kallsyms_token_index=c0a1ff30
[+]kallsyms_lookup_name
...
...
...
//这个地址是小米3的地址,我手里的测试机是小米2s,而小米2s的zImage使用kallsymsprint找不到这几个符号和地址
c0d2a184 selinux_enabled
c0fe9640 selinux_enforcing
c0feb0a0 policydb
c0feb1a4 sidtab
c0fefdd0 ptmx_fops
...
...
2.2 添加offset
把2.1获取的5个符号的地址和model,linux version按格式添加在offsets.c的offsets数组中即可。
在iovyroot根目录运行ndk-build编译即可。
2.3 使用kallsymsprint无法获取到5个符号的地址
上面我们已经讲到有些zImage使用kallsymsprint没法获取我们需要的符号地址,比如我的小米2s,那么怎么办呢?
别着急,祭出我们的大杀器IDA神器+源码引用(小米内核源码的地址见附录)
2.4 IDA加载32位的zImage
Load a New file使用32位的IDA加载zImage时,Processer Type选择 ARM,勾选Manual load, 点击Ok
在ROM start address和Loading address填写0xc0008000,这个是内核.text段的起始地址,通常都为0xc0008000,点击OK等待IDA加载完成。
如果你不确定是不是这个地址, 你可以使用如下命令直接从手机中获取
adb reboot; adb wait-for-device; adb shell dmesg
往上查看dmesg的输出信息可以找到
.text address2.5 寻找 ptmx_fops 地址
在小米内核源码中搜索,直接在github中搜索的ptmx_fops
ptmx_fops
由上图可以看到ptmx_fops仅在pty.c文件中被引用了两次,随便选一处引用
tty_default_fops(&ptmx_fops);
再次搜索tty_default_fops发现就只有一处调用该函数,那就用这个tty_default_fops函数去找到ptmx_fops吧
$kallsymsprint.x86 3DEB | grep -E "tty_default_fops"
[+]mmap
mem=f6585000 length=00fd6d84 offset=c9a83000
[+]kallsyms_addresses=c09c7dc0
count=00010242
[+]kallsyms_num_syms=00010242
[+]kallsyms_names=c0a086e0
[+]kallsyms_markers=c0ac5ef0
[+]kallsyms_token_table=c0ac6300
[+]kallsyms_token_index=c0ac6690
[+]kallsyms_lookup_name
c03fb21c tty_default_fops
//如果使用kallsymsprint也没有找到tty_default_fops的地址,那么就选择其他引用ptmx_fops的函数或全局变量符号
IDA跳转到地址c03fb21c(按g,输入地址,即可跳转到指定地址),把c03fb21c重命名为tty_default_fops(按n, 输入名称,即可修改别名)
IDA查找tty_default_fops的引用(按x,可以找到所有引用该函数的地址),由于从源码知道只有一次引用该函数,就直接跳转到应用tty_default_fops的地方
//根据tty_default_fops源码知道它只有一个参数,有如下汇编代码:
ROM:C0D1F41C STMFD SP!, {R0-R2,R4-R11,LR}
ROM:C0D1F420 MOV R0, #0x100000
ROM:C0D1F424 MOV R1, #0
ROM:C0D1F428 LDR R5, =0xC12D5290
ROM:C0D1F42C BL sub_C03F7CD0
//...此处省略N多代码
ROM:C0D1F588 ADD R0, R5, #8
ROM:C0D1F58C BL tty_default_fops
ADD R0, R5, #8 的意思是R0=R5+8
因为
LDR R5, =0xC12D5290
可以得到R0 = 0xC12D5290 + 8 = 0xC12D5298
那么就找到了ptmx_fops的地址为0xC12D5298
2.6 寻找 sidtab 地址
源码中搜索sidtab,找到很多处引用,发现在services.c 中有个静态的结构体变量,
sidtab
进入services.c中,找引用sidtab的地方,找啊找啊找朋友~~~,找到一个好朋友
什么是好朋友呢,就是参数越少的函数,引用越少的函数,这种函数方便分析,
sidtab_shutdown$kallsymsprint.x86 3DEB | grep -E "sidtab_shutdown"
[+]mmap
mem=f656f000 length=00fd6d84 offset=c9a99000
[+]kallsyms_addresses=c09c7dc0
count=00010242
[+]kallsyms_num_syms=00010242
[+]kallsyms_names=c0a086e0
[+]kallsyms_markers=c0ac5ef0
[+]kallsyms_token_table=c0ac6300
[+]kallsyms_token_index=c0ac6690
[+]kallsyms_lookup_name
c0345b64 sidtab_shutdown
//IDA中找到如下汇编:
ROM:C034DE54 LDR R4, =0xC12BC420
ROM:C034DE58 ADD R0, R4, #0x110
ROM:C034DE5C BL sidtab_shutdown
//可以得到R0 = R4 + 0x110 = 0xC12BC420 + 0x110 = 0xC12BC530
sidtab的地址为0xC12BC530
2.7 寻找 policydb 地址
policydb policydb_read$ kallsymsprint.x86 3DEB | grep -E "policydb_read"
[+]mmap
mem=f65d9000 length=00fd6d84 offset=c9a2f000
[+]kallsyms_addresses=c09c7dc0
count=00010242
[+]kallsyms_num_syms=00010242
[+]kallsyms_names=c0a086e0
[+]kallsyms_markers=c0ac5ef0
[+]kallsyms_token_table=c0ac6300
[+]kallsyms_token_index=c0ac6690
[+]kallsyms_lookup_name
c0348f4c policydb_read
ROM:C034DC58 STMFD SP!, {R4-R7,LR}
ROM:C034DC5C MOV R3, #0
ROM:C034DC60 LDR R4, =0xC12BC420
//...此处省略一点代码
ROM:C034DC88 MOV R0, R4
ROM:C034DC8C ADD R1, SP, #0x260+var_250
ROM:C034DC90 BL policydb_read
由policydb_read(&policydb, fp);得到R0为policydb
policydb地址0xC12BC420
2.8 寻找 selinux_enabled 地址
selinux_enabled$ kallsymsprint.x86 3DEB | grep -E "selinux_is_enabled"
[+]mmap
mem=f6606000 length=00fd6d84 offset=c9a02000
[+]kallsyms_addresses=c09c7dc0
count=00010242
[+]kallsyms_num_syms=00010242
[+]kallsyms_names=c0a086e0
[+]kallsyms_markers=c0ac5ef0
[+]kallsyms_token_table=c0ac6300
[+]kallsyms_token_index=c0ac6690
[+]kallsyms_lookup_name
c03445b8 selinux_is_enabled
c0bbfa24 __ksymtab_selinux_is_enabled
c0bc711c __kcrctab_selinux_is_enabled
c0bd1a63 __kstrtab_selinux_is_enabled
ROM:C03445B8 selinux_is_enabled
ROM:C03445B8 LDR R3, =dword_C0F5332C
ROM:C03445BC LDR R0, [R3]
ROM:C03445C0 MOVS R0, R0
ROM:C03445C4 MOVNE R0, #1
ROM:C03445C8 BX LR
ROM:C03445C8 ; End of function selinux_is_enabled
由selinux_is_enabled得到selinux_enabled的地址0xC0F5332C
selinux_enabled地址0xC0F5332C
2.9 寻找 selinux_enforcing 地址
selinux_enforcing selnl_notify_setenforce$ kallsymsprint.x86 3DEB | grep -E "selnl_notify_setenforce"
[+]mmap
mem=f659c000 length=00fd6d84 offset=c9a6c000
[+]kallsyms_addresses=c09c7dc0
count=00010242
[+]kallsyms_num_syms=00010242
[+]kallsyms_names=c0a086e0
[+]kallsyms_markers=c0ac5ef0
[+]kallsyms_token_table=c0ac6300
[+]kallsyms_token_index=c0ac6690
[+]kallsyms_lookup_name
c0343b60 selnl_notify_setenforce
ROM:C0342DB8 BNE loc_C0342E68
ROM:C0342DBC LDR R7, =0xC12BA9D0
//...此处省略N多代码
ROM:C0342E40 loc_C0342E40 ; CODE XREF: ROM:C0342E34�j
ROM:C0342E40 LDR R0, [R7]
ROM:C0342E44 BL selnl_notify_setenforce
根据汇编代码R0=R7=0xC12BA9D0,得到selinux_enforcing的地址0xC12BA9D0
selinux_enforcing地址0xC12BA9D0
2.10 offset
//Xiaomi MI 2, MIUI 7.2.4.0 Android 5.0.2 LRX22G
{ "MI 2", "Linux version 3.4.0-perf-g9b728b6-00625-ge66671e (builder@qh-miui-ota-bd53) (gcc version 4.8 (GCC) ) #1 SMP PREEMPT Mon Mar 7 20:05:25 CST 2016",
{ (void*)FSYNC_OFFSET(0xC12D5298) },
(void*)0xC12BC530, (void*)0xC12BC420, (void*)0xC0F5332C, (void*)0xC12BA9D0 },
2.11 演示结果
aries_iovyroot3. 64位rom获取符号的内核地址(待续)
A.附录:
iovyroot: https://github.com/dosomder/iovyroot
unpackbootimg: https://github.com/CyanogenMod/android_system_core 文件目录mkbootimg/unpackbootimg,需要自己编译
解压boot.img的其他工具: https://github.com/jpacg/bootimg
kallsymsprint: https://github.com/fi01/kallsymsprint
小米内核源码: https://github.com/MiCode/Xiaomi_Kernel_OpenSource
B.参考链接
https://github.com/dosomder/iovyroot/issues/1
http://h3ysatan.blogspot.sg/2016/04/iovyroot-cve-2015-1805.html
网友评论