跳到主要内容

Windows Native API(NT API)

在 Windows 安全语境里,NtWriteVirtualMemoryNtOpenProcessNtCreateThreadEx 这类接口常被称为“未公开内核 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作用
OpenProcessNtOpenProcess打开目标进程对象并申请句柄
ReadProcessMemoryNtReadVirtualMemory读取目标进程地址空间内容
WriteProcessMemoryNtWriteVirtualMemory向目标进程地址空间写数据
VirtualAllocExNtAllocateVirtualMemory在目标进程中申请虚拟内存
VirtualProtectExNtProtectVirtualMemory修改目标进程内存页保护属性
CreateRemoteThreadNtCreateThreadEx在目标进程中创建线程
QueueUserAPCNtQueueApcThread向目标线程排队 APC
CreateFileMappingNtCreateSection创建节区对象
MapViewOfFile / 跨进程节区映射NtMapViewOfSection将节区映射到进程地址空间
DuplicateHandleNtDuplicateObject复制已有句柄
SetThreadContextNtSetContextThread修改目标线程上下文

行为链

从攻击链角度看,这些 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

用户态讨论时,经常会看到 NtOpenProcessZwOpenProcess 这样的成对名称。学习安全时最重要的结论是:

  • 用户态通常主要接触 ntdll.dll 导出的 Nt* 接口。
  • Nt / Zw 的真正区别,主要体现在内核态调用时对调用者模式和参数探测语义的处理。
  • 分析用户态恶意代码或 EDR 遥测时,通常不必过多纠结二者的命名差异。

更值得关注的是:

它最终操作什么对象
需要什么句柄权限
是否构成完整的高风险行为链

攻防视角

攻击者为什么关注

这些 API 更贴近底层能力,容易直接对应到以下动作:

  • 跨进程打开句柄。
  • 读写目标进程内存。
  • 修改页面权限。
  • 映射节区。
  • 创建远程线程。
  • 修改线程上下文。

这不意味着 API 本身恶意,但它们确实是实现跨进程操作时经常出现的能力边界。

防守者为什么关注

从防守者角度,Native API 的价值在于:

  • 比纯高层 Win32 API 更接近真实系统服务行为。
  • 很多恶意样本会直接使用 Nt*,而不是只停留在 Kernel32 / KernelBase 层。
  • 若只监控高层 API,可能遗漏一部分更贴近底层的实现路径。

因此终端安全产品通常同时关注:

Win32 层调用
+
ntdll!Nt* 层调用
+
内核对象访问与系统遥测

EDR 检测

检测对象

如果只问“EDR 要不要检测 NtWriteVirtualMemory”,答案当然是要。

但可靠的检测对象通常不是某一次孤立调用,而是下面这条完整序列:

获取跨进程能力

修改内存

建立执行

例如,以下组合通常比单点 API 更有价值:

  • 先申请目标进程高权限句柄,随后立即发生跨进程写内存。
  • 先写内存,随后内存页从 RW 变为 RXRWX
  • 跨进程写入后,紧接着出现远程线程、APC、线程上下文修改或异常模块加载。
  • 目标进程是敏感进程、常见宿主,或不应被普通业务进程操作的对象。

用户态观测

许多产品会观测,甚至 Hook 下列更常见的高层入口:

  • OpenProcess
  • ReadProcessMemory
  • WriteProcessMemory
  • VirtualAllocEx
  • VirtualProtectEx
  • CreateRemoteThread
  • QueueUserAPC
  • SetThreadContext

优点是语义直观、参数更贴近开发者习惯,也能覆盖很多普通样本和红队工具的调用路径。

局限也很明显:样本若直接改走 ntdll!Nt*,只 Hook 这一层可能不够;用户态 Hook 本身也可能被绕过、卸钩或篡改。

ntdll!Nt* 层

用户态进一步下沉时,常见关注点包括:

  • ntdll!NtOpenProcess
  • ntdll!NtReadVirtualMemory
  • ntdll!NtWriteVirtualMemory
  • ntdll!NtAllocateVirtualMemory
  • ntdll!NtProtectVirtualMemory
  • ntdll!NtCreateThreadEx
  • ntdll!NtQueueApcThread
  • ntdll!NtMapViewOfSection
  • ntdll!NtDuplicateObject
  • ntdll!NtSetContextThread

这一层更接近系统调用边界,因此覆盖面通常比只看 Kernel32 / KernelBase 更好。

ntdll Hook 仍不是最终可信源:它适合做高语义遥测和行为拼接,不应作为唯一防线。

内核侧观测

从稳健、长期兼容且受支持的 EDR 实现角度,应重点依赖内核侧受支持能力,而不是 SSDT Hook 或内核 inline hook。常见重点包括:

  • ObRegisterCallbacks
  • 进程、线程、映像加载通知回调。
  • ETW 遥测。
  • 文件、注册表、网络和内存行为的关联分析。

其中一类关键能力是:

对象回调观察谁在申请高风险句柄

对于进程 / 线程对象,应重点关注:

  • PROCESS_VM_READ
  • PROCESS_VM_WRITE
  • PROCESS_VM_OPERATION
  • PROCESS_CREATE_THREAD
  • PROCESS_DUP_HANDLE
  • THREAD_SET_CONTEXT
  • THREAD_SUSPEND_RESUME

这些信息通常比某个 API 名字更稳定,因为它们直接反映:

该进程是否真的获得了跨进程操作能力

为什么不推荐 SSDT Hook

学习 Native API 时,很容易想到:

既然 Nt* 最后进入系统调用
那 EDR 是否直接 Hook SSDT 最好?

从现代 Windows 的产品化实现角度,这通常不是推荐方向,原因包括:

  • 兼容性与稳定性风险高。
  • 容易与 PatchGuard、内核完整性保护机制冲突。
  • 维护成本高,版本差异大。
  • 对商业 EDR 来说,不是稳妥的长期工程方案。

更现实的答案是:

用受支持的对象回调、内核通知、ETW 和多源行为关联
去观察同一条攻击链

关联判断

成熟的检测逻辑不是“某个 Nt* 一出现就判恶意”,而是看上下文。高价值判断点包括:

  • 调用方是否应具备调试器、辅助功能、性能分析或安全产品属性。
  • 目标是否属于浏览器、explorer.exelsass.exe、办公软件、脚本宿主或其他高价值宿主。
  • 短时间内是否连续出现“开句柄 → 写内存 → 改权限 → 建执行”。
  • 线程起始地址是否位于私有内存,而不是受信任模块映像。
  • 是否出现私有可执行内存、异常映像映射、线程上下文突变或 APC 执行。
  • 父子进程关系、签名、命令行、落地路径和会话信息是否异常。

可以将成熟产品的逻辑概括为:

多点观测

把句柄、内存、线程、模块和进程画像串起来

再做风险判断

误报边界

合法使用场景

这些能力并不天然等于恶意。以下合法软件都可能使用类似能力:

  • 调试器。
  • IDE。
  • 性能分析器。
  • 自动化与辅助工具。
  • 安全产品本身。
  • 浏览器、多进程沙箱和部分反作弊组件。

因此如果只看到:

NtWriteVirtualMemory 出现了

误报通常会非常高。

判断原则

合理的方式始终是把行为放回完整语境:

谁在调用
对谁调用
前面拿到了什么权限
后面又建立了什么执行

小结

NtWriteVirtualMemory 这类 Windows Native API,本质上是比 Win32 更贴近系统调用和内核对象模型的一层接口;它们不是“绕过安全模型的神秘后门”,但因为更靠近真实跨进程能力边界,所以在恶意代码分析和 EDR 检测里都非常关键。