Linuxはカーネル2.6系からシステムコールのテーブルを使って任意のシステムコールをフックしづらくなったのでその方法のメモ。
#configのメニューでどこかをいじれば2.4系と同じようにもできるらしいけど。
テスト環境はx86_64マシン。
やってることはmmap()をフックして、エラーを返すだけのサンプル。
これのfind_sys_call_table()は、前にあったセキュリティホールで、システムコールを無効化するパッチが使っていた方法をそのまま利用。
find_sys_call_table()で使っている0xffffffff80200000は、sys_XXX()がある場所を大体で決め打ち。
この辺のアドレスは/proc/kallsymsを参照。
つーか、ちゃんとやるならフックしたい関数のアドレスを/proc/kallsymsで調べて、Makefile内でsedあたりで書き換えれば良い気がする。
find_sys_call_table()が肝なくらいであとは見ればわかると思います。。
#include <linux/module.h> #include <linux/kernel.h> #include <asm/current.h> #include <linux/syscalls.h> #include <linux/smp_lock.h> #include <linux/mman.h> MODULE_AUTHOR("hoge <hoge@hoge>"); MODULE_DESCRIPTION("System call hook module sample"); MODULE_LICENSE("GPL"); static unsigned long orig_sys_mmap_addr; static unsigned long **syscalls; static unsigned long **find_sys_call_table(void); static long call_orig_sys_mmap(unsigned long addr, unsigned long len, unsigned long prot, unsigned long flags, unsigned long fd, unsigned long off); /* original sys_mmap's address */ asmlinkage long (*orig_sys_mmap)(unsigned long addr, unsigned long len, unsigned long prot, unsigned long flags, unsigned long fd, unsigned long off); /* own sys_mmap */ asmlinkage long syscall_hook_sys_mmap(unsigned long addr, unsigned long len, unsigned long prot, unsigned long flags, unsigned long fd, unsigned long off); static int target_pid = -1; module_param(target_pid, int, S_IRUGO | S_IWUSR); MODULE_PARM_DESC( target_pid, "A pid which is test target"); /* * Get the system call table address */ static unsigned long **find_sys_call_table(void) { unsigned long **sctable; unsigned long ptr; sctable = NULL; for (ptr = (unsigned long) 0xffffffff80200000; ptr < (unsigned long) &loops_per_jiffy; ptr += sizeof(void *)) { unsigned long *p; p = (unsigned long *)ptr; if (p[__NR_close] == (unsigned long) sys_close) { sctable = (unsigned long **)p; return &sctable[0]; } } return NULL; } static long call_orig_sys_mmap(unsigned long addr, unsigned long len, unsigned long prot, unsigned long flags, unsigned long fd, unsigned long off) { long ret = -EINVAL; if (orig_sys_mmap) ret = orig_sys_mmap(addr, len, prot, flags, fd, off); return ret; } asmlinkage long syscall_hook_sys_mmap(unsigned long addr, unsigned long len, unsigned long prot, unsigned long flags, unsigned long fd, unsigned long off) { long ret = -EINVAL; if (target_pid == -1 || current->pid != target_pid) { printk("do real sys_mmap\n"); ret = call_orig_sys_mmap(addr, len, prot, flags, fd, off); } else { if (flags & MAP_ANONYMOUS) { printk("return false value %d\n", -EBADF); printk("pid=[%i]\n", current->pid); ret = -EBADF; } else { ret = call_orig_sys_mmap(addr, len, prot, flags, fd, off); } } return ret; }; int __init syscall_hook_init(void) { syscalls = find_sys_call_table(); if (!syscalls) { printk("Couldn't find a system call table\n"); return -1; } /* set our own system call */ orig_sys_mmap_addr = (unsigned long) syscalls[__NR_mmap]; syscalls[__NR_mmap] = (void *) &syscall_hook_sys_mmap; /* remember original address */ orig_sys_mmap = (long (*)(unsigned long, unsigned long, unsigned long, unsigned long, unsigned long, unsigned long)) orig_sys_mmap_addr; printk("syscall_hook kernel module loaded with pid=[%i]\n", current->pid); printk("sys_mmap's address is %lx\n", orig_sys_mmap_addr); return 0; } void __exit syscall_hook_exit(void) { syscalls[__NR_mmap] = (long *) orig_sys_mmap_addr; printk("syscall_hook kernel module ended with pid=[%i]\n", current->pid); printk("sys_mmap's address is %p\n", syscalls[__NR_mmap]); } module_init(syscall_hook_init); module_exit(syscall_hook_exit); MODULE_DESCRIPTION( "syscall_hook" ); MODULE_LICENSE( "GPL2" );
obj-m := syscall_hook.o KDIR := /lib/modules/$(shell uname -r)/build PWD := $(shell pwd) default: all all: $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules clean: rm -f *.o *.ko *~ .syscall_* Module.symvers modules.order syscall_hook.mod.c
テストは下のコードを適当にコンパイルして、バックグラウンドで実行させて、pidを調べておく
$ gcc mmap_test.c -o mmap_test
$ ./mmap_test&
そしたら、カーネルモジュールを引数付きでinsmodすればおk。
# insmod ./syscall_hook.ko target_pid=XXX
そうするとこんな感じでログが残ってる
[ 4007.334971] syscall_hook kernel module ended with pid=[13443] [ 4007.334975] sys_mmap's address is ffffffff80218c83 [ 4098.555782] syscall_hook kernel module loaded with pid=[13458] [ 4098.555790] sys_mmap's address is ffffffff80218c83 [ 4098.965210] do real sys_mmap [ 4099.637249] return false value -9 [ 4099.637255] pid=[13452] [ 4099.637394] return false value -9 [ 4099.637397] pid=[13452] [ 4099.638053] do real sys_mmap [ 4099.966346] do real sys_mmap
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/stat.h> #include <sys/types.h> #include <fcntl.h> #include <sys/mman.h> #include <assert.h> void *mmap_fd(void) { void *p; int fd = open("./mmap_test.c", O_RDONLY); assert(fd >= 0); p = mmap(NULL, 1024, PROT_READ, MAP_SHARED, fd, 0); close(fd); return p; } void *mmap_anonymous(void ) { return mmap(NULL, 1024, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); } int main(int argc, char **argv) { void *p; unsigned long i = 0; char *msg; while (1) { if ((i % 2) == 0) { p = mmap_anonymous(); msg = "mmap anonymous"; } else { p = mmap_fd(); msg = "mmap fd"; } if (p != MAP_FAILED) { printf("mmap success %s - %ld\n", msg, i++); munmap(p, 1024); } else { perror("mmap"); exit(EXIT_FAILURE); } sleep(1); } return 0; }