内部本机应用程序
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 模式下运行。
在本机应用程序内
本机应用程序的入口点不是 winmain 或 main,而是 NtProcessStartup。 与其他 Win32 入口点不同,本机应用程序必须访问作为唯一参数传递的数据结构,才能找到命令行参数。
本机应用程序的大多数运行时环境都由 NT 的本机 API 导出库 NTDLL.DLL 提供。 本机应用程序必须使用 NTDLL 函数 RtlCreateHeap 创建自己的堆,以便从中分配存储。 内存通过 RtlAllocateHeap 从堆分配,并使用 RtlFreeHeap 释放。 如果本机应用程序希望在屏幕上打印某些内容,则必须使用函数 NtDisplayString,该函数将输出到初始化蓝屏。
本机应用程序不会像 Win32 程序一样简单地从其启动函数返回,因为没有要返回到的运行时代码。 相反,它们必须通过调用 NtProcessTerminate 来终止自身。
NTDLL 运行时由数百个函数组成,这些函数允许本机应用程序执行文件 I/O、与设备驱动程序交互以及执行进程间通信。 不幸的是,正如我前面所指出的,这些函数中的绝大多数都是没有记录的。