Native Application 開發詳解

文章目錄:

                 

1. 引子:

2. Native Application Demo 展示:

3. Native Application 簡介:

4. Native Application 有何妙用:

5. MJ0011 關於 Native Application 的文章整理:

6. 互聯網上其他關於 Native Application 的文章整理:

7. 小結:

                

1. 引子:

                         

其實在好久以前就看了 MJ0011 翻譯的那個《Native 應用程序詳細》系列的文章,

(PS: MJ0011 爲 360 的首席技術執行官,技術是沒的說,不過貌似有點狂妄之說 ~ )

而且看完後對這一系列文章也很感興趣的,所以又去 Google 上找了幾個小資料學習了一下,

而這篇文章呢,則是將我前陣子的所謂的學習給總結出來也順道給大夥分享一下。

雖然這裏是說的總結 Native Application,但最早出現 Native Application 應該是 06 年的事了,

(當然,Native Application 這個技術是一直存在的,只是在 06 年後有了下面這篇文章後就稍微火了點)

其是 Sysinternals 上的一篇由 Mark Russinovich 發表的文章《Inside Native Applications》

文章地址如下:http://technet.microsoft.com/en-us/sysinternals/bb897447

而我卻在 2011 年才得以來總結這個技術,所以我確是屬於研究這些東西的落後者啊 ~

下面我給出一副截圖來看一下 Windows 操作系統下的程序的類型:

image

            

在 Windows 最初設計的時候考慮到了兼容各種系統的應用程序,所以有了環境子系統之說,

其中一開始的時候考慮到了子系統,POSIX 子系統和 OS/2 子系統,

但是隨着歷史的發展,現在也就剩下個 Windows 子系統了,

我們日常使用的 Windows 操作系統的上層其實也就是指的這個 Windows 子系統了,

至於這裏爲何要扯到 Windows 子系統的話,就看下文介紹了。

很多朋友都知道有 Windows 應用程序和 Windows 內核驅動程序之說,

卻很少有知道在 Windows 中還有 Native Application 一說了,

但是這類程序確實是存在的 ~只不過這類程序應用比較窄,也沒有被很好的推廣開來,

當然還有一點就是 Microsoft 自然不希望你隨隨便便的使用 Native API。

           

              

2. Native Application Demo 展示:

              

首先你需要將下載(博文的最後面附上 Demo 的下載地址)的 EXE 文件拷貝一份到 system32 目錄下,

(博文的 Demo 只是拿了網上的代碼然後自行使用 DDK 編譯了而已,Demo 並非筆者原創)

然後再在註冊表以下路徑中修改 BootExecute,

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager

在其中添加 NativeApp_01 Hello World ! 這個字符串,

image

                    

image

          

重啓電腦,然後就可以看到下面的效果圖了(僅在 XP SP3 上進行了測試)

2011-09-18_111934

              

                   

3. Native Application 簡介:

              

何爲 Native API ?

Native API 就是你 system32 目錄下的那個 ntdll.dll 中所公開的 API(大部分爲 Undocument)~

如果讀者看過我前面的《進程隱藏與進程保護(SSDT Hook 實現)》系列文章的話,

相信肯定會知道在 Windows 中 kernel32.dll 中的 API 的調用都會經過轉換,

也就是跳轉到 ntdll.dll 中,並且在 ntdll.dll 中也有與之相對於的 API 調用,

(比如 Kernel32.dll 中的 CreateProcess 在 ntdll.dll 中有 NtCreateProcess 與之對應)

那麼什麼稱之爲 Native 應用程序呢 ?

下面給出一副截圖:

image

            

從上面的截圖可以看出,在一開始的 Windows NT 內核中是支持三個環境子系統的,

即 POSIX,WINDOWS,OS/2,這些子系統屬於同一層,它們共用了 Windows NT 所提供的 API,

即每一個子系統中的 API 的調用都會轉換到下一層的相同調用上,

在 Windows 環境子系統(有 Windows,Posix,OS/2)中的程序,

都會調用其相對於的子系統下的 API,比如 Windows 子系統中的程序有可能會調用 Win32 API CreateProcess,

而 Posix 子系統中的程序也有可能會調用 Posix API CreateProcess(當然有可能在 POSIX 下創建進程不是這個名稱),

但是終歸來說,這兩個 CreateProcess 的調用都會轉換到 Ntdll.dll 中的 NtCreateProcess 中,

也就是上面的三個子系統最後的調用都會迴歸到 ntdll.dll 上,

而我們的 Native Application 則是繞過 Windows 子系統,

直接自己調用 Native API,比如創建進程的話,我就不再通過子系統中的神馬 CreateProcess 來完成了,

而是直接在程序中調用 ntdll.dll 中的 Native API NtCreateProcess 來完成,

而這類程序即稱之爲 Native Application !

           

Native Application 的運行環境:

上面也說了,Native Application 是隻能夠訪問 ntdll.dll 中的內容的,

而如果是在子系統下運行一個程序的話,必然會加載其他的 DLL,

比如在 Windows 子系統下一個 kernel32.dll 是必不可少的吧,

如果 Native Application 能夠運行在 Windows 子系統下的話,必然也會加載到 Kernel32.dll,

這樣不就和上面相違背了嘛 ~

總之:Native Application 是不能夠運行在任何子系統下的 !

比如在 Windows 子系統下運行 Native Application 會彈出如下錯誤對話框:

image

          

Native Application 的啓動時機:

對於 Windows 操作系統的引導過程,這裏需要帶一筆的,Windows 操作系統啓動時,

當 Windows 內核的引導完成以後,就會啓動會話管理器 smss.exe 進程了,

smss.exe 進程雖然是一個用戶模式的進程,但是這個進程相對於其他用戶模式進程是具有一定特殊性的,

首先 smss.exe 進程是直接建立在 Windows NT 內核上的,其不依賴於任何一個環境子系統,

至於不依賴於任何一個環境子系統這一說的話,還是可以很好的解釋的,

因爲當環境子系統進程(Windows 子系統進程爲 csrss.exe)就是由 smss.exe 進程啓動的,

然後 smss.exe 是 Windows 操作系統啓動的第一個用戶態進程,

而 Native Application 也屬於用戶態程序,自然 Native Application 的啓動是在 smss.exe 之後,

而後前面也說過,Native Application 運行時,子系統進程還尚未啓動,

所以 Native Application 的啓動則是在 csrss.exe 之前的,

而話又說回來,csrss.exe 就是由會話管理器(smss.exe)啓動的,

所以 Native Application 的啓動時機也就只有一種可能了,

即 smss.exe 先啓動 Native Application,然後 Native Application 開始執行,

等到 Native Application 都給執行完了後 smss.exe 再啓動 csrss.exe 進程。

(事實上,Win32 應用程序環境子系統 csrss.exe 本質上也是一個 Native Application ~)

下面給出一副截圖以說明 SMSS.EXE 的在啓動過程中所完成的工作:

運行啓動時執行的程序即是執行 Native Application

image

          

           

4. Native Application 有何妙用:

              

前面也提到過,Native Application 的應用範圍比較窄,這主要受以下幾點約束:

首先,Native Application 是直接調用 Native API 來完成任務的,

而在 Windows 中,Native API 絕大部分都是 Undocument 的,這樣開發起來自然難度會大很多了。

然後,由於 Native Application 是調用的 Undocument API,

說不準那一天 Microsoft 就在下一代 Windows 中修改了這個 API,這樣的話,你程序的可移植性也就完全沒了。

最後的話,Native Application 的執行環境並非是在 Windows 子系統中,

事實上,當 Native Application 開始執行的時候,Windows 子系統進程(csrss.exe)進程都還沒有啓動的 ~

所以 Native Application 的執行是受限制的,其不能夠執行子系統中的任何東西,

說白一點的話就是 Native Application 只能夠調用 ntdll.dll 中的 API,

其他 DLL 中的 API 一律不得調用,這樣也就註定 Native Application 的應用範圍不會很廣泛了。

          

應用範圍比較窄並不等於說沒有應用價值,

也還是有蠻多的軟件,包括一些商業軟件也都是用了 Native Application,

下面來看一些 Native Application 的應用(我也只是從網上道聽途說,不過有些也確實是自己也用過的):

最典型的應用自然是屬於 Windows 操作系統自帶的磁盤自檢程序了;

然後比較典型的商業應用是瑞星的開機殺毒,即實現讓病毒在你還沒有完全開機之前死掉;

然後就是可以通過 Native Application 來實現接管 Windows 的開機啓動界面和密碼輸入界;

還可以通過 Native Application 來實現開機前的磁盤修復(Windows 自帶了這款工具也是這樣實現的);

      

           

5. MJ0011 關於 Native Application 的文章整理:

          

這一小節裏面主要是整理一下關於 MJ0011 的 Native Application 的博文,

也就是說我這裏純粹是將他的東西給貼出來瞧瞧,關於 MJ0011 博文的原地址爲:

《NATIVE應用程序詳細之一》:

http://hi.baidu.com/mj0011/blog/item/7ee496d67a4d4d2f07088bc7.html

《NATIVE應用程序詳細之2 NATIVE應用程序的優勢和劣勢》

http://hi.baidu.com/mj0011/blog/item/f8108f2f5890fb381e30896d.html

《native應用程序詳細之三 構建native應用程序》:

http://hi.baidu.com/mj0011/blog/item/725b4882042b03a20df4d269.html

《深入Native應用程序》(該文翻譯自《Inside Native Application》):

http://hi.baidu.com/mj0011/blog/item/85c0b50f80b1baedab6457de.html

《native app GUI界面的實現》:

http://hi.baidu.com/mj0011/blog/item/6e5a22fa214c8116a9d3115b.html

                   

Native 應用程序簡介:

NT 系統被設計成爲支持子系統(封裝),它可以執行在不同平臺上的代碼。包括但不限於:POSIX、OS/2和Win32,

爲了管理這些子系統,NT內核輸出了大量名爲 Native API的API函數,子系統服務將這些函數包裝爲他們自己的函數。

例如:CreateFile和fopen都被映射到NtCreateFile.那麼子系統管理程序運行在哪個子系統中?

爲了避免先有雞還是先有蛋的問題,NT系統同樣支持原生的Native應用程序。

這些Native應用程序是獨立於子系統的。現在,所有的子系統應用程序都需要註冊它們自己的子系統服務,

顯然,Kernel32是一個Win32應用程序,csrss註冊WIN32子系統,然後通知子系統管理器SMSS,

因此,一個Native應用程序是無法調用其中數千種API的,

同時它也無法使用一些基本的DLL中的函數例如kernel32、user32、gdi32等,

其它任何調用了這三者的DLL中的函數的DLL也無法被使用。

事實上,加載者在其入口點沒有被加載前,並不允許加載決大多數的Win32 DLL。

因此,native應用程序被限制只許使用一個DLL:ntdll.dll,這個DLL供應所有Native和運行函數。

但是想想,既然所有Win32函數最後都是去調用Ntdll.dll中的函數來實現的(除了GUI部分),

這些函數就已經足夠了,不是嗎?

           

Native應用程序優勢:

(一) 內存使用和大小:因爲Native應用程序並不需要加載90多個DLL到內存中去,因此其使用的內存是很小的。

(二) 速度:Native API比Win32對應的函數要更快(通常會快很多),

儘管實際上很多時間都被消耗在Win32 API的封裝上,這些包括修改、兼容性選項,和其他的代碼部分,

這些都會在執行真正的Native調用前執行。如果你知道如何去做而且希望它運行得更快,Native是一個很好的方法。

因爲不需要加載那90多個DLL,kernel32不需要到csrss和smss做lpc註冊,因此啓動也是非常快速和直接的。

(三) 熟悉程度和特性:Native應用程序並沒有古怪的入口點或者奇怪的Hack,就象讀命令行一樣簡單使用,

而事實上將在後面介紹,Native應用程序和Console程序一樣啓動與_main函數,

通過一組簡單的char*[]隊列來接受命令行參數和環境變量,就象一個典型的Win32 Console程序一樣,

一些特性例如緩衝區溢出保護(/GS)、Safe SEH(/SAFESEH)、hotpathing(/HOTPATCH),

以及其他的特性都被支持。

(四) 豐富:Native API非常豐富,它們提供的特性和功能性要遠遠超越Win32函數所能達到的程度。

這並不是說Win32無法做到Native API那樣的更困難和更復雜的工作,而是Win32太過簡單而已,

比如Win32函數中,無法遠程注入一個Section到一個進程中,因爲MapViewOfFileEx不提供進程句柄接口,

而Native API則可以實現這個功能。

(五) 安全:但是Native應用程序有這樣一個特點:人們對於Native API不是十分熟悉,

他們需要花費更多的時間才能理解你的代碼,而更重要的是,無法使用一個用戶模式的debugger來調試Native應用程序,

只有SoftIce和使用內核模式遠程連接的WinDbg纔可以對其進行調試,這足以讓那些廢物腳本小子去死了,

再說一遍,這並不意味着Native 應用程序是“不可調試的,安全的”,只是它更模糊更難被破解。

(六) 同內核模式的連接:因爲Native應用程序只使用Native API,這些函數在內核模式仍然可用,

這樣,一個Native程序只需少量修改就可修改爲內核驅動,而Win32程序則需要幾乎重寫所有代碼。

(七) 運行在獨立於子系統的環境中:因爲Native應用程序並不依賴與任何子系統,

它運行於一個正常應用永遠無法再次得到的環境中,比如autochk.exe,它運行與任何子系統加載之前,

並負責顯示'press any key to scan your hard disk"信息,並掃描你的硬盤是否有錯誤。

在這個模式運行允許你顯示信息到啓動屏幕上,以及很多增強特性。

(八) 標準性:不象Win32函數有一個正常版本,一個"Ex"版本,以及經常有一個"擴展其他功能"的版本,

以及經常返回0,1,-1或一些隨機的數值來表示成功,並且要使用SetLastError來設置返回錯誤,

在Native應用程序中,這些垃圾都是不存在的,所有的Native API都有統一的標準,

所有都返回NTSTAUS(除非明確表示會返回一個特定的值),NTSTAUS是一個標準的錯誤碼定義,

而且使用它們時你也不需要考慮該死的Ex版本。

             

Native 應用程序劣勢:

(一) 和Win32開發的差異較大:如果你以前從來沒有進行過Native API或內核驅動的相關開發,

你可能需要學習所有的API相關知識,當然,他們的名字是相似的,但是他們的標誌經常是完全不同的,

而且他們的返回值,很可能使你感到迷惑。

(二) 缺少文檔:雖然所有的Rtl*函數都是有文檔的,數百個其他的Native API仍是無文檔的。

(三) 缺少商業價值:雖然Native程序如此美好,但是建議你不要在商業產品中使用 Native API 或着使用Native應用程序,

Native API是可能改變的,雖然它們通常沒有改變,但是他們很可能變得不再有用,不要拿你的客戶的錢冒險。

(四) 沒有GUI、及輸入/輸出接口:沒有"Native控制檯程序",你無法簡單地從用戶那裏接受到輸入或顯示些什麼到屏幕上,

因爲那些接口都無法再使用(控制檯API都是Win32 API)。

         

Native 應用程序構建:

你需要這兩者或兩者之一來創建你的 native 應用程序:

(1)Visual C++ 2005 Express(or higher)

(2)Windows Driver Kit

我們將從基礎開始,首先你需要創建你的應用程序的頭文件,precomp.h,ntddk.h

   1:  #include "ntddk.h"

Now that that's done, create your main initialization file,

which we will call init.c. In this file, add precomp.h like this:

   1:  #include "precomp.h"

And define your entrypoint:

   1:  NTSTATUS __cdeclmain(
   2:   
   3:  INT argc,
   4:   
   5:  PCHAR argv[],
   6:   
   7:  PCHAR envp[],
   8:   
   9:  ULONG DebugFlag OPTIONAL)
  10:   
  11:  {
  12:   
  13:  // Entry code here
  14:   
  15:  }

Hopefully you are familiar with this entrypoint, it's the typical one used by C programs,

except with an addon: the "DebugFlag". Right now, we don't need to care about this.

We'll keep this entry simple, and turn this into a "Hello World":

   1:  NTSTATUS __cdecl main(
   2:   
   3:  INT argc,
   4:   
   5:  PCHAR argv[],
   6:   
   7:  PCHAR envp[],
   8:   
   9:  ULONG DebugFlag OPTIONAL)
  10:   
  11:  {
  12:   
  13:  UNICODE_STRING HelloMsg = RTL_CONSTANT_STRING(L"Hello World!\n");
  14:   
  15:  //Say hello
  16:   
  17:  NtDisplayString(&HelloMsg);
  18:   
  19:  }
  20:   

Now, if you're wondering what NTSTATUS is,

what NtDisplayString is or what

RTL_CONSTANT_STRING and UNICODE_STRINGs are,

then you'll need to read all the DDK documentation you can swallow,

as well as Nebett's Undocumented Native API book.

Although it's outdated, the information about the APIs is still pretty valid.

I also plan to possibly give a fully-fledged lesson on this if lots of

people are interested.So now that we have our simple program, we need to build it.

I prefer using the WDK myself,

because it's much simpler and doesn't require changing 100 of MSVC's default settings.

Assuming you've properly installed the WDK and entered the Windows build environment for

your OS (from the Start Menu),

you'll need to create two files in the directory where init.c and precomp.h are:

sources and makefile.

Sources should look something like this:

--

   1:  TARGETNAME=native
   2:   
   3:  TARGETTYPE=PROGRAM
   4:   
   5:  UMTYPE=nt
   6:   
   7:  INCLUDES=\
   8:   
   9:  $(DDK_INC_PATH); \
  10:   
  11:  $(NDK_INC_PATH);
  12:   
  13:  SOURCES=init.c
  14:   
  15:  PRECOMPILED_INCLUDE=precomp.h
  16:   
--

that you'll have to set NDK_INC_PATH as an environment variable yourself,

to where the NDK is installed (DDK_INC_PATH is already setup by the WDK).

Finally, you'll need a makefile:

--

   1:  INCLUDE $(NTMAKEENV)\makefile.def

--

That's all you really need for our purposes.

So now you should have init.c, precomp.h, sources and makefile.

The only thing left is to write "build",

and the WDK should do the magic and create your first native application.

Unforunately, you can't really test it for now,

unless you do the following:Open registry editor and browse to

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager

Edit the "BootExecute" key and write "native" instead of what's currently in it,

then copy native.exe to your system32 directory and restart the computer.

You should see the message appear on the screen for a little while.

Make sure that you do NOT boot with /NOGUIBOOT, or else you will never see it.

      

//////////////////////////////////////////////////////////////////////////////  

//////////////////////////////////////////////////////////////////////////////  

/////////////////MJ0011 翻譯《Inside Native Application》//////////////////////

//////////////////////////////////////////////////////////////////////////////  

//////////////////////////////////////////////////////////////////////////////  

           

導言

如果你對Windows Nt結構有一定的瞭解,你可能會知道,Win32 應用程序所使用的API,

並非是"真正"的NT API,POSIX、OS/2和Win32這些Windows NT 操作系統環境,

使用他們自己的 API 同他們的客戶應用程序進行交流,但卻使用Windows NT的"Native" API同Windows NT交流,

這些Native API大都是未公開UnDocumented 的。

大約只有25個API(包含250種功能)在Windows NT設備驅動開發工具包(DDK)裏有所描述。

儘管絕大多數人都不知道,但"Native"應用程序的確存在與Windows NT上,他們在操作環境上沒有任何客戶程序,

這些程序交流着Native NT API並且不能使用任何操作環境API比如 Win32,爲什麼這樣一種程序是必須的呢?

因爲在Win32子系統啓動之前(大約在登陸對話框出現時)只可以運行Native應用程序,

最常見的Native應用程序的例子是"autochk"程序,他在初始化藍色登陸屏幕前運行chkdsk(程序在屏幕上打印一串".")。

當然,Win32應用程序環境服務程序:CSRSS.exe(客戶-服務運行時間子系統),也是一個Native應用程序。

在這篇文章裏,我將會講述如何構造一個Native應用程序以及它們是如何工作的,

同時我也會提供一個Native應用程序的示例源代碼。

這個示例很容易安裝,它會在啓動時的藍色屏幕打印一段你指定的字符串。

               

Autochk是如何被執行的

Autochk在當內存分頁被打開,Windows啓動和系統開始驅動被載入之間的時間內運行,

在這個時間點會啓動會話管理器(smss.exe)進入Windows NT用戶模式,並且沒有任何程序被啓動。

註冊表中:HKLM\System\CurrentControlSet\Control\Session Manager\BootExecute

一個MULTI_SZ類型的鍵值,這裏存放着將被會話管理器所執行的程序名稱和參數,通常是Autochk後加*號作爲其參數

Autocheck Autochk *
;名稱 程序名 參數

會話管理器在<winnt>\system32目錄下查找該值列出的可執行程序,當Autochk運行時,沒有任何文件被打開,

所以Autochk可以用raw模式打開任何一個驅動器卷(包括根驅動器),並操作其磁盤數據結構,

之後的任何時間點都無法進行類似這樣的操作。

              

編譯Native應用程序

1. 微軟沒有提供相應的文檔,但是NT DDK構建器知道如何去生成一個Native應用程序,

而且它可以被用來編譯autochk程序,和編寫設備驅動程序一樣,你必須指定SOURCE文件中的信息來定義應用程序,

然而和編寫驅動不同的時,你在SOURCE文件中要求生成一個Native應用程序需要這樣定義:

TARGETTYPE=PROGRAM

2. 構建器使用一個標準的makefile來進行嚮導:\ddk\inc\makefile.def 在編譯Native應用程序時,

會查找名爲nt.lib的運行庫。不幸的是,微軟並沒在DDK上裝載這個文件(在Windows Server 2003 DDK裏包括了這個文件,

但是我懷疑你用這個版本來連接你的Native應用程序是無法運行在Windows XP或Windows 2000上的)不管怎樣,

你可以忽略這個錯誤,方法是加入一行不考慮nt.lib,而指定Visual C++ 的運行庫msvcrt.lib到makefile.lib中。

3. 如果你在DDK的"Checked Build"環境下進行編譯,

將會在%BASEDIR%\lib\%CPU%\ Checked(例如c:\ddk\lib\i386\checked\ native.exe)

產生一個包含了全部調試信息的Native應用程序。

4. 如果在"Free Build"環境中編譯,你會在%BASEDIR%\lib\%CPU%\Free得到一個釋出版本的程序,

這些和構造設備驅動程序放置的位置是一樣的。

5. Native應用程序有着".exe"的擴展名,但是你不能像 Win32的.exe文件那樣去運行它,

如果你在Win32環境下運行下,將會得到如下提示:"<應用程序名> 應用程序無法在Win32模式中運行。"

             

深入學習Native應用程序

1. Native應用程序的入口點是NtProcessStartup,類似WinMain或Main,不同於其他的 Win32入口點的是,

Native應用程序提供一個數據結構來存放它的唯一的參數來定位命令行參數。

2. 大多數的Native應用程序的運行環境是由Windows Nt的Native API輸出庫 - NTDLL.DLL提供的。

3. Native應用程序必須使用RtlCreateHeap(一個ntdll函數)來創建他們自己的堆來分配存儲,

使用RtlAllocateHeap來分配內存以及用RtlFreeHeap來釋放內存。

4. Native應用程序需要使用NtDisplayString 函數纔可以打印想要的內容到屏幕上(將被輸出到初始化時的藍色屏幕上)。

5. Native應用程序不像Win32程序那樣簡單地從他們的啓動函數返回,你需要調用NtProcessTerminate函數來結束它的進程。

6. NTDLL運行包含了數百個函數允許Native應用程序執行文件I/O,與設備驅動進行相連,並執行進程間通訊。

不幸的是,他們大部分都是未公開的。

         

Native應用程序實例

1. 我創建一個Native應用程序用來演示Native應用程序是如何構建的以及他們是如何工作的。

運行install.bat來安裝Native程序。

2. 批處理程序複製Native.exe到你的<winnt>\system32目錄,

並在註冊表中增加一個BootExecute的入口點: native Hello World!

3. 當你重新啓動時,會話管理器運行完autochk後就會執行Native,Native分配一些堆,

定位它的命令行參數並打印參數("Hello world!")到藍色屏幕上,它所使用的函數上面已說過了。

如果你想要打印其他的簡單內容,可以編輯BootExecute值使用regedit或regedit32,

修改"Hello world"爲你想要的信息。

4. 運行uinstall.bat可以卸載這個Native執行程序。它從<winnt>\system32 目錄刪除 Native.exe,

並修改BootExecute值爲通常的值。

5. 如果你想要構建native程序你必須要用Windows設備驅動工具包(DDK),

複製makefile.def到 \ddk\inc然後你可以運行構建。

        

Native.H

   1:  //Environment information, which includes command line and image file name
   2:  typedef struct 
   3:  {
   4:      ULONG            Unknown[21];     
   5:      UNICODE_STRING   CommandLine;
   6:      UNICODE_STRING   ImageFile;
   7:  } ENVIRONMENT_INFORMATION, *PENVIRONMENT_INFORMATION;
   8:   
   9:  // This structure is passed as NtProcessStartup's parameter
  10:  typedef struct 
  11:  {
  12:      ULONG                     Unknown[3];
  13:      PENVIRONMENT_INFORMATION  Environment;
  14:  } STARTUP_ARGUMENT, *PSTARTUP_ARGUMENT;
  15:   
  16:  // Data structure for heap definition. 
  17:  // This includes various sizing parameters and callback routines, 
  18:  // which, if left NULL, result in default behavior
  19:  typedef struct 
  20:  {
  21:      ULONG     Length;
  22:      ULONG     Unknown[11];
  23:  } RTL_HEAP_DEFINITION, *PRTL_HEAP_DEFINITION;
  24:   
  25:  // Native NT api function to write something to the boot-time
  26:  // blue screen
  27:  NTSTATUS NTAPI NtDisplayString(
  28:      PUNICODE_STRING String
  29:      );
  30:   
  31:  // Native applications must kill themselves when done - 
  32:  // the job of this native API
  33:  NTSTATUS NTAPI NtTerminateProcess(
  34:      HANDLE ProcessHandle, 
  35:      LONG ExitStatus 
  36:      );
  37:   
  38:  // Definition to represent current process
  39:  #define NtCurrentProcess() ( (HANDLE) -1 )
  40:   
  41:  // Heap creation routine
  42:  HANDLE NTAPI RtlCreateHeap(
  43:      ULONG Flags, 
  44:      PVOID BaseAddress, 
  45:      ULONG SizeToReserve, 
  46:      ULONG SizeToCommit, 
  47:      PVOID Unknown,
  48:      PRTL_HEAP_DEFINITION Definition
  49:      );
  50:   
  51:  // Heap allocation function (ala "malloc")
  52:  PVOID NTAPI RtlAllocateHeap(
  53:      HANDLE Heap, 
  54:      ULONG Flags, 
  55:      ULONG Size 
  56:      );
  57:   
  58:  // Heap free function (ala "free")
  59:  BOOLEAN NTAPI RtlFreeHeap(
  60:      HANDLE Heap, 
  61:      ULONG Flags, 
  62:      PVOID Address 
  63:      );

       

Native.C

   1:  //======================================================================
   2:  //
   3:  // This is a demonstration of a Native NT program. These programs
   4:  // run outside of the Win32 environment and must rely on the raw
   5:  // services provided by NTDLL.DLL. AUTOCHK (the program that executes
   6:  // a chkdsk activity during the system boot) is an example of a
   7:  // native NT application.
   8:  //
   9:  // This example is a native 'hello world' program. When installed with
  10:  // the regedit file associated with it, you will see it print 
  11:  // "hello world" on the initialization blue screen during the system
  12:  // boot. This program cannot be run from inside the Win32 environment.
  13:  //
  14:  //======================================================================
  15:  #include "ntddk.h"
  16:  #include "stdio.h"
  17:  #include "native.h"
  18:   
  19:  // Our heap
  20:  HANDLE Heap;
  21:  //----------------------------------------------------------------------
  22:  // NtProcessStartup
  23:  // Instead of a 'main', NT applications are entered via this entry point.  
  24:  //----------------------------------------------------------------------
  25:  void NtProcessStartup( PSTARTUP_ARGUMENT Argument )
  26:  {
  27:      PUNICODE_STRING commandLine;
  28:      PWCHAR stringBuffer;
  29:      PWCHAR argPtr;
  30:      UNICODE_STRING helloWorld;
  31:      RTL_HEAP_DEFINITION  heapParams;
  32:      
  33:      // Initialize some heap
  34:      memset( &heapParams, 0, sizeof( RTL_HEAP_DEFINITION ));
  35:      heapParams.Length = sizeof( RTL_HEAP_DEFINITION );
  36:      Heap = RtlCreateHeap( 2, 0, 0x100000, 0x1000, 0, &heapParams );
  37:      
  38:      // Point at command line
  39:      commandLine = &Argument->Environment->CommandLine;
  40:      // Locate the argument
  41:      argPtr = commandLine->Buffer;
  42:      while( *argPtr != L' ' )
  43:      {
  44:          argPtr++;
  45:      }
  46:      argPtr++;
  47:      
  48:      // Print out the argument
  49:      stringBuffer = RtlAllocateHeap( Heap, 0, 256 );
  50:      swprintf( stringBuffer, L"\n%s", argPtr );
  51:   
  52:      helloWorld.Buffer = stringBuffer;
  53:      helloWorld.Length = wcslen( stringBuffer ) * sizeof(WCHAR);
  54:      helloWorld.MaximumLength = helloWorld.Length + sizeof(WCHAR);
  55:   
  56:      NtDisplayString( &helloWorld );
  57:      
  58:      // Free heap
  59:      RtlFreeHeap( Heap, 0, stringBuffer );
  60:   
  61:      // Terminate
  62:      NtTerminateProcess( NtCurrentProcess(), 0 );
  63:  }

                         

Install.bat:

   1:  @echo off
   2:  copy native.exe %systemroot%\system32\.
   3:  regedit /s add.reg
   4:  echo Native Example Installed

              

UnInstall.bat:

   1:  @echo off
   2:  del %systemroot%\system32\native.exe
   3:  regedit /s remove.reg
   4:  echo Native Example Uninstalled

           

           

6. 互聯網上其他關於 Native Application 的文章整理:

         

下面給出的則是我在互聯網上其他地方找到的關於 Native Application 的比較好的資料,

這裏也只是將這些資料進行一個整理。

《native app 開發小結(一)》:

http://hi.baidu.com/316526334/blog/item/32823e8a83d87b1fc9fc7a0f.html

《native app 開發小結(二)》:

http://hi.baidu.com/316526334/blog/item/43a21061ad619cd28cb10d08.html

《Native Application 應用之開機殺毒》:

http://www.cublog.cn/u/8754/showart_447592.html

             

程序類型

關於程序級別的分類,大概可以分爲三層:應用層程序,即普通的APP程序;Native App程序,如常見的chkdsk工具,PQ分區工具等,都屬於這類,它是在win子系統未啓動起來就執行的程序,執行環境比較純淨,只能調用ntdll.dll導出的函數;

Driver 內核驅動程序,因功能不同也分爲若干類,如設備驅動程序,內核擴展程序,文件系統驅動,過濾驅動等,

同屬於內核態程序;其中,native app在項目開發中用的較少,所用的函數接口MS也均未公開,

開發難度和驅動相當,所以很少有人問津。但是,事實上,在某些應用場景下,用native app來實現是非常完美的,

比如:接管winodws的開機啓動界面和密碼輸入界面,需要native app;

在開機前,執行磁盤修復,需要native app;開機前,執行殺毒,或者磁盤整理,

因爲此時環境比較純淨,需要native app。諸如此類~ 最近,開發一個Native App項目,其規模和複雜度也不一般。

期間遇到很多問題,在逐一解決的時候也收穫了很多東西。現在略作整理,以備將來查用,二來與大家分享之~

          

Native app 基本工作原理

這裏,只想簡單的描述下。

Windows的設計是基於分層模型的,在設計之初,內核NT支持三個子系統,OS/2,posix,win32,

這些子系統同屬於一個層面上,它們公用windows nt提供的系統API和例程。其中,在某一個子系統上的API調用,

都會經過NT”native”API同windowsNT進行通信。這些native API就是ntdll.dll導出的函數,

因爲它導出的大部分函數都只是起一個從子系統到NT內核的轉發傳遞作用,所以也成爲stub函數,

這些函數的原型大多是未公開的,在早期的DDK裏會有相關的描述,但是現在沒了,

取而代之的是內核實現的zw*,nt*開頭的驅動函數。這裏表明了MS的一個態度,

不希望第三方在native app上干涉windows的太多工作,比如,接管了開機啓動系統,接管了登錄密碼界面:D.

後來,因爲種種歷史原因,對OS/2和posix子系統的支持逐漸被淡忘。但是這種分層模型仍然存在,

native app就是工作在子系統未啓動之前,此時的系統環境很純淨,權限也相對較高;

另外,對操作系統來說,支持native app也是一種必須,因爲在子系統啓動之前,

很多功能的程序只能以native app來呈現,比如登錄界面,CSRSS~

具體的啓動時機:

Native app由啓動會話管理器(smss.exe)來啓動,如果想通知smss來執行一個native app程序,

只需要修改一個註冊表項,smss在每次啓動的時候會去檢查該項,確保該項下面的每個native app程序依次執行。

註冊表項爲:HKLM\System\CurrentControlSet\Control\Session Manager\BootExecute ,

其類型爲MULTI_SZ, 又是MS玩的一種字符串類型—多字符串類型,也就是說,

這個MULTI_SZ字符串包含多個以\0結尾的子字符串,而整個字符串以\0\0結尾。

在該註冊表項後面添加要註冊的native 程序名和參數就可以了。

關於native app的更多詳細介紹,可以參考Mark Russinovich的一篇關於native的文章。

                  

Native app程序結構

Native app程序結構很簡單,就好像我們寫hello world,需要寫一個main函數入口一樣,

native app的入口函數是NtProcessStartup,形如:

   1:  void NtProcessStartup( PSTARTUP_ARGUMENT Argument )

其中,參數PSTARTUP_ARGUMENT是一個結構體,用來存放傳入參數。

程序退出時,主動調用函數NtProcessTerminate退出,它不會像普通應用程序一樣一個返回return就退出了。

基本的結構就是這樣了:

   1:  void NtProcessStartup( PSTARTUP_ARGUMENT Argument )
   2:   
   3:  {
   4:   
   5:  // do something else
   6:   
   7:  NtProcessTerminate( NtCurrentProcess(), 0 );
   8:   
   9:  }
  10:   

當然現在還沒有包含頭文件之類的。

                             

開發語言及第三方的庫

Native app支持的開發語言有C/ASM/C++,並且完美的支持C++的類特性,不過要像編寫驅動一樣,

需要重載new,delete等內存分配函數。在內存使用上,可以使用兩套接口:堆函數接口,以及虛擬內存函數接口,

但是根據我的經驗,使用堆函數接口,更高效並且內存bug出現的頻率會大大降低。

舉個例子:我在調試native app的時候,一切正常,且全部通過,但是雙機調試的時候,

功能代碼都運行完了,在子系統起來的時候,提示memory_corruption錯誤,

這個問題整整找了好幾天都沒有找到,到最後,無意間屏蔽掉了系統的DbgPrint函數,memory錯誤才解決。

其原因是,native gui圖形模塊的debug消息打印的太快太頻繁,導致調試緩衝被溢出,

當屏蔽了系統DbgPrint的時候,也就是在windbg 下bp DbgPrint,然後a eip,ret後,就正常了。

雖然這個現象和程序無關,但是,用了虛擬內存的話,這個問題會更加容易重現。關於這個問題的重現很容易,

就是在native 環境下雙機調試,target機器一直打印消息,當打印到10W條以上時,調試緩衝就“爆”了。

這真不知道是ms的bug,還是自己~~

                      

關於 new, delete 的重載。

使用堆函數過程如下:首先創建一個全局堆,然後在這個全局堆上分配和釋放局部堆。

   1:  HANDLE hGlobalHeap = NULL; // for globle call
   2:   
   3:  void* __cdecl operator new(size_t size)
   4:   
   5:  {
   6:          if(hGlobalHeap == NULL)
   7:               return NULL;
   8:          return RtlAllocateHeap(hGlobalHeap,0/*HEAP_ZERO_MEMORY*/,size);
   9:  }
  10:   
  11:  void __cdecl operator delete(void* addr)
  12:   
  13:  {
  14:          if(hGlobalHeap && addr) (void)RtlFreeHeap(hGlobalHeap,0,addr);
  15:  }
  16:   

          

關於 NDK:前面說到,native app用的是ntdll.dll的導出函數,而這些函數MS並沒有公開接口聲明,

那麼我們使用的時候,首先必須要自己定義函數聲明。

NDK就是這樣的一個類庫,它幾乎定義了ntdll.dll導出的全部函數的聲明以及一些常用的數據結構的定義,

我們只需要包含相應的頭文件,導入ntdll.lib庫,就可以像使用普通的API函數一樣開發native app了。

關於DLL:native app可以調用同一級別的DLL,使大型的項目開發更加容易,更加容易劃分模塊。

注意,DLL的編譯環境要和native app一致。

關於native app的編譯:可以選擇用vs環境,也可以用DDK/WDK,但是推薦使用WDK。

用VS環境的話,需要簡單的設置下,隨意創建一個類型的工程,然後修改Linker->system->Native,

就可以了。如果用WDK編譯,需要寫一個SOURCE模板,如:

   1:  TARGETNAME=defrag
   2:   
   3:  TARGETPATH=obj
   4:   
   5:  TARGETTYPE=PROGRAM
   6:   
   7:  INCLUDES=$(PUBLIC_ROOT)\inc\ddk
   8:   
   9:  SOURCES=defrag.cpp
  10:   

注意:上面說的DLL的編譯環境和native app的編譯環境要一致,

指的是不要用WDK編譯的native去嘗試鏈接VS編譯的DLL,反之亦然。

   1:  //NTSTATUS NTAPI NtDisplayString( PUNICODE_STRING String);
              

Native GUI               

在native app執行環境下畫界面是不可行的,但是不是說做不到。

前面說了,可以寫一個native app來接管windows的啓動界面和密碼輸入界面,那麼這個界面是如何畫的呢?

也有從驅動裏實現的。但是,事實上,MS提供了一個native級別的動態庫,名爲:Bootvid.dll,

用來實現GUI啓動屏幕的引導視頻驅動,這個dll導出了一些函數,可以實現畫圖,貼圖功能,

當然,這些函數接口仍然是未公開的。呵呵~

另外,在native app程序,可以利用一個函數向屏幕打印輸出字符串,名爲:

但是,事實上,這個函數已經不推薦使用了,

在用WDK編譯native app的時候,會提示一個警告信息,deprecated~~

           

Native app的靈活性

native app由於受到調用函數接口未公開,以及開發難度和調試難度不小的原因,很少有項目問津,

但是,事實上,它仍然是一種工作於ring3的用戶態程序,只是在子系統啓動之前運行,

所以,native app程序一般不會引起系統藍屏的問題。

也正是如此,native 程序工作在一個相對純淨的環境下,可以訪問任何文件,甚至搬移MFT,系統元文件等。

Ntdll.dll爲native app導出的函數雖少,但是可以完成很多的功能。

這些導出函數,基本上和內核的系統例程都是一一對應的。結合其他的模塊,driver,應用程app,

在加之native獨特的運行環境和執行能力,往往會達到一個良好的效果。在這裏,僅僅是一個普及~

         

具體應用及實例

在開發native app之前,我想,最好有開發驅動的基礎。因爲native app程序的編寫規範基本上和驅動一樣,

除了入口函數,調試方法也一樣,必須要雙機調試。當然,只是說編寫規則一樣,驅動特有的函數例程以及工作原理,

二者是毫不相干的。舉個例子,操作註冊表,文件IO,線程創建,內存使用,二者基本上是一致的,具體的在使用中自己體會吧。

          

作爲入門例子

作爲入門我想還是用Mark Russinovich的例子吧,就好像Hello World的作用一樣,

讓你真切的感受下native app程序。該程序在系統啓動時,藍屏界面上輸出一行字符串信息。

程序在附件,沒什麼過多的需要解釋的。

          

實際開發

實際開發中,有不少的應用例子。比如,影子系統的啓動界面,PQ分區工具,

win系統自帶的chkdsk工具,都屬於這類程序。下面說下自己的一些經歷吧:

項目中有個需要,對特定文件進行磁盤整理。我們知道,文件系統導出了一組函數接口,

用於對磁盤上的文件進行搬移操作,使文件內碎片和外碎片減少,提高IO吞吐率和磁盤訪問率。

唯一的限制是,pagefile.sys和日誌文件不能整理,其他的文件,如MFT,系統元文件都可以整理。

對於文件整理,更重要的是算法和穩定法,當然,這是另外一個話題了。

根據prefreth原理,一個應用程序的工作集頁面在運行時基本上是趨於穩定的,

那麼這些穩定的頁面如果位於不同的文件(可能是鏈接庫之類),且這些文件在磁盤上比較分散,

那麼就會影響程序的啓動時間了,雖然prefretch做了改善,會自動的激發系統的磁盤整理來對“相關”的文件緊密排放,

但仍然是不夠的。所以,關於這種性能的改善看起來微乎其微,但是做好了,價值是不可估量的。

這僅僅是一個方面,當然,項目中的主要目的並不是這個,雖然也是爲了提高性能。

在native app下操作文件,考慮的情況是比較單一的,不會擔心文件或目錄被鎖,

從而出現不能訪問的情況,也不會考慮過多的併發問題。

所以,功能會更加集中,運行效率會更高,操作的空間和權限也更大。

僅限於此,就到這吧 ~

           

        

7. 小結:

          

上面的文字呢就大體的把 Native Application 給介紹的差不多了,

主要是總結了一下自己關於 Native Application 的理解,

然後再將 MJ0011 和互聯網上的幾位博主的文章給整理了一下(原博文排版不怎麼好),

這裏對 MJ0011 以及所整理的文章的博主表示感謝 ~

博文中的 Demo 我是採用的 DDK 2600.1106 編譯的,至於 WDK 7 的話,我沒有去試哦 ~

有興趣的可以直接下載了 Demo 進行測試的,代碼什麼的都在裏面,

source 和 makefile 我也都整理好了放在 Demo 中 ~

       

最後還是和以往一樣,給出些我近來的一些疑問:

我曾經從一臺電腦 A 上下載了卡巴斯基的試用版,然後我將這個試用版的安裝文件拷貝到了電腦 B 上,

這時我發現卡巴斯基的這個安裝文件在電腦 B 上是無法運行的 ~ 直接報錯,

當然,這個安裝文件在電腦 A 上是可以運行安裝的,

我想應該是卡巴斯基的這個安裝文件在拷貝的過程中還是下載的過程中記錄下了我的電腦 A,

然後在安裝的時候,其自動判斷是否是在電腦 A 上安裝運行,

如果是的話,則可以成功安裝,如果不是,則直接報錯 ~

我想知道上面是怎麼來實現的呢 ? 我只是下載和拷貝了一下哦 ~

                

還有就是貌似要考個微軟的 MCTS 證書,

應該是 Windows 方向的 71-511 或者 Web 方向的 71-515 這兩門考試,

不曉得大夥有什麼好建議或者好的資料沒有哦 ~ 可否提供一些呢 ~ 謝謝咯 ~

    

然後再問一下哦,有沒有湖南婁底的啊 ? 國慶組隊回去否 ?(我在深圳 ~)

            

下載 Demo Source Code

                                    

           

版權所有,迎轉載,但轉載請註明: 轉載自  Zachary.XiaoZhen - 夢想的天空

            

          

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章