[EXPTECH]__Get symbols from Android kernel image
0x0 为什么需要从kernel image文件提取符号
在进行Android内核exp的时候,常常都需要用到内核符号和它们的地址。linux的/proc/kallsyms中可以找到这些符号的名字和地址,但是这些符号在目前的Android系统中都是被kptr_restrict机制block掉的。
代码位置kernel: lib/vsprintf.c
|
|
所以,现在读取kallsyms中的内容会发现所有的符号地址都是0,通过下面的命令可以关闭kptr_restrict。
然而,
这条命令需要root权限,很可能我们是没有root权限的。那么就尝试直接从kernel image中提取符号,并且在N以及N之前的内核中并没有开启KALSR(下个版本就KALSR了),因此,提取到的地址就是符号真实的地址。
0x01 手动定位
在学习的过程中发现了android手机内核提取及逆向分析,文章提供了一种手动搜索的符号地址的方法。我测试了几个符号的查找,发现并不是所有的符号字符串都可以在内核镜像文件中查找到。比如,在下图ida中加载的image文件的string窗口中(已经按string排序)可以看到,有sys_close,却没有sys_clone。查了才知道原来符号字符串是压缩存储的。
为了提取所有的符号,那么可以模拟kallsyms提取内核符号的过程将符号还原即可。
0x02 分析kallsyms
代码位置kernel: kernel/kallsyms.c
相关的关键函数是kallsyms_lookup_name,这个函数传入一个符号名,返回这个符号的地址
kalsyms_lookup_name查找符号的步骤是:
- 1.遍历所有的符号(kallsyms_num_syms即内核符号数量)
- 2.比较是否要查找的符号
- 3.是 返回kallsyms_addresses中对应的地址,否继续
- 4.如果遍历完也没找到,尝试查找LKM中的符号
从这个函数可以得出有一张存放内核符号地址的表kallsyms_addresses和一张存放内核符号名的表,这两张表的内容的顺序存在对应关系,也就是内核符号名表的第那n个符号对应内核符号地址表的第n个地址。kallsyms_expand_symbol就是用来解析内核符号名表获取符号名的。继续分析,
代码位置kernel: kernel/kallsyms.c
很明显kallsyms_expand_symbol就是用来还原字符串的,函数进行了多次索引。
过程是(以符号_text为例):
- 1.从kallsyms_names[off]读出第一个字节,值为n,这个字节代表符号名的分片个数(text被分为 __ ,t,ext共3片存储,最前面还存放一个type信息共4个部分,n为4)
- 2.kallsyms_names[off]之后的第n个字节的值对应该分片的索引在kallsyms_token_index中的偏移,在kallsyms_token_index中取出这个索引index[n]。
- 3.index[n]代表这个分片在kallsyms_token_table表中的偏移,这个表中存储的就是分片的实际内容了。以0作为分片的结尾
- 4.取出所有分片后拼接在一起,就是 symtype(1byte)+symstring(mbyte),取后n个byte就是符号名了
画个图好了
0x03 代码获取image中的符号
现在知道如何获取内核符号名以及内核符号地址了,剩下问题就在于如何从Image中定位四张表,在符号地址中有三个连续的符号的地址都是0xffffffc000081000,在kallsyms中可以看到这三个符号如下图
从image文件中定位,三个连续的ffffffc000081000,即可找到kallsyms_addresses表,从这个位置向前查找到第一个不为0的位置,就是kallsyms_addresses表的首地址。
|
|
kallsyms_addresses表结束后,有两个字节存放kallsyms_num_syms,然后依次是kallsym_names ,markers ,kallsym_token_index ,kallsym_token_tables ,各个部分之间通过长度不定的00分割,因此找到一张表的末尾之后,跳过0就是下一张表的首地址。
注:markers这张表用于probe,本文获取内核符号时并不需要用到
完整code:边测边写,丑到无法直视