馬克·魯西諾維奇 發表日期: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 模式下執行。
在原生應用程式中
原生應用程式的入口不是 Winmain 或 main,而是 NtProcessStartup。 此外,與 Win32 其他入口點不同,原生應用程式必須進入作為唯一參數的資料結構來定位命令列參數。
原生應用程式的大部分執行環境由 NTDLL.DLL 提供,NT 的原生 API 匯出函式庫。 原生應用程式必須自行建立堆積,並使用 RtlCreateHeap(一個 NTDLL 函式)來分配儲存空間。 記憶體是從堆積中分配 RtlAllocateHeap ,並以 RtlFreeHeap 釋放。 如果原生應用程式想要將某些東西印到螢幕上,必須使用 NtDisplayString 這個函式,該函式會輸出到初始化的藍屏(Blue Screen)。
原生應用程式不像 Win32 程式那樣直接從啟動功能回傳,因為沒有執行時程式碼可回傳。 相反地,他們必須透過呼叫 NtProcessTerminate 來終止自己。
NTDLL 執行時包含數百個函式,允許原生應用程式執行檔案 I/O、與裝置驅動程式互動,以及進行程序間通訊。 不幸的是,如我之前所說,絕大多數這些功能都沒有文件說明。