本文的博客链接:http://blog.csdn.net/qq1084283172/article/details/55657300
一、Android内核源码的编译环境
系统环境:Ubuntu 14.04 x64bit
Android系统版本:Android 4.4.4 r1
Android内核版本:android-msm-hammerhead-3.4-kitkat-mr1
手机设备:Nexus 5
研究Android系统调用的目的:
Android的内核是逆向工程师的好伙伴。虽然常规的Android应用被限制和沙盒化,逆向工程师可以按自己希望自定义和改变操作系统和内核中行为。这给了你不可多得的优势,因为大部分完整性校验和防篡改功能都依赖内核的服务。部署这种可以滥用信任并自我欺骗的内核和环境,可以避免走很多歪路。
Android应用有几种方式和系统环境交互。标准方法是通过安卓应用框架的API函数。然而在底层,很多重要的函数,例如分配内存和访问文件,都是被转化为Linux的系统调用。在ARM linux中,系统调用的调用是通过SVC指令触发软件中断实现的。中断调用内核函数vector_swi(),用系统调用号作为一个函数指针表的偏移(如安卓上的sys_call_table)。
拦截系统调用最直接的方法是注入你的代码到内核内存中,覆盖系统调用表中的原始函数地址重定向执行。不幸的是,目前Android内核加强了内存限制阻止了这种操作。具体来说,内核是在启用CONFIG_STRICT_MEMORY_RWX选项的情况下构建的。这阻止了向只读内核内存区写入,意味着任何试图修改系统代码或者系统调用表的操作都将导致崩溃和重启。绕过这个的一个方法就是自己编译内核:能够禁用这种保护,做更多自定义的修改有利于逆向分析。如果你按常规方法逆向Android应用,构建你自己的逆向沙盒是不明智的。
来源:http://bobao.360.cn/learning/detail/3432.html
获取Nexus 5手机Android 4.4.4 r1的内核源码android-msm-hammerhead-3.4-kitkat-mr1,需要克隆“msm”仓库并检出一个分支"android-msm-hammerhead-3.4-kitkat-mr1"。待到Android内核源码下载完成,执行 make hammerhead_defconfig (或者xxxx_defconfig 取决于你的设备)命令,创建默认内核编译的配置文件.config。在下载Android的Nexus系列手机的内核源码的时候,选择清华的下载源 git clone https://aosp.tuna.tsinghua.edu.cn/kernel/msm.git 会快一点。如果从谷歌官方的地址下载Android内核源码需要FQ而且速度也不是很快(不过还是推荐从谷歌官方下载)。
$ cd /home/androidcode/AndroidDevlop/Android4.4.4r1Kernel
$ git clone https://aosp.tuna.tsinghua.edu.cn/kernel/msm.git (清华的源)
$ git clone https://android.googlesource.com/kernel/msm.git (或者谷歌官方的源需要FQ)
$ cd msm
$ git checkout origin/android-msm-hammerhead-3.4-kitkat-mr1
$ export ARCH=arm
$ export SUBARCH=arm
$ make hammerhead_defconfig
$ gedit .config
执行结果截图:
为了实现Android系统调用挂钩功能,增加Android内核对可加载内核模块的支持,/dev/kmem接口可读写支持以及导出全局内核符号表的支持并且不要忘了禁用Android内核的内存保护。这些选项的值在配置文件中已经存在,只需要 gedit .config 打开Android内核的编译配置文件.config,简单的修改和增加一下下面的编译选项参数的值即可,然后保存关闭.config文件。
# 支持内核模块的加载(需要添加)
CONFIG_MODULES=y
# 支持内核模块的卸载(需要添加)
CONFIG_MODULE_UNLOAD=y
# 去掉内核内存只读的保护(需要修改)
CONFIG_STRICT_MEMORY_RWX=n
# 对/dev/kmem接口的支持(默认)
CONFIG_DEVMEM=y
CONFIG_DEVKMEM=y
# 内核中启用kallsyms功能(默认)
# 符号表中包含所有的函数
CONFIG_KALLSYMS=y
# 在kallsyms中包含全部符号信息(默认)
# 符号表中包括所有的变量(包括没有用EXPORT_SYMBOL导出的变量)
CONFIG_KALLSYMS_ALL=y
执行更新.config文件的修改,使修改的编译选项的参数生效。
source .config
###################################################################################################################
说明:当然了有关Android内核编译配置文件.config的编译选项的修改也可以通过图像化的菜单界面来进行配置修改,执行 make menuconfig 命令。
$ make menuconfig
但是第一次直接执行 make menuconfig 命令,一般会报错,具体的错误如下面所示,解决的办法参考:<make menuconfig错误的解决办法> 。
解决办法:
$ sudo apt-get install libncurses5-dev
再次执行 make menuconfig 命令,可能还会出现下面的错误,错误的解决办法参考:<make menuconfig显示错误“Your display is too small to run Menuconfig!”>
解决办法:
问题解决了,成功执行 make menuconfig命令,Andorid内核编译选项的配置菜单显示出来了,按照配置菜单上的操作说明,进行Android内核编译选项的配置。不了解的可以参考下学习下:<menuconfig过程详解>、<Make Menuconfig详解 (配置内核选择)> 等几篇文章。
例如,让Android内核支持内核模块的动态加载和卸载:
###################################################################################################################
Android内核的编译配置文件.config修改完毕了,现在从谷歌官方或者清华源下载Android内核源码的交叉编译链接工具arm-eabi-4.8进行Android内核源码的编译。然后设置CROSS_COMPILE环境变量如下,运行make编译Android内核源码。
$ git clone https://android.googlesource.com/platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7/ (谷歌官方源)
$ git clone https://aosp.tuna.tsinghua.edu.cn/platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7/ (或者清华源)
$ export CROSS_COMPILE=$(pwd)/arm-eabi-4.7/bin/arm-eabi-
$ make -j4
当Android内核构建过程完成后,能找到引导内核模块 /home/androidcode/AndroidDevlop/Android4.4.4r1Kernel/msm/arch/arm/boot/zImage-dtb。
在将上面编译的新内核刷到Nexus 5手机之前,需要将Nexus 5手机的原始引导镜像进行备份一下,对于高通的设备Nexus 5,可以通过下面的方式查找到启动分区的位置。
$ adb shell
# msm 代表高通的芯片,msm_sdcc.1是外接的SD卡挂载的目录,by-name指的是这个sd卡分区的名称
$ ls -al /dev/block/platform/msm_sdcc.1/by-name/
在root权限下,将boot镜像所有的内容转储到Nexus 5手机的 /sdcard/boot.img 文件夹下,然后导出到Ubuntu 14.04系统的 /home/androidcode/AndroidDevlop/Android4.4.4r1Kernel/bootimg 目录下。
$ adb shell "su -c dd if=/dev/block/mmcblk0p19 of=/sdcard/boot.img"
$ adb pull /sdcard/boot.img /home/androidcode/AndroidDevlop/Android4.4.4r1Kernel/bootimg
对导出的boot.img内核引导文件进行解包,用以提取ramdisk以及有关引导映像结构的一些信息。解包和打包boot.img的工具有很多,这里使用Gilles Grandou的abootimg工具,该工具的源码可以通过 git clone https://github.com/shuixi2013/abootimg-1.git 进行下载。安装abootimg工具和执行以下的命令,在boot_img目录下会创建bootimg.cfg、initrd.img和zImage(原始的内核)文件。有关abootimg工具的详细用法可以参考:https://github.com/ggrandou/abootimg。
$ sudo apt-get install android-tools-adb android-tools-fastboot
$ sudo apt-get install build-essential abootimg
$ cd /home/androidcode/AndroidDevlop/Android4.4.4r1Kernel/bootimg
$ ls -l
$ abootimg -x boot.img
$ ls -al
boot.img文件解包结果截图:
boot.img文件解包得到的解包参数保存在bootimg.cfg文件中,在后面的打包操作中可能会用到。
bootsize = 0x1600000
pagesize = 0x800
kerneladdr = 0x8000
ramdiskaddr = 0x2900000
secondaddr = 0xf00000
tagsaddr = 0x2700000
name =
cmdline = console=ttyHSL0,115200,n8 androidboot.hardware=hammerhead user_debug=31 maxcpus=2 msm_watchdog_v2.enable=1
将上面编译的新的内核文件 msm/arch/arm/boot/zImage-dtb 拷贝到boot_img目录下,然后重新打包boot.img文件,重启Nexus 5手机进入刷机模式,用“fastboot boot”命令引导Android的新内核。除了新建的内核和原始ramdisk,还需指定内核偏移量,ramdisk偏移量,标签偏移量和命令行(使用在之前提取的bootimg.cfg中列出的值)。
$ cp ../msm/arch/arm/boot/zImage-dtb .
$ ls -l
$ abootimg --create myboot.img -f bootimg.cfg -k zImage-dtb -r initrd.img
$ ls -al
$ adb reboot bootloader
$ fastboot boot myboot.img
操作的结果截图:
fastboot boot 更新Nexus 5手机的内核成功后,重启手机设备。为了快速验证新的Android内核正确运行了,通过校验Settings->About phone中的“内核版本”的值,如下图。如果一切运行良好的话,内核版本下面将显示自定义构建的版本字符串。
abootimg工具的使用重要的参考:《编译Nexus5内核》、https://github.com/ggrandou/abootimg。
四、用内核模块hook系统调用
Hook系统调用能让我们绕过任何依赖内核提供的反逆向防御措施。在我们自定义的内核中,我们能用LKM加载例外的代码到内核中。我们也可以访问/dev/kmem接口,用来修改内核内存。这是个经典的linux rootkit技术。
首先需要找到的是sys_call_table的地址。幸运的是它被Android内核导出了符号。root权限下,在 /proc/kallsyms 中寻找sys_call_table地址。
# root权限下,关闭symbol符号屏蔽,将其重置为0,就可以打印显示
$ adb shell
$ su
$ echo 0 > /proc/sys/kernel/kptr_restrict
# 获取sys_call_table的内存地址
$ cat /proc/kallsyms | grep sys_call_table
c000f884 T sys_call_table
操作的结果截图:
sys_call_table=c000f884 即为我们需要找到的内存地址,后面很多Android系统调用的系统函数调用地址都需要通过这个地址加函数的偏移计算出来。
下面以使用内核模块隐藏一个文件为例子进行学习。先再设备设备上创建一个文件,以便我们能在后面隐藏它:
$ adb shell
$ su
$ echo HelloWorld > /data/local/tmp/nowyouseeme
# 显示创建的文件的内容
$ cat /data/local/tmp/nowyouseeme
HelloWorld
为了文件隐藏,需要挂钩用来打开文件的一个系统调用。有很多关于打开open, openat, access, accessat, facessat, stat, fstat,等等。现在我们只需要挂钩 openat 系统调用,这个系统调用被 “/bin/cat” 程序访问文件时使用。当openat系统调用被Hook以后,就可以进行文件显示的过滤,需要隐藏的文件就不显出来。
在Android内核源码的头文件(arch/arm/include/asm/unistd.h)中找到Android所有系统调用的函数原型,如下:
/*
* arch/arm/include/asm/unistd.h
*
* Copyright (C) 2001-2005 Russell King
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Please forward _all_ changes to this file to rmk@arm.linux.org.uk,
* no matter what the change is. Thanks!
*/
#ifndef __ASM_ARM_UNISTD_H
#define __ASM_ARM_UNISTD_H
#define __NR_OABI_SYSCALL_BASE 0x900000
#if defined(__thumb__) || defined(__ARM_EABI__)
#define __NR_SYSCALL_BASE 0
#else
#define __NR_SYSCALL_BASE __NR_OABI_SYSCALL_BASE
#endif
/*
* This file contains the system call numbers.
*/
#define __NR_restart_syscall (__NR_SYSCALL_BASE+ 0)
#define __NR_exit (__NR_SYSCALL_BASE+ 1)
#define __NR_fork (__NR_SYSCALL_BASE+ 2)
#define __NR_read (__NR_SYSCALL_BASE+ 3)
#define __NR_write (__NR_SYSCALL_BASE+ 4)
#define __NR_open (__NR_SYSCALL_BASE+ 5)
#define __NR_close (__NR_SYSCALL_BASE+ 6)
/* 7 was sys_waitpid */
#define __NR_creat (__NR_SYSCALL_BASE+ 8)
#define __NR_link (__NR_SYSCALL_BASE+ 9)
#define __NR_unlink (__NR_SYSCALL_BASE+ 10)
#define __NR_execve (__NR_SYSCALL_BASE+ 11)
#define __NR_chdir (__NR_SYSCALL_BASE+ 12)
#define __NR_time (__NR_SYSCALL_BASE+ 13)
#define __NR_mknod (__NR_SYSCALL_BASE+ 14)
#define __NR_chmod (__NR_SYSCALL_BASE+ 15)
#define __NR_lchown (__NR_SYSCALL_BASE+ 16)
/* 17 was sys_break */
/* 18 was sys_stat */
#define __NR_lseek (__NR_SYSCALL_BASE+ 19)
#define __NR_getpid (__NR_SYSCALL_BASE+ 20)
#define __NR_mount (__NR_SYSCALL_BASE+ 21)
#define __NR_umount (__NR_SYSCALL_BASE+ 22)
#define __NR_setuid (__NR_SYSCALL_BASE+ 23)
#define __NR_getuid (__NR_SYSCALL_BASE+ 24)
#define __NR_stime (__NR_SYSCALL_BASE+ 25)
#define __NR_ptrace (__NR_SYSCALL_BASE+ 26)
#define __NR_alarm (__NR_SYSCALL_BASE+ 27)
/* 28 was sys_fstat */
#define __NR_pause (__NR_SYSCALL_BASE+ 29)
#define __NR_utime (__NR_SYSCALL_BASE+ 30)
/* 31 was sys_stty */
/* 32 was sys_gtty */
#define __NR_access (__NR_SYSCALL_BASE+ 33)
#define __NR_nice (__NR_SYSCALL_BASE+ 34)
/* 35 was sys_ftime */
#define __NR_sync (__NR_SYSCALL_BASE+ 36)
#define __NR_kill (__NR_SYSCALL_BASE+ 37)
#define __NR_rename (__NR_SYSCALL_BASE+ 38)
#define __NR_mkdir (__NR_SYSCALL_BASE+ 39)
#define __NR_rmdir (__NR_SYSCALL_BASE+ 40)
#define __NR_dup (__NR_SYSCALL_BASE+ 41)
#define __NR_pipe (__NR_SYSCALL_BASE+ 42)
#define __NR_times (__NR_SYSCALL_BASE+ 43)
/* 44 was sys_prof */
#define __NR_brk (__NR_SYSCALL_BASE+ 45)
#define __NR_setgid (__NR_SYSCALL_BASE+ 46)
#define __NR_getgid (__NR_SYSCALL_BASE+ 47)
/* 48 was sys_signal */
#define __NR_geteuid (__NR_SYSCALL_BASE+ 49)
#define __NR_getegid (__NR_SYSCALL_BASE+ 50)
#define __NR_acct (__NR_SYSCALL_BASE+ 51)
#define __NR_umount2 (__NR_SYSCALL_BASE+ 52)
/* 53 was sys_lock */
#define __NR_ioctl (__NR_SYSCALL_BASE+ 54)
#define __NR_fcntl (__NR_SYSCALL_BASE+ 55)
/* 56 was sys_mpx */
#define __NR_setpgid (__NR_SYSCALL_BASE+ 57)
/* 58 was sys_ulimit */
/* 59 was sys_olduname */
#define __NR_umask (__NR_SYSCALL_BASE+ 60)
#define __NR_chroot (__NR_SYSCALL_BASE+ 61)
#define __NR_ustat (__NR_SYSCALL_BASE+ 62)
#define __NR_dup2 (__NR_SYSCALL_BASE+ 63)
#define __NR_getppid (__NR_SYSCALL_BASE+ 64)
#define __NR_getpgrp (__NR_SYSCALL_BASE+ 65)
#define __NR_setsid (__NR_SYSCALL_BASE+ 66)
#define __NR_sigaction (__NR_SYSCALL_BASE+ 67)
/* 68 was sys_sgetmask */
/* 69 was sys_ssetmask */
#define __NR_setreuid (__NR_SYSCALL_BASE+ 70)
#define __NR_setregid (__NR_SYSCALL_BASE+ 71)
#define __NR_sigsuspend (__NR_SYSCALL_BASE+ 72)
#define __NR_sigpending (__NR_SYSCALL_BASE+ 73)
#define __NR_sethostname (__NR_SYSCALL_BASE+ 74)
#define __NR_setrlimit (__NR_SYSCALL_BASE+ 75)
#define __NR_getrlimit (__NR_SYSCALL_BASE+ 76) /* Back compat 2GB limited rlimit */
#define __NR_getrusage (__NR_SYSCALL_BASE+ 77)
#define __NR_gettimeofday (__NR_SYSCALL_BASE+ 78)
#define __NR_settimeofday (__NR_SYSCALL_BASE+ 79)
#define __NR_getgroups (__NR_SYSCALL_BASE+ 80)
#define __NR_setgroups (__NR_SYSCALL_BASE+ 81)
#define __NR_select (__NR_SYSCALL_BASE+ 82)
#define __NR_symlink (__NR_SYSCALL_BASE+ 83)
/* 84 was sys_lstat */
#define __NR_readlink (__NR_SYSCALL_BASE+ 85)
#define __NR_uselib (__NR_SYSCALL_BASE+ 86)
#define __NR_swapon (__NR_SYSCALL_BASE+ 87)
#define __NR_reboot (__NR_SYSCALL_BASE+ 88)
#define __NR_readdir (__NR_SYSCALL_BASE+ 89)
#define __NR_mmap (__NR_SYSCALL_BASE+ 90)
#define __NR_munmap (__NR_SYSCALL_BASE+ 91)
#define __NR_truncate (__NR_SYSCALL_BASE+ 92)
#define __NR_ftruncate (__NR_SYSCALL_BASE+ 93)
#define __NR_fchmod (__NR_SYSCALL_BASE+ 94)
#define __NR_fchown (__NR_SYSCALL_BASE+ 95)
#define __NR_getpriority (__NR_SYSCALL_BASE+ 96)
#define __NR_setpriority (__NR_SYSCALL_BASE+ 97)
/* 98 was sys_profil */
#define __NR_statfs (__NR_SYSCALL_BASE+ 99)
#define __NR_fstatfs (__NR_SYSCALL_BASE+100)
/* 101 was sys_ioperm */
#define __NR_socketcall (__NR_SYSCALL_BASE+102)
#define __NR_syslog (__NR_SYSCALL_BASE+103)
#define __NR_setitimer (__NR_SYSCALL_BASE+104)
#define __NR_getitimer (__NR_SYSCALL_BASE+105)
#define __NR_stat (__NR_SYSCALL_BASE+106)
#define __NR_lstat (__NR_SYSCALL_BASE+107)
#define __NR_fstat (__NR_SYSCALL_BASE+108)
/* 109 was sys_uname */
/* 110 was sys_iopl */
#define __NR_vhangup (__NR_SYSCALL_BASE+111)
/* 112 was sys_idle */
#define __NR_syscall (__NR_SYSCALL_BASE+113) /* syscall to call a syscall! */
#define __NR_wait4 (__NR_SYSCALL_BASE+114)
#define __NR_swapoff (__NR_SYSCALL_BASE+115)
#define __NR_sysinfo (__NR_SYSCALL_BASE+116)
#define __NR_ipc (__NR_SYSCALL_BASE+117)
#define __NR_fsync (__NR_SYSCALL_BASE+118)
#define __NR_sigreturn (__NR_SYSCALL_BASE+119)
#define __NR_clone (__NR_SYSCALL_BASE+120)
#define __NR_setdomainname (__NR_SYSCALL_BASE+121)
#define __NR_uname (__NR_SYSCALL_BASE+122)
/* 123 was sys_modify_ldt */
#define __NR_adjtimex (__NR_SYSCALL_BASE+124)
#define __NR_mprotect (__NR_SYSCALL_BASE+125)
#define __NR_sigprocmask (__NR_SYSCALL_BASE+126)
/* 127 was sys_create_module */
#define __NR_init_module (__NR_SYSCALL_BASE+128)
#define __NR_delete_module (__NR_SYSCALL_BASE+129)
/* 130 was sys_get_kernel_syms */
#define __NR_quotactl (__NR_SYSCALL_BASE+131)
#define __NR_getpgid (__NR_SYSCALL_BASE+132)
#define __NR_fchdir (__NR_SYSCALL_BASE+133)
#define __NR_bdflush (__NR_SYSCALL_BASE+134)
#define __NR_sysfs (__NR_SYSCALL_BASE+135)
#define __NR_personality (__NR_SYSCALL_BASE+136)
/* 137 was sys_afs_syscall */
#define __NR_setfsuid (__NR_SYSCALL_BASE+138)
#define __NR_setfsgid (__NR_SYSCALL_BASE+139)
#define __NR__llseek (__NR_SYSCALL_BASE+140)
#define __NR_getdents (__NR_SYSCALL_BASE+141)
#define __NR__newselect (__NR_SYSCALL_BASE+142)
#define __NR_flock (__NR_SYSCALL_BASE+143)
#define __NR_msync (__NR_SYSCALL_BASE+144)
#define __NR_readv (__NR_SYSCALL_BASE+145)
#define __NR_writev (__NR_SYSCALL_BASE+146)
#define __NR_getsid (__NR_SYSCALL_BASE+147)
#define __NR_fdatasync (__NR_SYSCALL_BASE+148)
#define __NR__sysctl (__NR_SYSCALL_BASE+149)
#define __NR_mlock (__NR_SYSCALL_BASE+150)
#define __NR_munlock (__NR_SYSCALL_BASE+151)
#define __NR_mlockall (__NR_SYSCALL_BASE+152)
#define __NR_munlockall (__NR_SYSCALL_BASE+153)
#define __NR_sched_setparam (__NR_SYSCALL_BASE+154)
#define __NR_sched_getparam (__NR_SYSCALL_BASE+155)
#define __NR_sched_setscheduler (__NR_SYSCALL_BASE+156)
#define __NR_sched_getscheduler (__NR_SYSCALL_BASE+157)
#define __NR_sched_yield (__NR_SYSCALL_BASE+158)
#define __NR_sched_get_priority_max (__NR_SYSCALL_BASE+159)
#define __NR_sched_get_priority_min (__NR_SYSCALL_BASE+160)
#define __NR_sched_rr_get_interval (__NR_SYSCALL_BASE+161)
#define __NR_nanosleep (__NR_SYSCALL_BASE+162)
#define __NR_mremap (__NR_SYSCALL_BASE+163)
#define __NR_setresuid (__NR_SYSCALL_BASE+164)
#define __NR_getresuid (__NR_SYSCALL_BASE+165)
/* 166 was sys_vm86 */
/* 167 was sys_query_module */
#define __NR_poll (__NR_SYSCALL_BASE+168)
#define __NR_nfsservctl (__NR_SYSCALL_BASE+169)
#define __NR_setresgid (__NR_SYSCALL_BASE+170)
#define __NR_getresgid (__NR_SYSCALL_BASE+171)
#define __NR_prctl (__NR_SYSCALL_BASE+172)
#define __NR_rt_sigreturn (__NR_SYSCALL_BASE+173)
#define __NR_rt_sigaction (__NR_SYSCALL_BASE+174)
#define __NR_rt_sigprocmask (__NR_SYSCALL_BASE+175)
#define __NR_rt_sigpending (__NR_SYSCALL_BASE+176)
#define __NR_rt_sigtimedwait (__NR_SYSCALL_BASE+177)
#define __NR_rt_sigqueueinfo (__NR_SYSCALL_BASE+178)
#define __NR_rt_sigsuspend (__NR_SYSCALL_BASE+179)
#define __NR_pread64 (__NR_SYSCALL_BASE+180)
#define __NR_pwrite64 (__NR_SYSCALL_BASE+181)
#define __NR_chown (__NR_SYSCALL_BASE+182)
#define __NR_getcwd (__NR_SYSCALL_BASE+183)
#define __NR_capget (__NR_SYSCALL_BASE+184)
#define __NR_capset (__NR_SYSCALL_BASE+185)
#define __NR_sigaltstack (__NR_SYSCALL_BASE+186)
#define __NR_sendfile (__NR_SYSCALL_BASE+187)
/* 188 reserved */
/* 189 reserved */
#define __NR_vfork (__NR_SYSCALL_BASE+190)
#define __NR_ugetrlimit (__NR_SYSCALL_BASE+191) /* SuS compliant getrlimit */
#define __NR_mmap2 (__NR_SYSCALL_BASE+192)
#define __NR_truncate64 (__NR_SYSCALL_BASE+193)
#define __NR_ftruncate64 (__NR_SYSCALL_BASE+194)
#define __NR_stat64 (__NR_SYSCALL_BASE+195)
#define __NR_lstat64 (__NR_SYSCALL_BASE+196)
#define __NR_fstat64 (__NR_SYSCALL_BASE+197)
#define __NR_lchown32 (__NR_SYSCALL_BASE+198)
#define __NR_getuid32 (__NR_SYSCALL_BASE+199)
#define __NR_getgid32 (__NR_SYSCALL_BASE+200)
#define __NR_geteuid32 (__NR_SYSCALL_BASE+201)
#define __NR_getegid32 (__NR_SYSCALL_BASE+202)
#define __NR_setreuid32 (__NR_SYSCALL_BASE+203)
#define __NR_setregid32 (__NR_SYSCALL_BASE+204)
#define __NR_getgroups32 (__NR_SYSCALL_BASE+205)
#define __NR_setgroups32 (__NR_SYSCALL_BASE+206)
#define __NR_fchown32 (__NR_SYSCALL_BASE+207)
#define __NR_setresuid32 (__NR_SYSCALL_BASE+208)
#define __NR_getresuid32 (__NR_SYSCALL_BASE+209)
#define __NR_setresgid32 (__NR_SYSCALL_BASE+210)
#define __NR_getresgid32 (__NR_SYSCALL_BASE+211)
#define __NR_chown32 (__NR_SYSCALL_BASE+212)
#define __NR_setuid32 (__NR_SYSCALL_BASE+213)
#define __NR_setgid32 (__NR_SYSCALL_BASE+214)
#define __NR_setfsuid32 (__NR_SYSCALL_BASE+215)
#define __NR_setfsgid32 (__NR_SYSCALL_BASE+216)
#define __NR_getdents64 (__NR_SYSCALL_BASE+217)
#define __NR_pivot_root (__NR_SYSCALL_BASE+218)
#define __NR_mincore (__NR_SYSCALL_BASE+219)
#define __NR_madvise (__NR_SYSCALL_BASE+220)
#define __NR_fcntl64 (__NR_SYSCALL_BASE+221)
/* 222 for tux */
/* 223 is unused */
#define __NR_gettid (__NR_SYSCALL_BASE+224)
#define __NR_readahead (__NR_SYSCALL_BASE+225)
#define __NR_setxattr (__NR_SYSCALL_BASE+226)
#define __NR_lsetxattr (__NR_SYSCALL_BASE+227)
#define __NR_fsetxattr (__NR_SYSCALL_BASE+228)
#define __NR_getxattr (__NR_SYSCALL_BASE+229)
#define __NR_lgetxattr (__NR_SYSCALL_BASE+230)
#define __NR_fgetxattr (__NR_SYSCALL_BASE+231)
#define __NR_listxattr (__NR_SYSCALL_BASE+232)
#define __NR_llistxattr (__NR_SYSCALL_BASE+233)
#define __NR_flistxattr (__NR_SYSCALL_BASE+234)
#define __NR_removexattr (__NR_SYSCALL_BASE+235)
#define __NR_lremovexattr (__NR_SYSCALL_BASE+236)
#define __NR_fremovexattr (__NR_SYSCALL_BASE+237)
#define __NR_tkill (__NR_SYSCALL_BASE+238)
#define __NR_sendfile64 (__NR_SYSCALL_BASE+239)
#define __NR_futex (__NR_SYSCALL_BASE+240)
#define __NR_sched_setaffinity (__NR_SYSCALL_BASE+241)
#define __NR_sched_getaffinity (__NR_SYSCALL_BASE+242)
#define __NR_io_setup (__NR_SYSCALL_BASE+243)
#define __NR_io_destroy (__NR_SYSCALL_BASE+244)
#define __NR_io_getevents (__NR_SYSCALL_BASE+245)
#define __NR_io_submit (__NR_SYSCALL_BASE+246)
#define __NR_io_cancel (__NR_SYSCALL_BASE+247)
#define __NR_exit_group (__NR_SYSCALL_BASE+248)
#define __NR_lookup_dcookie (__NR_SYSCALL_BASE+249)
#define __NR_epoll_create (__NR_SYSCALL_BASE+250)
#define __NR_epoll_ctl (__NR_SYSCALL_BASE+251)
#define __NR_epoll_wait (__NR_SYSCALL_BASE+252)
#define __NR_remap_file_pages (__NR_SYSCALL_BASE+253)
/* 254 for set_thread_area */
/* 255 for get_thread_area */
#define __NR_set_tid_address (__NR_SYSCALL_BASE+256)
#define __NR_timer_create (__NR_SYSCALL_BASE+257)
#define __NR_timer_settime (__NR_SYSCALL_BASE+258)
#define __NR_timer_gettime (__NR_SYSCALL_BASE+259)
#define __NR_timer_getoverrun (__NR_SYSCALL_BASE+260)
#define __NR_timer_delete (__NR_SYSCALL_BASE+261)
#define __NR_clock_settime (__NR_SYSCALL_BASE+262)
#define __NR_clock_gettime (__NR_SYSCALL_BASE+263)
#define __NR_clock_getres (__NR_SYSCALL_BASE+264)
#define __NR_clock_nanosleep (__NR_SYSCALL_BASE+265)
#define __NR_statfs64 (__NR_SYSCALL_BASE+266)
#define __NR_fstatfs64 (__NR_SYSCALL_BASE+267)
#define __NR_tgkill (__NR_SYSCALL_BASE+268)
#define __NR_utimes (__NR_SYSCALL_BASE+269)
#define __NR_arm_fadvise64_64 (__NR_SYSCALL_BASE+270)
#define __NR_pciconfig_iobase (__NR_SYSCALL_BASE+271)
#define __NR_pciconfig_read (__NR_SYSCALL_BASE+272)
#define __NR_pciconfig_write (__NR_SYSCALL_BASE+273)
#define __NR_mq_open (__NR_SYSCALL_BASE+274)
#define __NR_mq_unlink (__NR_SYSCALL_BASE+275)
#define __NR_mq_timedsend (__NR_SYSCALL_BASE+276)
#define __NR_mq_timedreceive (__NR_SYSCALL_BASE+277)
#define __NR_mq_notify (__NR_SYSCALL_BASE+278)
#define __NR_mq_getsetattr (__NR_SYSCALL_BASE+279)
#define __NR_waitid (__NR_SYSCALL_BASE+280)
#define __NR_socket (__NR_SYSCALL_BASE+281)
#define __NR_bind (__NR_SYSCALL_BASE+282)
#define __NR_connect (__NR_SYSCALL_BASE+283)
#define __NR_listen (__NR_SYSCALL_BASE+284)
#define __NR_accept (__NR_SYSCALL_BASE+285)
#define __NR_getsockname (__NR_SYSCALL_BASE+286)
#define __NR_getpeername (__NR_SYSCALL_BASE+287)
#define __NR_socketpair (__NR_SYSCALL_BASE+288)
#define __NR_send (__NR_SYSCALL_BASE+289)
#define __NR_sendto (__NR_SYSCALL_BASE+290)
#define __NR_recv (__NR_SYSCALL_BASE+291)
#define __NR_recvfrom (__NR_SYSCALL_BASE+292)
#define __NR_shutdown (__NR_SYSCALL_BASE+293)
#define __NR_setsockopt (__NR_SYSCALL_BASE+294)
#define __NR_getsockopt (__NR_SYSCALL_BASE+295)
#define __NR_sendmsg (__NR_SYSCALL_BASE+296)
#define __NR_recvmsg (__NR_SYSCALL_BASE+297)
#define __NR_semop (__NR_SYSCALL_BASE+298)
#define __NR_semget (__NR_SYSCALL_BASE+299)
#define __NR_semctl (__NR_SYSCALL_BASE+300)
#define __NR_msgsnd (__NR_SYSCALL_BASE+301)
#define __NR_msgrcv (__NR_SYSCALL_BASE+302)
#define __NR_msgget (__NR_SYSCALL_BASE+303)
#define __NR_msgctl (__NR_SYSCALL_BASE+304)
#define __NR_shmat (__NR_SYSCALL_BASE+305)
#define __NR_shmdt (__NR_SYSCALL_BASE+306)
#define __NR_shmget (__NR_SYSCALL_BASE+307)
#define __NR_shmctl (__NR_SYSCALL_BASE+308)
#define __NR_add_key (__NR_SYSCALL_BASE+309)
#define __NR_request_key (__NR_SYSCALL_BASE+310)
#define __NR_keyctl (__NR_SYSCALL_BASE+311)
#define __NR_semtimedop (__NR_SYSCALL_BASE+312)
#define __NR_vserver (__NR_SYSCALL_BASE+313)
#define __NR_ioprio_set (__NR_SYSCALL_BASE+314)
#define __NR_ioprio_get (__NR_SYSCALL_BASE+315)
#define __NR_inotify_init (__NR_SYSCALL_BASE+316)
#define __NR_inotify_add_watch (__NR_SYSCALL_BASE+317)
#define __NR_inotify_rm_watch (__NR_SYSCALL_BASE+318)
#define __NR_mbind (__NR_SYSCALL_BASE+319)
#define __NR_get_mempolicy (__NR_SYSCALL_BASE+320)
#define __NR_set_mempolicy (__NR_SYSCALL_BASE+321)
#define __NR_openat (__NR_SYSCALL_BASE+322)
#define __NR_mkdirat (__NR_SYSCALL_BASE+323)
#define __NR_mknodat (__NR_SYSCALL_BASE+324)
#define __NR_fchownat (__NR_SYSCALL_BASE+325)
#define __NR_futimesat (__NR_SYSCALL_BASE+326)
#define __NR_fstatat64 (__NR_SYSCALL_BASE+327)
#define __NR_unlinkat (__NR_SYSCALL_BASE+328)
#define __NR_renameat (__NR_SYSCALL_BASE+329)
#define __NR_linkat (__NR_SYSCALL_BASE+330)
#define __NR_symlinkat (__NR_SYSCALL_BASE+331)
#define __NR_readlinkat (__NR_SYSCALL_BASE+332)
#define __NR_fchmodat (__NR_SYSCALL_BASE+333)
#define __NR_faccessat (__NR_SYSCALL_BASE+334)
#define __NR_pselect6 (__NR_SYSCALL_BASE+335)
#define __NR_ppoll (__NR_SYSCALL_BASE+336)
#define __NR_unshare (__NR_SYSCALL_BASE+337)
#define __NR_set_robust_list (__NR_SYSCALL_BASE+338)
#define __NR_get_robust_list (__NR_SYSCALL_BASE+339)
#define __NR_splice (__NR_SYSCALL_BASE+340)
#define __NR_arm_sync_file_range (__NR_SYSCALL_BASE+341)
#define __NR_sync_file_range2 __NR_arm_sync_file_range
#define __NR_tee (__NR_SYSCALL_BASE+342)
#define __NR_vmsplice (__NR_SYSCALL_BASE+343)
#define __NR_move_pages (__NR_SYSCALL_BASE+344)
#define __NR_getcpu (__NR_SYSCALL_BASE+345)
#define __NR_epoll_pwait (__NR_SYSCALL_BASE+346)
#define __NR_kexec_load (__NR_SYSCALL_BASE+347)
#define __NR_utimensat (__NR_SYSCALL_BASE+348)
#define __NR_signalfd (__NR_SYSCALL_BASE+349)
#define __NR_timerfd_create (__NR_SYSCALL_BASE+350)
#define __NR_eventfd (__NR_SYSCALL_BASE+351)
#define __NR_fallocate (__NR_SYSCALL_BASE+352)
#define __NR_timerfd_settime (__NR_SYSCALL_BASE+353)
#define __NR_timerfd_gettime (__NR_SYSCALL_BASE+354)
#define __NR_signalfd4 (__NR_SYSCALL_BASE+355)
#define __NR_eventfd2 (__NR_SYSCALL_BASE+356)
#define __NR_epoll_create1 (__NR_SYSCALL_BASE+357)
#define __NR_dup3 (__NR_SYSCALL_BASE+358)
#define __NR_pipe2 (__NR_SYSCALL_BASE+359)
#define __NR_inotify_init1 (__NR_SYSCALL_BASE+360)
#define __NR_preadv (__NR_SYSCALL_BASE+361)
#define __NR_pwritev (__NR_SYSCALL_BASE+362)
#define __NR_rt_tgsigqueueinfo (__NR_SYSCALL_BASE+363)
#define __NR_perf_event_open (__NR_SYSCALL_BASE+364)
#define __NR_recvmmsg (__NR_SYSCALL_BASE+365)
#define __NR_accept4 (__NR_SYSCALL_BASE+366)
#define __NR_fanotify_init (__NR_SYSCALL_BASE+367)
#define __NR_fanotify_mark (__NR_SYSCALL_BASE+368)
#define __NR_prlimit64 (__NR_SYSCALL_BASE+369)
#define __NR_name_to_handle_at (__NR_SYSCALL_BASE+370)
#define __NR_open_by_handle_at (__NR_SYSCALL_BASE+371)
#define __NR_clock_adjtime (__NR_SYSCALL_BASE+372)
#define __NR_syncfs (__NR_SYSCALL_BASE+373)
#define __NR_sendmmsg (__NR_SYSCALL_BASE+374)
#define __NR_setns (__NR_SYSCALL_BASE+375)
#define __NR_process_vm_readv (__NR_SYSCALL_BASE+376)
#define __NR_process_vm_writev (__NR_SYSCALL_BASE+377)
#define __NR_seccomp (__NR_SYSCALL_BASE+383)
/*
* The following SWIs are ARM private.
*/
#define __ARM_NR_BASE (__NR_SYSCALL_BASE+0x0f0000)
#define __ARM_NR_breakpoint (__ARM_NR_BASE+1)
#define __ARM_NR_cacheflush (__ARM_NR_BASE+2)
#define __ARM_NR_usr26 (__ARM_NR_BASE+3)
#define __ARM_NR_usr32 (__ARM_NR_BASE+4)
#define __ARM_NR_set_tls (__ARM_NR_BASE+5)
/*
* *NOTE*: This is a ghost syscall private to the kernel. Only the
* __kuser_cmpxchg code in entry-armv.S should be aware of its
* existence. Don't ever use this from user code.
*/
#ifdef __KERNEL__
#define __ARM_NR_cmpxchg (__ARM_NR_BASE+0x00fff0)
#endif
/*
* The following syscalls are obsolete and no longer available for EABI.
*/
#if !defined(__KERNEL__)
#if defined(__ARM_EABI__)
#undef __NR_time
#undef __NR_umount
#undef __NR_stime
#undef __NR_alarm
#undef __NR_utime
#undef __NR_getrlimit
#undef __NR_select
#undef __NR_readdir
#undef __NR_mmap
#undef __NR_socketcall
#undef __NR_syscall
#undef __NR_ipc
#endif
#endif
#ifdef __KERNEL__
#define __ARCH_WANT_IPC_PARSE_VERSION
#define __ARCH_WANT_STAT64
#define __ARCH_WANT_SYS_GETHOSTNAME
#define __ARCH_WANT_SYS_PAUSE
#define __ARCH_WANT_SYS_GETPGRP
#define __ARCH_WANT_SYS_LLSEEK
#define __ARCH_WANT_SYS_NICE
#define __ARCH_WANT_SYS_SIGPENDING
#define __ARCH_WANT_SYS_SIGPROCMASK
#define __ARCH_WANT_SYS_RT_SIGACTION
#define __ARCH_WANT_SYS_RT_SIGSUSPEND
#define __ARCH_WANT_SYS_OLD_MMAP
#define __ARCH_WANT_SYS_OLD_SELECT
#if !defined(CONFIG_AEABI) || defined(CONFIG_OABI_COMPAT)
#define __ARCH_WANT_SYS_TIME
#define __ARCH_WANT_SYS_IPC
#define __ARCH_WANT_SYS_OLDUMOUNT
#define __ARCH_WANT_SYS_ALARM
#define __ARCH_WANT_SYS_UTIME
#define __ARCH_WANT_SYS_OLD_GETRLIMIT
#define __ARCH_WANT_OLD_READDIR
#define __ARCH_WANT_SYS_SOCKETCALL
#endif
/*
* "Conditional" syscalls
*
* What we want is __attribute__((weak,alias("sys_ni_syscall"))),
* but it doesn't work on all toolchains, so we just do it by hand
*/
#define cond_syscall(x) asm(".weak " #x "
.set " #x ",sys_ni_syscall")
/*
* Unimplemented (or alternatively implemented) syscalls
*/
#define __IGNORE_fadvise64_64
#define __IGNORE_migrate_pages
#endif /* __KERNEL__ */
#endif /* __ASM_ARM_UNISTD_H */
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/unistd.h>
#include <linux/slab.h>
#include <asm/uaccess.h>
asmlinkage int (*real_openat)(int, const char __user*, int);
void **sys_call_table;
// 替换Android系统调用的新的new_openat函数
int new_openat(int dirfd, const char __user* pathname, int flags)
{
char *kbuf;
size_t len;
// 在内核中申请内存空间
kbuf=(char*)kmalloc(256, GFP_KERNEL);
// 获取需要打开的文件的文件路径
len = strncpy_from_user(kbuf, pathname,255);
// 过滤,隐藏掉/data/local/tmp/nowyouseeme文件
if (strcmp(kbuf, "/data/local/tmp/nowyouseeme") == 0)
{
printk("Hiding file!
");
return -ENOENT;
}
// 释放申请的内存空间
kfree(kbuf);
// 调用Android系统原来的系统调用openat函数
return real_openat(dirfd, pathname, flags);
}
// ########### 将被加载的Android内核模块 ###############
int init_module() {
// 前面查找的内存地址
sys_call_table = (void*)0x000f884;
// 获取Android系统的openat函数的调用地址
real_openat = (void*)(sys_call_table[__NR_openat]);
return 0;
}
为kernel_hook.c文件的编译配置内核源码文件路径和交叉编译工具,写编译的makefile文件:
KERNEL=/home/androidcode/AndroidDevlop/Android4.4.4r1Kernel/msm/
TOOLCHAIN=/home/androidcode/AndroidDevlop/Android4.4.4r1Kernel/msm/arm-eabi-4.7/bin/arm-eabi-
obj-m := kernel_hook.o
all:
make ARCH=arm CROSS_COMPILE=$(TOOLCHAIN) -C $(KERNEL) M=$(shell pwd) CFLAGS_MODULE=-fno-pic modules
clean:
make -C $(KERNEL) M=$(shell pwd) clean
内核模块与内核是紧密联系的。由于模块处于内核空间,一旦发生问题便会直接造成严重的系统崩溃,因此编译模块时需要内核的相关信息。一般的流程是:首先编译内核,之后根据得到的内核配置、符号信息等再编译模块。这也意味着,当系统内核更新后,外部模块往往需要重新编译以兼容新内核。Linux系统下可通过Dynamic Kernel Module Support(DKMS)自动重新编译内核模块。
因此,在前面的Andorid内核源码android-msm-hammerhead-3.4-kitkat-mr1的编译配置环境下,运行make编译kernel_hook.c的源码,得到文件 kernel_hook.ko 。如果前面的Android内核编译环境丢失,可以选择下面的编译命令方式之一进行内核模块的编译。
$ cd /home/androidcode/AndroidDevlop/Android4.4.4r1Kernel/msm/
$ export ARCH=arm
$ export SUBARCH=arm
$ make hammerhead_defconfig
$ gedit .config
###################################################
# 修改.config编译配置文件
CONFIG_MODULES=y
CONFIG_MODULE_UNLOAD=y
CONFIG_STRICT_MEMORY_RWX=n
CONFIG_DEVMEM=y
CONFIG_DEVKMEM=y
CONFIG_KALLSYMS=y
CONFIG_KALLSYMS_ALL=y
###################################################
$ source .config
$ export CROSS_COMPILE=$(pwd)/arm-eabi-4.7/bin/arm-eabi-
$ make prepare
$ make scripts
# 切换到需要的内核模块目录下,编译kernel_hook.c
$ cd /home/androidcode/AndroidDevlop/Android4.4.4r1Kernel/kernelmodle
$ make
或者
$ cd /home/androidcode/AndroidDevlop/Android4.4.4r1Kernel/msm/
$ export ARCH=arm
$ export SUBARCH=arm
$ make hammerhead_defconfig
$ gedit .config
###################################################
# 修改.config编译配置文件
CONFIG_MODULES=y
CONFIG_MODULE_UNLOAD=y
CONFIG_STRICT_MEMORY_RWX=n
CONFIG_DEVMEM=y
CONFIG_DEVKMEM=y
CONFIG_KALLSYMS=y
CONFIG_KALLSYMS_ALL=y
###################################################
$ source .config
$ export CROSS_COMPILE=$(pwd)/arm-eabi-4.7/bin/arm-eabi-
$ make -j4
# 切换到需要的内核模块目录下,编译kernel_hook.c
$ cd /home/androidcode/AndroidDevlop/Android4.4.4r1Kernel/kernelmodle
$ make
拷贝文件 kernel_hook.ko 到Nexus 5手机的 /data/local/tmp/ 目录下并用 insmod 命令加载编译后的内核模块kernel_hook.ko。用lsmod 命令 验证该内核模块是否加载成功。
$ adb push kernel_hook.ko /data/local/tmp/
$ adb shell su -c insmod /data/local/tmp/kernel_hook.ko
$ adb shell lsmod
悲剧的是前面编译Android内核模块的时候也是出现奇葩的错误,后来换实验的Android内核版本,现在在加载Android内核模块文件kernel_hook.ko的时候,又出现了错误,我也是醉了。错误的提示如图啊,经过 modinfo kernel_hook.ko 命令验证发现,编译成功的Android内核模块的文件的版本没有问题。
$ modinfo kernel_hook.ko
五、修改系统调用表
通过访问 /dev/kmem接口 用将Hook的新函数地址new_openat来覆盖sys_call_table中的原始函数openat的调用地址(这也能直接在内核模块中做,但是用/dev/kmem更加简单)。在参考了Dong-Hoon You的文章后,决定使用文件接口代替nmap(),因为经过试验发现会引起一些内核警告。用下面代码创建文件
kmem_util.c:
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <asm/unistd.h>
#include <sys/mman.h>
#define MAP_SIZE 4096UL
#define MAP_MASK (MAP_SIZE - 1)
// 保存内存文件的句柄
int kmem;
// 读取内存文件中的数据
void read_kmem2(unsigned char *buf, off_t off, int sz)
{
off_t offset;
ssize_t bread;
// 设置内存文件的偏移在(从文件头开始)
offset = lseek(kmem, off, SEEK_SET);
// 读取内存文件的数据
bread = read(kmem, buf, sz);
return;
}
// 向内存文件写入数据
void write_kmem2(unsigned char *buf, off_t off, int sz)
{
off_t offset;
ssize_t written;
// 设置内存文件的偏移
offset = lseek(kmem, off, SEEK_SET);
// 向内存文件写入数据
if (written = write(kmem, buf, sz) == -1)
{
perror("Write error");
exit(0);
}
return;
}
// 主函数
int main(int argc, char *argv[])
{
off_t sys_call_table;
unsigned int addr_ptr, sys_call_number;
// 对传入的参数的个数进行校验,不能少于3个
if (argc < 3)
{
return 0;
}
// 打开内核文件接口/dev/kmem
kmem = open("/dev/kmem", O_RDWR);
// 判断文件是否打开成功
if(kmem < 0)
{
perror("Error opening kmem");
return 0;
}
// 获取输入的sys_call_table地址
sscanf(argv[1], "%x", &sys_call_table);
// 获取Android系统调用openat函数的偏移值
sscanf(argv[2], "%d", &sys_call_number);
// 获取新的new_openat的调用地址
sscanf(argv[3], "%x", &addr_ptr);
char buf[256];
// 内存清零
memset(buf, 0, 256);
// 获取Android系统调用openat函数的原始调用地址
read_kmem2(buf, sys_call_table+(sys_call_number*4), 4);
// 打印Android系统调用openat函数的原始调用地址
printf("Original value: %02x%02x%02x%02x
", buf[3], buf[2], buf[1], buf[0]);
// 将Android系统调用的openat函数的原始调用地址替换为新的new_openat的调用地址
write_kmem2((void*)&addr_ptr,sys_call_table+(sys_call_number*4), 4);
// 获取替换后的新new_openat函数的调用地址
read_kmem2(buf,sys_call_table+(sys_call_number*4), 4);
// 打印替换后的new_openat函数的调用地址
printf("New value: %02x%02x%02x%02x
", buf[3], buf[2], buf[1], buf[0]);
// 关闭文件
close(kmem);
return 0;
}
编译构建 kmem_util.c 并拷贝编译后的文件kmem_util到Nexus 5手机的 /data/local/tmp/ 目录下。注意:如果是Android Lollipop即Android 5.x版本,则所有的可执行文件必须是PIE支持编译的,需要添加编译选项 -pie -fpie 。
$ cd /home/androidcode/AndroidDevlop/Android4.4.4r1Kernel/bin_elf
$ /home/androidcode/AndroidDevlop/Android4.4.4r1Kernel/msm/arm-eabi-4.7/bin/arm-eabi-gcc -pie -fpie -o kmem_util kmem_util.c
$ adb push kmem_util /data/local/tmp/
$ adb shell chmod 755 /data/local/tmp/kmem_util
在开始修改Android内核内存之前,需要先知道 Android系统调用表 中openat函数的正确偏移位置。openat系统调用在Android内核源码的 arch/arm/include/asm/unistd.h 中定义:
$ cd /home/androidcode/AndroidDevlop/Android4.4.4r1Kernel/msm
$ grep -r "__NR_openat" arch/arm/include/asm/unistd.h
#define __NR_openat (__NR_SYSCALL_BASE+322)
操作结果截图:
从上面的操作结果获取到openat系统调用的偏移量为322. 获取已经加载到Android内核内存中替换原始openat函数调用地址的新的new_openat函数的调用地址。从符号文件 /proc/kallsyms 中可以得到new_openat函数的调用地址:
$ adb shell cat /proc/kallsyms | grep new_openat
bf000000 t new_openat [kernel_hook]
现在可以覆盖Android内核内存中系统调用函数的调用地址了。kmem_util可执行程序的用法如下:
./kmem_util <syscall_table_base_address> <offset> <new_fun_addr>
root权限下,执行下面的命令修改Android的系统调用表中函数的调用地址将其替换为我们自定的Hook函数的调用地址。
~/home/androidcode/AndroidDevlop/Android4.4.4r1Kernel/msm$ adb shell su -c /data/local/tmp/kmem_util c000f984 322 bf000000
Original value: c017a390
New value: bf000000
$ adb shell su -c cat /data/local/tmp/nowyouseeme
tmp-mksh: cat: /data/local/tmp/nowyouseeme: No such file or directory
现在通过所有的用户进程已经无法看见隐藏的文件了(但是为了隐藏文件有许多需要做的,包括挂钩stat,access和其他系统调用,以及在文件夹中隐藏)。
此处,文件隐藏的教程只是一个小例子:你可以完成一大堆事,包括绕过启动检测,完整性校验和反调试技巧。
后记:这篇文章主要是参考Hooking Android System Calls for Pleasure and Benefit及其翻译的文章 hook Android系统调用的乐趣和好处 来写的实验教程。开始的时候按照作者的思路进行操作,前面部分还是比较顺利,可是到了后半部分那就悲剧了。按照作者的步骤使用Android 5.1的内核源码lollipop,Android的内核虽然编译成功,但是Android的内核加载模块kernel_hook.ko生成不了老是报错,网上查了也没有找到解决的方法,在折腾了几次Android 5.1的内核源码后,换回Android 4.4.4r1的内核源码进行Android内核加载模块kernel_hook.ko的编译生成,期间也是出现各种莫名其妙的错误,后来终于编译kernel_hook.ko模块成功了,但是加载该内核模块的时候又报错了,后面的操作也是无法进行,错的一塌糊涂,后面有时间再研究研究。经过实践发现,Hooking Android System Calls for Pleasure and Benefit 原文的作者的这篇文章完全是拼凑起来的,有些步骤上明显执行的命令是错误还能继续下去,我也是服了。至少经过测试多次,不少地方原文作者没有给出细节而且执行的命令有问题的,给出的Hook方法也不是最优的。
重要的参考资料:
《Hooking Android System Calls for Pleasure and Benefit》