> For the complete documentation index, see [llms.txt](https://lightc.gitbook.io/pwn-gitbook/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://lightc.gitbook.io/pwn-gitbook/kpwn/kpwn-tricks/iop-yu-smap-rao-guo.md).

# IOP与SMAP绕过

## IOP（I/O Port 利用）

`IDT` 是中断描述符表，其中包括一个处理程序地址的字段，可以泄露 `KASLR`，修改该字段实现 `RIP` 劫持。

中断只会修改 `RIP`、`RSP`、`CS` 和 `SS`，我们可以完全控制其他寄存器。

### 利用链

从用户态除以零进入链 → 偏移至 `entry_SYSCALL_64`（KPTI 页表交换）→ 引发页错误（基于 `RSP` 的 `gs` 获取，由于用户 `gs` 无效）

双重故障（`RSP` 被 `CR3` 覆盖，导致缺页处理程序调用失败）→ `entry_SYSRETQ_unsafe_stack` (`swapgs`; `sysret`) →

通用保护错误（`sysret` 要求 `RCX` 中包含规范的返回地址，由于我们能完全控制寄存器，可使其触发错误）→ `set_memory_x`（将 `IDT` 作为地址传递以使其可执行）→

无效操作码（由于 `cpa_flush` 中的检查，会调用 `BUG_ON`）→ 通过 `IDT` 执行 shellcode

## bypass SMAP

在 `EFLAGS` 寄存器中有一个名为 `AC`（对齐检查）的位，若启用该位，则不允许非对齐内存移动操作；但在内核空间中，它却有另一层含义：用于临时禁用 `SMAP`，以便在内核空间与用户空间之间复制数据。

在每次 ring 3 到 ring 0 的上下文切换例程中，`AC` 都会以某种方式被清除。对于中断处理，每个处理程序都以 `clac` 指令开头。这导致在 IOP 场景中绕过 `SMAP`：由于不执行实际的处理程序，`clac` 指令将永远不会运行，因此 `AC` 将保持设置状态，`SMAP` 被禁用。

启用 `AC`，在用户态设置一个伪造的栈，并将中断重定向到 `mov rsp, X; ret` gadget：简单的 ROP。

如果 `KPTI` 关闭，那将轻松获胜；但如果它已启用，我们可能仍需要 IOP 来交换页表。

在 ARM 架构中，紧接在 `zero_pfn` 之后的页面正是映射 `KPTI` 跳板的第一级页表，从这里开始，我们可以通过修改各个条目，使四级页表重叠在同一个页面上。这样我们就不需要物理地址泄露来伪造页表。为了映射实际代码，我们可以再次重叠页面，并在与伪造页表相同的页面中写入 ring 0 shellcode。
