SkillAgentSearch skills...

Stlkrn

C++ STL in the Windows Kernel with C++ Exception Support

Install / Use

/learn @jxy-s/Stlkrn

README

C++ STL in Windows Drivers

This project uses MSVC C++ STL in a Windows Kernel Driver. In this solution jxystl.lib is implemented as a kernel-tuned, pool type/tag aware, template library and MSVC implementation. Which, under the hood, uses the 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 "\Driver\stlkrn"
   RegistryPath = 0xffffca83`5227f000 "\REGISTRY\MACHINE\SYSTEM\ControlSet001\Services\stlkrn"
     helloWorld = "Hello, World!"

Supported Standards

| Standard | Supported | Notes | | :------: | :-------: | ----- | | cpp11 | No | untested, your milage my vary | | cpp14 | Yes | | | cpp17 | Yes | | | cpp20 | Yes | |

The test driver in this solution, stdtest.sys, houses the unit tests for the project. Unit tests are run in the kernel with driver verifier. The unit test framework is bare bones but is sufficient for exercising jxystl.lib.

Another driver implemented in this solution, stdkrn.sys, puts jxystl.lib to use in a practical scenario. It uses various std namespace facilities and containers (wrapped under the jxy namespace). This driver registers for process, thread, and image notifications; then uses modern C++ to track process contexts, thread contexts, and module contexts.

Exception Handling - vcrtl

Exception handling enables C++ objects to unwind when an exception is thrown. This is a core feature of C++ which gets little attention for kernel drivers. Microsoft does not natively support C++ exceptions for kernel drivers.

C++ exception handling is made possible by [avakar's vcrtl libraray][github.vcrtl]. This project would have been far more work without avakar's awesome contribution. For information on exception handling in Windows Drivers head over to [avakar's vcrtl github][github.vcrtl]. Also, [this page][github.vcrtl.x64] gives excellent details on exception handling on AMD64.

MSVC C++ STL Support - jxystl

Windows Kernel allocations are associated with a memory pool. Further, pool tagging is built into the Windows Kernel. Pool tagging facilitates tracking of allocations made by drivers. This tagging facility enables debugging and monitoring of allocations.

The jxy namespace, in this solution, empowers development of Windows drivers using the std namespace objects with pool typing and tagging.

The library opts not to implement "global" new/delete operators. It implements only new/delete operators with pool typing and tagging capability. This requires specifying pool types and tags. If some functionality is used that would require a "global allocator" it will not link. This is an intentional design decision such that no global allocators are used, all allocations must specify a pool type and tag.

The jxy namespace implements allocators and deleters which conform to the standard for use in template containers. These allocators and deleters are pool type/tag aware. They require specifying the pool type and tag and prevent conversions/rebinding across tool types and tags - they should be used in place of the STL allocators.

jxy::allocator<T, PagedPool, '0GAT'>;
jxy::default_delete<T, PagedPool, '0GAT'>;

jxystl.lib implements necessary "fill" functionality for use of MSVC STL containers. The implementations (in msvcfill.cpp) are considerate to the kernel. This functionality enables the MSVC STL containers to link to kernel-appropriate functionality. This also means that if some std container functionality is used that doesn't have "fill" functionality behind it - the linker will fail. This is an intentional design decision such that any implementations are thought through for use in the kernel.

CRT initialization and atexit functionality is intentionally not supported. Order of CRT initialization is unclear and non-obvious. When a kernel driver loads global data should be clearly setup and torn down during driver load and unload. Global CRT initialization "hides" this initialization in a non-obvious way. Further, CRT atexit functionality is not supported. Emission of necessary synchronization enabling local static initialization of C++ objects is not done by the compiler. And would introduces non-obvious synchronization in the kernel. Lack of CRT initialization and atexit support is an intentional design decision. I strongly recommend avoiding it when developing kernel drivers.

As an example, the jxy namespace "wraps" std::vector and forces use of pool types and tags:

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]

Below is table of functionality under the jxy namespace:

| jxy | STL equivalent | Include | Notes | | ------ | -------------- | ------- | ----- | | 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> | Uses KGUARDED_MUTEX | | jxy::shared_mutex | std::shared_mutex | <jxy/locks.hpp> | Uses EX_PUSH_LOCK | | jxy::unique_lock | std::unique_lock | <jxy/locks.hpp> | | | jxy::shared_lock | std::shared_lock | <jxy/locks.hpp> | | | jxy::scope_resource | None | <jxy/scope.hpp> | Similar to std::experimental::unique_resource | | jxy::scope_exit | None | <jxy/scope.hpp> | Similar to 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> | |

Tests - stltest.sys

The stltest project implements a driver that runs some tests against jxystl, usage of STL, and exceptions in the Windows Kernel.

Practical Usage - stlkrn.sys

The stlkrn project is a Windows Driver that uses jxystl.lib to implement process, thread, and module tracking in the Windows Kernel.

stlkrn.sys registers for process, thread, and image notifications using functionality exported by ntoskrnl. Using these callbacks it tracks processes, threads, and image loads in various objects which use jxy::map, jxy::shared_mutex, jxy::wstring, and more.

The driver has two singletons. jxy::ProcessMap and jxy::ThreadMap, these are constructed when the driver loads (DriverEntry) and torn down when the driver unloads (DriverUnload). It is worth noting here each process tracked in the jxy::ProcessMap (implemented as jxy::ProcessContext) also manages a jxy::ThreadMap. Each "context" (jxy::ProcessContext, jxy::ThreadContext, and jxy::ModuleContext) is a shared (referenced) object (jxy::shared_ptr). Therefore, the thread context that exists in the thread map singleton is the same context associated with the process context.

Key components of stlkrn.sys:

| Object | Purpose | Source | Notes | | ------ | ------- | ------ | ----- | | jxy::ProcessContext | Information for a process running on the system. | process_context.hpp/cpp | Uses jxy::wstring. Has thread (jxy::ThreadMap) and module (jxy::ModuleMap) map members. | | jxy::ThreadContext | Information for a thread running on the system. | thread_context.hpp/cpp | Uses std::atomic. | | jxy::ModuleContext | Information for an image loaded in a given process. | module_context.hpp/cpp | Uses jxy::wstring and jxy::shared_mutex. | | jxy::ProcessMap | Singleton, maps shared jxy::ProcessContext objects to a PID. | process_map.hpp/cpp | Singleton is accessed via jxy::GetProcessMap. Uses jxy::shared_mutex and jxy::map. | | jxy::ThreadMap | Maps shared jxy::ThreadContext objects to a TID. | thread_map.hpp/cpp | The global thread table (singleton) is accessed via jxy::GetThreadMap. Each jxy::ProcessContext also has a thread map which is accessed through jxy::ProcessContext::GetThreads. Uses jxy::shared_mutex and jxy::map. | |

View on GitHub
GitHub Stars434
CategoryCustomer
Updated25d ago
Forks83

Languages

C++

Security Score

100/100

Audited on Mar 3, 2026

No findings