이 프로젝트는 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의 멋진 기여없이 훨씬 더 많은 일이었을 것입니다. Wind 또한이 페이지는 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 와 수영장 유형 및 태그의 힘을 사용합니다.
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 드라이버입니다.
ntoskrnl 에서 내보낸 기능을 사용하여 프로세스, 스레드 및 이미지 알림에 대한 stlkrn.sys 레지스터. 이 콜백을 사용하면 jxy::map , jxy::shared_mutex , jxy::wstring 등을 사용하는 다양한 객체에서 프로세스, 스레드 및 이미지로드를 추적합니다.
운전자는 2 개의 싱글 톤이 있습니다. jxy::ProcessMap 및 jxy::ThreadMap 은 드라이버가로드 할 때 ( DriverEntry )가 언로드 할 때 찢어 질 때 구성됩니다 ( DriverUnload unload). jxy::ProcessMap ( jxy::ProcessContext 로 구현)에서 추적 된 각 프로세스는 jxy::ThreadMap 관리하는 것이 좋습니다. 각 "context"( 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 | 싱글 톤은 jxy::GetProcessMap 통해 액세스합니다. jxy::shared_mutex 및 jxy::map 사용합니다. |
jxy::ThreadMap | 지도 공유 jxy::ThreadContext 객체를 TID에 공유했습니다. | thread_map.hpp/cpp | 글로벌 스레드 테이블 (싱글 톤)은 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 커널의 Floating Point 산술에는 몇 가지 과제가 있습니다. 따라서 지금은 적절한 솔루션이 설계 될 때까지 생략됩니다.
이 솔루션은 열정 프로젝트입니다. 현재는 생산 코드를위한 것이 아닙니다. x64 는 잘 테스트되고 안정적이며 stlkrn.sys 전체 드라이버 검증 자 옵션 (무작위 저자 리소스 시뮬레이션 포함)을 전달합니다. 파견 또는 그 이상의 예외 처리는 테스트되었지만 실제 사용 사례에서는 그렇지 않습니다. x86 테스트되지 않았습니다 . jxy 네임 스페이스 아래에는 불완전/사용하지 않은/피복되지 않은 기능이 있습니다. 당신의 마일리지는 다를 수 있습니다 - 나는 시간이 지남에 따라이 작업을 계속하고 싶습니다. 문제/버그가 발견되면이 repo에 대한 문제를 자유롭게 열 수 있습니다.
이 프로젝트는 가능한 많은 STL 시설을 사용하여 Windows 커널에서 STL 지원을 제공합니다. 커널 개발에서 STL을 사용하기위한 다른 솔루션이 있습니다. 이 섹션은 대안을 개요 할 것입니다. 먼저이 작업을 요약하겠습니다.
이 프로젝트 :
new 또는 delete 구현되지 않습니다.atexit 기능을 피하십시오. CRT 초기화 순서는 불쾌하지 않으며 드라이버 초기화 및 분해는 분명해야합니다 . atexit 기능은 커널 코드 용 데이터 레이스를 도입 할 수 있으며 atexit 구현되지 않습니다.베어 플랑크 하이퍼 바이저 :
BareFlank는 하이퍼 바이저에서 C ++ 실행을 지원합니다. 그들은 완전한 STL 및 CRT 지원이 있습니다. 이것은 커널 모드 (예외 포함)에서 표준의 풍부한 기능을 가능하게하는 포괄적 인 프로젝트입니다. 솔루션을 이해하면서 글로벌 new / delete 할당에 대한 NonPagedPool 강요합니다. 나는 그들의 구현에 대해 BareFlank를 칭찬해야합니다. 그것은 잘 생각되고 크로스 플랫폼입니다. 그러나 Windows 구현은 Windows 커널을 지원하기 위해 Cygwin 및 "Shims"를 통해 구축됩니다. 이에 비해이 프로젝트는 Windows 커널을 고려하는 것을 목표로합니다. 이를 통해 풀 태그 및 유형 (PAGED vs 비 PAGE)을 지정할 수 있으며 커널 모드에서 C ++ 및 STL 사용과 관련된 "날카로운 모서리"를 최소화하기를 희망합니다. 바로 말하면, 베어 플랑크는하는 일에 인상적입니다. BareFlank의 C ++ 지원에 대한 훌륭한 프레젠테이션은 CPPCon 2016에서 Rian Quinn 박사의 프레젠테이션을 시청하는 것이 좋습니다.
win32kernelstl :
Win32kernelstl 프로젝트를 사용하면 커널에서 직접 STL 기능을 사용할 수 있습니다. 이 프로젝트는 Global new / delete 구현하고 NonPagedPool 강제하고 CPP 예외가 발생하면 CRT 초기화 지원 및 버그 체크를 구현합니다. CPP 예외를 풀려고 시도하지 않습니다. 가정으로 인해 심각한 사용 사례에 비현실적이라는 것을 알 수 있습니다. 이 코드는 합리적으로 명확하고 문서화되어 있습니다.이 프로젝트에 커널에서 C ++ 지원을 교육하기위한 탐색을 권장합니다. 하나의 참고 사항, Win32kernelstl의 CRT 코드는 atexit 구현하지만 여기서 컴파일러에 의해 동기화 된 동기화는 없음 (사용자 모드와 반대). 따라서 atexit 목록에 항목을 삽입 해야하는 로컬 정적으로 인해 경주하면 이중 인트 또는 이중 무료가 발생할 수 있습니다.
드라이버 플러스 :
이 프로젝트는 여러 C ++ 솔루션을 커널 모드 ( EASTL , msgpack 등)로 끌어 당기는 데 필요한 C ++ 시설을 구현합니다. Driver Plus Plus는 CRT 초기화 및 글로벌 new / delete 지원 ( NonPagedPool 강요)을 구현합니다. 다시 이것은이 프로젝트의 목표와 반대입니다. 그러나이 프로젝트는 커널 모드에서 사용할 수있는 많은 C ++ 시설을 가능하게합니다. 그것은 사용 사례를 지원하기 위해 Shim으로 끌리는 C ++ 솔루션을 수정합니다. Driver Plus는 또한 앞에서 언급했듯이 atexit 주변의 가정을 만듭니다.
KTL -dymok93
KTL (Dymok93의 Windows 커널 템플릿 라이브러리)은 많은 양의 최신 C ++를 상환하며 Windows 커널에서 더 많은 지원을 지원하고 있습니다. 또한 많은 커널 프리미티브 주변의 RAII를 지원하며 기본 ANSI_STRING 및 UNICODE_STRING 지원을 제공하며 커널 콜백을 등록하는 데 유용한 포장지와 Windows 커널 주변의 더 편리한 기능을 제공합니다. 기본 PAGEND 또는 비 PAGE를 전환하기 위해 글로벌 new / delete 구현하고 기본 PAGEND 또는 비 PAGE를 전환하기위한 전처리 서적 정의 ( KTL_USING_NON_PAGED_NEW_AS_DEFAULT )가 있습니다. 그러나 단일 풀 태그 ( KTL_HEAP_TAG )를 사용합니다. 또한 기존 할당 템플릿은 개발자가 풀 태그를 지정할 수 없으므로이 라이브러리를 사용하면 모든 할당이 동일한 풀 태그로 태그가 지정됩니다. 즉, 할당 태그에 맞는 맞춤형 할당자를 구현하는 것이 합리적 일 것입니다. 도서관은 x64에 불과하지만 예외 지원이 있습니다. KTL의 예외 지원은 향상 및 수정 사항이있는 Avakar의 기반입니다. 나는 여기서 작업을 칭찬하고 존재하는 시설의 양에 깊은 인상을 받았으며, 합리적으로 포장되고 적극적으로 개발되고 있습니다. 앞으로 더 많이 사용하고 stlkrn 과 KTL 에 대한 더 나은 예외 지원에 대한 협력을 탐색하고 싶습니다. STL 기능의 상환, 기본 풀 태그 지원 지원 및 글로벌 할당자는이 프로젝트의 이데올로기에 대응합니다.
KTL- Meesong :
KTL (Meesong의 Windows 커널 템플릿 라이브러리)은 Windows 커널에서 사용하기위한 많은 양의 최신 C ++ 기능을 상환합니다. 또한 Global new / delete 구현하지만 가능한 경우 풀 태그 및 유형을 지정할 수있는 시설을 제공하는 데 괜찮은 작업을 수행합니다. 그러나 이는 글로벌 할당자가 무성한 풀에서 할당을 숨길 수 있음을 의미합니다. 또한이 프로젝트의 템플릿 할당자는 할당 자 및 거래가 객체에 대한 두 지점의 비용을 전달할 수 있습니다. 또한 할당 기 유형 간의 변환이 크로스 풀/태그 Allocs/Free를 허용 할 수 있습니다. 전반적으로 나는 여기에서 구현 된 시설의 양에 깊은 인상을 받았습니다. STL 기능과 글로벌 할당 자의 상환은이 프로젝트의 이데올로기에 대응합니다.
커널 브리지 :
커널 브리지는 Windows 커널 개발을위한 훌륭한 시설을 구현합니다. 라이브러리는 C ++ 객체를 사용하여 Windows 콜백에 등록 할 수있는 포장지를 제공합니다. 이 솔루션을 사용하고 조사하는 데 더 많은 시간을 찾고 싶습니다. CRT 지원을 구현합니다. 구현 된 atexit 기능은 동적이지 않습니다. 정적 배열을 사용하여 슬롯이 부족하면 실패합니다. 기본 new / delete 힘이 NonPagedPool . 전체 예외 지원이 없으므로 CPP 예외가 발생하면 버그 체크가됩니다. 스택에서 객체를 풀지 않습니다.
이 저장소는 일부 기존 작업에서 나옵니다. 저자에게 크레딧.