1. 程式人生 > >Hook android系統呼叫研究(一)

Hook android系統呼叫研究(一)

一、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
二、構建Android核心

獲取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核心原始碼需要翻牆而且速度也不是很快(不過還是推薦從谷歌官方下載)。

$ 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   (或者谷歌官方的源需要翻牆)

$ 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命令,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中的“核心版本”的值,如下圖。如果一切執行良好的話,核心版本下面將顯示自定義構建的版本字串


四、用核心模組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 [email protected],
 * 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\t" #x "\n\t.set\t" #x ",sys_ni_syscall")

/*
 * Unimplemented (or alternatively implemented) syscalls
 */
#define __IGNORE_fadvise64_64
#define __IGNORE_migrate_pages

#endif /* __KERNEL__ */
#endif /* __ASM_ARM_UNISTD_H */
建立一個掛鉤Android系統呼叫 openat 的程式碼檔案 kernel_hook.c
#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!\n");
    
    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.c編譯失敗的情況,先 make clean 一下再進行編譯):



拷貝檔案 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\n", 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\n", 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
如果一切操作正常的話,root許可權下,執行 /bin/cat 應該不能列印顯示出我們隱藏的檔案/data/local/tmp/nowyouseeme。
$ adb shell su -c cat /data/local/tmp/nowyouseeme

tmp-mksh: cat: /data/local/tmp/nowyouseeme: No such file or directory

現在通過所有的使用者程序已經無法看見隱藏的檔案了(但是為了隱藏檔案有許多需要做的,包括掛鉤stataccess其他系統呼叫以及在資料夾中隱藏)。
此處,檔案隱藏的教程只是一個小例子:你可以完成一大堆事,包括繞過啟動檢測完整性校驗反除錯技巧

後記:這篇文章主要是參考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方法也不是最優的。

重要的參考資料:

相關推薦

Hook android系統呼叫研究

一、Android核心原始碼的編譯環境 系統環境:Ubuntu 14.04 x64bit Android系統版本:Android 4.4.4 r1 Android核心版本:android-msm-hammerhead-3.4-kitkat-mr1 手機裝置:Nexus

Android系統啟動流程解析init進程啟動過程

option 寫入 android change failed miss 通知 target sna 前言 作為“Android框架層”這個大系列中的第一個系列,我們首先要了解的是Android系統啟動流程,在這個流程中會涉及到很多重要的知識點,這個系列我們就來一一講解它們

Android系統啟動流程解析init程序啟動過程

前言 作為“Android框架層”這個大系列中的第一個系列,我們首先要了解的是Android系統啟動流程,在這個流程中會涉及到很多重要的知識點,這個系列我們就來一一講解它們,這一篇我們就來學習init程序。 1.init簡介 init程序是An

Android Hook框架adbi原始碼淺析

adbi(The Android Dynamic Binary Instrumentation Toolkit)是一個Android平臺通用hook框架,基於動態庫注入與inline hook技術實現。該框架由兩個主要模組構成,1.hijack負責將動態庫注入到目標程序;2.libbase提供動態庫本身,它實

Android編譯系統詳解

++++++++++++++++++++++++++++++++++++++++++ 本文系本站原創,歡迎轉載! 轉載請註明出處: ++++++++++++++++++++++++++++++++++++++++++ Android的優勢就在於其開源,手機和平板

Android與JS之間的互相呼叫互動

Android裝置多解析度的問題Android瀏覽器預設預覽模式瀏覽 會縮小頁面 WebView中則會以原始大小顯示Android瀏覽器和WebView預設為mdpi。hdpi相當於mdpi的1.5倍 ldpi相當於0.75倍三種解決方式:1 viewport屬性 2 CS

步步淺析Android系統導航欄NavigationBar

Android手機可分為有導航欄以及沒導航欄兩種,一般有物理按鍵的機器不會帶有導航欄,而沒有物理按鍵的機器則基本會帶,比如華為的手機基本都是帶導航欄的。 導航欄是如何載入到桌面上?是如何實現與物理按鍵相同的功能的呢?帶著種種疑問,我們來read the fuc

ICTCLAS分詞系統研究

        ICTClAS分詞系統是由中科院計算所的張華平、劉群所開發的一套獲得廣泛好評的分詞系統,難能可貴的是該版的Free版開放了原始碼,為我們很多初學者提供了寶貴的學習材料。       但有一點不完美的是,該原始碼沒有配套的文件,閱讀起來可能有一定的障礙,尤其是對

Android內存優化DVM和ART原理初探

java虛擬機 劃分 cimage beef 靜態 由於 jar blank 查找 要學習Android的內存優化,首先要了解Java虛擬機,此前我用了多篇文章來介紹Java虛擬機的知識,就是為了這個系列做鋪墊。在Android開發中我們接觸的是與Java虛擬機類似的Dal

Redis研究—簡介

創始人 存儲結構 隊列 cached tar 寫入 關系 退出 使用 http://blog.csdn.net/wtyvhreal/article/details/41855327 Redis是一個開源的高性能鍵值對數據庫。它通過提供多種鍵值數據類型來適應不同場景下的

android的入門記錄

下載到本地 可能 安裝 subst 數據 工具包 一次 以及 由於 ---恢復內容開始--- 首先,這是我人生中的第一篇博客,也許嚴格意義上它並不算是一篇博客,但也代表著一些東西。 前言 我們往往在開始學習一門新的語言或者課程時會遇見各式各樣的問題,比

win10系統docker學習——docker安裝

tle png 問題: 方案 start htm 完成後 系統 mage windows系統中目前有兩種安裝docker的方式,分別為boot2docker和docker toolbox。按照官方說明和實際安裝過程來講,推薦使用docker toolbox方

項目管理系統的實施

項目管理系統 項目交付過程 項目管理系統是IT服務公司,軟件公司,系統集成公司或者是其他依賴軟硬件的高科技公司或組織必須的保證組織正常運營,成熟發展,健康生長的必要企業內部運營性平臺。要做好項目管理系統實施,首先要做好項目管理評估。在CMMi或者其他項目成熟度管理體系中,叫做過程評估。而其中第一步要建

linux 配置文件.conf 非打印字符出錯的研究

highlight 運行錯誤 配置文件 檢查 bsp 換行符 span 字符 future linux 大量使用 .conf配置文件,經常從網上復制的配置信息,保存後,會出現運行錯誤。原因就是復制到了一個不可見的,無效的非打印字符。如何檢查並清除這些無效字符,今天探討一下。

對賬系統產品設計

例如 產品經理 表模塊 放心 比較 div 第一篇 訂單 系列 我是做技術的,為什麽會要寫產品設計呢?就像一句俗話“久病成醫”,當你負責一個系統足夠久了,可能你就懂的比較多了。我想把自己遇見的聽見的做一個系列,算是對自己過去工作的總結。 本文的基調是,少專業術語,全用大白話

linux系統程序安裝rpm工具

erl fields program 軟件 方法 owin lib 直接 源碼安裝 linux系統下程序安裝主要采用三種方式:1、rpm,有點類似.msi 和.exe比較類似,軟件包(相當於windows的某個程序的所有文件)的安裝路徑和文件名稱基本是固定的,但是他不會安裝

linux系統程序安裝yum工具

sync 內容 sim lean provide for 就是 grep 大型 yum安裝工具類似appstore,運用yum工具可以方便的下載所需的程序,同時yum工具會自動檢查程序的依賴關系,並安裝相應的依賴包,有點像windows的某些大型軟件安裝程序會自動檢查幫你安

Linux系統 shell基礎編輯中

初始 輸入 分享 用戶命令 解釋器 sts ima 理解 操作 shell是操作系統中重要的應用,尤其對linux這種命令行模式的操作系統,shell具有重要的意義。 一、什麽是shell shell是一個命令解釋器,提供用戶和機器之間的交互。每次我們登錄系統後,出現的界面

系統調用

number 系統調用的實現 getpid 結果 而不是 簡單介紹 ptr 64位 esc (一):與內核通信 系統調用在用戶空間和硬件設備之間加入了一個中間層。該

操作系統基本知識

兩個 code 緩沖 inline 內存管理 中斷 字節 核心 結束 計算機系統體系結構: 單處理器的計算機系統,它只有一個general purpose 的處理器,它有一個CPU來執行通用的指令集, 另外,裏面也包括一些專用的控制器(處理器),比如控制硬盤、網卡、