博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
从Android设备中提取内核和逆向分析
阅读量:4323 次
发布时间:2019-06-06

本文共 12917 字,大约阅读时间需要 43 分钟。

转载自http://blog.csdn.net/qq1084283172/article/details/57074695

一、手机设备环境

 

[cpp]   
 
  1. Model number: Nexus 5  
  2. OS Version: Android 4.4.4 KTU84P  
  3. Kernel Version: 3.4.0-gd59db4e  

 

二、Android内核提取

 

[cpp]   
 
  1. adb shell  
  2. su  
  3. cd /dev/block/platform/msm_sdcc.1/by-name  
  4. ls -l boot  

 

boot 是个系统符号软链接,/dev/block/mmcblk0p19 就是boot分区

 

用 dd 将其dump到Nexus 5手机的sdcard文件夹下:

 

[cpp]   
 
  1. dd if=/dev/block/mmcblk0p19 of=/sdcard/boot.img  

 

adb pull 将dump出来的boot.img文件导出到 /home/androidcode/AndroidDevlop/Nexus5Boot 文件夹下

 

[cpp]   
 
  1. adb pull /sdcard/boot.img /home/androidcode/AndroidDevlop/Nexus5Boot  

 

 

用 Binwalk 工具分析boot.img文件

1.Binwalk工具的详细使用说明:

2.Binwalk工具的github地址:

3.Binwalk工具的官方网址:

4.Binwalk工具的wiki使用说明的地址:

5.Binwalk工具作者收集的IDA插件和脚本:

6.Binwalk工具的安装说明:

 

安装Binwalk工具并分析boot.img文件

 

[cpp]   
 
  1. cd /home/androidcode/AndroidDevlop/Nexus5Boot/binwalk-master  
  2.   
  3. # 按照binwalk工具的说明安装binwalk  
  4. sudo python setup.py install  
  5.   
  6. # 分析boot.img文件  
  7. sudo binwalk ../boot.img >log  

 

分析的结果截图:

boot.img文件跳过2k的文件头之后,包括有两个gz包,一个是boot.img-kernel.gz即Linux内核,一个是boot.img-ramdisk.cpio.gz,

大概的组成结构如下图,详细的信息可以参考Android源码的 android/platform/system/core/master/mkbootimg/bootimg.h 文件,在线查看 booting.h 文件地址: 。

 

[cpp]   
 
  1. /* tools/mkbootimg/bootimg.h 
  2. ** 
  3. ** Copyright 2007, The Android Open Source Project 
  4. ** 
  5. ** Licensed under the Apache License, Version 2.0 (the "License");  
  6. ** you may not use this file except in compliance with the License.  
  7. ** You may obtain a copy of the License at  
  8. ** 
  9. **     http://www.apache.org/licenses/LICENSE-2.0  
  10. ** 
  11. ** Unless required by applicable law or agreed to in writing, software  
  12. ** distributed under the License is distributed on an "AS IS" BASIS,  
  13. ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  
  14. ** See the License for the specific language governing permissions and  
  15. ** limitations under the License. 
  16. */  
  17. #include <stdint.h>  
  18. #ifndef _BOOT_IMAGE_H_  
  19. #define _BOOT_IMAGE_H_  
  20. typedef struct boot_img_hdr boot_img_hdr;  
  21. #define BOOT_MAGIC "ANDROID!"  
  22. #define BOOT_MAGIC_SIZE 8  
  23. #define BOOT_NAME_SIZE 16  
  24. #define BOOT_ARGS_SIZE 512  
  25. #define BOOT_EXTRA_ARGS_SIZE 1024  
  26. struct boot_img_hdr  
  27. {  
  28.     uint8_t magic[BOOT_MAGIC_SIZE];  
  29.     uint32_t kernel_size;  /* size in bytes */  
  30.     uint32_t kernel_addr;  /* physical load addr */  
  31.     uint32_t ramdisk_size; /* size in bytes */  
  32.     uint32_t ramdisk_addr; /* physical load addr */  
  33.     uint32_t second_size;  /* size in bytes */  
  34.     uint32_t second_addr;  /* physical load addr */  
  35.     uint32_t tags_addr;    /* physical addr for kernel tags */  
  36.     uint32_t page_size;    /* flash page size we assume */  
  37.     uint32_t unused;       /* reserved for future expansion: MUST be 0 */  
  38.     /* operating system version and security patch level; for 
  39.      * version "A.B.C" and patch level "Y-M-D": 
  40.      * ver = A << 14 | B << 7 | C         (7 bits for each of A, B, C) 
  41.      * lvl = ((Y - 2000) & 127) << 4 | M  (7 bits for Y, 4 bits for M) 
  42.      * os_version = ver << 11 | lvl */  
  43.     uint32_t os_version;  
  44.     uint8_t name[BOOT_NAME_SIZE]; /* asciiz product name */  
  45.     uint8_t cmdline[BOOT_ARGS_SIZE];  
  46.     uint32_t id[8]; /* timestamp / checksum / sha1 / etc */  
  47.     /* Supplemental command line data; kept here to maintain 
  48.      * binary compatibility with older versions of mkbootimg */  
  49.     uint8_t extra_cmdline[BOOT_EXTRA_ARGS_SIZE];  
  50. } __attribute__((packed));  
  51. /* 
  52. ** +-----------------+  
  53. ** | boot header     | 1 page 
  54. ** +-----------------+ 
  55. ** | kernel          | n pages   
  56. ** +-----------------+ 
  57. ** | ramdisk         | m pages   
  58. ** +-----------------+ 
  59. ** | second stage    | o pages 
  60. ** +-----------------+ 
  61. ** 
  62. ** n = (kernel_size + page_size - 1) / page_size 
  63. ** m = (ramdisk_size + page_size - 1) / page_size 
  64. ** o = (second_size + page_size - 1) / page_size 
  65. ** 
  66. ** 0. all entities are page_size aligned in flash 
  67. ** 1. kernel and ramdisk are required (size != 0) 
  68. ** 2. second is optional (second_size == 0 -> no second) 
  69. ** 3. load each element (kernel, ramdisk, second) at 
  70. **    the specified physical address (kernel_addr, etc) 
  71. ** 4. prepare tags at tag_addr.  kernel_args[] is 
  72. **    appended to the kernel commandline in the tags. 
  73. ** 5. r0 = 0, r1 = MACHINE_TYPE, r2 = tags_addr 
  74. ** 6. if second_size != 0: jump to second_addr 
  75. **    else: jump to kernel_addr 
  76. */  
  77. #if 0  
  78. typedef struct ptentry ptentry;  
  79. struct ptentry {  
  80.     char name[16];      /* asciiz partition name    */  
  81.     unsigned start;     /* starting block number    */  
  82.     unsigned length;    /* length in blocks         */  
  83.     unsigned flags;     /* set to zero              */  
  84. };  
  85. /* MSM Partition Table ATAG 
  86. ** 
  87. ** length: 2 + 7 * n 
  88. ** atag:   0x4d534d70 
  89. **         <ptentry> x n 
  90. */  
  91. #endif  
  92. #endif  

有关boot.img文件的生成可以参考Android源码的 android/platform/system/core/master/mkbootimg/bootimg 文件,在线查看 booting文件地址:。

 

 

[cpp]   
 
  1. #!/usr/bin/env python  
  2. # Copyright 2015, The Android Open Source Project  
  3. #  
  4. # Licensed under the Apache License, Version 2.0 (the "License");  
  5. # you may not use this file except in compliance with the License.  
  6. # You may obtain a copy of the License at  
  7. #  
  8. #     http://www.apache.org/licenses/LICENSE-2.0  
  9. #  
  10. # Unless required by applicable law or agreed to in writing, software  
  11. # distributed under the License is distributed on an "AS IS" BASIS,  
  12. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  
  13. # See the License for the specific language governing permissions and  
  14. # limitations under the License.  
  15. from __future__ import print_function  
  16. from sys import argv, exit, stderr  
  17. from argparse import ArgumentParser, FileType, Action  
  18. from os import fstat  
  19. from struct import pack  
  20. from hashlib import sha1  
  21. import sys  
  22. import re  
  23. def filesize(f):  
  24.     if f is None:  
  25.         return 0  
  26.     try:  
  27.         return fstat(f.fileno()).st_size  
  28.     except OSError:  
  29.         return 0  
  30. def update_sha(sha, f):  
  31.     if f:  
  32.         sha.update(f.read())  
  33.         f.seek(0)  
  34.         sha.update(pack('I', filesize(f)))  
  35.     else:  
  36.         sha.update(pack('I', 0))  
  37. def pad_file(f, padding):  
  38.     pad = (padding - (f.tell() & (padding - 1))) & (padding - 1)  
  39.     f.write(pack(str(pad) + 'x'))  
  40. def write_header(args):  
  41.     BOOT_MAGIC = 'ANDROID!'.encode()  
  42.     args.output.write(pack('8s', BOOT_MAGIC))  
  43.     args.output.write(pack('10I',  
  44.         filesize(args.kernel),                          # size in bytes  
  45.         args.base + args.kernel_offset,                 # physical load addr  
  46.         filesize(args.ramdisk),                         # size in bytes  
  47.         args.base + args.ramdisk_offset,                # physical load addr  
  48.         filesize(args.second),                          # size in bytes  
  49.         args.base + args.second_offset,                 # physical load addr  
  50.         args.base + args.tags_offset,                   # physical addr for kernel tags  
  51.         args.pagesize,                                  # flash page size we assume  
  52.         0,                                              # future expansion: MUST be 0  
  53.         (args.os_version << 11) | args.os_patch_level)) # os version and patch level  
  54.     args.output.write(pack('16s', args.board.encode())) # asciiz product name  
  55.     args.output.write(pack('512s', args.cmdline[:512].encode()))  
  56.     sha = sha1()  
  57.     update_sha(sha, args.kernel)  
  58.     update_sha(sha, args.ramdisk)  
  59.     update_sha(sha, args.second)  
  60.     img_id = pack('32s', sha.digest())  
  61.     args.output.write(img_id)  
  62.     args.output.write(pack('1024s', args.cmdline[512:].encode()))  
  63.     pad_file(args.output, args.pagesize)  
  64.     return img_id  
  65. class ValidateStrLenAction(Action):  
  66.     def __init__(self, option_strings, dest, nargs=None, **kwargs):  
  67.         if 'maxlen' not in kwargs:  
  68.             raise ValueError('maxlen must be set')  
  69.         self.maxlen = int(kwargs['maxlen'])  
  70.         del kwargs['maxlen']  
  71.         super(ValidateStrLenAction, self).__init__(option_strings, dest, **kwargs)  
  72.     def __call__(self, parser, namespace, values, option_string=None):  
  73.         if len(values) > self.maxlen:  
  74.             raise ValueError('String argument too long: max {0:d}, got {1:d}'.  
  75.                 format(self.maxlen, len(values)))  
  76.         setattr(namespace, self.dest, values)  
  77. def write_padded_file(f_out, f_in, padding):  
  78.     if f_in is None:  
  79.         return  
  80.     f_out.write(f_in.read())  
  81.     pad_file(f_out, padding)  
  82. def parse_int(x):  
  83.     return int(x, 0)  
  84. def parse_os_version(x):  
  85.     match = re.search(r'^(\d{1,3})(?:\.(\d{1,3})(?:\.(\d{1,3}))?)?', x)  
  86.     if match:  
  87.         a = int(match.group(1))  
  88.         b = c = 0  
  89.         if match.lastindex >= 2:  
  90.             b = int(match.group(2))  
  91.         if match.lastindex == 3:  
  92.             c = int(match.group(3))  
  93.         # 7 bits allocated for each field  
  94.         assert a < 128  
  95.         assert b < 128  
  96.         assert c < 128  
  97.         return (a << 14) | (b << 7) | c  
  98.     return 0  
  99. def parse_os_patch_level(x):  
  100.     match = re.search(r'^(\d{4})-(\d{2})-(\d{2})', x)  
  101.     if match:  
  102.         y = int(match.group(1)) - 2000  
  103.         m = int(match.group(2))  
  104.         # 7 bits allocated for the year, 4 bits for the month  
  105.         assert y >= 0 and y < 128  
  106.         assert m > 0 and m <= 12  
  107.         return (y << 4) | m  
  108.     return 0  
  109. def parse_cmdline():  
  110.     parser = ArgumentParser()  
  111.     parser.add_argument('--kernel', help='path to the kernel', type=FileType('rb'),  
  112.                         required=True)  
  113.     parser.add_argument('--ramdisk', help='path to the ramdisk', type=FileType('rb'))  
  114.     parser.add_argument('--second', help='path to the 2nd bootloader', type=FileType('rb'))  
  115.     parser.add_argument('--cmdline', help='extra arguments to be passed on the '  
  116.                         'kernel command line', default='', action=ValidateStrLenAction, maxlen=1536)  
  117.     parser.add_argument('--base', help='base address', type=parse_int, default=0x10000000)  
  118.     parser.add_argument('--kernel_offset', help='kernel offset', type=parse_int, default=0x00008000)  
  119.     parser.add_argument('--ramdisk_offset', help='ramdisk offset', type=parse_int, default=0x01000000)  
  120.     parser.add_argument('--second_offset', help='2nd bootloader offset', type=parse_int,  
  121.                         default=0x00f00000)  
  122.     parser.add_argument('--os_version', help='operating system version', type=parse_os_version,  
  123.                         default=0)  
  124.     parser.add_argument('--os_patch_level', help='operating system patch level',  
  125.                         type=parse_os_patch_level, default=0)  
  126.     parser.add_argument('--tags_offset', help='tags offset', type=parse_int, default=0x00000100)  
  127.     parser.add_argument('--board', help='board name', default='', action=ValidateStrLenAction,  
  128.                         maxlen=16)  
  129.     parser.add_argument('--pagesize', help='page size', type=parse_int,  
  130.                         choices=[2**i for i in range(11,15)], default=2048)  
  131.     parser.add_argument('--id', help='print the image ID on standard output',  
  132.                         action='store_true')  
  133.     parser.add_argument('-o', '--output', help='output file name', type=FileType('wb'),  
  134.                         required=True)  
  135.     return parser.parse_args()  
  136. def write_data(args):  
  137.     write_padded_file(args.output, args.kernel, args.pagesize)  
  138.     write_padded_file(args.output, args.ramdisk, args.pagesize)  
  139.     write_padded_file(args.output, args.second, args.pagesize)  
  140. def main():  
  141.     args = parse_cmdline()  
  142.     img_id = write_header(args)  
  143.     write_data(args)  
  144.     if args.id:  
  145.         if isinstance(img_id, str):  
  146.             # Python 2's struct.pack returns a string, but py3 returns bytes.  
  147.             img_id = [ord(x) for x in img_id]  
  148.         print('0x' + ''.join('{:02x}'.format(c) for c in img_id))  
  149. if __name__ == '__main__':  
  150.     main()  

 

 

 

根据上面的信息,从boot.img中提取出压缩的内核文件:

 

[cpp]   
 
  1. cd ../  
  2.   
  3. dd if=boot.img of=kernel.gz bs=1 skip=20660  

 

 

由于Android的内核文件经过了gzip压缩,因此要拿到最终的Android内核文件还需要进行解压缩:

 

[cpp]   
 
  1. gzip -d kernel.gz  

 

 

 

补充说明:关于Android的内核文件的提取和解压方法很多,常用的工具也比较多,也可以使用下面的几个工具之一来进行boot.img文件的解包和gzip的解压缩操作。

 

[cpp]   
 
  1. bootimg.exe https://github.com/cofface/android_bootimg   
  2.   
  3. bootimg-tools   https://github.com/pbatard/bootimg-tools.git    
  4.   
  5. unpackbootimg   http://bbs.pediy.com/showthread.php?t=197334  
  6.   
  7. abootimg    https://github.com/ggrandou/abootimg  

 

 

解压后的Android内核文件kernel中不包含符号信息。所以还要从Android设备中提取符号信息,尽管 /proc/kallsyms 文件中存储了所有内核符号信息,但是从分析的结果来看,文件中存储的内存地址值都是0,这是为了防止内核地址泄露。在dump 镜像文件boot.img的Android设备上执行下面的命令,就会发现Android设备上的所有内核符号都被屏蔽隐藏了。

 

[cpp]   
 
  1. adb shell  
  2.   
  3. cat /proc/kallsyms  

 

 

为了要获取Android内核中所有的内核符号信息,可以通过在root权限下,修改Andriod设备中的/proc/sys/kernel/kptr_restrict的值来实现,去掉Android内核符号的信息屏蔽。

 

[cpp]   
 
  1. adb shell  
  2. su  
  3.   
  4. # 查看默认值  
  5. cat /proc/sys/kernel/kptr_restrict  
  6.   
  7. # 关闭内核符号屏蔽  
  8. echo 0 > /proc/sys/kernel/kptr_restrict   
  9.   
  10. # 查看修改后的值  
  11. cat /proc/sys/kernel/kptr_restrict  
  12.   
  13. cat /proc/kallsyms  

 

 

关闭Android设备的内核符号的屏蔽以后,再次执行 cat /proc/kallsyms ,发现被隐藏的内核符号信息都显示出来了。

 

在root权限下,将Android设备中的内核符号信息dump出来,导出到 /home/androidcode/AndroidDevlop/Nexus5Boot/syms.txt文件中。因此,Android内核文件的内核符号信息都保存在syms.txt文件中了。

 

[cpp]   
 
  1. # cat /proc/kallsyms > /sdcard/syms.txt  
  2.   
  3. # exit  
  4. $ exit  
  5.   
  6. $ adb pull /sdcard/syms.txt syms.txt  

 

 

三、IDA分析导出的Androd内核文件

将提取出来的Android内核 kernel文件 拖到IDA Pro 6.8中进行分析,设置处理器类型ARM Little-endian

 

 

在 ROM start addressLoading address 处填上0xc0008000,然后点击 OK 完成 。

 

*至于这里为什么要设置 ROM start address 和 Loading address的地址为 0xc0008000? 具体的可以参考 bootheader这个数据结构,在这里需要关注其中几个比较重要的值,这些值定义在boot/boardconfig.h中,不同的芯片对应vendor下不同的boardconfig,在这里我们的值分别是(分别是kernel/ramdis/tags 载入ram的物理地址):

 

[cpp]   
 
  1. #define PHYSICAL_DRAM_BASE   0x00200000   
  2. #define KERNEL_ADDR          (PHYSICAL_DRAM_BASE + 0x00008000)  
  3. #define RAMDISK_ADDR         (PHYSICAL_DRAM_BASE + 0x01000000)  
  4. #define TAGS_ADDR            (PHYSICAL_DRAM_BASE + 0x00000100)  
  5. #define NEWTAGS_ADDR         (PHYSICAL_DRAM_BASE + 0x00004000)  

上面这些值分中 KERNEL_ADDR 就是 ZTEXTADDR,RAMDISK_ADDR 就是 INITRD_PHYS,而 TAGS_ADDR 就是PARAMS_PHYS。bootloader会从boot.img的分区中将kernel和ramdisk分别读入RAM上面定义的内存地址中,然后就会跳到ZTEXTADDR开始执行。

 

详细的参考:

OK,现在就可以在IDA中查看和分析Android内核文件中的代码了,但是函数名称不是很友好,很多系统函数的名称都没有显示出来,只是显示成IDA中默认的普通函数名称。

 

前面我们已经将Androd内核文件中的内核符号信息都dump出来,这里大有用武之地。因此,向IDA中导入之前提取出来的内核符号信息就可以看到对应的函数名称了。需要用到下面的python脚本:

 

[cpp]   
 
  1. ksyms = open("C:\Users\Fly2016\Desktop\Binwalk工具\Nexus5_kernel\syms.txt")  
  2. for line in ksyms:  
  3.     addr = int(line[0:8],16)  
  4.     name = line[11:]  
  5.     idaapi.set_debug_name(addr,name)  
  6.     MakeNameEx(addr,name,SN_NOWARN)  
  7.     Message("%08X:%sn"%(addr,name))  

 

在IDA的 File->Script Command中运行上述python脚本,之后就可以在IDA中成功添加内核符号信息使IDA显示出正确的系统调用的函数名称来。

 

大功告成,现在可以愉快的分析Android内核的代码了:

 

总结:通过这种dump设备固件的方法,可以逆向分析没有源码的固件二进制文件,对于Android设备来说又可以通过这种方法修改Android的内核文件来进行反调试或者其他的目的。将修改好的Android内核文件使用boot.img等打包解包工具还原打包回到boot.img文件中,然后 fastboot flash boot boot.img  更新Android设备的内核文件即可达到目的。

 

 

学习链接

 <主要参考>

转载于:https://www.cnblogs.com/ocean1100/articles/8056786.html

你可能感兴趣的文章
Linux发送qq、网易邮件服务配置
查看>>
几道面试题
查看>>
【转】使用 WebGL 进行 3D 开发,第 1 部分: WebGL 简介
查看>>
js用正则表达式控制价格输入
查看>>
chromium浏览器开发系列第三篇:chromium源码目录结构
查看>>
java开发操作系统内核:由实模式进入保护模式之32位寻址
查看>>
第五讲:单例模式
查看>>
Python编程语言的起源
查看>>
Azure ARMTemplate模板,VM扩展命令
查看>>
(转)arguments.callee移除AS3匿名函数的侦听
查看>>
onNewIntent调用时机
查看>>
MYSQL GTID使用运维介绍(转)
查看>>
04代理,迭代器
查看>>
解决Nginx+PHP-FPM出现502(Bad Gateway)错误问题
查看>>
Java 虚拟机:互斥同步、锁优化及synchronized和volatile
查看>>
2.python的基本数据类型
查看>>
python学习笔记-day10-01-【 类的扩展: 重写父类,新式类与经典的区别】
查看>>
查看端口被占用情况
查看>>
浅谈css(块级元素、行级元素、盒子模型)
查看>>
Ubuntu菜鸟入门(五)—— 一些编程相关工具
查看>>