このプロジェクトでは、WindowsカーネルドライバーでMSVC C ++ STLを使用しています。このソリューションでは、 jxystl.libはカーネルチューニング、プールタイプ/タグ認識、テンプレートライブラリ、およびMSVC実装として実装されています。ボンネットの下で、MSVC C ++ STLを使用します。
# include < wdm.h >
# include < jxy/string.hpp >
extern " C "
NTSTATUS DriverEntry (
PDRIVER_OBJECT DriverObject,
PUNICODE_STRING RegistryPath)
{
jxy::wstring<PagedPool, ' 0GAT ' > helloWorld;
try
{
helloWorld. assign ( L" Hello, World! " );
}
catch ( const std::bad_alloc&)
{
return STATUS_INSUFFICIENT_RESOURCES;
}
return STATUS_SUCCESS;
} 1: kd> dv
DriverObject = 0xffffca83`5380d300 Driver "Driverstlkrn"
RegistryPath = 0xffffca83`5227f000 "REGISTRYMACHINESYSTEMControlSet001Servicesstlkrn"
helloWorld = "Hello, World!"
| 標準 | サポート | メモ |
|---|---|---|
| CPP11 | いいえ | テストされていない、あなたのマイレージは私のさまざまです |
| CPP14 | はい | |
| CPP17 | はい | |
| CPP20 | はい |
このソリューションのテストドライバーであるstdtest.sysは、プロジェクトのユニットテストを収容しています。ユニットテストは、ドライバー検証剤を使用してカーネルで実行されます。ユニットテストフレームワークは裸の骨ですが、 jxystl.lib行使するのに十分です。
このソリューションに実装された別のドライバーであるstdkrn.sysは、 jxystl.lib実際のシナリオで使用するようにします。さまざまなstdネームスペース機能とコンテナを使用しています( jxyネームスペースの下に包まれています)。このドライバーは、プロセス、スレッド、および画像通知を登録します。次に、最新のC ++を使用して、プロセスコンテキスト、スレッドコンテキスト、モジュールコンテキストを追跡します。
vcrtl例外処理により、例外がスローされたときにC ++オブジェクトがリラックスできます。これは、カーネルドライバーにほとんど注目されないC ++のコア機能です。 Microsoftは、カーネルドライバーのC ++例外をネイティブにサポートしていません。
C ++例外処理は、AvakarのVCRTL Librarayによって可能になります。このプロジェクトは、Avakarの素晴らしい貢献がなければ、はるかに多くの仕事でした。 Windowsドライバーでの例外処理の詳細については、AvakarのVCRTL Githubにアクセスしてください。また、このページは、AMD64での例外処理に関する優れた詳細を示しています。
jxystlWindowsカーネルの割り当ては、メモリプールに関連付けられています。さらに、プールのタグ付けはWindowsカーネルに組み込まれています。プールタグ付けにより、ドライバーが作成した割り当ての追跡が容易になります。このタグ付け機能により、割り当てのデバッグと監視が可能になります。
jxyネームスペースは、このソリューションでは、プールのタイピングとタグ付けを備えたstdネームスペースオブジェクトを使用して、Windowsドライバーの開発を強化します。
ライブラリは、「グローバル」なnew / delete演算子を実装しないことを選択します。プールのタイピングとタグ機能を使用して、 new / deleteオペレーターのみを実装します。これには、プールの種類とタグを指定する必要があります。 「グローバルアロケーター」を必要とするいくつかの機能が使用されている場合、リンクしません。これは、グローバルアロケーターが使用されないように意図的な設計決定であり、すべての割り当てはプールの種類とタグを指定する必要があります。
jxy名空間は、テンプレートコンテナで使用するための標準に適合するアロケーターと削除を実装します。これらのアロケーターと削除は、プールタイプ/タグ認識です。プールの種類とタグを指定し、ツールの種類とタグ間での変換/リバインティングを防ぐ必要があります - それらはSTLアロケーターの代わりに使用する必要があります。
jxy::allocator<T, PagedPool, ' 0GAT ' >;
jxy::default_delete<T, PagedPool, ' 0GAT ' >; jxystl.lib 、MSVC STLコンテナを使用するために必要な「充填」機能を実装します。実装( msvcfill.cpp )はカーネルに思いやりがあります。この機能により、MSVC STLコンテナはカーネルに適した機能にリンクできます。これはまた、その背後に「塗りつぶし」機能を持たないstdコンテナ機能を使用する場合、リンカーが故障することを意味します。これは、実装がカーネルで使用するために検討されるように、意図的な設計上の決定です。
CRTの初期化とAtexit機能は意図的にサポートされていません。 CRTの初期化の順序は不明確で非自明です。カーネルドライバーがグローバルデータをロードするときは、ドライバーの荷重とアンロード中にはっきりとセットアップされ、取り壊されるはずです。グローバルCRTの初期化は、この初期化を非自明な方法で「隠す」。さらに、CRT Atexit機能はサポートされていません。 C ++オブジェクトのローカル静的初期化を可能にする必要な同期の放出は、コンパイラによって行われません。カーネルに非自明な同期を導入します。 CRTの初期化とATEXITサポートの欠如は、意図的な設計上の決定です。カーネルドライバーを開発するときは、それを避けることを強くお勧めします。
例として、 jxyネームスペースはstd::vectorおよびforceのプールタイプとタグの使用を「ラップ」します。
namespace jxy
{
template < typename T,
POOL_TYPE t_PoolType,
ULONG t_PoolTag,
typename TAllocator = jxy::allocator<T, t_PoolType, t_PoolTag>>
using vector = std::vector<T, TAllocator>;
}
jxy::vector< int , PagedPool, ' 0GAT ' > integers; stlkrn!DriverEntry+0xea:
0: kd> dx integers
integers : { size=10 } [Type: std::vector<int,jxy::details::allocator<int,1,809976148> >]
[<Raw View>] [Type: std::vector<int,jxy::details::allocator<int,1,809976148> >]
[capacity] : 10
[allocator] : {...} [Type: std::_Compressed_pair<jxy::details::allocator<int,1,809976148>,std::_Vector_val<std::_Simple_types<int> >,1>]
[0] : 1 [Type: int]
[1] : 2 [Type: int]
[2] : 3 [Type: int]
[3] : 4 [Type: int]
[4] : 5 [Type: int]
[5] : 6 [Type: int]
[6] : 7 [Type: int]
[7] : 8 [Type: int]
[8] : 9 [Type: int]
[9] : 10 [Type: int]
以下は、 jxy名空間の下の機能の表を次のとおりです。
| jxy | STL相当 | 含む | メモ |
|---|---|---|---|
jxy::allocator | std::allocator | <jxy/memory.hpp> | |
jxy::default_delete | std::default_delete | <jxy/memory.hpp> | |
jxy::unique_ptr | std::unique_ptr | <jxy/memory.hpp> | |
jxy::shared_ptr | std::shared_ptr | <jxy/memory.hpp> | |
jxy::basic_string | std::basic_string | <jxy/string.hpp> | |
jxy::string | std::string | <jxy/string.hpp> | |
jxy::wstring | std::wstring | <jxy/string.hpp> | |
jxy::vector | std::vector | <jxy/vector.hpp> | |
jxy::map | std::map | <jxy/map.hpp> | |
jxy::multimap | std::miltimap | <jxy/map.hpp> | |
jxy::mutex | std::mutex | <jxy/locks.hpp> | KGUARDED_MUTEXを使用します |
jxy::shared_mutex | std::shared_mutex | <jxy/locks.hpp> | EX_PUSH_LOCKを使用します |
jxy::unique_lock | std::unique_lock | <jxy/locks.hpp> | |
jxy::shared_lock | std::shared_lock | <jxy/locks.hpp> | |
jxy::scope_resource | なし | <jxy/scope.hpp> | std::experimental::unique_resourceに似ています |
jxy::scope_exit | なし | <jxy/scope.hpp> | std::experimental::scope_exitに似ています |
jxy::thread | std::thread | <jxy/thread.hpp> | |
jxy::deque | std::deque | <jxy/deque.hpp> | |
jxy::queue | std:queue | <jxy/queue.hpp> | |
jxy::priority_queue | std::priority_queue | <jxy/queue.hpp> | |
jxy::set | std::set | <jxy/set.hpp> | |
jxy::multiset | std::multiset | <jxy/set.hpp> | |
jxy::stack | std::stack | <jxy/stack.hpp> |
stltest.sys stltestプロジェクトは、JXYSTL、STLの使用、およびWindowsカーネルの例外に対するいくつかのテストを実行するドライバーを実装しています。
stlkrn.sys stlkrnプロジェクトは、 jxystl.libを使用してWindowsカーネルにプロセス、スレッド、およびモジュールトラッキングを実装するWindowsドライバーです。
stlkrn.sys 、 ntoskrnlによってエクスポートされた機能を使用したプロセス、スレッド、および画像通知のために登録します。これらのコールバックを使用してjxy::map 、 jxy::shared_mutex 、 jxy::wstringなどを使用するさまざまなオブジェクトのプロセス、スレッド、および画像ロードを追跡します。
ドライバーには2つのシングルトンがあります。 jxy::ProcessMap and jxy::ThreadMap DriverUnload DriverEntryを取り壊します。ここでは、 jxy::ProcessMap ( jxy::ProcessContextとして実装されている)で追跡されている各プロセスもjxy::ThreadMapを管理します。各「コンテキスト」( jxy::ProcessContext 、 jxy::ThreadContext 、およびjxy::ModuleContext )は、共有(参照)オブジェクト( jxy::shared_ptr )です。したがって、スレッドマップシングルトンに存在するスレッドコンテキストは、プロセスコンテキストに関連付けられたコンテキストと同じです。
stlkrn.sysの重要なコンポーネント:
| 物体 | 目的 | ソース | メモ |
|---|---|---|---|
jxy::ProcessContext | システムで実行されているプロセスの情報。 | process_context.hpp/cpp | jxy::wstringを使用します。スレッド( jxy::ThreadMap )とモジュール( jxy::ModuleMap )マップメンバーがあります。 |
jxy::ThreadContext | システムで実行されているスレッドの情報。 | thread_context.hpp/cpp | std::atomicを使用します。 |
jxy::ModuleContext | 特定のプロセスでロードされた画像の情報。 | module_context.hpp/cpp | jxy::wstringとjxy::shared_mutex使用します。 |
jxy::ProcessMap | Singleton、マップはjxy::ProcessContextオブジェクトをPIDに共有しました。 | process_map.hpp/cpp | Singletonにはjxy::GetProcessMapからアクセスされます。 jxy::shared_mutexおよびjxy::mapを使用します。 |
jxy::ThreadMap | マップはjxy::ThreadContextオブジェクトをtidに共有しました。 | thread_map.hpp/cpp | グローバルスレッドテーブル(Singleton)にはjxy::GetThreadMapからアクセスされます。各jxy::ProcessContextはjxy::ProcessContext::GetThreadsを介してアクセスされるスレッドマップもあります。 jxy::shared_mutexおよびjxy::mapを使用します。 |
jxy::GetModuleMap | マップはjxy::ModuleContextをロードされた画像範囲(ベースおよびエンドアドレス)に共有しました。 | module_map.hpp/cpp | 各プロセスコンテキストには、モジュールマップメンバーがあります。特定のプロセスのロードされた画像は、このオブジェクトを使用して追跡されます。 jxy::shared_mutexおよびjxy::mapを使用します |
std::unordered_mapオブジェクトマップの注文ツリー( std::map )よりも優れた選択でした。これが使用されない理由があります( TODOセクションを参照)。
stlkrn!jxy::nt::CreateProcessNotifyRoutine+0xa6:
3: kd> dx proc
proc : {...} [Type: std::shared_ptr<jxy::ProcessContext>]
[<Raw View>] [Type: std::shared_ptr<jxy::ProcessContext>]
[ptr] : 0xffffaa020d73cf70 [Type: jxy::ProcessContext *]
[control block] : custom deleter, custom allocator [Type: std::_Ref_count_resource_alloc<jxy::ProcessContext *,jxy::details::default_delete<jxy::ProcessContext,1,1668307018>,jxy::details::allocator<jxy::ProcessContext,1,1668307018> > (derived from std::_Ref_count_base)]
[+0x000] m_ProcessId : 0x2760 [Type: unsigned int]
[+0x004] m_SessionId : 0x2 [Type: unsigned int]
[+0x008] m_ParentProcessId : 0xcc4 [Type: unsigned int]
[+0x010] m_FileName : "DeviceHarddiskVolume4WindowsSystem32cmd.exe" [Type: std::basic_string<unsigned short,std::char_traits<unsigned short>,jxy::details::allocator<unsigned short,1,1852856394> >]
[+0x030] m_FilePart : "cmd.exe" [Type: std::basic_string<unsigned short,std::char_traits<unsigned short>,jxy::details::allocator<unsigned short,1,1886410826> >]
[+0x050] m_CreatorProcessId : 0x1b08 [Type: unsigned int]
[+0x054] m_CreatorThreadId : 0x26a0 [Type: unsigned int]
[+0x058] m_Threads [Type: jxy::ThreadMap]
[+0x070] m_Modules [Type: jxy::ModuleMap]
最初はstd::unordered_mapを含めたいと思っていましたが、 ceilfを使用していました。 Windowsカーネルの浮動小数点算術には、いくつかの課題があります。したがって、今のところ、適切なソリューションが設計されるまで省略されています。
このソリューションは情熱プロジェクトです。現時点では、生産コードを意図したものではありません。 x64はよくテストされ、安定しています。STLKRN.SYS stlkrn.sys 、完全なドライバー検証剤オプション(ランダム化低リソースシミュレーションを含む)を通過します。派遣以上の例外処理がテストされていますが、実際のユースケースではありません。 x86はテストされていません。 jxyネームスペースの下には、不完全/未使用/未テストの機能があります。あなたの走行距離は異なる場合があります- 問題/バグが見つかった場合は、このレポに対して自由に問題を開いてください。
このプロジェクトは、できるだけ多くのSTL施設を使用することにより、WindowsカーネルでSTLサポートを提供します。カーネル開発にSTLを使用する他のソリューションがあります。このセクションでは、代替案の概要を説明します。まず、この作業を要約します。
このプロジェクト:
newまたはdelete実装されていません。atexit機能を回避します。 CRTの初期化順序は明確ではありません。ドライバーの初期化と分解は明らかです。 atexit機能は、カーネルコードのデータレースを導入する場合があります。ATEXIT atexit実装されていません。BareFlankハイパーバイザー:
BareFlankは、ハイパーバイザーでC ++を実行するためのサポートを実装します。彼らは完全なSTLおよびCRTのサポートを持っています。これは、カーネルモード(例外を含む)の標準の不安定な機能を可能にする包括的なプロジェクトです。私が理解しているように、彼らのソリューションはグローバルなnew / delete割り当てでNonPagedPoolを強制します。私は彼らの実装でBareFlankを賞賛しなければなりません、それはよく考えられていて、クロスプラットフォームです。ただし、Windowsの実装は、WindowsカーネルをサポートするCygwinと「Shims」を介して構築されます。それに比べて、このプロジェクトは、Windowsカーネルに思いやりがあることを目指しています。これにより、プールタグとタイプ(ページとページのページ)を指定し、C ++とSTLをカーネルモードで使用することに関連する「シャープエッジ」を最小限に抑えることを望んでいます。とはいえ、Bareflankは何がしているのかという印象的です。 BareflankのC ++のサポートに関する優れたプレゼンテーションについては、CPPCON 2016でのRian Quinn博士のプレゼンテーションを見ることを強くお勧めします。
win32kernelstl:
win32kernelstlプロジェクトでは、カーネルでSTL機能を直接使用できます。このプロジェクトは、グローバルなnew / deleteと強制NonPagedPoolを実装し、CRTの初期化サポートを実装し、CPP例外がスローされたときにバグチェックを実装します。 CPP例外を巻き上げようとはしません。仮定のために、それは深刻なユースケースに対してそれが非実践的であると感じます。コードは合理的に明確で文書化されています。このプロジェクトに、カーネルでのC ++サポートについて教育するための閲覧を閲覧することをお勧めします。 1つの注意事項は、Win32KernelStlのCRTコードはatexitを実装していますが、ここでは(ユーザーモードとは対照的に)コンパイラによって放出される同期はないことに留意してください。したがって、 atexitリストにエントリを挿入する必要があるローカル静的は、競争して2倍または二重フリーを引き起こす可能性があります。
ドライバープラス:
このプロジェクトは、多くのC ++ソリューションをカーネルモード( EASTL 、 msgpackなど)に引き込むために必要なC ++施設を実装しています。 Driver Plus Plusは、CRTの初期化とグローバルなnew / deleteサポート( NonPagedPool強制する)を実装します。繰り返しますが、これはこのプロジェクトの目標に対抗します。ただし、このプロジェクトでは、カーネルモードで使用するための多くの優れたC ++施設が可能になります。ユースケースをサポートするために、シムに引き込むC ++ソリューションを変更します。 Driver Plus Plusは、前述のようにatexitについても仮定します。
KTL -Dymok93
KTL(Dymok93によるWindows Kernelテンプレートライブラリ)は、かなりの量の最新のC ++を再現し、Windowsカーネルでの使用をより多くのサポートを開発しています。また、多くのカーネルプリミティブを中心にRAIIをサポートし、ネイティブANSI_STRINGとUNICODE_STRINGサポートを提供し、カーネルコールバックを登録するための有用なラッパーを提供し、Windows Kernelの周りのより便利な機能を提供します。グローバルなnew / deleteを実装し、デフォルトのページまたは非ページを切り替えるためのプリプロセッサ定義( KTL_USING_NON_PAGED_NEW_AS_DEFAULT )を備えています。ただし、単一のプールタグ( KTL_HEAP_TAG )を使用します。さらに、既存のAllocatorテンプレートでは、開発者がプールタグを指定できないため、このライブラリAS-AS-AS-AS-すべての割り当てが同じプールタグでタグ付けされます。とはいえ、割り当てのタグ付けを強化するカスタムアロケーターを実装することは合理的です。ライブラリには、x64だけではありますが、例外サポートがあります。 KTLの例外サポートは、拡張と修正を備えたAvakarのサポートに基づいています。私はここで作業を称賛し、存在する施設の量に感銘を受けました。それは合理的に機能し、積極的な開発中です。将来、より多くの使用を調べて、 stlkrnとKTLの両方のより良い例外サポートについて協力する可能性があります。 STL機能の再実装、ネイティブのプールタグサポートの欠如、およびグローバルアロケーターは、このプロジェクトのイデオロギーに対抗しています。
KTL -Meesong:
KTL(MeesongによるWindows Kernelテンプレートライブラリ)は、Windowsカーネルで使用するための最新のC ++機能のかなりの量を再現します。また、Global new / deleteを実装していますが、可能な場合はプールタグとタイプを指定するための施設を提供するのに適切な仕事をしています。ただし、これは、グローバルアロケーターが非自明なプールで割り当てを隠す可能性があることを意味します。さらに、このプロジェクトのテンプレートアロケーターは、アロケーターとディールロケーターオブジェクトの2つのポイントのコストを帯びています。また、アロケータータイプ間の変換により、クロスプール/タグアロック/フリーが可能になる可能性があることも懸念しています。全体として、ここで実装されている施設の量に感銘を受けました。 STL機能とグローバルアロケーターの再実装は、このプロジェクトのイデオロギーに対抗します。
カーネルブリッジ:
カーネルブリッジは、Windowsカーネル開発に最適な施設を実装しています。ライブラリは、C ++オブジェクトを使用してWindowsコールバックに登録するためのラッパーを提供します。このソリューションを使用して調査する時間をもっと見つけたいと思います。 CRTサポートを実装します。実装されたatexit機能は動的ではありません - 静的配列を使用します。スロットがなくなると失敗します。デフォルトのnew / delete力NonPagedPool 。完全な例外のサポートはありません。CPP例外がスローされた場合、それはバグチェックされます - スタック上のオブジェクトを巻き戻しません。
このリポジトリは、既存の作業から引き出されます。著者へのクレジット。