reverse-skills-max: initial public release (restored, redacted, hardened)
This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
# Linux 平台分支:VMA / mmap-mprotect 追踪 / process_vm_readv
|
||||
|
||||
观测对象是**进程的 VMA(virtual memory area)**——Windows 的 VAD 在这里叫 VMA,但事件与读取路径完全不同。
|
||||
|
||||
## 一、VMA 枚举与信号
|
||||
|
||||
- 主线:`/proc/<pid>/maps`(或 `smaps`,含更细的统计)→ 筛可执行 VMA → backing-file/provenance 判断 → 线程 PC/栈归属 → dump
|
||||
- `maps` 每行字段:`address perms offset dev inode pathname`
|
||||
- `perms` 四字符含义:`r` 读 / `w` 写 / `x` 执行 / `s` 共享 / `p` 私有(COW)
|
||||
- `pathname` 为**空 = 匿名映射**;无文件映射另显示为 `[heap]`、`[stack]`、`[vdso]`、`[anon:<名字>]`、`[anon_shmem:<名字>]`
|
||||
- **高效查询**:Linux **6.11 起**提供 `PROCMAP_QUERY` **ioctl**(`struct procmap_query`,定义在 `linux/fs.h`),可按条件过滤单个 VMA,适合程序化扫描——比解析文本更稳(内核文档:`Documentation/filesystems/proc.rst`)
|
||||
- **高价值信号**:
|
||||
- **匿名 `r-x` / `rwx` VMA**(无文件背书却可执行)
|
||||
- **`/memfd:<名字> (deleted)`** 或 "deleted" 的文件背书可执行映射(载体已从文件系统消失)
|
||||
- **正常 ELF 映射的内容与磁盘上的 ELF 不一致**(代码被改写/替换,对应 Windows 的 module stomping)
|
||||
- **heap/匿名 VMA 事后变为可执行**(W→X 转换)
|
||||
- **线程 PC/RIP 落进这些区域**;**栈上的返回地址指向这些区域**
|
||||
- |注意:**"匿名可执行"是 signal,不是 verdict**——JIT(JVM / V8 / Mono)、Wine/Proton、QEMU TCG 都产生这类 VMA;要看 provenance 与执行史(见本节末)
|
||||
|
||||
## 二、动态侧(trigger):不要只 hook libc
|
||||
|
||||
- 只 hook `libc` 的 `mmap()` / `mprotect()` 会被**直系统调用**绕过
|
||||
- 更底层的事件源:
|
||||
- **syscall tracepoint / kprobe**(`bpftrace`、`perf`)——直接看 `mmap`/`mprotect`/`memfd_create` 等系统调用
|
||||
- **BPF LSM**——把 BPF 程序挂到 LSM hook 上;内核 BPF LSM 文档(`Documentation/bpf/bpf_lsm.rst`,新版为 `prog_lsm.rst`)**正是拿 `file_mprotect` 作示例**:
|
||||
`int file_mprotect(struct vm_area_struct *vma, unsigned long reqprot, unsigned long prot);`,示例程序 `SEC("lsm/file_mprotect")` 在命中时返回 `-EPERM`(即可审计、也可拦截)
|
||||
- 可挂 hook 的清单见内核 `include/linux/lsm_hooks.h`(新版在 `security/security.c`);挂载用 `BPF_RAW_TRACEPOINT_OPEN` 或 libbpf `bpf_program__attach_lsm`;需特权(文档提到 `CAP_MAC_ADMIN` 与 `CAP_SYS_ADMIN`)
|
||||
- 采集侧只需要 **executable protection transition** 这类事件作 trigger,落点即 dump 时机(见 [[re-ebpf]] 的 eBPF 侧细节)
|
||||
|
||||
## 三、执行上下文归属
|
||||
|
||||
- 线程:枚举 `/proc/<pid>/task/`;读 `stat`/`status` 得到状态与位置线索;对可疑线程做栈回溯(gdb/`eu-stack`)取返回地址
|
||||
- 判据:线程 PC 或栈上返回地址落在**匿名/已删除背书的可执行 VMA** 内
|
||||
|
||||
## 四、Dump
|
||||
|
||||
- **`process_vm_readv()`**:跨进程地址空间读取,直接按 [基址, 长度] 取区域;权限受 **ptrace access check** 约束(含 Yama `ptrace_scope`,见 [[re-memdump]] 坑)
|
||||
- 其他路径:`ptrace`(`PTRACE_ATTACH` + `PTRACE_PEEKDATA`/`process_vm_readv`)、`gcore`(整进程 core,注意它是单进程镜像不是整机镜像)、`/proc/<pid>/mem`(同样受 ptrace 权限约束)
|
||||
- Linux 的可执行格式是 ELF:**擦头/无头的载荷同样存在**——保存整区后按内容分类(完整 ELF / 手工映射 ELF / 裸 shellcode / JIT code / 解密 blob)
|
||||
- **内存 vs 磁盘 diff**:`/proc/<pid>/maps` 给出 backing 路径的,取磁盘副本与内存内容逐页/摘要比对(发现代码被替换);配 `[[re-variant]]` 思路做差异定位
|
||||
|
||||
## 五、误报与白名单
|
||||
|
||||
- 白名单/降权:JVM、V8/Node、Mono/.NET、Wine/Proton、QEMU TCG、解释器生成的 trampoline、安全软件自身的探针
|
||||
- 评分与 Windows 分支同构:匿名可执行 + 线程 PC/栈命中 + 近期 W→X + 内容熵/ELF 特征 + 无已知运行时来源
|
||||
|
||||
## 工具与验证
|
||||
|
||||
- 基础:`cat /proc/<pid>/maps`(验证能看到 address/perms/pathname 三段);`smaps` 看 `Rss`/`Private_Dirty` 等
|
||||
- 事件:`bpftrace -l 'tracepoint:syscalls:*mprotect*'` 应与目标内核实际提供的 tracepoint 对得上;BPF LSM 需内核 `CONFIG_BPF_LSM` 与 BTF(验证 `ls /sys/kernel/btf/vmlinux`)
|
||||
- 采集:`python3 -c "import ctypes; ..."` 或直接用 gdb `dump memory` 验证 `process_vm_readv` 路径可达
|
||||
|
||||
## 该平台的坑
|
||||
|
||||
- **只 hook libc 就以为覆盖**:直系统调用绕过——用 tracepoint/kprobe 或 BPF LSM
|
||||
- **把 VMA 当 VAD 直接翻译**:Linux 没有"模块链与 VAD 交叉"那套(模块 = 文件背书映射),替代物是 maps 的 pathname/inode 与磁盘文件比对
|
||||
- **`process_vm_readv` 报 EPERM**:先看 Yama ptrace_scope 与目标进程关系([[re-memdump]] 坑),不要误判为载荷消失
|
||||
- **把匿名可执行当恶意**:JIT/Wine/TCG 大量产生——先基线、再评分
|
||||
|
||||
## 六、内核态载荷(LKM / rootkit)的采集
|
||||
|
||||
用户态扫描看不到的内核态载荷,采集逻辑与用户态同构但要换观测面(分析方法见 [[re-kernel]] 的 Linux 分支):
|
||||
|
||||
- **cross-view 是前提**:`lsmod` / `/proc/modules` 干净**不构成结论**(rootkit 可把模块从链表摘掉,模块仍在运行)。交叉比对:
|
||||
- 模块视图:`/proc/modules`、`/sys/module/<name>/`
|
||||
- 符号视图:`/proc/kallsyms`(受 `kptr_restrict` 影响)
|
||||
- 节地址视图:`/sys/module/<name>/sections/`
|
||||
- 内核可执行内存区间(离线镜像侧见 [[re-mem-forensics]];在线侧需要内核态手段,属授权场景)
|
||||
- 视图互相矛盾时,**优先信任离线内存取证**,而不是被分析系统自己给出的输出
|
||||
- **inline text patch 的发现**:内核 text 与磁盘 `vmlinux` / 模块副本逐段比对(函数头被改写但地址不变——与用户态的 inline hook 同构)
|
||||
- **hook 落点不止 syscall table**:`file_operations`、`seq_operations`、proc handlers、网络协议回调、ftrace、kprobes 都被用过——"syscall table 干净"不是结论
|
||||
- **采集时机**:内核态载荷同样有"用后清除"(模块自卸、hook 复原)——命中即 dump
|
||||
- 授权边界:内核态采集通常需要加载自己的模块 / 用 kprobe 一类机制,**只在自有或已授权设备上做**,并记录停止条件
|
||||
@@ -0,0 +1,71 @@
|
||||
# macOS 平台分支:Mach task / VM region / Endpoint Security
|
||||
|
||||
观测对象是 **Mach task + VM region**(不是 `/proc`,也不是 Windows 的 VAD 模型)。核心区别在于**权限**:macOS 上"有权限"取决于 task port 与签名状态,不是 uid。
|
||||
|
||||
## 一、静态主线
|
||||
|
||||
```
|
||||
task 端口 → VM regions → RX/RWX/private region 筛 → Mach-O/dyld provenance → 线程 PC/backtrace → dump
|
||||
```
|
||||
|
||||
- 遍历 region:`vm_region_recurse_64()` / `mach_vm_region*()`——返回区域的地址范围、**protection**、sharing、resident/private 等属性
|
||||
- 读取内容:`mach_vm_read()` / `mach_vm_read_overwrite()`(需目标 task 的 read 权限)
|
||||
- 归属判断:线程 PC 与回溯是否落在"无 Mach-O/dyld 背书的可执行 region"内
|
||||
- 相关 API 一族(都需要 task port):`task_for_pid`、`mach_vm_region`、`mach_vm_read` / `mach_vm_read_overwrite`、`vm_region_recurse_64`、`mach_vm_protect`
|
||||
|
||||
## 二、权限边界(本平台最容易误判的地方)
|
||||
|
||||
- **SIP**(System Integrity Protection,10.11 起):对 SIP 保护的进程,`task_for_pid()` / `processor_set_tasks()` 返回 **EPERM**;需要 Apple 内部的特殊 entitlement(不对外提供)。SIP 下 Mach 特殊端口在 `exec` 时被重置、dyld 环境变量被忽略、DTrace 探针不可用
|
||||
- **Hardened Runtime**(公证要求):默认**吊销** `task_for_pid` 权限,并拒绝动态库注入、`mprotect(PROT_EXEC)` 标记任意页可执行、`task_set_exception_ports` 等——除非 App 声明了例外 entitlement
|
||||
- `com.apple.security.get-task-allow`(调试工具 entitlement):**目标进程**带此 entitlement 时才允许被 attach / 取 task port(发布版不该有,出现即可疑信号)
|
||||
- 结论:**不是 root 就能读**。采集前先确认目标是否 SIP 保护、是否 Hardened Runtime、是否可 attach——不可读时明确标注"权限不足",不要当成"内存里没有东西"(禁用 SIP 需进恢复模式,且会削弱多项系统防护,非授权研究场景不建议)
|
||||
|
||||
## 三、动态侧(trigger):Endpoint Security
|
||||
|
||||
macOS 上不需要(也不应该)写成"hook `mprotect()`"——系统提供了正规的观测接口 **Endpoint Security(ES)**:
|
||||
|
||||
- 与内存/执行相关的**已文档化事件**:
|
||||
- `ES_EVENT_TYPE_NOTIFY_MMAP` —— 进程**把文件映射进内存**
|
||||
- `ES_EVENT_TYPE_NOTIFY_MPROTECT` —— 进程**改变内存映射页的保护属性**(RWX/可执行化信号)
|
||||
- `ES_EVENT_TYPE_NOTIFY_REMOTE_THREAD_CREATE` —— 进程**在别的进程里创建线程**(macOS 11.0+)
|
||||
- `ES_EVENT_TYPE_NOTIFY_GET_TASK` —— 进程**获取别的进程的 task 控制端口**(macOS 10.15.4+;另有 `GET_TASK_NAME` / `GET_TASK_READ` / `GET_TASK_INSPECT`,11.0/11.3+)
|
||||
- **NOTIFY 与 AUTH 两类**:`NOTIFY_*` 只观测;`AUTH_*`(如 `AUTH_MMAP` / `AUTH_MPROTECT` / `AUTH_GET_TASK`)可**允许或拒绝**操作——授权场景下可做阻断式采集(先取证再放行/拒绝)
|
||||
- 事件订阅通过 `es_subscribe` 注册(常量类型 `es_event_type_t`,可用项随 macOS 版本 gating,按目标 SDK 头文件核对)
|
||||
- 采集侧的分工,与 Windows/Linux 同构:**ES 负责"何时"触发,Mach VM 负责"是什么"的事实确认**
|
||||
|
||||
**高置信组合**(多事件相关,而不是单点告警):
|
||||
|
||||
```
|
||||
REMOTE_THREAD_CREATE(在他进程建线程)
|
||||
+ GET_TASK(先取了 task port)
|
||||
+ MPROTECT(W→X 保护转换)
|
||||
+ 匿名/私有可执行 VM region(Mach VM 侧确认)
|
||||
```
|
||||
|
||||
## 四、代码完整性对比(发现 patch / 替换)
|
||||
|
||||
- **Mach-O `__TEXT` 内存页与磁盘副本比对**(按页/摘要):代码段被改写(patch、inline hook、模块替换)时两者不一致
|
||||
- 结合 [[re-format-macho]] 的段/签名解析与 [[re-macos]] 的签名公证视图(CDHash、签名者)判断"内存中的镜像是否仍是签名时那份"
|
||||
|
||||
## 五、工具与验证
|
||||
|
||||
- `lldb`([[re-lldb]]):attach 目标后 `image list`、`memory region`、`process save-core`
|
||||
- `vmmap` / `otool` / `codesign -d`:region 视图、Mach-O 结构、签名与 entitlement 核对
|
||||
- ES 客户端:需相应 entitlement 与用户授权(系统设置中批准),验证方式是先订阅 `MPROTECT` 事件看能否收到系统内正常进程的事件
|
||||
|
||||
## 该平台的坑
|
||||
|
||||
- **以为 root 能读一切**:SIP / Hardened Runtime 下 `task_for_pid` 直接 EPERM——先判断权限,再设计采集方式;读不到不是"没有异常"
|
||||
- **照搬 hook 思路**:macOS 上有 ES 这类正规接口,优先用事件而不是用户态 hook(hook 在 Hardened Runtime/SIP 下也更容易失效)
|
||||
- **忽略 AUTH 与 NOTIFY 的区别**:只有 AUTH 类事件能拦;NOTIFY 类只能观测(采集取证通常够用)
|
||||
- **ES 事件随版本 gating**:新事件常量在旧系统不可用——按目标系统版本与 SDK 头文件核对可用集
|
||||
|
||||
## 六、内核态载荷(KEXT / DEXT)的采集
|
||||
|
||||
macOS 上"内核态载荷"分两代,采集路径完全不同(分析方法见 [[re-kernel]] 的 macOS 分支):
|
||||
|
||||
- **DEXT(System Extension / DriverKit)——按用户态进程处理**:它跑在用户空间,所以正常走本文件的用户态路径(进程枚举、`mach_vm_read` 等),不需要内核态手段;注意先确认 **activation 状态与 entitlement**(未激活/entitlement 不匹配时进程根本起不来,别误判为"没有载荷")
|
||||
- **KEXT(legacy)——先确认运行版本**:macOS 11+ 第三方 kext 并入 **AuxKC 在启动时加载**,更新后重启前**旧版本可能仍在运行**——采集前记录 **disk bundle version / AuxKC version / running version / boot time**,否则会采到/分析到"不是正在跑的那一份"
|
||||
- **kext 列表的 cross-view**:磁盘 kext 目录 / `kmutil` 视图 / IORegistry 绑定关系 交叉比对(与 Linux 的模块列表交叉同构)
|
||||
- **内核内存读取的现实**:SIP 与 Hardened Runtime 下 `task_for_pid` 对受保护目标 EPERM(见本文件第二节)——内核内存采集通常需要关闭 SIP 或用具备相应授权的机制,属授权研究场景,先确认权限与合规要求
|
||||
- 采集产物按 [[re-analyze/analysis-contract]] 入档,并标注"采集自磁盘 / AuxKC / 运行内存"三者中的哪一份
|
||||
@@ -0,0 +1,75 @@
|
||||
# seL4 平台分支:capability provenance(不是"扫描进程")
|
||||
|
||||
seL4 上**观测模型与前三个平台根本不同**,因此方法不能翻译过来:这里**没有"root 枚举全系统进程、偷看别人内存"这回事**。
|
||||
|
||||
## 一、为什么不同
|
||||
|
||||
- **无 capability 即无访问权**:一切操作都必须由持有**不可伪造的 capability** 的线程发起。内核对象(TCB、VSpace、CNode、Endpoint、Notification、frame/page)全部是 capability 控制的资源
|
||||
- 初始 **root task** 在启动时获得系统资源的 caps;此后资源通过 capability 派生/授予分发
|
||||
- Microkit 的 **protection domain(PD)** 默认只有自己的 ELF/VSpace 与**显式授予**的资源(每个 PD = 自己的 CSpace + VSpace + TCB,入口点 `init`/`notified`/`protected`/`fault`)
|
||||
- 因此"从一个被注入的 PD 内部往外看"与"从系统外扫面"这两条路都不成立——**能观察什么,完全由你持有的 capability 决定**
|
||||
|
||||
## 二、正确路线:构造期记录 provenance
|
||||
|
||||
不要做"扫描 → 猜哪个 PD 被注入",而是**在系统构造期就让 security monitor 掌握 provenance**:
|
||||
|
||||
```
|
||||
root task / system monitor
|
||||
├── TCB caps(线程上下文)
|
||||
├── frame/page caps(物理页来源)
|
||||
├── VSpace 构造记录(谁映射了什么、什么权限)
|
||||
├── 可执行页策略(哪些 frame 允许被映射为可执行)
|
||||
├── fault endpoints(异常事件入口)
|
||||
└── loader provenance(ELF loader / 可信组件登记的镜像)
|
||||
↓
|
||||
execution provenance graph(可执行页 ← 来源 frame ← 映射者 ← 授权链)
|
||||
```
|
||||
|
||||
- seL4 的**页映射本身就是显式的 capability invocation**:一个 frame/page 被映射进哪个 VSpace、以什么权限映射,都是系统构造过程的一部分,因此天然可记录
|
||||
- 由此可回答前三个平台很难回答的问题:**"这个可执行页是谁创建的、来自哪个 untyped/frame、被映射进哪个 VSpace、谁持有映射权限"**
|
||||
- 检测/采集逻辑(策略驱动,优于定时轮询):
|
||||
|
||||
```
|
||||
ELF loader / 可信组件登记合法可执行 frames
|
||||
↓
|
||||
出现任何新的可执行映射
|
||||
↓
|
||||
校验 frame provenance + capability 路径
|
||||
↓
|
||||
不合策略 → 阻止 / 触发 fault / 现场快照(snapshot)
|
||||
```
|
||||
|
||||
## 三、提问方式的转变
|
||||
|
||||
- 前三个平台问:"恶意代码藏在哪个进程里?"(事后从复杂共享系统里**恢复** provenance)
|
||||
- seL4 上应该问:**"哪个 capability flow 让未授权代码获得了 executable authority?"**(provenance 在设计期就**保留**下来了)
|
||||
- 这两条路线的对照本身很有价值:前三者是"事后重建",seL4 是"设计即保留"——同一技能里并列,能覆盖两类系统的采集思路
|
||||
|
||||
## 四、可用的内核设施(前提:monitor 持有相应 cap)
|
||||
|
||||
- **TCB debug API**(硬件调试能力,需以 `-DHardwareDebugAPI=1` 构建;不支持的平台内核启动即 abort):
|
||||
- `seL4_TCB_SetBreakpoint` / `seL4_TCB_UnsetBreakpoint` / `seL4_TCB_GetBreakpoint` —— 按虚拟地址设置**断点/观察点**(指令或数据、读/写/读写、范围)
|
||||
- `seL4_TCB_ConfigureSingleStepping` —— 单步(每 N 条指令中断一次;`num_instructions=0` 关闭),**每次触发都向该线程的 fault endpoint 发消息**,回复 fault 消息即继续执行
|
||||
- 这些 invocation 都要**指向目标 TCB 的 capability**——再次说明:能调试谁,取决于你持有什么
|
||||
- **fault 是一等事件**:VM fault、debug fault 等按配置发送到**fault endpoint**;Microkit 中 PD 的 `fault` 入口由 monitor 的 fault handler 处理
|
||||
- 监视器可据此把"异常执行"变成**事件驱动**(而不是采样扫描)
|
||||
|
||||
## 五、落地清单(按目标系统构造裁剪)
|
||||
|
||||
1. 确认系统构造:root task / monitor 结构、各 PD 的资源与通道(Microkit 的 system 描述文件是权威来源)
|
||||
2. 收集 provenance 来源:loader 记录、VSpace 构造 invocation 记录、可执行页策略、fault endpoint 订阅
|
||||
3. 定义"合法可执行页"集合(哪些 frame/VSpace/权限组合是构造期认可的)
|
||||
4. 建立运行时校验:新可执行映射 → 回溯 frame provenance 与 capability 路径 → 判定
|
||||
5. 处置:阻止(策略拒绝)/ 触发 fault 进入监视器 / 现场快照(保存涉事 frame 与映射记录)——**先取证再处置**
|
||||
6. 记录输出按 [[re-analyze/analysis-contract]] 的字段(target_id 用系统/PD 标识;evidence 用 capability 路径与映射记录)
|
||||
|
||||
## 方法侧入口
|
||||
|
||||
本篇是**采集/监视视角**(假定你是有权限的一方,在系统构造期记录 provenance)。若目标是一个**已有的 seL4 系统**(要逆向它、理解它的对象图与 IPC 拓扑),走 [[re-kernel]] 的 [[re-kernel/sel4-kernel]] 分支——那边给的是"capability 系统怎么读、哪些现象不能按普通 OS 判"。
|
||||
|
||||
## 该平台的坑
|
||||
|
||||
- **套用前三平台的思路**:从"root 扫面"或"注入检测工具"出发在 seL4 上没有对应物——先明确"我持有哪些 capability"
|
||||
- **把 seL4 当"Linux 的特殊版本"**:观测边界完全不同,方法要从 capability 模型重推
|
||||
- **忽略构建期配置**:TCB debug API 需显式构建开关;不同平台支持度不同(不支持的平台内核启动即 abort)
|
||||
- **只做周期性扫描**:capability 路径与 fault endpoint 本来就能给出事件与来源,定时轮询是退化用法
|
||||
@@ -0,0 +1,58 @@
|
||||
# Windows 平台分支:VAD / 线程 / 事件 / 内核
|
||||
|
||||
观测对象是**进程的 VAD(虚拟地址描述符)与 region + 线程 + 模块链**;进程只是容器。
|
||||
|
||||
## 一、异常执行区扫描(不猜 PID)
|
||||
|
||||
- 遍历:`VirtualQueryEx` / `NtQueryVirtualMemory` 走目标地址空间;全进程扫面用 HollowsHunter(PE-sieve 引擎)
|
||||
- **优先特征**:
|
||||
- `State=MEM_COMMIT` + `Type=MEM_PRIVATE` + 保护含执行位(Volatility `malfind` 的经典判据:private + committed + executable)
|
||||
- **保护属性转换**:W→X、RW→RWX→RX——先写后改可执行是最强信号;转换瞬间即最佳捕获时机(步骤三的 hook 点)
|
||||
- 保护值参考:`PAGE_EXECUTE_READWRITE`(0x40)、`PAGE_EXECUTE_READ`(0x20)、`PAGE_EXECUTE_WRITECOPY`(0x80)
|
||||
- **`MEM_IMAGE` 不能判白**:
|
||||
- module stomping / DLL hollowing 把 payload 塞进看似合法的 DLL 映射(映射类型仍是 `MEM_IMAGE`)
|
||||
- copy-on-write 的 image 页在 `VirtualQueryEx` 里**仍报 `MEM_IMAGE`**——要判断它是否已成本进程私有修改页,需查 **Working Set 的 `.Shared` 位**(`K32QueryWorkingSetEx`);该位也是发现**内联 hook** 的手段(写钩子触发 COW 私有化)
|
||||
- **模块链交叉(Missing PEB module)**:用 `VirtualQueryEx` + `NtQueryVirtualMemory` 取 `MEM_IMAGE` 的映射文件路径,与 PEB 加载模块列表比对——"有映射、无 PEB 条目"= 被摘链隐藏的模块(`GetModuleHandle` / `EnumProcessModules` 看不到它,内存视图能看到)
|
||||
|
||||
## 二、执行上下文归属
|
||||
|
||||
- 线程起点:`NtQueryInformationThread(hThread, 9 /* ThreadQuerySetWin32StartAddress */, &addr, sizeof(addr), NULL)` → `VirtualQueryEx` 查该地址所在 region:`MEM_COMMIT && Type != MEM_IMAGE` 即可疑(Get-InjectedThread 类脚本的判据)
|
||||
- **只看起点会漏**(已知绕过):① LoadLibrary/DLL 注入(代码落在 `MEM_IMAGE`);② `SetThreadContext` 改写 RIP(起点显示在合法模块);③ `jmp rcx` 类 gadget(借模块内已有跳转指令作入口);④ Win11 经 `_beginthreadex` 的线程起点落在 msvcrt;⑤ 从合法 DLL 起跳后 trampoline 进 private 可执行区
|
||||
- **补三层证据**:当前 RIP、**调用栈返回地址**、region ownership——PE-sieve `/threads` 专扫线程调用栈(可抓 shellcode 与 sleeping beacon)
|
||||
|
||||
## 三、事件侧(trigger,不是 ground truth)
|
||||
|
||||
- **Sysmon**:Event 10(ProcessAccess——谁打开了目标)、Event 8(CreateRemoteThread,带 `StartAddress` / `StartModule` / `StartFunction`)、Event 25(ProcessTampering / image hollowing)
|
||||
- **ETW**:Threat-Intelligence 提供者可暴露直系统调用、内存分配与线程上下文改写(驱动/EDR 实现侧见 [[re-evasion]])
|
||||
- **内核回调**(有驱动条件时):`PsSetCreateProcessNotifyRoutine` / `PsSetCreateThreadNotifyRoutine` / `PsSetLoadImageNotifyRoutine`——LoadImage 发现"无对应磁盘文件的可执行映射"、线程起点不在任何模块内,都是强证据
|
||||
- 与内存扫描的分工:事件源回答"**何时** dump",内存扫描回答"**dump 什么**";直系统调用 / 共享 section / 覆盖已有 RX 区可绕过用户态 hook
|
||||
|
||||
## 四、Dump 与重建
|
||||
|
||||
- 工具:`pe-sieve.exe /pid <pid> /shellc /hooks /imp /dmode unmap`(注入 PE 自动解映射重建)、`/minidmp`、`procdump -ma <pid> out.dmp`
|
||||
- **保存整个可疑 region**,不要只按 `MZ` 筛:载荷可能无 PE 头或故意擦头(经典案例:注入后擦 PE 头 → 靠区域定位 → dump 后按节重建 PE)
|
||||
- 内容分类:完整 PE / 手工映射 PE / 裸 shellcode / JIT 代码 / 解密后的 code blob
|
||||
- **模块内存 vs 磁盘差异**:同名模块的磁盘副本与内存副本比对(发现 stomping、patch、擦头);PE-sieve 的 `/dmode unmap|realign` 与导入表恢复(`/imp`)就是为重建服务
|
||||
|
||||
## 五、内核与驱动载体(外挂 / rootkit 常见)
|
||||
|
||||
- 内核可执行内存扫描 + **驱动加载前后对比**定位无模块驱动(驱动可把自身从模块链摘除)
|
||||
- 记录:驱动对象、Device/Io 派遣例程是否被改写(劫持系统驱动指针通信是常见手法)、回调注册(`ObRegisterCallbacks` 等)是否被摘除
|
||||
- 方法细节与工具见 [[re-kernel]]
|
||||
|
||||
## 六、误报与白名单
|
||||
|
||||
- 白名单/降权:.NET 与浏览器 JIT、AV/EDR 自身的 hook 与 patch、shim/hotpatch、profiler、overlay
|
||||
- 评分:private executable + 线程/RIP/栈命中 + 近期 W→X + 高熵/PE 特征 + 无已知来源 → 高置信;单项不足以定性
|
||||
|
||||
## 工具与验证
|
||||
|
||||
- PE-sieve / HollowsHunter:GitHub releases(`hasherezade/pe-sieve`、`hasherezade/hollows_hunter`),核对 release 页 sha256;验证 `pe-sieve.exe /help`
|
||||
- System Informer(systeminformer.com)/ Process Explorer(Sysinternals):region、线程栈、句柄三视图
|
||||
- 验证采集面:能在目标进程里同时看到「可执行 region 清单 + 每个线程的起点与所在 region + 模块链」
|
||||
|
||||
## 该平台的坑
|
||||
|
||||
- **只 hook `VirtualProtect`(kernel32)**:样本直调 `NtProtectVirtualMemory` 就绕过了——两处都要挂(或改用内核回调)
|
||||
- **把 `MEM_PRIVATE + executable` 当结论**:JIT 与安全软件同样产生——先采基线(同类进程干净环境)只报增量
|
||||
- **无痕 hook**:影子页/硬件断点实现的无痕 hook 让"读到"与"执行到"不是同一份内存——多视图(用户态读取、内核侧读取、PE-sieve)交叉,差异即证据
|
||||
Reference in New Issue
Block a user