Recently i was looking for another approaches how to use c++ features in kernel mode drivers. I found some references, but no one will fullfill my needs & desires to use also boost & std (at least partially).
Some time ago my friend show me a way how to add mentioned libraries to kernel code, so i decided to do it from scratch, do some minimalistic approach with some kind of ‘manual’ and PoC, and maybe it can be for someone, except myself, usefull.
- Part I. – force c++ to cooperate
Firstly, it is no big deal to use c++ in kernel mode. For example, some of references that i was able to found :
What is needed, is well known, and it is to provide minimalistic c++ enviroment, including :
- malloc
- free
- realloc
- operator new
- operator delete
it is easy doable :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
EXTERN_C __drv_when(return!=0, __drv_allocatesMem(pBlock)) __checkReturn __drv_maxIRQL(DISPATCH_LEVEL) __bcount_opt(size) void* __cdecl malloc( __in size_t size ) { MEMBLOCK *pBlock = static_cast<MEMBLOCK*>( ExAllocatePoolWithTag( NonPagedPoolNxCacheAligned, size + sizeof(MEMBLOCK), _LIBC_POOL_TAG)); if (nullptr == pBlock) return nullptr; pBlock->size = size; return pBlock->data; } EXTERN_C __drv_maxIRQL(DISPATCH_LEVEL) void __cdecl free( __inout_opt __drv_freesMem(Mem) void* ptr ) { if (ptr) ExFreePoolWithTag(CONTAINING_RECORD(ptr, MEMBLOCK, data), _LIBC_POOL_TAG); } __drv_when(return!=0, __drv_allocatesMem(ptr)) __checkReturn __drv_maxIRQL(DISPATCH_LEVEL) __bcount_opt(size) void* __cdecl operator new( __in size_t size ) { return malloc(size); } __drv_maxIRQL(DISPATCH_LEVEL) void __cdecl operator delete( __inout void* ptr ) { if (ptr) free(ptr); } |
another essential think, is to add implementation of calling ctors & dtors, at loading / unloading drivers (allow creating singletons, …) :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
EXTERN_C int __cdecl atexit( __in void(__cdecl *destructor)(void) ) { if (!destructor) return 0; //_kebreak(); ATEXIT_ENTRY* entry = new ATEXIT_ENTRY(destructor, g_pTopAtexitEntry); if (!entry) return 0; g_pTopAtexitEntry = entry; return 1; } #if defined(_IA64_) || defined(_AMD64_) #pragma section(".CRT$XCA",long,read) __declspec(allocate(".CRT$XCA")) void(*__ctors_begin__[1])(void) = { 0 }; #pragma section(".CRT$XCZ",long,read) __declspec(allocate(".CRT$XCZ")) void(*__ctors_end__[1])(void) = { 0 }; #pragma data_seg() #else #pragma data_seg(".CRT$XCA") void(*__ctors_begin__[1])(void) = { 0 }; #pragma data_seg(".CRT$XCZ") void(*__ctors_end__[1])(void) = { 0 }; #pragma data_seg() #endif #pragma data_seg(".STL$A") void(*___StlStartInitCalls__[1])(void) = { 0 }; #pragma data_seg(".STL$L") void(*___StlEndInitCalls__[1])(void) = { 0 }; #pragma data_seg(".STL$M") void(*___StlStartTerminateCalls__[1])(void) = { 0 }; #pragma data_seg(".STL$Z") void(*___StlEndTerminateCalls__[1])(void) = { 0 }; #pragma data_seg() EXTERN_C void __cdecl doexit( __in int /*code*/, __in int /*quick*/, __in int /*retcaller*/ ) { for (ATEXIT_ENTRY* entry = g_pTopAtexitEntry; entry;) { ATEXIT_ENTRY* next = entry->Next; delete entry; entry = next; } } EXTERN_C int __cdecl _cinit( __in int ) { for (void(**ctor)(void) = __ctors_begin__ + 1; ctor < __ctors_end__; ctor++) { (*ctor)(); } return 0; } |
Ok, now it is good enough to use c++ in Kernel mode, and also usage of nice feature of std::unique_ptr. But when you attempt to use some boost::intrusive or std::shared_ptr you may encounter some probems! And this problemes are c++ E X C E P T I O N S , on code project resides very good article for reading about c++ exceptions vs kernel.
problem makers (vs 2013, c++ 11) :
- _CxxThrowException
- _wassert
- __CxxFrameHandler3
- some std friends (_Xbad_alloc, _Xlength_error, _Xout_of_range, _Syserror_map, _Winerror_map)
so for first two, to avoid redefinition, is dummy solution in .asm :
1 2 3 4 5 6 7 8 9 10 11 12 13 |
.code _CxxThrowException proc int 3 ret _CxxThrowException endp _wassert proc xor rax, rax ret _wassert endp end |
for others can it be done in similliar dummy way but in c++ :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
EXTERN_C _CRTIMP EXCEPTION_DISPOSITION __CxxFrameHandler3( __in void* pExcept, // Information for this exception __in ULONG_PTR RN, // Dynamic information for this frame __in void* pContext, // Context info __in void* pDC // More dynamic info for this frame ) { DbgBreakPoint(); return EXCEPTION_DISPOSITION::ExceptionNestedException; } namespace std { void __cdecl _Xbad_alloc() { DbgBreakPoint(); } ... void __cdecl _Xout_of_range( __in char const* ) { DbgBreakPoint(); } ... char const* __cdecl _Winerror_map( __in int ) { DbgBreakPoint(); return nullptr; } } |
But remember, those are really dumb implementations, wich allows you to use c++ and some of nice c++ libraries (like std, boost), but you probably want to implement this methods correctly
Alltogether you can find .lib source codes (projects, etc. … vs2013) on my github
- Part II. – Set up enviroment to cooperate (Visual Studio 2013 & WDM driver)
Things what we need to do :
- link libc.lib to project
- add additional .lib to project (to cover f.e. functions like _hypot, .. )
- Set code generation, entry point, runtime .. c++ and linker options mainly
so lets finalize it :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
linker/input add dependecies : %(AdditionalDependencies);$(KernelBufferOverflowLib);$(DDK_LIB_PATH)\ntoskrnl.lib;$(DDK_LIB_PATH)\hal.lib;$(DDK_LIB_PATH)\wmilib.lib; ifnore all def libs : Yes (/NODEFAULTLIB) igonere specific lib : libcmt.lib linker/advanced EP : DriverEntry Rnd Base : Yes (/DYNAMICBASE) DEP : Yes (/NXCOMPAT) linker/cmd linde /INTEGRITYCHECK c++ General : $(SolutionDir);$(SolutionDir)/Common/;$(IntDir);$(VCInstallDir)/Include;%(AdditionalIncludeDirectories); Code generation: c++ exceptions : No runtime library: Multi-threaded (/MT) security checks : Enable Security Check (/GS) language : enable run-time type info : No (/GR-) precompiled header (these is just optional ;) : Create (/Yc) drv_common.h advanced (same, optinal) calling convention : __fastcall (/Gr) all options aditional options : clear this field and remove - "/kernel %(ClCompile.AdditionalOptions)" Driver signing : Sign Mode : Test Sign Test Certificate : create test certificate |
in libc.git project, i use $(DDK_LIB_PATH)\libcntpr.lib; for including some additional functions (_hypot and friends…). And in next version of vs can be needed also another functions, and for this you should firstly find in .lib in $(DDK_LIB_PATH) and try to link it to project, or after that if nothing was found implement it by yourself …
But important here, is to avoid linking usermode .dll into your kernel mode driver
I also pushed on github KernelProject PoC of usage of libc.git and new WDM driver for visual studio 2013 – (shared_ptr, unique_ptr, boost::intrusive::avltree). (i used also Common repo because of demonstrating on Vads, and usage of CCppDriver class as “main” of driver) – Windows 8.1 Release is setup-ed configuration – platform, so you can try to set-up Windows 7.1 release for sucessfully running also Vad test (because in undoc is setuped constans just for win7sp1 and win7, both x64)
Well done, I tried doing this before. And wanted to implement a base library base on COM likes. Whant I recommend is how about all class inherit base class, which contain object reference. In future, we can implement ourself GC