`
kongweile
  • 浏览: 517343 次
  • 性别: 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_ 开头的常量,紧随其后的即为系统调用名,相应的即为系统调用号。

分享到:
评论

相关推荐

    ret2libc exploit

    Return-to-libc是一种攻击方法,主要用于绕过现代Linux系统中的栈保护机制。在大多数现代Linux系统中,都采用了特定的安全机制来防止栈上的代码执行,这使得传统的缓冲区溢出攻击难以实施。具体来说,当程序试图在栈...

    复旦大学_软件安全_SEED labs_2-Return_to_libc.zip

    在软件安全领域,Return-to-libc攻击是绕过非执行数据(NX)保护和地址空间布局随机化(ASLR)等现代安全机制的一种手段。在传统的缓冲区溢出攻击中,攻击者通过注入超过缓冲区边界的数据来覆盖返回地址,使得程序...

    Bypassing non-executable-stack during exploitation using return-to-libc.pdf

    在IT安全领域,尤其是针对软件漏洞的利用与防御机制中,“绕过非执行堆栈进行利用,使用返回至libc技术”(Bypassing non-executable-stack during exploitation using return-to-libc)是一个高度专业且复杂的主题...

    返回libc攻击实验报告.docx

    return-to-libc攻击的原理是利用程序已加载库(如libc)中的现有函数,尤其是那些可以直接调用系统命令的函数,如`system()`,来达到攻击目的。在这种攻击中,攻击者不再需要在栈上放置可执行的shellcode,而是寻找...

    Lab2-Return-To-Libc

    在网络安全领域,Return-to-libc攻击是一种常见的利用技术,主要用于绕过数据执行防止(DEP)和地址空间布局随机化(ASLR)等安全防护机制。本资料包“Lab2-Return-To-Libc”提供了相关的代码示例、实验说明以及相关...

    Performing a ret2libc Attack-InVoLuNTaRy

    那么,我们如何绕过这种保护机制呢?本篇文章将详细解答这一问题。 #### 三、设置攻击场景 我们将对一个简单的易受攻击程序进行ret2libc攻击。本文使用的环境是最新版本的Arch Linux,并在其上进行了测试。在撰写...

    Ret2Libc 实战之利用 ZwSetInformationProcess.zip

    然而,在某些情况下,攻击者可能会寻找方法绕过DEP,以便能够执行其恶意代码。 ZwSetInformationProcess是Windows NT内核API的一部分,它允许进程获取或设置信息。其中,我们可以利用ZwSetInformationProcess的...

    带exp的pwn测试文件

    2. **ret2syscall**:与ret2text类似,它通过改变返回地址来调用系统调用,绕过程序的安全机制,执行攻击者的指令序列。 3. **ret2shellcode**:这是一种常见的漏洞利用方法,通过溢出将返回地址设置为内存中的一段...

    shellcode编程揭秘

    - 绕过安全防御:例如使用NOP滑行(NOP sleds)来提高shellcode执行的稳定性,即在实际的shellcode前填充大量的NOP(空操作)指令。 4. 面对不同安全机制的策略: - 应对ASLR:找到固定地址或者使用返回导向编程...

    安卓Android源码——击溃360手机卫士的三大防护.zip

    2. **系统库和框架服务**:Android的系统库如Bionic libc、dalvik/v8虚拟机以及各种系统服务(如ActivityManagerService)为应用程序提供运行环境。360手机卫士可能会利用这些服务进行权限控制、恶意软件检测和系统...

    周玉川-2017221302006-第三次作业1

    然而,这种方法在某些旧版Windows系统中不兼容,并且可以通过特定的绕过技术被攻破。 2. DEP (Data Execution Prevention) 机制及其对抗策略: DEP 旨在防止栈溢出攻击,通过区分数据段和代码段,阻止执行栈上的...

    关于Linux内核级后门的原理和简单实战

    Linux内核级后门是一种恶意程序,它隐藏在操作系统的核心层,允许攻击者绕过常规的安全机制,获得对系统的非法访问权限。理解其原理对于安全防护至关重要。在Linux环境下,内核与用户空间之间有着严格的隔离,这正是...

    从ByteCTF到bypass_disable_function1

    标题中的"从ByteCTF到bypass_disable_function1"指的是在网络安全竞赛ByteCTF中涉及到的一个技术挑战,即如何绕过`disable_function`的安全限制。`disable_function`是PHP配置中的一个选项,用于禁用某些可能带来...

    【技术分享】house of pig一个新的堆利用详解 .pdf

    在某些特定情况下,即程序仅使用calloc分配内存而没有使用malloc,House of Pig能够绕过tcache攻击的限制。 大型bin攻击在libc 2.29之后经历了修复,现在只有一条路径可用于攻击。攻击者首先利用未初始化的指针使用...

    SploitFun Linux x86 Exploit 开发系列教程.zip

    2. **Linux内核理解**:学习Linux内核的结构、进程管理、内存管理、系统调用等,这对于定位和利用内核级别的漏洞至关重要。 3. **缓冲区溢出**:详述缓冲区溢出的概念,它是许多经典exploit的基础。讲解如何通过...

    hello.ll:以LLVM语言为OS X实现Hello World程序的几种方法

    在这里,我们可以完全绕过libc,但是LLVM无法内联或优化系统调用。 用LLVM语言实现您自己的最小标准库,并使用llvm-link与之llvm-link 。 这是导致最严格的机器代码的最有趣的方式。 系统调用可能成为全面的LLVM...

    Linux内核级后门的原理及简单实战

    内核级后门是指恶意软件在操作系统内核中植入的隐蔽通道,允许攻击者绕过权限限制,直接访问或控制内核资源。创建内核级后门通常涉及到修改内核源码,篡改系统调用表,或者利用已知的内核漏洞。由于内核拥有最高权限...

    SploitFun Linux x86 Exploit 开发系列教程

    Return-to-libc 攻击方法通过控制程序的执行流,跳转到系统调用的入口处,执行已存在的库函数(如 exec())来绕过 NX 保护。 5. 绕过 ASLR(Address Space Layout Randomization): ASLR 是一种用于增强系统安全...

Global site tag (gtag.js) - Google Analytics