`
kongweile
  • 浏览: 508207 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类
最新评论

绕过 libc 直接使用系统调用

 
阅读更多

 http://blog.openrays.org/blog.php?do=showone&tid=422

系统调用是用户态进程切入内核态的唯一入口,是内核为用户态进程提供服务的接口。Linux Kernel 提供了大约300个左右的系统调用,作为用户空间进程访问内核的 API。

C 语言环境下,经常使用的系统调用(如getpid)是系统基础库 libc 封装过的,也就是系统调用的实现库是libc,在其后面究竟隐藏着什么样的秘密呢?


1. 先看一个例子:

#include <stdio.h>

int main()
{
    long ID1;

    ID1 = getpid();
    printf("pid=%d\n", ID1);

    return 0;
}

存为 sc.c,如下命令编译之:

gcc sc.c -o sc -static


看看它的汇编码:

objdump -d sc > sc.S

查看 sc.S 我们发现 getpid 被 libc 替换成 __getpid,而这个函数只有下面的 3 行指令:

0804e0d0 <__getpid>:
804e0d0:   b8 14 00 00 00       mov   $0x14,%eax
804e0d5:   cd 80             int   $0x80
804e0d7:   c3               ret

将 0x14 置于 eax 后,触发 0x80 号软件中断。

这个 0x80 号软件中断服务程序,就是内核系统调用的统一入口。

至于如何区分具体的是哪个系统调用,则通过 eax 传过来的整型值来区分。内核定义了一个系统调用表,表的每项是一个函数指针,指向具体的实现函数,每个系统调用函数在表中的位置固定,其序号 (从 0 开始计数)就是该系统调用的系统调用号。实际上就是一个元素固定的数组。如 i386 内核,系统调用表名为 sys_call_table,则 sys_call_table[20] 就是 getpid 的函数指针:

[arch/i386/kernel/syscall_table.S]

ENTRY(sys_call_table)
  .long sys_restart_syscall   /* 0 - old "setup()" system call, used for restarting */
  .long sys_exit
  .long sys_fork
  .long sys_read
  .long sys_write
  .long sys_open     /* 5 */
  .long sys_close
  .long sys_waitpid
  .long sys_creat
  .long sys_link
  .long sys_unlink   /* 10 */
  .long sys_execve
  .long sys_chdir
  .long sys_time
  .long sys_mknod
  .long sys_chmod   /* 15 */
  .long sys_lchown16
  .long sys_ni_syscall   /* old break syscall holder */
  .long sys_stat
  .long sys_lseek
  .long sys_getpid   /* 20 */
  .long sys_mount

...

int 0x80 触发,使内核进入相应的处理程序,在保存了寄存器后,就使用 eax 的值索引 sys_call_table,得到地址后,跳转过去就进入了实质的服务函数。

因此,我们可以改写上面的程序为:

#include <stdio.h>

int main()
{
    long ID1;

    //ID1 = getpid();
    asm volatile(
              "int $0x80\n\t"
              : "=a" (ID1)     /* eax 亦是返回值所在,因此要与ID1 对应 */
              : "0" (20)         /* 20 置入 eax */
        );

    printf("pid=%d\n", ID1);

    return 0;
}

编译 gcc sc.c -o sc,执行之,结果是一样的。

上面简单的系统调用不需要传递参数,当需要传递 3 个参数时,那么如何处理呢?


2. 参数传递 

i386 内核实现系统调用时有一个参数传递约定:

ebx --- 置第一个参数
ecx --- 置第二个参数
edx --- 置第三个参数
esi --- 置第四个参数
edi --- 置第五个参数
ebp --- 置第六个参数 (系统调用最大参数个数为6)

特别注意,使用 ebp 前要先将其进栈, int 0x80 返回后,要恢复之。



3. 使用标准宏

上面使用嵌入式汇编不是很方便,可以使用 POSIX 标准约定的一系列宏:
    
    #include <linux/unistd.h>

    _syscallX(type, name, type1, arg1, type2, arg2, ...)

其中 X 取值 0~5 表示系统调用的参数个数
name 为系统调用的名字,如 getpid, read 等
type1 为第一个参数类型
arg1 为第一个参数值
type2 为第二个参数类型
arg2 为第二个参数值
...

很显然,使用这些宏,要对所用之系统调用的参数个数和类型有清晰的了解。

如上面的程序改写为:

#include <stdio.h>
#include <linux/unistd.h>

_syscall0(int, getpid);

int main()
{
    long id = getpid();
    printf("id = %d\n", id);

    return 0;
}

就绕过了 libc。可以使用 gcc -E sc.c > sc.C 看看预处理后的结果,就知道这个_syscall 宏做了什么事了。

这些宏定义于内核源码目录的 include/asm-i386/unistd.h 下,抑或 /usr/include/asm-i486/unistd.h 下。


4. 查看系统调用号

可以查看内核源码目录的 include/asm-i386/unistd.h,该文件中定义了一系列以 __NR_ 开头的常量,紧随其后的即为系统调用名,相应的即为系统调用号。

分享到:
评论

相关推荐

    libc库和系统调用

    libc库和系统调用

    实验五Linux系统调用的编程技术

    libc 提供的 API 可能直接提供一些用户态的服务,并不需要通过系统调用与内核打交道,比如一些数学函数等,但涉及与内核空间进行交互的 API 内部会封装系统调用。一个 API 可能只对应一个系统调用,也可能内部由多个...

    arm64 libc 库。可解决libc版本过低问题。

    arm64 libc 库。可解决libc版本过低问题。arm64 libc 库。可解决libc版本过低问题。

    Open BSD libc 源码

    OpenBSD libc 是 OpenBSD 操作系统自带的 C 标准库实现,它与其他 C 标准库有着一些显著的区别和特点,下面是一些主要的特点: ...OpenBSD libc 专注于开发高效的系统调用和底层操作,以充分利用操作系统的功能和

    libc-2.24源代码

    libc-2.24源代码

    unix汇编技术

    在汇编程序中是否使用libc不仅仅是一个编程风格的问题,而libc封装用来确保当系统调用接口发生变化时,无须对使用libc的程序进行修改,和用来提供POSIX兼容的接口。而UNIX内核调用往往是和POSIX兼容的,这意味着...

    libc.so.6 libc.so.6

    libc.so.6

    centos6.5系统动态链接库文件libc-2.14

    在安装运行hadoop环境是遇到了动态链接库版本太低导致hadoop命令运行不了,经过一番努力找到了方法,经过编译安装生成了这个文件,替换上去问题解决,因此分享出来,希望能帮到其他人。

    musl libc 源码实现

    嵌入式系统通常对资源消耗有较高要求,而 musl libc 的小体积和高效性使得它成为开发嵌入式系统的理想选择。 一些 Linux 发行版如 Alpine Linux 就采用了 musl libc 作为其默认的 C 标准库实现。相比其他标准库,...

    关于系统调用原理的介绍

    应用编程接口(application program interface, API)和...Libc库定义的一些API引用了封装例程(wrapper routine,唯一目的就是发布系统调用) 一般每个系统调用对应一个封装例程 库再用这些封装例程定义出给用户的API

    libc+linux_kernel_API

    libc库的pdf文档,以及linux kernel的api函数

    linux系统编程,libc.pdf,libc-ape.pdf

    想学习linux编程的童鞋可以下载看看, 三个使用的文档

    libc 2.4 源代码

    libc 2.4 源代码

    bugku pwn题libc

    bugku pwn题libc,pwn3做题时可在里面查找system、binsh等

    libc-2.17.so

    jinkens 部署node js 时需要更高版本的gcc ,否则不能编译,此处提供libc-2.17.so下载,可以nodejs正常使用

    最新版本linux libc库实现源码

    最新版本linux libc库实现源码.里面有各种函数的实现源码,很适合学习

    valgrind安装程序时缺少libc6-dbg

    valgrind在linux系统安装时,有时会出现缺少;libc6-dbg错误。将此文件安装对应版本的libc6-dbg库,将文件夹中libc6-dbg_2.23-0ubuntu11.2_amd64.deb移动到服务器,到对应目录下安装libc6-dbg,执行: dpkg -i libc6...

    libc介绍---基础

    本教程只是为了简明地介绍一下 LIBC,本教程所介 绍的距离完整的差得很远。所以您要精通的话,还望继续努力哦。

    Xcode10-libc资源

    Xcode10-libc资源

    libc 源代码 source code libc

    真正的LIB 库源码,找了好久才找到. 发现自己写的跟库差太远了,想要研究一下,现在给大家分享一下.

Global site tag (gtag.js) - Google Analytics