在原生應用程式內部

馬克·魯西諾維奇 發表日期:2006年11月1日

簡介

如果你對 NT 架構有些了解,應該知道 Win32 應用程式使用的 API 並不是「真正的」NT API。 NT 的作業系統,包括 POSIX、OS/2 和 Win32,透過自己的 API 與客戶端應用程式溝通,但與 NT 的溝通則是使用 NT 的「原生」API。 原生 API 大多未被文件化,Windows NT 裝置驅動套件中僅描述約 25 項函式。

然而,大多數人不知道的是,NT 上存在一些「原生」應用程式,並非任何作業系統的用戶端。 這些程式使用原生 NT API,無法使用像 Win32 這類作業系統的 API。 為什麼需要這種程式?」任何必須在 Win32 子系統啟動前(大約在登入視窗出現時)執行的程式,都必須是原生應用程式。 原生應用程式最明顯的例子是「autochk」程式,它會在初始化時顯示的藍色畫面期間執行 chkdsk(也就是那個會在螢幕上印出句點「.」的程式)。 當然,Win32 作業系統伺服器 CSRSS.EXE(Client-Server 執行時子系統)也必須是原生應用程式。

在本文中,我將描述原生應用程式是如何被建置以及它們如何運作的。

Autochk 是如何被處決的

Autochk 會運行在 NT 的開機和系統啟動驅動程式載入時間與開啟分頁之間。 在啟動序列的這個階段,Session Manager(smss.exe)正在建立 NT 的使用者模式環境,而且沒有其他程式在執行。 HKLM\System\CurrentControlSet\Control\Session Manager\BootExecute 這個MULTI_SZ值,包含 Session Manager 執行的程式名稱與參數,並指定 Autochk 位置。 如果你查看這個值,通常會看到以下內容,其中會將「*」作為引數傳遞給「Autochk」:

Autocheck Autochk *

Session Manager 會在 winnt<\system32 目錄中尋找>此值中列出的可執行檔。 當 Autochk 執行時,沒有任何檔案處於開啟狀態,因此 Autochk 可以以原始模式開啟任何磁碟區,包括開機磁碟機,並操控其磁碟上的資料結構。 這在未來的時刻是不可能的。

建置原生應用程式

Microsoft 沒有說明,但 NT DDK Build 工具知道如何製作原生應用程式(而且很可能用來編譯 Autochk)。 你在一個定義應用程式的 SOURCES 檔案中指定資訊,就像裝置驅動程式一樣。 不過,你不是直接告訴 Build 你想要驅動程式,而是在 SOURCES 檔案裡告訴它你想要一個原生應用程式,就像這樣:

TARGETTYPE=PROGRAM

Build 工具使用標準的 makefile \ddk\inc\makefile.def 來引導,該檔案在編譯原生應用程式時尋找名為 nt.lib 的執行時函式庫。 可惜的是,Microsoft 並沒有隨 DDK 附送這個檔案(它包含在 Server 2003 DDK 裡,但我猜如果你連結到那個版本,原生應用程式在 XP 或 Windows 2000 上可能無法執行)。 不過,你可以在 makefile.def 中加入一行,藉由指定 Visual C++ 的執行階段程式庫 msvcrt.lib,來覆寫對 nt.lib 的選用,以繞過這個問題。

如果你在 DDK 的「已檢查建置」環境下執行 Build ,它會在 %BASEDIR%\lib%CPU%\Checked(例如 c:\ddk\lib\i386\checked\native.exe)下產生一個包含完整除錯資訊的原生應用程式;如果你在「自由建置」環境中呼叫,該程式的版本會落在 %BASEDIR%\lib%CPU%\Free。 這些都是 Build 放置裝置驅動程式映像的位置。

原生應用程式有「.exe」檔名,但你不能像 Win32 .exe那樣執行。 如果你嘗試,你會收到訊息:

該應用程式無法在 Windows NT 模式下執行。

在原生應用程式中

原生應用程式的入口不是 Winmainmain,而是 NtProcessStartup。 此外,與 Win32 其他入口點不同,原生應用程式必須進入作為唯一參數的資料結構來定位命令列參數。

原生應用程式的大部分執行環境由 NTDLL.DLL 提供,NT 的原生 API 匯出函式庫。 原生應用程式必須自行建立堆積,並使用 RtlCreateHeap(一個 NTDLL 函式)來分配儲存空間。 記憶體是從堆積中分配 RtlAllocateHeap ,並以 RtlFreeHeap 釋放。 如果原生應用程式想要將某些東西印到螢幕上,必須使用 NtDisplayString 這個函式,該函式會輸出到初始化的藍屏(Blue Screen)。

原生應用程式不像 Win32 程式那樣直接從啟動功能回傳,因為沒有執行時程式碼可回傳。 相反地,他們必須透過呼叫 NtProcessTerminate 來終止自己。

NTDLL 執行時包含數百個函式,允許原生應用程式執行檔案 I/O、與裝置驅動程式互動,以及進行程序間通訊。 不幸的是,如我之前所說,絕大多數這些功能都沒有文件說明。