最近在学习安卓goldfish的内核hook,把最近半个月的成果写个教程,供大家分享,希望大家不要走弯路。
关于源码下载、内核编译、动态插入内核等等再次不介绍了,可以参考其他的博客,里面写的很详细,在这里我主要介绍如何进行通讯录的调用hook:
应用环境:
本教程适用于,下载安卓模拟器的goldfish源码,然后自己编译运行,并且可以把虚拟机运行在上面。然后通过动态插入内核,可以验证已经安卓模拟器上已经假如了自己的内核。当然,这样做基本都是为了hook linux内核调用。然后写了自己的模块,用于当应用程序去读取安卓设备的通讯录时,去动态hook。
程序代码:
-----------------------------------------------------hook.c
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/unistd.h>
#include <linux/semaphore.h>
#include <asm/cacheflush.h>
#include <linux/string.h>
#include <net/sock.h>
#include <linux/netlink.h>
#include <linux/skbuff.h>
#include <stddef.h>
#define NETLINK_USER 16
struct sock *nl_sk = NULL;
void **sys_call_table;
asmlinkage int (*original_call_open) (const char*, int, int);
asmlinkage int (*original_call_read) (unsigned int, char*, int);
asmlinkage long (*sys_openat) (int, const char*, int, int);
asmlinkage long our_openat(int dfd, const char *filename, int flags, int mode){
printk("%s\n",filename);
return sys_openat(dfd,filename,flags,mode);
}
asmlinkage int our_sys_read(unsigned int fd, char * buf, int count){
if(fd == 0 && count == 1){
//printk("有文件正在被读取intercept 0x%02X", buf[0]);
}
return original_call_read(fd, buf, count);
}
asmlinkage int our_sys_open(const char* file, int flags, int mode)
{
//联系人 /data/data/com.android.providers.contacts/databases/contacts2.db
//通话记录 /data/data/com.android.providers.telephony/databases/telephony.db
//短信记录 /data/data/com.android.providers.telephony/databases/mmssms.db
char * contact = "/data/data/com.android.providers.contacts/databases/contacts2.db";
char * telephony = "/data/data/com.android.providers.telephony/databases/telephony.db";
char * sms = "/data/data/com.android.providers.telephony/databases/mmssms.db";
if (strcmp(file, contact) == 0 || strstr(file, contact) != NULL){ //这里一定要注意比较条件
printk("应用程序正在读取手机的联系人记录!!!\n");
}
if (strcmp(file, telephony) == 0){
printk("应用程序正在读取手机的通话记录!!!\n");
}
if (strcmp(file, sms) == 0){
printk("应用程序正在读取手机的短信记录!!!\n");
}
return original_call_open(file, flags, mode);
}
//用来接受用户态发来的消息
void hello_nl_recv_msg(struct sk_buff *skb)
{
struct nlmsghdr *nlh;
int pid;/data/data/com.android.providers.contacts/databases/contacts2.db
struct sk_buff *skb_out;
int msg_size;
char *msg = "Hello from kernel";
int res;
printk(KERN_INFO "Entering: %s\n", __FUNCTION__);
msg_size = strlen(msg);
nlh = (struct nlmsghdr *)skb->data;
printk(KERN_INFO "Netlink received msg payload:%s\n", (char *)nlmsg_data(nlh));
pid = nlh->nlmsg_pid; //pid of sending process
skb_out = nlmsg_new(msg_size, 0);
if (!skb_out)
{
printk(KERN_ERR "Failed to allocate new skb\n");
return;
}
nlh = nlmsg_put(skb_out, 0, 0, NLMSG_DONE, msg_size, 0);
NETLINK_CB(skb_out).dst_group = 0; //not in mcast group
strncpy(nlmsg_data(nlh), msg, msg_size);
res = nlmsg_unicast(nl_sk, skb_out, pid);
if (res < 0)
printk(KERN_INFO "Error while sending bak to user\n");
}
static void disable_page_protection(void){
// unsigned long value = 0xc000de04;
// asm volatile ("mov %%cr0,%0" : "=r" (value));
// if(value & 0x00010000){
// value &= ~0x00010000;
// asm volatile("mov %0,%%cr0" : : "r" (value));
// }
}
void set_addr_rw(unsigned long addr){
//unsigned int lever;
//pte_t *pte = lookup_address(addr,&level);
//if(pte->pte &~ _PAGE_RW)
// pte->pte |= _PAGE_RW;
}
int set_page_rw(long unsigned int _addr)
{
// struct page *pg;
//pgprot_t prot;
// pg = virt_to_page(_addr);
// prot = VM_READ | VM_WRITE;
// return change_page_attr(pg, 1, prot);
// return set_memory_rw(PAGE_ALIGN(_addr) - PAGE_SIZE, 1);
// unsigned int level;
// pte_t * pte = lookup_address(address,&level);
// if(pte->pte &~ _PAGE_RW)
// pte->pte |= _PAGE_RW;
// return 0;
//return set_memory_rw(PAGE_ALIGN(_addr) - PAGE_SIZE, 1);
}
int init_module()
{
// sys_call_table address in System.map
sys_call_table = (void*)0xc0022f24;
original_call_open = sys_call_table[__NR_open];
original_call_read = sys_call_table[__NR_read];
sys_openat = sys_call_table[__NR_openat];
// set_page_rw(sys_call_table);
// set_addr_rw(sys_call_table);
// disable_page_protection();
sys_call_table[__NR_open] = our_sys_open;
sys_call_table[__NR_read] = our_sys_read;
sys_call_table[__NR_openat] = our_openat;
// printk("Entering: %s\n", __FUNCTION__);
// nl_sk = netlink_kernel_create(&init_net, NETLINK_USER, 0, &hello_nl_recv_msg, NULL, THIS_MODULE);
// nl_sk = netlink_kernel_create(NETLINK_USER, input);
//if (!nl_sk)
//{
// printk(KERN_ALERT "Error creating socket.\n");
// return -10;
//}
return 0;
}
void cleanup_module()
{
// Restore the original call
sys_call_table[__NR_open] = original_call_open;
sys_call_table[__NR_read] = original_call_read;
sys_call_table[__NR_openat] = sys_openat;
printk(KERN_INFO "exiting hello module\n");
netlink_kernel_release(nl_sk);
}
//MODULE_LICENSE("GPL");
//module_init(init_module);
//module_exit(cleanup_module);
--------------------------------------------------------Makefile
obj-m := sys-hook.o
sys-hook-objs := hook.o #由于我们的模块叫做hello-yf,所以写hello-yf-objs :=表示该模块由N个模块组成,例如hello-yf-objs := file1.o file2.o
KID :=~/android-kernel-2.6/goldfish
PWD := $(shell pwd) #表示当前Makefile所在的路径
ARCH=arm
CROSS_COMPILE=arm-eabi-
CC=$(CROSS_COMPILE)gcc
LD=$(CROSS_COMPILE)ld
all:
make -C $(KID) ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) M=${PWD} modules
#M=表示在建立模块target的时候,makefile回归到我们模块程序的目录。
clean:
rm -rf *.o .cmd *.ko *.mod.c .tmp_versions *.order *.symvers
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
关于代码部分,我只想说一个地方:
if (strcmp(file, contact) == 0 || strstr(file, contact) != NULL){
printk("应用程序正在读取手机的联系人记录!!!\n");
}
大家一定注意,这里一定是用strstr和strcmp同时去比较,他们之间的关系是“或”的关系。因为应用程序读取用户通讯录的时候不是去死板的调用
"/data/data/com.android.providers.contacts/databases/contacts2.db"这个目录,而是去调用这个数据库的其中几个表和几个字段,这里不赘述了,所以如果只是比较strcmp()的话,并不能准确的hook,这个问题我纠结了三天终于想到了问题的症结。另外,我把部分代码的注释用‘//’加了注释。
最后,希望大家看到这个教程之后,能够顺利的hook,少走弯路。