多种方法获取sys_call_table(linux系统调用表)的地址

简介:

一.方法一:常用方式,也是一google一堆的方式

我们首先需要找到call table-with-offset的特征,先看下面的代码

syscall_call:
        call *sys_call_table(,%eax,4)
假设我们没有vmlinux可供gdb反汇编,那也只有采用模拟的方式了,模拟出一个call *sys_call_table(,%eax,4),然后看其机器码,然后在system_call的附近基于这个特征进行寻找 :
 
然后用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的方法。为了不让人说我是胡扯的,贴上代码:
 
然后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.代码: 
 
3.5.此方法不需要获取system_call的地址。 
四.方法四:通过/dev/mem在用户态完成 
这种方式不会污染运行中的内核(不会载入任何模块),然而弄不好很容易PTD(panic to death,对应windows的blue screen...)。这种方式实际上是最直接的,相当于直接使用机器码对整个物理内存编程,需要相当高的水平。不过,整个内存都拥有了,还有什么做不到呢?
     之所以可以动态修改机器码,是因为冯诺依曼机器是基于存储模型的,指令和数据一样是存储在内存中的,而内存是可存取的 ,虽然现代机器架构使用了保护模型比如内存存取权限或者特权环等机制限制了某些存取,但是却无法从根本上改变冯诺依曼模型的存取特征,因为在任何领域,对于主体的鉴权 都是有缺陷了,比如一旦有特权的主体被以某种方式劫持了,那么它的行为将是危险的和有害的(暂且不考虑禁止向下写等单向信息鉴权模型,那样会引入新的复杂性和新的不确定因素),所以模型决定了一切而不是局部的设计决定了一切。 
五:总结 
1.不赞成替换系统调用。 因为这是linux,不是windows,你既然能编译并加载模块,说明你有root权限,既然你有root权限,替换一个系统调用毫无疑义,说明不了你的水平
2.不赞成用gdb反汇编内核, 我们需要的仅仅是一个地址值而已,没有必要那么麻烦反汇编内核。
3.我所作的一切只是为了调试新添加的系统调用而不希望重新编译内核,并不是搞攻击 ,如果真的搞攻击,最难的不是写代码,是如何发现漏洞以及如何利用漏洞,首先你不是root,弄到最后你成了root,接下来你就可以做替换系统调用这种简单至极的事了,前提是你怎么从非root成为root?最简单也是最难的攻击办法就是:直接逼问管理员!




 本文转自 dog250 51CTO博客,原文链接:http://blog.51cto.com/dog250/1271018

相关文章
|
6天前
|
Linux Shell
Linux系统下快速批量创建和删除文件的方法
总的来说,使用shell脚本来批量处理文件是一种非常强大的工具,只要你愿意花时间学习和实践,你会发现它能大大提高你的工作效率。
54 19
|
23天前
|
JavaScript Linux Python
在Linux服务器中遇到的立即重启后的绑定错误:地址已被使用问题解决
总的来说,解决"地址已被使用"的问题需要理解Linux的网络资源管理机制,选择合适的套接字选项,以及合适的时间点进行服务重启。以上就是对“立即重启后的绑定错误:地址已被使用问题”的全面解答。希望可以帮你解决问题。
82 20
|
19天前
|
Ubuntu Linux 网络安全
如何在Linux中更改主机名?修改主机名最新方法
本期教程将指导您如何在Linux系统中更改主机名。主机名是Linux系统的常用功能,用于识别服务器,帮助区分不同服务器,并与网络进程和其他应用程序协同工作。教程涵盖显示当前主机名的方法(通过`hostname`命令),以及在CentOS 7、Debian 9和Ubuntu 16.04及以上版本中更改主机名的步骤(使用`hostnamectl set-hostname`命令)。对于其他Linux版本,可编辑`/etc/hostname`文件实现更改。记得重启相关服务或服务器以使更改生效!
118 12
|
21天前
|
安全 Linux 网络安全
在Linux(CentOS和AWS)上安装更新的git2的方法并配置github-ssh
经过以上这些步骤,你现在就能在GitHub上顺利往返,如同海洋中的航海者自由驰骋。欢迎你加入码农的世界,享受这编程的乐趣吧!
50 10
|
24天前
|
缓存 Linux
如何创建Linux交换文件?Linux交换文件最新创建方法
Swap是Linux中的虚拟内存空间,用于在物理内存不足时将非活动进程移至磁盘,从而优化活动进程的性能。通过创建交换文件(如1GB),可灵活调整交换空间而无需重新分区。步骤包括:使用`fallocate`或`dd`创建文件、设置权限 (`chmod 600`)、格式化 (`mkswap`)、启用交换 (`swapon`)、修改`/etc/fstab`以持久化配置,以及调整`vm.swappiness`值(默认60,建议从10开始)来平衡内存与交换的使用。最后通过`swapon -s`检查状态并重启生效。此方法适用于VPS和专用服务器,需以root用户操作。
47 2
|
2月前
|
存储 网络协议 Linux
【Linux】进程IO|系统调用|open|write|文件描述符fd|封装|理解一切皆文件
本文详细介绍了Linux中的进程IO与系统调用,包括 `open`、`write`、`read`和 `close`函数及其用法,解释了文件描述符(fd)的概念,并深入探讨了Linux中的“一切皆文件”思想。这种设计极大地简化了系统编程,使得处理不同类型的IO设备变得更加一致和简单。通过本文的学习,您应该能够更好地理解和应用Linux中的进程IO操作,提高系统编程的效率和能力。
125 34
|
6月前
|
Ubuntu Linux 网络安全
在Linux上安装软件有多种方法
在Linux上安装软件有多种方法
208 64
|
5月前
|
Linux Shell 数据库
文件查找是Linux用户日常工作的重要技能介绍了几种不常见的文件查找方法
文件查找是Linux用户日常工作的重要技能。本文介绍了几种不常见的文件查找方法,包括使用`find`和`column`组合、`locate`和`mlocate`快速查找、编写Shell脚本、使用现代工具`fd`、结合`grep`搜索文件内容,以及图形界面工具如`Gnome Search Tool`和`Albert`。这些方法能显著提升文件查找的效率和准确性。
121 2
|
6月前
|
网络协议 Linux 调度
深入探索Linux操作系统的心脏:内核与系统调用####
本文旨在揭开Linux操作系统中最为核心的部分——内核与系统调用的神秘面纱,通过生动形象的语言和比喻,让读者仿佛踏上了一段奇妙的旅程,从宏观到微观,逐步深入了解这两个关键组件如何协同工作,支撑起整个操作系统的运行。不同于传统的技术解析,本文将以故事化的方式,带领读者领略Linux内核的精妙设计与系统调用的魅力所在,即便是对技术细节不甚了解的读者也能轻松享受这次知识之旅。 ####
|
6月前
|
运维 Linux
Linux查找占用的端口,并杀死进程的简单方法
通过上述步骤和命令,您能够迅速识别并根据实际情况管理Linux系统中占用特定端口的进程。为了获得更全面的服务器管理技巧和解决方案,提供了丰富的资源和专业服务,是您提升运维技能的理想选择。
290 1
下一篇
oss创建bucket