安卓内核定制开发笔记(二)系统调用拦截
安卓内核定制开发笔记(二)系统拦截
在第一篇文章中,我们已经已一加3T为例,尝试编译和刷入了官方内核,并且解决的一些驱动问题,接下来我们将在此基础上去实现对系统调用的Hook,以openat为例
Hook原理
系统调用是内核开放给用户态的"接口",而各个系统调用的地址被保存在系统调用表(sys_call_table)中,所以只需要修改对应调用号(例如openat)在系统调用表中的地址就可以实现对系统调用的hook了
开发流程
1.获取sys_call_table地址
方法一:
在内核编译页面,找到System.map,里面保存了编译后的符号地址,可以在内核编译输出目录直接执行以下命令
cat System.map |grep "sys_call_table"
方法二:
动态获取,内核版本4.4以下可以用
// 内核4.4以下可以用
static void getsyscall_table(void)
{
unsigned long *syscall_table;
unsigned long int i;
for (i = (unsigned long int)sys_close; i < ULONG_MAX; i += sizeof(void *))
{
syscall_table = (unsigned long *)i;
if (syscall_table[__NR_close] == (unsigned long)sys_close)
{
sys_call_table_func = (void **)syscall_table;
return;
}
}
}
2.编写内核模块
核心逻辑:
- 在syscall_table中查找目标调用号(openat)的地址
- 替换原调用号地址为自定义函数,并保存原地址
- 在自定义函数中实现hook逻辑,例如打印openat的地址
注意在卸载ko模块的时候需要恢复系统调用表中的调用号地址,否则系统会崩溃
核心代码如下
// 模块加载的时候调用
static int __init hook_init(void)
{
printk("Cube Module Init Version:%s\n", CUBE_MODULE_VERSION);
getsyscall_table();
if (!sys_call_table_func)
{
printk("syscall_table获取失败\n");
return 0;
}
// sys_call_table_func符号地址
sys_call_table_func = (void *)0xffffffc000d17000;
real_openat = (void *)(sys_call_table_func[__NR_openat]);
// 替换系统调用表openat函数地址
sys_call_table_func[__NR_openat] = (unsigned long *)new_openat;
return 0;
}
static void __exit hook_exit(void)
{
printk(KERN_ALERT "Cube Module Exit\n");
if (sys_call_table_func)
{
sys_call_table_func[__NR_openat] = (unsigned long *)real_openat;
cleanup_lines();
}
}
3.加载内核模块
开发阶段,可以使用root权限直接加载内核模块(需要内核开启动态加载)
将模块推送到设备,使用insmod命令即可,同理,如果需要卸载模块,使用rmmod命令
4.查看模块输出
使用dmesg命令可以查看模块的输出,在模块代码中可以使用printk函数进行日志打印
标题:安卓内核定制开发笔记(二)系统调用拦截
作者:Cubeeeee
地址:http://blog.nps.fuguicun.com/articles/2023/11/27/1701071668789.html