io_uring
|0| 前言
- 在我打算认真了解前已经在校队面试,VN面试被问过io_uring,本来我想着考过就不会考的原则忽视这个内容,但昨晚被winmt大哥拷打完后又觉得不能自我欺骗,刚刚好复现actf_2023遇到了io_uring,打算认真了解一下.*
|1| 原理
- io_uring是Linux 内核的高性能异步 I/O 框架
- io_uring 特点:完全异步,支持任意类型的 I/O,灵活可拓展
- io_uring 提供两个接口,一个将 I/O 请求提交到内核,一个从内核接收完成事件,io_uring 在当前进程和内核之间共享内存 (通过
mmap
在当前进程映射) ,通过操控共享内存与内核交互,避免了频繁调用syscall
.
|2| payload解析:
这篇payload是采用c语言编写,然后编译为机器码…总之先看看c语言源码吧,我尽量逐行解析:
1 | // gcc -o shellcode shellcode.c -luring -lseccomp -static |
初始化变量:
1
2
3
4
5struct io_uring ring;
struct io_uring_cqe *cqe;
struct io_uring_sqe *sqe;
char buffer[BUFFER_SIZE] = {0};
int fd;struct io_uring ring
是io_uring 实例的核心控制结构,管理异步I/O的提交队列(SQ)和完成队列(CQ).struct io_uring_cqe *cqe
指向完成队列条目(CQE),用于接收异步操作的结果。struct io_uring_sqe *sqe
指向提交队列条目(SQE),用于配置一个异步I/O请求。char buffer[BUFFER_SIZE] = {0}
用于接受读入的flagint fd
接收flag文件的文件描述符
初始化队列:
1
2io_uring_queue_init(16, &ring, 0);
sqe = io_uring_get_sqe(&ring);- 初始化
io_uring
实例,创建提交队列(SQ)和完成队列(CQ)。 - 从提交队列(SQ)中获取一个空闲的 提交队列条目(SQE),用于描述一个 I/O 操作。(第一个操作为打开文件)
- 初始化
打开文件:
1
2
3
4io_uring_prep_openat(sqe, AT_FDCWD, "flag", O_RDONLY, 0);
io_uring_submit(&ring);
io_uring_wait_cqe(&ring, &cqe);
fd = cqe->res;- io_uring_prep_openat:准备一个 异步打开文件 操作的 SQE。
- 函数原型 :
void io_uring_prep_openat(struct io_uring_sqe *sqe,int dfd,const char *path,int flagsmode_t mode);
struct io_uring_sqe *sqe
:刚刚初始化的队列获取的空闲的提交队列条目int dfd
: 基础目录的文件描述符,示例中为AT_FDCWD
表示相对当前工作目录。(相当于’./‘)const char *path
: 文件名的地址,这里使用”flag”字符串flags
: 文件打开方式,这里选择只读(O_RDONLY)mode
: 文件权限(0表示仅在创建文件时有效)。
io_uring_submit
:将提交队列(SQ)中所有准备好的 SQE 提交给内核执行io_uring_wait_cqe
:阻塞等待至少一个 完成队列条目(CQE) 可用,即文件成功打开- 使用fd接收flag文件的文件描述符
将flag从磁盘读入内存:
1
2
3
4sqe = io_uring_get_sqe(&ring);
io_uring_prep_read(sqe, fd, buffer, BUFFER_SIZE, 0);
io_uring_submit(&ring);
io_uring_wait_cqe(&ring, &cqe);- 从提交队列(SQ)中获取一个空闲的 提交队列条目(SQE),用于描述一个 I/O 操作。
io_uring_prep_read
:准备一个读取磁盘文件操作的 SQE,函数原型:void io_uring_prep_read(struct io_uring_sqe *sqe,int fd,void *buf,unsigned nbytes,off_t offset);
struct io_uring_sqe *sqe
:刚刚初始化的队列获取的空闲的提交队列条目int fd
: 刚刚获取的文件描述符void *buf
: 数据缓冲区地址- 要读取的最大字节数(BUFFER_SIZE=4096)
offset
: 文件偏移量
io_uring_submit
:将提交队列(SQ)中所有准备好的 SQE 提交给内核执行io_uring_wait_cqe
:阻塞等待至少一个 完成队列条目(CQE) 可用,即文件成功打开
将buf中的内容打印到标准输出流:
1
2
3sqe = io_uring_get_sqe(&ring);
io_uring_prep_write(sqe, STDOUT_FILENO, buffer, BUFFER_SIZE, 0);
io_uring_submit(&ring);- 从提交队列(SQ)中获取一个空闲的 提交队列条目(SQE),用于描述一个 I/O 操作。io_uring_prep_openat:准备一个 **异步操作的 SQE。
- 参数形式与io_uring_read基本相同
io_uring_submit
:将提交队列(SQ)中所有准备好的 SQE 提交给内核执行
此时我们在标准输出中获得了flag : )