用C++進行Windows驅動開發的一些進展

blowfish發表於2017-11-14
Windows驅動開發這一塊一直是純C的天下。為什麼要用C++進行Windows驅動開發呢?當然是為了利用C++特性帶來的便利。

大致上,用C++開發Windows驅動,有這麼一些便利需求:
1、利用C++的封裝、繼承、多型,實現程式碼高效複用;
2、利用C++的RAII機制,避免資源洩漏;
   如果沒有RAII釋放,開發者得回到資源釋放的老路上去。通常是有這麼幾條路可走:
   a、在函式中的每個返回之前,都加上釋放資源的語句。這是最原始的做法,繁瑣,而且一個地方忘加就意味著洩漏。
   b、使用goto,跳到函式的尾部,在尾部統一釋放所有資源。受“儘量不要使用goto”的現代編碼規範的約束,一般人不愛用這個,怕被別人鄙視。
   c、使用do {} while(0),遇到失敗時break,在do/while迴圈的後面統一釋放所有資源;
   d、使用try {} finally {},遇到失敗時執行leave,在SEH塊的finally塊中統一釋放所有資源。
   一旦享受了C++的RAII福利後,以上不優雅的做法,基本上都無法接受了。
3、利用C++的輪子庫STL。還是為了複用;
    STL中大量使用allocator、exception等特性,需要做特殊處理。

由於Windows驅動需要考慮paged/nonpaged記憶體、各級IRQL、核心異常處理、浮點協處理器等特性,而編譯器在處理C++程式碼時會自動插入一些"不可見"的資料和程式碼(包括自動連結CRT庫實現全域性/靜態物件的自動構造/析構、C++異常處理、RTTI、vtable、模板展開等),早期編譯器不能精確指定這些“隱藏”資料和程式碼的位置,也從沒提供過核心層的CRT庫,所以用C++編寫Windows驅動面臨較大的困難。微軟和OSR也針對用C++開發Windows驅動出過一些調研paper,結論當然是比較悲觀,一般不推薦用C++寫驅動,要寫的話,需要清楚地知道自己在幹什麼,得是專家中的專家,可能還要經常把編譯器生成的程式碼反彙編出來看看是否滿足預期。

微軟官方白皮書《C++ for Kernel Mode Drivers: Pros and Cons》:
http://download.microsoft.com/download/5/b/5/5b5bec17-ea71-4653-9539-204a672f11cf/kmcode.doc

Advanced C++ features and Kernel-mode programming don’t mix:
https://blogs.msdn.microsoft.com/adioltean/2005/04/24/advanced-c-features-and-kernel-mode-programming-dont-mix/

OSR上的經典文章《Guest Article: C++ in an NT Driver》(不一定是OSR官方帖子):
http://www.osronline.com/article.cfm?article=490

由於C++便利性的誘惑,自然有開發者前赴後繼地研究怎麼用Windows開發驅動,並搞出來自己的C++ framework for windows driver。

最早的應該是曾經大名鼎鼎的DriverStudio(順帶懷念一下偵錯程式王者SoftICE,那耗掉很多日夜的綠色文字介面),現在還能搜到3.2版本的下載。

BazisLib:
http://bazislib.sysprogs.org

Global Relief Effort - C++ Runtime Support for the NT DDK:
http://www.osronline.com/article.cfm?article=57

Kernel C++ Runtime Library:
http://www.hollistech.com/Resources/Cpp/kernel_c_runtime_library.htm

C++ in Kernel Drivers (c++, boost, std):
http://www.zer0mem.sk/?p=517

下面這個應該是集大成者,實現了異常處理、RTTI,可能是逆向後實現的。能否用於生產程式碼,使用者需自行斟酌。

ntke cpprtl:
http://www.osronline.com/showthread.cfm?link=250151

微軟在用C++進行Windows驅動開發方面,也做了一些改進。

1、在VS2012中增加了/kernel編譯選項,可以關閉一些核心不直接支援的C++特性:C++異常、RTTI、new/delete運算子(如果用到,必須由開發者自己提供)。
https://msdn.microsoft.com/en-us/library/jj620896.aspx

2、在VS2013中增加了code_seg()編譯指示的擴充套件。開發者可以透過這個編譯指示告訴編譯器將對應的語言實體(類、變數、成員函式、模板、lambda表示式等)放在哪個段中。通常是用code_seg()指定哪些放在paged段中,因為預設是放在nonpaged的text/data段的。
這對於用C++開發Windows驅動是一個非常大的利好,雖然喜歡用C++的驅動開發者仍舊得帶著鐐銬跳舞。
https://msdn.microsoft.com/en-us/library/dn636922(v=vs.140).aspx

3、在自家開源的驅動NetAdpaterCx.sys的程式碼中,用C++實現了簡單的runtime支援,主要是幾個標頭檔案。這說明現在微軟自家也是有用C++來寫驅動的。
https://github.com/Microsoft/Network-Adapter-Class-Extension/tree/master/rtl

相關文章