`
totoxian
  • 浏览: 1027658 次
  • 性别: Icon_minigender_2
  • 来自: 西安
文章分类
社区版块
存档分类
最新评论

多种方法获取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?最简单也是最难的攻击办法就是:直接逼问管理员!

分享到:
评论

相关推荐

    自己的看书总结,安防系统设备端的书书籍,哈哈哈

    IPC设备的前端软件应用于linux操作系统,...Linux实现系统调用首先要有一个表来存放所有与用户态函数对应的系统态的函数(sys_call_table), 调用过程:user_func() --&gt; int 0x80 (user_func对应的系统态函数在表中的

    linux下2.6.32的rootkit,隐藏文件目录

    使用的是enyelkm v1.1本身的获取系统调用表的过程。 隐藏文件、目录通过修改sys_call_table第220项。

    基于Linux内核的键盘模拟实现

    一般当前系统的系统调用作为一张表sys_call_table进行定义的,是由指向实现各种系统调用的内核函数的函数指针组成的表。具体参数参见Linux内核源代码arch/i386/kernel/entry.S文件中: ENTRY(sys_call_table) l long...

    linux2.6.32下rootkit,仿照all_root

    all_root只能用于2.4版本。这里改到适用于2.6.32版本。 使用一个脚本获得sys_call_table地址,并传给可加载模块,可加载模块替换系统调用getuid,全给root权限。

    Linux Kernel代码艺术?数组初始化

     const sys_call_ptr_t sys_call_table[__NR_syscall_max+1] = {  /*  * Smells like a compiler bug — it doesn't work  * when the & below is removed.  */  [0 … __NR_syscall_max] = &sys_ni_...

    Android-Rootkit:适用于 Android 的 rootkit。 基于 Phrack Issue 68 的“Android platform based linux kernel rootkit”

    文件名: sys_call_table.ko描述:这个 rookit 是为了拦截以下调用而开发的 SYS_WRITE SYS_READ SYS_CREAT SYS_MKDIR SYS_RMDIR SYS_KILL SYS_OPEN SYS_CLOSE SYS_GETDENT SYS_UNLINK SYS_KILL

    syscall-hooks

    sys_call_table 中的索引可以在/usr/include/x86_64-linux-gnu/asm/unistd{,_64}.h或/usr/include/asm/unistd_{32,64}.h找到,具体取决于发行版。 索引和函数之间的匹配可以在/usr/src/$(uname -r)/arch/x86/...

    linux内核 0.11版本源码 带中文注释

    // Linux 的系统调用中断0x80。该中断是所有系统调用的 // 入口。该条语句实际上是int fork()创建进程系统调用。 // syscall0 名称中最后的0 表示无参数,1 表示1 个参数。 static inline _syscall0 (int, pause...

    oracle实验报告

    例1 定义一个人事信息管理系统中存放职工基本信息的一张表。可输入如下命令: SQL&gt;CREATE TABLE employee (empno number(6) PRIMARY KEY, /* 职工编号 name varchar2(10) NOT NULL, /* 姓名 deptno number(2) ...

    uboott移植实验手册及技术文档

    了解 U-Boot-1.3.1 的代码结构,掌握其移植方法。 【实验环境】 1、Ubuntu 7.0.4发行版 2、u-boot-1.3.1 3、FS2410平台 4、交叉编译器 arm-softfloat-linux-gnu-gcc-3.4.5 【实验步骤】 一、建立自己的平台...

    python入门到高级全栈工程师培训 第3期 附课件代码

    12 call方法 13 迭代器协议 14 迭代器协议实现斐波那契数列 16 描述符答疑 17 描述符优先级 18 软件开发规范 19 pycharm干的好事 第28章 01 上节课复习 02 上下文管理协议 04 异常的构成简单了解 05 描述符应用 08...

    网管教程 从入门到精通软件篇.txt

    如果系统检测到无效或非标准分区表标记,将提示用户是否继续执行该命令。除非您访问驱动器有问题,否则不要继续进行。向系统分区写入新的主引导记录可能破坏分区表并导致分区无法访问。  format  将指定的驱动器...

Global site tag (gtag.js) - Google Analytics