/* ** Copyright (C) 2013 - Jonathan Salwan - http://twitter.com/JonathanSalwan ** ** This program is free software: you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation, either version 3 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program. If not, see . ** ** ** Little script to trace a specific kernel function and display ** arguments / call stack. Send your function name in the procfs ** node to start the tracing. Send 'none' in procfs node to stop ** the tracing. Below, a little example. ** ** # insmod ./trace.ko ** # cat /proc/trace_func ** Function traced : none ** # printf '__kmalloc' > /proc/trace_func ** # cat /proc/trace_func ** Function traced : __kmalloc ** # dmesg ** ... ** [ 1880.977375] ]=------------------------ ** [ 1880.977378] Function : __kmalloc ** [ 1880.977378] ** [ 1880.977380] args 0: 000000e0 args 1: 000000d0 args 2: cb0463c0 ** [ 1880.977382] args 3: 00000003 args 4: 00001812 args 5: 00000000 ** [ 1880.977382] ** [ 1880.977386] Pid: 6974, comm: dmesg Tainted: G O 3.5.7-gentoo #3 ** [ 1880.977387] Call Trace: ** [ 1880.977391] [] trace+0x74/0xa0 [trace] ** [ 1880.977396] [] load_elf_binary+0x83a/0x11c0 ** [ 1880.977400] [] ? _copy_from_user+0x3f/0x60 ** [ 1880.977404] [] ? elf_map+0xc0/0xc0 ** [ 1880.977408] [] search_binary_handler+0xc7/0x2c0 ** [ 1880.977413] [] do_execve+0x2f0/0x3a0 ** [ 1880.977418] [] sys_execve+0x32/0x70 ** [ 1880.977423] [] ptregs_execve+0x12/0x18 ** [ 1880.977427] [] ? sysenter_do_call+0x12/0x22 ** [ 1880.977429] ]= EOF ------------------- ** # printf 'none' > /proc/trace_func ** ** ** See: http://shell-storm.org/blog/Trace-and-debug-the-Linux-Kernel-functons/ ** ** Tested on 3.5.7 custom kernel with gentoo. ** */ #include #include #include #include #include static void trace(void *arg0, void *arg1, void *arg2, void *arg3, void *arg4, void *arg5); static struct jprobe jp = { .entry = JPROBE_ENTRY(trace), .kp = { .symbol_name = NULL, } }; static void trace(void *arg0, void *arg1, void *arg2, void *arg3, void *arg4, void *arg5) { printk("]=------------------------\n"); printk("Function : %s\n\n", jp.kp.symbol_name); printk("Arg0 : %08x Arg1 : %08x Arg2 : %08x\n", (unsigned int)arg0, (unsigned int)arg1, (unsigned int)arg2); printk("Arg3 : %08x Arg4 : %08x Arg5 : %08x\n\n", (unsigned int)arg3, (unsigned int)arg4, (unsigned int)arg5); dump_stack(); printk("]= EOF -------------------\n"); jprobe_return(); } static ssize_t handler_proc_write(struct file *file, const char __user *buffer, unsigned long count, void *data) { int ret; if (jp.kp.symbol_name) kfree(jp.kp.symbol_name); jp.kp.symbol_name = kzalloc(count +1, GFP_KERNEL); if (!jp.kp.symbol_name) return -ENOMEM; memcpy((void*)jp.kp.symbol_name, (void *)buffer, count); if (!memcmp(jp.kp.symbol_name, "none", 4)){ kfree(jp.kp.symbol_name); jp.kp.symbol_name = NULL; unregister_jprobe(&jp); printk("trace: Tracing stoped\n"); return count; } else if ((ret = register_jprobe(&jp)) < 0) { printk("trace: register_jprobe failed, returned %d\n", ret); kfree(jp.kp.symbol_name); jp.kp.symbol_name = NULL; return -ENOSYS; } printk("trace: %s traced\n", jp.kp.symbol_name); return count; } static ssize_t handler_proc_read(char *page, char **start, off_t off, int count, int *eof, void *data) { int ret; if (!jp.kp.symbol_name){ sprintf(page, "Function traced : none\n"); ret = 23; } else { sprintf(page, "Function traceed : %s\n", jp.kp.symbol_name); ret = strlen(jp.kp.symbol_name) + 20; } return ret; } static int __init mod_init(void) { struct proc_dir_entry *proc_entry; proc_entry = create_proc_entry("trace", 0666, NULL); if (proc_entry){ proc_entry->write_proc = handler_proc_write; proc_entry->read_proc = handler_proc_read; } return 0; } static void __exit mod_exit(void) { unregister_jprobe(&jp); remove_proc_entry("trace", NULL); } module_init(mod_init); module_exit(mod_exit); MODULE_LICENSE("GPL");