Este projeto usa o MSVC C ++ STL em um driver do kernel do Windows. Nesta solução, jxystl.lib é implementado como um tipo de kernel, tipo de piscina/tag consciente, biblioteca de modelos e implementação do MSVC. Que, sob o capô, usa o 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!"
| Padrão | Suportado | Notas |
|---|---|---|
| CPP11 | Não | não testado, sua milhagem varia |
| CPP14 | Sim | |
| CPP17 | Sim | |
| CPP20 | Sim |
O driver de teste nesta solução, stdtest.sys , abriga os testes de unidade para o projeto. Os testes de unidade são executados no kernel com verificador de motorista. A estrutura de teste de unidade é de ossos nus, mas é suficiente para exercitar jxystl.lib .
Outro motorista implementado nesta solução, stdkrn.sys , coloca jxystl.lib em um cenário prático. Ele usa várias instalações e contêineres de namespace std (embrulhados no espaço para nome jxy ). Esse driver se registra para notificações de processo, encadeamento e imagem; Em seguida, usa C ++ moderno para rastrear contextos de processo, contextos de encadeamento e contextos de módulos.
vcrtlO manuseio de exceção permite que os objetos C ++ se desenrolem quando uma exceção é lançada. Esta é uma característica central do C ++, que recebe pouca atenção para os drivers do kernel. A Microsoft não suporta nativamente as exceções C ++ para os drivers do kernel.
O manuseio de exceção do C ++ é possível pelo VCCRTL Librarray da Avakar. Este projeto teria sido muito mais trabalho sem a incrível contribuição de Avakar. Para obter informações sobre o manuseio de exceções nos drivers do Windows, vá para o VCRTL Github de Avakar. Além disso, esta página fornece excelentes detalhes sobre o manuseio de exceções no AMD64.
jxystlAs alocações do kernel do Windows estão associadas a um pool de memória. Além disso, a marcação de piscina é embutida no kernel do Windows. A etiqueta de piscina facilita o rastreamento de alocações feitas pelos motoristas. Essa instalação de marcação permite a depuração e o monitoramento das alocações.
O espaço para nome jxy , nesta solução, capacita o desenvolvimento de drivers do Windows usando os objetos de namespace std com digitação e marcação de pool.
A biblioteca opta para não implementar operadores new " delete ". Ele implementa apenas os operadores new / delete com a capacidade de digitação e etiqueta do pool. Isso requer especificar tipos e tags do pool. Se for usada alguma funcionalidade que exigiria um "alocador global", ela não será vinculada. Esta é uma decisão de design intencional, de modo que nenhum alocador global é usado, todas as alocações devem especificar um tipo de pool e uma tag.
O namespace jxy implementa alocadores e deleteres que estão em conformidade com o padrão para uso em contêineres de modelo. Esses alocadores e exclusão são do tipo de piscina/tag consciente. Eles exigem especificar o tipo de pool e marcar e impedir conversões/reencontrar os tipos de ferramentas e tags - devem ser usados no lugar dos alocadores do STL.
jxy::allocator<T, PagedPool, ' 0GAT ' >;
jxy::default_delete<T, PagedPool, ' 0GAT ' >; jxystl.lib implementa a funcionalidade "preenchimento" necessária para o uso de contêineres MSVC STL. As implementações (no msvcfill.cpp ) são atenciosas com o kernel. Essa funcionalidade permite que os contêineres MSVC STL se vinculem à funcionalidade apropriada para o kernel. Isso também significa que, se alguma funcionalidade de contêiner std for usada, que não possui funcionalidade "preencher" por trás disso - o vinculador falhará. Esta é uma decisão de design intencional, de modo que quaisquer implementações são pensadas para uso no kernel.
A inicialização do CRT e a funcionalidade ATEXIT não são intencionalmente suportadas. A ordem da inicialização do CRT não é clara e não óbvia. Quando um driver do kernel carrega dados globais deve ser claramente configurado e destruído durante a carga e descarregamento do driver. A inicialização global do CRT "oculta" essa inicialização de uma maneira não óbvia. Além disso, a funcionalidade CRT ATEXIT não é suportada. A emissão de sincronização necessária que permite a inicialização estática local de objetos C ++ não é feita pelo compilador. E introduziria a sincronização não óbvia no kernel. A falta de inicialização do CRT e suporte do Atexit é uma decisão de design intencional. Eu recomendo fortemente evitá -lo ao desenvolver motoristas de kernel.
Como exemplo, o namespace jxy "envolve" std::vector e força o uso de tipos e tags de piscina:
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]
Abaixo está a tabela de funcionalidade no espaço de nome jxy :
| jxy | STL equivalente | Incluir | Notas |
|---|---|---|---|
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> | Usa KGUARDED_MUTEX |
jxy::shared_mutex | std::shared_mutex | <jxy/locks.hpp> | Usa EX_PUSH_LOCK |
jxy::unique_lock | std::unique_lock | <jxy/locks.hpp> | |
jxy::shared_lock | std::shared_lock | <jxy/locks.hpp> | |
jxy::scope_resource | Nenhum | <jxy/scope.hpp> | Semelhante a std::experimental::unique_resource |
jxy::scope_exit | Nenhum | <jxy/scope.hpp> | Semelhante ao 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 O projeto stltest implementa um driver que executa alguns testes contra o JXYSTL, o uso do STL e as exceções no kernel do Windows.
stlkrn.sys O projeto stlkrn é um driver do Windows que usa jxystl.lib para implementar o rastreamento de processo, thread e módulo no kernel do Windows.
stlkrn.sys Registre -se para notificações de processo, encadeamento e imagem usando funcionalidade exportada pelo ntoskrnl . Usando esses retornos de chamada, ele rastreia processos, threads e cargas de imagem em vários objetos que usam jxy::map , jxy::shared_mutex , jxy::wstring e muito mais.
O motorista tem dois singletons. jxy::ProcessMap e jxy::ThreadMap , eles são construídos quando o driver carrega ( DriverEntry ) e rasgado quando o motorista descarrega ( DriverUnload ). Vale a pena notar aqui cada processo rastreado no jxy::ProcessMap (implementado como jxy::ProcessContext ) também gerencia um jxy::ThreadMap . Cada "contexto" ( jxy::ProcessContext , jxy::ThreadContext e jxy::ModuleContext ) é um objeto compartilhado (referenciado) ( jxy::shared_ptr ). Portanto, o contexto do encadeamento que existe no mapa do thread Singleton é o mesmo contexto associado ao contexto do processo.
Principais componentes de stlkrn.sys :
| Objeto | Propósito | Fonte | Notas |
|---|---|---|---|
jxy::ProcessContext | Informações para um processo em execução no sistema. | process_context.hpp/cpp | Usa jxy::wstring . Tem thread ( jxy::ThreadMap ) e módulo ( jxy::ModuleMap ) membros do mapa. |
jxy::ThreadContext | Informações para um tópico em execução no sistema. | thread_context.hpp/cpp | Usa std::atomic . |
jxy::ModuleContext | Informações para uma imagem carregada em um determinado processo. | module_context.hpp/cpp | Usa jxy::wstring e jxy::shared_mutex . |
jxy::ProcessMap | Singleton, Maps compartilhados jxy::ProcessContext objetos para um PID. | process_map.hpp/cpp | Singleton é acessado via jxy::GetProcessMap . Usa jxy::shared_mutex e jxy::map . |
jxy::ThreadMap | Mapas compartilhados jxy::ThreadContext objetos em um TID. | thread_map.hpp/cpp | A tabela Global Thread (Singleton) é acessada via jxy::GetThreadMap . Cada jxy::ProcessContext também possui um mapa de threads que é acessado através jxy::ProcessContext::GetThreads . Usa jxy::shared_mutex e jxy::map . |
jxy::GetModuleMap | Mapas compartilhados jxy::ModuleContext para uma extensão de imagem carregada (endereço base e final). | module_map.hpp/cpp | Cada contexto do processo possui um membro do mapa do módulo. As imagens carregadas para um determinado processo são rastreadas usando este objeto. Usa jxy::shared_mutex e jxy::map |
std::unordered_map teria sido uma escolha melhor sobre a árvore ordenada ( std::map :) para os mapas de objetos. Há uma razão pela qual isso não é usado (consulte a seção 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]
Eu queria incluir std::unordered_map inicialmente, no entanto, ele usa ceilf . A aritmética do ponto flutuante no kernel do Windows vem com alguns desafios. Portanto, por enquanto é omitido até que uma solução apropriada seja projetada.
Esta solução é um projeto de paixão. Neste momento, não se destina ao código de produção. x64 é bem testado e estável, stlkrn.sys passa opções completas de verificador de driver (incluindo simulação randomizada de baixo recurso). O manuseio de exceções no despacho ou acima foi testado, mas não em casos de uso prático. x86 não foi testado. Há funcionalidade no espaço de nome jxy que é incompleto/não utilizado/não testado. Sua milhagem pode variar - gostaria de continuar esse trabalho ao longo do tempo, se algum problema for encontrado, sinta -se à vontade para abrir problemas contra este repositório.
Este projeto fornece suporte STL no kernel do Windows usando o máximo possível da instalação STL. Existem outras soluções para o uso do STL no desenvolvimento do kernel. Esta seção delineará alternativas, primeiro resumirei este trabalho:
Este projeto:
new ou delete global é implementado.atexit . A ordem de inicialização do CRT não é óbvia, a inicialização do motorista e a desmontagem devem ser óbvias . A funcionalidade atexit pode introduzir corridas de dados para o código do kernel, atexit não é implementado.Bareflank Hypervisor:
Bareflank implementa o suporte para a execução do C ++ em seu hipervisor. Eles têm suporte completo de STL e CRT. Este é um projeto abrangente que permite uma infinidade de recursos do padrão no modo kernel (incluindo exceções). Pelo que entendi, a solução deles força NonPagedPool em alocações globais new / delete . Eu tenho que elogiar o Bareflank com a implementação deles, é bem pensado e cruzado. No entanto, a implementação do Windows aumenta através do Cygwin e "Shims" no suporte ao kernel do Windows. Em comparação, este projeto pretende ser atencioso com o kernel do Windows. Ele permite especificar tags e tipos de pool (paginado versus não-gagado) e espera minimizar as "bordas nítidas" associadas ao uso de C ++ e o STL no modo de kernel. Tudo isso dito, o Bareflank é impressionante para o que é. Para uma excelente apresentação sobre o apoio do Bareflank ao C ++, recomendo assistir à apresentação do Dr. Rian Quinn no CPPCON 2016.
Win32kernelStl:
O projeto Win32KernelStl permite que você use a funcionalidade STL diretamente no kernel. O projeto implementa o new / delete e force NonPagedPool , implementa o suporte de inicialização do CRT e bugchecks quando uma exceção do CPP é lançada. Não faz nenhuma tentativa de fazer a exceção do CPP. Devido às suposições, a considero pouco prática para qualquer caso de uso sério. O código é razoavelmente claro e documentado, recomendo dar a este projeto uma navegação para educar o suporte ao C ++ no kernel. Uma nota, o código do CRT no Win32KernelStl implementa atexit , mas lembre -se de que não há sincronização emitida pelo compilador aqui (em oposição ao modo de usuário). Portanto, uma estática local que exige a inserção de uma entrada na lista atexit pode correr, causando uma entrada dupla ou sem dupla.
Driver Plus Plus:
Este projeto implementa a instalação C ++ necessária para atrair várias soluções C ++ no modo de kernel ( EASTL , msgpack , etc.). Driver Plus implementa a inicialização do CRT e o suporte global new / delete (que força NonPagedPool ). Novamente, isso é contra os objetivos deste projeto. No entanto, este projeto permite muitas ótimas instalações C ++ para uso no modo de kernel. Ele faz modificações nas soluções C ++ que ele atrai para o Shim em suporte para seus casos de uso. O Driver Plus também faz com que a suposição em torno atexit como mencionado anteriormente.
KTL - DYMOK93
KTL (biblioteca de modelos do Windows Kernel por Dymok93) reimplementa uma boa quantidade de C ++ moderno e está desenvolvendo ativamente mais suporte para uso no kernel do Windows. Ele também possui suporte para RAII em muitas primitivas do kernel, fornece suporte nativo ANSI_STRING e UNICODE_STRING , fornece alguns invólucros úteis para registrar retornos de chamada do kernel e mais recursos de conveniência ao redor do kernel do Windows. Ele implementa a Global new / delete e possui uma definição de pré-processador ( KTL_USING_NON_PAGED_NEW_AS_DEFAULT ) para alternar entre o Padrado Padrado ou o Pagado, o que é bom. No entanto, ele usa uma única tag de pool ( KTL_HEAP_TAG ). Além disso, os modelos de alocadores existentes não permitem que um desenvolvedor especifique uma tag de pool; portanto, usar essa biblioteca como é faz com que todas as alocações sejam marcadas com a mesma etiqueta do pool. Dito isto, seria razoável implementar um alocador personalizado que capacite a marcação de alocações. A biblioteca possui suporte de exceção, embora apenas x64. O suporte de exceção no KTL é baseado no Avakar's com aprimoramentos e correções. Recomendo o trabalho aqui e estou impressionado com a quantidade de instalações que existe, é razoavelmente embalada e sob desenvolvimento ativo. Gostaria de explorar usá -lo mais no futuro e potencialmente colaborar com um melhor suporte de exceção para stlkrn e KTL . A reimplementação da funcionalidade STL, a falta de suporte de marcação de pool nativo e os alocadores globais são contrários às ideologias deste projeto.
KTL - Meesong:
KTL (biblioteca de modelos do Windows Kernel por Meesong) reimplementa uma boa quantidade de funcionalidade moderna de C ++ para uso no kernel do Windows. Ele também implementa new / delete globais, mas faz um trabalho decente no fornecimento de instalações para especificar tags e tipos de pool sempre que possível. No entanto, isso significa que o alocador global pode ocultar uma alocação em um pool não óbvio. Além disso, os alocadores de modelo neste projeto carregam o custo de dois pontos para um objeto de alocador e desalocador, também estou preocupado com o fato de a conversão entre os tipos de alocador pode permitir aloces/aloces de tags/tags cruzados. No geral, estou impressionado com a quantidade de instalação que é implementada aqui. A reimplementação da funcionalidade STL e dos alocadores globais são contrários às ideologias deste projeto.
Kernel-Bridge:
O Kernel-Bridge implementa algumas ótimas instalações para o desenvolvimento do kernel do Windows. A biblioteca fornece invólucros para se registrar para retornos de chamada do Windows usando objetos C ++. Eu gostaria de encontrar mais tempo para usar e investigar esta solução. Ele implementa suporte ao CRT. A funcionalidade atexit implementada não é dinâmica - ele usa uma matriz estática, se ficar sem slots, falhará. O new / delete forças de NonPagedPool . Ele não possui suporte total à exceção, se uma exceção de bugs se uma exceção do CPP for lançada - não desenrolará os objetos na pilha.
Este repositório se baseia em algum trabalho preexistente. Créditos para seus autores.