-
-
[原创]CVE-2015-3456 Qemu逃逸复现
-
发表于: 2025-12-1 08:49 1679
-
参考文章:4ffK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6%4N6%4N6Q4x3X3g2K6k6h3y4H3N6h3I4K6k6g2)9J5k6h3y4G2L8g2)9J5c8X3q4J5j5$3S2A6N6X3g2K6i4K6u0r3y4U0j5H3x3W2)9J5k6h3S2@1L8h3H3`.
QEMU版本:1.5.3
FDC(Floppy Disk Controller)是模拟 Intel 82078 软盘控制器的硬件设备,用于:
当 offset 为 5则会调用 fdctrl_write_data
FIFO是FDC的核心缓冲区,用于:
我们可以看见 fdctrl->fifo 被分配了较为固定的512字节大小的存储。
一共这里我们可以看见分为两个板块,一个板块是接收数据
一个板块是判断数据接收完毕调用函数,然后设置标识位
那么根据映射表我们可以知道 fdctrl_handle_drive_specification_command 函数会被调用
当我们调用0x3f5,并且传入0x8e这种情况的时候,会去调用**fdctrl_handle_drive_specification_command函数,**然后在参数传递完毕后该函数并没有很好的处理fifo的状态,并且 后续的参数传递依赖的 data_pos,而data_pos是无限增加的。
QEMU启动 ↓设备注册 (fdc_register_types) ↓设备实例化 (qdev_create) ↓设备初始化 (qdev_init_nofail) ↓调用设备特定的init函数: ├─→ isabus_fdc_init1 (ISA设备) ├─→ sysbus_fdc_init1 (SysBus设备) └─→ sun4m_fdc_init1 (Sun4m设备) ↓fdctrl_init_common(fdctrl) ↓初始化command_to_handler查找表 ↓分配fifo缓冲区: qemu_memalign(512, 512) ↓设置fifo_size = 512 ↓初始化其他控制器状态 ↓连接驱动器 (fdctrl_connect_drives)QEMU启动 ↓设备注册 (fdc_register_types) ↓设备实例化 (qdev_create) ↓设备初始化 (qdev_init_nofail) ↓调用设备特定的init函数: ├─→ isabus_fdc_init1 (ISA设备) ├─→ sysbus_fdc_init1 (SysBus设备) └─→ sun4m_fdc_init1 (Sun4m设备) ↓fdctrl_init_common(fdctrl) ↓初始化command_to_handler查找表 ↓分配fifo缓冲区: qemu_memalign(512, 512) ↓设置fifo_size = 512 ↓初始化其他控制器状态 ↓连接驱动器 (fdctrl_connect_drives)/* FDC I/O端口映射表 - outb机制的关键 * c * 结构说明: * { offset, length, size, .read = func, .write = func } * - offset: 相对于iobase的偏移量 * - length: 端口范围长度 * - size: 访问大小(1=字节,2=字,4=双字) * * 端口映射(假设iobase=0x3f0): * - 0x3f1-0x3f5: 偏移1-5,映射到fdctrl_read/fdctrl_write * * 0x3f1 (offset=1): FD_REG_SRA (状态寄存器A) * * 0x3f2 (offset=2): FD_REG_DOR (数字输出寄存器) * * 0x3f3 (offset=3): FD_REG_TDR (磁带驱动器寄存器) * * 0x3f4 (offset=4): FD_REG_MSR (主状态寄存器) * * 0x3f5 (offset=5): FD_REG_FIFO (FIFO数据寄存器) ← 漏洞触发端口! * - 0x3f7: 偏移7,映射到fdctrl_read/fdctrl_write * * 0x3f7 (offset=7): FD_REG_DIR (数字输入寄存器) 或 FD_REG_CCR (配置控制寄存器) * * outb(0x8e, 0x3f5) 的完整路径: * 1. 主机执行outb(0x8e, 0x3f5) * 2. QEMU CPU模拟层:cpu_outb(0x3f5, 0x8e) * 3. I/O端口层:ioport_write(0, 0x3f5, 0x8e) * 4. 查找注册的handler:ioport_write_table[0][0x3f5] * 5. 调用:portio_writeb_thunk() → fdctrl_write(offset=5, value=0x8e) * 6. fdctrl_write识别offset=5为FD_REG_FIFO * 7. 调用fdctrl_write_data(fdctrl, 0x8e) * 8. fdctrl_write_data识别data_pos=0,进入命令模式 */static const MemoryRegionPortio fdc_portio_list[] = { { 1, 5, 1, .read = fdctrl_read, .write = fdctrl_write }, /* 0x3f1-0x3f5 */ { 7, 1, 1, .read = fdctrl_read, .write = fdctrl_write }, /* 0x3f7 */ PORTIO_END_OF_LIST(),};/* FDC I/O端口映射表 - outb机制的关键 * c * 结构说明: * { offset, length, size, .read = func, .write = func } * - offset: 相对于iobase的偏移量 * - length: 端口范围长度 * - size: 访问大小(1=字节,2=字,4=双字) * * 端口映射(假设iobase=0x3f0): * - 0x3f1-0x3f5: 偏移1-5,映射到fdctrl_read/fdctrl_write * * 0x3f1 (offset=1): FD_REG_SRA (状态寄存器A) * * 0x3f2 (offset=2): FD_REG_DOR (数字输出寄存器) * * 0x3f3 (offset=3): FD_REG_TDR (磁带驱动器寄存器) * * 0x3f4 (offset=4): FD_REG_MSR (主状态寄存器) * * 0x3f5 (offset=5): FD_REG_FIFO (FIFO数据寄存器) ← 漏洞触发端口! * - 0x3f7: 偏移7,映射到fdctrl_read/fdctrl_write * * 0x3f7 (offset=7): FD_REG_DIR (数字输入寄存器) 或 FD_REG_CCR (配置控制寄存器) * * outb(0x8e, 0x3f5) 的完整路径: * 1. 主机执行outb(0x8e, 0x3f5) * 2. QEMU CPU模拟层:cpu_outb(0x3f5, 0x8e) * 3. I/O端口层:ioport_write(0, 0x3f5, 0x8e) * 4. 查找注册的handler:ioport_write_table[0][0x3f5] * 5. 调用:portio_writeb_thunk() → fdctrl_write(offset=5, value=0x8e) * 6. fdctrl_write识别offset=5为FD_REG_FIFO * 7. 调用fdctrl_write_data(fdctrl, 0x8e) * 8. fdctrl_write_data识别data_pos=0,进入命令模式 */static const MemoryRegionPortio fdc_portio_list[] = { { 1, 5, 1, .read = fdctrl_read, .write = fdctrl_write }, /* 0x3f1-0x3f5 */ { 7, 1, 1, .read = fdctrl_read, .write = fdctrl_write }, /* 0x3f7 */ PORTIO_END_OF_LIST(),};enum { FD_REG_SRA = 0x00, FD_REG_SRB = 0x01, FD_REG_DOR = 0x02, FD_REG_TDR = 0x03, FD_REG_MSR = 0x04, FD_REG_DSR = 0x04, FD_REG_FIFO = 0x05, FD_REG_DIR = 0x07, FD_REG_CCR = 0x07,};static void fdctrl_write (void *opaque, uint32_t reg, uint32_t value){ FDCtrl *fdctrl = opaque; FLOPPY_DPRINTF("write reg%d: 0x%02x\n", reg & 7, value); reg &= 7; switch (reg) { case FD_REG_DOR: fdctrl_write_dor(fdctrl, value); break; case FD_REG_TDR: fdctrl_write_tape(fdctrl, value); break; case FD_REG_DSR: fdctrl_write_rate(fdctrl, value); break; case FD_REG_FIFO: fdctrl_write_data(fdctrl, value); break; case FD_REG_CCR: fdctrl_write_ccr(fdctrl, value); break; default: break; }}enum { FD_REG_SRA = 0x00, FD_REG_SRB = 0x01, FD_REG_DOR = 0x02, FD_REG_TDR = 0x03, FD_REG_MSR = 0x04, FD_REG_DSR = 0x04, FD_REG_FIFO = 0x05, FD_REG_DIR = 0x07, FD_REG_CCR = 0x07,};static void fdctrl_write (void *opaque, uint32_t reg, uint32_t value){ FDCtrl *fdctrl = opaque; FLOPPY_DPRINTF("write reg%d: 0x%02x\n", reg & 7, value); reg &= 7; switch (reg) { case FD_REG_DOR: fdctrl_write_dor(fdctrl, value); break; case FD_REG_TDR: fdctrl_write_tape(fdctrl, value); break; case FD_REG_DSR: fdctrl_write_rate(fdctrl, value); break; case FD_REG_FIFO: fdctrl_write_data(fdctrl, value); break; case FD_REG_CCR: fdctrl_write_ccr(fdctrl, value); break; default: break; }}/* FDC FIFO数据写入处理函数 - 漏洞核心函数 * * 函数调用链: * fdctrl_write (FD_REG_FIFO) → fdctrl_write_data * * 整体流程: * 1. 检查控制器状态(RESET、RQM、DIO) * 2. 判断当前模式: * a) 非DMA数据传输模式 (FD_MSR_NONDMA): 处理数据传输 * b) 命令模式 (data_pos == 0): 识别命令,设置data_len * c) 参数接收模式: 接收命令参数,写入FIFO * 3. 当data_pos == data_len时,调用命令处理器 * * 状态机转换: * 空闲 → 命令接收 → 参数接收 → 命令处理 → 数据传输/结果返回 → 空闲 * * 漏洞触发条件: * 1. 控制器处于非RESET状态 * 2. MSR_RQM标志置位(请求主模式) * 3. MSR_DIO标志未置位(写方向) * 4. 不在非DMA数据传输模式(否则会走另一个分支) * 5. 攻击者发送命令后,继续发送超过data_len的数据 * * 漏洞触发流程: * Step 1: 发送命令字节(如0x8e) * - data_pos = 0,识别命令,设置data_len = 6 * Step 2: 发送参数(正常5个参数) * - data_pos: 1 → 2 → 3 → 4 → 5 → 6 * Step 3: 攻击:继续发送数据 * - data_pos继续增长:7 → 8 → ... → 512 → 513 → ... * - 当data_pos >= 512时,fifo[data_pos]越界写入堆内存 * * 关键变量: * - fdctrl->fifo: 堆上分配的512字节缓冲区 * - fdctrl->data_pos: 当前写入位置(可能超过512) * - fdctrl->data_len: 预期数据长度(根据命令设置) * - fdctrl->msr: 主状态寄存器,控制状态机 */static void fdctrl_write_data(FDCtrl *fdctrl, uint32_t value){ FDrive *cur_drv; int pos; /* 检查1: 控制器必须在非RESET状态 */ if (!(fdctrl->dor & FD_DOR_nRESET)) { FLOPPY_DPRINTF("Floppy controller in RESET state !\n"); return; } /* 检查2: 控制器必须准备好接收数据 * - MSR_RQM: 请求主模式标志,必须置位 * - MSR_DIO: 数据方向标志,0=写(主机→控制器),1=读(控制器→主机) * 如果DIO置位,说明控制器在等待主机读取,不应该写入 */ if (!(fdctrl->msr & FD_MSR_RQM) || (fdctrl->msr & FD_MSR_DIO)) { FLOPPY_DPRINTF("error: controller not ready for writing\n"); return; } fdctrl->dsr &= ~FD_DSR_PWRDOWN; /* Is it write command time ? */ if (fdctrl->msr & FD_MSR_NONDMA) { pos = fdctrl->data_pos++; pos %= FD_SECTOR_LEN; fdctrl->fifo[pos] = value; if (pos == FD_SECTOR_LEN - 1 || fdctrl->data_pos == fdctrl->data_len) { cur_drv = get_cur_drv(fdctrl); if (bdrv_write(cur_drv->bs, fd_sector(cur_drv), fdctrl->fifo, 1) < 0) { FLOPPY_DPRINTF("error writing sector %d\n", fd_sector(cur_drv)); return; } if (!fdctrl_seek_to_next_sect(fdctrl, cur_drv)) { FLOPPY_DPRINTF("error seeking to next sector %d\n", fd_sector(cur_drv)); return; } } /* Switch from transfer mode to status mode * then from status mode to command mode */ if (fdctrl->data_pos == fdctrl->data_len) fdctrl_stop_transfer(fdctrl, 0x00, 0x00, 0x00); return; } /* ======================================================================== * 命令识别阶段 - 当data_pos==0时,识别这是一个新命令 * ======================================================================== * * 触发条件:data_pos == 0(FIFO为空,准备接收新命令) * * 命令识别流程: * 1. 通过command_to_handler[value]查找命令对应的handler索引 * - command_to_handler数组在fdctrl_init_common()中初始化 * - 建立命令字节值(0x00-0xff)到handlers数组索引的映射 * * 2. 设置预期数据长度:data_len = parameters + 1 * - parameters: 命令需要的参数数量(不包括命令字节本身) * - +1: 包括命令字节本身 * - 例如:0x8e命令parameters=5,所以data_len=6 * * 3. 设置MSR_CMDBUSY标志 * - 表示控制器正在处理命令,防止其他操作干扰 * * 示例:outb(0x8e, 0x3f5) * - value = 0x8e * - pos = command_to_handler[0x8e] → 找到handlers数组索引 * - handlers[pos].name = "DRIVE SPECIFICATION COMMAND" * - handlers[pos].parameters = 5 * - data_len = 5 + 1 = 6 * - 设置MSR_CMDBUSY标志 * * 后续流程: * - 命令字节0x8e被写入fifo[0] * - data_pos变为1 * - 等待接收5个参数 */ if (fdctrl->data_pos == 0) { /* Command - 命令识别 */ pos = command_to_handler[value & 0xff]; /* 查找命令对应的handler索引 */ FLOPPY_DPRINTF("%s command\n", handlers[pos].name); /* 设置预期数据长度:命令字节 + 参数数量 */ fdctrl->data_len = handlers[pos].parameters + 1; /* 设置命令忙标志,防止其他操作干扰 */ fdctrl->msr |= FD_MSR_CMDBUSY; } FLOPPY_DPRINTF("%s: %02x\n", __func__, value); /* ======================================================================== * CVE-2015-3456 漏洞核心代码 - 无边界检查的FIFO写入 * ======================================================================== * * 漏洞点:直接使用data_pos作为数组索引,没有检查是否超过fifo_size(512) * * 正常流程: * - data_pos从0开始,每次写入后递增 * - 当data_pos == data_len时,调用命令处理器 * - 命令处理器通常会调用fdctrl_set_fifo或fdctrl_reset_fifo重置data_pos=0 * * 攻击流程(0x8e命令): * 1. 发送命令字节0x8e,data_pos=0,设置data_len=6 * 2. 发送5个参数,data_pos: 1→2→3→4→5→6 * 3. 当data_pos==6时,调用fdctrl_handle_drive_specification_command * 4. 如果第5个参数(fifo[5])的bit7=0且bit6=0,函数直接返回 * 5. data_pos保持为6,不会被重置 * 6. 攻击者继续发送数据: * - data_pos=7: fifo[7] = value ← 仍在FIFO范围内 * - ... * - data_pos=512: fifo[512] = value ← 越界写入!开始覆盖堆内存 * - data_pos=513: fifo[513] = value ← 继续越界写入 * - ... * */ fdctrl->fifo[fdctrl->data_pos++] = value; /* 漏洞:直接使用data_pos,无边界检查! */ /* ======================================================================== * 命令处理阶段 - 当data_pos == data_len时,所有参数接收完毕 * ======================================================================== * * 触发条件:data_pos == data_len(所有参数已接收) * * 处理流程: * 1. 检查是否为格式化命令(特殊处理) * 2. 根据命令字节(fifo[0])查找handler索引 * 3. 调用对应的命令处理函数 * * 函数调用链: * fdctrl_write_data → handlers[pos].handler(fdctrl, direction) * * 可能的处理器: * - fdctrl_start_transfer: READ/WRITE等数据传输命令 * - fdctrl_handle_drive_specification_command: 0x8e命令 ← 漏洞利用的关键 * - fdctrl_handle_seek: SEEK命令 * - fdctrl_handle_specify: SPECIFY命令 * - 其他命令处理器 * * 示例:0x8e命令处理 * - fifo[0] = 0x8e(命令字节) * - fifo[1-5] = 5个参数 * - data_pos = 6,data_len = 6 * - pos = command_to_handler[0x8e] * - handlers[pos].handler = fdctrl_handle_drive_specification_command * - 调用:fdctrl_handle_drive_specification_command(fdctrl, ...) * * 漏洞利用的关键: * - 如果handler函数不调用fdctrl_set_fifo()或fdctrl_reset_fifo() * - data_pos不会被重置,可以继续写入数据 * - 导致data_pos超过512,触发缓冲区溢出 */ /* 检查是否接收完所有参数 */ if (fdctrl->data_pos == fdctrl->data_len) { /* 所有参数接收完毕,可以处理命令了 */ if (fdctrl->data_state & FD_STATE_FORMAT) { /* 格式化命令的特殊处理 */ fdctrl_format_sector(fdctrl); return; } /* 根据命令字节查找并调用相应的处理器 */ pos = command_to_handler[fdctrl->fifo[0] & 0xff]; /* 从fifo[0]获取命令字节 */ FLOPPY_DPRINTF("treat %s command\n", handlers[pos].name); /* 调用命令处理函数 */ (*handlers[pos].handler)(fdctrl, handlers[pos].direction); } /* 注意:如果data_pos != data_len,函数返回,等待下一次写入 * 攻击者可以继续发送数据,使data_pos继续增长,直到超过512 */}/* FDC FIFO数据写入处理函数 - 漏洞核心函数 * * 函数调用链: * fdctrl_write (FD_REG_FIFO) → fdctrl_write_data * * 整体流程: * 1. 检查控制器状态(RESET、RQM、DIO) * 2. 判断当前模式: * a) 非DMA数据传输模式 (FD_MSR_NONDMA): 处理数据传输 * b) 命令模式 (data_pos == 0): 识别命令,设置data_len * c) 参数接收模式: 接收命令参数,写入FIFO * 3. 当data_pos == data_len时,调用命令处理器 * * 状态机转换: * 空闲 → 命令接收 → 参数接收 → 命令处理 → 数据传输/结果返回 → 空闲 * * 漏洞触发条件: * 1. 控制器处于非RESET状态 * 2. MSR_RQM标志置位(请求主模式) * 3. MSR_DIO标志未置位(写方向) * 4. 不在非DMA数据传输模式(否则会走另一个分支) * 5. 攻击者发送命令后,继续发送超过data_len的数据 * * 漏洞触发流程: * Step 1: 发送命令字节(如0x8e) * - data_pos = 0,识别命令,设置data_len = 6 * Step 2: 发送参数(正常5个参数) * - data_pos: 1 → 2 → 3 → 4 → 5 → 6 * Step 3: 攻击:继续发送数据 * - data_pos继续增长:7 → 8 → ... → 512 → 513 → ... * - 当data_pos >= 512时,fifo[data_pos]越界写入堆内存 * * 关键变量: * - fdctrl->fifo: 堆上分配的512字节缓冲区 * - fdctrl->data_pos: 当前写入位置(可能超过512) * - fdctrl->data_len: 预期数据长度(根据命令设置) * - fdctrl->msr: 主状态寄存器,控制状态机 */static void fdctrl_write_data(FDCtrl *fdctrl, uint32_t value){ FDrive *cur_drv; int pos; /* 检查1: 控制器必须在非RESET状态 */ if (!(fdctrl->dor & FD_DOR_nRESET)) { FLOPPY_DPRINTF("Floppy controller in RESET state !\n"); return; } /* 检查2: 控制器必须准备好接收数据 * - MSR_RQM: 请求主模式标志,必须置位 * - MSR_DIO: 数据方向标志,0=写(主机→控制器),1=读(控制器→主机) * 如果DIO置位,说明控制器在等待主机读取,不应该写入 */ if (!(fdctrl->msr & FD_MSR_RQM) || (fdctrl->msr & FD_MSR_DIO)) { FLOPPY_DPRINTF("error: controller not ready for writing\n"); return; } fdctrl->dsr &= ~FD_DSR_PWRDOWN; /* Is it write command time ? */ if (fdctrl->msr & FD_MSR_NONDMA) { pos = fdctrl->data_pos++; pos %= FD_SECTOR_LEN; fdctrl->fifo[pos] = value; if (pos == FD_SECTOR_LEN - 1 || fdctrl->data_pos == fdctrl->data_len) { cur_drv = get_cur_drv(fdctrl); if (bdrv_write(cur_drv->bs, fd_sector(cur_drv), fdctrl->fifo, 1) < 0) { FLOPPY_DPRINTF("error writing sector %d\n", fd_sector(cur_drv)); return; } if (!fdctrl_seek_to_next_sect(fdctrl, cur_drv)) { FLOPPY_DPRINTF("error seeking to next sector %d\n", fd_sector(cur_drv)); return; } } /* Switch from transfer mode to status mode * then from status mode to command mode */ if (fdctrl->data_pos == fdctrl->data_len) fdctrl_stop_transfer(fdctrl, 0x00, 0x00, 0x00); return; } /* ======================================================================== * 命令识别阶段 - 当data_pos==0时,识别这是一个新命令 * ======================================================================== * * 触发条件:data_pos == 0(FIFO为空,准备接收新命令) * * 命令识别流程: * 1. 通过command_to_handler[value]查找命令对应的handler索引 * - command_to_handler数组在fdctrl_init_common()中初始化 * - 建立命令字节值(0x00-0xff)到handlers数组索引的映射 * * 2. 设置预期数据长度:data_len = parameters + 1 * - parameters: 命令需要的参数数量(不包括命令字节本身) * - +1: 包括命令字节本身 * - 例如:0x8e命令parameters=5,所以data_len=6 * * 3. 设置MSR_CMDBUSY标志 * - 表示控制器正在处理命令,防止其他操作干扰 * * 示例:outb(0x8e, 0x3f5) * - value = 0x8e * - pos = command_to_handler[0x8e] → 找到handlers数组索引 * - handlers[pos].name = "DRIVE SPECIFICATION COMMAND" * - handlers[pos].parameters = 5 * - data_len = 5 + 1 = 6 * - 设置MSR_CMDBUSY标志 * * 后续流程: * - 命令字节0x8e被写入fifo[0] * - data_pos变为1 * - 等待接收5个参数 */ if (fdctrl->data_pos == 0) { /* Command - 命令识别 */ pos = command_to_handler[value & 0xff]; /* 查找命令对应的handler索引 */ FLOPPY_DPRINTF("%s command\n", handlers[pos].name); /* 设置预期数据长度:命令字节 + 参数数量 */ fdctrl->data_len = handlers[pos].parameters + 1; /* 设置命令忙标志,防止其他操作干扰 */ fdctrl->msr |= FD_MSR_CMDBUSY; } FLOPPY_DPRINTF("%s: %02x\n", __func__, value); /* ======================================================================== * CVE-2015-3456 漏洞核心代码 - 无边界检查的FIFO写入 * ======================================================================== * * 漏洞点:直接使用data_pos作为数组索引,没有检查是否超过fifo_size(512) * * 正常流程: * - data_pos从0开始,每次写入后递增 * - 当data_pos == data_len时,调用命令处理器 * - 命令处理器通常会调用fdctrl_set_fifo或fdctrl_reset_fifo重置data_pos=0 * * 攻击流程(0x8e命令): * 1. 发送命令字节0x8e,data_pos=0,设置data_len=6 * 2. 发送5个参数,data_pos: 1→2→3→4→5→6 * 3. 当data_pos==6时,调用fdctrl_handle_drive_specification_command * 4. 如果第5个参数(fifo[5])的bit7=0且bit6=0,函数直接返回 * 5. data_pos保持为6,不会被重置 * 6. 攻击者继续发送数据: * - data_pos=7: fifo[7] = value ← 仍在FIFO范围内 * - ... * - data_pos=512: fifo[512] = value ← 越界写入!开始覆盖堆内存 * - data_pos=513: fifo[513] = value ← 继续越界写入 * - ... * */ fdctrl->fifo[fdctrl->data_pos++] = value; /* 漏洞:直接使用data_pos,无边界检查! */ /* ======================================================================== * 命令处理阶段 - 当data_pos == data_len时,所有参数接收完毕 * ======================================================================== * * 触发条件:data_pos == data_len(所有参数已接收) * * 处理流程: * 1. 检查是否为格式化命令(特殊处理) * 2. 根据命令字节(fifo[0])查找handler索引 * 3. 调用对应的命令处理函数 * * 函数调用链: * fdctrl_write_data → handlers[pos].handler(fdctrl, direction) * * 可能的处理器: * - fdctrl_start_transfer: READ/WRITE等数据传输命令 * - fdctrl_handle_drive_specification_command: 0x8e命令 ← 漏洞利用的关键 * - fdctrl_handle_seek: SEEK命令 * - fdctrl_handle_specify: SPECIFY命令 * - 其他命令处理器 * * 示例:0x8e命令处理 * - fifo[0] = 0x8e(命令字节) * - fifo[1-5] = 5个参数 * - data_pos = 6,data_len = 6 * - pos = command_to_handler[0x8e] * - handlers[pos].handler = fdctrl_handle_drive_specification_command * - 调用:fdctrl_handle_drive_specification_command(fdctrl, ...) * * 漏洞利用的关键: * - 如果handler函数不调用fdctrl_set_fifo()或fdctrl_reset_fifo() * - data_pos不会被重置,可以继续写入数据 * - 导致data_pos超过512,触发缓冲区溢出 */ /* 检查是否接收完所有参数 */ if (fdctrl->data_pos == fdctrl->data_len) { /* 所有参数接收完毕,可以处理命令了 */ if (fdctrl->data_state & FD_STATE_FORMAT) { /* 格式化命令的特殊处理 */ fdctrl_format_sector(fdctrl); return; } /* 根据命令字节查找并调用相应的处理器 */ pos = command_to_handler[fdctrl->fifo[0] & 0xff]; /* 从fifo[0]获取命令字节 */ FLOPPY_DPRINTF("treat %s command\n", handlers[pos].name); /* 调用命令处理函数 */ (*handlers[pos].handler)(fdctrl, handlers[pos].direction); } /* 注意:如果data_pos != data_len,函数返回,等待下一次写入 * 攻击者可以继续发送数据,使data_pos继续增长,直到超过512 */}enum { FD_CMD_READ_TRACK = 0x02, FD_CMD_SPECIFY = 0x03, FD_CMD_SENSE_DRIVE_STATUS = 0x04, FD_CMD_WRITE = 0x05, FD_CMD_READ = 0x06, FD_CMD_RECALIBRATE = 0x07, FD_CMD_SENSE_INTERRUPT_STATUS = 0x08, FD_CMD_WRITE_DELETED = 0x09, FD_CMD_READ_ID = 0x0a, FD_CMD_READ_DELETED = 0x0c, FD_CMD_FORMAT_TRACK = 0x0d, FD_CMD_DUMPREG = 0x0e, FD_CMD_SEEK = 0x0f, FD_CMD_VERSION = 0x10, FD_CMD_SCAN_EQUAL = 0x11, FD_CMD_PERPENDICULAR_MODE = 0x12, FD_CMD_CONFIGURE = 0x13, FD_CMD_LOCK = 0x14, FD_CMD_VERIFY = 0x16, FD_CMD_POWERDOWN_MODE = 0x17, FD_CMD_PART_ID = 0x18, FD_CMD_SCAN_LOW_OR_EQUAL = 0x19, FD_CMD_SCAN_HIGH_OR_EQUAL = 0x1d, FD_CMD_SAVE = 0x2e, FD_CMD_OPTION = 0x33, FD_CMD_RESTORE = 0x4e, FD_CMD_DRIVE_SPECIFICATION_COMMAND = 0x8e, FD_CMD_RELATIVE_SEEK_OUT = 0x8f, FD_CMD_FORMAT_AND_WRITE = 0xcd, FD_CMD_RELATIVE_SEEK_IN = 0xcf,};static const struct { uint8_t value; uint8_t mask; const char* name; int parameters; void (*handler)(FDCtrl *fdctrl, int direction); int direction;} handlers[] = { { FD_CMD_READ, 0x1f, "READ", 8, fdctrl_start_transfer, FD_DIR_READ }, { FD_CMD_WRITE, 0x3f, "WRITE", 8, fdctrl_start_transfer, FD_DIR_WRITE }, { FD_CMD_SEEK, 0xff, "SEEK", 2, fdctrl_handle_seek }, { FD_CMD_SENSE_INTERRUPT_STATUS, 0xff, "SENSE INTERRUPT STATUS", 0, fdctrl_handle_sense_interrupt_status }, { FD_CMD_RECALIBRATE, 0xff, "RECALIBRATE", 1, fdctrl_handle_recalibrate }, { FD_CMD_FORMAT_TRACK, 0xbf, "FORMAT TRACK", 5, fdctrl_handle_format_track }, { FD_CMD_READ_TRACK, 0xbf, "READ TRACK", 8, fdctrl_start_transfer, FD_DIR_READ }, { FD_CMD_RESTORE, 0xff, "RESTORE", 17, fdctrl_handle_restore }, /* part of READ DELETED DATA */ { FD_CMD_SAVE, 0xff, "SAVE", 0, fdctrl_handle_save }, /* part of READ DELETED DATA */ { FD_CMD_READ_DELETED, 0x1f, "READ DELETED DATA", 8, fdctrl_start_transfer_del, FD_DIR_READ }, { FD_CMD_SCAN_EQUAL, 0x1f, "SCAN EQUAL", 8, fdctrl_start_transfer, FD_DIR_SCANE }, { FD_CMD_VERIFY, 0x1f, "VERIFY", 8, fdctrl_start_transfer, FD_DIR_VERIFY }, { FD_CMD_SCAN_LOW_OR_EQUAL, 0x1f, "SCAN LOW OR EQUAL", 8, fdctrl_start_transfer, FD_DIR_SCANL }, { FD_CMD_SCAN_HIGH_OR_EQUAL, 0x1f, "SCAN HIGH OR EQUAL", 8, fdctrl_start_transfer, FD_DIR_SCANH }, { FD_CMD_WRITE_DELETED, 0x3f, "WRITE DELETED DATA", 8, fdctrl_start_transfer_del, FD_DIR_WRITE }, { FD_CMD_READ_ID, 0xbf, "READ ID", 1, fdctrl_handle_readid }, { FD_CMD_SPECIFY, 0xff, "SPECIFY", 2, fdctrl_handle_specify }, { FD_CMD_SENSE_DRIVE_STATUS, 0xff, "SENSE DRIVE STATUS", 1, fdctrl_handle_sense_drive_status }, { FD_CMD_PERPENDICULAR_MODE, 0xff, "PERPENDICULAR MODE", 1, fdctrl_handle_perpendicular_mode }, { FD_CMD_CONFIGURE, 0xff, "CONFIGURE", 3, fdctrl_handle_configure }, { FD_CMD_POWERDOWN_MODE, 0xff, "POWERDOWN MODE", 2, fdctrl_handle_powerdown_mode }, { FD_CMD_OPTION, 0xff, "OPTION", 1, fdctrl_handle_option }, { FD_CMD_DRIVE_SPECIFICATION_COMMAND, 0xff, "DRIVE SPECIFICATION COMMAND", 5, fdctrl_handle_drive_specification_command }, { FD_CMD_RELATIVE_SEEK_OUT, 0xff, "RELATIVE SEEK OUT", 2, fdctrl_handle_relative_seek_out }, { FD_CMD_FORMAT_AND_WRITE, 0xff, "FORMAT AND WRITE", 10, fdctrl_unimplemented }, { FD_CMD_RELATIVE_SEEK_IN, 0xff, "RELATIVE SEEK IN", 2, fdctrl_handle_relative_seek_in }, { FD_CMD_LOCK, 0x7f, "LOCK", 0, fdctrl_handle_lock }, { FD_CMD_DUMPREG, 0xff, "DUMPREG", 0, fdctrl_handle_dumpreg }, { FD_CMD_VERSION, 0xff, "VERSION", 0, fdctrl_handle_version }, { FD_CMD_PART_ID, 0xff, "PART ID", 0, fdctrl_handle_partid }, { FD_CMD_WRITE, 0x1f, "WRITE (BeOS)", 8, fdctrl_start_transfer, FD_DIR_WRITE }, /* not in specification ; BeOS 4.5 bug */ { 0, 0, "unknown", 0, fdctrl_unimplemented }, /* default handler */};enum { FD_CMD_READ_TRACK = 0x02, FD_CMD_SPECIFY = 0x03, FD_CMD_SENSE_DRIVE_STATUS = 0x04, FD_CMD_WRITE = 0x05, FD_CMD_READ = 0x06, FD_CMD_RECALIBRATE = 0x07, FD_CMD_SENSE_INTERRUPT_STATUS = 0x08, FD_CMD_WRITE_DELETED = 0x09, FD_CMD_READ_ID = 0x0a, FD_CMD_READ_DELETED = 0x0c, FD_CMD_FORMAT_TRACK = 0x0d, FD_CMD_DUMPREG = 0x0e, FD_CMD_SEEK = 0x0f, FD_CMD_VERSION = 0x10, FD_CMD_SCAN_EQUAL = 0x11, FD_CMD_PERPENDICULAR_MODE = 0x12, FD_CMD_CONFIGURE = 0x13, FD_CMD_LOCK = 0x14, FD_CMD_VERIFY = 0x16, FD_CMD_POWERDOWN_MODE = 0x17, FD_CMD_PART_ID = 0x18, FD_CMD_SCAN_LOW_OR_EQUAL = 0x19,赞赏记录
参与人
雪币
留言
时间
nothing233
感谢你的积极参与,期待更多精彩内容!
2025-12-1 09:13
winmt
+10
感谢你的积极参与,期待更多精彩内容!
2025-12-1 09:10
赞赏
他的文章
赞赏
雪币:
留言: