The Android kernel is a powerful ally to the reverse engineer. While regular Android apps are hopelessly restricted and sandboxed, you - the reverser - can customize and alter the behavior of the operating system and kernel any way you wish. This gives you a really unfair advantage, because most integrity checks and anti-tampering features ultimately rely on services performed by the kernel. Deploying a kernel that abuses this trust, and unabashedly lies about itself and the environment, goes a long way in defeating most reversing defenses that malware authors (or normal developers) can throw at you.

Android apps have several ways of interacting with the OS environment. The standard way is through the APIs of the Android Application Framework. On the lowest level however, many important functions, such as allocating memory and accessing files, are translated into perfectly old-school Linux system calls. In ARM Linux, system calls are invoked via the SVC instruction which triggers a software interrupt. This interrupt calls the vector_swi() kernel function, which then uses the system call number as an offset into a table of function pointers (a.k.a. sys_call_table on Android).

The most straightforward way of intercepting system calls is injecting your own code into kernel memory, then overwriting the original function in the system call table to redirect execution. Unfortunately, current stock Android kernels enforce memory restrictions that prevent this from working. Specifically, stock Lollipop and Marshmallow kernel are built with the CONFIG_STRICT_MEMORY_RWX option enabled. This prevents writing to kernel memory regions marked as read-only, which means that any attempts to patch kernel code or the system call table result in a segmentation fault and reboot. A way to get around this is to build your own kernel: You can then deactivate this protection, and make many other useful customizations to make reverse engineering easier. If you're reversing Android apps on a regular basis, building your own reverse engineering sandbox is a no-brainer.

Note: The steps below work on Ubuntu 14.04 with Android NDK 4.8. Personally I'm still traumatized from multiple failed attempts of getting this to work on Mac OS. I recommend taking that route only if you're a masochist - everyone else is better served by using an Ubuntu VM.

Building the Kernel
For hacking purposes, I recommend using an AOSP-supported device. Google’s Nexus smartphones and tablets are the most logical candidates – kernels and system components built from the AOSP run on them without issues. Alternatively, Sony’s Xperia series is also known for its openness. To build the AOSP kernel you need a toolchain (set of programs to cross-compile the sources) as well as the appropriate version of the kernel sources. Follow Google's instructions to identify the correct git repo and branch for a given device and Android version.

For example, to get kernel sources for Lollipop that are compatible with the Nexus 5, you need to clone the "msm" repo and check out one the "android-msm-hammerhead" branch (hammerhead is the codename of the Nexus 5, and yes, finding the right branch is a confusing process). Once the sources are downloaded, create the default kernel config with the command make hammerhead_defconfig (or whatever_defconfig, depending on your target device).

$ git clone https://android.googlesource.com/kernel/msm.git
$ cd msm
$ git checkout origin/android-msm-hammerhead-3.4-lollipop-mr1
$ export ARCH=arm
$ export SUBARCH=arm
$ make hammerhead_defconfig
$ vim .config

To enable system call hooking, I recommend adding loadable module support, exposing the /dev/kmem interface, and exporting the global kernel symbols. Don't forget to deactivate strict memory protection as well. Most of these options should already exist in the config file - simply set them to the values recommended below.

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

Once you are finished editing save the .config file. Optionally, you can now create a standanlone toolchain for cross-compiling the kernel and later tasks. To create a toolchain for Android 5.1, run make-standalone-toolchain.sh from the Android NDK package as follows:

$ cd android-ndk-rXXX
$ build/tools/make-standalone-toolchain.sh --arch=arm --platform=android- --install-dir=/tmp/my-android-toolchain

Set the CROSS_COMPILE environment variable to point to your NDK directory and run "make" to build the kernel.

$ export CROSS_COMPILE=/tmp/my-android-toolchain/bin/arm-eabi-
$ make

When the build process (hopefully) completes successfully, you will find the bootable kernel image at arch/arm/boot/zImage-dtb.

Booting Your Shiny New Kernel

Before booting into the new Kernel, make a copy of the original boot image from your device. Look up the location of the boot partition as follows:

root@hammerhead:/dev # ls -al /dev/block/platform/msm_sdcc./by-name/
lrwxrwxrwx root root -- : DDR -> /dev/block/mmcblk0p24
lrwxrwxrwx root root -- : aboot -> /dev/block/mmcblk0p6
lrwxrwxrwx root root -- : abootb -> /dev/block/mmcblk0p11
lrwxrwxrwx root root -- : boot -> /dev/block/mmcblk0p19
(...)
lrwxrwxrwx root root -- : userdata -> /dev/block/mmcblk0p28

Then, dump the whole thing into a file:

$ adb shell "su -c dd if=/dev/block/mmcblk0p19 of=/data/local/tmp/boot.img"
$ adb pull /data/local/tmp/boot.img

Next, extract the ramdisk as well as some information about the structure of the boot image. There are various tools that can do this - I used Gilles Grandou's abootimg tool. Install the tool and run the following command on your boot image:

$ abootimg -x boot.img 

This should create the files bootimg.cfg, initrd.img and zImage (your original kernel) in the local directory.

You can now use fastboot to test the new kernel. The "fastboot boot" command allows you to run the kernel without actually flashing it (once you’re sure everything works, you can make the changes permanent with fastboot flash - but you don't have to). Restart the device in fastboot mode with the following command:

$ adb reboot bootloader

Then, use the "fastboot boot" command to boot Android with the new kernel. In addition to the newly built kernel and the original ramdisk, specify the kernel offset, ramdisk offset, tags offset and commandline (use the values listed in your previously extracted bootimg.cfg).

$ fastboot boot zImage-dtb initrd.img --base  --kernel-offset 0x8000 --ramdisk-offset 0x2900000 --tags-offset 0x2700000 -c "console=ttyHSL0,115200,n8 androidboot.hardware=hammerhead user_debug=31 maxcpus=2 msm_watchdog_v2.enable=1"

The system should now boot normally. To quickly verify that the correct kernel is running, navigate to Settings->About phone and check the “kernel version” field.

If everything went well, this should show the version string of your custom-built kernel. Pat yourself on the back / howl triumphantly, you're now ready for primetime!

Syscall Hooking using Kernel Modules

System call hooking allows us to attack any anti-reversing defenses that depend on functionality provided by the kernel. With our custom kernel in place, we can now use a LKM to load additional code into the kernel. We also have access to the /dev/kmem interface, which we can use to patch kernel memory on-the-fly. This is a classical Linux rootkit technique and has been described for Android by Dong-Hoon You [1].

The first piece of information we need is the address of sys_call_table. Fortunately, it is exported as a symbol in the Android kernel (iOS reversers are not so lucky). We can look up the address in the /proc/kallsyms file:

$ adb shell "su -c echo 0 > /proc/sys/kernel/kptr_restrict"
$ adb shell cat /proc/kallsyms | grep sys_call_table
c000f984 T sys_call_table

This is the only memory address we need for writing our kernel module - everything else can be calculated using offsets taken from the Kernel headers (hopefully you didn't delete them yet?).

In this howto, we're going to use a Kernel module to hide a file. Let's create a file on the device so we can hide it later:

$ adb shell "su -c echo ABCD > /data/local/tmp/nowyouseeme"
$ adb shell cat /data/local/tmp/nowyouseeme
ABCD

Finally it's time to write the kernel module. For file hiding purposes, we'll need to hook one of the system calls used to open (or check for the existence of) files. Actually, there many of those - open, openat, access, accessat, facessat, stat, fstat, and more. For now, we'll only hook the openat system call - this is the syscall used by the "/bin/cat" program when accessing a file, so it should be servicable enough for a demonstration.

You can find the function prototypes for all system calls in the kernel header file arch/arm/include/asm/unistd.h. Create a file called kernel_hook.c with the following code:

#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; int new_openat(int dirfd, const char __user* pathname, int flags)
{
char *kbuf;
size_t len; kbuf=(char*)kmalloc(,GFP_KERNEL);
len = strncpy_from_user(kbuf,pathname,); if (strcmp(kbuf, "/data/local/tmp/nowyouseeme") == ) {
printk("Hiding file!\n");
return -ENOENT;
} kfree(kbuf); return real_openat(dirfd, pathname, flags);
} int init_module() { sys_call_table = (void*)0xc000f984;
real_openat = (void*)(sys_call_table[__NR_openat]); return ; }

To build the kernel module, you need the kernel sources and a working toolchain - since you already built a complete kernel before, you are all set. Create a Makefile with the following content:

KERNEL=[YOUR KERNEL PATH]
TOOLCHAIN=[YOUR TOOLCHAIN PATH] obj-m := kernel_hook.o all:
make ARCH=arm CROSS_COMPILE=$(TOOLCHAIN)/bin/arm-eabi- -C $(KERNEL) M=$(shell pwd) CFLAGS_MODULE=-fno-pic modules clean:
make -C $(KERNEL) M=$(shell pwd) clean

Run "make" to compile the code – this should create the file kernel_hook.ko. Copy the kernel_hook.ko file to the device and load it with the insmod command. Verify with the lsmod command that the module has been loaded successfully.

 make
(...)
$ adb push kernel_hook.ko /data/local/tmp/
[%] /data/local/tmp/kernel_hook.ko
$ adb shell su -c insmod /data/local/tmp/kernel_hook.ko
$ adb shell lsmod
kernel_hook [permanent], Live 0xbf000000 (PO)

Patching the System Call Table

Now, we’ll access /dev/kmem to overwrite the original function pointer in sys_call_table with the address of our newly injected function (this could have been done directly in the kernel module as well, but using /dev/kmem gives us an easy way to toggle our hooks on and off). I have adapted the code from Dong-Hoon You’s Phrack article [1] for this purpose - however, I used the file interface instead of mmap(), as I found the latter to cause kernel panics for some reason. Create a file called kmem_util.c with the following code:

#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) == -) { perror("Write error");
exit();
}
return;
} int main(int argc, char *argv[]) { off_t sys_call_table;
unsigned int addr_ptr, sys_call_number; if (argc < ) {
return ;
} kmem=open("/dev/kmem",O_RDWR); if(kmem<){
perror("Error opening kmem"); return ;
} sscanf(argv[], "%x", &sys_call_table); sscanf(argv[], "%d", &sys_call_number);
sscanf(argv[], "%x", &addr_ptr); char buf[];
memset (buf, , ); read_kmem2(buf,sys_call_table+(sys_call_number*),);
printf("Original value: %02x%02x%02x%02x\n", buf[], buf[], buf[], buf[]);
write_kmem2((void*)&addr_ptr,sys_call_table+(sys_call_number*),);
read_kmem2(buf,sys_call_table+(sys_call_number*),);
printf("New value: %02x%02x%02x%02x\n", buf[], buf[], buf[], buf[]);
close(kmem); return ;
}

Build kmem_util.c using the prebuilt toolchain and copy it to the device. Note that from Android Lollipop, all executables must be compiled with PIE support:

$ /tmp/my-android-toolchain/bin/arm-linux-androideabi-gcc -pie -fpie -o kmem_util kmem_util.c
$ adb push kmem_util /data/local/tmp/
$ adb shell chmod /data/local/tmp/kmem_util

Before we start messing with kernel memory we still need to know the correct offset into the system call table. The openat system call is defined in unistd.h which is found in the kernel sources:

$ grep -r "__NR_openat" arch/arm/include/asm/unistd.h
#define __NR_openat (__NR_SYSCALL_BASE+322)

The final piece of the puzzle is the address of our replacement-openat. Again, we can get this address from /proc/kallsyms.

$ adb shell cat /proc/kallsyms | grep new_openat
bf000000 t new_openat [kernel_hook]

Now we have everything we need to overwrite the sys_call_table entry. The syntax for kmem_util is:

./kmem_util <syscall_table_base_address> <offset> <func_addr>

The following command patches the openat system call table to point to our new function.

berndt@osboxes:~/Host/Research/SoftToken/Android/Kernel/msm$ adb shell su -c /data/local/tmp/kmem_util c000f984  bf000000
Original value: c017a390
New value: bf000000

Assuming that everything worked, /bin/cat should now be unable to "see" the file.

berndt@osboxes:~/Desktop/Module$ adb shell su -c cat /data/local/tmp/nowyouseeme
tmp-mksh: cat: /data/local/tmp/nowyouseeme: No such file or directory

Voilá! The file "nowyouseeme" is now somewhat hidden from the view of all usermode processes (note that there's a lot more you need to do to properly hide a file, including hooking stat(), access(), and other system calls, as well as hiding the file in directory listings).

File hiding is of course only the tip of the iceberg: You can accomplish a whole lot of things, including bypassing many root detection measures, integrity checks, and anti-debugging tricks. You can find some additional examples in the "case studies" section of my recent paper.

https://www.vantagepoint.sg/blog/82-hooking-android-system-calls-for-pleasure-and-benefit

http://www.phrack.org/issues/68/6.html#article

Hooking Android System Calls for Pleasure and Benefit的更多相关文章

  1. MIT 6.828 JOS学习笔记18. Lab 3.2 Part B: Page Faults, Breakpoints Exceptions, and System Calls

    现在你的操作系统内核已经具备一定的异常处理能力了,在这部分实验中,我们将会进一步完善它,使它能够处理不同类型的中断/异常. Handling Page Fault 缺页中断是一个非常重要的中断,因为我 ...

  2. The behavior of App killed or restored by Android System or by users

    What's the behavior of App killed or restored by Android System or by users? First, user kills the a ...

  3. 解决appium自带的Chromedriver版本和设备Android System Webview版本不一致的问题

    报错信息 selenium.common.exceptions.WebDriverException: Message: An unknown server-side error occurred w ...

  4. Linux System Calls Hooking Method Summary

    http://www.cnblogs.com/LittleHann/p/3854977.html http://www.cnblogs.com/cozy/articles/3175615.html h ...

  5. android system.img

    哥们要我做些模拟包,给过来的是mtk的底包,需要从system.img中提取部分文件. 网上一找资料,说是yaffs2文件系统,同时以前做linux的时候也是用yaffs2,感觉碰到老朋友了,不管三七 ...

  6. 图解Android - System Service 概论 和 Android GUI 系统

    通过 图解Android - Binder 和 Service 一文中,我们已经分析了Binder 和 Service的工作原理.接下来,我们来简要分析Android 系统里面都有哪些重要的Servi ...

  7. Android Capture Android System Audio

    项目需要获取播放视频的实时音量值,最简捷的方法是监听音频输出端,取得音频输出流,再进行转换. 调查时,首先找到这篇博客: http://blog.csdn.net/jinzhuojun/article ...

  8. Android System Property 解析

    一 System Property       今天在折腾HDMI 显示,为Setting提供接口,遇到非常多跟Android系统属性相关的问题.因此,顺便分析和总结一些. android的代码中大量 ...

  9. 安卓系统广播暴露设备信息-Android System Broadcasts Expose Device Information

    Android device details are being exposed to running applications via Wi-Fi broadcasts in the mobile ...

随机推荐

  1. UVA - 10048 Audiophobia (Floyd应用)

    题意:求出两点之间所有路径最大权值的最小值. 思路:转变一下Floyd的形式即可: 注意:注意初始化问题,还有UVA奇葩的输出形式. 代码如下: #include<iostream> #i ...

  2. 转:透析QTP自动化测试框架SAFFRON

    1.为什么要使用框架? 框架是一组自动化测试的规范.测试脚本的基础代码,以及测试思想.惯例的集合.可用于减少冗余代码.提高代码生产率.提高代码重用性和可维护性.例如QTestWare就是QTP自动化测 ...

  3. Selenium2+python自动化28-table定位

    前言 在web页面中经常会遇到table表格,特别是后台操作页面比较常见.本篇详细讲解table表格如何定位. 一.认识table 1.首先看下table长什么样,如下图,这种网状表格的都是table ...

  4. Django: 之Apache、Nginx部署以及发送邮件

    在这里讲述部署的方法和常见的问题,并给出了在BAE,JAE,SAE等上面部署的实例. Django + nginx + Gunicorn/uwsgi部署方式,参见另一篇:Django部署(nginx) ...

  5. Django: 之Model、Cookis、Session

    到目前为止,当我们的程序涉及到数据库相关操作时,我们一般都会这么搞: 创建数据库,设计表结构和字段 使用MySQLdb来连接数据库,并编写数据访问层代码 业务逻辑层去调用数据访问层执行数据库操作 im ...

  6. access restriction

    一.既然存在访问规则,那么修改访问规则即可.打开项目的Build Path Configuration页面,打开报错的JAR包,选中Access rules条目,选择右侧的编辑按钮,添加一个访问规则即 ...

  7. HDU 4287 Intelligent IME(字典树)

    在我没用hash之前,一直TLE,字符串处理时间过长,用了hash之后一直CE,(请看下图)我自从经历我的字典树G++MLE,C++AC以后,一直天真的用C++,后来的CE就是因为这个,G++才支持这 ...

  8. XHTML与HTML的差别

    HTML与XHTML之间的差别,粗略可以分为两大类比较:一个是功能上的差别,另外是书写习惯的差别.关于功能上的差别,主要是XHTML可兼容各大浏览器.手机以及PDA,并且浏览器也能快速正确地编译网页. ...

  9. 创建mysql数据库的时候指定编码

    CREATE DATABASE xxx DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;

  10. 创建表结构相同的表,表结构相同的表之间复制数据,Oracle 中 insert into XXX select from 的用法

    /**1. 用select 创建相同表结构的表*/create table test_tbl2 as select * from test_tbl1 where 1<>1; /**  2. ...