每次启动都说着不同的语言。昨日的二进制代码,今日已是死代码。
一个会话唯一的RISC-V指令集重映射系统。每次系统启动时,都会生成全新的加密置换指令集。为一次会话编译的二进制文件会在下一次被加密拒绝。生存过重启的恶意软件无法执行——它说着一种已灭绝的方言。
标准ISA是固定不变的契约。每个程序、每个库、每个恶意软件都对0x13的含义、系统调用64的作用、哪个寄存器是栈指针达成一致。DeadDialect在每个会话中打破这个契约。
启动A: addi = 0x13, write = 系统调用64, t0 = x5
启动B: addi = 0x33, write = 系统调用178, t0 = x19
启动C: addi = 0x67, write = 系统调用291, t0 = x23
为启动A编译的二进制文件在启动B上是无意义的。它会执行错误的指令、调用错误的内核函数,或者无法通过加密指纹检查——无论哪种情况最先发生。
256位会话种子 (os.urandom)
│
├─── 操作码置换 → /etc/isa/map 12! ≈ 2²⁹
├─── 寄存器置换 → /etc/isa/register_keyring 21! ≈ 2⁶⁵
└─── 系统调用置换 → /etc/isa/syscall_keyring 436! ≈ 2³⁰⁰⁰⁺
─────────────
组合熵:2³⁰⁹⁴⁺
三个独立层。攻击者即使攻破一层,仍需面对另外两层。
┌──────────────────────────────────────────────────────────────┐
│ 二进制 (.text) │
│ 在编译时由 isa_integrate.py 重写 │
│ 在 .text+0 处嵌入指纹NOP │
└─────────────────────────┬────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────┐
│ 修补后的 QEMU 8.2.0 │
│ │
│ translate.c ← register_mapping.h │
│ ├── 验证24位SHA-256指纹 │
│ │ 不匹配 → 在执行第一条指令前触发SIGILL │
│ └── 重映射21个可混洗寄存器 │
│ │
│ syscall.c ← syscall_mapping.h │
│ └── 将置换后的系统调用号翻译为标准号 │
│ │
│ [阶段1/2] ← isa_mapping.h │
│ └── 重映射12个主要操作码 │
└──────────────────────────┬───────────────────────────────────┘
│
▼
Alpine Linux RISC-V (正常启动)
OS 不知道重映射的存在
每个编译后的二进制文件都携带一个24位加密指纹,嵌入在.text开头两条无害的addi x0,x0,N NOP中。它们是真正的RISC-V NOP——写入零寄存器且无架构影响——但编码了会话证明。
QEMU 看到指纹NOP + 匹配的密钥环 → 运行
QEMU 看到指纹NOP + 错误的密钥环 → SIGILL
QEMU 无指纹 + 激活的密钥环 → SIGILL ← 标准二进制被阻止
QEMU 看到指纹NOP + 空密钥环 → SIGILL ← 重映射的二进制被阻止
指纹由SHA-256(种子)派生。在前100,000个测试种子中未发现碰撞。
DeadDialect/
├── opcode-remapping/ 阶段1 — 12操作码混洗
├── trigger-remapping/ 阶段2 — 实时触发,无需重启
│ └── alpine/ Alpine Linux 演示
├── syscall-remapping/ 阶段3 M1 — 436系统调用混洗
├── register-remapping/ 阶段3 M2 — 21寄存器混洗 + 指纹
└── integration/ 阶段3 M3 — 所有层同时工作
├── isa_integrate.py 统一重写器
├── register_mapping.h
├── syscall_mapping.h
├── isa_remap_ldso.h musl ld.so 补丁(动态二进制)
├── trigger/
│ └── trigger_demo.sh 一个触发器原子性地旋转所有层
├── alpine/
│ ├── boot_alpine.sh 在修补后的QEMU下启动Alpine
│ ├── alpine_demo.sh 在Alpine内部进行完整ISA演示
│ ├── full_alpine_test.sh
│ └── setup_alpine.sh 全新机器设置
├── build.sh 下载并修补 QEMU 8.2.0
├── demo.sh
└── audit.sh 51/51 检查
要求: Ubuntu 22.04, clang, lld, python3, wget, ninja-build, libglib2.0-dev, libpixman-1-dev, libslirp-dev
git clone https://github.com/engrbilal992/DeadDialect.git
cd DeadDialect/integration
# 构建修补后的 QEMU 8.2.0(首次运行约10分钟)
bash build.sh
# 安全演示
bash demo.sh
# 一个触发器同时旋转两个层
bash trigger/trigger_demo.sh
# 在修补后的QEMU下启动Alpine Linux
cd alpine
bash setup_alpine.sh # 仅首次——下载内核+根文件系统
bash boot_alpine.sh # 约30秒后启动到 ~ # 提示符
# Alpine ISA 演示
bash alpine_demo.sh
bash full_alpine_test.sh
T1: 标准二进制,空密钥环 → 运行 ✓
T2: 集成二进制,正确密钥环 → 运行 ✓
T3: 标准二进制,激活密钥环 → SIGILL ✗
T4: 集成二进制,空密钥环 → SIGILL ✗
T5: 错误种子二进制 → SIGILL ✗
T6: 正确种子二进制 → 运行 ✓
会话A 二进制运行 成功 ✓
会话A 恶意软件运行 已执行(预期——同一会话)
── 触发触发 ──
会话B 旧的二进制 已阻止 ✓
会话B 恶意软件 已阻止 ✓
会话B 为B编译的新二进制 成功 ✓
100,000个种子 — 零碰撞
种子999999999 → 在不同会话下被阻止 ✓
种子2²⁵⁶-1 (最大256位) → 在不同会话下被阻止 ✓
密钥环在运行中被破坏 → 在破坏的密钥环上被阻止 ✓
| 里程碑 | 检查项 | 状态 |
|---|
猜测寄存器置换:1 in 21! ≈ 1 in 510亿亿
猜测系统调用置换:1 in 436! ≈ 1 in 10^(1000+)
同时猜测两者:不是可行的攻击
-march=rv64g (RVC压缩指令尚未处理)isa_remap_ldso.h)register_mapping.h)32个RISC-V寄存器中的21个被混洗。ABI寄存器固定。QEMU钩子使用OPCODE_FIELDS表——仅真正的寄存器字段被重映射,立即数从不被触及(Curtis的修复)。
| 固定 | 可混洗 |
|---|---|
| x0 x1 x2 x10-x17 | x3-x9, x18-x31 (21个寄存器) |
syscall_mapping.h)436个Linux RISC-V系统调用被置换。do_syscall()中一行代码。支持mtime-reload——密钥环更新无需重启QEMU。
isa_mapping.h)12个主要操作码被混洗。translate.c中的解码钩子在解码前将标准操作码恢复。
isa_remap_ldso.h)musl动态链接器补丁——在加载时重映射每个ELF的.text。无法通过LD_PRELOAD绕过。需要重新构建Alpine musl。
.text重映射Curtis Cole
Muhammad Bilal
"I knew my idea was real but I'm pretty sure everyone just thought I was crazy." — Curtis Cole
| 触发重映射 | 37/37 | ✓ |
| 系统调用重映射 | 40/40 | ✓ |
| 寄存器重映射 | 43/43 | ✓ |
| 集成 | 51/51 | ✓ |