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 | struct io_uring ring; |
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}用于接受读入的flag
int fd接收flag文件的文件描述符
初始化队列:
1 | io_uring_queue_init(16, &ring, 0); |
初始化 io_uring 实例,创建提交队列(SQ)和完成队列(CQ)。
从提交队列(SQ)中获取一个空闲的 提交队列条目(SQE),用于描述一个 I/O 操作。(第一个操作为打开文件)
打开文件:
1 | io_uring_prep_openat(sqe, AT_FDCWD, "flag", O_RDONLY, 0); |
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 | sqe = io_uring_get_sqe(&ring); |
从提交队列(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 | sqe = io_uring_get_sqe(&ring); |
从提交队列(SQ)中获取一个空闲的 提交队列条目(SQE),用于描述一个 I/O 操作。io_uring_prep_openat:准备一个 **异步操作的 SQE。
参数形式与io_uring_read基本相同
io_uring_submit:将提交队列(SQ)中所有准备好的 SQE 提交给内核执行
此时我们在标准输出中获得了flag : )