【发布时间】: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