内部本机应用程序

Mark Russinovich 发布时间:2006 年 11 月 1 日

简介

如果你对 NT 的体系结构有一些熟悉,你可能知道 Win32 应用程序使用的 API 不是“真正的”NT API。 NT 的操作环境(包括 POSIX、OS/2 和 Win32)通过自己的 API 与其客户端应用程序通信,但使用 NT“本机”API 与 NT 通信。 本机 API 大多是没有记录的,Windows NT 设备驱动程序工具包中只介绍了其 250 个函数中的约 25 个。

但大多数人不知道的是,NT 上的“本机”应用程序不是任何操作环境的客户端。 这些程序使用本机 NT API,不能使用 Win32 等操作环境 API。 为什么需要此类程序“在启动 Win32 子系统之前必须运行的任何程序(登录框出现时))必须是本机应用程序。 本机应用程序最明显的示例是在初始化蓝屏期间运行 chkdsk 的“autochk”程序(它是在屏幕上打印“.”的程序)。 当然,Win32 操作环境服务器 CSRSS.EXE(客户端-服务器运行时子系统)也必须是本机应用程序。

在本文中,我将介绍本机应用程序的生成方式及其工作原理。

Autochk 如何得到执行

Autochk 在加载 NT 的启动和系统启动驱动程序之间以及打开分页时运行。 此时,启动序列会话管理器 (smss.exe) 正在将 NT 的用户模式环境关闭,并且没有其他程序处于活动状态。 HKLM\System\CurrentControlSet\Control\Session Manager\BootExecute 值(MULTI_SZ)包含由会话管理器执行的程序的名称和参数,是指定 Autochk 的位置。 如果查看此值,通常会发现以下内容,其中“Autochk”作为参数传递“*”:

Autocheck Autochk *

会话管理器 在 <winnt>\system32 目录中查找此值中列出的可执行文件。 Autochk 运行时,不会打开任何文件,因此 Autochk 可以在原始模式下打开任何卷(包括启动驱动器),并操作其磁盘上的数据结构。 这在以后的任何时间点都不可能实现。

生成本机应用程序

Microsoft 不会记录它,但 NT DDK 生成实用工具知道如何使本机应用程序(且可能将其用于编译 Autochk)。 可以在定义应用程序的 SOURCES 文件中指定信息,这与对设备驱动程序执行的操作相同。 但是,并非是向 Build 指示你需要一个驱动程序,而是告诉它你需要一个位于 SOURCES 文件中的本机应用程序,如下所示:

TARGETTYPE=PROGRAM

生成 实用工具使用标准生成文件来指导 \ddk\inc\makefile.def,它在编译本机应用程序时查找名为 nt.lib 的运行时库。 遗憾的是,Microsoft 不会将此文件与 DDK 一起交付(它包含在 Server 2003 DDK 中,但我怀疑,如果你链接到该版本,本机应用程序将无法在 XP 或 Windows 2000 上运行)。 但是,可以通过在 makefile.def 中包含一行来解决此问题,该行通过指定 Visual C++ 的运行时库 msvcrt.lib 替代 nt.lib 的选择

如果在 DDK 的“已检查生成”环境中运行 生成 ,它将在 %BASEDIR%\lib%CPU%\Checked(例如c:\ddk\lib\i386\checked\native.exe)下生成具有完整调试信息的本机应用程序,如果在“免费生成”环境中调用它,则程序的发布版本最终将位于 %BASEDIR%\lib%CPU%\Free 中。 这些位置与设备驱动程序映像通过生成放置的位置相同。

本机应用程序具有“.exe”文件扩展名,但无法像运行 Win32 .exe 一样运行它们。 如果尝试,你将收到以下消息:

应用程序不能在 Windows NT 模式下运行。

在本机应用程序内

本机应用程序的入口点不是 winmainmain,而是 NtProcessStartup。 与其他 Win32 入口点不同,本机应用程序必须访问作为唯一参数传递的数据结构,才能找到命令行参数。

本机应用程序的大多数运行时环境都由 NT 的本机 API 导出库 NTDLL.DLL 提供。 本机应用程序必须使用 NTDLL 函数 RtlCreateHeap 创建自己的堆,以便从中分配存储。 内存通过 RtlAllocateHeap 从堆分配,并使用 RtlFreeHeap 释放。 如果本机应用程序希望在屏幕上打印某些内容,则必须使用函数 NtDisplayString,该函数将输出到初始化蓝屏。

本机应用程序不会像 Win32 程序一样简单地从其启动函数返回,因为没有要返回到的运行时代码。 相反,它们必须通过调用 NtProcessTerminate 来终止自身。

NTDLL 运行时由数百个函数组成,这些函数允许本机应用程序执行文件 I/O、与设备驱动程序交互以及执行进程间通信。 不幸的是,正如我前面所指出的,这些函数中的绝大多数都是没有记录的。