[
  {
    "path": "Hunt-Weird-Syscalls/Detectors.cpp",
    "content": "#include \"Detectors.h\"\n\nnamespace Detectors {\n\n\tstd::vector<PCSTR> allowedSyscallmodules = { \n\t\t\"ntdll.dll\", \n\t\t\"win32u.dll\", \n\t\t\"wow64win.dll\" \n\t};\n\n\tPCSTR SyscallAllowOpenThread = \"NtOpenThread\";\n\tPCSTR SyscallAllowSetThreadContext = \"NtSetContextThread\";\n\n\tVOID DirectSyscall ( DWORD pid, HANDLE hProcess, std::vector<ULONG_PTR> stack ) {\n\n\t\tstd::string lastModule;\n\t\tBOOL bSuccess = FALSE;\n\n\t\tbSuccess = Helpers::ModuleNameFromAddress ( hProcess, ( PVOID ) stack.front ( ), lastModule );\n\t\tif ( bSuccess == FALSE )\n\t\t\treturn;\n\n\t\tfor ( auto it = allowedSyscallmodules.begin ( ); it != allowedSyscallmodules.end ( ); ++it ) {\n\t\t\tif ( !_stricmp ( *it, lastModule.c_str ( ) ) )\n\t\t\t\treturn;\n\t\t}\n\n\t\tprintf ( \"! Direct Syscall detected from process: %d\\n\", pid );\n\t\tprintf ( \"\\t Syscall from: 0x%p (%s)\\n\", ( PVOID ) stack.front ( ), lastModule.c_str ( ) );\n\n\t}\n\n\tVOID InDirectSyscall ( DWORD pid, HANDLE hProcess, std::vector<ULONG_PTR> stack, PCSTR allowedSyscall ) {\n\n\t\tMEMORY_BASIC_INFORMATION mbi = { 0 };\n\n\t\tstd::string lastModule;\n\t\tHMODULE hNtdll = NULL;\n\n\t\tBOOL bSuccess = FALSE;\n\t\tSIZE_T s = 0;\n\t\tULONG_PTR offsetIs = 0, offsetExpected = 0;\n\t\tPVOID returnExpected = NULL;\n\n\t\tbSuccess = Helpers::ModuleNameFromAddress ( hProcess, ( PVOID ) stack.front ( ), lastModule );\n\t\tif ( bSuccess == FALSE )\n\t\t\treturn;\n\n\t\tif ( _strcmpi ( lastModule.c_str ( ), \"ntdll.dll\"))\n\t\t\treturn; // Currently only verifying ntdll.dll syscalls\n\n\t\ts = VirtualQueryEx ( hProcess, ( LPCVOID ) stack.front ( ), &mbi, sizeof ( MEMORY_BASIC_INFORMATION ) );\n\t\tif ( s == 0 )\n\t\t\treturn;\n\n\t\toffsetIs = stack.front ( ) - ( ULONG_PTR ) mbi.BaseAddress;\n\t\t\n\t\thNtdll = GetModuleHandleA ( \"ntdll.dll\" );\n\n\t\treturnExpected = GetProcAddress ( hNtdll, allowedSyscall);\n\t\ts = VirtualQuery ( returnExpected, &mbi, sizeof ( MEMORY_BASIC_INFORMATION ) );\n\t\tif (s == 0) {\n\t\t\treturn;\n\t\t}\n\n\t\toffsetExpected = ( ( PBYTE ) returnExpected - ( PBYTE ) mbi.BaseAddress );\n\t\tif ( offsetExpected < offsetIs && offsetIs <=  offsetExpected + 23 ) {\n\t\t\treturn;\n\t\t}\n\t\t\n\t\tprintf ( \"! Indirect Syscall detected from process: %d\\n\", pid );\n\t\tprintf ( \"\\t Syscall %s expected from: stub at 0x%p but was: 0x%llx\\n\", allowedSyscall, returnExpected, stack.front ( ) );\n\n\t}\n\n}"
  },
  {
    "path": "Hunt-Weird-Syscalls/Detectors.h",
    "content": "#pragma once\n\n#include \"windows.h\"\n#include <vector>\n\n#include \"Helpers.h\"\n\nnamespace Detectors {\n\n\textern std::vector<PCSTR> SyscallsAllowOpenProcess;\n\textern PCSTR SyscallAllowOpenThread;\n\textern PCSTR SyscallAllowSetThreadContext;\n\n\tVOID DirectSyscall ( DWORD pid, HANDLE hProcess, std::vector<ULONG_PTR> stack );\n\tVOID InDirectSyscall ( DWORD pid, HANDLE hProcess, std::vector<ULONG_PTR> stack, PCSTR );\n\n}"
  },
  {
    "path": "Hunt-Weird-Syscalls/Helpers.cpp",
    "content": "#include \"Helpers.h\"\n\nnamespace Helpers {\n\n\tBOOL ModuleNameFromAddress ( HANDLE hProcess, PVOID pAddr, std::string& moduleName ) {\n\n\t\tBOOL bSuccess = FALSE;\n\t\tSIZE_T s = 0;\n\t\tMEMORY_BASIC_INFORMATION mbi = { 0 };\n\t\tCHAR cmoduleName [ MAX_PATH ] = { 0 };\n\n\t\ts = VirtualQueryEx ( hProcess, ( LPCVOID ) pAddr, &mbi, sizeof ( MEMORY_BASIC_INFORMATION ) );\n\t\tif ( s == 0 )\n\t\t\tgoto Cleanup;\n\n\t\tbSuccess = K32GetModuleBaseNameA ( hProcess, ( HMODULE ) mbi.AllocationBase, ( LPSTR ) cmoduleName, MAX_PATH );\n\t\tif ( bSuccess == FALSE )\n\t\t\tgoto Cleanup;\n\n\t\tmoduleName = std::string ( cmoduleName );\n\n\t\tbSuccess = TRUE;\n\n\tCleanup:\n\n\t\treturn bSuccess;\n\n\t}\n\n\tVOID RemoveKernelAddrs ( std::vector<ULONG_PTR>& stack ) {\n\n\t\tauto it = stack.begin ( );\n\t\twhile ( it != stack.end ( ) ) {\n\n\t\t\tULONG_PTR addr = *it;\n\t\t\tif ( addr > 0xFFFF000000000000 ) {\n\t\t\t\tit = stack.erase ( it );\n\t\t\t}\n\t\t\telse {\n\t\t\t\t++it;\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\t//https://github.com/outflanknl/Dumpert/blob/master/Dumpert/Outflank-Dumpert/Dumpert.c Is Elevated() was taken from here :).\n\tBOOL IsElevated ( VOID ) {\n\t\tBOOL fRet = FALSE;\n\t\tHANDLE hToken = NULL;\n\t\tif ( OpenProcessToken ( GetCurrentProcess ( ), TOKEN_QUERY, &hToken ) ) {\n\t\t\tTOKEN_ELEVATION Elevation = { 0 };\n\t\t\tDWORD cbSize = sizeof ( TOKEN_ELEVATION );\n\t\t\tif ( GetTokenInformation ( hToken, TokenElevation, &Elevation, sizeof ( Elevation ), &cbSize ) ) {\n\t\t\t\tfRet = Elevation.TokenIsElevated;\n\t\t\t}\n\t\t}\n\t\tif ( hToken ) {\n\t\t\tCloseHandle ( hToken );\n\t\t}\n\t\treturn fRet;\n\t}\n\n\n}"
  },
  {
    "path": "Hunt-Weird-Syscalls/Helpers.h",
    "content": "#pragma once\n#include \"windows.h\"\n#include \"psapi.h\"\n\n#include <string>\n#include <vector>\n\nnamespace Helpers {\n\tVOID RemoveKernelAddrs ( std::vector<ULONG_PTR>& );\n\tBOOL ModuleNameFromAddress ( HANDLE, PVOID, std::string& );\n\tBOOL IsElevated ( VOID );\n}"
  },
  {
    "path": "Hunt-Weird-Syscalls/Hunt-Weird-Syscalls.sln",
    "content": "﻿\nMicrosoft Visual Studio Solution File, Format Version 12.00\n# Visual Studio Version 16\nVisualStudioVersion = 16.0.32413.511\nMinimumVisualStudioVersion = 10.0.40219.1\nProject(\"{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}\") = \"Hunt-Weird-Syscalls\", \"Hunt-Weird-Syscalls.vcxproj\", \"{B1446E95-C861-4AD9-8A49-D18B97C3108C}\"\nEndProject\nProject(\"{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}\") = \"SetThreadContext_Direct\", \"SetThreadContext_Direct\\SetThreadContext_Direct.vcxproj\", \"{C49CD6BF-0525-4270-BEF7-7DEC29630191}\"\nEndProject\nProject(\"{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}\") = \"SetThreadContext_Indirect\", \"SetThreadContext_Indirect\\SetThreadContext_Indirect.vcxproj\", \"{77C9B594-A2E6-40AB-BA92-E23FC1EAE781}\"\nEndProject\nGlobal\n\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\n\t\tDebug|x64 = Debug|x64\n\t\tDebug|x86 = Debug|x86\n\t\tRelease|x64 = Release|x64\n\t\tRelease|x86 = Release|x86\n\tEndGlobalSection\n\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\n\t\t{B1446E95-C861-4AD9-8A49-D18B97C3108C}.Debug|x64.ActiveCfg = Debug|x64\n\t\t{B1446E95-C861-4AD9-8A49-D18B97C3108C}.Debug|x64.Build.0 = Debug|x64\n\t\t{B1446E95-C861-4AD9-8A49-D18B97C3108C}.Debug|x86.ActiveCfg = Debug|Win32\n\t\t{B1446E95-C861-4AD9-8A49-D18B97C3108C}.Debug|x86.Build.0 = Debug|Win32\n\t\t{B1446E95-C861-4AD9-8A49-D18B97C3108C}.Release|x64.ActiveCfg = Release|x64\n\t\t{B1446E95-C861-4AD9-8A49-D18B97C3108C}.Release|x64.Build.0 = Release|x64\n\t\t{B1446E95-C861-4AD9-8A49-D18B97C3108C}.Release|x86.ActiveCfg = Release|Win32\n\t\t{B1446E95-C861-4AD9-8A49-D18B97C3108C}.Release|x86.Build.0 = Release|Win32\n\t\t{C49CD6BF-0525-4270-BEF7-7DEC29630191}.Debug|x64.ActiveCfg = Debug|x64\n\t\t{C49CD6BF-0525-4270-BEF7-7DEC29630191}.Debug|x64.Build.0 = Debug|x64\n\t\t{C49CD6BF-0525-4270-BEF7-7DEC29630191}.Debug|x86.ActiveCfg = Debug|Win32\n\t\t{C49CD6BF-0525-4270-BEF7-7DEC29630191}.Debug|x86.Build.0 = Debug|Win32\n\t\t{C49CD6BF-0525-4270-BEF7-7DEC29630191}.Release|x64.ActiveCfg = Release|x64\n\t\t{C49CD6BF-0525-4270-BEF7-7DEC29630191}.Release|x64.Build.0 = Release|x64\n\t\t{C49CD6BF-0525-4270-BEF7-7DEC29630191}.Release|x86.ActiveCfg = Release|Win32\n\t\t{C49CD6BF-0525-4270-BEF7-7DEC29630191}.Release|x86.Build.0 = Release|Win32\n\t\t{77C9B594-A2E6-40AB-BA92-E23FC1EAE781}.Debug|x64.ActiveCfg = Debug|x64\n\t\t{77C9B594-A2E6-40AB-BA92-E23FC1EAE781}.Debug|x64.Build.0 = Debug|x64\n\t\t{77C9B594-A2E6-40AB-BA92-E23FC1EAE781}.Debug|x86.ActiveCfg = Debug|Win32\n\t\t{77C9B594-A2E6-40AB-BA92-E23FC1EAE781}.Debug|x86.Build.0 = Debug|Win32\n\t\t{77C9B594-A2E6-40AB-BA92-E23FC1EAE781}.Release|x64.ActiveCfg = Release|x64\n\t\t{77C9B594-A2E6-40AB-BA92-E23FC1EAE781}.Release|x64.Build.0 = Release|x64\n\t\t{77C9B594-A2E6-40AB-BA92-E23FC1EAE781}.Release|x86.ActiveCfg = Release|Win32\n\t\t{77C9B594-A2E6-40AB-BA92-E23FC1EAE781}.Release|x86.Build.0 = Release|Win32\n\tEndGlobalSection\n\tGlobalSection(SolutionProperties) = preSolution\n\t\tHideSolutionNode = FALSE\n\tEndGlobalSection\n\tGlobalSection(ExtensibilityGlobals) = postSolution\n\t\tSolutionGuid = {D52D4FC8-D03B-44EE-95E7-EEF1749CA236}\n\tEndGlobalSection\nEndGlobal\n"
  },
  {
    "path": "Hunt-Weird-Syscalls/Hunt-Weird-Syscalls.vcxproj",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project DefaultTargets=\"Build\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <ItemGroup Label=\"ProjectConfigurations\">\n    <ProjectConfiguration Include=\"Debug|Win32\">\n      <Configuration>Debug</Configuration>\n      <Platform>Win32</Platform>\n    </ProjectConfiguration>\n    <ProjectConfiguration Include=\"Release|Win32\">\n      <Configuration>Release</Configuration>\n      <Platform>Win32</Platform>\n    </ProjectConfiguration>\n    <ProjectConfiguration Include=\"Debug|x64\">\n      <Configuration>Debug</Configuration>\n      <Platform>x64</Platform>\n    </ProjectConfiguration>\n    <ProjectConfiguration Include=\"Release|x64\">\n      <Configuration>Release</Configuration>\n      <Platform>x64</Platform>\n    </ProjectConfiguration>\n  </ItemGroup>\n  <PropertyGroup Label=\"Globals\">\n    <VCProjectVersion>16.0</VCProjectVersion>\n    <Keyword>Win32Proj</Keyword>\n    <ProjectGuid>{b1446e95-c861-4ad9-8a49-d18b97c3108c}</ProjectGuid>\n    <RootNamespace>HuntWeirdSyscalls</RootNamespace>\n    <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>\n  </PropertyGroup>\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.Default.props\" />\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\" Label=\"Configuration\">\n    <ConfigurationType>Application</ConfigurationType>\n    <UseDebugLibraries>true</UseDebugLibraries>\n    <PlatformToolset>v143</PlatformToolset>\n    <CharacterSet>Unicode</CharacterSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\" Label=\"Configuration\">\n    <ConfigurationType>Application</ConfigurationType>\n    <UseDebugLibraries>false</UseDebugLibraries>\n    <PlatformToolset>v143</PlatformToolset>\n    <WholeProgramOptimization>true</WholeProgramOptimization>\n    <CharacterSet>Unicode</CharacterSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\" Label=\"Configuration\">\n    <ConfigurationType>Application</ConfigurationType>\n    <UseDebugLibraries>true</UseDebugLibraries>\n    <PlatformToolset>v142</PlatformToolset>\n    <CharacterSet>Unicode</CharacterSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\" Label=\"Configuration\">\n    <ConfigurationType>Application</ConfigurationType>\n    <UseDebugLibraries>false</UseDebugLibraries>\n    <PlatformToolset>v142</PlatformToolset>\n    <WholeProgramOptimization>true</WholeProgramOptimization>\n    <CharacterSet>Unicode</CharacterSet>\n  </PropertyGroup>\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.props\" />\n  <ImportGroup Label=\"ExtensionSettings\">\n  </ImportGroup>\n  <ImportGroup Label=\"Shared\">\n  </ImportGroup>\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <PropertyGroup Label=\"UserMacros\" />\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\n    <LinkIncremental>true</LinkIncremental>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\n    <LinkIncremental>false</LinkIncremental>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\n    <LinkIncremental>true</LinkIncremental>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\n    <LinkIncremental>false</LinkIncremental>\n  </PropertyGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\n    <ClCompile>\n      <WarningLevel>Level3</WarningLevel>\n      <SDLCheck>true</SDLCheck>\n      <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <ConformanceMode>true</ConformanceMode>\n    </ClCompile>\n    <Link>\n      <SubSystem>Console</SubSystem>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n    </Link>\n  </ItemDefinitionGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\n    <ClCompile>\n      <WarningLevel>Level3</WarningLevel>\n      <FunctionLevelLinking>true</FunctionLevelLinking>\n      <IntrinsicFunctions>true</IntrinsicFunctions>\n      <SDLCheck>true</SDLCheck>\n      <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <ConformanceMode>true</ConformanceMode>\n    </ClCompile>\n    <Link>\n      <SubSystem>Console</SubSystem>\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\n      <OptimizeReferences>true</OptimizeReferences>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n    </Link>\n  </ItemDefinitionGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\n    <ClCompile>\n      <WarningLevel>Level3</WarningLevel>\n      <SDLCheck>true</SDLCheck>\n      <PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <ConformanceMode>true</ConformanceMode>\n    </ClCompile>\n    <Link>\n      <SubSystem>Console</SubSystem>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n    </Link>\n  </ItemDefinitionGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\n    <ClCompile>\n      <WarningLevel>Level3</WarningLevel>\n      <FunctionLevelLinking>true</FunctionLevelLinking>\n      <IntrinsicFunctions>true</IntrinsicFunctions>\n      <SDLCheck>true</SDLCheck>\n      <PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <ConformanceMode>true</ConformanceMode>\n    </ClCompile>\n    <Link>\n      <SubSystem>Console</SubSystem>\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\n      <OptimizeReferences>true</OptimizeReferences>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n    </Link>\n  </ItemDefinitionGroup>\n  <ItemGroup>\n    <ClCompile Include=\"Detectors.cpp\" />\n    <ClCompile Include=\"Helpers.cpp\" />\n    <ClCompile Include=\"Main.cpp\" />\n  </ItemGroup>\n  <ItemGroup>\n    <ClInclude Include=\"Detectors.h\" />\n    <ClInclude Include=\"Helpers.h\" />\n  </ItemGroup>\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.targets\" />\n  <ImportGroup Label=\"ExtensionTargets\">\n  </ImportGroup>\n</Project>"
  },
  {
    "path": "Hunt-Weird-Syscalls/Hunt-Weird-Syscalls.vcxproj.filters",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project ToolsVersion=\"4.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <ItemGroup>\n    <Filter Include=\"Source Files\">\n      <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>\n      <Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>\n    </Filter>\n    <Filter Include=\"Header Files\">\n      <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>\n      <Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>\n    </Filter>\n    <Filter Include=\"Resource Files\">\n      <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>\n      <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>\n    </Filter>\n  </ItemGroup>\n  <ItemGroup>\n    <ClCompile Include=\"Main.cpp\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"Helpers.cpp\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"Detectors.cpp\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n  </ItemGroup>\n  <ItemGroup>\n    <ClInclude Include=\"Helpers.h\">\n      <Filter>Header Files</Filter>\n    </ClInclude>\n    <ClInclude Include=\"Detectors.h\">\n      <Filter>Header Files</Filter>\n    </ClInclude>\n  </ItemGroup>\n</Project>"
  },
  {
    "path": "Hunt-Weird-Syscalls/Hunt-Weird-Syscalls.vcxproj.user",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project ToolsVersion=\"Current\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <PropertyGroup />\n</Project>"
  },
  {
    "path": "Hunt-Weird-Syscalls/Main.cpp",
    "content": "#include \"../libs/krabs/krabs.hpp\"\n\n#include \"Detectors.h\"\n#include \"Helpers.h\"\n\n#define EVENTID_SETTHTREADCONTEXT 4\n#define EVENTID_OPENTHREAD 6\n\nVOID EnableAuditApiTracing ( krabs::user_trace& );\nVOID OnObservableSyscall ( const EVENT_RECORD&, const krabs::trace_context& );\n\nVOID EnableAuditApiTracing ( krabs::user_trace& userTrace ) {\n\n\tkrabs::provider<>* providerApiTracing = new krabs::provider<> ( L\"Microsoft-Windows-Kernel-Audit-API-Calls\" );\n\n\tproviderApiTracing->trace_flags ( providerApiTracing->trace_flags ( ) | EVENT_ENABLE_PROPERTY_STACK_TRACE );\n\n\tkrabs::event_filter* filterOpenThread = new krabs::event_filter ( krabs::predicates::id_is ( EVENTID_OPENTHREAD ) ); // OpenThread\n\tkrabs::event_filter* filterSetContextThread = new krabs::event_filter ( krabs::predicates::id_is ( EVENTID_SETTHTREADCONTEXT ) ); // OpenThread\n\n\t/* For now no distinction between events */\n\tfilterOpenThread->add_on_event_callback ( OnObservableSyscall );\n\tfilterSetContextThread->add_on_event_callback ( OnObservableSyscall );\n\n\tproviderApiTracing->add_filter ( *filterSetContextThread );\n\tproviderApiTracing->add_filter ( *filterOpenThread );\n\n\tuserTrace.enable ( *providerApiTracing );\n\n}\n\nVOID OnObservableSyscall ( const EVENT_RECORD& record, const krabs::trace_context& trace_context ) {\n\n\tBOOL bSuccess = FALSE;\n\tHANDLE hProcess = NULL;\n\tDWORD pid = 0;\n\n\tkrabs::schema schema ( record, trace_context.schema_locator );\n\tkrabs::parser parser ( schema );\n\tstd::vector<ULONG_PTR> stack;\n\n\tpid = record.EventHeader.ProcessId;\n\tstack = schema.stack_trace ( );\n\n\tif ( pid == GetCurrentProcessId ( ) )\n\t\treturn;\n\n\tHelpers::RemoveKernelAddrs ( stack );\n\tif ( stack.size ( ) == 0 )\n\t\treturn;\n\n\thProcess = OpenProcess ( PROCESS_ALL_ACCESS, FALSE, pid );\n\tif ( hProcess == NULL )\n\t\treturn;\n\n\tDetectors::DirectSyscall ( pid, hProcess, stack );\n\n\tif ( record.EventHeader.EventDescriptor.Id == EVENTID_OPENTHREAD )\n\t\tDetectors::InDirectSyscall ( pid, hProcess, stack, Detectors::SyscallAllowOpenThread);\n\telse if ( record.EventHeader.EventDescriptor.Id == EVENTID_SETTHTREADCONTEXT )\n\t\tDetectors::InDirectSyscall ( pid, hProcess, stack, Detectors::SyscallAllowSetThreadContext );\n\nCleanup:\n\n\tif ( hProcess )\n\t\tCloseHandle ( hProcess );\n\n}\n\nVOID Go ( krabs::user_trace* userTrace ) {\n\tuserTrace->start ( );\n}\n\nint main ( int argc, char** argv ) {\n\n\tHANDLE traceThread = NULL;\n\n\tkrabs::user_trace userTrace ( L\"Hunt-Weird-Syscalls\" );\n\n\tif ( !Helpers::IsElevated ( ) ) {\n\t\tprintf ( \"- Not elevated\\n\" );\n\t\treturn 0;\n\t}\n\n\tprintf ( \"* Enabling trace, might take a bit ... \\n\" );\n\n\tEnableAuditApiTracing ( userTrace );\n\ttraceThread = CreateThread ( NULL, 0, ( LPTHREAD_START_ROUTINE ) Go, &userTrace, 0, NULL );\n\tif ( traceThread == NULL )\n\t\treturn 0; // o.0\n\n\tprintf ( \"* Started monitoring, press any key to exit ... \\n\" );\n\n\tgetchar ( );\n\tprintf ( \"* exiting ... \\n\" );\n\tuserTrace.stop ( );\n\tWaitForSingleObject ( traceThread, INFINITE );\n\n\treturn 0;\n\n}"
  },
  {
    "path": "Hunt-Weird-Syscalls/SetThreadContext_Direct/Main.c",
    "content": "#include \"windows.h\"\n#include \"threadcontext_embedded.h\"\n\nint main(int argc, char** argv) {\n\n\tNtSetContextThread(-1, NULL);\n\n\tgetchar();\n\n\treturn 0;\n}"
  },
  {
    "path": "Hunt-Weird-Syscalls/SetThreadContext_Direct/SetThreadContext_Direct.vcxproj",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project DefaultTargets=\"Build\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <ItemGroup Label=\"ProjectConfigurations\">\n    <ProjectConfiguration Include=\"Debug|Win32\">\n      <Configuration>Debug</Configuration>\n      <Platform>Win32</Platform>\n    </ProjectConfiguration>\n    <ProjectConfiguration Include=\"Release|Win32\">\n      <Configuration>Release</Configuration>\n      <Platform>Win32</Platform>\n    </ProjectConfiguration>\n    <ProjectConfiguration Include=\"Debug|x64\">\n      <Configuration>Debug</Configuration>\n      <Platform>x64</Platform>\n    </ProjectConfiguration>\n    <ProjectConfiguration Include=\"Release|x64\">\n      <Configuration>Release</Configuration>\n      <Platform>x64</Platform>\n    </ProjectConfiguration>\n  </ItemGroup>\n  <PropertyGroup Label=\"Globals\">\n    <VCProjectVersion>16.0</VCProjectVersion>\n    <Keyword>Win32Proj</Keyword>\n    <ProjectGuid>{c49cd6bf-0525-4270-bef7-7dec29630191}</ProjectGuid>\n    <RootNamespace>SetThreadContextDirect</RootNamespace>\n    <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>\n  </PropertyGroup>\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.Default.props\" />\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\" Label=\"Configuration\">\n    <ConfigurationType>Application</ConfigurationType>\n    <UseDebugLibraries>true</UseDebugLibraries>\n    <PlatformToolset>v143</PlatformToolset>\n    <CharacterSet>Unicode</CharacterSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\" Label=\"Configuration\">\n    <ConfigurationType>Application</ConfigurationType>\n    <UseDebugLibraries>false</UseDebugLibraries>\n    <PlatformToolset>v143</PlatformToolset>\n    <WholeProgramOptimization>true</WholeProgramOptimization>\n    <CharacterSet>Unicode</CharacterSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\" Label=\"Configuration\">\n    <ConfigurationType>Application</ConfigurationType>\n    <UseDebugLibraries>true</UseDebugLibraries>\n    <PlatformToolset>v142</PlatformToolset>\n    <CharacterSet>Unicode</CharacterSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\" Label=\"Configuration\">\n    <ConfigurationType>Application</ConfigurationType>\n    <UseDebugLibraries>false</UseDebugLibraries>\n    <PlatformToolset>v142</PlatformToolset>\n    <WholeProgramOptimization>true</WholeProgramOptimization>\n    <CharacterSet>Unicode</CharacterSet>\n  </PropertyGroup>\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.props\" />\n  <ImportGroup Label=\"ExtensionSettings\">\n    <Import Project=\"$(VCTargetsPath)\\BuildCustomizations\\masm.props\" />\n  </ImportGroup>\n  <ImportGroup Label=\"Shared\">\n  </ImportGroup>\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <PropertyGroup Label=\"UserMacros\" />\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\n    <LinkIncremental>true</LinkIncremental>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\n    <LinkIncremental>false</LinkIncremental>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\n    <LinkIncremental>true</LinkIncremental>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\n    <LinkIncremental>false</LinkIncremental>\n  </PropertyGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\n    <ClCompile>\n      <WarningLevel>Level3</WarningLevel>\n      <SDLCheck>true</SDLCheck>\n      <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <ConformanceMode>true</ConformanceMode>\n    </ClCompile>\n    <Link>\n      <SubSystem>Console</SubSystem>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n    </Link>\n  </ItemDefinitionGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\n    <ClCompile>\n      <WarningLevel>Level3</WarningLevel>\n      <FunctionLevelLinking>true</FunctionLevelLinking>\n      <IntrinsicFunctions>true</IntrinsicFunctions>\n      <SDLCheck>true</SDLCheck>\n      <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <ConformanceMode>true</ConformanceMode>\n    </ClCompile>\n    <Link>\n      <SubSystem>Console</SubSystem>\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\n      <OptimizeReferences>true</OptimizeReferences>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n    </Link>\n  </ItemDefinitionGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\n    <ClCompile>\n      <WarningLevel>Level3</WarningLevel>\n      <SDLCheck>true</SDLCheck>\n      <PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <ConformanceMode>true</ConformanceMode>\n    </ClCompile>\n    <Link>\n      <SubSystem>Console</SubSystem>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n    </Link>\n  </ItemDefinitionGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\n    <ClCompile>\n      <WarningLevel>Level3</WarningLevel>\n      <FunctionLevelLinking>true</FunctionLevelLinking>\n      <IntrinsicFunctions>true</IntrinsicFunctions>\n      <SDLCheck>true</SDLCheck>\n      <PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <ConformanceMode>true</ConformanceMode>\n    </ClCompile>\n    <Link>\n      <SubSystem>Console</SubSystem>\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\n      <OptimizeReferences>true</OptimizeReferences>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n    </Link>\n  </ItemDefinitionGroup>\n  <ItemGroup>\n    <ClInclude Include=\"threadcontext_embedded.h\" />\n  </ItemGroup>\n  <ItemGroup>\n    <ClCompile Include=\"Main.c\" />\n    <ClCompile Include=\"threadcontext_embedded.c\" />\n  </ItemGroup>\n  <ItemGroup>\n    <MASM Include=\"threadcontext_embedded_-asm.x64.asm\">\n      <FileType>Document</FileType>\n    </MASM>\n  </ItemGroup>\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.targets\" />\n  <ImportGroup Label=\"ExtensionTargets\">\n    <Import Project=\"$(VCTargetsPath)\\BuildCustomizations\\masm.targets\" />\n  </ImportGroup>\n</Project>"
  },
  {
    "path": "Hunt-Weird-Syscalls/SetThreadContext_Direct/SetThreadContext_Direct.vcxproj.filters",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project ToolsVersion=\"4.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <ItemGroup>\n    <Filter Include=\"Source Files\">\n      <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>\n      <Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>\n    </Filter>\n    <Filter Include=\"Header Files\">\n      <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>\n      <Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>\n    </Filter>\n    <Filter Include=\"Resource Files\">\n      <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>\n      <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>\n    </Filter>\n  </ItemGroup>\n  <ItemGroup>\n    <ClInclude Include=\"threadcontext_embedded.h\">\n      <Filter>Header Files</Filter>\n    </ClInclude>\n  </ItemGroup>\n  <ItemGroup>\n    <ClCompile Include=\"threadcontext_embedded.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"Main.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n  </ItemGroup>\n  <ItemGroup>\n    <MASM Include=\"threadcontext_embedded_-asm.x64.asm\">\n      <Filter>Source Files</Filter>\n    </MASM>\n  </ItemGroup>\n</Project>"
  },
  {
    "path": "Hunt-Weird-Syscalls/SetThreadContext_Direct/SetThreadContext_Direct.vcxproj.user",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project ToolsVersion=\"Current\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <PropertyGroup />\n</Project>"
  },
  {
    "path": "Hunt-Weird-Syscalls/SetThreadContext_Direct/threadcontext_embedded.c",
    "content": "#include \"threadcontext_embedded.h\"\n#include <stdio.h>\n\n//#define DEBUG\n\n// JUMPER\n\n#ifdef _M_IX86\n\nEXTERN_C PVOID internal_cleancall_wow64_gate(VOID) {\n    return (PVOID)__readfsdword(0xC0);\n}\n\n__declspec(naked) BOOL local_is_wow64(void)\n{\n    __asm {\n        mov eax, fs:[0xc0]\n        test eax, eax\n        jne wow64\n        mov eax, 0\n        ret\n        wow64:\n        mov eax, 1\n        ret\n    }\n}\n\n\n#endif\n\n// Code below is adapted from @modexpblog. Read linked article for more details.\n// https://www.mdsec.co.uk/2020/12/bypassing-user-mode-hooks-and-direct-invocation-of-system-calls-for-red-teams\n\nSW3_SYSCALL_LIST SW3_SyscallList;\n\n// SEARCH_AND_REPLACE\n#ifdef SEARCH_AND_REPLACE\n// THIS IS NOT DEFINED HERE; don't know if I'll add it in a future release\nEXTERN void SearchAndReplace(unsigned char[], unsigned char[]);\n#endif\n\nDWORD SW3_HashSyscall(PCSTR FunctionName)\n{\n    DWORD i = 0;\n    DWORD Hash = SW3_SEED;\n\n    while (FunctionName[i])\n    {\n        WORD PartialName = *(WORD*)((ULONG_PTR)FunctionName + i++);\n        Hash ^= PartialName + SW3_ROR8(Hash);\n    }\n\n    return Hash;\n}\n\n#ifndef JUMPER\nPVOID SC_Address(PVOID NtApiAddress)\n{\n    return NULL;\n}\n#else\nPVOID SC_Address(PVOID NtApiAddress)\n{\n    DWORD searchLimit = 512;\n    PVOID SyscallAddress;\n\n   #ifdef _WIN64\n    // If the process is 64-bit on a 64-bit OS, we need to search for syscall\n    BYTE syscall_code[] = { 0x0f, 0x05, 0xc3 };\n    ULONG distance_to_syscall = 0x12;\n   #else\n    // If the process is 32-bit on a 32-bit OS, we need to search for sysenter\n    BYTE syscall_code[] = { 0x0f, 0x34, 0xc3 };\n    ULONG distance_to_syscall = 0x0f;\n   #endif\n\n  #ifdef _M_IX86\n    // If the process is 32-bit on a 64-bit OS, we need to jump to WOW32Reserved\n    if (local_is_wow64())\n    {\n    #ifdef DEBUG\n        printf(\"[+] Running 32-bit app on x64 (WOW64)\\n\");\n    #endif\n        return NULL;\n    }\n  #endif\n\n    // we don't really care if there is a 'jmp' between\n    // NtApiAddress and the 'syscall; ret' instructions\n    SyscallAddress = SW3_RVA2VA(PVOID, NtApiAddress, distance_to_syscall);\n\n    if (!memcmp((PVOID)syscall_code, SyscallAddress, sizeof(syscall_code)))\n    {\n        // we can use the original code for this system call :)\n        #if defined(DEBUG)\n            printf(\"Found Syscall Opcodes at address 0x%p\\n\", SyscallAddress);\n        #endif\n        return SyscallAddress;\n    }\n\n    // the 'syscall; ret' intructions have not been found,\n    // we will try to use one near it, similarly to HalosGate\n\n    for (ULONG32 num_jumps = 1; num_jumps < searchLimit; num_jumps++)\n    {\n        // let's try with an Nt* API below our syscall\n        SyscallAddress = SW3_RVA2VA(\n            PVOID,\n            NtApiAddress,\n            distance_to_syscall + num_jumps * 0x20);\n        if (!memcmp((PVOID)syscall_code, SyscallAddress, sizeof(syscall_code)))\n        {\n        #if defined(DEBUG)\n            printf(\"Found Syscall Opcodes at address 0x%p\\n\", SyscallAddress);\n        #endif\n            return SyscallAddress;\n        }\n\n        // let's try with an Nt* API above our syscall\n        SyscallAddress = SW3_RVA2VA(\n            PVOID,\n            NtApiAddress,\n            distance_to_syscall - num_jumps * 0x20);\n        if (!memcmp((PVOID)syscall_code, SyscallAddress, sizeof(syscall_code)))\n        {\n        #if defined(DEBUG)\n            printf(\"Found Syscall Opcodes at address 0x%p\\n\", SyscallAddress);\n        #endif\n            return SyscallAddress;\n        }\n    }\n\n#ifdef DEBUG\n    printf(\"Syscall Opcodes not found!\\n\");\n#endif\n\n    return NULL;\n}\n#endif\n\n\nBOOL SW3_PopulateSyscallList()\n{\n    // Return early if the list is already populated.\n    if (SW3_SyscallList.Count) return TRUE;\n\n    #ifdef _WIN64\n    PSW3_PEB Peb = (PSW3_PEB)__readgsqword(0x60);\n    #else\n    PSW3_PEB Peb = (PSW3_PEB)__readfsdword(0x30);\n    #endif\n    PSW3_PEB_LDR_DATA Ldr = Peb->Ldr;\n    PIMAGE_EXPORT_DIRECTORY ExportDirectory = NULL;\n    PVOID DllBase = NULL;\n\n    // Get the DllBase address of NTDLL.dll. NTDLL is not guaranteed to be the second\n    // in the list, so it's safer to loop through the full list and find it.\n    PSW3_LDR_DATA_TABLE_ENTRY LdrEntry;\n    for (LdrEntry = (PSW3_LDR_DATA_TABLE_ENTRY)Ldr->Reserved2[1]; LdrEntry->DllBase != NULL; LdrEntry = (PSW3_LDR_DATA_TABLE_ENTRY)LdrEntry->Reserved1[0])\n    {\n        DllBase = LdrEntry->DllBase;\n        PIMAGE_DOS_HEADER DosHeader = (PIMAGE_DOS_HEADER)DllBase;\n        PIMAGE_NT_HEADERS NtHeaders = SW3_RVA2VA(PIMAGE_NT_HEADERS, DllBase, DosHeader->e_lfanew);\n        PIMAGE_DATA_DIRECTORY DataDirectory = (PIMAGE_DATA_DIRECTORY)NtHeaders->OptionalHeader.DataDirectory;\n        DWORD VirtualAddress = DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;\n        if (VirtualAddress == 0) continue;\n\n        ExportDirectory = (PIMAGE_EXPORT_DIRECTORY)SW3_RVA2VA(ULONG_PTR, DllBase, VirtualAddress);\n\n        // If this is NTDLL.dll, exit loop.\n        PCHAR DllName = SW3_RVA2VA(PCHAR, DllBase, ExportDirectory->Name);\n\n        if ((*(ULONG*)DllName | 0x20202020) != 0x6c64746e) continue;\n        if ((*(ULONG*)(DllName + 4) | 0x20202020) == 0x6c642e6c) break;\n    }\n\n    if (!ExportDirectory) return FALSE;\n\n    DWORD NumberOfNames = ExportDirectory->NumberOfNames;\n    PDWORD Functions = SW3_RVA2VA(PDWORD, DllBase, ExportDirectory->AddressOfFunctions);\n    PDWORD Names = SW3_RVA2VA(PDWORD, DllBase, ExportDirectory->AddressOfNames);\n    PWORD Ordinals = SW3_RVA2VA(PWORD, DllBase, ExportDirectory->AddressOfNameOrdinals);\n\n    // Populate SW3_SyscallList with unsorted Zw* entries.\n    DWORD i = 0;\n    PSW3_SYSCALL_ENTRY Entries = SW3_SyscallList.Entries;\n    do\n    {\n        PCHAR FunctionName = SW3_RVA2VA(PCHAR, DllBase, Names[NumberOfNames - 1]);\n\n        // Is this a system call?\n        if (*(USHORT*)FunctionName == 0x775a)\n        {\n            Entries[i].Hash = SW3_HashSyscall(FunctionName);\n            Entries[i].Address = Functions[Ordinals[NumberOfNames - 1]];\n            Entries[i].SyscallAddress = SC_Address(SW3_RVA2VA(PVOID, DllBase, Entries[i].Address));\n\n            i++;\n            if (i == SW3_MAX_ENTRIES) break;\n        }\n    } while (--NumberOfNames);\n\n    // Save total number of system calls found.\n    SW3_SyscallList.Count = i;\n\n    // Sort the list by address in ascending order.\n    for (DWORD i = 0; i < SW3_SyscallList.Count - 1; i++)\n    {\n        for (DWORD j = 0; j < SW3_SyscallList.Count - i - 1; j++)\n        {\n            if (Entries[j].Address > Entries[j + 1].Address)\n            {\n                // Swap entries.\n                SW3_SYSCALL_ENTRY TempEntry;\n\n                TempEntry.Hash = Entries[j].Hash;\n                TempEntry.Address = Entries[j].Address;\n                TempEntry.SyscallAddress = Entries[j].SyscallAddress;\n\n                Entries[j].Hash = Entries[j + 1].Hash;\n                Entries[j].Address = Entries[j + 1].Address;\n                Entries[j].SyscallAddress = Entries[j + 1].SyscallAddress;\n\n                Entries[j + 1].Hash = TempEntry.Hash;\n                Entries[j + 1].Address = TempEntry.Address;\n                Entries[j + 1].SyscallAddress = TempEntry.SyscallAddress;\n            }\n        }\n    }\n\n    return TRUE;\n}\n\nEXTERN_C DWORD SW3_GetSyscallNumber(DWORD FunctionHash)\n{\n    // Ensure SW3_SyscallList is populated.\n    if (!SW3_PopulateSyscallList()) return -1;\n\n    for (DWORD i = 0; i < SW3_SyscallList.Count; i++)\n    {\n        if (FunctionHash == SW3_SyscallList.Entries[i].Hash)\n        {\n            return i;\n        }\n    }\n\n    return -1;\n}\n\nEXTERN_C PVOID SW3_GetSyscallAddress(DWORD FunctionHash)\n{\n    // Ensure SW3_SyscallList is populated.\n    if (!SW3_PopulateSyscallList()) return NULL;\n\n    for (DWORD i = 0; i < SW3_SyscallList.Count; i++)\n    {\n        if (FunctionHash == SW3_SyscallList.Entries[i].Hash)\n        {\n            return SW3_SyscallList.Entries[i].SyscallAddress;\n        }\n    }\n\n    return NULL;\n}\n\nEXTERN_C PVOID SW3_GetRandomSyscallAddress(DWORD FunctionHash)\n{\n    // Ensure SW3_SyscallList is populated.\n    if (!SW3_PopulateSyscallList()) return NULL;\n\n    DWORD index = ((DWORD) rand()) % SW3_SyscallList.Count;\n\n    while (FunctionHash == SW3_SyscallList.Entries[index].Hash){\n        // Spoofing the syscall return address\n        index = ((DWORD) rand()) % SW3_SyscallList.Count;\n    }\n    return SW3_SyscallList.Entries[index].SyscallAddress;\n}\n"
  },
  {
    "path": "Hunt-Weird-Syscalls/SetThreadContext_Direct/threadcontext_embedded.h",
    "content": "#pragma once\n\n// Code below is adapted from @modexpblog. Read linked article for more details.\n// https://www.mdsec.co.uk/2020/12/bypassing-user-mode-hooks-and-direct-invocation-of-system-calls-for-red-teams\n\n#ifndef SW3_HEADER_H_\n#define SW3_HEADER_H_\n\n#include <windows.h>\n\n#define SW3_SEED 0x3A397AC2\n#define SW3_ROL8(v) (v << 8 | v >> 24)\n#define SW3_ROR8(v) (v >> 8 | v << 24)\n#define SW3_ROX8(v) ((SW3_SEED % 2) ? SW3_ROL8(v) : SW3_ROR8(v))\n#define SW3_MAX_ENTRIES 500\n#define SW3_RVA2VA(Type, DllBase, Rva) (Type)((ULONG_PTR) DllBase + Rva)\n\n// Typedefs are prefixed to avoid pollution.\n\ntypedef struct _SW3_SYSCALL_ENTRY\n{\n    DWORD Hash;\n    DWORD Address;\n\tPVOID SyscallAddress;\n} SW3_SYSCALL_ENTRY, *PSW3_SYSCALL_ENTRY;\n\ntypedef struct _SW3_SYSCALL_LIST\n{\n    DWORD Count;\n    SW3_SYSCALL_ENTRY Entries[SW3_MAX_ENTRIES];\n} SW3_SYSCALL_LIST, *PSW3_SYSCALL_LIST;\n\ntypedef struct _SW3_PEB_LDR_DATA {\n\tBYTE Reserved1[8];\n\tPVOID Reserved2[3];\n\tLIST_ENTRY InMemoryOrderModuleList;\n} SW3_PEB_LDR_DATA, *PSW3_PEB_LDR_DATA;\n\ntypedef struct _SW3_LDR_DATA_TABLE_ENTRY {\n\tPVOID Reserved1[2];\n\tLIST_ENTRY InMemoryOrderLinks;\n\tPVOID Reserved2[2];\n\tPVOID DllBase;\n} SW3_LDR_DATA_TABLE_ENTRY, *PSW3_LDR_DATA_TABLE_ENTRY;\n\ntypedef struct _SW3_PEB {\n\tBYTE Reserved1[2];\n\tBYTE BeingDebugged;\n\tBYTE Reserved2[1];\n\tPVOID Reserved3[2];\n\tPSW3_PEB_LDR_DATA Ldr;\n} SW3_PEB, *PSW3_PEB;\n\nDWORD SW3_HashSyscall(PCSTR FunctionName);\nBOOL SW3_PopulateSyscallList();\nEXTERN_C DWORD SW3_GetSyscallNumber(DWORD FunctionHash);\nEXTERN_C PVOID SW3_GetSyscallAddress(DWORD FunctionHash);\nEXTERN_C PVOID internal_cleancall_wow64_gate(VOID);\nEXTERN_C NTSTATUS NtSetContextThread(\n\tIN HANDLE ThreadHandle,\n\tIN PCONTEXT Context);\n\n#endif\n"
  },
  {
    "path": "Hunt-Weird-Syscalls/SetThreadContext_Direct/threadcontext_embedded_-asm.x64.asm",
    "content": ".code\n\nEXTERN SW3_GetSyscallNumber: PROC\n\nNtSetContextThread PROC\n\tmov [rsp +8], rcx          ; Save registers.\n\tmov [rsp+16], rdx\n\tmov [rsp+24], r8\n\tmov [rsp+32], r9\n\tsub rsp, 28h\n\tmov ecx, 0FA5F256Dh        ; Load function hash into ECX.\n\tcall SW3_GetSyscallNumber              ; Resolve function hash into syscall number.\n\tadd rsp, 28h\n\tmov rcx, [rsp+8]                      ; Restore registers.\n\tmov rdx, [rsp+16]\n\tmov r8, [rsp+24]\n\tmov r9, [rsp+32]\n\tmov r10, rcx\n\tsyscall                    ; Invoke system call.\n\tret\nNtSetContextThread ENDP\n\nend"
  },
  {
    "path": "Hunt-Weird-Syscalls/SetThreadContext_Indirect/Main.c",
    "content": "#include \"windows.h\"\n#include \"threadcontext_jumper_randomized.h\"\n\nint main(int argc, char** argv) {\n\n\tNtSetContextThread(-1, NULL);\n\n\tgetchar();\n\n\treturn 0;\n}"
  },
  {
    "path": "Hunt-Weird-Syscalls/SetThreadContext_Indirect/SetThreadContext_Indirect.vcxproj",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project DefaultTargets=\"Build\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <ItemGroup Label=\"ProjectConfigurations\">\n    <ProjectConfiguration Include=\"Debug|Win32\">\n      <Configuration>Debug</Configuration>\n      <Platform>Win32</Platform>\n    </ProjectConfiguration>\n    <ProjectConfiguration Include=\"Release|Win32\">\n      <Configuration>Release</Configuration>\n      <Platform>Win32</Platform>\n    </ProjectConfiguration>\n    <ProjectConfiguration Include=\"Debug|x64\">\n      <Configuration>Debug</Configuration>\n      <Platform>x64</Platform>\n    </ProjectConfiguration>\n    <ProjectConfiguration Include=\"Release|x64\">\n      <Configuration>Release</Configuration>\n      <Platform>x64</Platform>\n    </ProjectConfiguration>\n  </ItemGroup>\n  <PropertyGroup Label=\"Globals\">\n    <VCProjectVersion>16.0</VCProjectVersion>\n    <Keyword>Win32Proj</Keyword>\n    <ProjectGuid>{77c9b594-a2e6-40ab-ba92-e23fc1eae781}</ProjectGuid>\n    <RootNamespace>SetThreadContextIndirect</RootNamespace>\n    <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>\n  </PropertyGroup>\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.Default.props\" />\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\" Label=\"Configuration\">\n    <ConfigurationType>Application</ConfigurationType>\n    <UseDebugLibraries>true</UseDebugLibraries>\n    <PlatformToolset>v143</PlatformToolset>\n    <CharacterSet>Unicode</CharacterSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\" Label=\"Configuration\">\n    <ConfigurationType>Application</ConfigurationType>\n    <UseDebugLibraries>false</UseDebugLibraries>\n    <PlatformToolset>v143</PlatformToolset>\n    <WholeProgramOptimization>true</WholeProgramOptimization>\n    <CharacterSet>Unicode</CharacterSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\" Label=\"Configuration\">\n    <ConfigurationType>Application</ConfigurationType>\n    <UseDebugLibraries>true</UseDebugLibraries>\n    <PlatformToolset>v142</PlatformToolset>\n    <CharacterSet>Unicode</CharacterSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\" Label=\"Configuration\">\n    <ConfigurationType>Application</ConfigurationType>\n    <UseDebugLibraries>false</UseDebugLibraries>\n    <PlatformToolset>v142</PlatformToolset>\n    <WholeProgramOptimization>true</WholeProgramOptimization>\n    <CharacterSet>Unicode</CharacterSet>\n  </PropertyGroup>\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.props\" />\n  <ImportGroup Label=\"ExtensionSettings\">\n    <Import Project=\"$(VCTargetsPath)\\BuildCustomizations\\masm.props\" />\n  </ImportGroup>\n  <ImportGroup Label=\"Shared\">\n  </ImportGroup>\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <PropertyGroup Label=\"UserMacros\" />\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\n    <LinkIncremental>true</LinkIncremental>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\n    <LinkIncremental>false</LinkIncremental>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\n    <LinkIncremental>true</LinkIncremental>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\n    <LinkIncremental>false</LinkIncremental>\n  </PropertyGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\n    <ClCompile>\n      <WarningLevel>Level3</WarningLevel>\n      <SDLCheck>true</SDLCheck>\n      <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <ConformanceMode>true</ConformanceMode>\n    </ClCompile>\n    <Link>\n      <SubSystem>Console</SubSystem>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n    </Link>\n  </ItemDefinitionGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\n    <ClCompile>\n      <WarningLevel>Level3</WarningLevel>\n      <FunctionLevelLinking>true</FunctionLevelLinking>\n      <IntrinsicFunctions>true</IntrinsicFunctions>\n      <SDLCheck>true</SDLCheck>\n      <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <ConformanceMode>true</ConformanceMode>\n    </ClCompile>\n    <Link>\n      <SubSystem>Console</SubSystem>\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\n      <OptimizeReferences>true</OptimizeReferences>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n    </Link>\n  </ItemDefinitionGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\n    <ClCompile>\n      <WarningLevel>Level3</WarningLevel>\n      <SDLCheck>true</SDLCheck>\n      <PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <ConformanceMode>true</ConformanceMode>\n    </ClCompile>\n    <Link>\n      <SubSystem>Console</SubSystem>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n    </Link>\n  </ItemDefinitionGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\n    <ClCompile>\n      <WarningLevel>Level3</WarningLevel>\n      <FunctionLevelLinking>true</FunctionLevelLinking>\n      <IntrinsicFunctions>true</IntrinsicFunctions>\n      <SDLCheck>true</SDLCheck>\n      <PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <ConformanceMode>true</ConformanceMode>\n    </ClCompile>\n    <Link>\n      <SubSystem>Console</SubSystem>\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\n      <OptimizeReferences>true</OptimizeReferences>\n      <GenerateDebugInformation>true</GenerateDebugInformation>\n    </Link>\n  </ItemDefinitionGroup>\n  <ItemGroup>\n    <ClInclude Include=\"threadcontext_jumper_randomized.h\" />\n  </ItemGroup>\n  <ItemGroup>\n    <ClCompile Include=\"Main.c\" />\n    <ClCompile Include=\"threadcontext_jumper_randomized.c\" />\n  </ItemGroup>\n  <ItemGroup>\n    <MASM Include=\"threadcontext_jumper_randomized_-asm.x64.asm\">\n      <FileType>Document</FileType>\n    </MASM>\n  </ItemGroup>\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.targets\" />\n  <ImportGroup Label=\"ExtensionTargets\">\n    <Import Project=\"$(VCTargetsPath)\\BuildCustomizations\\masm.targets\" />\n  </ImportGroup>\n</Project>"
  },
  {
    "path": "Hunt-Weird-Syscalls/SetThreadContext_Indirect/SetThreadContext_Indirect.vcxproj.filters",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project ToolsVersion=\"4.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <ItemGroup>\n    <Filter Include=\"Source Files\">\n      <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>\n      <Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>\n    </Filter>\n    <Filter Include=\"Header Files\">\n      <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>\n      <Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>\n    </Filter>\n    <Filter Include=\"Resource Files\">\n      <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>\n      <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>\n    </Filter>\n  </ItemGroup>\n  <ItemGroup>\n    <ClInclude Include=\"threadcontext_jumper_randomized.h\">\n      <Filter>Header Files</Filter>\n    </ClInclude>\n  </ItemGroup>\n  <ItemGroup>\n    <ClCompile Include=\"threadcontext_jumper_randomized.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"Main.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n  </ItemGroup>\n  <ItemGroup>\n    <MASM Include=\"threadcontext_jumper_randomized_-asm.x64.asm\">\n      <Filter>Source Files</Filter>\n    </MASM>\n  </ItemGroup>\n</Project>"
  },
  {
    "path": "Hunt-Weird-Syscalls/SetThreadContext_Indirect/SetThreadContext_Indirect.vcxproj.user",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project ToolsVersion=\"Current\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <PropertyGroup />\n</Project>"
  },
  {
    "path": "Hunt-Weird-Syscalls/SetThreadContext_Indirect/threadcontext_jumper_randomized.c",
    "content": "#include \"threadcontext_jumper_randomized.h\"\n#include <stdio.h>\n\n//#define DEBUG\n\n#define JUMPER\n\n#ifdef _M_IX86\n\nEXTERN_C PVOID internal_cleancall_wow64_gate(VOID) {\n    return (PVOID)__readfsdword(0xC0);\n}\n\n__declspec(naked) BOOL local_is_wow64(void)\n{\n    __asm {\n        mov eax, fs:[0xc0]\n        test eax, eax\n        jne wow64\n        mov eax, 0\n        ret\n        wow64:\n        mov eax, 1\n        ret\n    }\n}\n\n\n#endif\n\n// Code below is adapted from @modexpblog. Read linked article for more details.\n// https://www.mdsec.co.uk/2020/12/bypassing-user-mode-hooks-and-direct-invocation-of-system-calls-for-red-teams\n\nSW3_SYSCALL_LIST SW3_SyscallList;\n\n// SEARCH_AND_REPLACE\n#ifdef SEARCH_AND_REPLACE\n// THIS IS NOT DEFINED HERE; don't know if I'll add it in a future release\nEXTERN void SearchAndReplace(unsigned char[], unsigned char[]);\n#endif\n\nDWORD SW3_HashSyscall(PCSTR FunctionName)\n{\n    DWORD i = 0;\n    DWORD Hash = SW3_SEED;\n\n    while (FunctionName[i])\n    {\n        WORD PartialName = *(WORD*)((ULONG_PTR)FunctionName + i++);\n        Hash ^= PartialName + SW3_ROR8(Hash);\n    }\n\n    return Hash;\n}\n\n#ifndef JUMPER\nPVOID SC_Address(PVOID NtApiAddress)\n{\n    return NULL;\n}\n#else\nPVOID SC_Address(PVOID NtApiAddress)\n{\n    DWORD searchLimit = 512;\n    PVOID SyscallAddress;\n\n   #ifdef _WIN64\n    // If the process is 64-bit on a 64-bit OS, we need to search for syscall\n    BYTE syscall_code[] = { 0x0f, 0x05, 0xc3 };\n    ULONG distance_to_syscall = 0x12;\n   #else\n    // If the process is 32-bit on a 32-bit OS, we need to search for sysenter\n    BYTE syscall_code[] = { 0x0f, 0x34, 0xc3 };\n    ULONG distance_to_syscall = 0x0f;\n   #endif\n\n  #ifdef _M_IX86\n    // If the process is 32-bit on a 64-bit OS, we need to jump to WOW32Reserved\n    if (local_is_wow64())\n    {\n    #ifdef DEBUG\n        printf(\"[+] Running 32-bit app on x64 (WOW64)\\n\");\n    #endif\n        return NULL;\n    }\n  #endif\n\n    // we don't really care if there is a 'jmp' between\n    // NtApiAddress and the 'syscall; ret' instructions\n    SyscallAddress = SW3_RVA2VA(PVOID, NtApiAddress, distance_to_syscall);\n\n    if (!memcmp((PVOID)syscall_code, SyscallAddress, sizeof(syscall_code)))\n    {\n        // we can use the original code for this system call :)\n        #if defined(DEBUG)\n            printf(\"Found Syscall Opcodes at address 0x%p\\n\", SyscallAddress);\n        #endif\n        return SyscallAddress;\n    }\n\n    // the 'syscall; ret' intructions have not been found,\n    // we will try to use one near it, similarly to HalosGate\n\n    for (ULONG32 num_jumps = 1; num_jumps < searchLimit; num_jumps++)\n    {\n        // let's try with an Nt* API below our syscall\n        SyscallAddress = SW3_RVA2VA(\n            PVOID,\n            NtApiAddress,\n            distance_to_syscall + num_jumps * 0x20);\n        if (!memcmp((PVOID)syscall_code, SyscallAddress, sizeof(syscall_code)))\n        {\n        #if defined(DEBUG)\n            printf(\"Found Syscall Opcodes at address 0x%p\\n\", SyscallAddress);\n        #endif\n            return SyscallAddress;\n        }\n\n        // let's try with an Nt* API above our syscall\n        SyscallAddress = SW3_RVA2VA(\n            PVOID,\n            NtApiAddress,\n            distance_to_syscall - num_jumps * 0x20);\n        if (!memcmp((PVOID)syscall_code, SyscallAddress, sizeof(syscall_code)))\n        {\n        #if defined(DEBUG)\n            printf(\"Found Syscall Opcodes at address 0x%p\\n\", SyscallAddress);\n        #endif\n            return SyscallAddress;\n        }\n    }\n\n#ifdef DEBUG\n    printf(\"Syscall Opcodes not found!\\n\");\n#endif\n\n    return NULL;\n}\n#endif\n\n\nBOOL SW3_PopulateSyscallList()\n{\n    // Return early if the list is already populated.\n    if (SW3_SyscallList.Count) return TRUE;\n\n    #ifdef _WIN64\n    PSW3_PEB Peb = (PSW3_PEB)__readgsqword(0x60);\n    #else\n    PSW3_PEB Peb = (PSW3_PEB)__readfsdword(0x30);\n    #endif\n    PSW3_PEB_LDR_DATA Ldr = Peb->Ldr;\n    PIMAGE_EXPORT_DIRECTORY ExportDirectory = NULL;\n    PVOID DllBase = NULL;\n\n    // Get the DllBase address of NTDLL.dll. NTDLL is not guaranteed to be the second\n    // in the list, so it's safer to loop through the full list and find it.\n    PSW3_LDR_DATA_TABLE_ENTRY LdrEntry;\n    for (LdrEntry = (PSW3_LDR_DATA_TABLE_ENTRY)Ldr->Reserved2[1]; LdrEntry->DllBase != NULL; LdrEntry = (PSW3_LDR_DATA_TABLE_ENTRY)LdrEntry->Reserved1[0])\n    {\n        DllBase = LdrEntry->DllBase;\n        PIMAGE_DOS_HEADER DosHeader = (PIMAGE_DOS_HEADER)DllBase;\n        PIMAGE_NT_HEADERS NtHeaders = SW3_RVA2VA(PIMAGE_NT_HEADERS, DllBase, DosHeader->e_lfanew);\n        PIMAGE_DATA_DIRECTORY DataDirectory = (PIMAGE_DATA_DIRECTORY)NtHeaders->OptionalHeader.DataDirectory;\n        DWORD VirtualAddress = DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;\n        if (VirtualAddress == 0) continue;\n\n        ExportDirectory = (PIMAGE_EXPORT_DIRECTORY)SW3_RVA2VA(ULONG_PTR, DllBase, VirtualAddress);\n\n        // If this is NTDLL.dll, exit loop.\n        PCHAR DllName = SW3_RVA2VA(PCHAR, DllBase, ExportDirectory->Name);\n\n        if ((*(ULONG*)DllName | 0x20202020) != 0x6c64746e) continue;\n        if ((*(ULONG*)(DllName + 4) | 0x20202020) == 0x6c642e6c) break;\n    }\n\n    if (!ExportDirectory) return FALSE;\n\n    DWORD NumberOfNames = ExportDirectory->NumberOfNames;\n    PDWORD Functions = SW3_RVA2VA(PDWORD, DllBase, ExportDirectory->AddressOfFunctions);\n    PDWORD Names = SW3_RVA2VA(PDWORD, DllBase, ExportDirectory->AddressOfNames);\n    PWORD Ordinals = SW3_RVA2VA(PWORD, DllBase, ExportDirectory->AddressOfNameOrdinals);\n\n    // Populate SW3_SyscallList with unsorted Zw* entries.\n    DWORD i = 0;\n    PSW3_SYSCALL_ENTRY Entries = SW3_SyscallList.Entries;\n    do\n    {\n        PCHAR FunctionName = SW3_RVA2VA(PCHAR, DllBase, Names[NumberOfNames - 1]);\n\n        // Is this a system call?\n        if (*(USHORT*)FunctionName == 0x775a)\n        {\n            Entries[i].Hash = SW3_HashSyscall(FunctionName);\n            Entries[i].Address = Functions[Ordinals[NumberOfNames - 1]];\n            Entries[i].SyscallAddress = SC_Address(SW3_RVA2VA(PVOID, DllBase, Entries[i].Address));\n\n            i++;\n            if (i == SW3_MAX_ENTRIES) break;\n        }\n    } while (--NumberOfNames);\n\n    // Save total number of system calls found.\n    SW3_SyscallList.Count = i;\n\n    // Sort the list by address in ascending order.\n    for (DWORD i = 0; i < SW3_SyscallList.Count - 1; i++)\n    {\n        for (DWORD j = 0; j < SW3_SyscallList.Count - i - 1; j++)\n        {\n            if (Entries[j].Address > Entries[j + 1].Address)\n            {\n                // Swap entries.\n                SW3_SYSCALL_ENTRY TempEntry;\n\n                TempEntry.Hash = Entries[j].Hash;\n                TempEntry.Address = Entries[j].Address;\n                TempEntry.SyscallAddress = Entries[j].SyscallAddress;\n\n                Entries[j].Hash = Entries[j + 1].Hash;\n                Entries[j].Address = Entries[j + 1].Address;\n                Entries[j].SyscallAddress = Entries[j + 1].SyscallAddress;\n\n                Entries[j + 1].Hash = TempEntry.Hash;\n                Entries[j + 1].Address = TempEntry.Address;\n                Entries[j + 1].SyscallAddress = TempEntry.SyscallAddress;\n            }\n        }\n    }\n\n    return TRUE;\n}\n\nEXTERN_C DWORD SW3_GetSyscallNumber(DWORD FunctionHash)\n{\n    // Ensure SW3_SyscallList is populated.\n    if (!SW3_PopulateSyscallList()) return -1;\n\n    for (DWORD i = 0; i < SW3_SyscallList.Count; i++)\n    {\n        if (FunctionHash == SW3_SyscallList.Entries[i].Hash)\n        {\n            return i;\n        }\n    }\n\n    return -1;\n}\n\nEXTERN_C PVOID SW3_GetSyscallAddress(DWORD FunctionHash)\n{\n    // Ensure SW3_SyscallList is populated.\n    if (!SW3_PopulateSyscallList()) return NULL;\n\n    for (DWORD i = 0; i < SW3_SyscallList.Count; i++)\n    {\n        if (FunctionHash == SW3_SyscallList.Entries[i].Hash)\n        {\n            return SW3_SyscallList.Entries[i].SyscallAddress;\n        }\n    }\n\n    return NULL;\n}\n\nEXTERN_C PVOID SW3_GetRandomSyscallAddress(DWORD FunctionHash)\n{\n    // Ensure SW3_SyscallList is populated.\n    if (!SW3_PopulateSyscallList()) return NULL;\n\n    DWORD index = ((DWORD) rand()) % SW3_SyscallList.Count;\n\n    while (FunctionHash == SW3_SyscallList.Entries[index].Hash){\n        // Spoofing the syscall return address\n        index = ((DWORD) rand()) % SW3_SyscallList.Count;\n    }\n    return SW3_SyscallList.Entries[index].SyscallAddress;\n}\n"
  },
  {
    "path": "Hunt-Weird-Syscalls/SetThreadContext_Indirect/threadcontext_jumper_randomized.h",
    "content": "#pragma once\n\n// Code below is adapted from @modexpblog. Read linked article for more details.\n// https://www.mdsec.co.uk/2020/12/bypassing-user-mode-hooks-and-direct-invocation-of-system-calls-for-red-teams\n\n#ifndef SW3_HEADER_H_\n#define SW3_HEADER_H_\n\n#include <windows.h>\n\n#define SW3_SEED 0xE331CC7E\n#define SW3_ROL8(v) (v << 8 | v >> 24)\n#define SW3_ROR8(v) (v >> 8 | v << 24)\n#define SW3_ROX8(v) ((SW3_SEED % 2) ? SW3_ROL8(v) : SW3_ROR8(v))\n#define SW3_MAX_ENTRIES 500\n#define SW3_RVA2VA(Type, DllBase, Rva) (Type)((ULONG_PTR) DllBase + Rva)\n\n// Typedefs are prefixed to avoid pollution.\n\ntypedef struct _SW3_SYSCALL_ENTRY\n{\n    DWORD Hash;\n    DWORD Address;\n\tPVOID SyscallAddress;\n} SW3_SYSCALL_ENTRY, *PSW3_SYSCALL_ENTRY;\n\ntypedef struct _SW3_SYSCALL_LIST\n{\n    DWORD Count;\n    SW3_SYSCALL_ENTRY Entries[SW3_MAX_ENTRIES];\n} SW3_SYSCALL_LIST, *PSW3_SYSCALL_LIST;\n\ntypedef struct _SW3_PEB_LDR_DATA {\n\tBYTE Reserved1[8];\n\tPVOID Reserved2[3];\n\tLIST_ENTRY InMemoryOrderModuleList;\n} SW3_PEB_LDR_DATA, *PSW3_PEB_LDR_DATA;\n\ntypedef struct _SW3_LDR_DATA_TABLE_ENTRY {\n\tPVOID Reserved1[2];\n\tLIST_ENTRY InMemoryOrderLinks;\n\tPVOID Reserved2[2];\n\tPVOID DllBase;\n} SW3_LDR_DATA_TABLE_ENTRY, *PSW3_LDR_DATA_TABLE_ENTRY;\n\ntypedef struct _SW3_PEB {\n\tBYTE Reserved1[2];\n\tBYTE BeingDebugged;\n\tBYTE Reserved2[1];\n\tPVOID Reserved3[2];\n\tPSW3_PEB_LDR_DATA Ldr;\n} SW3_PEB, *PSW3_PEB;\n\nDWORD SW3_HashSyscall(PCSTR FunctionName);\nBOOL SW3_PopulateSyscallList();\nEXTERN_C DWORD SW3_GetSyscallNumber(DWORD FunctionHash);\nEXTERN_C PVOID SW3_GetSyscallAddress(DWORD FunctionHash);\nEXTERN_C PVOID internal_cleancall_wow64_gate(VOID);\nEXTERN_C NTSTATUS NtSetContextThread(\n\tIN HANDLE ThreadHandle,\n\tIN PCONTEXT Context);\n\n#endif\n"
  },
  {
    "path": "Hunt-Weird-Syscalls/SetThreadContext_Indirect/threadcontext_jumper_randomized_-asm.x64.asm",
    "content": ".code\n\nEXTERN SW3_GetSyscallNumber: PROC\n\nEXTERN SW3_GetRandomSyscallAddress: PROC\n\nNtSetContextThread PROC\n\tmov [rsp +8], rcx          ; Save registers.\n\tmov [rsp+16], rdx\n\tmov [rsp+24], r8\n\tmov [rsp+32], r9\n\tsub rsp, 28h\n\tmov ecx, 0123E5E95h        ; Load function hash into ECX.\n\tcall SW3_GetRandomSyscallAddress        ; Get a syscall offset from a different api.\n\tmov r15, rax                           ; Save the address of the syscall\n\tmov ecx, 0123E5E95h        ; Re-Load function hash into ECX (optional).\n\tcall SW3_GetSyscallNumber              ; Resolve function hash into syscall number.\n\tadd rsp, 28h\n\tmov rcx, [rsp+8]                      ; Restore registers.\n\tmov rdx, [rsp+16]\n\tmov r8, [rsp+24]\n\tmov r9, [rsp+32]\n\tmov r10, rcx\n\tjmp r15                                ; Jump to -> Invoke system call.\nNtSetContextThread ENDP\n\nend"
  },
  {
    "path": "README.md",
    "content": "# Hunt-Weird-Syscalls\n\nThis is a ETW based POC to monitor for abnormal syscalls.   \n\nFor now, the syscalls ``NtOpenThread`` and ``NtSetContextThread`` are monitored to identify IOCs indicating both **direct** and **indirect** syscalls.\n\n## Description\n\nThis project uses ``ETW``, more precisely kernel based ETW providers, to monitor for IOCs.    \n``ETW`` providers sitting in the kernel can effectively be leveraged, as the calltraces of emitted events contain the usermode address from where the syscall was conducted.    \n\nThis allows monitoring IOCs indicating direct and indirect syscalls, a technique often leveraged by threat actors:\n\n1: A syscall was conducted from an untrusted module (=direct syscall)   \n2: The used syscall stub in ntdll does not match the conducted syscall (=indirect syscall)\n\nThis project uses the Provider: ``Microsoft-Windows-Kernel-Audit-API-Calls`` to monitor for ``OpenThread`` and ``SetContextThread`` events triggered by the syscalls ``NtSetContextThread`` or ``NtOpenThread`` respectively.    \nCalltraces are enabled, using the flag ``EVENT_ENABLE_PROPERTY_STACK_TRACE``.   \n\nThis is a POC, and only monitors two specific syscalls. It is of course possible to use other kernel based providers to enhance telemetry.    \n\n## Tests\n\nThis project contains two sample programs using direct and indirect syscalls created using the amazing [SysWhispers3](https://github.com/klezVirus/SysWhispers3).\nThey were generated as follows:\n\n```\npython3 syswhispers.py -a x64 -m jumper_randomized --functions NtSetContextThread\npython3 syswhispers.py -a x64 -m embedded --functions NtSetContextThread\n```\n\nUpon execution, abnormal syscalls should be identified:\n\n![Identification of Abnormal Syscalls](/Screenshots/1.png?raw=true)\n\n**Tested on ``10.0.19044``.**\n\n## Credits\n\n- [KrabsETW](https://github.com/microsoft/krabsetw) \n- [SysWhispers3](https://github.com/klezVirus/SysWhispers3)\n- [etw provider docs by repnz](https://github.com/repnz/etw-providers-docs)\n- [@OutflankNL](https://twitter.com/OutflankNL) for ``IsElevated()``\n- [@trickster012](https://twitter.com/trickster012) for testing and support <3\n"
  },
  {
    "path": "libs/krabs/LICENSE",
    "content": "krabsetw\n\nCopyright (c) Microsoft Corporation\n\nAll rights reserved.\n\nMIT License\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"\"Software\"\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "libs/krabs/README.md",
    "content": "# Krabs Readme\n\nImportant Preprocessor Definitions:\n\n* `UNICODE` - krabsetw expects the `UNICODE` preprocessor definition to be\n  defined. The code will not successfully compile without this flag. There is\n  no plan to support compilation without `UNICODE` being set.\n\n* `NDEBUG` - Set this variable in release builds to disable runtime type\n   assertions. You'll still get a runtime error if the size type you're\n   requesting is not the same size as the property in the event schema.\n\n* `TYPEASSERT` - Set this variable only in debug builds (not `NDEBUG`) to enable\n   strict assertions. This means that if an explicit type check is not defined\n   for a requested type, a `static_assert` is thrown and the code will not \n   compile until one is added. This is mainly used for krabs development to\n   ensure that we don't miss asserts for types that are supported.\n"
  },
  {
    "path": "libs/krabs/krabs/client.hpp",
    "content": "// Copyright (c) Microsoft. All rights reserved.\n// Licensed under the MIT license. See LICENSE file in the project root for full license information.\n\n#pragma once\n\n#include \"compiler_check.hpp\"\n#include \"ut.hpp\"\n#include \"kt.hpp\"\n#include \"trace.hpp\"\n\nnamespace krabs {\n\n    /**\n     * <summary>\n     * Specialization of the base trace class for user traces.\n     * </summary>\n     */\n    typedef krabs::trace<krabs::details::ut> user_trace;\n\n    /**\n     * <summary>\n     * Specialization of the base trace class for kernel traces.\n     * </summary>\n     */\n    typedef krabs::trace<krabs::details::kt> kernel_trace;\n}\n"
  },
  {
    "path": "libs/krabs/krabs/collection_view.hpp",
    "content": "// Copyright (c) Microsoft. All rights reserved.\n// Licensed under the MIT license. See LICENSE file in the project root for full license information.\n\n#pragma once\n\n#include <string>\n\n#include \"compiler_check.hpp\"\n\nnamespace krabs {\n\n    /**\n     * Wraps a range of a collection starting at the location\n     * specified by the begin iterator and ending a the location\n     * specified by the end iterator. The underlying items are\n     * left in-place and should be considered const\n     */\n    template <typename T>\n    struct collection_view\n    {\n    private:\n        const T beg_;\n        const T end_;\n\n    public:\n        /**\n         * Construct a new view for the range specified by the\n         * iterators 'begin' and 'end'\n         */\n        collection_view(const T begin, const T end)\n            : beg_(begin)\n            , end_(end)\n        { }\n\n        /**\n         * Get the iterator for the beginning of the view range\n         */\n        const T begin() const\n        {\n            return beg_;\n        }\n\n        /**\n         * Get the iterator for the end of the view range\n         */\n        const T end() const\n        {\n            return end_;\n        }\n    };\n\n    /**\n     * Create a view over the range specified by iterators 'begin' and 'end'\n     */\n    template <typename T>\n    inline collection_view<T> view(const T& begin, const T& end)\n    {\n        return{ begin, end };\n    }\n\n    /**\n     * Create a const_iterator view over the specified string\n     */\n    template <typename T>\n    inline collection_view<typename std::basic_string<T>::const_iterator> view(const std::basic_string<T>& string)\n    {\n        return{ string.cbegin(), string.cend() };\n    }\n\n    /**\n     * Create a const view over the range starting at 'begin' extending 'length' items\n     */\n    template <typename T>\n    inline collection_view<const T*> view(const T* begin, size_t length)\n    {\n        return{ begin, begin + length };\n    }\n\n    /**\n     * Create a const view over the specified array\n     */\n    template <typename T, size_t n>\n    inline collection_view<const T*> view(const T(&arr)[n])\n    {\n        return{ arr, arr + n };\n    }\n\n} /* namespace krabs */"
  },
  {
    "path": "libs/krabs/krabs/compiler_check.hpp",
    "content": "// Copyright (c) Microsoft. All rights reserved.\n// Licensed under the MIT license. See LICENSE file in the project root for full license information.\n\n#pragma once\n\n#if (_MSC_VER < 1900)\n#error \"krabsetw is only supported with Visual Studio 2015 and above (MSVC++ 14.0)\"\n#endif\n"
  },
  {
    "path": "libs/krabs/krabs/errors.hpp",
    "content": "// Copyright (c) Microsoft. All rights reserved.\n// Licensed under the MIT license. See LICENSE file in the project root for full license information.\n\n#pragma once\n\n#include <stdexcept>\n\n#include \"compiler_check.hpp\"\n\nnamespace krabs {\n\n    class trace_already_registered : public std::runtime_error {\n    public:\n        trace_already_registered()\n            : std::runtime_error(\"The trace session has already been registered\")\n        {}\n    };\n\n    class invalid_parameter : public std::logic_error {\n    public:\n        invalid_parameter()\n            : std::logic_error(\"Invalid parameter given\")\n        {}\n    };\n\n    class open_trace_failure : public std::runtime_error {\n    public:\n        open_trace_failure()\n            : std::runtime_error(\"Failure to open trace\")\n        {}\n    };\n\n    class need_to_be_admin_failure : public std::runtime_error {\n    public:\n        need_to_be_admin_failure()\n            : std::runtime_error(\"Need to be an admin\")\n        {}\n    };\n\n    class could_not_find_schema : public std::runtime_error {\n    public:\n        could_not_find_schema()\n        : std::runtime_error(\"Could not find the schema\")\n        {}\n\n        could_not_find_schema(const std::string& context)\n            : std::runtime_error(std::string(\"Could not find the schema: \") + context)\n        {}\n    };\n\n    class type_mismatch_assert : public std::runtime_error {\n    public:\n        type_mismatch_assert(\n            const char* property,\n            const char* actual,\n            const char* requested)\n            : std::runtime_error(std::string(\"Attempt to read property '\") +\n            property + \"' type \" + actual + \" as \" + requested)\n        {}\n    };\n\n    class no_trace_sessions_remaining : public std::runtime_error {\n    public:\n        no_trace_sessions_remaining()\n            : std::runtime_error(\"No more trace sessions available.\")\n        {}\n    };\n\n    class function_not_supported : public std::runtime_error {\n    public:\n        function_not_supported()\n            : std::runtime_error(\"This function is not supported on this system.\")\n        {}\n    };\n\n    class unexpected_error : public std::runtime_error {\n    public:\n        unexpected_error(ULONG status)\n            : std::runtime_error(std::string(\"An unexpected error occurred: status_code=\") +\n                std::to_string(status))\n        {}\n\n        unexpected_error(const std::string &context)\n            : std::runtime_error(std::string(\"An unexpected error occurred: \") + context)\n        {}\n    };\n\n    inline std::string get_status_and_record_context(ULONG status, const EVENT_RECORD& record)\n    {\n        std::stringstream message;\n        message << \"status_code=\"\n            << status\n            << \" provider_id=\"\n            << std::to_string(record.EventHeader.ProviderId)\n            << \" event_id=\"\n            << record.EventHeader.EventDescriptor.Id;\n\n        return message.str();\n    }\n\n    /**\n     * <summary>Checks for common ETW API error codes.</summary>\n     */\n    inline void error_check_common_conditions(ULONG status)\n    {\n        if (status == ERROR_SUCCESS) {\n            return;\n        }\n\n        switch (status) {\n            case ERROR_ALREADY_EXISTS:\n                throw krabs::trace_already_registered();\n            case ERROR_INVALID_PARAMETER:\n                throw krabs::invalid_parameter();\n            case ERROR_ACCESS_DENIED:\n                throw krabs::need_to_be_admin_failure();\n            case ERROR_NOT_FOUND:\n                throw krabs::could_not_find_schema();\n            case ERROR_NO_SYSTEM_RESOURCES:\n                throw krabs::no_trace_sessions_remaining();\n            case ERROR_NOT_SUPPORTED:\n                throw krabs::function_not_supported();\n            default:\n                throw krabs::unexpected_error(status);\n        }\n    }\n\n    /**\n     * <summary>Checks for common ETW API error codes and includes properties from the event record.</summary>\n     */\n    inline void error_check_common_conditions(ULONG status, const EVENT_RECORD &record)\n    {\n        if (status == ERROR_SUCCESS) {\n            return;\n        }\n\n        auto context = get_status_and_record_context(status, record);\n\n        switch (status) {\n        case ERROR_ALREADY_EXISTS:\n            throw krabs::trace_already_registered();\n        case ERROR_INVALID_PARAMETER:\n            throw krabs::invalid_parameter();\n        case ERROR_ACCESS_DENIED:\n            throw krabs::need_to_be_admin_failure();\n        case ERROR_NOT_FOUND:\n            throw krabs::could_not_find_schema(context);\n        case ERROR_NO_SYSTEM_RESOURCES:\n            throw krabs::no_trace_sessions_remaining();\n        case ERROR_NOT_SUPPORTED:\n            throw krabs::function_not_supported();\n        default:\n            throw krabs::unexpected_error(context);\n        }\n    }\n}\n"
  },
  {
    "path": "libs/krabs/krabs/etw.hpp",
    "content": "// Copyright (c) Microsoft. All rights reserved.\n// Licensed under the MIT license. See LICENSE file in the project root for full license information.\n\n// Interface for ETW.\n\n#pragma once\n\n#define INITGUID\n\n#include \"compiler_check.hpp\"\n#include \"trace.hpp\"\n#include \"errors.hpp\"\n\n#include <cassert>\n#include <map>\n\n#include <evntrace.h>\n#include <evntcons.h>\n\nnamespace krabs { namespace details {\n\n    // The ETW API requires that we reserve enough memory behind\n    // an EVENT_TRACE_PROPERTIES buffer in order to store an ETW trace name\n    // and an optional ETW log file name. The easiest way to do this is to\n    // use a struct to reserve this space -- the alternative is to malloc\n    // the bytes at runtime (ew).\n    class trace_info {\n    public:\n        EVENT_TRACE_PROPERTIES properties;\n        wchar_t traceName[MAX_PATH];\n        wchar_t logfileName[MAX_PATH];\n    };\n\n    /**\n     * <summary>\n     * Used to implement starting and stopping traces.\n     * </summary>\n     */\n    template <typename T>\n    class trace_manager {\n    public:\n        trace_manager(T &trace);\n\n        /**\n         * <summary>\n         * Starts the ETW trace identified by the info in the trace type.\n         * </summary>\n         */\n        void start();\n\n        /**\n         * <summary>\n         * Stops the ETW trace identified by the info in the trace type.\n         * </summary>\n         */\n        void stop();\n\n        /**\n        * <summary>\n        * Opens the ETW trace identified by the info in the trace type.\n        * </summary>\n        */\n        EVENT_TRACE_LOGFILE open();\n\n        /**\n        * <summary>\n        * Starts processing the ETW trace identified by the info in the trace type.\n        * open() needs to called for this to work first.\n        * </summary>\n        */\n        void process();\n\n        /**\n         * <summary>\n         * Queries the ETW trace identified by the info in the trace type.\n         * </summary>\n         */\n        EVENT_TRACE_PROPERTIES query();\n\n        /**\n         * <summary>\n         * Configures the ETW trace session settings.\n         * See https://docs.microsoft.com/en-us/windows/win32/api/evntrace/nf-evntrace-tracesetinformation.\n         * </summary>\n         */\n        void set_trace_information(\n            TRACE_INFO_CLASS information_class,\n            PVOID trace_information,\n            ULONG information_length);\n\n        /**\n         * <summary>\n         * Notifies the underlying trace of the buffers that were processed.\n         * </summary>\n         */\n        void set_buffers_processed(size_t processed);\n\n        /**\n         * <summary>\n         * Notifies the underlying trace that an event occurred.\n         * </summary>\n         */\n        void on_event(const EVENT_RECORD &record);\n\n    private:\n        trace_info fill_trace_info();\n        EVENT_TRACE_LOGFILE fill_logfile();\n        void close_trace();\n        void register_trace();\n        EVENT_TRACE_PROPERTIES query_trace();\n        void stop_trace();\n        EVENT_TRACE_LOGFILE open_trace();\n        void process_trace();\n        void enable_providers();\n\n    private:\n        T &trace_;\n    };\n\n    // Implementation\n    // ------------------------------------------------------------------------\n\n    /**\n     * <summary>\n     *   Called by ETW when an event occurs, forwards calls to the\n     *   appropriate instance.\n     * </summary>\n     * <remarks>\n     *   A pointer to the instance is stored in the UserContext\n     *   field of the EVENT_RECORD. This is set via the Context field of the\n     *   EVENT_TRACE_LOGFILE structure.\n     * </remarks>\n     */\n    template <typename T>\n    static void __stdcall trace_callback_thunk(EVENT_RECORD *pRecord)\n    {\n        auto *pUserTrace = (T*)(pRecord->UserContext);\n        trace_manager<T> trace(*pUserTrace);\n        trace.on_event(*pRecord);\n    }\n\n    /**\n     * <summary>\n     *   Called by ETW after the events for each buffer are delivered, gives\n     *   statistics like the number of buffers processed and the number of\n     *   events dropped.\n     * </summary>\n     * <remarks>\n     *   A pointer to the instance is stored in the UserContext\n     *   field of the EVENT_RECORD. This is set via the Context field of the\n     *   EVENT_TRACE_LOGFILE structure.\n     * </remarks>\n     */\n    template <typename T>\n    static ULONG __stdcall trace_buffer_callback(EVENT_TRACE_LOGFILE *pLogFile)\n    {\n        auto *pTrace = (T*)(pLogFile->Context);\n        trace_manager<T> trace(*pTrace);\n\n        // NOTE: EventsLost is not set on this type\n        trace.set_buffers_processed(pLogFile->BuffersRead);\n        return TRUE;\n    }\n\n    template <typename T>\n    trace_manager<T>::trace_manager(T &trace)\n    : trace_(trace)\n    {}\n\n    template <typename T>\n    void trace_manager<T>::start()\n    {\n        if (trace_.sessionHandle_ == INVALID_PROCESSTRACE_HANDLE) {\n            (void)open();\n        }\n        process_trace();\n    }\n\n    template <typename T>\n    EVENT_TRACE_LOGFILE trace_manager<T>::open()\n    {\n        register_trace();\n        enable_providers();\n        return open_trace();\n    }\n\n    template <typename T>\n    void trace_manager<T>::process()\n    {\n        process_trace();\n    }\n\n    template <typename T>\n    EVENT_TRACE_PROPERTIES trace_manager<T>::query()\n    {\n        return query_trace();\n    }\n\n    template <typename T>\n    void trace_manager<T>::set_trace_information(\n        TRACE_INFO_CLASS information_class,\n        PVOID trace_information,\n        ULONG information_length)\n    {\n        ULONG status = TraceSetInformation(\n            trace_.registrationHandle_, \n            information_class,\n            trace_information,\n            information_length);\n\n        error_check_common_conditions(status);\n    }\n\n    template <typename T>\n    void trace_manager<T>::stop()\n    {\n        stop_trace();\n        close_trace();\n    }\n\n    template <typename T>\n    void trace_manager<T>::set_buffers_processed(size_t processed)\n    {\n        trace_.buffersRead_ = processed;\n    }\n\n    template <typename T>\n    void trace_manager<T>::on_event(const EVENT_RECORD &record)\n    {\n        trace_.on_event(record);\n    }\n\n    template <typename T>\n    trace_info trace_manager<T>::fill_trace_info()\n    {\n        trace_info info = {};\n        info.properties.Wnode.BufferSize    = sizeof(trace_info);\n        info.properties.Wnode.Guid          = T::trace_type::get_trace_guid();\n        info.properties.Wnode.Flags         = WNODE_FLAG_TRACED_GUID;\n        info.properties.Wnode.ClientContext = 1; // QPC clock resolution\n        info.properties.BufferSize          = trace_.properties_.BufferSize;\n        info.properties.MinimumBuffers      = trace_.properties_.MinimumBuffers;\n        info.properties.MaximumBuffers      = trace_.properties_.MaximumBuffers;\n        info.properties.FlushTimer          = trace_.properties_.FlushTimer;\n\n        if (trace_.properties_.LogFileMode)\n            info.properties.LogFileMode     = trace_.properties_.LogFileMode;\n        else\n            info.properties.LogFileMode     = EVENT_TRACE_REAL_TIME_MODE\n                                            | EVENT_TRACE_NO_PER_PROCESSOR_BUFFERING;\n\n        info.properties.LogFileMode         |= T::trace_type::augment_file_mode();\n        info.properties.LoggerNameOffset    = offsetof(trace_info, logfileName);\n        info.properties.EnableFlags         = T::trace_type::construct_enable_flags(trace_);\n        assert(info.traceName[0] == '\\0');\n        assert(info.logfileName[0] == '\\0');\n        trace_.name_._Copy_s(info.traceName, ARRAYSIZE(info.traceName), trace_.name_.length());\n        return info;\n    }\n\n    template <typename T>\n    EVENT_TRACE_LOGFILE trace_manager<T>::fill_logfile()\n    {\n        EVENT_TRACE_LOGFILE file = {};\n\n        if (!trace_.logFilename_.empty())\n        {\n            file.LogFileName      = const_cast<wchar_t*>(trace_.logFilename_.c_str());\n            file.ProcessTraceMode = PROCESS_TRACE_MODE_EVENT_RECORD;\n        }\n        else\n        {\n            file.LoggerName       = const_cast<wchar_t*>(trace_.name_.c_str());\n            file.ProcessTraceMode = PROCESS_TRACE_MODE_EVENT_RECORD |\n                PROCESS_TRACE_MODE_REAL_TIME;\n        }\n        file.Context             = (void *)&trace_;\n        file.EventRecordCallback = trace_callback_thunk<T>;\n        file.BufferCallback      = trace_buffer_callback<T>;\n        return file;\n    }\n\n    template <typename T>\n    void trace_manager<T>::stop_trace()\n    {\n        trace_info info = fill_trace_info();\n        ULONG status = ControlTrace(\n            NULL,\n            trace_.name_.c_str(),\n            &info.properties,\n            EVENT_TRACE_CONTROL_STOP);\n\n        if (status != ERROR_WMI_INSTANCE_NOT_FOUND) {\n            error_check_common_conditions(status);\n        }\n    }\n\n    template <typename T>\n    EVENT_TRACE_PROPERTIES trace_manager<T>::query_trace()\n    {\n        trace_info info = fill_trace_info();\n        ULONG status = ControlTrace(\n            NULL,\n            trace_.name_.c_str(),\n            &info.properties,\n            EVENT_TRACE_CONTROL_QUERY);\n\n        if (status != ERROR_WMI_INSTANCE_NOT_FOUND) {\n            error_check_common_conditions(status);\n\n            return info.properties;\n        }\n\n        return { };\n    }\n\n    template <typename T>\n    void trace_manager<T>::register_trace()\n    {\n        trace_info info = fill_trace_info();\n\n        ULONG status = StartTrace(&trace_.registrationHandle_,\n                                  trace_.name_.c_str(),\n                                  &info.properties);\n        if (status == ERROR_ALREADY_EXISTS) {\n            try {\n                stop_trace();\n                status = StartTrace(&trace_.registrationHandle_,\n                    trace_.name_.c_str(),\n                    &info.properties);\n            }\n            catch (need_to_be_admin_failure) {\n                (void)open_trace();\n                close_trace();\n                // insufficient privilege to stop/configure\n                // but if open/close didn't throw also\n                // then we're okay to process events\n                status = ERROR_SUCCESS;\n                // we also invalidate the registrationHandle_\n                // StartTrace() actually sets this to 0 on failure\n                trace_.registrationHandle_ = INVALID_PROCESSTRACE_HANDLE;\n            }\n            catch (invalid_parameter) {\n                // In some versions, the error code is 87 when using\n                // SystemTraceControlGuid session. If open/close doesn't\n                // throw, then we can continually processing events.\n                (void)open_trace();\n                close_trace();\n                status = ERROR_SUCCESS;\n                trace_.registrationHandle_ = INVALID_PROCESSTRACE_HANDLE;\n            }\n        }\n\n        error_check_common_conditions(status);\n    }\n\n    template <typename T>\n    EVENT_TRACE_LOGFILE trace_manager<T>::open_trace()\n    {\n        auto file = fill_logfile();\n        trace_.sessionHandle_ = OpenTrace(&file);\n        if (trace_.sessionHandle_ == INVALID_PROCESSTRACE_HANDLE) {\n            throw open_trace_failure();\n        }\n        return file;\n    }\n\n    template <typename T>\n    void trace_manager<T>::process_trace()\n    {\n        if (trace_.sessionHandle_ == INVALID_PROCESSTRACE_HANDLE) {\n            throw open_trace_failure();\n        }\n\n        // Refactoring warning.\n        // During the testing of the (slower) C++/CLI implementation it became evident that\n        // EnableTraceEx2(EVENT_CONTROL_CODE_CAPTURE_STATE) must be called very shortly\n        // before ProcessTrace() in order for the rundown events to be generated.\n        T::trace_type::enable_rundown(trace_);\n\n        ULONG status = ProcessTrace(&trace_.sessionHandle_, 1, NULL, NULL);\n        error_check_common_conditions(status);\n    }\n\n    template <typename T>\n    void trace_manager<T>::close_trace()\n    {\n        if (trace_.sessionHandle_ != INVALID_PROCESSTRACE_HANDLE) {\n            ULONG status = CloseTrace(trace_.sessionHandle_);\n            trace_.sessionHandle_ = INVALID_PROCESSTRACE_HANDLE;\n\n            if (status != ERROR_CTX_CLOSE_PENDING) {\n                error_check_common_conditions(status);\n            }\n        }\n    }\n\n    template <typename T>\n    void trace_manager<T>::enable_providers()\n    {\n        T::trace_type::enable_providers(trace_);\n    }\n} /* namespace details */ } /* namespace krabs */\n"
  },
  {
    "path": "libs/krabs/krabs/filtering/comparers.hpp",
    "content": "// Copyright (c) Microsoft. All rights reserved.\n// Licensed under the MIT license. See LICENSE file in the project root for full license information.\n\n#pragma once\n\n#include <algorithm>\n\n#include \"../compiler_check.hpp\"\n\nnamespace krabs { namespace predicates {\n\n    namespace comparers {\n\n        // Algorithms\n        // --------------------------------------------------------------------\n\n        /**\n         * Iterator based equals\n         */\n        template <typename Comparer>\n        struct equals\n        {\n            template <typename Iter1, typename Iter2>\n            bool operator()(Iter1 first1, Iter1 last1, Iter2 first2, Iter2 last2) const\n            {\n                return std::equal(first1, last1, first2, last2, Comparer());\n            }\n        };\n\n        /**\n         * Iterator based search\n         */\n        template <typename Comparer>\n        struct contains\n        {\n            template <typename Iter1, typename Iter2>\n            bool operator()(Iter1 first1, Iter1 last1, Iter2 first2, Iter2 last2) const\n            {\n                // empty test range always contained, even when input range empty\n                return first2 == last2\n                    || std::search(first1, last1, first2, last2, Comparer()) != last1;\n            }\n        };\n\n        /**\n         * Iterator based starts_with\n         */\n        template <typename Comparer>\n        struct starts_with\n        {\n            template <typename Iter1, typename Iter2>\n            bool operator()(Iter1 first1, Iter1 last1, Iter2 first2, Iter2 last2) const\n            {\n                const auto first_nonequal = std::mismatch(first1, last1, first2, last2, Comparer());\n                return first_nonequal.second == last2;\n            }\n        };\n\n        /**\n        * Iterator based ends_with\n        */\n        template <typename Comparer>\n        struct ends_with\n        {\n            template <typename Iter1, typename Iter2>\n            bool operator()(Iter1 first1, Iter1 last1, Iter2 first2, Iter2 last2) const\n            {\n                const auto dist1 = std::distance(first1, last1);\n                const auto dist2 = std::distance(first2, last2);\n\n                if (dist2 > dist1)\n                    return false;\n\n                const auto suffix_begin = std::next(first1, dist1 - dist2);\n                return std::equal(suffix_begin, last1, first2, last2, Comparer());\n            }\n        };\n\n        // Custom Comparison\n        // --------------------------------------------------------------------\n\n        template <typename T>\n        struct iequal_to\n        {\n            bool operator()(const T& a, const T& b) const\n            {\n                static_assert(sizeof(T) == 0,\n                    \"iequal_to needs a specialized overload for type\");\n            }\n        };\n\n        /**\n        * <summary>\n        *   Binary predicate for comparing two wide characters case insensitively\n        *   Does not handle all locales\n        * </summary>\n        */\n        template <>\n        struct iequal_to<wchar_t>\n        {\n            bool operator()(const wchar_t& a, const wchar_t& b) const\n            {\n                return towupper(a) == towupper(b);\n            }\n        };\n\n        /**\n        * <summary>\n        *   Binary predicate for comparing two characters case insensitively\n        *   Does not handle all locales\n        * </summary>\n        */\n        template <>\n        struct iequal_to<char>\n        {\n            bool operator()(const char& a, const char& b) const\n            {\n                return toupper(a) == toupper(b);\n            }\n        };\n\n    } /* namespace comparers */\n\n} /* namespace predicates */ } /* namespace krabs */"
  },
  {
    "path": "libs/krabs/krabs/filtering/event_filter.hpp",
    "content": "// Copyright (c) Microsoft. All rights reserved.\n// Licensed under the MIT license. See LICENSE file in the project root for full license information.\n\n#pragma once\n\n#include <evntcons.h>\n#include <functional>\n#include <deque>\n#include <vector>\n\n#include \"../compiler_check.hpp\"\n#include \"../trace_context.hpp\"\n\nnamespace krabs { namespace testing {\n    class event_filter_proxy;\n} /* namespace testing */} /* namespace krabs */\n\nnamespace krabs { namespace details {\n    template <typename T> class base_provider;\n} /* namespace details */} /* namespace krabs */\n\n\nnamespace krabs {\n\n    typedef void(*c_provider_callback)(const EVENT_RECORD &, const krabs::trace_context &);\n    typedef void(*c_provider_error_callback)(const EVENT_RECORD&, const std::string&);\n    typedef std::function<void(const EVENT_RECORD &, const krabs::trace_context &)> provider_event_callback;\n    typedef std::function<void(const EVENT_RECORD&, const std::string&)> provider_error_callback;\n    typedef std::function<bool(const EVENT_RECORD &, const krabs::trace_context &)> filter_predicate;\n\n    template <typename T> class provider;\n\n    /**\n     * <summary>\n     *   Use this to provide event filtering before an event bubbles to\n     *   specific callbacks.\n     * </summary>\n     * <remarks>\n     *   Each event_filter has a single predicate (which can do complicated\n     *   checks and logic on the event). All callbacks registered under the\n     *   filter are invoked only if the predicate returns true for a given\n     *   event.\n     * </remarks>\n     */\n    class event_filter {\n    public:\n\n        /**\n         * <summary>\n         *   Constructs an event_filter that applies the given predicate to all\n         *   events.\n         * </summary>\n         */\n        event_filter(filter_predicate predicate);\n\n        /**\n         * <summary>\n         *   Constructs an event_filter that applies event id filtering by event_id\n         *   which will be added to list of filtered event ids in ETW API.\n         *   This way is more effective from performance point of view.\n         *   Given optional predicate will be applied to ETW API filtered results\n         * </summary>\n         */\n        event_filter(unsigned short event_id, filter_predicate predicate=nullptr);\n\n        /**\n         * <summary>\n         *   Constructs an event_filter that applies event id filtering by event_id\n         *   which will be added to list of filtered event ids in ETW API.\n         *   This way is more effective from performance point of view.\n         *   Given optional predicate will be applied to ETW API filtered results\n         * </summary>\n         */\n        event_filter(std::vector<unsigned short> event_ids, filter_predicate predicate = nullptr);\n\n        /**\n         * <summary>\n         * Adds a function to call when an event for this filter is fired.\n         * </summary>\n         */\n        void add_on_event_callback(c_provider_callback callback);\n\n        template <typename U>\n        void add_on_event_callback(U &callback);\n\n        template <typename U>\n        void add_on_event_callback(const U &callback);\n\n        /**\n         * <summary>\n         * Adds a function to call when an error occurs.\n         * </summary>\n         */\n        void add_on_error_callback(c_provider_error_callback callback);\n\n        template <typename U>\n        void add_on_error_callback(U& callback);\n\n        template <typename U>\n        void add_on_error_callback(const U& callback);\n\n        const std::vector<unsigned short>& provider_filter_event_ids() const\n        {\n            return provider_filter_event_ids_;\n        }\n\n    private:\n\n        /**\n         * <summary>\n         *   Called when an event occurs, forwards to callbacks if the event\n         *   satisfies the predicate.\n         * </summary>\n         */\n        void on_event(const EVENT_RECORD &record, const krabs::trace_context &trace_context) const;\n\n        /**\n         * <summary>\n         *   Called when an error occurs, forwards to the error callback\n         * </summary>\n         */\n        void on_error(const EVENT_RECORD& record, const std::string& error_message) const;\n\n    private:\n        std::deque<provider_event_callback> event_callbacks_;\n        std::deque<provider_error_callback> error_callbacks_;\n        filter_predicate predicate_{ nullptr };\n        std::vector<unsigned short> provider_filter_event_ids_;\n\n    private:\n        template <typename T>\n        friend class details::base_provider;\n\n        friend class krabs::testing::event_filter_proxy;\n    };\n\n    // Implementation\n    // ------------------------------------------------------------------------\n\n    inline event_filter::event_filter(filter_predicate predicate)\n    : predicate_(predicate)\n    {}\n\n    inline event_filter::event_filter(std::vector<unsigned short> event_ids, filter_predicate predicate/*=nullptr*/)\n    : provider_filter_event_ids_{ event_ids },\n      predicate_(predicate)\n    {}\n\n    inline event_filter::event_filter(unsigned short event_id, filter_predicate predicate/*=nullptr*/)\n    : provider_filter_event_ids_{ event_id },\n      predicate_(predicate)\n    {}\n\n    inline void event_filter::add_on_event_callback(c_provider_callback callback)\n    {\n        // C function pointers don't interact well with std::ref, so we\n        // overload to take care of this scenario.\n        event_callbacks_.push_back(callback);\n    }\n\n    template <typename U>\n    void event_filter::add_on_event_callback(U &callback)\n    {\n        // std::function copies its argument -- because our callbacks list\n        // is a list of std::function, this causes problems when a user\n        // intended for their particular instance to be called.\n        // std::ref lets us get around this and point to a specific instance\n        // that they handed us.\n        event_callbacks_.push_back(std::ref(callback));\n    }\n\n    template <typename U>\n    void event_filter::add_on_event_callback(const U &callback)\n    {\n        // This is where temporaries bind to. Temporaries can't be wrapped in\n        // a std::ref because they'll go away very quickly. We are forced to\n        // actually copy these.\n        event_callbacks_.push_back(callback);\n    }\n\n    inline void event_filter::add_on_error_callback(c_provider_error_callback callback)\n    {\n        // C function pointers don't interact well with std::ref, so we\n        // overload to take care of this scenario.\n        error_callbacks_.push_back(callback);\n    }\n\n    template <typename U>\n    void event_filter::add_on_error_callback(U& callback)\n    {\n        // std::function copies its argument -- because our callbacks list\n        // is a list of std::function, this causes problems when a user\n        // intended for their particular instance to be called.\n        // std::ref lets us get around this and point to a specific instance\n        // that they handed us.\n        error_callbacks_.push_back(std::ref(callback));\n    }\n\n    template <typename U>\n    void event_filter::add_on_error_callback(const U& callback)\n    {\n        // This is where temporaries bind to. Temporaries can't be wrapped in\n        // a std::ref because they'll go away very quickly. We are forced to\n        // actually copy these.\n        error_callbacks_.push_back(callback);\n    }\n\n    inline void event_filter::on_event(const EVENT_RECORD &record, const krabs::trace_context &trace_context) const\n    {\n        if (event_callbacks_.empty()) {\n            return;\n        }\n\n        try\n        {\n            if (predicate_ != nullptr && !predicate_(record, trace_context)) {\n                return;\n            }\n\n            for (auto& callback : event_callbacks_) {\n                callback(record, trace_context);\n            }\n        }\n        catch (const krabs::could_not_find_schema& ex)\n        {\n            // this occurs when a predicate is applied to an event for which\n            // no schema exists. instead of allowing the exception to halt\n            // the entire trace, send a notification to the filter's error callback\n            for (auto& error_callback : error_callbacks_) {\n                error_callback(record, ex.what());\n            }\n        }\n    }\n} /* namespace krabs */\n"
  },
  {
    "path": "libs/krabs/krabs/filtering/predicates.hpp",
    "content": "// Copyright (c) Microsoft. All rights reserved.\n// Licensed under the MIT license. See LICENSE file in the project root for full license information.\n\n#pragma once\n\n#include <evntcons.h>\n#include <functional>\n#include <algorithm>\n#include <string>\n\n#include \"../compiler_check.hpp\"\n#include \"comparers.hpp\"\n#include \"../trace_context.hpp\"\n#include \"view_adapters.hpp\"\n\nusing namespace krabs::predicates::adapters;\nusing namespace krabs::predicates::comparers;\n\nnamespace krabs { namespace predicates {\n\n    namespace details {\n\n        /**\n         * <summary>\n         *   The base predicate struct, use to create a vector or list of\n         *   Arbitrary predicate types\n         * </summary>\n         */\n        struct predicate_base\n        {\n            virtual bool operator()(const EVENT_RECORD&, const krabs::trace_context&) const = 0;\n        };\n\n        /**\n         * <summary>\n         *   Returns true for any event.\n         * </summary>\n         */\n        struct any_event : predicate_base {\n            bool operator()(const EVENT_RECORD &, const krabs::trace_context &) const\n            {\n                return true;\n            }\n        };\n\n        /**\n         * <summary>\n         *   Returns false for any event.\n         * </summary>\n         */\n        struct no_event : predicate_base {\n            bool operator()(const EVENT_RECORD &, const krabs::trace_context &) const\n            {\n                return false;\n            }\n        };\n\n        /**\n         * <summary>\n         *   Performs a logical AND on two filters.\n         * </summary>\n         */\n        template <typename T1, typename T2>\n        struct and_filter : predicate_base {\n            and_filter(const T1 &t1, const T2 &t2)\n                : t1_(t1)\n                , t2_(t2)\n            {}\n\n            bool operator()(const EVENT_RECORD &record, const krabs::trace_context &trace_context) const\n            {\n                return (t1_(record, trace_context) && t2_(record, trace_context));\n            }\n\n        private:\n            const T1 t1_;\n            const T2 t2_;\n        };\n\n        /**\n         * <summary>\n         *   Performs a logical OR on two filters.\n         * </summary>\n         */\n        template <typename T1, typename T2>\n        struct or_filter : predicate_base {\n            or_filter(const T1 &t1, const T2 &t2)\n                : t1_(t1)\n                , t2_(t2)\n            {}\n\n            bool operator()(const EVENT_RECORD &record, const krabs::trace_context &trace_context) const\n            {\n                return (t1_(record, trace_context) || t2_(record, trace_context));\n            }\n\n        private:\n            const T1 t1_;\n            const T2 t2_;\n        };\n\n        /**\n         * <summary>\n         *   Performs a logical NOT on a filter.\n         * </summary>\n         */\n        template <typename T1>\n        struct not_filter : predicate_base {\n            not_filter(const T1 &t1)\n                : t1_(t1)\n            {}\n\n            bool operator()(const EVENT_RECORD &record, const krabs::trace_context &trace_context) const\n            {\n                return !t1_(record, trace_context);\n            }\n\n        private:\n            const T1 t1_;\n        };\n\n        /**\n        * <summary>\n        *   Returns true if the event property matches the expected value.\n        * </summary>\n        */\n        template <typename T>\n        struct property_is : predicate_base {\n            property_is(const std::wstring &property, const T &expected)\n                : property_(property)\n                , expected_(expected)\n            {}\n\n            bool operator()(const EVENT_RECORD &record, const krabs::trace_context &trace_context) const\n            {\n                krabs::schema schema(record, trace_context.schema_locator);\n                krabs::parser parser(schema);\n\n                try {\n                    return (expected_ == parser.parse<T>(property_));\n                }\n                catch (...) {\n                    return false;\n                }\n            }\n\n        private:\n            const std::wstring property_;\n            const T expected_;\n        };\n\n        /**\n         * <summary>\n         *   Gets a collection_view of a property using the specified adapter\n         *   and executes the specified predicate against the view.\n         *   This is used to provide type-specialization for properties\n         *   that can be represented by the collection_view.\n         * </summary>\n         */\n        template <typename T, typename Adapter, typename Predicate>\n        struct property_view_predicate : details::predicate_base\n        {\n            property_view_predicate(\n                const std::wstring &property,\n                const T &expected,\n                Adapter adapter,\n                Predicate predicate)\n                : property_(property)\n                , expected_(expected)\n                , adapter_(adapter)\n                , predicate_(predicate)\n            { }\n\n            //bool operator()(const EVENT_RECORD &record, const krabs::trace_context &trace_context)\n            bool operator()(const EVENT_RECORD& record, const krabs::trace_context& trace_context) const\n            {\n                krabs::schema schema(record, trace_context.schema_locator);\n                krabs::parser parser(schema);\n\n                try {\n                    auto view = parser.view_of(property_, adapter_);\n                    return predicate_(view.begin(), view.end(), expected_.begin(), expected_.end());\n                }\n                catch (...) {\n                    return false;\n                }\n            }\n\n        private:\n            const std::wstring property_;\n            const T expected_;\n            Adapter adapter_;\n            Predicate predicate_;\n        };\n    } /* namespace details */\n\n    // Filter factory functions\n    // ------------------------------------------------------------------------\n\n    /**\n     * <summary>\n     *   A simple filter that accepts any event.\n     * </summary>\n     */\n    static details::any_event any_event;\n\n    /**\n     * <summary>\n     *   A simple filter that accepts no events.\n     * </summary>\n     */\n    static details::no_event no_event;\n\n    /**\n     * <summary>\n     *   Accepts an event if its ID matches the expected value.\n     * </summary>\n     */\n    struct id_is : details::predicate_base {\n        id_is(size_t expected)\n        : expected_(USHORT(expected))\n        {}\n\n        bool operator()(const EVENT_RECORD &record, const krabs::trace_context &) const\n        {\n            return (record.EventHeader.EventDescriptor.Id == expected_);\n        }\n\n    private:\n        USHORT expected_;\n    };\n\n    /**\n     * <summary>\n     *   Accepts an event if its opcode matches the expected value.\n     * </summary>\n     */\n    struct opcode_is : details::predicate_base {\n        opcode_is(size_t expected)\n        : expected_(USHORT(expected))\n        {}\n\n        bool operator()(const EVENT_RECORD &record, const krabs::trace_context &) const\n        {\n            return (record.EventHeader.EventDescriptor.Opcode == expected_);\n        }\n\n    private:\n        USHORT expected_;\n    };\n\n    /**\n     * <summary>\n     *   Accepts an event if any of the predicates in the vector matches\n     * </summary>\n     */\n    struct any_of : details::predicate_base {\n        any_of(std::vector<details::predicate_base*> list)\n        : list_(list)\n        {}\n\n        bool operator()(const EVENT_RECORD &record, const krabs::trace_context &trace_context) const\n        {\n            for (auto &item : list_) {\n                if (item->operator()(record, trace_context)) {\n                    return true;\n                };\n            }\n            return false;\n        }\n    private:\n        std::vector<details::predicate_base*> list_;\n    };\n\n    /**\n     * <summary>\n     *   Accepts an event if all of the predicates in the vector matches\n     * </summary>\n     */\n    struct all_of : details::predicate_base {\n        all_of(std::vector<details::predicate_base*> list)\n            : list_(list)\n        {}\n\n        bool operator()(const EVENT_RECORD& record, const krabs::trace_context& trace_context) const\n        {\n            if (list_.empty()) {\n                return false;\n            }\n            for (auto& item : list_) {\n                if (!item->operator()(record, trace_context)) {\n                    return false;\n                };\n            }\n            return true;\n        }\n    private:\n        std::vector<details::predicate_base*> list_;\n    };\n\n    /**\n     * <summary>\n     *   Accepts an event only if none of the predicates in the vector match\n     * </summary>\n     */\n    struct none_of : details::predicate_base {\n        none_of(std::vector<details::predicate_base*> list)\n            : list_(list)\n        {}\n\n        bool operator()(const EVENT_RECORD& record, const krabs::trace_context& trace_context) const\n        {\n            for (auto& item : list_) {\n                if (item->operator()(record, trace_context)) {\n                    return false;\n                };\n            }\n            return true;\n        }\n    private:\n        std::vector<details::predicate_base*> list_;\n    };\n\n    /**\n     * <summary>\n     *   Accepts an event if its version matches the expected value.\n     * </summary>\n     */\n    struct version_is : details::predicate_base {\n        version_is(size_t expected)\n        : expected_(USHORT(expected))\n        {}\n\n        bool operator()(const EVENT_RECORD &record, const krabs::trace_context &) const\n        {\n            return (record.EventHeader.EventDescriptor.Version == expected_);\n        }\n\n    private:\n        USHORT expected_;\n    };\n\n    /**\n    * <summary>\n    *   Accepts an event if its PID matches the expected value.\n    * </summary>\n    */\n    struct process_id_is : details::predicate_base {\n        process_id_is(size_t expected)\n        : expected_(ULONG(expected))\n        {}\n\n        bool operator()(const EVENT_RECORD &record, const krabs::trace_context &) const\n        {\n            return (record.EventHeader.ProcessId == expected_);\n        }\n\n    private:\n        ULONG expected_;\n    };\n\n    /**\n     * <summary>\n     *   Accepts an event if the named property matches the expected value.\n     * </summary>\n     */\n    template <typename T>\n    details::property_is<T> property_is(\n        const std::wstring &prop,\n        const T &expected)\n    {\n        return details::property_is<T>(prop, expected);\n    }\n\n     /**\n      * <summary>\n      *   Explicit specialization for string arrays, because C++.\n      * </summary>\n      */\n    inline details::property_is<std::wstring> property_is(\n        const std::wstring &prop,\n        const wchar_t *expected)\n    {\n        return details::property_is<std::wstring>(prop, std::wstring(expected));\n    }\n\n    inline details::property_is<std::string> property_is(\n        const std::wstring &prop,\n        const char *expected)\n    {\n        return details::property_is<std::string>(prop, std::string(expected));\n    }\n\n    // View-based filters\n    // ------------------------------------------------------------------------\n\n    /**\n     * View based filters work on ranges of data in-place in the etw record.\n     * By default, these expect a null terminated string property in the\n     * record, but if an adapter is specified, any range of data can be\n     * compared. The comparison functor must support taking two iterator\n     * pairs that iterate the value_type specified by the adapter. Because\n     * comparisons use iterators, it's possible to compare various flavors\n     * of strings as well as arrays and binary data.\n     */\n\n    /**\n     * Accepts events if property exactly matches the expected value\n     */\n    template <\n        typename Adapter = adapters::generic_string<wchar_t>,\n        typename T,\n        typename Comparer = equals<std::equal_to<typename Adapter::value_type>>>\n    details::property_view_predicate<T, Adapter, Comparer> property_equals(\n        const std::wstring &prop,\n        const T& expected)\n    {\n        return { prop, expected, Adapter(), Comparer() };\n    }\n\n    /**\n     * Accepts events if property case insensitive matches the expected value\n     */\n    template <\n        typename Adapter = adapters::generic_string<wchar_t>,\n        typename T,\n        typename Comparer = equals<iequal_to<typename Adapter::value_type>>>\n    details::property_view_predicate<T, Adapter, Comparer> property_iequals(\n        const std::wstring &prop,\n        const T& expected)\n    {\n        return{ prop, expected, Adapter(), Comparer() };\n    }\n\n    /**\n     * Accepts events if property contains expected value\n     */\n    template <\n        typename Adapter = adapters::generic_string<wchar_t>,\n        typename T,\n        typename Comparer = contains<std::equal_to<typename Adapter::value_type>>>\n    details::property_view_predicate<T, Adapter, Comparer> property_contains(\n        const std::wstring &prop,\n        const T& expected)\n    {\n        return {prop, expected, Adapter(), Comparer()};\n    }\n\n    /**\n     * Accepts events if property case insensitive contains expected value\n     */\n    template <\n        typename Adapter = adapters::generic_string<wchar_t>,\n        typename T,\n        typename Comparer = contains<iequal_to<typename Adapter::value_type>>>\n    details::property_view_predicate<T, Adapter, Comparer> property_icontains(\n        const std::wstring &prop,\n        const T& expected)\n    {\n        return{ prop, expected, Adapter(), Comparer() };\n    }\n\n    /**\n     * Accepts events if property starts with expected value\n     */\n    template <\n        typename Adapter = adapters::generic_string<wchar_t>,\n        typename T,\n        typename Comparer = starts_with<std::equal_to<typename Adapter::value_type>>>\n    details::property_view_predicate<T, Adapter, Comparer> property_starts_with(\n        const std::wstring &prop,\n        const T& expected)\n    {\n        return{ prop, expected, Adapter(), Comparer() };\n    }\n\n    /**\n     * Accepts events if property case insensitive starts with expected value\n     */\n    template <\n        typename Adapter = adapters::generic_string<wchar_t>,\n        typename T,\n        typename Comparer = starts_with<iequal_to<typename Adapter::value_type>>>\n    details::property_view_predicate<T, Adapter, Comparer> property_istarts_with(\n        const std::wstring &prop,\n        const T& expected)\n    {\n        return{ prop, expected, Adapter(), Comparer() };\n    }\n\n    /**\n    * Accepts events if property ends with expected value\n    */\n    template <\n        typename Adapter = adapters::generic_string<wchar_t>,\n        typename T,\n        typename Comparer = ends_with<std::equal_to<typename Adapter::value_type>>>\n        details::property_view_predicate<T, Adapter, Comparer> property_ends_with(\n            const std::wstring &prop,\n            const T& expected)\n    {\n        return{ prop, expected, Adapter(), Comparer() };\n    }\n\n    /**\n    * Accepts events if property case insensitive ends with expected value\n    */\n    template <\n        typename Adapter = adapters::generic_string<wchar_t>,\n        typename T,\n        typename Comparer = ends_with<iequal_to<typename Adapter::value_type>>>\n        details::property_view_predicate<T, Adapter, Comparer> property_iends_with(\n            const std::wstring &prop,\n            const T& expected)\n    {\n        return{ prop, expected, Adapter(), Comparer() };\n    }\n\n    /**\n     * <summary>\n     *   Accepts an event if its two component filters both accept the event.\n     * </summary>\n     */\n    template <typename T1, typename T2>\n    details::and_filter<T1, T2> and_filter(const T1 &t1, const T2 &t2)\n    {\n        return details::and_filter<T1, T2>(t1, t2);\n    }\n\n    /**\n     * <summary>\n     *   Accepts an event if either of its two component filters accept the event.\n     * </summary>\n     */\n    template <typename T1, typename T2>\n    details::or_filter<T1, T2> or_filter(const T1 &t1, const T2 &t2)\n    {\n        return details::or_filter<T1, T2>(t1, t2);\n    }\n\n    /**\n     * <summary>\n     *   Negates the filter that is given to it.\n     * </summary>\n     */\n    template <typename T1>\n    details::not_filter<T1> not_filter(const T1 &t1)\n    {\n        return details::not_filter<T1>(t1);\n    }\n\n} /* namespace predicates */ } /* namespace krabs */\n"
  },
  {
    "path": "libs/krabs/krabs/filtering/view_adapters.hpp",
    "content": "// Copyright (c) Microsoft. All rights reserved.\n// Licensed under the MIT license. See LICENSE file in the project root for full license information.\n\n#pragma once\n\n#include <string>\n\n#include \"../compiler_check.hpp\"\n#include \"../parser.hpp\"\n\nnamespace krabs { namespace predicates {\n\n    namespace adapters {\n\n        /**\n        * View adapter for counted_string strings\n        */\n        struct counted_string\n        {\n            using value_type = krabs::counted_string::value_type;\n            using const_iterator = krabs::counted_string::const_iterator;\n\n            collection_view<const_iterator> operator()(const property_info& propInfo) const\n            {\n                auto cs_ptr = reinterpret_cast<const krabs::counted_string*>(propInfo.pPropertyIndex_);\n                return krabs::view(cs_ptr->string(), cs_ptr->length());\n            }\n        };\n        \n        /**\n         * View adapter for fixed width and null-terminated strings\n         */\n        template <typename ElemT>\n        struct generic_string\n        {\n            using value_type = ElemT;\n            using const_iterator = const value_type*;\n\n            collection_view<const_iterator> operator()(const property_info& propInfo) const\n            {\n                auto pString = reinterpret_cast<const value_type*>(propInfo.pPropertyIndex_);\n                auto length = get_string_content_length(pString, propInfo.length_);\n\n                return krabs::view(pString, length);\n            }\n        };\n\n    } /* namespace adapters */\n\n} /* namespace predicates */ } /* namespace krabs */"
  },
  {
    "path": "libs/krabs/krabs/guid.hpp",
    "content": "// Copyright (c) Microsoft. All rights reserved.\n// Licensed under the MIT license. See LICENSE file in the project root for full license information.\n\n#pragma once\n\n#ifndef  WIN32_LEAN_AND_MEAN\n#define  WIN32_LEAN_AND_MEAN\n#endif\n\n#include <windows.h>\n#include <objbase.h>\n\n#include <new>\n#include <memory>\n#include <sstream>\n#include <iomanip>\n#include <cassert>\n\n#include \"compiler_check.hpp\"\n\nnamespace krabs {\n\n    /** <summary>\n     * Represents a GUID, allowing simplified construction from a string or\n     * Windows GUID structure.\n     * </summary>\n     */\n    class guid {\n    public:\n        guid(GUID guid);\n        guid(const std::wstring &guid);\n\n        bool operator==(const guid &rhs) const;\n        bool operator==(const GUID &rhs) const;\n\n        bool operator<(const guid &rhs) const;\n        bool operator<(const GUID &rhs) const;\n\n        operator GUID() const;\n        operator const GUID*() const;\n\n        /** <summary>\n          * Constructs a new random guid.\n          * </summary>\n          */\n        static inline guid random_guid();\n\n    private:\n        GUID guid_;\n\n        friend struct std::hash<guid>;\n    };\n\n    /** <summary>\n      * Helper functions for parsing GUID's.\n      * </summary>\n      */\n    class guid_parser {\n        // Implementing in Krabs instead of Lobsters so that we can test it in unmanaged code.\n    private:\n        // Number of characters in the UUID's 8-4-4-4-12 string format.\n        static const size_t UUID_STRING_LENGTH = 36;\n\n        static const unsigned char DELIMITER = '-';\n\n        // Expected character positions of runs of hex digits in 8-4-4-4-12 format, e.g.\n        // 00000000-0000-0000-0000-000000000000\n        // Names correspond to struct members of GUID.\n        static const size_t STR_POSITION_DATA1 = 0;\n        static const size_t STR_POSITION_DATA2 = 8 + 1;\n        static const size_t STR_POSITION_DATA3 = STR_POSITION_DATA2 + 4 + 1;\n        static const size_t STR_POSITION_DATA4_PART1 = STR_POSITION_DATA3 + 4 + 1;\n        static const size_t STR_POSITION_DATA4_PART2 = STR_POSITION_DATA4_PART1 + 4 + 1;\n\n    public:\n        // str_input must have at least 2 valid chars in allocated buffer\n        static bool hex_octet_to_byte(const char* str_input, unsigned char& byte_output);\n        // str_input must have at least 2*sizeof(T) valid chars in allocated buffer\n        template<typename T>\n        static bool hex_string_to_number(const char* str_input, T& int_output);\n        // str_input must have at least 2*byte_count valid chars in allocated buffer,\n        // and byte_output must have at least byte_count bytes in allocated buffer\n        static bool hex_string_to_bytes(const char* str_input, unsigned char* byte_output, size_t byte_count);\n        \n        /** <summary>\n          * Parses GUID of \"D\" format. For example, the nil GUID would be \"00000000-0000-0000-0000-000000000000\".\n          * See: https://docs.microsoft.com/en-us/dotnet/api/system.guid.tostring?view=netframework-4.8\n          *\n          * (str) must have at least (length) valid characters for memory safety. A null terminator is not\n          * required. Instead, (length) is used for the bounds check.\n          *\n          * Returns the parsed GUID. Throws a std::runtime_error if there is a bounds error or format error.\n          *\n          * This function is for performance, to help deal with container ID extended data, which has no null\n          * terminator, which would force us to clone the data to append a null terminator in order to use\n          * existing GUID parsing functions.\n          * </summary>\n          */\n        static GUID parse_guid(const char* str, unsigned int length);\n    };\n\n    // Implementation\n    // ------------------------------------------------------------------------\n\n    inline guid::guid(GUID guid)\n        : guid_(guid)\n    {}\n\n    inline guid::guid(const std::wstring &guid)\n    {\n        HRESULT hr = CLSIDFromString(guid.c_str(), &guid_);\n        if (FAILED(hr)) {\n#pragma warning(push)\n#pragma warning(disable: 4244) // narrowing guid wchar_t to char for this error message\n            std::string guidStr(guid.begin(), guid.end());\n#pragma warning(pop)\n            std::stringstream stream;\n            stream << \"Error in constructing guid from string (\";\n            stream << guidStr;\n            stream << \"), hr = 0x\";\n            stream << std::hex << hr;\n            throw std::runtime_error(stream.str());\n        }\n    }\n\n    inline bool guid::operator==(const guid &rhs) const\n    {\n        return (0 == memcmp(&guid_, &rhs.guid_, sizeof(GUID)));\n    }\n\n    inline bool guid::operator==(const GUID &rhs) const\n    {\n        return (0 == memcmp(&guid_, &rhs, sizeof(GUID)));\n    }\n\n    inline bool guid::operator<(const guid &rhs) const\n    {\n        return (memcmp(&guid_, &rhs.guid_, sizeof(guid_)) < 0);\n    }\n\n    inline bool guid::operator<(const GUID &rhs) const\n    {\n        return (memcmp(&guid_, &rhs, sizeof(guid_)) < 0);\n    }\n\n    inline guid::operator GUID() const\n    {\n        return guid_;\n    }\n\n    inline guid::operator const GUID*() const\n    {\n        return &guid_;\n    }\n\n    inline guid guid::random_guid()\n    {\n        GUID tmpGuid;\n        CoCreateGuid(&tmpGuid);\n        return guid(tmpGuid);\n    }\n\n    struct CoTaskMemDeleter {\n        void operator()(wchar_t *mem) {\n            CoTaskMemFree(mem);\n        }\n    };\n\n    inline bool guid_parser::hex_octet_to_byte(const char* str_input, unsigned char& byte_output)\n    {\n        // Accepts chars '0' through '9' (0x30 to 0x39),\n        // 'A' through 'F' (0x41 to 0x46)\n        // 'a' through 'f' (0x61 to 0x66)\n\n        // Narrow the value later, for safety checking.\n        auto value = 0;\n        // most significant digit in the octet\n        auto msd = str_input[0];\n        // least significant digit in the octet\n        auto lsd = str_input[1];\n\n        if (msd >= '0' && msd <= '9')\n        {\n            value |= ((int)msd & 0x0F) << 4;\n        }\n        else if ((msd >= 'A' && msd <= 'F') || (msd >= 'a' && msd <= 'f'))\n        {\n            value |= (((int)msd & 0x0F) + 9) << 4;\n        }\n        else\n        {\n            return false;\n        }\n\n        if (lsd >= '0' && lsd <= '9')\n        {\n            value |= ((int)lsd & 0x0F);\n        }\n        else if ((lsd >= 'A' && lsd <= 'F') || (lsd >= 'a' && lsd <= 'f'))\n        {\n            value |= (((int)lsd & 0x0F) + 9);\n        }\n        else\n        {\n            return false;\n        }\n\n        assert(value >= 0 && value <= UCHAR_MAX);\n        byte_output = static_cast<unsigned char>(value);\n        return true;\n    }\n\n    template<typename T>\n    bool guid_parser::hex_string_to_number(const char* str_input, T& int_output)\n    {\n        auto byte_count = sizeof(T);\n        T value = 0;\n        unsigned char byte = 0;\n\n        for (size_t i = 0; i < byte_count; i++)\n        {\n            if (!guid_parser::hex_octet_to_byte(str_input + i * 2, byte))\n            {\n                return false;\n            }\n\n            value = (value << 8) | static_cast<T>(byte);\n        }\n\n        int_output = value;\n        return true;\n    }\n\n    inline bool guid_parser::hex_string_to_bytes(const char* str_input, unsigned char* byte_output, size_t byte_count)\n    {\n        for (size_t i = 0; i < byte_count; i++)\n        {\n            if (!hex_octet_to_byte(str_input + (i * 2), byte_output[i]))\n            {\n                return false;\n            }\n        }\n\n        return true;\n    }\n\n    /** <summary>\n      * Parses GUID of \"D\" format. For example, the nil GUID would be \"00000000-0000-0000-0000-000000000000\".\n      * See: https://docs.microsoft.com/en-us/dotnet/api/system.guid.tostring?view=netframework-4.8\n      *\n      * (str) must have at least (length) valid characters for memory safety. A null terminator is not\n      * required. Instead, (length) is used for the bounds check.\n      *\n      * Returns the parsed GUID. Throws a std::runtime_error if there is a bounds error or format error.\n      * \n      * This function is for performance, to help deal with container ID extended data, which has no null \n      * terminator, which would force us to clone the data to append a null terminator in order to use \n      * existing GUID parsing functions.\n      * </summary>\n      */\n    inline GUID guid_parser::parse_guid(const char* str, unsigned int length)\n    {\n        if (length != UUID_STRING_LENGTH)\n        {\n            std::stringstream message;\n            message << \"Input data has incorrect length. Expected \"\n                << UUID_STRING_LENGTH\n                << \", got \"\n                << length;\n            throw std::runtime_error(message.str());\n        }\n\n        GUID guid = { 0 };\n\n        // Check that hyphens are in expected places as a formatting issue.\n        if (str[STR_POSITION_DATA2 - 1] != DELIMITER ||\n            str[STR_POSITION_DATA3 - 1] != DELIMITER ||\n            str[STR_POSITION_DATA4_PART1 - 1] != DELIMITER ||\n            str[STR_POSITION_DATA4_PART2 - 1] != DELIMITER)\n        {\n            throw std::runtime_error(\"Missing a hyphen where one was expected.\");\n        }\n\n        // Use from_hex_string for Data1, Data2, and Data3 because of endianness of the data\n        // Use hex_string_to_bytes for Data4's array elements because it's byte by byte instead\n        auto success = guid_parser::hex_string_to_number(str + STR_POSITION_DATA1, guid.Data1)\n            && guid_parser::hex_string_to_number(str + STR_POSITION_DATA2, guid.Data2)\n            && guid_parser::hex_string_to_number(str + STR_POSITION_DATA3, guid.Data3)\n            && guid_parser::hex_string_to_bytes(str + STR_POSITION_DATA4_PART1, reinterpret_cast<unsigned char*>(&guid.Data4[0]), 2)\n            && guid_parser::hex_string_to_bytes(str + STR_POSITION_DATA4_PART2, reinterpret_cast<unsigned char*>(&guid.Data4[2]), 6);\n\n        if (!success)\n        {\n            throw std::runtime_error(\"GUID string contains non-hex digits where hex digits are expected.\");\n        }\n\n        return guid;\n    }\n}\n\nnamespace std\n{\n    /*\n    * Converts a krabs GUID to a wide string\n    */\n    inline std::wstring to_wstring(const krabs::guid& guid)\n    {\n        wchar_t* guidString;\n        HRESULT hr = StringFromCLSID(guid, &guidString);\n\n        if (FAILED(hr)) throw std::bad_alloc();\n\n        std::unique_ptr<wchar_t, krabs::CoTaskMemDeleter> managed(guidString);\n\n        return { managed.get() };\n    }\n\n    /*\n    * Converts a Windows GUID to a C string\n    */\n    inline std::string to_string(const GUID& guid)\n    {\n        char guid_string[37]; // 32 hex chars + 4 hyphens + null terminator\n        snprintf(\n            guid_string, sizeof(guid_string),\n            \"%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x\",\n            guid.Data1, guid.Data2, guid.Data3,\n            guid.Data4[0], guid.Data4[1], guid.Data4[2],\n            guid.Data4[3], guid.Data4[4], guid.Data4[5],\n            guid.Data4[6], guid.Data4[7]);\n        return guid_string;\n    }\n\n    template<>\n    struct std::hash<krabs::guid>\n    {\n        size_t operator()(const krabs::guid& guid) const\n        {\n            // This algorithm comes from .NET's reference source for Guid.GetHashCode()\n            return guid.guid_.Data1 ^\n                 ((guid.guid_.Data2    << 16) | guid.guid_.Data3   ) ^\n                 ((guid.guid_.Data4[2] << 24) | guid.guid_.Data4[7]);\n        }\n    };\n}\n"
  },
  {
    "path": "libs/krabs/krabs/kernel_guids.hpp",
    "content": "// Copyright (c) Microsoft. All rights reserved.\n// Licensed under the MIT license. See LICENSE file in the project root for full license information.\n\n#pragma once\n\n#include <guiddef.h>\n\n#include \"compiler_check.hpp\"\n\nnamespace krabs { namespace guids {\n\nDEFINE_GUID ( /* 45d8cccd-539f-4b72-a8b7-5c683142609a */\n    alpc,\n    0x45d8cccd,\n    0x539f,\n    0x4b72,\n    0xa8, 0xb7, 0x5c, 0x68, 0x31, 0x42, 0x60, 0x9a\n  );\n\nDEFINE_GUID ( /* 13976d09-a327-438c-950b-7f03192815c7 */\n    debug,\n    0x13976d09,\n    0xa327,\n    0x438c,\n    0x95, 0x0b, 0x7f, 0x03, 0x19, 0x28, 0x15, 0xc7\n  );\n\nDEFINE_GUID ( /* 3d6fa8d4-fe05-11d0-9dda-00c04fd7ba7c */\n    disk_io,\n    0x3d6fa8d4,\n    0xfe05,\n    0x11d0,\n    0x9d, 0xda, 0x00, 0xc0, 0x4f, 0xd7, 0xba, 0x7c\n  );\n\nDEFINE_GUID ( /* 01853a65-418f-4f36-aefc-dc0f1d2fd235 */\n    event_trace_config,\n    0x01853a65,\n    0x418f,\n    0x4f36,\n    0xae, 0xfc, 0xdc, 0x0f, 0x1d, 0x2f, 0xd2, 0x35\n  );\n\nDEFINE_GUID ( /* 90cbdc39-4a3e-11d1-84f4-0000f80464e3 */\n    file_io,\n    0x90cbdc39,\n    0x4a3e,\n    0x11d1,\n    0x84, 0xf4, 0x00, 0x00, 0xf8, 0x04, 0x64, 0xe3\n  );\n\nDEFINE_GUID ( /* 2cb15d1d-5fc1-11d2-abe1-00a0c911f518 */\n    image_load,\n    0x2cb15d1d,\n    0x5fc1,\n    0x11d2,\n    0xab, 0xe1, 0x00, 0xa0, 0xc9, 0x11, 0xf5, 0x18\n  );\n\nDEFINE_GUID ( /* 3d6fa8d3-fe05-11d0-9dda-00c04fd7ba7c */\n    page_fault,\n    0x3d6fa8d3,\n    0xfe05,\n    0x11d0,\n    0x9d, 0xda, 0x00, 0xc0, 0x4f, 0xd7, 0xba, 0x7c\n  );\n\nDEFINE_GUID ( /* ce1dbfb4-137e-4da6-87b0-3f59aa102cbc */\n    perf_info,\n    0xce1dbfb4,\n    0x137e,\n    0x4da6,\n    0x87, 0xb0, 0x3f, 0x59, 0xaa, 0x10, 0x2c, 0xbc\n  );\n\nDEFINE_GUID ( /* 3d6fa8d0-fe05-11d0-9dda-00c04fd7ba7c */\n    process,\n    0x3d6fa8d0,\n    0xfe05,\n    0x11d0,\n    0x9d, 0xda, 0x00, 0xc0, 0x4f, 0xd7, 0xba, 0x7c\n  );\n\nDEFINE_GUID ( /* AE53722E-C863-11d2-8659-00C04FA321A1 */\n    registry,\n    0xae53722e,\n    0xc863,\n    0x11d2,\n    0x86, 0x59, 0x0, 0xc0, 0x4f, 0xa3, 0x21, 0xa1\n  );\n\nDEFINE_GUID ( /* d837ca92-12b9-44a5-ad6a-3a65b3578aa8 */\n    split_io,\n    0xd837ca92,\n    0x12b9,\n    0x44a5,\n    0xad, 0x6a, 0x3a, 0x65, 0xb3, 0x57, 0x8a, 0xa8\n  );\n\nDEFINE_GUID ( /* 9a280ac0-c8e0-11d1-84e2-00c04fb998a2 */\n    tcp_ip,\n    0x9a280ac0,\n    0xc8e0,\n    0x11d1,\n    0x84, 0xe2, 0x00, 0xc0, 0x4f, 0xb9, 0x98, 0xa2\n  );\n\nDEFINE_GUID ( /* 3d6fa8d1-fe05-11d0-9dda-00c04fd7ba7c */\n    thread,\n    0x3d6fa8d1,\n    0xfe05,\n    0x11d0,\n    0x9d, 0xda, 0x00, 0xc0, 0x4f, 0xd7, 0xba, 0x7c\n  );\n\nDEFINE_GUID ( /* bf3a50c5-a9c9-4988-a005-2df0b7c80f80 */\n    udp_ip,\n    0xbf3a50c5,\n    0xa9c9,\n    0x4988,\n    0xa0, 0x05, 0x2d, 0xf0, 0xb7, 0xc8, 0x0f, 0x80\n  );\n\nDEFINE_GUID ( /* 9e814aad-3204-11d2-9a82-006008a86939 */\n    system_trace,\n    0x9e814aad,\n    0x3204,\n    0x11d2,\n    0x9a, 0x82, 0x00, 0x60, 0x08, 0xa8, 0x69, 0x39);\n\nDEFINE_GUID( /* 89497f50-effe-4440-8cf2-ce6b1cdcaca7 */\n    ob_trace,\n    0x89497f50,\n    0xeffe,\n    0x4440,\n    0x8c, 0xf2, 0xce, 0x6b, 0x1c, 0xdc, 0xac, 0xa7);\n\nDEFINE_GUID( /* 0268a8b6-74fd-4302-9dd0-6e8f1795c0cf */\n    pool_trace,\n    0x0268a8b6,\n    0x74fd,\n    0x4302,\n    0x9d, 0xd0, 0x6e, 0x8f, 0x17, 0x95, 0xc0, 0xcf);\n\nDEFINE_GUID( /* 68fdd900-4a3e-11d1-84f4-0000f80464e3 */\n    event_trace,\n    0x68fdd900,\n    0x4a3e,\n    0x11d1,\n    0x84, 0xf4, 0x00, 0x00, 0xf8, 0x04, 0x64, 0xe3);\n\nDEFINE_GUID( /* 6a399ae0-4bc6-4de9-870b-3657f8947e7e */\n    lost_event,\n    0x6a399ae0,\n    0x4bc6,\n    0x4de9,\n    0x87, 0x0b, 0x36, 0x57, 0xf8, 0x94, 0x7e, 0x7e);\n\nDEFINE_GUID( /* 9aec974b-5b8e-4118-9b92-3186d8002ce5 */\n    ums_event,\n    0x9aec974b,\n    0x5b8e,\n    0x4118,\n    0x9b, 0x92, 0x31, 0x86, 0xd8, 0x00, 0x2c, 0xe5);\n\nDEFINE_GUID( /* def2fe46-7bd6-4b80-bd94-f57fe20d0ce3 */\n    stack_walk,\n    0xdef2fe46,\n    0x7bd6,\n    0x4b80,\n    0xbd, 0x94, 0xf5, 0x7f, 0xe2, 0x0d, 0x0c, 0xe3);\n\nDEFINE_GUID( /* e43445e0-0903-48c3-b878-ff0fccebdd04 */\n    power,\n    0xe43445e0,\n    0x0903,\n    0x48c3,\n    0xb8, 0x78, 0xff, 0x0f, 0xcc, 0xeb, 0xdd, 0x04);\n\nDEFINE_GUID( /* f8f10121-b617-4a56-868b-9df1b27fe32c */\n    mmcss_trace,\n    0xf8f10121,\n    0xb617,\n    0x4a56,\n    0x86, 0x8b, 0x9d, 0xf1, 0xb2, 0x7f, 0xe3, 0x2c);\n\nDEFINE_GUID( /* 3b9c9951-3480-4220-9377-9c8e5184f5cd */\n    rundown,\n    0x3b9c9951,\n    0x3480,\n    0x4220,\n    0x93, 0x77, 0x9c, 0x8e, 0x51, 0x84, 0xf5, 0xcd);\n\n} /* namespace guids */ } /* namespace krabs */\n"
  },
  {
    "path": "libs/krabs/krabs/kernel_providers.hpp",
    "content": "// Copyright (c) Microsoft. All rights reserved.\n// Licensed under the MIT license. See LICENSE file in the project root for full license information.\n\n#pragma once\n\n#include \"compiler_check.hpp\"\n#include \"kernel_guids.hpp\"\n#include \"perfinfo_groupmask.hpp\"\n#include \"provider.hpp\"\n\n#define INITGUID\n#include <Evntrace.h>\n\nnamespace krabs { namespace kernel {\n\n#define CREATE_CONVENIENCE_KERNEL_PROVIDER(__name__, __value__, __guid__)     \\\n    struct __name__ : public krabs::kernel_provider                           \\\n    {                                                                         \\\n        __name__()                                                            \\\n        : krabs::kernel_provider(__value__, __guid__)                         \\\n        {}                                                                    \\\n    };\n\n#define CREATE_CONVENIENCE_KERNEL_PROVIDER_MASK(__name__, __guid__, __mask__) \\\n    struct __name__ : public krabs::kernel_provider                           \\\n    {                                                                         \\\n        __name__()                                                            \\\n        : krabs::kernel_provider(__guid__, __mask__)                          \\\n        {}                                                                    \\\n    };\n\n    /**\n     * <summary>A provider that enables ALPC events.</summary>\n     */\n    CREATE_CONVENIENCE_KERNEL_PROVIDER(\n        alpc_provider,\n        EVENT_TRACE_FLAG_ALPC,\n        krabs::guids::alpc);\n\n     /**\n      * <summary>A provider that enables context switch events.</summary>\n      */\n     CREATE_CONVENIENCE_KERNEL_PROVIDER(\n         context_switch_provider,\n         EVENT_TRACE_FLAG_CSWITCH,\n         krabs::guids::thread);\n\n    /**\n     * <summary>A provider that enables debug print events.</summary>\n     */\n    CREATE_CONVENIENCE_KERNEL_PROVIDER(\n        debug_print_provider,\n        EVENT_TRACE_FLAG_DBGPRINT,\n        krabs::guids::debug);\n\n    /**\n     * <summary>A provider that enables file I/O name events.</summary>\n     */\n    CREATE_CONVENIENCE_KERNEL_PROVIDER(\n        disk_file_io_provider,\n        EVENT_TRACE_FLAG_DISK_FILE_IO,\n        krabs::guids::file_io);\n\n    /**\n     * <summary>A provider that enables disk I/O completion events.</summary>\n     */\n    CREATE_CONVENIENCE_KERNEL_PROVIDER(\n        disk_io_provider,\n        EVENT_TRACE_FLAG_DISK_IO,\n        krabs::guids::disk_io);\n\n    /**\n     * <summary>A provider that enables disk I/O start events.</summary>\n    */\n    CREATE_CONVENIENCE_KERNEL_PROVIDER(\n        disk_init_io_provider,\n        EVENT_TRACE_FLAG_DISK_IO_INIT,\n        krabs::guids::disk_io);\n\n    /**\n    * <summary>A provider that enables file I/O completion events.</summary>\n    */\n    CREATE_CONVENIENCE_KERNEL_PROVIDER(\n        file_io_provider,\n        EVENT_TRACE_FLAG_FILE_IO,\n        krabs::guids::file_io);\n\n    /**\n    * <summary>A provider that enables file I/O start events.</summary>\n    */\n    CREATE_CONVENIENCE_KERNEL_PROVIDER(\n        file_init_io_provider,\n        EVENT_TRACE_FLAG_FILE_IO_INIT,\n        krabs::guids::file_io);\n\n    /**\n     * <summary>A provider that enables thread dispatch events.</summary>\n     */\n    CREATE_CONVENIENCE_KERNEL_PROVIDER(\n        thread_dispatch_provider,\n        EVENT_TRACE_FLAG_DISPATCHER,\n        krabs::guids::thread);\n\n     /**\n      * <summary>A provider that enables device deferred procedure call events.</summary>\n      */\n     CREATE_CONVENIENCE_KERNEL_PROVIDER(\n         dpc_provider,\n         EVENT_TRACE_FLAG_DPC,\n         krabs::guids::perf_info);\n\n     /**\n      * <summary>A provider that enables driver events.</summary>\n      */\n     CREATE_CONVENIENCE_KERNEL_PROVIDER(\n         driver_provider,\n         EVENT_TRACE_FLAG_DRIVER,\n         krabs::guids::disk_io);\n\n    /**\n     * <summary>A provider that enables image load events.</summary>\n     */\n    CREATE_CONVENIENCE_KERNEL_PROVIDER(\n        image_load_provider,\n        EVENT_TRACE_FLAG_IMAGE_LOAD,\n        krabs::guids::image_load);\n\n     /**\n      * <summary>A provider that enables interrupt events.</summary>\n      */\n     CREATE_CONVENIENCE_KERNEL_PROVIDER(\n         interrupt_provider,\n         EVENT_TRACE_FLAG_INTERRUPT,\n         krabs::guids::perf_info);\n\n    /**\n     * <summary>A provider that enables memory hard fault events.</summary>\n     */\n    CREATE_CONVENIENCE_KERNEL_PROVIDER(\n        memory_hard_fault_provider,\n        EVENT_TRACE_FLAG_MEMORY_HARD_FAULTS,\n        krabs::guids::page_fault);\n\n    /**\n     * <summary>A provider that enables memory page fault events.</summary>\n     */\n    CREATE_CONVENIENCE_KERNEL_PROVIDER(\n        memory_page_fault_provider,\n        EVENT_TRACE_FLAG_MEMORY_PAGE_FAULTS,\n        krabs::guids::page_fault);\n\n    /**\n     * <summary>A provider that enables network tcp/ip events.</summary>\n     */\n    CREATE_CONVENIENCE_KERNEL_PROVIDER(\n        network_tcpip_provider,\n        EVENT_TRACE_FLAG_NETWORK_TCPIP,\n        krabs::guids::tcp_ip);\n\n    /**\n     * <summary>A provider that enables process events.</summary>\n     */\n    CREATE_CONVENIENCE_KERNEL_PROVIDER(\n        process_provider,\n        EVENT_TRACE_FLAG_PROCESS,\n        krabs::guids::process);\n\n    /**\n     * <summary>A provider that enables process counter events.</summary>\n     */\n    CREATE_CONVENIENCE_KERNEL_PROVIDER(\n        process_counter_provider,\n        EVENT_TRACE_FLAG_PROCESS_COUNTERS,\n        krabs::guids::process);\n\n    /**\n     * <summary>A provider that enables profiling events.</summary>\n     */\n    CREATE_CONVENIENCE_KERNEL_PROVIDER(\n        profile_provider,\n        EVENT_TRACE_FLAG_PROFILE,\n        krabs::guids::perf_info);\n\n    /**\n     * <summary>A provider that enables registry events.</summary>\n     */\n    CREATE_CONVENIENCE_KERNEL_PROVIDER(\n        registry_provider,\n        EVENT_TRACE_FLAG_REGISTRY,\n        krabs::guids::registry);\n\n    /**\n     * <summary>A provider that enables split I/O events.</summary>\n     */\n    CREATE_CONVENIENCE_KERNEL_PROVIDER(\n        split_io_provider,\n        EVENT_TRACE_FLAG_SPLIT_IO,\n        krabs::guids::split_io);\n\n    /**\n     * <summary>A provider that enables system call events.</summary>\n     */\n    CREATE_CONVENIENCE_KERNEL_PROVIDER(\n        system_call_provider,\n        EVENT_TRACE_FLAG_SYSTEMCALL,\n        krabs::guids::perf_info);\n\n    /**\n     * <summary>A provider that enables thread start and stop events.</summary>\n     */\n    CREATE_CONVENIENCE_KERNEL_PROVIDER(\n        thread_provider,\n        EVENT_TRACE_FLAG_THREAD,\n        krabs::guids::thread);\n\n     /**\n      * <summary>A provider that enables file map and unmap (excluding images) events.</summary>\n      */\n     CREATE_CONVENIENCE_KERNEL_PROVIDER(\n         vamap_provider,\n         EVENT_TRACE_FLAG_VAMAP,\n         krabs::guids::file_io);\n\n     /**\n      * <summary>A provider that enables VirtualAlloc and VirtualFree events.</summary>\n      */\n     CREATE_CONVENIENCE_KERNEL_PROVIDER(\n         virtual_alloc_provider,\n         EVENT_TRACE_FLAG_VIRTUAL_ALLOC,\n         krabs::guids::page_fault);\n\n     /**\n      * <summary>A provider that enables Object Manager events.</summary>\n      */\n     CREATE_CONVENIENCE_KERNEL_PROVIDER_MASK(\n         object_manager_provider,\n         krabs::guids::ob_trace,\n         PERF_OB_HANDLE);\n\n#undef CREATE_CONVENIENCE_KERNEL_PROVIDER\n#undef CREATE_CONVENIENCE_KERNEL_PROVIDER_MASK\n\n} /* namespace kernel */ } /* namespace krabs */\n"
  },
  {
    "path": "libs/krabs/krabs/kt.hpp",
    "content": "// Copyright (c) Microsoft. All rights reserved.\n// Licensed under the MIT license. See LICENSE file in the project root for full license information.\n\n#pragma once\n\n#include \"compiler_check.hpp\"\n#include \"kernel_guids.hpp\"\n#include \"perfinfo_groupmask.hpp\"\n#include \"provider.hpp\"\n#include \"trace.hpp\"\n#include \"ut.hpp\"\n#include \"version_helpers.hpp\"\n\n#include <Evntrace.h>\n\nnamespace krabs { namespace details {\n\n    /**\n     * <summary>\n     *   Used as a template argument to a trace instance. This class implements\n     *   code paths for kernel traces. Should never be used or seen by client\n     *   code.\n     * </summary>\n     */\n    struct kt {\n\n        typedef krabs::kernel_provider provider_type;\n\n        /**\n         * <summary>\n         *   Used to assign a name to the trace instance that is being\n         *   instantiated.\n         * </summary>\n         * <remarks>\n         *   In pre-Win8 days, there could only be a single kernel trace\n         *   instance on an entire machine, and that instance had to be named\n         *   a particular name. This restriction was loosened in Win8, but\n         *   the trace still needs to do the right thing on older OSes.\n         * </remarks>\n         */\n        static const std::wstring enforce_name_policy(\n            const std::wstring &name);\n\n        /**\n         * <summary>\n         *   Generates a value that fills the EnableFlags field in an\n         *   EVENT_TRACE_PROPERTIES structure. This controls the providers that\n         *   get enabled for a kernel trace.\n         * </summary>\n         */\n        static const unsigned long construct_enable_flags(\n            const krabs::trace<krabs::details::kt> &trace);\n\n        /** \n         * <summary>\n         *   Enables the providers that are attached to the given trace.\n         * </summary>\n         */\n        static void enable_providers(\n            const krabs::trace<krabs::details::kt> &trace);\n\n        /**\n         * <summary>\n         *   Enables the configured kernel rundown flags.\n         * </summary>\n         * <remarks>\n         *   This ETW feature is undocumented and should be used with caution.\n         * </remarks>\n         */\n        static void enable_rundown(\n            const krabs::trace<krabs::details::kt>& trace);\n\n        /**\n         * <summary>\n         *   Decides to forward an event to any of the providers in the trace.\n         * </summary>\n         */\n        static void forward_events(\n            const EVENT_RECORD &record,\n            const krabs::trace<krabs::details::kt> &trace);\n\n        /**\n         * <summary>\n         *   Sets the ETW trace log file mode.\n         * </summary>\n         */\n        static unsigned long augment_file_mode();\n\n        /**\n         * <summary>\n         *   Returns the GUID of the trace session.\n         * </summary>\n         */\n        static krabs::guid get_trace_guid();\n\n    };\n\n    // Implementation\n    // ------------------------------------------------------------------------\n\n    inline const std::wstring kt::enforce_name_policy(\n        const std::wstring &name_hint)\n    {\n        if (IsWindows8OrGreater()) {\n            return krabs::details::ut::enforce_name_policy(name_hint);\n        }\n\n        return KERNEL_LOGGER_NAME;\n    }\n\n    inline const unsigned long kt::construct_enable_flags(\n        const krabs::trace<krabs::details::kt> &trace)\n    {\n        unsigned long flags = 0;\n        for (auto &provider : trace.providers_) {\n            flags |= provider.get().flags();\n        }\n\n        return flags;\n    }\n\n    inline void kt::enable_providers(\n        const krabs::trace<krabs::details::kt> &trace)\n    {\n        EVENT_TRACE_GROUPMASK_INFORMATION gmi = { 0 };\n        gmi.EventTraceInformationClass = EventTraceGroupMaskInformation;\n        gmi.TraceHandle = trace.registrationHandle_;\n\n        // initialise EventTraceGroupMasks to the values that have been enabled via the trace flags\n        ULONG status = NtQuerySystemInformation(SystemPerformanceTraceInformation, &gmi, sizeof(gmi), nullptr);\n        error_check_common_conditions(status);\n\n        auto group_mask_set = false;\n        for (auto& provider : trace.providers_) {\n            auto group = provider.get().group_mask();\n            PERFINFO_OR_GROUP_WITH_GROUPMASK(group, &(gmi.EventTraceGroupMasks));\n            group_mask_set |= (group != 0);\n        }\n\n        if (group_mask_set) {\n            // This will fail on Windows 7, so only call it if truly neccessary\n            status = NtSetSystemInformation(SystemPerformanceTraceInformation, &gmi, sizeof(gmi));\n            error_check_common_conditions(status);\n        }\n\n        return;\n    }\n\n    inline void kt::enable_rundown(\n        const krabs::trace<krabs::details::kt>& trace)\n    {\n        bool rundown_enabled = false;\n        ULONG rundown_flags = 0;\n        for (auto& provider : trace.providers_) {\n            rundown_enabled |= provider.get().rundown_enabled();\n            rundown_flags |= provider.get().rundown_flags();\n        }\n\n        if (rundown_enabled) {\n            ULONG status = EnableTraceEx2(trace.registrationHandle_,\n                                          &krabs::guids::rundown,\n                                          EVENT_CONTROL_CODE_ENABLE_PROVIDER,\n                                          0,\n                                          rundown_flags,\n                                          0,\n                                          0,\n                                          NULL);\n            error_check_common_conditions(status);\n        }\n    }\n\n\n    inline void kt::forward_events(\n        const EVENT_RECORD &record,\n        const krabs::trace<krabs::details::kt> &trace)\n    {\n        for (auto &provider : trace.providers_) {\n            if (provider.get().id() == record.EventHeader.ProviderId) {\n                provider.get().on_event(record, trace.context_);\n                return;\n            }\n        }\n\n        if (trace.default_callback_ != nullptr)\n            trace.default_callback_(record, trace.context_);\n    }\n\n    inline unsigned long kt::augment_file_mode()\n    {\n        if (IsWindows8OrGreater()) {\n            return EVENT_TRACE_SYSTEM_LOGGER_MODE;\n        }\n\n        return 0;\n    }\n\n    inline krabs::guid kt::get_trace_guid()\n    {\n        if (IsWindows8OrGreater()) {\n            return krabs::guid::random_guid();\n        }\n\n        return krabs::guid(SystemTraceControlGuid);\n    }\n\n} /* namespace details */ } /* namespace krabs */\n"
  },
  {
    "path": "libs/krabs/krabs/parse_types.hpp",
    "content": "// Copyright (c) Microsoft. All rights reserved.\n// Licensed under the MIT license. See LICENSE file in the project root for full license information.\n\n#pragma once\n\n#ifndef  WIN32_LEAN_AND_MEAN\n#define  WIN32_LEAN_AND_MEAN\n#endif\n\n#include <windows.h>\n#include <winsock2.h>\n#include <ws2tcpip.h>\n#include <sddl.h>\n\n#include <vector>\n#include <type_traits>\n\n#include \"compiler_check.hpp\"\n\nnamespace krabs {\n\n    /**\n     * <summary>\n     *   Provided entirely for code clarity purposes.\n     *   Indicates that the number is intended to be used as an ID\n     * </summary>\n     * <remarks>\n     *   This should be turned into an _id user defined literal when our\n     *   compiler decides to catch up to the times.\n     * </remarks>\n     * <example>\n     *     id(1000);\n     * </example>\n     */\n     template <typename T>\n     T id(T n)\n     {\n        return n;\n     }\n\n    /**\n     * <summary>\n     *   Provided entirely for code clarity purposes.\n     *   Indicates that the number is intended to be used as a version\n     * </summary>\n     * <remarks>\n     *   This should be turned into a _vers user defined literal when our\n     *   compiler decides to catch up to the times.\n     * </remarks>\n     * <example>\n     *     id(1000);\n     * </example>\n     */\n     template <typename T>\n     T version(T n)\n     {\n        return n;\n     }\n\n    /**\n     * <summary>\n     *   Provided entirely for code clarity purposes.\n     *   Indicates that the number is intended to be used as an opcode\n     * </summary>\n     * <remarks>\n     *   This should be turned into a _opcode user defined literal when our\n     *   compiler decides to catch up to the times.\n     * </remarks>\n     * <example>\n     *     opcode(1000);\n     * </example>\n     */\n     template <typename T>\n     T opcode(T n)\n     {\n        return n;\n     }\n\n\n    /**\n     * <summary>\n     * Used to discriminate between hex ints and regular ints in ETW events.\n     * </summary>\n     * <remarks>\n     * Q: Why in the world? I can't even.\n     * A: ETW differentiates between hexints and regular ints. When\n     *    record_builder validates that the input type matches the type\n     *    specified in the schema, getting this wrong will cause an\n     *    exception. A quick little type wrapper like this lets us\n     *    discriminate based on the type and everything turns out better.\n     * </remarks>\n     */\n    struct hexint32 {\n        hexint32(int v)\n        : value(v)\n        {}\n\n        int value;\n    };\n\n    struct hexint64 {\n        hexint64(long long v)\n        : value(v)\n        {}\n\n        long long value;\n    };\n\n    /**\n     * <summary>\n     * Used to support parsing and creation of binary ETW fields.\n     * </summary>\n     */\n    struct binary {\n    public:\n        binary() : bytes_() { }\n\n        binary(const BYTE* start, size_t n)\n        : bytes_(start, start + n)\n        { }\n\n        const std::vector<BYTE>& bytes() const\n        {\n            return bytes_;\n        }\n\n    private:\n        std::vector<BYTE> bytes_;\n    };\n\n    template<typename T>\n    binary make_binary(const T& value, size_t n)\n    {\n        const auto start = (BYTE*)&value;\n        return binary(start, n);\n    }\n\n    /**\n     * <summary>\n     * Used to handle parsing of IPv4 and IPv6 fields in an ETW record.\n     * This is used in the parser class in a template specialization.\n     * </summary>\n     */\n    struct ip_address {\n        union {\n            DWORD v4;\n            BYTE v6[16];\n        };\n        bool is_ipv6;\n\n        static ip_address from_ipv6(const BYTE* bytes)\n        {\n            ip_address addr;\n            addr.is_ipv6 = true;\n            memcpy_s(addr.v6, 16, bytes, 16);\n            return addr;\n        }\n\n        static ip_address from_ipv4(DWORD val)\n        {\n            ip_address addr;\n            addr.is_ipv6 = false;\n            addr.v4 = val;\n            return addr;\n        }\n\n        ip_address() {}\n     };\n\n    /**\n    * <summary>\n    * Used to handle parsing of socket addresses in\n    * network order. This union is a convenient wrapper\n    * around the type IPv4 and IPv6 types provided by\n    * the Winsock (v2) APIs.\n    * </summary>\n    */\n    struct socket_address {\n        union {\n            struct sockaddr sa;\n            struct sockaddr_in sa_in;\n            struct sockaddr_in6 sa_in6;\n            struct sockaddr_storage sa_stor;\n        };\n        size_t size;\n\n        static socket_address from_bytes(const BYTE* bytes, size_t size_in_bytes)\n        {\n            socket_address sa;\n            memcpy_s(&(sa.sa_stor), sizeof sa.sa_stor, bytes, size_in_bytes);\n            sa.size = size_in_bytes;\n            return sa;\n        }\n    };\n\n    /**\n     * <summary>\n     * Holds information about an property extracted from the etw schema\n     * </summary>\n     */\n    struct property_info {\n        const BYTE *pPropertyIndex_;\n        const EVENT_PROPERTY_INFO *pEventPropertyInfo_;\n        ULONG length_;\n\n        property_info(\n            const BYTE *offset,\n            const EVENT_PROPERTY_INFO &evtPropInfo,\n            ULONG length)\n            : pPropertyIndex_(offset)\n            , pEventPropertyInfo_(&evtPropInfo)\n            , length_(length)\n        { }\n\n        property_info()\n            : pPropertyIndex_(nullptr)\n            , pEventPropertyInfo_(nullptr)\n            , length_(0)\n        { }\n\n        inline bool found() const\n        {\n            return pPropertyIndex_ != nullptr;\n        }\n    };\n\n    /**\n    * <summary>\n    * Used to handle parsing of SIDs from either a\n    * SID or WBEMSID property\n    * </summary>\n    */\n    struct sid {\n        // SIDs are variable-length\n        // So the 'best' way to store them is to convert to a string\n        // The other-end can either print the string or call ConvertStringSidToSidA\n        // to get the SID back\n        std::string sid_string;\n\n        static sid from_bytes(const BYTE* bytes, size_t size_in_bytes)\n        {\n            sid ws;\n            LPSTR temp_sid_string;\n            UNREFERENCED_PARAMETER(size_in_bytes);\n\n            if (!ConvertSidToStringSidA((PSID)bytes, &temp_sid_string)) {\n                throw std::runtime_error(\n                    \"Failed to get a SID from a property\");\n            }\n            ws.sid_string = temp_sid_string;\n            LocalFree(temp_sid_string);\n            return ws;\n        }\n\n    private:\n    };\n\n    /**\n    * <summary>\n    * Used to handle parsing of Pointer Address types.\n    * </summary>\n    */\n    struct pointer {\n        /**\n        * We store the pointer as an uint64_t, as it is highly unlikley\n        * to be pointing to somewhere accessible to our process\n        */\n        uint64_t address;\n\n        static pointer from_bytes(const BYTE* bytes, size_t size_in_bytes)\n        {\n            pointer pt;\n\n            // If 32-Bit, first parse as a uint32\n            // Then we can 'cast' that to our uint64_t\n            if (size_in_bytes == sizeof(uint32_t)) {\n                pt.address = *reinterpret_cast<const uint32_t*>(bytes);\n            }\n            else if (size_in_bytes == sizeof(uint64_t)) {\n                pt.address = *reinterpret_cast<const uint64_t*>(bytes);\n            }\n            else {\n                throw std::runtime_error(\n                    \"Failed to get a POINTER from a property\");\n            }\n\n            return pt;\n        }\n\n    private:\n    };\n\n\n    /**\n     * <summary>\n     * Used to handle parsing of CountedStrings in an ETW Record.\n     * This is used in the parser class in a template specialization.\n     * </summary>\n     */\n#pragma pack(push,1)\n    struct counted_string {\n        using value_type = wchar_t;\n        using reference = value_type&;\n        using pointer = value_type*;\n        using const_reference = const value_type&;\n        using const_pointer = const value_type*;\n        using iterator = value_type*;\n        using const_iterator = const value_type*;\n\n        /**\n         * size of the string in bytes\n         */\n        uint16_t size_;\n        wchar_t string_[1];\n\n        const_pointer string() const\n        {\n            return string_;\n        }\n\n        size_t length() const\n        {\n            return size_ / sizeof(value_type);\n        }\n    };\n#pragma pack(pop)\n\n    static_assert(std::is_trivial<counted_string>::value && std::is_standard_layout<counted_string>::value , \"Do not modify counted_string\");\n\n} /* namespace krabs */\n"
  },
  {
    "path": "libs/krabs/krabs/parser.hpp",
    "content": "// Copyright (c) Microsoft. All rights reserved.\n// Licensed under the MIT license. See LICENSE file in the project root for full license information.\n\n#pragma once\n\n#include <cassert>\n#include <deque>\n#include <utility>\n#include <stdexcept>\n\n#include <functional>\n\n#include \"compiler_check.hpp\"\n#include \"collection_view.hpp\"\n#include \"property.hpp\"\n#include \"parse_types.hpp\"\n#include \"size_provider.hpp\"\n#include \"tdh_helpers.hpp\"\n\nnamespace krabs {\n\n    class schema;\n\n    /**\n     * <summary>\n     * Used to parse specific properties out of an event schema.\n     * </summary>\n     * <remarks>\n     * The parser class dodges the task of trying to validate that the expected\n     * type of a field matches the actual type of a field -- the onus is on\n     * client code to get this right.\n     * </remarks>\n     */\n    class parser {\n    public:\n\n        /**\n         * <summary>\n         * Constructs an event parser from an event schema.\n         * </summary>\n         * <example>\n         *     void on_event(const EVENT_RECORD &record)\n         *     {\n         *         krabs::schema schema(record);\n         *         krabs::parser parser(schema);\n         *     }\n         * </example>\n         */\n        parser(const schema &);\n\n        /**\n         * <summary>\n         * Returns an iterator that returns each property in the event.\n         * </summary>\n         * <example>\n         *    void on_event(const EVENT_RECORD &record)\n         *    {\n         *        krabs::schema schema(record);\n         *        krabs::parser parser(schema);\n         *        for (property &property : parser.properties())\n         *        {\n         *            // ...\n         *        }\n         *    }\n         * </example>\n         */\n        property_iterator properties() const;\n\n        /**\n         * <summary>\n         * Attempts to retrieve the given property by name and type.\n         * </summary>\n         * <remarks>\n         * Type hinting here is taken as the authoritative source. There is no\n         * validation that the request for type is correct.\n         * </remarks>\n         */\n        template <typename T>\n        bool try_parse(const std::wstring &name, T &out);\n\n        /**\n         * <summary>\n         * Attempts to parse the given property by name and type. If the\n         * property does not exist, an exception is thrown.\n         * </summary>\n         */\n        template <typename T>\n        T parse(const std::wstring &name);\n\n        template <typename Adapter>\n        auto view_of(const std::wstring &name, Adapter &adapter) -> collection_view<typename Adapter::const_iterator>;\n\n    private:\n        property_info find_property(const std::wstring &name);\n        void cache_property(const wchar_t *name, property_info info);\n\n    private:\n        const schema &schema_;\n        const BYTE *pEndBuffer_;\n        BYTE *pBufferIndex_;\n        ULONG lastPropertyIndex_;\n\n        // Maintain a mapping from property name to blob data index.\n        std::deque<std::pair<const wchar_t *, property_info>> propertyCache_;\n    };\n\n    // Implementation\n    // ------------------------------------------------------------------------\n\n    inline parser::parser(const schema &s)\n    : schema_(s)\n    , pEndBuffer_((BYTE*)s.record_.UserData + s.record_.UserDataLength)\n    , pBufferIndex_((BYTE*)s.record_.UserData)\n    , lastPropertyIndex_(0)\n    {}\n\n    inline property_iterator parser::properties() const\n    {\n        return property_iterator(schema_);\n    }\n\n    inline property_info parser::find_property(const std::wstring &name)\n    {\n        // A schema contains a collection of properties that are keyed by name.\n        // These properties are stored in a blob of bytes that needs to be\n        // interpreted according to information that is packaged up in the\n        // schema and that can be retrieved using the Tdh* APIs. This format\n        // requires a linear traversal over the blob, incrementing according to\n        // the contents within it. This is janky, so our strategy is to\n        // minimize this as much as possible via caching.\n\n        // The first step is to use our cache for the property to see if we've\n        // discovered it already.\n        for (auto &item : propertyCache_) {\n            if (name == item.first) {\n                return item.second;\n            }\n        }\n\n        const ULONG totalPropCount = schema_.pSchema_->PropertyCount;\n\n        assert((pBufferIndex_ <= pEndBuffer_ && pBufferIndex_ >= schema_.record_.UserData) &&\n               \"invariant: we should've already thrown for falling off the edge\");\n\n        // accept that last property can be omitted from buffer. this happens if last property\n        // is string but empty and the provider strips the null terminator\n        assert((pBufferIndex_ == pEndBuffer_ ? ((totalPropCount - lastPropertyIndex_) <= 1)\n                                             : true)\n               && \"invariant: if we've exhausted our buffer, then we must've\"\n                  \"exhausted the properties as well\");\n\n        // We've not looked up this property before, so we have to do the work\n        // to find it. While we're going through the blob to find it, we'll\n        // remember what we've seen to save time later.\n        //\n        // Question: Why don't we just populate the cache before looking up any\n        //           properties and simplify our code (less state, etc)?\n        //\n        // Answer:   Doing that introduces overhead in the case that only a\n        //           subset of properties are needed. While this code is a bit\n        //           more complicated, we introduce no additional performance\n        //           overhead at runtime.\n        for (auto &i = lastPropertyIndex_; i < totalPropCount; ++i) {\n\n            auto &currentPropInfo = schema_.pSchema_->EventPropertyInfoArray[i];\n            const wchar_t *pName = reinterpret_cast<const wchar_t*>(\n                                        reinterpret_cast<BYTE*>(schema_.pSchema_) +\n                                        currentPropInfo.NameOffset);\n\n            ULONG propertyLength = size_provider::get_property_size(\n                                        pBufferIndex_,\n                                        pName,\n                                        schema_.record_,\n                                        currentPropInfo);\n\n            // verify that the length of the property doesn't exceed the buffer\n            if (pBufferIndex_ + propertyLength > pEndBuffer_) {\n                throw std::out_of_range(\"Property length past end of property buffer\");\n            }\n\n            property_info propInfo(pBufferIndex_, currentPropInfo, propertyLength);\n            cache_property(pName, propInfo);\n\n            // advance the buffer index since we've already processed this property\n            pBufferIndex_ += propertyLength;\n\n            // The property was found, return it\n            if (name == pName) {\n                // advance the index since we've already processed this property\n                ++i;\n                return propInfo;\n            }\n        }\n\n        // property wasn't found, return an empty propInfo\n        return property_info();\n    }\n\n    inline void parser::cache_property(const wchar_t *name, property_info propInfo)\n    {\n        propertyCache_.push_front(std::make_pair(name, propInfo));\n    }\n\n    inline void throw_if_property_not_found(const property_info &propInfo)\n    {\n        if (!propInfo.found()) {\n            throw std::runtime_error(\"Property with the given name does not exist\");\n        }\n    }\n\n    template <typename T>\n    size_t get_string_content_length(const T* string, size_t lengthBytes)\n    {\n        // for some string types the length includes the null terminator\n        // so we need to find the length of just the content part\n\n        T nullChar {0};\n        auto length = lengthBytes / sizeof(T);\n\n        for (auto i = length; i >= 1; --i)\n            if (string[i - 1] != nullChar) return i;\n\n        return 0;\n    }\n\n    // try_parse\n    // ------------------------------------------------------------------------\n\n    template <typename T>\n    bool parser::try_parse(const std::wstring &name, T &out)\n    {\n        try {\n            out = parse<T>(name);\n            return true;\n        }\n\n#ifndef NDEBUG\n        // in debug builds we want any mismatch asserts\n        // to get back to the caller. This is removed\n        // in release builds.\n        catch (const krabs::type_mismatch_assert&) {\n            throw;\n        }\n#endif // NDEBUG\n\n        catch (...) {\n            return false;\n        }\n    }\n\n    // parse\n    // ------------------------------------------------------------------------\n\n    template <typename T>\n    T parser::parse(const std::wstring &name)\n    {\n        auto propInfo = find_property(name);\n        throw_if_property_not_found(propInfo);\n\n        krabs::debug::assert_valid_assignment<T>(name, propInfo);\n\n        // ensure that size of the type we are requesting is\n        // the same size of the property in the event\n        if (sizeof(T) != propInfo.length_)\n            throw std::runtime_error(\"Property size doesn't match requested size\");\n\n        return *(T*)propInfo.pPropertyIndex_;\n    }\n\n    template<>\n    inline bool parser::parse<bool>(const std::wstring& name)\n    {\n        auto propInfo = find_property(name);\n        throw_if_property_not_found(propInfo);\n\n        krabs::debug::assert_valid_assignment<bool>(name, propInfo);\n\n        // Boolean in ETW is 4 bytes long\n        return static_cast<bool>(*(unsigned*)propInfo.pPropertyIndex_);\n    }\n\n    template <>\n    inline std::wstring parser::parse<std::wstring>(const std::wstring &name)\n    {\n        auto propInfo = find_property(name);\n        throw_if_property_not_found(propInfo);\n\n        krabs::debug::assert_valid_assignment<std::wstring>(name, propInfo);\n\n        auto string = reinterpret_cast<const wchar_t*>(propInfo.pPropertyIndex_);\n        auto length = get_string_content_length(string, propInfo.length_);\n\n        return std::wstring(string, length);\n    }\n\n    template <>\n    inline std::string parser::parse<std::string>(const std::wstring &name)\n    {\n        auto propInfo = find_property(name);\n        throw_if_property_not_found(propInfo);\n\n        krabs::debug::assert_valid_assignment<std::string>(name, propInfo);\n\n        auto string = reinterpret_cast<const char*>(propInfo.pPropertyIndex_);\n        auto length = get_string_content_length(string, propInfo.length_);\n\n        return std::string(string, length);\n    }\n\n    template<>\n    inline const counted_string* parser::parse<const counted_string*>(const std::wstring &name)\n    {\n        auto propInfo = find_property(name);\n        throw_if_property_not_found(propInfo);\n\n        krabs::debug::assert_valid_assignment<const counted_string*>(name, propInfo);\n\n        return reinterpret_cast<const counted_string*>(propInfo.pPropertyIndex_);\n    }\n\n    template<>\n    inline binary parser::parse<binary>(const std::wstring &name)\n    {\n        auto propInfo = find_property(name);\n        throw_if_property_not_found(propInfo);\n\n        // no type asserts for binary - anything can be read as binary\n\n        return binary(propInfo.pPropertyIndex_, propInfo.length_);\n    }\n\n    template<>\n    inline ip_address parser::parse<ip_address>(\n        const std::wstring &name)\n    {\n        auto propInfo = find_property(name);\n        throw_if_property_not_found(propInfo);\n\n        krabs::debug::assert_valid_assignment<ip_address>(name, propInfo);\n\n        auto outType = propInfo.pEventPropertyInfo_->nonStructType.OutType;\n\n        switch (outType) {\n        case TDH_OUTTYPE_IPV6:\n            return ip_address::from_ipv6(propInfo.pPropertyIndex_);\n\n        case TDH_OUTTYPE_IPV4:\n            return ip_address::from_ipv4(*(DWORD*)propInfo.pPropertyIndex_);\n\n        default:\n            throw std::runtime_error(\"IP Address was not IPV4 or IPV6\");\n        }\n    }\n\n    template<>\n    inline socket_address parser::parse<socket_address>(\n        const std::wstring &name)\n    {\n        auto propInfo = find_property(name);\n        throw_if_property_not_found(propInfo);\n\n        krabs::debug::assert_valid_assignment<socket_address>(name, propInfo);\n\n        return socket_address::from_bytes(propInfo.pPropertyIndex_, propInfo.length_);\n    }\n\n    template<>\n    inline sid parser::parse<sid>(\n        const std::wstring& name)\n    {\n        auto propInfo = find_property(name);\n        throw_if_property_not_found(propInfo);\n\n        krabs::debug::assert_valid_assignment<sid>(name, propInfo);\n        auto InType = propInfo.pEventPropertyInfo_->nonStructType.InType;\n\n        // A WBEMSID is actually a TOKEN_USER structure followed by the SID.\n        // We only care about the SID. From MSDN:\n        //\n        //      The size of the TOKEN_USER structure differs\n        //      depending on whether the events were generated on a 32 - bit\n        //      or 64 - bit architecture. Also the structure is aligned\n        //      on an 8 - byte boundary, so its size is 8 bytes on a\n        //      32 - bit computer and 16 bytes on a 64 - bit computer.\n        //      Doubling the pointer size handles both cases.\n        ULONG sid_start = 16;\n        if (EVENT_HEADER_FLAG_32_BIT_HEADER == (schema_.record_.EventHeader.Flags & EVENT_HEADER_FLAG_32_BIT_HEADER)) {\n            sid_start = 8;\n        }\n        switch (InType) {\n        case TDH_INTYPE_SID:\n            return sid::from_bytes(propInfo.pPropertyIndex_, propInfo.length_);\n        case TDH_INTYPE_WBEMSID:\n            // Safety measure to make sure we don't overflow\n            if (propInfo.length_ <= sid_start) {\n                throw std::runtime_error(\n                    \"Requested a WBEMSID property but data is too small\");\n            }\n            return sid::from_bytes(propInfo.pPropertyIndex_ + sid_start, propInfo.length_ - sid_start);\n\n        default:\n            throw std::runtime_error(\"SID was not a SID or WBEMSID\");\n        }\n    }\n\n    template<>\n    inline pointer parser::parse<pointer>(const std::wstring& name)\n    {\n        auto propInfo = find_property(name);\n        throw_if_property_not_found(propInfo);\n\n        krabs::debug::assert_valid_assignment<pointer>(name, propInfo);\n\n        return pointer::from_bytes(propInfo.pPropertyIndex_, propInfo.length_);\n    }\n\n    // view_of\n    // ------------------------------------------------------------------------\n\n    template <typename Adapter>\n    auto parser::view_of(const std::wstring &name, Adapter &adapter)\n        -> collection_view<typename Adapter::const_iterator>\n    {\n        auto propInfo = find_property(name);\n        throw_if_property_not_found(propInfo);\n\n        // TODO: type asserts?\n\n        return adapter(propInfo);\n    }\n}\n"
  },
  {
    "path": "libs/krabs/krabs/perfinfo_groupmask.hpp",
    "content": "// Copyright (c) Microsoft. All rights reserved.\n// Licensed under the MIT license. See LICENSE file in the project root for full license information.\n\n#ifndef PERFINFO_GROUPMASK_HPP\n#define PERFINFO_GROUPMASK_HPP\n\n#include <Windows.h>\n#pragma comment(lib, \"ntdll\")\n\n// https://geoffchappell.com/studies/windows/km/ntoskrnl/api/etw/tracesup/perfinfo_groupmask.htm\n\n#define PERF_MASK_INDEX         (0xe0000000)\n#define PERF_MASK_GROUP         (~PERF_MASK_INDEX)\n#define PERF_NUM_MASKS          8\n\ntypedef ULONG PERFINFO_MASK;\ntypedef struct _PERFINFO_GROUPMASK {\n    ULONG Masks[PERF_NUM_MASKS];\n} PERFINFO_GROUPMASK, *PPERFINFO_GROUPMASK;\n\n#define PERF_GET_MASK_INDEX(GM) (((GM) & PERF_MASK_INDEX) >> 29)\n#define PERF_GET_MASK_GROUP(GM) ((GM) & PERF_MASK_GROUP)\n#define PERFINFO_OR_GROUP_WITH_GROUPMASK(Group, pGroupMask) \\\n    (pGroupMask)->Masks[PERF_GET_MASK_INDEX(Group)] |= PERF_GET_MASK_GROUP(Group);\n\n// Masks[0]\n#define PERF_PROCESS            EVENT_TRACE_FLAG_PROCESS\n#define PERF_THREAD             EVENT_TRACE_FLAG_THREAD\n#define PERF_PROC_THREAD        EVENT_TRACE_FLAG_PROCESS | EVENT_TRACE_FLAG_THREAD\n#define PERF_LOADER             EVENT_TRACE_FLAG_IMAGE_LOAD\n#define PERF_PERF_COUNTER       EVENT_TRACE_FLAG_PROCESS_COUNTERS\n#define PERF_FILENAME           EVENT_TRACE_FLAG_DISK_FILE_IO\n#define PERF_DISK_IO            EVENT_TRACE_FLAG_DISK_FILE_IO | EVENT_TRACE_FLAG_DISK_IO\n#define PERF_DISK_IO_INIT       EVENT_TRACE_FLAG_DISK_IO_INIT\n#define PERF_ALL_FAULTS         EVENT_TRACE_FLAG_MEMORY_PAGE_FAULTS\n#define PERF_HARD_FAULTS        EVENT_TRACE_FLAG_MEMORY_HARD_FAULTS\n#define PERF_VAMAP              EVENT_TRACE_FLAG_VAMAP\n#define PERF_NETWORK            EVENT_TRACE_FLAG_NETWORK_TCPIP\n#define PERF_REGISTRY           EVENT_TRACE_FLAG_REGISTRY\n#define PERF_DBGPRINT           EVENT_TRACE_FLAG_DBGPRINT\n#define PERF_JOB                EVENT_TRACE_FLAG_JOB\n#define PERF_ALPC               EVENT_TRACE_FLAG_ALPC\n#define PERF_SPLIT_IO           EVENT_TRACE_FLAG_SPLIT_IO\n#define PERF_DEBUG_EVENTS       EVENT_TRACE_FLAG_DEBUG_EVENTS\n#define PERF_FILE_IO            EVENT_TRACE_FLAG_FILE_IO\n#define PERF_FILE_IO_INIT       EVENT_TRACE_FLAG_FILE_IO_INIT\n#define PERF_NO_SYSCONFIG       EVENT_TRACE_FLAG_NO_SYSCONFIG\n\n// Masks[1]\n#define PERF_MEMORY             0x20000001\n#define PERF_PROFILE            0x20000002  // equivalent to EVENT_TRACE_FLAG_PROFILE\n#define PERF_CONTEXT_SWITCH     0x20000004  // equivalent to EVENT_TRACE_FLAG_CSWITCH\n#define PERF_FOOTPRINT          0x20000008\n#define PERF_DRIVERS            0x20000010  // equivalent to EVENT_TRACE_FLAG_DRIVER\n#define PERF_REFSET             0x20000020\n#define PERF_POOL               0x20000040\n#define PERF_POOLTRACE          0x20000041\n#define PERF_DPC                0x20000080  // equivalent to EVENT_TRACE_FLAG_DPC\n#define PERF_COMPACT_CSWITCH    0x20000100\n#define PERF_DISPATCHER         0x20000200  // equivalent to EVENT_TRACE_FLAG_DISPATCHER\n#define PERF_PMC_PROFILE        0x20000400\n#define PERF_PROFILING          0x20000402\n#define PERF_PROCESS_INSWAP     0x20000800\n#define PERF_AFFINITY           0x20001000\n#define PERF_PRIORITY           0x20002000\n#define PERF_INTERRUPT          0x20004000  // equivalent to EVENT_TRACE_FLAG_INTERRUPT\n#define PERF_VIRTUAL_ALLOC      0x20008000  // equivalent to EVENT_TRACE_FLAG_VIRTUAL_ALLOC\n#define PERF_SPINLOCK           0x20010000\n#define PERF_SYNC_OBJECTS       0x20020000\n#define PERF_DPC_QUEUE          0x20040000\n#define PERF_MEMINFO            0x20080000\n#define PERF_CONTMEM_GEN        0x20100000\n#define PERF_SPINLOCK_CNTRS     0x20200000\n#define PERF_SPININSTR          0x20210000\n#define PERF_SESSION            0x20400000\n#define PERF_PFSECTION          0x20400000\n#define PERF_MEMINFO_WS         0x20800000\n#define PERF_KERNEL_QUEUE       0x21000000\n#define PERF_INTERRUPT_STEER    0x22000000\n#define PERF_SHOULD_YIELD       0x24000000\n#define PERF_WS                 0x28000000\n\n// Masks[2]\n#define PERF_ANTI_STARVATION    0x40000001\n#define PERF_PROCESS_FREEZE     0x40000002\n#define PERF_PFN_LIST           0x40000004\n#define PERF_WS_DETAIL          0x40000008\n#define PERF_WS_ENTRY           0x40000010\n#define PERF_HEAP               0x40000020\n#define PERF_SYSCALL            0x40000040  // equivalent to EVENT_TRACE_FLAG_SYSTEMCALL\n#define PERF_UMS                0x40000080\n#define PERF_BACKTRACE          0x40000100\n#define PERF_VULCAN             0x40000200\n#define PERF_OBJECTS            0x40000400\n#define PERF_EVENTS             0x40000800\n#define PERF_FULLTRACE          0x40001000\n#define PERF_DFSS               0x40002000\n#define PERF_PREFETCH           0x40004000\n#define PERF_PROCESSOR_IDLE     0x40008000\n#define PERF_CPU_CONFIG         0x40010000\n#define PERF_TIMER              0x40020000\n#define PERF_CLOCK_INTERRUPT    0x40040000\n#define PERF_LOAD_BALANCER      0x40080000\n#define PERF_CLOCK_TIMER        0x40100000\n#define PERF_IDLE_SELECTION     0x40200000\n#define PERF_IPI                0x40400000\n#define PERF_IO_TIMER           0x40800000\n#define PERF_REG_HIVE           0x41000000\n#define PERF_REG_NOTIF          0x42000000\n#define PERF_PPM_EXIT_LATENCY   0x44000000\n#define PERF_WORKER_THREAD      0x48000000\n\n// Masks[4]\n#define PERF_OPTICAL_IO         0x80000001\n#define PERF_OPTICAL_IO_INIT    0x80000002\n#define PERF_DLL_INFO           0x80000008\n#define PERF_DLL_FLUSH_WS       0x80000010\n#define PERF_OB_HANDLE          0x80000040\n#define PERF_OB_OBJECT          0x80000080\n#define PERF_WAKE_DROP          0x80000200\n#define PERF_WAKE_EVENT         0x80000400\n#define PERF_DEBUGGER           0x80000800\n#define PERF_PROC_ATTACH        0x80001000\n#define PERF_WAKE_COUNTER       0x80002000\n#define PERF_POWER              0x80008000\n#define PERF_SOFT_TRIM          0x80010000\n#define PERF_CC                 0x80020000\n#define PERF_FLT_IO_INIT        0x80080000\n#define PERF_FLT_IO             0x80100000\n#define PERF_FLT_FASTIO         0x80200000\n#define PERF_FLT_IO_FAILURE     0x80400000\n#define PERF_HV_PROFILE         0x80800000\n#define PERF_WDF_DPC            0x81000000\n#define PERF_WDF_INTERRUPT      0x82000000\n#define PERF_CACHE_FLUSH        0x84000000\n\n// Masks[5]\n#define PERF_HIBER_RUNDOWN      0xA0000001\n\n// Masks[6]\n#define PERF_SYSCFG_SYSTEM      0xC0000001\n#define PERF_SYSCFG_GRAPHICS    0xC0000002\n#define PERF_SYSCFG_STORAGE     0xC0000004\n#define PERF_SYSCFG_NETWORK     0xC0000008\n#define PERF_SYSCFG_SERVICES    0xC0000010\n#define PERF_SYSCFG_PNP         0xC0000020\n#define PERF_SYSCFG_OPTICAL     0xC0000040\n#define PERF_SYSCFG_ALL         0xDFFFFFFF\n\n// Masks[7] - Control Mask. All flags that change system behavior go here.\n#define PERF_CLUSTER_OFF        0xE0000001\n#define PERF_MEMORY_CONTROL     0xE0000002\n\n// TraceQueryInformation wasn't introduced until Windows 8, so we need to use\n// NtQuerySystemInformation instead in order to maintain support for Windows 7.\n// This requires the below additional definitions.\n\ntypedef enum _EVENT_TRACE_INFORMATION_CLASS {\n    EventTraceKernelVersionInformation,\n    EventTraceGroupMaskInformation,\n    EventTracePerformanceInformation,\n    EventTraceTimeProfileInformation,\n    EventTraceSessionSecurityInformation,\n    EventTraceSpinlockInformation,\n    EventTraceStackTracingInformation,\n    EventTraceExecutiveResourceInformation,\n    EventTraceHeapTracingInformation,\n    EventTraceHeapSummaryTracingInformation,\n    EventTracePoolTagFilterInformation,\n    EventTracePebsTracingInformation,\n    EventTraceProfileConfigInformation,\n    EventTraceProfileSourceListInformation,\n    EventTraceProfileEventListInformation,\n    EventTraceProfileCounterListInformation,\n    EventTraceStackCachingInformation,\n    EventTraceObjectTypeFilterInformation,\n    MaxEventTraceInfoClass\n} EVENT_TRACE_INFORMATION_CLASS;\n\ntypedef struct _EVENT_TRACE_GROUPMASK_INFORMATION {\n    EVENT_TRACE_INFORMATION_CLASS EventTraceInformationClass;\n    TRACEHANDLE TraceHandle;\n    PERFINFO_GROUPMASK EventTraceGroupMasks;\n} EVENT_TRACE_GROUPMASK_INFORMATION, * PEVENT_TRACE_GROUPMASK_INFORMATION;\n\n#ifndef _WINTERNL_\n\ntypedef enum _SYSTEM_INFORMATION_CLASS {\n} SYSTEM_INFORMATION_CLASS;\n\ntypedef LONG NTSTATUS;\n\nextern \"C\" NTSTATUS NTAPI NtQuerySystemInformation(\n    _In_ SYSTEM_INFORMATION_CLASS SystemInformationClass,\n    _Out_writes_bytes_to_opt_(SystemInformationLength, *ReturnLength) PVOID SystemInformation,\n    _In_ ULONG SystemInformationLength,\n    _Out_opt_ PULONG ReturnLength\n);\n\n#endif // _WINTERNL_\n\nextern \"C\" NTSTATUS NTAPI NtSetSystemInformation(\n    _In_ SYSTEM_INFORMATION_CLASS SystemInformationClass,\n    _In_reads_bytes_opt_(SystemInformationLength) PVOID SystemInformation,\n    _In_ ULONG SystemInformationLength\n);\n\nconstexpr auto SystemPerformanceTraceInformation{ static_cast<SYSTEM_INFORMATION_CLASS>(0x1f) };\n\n#endif // PERFINFO_GROUPMASK_HPP"
  },
  {
    "path": "libs/krabs/krabs/property.hpp",
    "content": "// Copyright (c) Microsoft. All rights reserved.\n// Licensed under the MIT license. See LICENSE file in the project root for full license information.\n\n#pragma once\n\n#ifndef  WIN32_LEAN_AND_MEAN\n#define  WIN32_LEAN_AND_MEAN\n#endif\n\n#define INITGUID\n\n#include <memory>\n#include <vector>\n\n#include \"compiler_check.hpp\"\n#include \"schema.hpp\"\n#include \"errors.hpp\"\n\n#include <windows.h>\n#include <tdh.h>\n#include <evntrace.h>\n\n#pragma comment(lib, \"tdh.lib\")\n\nnamespace krabs {\n\n    /**\n     * <summary>\n     * Represents a single property of the record schema.\n     * </summary>\n     * <remarks>\n     *   Noticeably absent from this property is the ability to ask what its\n     *   value is. The reason for this is that this property instance is\n     *   intended to work with synth_records, which don't always have data to\n     *   correspond with properties. This class *cannot* return a value because\n     *   there isn't always a value to return.\n     * </remarks>\n     */\n    class property {\n    public:\n\n        /**\n         * <summary>\n         * Constructs a property.\n         * </summary>\n         * <remarks>\n         *   This should be instantiated by client code -- let the parser\n         *   object do this for you with its `properties` method.\n         * </remarks>\n         */\n        property(const std::wstring &name, _TDH_IN_TYPE type);\n\n        /**\n         * <summary>\n         * Retrieves the name of the property.\n         * </summary>\n         */\n        const std::wstring &name() const;\n\n        /**\n         * <summary>\n         * Retrieves the Tdh type of the property.\n         * </summary>\n         */\n        _TDH_IN_TYPE type() const;\n\n    private:\n        std::wstring name_;\n        _TDH_IN_TYPE type_;\n    };\n\n\n    /**\n     * <summary>\n     * Iterates the properties in a given event record.\n     * </summary>\n     */\n    class property_iterator {\n    public:\n\n        /**\n         * <summary>\n         *   Constructs a new iterator that lazily retrieves the properties of\n         *   the given event record.\n         * </summary>\n         * <remarks>\n         *   Don't construct this yourself. Let the `parser` class do it for you.\n         * </remarks>\n         */\n        property_iterator(const schema &s);\n\n        /**\n         * <summary>\n         * Returns an iterator that hasn't yielded any properties yet.\n         * </summary>\n         */\n        std::vector<property>::iterator begin();\n\n        /**\n         * <summary>\n         * Returns an iterator that has yielded all properties.\n         * </summary>\n         */\n        std::vector<property>::iterator end();\n\n    private:\n\n        /**\n         * <summary>\n         *   Constructs a property instance out of the raw data of the\n         *   given property.\n         * </summary>\n         */\n        property get_property(size_t index) const;\n\n        /**\n         * <summary>\n         * Collects the names of the properties in the schema.\n         * </summary>\n         * <remarks>\n         *   This is a little lazy of us, as we end up iterating the properties\n         *   entirely before allowing enumeration by the client. Because this\n         *   code is most likely called in a non-critical path, there's not\n         *   much to worry about here.\n         */\n         std::vector<property> enum_properties() const;\n\n\n    private:\n        const krabs::schema &schema_;\n        size_t numProperties_;\n        std::vector<property> properties_;\n        std::vector<property>::iterator beg_;\n        std::vector<property>::iterator end_;\n        std::vector<property>::iterator curr_;\n    };\n\n    // Implementation\n    // ------------------------------------------------------------------------\n\n    inline property::property(const std::wstring &name, _TDH_IN_TYPE type)\n    : name_(name)\n    , type_(type)\n    {}\n\n    inline const std::wstring &property::name() const\n    {\n        return name_;\n    }\n\n    inline _TDH_IN_TYPE property::type() const\n    {\n        return type_;\n    }\n\n    // ------------------------------------------------------------------------\n\n    inline property_iterator::property_iterator(const schema &s)\n    : schema_(s)\n    , numProperties_(s.pSchema_->TopLevelPropertyCount)\n    , properties_(enum_properties())\n    , beg_(properties_.begin())\n    , end_(properties_.end())\n    , curr_(properties_.begin())\n    {}\n\n    inline std::vector<property>::iterator property_iterator::begin()\n    {\n        return beg_;\n    }\n\n    inline std::vector<property>::iterator property_iterator::end()\n    {\n        return end_;\n    }\n\n    inline property property_iterator::get_property(size_t index) const\n    {\n        const auto &curr_prop = schema_.pSchema_->EventPropertyInfoArray[index];\n\n        const wchar_t *pName = reinterpret_cast<const wchar_t*>(\n            reinterpret_cast<BYTE*>(schema_.pSchema_) +\n            curr_prop.NameOffset);\n\n        auto tdh_type = (_TDH_IN_TYPE)curr_prop.nonStructType.InType;\n\n        return property(pName, tdh_type);\n    }\n\n    inline std::vector<property> property_iterator::enum_properties() const\n    {\n        std::vector<property> props;\n        for (size_t i = 0; i < numProperties_; ++i) {\n            props.emplace_back(get_property(i));\n        }\n\n        return props;\n    }\n\n\n} /* namespace krabs */\n"
  },
  {
    "path": "libs/krabs/krabs/provider.hpp",
    "content": "// Copyright (c) Microsoft. All rights reserved.\n// Licensed under the MIT license. See LICENSE file in the project root for full license information.\n\n#pragma once\n\n#define INITGUID\n\n#include <deque>\n#include <functional>\n\n#include \"compiler_check.hpp\"\n#include \"filtering/event_filter.hpp\"\n#include \"perfinfo_groupmask.hpp\"\n#include \"trace_context.hpp\"\n#include \"wstring_convert.hpp\"\n\n#include <evntcons.h>\n#include <guiddef.h>\n#include <pla.h>\n#include <comutil.h>\n\n#ifdef _DEBUG\n#pragma comment(lib, \"comsuppwd.lib\")\n#else\n#pragma comment(lib, \"comsuppw.lib\")\n#endif\n\nnamespace krabs { namespace details {\n    template <typename T> class trace_manager;\n\n    struct kt;\n    struct ut;\n\n} /* namespace details */ } /* namespace krabs */\n\nnamespace krabs {\n\n    template <typename T>\n    class trace;\n\n    typedef void(*c_provider_callback)(const EVENT_RECORD &, const krabs::trace_context &);\n    typedef void(*c_provider_error_callback)(const EVENT_RECORD&, const std::string&);\n    typedef std::function<void(const EVENT_RECORD &, const krabs::trace_context &)> provider_callback;\n    typedef std::function<void(const EVENT_RECORD&, const std::string&)> provider_error_callback;\n\n    namespace details {\n\n        /**\n         * <summary>\n         *   Serves as a base for providers and kernel_providers. Handles event\n         *   registration and forwarding.\n         * </summary>\n         */\n        template <typename T>\n        class base_provider {\n        public:\n\n            /**\n             * <summary>\n             * Adds a function to call when an event for this provider is fired.\n             * </summary>\n             *\n             * <param name=\"callback\">the function to call into</param>\n             * <example>\n             *    void my_fun(const EVENT_RECORD &record, const krabs::trace_context &trace_context) { ... }\n             *    // ...\n             *    krabs::guid id(L\"{A0C1853B-5C40-4B15-8766-3CF1C58F985A}\");\n             *    provider<> powershell(id);\n             *    provider.add_on_event_callback(my_fun);\n             * </example>\n             *\n             * <example>\n             *    auto fun = [&](const EVENT_RECORD &record, const krabs::trace_context &trace_context) {...}\n             *    krabs::guid id(L\"{A0C1853B-5C40-4B15-8766-3CF1C58F985A}\");\n             *    provider<> powershell(id);\n             *    provider.add_on_event_callback(fun);\n             * </example>\n             */\n            void add_on_event_callback(c_provider_callback callback);\n\n            template <typename U>\n            void add_on_event_callback(U &callback);\n\n            template <typename U>\n            void add_on_event_callback(const U &callback);\n\n            /**\n             * <summary>\n             * Adds a function to call when an error occurs when this provider handles an event.\n             * </summary>\n             */\n            void add_on_error_callback(c_provider_error_callback callback);\n\n            template <typename U>\n            void add_on_error_callback(U& callback);\n\n            template <typename U>\n            void add_on_error_callback(const U& callback);\n\n            /**\n             * <summary>\n             *   Adds a new filter to a provider, where the filter is expected\n             *   to have callbacks attached to it.\n             * </summary>\n             * <example>\n             *   krabs::guid id(L\"{A0C1853B-5C40-4B15-8766-3CF1C58F985A}\");\n             *   krabs::provider<> powershell(id);\n             *   krabs::event_filter filter(krabs::filtering::any_event);\n             *   filter.add_on_event_callback([&](const EVENT_RECORD &record, const krabs::trace_context &trace_context) {...});\n             *   powershell.add_filter(filter);\n             * </example>\n             */\n            void add_filter(const event_filter &f);\n\n        protected:\n\n            /**\n             * <summary>\n             *   Called when an event occurs, forwards to callbacks and filters.\n             * </summary>\n             */\n            void on_event(const EVENT_RECORD &record, const krabs::trace_context &context) const;\n\n        protected:\n            std::deque<provider_callback> callbacks_;\n            std::deque<provider_error_callback> error_callbacks_;\n            std::deque<event_filter> filters_;\n\n        private:\n            template <typename T>\n            friend class details::trace_manager;\n\n            template <typename T>\n            friend class krabs::trace;\n\n            template <typename S>\n            friend class base_provider;\n        };\n\n    } // namespace details\n\n    /**\n     * <summary>\n     *   Used to enable specific types of events from specific event sources in\n     *   ETW. Corresponds tightly with the concept of an ETW provider. Used for\n     *   user trace instances (not kernel trace instances).\n     * </summary>\n     *\n     * <param name='T'>\n     * The type of flags to use when filtering event types via any and all.\n     * There is an implicitly requirement that T can be downcasted to a\n     * ULONGLONG.\n     * </param>\n     */\n    template <typename T = ULONGLONG>\n    class provider : public details::base_provider<T> {\n    public:\n\n        /**\n         * <summary>\n         * Constructs a provider with the given guid identifier.\n         * </summary>\n         *\n         * <param name=\"id\">the GUID that identifies the provider.</param>\n         * <example>\n         *    krabs::guid id(L\"{A0C1853B-5C40-4B15-8766-3CF1C58F985A}\");\n         *    provider<> powershell(id);\n         * </example>\n         */\n        provider(GUID id);\n\n        /**\n        * <summary>\n        * Constructs a provider with the given a provider name.\n        * </summary>\n        *\n        * <param name=\"id\">the provider name.</param>\n        * <example>\n        *    krabs::guid id(L\"Microsoft-Windows-WinINet\");\n        *    provider<> winINet(id);\n        * </example>\n        */\n        provider(const std::wstring &providerName);\n\n        /**\n         * <summary>\n         * Sets the \"any\" flag of the provider.\n         * </summary>\n         *\n         * <param name=\"value\">the value to set <c>any</c> to.</param>\n         * <example>\n         *    krabs::guid id(L\"{A0C1853B-5C40-4B15-8766-3CF1C58F985A}\");\n         *    provider<> powershell(id);\n         *    powershell.any(0xFF00);\n         * </example>\n         */\n        void any(T any);\n\n        /**\n         * <summary>\n         * Sets the \"all\" flag of the provider.\n         * </summary>\n         *\n         * <param name=\"value\">the value to set <c>all</c> to.</param>\n         * <example>\n         *    krabs::guid id(L\"{A0C1853B-5C40-4B15-8766-3CF1C58F985A}\");\n         *    provider<> powershell(id);\n         *    powershell.all(0xFF00);\n         * </example>\n         */\n        void all(T all);\n\n        /**\n        * <summary>\n        * Sets the \"level\" flag of the provider. Valid values are 0~255 (0xFF).\n        * </summary>\n        *\n        * <param name=\"value\">the value to set <c>level</c> to.</param>\n        * <example>\n        *    krabs::guid id(L\"{A0C1853B-5C40-4B15-8766-3CF1C58F985A}\");\n        *    provider<> powershell(id);\n        *    powershell.level(0x00);\n        * </example>\n        */\n        void level(T level);\n\n        /**\n        * <summary>\n        * Sets the \"EnableProperty\" flag on the ENABLE_TRACE_PARAMETER struct.\n        * These properties configure behaviours for a specified user-mode provider.\n        * Valid values can be found here:\n        *   https://msdn.microsoft.com/en-us/library/windows/desktop/dd392306(v=vs.85).aspx\n        * </summary>\n        *\n        * <param name=\"value\">the value to set</param>\n        * <example>\n        *    krabs::guid id(L\"{A0C1853B-5C40-4B15-8766-3CF1C58F985A}\");\n        *    provider<> powershell(id);\n        *    powershell.trace_flags(EVENT_ENABLE_PROPERTY_STACK_TRACE);\n        * </example>\n        */\n        void trace_flags(T trace_flags);\n\n        /**\n        * <summary>\n        * Gets the configured value for the \"EnableProperty\" flag on the \n        * ENABLE_TRACE_PARAMETER struct. See void trace_flags(T trace_flags)\n        * for details on what the values mean.\n        * </summary>\n        * <returns>The value to set when the provider is enabled.</returns>\n        * <example>\n        *    krabs::guid id(L\"{A0C1853B-5C40-4B15-8766-3CF1C58F985A}\");\n        *    provider<> powershell(id);\n        *    powershell.trace_flags(EVENT_ENABLE_PROPERTY_STACK_TRACE);\n        *    auto flags = powershell.get_trace_flags(); \n        *    assert(flags == EVENT_ENABLE_PROPERTY_STACK_TRACE);\n        * </example>\n        */\n        T trace_flags() const;\n\n        /**\n        * <summary>\n        * Requests that the provider log its state information. See:\n        *   https://docs.microsoft.com/en-us/windows/win32/api/evntrace/nf-evntrace-enabletraceex2\n        * </summary>\n        *\n        * <example>\n        *     krabs::provider<> process_provider(L\"Microsoft-Windows-Kernel-Process\");\n        *     process_provider.any(0x10);  // WINEVENT_KEYWORD_PROCESS\n        *     process_provider.enable_rundown_events();\n        * </example>\n        */\n        void enable_rundown_events();\n\n        /**\n         * <summary>\n         * Turns a strongly typed provider<T> to provider<> (useful for\n         * creating collections of providers).\n         * </summary>\n         *\n         * <example>\n         *    krabs::guid id(L\"{A0C1853B-5C40-4B15-8766-3CF1C58F985A}\");\n         *    provider<CustomFlagType> powershell(id);\n         *    provider<> blah(powershell);\n         * </example>\n         */\n        operator provider<>() const;\n\n    private:\n        GUID guid_;\n        T any_;\n        T all_;\n        T level_;\n        T trace_flags_;\n        bool rundown_enabled_;\n\n    private:\n        template <typename T>\n        friend class details::trace_manager;\n\n        template <typename T>\n        friend class krabs::trace;\n\n        template <typename S>\n        friend class base_provider;\n\n        friend struct details::ut;\n\n    };\n\n    /**\n     * <summary>\n     *   Used to enable specific types of event sources from an ETW kernel\n     *   trace.\n     * </summary>\n     */\n    class kernel_provider : public details::base_provider<ULONGLONG> {\n    public:\n\n        /**\n         * <summary>\n         *   Constructs a kernel_provider that enables events of the given\n         *   flags.\n         * </summary>\n         */\n        kernel_provider(unsigned long flags, const GUID &id)\n        : p_(flags)\n        , id_(id)\n        , gm_(0)\n        , r_(0)\n        , rundown_enabled_(false)\n        {}\n\n        /**\n         * <summary>\n         *   Constructs a kernel_provider that enables events of the given\n         *   group mask.\n         * </summary>\n         * <remarks>\n         *   Only supported on Windows 8 and newer.\n         * </remarks>\n         */\n        kernel_provider(const GUID& id, PERFINFO_MASK group_mask)\n        : p_(0)\n        , id_(id)\n        , gm_(group_mask)\n        , r_(0)\n        , rundown_enabled_(false)\n        {}\n\n        /**\n         * <summary>\n         *   Retrieves the GUID associated with this provider.\n         * </summary>\n         */\n         const krabs::guid &id() const;\n\n         /**\n         * <summary>\n         *   Sets flags to be enabled for the kernel rundown GUID.\n         * </summary>\n         * <remarks>\n         *   This ETW feature is undocumented and should be used with caution.\n         * </remarks>\n         */\n         void set_rundown_flags(unsigned long rundown_flags) {\n             r_ = rundown_flags;\n             rundown_enabled_ = true;\n         };\n\n    private:\n\n        /**\n         * <summary>\n         *   Retrieves the flag value associated with this provider.\n         * </summary>\n         */\n        unsigned long flags() const { return p_; }\n\n        /**\n         * <summary>\n         *   Retrieves the group mask value associated with this provider.\n         * </summary>\n         */\n        PERFINFO_MASK group_mask() const { return gm_; }\n\n        /**\n         * <summary>\n         *   Retrieves the rundown flag value associated with this provider.\n         * </summary>\n         */\n        unsigned long rundown_flags() const { return r_; }\n\n        /**\n         * <summary>\n         *   Have rundown flags been set for this this provider?\n         * </summary>\n         */\n        bool rundown_enabled() const { return rundown_enabled_; }\n\n    private:\n        unsigned long p_;\n        const krabs::guid id_;\n        PERFINFO_MASK gm_;\n        unsigned long r_;\n        bool rundown_enabled_;\n\n    private:\n        friend struct details::kt;\n    };\n\n\n    // Implementation\n    // ------------------------------------------------------------------------\n\n    namespace details {\n\n        template <typename T>\n        void base_provider<T>::add_on_event_callback(c_provider_callback callback)\n        {\n            // C function pointers don't interact well with std::ref, so we\n            // overload to take care of this scenario.\n            callbacks_.push_back(callback);\n        }\n\n        template <typename T>\n        template <typename U>\n        void base_provider<T>::add_on_event_callback(U &callback)\n        {\n            // std::function copies its argument -- because our callbacks list\n            // is a list of std::function, this causes problems when a user\n            // intended for their particular instance to be called.\n            // std::ref lets us get around this and point to a specific instance\n            // that they handed us.\n            callbacks_.push_back(std::ref(callback));\n        }\n\n        template <typename T>\n        template <typename U>\n        void base_provider<T>::add_on_event_callback(const U &callback)\n        {\n            // This is where temporaries bind to. Temporaries can't be wrapped in\n            // a std::ref because they'll go away very quickly. We are forced to\n            // actually copy these.\n            callbacks_.push_back(callback);\n        }\n\n        template <typename T>\n        void base_provider<T>::add_on_error_callback(c_provider_error_callback callback)\n        {\n            // C function pointers don't interact well with std::ref, so we\n            // overload to take care of this scenario.\n            error_callbacks_.push_back(callback);\n        }\n\n        template <typename T>\n        template <typename U>\n        void base_provider<T>::add_on_error_callback(U& callback)\n        {\n            // std::function copies its argument -- because our callbacks list\n            // is a list of std::function, this causes problems when a user\n            // intended for their particular instance to be called.\n            // std::ref lets us get around this and point to a specific instance\n            // that they handed us.\n            error_callbacks_.push_back(std::ref(callback));\n        }\n\n        template <typename T>\n        template <typename U>\n        void base_provider<T>::add_on_error_callback(const U& callback)\n        {\n            // This is where temporaries bind to. Temporaries can't be wrapped in\n            // a std::ref because they'll go away very quickly. We are forced to\n            // actually copy these.\n            error_callbacks_.push_back(callback);\n        }\n\n        template <typename T>\n        void base_provider<T>::add_filter(const event_filter &f)\n        {\n            filters_.push_back(f);\n        }\n\n        template <typename T>\n        void base_provider<T>::on_event(const EVENT_RECORD &record, const krabs::trace_context &trace_context) const\n        {\n            try\n            {\n                for (auto& callback : callbacks_) {\n                    callback(record, trace_context);\n                }\n\n                for (auto& filter : filters_) {\n                    filter.on_event(record, trace_context);\n                }\n            }\n            catch (krabs::could_not_find_schema& ex)\n            {\n                for (auto& error_callback : error_callbacks_) {\n                    error_callback(record, ex.what());\n                }\n            }\n        }\n    } // namespace details\n\n    // ------------------------------------------------------------------------\n\n    static const GUID emptyGuid = { 0 };\n\n    template <typename T>\n    provider<T>::provider(GUID id)\n    : guid_(id)\n    , any_(0)\n    , all_(0)\n    , level_(5)\n    , trace_flags_(0)\n    , rundown_enabled_(false)\n    {}\n\n\n    inline void check_com_hr(HRESULT hr) {\n        if (FAILED(hr)) {\n            std::stringstream stream;\n            stream << \"Error in creating instance of trace providers\";\n            stream << \", hr = 0x\";\n            stream << std::hex << hr;\n            throw std::runtime_error(stream.str());\n        }\n    }\n\n    inline void check_provider_hr(HRESULT hr, const std::wstring &providerName) {\n        if (FAILED(hr)) {\n            std::stringstream stream;\n            stream << \"Error in constructing guid from provider name (\";\n            stream << from_wstring(providerName);\n            stream << \"), hr = 0x\";\n            stream << std::hex << hr;\n            throw std::runtime_error(stream.str());\n        }\n    }\n\n    template <typename T>\n    provider<T>::provider(const std::wstring &providerName)\n    {\n        ITraceDataProviderCollection *allProviders;\n\n        HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);\n        check_com_hr(hr);\n        {\n            hr = CoCreateInstance(\n                CLSID_TraceDataProviderCollection,\n                NULL,\n                CLSCTX_SERVER,\n                IID_ITraceDataProviderCollection,\n                (void**)&allProviders);\n            check_com_hr(hr);\n\n            auto release_ptr = [](IUnknown* ptr) { ptr->Release(); };\n            std::unique_ptr<ITraceDataProviderCollection, decltype(release_ptr)> allProvidersPtr(allProviders, release_ptr);\n\n            hr = allProviders->GetTraceDataProviders(NULL);\n            check_provider_hr(hr, providerName);\n\n            ULONG count;\n            hr = allProviders->get_Count((long*)&count);\n            check_provider_hr(hr, providerName);\n\n            VARIANT index;\n            index.vt = VT_UI4;\n\n            GUID providerGuid = { 0 };\n\n            for (index.ulVal = 0; index.ulVal < count; index.ulVal++){\n                ITraceDataProvider *provider;\n                hr = allProviders->get_Item(index, &provider);\n                check_provider_hr(hr, providerName);\n\n                std::unique_ptr<ITraceDataProvider, decltype(release_ptr)> providerPtr(provider, release_ptr);\n\n                _bstr_t name;\n                hr = provider->get_DisplayName(name.GetAddress());\n                check_provider_hr(hr, providerName);\n\n                if (wcscmp(name, providerName.c_str()) == 0){\n                    hr = provider->get_Guid(&providerGuid);\n                    check_provider_hr(hr, providerName);\n                    break;\n                }\n            }\n\n            if (memcmp((void*)&providerGuid, (void*)&emptyGuid, sizeof(emptyGuid)) == 0)\n            {\n                std::stringstream stream;\n                stream << \"Provider name does not exist. (\";\n                stream << from_wstring(providerName);\n                stream << \"), hr = 0x\";\n                stream << std::hex << hr;\n                throw std::runtime_error(stream.str());\n            }\n\n            guid_ = providerGuid;\n            any_ = 0;\n            all_ = 0;\n            level_ = 5;\n            trace_flags_ = 0;\n            rundown_enabled_ = false;\n        }\n\n        CoUninitialize();\n    }\n\n    template <typename T>\n    void provider<T>::any(T any)\n    {\n        any_ = any;\n    }\n\n    template <typename T>\n    void provider<T>::all(T all)\n    {\n        all_ = all;\n    }\n\n    template <typename T>\n    void provider<T>::level(T level)\n    {\n        level_ = level;\n    }\n\n    template <typename T>\n    void provider<T>::trace_flags(T trace_flags)\n    {\n        trace_flags_ = trace_flags;\n    }\n\n    template <typename T>\n    T provider<T>::trace_flags() const\n    {\n        return static_cast<T>(trace_flags_);\n    }\n\n    template <typename T>\n    void provider<T>::enable_rundown_events()\n    {\n        rundown_enabled_ = true;\n    }\n\n    template <typename T>\n    provider<T>::operator provider<>() const\n    {\n        provider<> tmp(guid_);\n        tmp.any_            = static_cast<ULONGLONG>(any_);\n        tmp.all_            = static_cast<ULONGLONG>(all_);\n        tmp.level_          = static_cast<UCHAR>(level_);\n        tmp.trace_flags_    = static_cast<ULONG>(trace_flags_);\n        tmp.callbacks_      = this.callbacks_;\n\n        return tmp;\n    }\n\n    inline const krabs::guid &kernel_provider::id() const\n    {\n        return id_;\n    }\n\n}\n"
  },
  {
    "path": "libs/krabs/krabs/schema.hpp",
    "content": "// Copyright (c) Microsoft. All rights reserved.\n// Licensed under the MIT license. See LICENSE file in the project root for full license information.\n\n#pragma once\n\n#ifndef  WIN32_LEAN_AND_MEAN\n#define  WIN32_LEAN_AND_MEAN\n#endif\n\n#define INITGUID\n\n#include <memory>\n\n#include <windows.h>\n#include <tdh.h>\n#include <evntrace.h>\n\n#include \"compiler_check.hpp\"\n#include \"schema_locator.hpp\"\n\n#pragma comment(lib, \"tdh.lib\")\n\n\nnamespace krabs { namespace testing {\n    class record_builder;\n} /* namespace testing */ } /* namespace krabs */\n\n\nnamespace krabs {\n\n    class schema;\n    class parser;\n\n    /**\n     * <summary>\n     * Used to query events for detailed information. Creation is rather\n     * costly, so client code should try hard to delay creation of this.\n     * </summary>\n     */\n    class schema {\n    public:\n\n        /**\n         * <summary>\n         * Constructs a schema from an event record instance\n         * using the provided schema_locator.\n         * </summary>\n         *\n         * <example>\n         *   void on_event(const EVENT_RECORD &record, const krabs::trace_context &trace_context)\n         *   {\n         *       krabs::schema schema(record, trace_context.schema_locator);\n         *   }\n         * </example>\n         */\n        schema(const EVENT_RECORD &, const krabs::schema_locator &);\n\n        /**\n         * <summary>Compares two schemas for equality.<summary>\n         *\n         * <example>\n         *   schema1 == schema2;\n         *   schema1 != schema2;\n         * </example>\n         */\n        bool operator==(const schema &other) const;\n        bool operator!=(const schema &other) const;\n\n        /*\n         * <summary>\n         * Returns the name of an event via its schema.\n         * </summary>\n         * <example>\n         *    void on_event(const EVENT_RECORD &record, const krabs::trace_context &trace_context)\n         *    {\n         *        krabs::schema schema(record, trace_context.schema_locator);\n         *        std::wstring name = krabs::event_name(schema);\n         *    }\n         * </example>\n         */\n        const wchar_t *event_name() const;\n\n        /*\n         * <summary>\n         * Returns the name of an opcode via its schema.\n         * </summary>\n         * <example>\n         *    void on_event(const EVENT_RECORD &record, const krabs::trace_context &trace_context)\n         *    {\n         *        krabs::schema schema(record, trace_context.schema_locator);\n         *        std::wstring name = krabs::opcode_name(schema);\n         *    }\n         * </example>\n         */\n        const wchar_t* opcode_name() const;\n\n        /*\n         * <summary>\n         * Returns the taskname of an event via its schema.\n         * </summary>\n         * <example>\n         *    void on_event(const EVENT_RECORD &record, const krabs::trace_context &trace_context)\n         *    {\n         *        krabs::schema schema(record, trace_context.schema_locator);\n         *        std::wstring name = krabs::task_name(schema);\n         *    }\n         * </example>\n         */\n        const wchar_t *task_name() const;\n\n        /*\n         * <summary>\n         * Returns the DECODING_SOURCE of an event via its schema.\n         * </summary>\n         * <example>\n         *    void on_event(const EVENT_RECORD &record, const krabs::trace_context &trace_context)\n         *    {\n         *        krabs::schema schema(record, trace_context.schema_locator);\n         *        DECODING_SOURCE source = krabs::decoding_source(schema);\n         *    }\n         * </example>\n         */\n        DECODING_SOURCE decoding_source() const;\n\n        /**\n         * <summary>\n         * Returns the event ID via its schema.\n         * </summary>\n         * <example>\n         *    void on_event(const EVENT_RECORD &record, const krabs::trace_context &trace_context)\n         *    {\n         *        krabs::schema schema(record, trace_context.schema_locator);\n         *        int id = schema.event_id();\n         *    }\n         * </example>\n         */\n        int event_id() const;\n\n        /**\n         * <summary>\n         * Returns the event opcode.\n         * </summary>\n         * <example>\n         *    void on_event(const EVENT_RECORD &record, const krabs::trace_context &trace_context)\n         *    {\n         *        krabs::schema schema(record, trace_context.schema_locator);\n         *        int opcode = schema.event_opcode();\n         *    }\n         * </example>\n         */\n        int event_opcode() const;\n\n        /**\n         * <summary>\n         * Returns the version of the event.\n         * </summary>\n         */\n        unsigned int event_version() const;\n\n        /**\n         * <summary>\n         * Returns the flags of the event.\n         * </summary>\n         */\n        unsigned int event_flags() const;\n\n        /**\n         * <summary>\n         * Returns the provider name of an event via its schema.\n         * </summary>\n         * <example>\n         *    void on_event(const EVENT_RECORD &record, const krabs::trace_context &trace_context)\n         *    {\n         *        krabs::schema schema(record, trace_context.schema_locator);\n         *        std::wstring name = krabs::provider_name(schema);\n         *    }\n         * </example>\n         */\n        const wchar_t *provider_name() const;\n\n        /**\n        * <summary>\n        * Returns the PID associated with the event via its schema.\n        * </summary>\n        * <example>\n        *    void on_event(const EVENT_RECORD &record, const krabs::trace_context &trace_context)\n        *    {\n        *        krabs::schema schema(record, trace_context.schema_locator);\n        *        unsigned int name = krabs::process_id(schema);\n        *    }\n        * </example>\n        */\n        unsigned int process_id() const;\n\n        /**\n        * <summary>\n        * Returns the Thread ID associated with the event via its schema.\n        * </summary>\n        * <example>\n        *    void on_event(const EVENT_RECORD &record, const krabs::trace_context &trace_context)\n        *    {\n        *        krabs::schema schema(record, trace_context.schema_locator);\n        *        unsigned int name = krabs::thread_id(schema);\n        *    }\n        * </example>\n        */\n        unsigned int thread_id() const;\n\n        /**\n        * <summary>\n        * Returns the timestamp associated with the event via its schema.\n        * </summary>\n        * <example>\n        *    void on_event(const EVENT_RECORD &record, const krabs::trace_context &trace_context)\n        *    {\n        *        krabs::schema schema(record, trace_context.schema_locator);\n        *        LARGE_INTEGER time = krabs::timestamp(schema);\n        *    }\n        * </example>\n        */\n        LARGE_INTEGER timestamp() const;\n\n        /**\n        * <summary>\n        * Returns the Activity ID associated with the event via its schema.\n        * </summary>\n        * <example>\n        *    void on_event(const EVENT_RECORD &record, const krabs::trace_context &trace_context)\n        *    {\n        *        krabs::schema schema(record, trace_context.schema_locator);\n        *        GUID activity_id = krabs::activity_id(schema);\n        *    }\n        * </example>\n        */\n        GUID activity_id() const;\n\n        /**\n        * <summary>\n        * Retrieves the call stack associated with the record, if enabled.\n        * </summary>\n        * <example>\n        *    void on_event(const EVENT_RECORD &record, const krabs::trace_context &trace_context)\n        *    {\n        *        krabs::schema schema(record, trace_context.schema_locator);\n        *        std::vector<ULONG_PTR> stack_trace = schema.stack_trace();\n        *    }\n        * </example>\n        */\n        std::vector<ULONG_PTR> stack_trace() const;\n\n    private:\n        const EVENT_RECORD &record_;\n        TRACE_EVENT_INFO *pSchema_;\n\n    private:\n        friend std::wstring event_name(const schema &);\n        friend std::wstring opcode_name(const schema &);\n        friend std::wstring task_name(const schema &);\n        friend DECODING_SOURCE decoding_source(const schema &);\n        friend std::wstring provider_name(const schema &);\n        friend unsigned int process_id(const schema &);\n        friend LARGE_INTEGER timestamp(const schema &);\n        friend GUID activity_id(const schema&);\n        friend int event_id(const EVENT_RECORD &);\n        friend int event_id(const schema &);\n        friend std::vector<ULONG_PTR> stack_trace(const schema&);\n        friend std::vector<ULONG_PTR> stack_trace(const EVENT_RECORD&);\n\n        friend class parser;\n        friend class property_iterator;\n        friend class record_builder;\n    };\n\n\n    // Implementation\n    // ------------------------------------------------------------------------\n\n    inline schema::schema(const EVENT_RECORD &record, const krabs::schema_locator &schema_locator)\n        : record_(record)\n        , pSchema_(schema_locator.get_event_schema(record))\n    { }\n\n    inline bool schema::operator==(const schema &other) const\n    {\n        return (pSchema_->ProviderGuid == other.pSchema_->ProviderGuid &&\n                pSchema_->EventDescriptor.Id == other.pSchema_->EventDescriptor.Id &&\n                pSchema_->EventDescriptor.Version == other.pSchema_->EventDescriptor.Version);\n    }\n\n    inline bool schema::operator!=(const schema &other) const\n    {\n        return !(*this == other);\n    }\n\n    inline const wchar_t *schema::event_name() const\n    {\n        /*\n        EventNameOffset will be 0 if the event does not have an assigned name or\n        if this event is decoded on a system that does not support decoding\n        manifest event names. Event name decoding is supported on Windows\n        10 Fall Creators Update (2017) and later.\n        */\n        if (pSchema_->EventNameOffset != 0) {\n            return reinterpret_cast<const wchar_t*>(\n                reinterpret_cast<const char*>(pSchema_) +\n                pSchema_->EventNameOffset);\n        }\n        else {\n            return L\"\";\n        }\n    }\n\n    inline const wchar_t* schema::opcode_name() const\n    {\n        /*\n        In WPP Traces OpcodeName is not used\n        */\n        if (pSchema_->OpcodeNameOffset != 0) {\n            return reinterpret_cast<const wchar_t*>(\n                reinterpret_cast<const char*>(pSchema_) +\n                pSchema_->OpcodeNameOffset);\n        }\n        else {\n            return L\"\";\n        }\n    }\n\n    inline const wchar_t *schema::task_name() const\n    {\n        if (pSchema_->TaskNameOffset != 0) {\n            return reinterpret_cast<const wchar_t*>(\n                reinterpret_cast<const char*>(pSchema_) +\n                pSchema_->TaskNameOffset);\n        }\n        else {\n            return L\"\";\n        }\n    }\n\n    inline DECODING_SOURCE schema::decoding_source() const\n    {\n        return pSchema_->DecodingSource;\n    }\n\n    inline int schema::event_id() const\n    {\n        return record_.EventHeader.EventDescriptor.Id;\n    }\n\n    inline int schema::event_opcode() const\n    {\n        return record_.EventHeader.EventDescriptor.Opcode;\n    }\n\n    inline unsigned int schema::event_version() const\n    {\n        return record_.EventHeader.EventDescriptor.Version;\n    }\n\n    inline unsigned int schema::event_flags() const\n    {\n        return record_.EventHeader.Flags;\n    }\n\n    inline const wchar_t *schema::provider_name() const\n    {\n       return reinterpret_cast<const wchar_t*>(\n           reinterpret_cast<const char*>(pSchema_) +\n           pSchema_->ProviderNameOffset);\n    }\n\n    inline unsigned int schema::process_id() const\n    {\n        return record_.EventHeader.ProcessId;\n    }\n\n    inline unsigned int schema::thread_id() const\n    {\n        return record_.EventHeader.ThreadId;\n    }\n\n    inline LARGE_INTEGER schema::timestamp() const\n    {\n        return record_.EventHeader.TimeStamp;\n    }\n\n    inline GUID schema::activity_id() const\n    {\n        return record_.EventHeader.ActivityId;\n    }\n\n    inline std::vector<ULONG_PTR> schema::stack_trace() const\n    {\n        std::vector<ULONG_PTR> call_stack;\n        if (record_.ExtendedDataCount != 0) {\n            for (USHORT i = 0; i < record_.ExtendedDataCount; i++)\n            {\n                auto item = record_.ExtendedData[i];\n                if (item.ExtType == EVENT_HEADER_EXT_TYPE_STACK_TRACE64) {\n                    auto stacktrace = reinterpret_cast<PEVENT_EXTENDED_ITEM_STACK_TRACE64>(item.DataPtr);\n                    auto stack_length = (item.DataSize - sizeof(ULONG64)) / sizeof(ULONG64);\n                    for (size_t j = 0; j < stack_length; j++)\n                    {\n                        call_stack.push_back(stacktrace->Address[j]);\n                    }\n                }\n                else if (item.ExtType == EVENT_HEADER_EXT_TYPE_STACK_TRACE32) {\n                    auto stacktrace = reinterpret_cast<PEVENT_EXTENDED_ITEM_STACK_TRACE32>(item.DataPtr);\n                    auto stack_length = (item.DataSize - sizeof(ULONG64)) / sizeof(ULONG);\n                    for (size_t j = 0; j < stack_length; j++)\n                    {\n                        call_stack.push_back(stacktrace->Address[j]);\n                    }\n                }\n            }\n        }\n\n        return call_stack;\n    }\n}\n"
  },
  {
    "path": "libs/krabs/krabs/schema_locator.hpp",
    "content": "// Copyright (c) Microsoft. All rights reserved.\n// Licensed under the MIT license. See LICENSE file in the project root for full license information.\n\n#pragma once\n\n#ifndef  WIN32_LEAN_AND_MEAN\n#define  WIN32_LEAN_AND_MEAN\n#endif\n\n#define INITGUID\n\n#include <windows.h>\n#include <tdh.h>\n#include <evntrace.h>\n\n#include <memory>\n#include <unordered_map>\n\n#include \"compiler_check.hpp\"\n#include \"errors.hpp\"\n#include \"guid.hpp\"\n\n#pragma comment(lib, \"tdh.lib\")\n\nnamespace krabs {\n\n    /**\n     * <summary>\n     * Type used as the key for cache lookup in a schema_locator.\n     * </summary>\n     */\n    struct schema_key\n    {\n        guid      provider;\n        uint16_t  id;\n        uint8_t   opcode;\n        uint8_t   version;\n        uint8_t   level;\n\n        schema_key(const EVENT_RECORD &record)\n            : provider(record.EventHeader.ProviderId)\n            , id(record.EventHeader.EventDescriptor.Id)\n            , opcode(record.EventHeader.EventDescriptor.Opcode)\n            , level(record.EventHeader.EventDescriptor.Level)\n            , version(record.EventHeader.EventDescriptor.Version) { }\n\n        bool operator==(const schema_key &rhs) const\n        {\n            return provider == rhs.provider &&\n                   id == rhs.id &&\n                   opcode == rhs.opcode &&\n                   level == rhs.level &&\n                   version == rhs.version;\n        }\n\n        bool operator!=(const schema_key &rhs) const { return !(*this == rhs); }\n    };\n}\n\nnamespace std {\n\n    /**\n     * <summary>\n     * Builds a hash code for a schema_key\n     * </summary>\n     */\n    template<>\n    struct std::hash<krabs::schema_key>\n    {\n        size_t operator()(const krabs::schema_key &key) const\n        {\n            // Shift-Add-XOR hash - good enough for the small sets we deal with\n            size_t h = 2166136261;\n\n            h ^= (h << 5) + (h >> 2) + std::hash<krabs::guid>()(key.provider);\n            h ^= (h << 5) + (h >> 2) + key.id;\n            h ^= (h << 5) + (h >> 2) + key.opcode;\n            h ^= (h << 5) + (h >> 2) + key.version;\n            h ^= (h << 5) + (h >> 2) + key.level;\n\n            return h;\n        }\n    };\n}\n\nnamespace krabs {\n\n    /**\n     * <summary>\n     * Get event schema from TDH.\n     * </summary>\n     */\n    std::unique_ptr<char[]> get_event_schema_from_tdh(const EVENT_RECORD &);\n\n    /**\n     * <summary>\n     * Fetches and caches schemas from TDH.\n     * NOTE: this cache also reduces the number of managed to native transitions\n     * when krabs is compiled into a managed assembly.\n     * </summary>\n     */\n    class schema_locator {\n    public:\n\n        /**\n         * <summary>\n         * Retrieves the event schema from the cache or falls back to\n         * TDH to load the schema.\n         * </summary>\n         */\n        const PTRACE_EVENT_INFO get_event_schema(const EVENT_RECORD &record) const;\n\n    private:\n        mutable std::unordered_map<schema_key, std::unique_ptr<char[]>> cache_;\n    };\n\n    // Implementation\n    // ------------------------------------------------------------------------\n\n    inline const PTRACE_EVENT_INFO schema_locator::get_event_schema(const EVENT_RECORD &record) const\n    {\n        // check the cache\n        auto key = schema_key(record);\n        auto& buffer = cache_[key];\n\n        if (!buffer) {\n            auto temp = get_event_schema_from_tdh(record);\n            buffer.swap(temp);\n        }\n\n        return (PTRACE_EVENT_INFO)(buffer.get());\n    }\n\n    inline std::unique_ptr<char[]> get_event_schema_from_tdh(const EVENT_RECORD &record)\n    {\n        // get required size\n        ULONG bufferSize = 0;\n        ULONG status = TdhGetEventInformation(\n            (PEVENT_RECORD)&record,\n            0,\n            NULL,\n            NULL,\n            &bufferSize);\n\n        if (status != ERROR_INSUFFICIENT_BUFFER) {\n            error_check_common_conditions(status, record);\n        }\n\n        // allocate and fill the schema from TDH\n        auto buffer = std::unique_ptr<char[]>(new char[bufferSize]);\n\n        error_check_common_conditions(\n            TdhGetEventInformation(\n            (PEVENT_RECORD)&record,\n            0,\n            NULL,\n            (PTRACE_EVENT_INFO)buffer.get(),\n            &bufferSize),\n            record);\n\n        return buffer;\n    }\n}\n"
  },
  {
    "path": "libs/krabs/krabs/size_provider.hpp",
    "content": "// Copyright (c) Microsoft. All rights reserved.\n// Licensed under the MIT license. See LICENSE file in the project root for full license information.\n\n#pragma once\n\n#ifndef  WIN32_LEAN_AND_MEAN\n#define  WIN32_LEAN_AND_MEAN\n#endif\n\n#include <windows.h>\n#include <tdh.h>\n#include <evntrace.h>\n\n#include \"compiler_check.hpp\"\n\nnamespace krabs {\n\n    // TODO: I don't like this interface - it's too tightly\n    // coupled to parser.hpp mainly because the code was\n    // lifted directly out of parser::find_property.\n\n    class size_provider {\n    public:\n        /**\n         * <summary>\n         * Get the size of the specified property from the specified record.\n         * </summary>\n         * BYTE* offset into the user data buffer where the property starts\n         * wchar_t* name of the property to query\n         * EVENT_RECORD& record to query\n         * EVENT_PROPERTY_INFO& property info for the property to query\n         */\n        static ULONG get_property_size(\n            const BYTE*,\n            const wchar_t*,\n            const EVENT_RECORD&,\n            const EVENT_PROPERTY_INFO&);\n\n    private:\n        static ULONG get_heuristic_size(\n            const BYTE*,\n            const EVENT_PROPERTY_INFO&,\n            const EVENT_RECORD&);\n\n        static ULONG get_tdh_size(\n            const wchar_t*,\n            const EVENT_RECORD&);\n    };\n\n    // Implementation\n    // ------------------------------------------------------------------------\n\n    inline ULONG size_provider::get_property_size(\n        const BYTE* propertyStart,\n        const wchar_t* propertyName,\n        const EVENT_RECORD& record,\n        const EVENT_PROPERTY_INFO& propertyInfo)\n    {\n        // The values of the event are essentially stored as an ad-hoc\n        // variant. In order to determine how far we need to advance the\n        // seeking pointer, we need to know the size of the property that\n        // we've just looked at. For certain variable-sized types (like a\n        // string), we need to ask Tdh* to determine the length of the\n        // property. For others, the size is immediately accessible in\n        // the schema structure.\n\n        if ((propertyInfo.Flags & PropertyParamLength) == 0 &&\n            propertyInfo.length > 0)\n        {\n            // length is a union that may refer to another field for a length\n            // value. In that case, defer to TDH for the value otherwise\n            // use the length value directly.\n\n            // For pointers check header instead of size, see PointerSize at\n            // https://docs.microsoft.com/en-us/windows/win32/api/tdh/nf-tdh-tdhformatproperty\n            // for details\n            if (propertyInfo.nonStructType.InType == TDH_INTYPE_POINTER)\n            {\n                return record.EventHeader.Flags & EVENT_HEADER_FLAG_32_BIT_HEADER ? 4 : 8;\n            }\n\n            return propertyInfo.length;\n        }\n\n        ULONG propertyLength = 0;\n\n        // If no flags are set on the property, attempt to use the length\n        // field. If that field is 0, try using our heuristic.\n        if (propertyInfo.Flags == 0)\n        {\n            if (propertyInfo.length > 0)\n                propertyLength = propertyInfo.length;\n            else\n                propertyLength = get_heuristic_size(propertyStart, propertyInfo, record);\n        }\n\n        // Couldn't get the length from the 'length' field or\n        // the heuristic for size failed -> ask Tdh.\n        if (propertyLength == 0)\n            propertyLength = get_tdh_size(propertyName, record);\n\n        return propertyLength;\n    }\n\n    inline ULONG size_provider::get_heuristic_size(\n        const BYTE* propertyStart,\n        const EVENT_PROPERTY_INFO& propertyInfo,\n        const EVENT_RECORD& record)\n    {\n        ULONG propertyLength = 0;\n        PBYTE pRecordEnd = (PBYTE)record.UserData + record.UserDataLength;\n\n        // The calls to Tdh are kind of expensive, especially when krabs is\n        // included in a managed assembly as this call will be a thunk.\n        // The following _very_ common property types can be short-circuited\n        // to prevent the expensive call.\n\n        // Be careful! Check IN and OUT types before making an assumption.\n\n        // Strings that appear at the end of a record may not be null-terminated.\n        // If a string is null-terminated, propertyLength includes the null character.\n        // If a string is not-null terminated, propertyLength includes all bytes up\n        // to the end of the record buffer.\n\n        if (propertyInfo.nonStructType.OutType == TDH_OUTTYPE_STRING)\n        {\n            if (propertyInfo.nonStructType.InType == TDH_INTYPE_UNICODESTRING)\n            {\n                auto p = (const wchar_t*)propertyStart;\n                auto pEnd = (const wchar_t*)pRecordEnd;\n                while (p < pEnd) {\n                    if (!*p++) {\n                        break;\n                    }\n                }\n                propertyLength = static_cast<ULONG>(((PBYTE)p) - propertyStart);\n            }\n            else if (propertyInfo.nonStructType.InType == TDH_INTYPE_ANSISTRING)\n            {\n                auto p = (const char*)propertyStart;\n                auto pEnd = (const char*)pRecordEnd;\n                while (p < pEnd) {\n                    if (!*p++) {\n                        break;\n                    }\n\n                }\n                propertyLength = static_cast<ULONG>(((PBYTE)p) - propertyStart);\n            }\n        }\n\n        return propertyLength;\n    }\n\n    inline ULONG size_provider::get_tdh_size(\n        const wchar_t* propertyName,\n        const EVENT_RECORD& record)\n    {\n        ULONG propertyLength = 0;\n\n        PROPERTY_DATA_DESCRIPTOR desc;\n        desc.PropertyName = (ULONGLONG)propertyName;\n        desc.ArrayIndex = ULONG_MAX;\n\n        TdhGetPropertySize((PEVENT_RECORD)&record, 0, NULL, 1, &desc, &propertyLength);\n\n        return propertyLength;\n    }\n}"
  },
  {
    "path": "libs/krabs/krabs/tdh_helpers.hpp",
    "content": "// Copyright (c) Microsoft. All rights reserved.\n// Licensed under the MIT license. See LICENSE file in the project root for full license information.\n\n#pragma once\n\n#define INITGUID\n\n#include <tdh.h>\n#include <string>\n#include <stdexcept>\n\n#include \"compiler_check.hpp\"\n#include \"parse_types.hpp\"\n\nnamespace krabs {\n\n#define CASE_TYPE(enum) case TDH_INTYPE_##enum: return #enum\n\n    inline const char* in_type_to_string(_TDH_IN_TYPE type)\n    {\n        switch (type)\n        {\n            CASE_TYPE(NULL);\n            CASE_TYPE(UNICODESTRING);\n            CASE_TYPE(ANSISTRING);\n            CASE_TYPE(INT8);\n            CASE_TYPE(UINT8);\n            CASE_TYPE(INT16);\n            CASE_TYPE(UINT16);\n            CASE_TYPE(INT32);\n            CASE_TYPE(UINT32);\n            CASE_TYPE(INT64);\n            CASE_TYPE(UINT64);\n            CASE_TYPE(FLOAT);\n            CASE_TYPE(DOUBLE);\n            CASE_TYPE(BOOLEAN);\n            CASE_TYPE(BINARY);\n            CASE_TYPE(GUID);\n            CASE_TYPE(POINTER);\n            CASE_TYPE(FILETIME);\n            CASE_TYPE(SYSTEMTIME);\n            CASE_TYPE(SID);\n            CASE_TYPE(HEXINT32);\n            CASE_TYPE(HEXINT64);\n            CASE_TYPE(COUNTEDSTRING);\n            CASE_TYPE(COUNTEDANSISTRING);\n            CASE_TYPE(REVERSEDCOUNTEDSTRING);\n            CASE_TYPE(REVERSEDCOUNTEDANSISTRING);\n            CASE_TYPE(NONNULLTERMINATEDSTRING);\n            CASE_TYPE(NONNULLTERMINATEDANSISTRING);\n            CASE_TYPE(UNICODECHAR);\n            CASE_TYPE(ANSICHAR);\n            CASE_TYPE(SIZET);\n            CASE_TYPE(HEXDUMP);\n            CASE_TYPE(WBEMSID);\n            default: return \"<INVALID VALUE>\";\n        }\n    }\n\n#undef CASE_TYPE\n\n    namespace debug {\n\n        // this function provides a user-friendly compiler error\n        // which shows the type in question in the error message.\n        template <typename T>\n        inline void missing_assert_specialization_for()\n        {\n            static_assert(sizeof(T) == 0, __FUNCSIG__);\n        }\n\n        // The \"catch-all\" implementation of assert_valid_assignment just\n        // throws in debug to let us know that we are trying to parse a\n        // type that does not have any assignment validation. This compiles\n        // to a no-op in release.\n        template <typename T>\n        inline void assert_valid_assignment(const std::wstring&, const property_info&)\n        {\n#ifndef NDEBUG\n\n            // NOTE: if you want compile time assignment assertion define TYPEASSERT\n            // in the preprocessor or undefine it to disable compilation errors\n\n#ifdef TYPEASSERT\n            missing_assert_specialization_for<T>();\n#endif // TYPEASSERT\n#endif // NDEBUG\n        }\n\n#ifndef NDEBUG\n\n        // These specializations will be removed in release builds and compilation\n        // will fall back to the unspecialized version which is a no-op in release.\n\n        inline void throw_if_invalid(\n            const std::wstring& name,\n            const property_info& info,\n            _TDH_IN_TYPE requested)\n        {\n            auto actual = (_TDH_IN_TYPE)info.pEventPropertyInfo_->nonStructType.InType;\n\n            if (requested == actual) return;\n\n#pragma warning(push)\n#pragma warning(disable: 4244) // narrowing property name wchar_t to char for this error message\n            std::string ansiName(name.begin(), name.end());\n#pragma warning(pop)\n\n            throw type_mismatch_assert(\n                ansiName.c_str(),\n                in_type_to_string(actual),\n                in_type_to_string(requested));\n        }\n\n        // The macro below generates a specialized version of assert_valid_assignment\n        // only in debug builds. The specialized overload will be selected instead\n        // of the unspecialized version defined above. This allows us to have\n        // type-driven assertions only in debug builds.\n\n#define BUILD_ASSERT(type, tdh_type) \\\n        template <> \\\n        inline void assert_valid_assignment<type>(               \\\n            const std::wstring& name, const property_info& info) \\\n        {                                                        \\\n            throw_if_invalid(name, info, tdh_type);              \\\n        }\n\n        // NOTE: don't just blindly add assertions here, some types\n        // that seem trivial (e.g. bool) are not because of differences\n        // between the representation in C++ and the representation in ETW.\n        // Ensure that type sizes match and that the ETW form isn't\n        // a variant or variable length. A type that requires a specialized\n        // assertion will also require a specialized parser.\n\n        // strings\n        BUILD_ASSERT(std::wstring, TDH_INTYPE_UNICODESTRING);\n        BUILD_ASSERT(std::string, TDH_INTYPE_ANSISTRING);\n        BUILD_ASSERT(const counted_string*, TDH_INTYPE_COUNTEDSTRING);\n\n        // integers\n        BUILD_ASSERT(int8_t, TDH_INTYPE_INT8);\n        BUILD_ASSERT(uint8_t, TDH_INTYPE_UINT8);\n        BUILD_ASSERT(int16_t, TDH_INTYPE_INT16);\n        BUILD_ASSERT(uint16_t, TDH_INTYPE_UINT16);\n        BUILD_ASSERT(int32_t, TDH_INTYPE_INT32);\n        BUILD_ASSERT(uint32_t, TDH_INTYPE_UINT32);\n        BUILD_ASSERT(int64_t, TDH_INTYPE_INT64);\n        BUILD_ASSERT(uint64_t, TDH_INTYPE_UINT64);\n\n        // floating\n        BUILD_ASSERT(float, TDH_INTYPE_FLOAT);\n        BUILD_ASSERT(double, TDH_INTYPE_DOUBLE);\n\n        // FILETIME\n        BUILD_ASSERT(::FILETIME, TDH_INTYPE_FILETIME);\n        BUILD_ASSERT(::SYSTEMTIME, TDH_INTYPE_SYSTEMTIME);\n\n#undef BUILD_ASSERT\n\n        template <>\n        inline void assert_valid_assignment<ip_address>(\n            const std::wstring&, const property_info& info)\n        {\n            auto outType = info.pEventPropertyInfo_->nonStructType.OutType;\n\n            if (outType != TDH_OUTTYPE_IPV6 &&\n                outType != TDH_OUTTYPE_IPV4) {\n                throw std::runtime_error(\n                    \"Requested an IP address from non-IP address property\");\n            }\n        }\n\n        template <>\n        inline void assert_valid_assignment<socket_address>(\n            const std::wstring&, const property_info& info)\n        {\n            auto outType = info.pEventPropertyInfo_->nonStructType.OutType;\n\n            if (outType != TDH_OUTTYPE_SOCKETADDRESS) {\n                throw std::runtime_error(\n                    \"Requested a socket address from property that does not contain a socket address\");\n            }\n        }\n\n        template <>\n        inline void assert_valid_assignment<sid>(\n            const std::wstring&, const property_info& info)\n        {\n            auto inType = info.pEventPropertyInfo_->nonStructType.InType;\n\n            if (inType != TDH_INTYPE_WBEMSID && inType != TDH_INTYPE_SID) {\n                throw std::runtime_error(\n                    \"Requested a SID but was neither a SID nor WBEMSID\");\n            }\n        }\n\n        template <>\n        inline void assert_valid_assignment<pointer>(\n            const std::wstring&, const property_info& info)\n        {\n            auto inType = info.pEventPropertyInfo_->nonStructType.InType;\n\n            if (inType != TDH_INTYPE_POINTER) {\n                throw std::runtime_error(\n                    \"Requested a POINTER from property that is not one\");\n            }\n        }\n\n        template <>\n        inline void assert_valid_assignment<bool>(\n            const std::wstring&, const property_info& info)\n        {\n            auto inType = info.pEventPropertyInfo_->nonStructType.InType;\n\n            if (inType != TDH_INTYPE_BOOLEAN) {\n                throw std::runtime_error(\n                    \"Requested a BOOLEAN from property that is not one\");\n            }\n        }\n\n#endif // NDEBUG\n\n    } /* namespace debug */\n\n} /* namespace krabs */\n"
  },
  {
    "path": "libs/krabs/krabs/testing/event_filter_proxy.hpp",
    "content": "// Copyright (c) Microsoft. All rights reserved.\n// Licensed under the MIT license. See LICENSE file in the project root for full license information.\n\n#pragma once\n\n#define INITGUID\n\n\n#include \"../compiler_check.hpp\"\n#include \"../filtering/event_filter.hpp\"\n#include \"synth_record.hpp\"\n\nnamespace krabs { namespace testing {\n\n    /**\n     * <summary>\n     *   Serves as a fill-in for the event_filter class for testing purposes.\n     *   It acts as a liason for the actual filter instance and allows for forced event\n     *   testing.\n     * </summary>\n     */\n    class event_filter_proxy {\n    public:\n\n        /**\n         * <summary>\n         * Constructs a proxy for the given event_filter.\n         * </summary>\n         * <example>\n         *     krabs::event_filter event_filter;\n         *     krabs::testing::event_filter_proxy proxy(event_filter);\n         * </example>\n         */\n        event_filter_proxy(krabs::event_filter &filter);\n\n        /**\n         * <summary>\n         * Pushes an event through to the proxied filter instance.\n         * </summary>\n         * <example>\n         *     krabs::event_filter event_filter;\n         *     krabs::testing::event_filter_proxy proxy(event_filter);\n         *\n         *     krabs::guid powershell(L\"{A0C1853B-5C40-4B15-8766-3CF1C58F985A}\");\n         *     krabs::testing::record_builder builder(powershell, krabs::id(7942), krabs::version(1));\n         *\n         *     builder.add_properties()\n         *             (L\"ClassName\", L\"FakeETWEventForRealz\")\n         *             (L\"Message\", L\"This message is completely faked\");\n         *\n         *     auto record = builder.pack_incomplete();\n         *     proxy.push_event(record);\n         * </example>\n         */\n        void push_event(const synth_record &record);\n\n    private:\n        krabs::event_filter &event_filter_;\n        krabs::trace_context trace_context_;\n    };\n\n    // Implementation\n    // ------------------------------------------------------------------------\n\n    inline event_filter_proxy::event_filter_proxy(krabs::event_filter &event_filter)\n    : event_filter_(event_filter)\n    {\n    }\n\n    inline void event_filter_proxy::push_event(const synth_record &record)\n    {\n        event_filter_.on_event(record, trace_context_);\n    }\n\n} /* namespace testing */ } /* namespace krabs */\n"
  },
  {
    "path": "libs/krabs/krabs/testing/extended_data_builder.hpp",
    "content": "// Copyright (c) Microsoft. All rights reserved.\n// Licensed under the MIT license. See LICENSE file in the project root for full license information.\n\n#pragma once\n\n#include <algorithm>\n#include <vector>\n#include <memory>\n\n#include <evntcons.h>\n#include <WinDef.h>\n#include <objbase.h>\n\n// TODO: Remove this #define once Krabs starts using Windows SDK v. 10.0.19041.0 or later.\n// From evntcons.h starting in Windows SDK v. 10.0.19041.0.\n#ifndef EVENT_HEADER_EXT_TYPE_CONTAINER_ID\n    #define EVENT_HEADER_EXT_TYPE_CONTAINER_ID 16\n#endif\n\nnamespace krabs { namespace testing {\n    class extended_data_builder;\n\n    /**\n     * <summary>\n     *   Since extended data items have to be packed later, we have to hold onto the data\n     *   until we're ready to pack it.\n     * </summary>\n     */\n    class extended_data_thunk \n    {\n    public:\n        extended_data_thunk(USHORT ext_type, BYTE* data, size_t data_length);\n\n    private:\n        // Intentionally not defined.\n        extended_data_thunk();\n\n        USHORT ext_type_;\n        std::vector<BYTE> bytes_;\n\n        friend class extended_data_builder;\n    };\n\n    /**\n     * <summary>\n     *   Generates fake packed EVENT_HEADER_EXTENDED_DATA_ITEM structures to later add into test\n     *   synth_record objects. These are not guaranteed to be indistinguishable from the real\n     *   thing, just good enough to unit test code that reads/interprets extended data.\n     *\n     *   Note for testing: this builder just appends extended data structures, it won't stop you\n     *   from breaking any API invariants, such as only one of a specific extended data item type.\n     * </summary>\n     */\n    class extended_data_builder\n    {\n    public:\n        static constexpr size_t GUID_STRING_LENGTH_NO_BRACES = 36;\n        static constexpr size_t GUID_STRING_LENGTH_WITH_BRACES = GUID_STRING_LENGTH_NO_BRACES + 2;\n\n\n        extended_data_builder()\n        : items_()\n        {}\n\n        // Mocks a container ID type extended data item.\n        void add_container_id(const GUID& container_id);\n\n        // This generates a contiguous buffer holding all of the data for\n        // the extended data items. Non-trivial because the actual structs\n        // have to be a contiguous array, and they each contain pointers,\n        // not offsets, to dynamically sized data buffers.\n        std::pair<std::shared_ptr<BYTE[]>, size_t> pack() const;\n\n        // Returns the value that should correspond with EVENT_RECORD.ExtendedDataCount\n        inline size_t count() const { return items_.size(); }\n\n    private:\n        std::vector<extended_data_thunk> items_;\n    };\n\n    // Implementation\n    // ------------------------------------------------------------------------\n\n    inline extended_data_thunk::extended_data_thunk(USHORT ext_type, BYTE* data, size_t data_length)\n    : ext_type_(ext_type)\n    , bytes_()\n    {\n        bytes_.assign(data, data + data_length);\n    }\n\n    inline void extended_data_builder::add_container_id(const GUID& container_id)\n    {\n        // With null terminator\n        wchar_t wide_guid_buffer[GUID_STRING_LENGTH_WITH_BRACES + 1] = {};\n\n        // No null terminator\n        BYTE guid_data[GUID_STRING_LENGTH_NO_BRACES] = {};\n\n        StringFromGUID2(container_id, wide_guid_buffer, GUID_STRING_LENGTH_WITH_BRACES + 1);\n\n        for (int i = 0; i < GUID_STRING_LENGTH_NO_BRACES; i++)\n        {\n            // Offset by 1 to ignore the wrapping braces.\n            guid_data[i] = static_cast<BYTE>(wide_guid_buffer[i + 1]);\n        }\n\n        items_.emplace_back(static_cast<USHORT>(EVENT_HEADER_EXT_TYPE_CONTAINER_ID), guid_data, GUID_STRING_LENGTH_NO_BRACES);\n    }\n\n    inline std::pair<std::shared_ptr<BYTE[]>, size_t> extended_data_builder::pack() const\n    {\n        // Return null for buffer if there are no extended data items.\n        if (items_.size() == 0)\n        {\n            return std::make_pair(std::shared_ptr<BYTE[]>(nullptr), 0);\n        }\n\n        BYTE* data_buffer = nullptr;\n        size_t data_buffer_size = 0;\n\n        // Step 1: compute the required buffer size\n        size_t array_part_size = sizeof(EVENT_HEADER_EXTENDED_DATA_ITEM) * items_.size();\n        size_t data_part_size = 0;\n\n        for (const extended_data_thunk& item : items_)\n        {\n            data_part_size += item.bytes_.size();\n        }\n\n        // Allocate the buffer and zero it\n        data_buffer = new BYTE[array_part_size + data_part_size];\n        data_buffer_size = array_part_size + data_part_size;\n        ZeroMemory(data_buffer, data_buffer_size);\n\n        // Step 2: Fill the buffer. For each extended data item, write the object into the buffer at the back.\n        auto array_ptr = reinterpret_cast<EVENT_HEADER_EXTENDED_DATA_ITEM*>(data_buffer);\n        auto data_ptr = data_buffer + array_part_size;\n\n        for (int i = 0; i < items_.size(); i++)\n        {\n            // 2a: write the struct\n            auto& destination = array_ptr[i];\n            const auto& thunk = items_[i];\n            const size_t thunk_size = thunk.bytes_.size();\n\n            destination.ExtType = thunk.ext_type_;\n            destination.DataSize = static_cast<USHORT>(thunk_size);\n            // Assert that the conversion did not truncate thunk_size.\n            assert(static_cast<size_t>(destination.DataSize) == thunk_size);\n\n            // 2b: Write the data\n            assert((data_buffer + data_buffer_size) > data_ptr); // prevent wraparound with unsigned int math\n            size_t remaining = (data_buffer + data_buffer_size) - (data_ptr);\n            // Assert that we will not truncate the data due to not allocating enough space in the buffer.\n            assert(remaining >= thunk_size);\n            // Make sure we rather not copy all of the data than overrun the buffer.\n            memcpy_s(data_ptr, std::min<size_t>(remaining, thunk_size), thunk.bytes_.data(), thunk_size);\n\n            // 2c: point the DataPtr field at the data\n            destination.DataPtr = reinterpret_cast<ULONGLONG>(data_ptr);\n\n            // 2d: increment the pointer for where to write the next piece of data\n            data_ptr += destination.DataSize;\n        }\n\n        return std::make_pair(std::shared_ptr<BYTE[]>(data_buffer), data_buffer_size);\n    }\n} }\n"
  },
  {
    "path": "libs/krabs/krabs/testing/filler.hpp",
    "content": "// Copyright (c) Microsoft. All rights reserved.\n// Licensed under the MIT license. See LICENSE file in the project root for full license information.\n\n#pragma once\n\n#include <cstdint>\n\n#include \"../compiler_check.hpp\"\n#include \"../parse_types.hpp\"\n\nnamespace krabs { namespace testing { namespace details {\n\n    /**\n     * <summary>\n     *   Defines how much padding to inject into a synth_record when a property\n     *   is not filled by calling code.\n     * </summary>\n     */\n    inline size_t how_many_bytes_to_fill(_TDH_IN_TYPE type)\n    {\n        static_assert(sizeof(float) == 4, \"sizeof(float) must be 4, defined on MSDN\");\n\n        switch (type) {\n        case TDH_INTYPE_NULL:          throw std::runtime_error(\"supposed to be unusued -- something horrible is happening\");\n        case TDH_INTYPE_UNICODESTRING: return sizeof(wchar_t);\n        case TDH_INTYPE_ANSISTRING:    return sizeof(char);\n        case TDH_INTYPE_INT8:          return sizeof(int8_t);\n        case TDH_INTYPE_UINT8:         return sizeof(uint8_t);\n        case TDH_INTYPE_INT16:         return sizeof(int16_t);\n        case TDH_INTYPE_UINT16:        return sizeof(uint16_t);\n        case TDH_INTYPE_INT32:         return sizeof(int32_t);\n        case TDH_INTYPE_UINT32:        return sizeof(uint32_t);\n        case TDH_INTYPE_INT64:         return sizeof(int64_t);\n        case TDH_INTYPE_UINT64:        return sizeof(int64_t);\n        case TDH_INTYPE_FLOAT:         return sizeof(float);\n        case TDH_INTYPE_DOUBLE:        return sizeof(double);\n        case TDH_INTYPE_BOOLEAN:       return sizeof(uint32_t); // 4-byte bool, defined on MSDN\n        case TDH_INTYPE_BINARY:        return sizeof(char);\n        case TDH_INTYPE_GUID:          return sizeof(GUID);\n        case TDH_INTYPE_POINTER:       return sizeof(char*);\n        case TDH_INTYPE_FILETIME:      return sizeof(FILETIME);\n        case TDH_INTYPE_SYSTEMTIME:    return sizeof(SYSTEMTIME);\n        case TDH_INTYPE_SID:           return sizeof(PSID);\n        case TDH_INTYPE_HEXINT32:      return sizeof(uint32_t);\n        case TDH_INTYPE_HEXINT64:      return sizeof(uint64_t);\n        default: break;\n        };\n\n        throw std::runtime_error(\"Unexpected fill type\");\n    }\n\n    /**\n     * <summary>\n     * Maps C++ types to TDH types. Used to do runtime type checking of packed\n     * synthetic properties.\n     * </summary>\n     */\n\n     template <typename T>\n     struct tdh_morphism {\n        // This doesn't have a value field, so compilation will fail when we\n        // try to use a type in our record_builder that isn't recognized.\n     };\n\n     template <typename T>\n     struct tdh_morphism<T*> {\n        static const _TDH_IN_TYPE value = TDH_INTYPE_POINTER;\n     };\n\n     template <>\n     struct tdh_morphism<std::wstring> {\n        static const _TDH_IN_TYPE value = TDH_INTYPE_UNICODESTRING;\n     };\n\n     template <>\n     struct tdh_morphism<std::string> {\n        static const _TDH_IN_TYPE value = TDH_INTYPE_ANSISTRING;\n     };\n\n     template <>\n     struct tdh_morphism<char> {\n        static const _TDH_IN_TYPE value = TDH_INTYPE_INT8;\n     };\n\n     template <>\n     struct tdh_morphism<unsigned char> {\n        static const _TDH_IN_TYPE value = TDH_INTYPE_UINT8;\n     };\n\n     template <>\n     struct tdh_morphism<short> {\n        static const _TDH_IN_TYPE value = TDH_INTYPE_INT16;\n     };\n\n     template <>\n     struct tdh_morphism<unsigned short> {\n        static const _TDH_IN_TYPE value = TDH_INTYPE_UINT16;\n     };\n\n     template <>\n     struct tdh_morphism<int> {\n        static const _TDH_IN_TYPE value = TDH_INTYPE_INT32;\n     };\n\n     template <>\n     struct tdh_morphism<unsigned int> {\n        static const _TDH_IN_TYPE value = TDH_INTYPE_UINT32;\n     };\n\n     template <>\n     struct tdh_morphism<long long> {\n        static const _TDH_IN_TYPE value = TDH_INTYPE_INT64;\n     };\n\n     template <>\n     struct tdh_morphism<unsigned long long> {\n        static const _TDH_IN_TYPE value = TDH_INTYPE_UINT64;\n     };\n\n     template <>\n     struct tdh_morphism<float> {\n        static const _TDH_IN_TYPE value = TDH_INTYPE_FLOAT;\n     };\n\n     template <>\n     struct tdh_morphism<double> {\n        static const _TDH_IN_TYPE value = TDH_INTYPE_DOUBLE;\n     };\n\n     template <>\n     struct tdh_morphism<bool> {\n        static const _TDH_IN_TYPE value = TDH_INTYPE_BOOLEAN;\n     };\n\n     template <>\n     struct tdh_morphism<GUID> {\n        static const _TDH_IN_TYPE value = TDH_INTYPE_GUID;\n     };\n\n     template <>\n     struct tdh_morphism<krabs::guid> {\n        static const _TDH_IN_TYPE value = TDH_INTYPE_GUID;\n     };\n\n     template <>\n     struct tdh_morphism<FILETIME> {\n        static const _TDH_IN_TYPE value = TDH_INTYPE_FILETIME;\n     };\n\n     template <>\n     struct tdh_morphism<SYSTEMTIME> {\n        static const _TDH_IN_TYPE value = TDH_INTYPE_SYSTEMTIME;\n     };\n\n     template <>\n     struct tdh_morphism<krabs::hexint32> {\n        static const _TDH_IN_TYPE value = TDH_INTYPE_HEXINT32;\n     };\n\n     template <>\n     struct tdh_morphism<krabs::hexint64> {\n        static const _TDH_IN_TYPE value = TDH_INTYPE_HEXINT64;\n     };\n\n     template <>\n     struct tdh_morphism<SID> {\n        static const _TDH_IN_TYPE value = TDH_INTYPE_SID;\n     };\n\n     template <>\n     struct tdh_morphism<krabs::binary> {\n        static const _TDH_IN_TYPE value = TDH_INTYPE_BINARY;\n     };\n\n\n} /* namespace details */ } /* namespace testing */ } /* namespace krabs */\n"
  },
  {
    "path": "libs/krabs/krabs/testing/proxy.hpp",
    "content": "// Copyright (c) Microsoft. All rights reserved.\n// Licensed under the MIT license. See LICENSE file in the project root for full license information.\n\n#pragma once\n\n#define INITGUID\n\n#include \"../compiler_check.hpp\"\n#include \"../trace.hpp\"\n#include \"../client.hpp\"\n#include \"../testing/synth_record.hpp\"\n\nnamespace krabs { namespace testing {\n\n    /**\n     * <summary>\n     *   Serves as a fill-in for the trace class for testing purposes. It acts\n     *   as a liason for the actual trace instance and allows for forced event\n     *   testing.\n     * </summary>\n     */\n    template <typename T>\n    class trace_proxy {\n    public:\n\n        /**\n         * <summary>\n         * Constructs a proxy for the given trace.\n         * </summary>\n         * <example>\n         *     krabs::user_trace trace;\n         *     krabs::testing::trace_proxy proxy(trace);\n         * </example>\n         */\n\n        trace_proxy(T &trace);\n\n        /**\n         * <summary>\n         * Mocks starting the underlying trace.\n         * </summary>\n         * <example>\n         *     krabs::user_trace trace;\n         *     krabs::testing::trace_proxy proxy(trace);\n         *     proxy.start(); // do not call trace.start()\n         * </example>\n         */\n        void start();\n\n        /**\n         * <summary>\n         * Pushes an event through to the proxied trace instance.\n         * </summary>\n         * <remarks>\n         *   This is the primary mechanism for testing providers and their\n         *   callbacks. Create a fake event with an record_builder instance\n         *   and then push the created synth_record through the object\n         *   graph.\n         * </remarks>\n         * <example>\n         *     krabs::user_trace trace;\n         *     krabs::testing::trace_proxy proxy(trace);\n         *     proxy.start(); // do not call trace.start()\n         *\n         *     krabs::guid powershell(L\"{A0C1853B-5C40-4B15-8766-3CF1C58F985A}\");\n         *     krabs::testing::record_builder builder(powershell, krabs::id(7942), krabs::version(1));\n         *\n         *     builder.add_properties()\n         *             (L\"ClassName\", L\"FakeETWEventForRealz\")\n         *             (L\"Message\", L\"This message is completely faked\");\n         *\n         *     auto record = builder.pack_incomplete();\n         *     proxy.push_event(record);\n         * </example>\n         */\n        void push_event(const synth_record &record);\n\n    private:\n        T &trace_;\n    };\n\n    /**\n     * <summary>Specific instantiation for user traces.</summary>\n     */\n    typedef trace_proxy<krabs::user_trace> user_trace_proxy;\n\n    /**\n     * <summary>Specific instantiation for kernel traces.</summary>\n     */\n    typedef trace_proxy<krabs::kernel_trace> kernel_trace_proxy;\n\n    // Implementation\n    // ------------------------------------------------------------------------\n\n    template <typename T>\n    trace_proxy<T>::trace_proxy(T &trace)\n    : trace_(trace)\n    {\n    }\n\n    template <typename T>\n    void trace_proxy<T>::start()\n    {\n    }\n\n    template <typename T>\n    void trace_proxy<T>::push_event(const synth_record &record)\n    {\n        trace_.on_event(record);\n    }\n\n} /* namespace testing */ } /* namespace krabs */\n"
  },
  {
    "path": "libs/krabs/krabs/testing/record_builder.hpp",
    "content": "// Copyright (c) Microsoft. All rights reserved.\n// Licensed under the MIT license. See LICENSE file in the project root for full license information.\n\n#pragma once\n\n#define INITGUID\n#include <vector>\n#include <memory>\n#include <sstream>\n#include <utility>\n#include <algorithm>\n#include <iterator>\n\n#include \"../compiler_check.hpp\"\n#include \"../guid.hpp\"\n#include \"../schema.hpp\"\n#include \"../parser.hpp\"\n#include \"../tdh_helpers.hpp\"\n#include \"filler.hpp\"\n#include \"synth_record.hpp\"\n#include \"record_property_thunk.hpp\"\n#include \"extended_data_builder.hpp\"\n\nnamespace krabs { namespace testing {\n\n    class record_builder;\n\n    namespace details {\n\n        /**\n         * <summary>\n         *   Provides a convenient syntax for adding properties to a\n         *   record_builder.\n         * </summary>\n         * <remarks>\n         *   Really shouldn't be used by client code. An instance of this should\n         *   be handed out by a specific record_builder's `add_properties`\n         *   method.\n         * </remarks>\n         */\n        struct property_adder {\n        public:\n            property_adder(record_builder &builder);\n\n            /**\n             * <summary>\n             * Allows chaining of property addition.\n             * </summary>\n             * <example>\n             *    record_builder builder;\n             *    builder.add_properties()\n             *         (L\"Name\", L\"Bjarne Stroustrup\")\n             *         (L\"Level\", 9001);\n             * </example>\n             */\n            template <typename T>\n            property_adder &operator()(const std::wstring &name, T &&value);\n\n        private:\n            record_builder &builder_;\n        };\n    }\n\n    /**\n     * <summary>\n     *   Enables creation of synthetic events in order to test client code.\n     * </summary>\n     * <remarks>\n     *   This beast of a class enables the creation of EVENT_RECORD events\n     *   for testing. The class accepts a collection of keyed pairs that are\n     *   then packed into the event according to the schema on the local\n     *   machine. Because a lot of this is Dark Arts kind of stuff, there\n     *   really isn't a guarantee that this code works perfectly. Please\n     *   file bugs.\n     * </remarks>\n     */\n    class record_builder {\n    private:\n    public:\n        record_builder(\n            const krabs::guid &providerId,\n            size_t id,\n            size_t version,\n            size_t opcode = 0,\n            size_t level = 0,\n            bool trim_string_null_terminator = false);\n\n        /**\n         * <summary>Enables adding new properties to the builder.</summary>\n         * <example>\n         *    record_builder builder;\n         *    builder.add_properties()\n         *         (L\"Name\", L\"Bjarne Stroustrup\")\n         *         (L\"Level\", 9001);\n         * </example>\n         */\n        details::property_adder add_properties();\n\n        /**\n         * <summary>Packs the event properties into an EVENT_RECORD.</summary>\n         * <example>\n         *    record_builder builder;\n         *    builder.add_properties()\n         *         (L\"Name\", L\"Bjarne Stroustrup\")\n         *         (L\"Level\", 9001);\n         *    auto event = builder.pack();\n         * </example>\n         */\n       krabs::testing::synth_record pack() const;\n\n        /**\n         * <summary>\n         *   Packs the event properties into an EVENT_RECORD, but\n         *   doesn't throw when the properties are not complete.\n         * </summary>\n         * <example>\n         *    record_builder builder;\n         *    builder.add_properties()\n         *         (L\"Name\", L\"Grumpy Gills\");\n         *    auto event = builder.pack_incomplete();\n         * </example>\n         */\n        krabs::testing::synth_record pack_incomplete() const;\n\n        /**\n         * <summary>\n         * Provides access to the properties that have been added.\n         * </summary>\n         * <example>\n         *    record_builder builder;\n         *    builder.add_properties()(L\"Foo\", 10);\n         *    for (auto &prop : builder.properties()) {\n         *       // ...\n         *    }\n         * </example>\n         */\n         const std::vector<record_property_thunk> &properties() const;\n\n         /**\n         * <summary>\n         * Adds extended data representing a GUID for an Windows container ID\n         * </summary>\n         */\n         void add_container_id_extended_data(const GUID& container_id);\n\n        /**\n         * <summary>\n         * Gives direct access to the EVENT_HEADER that will be packed into\n         * the faked record.\n         * </summary>\n         */\n        EVENT_HEADER &header();\n\n        /**\n         * <summary>\n         *   Fills an EVENT_RECORD with the info necessary to grab its schema\n         *   via Tdh.\n         * </summary>\n         */\n         EVENT_RECORD create_stub_record() const;\n\n    private:\n\n        /**\n         * <summary>\n         * Does the dirty work of packing up an event record's user data.\n         * </summary>\n         * <returns>\n         *   A pair, where the first item is the packed user data and\n         *   the second is the properties that were not filled (because the\n         *   user never specified them).\n         * </returns>\n         */\n         std::pair<std::vector<BYTE>, std::vector<std::wstring>>\n         pack_impl(const EVENT_RECORD &record) const;\n\n    private:\n        const krabs::guid &providerId_;\n        const size_t id_;\n        const size_t version_;\n        const size_t opcode_;\n        const size_t level_;\n        EVENT_HEADER header_;\n        std::vector<record_property_thunk> properties_;\n        bool trim_string_null_terminator_;\n        extended_data_builder extended_data_;\n\n        friend struct details::property_adder;\n    };\n\n\n    // Implementation\n    // ------------------------------------------------------------------------\n\n    inline details::property_adder::property_adder(record_builder &builder)\n    : builder_(builder)\n    {\n    }\n\n    template <typename T>\n    details::property_adder &details::property_adder::operator()(\n        const std::wstring &name,\n        T &&value)\n    {\n        builder_.properties_.emplace_back(name, value);\n        return *this;\n    }\n\n    // ------------------------------------------------------------------------\n\n    inline record_builder::record_builder(\n        const krabs::guid &providerId,\n        size_t id,\n        size_t version,\n        size_t opcode,\n        size_t level,\n        bool trim_string_null_terminator)\n    : providerId_(providerId)\n    , id_(id)\n    , version_(version)\n    , opcode_(opcode)\n    , level_(level)\n    , trim_string_null_terminator_(trim_string_null_terminator)\n    {\n        ZeroMemory(&header_, sizeof(EVENT_HEADER));\n        header_.EventDescriptor.Id      = static_cast<USHORT>(id_);\n        header_.EventDescriptor.Version = static_cast<UCHAR>(version_);\n        header_.EventDescriptor.Opcode  = static_cast<UCHAR>(opcode_);\n        header_.EventDescriptor.Level   = static_cast<UCHAR>(level_);\n        memcpy(&header_.ProviderId, (const GUID *)&providerId_, sizeof(GUID));\n    }\n\n    inline EVENT_HEADER &record_builder::header()\n    {\n        return header_;\n    }\n\n    inline details::property_adder record_builder::add_properties()\n    {\n        return details::property_adder(*this);\n    }\n\n    inline synth_record record_builder::pack() const\n    {\n        EVENT_RECORD record = create_stub_record();\n\n        auto results = pack_impl(record);\n        if (!results.second.empty()) {\n            std::string msg = \"Not all the properties of the event were filled:\";\n\n            for (auto& s : results.second) {\n#pragma warning(push)\n#pragma warning(disable: 4244) // narrowing property name wchar_t to char for this error message\n                msg += \" \" + std::string(s.begin(), s.end());\n#pragma warning(pop)\n            }\n\n            throw std::invalid_argument(msg);\n        }\n\n        // If it's a size 0 list, pack() will return (nullptr, 0) and no buffer is allocated.\n        auto extended_data_buffer = extended_data_.pack();\n        record.ExtendedData = reinterpret_cast<EVENT_HEADER_EXTENDED_DATA_ITEM*>(extended_data_buffer.first.get());\n        record.ExtendedDataCount = static_cast<USHORT>(extended_data_.count());\n\n        // Pass shared_ptr of the extended data buffer to make sure the buffer isn't deleted before the synth_record is.\n        return krabs::testing::synth_record(record, results.first, extended_data_buffer.first);\n    }\n\n    inline synth_record record_builder::pack_incomplete() const\n    {\n        EVENT_RECORD record = create_stub_record();\n        auto results = pack_impl(record);\n\n        // If it's a size 0 list, pack() will return (nullptr, 0) and no buffer is allocated.\n        auto extended_data_buffer = extended_data_.pack();\n        record.ExtendedData = reinterpret_cast<EVENT_HEADER_EXTENDED_DATA_ITEM*>(extended_data_buffer.first.get());\n        record.ExtendedDataCount = static_cast<USHORT>(extended_data_.count());\n\n        // Pass shared_ptr of the extended data buffer to make sure the buffer isn't deleted before the synth_record is.\n        return krabs::testing::synth_record(record, results.first, extended_data_buffer.first);\n    }\n\n    inline EVENT_RECORD record_builder::create_stub_record() const\n    {\n        EVENT_RECORD record = {0};\n        memcpy(&record.EventHeader, &header_, sizeof(EVENT_HEADER));\n        if (record.EventHeader.Size == 0) {\n            record.EventHeader.Size = sizeof(record.EventHeader);\n        }\n\n        return record;\n    }\n\n    inline const std::vector<record_property_thunk> &\n    record_builder::properties() const\n    {\n        return properties_;\n    }\n\n    inline void record_builder::add_container_id_extended_data(const GUID& container_id)\n    {\n        extended_data_.add_container_id(container_id);\n    }\n\n    inline std::pair<std::vector<BYTE>, std::vector<std::wstring>>\n    record_builder::pack_impl(const EVENT_RECORD &record) const\n    {\n        std::pair<std::vector<BYTE>, std::vector<std::wstring>> results;\n        krabs::schema_locator schema_locator;\n        krabs::schema event_schema(record, schema_locator);\n        krabs::parser event_parser(event_schema);\n\n        // When the last property in a record is of string type (ansi or unicode), \n        // ETW may omit the string NULL terminator. bytes_to_trim below will eventually be\n        // set to the number of bytes that can be trimmed from the generated buffer.\n\n        auto bytes_to_trim = 0;\n        for (auto prop : event_parser.properties()) {\n            bytes_to_trim = 0;\n\n            auto found_prop = std::find_if(properties_.begin(),\n                                           properties_.end(),\n                                           [&](const record_property_thunk &thunk) {\n                                                return prop.name() == thunk.name();\n                                           });\n\n            if (found_prop != properties_.end()) {\n\n                // Verify that the user-provided property data matches the type\n                // that the schema expects.\n                if (found_prop->type() != prop.type()) {\n                    std::string ansi(prop.name().begin(), prop.name().end());\n                    auto msg = std::string(\n                        \"Invalid property type given for property \" + ansi +\n                        \" Expected: \" + krabs::in_type_to_string(prop.type()) +\n                        \" Received: \" + krabs::in_type_to_string(found_prop->type()));\n\n                    throw std::invalid_argument(msg.c_str());\n                }\n\n                // if this is a string type, we could trim the null terminator\n                // (assuming that there are no other properties after this one)\n                if (prop.type() == TDH_INTYPE_UNICODESTRING) {\n                    bytes_to_trim = sizeof(L'\\0');\n                }\n                else if (prop.type() == TDH_INTYPE_ANSISTRING) {\n                    bytes_to_trim = sizeof('\\0');\n                }\n\n                std::copy(found_prop->bytes().begin(),\n                          found_prop->bytes().end(),\n                          std::back_inserter(results.first));\n            } else {\n\n                // If the property wasn't filled by the user's tests, we fill\n                // it with empty data that is the size that is expected\n                // according to the schema. We also remember these properties,\n                // because it may be considered an error to not fill all\n                // properties manually.\n                results.second.emplace_back(prop.name());\n                std::fill_n(std::back_inserter(results.first),\n                            details::how_many_bytes_to_fill(prop.type()), static_cast<UCHAR>(0));\n            }\n        }\n\n        if (trim_string_null_terminator_) {\n            results.first.resize(results.first.size() - bytes_to_trim);\n        }\n\n        return results;\n    }\n} /* namespace testing */ } /* namespace krabs */\n"
  },
  {
    "path": "libs/krabs/krabs/testing/record_property_thunk.hpp",
    "content": "// Copyright (c) Microsoft. All rights reserved.\n// Licensed under the MIT license. See LICENSE file in the project root for full license information.\n\n#pragma once\n\n#include \"../compiler_check.hpp\"\n#include \"filler.hpp\"\n\nnamespace krabs { namespace testing {\n\n    class record_builder;\n\n    /**\n     * <summary>\n     *   Takes any value and turns it into a sequence of serialized bytes.\n     * </summary>\n     * <remarks>\n     *   When we're composing an event, we need to store heterogeneous types in\n     *   a collection while we wait until we know exactly how to pack the actual\n     *   event. Because the actual EVENT_RECORD structure properties are packed\n     *   into a byte collection, we take our cue from that and do similarly. We\n     *   keep all of the random property byte blobs separate until we know the\n     *   particular order to stash them in so we have less futzing to do later.\n     * </remarks>\n     */\n    class record_property_thunk {\n    public:\n\n        template <typename T>\n        record_property_thunk(const std::wstring &property, const T &value);\n\n        record_property_thunk(const std::wstring &property, const wchar_t *value);\n        record_property_thunk(const std::wstring &property, const char *value);\n        record_property_thunk(const std::wstring &property, bool value);\n\n        const std::wstring &name() const;\n        const std::vector<BYTE> &bytes() const;\n        const _TDH_IN_TYPE type() const;\n\n    private:\n\n        // We need this because we don't have delegating constructors in VS 2012.\n        template <typename T>\n        void common_string_init(const std::wstring &property, const T &value);\n\n        template <typename T>\n        void common_init(const std::wstring &property, const T &value);\n\n    private:\n        std::wstring name_;\n        std::vector<BYTE> bytes_;\n        _TDH_IN_TYPE type_;\n\n        friend class record_builder;\n    };\n\n    // Implementation\n    // ------------------------------------------------------------------------\n    template <typename T>\n    inline record_property_thunk::record_property_thunk(\n        const std::wstring &property,\n        const T &value)\n    {\n        common_init(property, value);\n    }\n\n    // Specialization for wstrings\n    template <>\n    inline record_property_thunk::record_property_thunk(\n        const std::wstring &property,\n        const std::wstring &value)\n    {\n        common_string_init(property, value);\n    }\n\n    // Specialization for strings\n    template <>\n    inline record_property_thunk::record_property_thunk(\n        const std::wstring &property,\n        const std::string &value)\n    {\n        common_string_init(property, value);\n    }\n\n    // Overload for wchar_t strings.\n    inline record_property_thunk::record_property_thunk(\n        const std::wstring &property,\n        const wchar_t *value)\n    {\n        common_string_init(property, std::move(std::wstring(value)));\n    }\n\n    // Overload for char strings.\n    inline record_property_thunk::record_property_thunk(\n        const std::wstring &property,\n        const char *value)\n    {\n        common_string_init(property, std::move(std::string(value)));\n    }\n\n    // Specialization for binary blobs\n    template <>\n    inline record_property_thunk::record_property_thunk(\n        const std::wstring &property,\n        const krabs::binary &bin)\n    : name_(property)\n    , bytes_(bin.bytes())\n    , type_(krabs::testing::details::tdh_morphism<krabs::binary>::value)\n    {\n    }\n\n    // Overload for booleans\n    inline record_property_thunk::record_property_thunk(\n        const std::wstring &property,\n        bool value)\n    {\n        common_init(property, (int)value);\n        type_ = krabs::testing::details::tdh_morphism<bool>::value;\n    }\n\n    template <typename T>\n    void record_property_thunk::common_init(\n        const std::wstring &property,\n        const T &value)\n    {\n        name_  = property;\n        bytes_ = std::move(std::vector<BYTE>((BYTE*)&value, (BYTE*)&value + sizeof(T)));\n        type_  = krabs::testing::details::tdh_morphism<T>::value;\n    }\n\n    template <typename T>\n    void record_property_thunk::common_string_init(\n        const std::wstring &property,\n        const T &value)\n    {\n        name_ = property;\n\n        const size_t size = value.size() * sizeof(typename T::value_type);\n        bytes_ = std::move(std::vector<BYTE>((BYTE*)&value[0], (BYTE*)&value[0] + size));\n        type_  =  krabs::testing::details::tdh_morphism<T>::value;\n\n        // Null terminate the string\n        for (size_t i = 0; i < sizeof(typename T::value_type); ++i) {\n            bytes_.push_back('\\0');\n        }\n    }\n\n    inline const std::wstring &record_property_thunk::name() const\n    {\n        return name_;\n    }\n\n    inline const std::vector<BYTE> &record_property_thunk::bytes() const\n    {\n        return bytes_;\n    }\n\n    inline const _TDH_IN_TYPE record_property_thunk::type() const\n    {\n        return type_;\n    }\n\n} /* namespace testing */ } /* namespace krabs */\n"
  },
  {
    "path": "libs/krabs/krabs/testing/synth_record.hpp",
    "content": "// Copyright (c) Microsoft. All rights reserved.\n// Licensed under the MIT license. See LICENSE file in the project root for full license information.\n\n#pragma once\n\n#define INITGUID\n\n#include <vector>\n#include <algorithm>\n\n#include \"../compiler_check.hpp\"\n#include \"../guid.hpp\"\n#include \"../schema.hpp\"\n#include \"../parser.hpp\"\n\n#include <evntrace.h>\n#include <evntcons.h>\n#include <memory>\n\nnamespace krabs { namespace testing {\n\n    /**\n     * <summary>\n     *   Represents a property that is faked -- one that is built by hand for the\n     *   purpose of testing event reaction code.\n     * </summary>\n     */\n    class synth_record {\n    public:\n\n        /**\n         * <summary>\n         *   Constructs a synthetic property, given a partially filled\n         *   EVENT_RECORD and a packed sequence of bytes that represent the\n         *   event's user data.\n         * </summary>\n         * <remarks>\n         *   This class should not be directly instantiated -- an record_builder\n         *   should return this with its `pack` methods.\n         * </remarks>\n         */\n        synth_record(const EVENT_RECORD& record,\n                     const std::vector<BYTE>& user_data);\n\n        /**\n         * <summary>\n         *   Constructs a synthetic property, given a partially filled\n         *   EVENT_RECORD and a packed sequence of bytes that represent the\n         *   event's user data.\n         * </summary>\n         * <remarks>\n         *   This class should not be directly instantiated -- an record_builder\n         *   should return this with its `pack` methods.\n         * </remarks>\n         */\n        synth_record(const EVENT_RECORD &record,\n                     const std::vector<BYTE> &user_data,\n                     const std::shared_ptr<BYTE[]> &extended_data);\n\n        /**\n         * <summary>\n         *   Copies a synth_record and updates the pointers\n         *   in the EVENT_RECORD appropriately.\n         * </summary>\n         */\n        synth_record(const synth_record& other);\n\n        /**\n         * <summary>\n         *   Moves a synth_record into a new instance.\n         * </summary>\n         */\n        synth_record(synth_record&& other);\n\n        /**\n         * <summary>\n         *   Assigns a synth_record to another.\n         * </summary>\n         * <remarks>by value to take advantage of move ctor</remarks>\n         */\n        synth_record& operator=(synth_record);\n\n        /**\n         * <summary>\n         *   Allows implicit casts to an EVENT_RECORD.\n         * </summary>\n         */\n         operator const EVENT_RECORD&() const;\n\n        /**\n         * <summary>\n         *   Swaps two synth_records.\n         * </summary>\n         */\n        friend void swap(synth_record& left, synth_record& right)\n        {\n            using std::swap; // ADL\n\n            swap(left.record_, right.record_);\n            swap(left.data_, right.data_);\n            swap(left.extended_data_, right.extended_data_);\n        }\n\n    private:\n        synth_record()\n            : record_()\n            , data_() { }\n\n        EVENT_RECORD record_;\n        std::vector<BYTE> data_;\n\n        // extended_data shared PTR is passed around to make sure that the data\n        // buffer is only deleted after all dependent synth_records are deleted.\n        // since the extended data structure uses direct pointers to data\n        // instead of offsets, we can't pass around a vector<BYTE> unless we\n        // also want to redo the pointers every time the buffer is copied.\n        std::shared_ptr<BYTE[]> extended_data_;\n    };\n\n    // Implementation\n    // ------------------------------------------------------------------------\n\n    inline synth_record::synth_record(const EVENT_RECORD& record,\n        const std::vector<BYTE>& user_data)\n    : synth_record(record, user_data, std::shared_ptr<BYTE[]>())\n    {\n        // Empty shared_ptr is fine here because there's no concern\n        // about managing lifetime of an extended data buffer if there\n        // is no extended data buffer.\n    }\n\n    inline synth_record::synth_record(\n        const EVENT_RECORD &record,\n        const std::vector<BYTE> &user_data,\n        const std::shared_ptr<BYTE[]> &extended_data)\n    : record_(record)\n    , data_(user_data)\n    , extended_data_(extended_data)\n    {\n        if (data_.size() > 0) {\n            record_.UserData = &data_[0];\n        } else {\n            record_.UserData = 0;\n        }\n\n        record_.UserDataLength = static_cast<USHORT>(data_.size());\n    }\n\n    inline synth_record::synth_record(const synth_record& other)\n        : synth_record(other.record_, other.data_, other.extended_data_)\n    { }\n\n    inline synth_record::synth_record(synth_record&& other)\n        : synth_record()\n    {\n        swap(*this, other);\n    }\n\n    inline synth_record& synth_record::operator=(synth_record other)\n    {\n        swap(*this, other);\n        return *this;\n    }\n\n    inline synth_record::operator const EVENT_RECORD&() const\n    {\n        return record_;\n    }\n\n} /* namespace testing */ } /* namespace krabs */\n"
  },
  {
    "path": "libs/krabs/krabs/trace.hpp",
    "content": "// Copyright (c) Microsoft. All rights reserved.\n// Licensed under the MIT license. See LICENSE file in the project root for full license information.\n\n#pragma once\n\n#include <deque>\n\n#include \"compiler_check.hpp\"\n#include \"guid.hpp\"\n#include \"provider.hpp\"\n#include \"trace_context.hpp\"\n#include \"etw.hpp\"\n\n\nnamespace krabs { namespace details {\n    template <typename T> class trace_manager;\n} /* namespace details */ } /* namespace krabs */\n\nnamespace krabs { namespace testing {\n    template <typename T> class trace_proxy;\n} /* namespace testing */ } /* namespace krabs */\n\n\nnamespace krabs {\n\n    template <typename T>\n    class provider;\n\n    /**\n     * <summary>\n     * Selected statistics about an ETW trace\n     * </summary>\n     */\n    class trace_stats\n    {\n    public:\n        const uint32_t buffersCount;\n        const uint32_t buffersFree;\n        const uint32_t buffersWritten;\n        const uint32_t buffersLost;\n        const uint64_t eventsTotal;\n        const uint64_t eventsHandled;\n        const uint32_t eventsLost;\n\n        trace_stats(uint64_t eventsHandled, const EVENT_TRACE_PROPERTIES& props)\n            : buffersCount(props.NumberOfBuffers)\n            , buffersFree(props.FreeBuffers)\n            , buffersWritten(props.BuffersWritten)\n            , buffersLost(props.RealTimeBuffersLost)\n            , eventsTotal(eventsHandled + props.EventsLost)\n            , eventsHandled(eventsHandled)\n            , eventsLost(props.EventsLost)\n        { }\n    };\n\n    /**\n     * <summary>\n     *    Represents a single trace session that can have multiple\n     *    enabled providers. Ideally, there should only need to be a\n     *    single trace instance for all ETW user traces.\n     * </summary>\n     */\n    template <typename T>\n    class trace {\n    public:\n\n        typedef T trace_type;\n\n        /**\n         * <summary>\n         *   Constructs a trace with an optional trace name, which can be\n         *   any arbitrary, unique name.\n         * </summary>\n         *\n         * <example>\n         *   trace trace;\n         *   trace namedTrace(L\"Some special name\");\n         * </example>\n         */\n        trace(const std::wstring &name);\n        trace(const wchar_t *name = L\"\");\n\n        /**\n         * <summary>\n         *   Destructs the trace session and unregisters the session, if\n         *   applicable.\n         * </summary>\n         *\n         * <example>\n         *   trace trace;\n         *   // ~trace implicitly called\n         * </example>\n         */\n        ~trace();\n\n        /**\n         * <summary>\n         * Sets the trace properties for a session.\n         * Must be called before open()/start().\n         * See https://docs.microsoft.com/en-us/windows/win32/etw/event-trace-properties\n         * for important details and restrictions.\n         * Configurable properties are ->\n         *  - BufferSize.  In KB. The maximum buffer size is 1024 KB.\n         *  - MinimumBuffers. Minimum number of buffers is two per processor*.\n         *  - MaximumBuffers.\n         *  - FlushTimer. How often, in seconds, the trace buffers are forcibly flushed.\n         *  - LogFileMode. EVENT_TRACE_NO_PER_PROCESSOR_BUFFERING simulates a *single* sequential processor.\n         * </summary>\n         * <example>\n         *    krabs::trace trace;\n         *    EVENT_TRACE_PROPERTIES properties = { 0 };\n         *    properties.BufferSize = 256;\n         *    properties.MinimumBuffers = 12;\n         *    properties.MaximumBuffers = 48;\n         *    properties.FlushTimer = 1;\n         *    properties.LogFileMode = EVENT_TRACE_REAL_TIME_MODE;\n         *    trace.set_trace_properties(&properties);\n         *    krabs::guid id(L\"{A0C1853B-5C40-4B15-8766-3CF1C58F985A}\");\n         *    provider<> powershell(id);\n         *    trace.enable(powershell);\n         *    trace.start();\n         * </example>\n         */\n        void set_trace_properties(const PEVENT_TRACE_PROPERTIES properties);\n\n        /**\n         * <summary>\n         * Configures trace session settings.\n         * Must be called after open().\n         * See https://docs.microsoft.com/en-us/windows/win32/api/evntrace/nf-evntrace-tracesetinformation\n         * for more information.\n         * </summary>\n         * <example>\n         *    krabs::trace trace;\n         *    // Adjust SE_SYSTEM_PROFILE_NAME token privilege through AdjustTokenPrivileges(...)\n         *    // to enable stack tracing (not done in this example). Then:\n         *    STACK_TRACING_EVENT_ID event_id = {0};\n         *    event_id.EventGuid = krabs::guids::perf_info;\n         *    event_id.Type = 46; // SampleProfile\n         *    trace.open();\n         *    trace.set_trace_information(TraceStackTracingInfo, &event_id, sizeof(STACK_TRACING_EVENT_ID));\n         *    krabs::kernel_provider stack_walk_provider(EVENT_TRACE_FLAG_PROFILE, krabs::guids::stack_walk);\n         *    trace.enable(stack_walk_provider);\n         *    trace.process();\n         * </example>\n         */\n        void set_trace_information(\n            TRACE_INFO_CLASS information_class,\n            PVOID trace_information,\n            ULONG information_length);\n\n        /**\n         * <summary>\n         * Configures trace to read from a file instead of realtime\n         * Must be called before open().\n         * See https://docs.microsoft.com/en-us/windows/win32/api/evntrace/nf-evntrace-tracesetinformation\n         * for more information.\n         * </summary>\n         * <example>\n         *    krabs::trace trace;\n         *    trace.set_trace_filename(L\"C:\\merged.etl\");\n         *    trace.process();\n         * </example>\n         */\n        void set_trace_filename(const std::wstring& filename);\n\n        /**\n         * <summary>\n         * Enables the provider on the given user trace.\n         * </summary>\n         * <example>\n         *    krabs::trace trace;\n         *    krabs::guid id(L\"{A0C1853B-5C40-4B15-8766-3CF1C58F985A}\");\n         *    provider<> powershell(id);\n         *    trace.enable(powershell);\n         * </example>\n         */\n        void enable(const typename T::provider_type &p);\n\n        /**\n         * <summary>\n         * Starts a trace session.\n         * </summary>\n         * <example>\n         *    krabs::trace trace;\n         *    krabs::guid id(L\"{A0C1853B-5C40-4B15-8766-3CF1C58F985A}\");\n         *    provider<> powershell(id);\n         *    trace.enable(powershell);\n         *    trace.start();\n         * </example>\n         */\n        void start();\n\n        /**\n         * <summary>\n         * Closes a trace session.\n         * </summary>\n         * <example>\n         *    krabs::trace trace;\n         *    krabs::guid id(L\"{A0C1853B-5C40-4B15-8766-3CF1C58F985A}\");\n         *    provider<> powershell(id);\n         *    trace.enable(powershell);\n         *    trace.start();\n         *    trace.stop();\n         * </example>\n         */\n        void stop();\n\n        /**\n        * <summary>\n        * Opens a trace session.\n        * This is an optional call before start() if you need the trace\n        * registered with the ETW subsystem before you start processing events.\n        * </summary>\n        * <example>\n        *    krabs::trace trace;\n        *    krabs::guid id(L\"{A0C1853B-5C40-4B15-8766-3CF1C58F985A}\");\n        *    provider<> powershell(id);\n        *    trace.enable(powershell);\n        *    auto logfile = trace.open();\n        * </example>\n        */\n        EVENT_TRACE_LOGFILE open();\n\n        /**\n        * <summary>\n        * Start processing events for an already opened session.\n        * </summary>\n        * <example>\n        *    krabs::trace trace;\n        *    krabs::guid id(L\"{A0C1853B-5C40-4B15-8766-3CF1C58F985A}\");\n        *    provider<> powershell(id);\n        *    trace.enable(powershell);\n        *    trace.open();\n        *    trace.process();\n        * </example>\n        */\n        void process();\n\n        /**\n         * <summary>\n         * Queries the trace session to get stats about\n         * events lost and buffers handled.\n         * </summary>\n         */\n        trace_stats query_stats();\n\n        /**\n         * <summary>\n         * Returns the number of buffers that were processed.\n         * </summary>\n         * <example>\n         *    krabs::trace trace;\n         *    krabs::guid id(L\"{A0C1853B-5C40-4B15-8766-3CF1C58F985A}\");\n         *    provider<> powershell(id);\n         *    trace.enable(powershell);\n         *    trace.start();\n         *    trace.stop();\n         *    std::wcout << trace.buffers_processed() << std::endl;\n         * </example>\n         */\n        size_t buffers_processed() const;\n\n        /**\n         * <summary>\n         * Adds a function to call when an event is fired which has no corresponding provider.\n         * </summary>\n         *\n         * <param name=\"callback\">the function to call into</param>\n         * <example>\n         *    void my_fun(const EVENT_RECORD &record) { ... }\n         *    // ...\n         *    krabs::trace trace;\n         *    trace.set_default_event_callback(my_fun);\n         * </example>\n         *\n         * <example>\n         *    auto fun = [&](const EVENT_RECORD &record) {...}\n         *    krabs::trace trace;\n         *    trace.set_default_event_callback(fun);\n         * </example>\n         */\n        void set_default_event_callback(c_provider_callback callback);\n\n    private:\n\n        /**\n         * <summary>\n         *   Invoked when an event occurs in the underlying ETW session.\n         * </summary>\n         */\n        void on_event(const EVENT_RECORD &);\n\n    private:\n        std::wstring name_;\n        std::wstring logFilename_;\n        std::deque<std::reference_wrapper<const typename T::provider_type>> providers_;\n\n        TRACEHANDLE registrationHandle_;\n        TRACEHANDLE sessionHandle_;\n\n        size_t buffersRead_;\n        uint64_t eventsHandled_;\n\n        EVENT_TRACE_PROPERTIES properties_;\n\n        const trace_context context_;\n\n        provider_callback default_callback_ = nullptr;\n\n    private:\n        template <typename T>\n        friend class details::trace_manager;\n\n        template <typename T>\n        friend class testing::trace_proxy;\n\n        friend typename T;\n    };\n\n    // Implementation\n    // ------------------------------------------------------------------------\n\n    template <typename T>\n    trace<T>::trace(const std::wstring &name)\n    : registrationHandle_(INVALID_PROCESSTRACE_HANDLE)\n    , sessionHandle_(INVALID_PROCESSTRACE_HANDLE)\n    , eventsHandled_(0)\n    , buffersRead_(0)\n    , context_()\n    {\n        name_ = T::enforce_name_policy(name);\n        ZeroMemory(&properties_, sizeof(EVENT_TRACE_PROPERTIES));\n    }\n\n    template <typename T>\n    trace<T>::trace(const wchar_t *name)\n    : registrationHandle_(INVALID_PROCESSTRACE_HANDLE)\n    , sessionHandle_(INVALID_PROCESSTRACE_HANDLE)\n    , eventsHandled_(0)\n    , buffersRead_(0)\n    , context_()\n    {\n        name_ = T::enforce_name_policy(name);\n        ZeroMemory(&properties_, sizeof(EVENT_TRACE_PROPERTIES));\n    }\n\n    template <typename T>\n    trace<T>::~trace()\n    {\n        stop();\n    }\n\n    template <typename T>\n    void trace<T>::set_trace_properties(const PEVENT_TRACE_PROPERTIES properties)\n    {\n        properties_.BufferSize = properties->BufferSize;\n        properties_.MinimumBuffers = properties->MinimumBuffers;\n        properties_.MaximumBuffers = properties->MaximumBuffers;\n        properties_.FlushTimer = properties->FlushTimer;\n        properties_.LogFileMode = properties->LogFileMode;\n    }\n\n    template <typename T>\n    void trace<T>::set_trace_information(\n        TRACE_INFO_CLASS information_class,\n        PVOID trace_information,\n        ULONG information_length)\n    {\n        details::trace_manager<trace> manager(*this);\n        manager.set_trace_information(information_class, trace_information, information_length);\n    }\n\n    template <typename T>\n    void trace<T>::set_trace_filename(const std::wstring& filename)\n    {\n        logFilename_ = filename;\n    }\n\n    template <typename T>\n    void trace<T>::on_event(const EVENT_RECORD &record)\n    {\n        ++eventsHandled_;\n        T::forward_events(record, *this);\n    }\n\n    template <typename T>\n    void trace<T>::enable(const typename T::provider_type &p)\n    {\n        providers_.push_back(std::ref(p));\n    }\n\n    template <typename T>\n    void trace<T>::start()\n    {\n        eventsHandled_ = 0;\n\n        details::trace_manager<trace> manager(*this);\n        manager.start();\n    }\n\n    template <typename T>\n    void trace<T>::stop()\n    {\n        details::trace_manager<trace> manager(*this);\n        manager.stop();\n    }\n\n    template <typename T>\n    EVENT_TRACE_LOGFILE trace<T>::open()\n    {\n        eventsHandled_ = 0;\n\n        details::trace_manager<trace> manager(*this);\n        return manager.open();\n    }\n\n    template <typename T>\n    void trace<T>::process()\n    {\n        eventsHandled_ = 0;\n\n        details::trace_manager<trace> manager(*this);\n        manager.process();\n    }\n\n    template <typename T>\n    trace_stats trace<T>::query_stats()\n    {\n        details::trace_manager<trace> manager(*this);\n        return { eventsHandled_, manager.query() };\n    }\n\n    template <typename T>\n    size_t trace<T>::buffers_processed() const\n    {\n        return buffersRead_;\n    }\n\n    template <typename T>\n    void trace<T>::set_default_event_callback(c_provider_callback callback)\n    {\n        default_callback_ = callback;\n    }\n\n}\n"
  },
  {
    "path": "libs/krabs/krabs/trace_context.hpp",
    "content": "// Copyright (c) Microsoft. All rights reserved.\n// Licensed under the MIT license. See LICENSE file in the project root for full license information.\n\n#pragma once\n\n#include \"schema_locator.hpp\"\n\nnamespace krabs {\n\n    /**\n     * <summary>\n     * Additional ETW trace context passed to event callbacks\n     * to enable processing.\n     * </summary>\n     */\n    struct trace_context\n    {\n        const schema_locator schema_locator;\n        /* Add additional trace context here. */\n    };\n\n}\n"
  },
  {
    "path": "libs/krabs/krabs/ut.hpp",
    "content": "// Copyright (c) Microsoft. All rights reserved.\n// Licensed under the MIT license. See LICENSE file in the project root for full license information.\n\n#pragma once\n\n#include <set>\n\n#include \"compiler_check.hpp\"\n#include \"trace.hpp\"\n#include \"provider.hpp\"\n\nnamespace krabs { namespace details {\n\n    /**\n     * <summary>\n     *   Used as a template argument to a trace instance. This class implements\n     *   code paths for user traces. Should never be used or seen by client\n     *   code.\n     * </summary>\n     */\n    struct ut {\n\n        typedef krabs::provider<> provider_type;\n        \n        struct filter_flags {\n            UCHAR level_;\n            ULONGLONG any_;\n            ULONGLONG all_;\n            ULONG trace_flags_;\n        };\n\n        struct filter_settings{\n            std::set<unsigned short> provider_filter_event_ids_;\n            filter_flags filter_flags_{};\n            bool rundown_enabled_ = false;\n        };\n\n        typedef std::map<krabs::guid, filter_settings> provider_filter_settings;\n        /**\n         * <summary>\n         *   Used to assign a name to the trace instance that is being\n         *   instantiated.\n         * </summary>\n         * <remarks>\n         *   There really isn't a name policy to enforce with user traces, but\n         *   kernel traces do have specific naming requirements.\n         * </remarks>\n         */\n        static const std::wstring enforce_name_policy(\n            const std::wstring &name);\n\n        /**\n         * <summary>\n         *   Generates a value that fills the EnableFlags field in an\n         *   EVENT_TRACE_PROPERTIES structure. This controls the providers that\n         *   get enabled for a kernel trace. For a user trace, it doesn't do\n         *   much of anything.\n         * </summary>\n         */\n        static const unsigned long construct_enable_flags(\n            const krabs::trace<krabs::details::ut> &trace);\n\n        /**\n         * <summary>\n         *   Enables the providers that are attached to the given trace.\n         * </summary>\n         */\n        static void enable_providers(\n            const krabs::trace<krabs::details::ut> &trace);\n\n        /**\n         * <summary>\n         *   Enables the configured rundown events for each provider.\n         *   Should be called immediately prior to ProcessTrace.\n         * </summary>\n         */\n        static void enable_rundown(\n            const krabs::trace<krabs::details::ut>& trace);\n\n        /**\n         * <summary>\n         *   Decides to forward an event to any of the providers in the trace.\n         * </summary>\n         */\n        static void forward_events(\n            const EVENT_RECORD &record,\n            const krabs::trace<krabs::details::ut> &trace);\n\n        /**\n         * <summary>\n         *   Sets the ETW trace log file mode.\n         * </summary>\n         */\n        static unsigned long augment_file_mode();\n\n        /**\n         * <summary>\n         *   Returns the GUID of the trace session.\n         * </summary>\n         */\n        static krabs::guid get_trace_guid();\n    };\n\n\n    // Implementation\n    // ------------------------------------------------------------------------\n\n    inline const std::wstring ut::enforce_name_policy(\n        const std::wstring &name_hint)\n    {\n        if (name_hint.empty()) {\n            return std::to_wstring(krabs::guid::random_guid());\n        }\n\n        return name_hint;\n    }\n\n    inline const unsigned long ut::construct_enable_flags(\n        const krabs::trace<krabs::details::ut> &)\n    {\n        return 0;\n    }\n\n    inline void ut::enable_providers(\n        const krabs::trace<krabs::details::ut> &trace)\n    {\n        if (trace.registrationHandle_ == INVALID_PROCESSTRACE_HANDLE)\n            return;\n\n        provider_filter_settings provider_flags;\n\n        // This function essentially takes the union of all the provider flags\n        // for a given provider GUID. This comes about when multiple providers\n        // for the same GUID are provided and request different provider flags.\n        // TODO: Only forward the calls that are requested to each provider.\n        for (auto &provider : trace.providers_) {\n            auto& settings = provider_flags[provider.get().guid_];\n            settings.filter_flags_.level_       |= provider.get().level_;\n            settings.filter_flags_.any_         |= provider.get().any_;\n            settings.filter_flags_.all_         |= provider.get().all_;\n            settings.filter_flags_.trace_flags_ |= provider.get().trace_flags_;\n            settings.rundown_enabled_           |= provider.get().rundown_enabled_;\n\n            for (const auto& filter : provider.get().filters_) {\n                settings.provider_filter_event_ids_.insert(\n                    filter.provider_filter_event_ids().begin(),\n                    filter.provider_filter_event_ids().end());\n            }\n        }\n\n        for (auto &provider : provider_flags) {\n            ENABLE_TRACE_PARAMETERS parameters;\n            parameters.ControlFlags = 0;\n            parameters.Version = ENABLE_TRACE_PARAMETERS_VERSION_2;\n            parameters.SourceId = provider.first;\n            \n            GUID guid = provider.first;\n            auto& settings = provider.second;\n\n            parameters.EnableProperty = settings.filter_flags_.trace_flags_;\n            parameters.EnableFilterDesc = nullptr;\n            parameters.FilterDescCount = 0;\n            EVENT_FILTER_DESCRIPTOR filterDesc{};\n            std::vector<BYTE> filterEventIdBuffer;\n            auto filterEventIdCount = settings.provider_filter_event_ids_.size();\n\n            if (filterEventIdCount > 0) {\n                //event filters existing, set native filters using API\n                parameters.FilterDescCount = 1;\n                filterDesc.Type = EVENT_FILTER_TYPE_EVENT_ID;\n\n                //allocate + size of expected events in filter\n                DWORD size = FIELD_OFFSET(EVENT_FILTER_EVENT_ID, Events[filterEventIdCount]);\n                filterEventIdBuffer.resize(size, 0);\n\n                auto filterEventIds = reinterpret_cast<PEVENT_FILTER_EVENT_ID>(&(filterEventIdBuffer[0]));\n                filterEventIds->FilterIn = TRUE;\n                filterEventIds->Count = static_cast<USHORT>(filterEventIdCount);\n\n                auto index = 0;\n                for (auto filter : settings.provider_filter_event_ids_) {\n                    filterEventIds->Events[index] = filter;\n                    index++;\n                }\n\n                filterDesc.Ptr = reinterpret_cast<ULONGLONG>(filterEventIds);\n                filterDesc.Size = size;\n\n                parameters.EnableFilterDesc = &filterDesc;\n            }\n\n            ULONG status = EnableTraceEx2(trace.registrationHandle_,\n                                          &guid,\n                                          EVENT_CONTROL_CODE_ENABLE_PROVIDER,\n                                          settings.filter_flags_.level_,\n                                          settings.filter_flags_.any_,\n                                          settings.filter_flags_.all_,\n                                          0,\n                                          &parameters);\n            error_check_common_conditions(status);\n        }\n    }\n\n    inline void ut::enable_rundown(\n        const krabs::trace<krabs::details::ut>& trace)\n    {\n        if (trace.registrationHandle_ == INVALID_PROCESSTRACE_HANDLE)\n            return;\n\n        for (auto& provider : trace.providers_) {\n            if (!provider.get().rundown_enabled_)\n                continue;\n\n            ULONG status = EnableTraceEx2(trace.registrationHandle_,\n                &provider.get().guid_,\n                EVENT_CONTROL_CODE_CAPTURE_STATE,\n                0,\n                0,\n                0,\n                0,\n                NULL);\n            error_check_common_conditions(status);\n        }\n    }\n\n    inline void ut::forward_events(\n        const EVENT_RECORD &record,\n        const krabs::trace<krabs::details::ut> &trace)\n    {\n        // for manifest providers, EventHeader.ProviderId is the Provider GUID\n        for (auto& provider : trace.providers_) {\n            if (record.EventHeader.ProviderId == provider.get().guid_) {\n                provider.get().on_event(record, trace.context_);\n                return;\n            }\n        }\n\n        // for MOF providers, EventHeader.Provider is the *Message* GUID\n        // we need to ask TDH for event information in order to determine the\n        // correct provider to pass this event to\n        auto schema = get_event_schema_from_tdh(record);\n        auto eventInfo = reinterpret_cast<PTRACE_EVENT_INFO>(schema.get());\n        for (auto& provider : trace.providers_) {\n            if (eventInfo->ProviderGuid == provider.get().guid_) {\n                provider.get().on_event(record, trace.context_);\n                return;\n            }\n        }\n\n        if (trace.default_callback_ != nullptr)\n            trace.default_callback_(record, trace.context_);\n    }\n\n    inline unsigned long ut::augment_file_mode()\n    {\n        return 0;\n    }\n\n    inline krabs::guid ut::get_trace_guid()\n    {\n        return krabs::guid::random_guid();\n    }\n\n} /* namespace details */ } /* namespace krabs */\n"
  },
  {
    "path": "libs/krabs/krabs/version_helpers.hpp",
    "content": "// Copyright (c) Microsoft. All rights reserved.\n// Licensed under the MIT license. See LICENSE file in the project root for full license information.\n\n// We manually include this file because it doesn't exist for VS2012.\n\n#ifndef _versionhelpers_H_INCLUDED_\n#define _versionhelpers_H_INCLUDED_\n\n#include <winapifamily.h>\n\n#ifdef _MSC_VER\n#pragma once\n#endif  // _MSC_VER\n\n#pragma region Application Family\n#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)\n\n#include <specstrings.h>    // for _In_, etc.\n\n#if !defined(__midl) && !defined(SORTPP_PASS)\n\n#if (NTDDI_VERSION >= NTDDI_WINXP)\n\n#ifdef __cplusplus\n\n#define VERSIONHELPERAPI inline bool\n\n#else  // __cplusplus\n\n#define VERSIONHELPERAPI FORCEINLINE BOOL\n\n#endif // __cplusplus\n\n#ifndef NTDDI_WINBLUE\n#define NTDDI_WINBLUE 0x06030000\n#endif\n\n#ifndef _WIN32_WINNT_WINBLUE\n#define _WIN32_WINNT_WINBLUE 0x0602\n#endif\n\nVERSIONHELPERAPI\nIsWindowsVersionOrGreater(WORD wMajorVersion, WORD wMinorVersion, WORD wServicePackMajor)\n{\n    OSVERSIONINFOEXW osvi = { sizeof(osvi), 0, 0, 0, 0, {0}, 0, 0 };\n    DWORDLONG        const dwlConditionMask = VerSetConditionMask(\n        VerSetConditionMask(\n        VerSetConditionMask(\n            0, VER_MAJORVERSION, VER_GREATER_EQUAL),\n               VER_MINORVERSION, VER_GREATER_EQUAL),\n               VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL);\n\n    osvi.dwMajorVersion = wMajorVersion;\n    osvi.dwMinorVersion = wMinorVersion;\n    osvi.wServicePackMajor = wServicePackMajor;\n\n    return VerifyVersionInfoW(&osvi, VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR, dwlConditionMask) != FALSE;\n}\n\nVERSIONHELPERAPI\nIsWindowsXPOrGreater()\n{\n    return IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WINXP), LOBYTE(_WIN32_WINNT_WINXP), 0);\n}\n\nVERSIONHELPERAPI\nIsWindowsXPSP1OrGreater()\n{\n    return IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WINXP), LOBYTE(_WIN32_WINNT_WINXP), 1);\n}\n\nVERSIONHELPERAPI\nIsWindowsXPSP2OrGreater()\n{\n    return IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WINXP), LOBYTE(_WIN32_WINNT_WINXP), 2);\n}\n\nVERSIONHELPERAPI\nIsWindowsXPSP3OrGreater()\n{\n    return IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WINXP), LOBYTE(_WIN32_WINNT_WINXP), 3);\n}\n\nVERSIONHELPERAPI\nIsWindowsVistaOrGreater()\n{\n    return IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_VISTA), LOBYTE(_WIN32_WINNT_VISTA), 0);\n}\n\nVERSIONHELPERAPI\nIsWindowsVistaSP1OrGreater()\n{\n    return IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_VISTA), LOBYTE(_WIN32_WINNT_VISTA), 1);\n}\n\nVERSIONHELPERAPI\nIsWindowsVistaSP2OrGreater()\n{\n    return IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_VISTA), LOBYTE(_WIN32_WINNT_VISTA), 2);\n}\n\nVERSIONHELPERAPI\nIsWindows7OrGreater()\n{\n    return IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WIN7), LOBYTE(_WIN32_WINNT_WIN7), 0);\n}\n\nVERSIONHELPERAPI\nIsWindows7SP1OrGreater()\n{\n    return IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WIN7), LOBYTE(_WIN32_WINNT_WIN7), 1);\n}\n\nVERSIONHELPERAPI\nIsWindows8OrGreater()\n{\n    return IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WIN8), LOBYTE(_WIN32_WINNT_WIN8), 0);\n}\n\nVERSIONHELPERAPI\nIsWindows8Point1OrGreater()\n{\n    return IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WINBLUE), LOBYTE(_WIN32_WINNT_WINBLUE), 0);\n}\n\nVERSIONHELPERAPI\nIsWindowsServer()\n{\n    OSVERSIONINFOEXW osvi = { sizeof(osvi), 0, 0, 0, 0, {0}, 0, 0, 0, VER_NT_WORKSTATION };\n    DWORDLONG        const dwlConditionMask = VerSetConditionMask( 0, VER_PRODUCT_TYPE, VER_EQUAL );\n\n    return !VerifyVersionInfoW(&osvi, VER_PRODUCT_TYPE, dwlConditionMask);\n}\n\n#endif // NTDDI_VERSION\n\n#endif // defined(__midl)\n\n#endif /* WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) */\n#pragma endregion\n\n#endif // _VERSIONHELPERS_H_INCLUDED_\n"
  },
  {
    "path": "libs/krabs/krabs/wstring_convert.hpp",
    "content": "// Copyright (c) Microsoft. All rights reserved.\n// Licensed under the MIT license. See LICENSE file in the project root for full license information.\n\n#include <windows.h>\n\nnamespace krabs {\n\n    /** <summary>\n      * Converts std::wstring argument to std::string using UTF-8 codepage\n      * Returns empty string if translation fails or input string is empty\n      * </summary>\n      */\n    inline std::string from_wstring(const std::wstring& wstr, UINT codePage = CP_UTF8)\n    {\n        if (wstr.empty())\n            return {};\n\n        const auto requiredLen = WideCharToMultiByte(codePage, 0, wstr.data(), static_cast<int>(wstr.size()), \n            nullptr, 0, nullptr, nullptr);\n        if (0 == requiredLen)\n            return {};\n\n        std::string result(requiredLen, 0);\n        const auto convertedLen = WideCharToMultiByte(codePage, 0, wstr.data(), static_cast<int>(wstr.size()),\n            &result[0], requiredLen, nullptr, nullptr);\n        if (0 == convertedLen)\n            return {};\n\n        return result;\n    }\n}\n"
  },
  {
    "path": "libs/krabs/krabs.hpp",
    "content": "// Copyright (c) Microsoft. All rights reserved.\n// Licensed under the MIT license. See LICENSE file in the project root for full license information.\n\n#pragma once\n\n#pragma comment(lib, \"advapi32.lib\")\n#pragma comment(lib, \"ole32.lib\")\n\n\n//\n//                              /\\\n//                             ( /   @ @    ()\n//                              \\  __| |__  /\n//                               -/   \"   \\-\n//                              /-|       |-\\\n//                             / /-\\     /-\\ \\\n//                              / /-`---'-\\ \\\n//                               /         \\\n//\n// Summary\n// ----------------------------------------------------------------------------\n// Krabs is a wrapper around ETW because ETW is the worst API ever made.\n\n#pragma warning(push)\n#pragma warning(disable: 4512) // stupid spurious \"can't generate assignment error\" warning\n#pragma warning(disable: 4634) // DocXml comment warnings in native C++\n#pragma warning(disable: 4635) // DocXml comment warnings in native C++\n\n#include \"krabs/compiler_check.hpp\"\n#include \"krabs/ut.hpp\"\n#include \"krabs/kt.hpp\"\n#include \"krabs/guid.hpp\"\n#include \"krabs/trace.hpp\"\n#include \"krabs/trace_context.hpp\"\n#include \"krabs/client.hpp\"\n#include \"krabs/errors.hpp\"\n#include \"krabs/schema.hpp\"\n#include \"krabs/schema_locator.hpp\"\n#include \"krabs/parse_types.hpp\"\n#include \"krabs/collection_view.hpp\"\n#include \"krabs/size_provider.hpp\"\n#include \"krabs/parser.hpp\"\n#include \"krabs/property.hpp\"\n#include \"krabs/provider.hpp\"\n#include \"krabs/etw.hpp\"\n#include \"krabs/tdh_helpers.hpp\"\n#include \"krabs/kernel_providers.hpp\"\n\n#include \"krabs/testing/proxy.hpp\"\n#include \"krabs/testing/filler.hpp\"\n#include \"krabs/testing/synth_record.hpp\"\n#include \"krabs/testing/record_builder.hpp\"\n#include \"krabs/testing/event_filter_proxy.hpp\"\n#include \"krabs/testing/record_property_thunk.hpp\"\n\n#include \"krabs/filtering/view_adapters.hpp\"\n#include \"krabs/filtering/comparers.hpp\"\n#include \"krabs/filtering/predicates.hpp\"\n#include \"krabs/filtering/event_filter.hpp\"\n\n#pragma warning(pop)\n"
  },
  {
    "path": "libs/krabs/krabs.runsettings",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RunSettings>\n  <!-- Configurations that affect the Test Framework -->\n  <RunConfiguration>\n    <DeploymentEnabled>False</DeploymentEnabled>\n    <ExecutionThreadApartmentState>MTA</ExecutionThreadApartmentState>\n    <TargetPlatform>x64</TargetPlatform>\n  </RunConfiguration>\n</RunSettings>"
  },
  {
    "path": "libs/krabs/krabs.sln",
    "content": "﻿\nMicrosoft Visual Studio Solution File, Format Version 12.00\n# Visual Studio Version 17\nVisualStudioVersion = 17.2.32317.152\nMinimumVisualStudioVersion = 10.0.40219.1\nProject(\"{2150E333-8FDC-42A3-9474-1A3956D46DE8}\") = \".nuget\", \".nuget\", \"{5C9E224B-AEB7-456B-B309-92292ACE1A0D}\"\n\tProjectSection(SolutionItems) = preProject\n\t\t.nuget\\NuGet.Config = .nuget\\NuGet.Config\n\t\t.nuget\\NuGet.exe = .nuget\\NuGet.exe\n\t\t.nuget\\NuGet.targets = .nuget\\NuGet.targets\n\tEndProjectSection\nEndProject\nProject(\"{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}\") = \"Microsoft.O365.Security.Native.ETW\", \"..\\Microsoft.O365.Security.Native.ETW\\Microsoft.O365.Security.Native.ETW.vcxproj\", \"{ED4E6027-541F-440A-A5EE-15DBB7B89423}\"\nEndProject\nProject(\"{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}\") = \"krabstests\", \"..\\tests\\krabstests\\krabstests.vcxproj\", \"{880977B8-15CA-421B-BF48-D01626A530A2}\"\nEndProject\nProject(\"{2150E333-8FDC-42A3-9474-1A3956D46DE8}\") = \"Solution Items\", \"Solution Items\", \"{E583602B-48AC-46A6-B0F0-343CE0B6AE33}\"\n\tProjectSection(SolutionItems) = preProject\n\t\tkrabs.runsettings = krabs.runsettings\n\t\tMTA.testsettings = MTA.testsettings\n\tEndProjectSection\nEndProject\nProject(\"{2150E333-8FDC-42A3-9474-1A3956D46DE8}\") = \"krabs headers\", \"krabs headers\", \"{1FD19105-D67C-492B-B98F-53E00A324269}\"\n\tProjectSection(SolutionItems) = preProject\n\t\tkrabs.hpp = krabs.hpp\n\tEndProjectSection\nEndProject\nProject(\"{2150E333-8FDC-42A3-9474-1A3956D46DE8}\") = \"krabs\", \"krabs\", \"{371361C8-96EC-4D6D-B80B-2E47E3453264}\"\n\tProjectSection(SolutionItems) = preProject\n\t\tkrabs\\client.hpp = krabs\\client.hpp\n\t\tkrabs\\collection_view.hpp = krabs\\collection_view.hpp\n\t\tkrabs\\compiler_check.hpp = krabs\\compiler_check.hpp\n\t\tkrabs\\errors.hpp = krabs\\errors.hpp\n\t\tkrabs\\etw.hpp = krabs\\etw.hpp\n\t\tkrabs\\guid.hpp = krabs\\guid.hpp\n\t\tkrabs\\kernel_guids.hpp = krabs\\kernel_guids.hpp\n\t\tkrabs\\kernel_providers.hpp = krabs\\kernel_providers.hpp\n\t\tkrabs\\kt.hpp = krabs\\kt.hpp\n\t\tkrabs\\parser.hpp = krabs\\parser.hpp\n\t\tkrabs\\parse_types.hpp = krabs\\parse_types.hpp\n\t\tkrabs\\perfinfo_groupmask.hpp = krabs\\perfinfo_groupmask.hpp\n\t\tkrabs\\property.hpp = krabs\\property.hpp\n\t\tkrabs\\provider.hpp = krabs\\provider.hpp\n\t\tkrabs\\schema.hpp = krabs\\schema.hpp\n\t\tkrabs\\schema_locator.hpp = krabs\\schema_locator.hpp\n\t\tkrabs\\size_provider.hpp = krabs\\size_provider.hpp\n\t\tkrabs\\tdh_helpers.hpp = krabs\\tdh_helpers.hpp\n\t\tkrabs\\trace.hpp = krabs\\trace.hpp\n\t\tkrabs\\trace_context.hpp = krabs\\trace_context.hpp\n\t\tkrabs\\ut.hpp = krabs\\ut.hpp\n\t\tkrabs\\version_helpers.hpp = krabs\\version_helpers.hpp\n\t\tkrabs\\wstring_convert.hpp = krabs\\wstring_convert.hpp\n\tEndProjectSection\nEndProject\nProject(\"{2150E333-8FDC-42A3-9474-1A3956D46DE8}\") = \"filtering\", \"filtering\", \"{96FA58B5-A1F6-4107-9FB4-226290F9D696}\"\n\tProjectSection(SolutionItems) = preProject\n\t\tkrabs\\filtering\\comparers.hpp = krabs\\filtering\\comparers.hpp\n\t\tkrabs\\filtering\\event_filter.hpp = krabs\\filtering\\event_filter.hpp\n\t\tkrabs\\filtering\\predicates.hpp = krabs\\filtering\\predicates.hpp\n\t\tkrabs\\filtering\\view_adapters.hpp = krabs\\filtering\\view_adapters.hpp\n\tEndProjectSection\nEndProject\nProject(\"{2150E333-8FDC-42A3-9474-1A3956D46DE8}\") = \"testing\", \"testing\", \"{9ED1AE76-2EAA-4CCF-8F01-458BDC8BCD53}\"\n\tProjectSection(SolutionItems) = preProject\n\t\tkrabs\\testing\\event_filter_proxy.hpp = krabs\\testing\\event_filter_proxy.hpp\n\t\tkrabs\\testing\\extended_data_builder.hpp = krabs\\testing\\extended_data_builder.hpp\n\t\tkrabs\\testing\\filler.hpp = krabs\\testing\\filler.hpp\n\t\tkrabs\\testing\\proxy.hpp = krabs\\testing\\proxy.hpp\n\t\tkrabs\\testing\\record_builder.hpp = krabs\\testing\\record_builder.hpp\n\t\tkrabs\\testing\\record_property_thunk.hpp = krabs\\testing\\record_property_thunk.hpp\n\t\tkrabs\\testing\\synth_record.hpp = krabs\\testing\\synth_record.hpp\n\tEndProjectSection\nEndProject\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"EtwTestsCS\", \"..\\tests\\ManagedETWTests\\EtwTestsCS.csproj\", \"{600CFE03-FD84-4323-9439-839D81C31972}\"\nEndProject\nProject(\"{2150E333-8FDC-42A3-9474-1A3956D46DE8}\") = \"Tests\", \"Tests\", \"{C4AB7F5F-2FB3-4C16-A1F3-F6700C655B02}\"\nEndProject\nProject(\"{2150E333-8FDC-42A3-9474-1A3956D46DE8}\") = \"Examples\", \"Examples\", \"{2E00634C-7E8B-4656-9505-78FF2F5D0EDD}\"\nEndProject\nProject(\"{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}\") = \"ManagedExamples\", \"..\\examples\\ManagedExamples\\ManagedExamples.csproj\", \"{32E71DD0-D11A-44DE-8CA8-572995AF2373}\"\nEndProject\nProject(\"{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}\") = \"NativeExamples\", \"..\\examples\\NativeExamples\\NativeExamples.vcxproj\", \"{D31B1A4B-8282-4AED-99FC-9AA5974B9134}\"\n\tProjectSection(ProjectDependencies) = postProject\n\t\t{ED4E6027-541F-440A-A5EE-15DBB7B89423} = {ED4E6027-541F-440A-A5EE-15DBB7B89423}\n\tEndProjectSection\nEndProject\nProject(\"{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}\") = \"Microsoft.O365.Security.Native.ETW.NetCore\", \"..\\Microsoft.O365.Security.Native.ETW.NetCore\\Microsoft.O365.Security.Native.ETW.NetCore.vcxproj\", \"{9DE6788C-5759-4A75-B484-ABA4C7EF5F08}\"\nEndProject\nGlobal\n\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\n\t\tDebug|x64 = Debug|x64\n\t\tDebugSigning|x64 = DebugSigning|x64\n\t\tRelease|x64 = Release|x64\n\t\tReleaseSigning|x64 = ReleaseSigning|x64\n\tEndGlobalSection\n\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\n\t\t{ED4E6027-541F-440A-A5EE-15DBB7B89423}.Debug|x64.ActiveCfg = Debug|x64\n\t\t{ED4E6027-541F-440A-A5EE-15DBB7B89423}.Debug|x64.Build.0 = Debug|x64\n\t\t{ED4E6027-541F-440A-A5EE-15DBB7B89423}.DebugSigning|x64.ActiveCfg = DebugSigning|x64\n\t\t{ED4E6027-541F-440A-A5EE-15DBB7B89423}.DebugSigning|x64.Build.0 = DebugSigning|x64\n\t\t{ED4E6027-541F-440A-A5EE-15DBB7B89423}.Release|x64.ActiveCfg = Release|x64\n\t\t{ED4E6027-541F-440A-A5EE-15DBB7B89423}.Release|x64.Build.0 = Release|x64\n\t\t{ED4E6027-541F-440A-A5EE-15DBB7B89423}.ReleaseSigning|x64.ActiveCfg = ReleaseSigning|x64\n\t\t{ED4E6027-541F-440A-A5EE-15DBB7B89423}.ReleaseSigning|x64.Build.0 = ReleaseSigning|x64\n\t\t{880977B8-15CA-421B-BF48-D01626A530A2}.Debug|x64.ActiveCfg = Debug|x64\n\t\t{880977B8-15CA-421B-BF48-D01626A530A2}.Debug|x64.Build.0 = Debug|x64\n\t\t{880977B8-15CA-421B-BF48-D01626A530A2}.DebugSigning|x64.ActiveCfg = DebugSigning|x64\n\t\t{880977B8-15CA-421B-BF48-D01626A530A2}.DebugSigning|x64.Build.0 = DebugSigning|x64\n\t\t{880977B8-15CA-421B-BF48-D01626A530A2}.Release|x64.ActiveCfg = Release|x64\n\t\t{880977B8-15CA-421B-BF48-D01626A530A2}.Release|x64.Build.0 = Release|x64\n\t\t{880977B8-15CA-421B-BF48-D01626A530A2}.ReleaseSigning|x64.ActiveCfg = ReleaseSigning|x64\n\t\t{880977B8-15CA-421B-BF48-D01626A530A2}.ReleaseSigning|x64.Build.0 = ReleaseSigning|x64\n\t\t{600CFE03-FD84-4323-9439-839D81C31972}.Debug|x64.ActiveCfg = Debug|Any CPU\n\t\t{600CFE03-FD84-4323-9439-839D81C31972}.Debug|x64.Build.0 = Debug|Any CPU\n\t\t{600CFE03-FD84-4323-9439-839D81C31972}.DebugSigning|x64.ActiveCfg = Debug|Any CPU\n\t\t{600CFE03-FD84-4323-9439-839D81C31972}.DebugSigning|x64.Build.0 = Debug|Any CPU\n\t\t{600CFE03-FD84-4323-9439-839D81C31972}.Release|x64.ActiveCfg = Release|Any CPU\n\t\t{600CFE03-FD84-4323-9439-839D81C31972}.Release|x64.Build.0 = Release|Any CPU\n\t\t{600CFE03-FD84-4323-9439-839D81C31972}.ReleaseSigning|x64.ActiveCfg = Release|Any CPU\n\t\t{600CFE03-FD84-4323-9439-839D81C31972}.ReleaseSigning|x64.Build.0 = Release|Any CPU\n\t\t{32E71DD0-D11A-44DE-8CA8-572995AF2373}.Debug|x64.ActiveCfg = Debug|Any CPU\n\t\t{32E71DD0-D11A-44DE-8CA8-572995AF2373}.Debug|x64.Build.0 = Debug|Any CPU\n\t\t{32E71DD0-D11A-44DE-8CA8-572995AF2373}.DebugSigning|x64.ActiveCfg = DebugSigning|Any CPU\n\t\t{32E71DD0-D11A-44DE-8CA8-572995AF2373}.DebugSigning|x64.Build.0 = DebugSigning|Any CPU\n\t\t{32E71DD0-D11A-44DE-8CA8-572995AF2373}.Release|x64.ActiveCfg = Release|Any CPU\n\t\t{32E71DD0-D11A-44DE-8CA8-572995AF2373}.Release|x64.Build.0 = Release|Any CPU\n\t\t{32E71DD0-D11A-44DE-8CA8-572995AF2373}.ReleaseSigning|x64.ActiveCfg = ReleaseSigning|Any CPU\n\t\t{32E71DD0-D11A-44DE-8CA8-572995AF2373}.ReleaseSigning|x64.Build.0 = ReleaseSigning|Any CPU\n\t\t{D31B1A4B-8282-4AED-99FC-9AA5974B9134}.Debug|x64.ActiveCfg = Debug|x64\n\t\t{D31B1A4B-8282-4AED-99FC-9AA5974B9134}.Debug|x64.Build.0 = Debug|x64\n\t\t{D31B1A4B-8282-4AED-99FC-9AA5974B9134}.DebugSigning|x64.ActiveCfg = DebugSigning|x64\n\t\t{D31B1A4B-8282-4AED-99FC-9AA5974B9134}.DebugSigning|x64.Build.0 = DebugSigning|x64\n\t\t{D31B1A4B-8282-4AED-99FC-9AA5974B9134}.Release|x64.ActiveCfg = Release|x64\n\t\t{D31B1A4B-8282-4AED-99FC-9AA5974B9134}.Release|x64.Build.0 = Release|x64\n\t\t{D31B1A4B-8282-4AED-99FC-9AA5974B9134}.ReleaseSigning|x64.ActiveCfg = ReleaseSigning|x64\n\t\t{D31B1A4B-8282-4AED-99FC-9AA5974B9134}.ReleaseSigning|x64.Build.0 = ReleaseSigning|x64\n\t\t{9DE6788C-5759-4A75-B484-ABA4C7EF5F08}.Debug|x64.ActiveCfg = Debug|x64\n\t\t{9DE6788C-5759-4A75-B484-ABA4C7EF5F08}.Debug|x64.Build.0 = Debug|x64\n\t\t{9DE6788C-5759-4A75-B484-ABA4C7EF5F08}.DebugSigning|x64.ActiveCfg = DebugSigning|x64\n\t\t{9DE6788C-5759-4A75-B484-ABA4C7EF5F08}.DebugSigning|x64.Build.0 = DebugSigning|x64\n\t\t{9DE6788C-5759-4A75-B484-ABA4C7EF5F08}.Release|x64.ActiveCfg = Release|x64\n\t\t{9DE6788C-5759-4A75-B484-ABA4C7EF5F08}.Release|x64.Build.0 = Release|x64\n\t\t{9DE6788C-5759-4A75-B484-ABA4C7EF5F08}.ReleaseSigning|x64.ActiveCfg = ReleaseSigning|x64\n\t\t{9DE6788C-5759-4A75-B484-ABA4C7EF5F08}.ReleaseSigning|x64.Build.0 = ReleaseSigning|x64\n\tEndGlobalSection\n\tGlobalSection(SolutionProperties) = preSolution\n\t\tHideSolutionNode = FALSE\n\tEndGlobalSection\n\tGlobalSection(NestedProjects) = preSolution\n\t\t{880977B8-15CA-421B-BF48-D01626A530A2} = {C4AB7F5F-2FB3-4C16-A1F3-F6700C655B02}\n\t\t{371361C8-96EC-4D6D-B80B-2E47E3453264} = {1FD19105-D67C-492B-B98F-53E00A324269}\n\t\t{96FA58B5-A1F6-4107-9FB4-226290F9D696} = {371361C8-96EC-4D6D-B80B-2E47E3453264}\n\t\t{9ED1AE76-2EAA-4CCF-8F01-458BDC8BCD53} = {371361C8-96EC-4D6D-B80B-2E47E3453264}\n\t\t{600CFE03-FD84-4323-9439-839D81C31972} = {C4AB7F5F-2FB3-4C16-A1F3-F6700C655B02}\n\t\t{32E71DD0-D11A-44DE-8CA8-572995AF2373} = {2E00634C-7E8B-4656-9505-78FF2F5D0EDD}\n\t\t{D31B1A4B-8282-4AED-99FC-9AA5974B9134} = {2E00634C-7E8B-4656-9505-78FF2F5D0EDD}\n\tEndGlobalSection\n\tGlobalSection(ExtensibilityGlobals) = postSolution\n\t\tSolutionGuid = {82BAA012-2EF9-4303-A429-CDA3655D5009}\n\tEndGlobalSection\nEndGlobal\n"
  }
]