系统调用

发布于 28 天前  415 次阅读


关于数据存储:

提一下数据在寄存器中的存放顺序,这个在格式化字符串漏洞中要格外注意,特别是64位。

在32位程序中,需要注意的是,不同的编译器和平台可能会使用不同的寄存器和传参方式。

一般32位程序,使用栈传递参数。c程序中的参数会以从右往左的方向依次压入栈中。这也是方便出栈时,左边第一个参数最先出来。(栈:先进后出)

如下:

有的32位程序如果参数个数超过5个,前五个先用寄存器,超过的则会使用栈来传递参数。函数调用时传递参数的顺序是从右往左。当参数个数小于等于6个时,前5个参数会放入寄存器中传递,第6个参数及以后的参数则会通过栈来传递。以下是常见的前5个参数对应的寄存器:

  • 第1个参数:放入 ECX 寄存器中。
  • 第2个参数:放入 EDX 寄存器中。
  • 第3个参数:放入 EBX 寄存器中。
  • 第4个参数:放入 EAX 寄存器中。
  • 第5个参数:放入 ESI 寄存器中。

对于X86架构:

举一个x86的32位架构(常被称为i386或者x86),利用系统调用read进行任意地址写入数据(严格来讲,为了不破坏程序内存数据的完整性,且有些地址是没有写入权限的,所以常常选择写入bss段,这段地址一般不存在数据且拥有可写可执行的权限),系统调用read进行传参时遵循的是第一个参数传给ebx寄存器,第二个参数传给ecx,第三个参数传给edx。如下是read函数的定义:

头文件:#include <unistd.h>

定义函数:ssize_t read(int fd, void * buf, size_t count);

函数说明:read()会把参数fd 所指的文件传送count 个字节到buf 指针所指的内存中. 若参数count 为0, 则read()不会有作用并返回0. 返回值为实际读取到的字节数, 如果返回0, 表示已到达文件尾或是无可读取的数据,此外文件读写位置会随读取到的字节移动.

例如函数read(0, buf, 10)的参数含义如下:

  1. 第一个参数:0
    • 该参数是文件描述符(file descriptor),用于指定要从中读取数据的文件或输入流。在这种情况下,0表示标准输入流(stdin),也就是读取用户的输入。
  2. 第二个参数:buf
    • 该参数是一个指向缓冲区的指针,用于存储读取的数据。缓冲区是一个内存区域,用于临时存储从输入流中读取的数据。
  3. 第三个参数:10
    • 该参数表示要读取的最大字节数。在这种情况下,10表示最多读取10个字节的数据。

综上所述,函数read(0, buf, 10)的意思是从标准输入流(stdin)读取最多10个字节的数据,并将其存储在buf指向的缓冲区中。

我们来实验验证下, 实验样例: read(0,bssaddr,0x10)。bssaddr的地址如下

首先eax寄存器存入0x3来系统调用read(0x3是32位的系统下read函数的系统调用号)

gdb调试:

按照上边的理论,ebx寄存器应该放的是第一个参数 0 ,ecx寄存器放的是第二个参数bssaddr,edx寄存器放的是第三个参数0x10,看下图寄存器数据是否正确:

然后完善后续的工作之后拿到shell,即验证成功利用系统调用read来实现任意地址写

验证成功,i386架构下的系统调用read函数时对于read函数的参数的对应寄存器设置是第一个参数传入ebx寄存器,第二个参数传入ecx寄存器,第三个参数传入edx寄存器。

在64位程序中,函数调用时传递参数的顺序是从右往左。当参数个数小于等于6个时,前6个参数会放入寄存器中传递,第7个参数及以后的参数则会通过栈来传递。以下是常见的前6个参数对应的寄存器:

  • 第1个参数:放入 RDI 寄存器中。
  • 第2个参数:放入 RSI 寄存器中。
  • 第3个参数:放入 RDX 寄存器中。
  • 第4个参数:放入 RCX 寄存器中。
  • 第5个参数:放入 R8 寄存器中。
  • 第6个参数:放入 R9 寄存器中。

需要注意的是,不同的编译器和平台可能会使用不同的寄存器和传参方式。如果参数个数超过6个,则会使用栈来传递参数。

关于gdb:

执行vmmap查看是否有执行权限

vmmap:查看当前程序映射的内存块。

r:具有读权限                        w:具有写权限

x:具有执行权限                  p:私有

关于ret2syscall中要用到的系统调用的知识

系统调用 - 维基百科,自由的百科全书 (wikipedia.org)


穿过云层我试着努力向你奔跑