然后dmesg的结果如下:
[<f88e000b>] init_module+0xb/0x50 [gettable]
[<c013adc4>] sys_init_module+0x104/0x250
[<c010620b>] syscall_call+0x7/0xb
ff 14 85 1c ad 2d c0 89
在这个方法中,即使不能从procfs中获取任何信息,还是可以使用dump_stack的,就算有一天这个函数也不能用了,那怎么办呢?很好办,在模块中故意访问一个NULL指针,然后内核就算替你打印stack了 ...逼到最后,大不了遍历所有的memory(通过/dev/mem?或者/proc/kcore?),然后从中查找匹配的机器码,这是最后的办法,即使这样,我们也可以肯定这个地址不会太靠后的。
三.方法三:直接使用栈结构 获取
这种方式不是那么直观,然而却很直接,在x86机器上,我们知道栈的重要性,栈保存了函数调用的路径,它就是程序执行流的家 ,任何后续需要的本执行流都有一个栈。对于内核模块而言,在insmod加载它并初始化的时候,这个栈是存在的,实际上就是insmod进程的内核栈。我们可以顺着这个内核栈来向上回溯 ,直到找到call *sys_call_table(,%eax,4)的下一跳指令的地址,这有个基本原则,那就是我们知道在调用call指令的时候,需要将下一条指令的地址压入栈(注意是地址),因此这个call *sys_call_table(,%eax,4)指令的下一条指令的地址肯定能在回溯的途中遇到,既然找到了call的下一条指令,那么往上一条指令不就是call吗?既然找到了call指令,通过分析Intel的指令格式,我们就能抽出sys_call_table的地址。
3.1.如何判断谁是call指令的下一条地址
答曰:在加载内核的时候,内核的text段被载入到了0xC0000000 + 0x100000这个地址 ,这是通过vmlinux.lds链接文件知道的,并且system_call这个0x80的entry直到call sys_call_table(,%eax,4)没有调用任何call指令(在正常的前提下,既然已经到了模块的init函数,当然正常了),而system_call这个entry是insmod进程切到内核栈的第一条指令,因此内核栈到此为止,因此从回溯的末尾开始,第一个遇到的0xc01XXXXX附近的值就是了。
3.2.前置知识
想这么干,并不需要知道内核栈的结构以及current宏的相关知识,不过理解了也没什么坏处!
3.3.如何找到call指令中的sys_call_table值
答曰:通过源代码知道这是一条:CALL dword ptr [REG*SCALE+BASE]
查阅Intel的指令手册或者google前人发现的捷径,可以知道这类指令是带有SIB的call指令,应该是FF 14 xx的样子,因为base是一个地址,因此xx就应该是85,这是从intel提供的一张表中获取的,从而最终,这条指令就应该是FF 14 XX Y1 Y2 Y3 Y4 这个样子,于是从找到的call指令的下一条指令地址直接减去4之后就能获取Y1 Y2 Y3 Y4了,而这就是最终需要的sys_call_table
3.4.代码:
#include <linux/init.h> #include <linux/kernel.h> #include <linux/module.h> int __init rm_init(void){ unsigned long addr; unsigned long *stack = &addr; int j = 128; //这个值足够了 while (j-- > 0) { addr = *stack++; printk("sssss:%x\n", addr); } return 0; } void __exit rm_exit(void){ } module_init(rm_init); module_exit(rm_exit); MODULE_LICENSE("GPL");
3.5.此方法不需要获取system_call的地址。