【问题标题】:Intercepting syscalls in Android kernel -- device reboots when module is removed在 Android 内核中拦截系统调用——移除模块时设备重启
【发布时间】:2014-10-01 01:40:19
【问题描述】:

我一直在尝试拦截 Android 内核中的读取系统调用(maguro 为 3.0.72)。我为此目的使用内核模块。一个例子如下:

#include <linux/module.h>
#include <linux/unistd.h>    

MODULE_LICENSE ("Dual BSD/GPL");

asmlinkage long
  (*orig_call_open) (const char __user * filename, int flags, int mode);

asmlinkage long
  (*orig_call_read) (unsigned int fd, char __user * buf, size_t count);

#define SYS_CALL_TABLE_ADDR 0xc0058828

void **sys_call_table;

asmlinkage long
new_sys_open (const char __user * filename, int flags, int mode)
{
  printk ("Calling my open\n");
  return orig_call_open (filename, flags, mode);
}

asmlinkage long
new_sys_read (unsigned int fd, char __user * buf, size_t count)
{
  printk ("Calling my read\n");
  return orig_call_read (fd, buf, count);
}

/* Module initialization and cleanup functions */

int
init_module ()
{
  sys_call_table = (void *) SYS_CALL_TABLE_ADDR;

  // save original function ptrs
  orig_call_open = (void *) sys_call_table[__NR_open];
  orig_call_read = (void*) sys_call_table[__NR_read];

  // replace existing functions with ours
  sys_call_table[__NR_open] = (unsigned long *) new_sys_open;
  sys_call_table[__NR_read] = (unsigned long *) new_sys_read;

  printk ("Initializing.\n");

  return 0;
}

void
cleanup_module ()
{
  sys_call_table[__NR_open] = (unsigned long *) orig_call_open;
  sys_call_table[__NR_read] = (unsigned long *) orig_call_read;
  printk ("Cleaning up.\n");

}

我可以正常使用insmod插入模块。但是,当我尝试删除它(使用 rmmod)时,内核会中断并且设备会重新启动。

[...]
[   80.512054] Unable to handle kernel paging request at virtual address bf000040
[   80.512145] pgd = c6d98000
[   80.512237] [bf000040] *pgd=85edc811, *pte=00000000, *ppte=00000000
[   80.512634] Internal error: Oops: 80000007 [#1] PREEMPT SMP
[   80.512725] Modules linked in: [last unloaded: privacy_capsules]
[   80.513061] CPU: 0    Not tainted  (3.0.72-gfb3c9ac-dirty #4)
[   80.513214] PC is at 0xbf000040
[   80.513336] LR is at sys_read+0x6c/0x78
[   80.513427] pc : [<bf000040>]    lr : [<c01533ec>]    psr: 200f0013
[...]

我还测试了其他系统调用(例如,仅适用于 sys_open 和 sys_write -- 而没有 sys_read)并且它工作正常(insmod 和 rmmod)。然而,这个问题似乎只发生在 sys_read 上。

有什么想法吗?非常感谢!

编辑:

这些是函数指针的地址:

orig_call_read : 0xc0153380
new_sys_read   : 0xbf000000

我拿了一段从模块代码生成的汇编代码:

00000000 <new_sys_read>:
   0:   e1a0c00d    mov ip, sp
   4:   e92dd878    push    {r3, r4, r5, r6, fp, ip, lr, pc}
   8:   e24cb004    sub fp, ip, #4
   c:   e1a04000    mov r4, r0
  10:   e3000000    movw    r0, #0
  14:   e3400000    movt    r0, #0
  18:   e1a06001    mov r6, r1
  1c:   e1a05002    mov r5, r2
  20:   ebfffffe    bl  0 <printk>
  24:   e3003000    movw    r3, #0
  28:   e3403000    movt    r3, #0
  2c:   e1a00004    mov r0, r4
  30:   e1a01006    mov r1, r6
  34:   e5933000    ldr r3, [r3]
  38:   e1a02005    mov r2, r5
  3c:   e12fff33    blx r3
  40:   e89da878    ldm sp, {r3, r4, r5, r6, fp, sp, pc}

因此,正如@ChrisStratton 建议的那样,一个阻塞的 read() 返回(在这种情况下,在 blx r3 之后),但找不到下一个地址 (0xbf000040)。

【问题讨论】:

  • 大声思考,如果你删除这个模块会发生什么,因此可能会触发包含你的包装函数的内核代码页的删除,而其他东西位于阻塞的 read() 调用中,并且然后尝试通过不再存在的代码返回?诚然,这个理论取决于内核没有任何保护措施,并且通过您的包装“不必要地”留下一个指向您的函数的实际堆栈框架的调用,而不是仅仅保存您自己的调用者的地址 - 我都没有我断言为事实。
  • 我很想打印你的包装器的地址,看看它是否与崩溃跟踪有关。
  • 谢谢@ChrisStratton,你是对的。我在其中添加了更多信息。

标签: android kernel kernel-module system-calls


【解决方案1】:

这确实看起来像是一个阻塞或延迟的读取调用,试图在卸载后通过您的代码返回。

我认为您在地址报告中交换了这两个函数的地址。

当原件试图在[&lt;bf000040&gt;] 处返回替换的最后一行时会发生故障,但由于您的模块已被卸载,这不再在内存中。我希望像这样的系统很容易有大量的读取调用,这些调用会长时间阻塞。

您可能需要在 sysfs 或类似的接口中实现一个接口,而不是卸载您的模块,您可以使用它来禁用新的重定向,同时将其留在内存中。

另一种选择是查看是否可以“跳转”而不是“调用”原件,以便原件的返回跳过您并直接返回给您的调用者。在 ARM 中,这将是一个没有链接的分支。查看代码,您似乎必须先清理本地堆栈,将寄存器恢复到代码开始时的状态,然后再进行跳转。

【讨论】:

    猜你喜欢
    • 2012-12-02
    • 2012-09-18
    • 2019-05-16
    • 1970-01-01
    • 1970-01-01
    • 2016-08-26
    • 2014-05-30
    • 2020-04-08
    • 2022-07-07
    相关资源
    最近更新 更多