一.方法一:常用方式
我们首先需要找到call table-with-offset的特征,先看下面的代码
syscall_call:
call *sys_call_table(,%eax,4)
假设我们没有vmlinux可供gdb反汇编,那也只有采用模拟的方式了,模拟出一个call *sys_call_table(,%eax,4),然后看其机器码,然后在system_call的附近基于这个特征进行寻找 :
void fun1() { printf("fun1\n"); } void fun2() { printf("fun2\n"); } unsigned int sys_call_table[2] = {fun1, fun2}; int main(int argc, char **argv) { asm("call *sys_call_table(,%eax,4) \n\t"); }
然后用objdump进行dump可见下面一行:
080483ac <main>:
...
80483bc: ff 14 85 1c 95 04 08 call *0x804951c(,%eax,4)
...
于是ff 14 85 后面就是sys_call_table的地址,注意大小端,x86机器是小端机器,因此是反着的。如果system_call也不知道,比如不能挂载procfs,并且也没有System.map,那么就只有通过中断描述符来先获取system_call的地址了,方法如下:
0.你必须知道中断描述符的结构以及有中断描述符寄存器这么一回事。不过就算不知道也比较好查,google即可;
1.通过sidt指令获取中断描述符的基地址;
2.将这个地址加上8*0x80就是系统调用描述符的地址了;
3.从这个描述符中取出系统调用处理程序即system_call地址的高16位和低16位,拼接在一起即可。
二.方法二:使用dump_stack
写一个很简单的内核模块,内部调用dump_stack ,然后就可以看到:
[<f88f300b>] init_module+0xb/0x53 [gettable]
[<c013adc4>] sys_init_module+0x104/0x250
[<c010620b>] syscall_call+0x7/0xb
既然看到了syscall_call+0x7的地址,那么也就知道了标号syscall_call的地址,而我们需要找的sys_call_table的地址就在它下面地址的指令中 ,对于2.6.8内核而言,就是它下面的第一条指令:
syscall_call:
call *sys_call_table(,%eax,4)
实际上syscall_call这个标号可以在/proc/kallsym中取到的,如果没有procfs再使用dump_stack的方法。为了不让人说我是胡扯的,贴上代码:
int __init rm_init(void){ dump_stack(); unsigned char *ptr=0xc010620b-0x7; //0xc010620b这个地址是从dump_stack中取到的,这里将两个模块合并成了一个 int i; for (i = 0; i < 8; i ++) { printk("%02x ", (unsigned char)*(ptr+i)); } return 0; } void __exit rm_exit(void){ } module_init(rm_init); module_exit(rm_exit); MODULE_LICENSE("GPL");