Arch: i386-32-little
RELRO: No RELRO
Stack: No canary found
NX: NX disabled
PIE: No PIE (0x8048000)
系统调用是 Linux 内核提供的一段代码(也可以理解为函数)用来实现特定的功能,32 位程序(x86 CPU)利用 int 0x80 来进行系统调用,64 位程序 (X64 CPU) 提供调用 syscall 来进行系统调用。Linux 内核提供用户空间程序与内核空间进行交互的接口(接口让用户态程序能受限访问硬件设备,比如申请系统资源,操作设备读写,创建新进程等),用户空间发起请求,内核空间负责进行执行,两者之间就需要接口作为桥梁,用户可以通过这种方式来进行系统调用,但是用户是受到限制的,不能直接执行内核代码,也不能随意进行修改系统,必须通过特定方式才能进行才能进入内核,也需要一定的权限才能使用接口。
用户调用系统调用时,通过向 eax 寄存器写入对应命令的系统调用号,这个号就是 sys_call_table 数组的下标,system_call 过程获取 eax 寄存器的值,然后通过 eax 寄存器的值找到要调用的系统入口并调用,系统调用完成后会把返回值保存到 eax 寄存器中 用户进行系统调用时,在 eax 寄存器写入对应的系统调用编号,而用户态和内核态使用的栈不同,系统调用是用户态调用然后进入系统调用后会转变成内核态,要经历用户态与内核态的转化,所以不能直接使用用户空间的栈来传递参数(32 位系统用户态内利用栈来传参,64 位仍然需要寄存器来传参)。Linux 使用寄存器来传递参数,其顺序如下: 第 1 个参数放置在 ebx 寄存器。 第 2 个参数放置在 ecx 寄存器。 第 3 个参数放置在 edx 寄存器。 第 4 个参数放置在 esi 寄存器。 第 5 个参数放置在 edi 寄存器。 第 6 个参数放置在 ebp 寄存器。
sysread 参数依次是: 3、文件描述符(标准输入 0)、首地址和输入个数
syswrite 也一样
注意到栈的地址是不变的,而函数的地址又是固定不变的,而这里的 system write 又比较蠢,相当于直接打印栈上最上面 20 个字节,所以可以重复执行这个 system write 一次来暴露动态的 sp 地址
要注意 esp 会有第二次增加,第二次写入的时候要计算好垃圾数据填充和重新定位跳转位置
这道题可以啊