This article blog link: http://blog.csdn.net/qq1084283172/article/details/57074695
I. Mobile Equipment Environment
Model number: Nexus 5 OS Version: Android 4.4.4 KTU84P Kernel Version: 3.4.0-gd59db4e
2. Android Kernel Extraction
adb shell su cd /dev/block/platform/msm_sdcc.1/by-name ls -l boot
Boot is a system symbol soft link, / dev/block/mmcblk0p19 is boot partition
Use dd to dump it into the sdcard folder of the Nexus 5 phone:
dd if=/dev/block/mmcblk0p19 of=/sdcard/boot.img
adb pull /sdcard/boot.img /home/androidcode/AndroidDevlop/Nexus5Boot
Analysis of boot.img file with Binwall tool
1. Detailed instructions for the use of Binwall tools: Binwalk: Backdoor (Firmware) Analysis Tool
2. The github address of the Binwall tool: https://github.com/devttys0/binwalk
3. Binwall Tool's official website: http://binwalk.org/
4. The address of the wiki instructions for the Binwall tool: https://github.com/devttys0/binwalk/wiki
5. IDA plug-ins and scripts collected by Binwall tool authors: https://github.com/devttys0/ida
6. Installation instructions for Binwall tools: https://github.com/devttys0/binwalk/blob/master/INSTALL.md
Install the Binwall tool and analyze the boot.img file
cd /home/androidcode/AndroidDevlop/Nexus5Boot/binwalk-master # Install binwalk as described in the binwalk tool sudo python setup.py install # Analysis of boot.img file sudo binwalk ../boot.img >log
After skipping the 2k header, the boot.img file includes two GZ packages: boot.img-kernel.gz, the Linux kernel, and boot.img-ramdisk.cpio.gz.
The general structure is as follows. For detailed information, you can refer to the android/platform/system/core/master/mkbootimg/bootimg.h file of Android source code to view the address of the booting.h file online. https://android.googlesource.com/platform/system/core/+/master/mkbootimg/bootimg.h
.
/* tools/mkbootimg/bootimg.h ** ** Copyright 2007, The Android Open Source Project ** ** Licensed under the Apache License, Version 2.0 (the "License"); ** you may not use this file except in compliance with the License. ** You may obtain a copy of the License at ** ** http://www.apache.org/licenses/LICENSE-2.0 ** ** Unless required by applicable law or agreed to in writing, software ** distributed under the License is distributed on an "AS IS" BASIS, ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ** See the License for the specific language governing permissions and ** limitations under the License. */ #include <stdint.h> #ifndef _BOOT_IMAGE_H_ #define _BOOT_IMAGE_H_ typedef struct boot_img_hdr boot_img_hdr; #define BOOT_MAGIC "ANDROID!" #define BOOT_MAGIC_SIZE 8 #define BOOT_NAME_SIZE 16 #define BOOT_ARGS_SIZE 512 #define BOOT_EXTRA_ARGS_SIZE 1024 struct boot_img_hdr { uint8_t magic[BOOT_MAGIC_SIZE]; uint32_t kernel_size; /* size in bytes */ uint32_t kernel_addr; /* physical load addr */ uint32_t ramdisk_size; /* size in bytes */ uint32_t ramdisk_addr; /* physical load addr */ uint32_t second_size; /* size in bytes */ uint32_t second_addr; /* physical load addr */ uint32_t tags_addr; /* physical addr for kernel tags */ uint32_t page_size; /* flash page size we assume */ uint32_t unused; /* reserved for future expansion: MUST be 0 */ /* operating system version and security patch level; for * version "A.B.C" and patch level "Y-M-D": * ver = A << 14 | B << 7 | C (7 bits for each of A, B, C) * lvl = ((Y - 2000) & 127) << 4 | M (7 bits for Y, 4 bits for M) * os_version = ver << 11 | lvl */ uint32_t os_version; uint8_t name[BOOT_NAME_SIZE]; /* asciiz product name */ uint8_t cmdline[BOOT_ARGS_SIZE]; uint32_t id[8]; /* timestamp / checksum / sha1 / etc */ /* Supplemental command line data; kept here to maintain * binary compatibility with older versions of mkbootimg */ uint8_t extra_cmdline[BOOT_EXTRA_ARGS_SIZE]; } __attribute__((packed)); /* ** +-----------------+ ** | boot header | 1 page ** +-----------------+ ** | kernel | n pages ** +-----------------+ ** | ramdisk | m pages ** +-----------------+ ** | second stage | o pages ** +-----------------+ ** ** n = (kernel_size + page_size - 1) / page_size ** m = (ramdisk_size + page_size - 1) / page_size ** o = (second_size + page_size - 1) / page_size ** ** 0. all entities are page_size aligned in flash ** 1. kernel and ramdisk are required (size != 0) ** 2. second is optional (second_size == 0 -> no second) ** 3. load each element (kernel, ramdisk, second) at ** the specified physical address (kernel_addr, etc) ** 4. prepare tags at tag_addr. kernel_args[] is ** appended to the kernel commandline in the tags. ** 5. r0 = 0, r1 = MACHINE_TYPE, r2 = tags_addr ** 6. if second_size != 0: jump to second_addr ** else: jump to kernel_addr */ #if 0 typedef struct ptentry ptentry; struct ptentry { char name[16]; /* asciiz partition name */ unsigned start; /* starting block number */ unsigned length; /* length in blocks */ unsigned flags; /* set to zero */ }; /* MSM Partition Table ATAG ** ** length: 2 + 7 * n ** atag: 0x4d534d70 ** <ptentry> x n */ #endif #endif
For the generation of boot.img file, you can refer to Android/platform/system/core/master/mkbootimg/boot img file of Android source code to view the address of booting file online: https://android.googlesource.com/platform/system/core/+/master/mkbootimg/mkbootimg.
#!/usr/bin/env python # Copyright 2015, The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. from __future__ import print_function from sys import argv, exit, stderr from argparse import ArgumentParser, FileType, Action from os import fstat from struct import pack from hashlib import sha1 import sys import re def filesize(f): if f is None: return 0 try: return fstat(f.fileno()).st_size except OSError: return 0 def update_sha(sha, f): if f: sha.update(f.read()) f.seek(0) sha.update(pack('I', filesize(f))) else: sha.update(pack('I', 0)) def pad_file(f, padding): pad = (padding - (f.tell() & (padding - 1))) & (padding - 1) f.write(pack(str(pad) + 'x')) def write_header(args): BOOT_MAGIC = 'ANDROID!'.encode() args.output.write(pack('8s', BOOT_MAGIC)) args.output.write(pack('10I', filesize(args.kernel), # size in bytes args.base + args.kernel_offset, # physical load addr filesize(args.ramdisk), # size in bytes args.base + args.ramdisk_offset, # physical load addr filesize(args.second), # size in bytes args.base + args.second_offset, # physical load addr args.base + args.tags_offset, # physical addr for kernel tags args.pagesize, # flash page size we assume 0, # future expansion: MUST be 0 (args.os_version << 11) | args.os_patch_level)) # os version and patch level args.output.write(pack('16s', args.board.encode())) # asciiz product name args.output.write(pack('512s', args.cmdline[:512].encode())) sha = sha1() update_sha(sha, args.kernel) update_sha(sha, args.ramdisk) update_sha(sha, args.second) img_id = pack('32s', sha.digest()) args.output.write(img_id) args.output.write(pack('1024s', args.cmdline[512:].encode())) pad_file(args.output, args.pagesize) return img_id class ValidateStrLenAction(Action): def __init__(self, option_strings, dest, nargs=None, **kwargs): if 'maxlen' not in kwargs: raise ValueError('maxlen must be set') self.maxlen = int(kwargs['maxlen']) del kwargs['maxlen'] super(ValidateStrLenAction, self).__init__(option_strings, dest, **kwargs) def __call__(self, parser, namespace, values, option_string=None): if len(values) > self.maxlen: raise ValueError('String argument too long: max {0:d}, got {1:d}'. format(self.maxlen, len(values))) setattr(namespace, self.dest, values) def write_padded_file(f_out, f_in, padding): if f_in is None: return f_out.write(f_in.read()) pad_file(f_out, padding) def parse_int(x): return int(x, 0) def parse_os_version(x): match = re.search(r'^(\d{1,3})(?:\.(\d{1,3})(?:\.(\d{1,3}))?)?', x) if match: a = int(match.group(1)) b = c = 0 if match.lastindex >= 2: b = int(match.group(2)) if match.lastindex == 3: c = int(match.group(3)) # 7 bits allocated for each field assert a < 128 assert b < 128 assert c < 128 return (a << 14) | (b << 7) | c return 0 def parse_os_patch_level(x): match = re.search(r'^(\d{4})-(\d{2})-(\d{2})', x) if match: y = int(match.group(1)) - 2000 m = int(match.group(2)) # 7 bits allocated for the year, 4 bits for the month assert y >= 0 and y < 128 assert m > 0 and m <= 12 return (y << 4) | m return 0 def parse_cmdline(): parser = ArgumentParser() parser.add_argument('--kernel', help='path to the kernel', type=FileType('rb'), required=True) parser.add_argument('--ramdisk', help='path to the ramdisk', type=FileType('rb')) parser.add_argument('--second', help='path to the 2nd bootloader', type=FileType('rb')) parser.add_argument('--cmdline', help='extra arguments to be passed on the ' 'kernel command line', default='', action=ValidateStrLenAction, maxlen=1536) parser.add_argument('--base', help='base address', type=parse_int, default=0x10000000) parser.add_argument('--kernel_offset', help='kernel offset', type=parse_int, default=0x00008000) parser.add_argument('--ramdisk_offset', help='ramdisk offset', type=parse_int, default=0x01000000) parser.add_argument('--second_offset', help='2nd bootloader offset', type=parse_int, default=0x00f00000) parser.add_argument('--os_version', help='operating system version', type=parse_os_version, default=0) parser.add_argument('--os_patch_level', help='operating system patch level', type=parse_os_patch_level, default=0) parser.add_argument('--tags_offset', help='tags offset', type=parse_int, default=0x00000100) parser.add_argument('--board', help='board name', default='', action=ValidateStrLenAction, maxlen=16) parser.add_argument('--pagesize', help='page size', type=parse_int, choices=[2**i for i in range(11,15)], default=2048) parser.add_argument('--id', help='print the image ID on standard output', action='store_true') parser.add_argument('-o', '--output', help='output file name', type=FileType('wb'), required=True) return parser.parse_args() def write_data(args): write_padded_file(args.output, args.kernel, args.pagesize) write_padded_file(args.output, args.ramdisk, args.pagesize) write_padded_file(args.output, args.second, args.pagesize) def main(): args = parse_cmdline() img_id = write_header(args) write_data(args) if args.id: if isinstance(img_id, str): # Python 2's struct.pack returns a string, but py3 returns bytes. img_id = [ord(x) for x in img_id] print('0x' + ''.join('{:02x}'.format(c) for c in img_id)) if __name__ == '__main__': main()
Based on the above information, extract the compressed kernel file from boot.img:
cd ../ dd if=boot.img of=kernel.gz bs=1 skip=20660
Because Android's kernel file has been compressed by gzip, it needs to be decompressed to get the final Android kernel file:
gzip -d kernel.gz
Supplementary Note: There are many ways to extract and decompress Android's kernel files, and there are many tools in common use. One of the following tools can also be used for unpacking boot.img files and gzip decompression operations.
bootimg.exe https://github.com/cofface/android_bootimg bootimg-tools https://github.com/pbatard/bootimg-tools.git unpackbootimg http://bbs.pediy.com/showthread.php?t=197334 abootimg https://github.com/ggrandou/abootimg
The decompressed Android kernel file kernel does not contain symbolic information. So we also need to extract symbol information from Android devices. Although all the kernel symbol information is stored in the / proc/kallsyms file, from the analysis results, the memory address value stored in the file is 0, which is to prevent the leakage of the kernel address. If you execute the following command on the Android device of the dump image file boot.img, you will find that all the kernel symbols on the Android device are masked and hidden.
adb shell cat /proc/kallsyms
To get all the kernel symbol information in the Android kernel, you can modify / proc/sys/kernel/kptr_restrict in Android devices under root permission
The value of Android is implemented to remove the information shielding of Android kernel symbols.
adb shell su # View default values cat /proc/sys/kernel/kptr_restrict # Turn off Kernel Symbol Shielding echo 0 > /proc/sys/kernel/kptr_restrict # View the modified values cat /proc/sys/kernel/kptr_restrict cat /proc/kallsyms
Under root permission, dump the kernel symbol information in Android device and export it to / home / Android code / Android Devlop / Nexus 5Boot / syms.txt file. Therefore, the kernel symbol information of the Android kernel file is stored in the syms.txt file.
# cat /proc/kallsyms > /sdcard/syms.txt # exit $ exit $ adb pull /sdcard/syms.txt syms.txt
III. IDA Analysis of Derived Androd Kernel Files
Drag the extracted Android kernel file to IDA Pro 6.8 for analysis, and set the processor type to ARM Little-endian.
Fill in 0xc0008000 at ROM start address and Loading address and click
Done.
OK, you can now view and analyze the code in the Android kernel file in IDA, but the function name is not very friendly, many system function names are not displayed, just as the default common function name in IDA.
We've dump out all the kernel symbols in Androd's kernel file before, and it's very useful here. Therefore, you can see the corresponding function name by importing the kernel symbol information extracted before you import it into IDA. The following python script is required:
ksyms = open("C:\Users\Fly2016\Desktop\Binwalk tool\Nexus5_kernel\syms.txt") for line in ksyms: addr = int(line[0:8],16) name = line[11:] idaapi.set_debug_name(addr,name) MakeNameEx(addr,name,SN_NOWARN) Message("%08X:%sn"%(addr,name))
After running the python script in File - > Script Command of IDA, the kernel symbol information can be successfully added to IDA to display the correct function name of the system call.
With great success, you can now happily analyze the Android kernel code:
Summary: Through this dump device firmware method, firmware binary files without source code can be retrospectively analyzed. For Android devices, this method can be used to modify Android kernel files for debugging or other purposes. The modified Android kernel file is restored and packaged back into the boot.img file using the package unpacking tools such as boot.img, and then fastboot flash boot boot.img Update the Android device's kernel files to achieve this goal.
Learning Links:
Extracting Kernel from Android Mobile Phone <Major References>
Core Extraction and Reverse Analysis of android Mobile Phone Behind root Technology
Reverse modification of mobile phone kernel to bypass reverse debugging
Method of Extracting Android Kernel
Understanding boot.img and static analysis of Android/linux kernels