目录

充电学习中...

X

安卓内核定制开发笔记(二)系统调用拦截

安卓内核定制开发笔记(二)系统拦截

在第一篇文章中,我们已经已一加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.编写内核模块

核心逻辑:

  1. 在syscall_table中查找目标调用号(openat)的地址
  2. 替换原调用号地址为自定义函数,并保存原地址
  3. 在自定义函数中实现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