CyberOneFR/Semantic-Shellcode-Injection
GitHub: CyberOneFR/Semantic-Shellcode-Injection
形式化定义语义 Shellcode 注入技术,系统分析其在编译型、JIT 和解释型语言运行时中的适用条件与攻击边界。
Stars: 7 | Forks: 1
# 语义 Shellcode 注入 — 扩展篇
## 形式化定义与适用条件
**作者:** 42 Lyon Auvergne Rhône Alpes 在校生,1 年 C 语言经验
**标签:** `SSI` `formal-definition` `language-theory` `JIT` `interpreter` `attack-surface`
## 前言
最初的报告将 SSI 记录为一种 C 语言技术。在将研究扩展到 JavaScript/V8 和 Lua 的过程中,出现了一个更深层次的问题:SSI 是 C 语言特有的,还是描述了一个更通用的攻击类别?
本扩展通过形式化 SSI 并推导其适用(或不适用)于任何语言运行时的条件,回答了这个问题。
## 形式化定义
三个属性将 SSI 与经典的注入技术区分开来:
**属性 1 — 语义合法性**
源代码在目标语言中在语法和语义上都是有效的。没有内存写入,没有缓冲区溢出,没有显式的 shellcode 缓冲区。编译器或 JIT 完全按照语言规范行事。
**属性 2 — 编译器强制放置**
攻击者不向内存写入字节。工具链写入字节,因为语言要求这样做。这种放置是编译语义的必然结果,而不是漏洞的副作用。
**属性 3 — 可执行区域**
字节落在 CPU 可以直接执行的区域:编译语言中的 `.text` 段,或 JIT 语言中的 JIT 代码区域。不需要 `mprotect`,不需要 `mmap`,不需要绕过 W^X。
## 必要条件 — 源码到可执行文件的边界
跨 C、JavaScript/V8 和 Lua 的研究确定了 SSI 的一个单一必要条件。我们称之为 **源码到可执行文件的边界**:
该边界以两种形式存在:
### 直接边界 — 编译型语言
```
Source constant → compiler encodes as x86 immediate → lands in .text
```
路径是确定性和无条件的。每个形式为 `x = 0xdeadbeef` 的 C 赋值都会导致编译器将这些字节写入 `.text`。SSI 可以轻松应用。
语言:C, C++, Rust, Zig, Go, 汇编。
### 间接边界 — JIT 语言
```
Source constant → JIT compiler → speculative native code → JIT region
```
路径存在,但取决于 JIT 启发式规则:函数必须足够“热”才能被编译,类型反馈必须足够稳定以便 JIT 进行特化,并且常量必须在常量折叠或致盲等优化中存活下来。
对 V8/TurboFan 的研究证实了 JavaScript 存在此边界:
```
// 4-byte injection via loop bound (cmpl imm32)
for (let i = 0; i < 0x1000bead; i++) { ... }
// TurboFan emits: 81 f9 ad be 00 10 in the JIT region
// 8-byte injection via BigUint64Array store
view[0] = 0xdeadbeefdeadbeefn;
// TurboFan emits: REX.W movq reg, 0xdeadbeefdeadbeef in the JIT region
```
语言:JavaScript (V8, SpiderMonkey), LuaJIT, Java (JVM JIT), C# (.NET RyuJIT), PyPy。
### 无边界 — 纯解释器
```
Source constant → interpreter heap → never becomes CPU instructions
```
在纯解释器中,常量作为数据对象存储在 VM 堆上。VM 通过 C 语言的 `switch/case` 分发循环执行它们 —— 常量是操作数,绝不是指令。源代码写入的字节永远不会到达可执行区域。
语言:Lua (标准版), CPython, Ruby MRI, 大多数嵌入式脚本引擎。
**x86 意义上的 SSI 在纯解释器中是不可能的。** 这不是一种保护措施 —— 它是解释模型在架构上的必然结果。
## 语言分类
| 运行时 | 类型 | 边界 | SSI 适用性 | 直接系统调用 |
|---|---|---|---|---|
| C / C++ / Rust / Zig | Compiled | Direct (.text) | ✓ trivial | ✓ |
| JavaScript / V8 | JIT (TurboFan) | Indirect (JIT region) | ◑ injection yes, execution blocked | ✗ sandboxed |
| LuaJIT | JIT (trace-based) | Indirect (JIT region) | ◑ likely achievable | ◑ context-dependent |
| Java / .NET | JIT (JVM / RyuJIT) | Indirect (JIT region) | ◑ context-dependent | ◑ context-dependent |
| Lua (standard) | Pure interpreter | None | ✗ impossible | ✗ |
| CPython / Ruby MRI | Pure interpreter | None | ✗ impossible | ✗ |
## SSI-VM — 纯解释器的字节码变体
即使直接的 x86 边界不存在,对于在运行时暴露其自身字节码的语言,也存在一个较弱的类似情况。我们称之为 **SSI-VM**。
在 Lua 中,这是原生暴露的:
```
-- string.dump() serializes a function to its raw bytecode
local bytecode = string.dump(function()
local x = 0xdeadbeef -- this constant appears verbatim in the bytecode
end)
-- The bytecode bytes can be modified directly
local patched = bytecode:sub(1, offset - 1) .. payload_opcodes .. bytecode:sub(offset + n)
-- load() compiles and executes arbitrary bytecode
load(patched)()
```
SSI-VM 的能力受限于 VM 本身所允许的范围。如果宿主应用程序从环境中移除了 `os`、`io` 和 `debug`(如 Roblox, CS2 和 WoW 所做的那样),SSI-VM 只能在这些限制内操作。不存在通往直接系统调用的路径。
SSI-VM 适用于:Roblox/Luau, WoW 插件, CS2 服务器插件, 任何可访问字节码 API 的嵌入式 Lua/Python 环境。
## SSI 的两阶段模型
JavaScript 研究表明,SSI 自然分解为两个独立的问题:
```
Phase 1 — Injection
Can attacker-controlled bytes appear in an executable region?
Depends on: boundary existence, JIT heuristics, constant survival
Phase 2 — Execution
Can execution be redirected to those bytes?
Depends on: address leakage, memory write primitive, sandbox model
```
在 C 中,这两个阶段都是简单的 —— 编译器将字节放置在已知偏移处,栈操作重定向执行。
在 JavaScript/V8 中,第一阶段已被确认可实现。第二阶段被三个独立的架构障碍所阻挡:
- **指针压缩** — 堆指针是 32 位偏移量,不是可用的地址。
- **JIT 区域隔离** — 没有 JS API 暴露 JIT 代码区域中的地址。
- **无任意内存读取** — 类型混淆原语 (addrof) 需要一个未修补的 CVE。
SSI-JS 的结论:注入存在,但 V8 通过设计阻止了其从 JavaScript 端的遍历,而非偶然。
## 攻击面总结
| 类别 | 字节注入 | 地址泄露 | 执行重定向 | 系统调用 |
|---|---|---|---|---|
| Compiled (C/Rust…) | ✓ trivial | ✓ native | ✓ stack/ptr | ✓ |
| JIT — browser JS (V8) | ✓ confirmed | ✗ sandboxed | ✗ blocked | ✗ |
| JIT — embedded engines | ✓ likely | ◑ less protected | ◑ possible | ◑ |
| JIT — LuaJIT | ✓ likely | ◑ exposed by -jdump | ◑ no browser sandbox | ◑ |
| Pure interpreter | ✗ impossible | N/A | N/A | ✗ |
| SSI-VM (bytecode) | VM opcodes only | N/A | ✓ via load() | ✗ |
## 结论
SSI 不是一种特定于 C 的技术。它是一个通用的攻击类别,由一个单一必要条件定义:目标运行时中存在源码到可执行文件的边界。
该边界在编译语言中是无条件的,在 JIT 语言中是有条件的,并且在纯解释器中结构性地不存在。攻击的强度随后受限于攻击者定位注入字节并将执行重定向到该处的能力 —— 这个问题在 C 中很简单,在浏览器中被 V8 沙箱阻止,而在保护较少的 JIT 上下文(如嵌入式引擎或 LuaJIT)中则可能解决。
分解为注入阶段和执行阶段是关键洞察:如果只有第一阶段可实现,运行时可能是部分脆弱的 —— 可注入但不可利用。V8 正处于这种状态。
## 开放研究方向
- **WebAssembly 作为执行桥梁** — WASM 的线性内存模型和较不激进的常量致盲可能为在浏览器上下文中完成第二阶段提供途径。
- **非 V8 JS 引擎** — QuickJS, Duktape, Hermes (React Native) 缺乏指针压缩和浏览器沙箱。完整的 SSI 可能是可实现的。
- **游戏引擎中的 LuaJIT** — 用于 Roblox, nginx/OpenResty 等上下文,其沙箱模型由应用定义,且可能比 V8 更弱。
- **Spectre/时序侧信道** — `SharedArrayBuffer` + 高分辨率计时器可以泄露任意内存地址,可能提供第二阶段所需的地址原语,而无需 CVE。
*这项工作是在纯粹的教育和个人研究背景下进行的。*
标签:AI工具, CMS安全, DNS 反向解析, JavaScript, JIT攻击, Lua, rizin, Shellcode, V8引擎, W^X, 二进制安全, 云资产清单, 免杀技术, 内存安全, 可视化界面, 多人体追踪, 客户端加密, 形式化定义, 技术调研, 攻击面分析, 数据可视化, 暴力破解检测, 编译器安全, 解释器安全, 语义壳码注入, 语言运行时, 逆向工程