Windows Native API(NT API)
在 Windows 安全语境里,NtWriteVirtualMemory、NtOpenProcess、NtCreateThreadEx 这类接口常被称为“未公开内核 API”。这个说法不算完全准确。
更准确地说,它们通常属于 Windows Native API / NT API:很多接口由用户态的 ntdll.dll 导出;它们比 Win32 API 更接近 Windows 内核对象和系统服务边界;但文档完整度、兼容性承诺和长期稳定性通常不如高层 Win32 API。
从学习和检测角度,更适合把它们理解为:
比 Win32 更底层
更贴近系统调用边界
经常被攻防双方关注
但不等于“神秘后门接口”
分层
Win32 到 Native API
理解这类 API,最重要的是先把 Windows 的接口分层想清楚。许多常见能力并不是应用程序直接“调用内核”,而是会经过一层层封装。
例如跨进程写内存,常见抽象关系可以理解为:
应用程序
↓
WriteProcessMemory
↓
KernelBase / Kernel32 的参数整理与兼容层
↓
ntdll!NtWriteVirtualMemory
↓
系统调用入口
↓
内核中的内存管理与对象访问检查逻辑
所以,NtWriteVirtualMemory 可以理解为 WriteProcessMemory 更底层的一层能力。
不过需要注意:
- 高层 API 并不只是机械转发一行代码,中间还可能有参数处理、兼容性适配、异常转换和边界检查。
- 不同 Windows 版本的实现细节可能变化。因此更稳妥的说法是“
WriteProcessMemory常见情况下会下沉到NtWriteVirtualMemory这一层能力”,而不是将某一版本的调用细节绝对化。
为什么会被称为“未公开 API”
这种说法能流行,通常有几个原因:
- 安全分析文章更常讨论
Nt*接口,而非普通开发文档中的 Win32 API。 - 这类接口的参数风格、返回值和命名更接近内核对象模型,不像面向一般业务开发者的易用封装。
- 微软对 Win32 API 的兼容性承诺通常更强;不少 Native API 虽长期存在,却不适合作为普通应用长期绑定的稳定 ABI。
因此,Nt* / Zw* 更像是:
系统内部长期使用
许多接口也确实可见、可调用
但不是微软最鼓励普通应用直接依赖的稳定开发层
这和“完全私有、谁也看不到”并不是一回事。
NtWriteVirtualMemory
定义
NtWriteVirtualMemory 是 Windows Native API 中与“向目标进程地址空间写入数据”相关的接口。放在更熟悉的 Win32 语境里,它通常对应:
WriteProcessMemory
公开攻防分析中经常会看到这样的 Win32 链路:
OpenProcess
↓
VirtualAllocEx
↓
WriteProcessMemory
↓
CreateRemoteThread
从更底层的 Native API 视角看,经常对应为:
NtOpenProcess
↓
NtAllocateVirtualMemory
↓
NtWriteVirtualMemory
↓
NtCreateThreadEx
这也是为什么逆向、恶意代码分析和 EDR 对抗文章喜欢直接讨论 NtWriteVirtualMemory。
权限误解
很多人第一次看到 NtWriteVirtualMemory,会下意识认为:
既然它更底层
是不是就比 WriteProcessMemory 更能绕过权限检查?
这通常是误解。更准确地说:
NtWriteVirtualMemory更接近系统服务边界。- 它不是“无条件写任何进程内存”的万能接口。
- 调用方仍要先拿到有效的目标进程句柄。
- 句柄仍须具有合适访问权限,例如
PROCESS_VM_WRITE,并通常配套PROCESS_VM_OPERATION。 - 内核仍会结合令牌、DACL、完整性级别和保护状态做访问检查。
因此:
NtWriteVirtualMemory
≠
绕过安全模型
它只是更贴近安全模型真正生效的位置。
常见 Native API
接口对照
下面这些接口,在 Windows 安全、恶意代码分析和 EDR 遥测里都很常见。
| Win32 / 常见高层能力 | Native API / NT API | 作用 |
|---|---|---|
OpenProcess | NtOpenProcess | 打开目标进程对象并申请句柄 |
ReadProcessMemory | NtReadVirtualMemory | 读取目标进程地址空间内容 |
WriteProcessMemory | NtWriteVirtualMemory | 向目标进程地址空间写数据 |
VirtualAllocEx | NtAllocateVirtualMemory | 在目标进程中申请虚拟内存 |
VirtualProtectEx | NtProtectVirtualMemory | 修改目标进程内存页保护属性 |
CreateRemoteThread | NtCreateThreadEx | 在目标进程中创建线程 |
QueueUserAPC | NtQueueApcThread | 向目标线程排队 APC |
CreateFileMapping | NtCreateSection | 创建节区对象 |
MapViewOfFile / 跨进程节区映射 | NtMapViewOfSection | 将节区映射到进程地址空间 |
DuplicateHandle | NtDuplicateObject | 复制已有句柄 |
SetThreadContext | NtSetContextThread | 修改目标线程上下文 |
行为链
从攻击链角度看,这些 API 的价值通常不在于孤立存在,而在于组成一条完整的行为链。
例如:
NtOpenProcess
↓
NtAllocateVirtualMemory / NtMapViewOfSection
↓
NtWriteVirtualMemory
↓
NtProtectVirtualMemory
↓
NtCreateThreadEx / NtQueueApcThread / NtSetContextThread
从防守角度,这条链比单独出现一个 NtWriteVirtualMemory 更有检测意义。
设计原理
Win32 与 Native API
Windows 之所以有 Win32 API 和 Native API 这样的分层,不是为了“方便恶意软件调用”,而是需要区分:
- 面向普通应用的兼容层。
- 面向对象管理、内存管理等底层服务边界。
Win32 API 更像微软给大多数应用开发者准备的稳定接口层,通常具有以下特点:
- 语义更友好。
- 文档更完整。
- 错误处理和参数风格更贴近应用开发习惯。
- 长期兼容性承诺更强。
Native API 则更贴近内核服务边界:
- 更接近 Windows 内核对象模型。
- 参数和返回值更偏系统内部风格,例如
NTSTATUS。 - 能更直接映射到对象管理、内存管理、线程管理和 I/O 管理能力。
对象、句柄与权限
Windows 并不是按“这个 API 名字危险不危险”来做安全判断,而是更多围绕对象和句柄模型控制能力。
进程、线程、节区、令牌、文件
都是内核对象
用户态程序要操作这些对象,通常需要先拿到:
句柄(Handle)
句柄本质上像一张由内核发放的操作凭证。真正决定能否写入其他进程内存的核心,不是调用 WriteProcessMemory 还是 NtWriteVirtualMemory,而是:
- 是否成功打开目标进程对象。
- 获得的句柄是否包含足够访问掩码。
- 令牌、完整性级别、特权和目标对象的安全描述符是否允许操作。
所以:
API 只是入口形式
句柄与访问控制才是权限判断的核心
系统调用边界
即便用户态程序调用的是 Nt*,也不是在用户态“替代了内核”。请求仍要跨越系统调用边界进入内核态,再由内核执行对象访问检查、地址校验、内存管理或线程管理逻辑。
Native API 的价值更多在于:
- 更接近系统调用边界。
- 更忠实地反映底层服务能力。
- 研究人员和攻击者可能用它绕过一部分高层封装与兼容层。
但这不意味着安全检查消失。
Nt 与 Zw
用户态讨论时,经常会看到 NtOpenProcess、ZwOpenProcess 这样的成对名称。学习安全时最重要的结论是:
- 用户态通常主要接触
ntdll.dll导出的Nt*接口。 Nt/Zw的真正区别,主要体现在内核态调用时对调用者模式和参数探测语义的处理。- 分析用户态恶意代码或 EDR 遥测时,通常不必过多纠结二者的命名差异。
更值得关注的是:
它最终操作什么对象
需要什么句柄权限
是否构成完整的高风险行为链
攻防视角
攻击者为什么关注
这些 API 更贴近底层能力,容易直接对应到以下动作:
- 跨进程打开句柄。
- 读写目标进程内存。
- 修改页面权限。
- 映射节区。
- 创建远程线程。
- 修改线程上下文。
这不意味着 API 本身恶意,但它们确实是实现跨进程操作时经常出现的能力边界。
防守者为什么关注
从防守者角度,Native API 的价值在于:
- 比纯高层 Win32 API 更接近真实系统服务行为。
- 很多恶意样本会直接使用
Nt*,而不是只停留在Kernel32/KernelBase层。 - 若只监控高层 API,可能遗漏一部分更贴近底层的实现路径。
因此终端安全产品通常同时关注:
Win32 层调用
+
ntdll!Nt* 层调用
+
内核对象访问与系统遥测
EDR 检测
检测对象
如果只问“EDR 要不要检测 NtWriteVirtualMemory”,答案当然是要。
但可靠的检测对象通常不是某一次孤立调用,而是下面这条完整序列:
获取跨进程能力
↓
修改内存
↓
建立执行
例如,以下组合通常比单点 API 更有价值:
- 先申请目标进程高权限句柄,随后立即发生跨进程写内存。
- 先写内存,随后内存页从
RW变为RX或RWX。 - 跨进程写入后,紧接着出现远程线程、APC、线程上下文修改或异常模块加载。
- 目标进程是敏感进程、常见宿主,或不应被普通业务进程操作的对象。
用户态观测
许多产品会观测,甚至 Hook 下列更常见的高层入口:
OpenProcessReadProcessMemoryWriteProcessMemoryVirtualAllocExVirtualProtectExCreateRemoteThreadQueueUserAPCSetThreadContext
优点是语义直观、参数更贴近开发者习惯,也能覆盖很多普通样本和红队工具的调用路径。
局限也很明显:样本若直接改走 ntdll!Nt*,只 Hook 这一层可能不够;用户态 Hook 本身也可能被绕过、卸钩或篡改。
ntdll!Nt* 层
用户态进一步下沉时,常见关注点包括:
ntdll!NtOpenProcessntdll!NtReadVirtualMemoryntdll!NtWriteVirtualMemoryntdll!NtAllocateVirtualMemoryntdll!NtProtectVirtualMemoryntdll!NtCreateThreadExntdll!NtQueueApcThreadntdll!NtMapViewOfSectionntdll!NtDuplicateObjectntdll!NtSetContextThread
这一层更接近系统调用边界,因此覆盖面通常比只看 Kernel32 / KernelBase 更好。
但 ntdll Hook 仍不是最终可信源:它适合做高语义遥测和行为拼接,不应作为唯一防线。
内核侧观测
从稳健、长期兼容且受支持的 EDR 实现角度,应重点依赖内核侧受支持能力,而不是 SSDT Hook 或内核 inline hook。常见重点包括:
ObRegisterCallbacks。- 进程、线程、映像加载通知回调。
- ETW 遥测。
- 文件、注册表、网络和内存行为的关联分析。
其中一类关键能力是:
对象回调观察谁在申请高风险句柄
对于进程 / 线程对象,应重点关注:
PROCESS_VM_READPROCESS_VM_WRITEPROCESS_VM_OPERATIONPROCESS_CREATE_THREADPROCESS_DUP_HANDLETHREAD_SET_CONTEXTTHREAD_SUSPEND_RESUME
这些信息通常比某个 API 名字更稳定,因为它们直接反映:
该进程是否真的获得了跨进程操作能力
为什么不推荐 SSDT Hook
学习 Native API 时,很容易想到:
既然 Nt* 最后进入系统调用
那 EDR 是否直接 Hook SSDT 最好?
从现代 Windows 的产品化实现角度,这通常不是推荐方向,原因包括:
- 兼容性与稳定性风险高。
- 容易与 PatchGuard、内核完整性保护机制冲突。
- 维护成本高,版本差异大。
- 对商业 EDR 来说,不是稳妥的长期工程方案。
更现实的答案是:
用受支持的对象回调、内核通知、ETW 和多源行为关联
去观察同一条攻击链
关联判断
成熟的检测逻辑不是“某个 Nt* 一出现就判恶意”,而是看上下文。高价值判断点包括:
- 调用方是否应具备调试器、辅助功能、性能分析或安全产品属性。
- 目标是否属于浏览器、
explorer.exe、lsass.exe、办公软件、脚本宿主或其他高价值宿主。 - 短时间内是否连续出现“开句柄 → 写内存 → 改权限 → 建执行”。
- 线程起始地址是否位于私有内存,而不是受信任模块映像。
- 是否出现私有可执行内存、异常映像映射、线程上下文突变或 APC 执行。
- 父子进程关系、签名、命令行、落地路径和会话信息是否异常。
可以将成熟产品的逻辑概括为:
多点观测
↓
把句柄、内存、线程、模块和进程画像串起来
↓
再做风险判断
误报边界
合法使用场景
这些能力并不天然等于恶意。以下合法软件都可能使用类似能力:
- 调试器。
- IDE。
- 性能分析器。
- 自动化与辅助工具。
- 安全产品本身。
- 浏览器、多进程沙箱和部分反作弊组件。
因此如果只看到:
NtWriteVirtualMemory 出现了
误报通常会非常高。
判断原则
合理的方式始终是把行为放回完整语境:
谁在调用
对谁调用
前面拿到了什么权限
后面又建立了什么执行
小结
NtWriteVirtualMemory这类 Windows Native API,本质上是比 Win32 更贴近系统调用和内核对象模型的一层接口;它们不是“绕过安全模型的神秘后门”,但因为更靠近真实跨进程能力边界,所以在恶意代码分析和 EDR 检测里都非常关键。