├── Hunt-Weird-Imageloads ├── Detectors.cpp ├── Detectors.h ├── Helpers.cpp ├── Helpers.h ├── Hunt-Weird-Imageloads.sln ├── Hunt-Weird-Imageloads.vcxproj ├── Hunt-Weird-Imageloads.vcxproj.filters ├── Hunt-Weird-Imageloads.vcxproj.user ├── Main.cpp ├── NewThreadLoadLibrary │ ├── Main.c │ ├── NewThreadLoadLibrary.vcxproj │ ├── NewThreadLoadLibrary.vcxproj.filters │ └── NewThreadLoadLibrary.vcxproj.user └── WorkItemLoadLibrary │ ├── Main.c │ ├── WorkItemLoadLibrary.vcxproj │ ├── WorkItemLoadLibrary.vcxproj.filters │ └── WorkItemLoadLibrary.vcxproj.user ├── Readme.md ├── libs └── krabs │ ├── LICENSE │ ├── README.md │ ├── krabs.hpp │ ├── krabs.runsettings │ ├── krabs.sln │ └── krabs │ ├── client.hpp │ ├── collection_view.hpp │ ├── compiler_check.hpp │ ├── errors.hpp │ ├── etw.hpp │ ├── filtering │ ├── comparers.hpp │ ├── event_filter.hpp │ ├── predicates.hpp │ └── view_adapters.hpp │ ├── guid.hpp │ ├── kernel_guids.hpp │ ├── kernel_providers.hpp │ ├── kt.hpp │ ├── parse_types.hpp │ ├── parser.hpp │ ├── perfinfo_groupmask.hpp │ ├── property.hpp │ ├── provider.hpp │ ├── schema.hpp │ ├── schema_locator.hpp │ ├── size_provider.hpp │ ├── tdh_helpers.hpp │ ├── testing │ ├── event_filter_proxy.hpp │ ├── extended_data_builder.hpp │ ├── filler.hpp │ ├── proxy.hpp │ ├── record_builder.hpp │ ├── record_property_thunk.hpp │ └── synth_record.hpp │ ├── trace.hpp │ ├── trace_context.hpp │ ├── ut.hpp │ ├── version_helpers.hpp │ └── wstring_convert.hpp └── screens └── 1.png /Hunt-Weird-Imageloads/Detectors.cpp: -------------------------------------------------------------------------------- 1 | #include "Detectors.h" 2 | #include 3 | 4 | namespace Detectors { 5 | 6 | VOID Detectors::PrivateRX::Check ( HANDLE hProcess, std::vector stack, DWORD dwPid, std::wstring imageName) { 7 | 8 | SIZE_T s = 0; 9 | MEMORY_BASIC_INFORMATION mbi = { 0 }; 10 | std::string processName; 11 | 12 | for ( ULONG_PTR pAddr : stack ) { 13 | 14 | s = VirtualQueryEx ( hProcess, ( LPCVOID ) pAddr, &mbi, sizeof ( MEMORY_BASIC_INFORMATION ) ); 15 | if ( s == 0 ) 16 | continue; 17 | 18 | if ( mbi.Type != MEM_PRIVATE ) 19 | continue; 20 | 21 | if ( mbi.Protect == PAGE_EXECUTE_READ ) { 22 | 23 | Helpers::ModuleNameFromAddress ( hProcess, NULL, processName ); 24 | 25 | wprintf ( L"! Process %S ( %d ) loaded %s and callstack contains RX Page: 0x%p\n", processName.c_str ( ), dwPid, imageName.c_str ( ), ( PVOID ) pAddr ); 26 | break; 27 | 28 | } 29 | 30 | } 31 | 32 | } 33 | 34 | VOID Detectors::PrivateRWX::Check ( HANDLE hProcess, std::vector stack, DWORD dwPid, std::wstring imageName ) { 35 | 36 | SIZE_T s = 0; 37 | MEMORY_BASIC_INFORMATION mbi = { 0 }; 38 | std::string processName; 39 | 40 | for ( ULONG_PTR pAddr : stack ) { 41 | 42 | s = VirtualQueryEx ( hProcess, ( LPCVOID ) pAddr, &mbi, sizeof ( MEMORY_BASIC_INFORMATION ) ); 43 | if ( s == 0 ) 44 | continue; 45 | 46 | if ( mbi.Type != MEM_PRIVATE ) 47 | continue; 48 | 49 | if ( mbi.Protect == PAGE_EXECUTE_READWRITE ) { 50 | 51 | Helpers::ModuleNameFromAddress ( hProcess, NULL, processName ); 52 | 53 | wprintf ( L"! Process %S ( %d ) loaded %s and callstack contains RWX Page: 0x%p\n", processName.c_str ( ), dwPid, imageName.c_str ( ), ( PVOID ) pAddr ); 54 | break; 55 | 56 | } 57 | 58 | } 59 | 60 | } 61 | 62 | VOID Detectors::ModuleStomped::Check ( HANDLE hProcess, std::vector stack, DWORD dwPid, std::wstring imageName ) { 63 | 64 | std::string stompedModule; 65 | std::string processName; 66 | 67 | for ( ULONG_PTR pAddr : stack ) { 68 | 69 | if ( Helpers::IsModuleStomped ( hProcess, ( PVOID ) pAddr ) ) { 70 | 71 | Helpers::ModuleNameFromAddress ( hProcess, ( PVOID ) pAddr, stompedModule ); 72 | Helpers::ModuleNameFromAddress ( hProcess, NULL, processName ); 73 | 74 | wprintf ( L"! Process %S ( %d ) loaded %s and callstack contains stomped module: %S\n", processName.c_str ( ), dwPid, imageName.c_str ( ), stompedModule.c_str ( ) ); 75 | break; 76 | 77 | } 78 | } 79 | 80 | } 81 | 82 | // Was ist mit ZwMapViewOfSection ? 83 | VOID Detectors::ModuleProxying::Check ( HANDLE hProcess, std::vector stack, DWORD dwPid, std::wstring imageName ) { 84 | 85 | BOOL bSuccess = FALSE; 86 | 87 | std::string callingmodule; 88 | std::string processName; 89 | std::string symbol; 90 | 91 | if ( stack.size ( ) < 2 ) 92 | return; 93 | 94 | for ( auto it = stack.begin ( ); it != stack.end ( ); ++it ) { 95 | 96 | bSuccess = Helpers::SymbolNameFromAddress ( hProcess, ( PVOID ) *it, symbol ); 97 | if ( bSuccess == FALSE ) 98 | break; 99 | 100 | if ( symbol.find ( "LoadLibrary" ) != std::string::npos ) { 101 | 102 | bSuccess = Helpers::ModuleNameFromAddress ( hProcess, ( PVOID ) * ( it + 1 ), callingmodule ); 103 | if ( bSuccess == FALSE ) 104 | break; 105 | 106 | if ( !_stricmp ( callingmodule.c_str ( ), "ntdll.dll" ) ){ 107 | 108 | Helpers::ModuleNameFromAddress ( hProcess, NULL, processName ); 109 | wprintf ( L"! Process %S ( %d ) loaded %s by proxying loadlibray through ntdll\n", processName.c_str ( ), dwPid, imageName.c_str ( ) ); 110 | 111 | } 112 | 113 | } 114 | 115 | } 116 | 117 | } 118 | 119 | VOID Detectors::DedicatedThread::Check ( HANDLE hProcess, std::vector stack, DWORD dwPid, PVOID baseAddr) { 120 | 121 | BOOL bSuccess = FALSE; 122 | PVOID pLoadLibraryA = NULL, pLoadLibraryW = NULL, pLoadLibraryExA = NULL, pLoadLibraryExW = NULL; 123 | 124 | std::string baseModule; 125 | std::string processName; 126 | 127 | bSuccess = Helpers::ModuleNameFromAddress ( hProcess, ( PVOID ) baseAddr, baseModule ); 128 | if ( bSuccess == FALSE ) 129 | return; 130 | 131 | pLoadLibraryA = GetProcAddress ( GetModuleHandleA ( "kernel32.dll" ), "LoadLibraryA" ); 132 | pLoadLibraryW = GetProcAddress ( GetModuleHandleA ( "kernel32.dll" ), "LoadLibraryW" ); 133 | pLoadLibraryExA = GetProcAddress ( GetModuleHandleA ( "kernel32.dll" ), "LoadLibraryExA" ); 134 | pLoadLibraryExW = GetProcAddress ( GetModuleHandleA ( "kernel32.dll" ), "LoadLibraryExW" ); 135 | 136 | if ( !_stricmp ( baseModule.c_str ( ), "kernel32.dll" ) || !_stricmp ( baseModule.c_str ( ), "kernelbase.dll" ) ) { 137 | 138 | Helpers::ModuleNameFromAddress ( hProcess, NULL, processName ); 139 | 140 | wprintf ( L"! Process %S ( %d ) abnormally started a new thread in %S\n", processName.c_str ( ), dwPid, baseModule.c_str ( ) ); 141 | 142 | if ( baseAddr == pLoadLibraryA || baseAddr == pLoadLibraryW || baseAddr == pLoadLibraryExA || baseAddr == pLoadLibraryExW ) 143 | wprintf ( L"\t !! Thread is abnormally started in a LoadLibrary function :o\n" ); 144 | 145 | } 146 | 147 | } 148 | 149 | 150 | } 151 | -------------------------------------------------------------------------------- /Hunt-Weird-Imageloads/Detectors.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "windows.h" 4 | #include 5 | 6 | #include "Helpers.h" 7 | 8 | namespace Detectors { 9 | 10 | class DetectorImageLoad { 11 | public: 12 | virtual VOID Check ( HANDLE, std::vector, DWORD, std::wstring ) = 0; 13 | }; 14 | 15 | class DetectorThreadStart { 16 | public: 17 | virtual VOID Check ( HANDLE, std::vector, DWORD, PVOID ) = 0; 18 | }; 19 | 20 | class PrivateRX : public DetectorImageLoad { public: VOID Check ( HANDLE, std::vector, DWORD, std::wstring ); }; 21 | class PrivateRWX : public DetectorImageLoad { public: VOID Check ( HANDLE, std::vector, DWORD, std::wstring ); }; 22 | class ModuleStomped : public DetectorImageLoad { public: VOID Check ( HANDLE, std::vector, DWORD, std::wstring ); }; 23 | class ModuleProxying : public DetectorImageLoad { public: VOID Check ( HANDLE, std::vector, DWORD, std::wstring ); }; 24 | 25 | class DedicatedThread : public DetectorThreadStart { public: VOID Check ( HANDLE, std::vector, DWORD, PVOID); }; 26 | 27 | } -------------------------------------------------------------------------------- /Hunt-Weird-Imageloads/Helpers.cpp: -------------------------------------------------------------------------------- 1 | #include "Helpers.h" 2 | #include "Dbghelp.h" 3 | 4 | namespace Helpers { 5 | 6 | BOOL IsModuleStomped ( HANDLE hProcess, PVOID pAddr ) { 7 | 8 | SIZE_T s = 0; 9 | MEMORY_BASIC_INFORMATION mbi = { 0 }; 10 | PSAPI_WORKING_SET_EX_INFORMATION workingSets = { 0 }; 11 | BOOL bSuccess = FALSE; 12 | 13 | s = VirtualQueryEx ( hProcess, ( LPCVOID ) pAddr, &mbi, sizeof ( MEMORY_BASIC_INFORMATION ) ); 14 | if ( s == 0 ) 15 | goto Cleanup; 16 | 17 | if ( mbi.Type != MEM_IMAGE || mbi.AllocationProtect == PAGE_NOACCESS ) 18 | goto Cleanup; 19 | 20 | workingSets.VirtualAddress = mbi.BaseAddress; 21 | 22 | bSuccess = K32QueryWorkingSetEx ( hProcess, &workingSets, sizeof ( PSAPI_WORKING_SET_EX_INFORMATION ) ); 23 | if ( bSuccess == FALSE ) 24 | goto Cleanup; 25 | 26 | bSuccess = FALSE; 27 | 28 | if ( workingSets.VirtualAttributes.Shared != 0 ) 29 | goto Cleanup; 30 | 31 | bSuccess = TRUE; 32 | 33 | Cleanup: 34 | 35 | return bSuccess; 36 | 37 | } 38 | 39 | BOOL SymbolNameFromAddress ( HANDLE hProcess, PVOID pAddr, std::string& symbol ) { 40 | 41 | BOOL bSuccess = FALSE; 42 | DWORD64 dw64Displacement = 0; 43 | CHAR cSymName [ 256 ] = { 0 }; 44 | 45 | PIMAGEHLP_SYMBOL64 pSymbol = NULL; 46 | 47 | pSymbol = ( PIMAGEHLP_SYMBOL64 ) HeapAlloc ( GetProcessHeap ( ), HEAP_ZERO_MEMORY, sizeof ( IMAGEHLP_SYMBOL64 ) + 256 * sizeof ( WCHAR ) ); 48 | if ( pSymbol == NULL ) 49 | goto Cleanup; 50 | 51 | pSymbol->SizeOfStruct = sizeof ( IMAGEHLP_SYMBOL64 ); 52 | pSymbol->MaxNameLength = 255; 53 | 54 | bSuccess = SymGetSymFromAddr64 ( hProcess, ( ULONG64 ) pAddr, &dw64Displacement, pSymbol ); 55 | if ( bSuccess == FALSE ) 56 | goto Cleanup; 57 | 58 | UnDecorateSymbolName ( pSymbol->Name, cSymName, 256, UNDNAME_COMPLETE ); 59 | symbol = std::string ( cSymName ); 60 | 61 | Cleanup: 62 | 63 | if ( pSymbol ) 64 | HeapFree ( GetProcessHeap ( ), 0, pSymbol ); 65 | 66 | return bSuccess; 67 | 68 | } 69 | 70 | BOOL ModuleNameFromAddress ( HANDLE hProcess, PVOID pAddr, std::string& moduleName ) { 71 | 72 | BOOL bSuccess = FALSE; 73 | SIZE_T s = 0; 74 | MEMORY_BASIC_INFORMATION mbi = { 0 }; 75 | CHAR cmoduleName [ MAX_PATH ] = { 0 }; 76 | 77 | moduleName.clear ( ); 78 | 79 | s = VirtualQueryEx ( hProcess, ( LPCVOID ) pAddr, &mbi, sizeof ( MEMORY_BASIC_INFORMATION ) ); 80 | if ( s == 0 ) 81 | goto Cleanup; 82 | 83 | bSuccess = K32GetModuleBaseNameA ( hProcess, ( HMODULE ) mbi.AllocationBase, ( LPSTR ) cmoduleName, MAX_PATH ); 84 | if ( bSuccess == FALSE ) 85 | goto Cleanup; 86 | 87 | moduleName = std::string ( cmoduleName ); 88 | 89 | bSuccess = TRUE; 90 | 91 | Cleanup: 92 | 93 | return bSuccess; 94 | 95 | } 96 | 97 | VOID RemoveKernelAddrs ( std::vector& stack ) { 98 | 99 | auto it = stack.begin ( ); 100 | while ( it != stack.end ( ) ) { 101 | 102 | ULONG_PTR addr = *it; 103 | if ( addr > 0xFFFF000000000000 ) { 104 | it = stack.erase ( it ); 105 | } 106 | else { 107 | ++it; 108 | } 109 | 110 | } 111 | 112 | } 113 | 114 | //https://github.com/outflanknl/Dumpert/blob/master/Dumpert/Outflank-Dumpert/Dumpert.c Is Elevated() was taken from here :). 115 | BOOL IsElevated ( VOID ) { 116 | BOOL fRet = FALSE; 117 | HANDLE hToken = NULL; 118 | if ( OpenProcessToken ( GetCurrentProcess ( ), TOKEN_QUERY, &hToken ) ) { 119 | TOKEN_ELEVATION Elevation = { 0 }; 120 | DWORD cbSize = sizeof ( TOKEN_ELEVATION ); 121 | if ( GetTokenInformation ( hToken, TokenElevation, &Elevation, sizeof ( Elevation ), &cbSize ) ) { 122 | fRet = Elevation.TokenIsElevated; 123 | } 124 | } 125 | if ( hToken ) { 126 | CloseHandle ( hToken ); 127 | } 128 | return fRet; 129 | } 130 | 131 | 132 | } -------------------------------------------------------------------------------- /Hunt-Weird-Imageloads/Helpers.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "windows.h" 3 | #include "psapi.h" 4 | 5 | #include 6 | #include 7 | 8 | namespace Helpers { 9 | VOID RemoveKernelAddrs ( std::vector& ); 10 | BOOL ModuleNameFromAddress ( HANDLE, PVOID, std::string& ); 11 | BOOL IsElevated ( VOID ); 12 | BOOL IsModuleStomped ( HANDLE, PVOID ); 13 | BOOL SymbolNameFromAddress ( HANDLE, PVOID, std::string& ); 14 | } -------------------------------------------------------------------------------- /Hunt-Weird-Imageloads/Hunt-Weird-Imageloads.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.32413.511 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Hunt-Weird-Imageloads", "Hunt-Weird-Imageloads.vcxproj", "{0020C8CD-76A4-4092-9CA5-AF05572CA661}" 7 | EndProject 8 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "NewThreadLoadLibrary", "NewThreadLoadLibrary\NewThreadLoadLibrary.vcxproj", "{D819FEE1-5004-4ABF-9B2D-6562FBBBE1C5}" 9 | EndProject 10 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WorkItemLoadLibrary", "WorkItemLoadLibrary\WorkItemLoadLibrary.vcxproj", "{7EC5D2D4-3E4C-418F-82D3-98C232F6CC9B}" 11 | EndProject 12 | Global 13 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 14 | Debug|x64 = Debug|x64 15 | Debug|x86 = Debug|x86 16 | Release|x64 = Release|x64 17 | Release|x86 = Release|x86 18 | EndGlobalSection 19 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 20 | {0020C8CD-76A4-4092-9CA5-AF05572CA661}.Debug|x64.ActiveCfg = Debug|x64 21 | {0020C8CD-76A4-4092-9CA5-AF05572CA661}.Debug|x64.Build.0 = Debug|x64 22 | {0020C8CD-76A4-4092-9CA5-AF05572CA661}.Debug|x86.ActiveCfg = Debug|Win32 23 | {0020C8CD-76A4-4092-9CA5-AF05572CA661}.Debug|x86.Build.0 = Debug|Win32 24 | {0020C8CD-76A4-4092-9CA5-AF05572CA661}.Release|x64.ActiveCfg = Release|x64 25 | {0020C8CD-76A4-4092-9CA5-AF05572CA661}.Release|x64.Build.0 = Release|x64 26 | {0020C8CD-76A4-4092-9CA5-AF05572CA661}.Release|x86.ActiveCfg = Release|Win32 27 | {0020C8CD-76A4-4092-9CA5-AF05572CA661}.Release|x86.Build.0 = Release|Win32 28 | {D819FEE1-5004-4ABF-9B2D-6562FBBBE1C5}.Debug|x64.ActiveCfg = Debug|x64 29 | {D819FEE1-5004-4ABF-9B2D-6562FBBBE1C5}.Debug|x64.Build.0 = Debug|x64 30 | {D819FEE1-5004-4ABF-9B2D-6562FBBBE1C5}.Debug|x86.ActiveCfg = Debug|Win32 31 | {D819FEE1-5004-4ABF-9B2D-6562FBBBE1C5}.Debug|x86.Build.0 = Debug|Win32 32 | {D819FEE1-5004-4ABF-9B2D-6562FBBBE1C5}.Release|x64.ActiveCfg = Release|x64 33 | {D819FEE1-5004-4ABF-9B2D-6562FBBBE1C5}.Release|x64.Build.0 = Release|x64 34 | {D819FEE1-5004-4ABF-9B2D-6562FBBBE1C5}.Release|x86.ActiveCfg = Release|Win32 35 | {D819FEE1-5004-4ABF-9B2D-6562FBBBE1C5}.Release|x86.Build.0 = Release|Win32 36 | {7EC5D2D4-3E4C-418F-82D3-98C232F6CC9B}.Debug|x64.ActiveCfg = Debug|x64 37 | {7EC5D2D4-3E4C-418F-82D3-98C232F6CC9B}.Debug|x64.Build.0 = Debug|x64 38 | {7EC5D2D4-3E4C-418F-82D3-98C232F6CC9B}.Debug|x86.ActiveCfg = Debug|Win32 39 | {7EC5D2D4-3E4C-418F-82D3-98C232F6CC9B}.Debug|x86.Build.0 = Debug|Win32 40 | {7EC5D2D4-3E4C-418F-82D3-98C232F6CC9B}.Release|x64.ActiveCfg = Release|x64 41 | {7EC5D2D4-3E4C-418F-82D3-98C232F6CC9B}.Release|x64.Build.0 = Release|x64 42 | {7EC5D2D4-3E4C-418F-82D3-98C232F6CC9B}.Release|x86.ActiveCfg = Release|Win32 43 | {7EC5D2D4-3E4C-418F-82D3-98C232F6CC9B}.Release|x86.Build.0 = Release|Win32 44 | EndGlobalSection 45 | GlobalSection(SolutionProperties) = preSolution 46 | HideSolutionNode = FALSE 47 | EndGlobalSection 48 | GlobalSection(ExtensibilityGlobals) = postSolution 49 | SolutionGuid = {BE1C43E1-9EF5-4D82-89DE-B7319B283FB1} 50 | EndGlobalSection 51 | EndGlobal 52 | -------------------------------------------------------------------------------- /Hunt-Weird-Imageloads/Hunt-Weird-Imageloads.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 16.0 23 | Win32Proj 24 | {0020c8cd-76a4-4092-9ca5-af05572ca661} 25 | HuntWeirdImageloads 26 | 10.0 27 | 28 | 29 | 30 | Application 31 | true 32 | v142 33 | Unicode 34 | 35 | 36 | Application 37 | false 38 | v142 39 | true 40 | Unicode 41 | 42 | 43 | Application 44 | true 45 | v142 46 | Unicode 47 | 48 | 49 | Application 50 | false 51 | v142 52 | true 53 | Unicode 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | true 75 | 76 | 77 | false 78 | 79 | 80 | true 81 | 82 | 83 | false 84 | 85 | 86 | 87 | Level3 88 | true 89 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 90 | true 91 | 92 | 93 | Console 94 | true 95 | 96 | 97 | 98 | 99 | Level3 100 | true 101 | true 102 | true 103 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 104 | true 105 | 106 | 107 | Console 108 | true 109 | true 110 | true 111 | 112 | 113 | 114 | 115 | Level3 116 | true 117 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 118 | true 119 | 120 | 121 | Console 122 | true 123 | dbghelp.lib;shlwapi.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) 124 | 125 | 126 | 127 | 128 | Level3 129 | true 130 | true 131 | true 132 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 133 | true 134 | 135 | 136 | Console 137 | true 138 | true 139 | true 140 | dbghelp.lib;shlwapi.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | -------------------------------------------------------------------------------- /Hunt-Weird-Imageloads/Hunt-Weird-Imageloads.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Source Files 20 | 21 | 22 | Source Files 23 | 24 | 25 | Source Files 26 | 27 | 28 | 29 | 30 | Header Files 31 | 32 | 33 | Header Files 34 | 35 | 36 | -------------------------------------------------------------------------------- /Hunt-Weird-Imageloads/Hunt-Weird-Imageloads.vcxproj.user: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /Hunt-Weird-Imageloads/Main.cpp: -------------------------------------------------------------------------------- 1 | #include "../libs/krabs/krabs.hpp" 2 | #include "dbghelp.h" 3 | #include 4 | 5 | #include "Detectors.h" 6 | #include "Helpers.h" 7 | 8 | #define EVENTID_THREADSTART 3 9 | #define EVENTID_IMAGELOAD 5 10 | 11 | VOID EnableProcessTracing ( krabs::user_trace& ); 12 | VOID Help ( VOID ); 13 | VOID OnImageLoad ( const EVENT_RECORD&, const krabs::trace_context& ); 14 | VOID OnThreadStart ( const EVENT_RECORD&, const krabs::trace_context& ); 15 | VOID ParseArgs ( int, char** ); 16 | 17 | std::vector activeDetectorsImageLoad; 18 | std::vector activeDetectorsThreadStart; 19 | 20 | VOID EnableProcessTracing ( krabs::user_trace& userTrace ) { 21 | 22 | krabs::provider<>* providerProcess = new krabs::provider<> ( L"Microsoft-Windows-Kernel-Process" ); 23 | providerProcess->any ( 0x20 | 0x40 ); // WINEVENT_KEYWORD_THREAD | WINEVENT_KEYWORD_IMAGE 24 | providerProcess->trace_flags ( providerProcess->trace_flags ( ) | EVENT_ENABLE_PROPERTY_STACK_TRACE ); 25 | 26 | krabs::event_filter* imageLoad = new krabs::event_filter ( krabs::predicates::id_is ( EVENTID_IMAGELOAD ) ); 27 | krabs::event_filter* threadStart = new krabs::event_filter ( krabs::predicates::id_is ( EVENTID_THREADSTART ) ); 28 | 29 | imageLoad->add_on_event_callback ( OnImageLoad ); 30 | threadStart->add_on_event_callback ( OnThreadStart ); 31 | 32 | providerProcess->add_filter ( *imageLoad ); 33 | providerProcess->add_filter ( *threadStart ); 34 | 35 | userTrace.enable ( *providerProcess ); 36 | 37 | } 38 | 39 | VOID OnThreadStart ( const EVENT_RECORD& record, const krabs::trace_context& trace_context ) { 40 | 41 | krabs::schema schema ( record, trace_context.schema_locator ); 42 | krabs::parser parser ( schema ); 43 | 44 | std::vector stack = schema.stack_trace ( ); 45 | 46 | DWORD pid = parser.parse ( L"ProcessID" ); 47 | DWORD tid = parser.parse ( L"ThreadID" ); 48 | 49 | PVOID startAddr = parser.parse ( L"StartAddr" ); 50 | 51 | HANDLE hProcess = NULL; 52 | BOOL bIs32Bit; 53 | 54 | hProcess = OpenProcess ( PROCESS_ALL_ACCESS, FALSE, pid ); 55 | if ( hProcess == NULL ) 56 | return; 57 | 58 | IsWow64Process ( hProcess, &bIs32Bit ); 59 | if ( bIs32Bit ) 60 | goto Cleanup; 61 | 62 | for ( Detectors::DetectorThreadStart* detector : activeDetectorsThreadStart ) 63 | detector->Check ( hProcess, stack, pid, startAddr ); 64 | 65 | Cleanup: 66 | 67 | if ( hProcess ) 68 | CloseHandle ( hProcess ); 69 | 70 | } 71 | 72 | VOID OnImageLoad ( const EVENT_RECORD& record, const krabs::trace_context& trace_context ) { 73 | 74 | krabs::schema schema ( record, trace_context.schema_locator ); 75 | krabs::parser parser ( schema ); 76 | 77 | BOOL bIs32Bit = FALSE; 78 | DWORD pid = parser.parse ( L"ProcessID" ); 79 | PVOID imageBase = parser.parse ( L"ImageBase" ); 80 | std::wstring imageName = parser.parse ( L"ImageName" ); 81 | 82 | std::vector stack = schema.stack_trace ( ); 83 | Helpers::RemoveKernelAddrs ( stack ); 84 | 85 | HANDLE hProcess = NULL; 86 | 87 | hProcess = OpenProcess ( PROCESS_ALL_ACCESS, FALSE, pid ); 88 | if ( hProcess == NULL ) 89 | return; 90 | 91 | IsWow64Process ( hProcess, &bIs32Bit ); 92 | if ( bIs32Bit ) 93 | goto Cleanup; 94 | 95 | SymInitialize ( hProcess, NULL, TRUE ); 96 | 97 | for ( Detectors::DetectorImageLoad *detector : activeDetectorsImageLoad ) 98 | detector->Check ( hProcess, stack, pid, imageName ); 99 | 100 | Cleanup: 101 | 102 | if ( hProcess ) { 103 | 104 | SymCleanup ( hProcess ); 105 | CloseHandle ( hProcess ); 106 | 107 | } 108 | 109 | } 110 | 111 | VOID Go ( krabs::user_trace* userTrace ) { 112 | userTrace->start ( ); 113 | } 114 | 115 | VOID Help ( VOID ) { 116 | printf ( "Hunt-Weird-ImageLoads.exe\n\n\t--all activates all alerts\n\t--rx alerts on private rx regions in callstack\n\t--rwx alerts on private rwx regions in callstack\n\t--stomped alerts on stomped modules in callstack\n\t--proxy alerts on abnormal calls to kernel32!loadlibrary from ntdll\n\t--dedicatedthread alerts on thread with baseaddr on loadlibrary*\n\n" ); 117 | exit ( 1 ); 118 | } 119 | 120 | VOID ParseArgs ( int argc, char** argv ) { 121 | 122 | if ( argc < 2 ) 123 | Help ( ); 124 | 125 | for ( int i = 1; i < argc; i++ ) { 126 | 127 | if ( !_strcmpi ( argv [ i ], "--rx" ) ) { 128 | printf ( "\t- Enabling detector RX\n" ); 129 | activeDetectorsImageLoad.push_back ( new Detectors::PrivateRX ( ) ); 130 | } 131 | 132 | else if ( !_strcmpi ( argv [ i ], "--rwx" ) ) { 133 | printf ( "\t- Enabling detector RWX\n" ); 134 | activeDetectorsImageLoad.push_back ( new Detectors::PrivateRWX ( ) ); 135 | } 136 | 137 | else if ( !_strcmpi ( argv [ i ], "--stomped" ) ) { 138 | printf ( "\t- Enabling detector stomped\n" ); 139 | activeDetectorsImageLoad.push_back ( new Detectors::ModuleStomped ( ) ); 140 | } 141 | 142 | else if ( !_strcmpi ( argv [ i ], "--proxy" ) ) { 143 | printf ( "\t- Enabling detector for module proxying\n" ); 144 | activeDetectorsImageLoad.push_back ( new Detectors::ModuleProxying ( ) ); 145 | } 146 | 147 | else if ( !_strcmpi ( argv [ i ], "--dedicatedthread" ) ) { 148 | printf ( "\t- Enabling detector dedicatedthread\n" ); 149 | activeDetectorsThreadStart.push_back ( new Detectors::DedicatedThread ( ) ); 150 | } 151 | 152 | else if ( !_strcmpi ( argv [ i ], "--all" ) ) { 153 | 154 | printf ( "\t- Enabling all detectors\n" ); 155 | 156 | activeDetectorsImageLoad.clear ( ); 157 | 158 | activeDetectorsImageLoad.push_back ( new Detectors::PrivateRX ( ) ); 159 | activeDetectorsImageLoad.push_back ( new Detectors::PrivateRWX ( ) ); 160 | activeDetectorsImageLoad.push_back ( new Detectors::ModuleStomped ( ) ); 161 | activeDetectorsImageLoad.push_back ( new Detectors::ModuleProxying ( ) ); 162 | 163 | activeDetectorsThreadStart.push_back ( new Detectors::DedicatedThread ( ) ); 164 | 165 | } 166 | 167 | else 168 | Help ( ); 169 | 170 | } 171 | 172 | } 173 | 174 | int main ( int argc, char** argv ) { 175 | 176 | HANDLE traceThread = NULL; 177 | 178 | krabs::user_trace userTrace ( L"Hunt-Weird-Imageloads" ); 179 | 180 | if ( !Helpers::IsElevated ( ) ) { 181 | printf ( "- Not elevated\n" ); 182 | return 0; 183 | } 184 | 185 | printf ( "* Hunt-Weird-ImageLoads\n" ); 186 | ParseArgs ( argc, argv ); 187 | 188 | printf ( "* Enabling trace, might take a bit ... \n" ); 189 | 190 | SymSetOptions ( SYMOPT_UNDNAME | SYMOPT_DEFERRED_LOADS ); 191 | 192 | EnableProcessTracing ( userTrace ); 193 | traceThread = CreateThread ( NULL, 0, ( LPTHREAD_START_ROUTINE ) Go, &userTrace, 0, NULL ); 194 | if ( traceThread == NULL ) 195 | return 0; // o.0 196 | 197 | printf ( "* Started monitoring, press any key to exit ... \n" ); 198 | 199 | getchar ( ); 200 | printf ( "* exiting ... \n" ); 201 | userTrace.stop ( ); 202 | WaitForSingleObject ( traceThread, INFINITE ); 203 | 204 | return 0; 205 | 206 | } -------------------------------------------------------------------------------- /Hunt-Weird-Imageloads/NewThreadLoadLibrary/Main.c: -------------------------------------------------------------------------------- 1 | #include "windows.h" 2 | 3 | #include "stdio.h" 4 | 5 | HMODULE MyLoadLibrary ( PCSTR mName ) { 6 | 7 | HANDLE hThread = NULL; 8 | 9 | hThread = CreateThread ( NULL, 0, ( LPTHREAD_START_ROUTINE ) LoadLibraryA, ( LPVOID ) mName, 0, NULL ); 10 | if ( hThread == NULL ) 11 | return 0; 12 | 13 | WaitForSingleObject ( hThread, INFINITE ); 14 | 15 | return GetModuleHandleA ( mName ); 16 | 17 | } 18 | 19 | int main ( int arg, char** argv ) { 20 | 21 | HMODULE hModule = NULL; 22 | 23 | hModule = GetModuleHandleA ( "dbghelp.dll" ); 24 | if ( hModule ) { 25 | printf ( "Dll already loaded\n" ); 26 | return 0; 27 | } 28 | 29 | hModule = MyLoadLibrary ( "dbghelp.dll" ); 30 | printf ( "Module at 0x%p\n", hModule ); 31 | 32 | getchar ( ); 33 | 34 | return 0; 35 | 36 | } -------------------------------------------------------------------------------- /Hunt-Weird-Imageloads/NewThreadLoadLibrary/NewThreadLoadLibrary.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 16.0 23 | Win32Proj 24 | {d819fee1-5004-4abf-9b2d-6562fbbbe1c5} 25 | NewThreadLoadLibrary 26 | 10.0 27 | 28 | 29 | 30 | Application 31 | true 32 | v142 33 | Unicode 34 | 35 | 36 | Application 37 | false 38 | v142 39 | true 40 | Unicode 41 | 42 | 43 | Application 44 | true 45 | v142 46 | Unicode 47 | 48 | 49 | Application 50 | false 51 | v142 52 | true 53 | Unicode 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | true 75 | 76 | 77 | false 78 | 79 | 80 | true 81 | 82 | 83 | false 84 | 85 | 86 | 87 | Level3 88 | true 89 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 90 | true 91 | 92 | 93 | Console 94 | true 95 | 96 | 97 | 98 | 99 | Level3 100 | true 101 | true 102 | true 103 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 104 | true 105 | 106 | 107 | Console 108 | true 109 | true 110 | true 111 | 112 | 113 | 114 | 115 | Level3 116 | true 117 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 118 | true 119 | 120 | 121 | Console 122 | true 123 | 124 | 125 | 126 | 127 | Level3 128 | true 129 | true 130 | true 131 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 132 | true 133 | 134 | 135 | Console 136 | true 137 | true 138 | true 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | -------------------------------------------------------------------------------- /Hunt-Weird-Imageloads/NewThreadLoadLibrary/NewThreadLoadLibrary.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Source Files 20 | 21 | 22 | -------------------------------------------------------------------------------- /Hunt-Weird-Imageloads/NewThreadLoadLibrary/NewThreadLoadLibrary.vcxproj.user: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /Hunt-Weird-Imageloads/WorkItemLoadLibrary/Main.c: -------------------------------------------------------------------------------- 1 | #include "windows.h" 2 | #include "stdio.h" 3 | 4 | /* 5 | 6 | Based on: https://github.com/rad9800/misc/blob/main/bypasses/WorkItemLoadLibrary.c 7 | 8 | */ 9 | 10 | typedef NTSTATUS ( NTAPI* RtlQueueWorkItem )( PVOID, PVOID, ULONG ); 11 | 12 | HMODULE MyLoadLibrary ( PCSTR mName ) { 13 | 14 | HANDLE hThread = NULL; 15 | RtlQueueWorkItem _RtlQueueWorkItem = ( RtlQueueWorkItem ) GetProcAddress ( GetModuleHandleA ( "ntdll.dll" ), "RtlQueueWorkItem" ); 16 | _RtlQueueWorkItem ( LoadLibraryA, ( PVOID ) mName, WT_EXECUTEDEFAULT ); 17 | 18 | Sleep ( 1000 ); // Dirty :-) 19 | 20 | return GetModuleHandleA ( mName ); 21 | 22 | } 23 | 24 | 25 | int 26 | main ( int argc, char** argv ) { 27 | 28 | HMODULE hModule = NULL; 29 | 30 | hModule = GetModuleHandleA ( "wininet.dll" ); 31 | if ( hModule ) { 32 | printf ( "Dll already loaded\n" ); 33 | return 0; 34 | } 35 | 36 | hModule = MyLoadLibrary ( "wininet.dll" ); 37 | printf ( "Module at 0x%p\n", hModule ); 38 | 39 | getchar ( ); 40 | 41 | return 0; 42 | 43 | } -------------------------------------------------------------------------------- /Hunt-Weird-Imageloads/WorkItemLoadLibrary/WorkItemLoadLibrary.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 16.0 23 | Win32Proj 24 | {7ec5d2d4-3e4c-418f-82d3-98c232f6cc9b} 25 | WorkItemLoadLibrary 26 | 10.0 27 | 28 | 29 | 30 | Application 31 | true 32 | v142 33 | Unicode 34 | 35 | 36 | Application 37 | false 38 | v142 39 | true 40 | Unicode 41 | 42 | 43 | Application 44 | true 45 | v142 46 | Unicode 47 | 48 | 49 | Application 50 | false 51 | v142 52 | true 53 | Unicode 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | true 75 | 76 | 77 | false 78 | 79 | 80 | true 81 | 82 | 83 | false 84 | 85 | 86 | 87 | Level3 88 | true 89 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 90 | true 91 | 92 | 93 | Console 94 | true 95 | 96 | 97 | 98 | 99 | Level3 100 | true 101 | true 102 | true 103 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 104 | true 105 | 106 | 107 | Console 108 | true 109 | true 110 | true 111 | 112 | 113 | 114 | 115 | Level3 116 | true 117 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 118 | true 119 | 120 | 121 | Console 122 | true 123 | 124 | 125 | 126 | 127 | Level3 128 | true 129 | true 130 | true 131 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 132 | true 133 | 134 | 135 | Console 136 | true 137 | true 138 | true 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | -------------------------------------------------------------------------------- /Hunt-Weird-Imageloads/WorkItemLoadLibrary/WorkItemLoadLibrary.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Source Files 20 | 21 | 22 | -------------------------------------------------------------------------------- /Hunt-Weird-Imageloads/WorkItemLoadLibrary/WorkItemLoadLibrary.vcxproj.user: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # Hunt-Weird-ImageLoads 2 | 3 | This project was created to play with different IOCs caused by Imageload events. 4 | It leverages ETW to monitor for ImageLoad events and walks the callstack to identify some possible IOCs, such as: 5 | 6 | - R(W)X page in callstack 7 | - Stomped module in callstack 8 | - Module proxying ( ntdll -> kernel32!LoadLibrary ) as described [here](https://github.com/rad9800/misc/blob/main/bypasses/WorkItemLoadLibrary.c) or [here](https://0xdarkvortex.dev/proxying-dll-loads-for-hiding-etwti-stack-tracing) 9 | - New thread dedicated to load a library 10 | 11 | There are two sample programs for **module proxying** and **dedicated threads** in this repository. 12 | 13 | ![In action](/screens/1.png?raw=true) 14 | 15 | ## Conclusion 16 | 17 | In my tests, I had a lot of false positives monitoring for private or module stomped pages in the callstack and this is probably not a valid IOC. 18 | However, it seems that both, **module proxying** and **dedicated threads** are quite abnormal, but see yourself. 19 | 20 | ## Usage 21 | 22 | ``` 23 | --all activates all alerts 24 | --rx alerts on private rx regions in callstack 25 | --rwx alerts on private rwx regions in callstack 26 | --stomped alerts on stomped modules in callstack 27 | --proxy alerts on abnormal calls to kernel32!loadlibrary from ntdll 28 | --dedicatedthread alerts on thread with baseaddr on loadlibrary* 29 | ``` 30 | 31 | ## Credits 32 | 33 | - [@rad9800](https://twitter.com/rad9800) [For an example implementation of LoadLibray via RtlQueueWorkItem](https://github.com/rad9800/misc/blob/main/bypasses/WorkItemLoadLibrary.c) 34 | - [@NinjaParanoid](https://twitter.com/NinjaParanoid) [For a super cool blogpost on this topic](https://0xdarkvortex.dev/proxying-dll-loads-for-hiding-etwti-stack-tracing/) -------------------------------------------------------------------------------- /libs/krabs/LICENSE: -------------------------------------------------------------------------------- 1 | krabsetw 2 | 3 | Copyright (c) Microsoft Corporation 4 | 5 | All rights reserved. 6 | 7 | MIT License 8 | 9 | Permission is hereby granted, free of charge, to any person obtaining a copy 10 | of this software and associated documentation files (the ""Software""), to deal 11 | in the Software without restriction, including without limitation the rights 12 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | copies of the Software, and to permit persons to whom the Software is 14 | furnished to do so, subject to the following conditions: 15 | 16 | The above copyright notice and this permission notice shall be included in all 17 | copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 25 | SOFTWARE. 26 | -------------------------------------------------------------------------------- /libs/krabs/README.md: -------------------------------------------------------------------------------- 1 | # Krabs Readme 2 | 3 | Important Preprocessor Definitions: 4 | 5 | * `UNICODE` - krabsetw expects the `UNICODE` preprocessor definition to be 6 | defined. The code will not successfully compile without this flag. There is 7 | no plan to support compilation without `UNICODE` being set. 8 | 9 | * `NDEBUG` - Set this variable in release builds to disable runtime type 10 | assertions. You'll still get a runtime error if the size type you're 11 | requesting is not the same size as the property in the event schema. 12 | 13 | * `TYPEASSERT` - Set this variable only in debug builds (not `NDEBUG`) to enable 14 | strict assertions. This means that if an explicit type check is not defined 15 | for a requested type, a `static_assert` is thrown and the code will not 16 | compile until one is added. This is mainly used for krabs development to 17 | ensure that we don't miss asserts for types that are supported. 18 | -------------------------------------------------------------------------------- /libs/krabs/krabs.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | #pragma once 5 | 6 | #pragma comment(lib, "advapi32.lib") 7 | #pragma comment(lib, "ole32.lib") 8 | 9 | 10 | // 11 | // /\ 12 | // ( / @ @ () 13 | // \ __| |__ / 14 | // -/ " \- 15 | // /-| |-\ 16 | // / /-\ /-\ \ 17 | // / /-`---'-\ \ 18 | // / \ 19 | // 20 | // Summary 21 | // ---------------------------------------------------------------------------- 22 | // Krabs is a wrapper around ETW because ETW is the worst API ever made. 23 | 24 | #pragma warning(push) 25 | #pragma warning(disable: 4512) // stupid spurious "can't generate assignment error" warning 26 | #pragma warning(disable: 4634) // DocXml comment warnings in native C++ 27 | #pragma warning(disable: 4635) // DocXml comment warnings in native C++ 28 | 29 | #include "krabs/compiler_check.hpp" 30 | #include "krabs/ut.hpp" 31 | #include "krabs/kt.hpp" 32 | #include "krabs/guid.hpp" 33 | #include "krabs/trace.hpp" 34 | #include "krabs/trace_context.hpp" 35 | #include "krabs/client.hpp" 36 | #include "krabs/errors.hpp" 37 | #include "krabs/schema.hpp" 38 | #include "krabs/schema_locator.hpp" 39 | #include "krabs/parse_types.hpp" 40 | #include "krabs/collection_view.hpp" 41 | #include "krabs/size_provider.hpp" 42 | #include "krabs/parser.hpp" 43 | #include "krabs/property.hpp" 44 | #include "krabs/provider.hpp" 45 | #include "krabs/etw.hpp" 46 | #include "krabs/tdh_helpers.hpp" 47 | #include "krabs/kernel_providers.hpp" 48 | 49 | #include "krabs/testing/proxy.hpp" 50 | #include "krabs/testing/filler.hpp" 51 | #include "krabs/testing/synth_record.hpp" 52 | #include "krabs/testing/record_builder.hpp" 53 | #include "krabs/testing/event_filter_proxy.hpp" 54 | #include "krabs/testing/record_property_thunk.hpp" 55 | 56 | #include "krabs/filtering/view_adapters.hpp" 57 | #include "krabs/filtering/comparers.hpp" 58 | #include "krabs/filtering/predicates.hpp" 59 | #include "krabs/filtering/event_filter.hpp" 60 | 61 | #pragma warning(pop) 62 | -------------------------------------------------------------------------------- /libs/krabs/krabs.runsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | False 6 | MTA 7 | x64 8 | 9 | -------------------------------------------------------------------------------- /libs/krabs/krabs.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.2.32317.152 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{5C9E224B-AEB7-456B-B309-92292ACE1A0D}" 7 | ProjectSection(SolutionItems) = preProject 8 | .nuget\NuGet.Config = .nuget\NuGet.Config 9 | .nuget\NuGet.exe = .nuget\NuGet.exe 10 | .nuget\NuGet.targets = .nuget\NuGet.targets 11 | EndProjectSection 12 | EndProject 13 | Project("{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}" 14 | EndProject 15 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "krabstests", "..\tests\krabstests\krabstests.vcxproj", "{880977B8-15CA-421B-BF48-D01626A530A2}" 16 | EndProject 17 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{E583602B-48AC-46A6-B0F0-343CE0B6AE33}" 18 | ProjectSection(SolutionItems) = preProject 19 | krabs.runsettings = krabs.runsettings 20 | MTA.testsettings = MTA.testsettings 21 | EndProjectSection 22 | EndProject 23 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "krabs headers", "krabs headers", "{1FD19105-D67C-492B-B98F-53E00A324269}" 24 | ProjectSection(SolutionItems) = preProject 25 | krabs.hpp = krabs.hpp 26 | EndProjectSection 27 | EndProject 28 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "krabs", "krabs", "{371361C8-96EC-4D6D-B80B-2E47E3453264}" 29 | ProjectSection(SolutionItems) = preProject 30 | krabs\client.hpp = krabs\client.hpp 31 | krabs\collection_view.hpp = krabs\collection_view.hpp 32 | krabs\compiler_check.hpp = krabs\compiler_check.hpp 33 | krabs\errors.hpp = krabs\errors.hpp 34 | krabs\etw.hpp = krabs\etw.hpp 35 | krabs\guid.hpp = krabs\guid.hpp 36 | krabs\kernel_guids.hpp = krabs\kernel_guids.hpp 37 | krabs\kernel_providers.hpp = krabs\kernel_providers.hpp 38 | krabs\kt.hpp = krabs\kt.hpp 39 | krabs\parser.hpp = krabs\parser.hpp 40 | krabs\parse_types.hpp = krabs\parse_types.hpp 41 | krabs\perfinfo_groupmask.hpp = krabs\perfinfo_groupmask.hpp 42 | krabs\property.hpp = krabs\property.hpp 43 | krabs\provider.hpp = krabs\provider.hpp 44 | krabs\schema.hpp = krabs\schema.hpp 45 | krabs\schema_locator.hpp = krabs\schema_locator.hpp 46 | krabs\size_provider.hpp = krabs\size_provider.hpp 47 | krabs\tdh_helpers.hpp = krabs\tdh_helpers.hpp 48 | krabs\trace.hpp = krabs\trace.hpp 49 | krabs\trace_context.hpp = krabs\trace_context.hpp 50 | krabs\ut.hpp = krabs\ut.hpp 51 | krabs\version_helpers.hpp = krabs\version_helpers.hpp 52 | krabs\wstring_convert.hpp = krabs\wstring_convert.hpp 53 | EndProjectSection 54 | EndProject 55 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "filtering", "filtering", "{96FA58B5-A1F6-4107-9FB4-226290F9D696}" 56 | ProjectSection(SolutionItems) = preProject 57 | krabs\filtering\comparers.hpp = krabs\filtering\comparers.hpp 58 | krabs\filtering\event_filter.hpp = krabs\filtering\event_filter.hpp 59 | krabs\filtering\predicates.hpp = krabs\filtering\predicates.hpp 60 | krabs\filtering\view_adapters.hpp = krabs\filtering\view_adapters.hpp 61 | EndProjectSection 62 | EndProject 63 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "testing", "testing", "{9ED1AE76-2EAA-4CCF-8F01-458BDC8BCD53}" 64 | ProjectSection(SolutionItems) = preProject 65 | krabs\testing\event_filter_proxy.hpp = krabs\testing\event_filter_proxy.hpp 66 | krabs\testing\extended_data_builder.hpp = krabs\testing\extended_data_builder.hpp 67 | krabs\testing\filler.hpp = krabs\testing\filler.hpp 68 | krabs\testing\proxy.hpp = krabs\testing\proxy.hpp 69 | krabs\testing\record_builder.hpp = krabs\testing\record_builder.hpp 70 | krabs\testing\record_property_thunk.hpp = krabs\testing\record_property_thunk.hpp 71 | krabs\testing\synth_record.hpp = krabs\testing\synth_record.hpp 72 | EndProjectSection 73 | EndProject 74 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EtwTestsCS", "..\tests\ManagedETWTests\EtwTestsCS.csproj", "{600CFE03-FD84-4323-9439-839D81C31972}" 75 | EndProject 76 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{C4AB7F5F-2FB3-4C16-A1F3-F6700C655B02}" 77 | EndProject 78 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Examples", "Examples", "{2E00634C-7E8B-4656-9505-78FF2F5D0EDD}" 79 | EndProject 80 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ManagedExamples", "..\examples\ManagedExamples\ManagedExamples.csproj", "{32E71DD0-D11A-44DE-8CA8-572995AF2373}" 81 | EndProject 82 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "NativeExamples", "..\examples\NativeExamples\NativeExamples.vcxproj", "{D31B1A4B-8282-4AED-99FC-9AA5974B9134}" 83 | ProjectSection(ProjectDependencies) = postProject 84 | {ED4E6027-541F-440A-A5EE-15DBB7B89423} = {ED4E6027-541F-440A-A5EE-15DBB7B89423} 85 | EndProjectSection 86 | EndProject 87 | Project("{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}" 88 | EndProject 89 | Global 90 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 91 | Debug|x64 = Debug|x64 92 | DebugSigning|x64 = DebugSigning|x64 93 | Release|x64 = Release|x64 94 | ReleaseSigning|x64 = ReleaseSigning|x64 95 | EndGlobalSection 96 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 97 | {ED4E6027-541F-440A-A5EE-15DBB7B89423}.Debug|x64.ActiveCfg = Debug|x64 98 | {ED4E6027-541F-440A-A5EE-15DBB7B89423}.Debug|x64.Build.0 = Debug|x64 99 | {ED4E6027-541F-440A-A5EE-15DBB7B89423}.DebugSigning|x64.ActiveCfg = DebugSigning|x64 100 | {ED4E6027-541F-440A-A5EE-15DBB7B89423}.DebugSigning|x64.Build.0 = DebugSigning|x64 101 | {ED4E6027-541F-440A-A5EE-15DBB7B89423}.Release|x64.ActiveCfg = Release|x64 102 | {ED4E6027-541F-440A-A5EE-15DBB7B89423}.Release|x64.Build.0 = Release|x64 103 | {ED4E6027-541F-440A-A5EE-15DBB7B89423}.ReleaseSigning|x64.ActiveCfg = ReleaseSigning|x64 104 | {ED4E6027-541F-440A-A5EE-15DBB7B89423}.ReleaseSigning|x64.Build.0 = ReleaseSigning|x64 105 | {880977B8-15CA-421B-BF48-D01626A530A2}.Debug|x64.ActiveCfg = Debug|x64 106 | {880977B8-15CA-421B-BF48-D01626A530A2}.Debug|x64.Build.0 = Debug|x64 107 | {880977B8-15CA-421B-BF48-D01626A530A2}.DebugSigning|x64.ActiveCfg = DebugSigning|x64 108 | {880977B8-15CA-421B-BF48-D01626A530A2}.DebugSigning|x64.Build.0 = DebugSigning|x64 109 | {880977B8-15CA-421B-BF48-D01626A530A2}.Release|x64.ActiveCfg = Release|x64 110 | {880977B8-15CA-421B-BF48-D01626A530A2}.Release|x64.Build.0 = Release|x64 111 | {880977B8-15CA-421B-BF48-D01626A530A2}.ReleaseSigning|x64.ActiveCfg = ReleaseSigning|x64 112 | {880977B8-15CA-421B-BF48-D01626A530A2}.ReleaseSigning|x64.Build.0 = ReleaseSigning|x64 113 | {600CFE03-FD84-4323-9439-839D81C31972}.Debug|x64.ActiveCfg = Debug|Any CPU 114 | {600CFE03-FD84-4323-9439-839D81C31972}.Debug|x64.Build.0 = Debug|Any CPU 115 | {600CFE03-FD84-4323-9439-839D81C31972}.DebugSigning|x64.ActiveCfg = Debug|Any CPU 116 | {600CFE03-FD84-4323-9439-839D81C31972}.DebugSigning|x64.Build.0 = Debug|Any CPU 117 | {600CFE03-FD84-4323-9439-839D81C31972}.Release|x64.ActiveCfg = Release|Any CPU 118 | {600CFE03-FD84-4323-9439-839D81C31972}.Release|x64.Build.0 = Release|Any CPU 119 | {600CFE03-FD84-4323-9439-839D81C31972}.ReleaseSigning|x64.ActiveCfg = Release|Any CPU 120 | {600CFE03-FD84-4323-9439-839D81C31972}.ReleaseSigning|x64.Build.0 = Release|Any CPU 121 | {32E71DD0-D11A-44DE-8CA8-572995AF2373}.Debug|x64.ActiveCfg = Debug|Any CPU 122 | {32E71DD0-D11A-44DE-8CA8-572995AF2373}.Debug|x64.Build.0 = Debug|Any CPU 123 | {32E71DD0-D11A-44DE-8CA8-572995AF2373}.DebugSigning|x64.ActiveCfg = DebugSigning|Any CPU 124 | {32E71DD0-D11A-44DE-8CA8-572995AF2373}.DebugSigning|x64.Build.0 = DebugSigning|Any CPU 125 | {32E71DD0-D11A-44DE-8CA8-572995AF2373}.Release|x64.ActiveCfg = Release|Any CPU 126 | {32E71DD0-D11A-44DE-8CA8-572995AF2373}.Release|x64.Build.0 = Release|Any CPU 127 | {32E71DD0-D11A-44DE-8CA8-572995AF2373}.ReleaseSigning|x64.ActiveCfg = ReleaseSigning|Any CPU 128 | {32E71DD0-D11A-44DE-8CA8-572995AF2373}.ReleaseSigning|x64.Build.0 = ReleaseSigning|Any CPU 129 | {D31B1A4B-8282-4AED-99FC-9AA5974B9134}.Debug|x64.ActiveCfg = Debug|x64 130 | {D31B1A4B-8282-4AED-99FC-9AA5974B9134}.Debug|x64.Build.0 = Debug|x64 131 | {D31B1A4B-8282-4AED-99FC-9AA5974B9134}.DebugSigning|x64.ActiveCfg = DebugSigning|x64 132 | {D31B1A4B-8282-4AED-99FC-9AA5974B9134}.DebugSigning|x64.Build.0 = DebugSigning|x64 133 | {D31B1A4B-8282-4AED-99FC-9AA5974B9134}.Release|x64.ActiveCfg = Release|x64 134 | {D31B1A4B-8282-4AED-99FC-9AA5974B9134}.Release|x64.Build.0 = Release|x64 135 | {D31B1A4B-8282-4AED-99FC-9AA5974B9134}.ReleaseSigning|x64.ActiveCfg = ReleaseSigning|x64 136 | {D31B1A4B-8282-4AED-99FC-9AA5974B9134}.ReleaseSigning|x64.Build.0 = ReleaseSigning|x64 137 | {9DE6788C-5759-4A75-B484-ABA4C7EF5F08}.Debug|x64.ActiveCfg = Debug|x64 138 | {9DE6788C-5759-4A75-B484-ABA4C7EF5F08}.Debug|x64.Build.0 = Debug|x64 139 | {9DE6788C-5759-4A75-B484-ABA4C7EF5F08}.DebugSigning|x64.ActiveCfg = DebugSigning|x64 140 | {9DE6788C-5759-4A75-B484-ABA4C7EF5F08}.DebugSigning|x64.Build.0 = DebugSigning|x64 141 | {9DE6788C-5759-4A75-B484-ABA4C7EF5F08}.Release|x64.ActiveCfg = Release|x64 142 | {9DE6788C-5759-4A75-B484-ABA4C7EF5F08}.Release|x64.Build.0 = Release|x64 143 | {9DE6788C-5759-4A75-B484-ABA4C7EF5F08}.ReleaseSigning|x64.ActiveCfg = ReleaseSigning|x64 144 | {9DE6788C-5759-4A75-B484-ABA4C7EF5F08}.ReleaseSigning|x64.Build.0 = ReleaseSigning|x64 145 | EndGlobalSection 146 | GlobalSection(SolutionProperties) = preSolution 147 | HideSolutionNode = FALSE 148 | EndGlobalSection 149 | GlobalSection(NestedProjects) = preSolution 150 | {880977B8-15CA-421B-BF48-D01626A530A2} = {C4AB7F5F-2FB3-4C16-A1F3-F6700C655B02} 151 | {371361C8-96EC-4D6D-B80B-2E47E3453264} = {1FD19105-D67C-492B-B98F-53E00A324269} 152 | {96FA58B5-A1F6-4107-9FB4-226290F9D696} = {371361C8-96EC-4D6D-B80B-2E47E3453264} 153 | {9ED1AE76-2EAA-4CCF-8F01-458BDC8BCD53} = {371361C8-96EC-4D6D-B80B-2E47E3453264} 154 | {600CFE03-FD84-4323-9439-839D81C31972} = {C4AB7F5F-2FB3-4C16-A1F3-F6700C655B02} 155 | {32E71DD0-D11A-44DE-8CA8-572995AF2373} = {2E00634C-7E8B-4656-9505-78FF2F5D0EDD} 156 | {D31B1A4B-8282-4AED-99FC-9AA5974B9134} = {2E00634C-7E8B-4656-9505-78FF2F5D0EDD} 157 | EndGlobalSection 158 | GlobalSection(ExtensibilityGlobals) = postSolution 159 | SolutionGuid = {82BAA012-2EF9-4303-A429-CDA3655D5009} 160 | EndGlobalSection 161 | EndGlobal 162 | -------------------------------------------------------------------------------- /libs/krabs/krabs/client.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | #pragma once 5 | 6 | #include "compiler_check.hpp" 7 | #include "ut.hpp" 8 | #include "kt.hpp" 9 | #include "trace.hpp" 10 | 11 | namespace krabs { 12 | 13 | /** 14 | * 15 | * Specialization of the base trace class for user traces. 16 | * 17 | */ 18 | typedef krabs::trace user_trace; 19 | 20 | /** 21 | * 22 | * Specialization of the base trace class for kernel traces. 23 | * 24 | */ 25 | typedef krabs::trace kernel_trace; 26 | } 27 | -------------------------------------------------------------------------------- /libs/krabs/krabs/collection_view.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | #pragma once 5 | 6 | #include 7 | 8 | #include "compiler_check.hpp" 9 | 10 | namespace krabs { 11 | 12 | /** 13 | * Wraps a range of a collection starting at the location 14 | * specified by the begin iterator and ending a the location 15 | * specified by the end iterator. The underlying items are 16 | * left in-place and should be considered const 17 | */ 18 | template 19 | struct collection_view 20 | { 21 | private: 22 | const T beg_; 23 | const T end_; 24 | 25 | public: 26 | /** 27 | * Construct a new view for the range specified by the 28 | * iterators 'begin' and 'end' 29 | */ 30 | collection_view(const T begin, const T end) 31 | : beg_(begin) 32 | , end_(end) 33 | { } 34 | 35 | /** 36 | * Get the iterator for the beginning of the view range 37 | */ 38 | const T begin() const 39 | { 40 | return beg_; 41 | } 42 | 43 | /** 44 | * Get the iterator for the end of the view range 45 | */ 46 | const T end() const 47 | { 48 | return end_; 49 | } 50 | }; 51 | 52 | /** 53 | * Create a view over the range specified by iterators 'begin' and 'end' 54 | */ 55 | template 56 | inline collection_view view(const T& begin, const T& end) 57 | { 58 | return{ begin, end }; 59 | } 60 | 61 | /** 62 | * Create a const_iterator view over the specified string 63 | */ 64 | template 65 | inline collection_view::const_iterator> view(const std::basic_string& string) 66 | { 67 | return{ string.cbegin(), string.cend() }; 68 | } 69 | 70 | /** 71 | * Create a const view over the range starting at 'begin' extending 'length' items 72 | */ 73 | template 74 | inline collection_view view(const T* begin, size_t length) 75 | { 76 | return{ begin, begin + length }; 77 | } 78 | 79 | /** 80 | * Create a const view over the specified array 81 | */ 82 | template 83 | inline collection_view view(const T(&arr)[n]) 84 | { 85 | return{ arr, arr + n }; 86 | } 87 | 88 | } /* namespace krabs */ -------------------------------------------------------------------------------- /libs/krabs/krabs/compiler_check.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | #pragma once 5 | 6 | #if (_MSC_VER < 1900) 7 | #error "krabsetw is only supported with Visual Studio 2015 and above (MSVC++ 14.0)" 8 | #endif 9 | -------------------------------------------------------------------------------- /libs/krabs/krabs/errors.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | #pragma once 5 | 6 | #include 7 | 8 | #include "compiler_check.hpp" 9 | 10 | namespace krabs { 11 | 12 | class trace_already_registered : public std::runtime_error { 13 | public: 14 | trace_already_registered() 15 | : std::runtime_error("The trace session has already been registered") 16 | {} 17 | }; 18 | 19 | class invalid_parameter : public std::logic_error { 20 | public: 21 | invalid_parameter() 22 | : std::logic_error("Invalid parameter given") 23 | {} 24 | }; 25 | 26 | class open_trace_failure : public std::runtime_error { 27 | public: 28 | open_trace_failure() 29 | : std::runtime_error("Failure to open trace") 30 | {} 31 | }; 32 | 33 | class need_to_be_admin_failure : public std::runtime_error { 34 | public: 35 | need_to_be_admin_failure() 36 | : std::runtime_error("Need to be an admin") 37 | {} 38 | }; 39 | 40 | class could_not_find_schema : public std::runtime_error { 41 | public: 42 | could_not_find_schema() 43 | : std::runtime_error("Could not find the schema") 44 | {} 45 | 46 | could_not_find_schema(const std::string& context) 47 | : std::runtime_error(std::string("Could not find the schema: ") + context) 48 | {} 49 | }; 50 | 51 | class type_mismatch_assert : public std::runtime_error { 52 | public: 53 | type_mismatch_assert( 54 | const char* property, 55 | const char* actual, 56 | const char* requested) 57 | : std::runtime_error(std::string("Attempt to read property '") + 58 | property + "' type " + actual + " as " + requested) 59 | {} 60 | }; 61 | 62 | class no_trace_sessions_remaining : public std::runtime_error { 63 | public: 64 | no_trace_sessions_remaining() 65 | : std::runtime_error("No more trace sessions available.") 66 | {} 67 | }; 68 | 69 | class function_not_supported : public std::runtime_error { 70 | public: 71 | function_not_supported() 72 | : std::runtime_error("This function is not supported on this system.") 73 | {} 74 | }; 75 | 76 | class unexpected_error : public std::runtime_error { 77 | public: 78 | unexpected_error(ULONG status) 79 | : std::runtime_error(std::string("An unexpected error occurred: status_code=") + 80 | std::to_string(status)) 81 | {} 82 | 83 | unexpected_error(const std::string &context) 84 | : std::runtime_error(std::string("An unexpected error occurred: ") + context) 85 | {} 86 | }; 87 | 88 | inline std::string get_status_and_record_context(ULONG status, const EVENT_RECORD& record) 89 | { 90 | std::stringstream message; 91 | message << "status_code=" 92 | << status 93 | << " provider_id=" 94 | << std::to_string(record.EventHeader.ProviderId) 95 | << " event_id=" 96 | << record.EventHeader.EventDescriptor.Id; 97 | 98 | return message.str(); 99 | } 100 | 101 | /** 102 | * Checks for common ETW API error codes. 103 | */ 104 | inline void error_check_common_conditions(ULONG status) 105 | { 106 | if (status == ERROR_SUCCESS) { 107 | return; 108 | } 109 | 110 | switch (status) { 111 | case ERROR_ALREADY_EXISTS: 112 | throw krabs::trace_already_registered(); 113 | case ERROR_INVALID_PARAMETER: 114 | throw krabs::invalid_parameter(); 115 | case ERROR_ACCESS_DENIED: 116 | throw krabs::need_to_be_admin_failure(); 117 | case ERROR_NOT_FOUND: 118 | throw krabs::could_not_find_schema(); 119 | case ERROR_NO_SYSTEM_RESOURCES: 120 | throw krabs::no_trace_sessions_remaining(); 121 | case ERROR_NOT_SUPPORTED: 122 | throw krabs::function_not_supported(); 123 | default: 124 | throw krabs::unexpected_error(status); 125 | } 126 | } 127 | 128 | /** 129 | * Checks for common ETW API error codes and includes properties from the event record. 130 | */ 131 | inline void error_check_common_conditions(ULONG status, const EVENT_RECORD &record) 132 | { 133 | if (status == ERROR_SUCCESS) { 134 | return; 135 | } 136 | 137 | auto context = get_status_and_record_context(status, record); 138 | 139 | switch (status) { 140 | case ERROR_ALREADY_EXISTS: 141 | throw krabs::trace_already_registered(); 142 | case ERROR_INVALID_PARAMETER: 143 | throw krabs::invalid_parameter(); 144 | case ERROR_ACCESS_DENIED: 145 | throw krabs::need_to_be_admin_failure(); 146 | case ERROR_NOT_FOUND: 147 | throw krabs::could_not_find_schema(context); 148 | case ERROR_NO_SYSTEM_RESOURCES: 149 | throw krabs::no_trace_sessions_remaining(); 150 | case ERROR_NOT_SUPPORTED: 151 | throw krabs::function_not_supported(); 152 | default: 153 | throw krabs::unexpected_error(context); 154 | } 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /libs/krabs/krabs/filtering/comparers.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | #pragma once 5 | 6 | #include 7 | 8 | #include "../compiler_check.hpp" 9 | 10 | namespace krabs { namespace predicates { 11 | 12 | namespace comparers { 13 | 14 | // Algorithms 15 | // -------------------------------------------------------------------- 16 | 17 | /** 18 | * Iterator based equals 19 | */ 20 | template 21 | struct equals 22 | { 23 | template 24 | bool operator()(Iter1 first1, Iter1 last1, Iter2 first2, Iter2 last2) const 25 | { 26 | return std::equal(first1, last1, first2, last2, Comparer()); 27 | } 28 | }; 29 | 30 | /** 31 | * Iterator based search 32 | */ 33 | template 34 | struct contains 35 | { 36 | template 37 | bool operator()(Iter1 first1, Iter1 last1, Iter2 first2, Iter2 last2) const 38 | { 39 | // empty test range always contained, even when input range empty 40 | return first2 == last2 41 | || std::search(first1, last1, first2, last2, Comparer()) != last1; 42 | } 43 | }; 44 | 45 | /** 46 | * Iterator based starts_with 47 | */ 48 | template 49 | struct starts_with 50 | { 51 | template 52 | bool operator()(Iter1 first1, Iter1 last1, Iter2 first2, Iter2 last2) const 53 | { 54 | const auto first_nonequal = std::mismatch(first1, last1, first2, last2, Comparer()); 55 | return first_nonequal.second == last2; 56 | } 57 | }; 58 | 59 | /** 60 | * Iterator based ends_with 61 | */ 62 | template 63 | struct ends_with 64 | { 65 | template 66 | bool operator()(Iter1 first1, Iter1 last1, Iter2 first2, Iter2 last2) const 67 | { 68 | const auto dist1 = std::distance(first1, last1); 69 | const auto dist2 = std::distance(first2, last2); 70 | 71 | if (dist2 > dist1) 72 | return false; 73 | 74 | const auto suffix_begin = std::next(first1, dist1 - dist2); 75 | return std::equal(suffix_begin, last1, first2, last2, Comparer()); 76 | } 77 | }; 78 | 79 | // Custom Comparison 80 | // -------------------------------------------------------------------- 81 | 82 | template 83 | struct iequal_to 84 | { 85 | bool operator()(const T& a, const T& b) const 86 | { 87 | static_assert(sizeof(T) == 0, 88 | "iequal_to needs a specialized overload for type"); 89 | } 90 | }; 91 | 92 | /** 93 | * 94 | * Binary predicate for comparing two wide characters case insensitively 95 | * Does not handle all locales 96 | * 97 | */ 98 | template <> 99 | struct iequal_to 100 | { 101 | bool operator()(const wchar_t& a, const wchar_t& b) const 102 | { 103 | return towupper(a) == towupper(b); 104 | } 105 | }; 106 | 107 | /** 108 | * 109 | * Binary predicate for comparing two characters case insensitively 110 | * Does not handle all locales 111 | * 112 | */ 113 | template <> 114 | struct iequal_to 115 | { 116 | bool operator()(const char& a, const char& b) const 117 | { 118 | return toupper(a) == toupper(b); 119 | } 120 | }; 121 | 122 | } /* namespace comparers */ 123 | 124 | } /* namespace predicates */ } /* namespace krabs */ -------------------------------------------------------------------------------- /libs/krabs/krabs/filtering/event_filter.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | #pragma once 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "../compiler_check.hpp" 12 | #include "../trace_context.hpp" 13 | 14 | namespace krabs { namespace testing { 15 | class event_filter_proxy; 16 | } /* namespace testing */} /* namespace krabs */ 17 | 18 | namespace krabs { namespace details { 19 | template class base_provider; 20 | } /* namespace details */} /* namespace krabs */ 21 | 22 | 23 | namespace krabs { 24 | 25 | typedef void(*c_provider_callback)(const EVENT_RECORD &, const krabs::trace_context &); 26 | typedef void(*c_provider_error_callback)(const EVENT_RECORD&, const std::string&); 27 | typedef std::function provider_event_callback; 28 | typedef std::function provider_error_callback; 29 | typedef std::function filter_predicate; 30 | 31 | template class provider; 32 | 33 | /** 34 | * 35 | * Use this to provide event filtering before an event bubbles to 36 | * specific callbacks. 37 | * 38 | * 39 | * Each event_filter has a single predicate (which can do complicated 40 | * checks and logic on the event). All callbacks registered under the 41 | * filter are invoked only if the predicate returns true for a given 42 | * event. 43 | * 44 | */ 45 | class event_filter { 46 | public: 47 | 48 | /** 49 | * 50 | * Constructs an event_filter that applies the given predicate to all 51 | * events. 52 | * 53 | */ 54 | event_filter(filter_predicate predicate); 55 | 56 | /** 57 | * 58 | * Constructs an event_filter that applies event id filtering by event_id 59 | * which will be added to list of filtered event ids in ETW API. 60 | * This way is more effective from performance point of view. 61 | * Given optional predicate will be applied to ETW API filtered results 62 | * 63 | */ 64 | event_filter(unsigned short event_id, filter_predicate predicate=nullptr); 65 | 66 | /** 67 | * 68 | * Constructs an event_filter that applies event id filtering by event_id 69 | * which will be added to list of filtered event ids in ETW API. 70 | * This way is more effective from performance point of view. 71 | * Given optional predicate will be applied to ETW API filtered results 72 | * 73 | */ 74 | event_filter(std::vector event_ids, filter_predicate predicate = nullptr); 75 | 76 | /** 77 | * 78 | * Adds a function to call when an event for this filter is fired. 79 | * 80 | */ 81 | void add_on_event_callback(c_provider_callback callback); 82 | 83 | template 84 | void add_on_event_callback(U &callback); 85 | 86 | template 87 | void add_on_event_callback(const U &callback); 88 | 89 | /** 90 | * 91 | * Adds a function to call when an error occurs. 92 | * 93 | */ 94 | void add_on_error_callback(c_provider_error_callback callback); 95 | 96 | template 97 | void add_on_error_callback(U& callback); 98 | 99 | template 100 | void add_on_error_callback(const U& callback); 101 | 102 | const std::vector& provider_filter_event_ids() const 103 | { 104 | return provider_filter_event_ids_; 105 | } 106 | 107 | private: 108 | 109 | /** 110 | * 111 | * Called when an event occurs, forwards to callbacks if the event 112 | * satisfies the predicate. 113 | * 114 | */ 115 | void on_event(const EVENT_RECORD &record, const krabs::trace_context &trace_context) const; 116 | 117 | /** 118 | * 119 | * Called when an error occurs, forwards to the error callback 120 | * 121 | */ 122 | void on_error(const EVENT_RECORD& record, const std::string& error_message) const; 123 | 124 | private: 125 | std::deque event_callbacks_; 126 | std::deque error_callbacks_; 127 | filter_predicate predicate_{ nullptr }; 128 | std::vector provider_filter_event_ids_; 129 | 130 | private: 131 | template 132 | friend class details::base_provider; 133 | 134 | friend class krabs::testing::event_filter_proxy; 135 | }; 136 | 137 | // Implementation 138 | // ------------------------------------------------------------------------ 139 | 140 | inline event_filter::event_filter(filter_predicate predicate) 141 | : predicate_(predicate) 142 | {} 143 | 144 | inline event_filter::event_filter(std::vector event_ids, filter_predicate predicate/*=nullptr*/) 145 | : provider_filter_event_ids_{ event_ids }, 146 | predicate_(predicate) 147 | {} 148 | 149 | inline event_filter::event_filter(unsigned short event_id, filter_predicate predicate/*=nullptr*/) 150 | : provider_filter_event_ids_{ event_id }, 151 | predicate_(predicate) 152 | {} 153 | 154 | inline void event_filter::add_on_event_callback(c_provider_callback callback) 155 | { 156 | // C function pointers don't interact well with std::ref, so we 157 | // overload to take care of this scenario. 158 | event_callbacks_.push_back(callback); 159 | } 160 | 161 | template 162 | void event_filter::add_on_event_callback(U &callback) 163 | { 164 | // std::function copies its argument -- because our callbacks list 165 | // is a list of std::function, this causes problems when a user 166 | // intended for their particular instance to be called. 167 | // std::ref lets us get around this and point to a specific instance 168 | // that they handed us. 169 | event_callbacks_.push_back(std::ref(callback)); 170 | } 171 | 172 | template 173 | void event_filter::add_on_event_callback(const U &callback) 174 | { 175 | // This is where temporaries bind to. Temporaries can't be wrapped in 176 | // a std::ref because they'll go away very quickly. We are forced to 177 | // actually copy these. 178 | event_callbacks_.push_back(callback); 179 | } 180 | 181 | inline void event_filter::add_on_error_callback(c_provider_error_callback callback) 182 | { 183 | // C function pointers don't interact well with std::ref, so we 184 | // overload to take care of this scenario. 185 | error_callbacks_.push_back(callback); 186 | } 187 | 188 | template 189 | void event_filter::add_on_error_callback(U& callback) 190 | { 191 | // std::function copies its argument -- because our callbacks list 192 | // is a list of std::function, this causes problems when a user 193 | // intended for their particular instance to be called. 194 | // std::ref lets us get around this and point to a specific instance 195 | // that they handed us. 196 | error_callbacks_.push_back(std::ref(callback)); 197 | } 198 | 199 | template 200 | void event_filter::add_on_error_callback(const U& callback) 201 | { 202 | // This is where temporaries bind to. Temporaries can't be wrapped in 203 | // a std::ref because they'll go away very quickly. We are forced to 204 | // actually copy these. 205 | error_callbacks_.push_back(callback); 206 | } 207 | 208 | inline void event_filter::on_event(const EVENT_RECORD &record, const krabs::trace_context &trace_context) const 209 | { 210 | if (event_callbacks_.empty()) { 211 | return; 212 | } 213 | 214 | try 215 | { 216 | if (predicate_ != nullptr && !predicate_(record, trace_context)) { 217 | return; 218 | } 219 | 220 | for (auto& callback : event_callbacks_) { 221 | callback(record, trace_context); 222 | } 223 | } 224 | catch (const krabs::could_not_find_schema& ex) 225 | { 226 | // this occurs when a predicate is applied to an event for which 227 | // no schema exists. instead of allowing the exception to halt 228 | // the entire trace, send a notification to the filter's error callback 229 | for (auto& error_callback : error_callbacks_) { 230 | error_callback(record, ex.what()); 231 | } 232 | } 233 | } 234 | } /* namespace krabs */ 235 | -------------------------------------------------------------------------------- /libs/krabs/krabs/filtering/view_adapters.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | #pragma once 5 | 6 | #include 7 | 8 | #include "../compiler_check.hpp" 9 | #include "../parser.hpp" 10 | 11 | namespace krabs { namespace predicates { 12 | 13 | namespace adapters { 14 | 15 | /** 16 | * View adapter for counted_string strings 17 | */ 18 | struct counted_string 19 | { 20 | using value_type = krabs::counted_string::value_type; 21 | using const_iterator = krabs::counted_string::const_iterator; 22 | 23 | collection_view operator()(const property_info& propInfo) const 24 | { 25 | auto cs_ptr = reinterpret_cast(propInfo.pPropertyIndex_); 26 | return krabs::view(cs_ptr->string(), cs_ptr->length()); 27 | } 28 | }; 29 | 30 | /** 31 | * View adapter for fixed width and null-terminated strings 32 | */ 33 | template 34 | struct generic_string 35 | { 36 | using value_type = ElemT; 37 | using const_iterator = const value_type*; 38 | 39 | collection_view operator()(const property_info& propInfo) const 40 | { 41 | auto pString = reinterpret_cast(propInfo.pPropertyIndex_); 42 | auto length = get_string_content_length(pString, propInfo.length_); 43 | 44 | return krabs::view(pString, length); 45 | } 46 | }; 47 | 48 | } /* namespace adapters */ 49 | 50 | } /* namespace predicates */ } /* namespace krabs */ -------------------------------------------------------------------------------- /libs/krabs/krabs/guid.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | #pragma once 5 | 6 | #ifndef WIN32_LEAN_AND_MEAN 7 | #define WIN32_LEAN_AND_MEAN 8 | #endif 9 | 10 | #include 11 | #include 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #include "compiler_check.hpp" 20 | 21 | namespace krabs { 22 | 23 | /** 24 | * Represents a GUID, allowing simplified construction from a string or 25 | * Windows GUID structure. 26 | * 27 | */ 28 | class guid { 29 | public: 30 | guid(GUID guid); 31 | guid(const std::wstring &guid); 32 | 33 | bool operator==(const guid &rhs) const; 34 | bool operator==(const GUID &rhs) const; 35 | 36 | bool operator<(const guid &rhs) const; 37 | bool operator<(const GUID &rhs) const; 38 | 39 | operator GUID() const; 40 | operator const GUID*() const; 41 | 42 | /** 43 | * Constructs a new random guid. 44 | * 45 | */ 46 | static inline guid random_guid(); 47 | 48 | private: 49 | GUID guid_; 50 | 51 | friend struct std::hash; 52 | }; 53 | 54 | /** 55 | * Helper functions for parsing GUID's. 56 | * 57 | */ 58 | class guid_parser { 59 | // Implementing in Krabs instead of Lobsters so that we can test it in unmanaged code. 60 | private: 61 | // Number of characters in the UUID's 8-4-4-4-12 string format. 62 | static const size_t UUID_STRING_LENGTH = 36; 63 | 64 | static const unsigned char DELIMITER = '-'; 65 | 66 | // Expected character positions of runs of hex digits in 8-4-4-4-12 format, e.g. 67 | // 00000000-0000-0000-0000-000000000000 68 | // Names correspond to struct members of GUID. 69 | static const size_t STR_POSITION_DATA1 = 0; 70 | static const size_t STR_POSITION_DATA2 = 8 + 1; 71 | static const size_t STR_POSITION_DATA3 = STR_POSITION_DATA2 + 4 + 1; 72 | static const size_t STR_POSITION_DATA4_PART1 = STR_POSITION_DATA3 + 4 + 1; 73 | static const size_t STR_POSITION_DATA4_PART2 = STR_POSITION_DATA4_PART1 + 4 + 1; 74 | 75 | public: 76 | // str_input must have at least 2 valid chars in allocated buffer 77 | static bool hex_octet_to_byte(const char* str_input, unsigned char& byte_output); 78 | // str_input must have at least 2*sizeof(T) valid chars in allocated buffer 79 | template 80 | static bool hex_string_to_number(const char* str_input, T& int_output); 81 | // str_input must have at least 2*byte_count valid chars in allocated buffer, 82 | // and byte_output must have at least byte_count bytes in allocated buffer 83 | static bool hex_string_to_bytes(const char* str_input, unsigned char* byte_output, size_t byte_count); 84 | 85 | /** 86 | * Parses GUID of "D" format. For example, the nil GUID would be "00000000-0000-0000-0000-000000000000". 87 | * See: https://docs.microsoft.com/en-us/dotnet/api/system.guid.tostring?view=netframework-4.8 88 | * 89 | * (str) must have at least (length) valid characters for memory safety. A null terminator is not 90 | * required. Instead, (length) is used for the bounds check. 91 | * 92 | * Returns the parsed GUID. Throws a std::runtime_error if there is a bounds error or format error. 93 | * 94 | * This function is for performance, to help deal with container ID extended data, which has no null 95 | * terminator, which would force us to clone the data to append a null terminator in order to use 96 | * existing GUID parsing functions. 97 | * 98 | */ 99 | static GUID parse_guid(const char* str, unsigned int length); 100 | }; 101 | 102 | // Implementation 103 | // ------------------------------------------------------------------------ 104 | 105 | inline guid::guid(GUID guid) 106 | : guid_(guid) 107 | {} 108 | 109 | inline guid::guid(const std::wstring &guid) 110 | { 111 | HRESULT hr = CLSIDFromString(guid.c_str(), &guid_); 112 | if (FAILED(hr)) { 113 | #pragma warning(push) 114 | #pragma warning(disable: 4244) // narrowing guid wchar_t to char for this error message 115 | std::string guidStr(guid.begin(), guid.end()); 116 | #pragma warning(pop) 117 | std::stringstream stream; 118 | stream << "Error in constructing guid from string ("; 119 | stream << guidStr; 120 | stream << "), hr = 0x"; 121 | stream << std::hex << hr; 122 | throw std::runtime_error(stream.str()); 123 | } 124 | } 125 | 126 | inline bool guid::operator==(const guid &rhs) const 127 | { 128 | return (0 == memcmp(&guid_, &rhs.guid_, sizeof(GUID))); 129 | } 130 | 131 | inline bool guid::operator==(const GUID &rhs) const 132 | { 133 | return (0 == memcmp(&guid_, &rhs, sizeof(GUID))); 134 | } 135 | 136 | inline bool guid::operator<(const guid &rhs) const 137 | { 138 | return (memcmp(&guid_, &rhs.guid_, sizeof(guid_)) < 0); 139 | } 140 | 141 | inline bool guid::operator<(const GUID &rhs) const 142 | { 143 | return (memcmp(&guid_, &rhs, sizeof(guid_)) < 0); 144 | } 145 | 146 | inline guid::operator GUID() const 147 | { 148 | return guid_; 149 | } 150 | 151 | inline guid::operator const GUID*() const 152 | { 153 | return &guid_; 154 | } 155 | 156 | inline guid guid::random_guid() 157 | { 158 | GUID tmpGuid; 159 | CoCreateGuid(&tmpGuid); 160 | return guid(tmpGuid); 161 | } 162 | 163 | struct CoTaskMemDeleter { 164 | void operator()(wchar_t *mem) { 165 | CoTaskMemFree(mem); 166 | } 167 | }; 168 | 169 | inline bool guid_parser::hex_octet_to_byte(const char* str_input, unsigned char& byte_output) 170 | { 171 | // Accepts chars '0' through '9' (0x30 to 0x39), 172 | // 'A' through 'F' (0x41 to 0x46) 173 | // 'a' through 'f' (0x61 to 0x66) 174 | 175 | // Narrow the value later, for safety checking. 176 | auto value = 0; 177 | // most significant digit in the octet 178 | auto msd = str_input[0]; 179 | // least significant digit in the octet 180 | auto lsd = str_input[1]; 181 | 182 | if (msd >= '0' && msd <= '9') 183 | { 184 | value |= ((int)msd & 0x0F) << 4; 185 | } 186 | else if ((msd >= 'A' && msd <= 'F') || (msd >= 'a' && msd <= 'f')) 187 | { 188 | value |= (((int)msd & 0x0F) + 9) << 4; 189 | } 190 | else 191 | { 192 | return false; 193 | } 194 | 195 | if (lsd >= '0' && lsd <= '9') 196 | { 197 | value |= ((int)lsd & 0x0F); 198 | } 199 | else if ((lsd >= 'A' && lsd <= 'F') || (lsd >= 'a' && lsd <= 'f')) 200 | { 201 | value |= (((int)lsd & 0x0F) + 9); 202 | } 203 | else 204 | { 205 | return false; 206 | } 207 | 208 | assert(value >= 0 && value <= UCHAR_MAX); 209 | byte_output = static_cast(value); 210 | return true; 211 | } 212 | 213 | template 214 | bool guid_parser::hex_string_to_number(const char* str_input, T& int_output) 215 | { 216 | auto byte_count = sizeof(T); 217 | T value = 0; 218 | unsigned char byte = 0; 219 | 220 | for (size_t i = 0; i < byte_count; i++) 221 | { 222 | if (!guid_parser::hex_octet_to_byte(str_input + i * 2, byte)) 223 | { 224 | return false; 225 | } 226 | 227 | value = (value << 8) | static_cast(byte); 228 | } 229 | 230 | int_output = value; 231 | return true; 232 | } 233 | 234 | inline bool guid_parser::hex_string_to_bytes(const char* str_input, unsigned char* byte_output, size_t byte_count) 235 | { 236 | for (size_t i = 0; i < byte_count; i++) 237 | { 238 | if (!hex_octet_to_byte(str_input + (i * 2), byte_output[i])) 239 | { 240 | return false; 241 | } 242 | } 243 | 244 | return true; 245 | } 246 | 247 | /** 248 | * Parses GUID of "D" format. For example, the nil GUID would be "00000000-0000-0000-0000-000000000000". 249 | * See: https://docs.microsoft.com/en-us/dotnet/api/system.guid.tostring?view=netframework-4.8 250 | * 251 | * (str) must have at least (length) valid characters for memory safety. A null terminator is not 252 | * required. Instead, (length) is used for the bounds check. 253 | * 254 | * Returns the parsed GUID. Throws a std::runtime_error if there is a bounds error or format error. 255 | * 256 | * This function is for performance, to help deal with container ID extended data, which has no null 257 | * terminator, which would force us to clone the data to append a null terminator in order to use 258 | * existing GUID parsing functions. 259 | * 260 | */ 261 | inline GUID guid_parser::parse_guid(const char* str, unsigned int length) 262 | { 263 | if (length != UUID_STRING_LENGTH) 264 | { 265 | std::stringstream message; 266 | message << "Input data has incorrect length. Expected " 267 | << UUID_STRING_LENGTH 268 | << ", got " 269 | << length; 270 | throw std::runtime_error(message.str()); 271 | } 272 | 273 | GUID guid = { 0 }; 274 | 275 | // Check that hyphens are in expected places as a formatting issue. 276 | if (str[STR_POSITION_DATA2 - 1] != DELIMITER || 277 | str[STR_POSITION_DATA3 - 1] != DELIMITER || 278 | str[STR_POSITION_DATA4_PART1 - 1] != DELIMITER || 279 | str[STR_POSITION_DATA4_PART2 - 1] != DELIMITER) 280 | { 281 | throw std::runtime_error("Missing a hyphen where one was expected."); 282 | } 283 | 284 | // Use from_hex_string for Data1, Data2, and Data3 because of endianness of the data 285 | // Use hex_string_to_bytes for Data4's array elements because it's byte by byte instead 286 | auto success = guid_parser::hex_string_to_number(str + STR_POSITION_DATA1, guid.Data1) 287 | && guid_parser::hex_string_to_number(str + STR_POSITION_DATA2, guid.Data2) 288 | && guid_parser::hex_string_to_number(str + STR_POSITION_DATA3, guid.Data3) 289 | && guid_parser::hex_string_to_bytes(str + STR_POSITION_DATA4_PART1, reinterpret_cast(&guid.Data4[0]), 2) 290 | && guid_parser::hex_string_to_bytes(str + STR_POSITION_DATA4_PART2, reinterpret_cast(&guid.Data4[2]), 6); 291 | 292 | if (!success) 293 | { 294 | throw std::runtime_error("GUID string contains non-hex digits where hex digits are expected."); 295 | } 296 | 297 | return guid; 298 | } 299 | } 300 | 301 | namespace std 302 | { 303 | /* 304 | * Converts a krabs GUID to a wide string 305 | */ 306 | inline std::wstring to_wstring(const krabs::guid& guid) 307 | { 308 | wchar_t* guidString; 309 | HRESULT hr = StringFromCLSID(guid, &guidString); 310 | 311 | if (FAILED(hr)) throw std::bad_alloc(); 312 | 313 | std::unique_ptr managed(guidString); 314 | 315 | return { managed.get() }; 316 | } 317 | 318 | /* 319 | * Converts a Windows GUID to a C string 320 | */ 321 | inline std::string to_string(const GUID& guid) 322 | { 323 | char guid_string[37]; // 32 hex chars + 4 hyphens + null terminator 324 | snprintf( 325 | guid_string, sizeof(guid_string), 326 | "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", 327 | guid.Data1, guid.Data2, guid.Data3, 328 | guid.Data4[0], guid.Data4[1], guid.Data4[2], 329 | guid.Data4[3], guid.Data4[4], guid.Data4[5], 330 | guid.Data4[6], guid.Data4[7]); 331 | return guid_string; 332 | } 333 | 334 | template<> 335 | struct std::hash 336 | { 337 | size_t operator()(const krabs::guid& guid) const 338 | { 339 | // This algorithm comes from .NET's reference source for Guid.GetHashCode() 340 | return guid.guid_.Data1 ^ 341 | ((guid.guid_.Data2 << 16) | guid.guid_.Data3 ) ^ 342 | ((guid.guid_.Data4[2] << 24) | guid.guid_.Data4[7]); 343 | } 344 | }; 345 | } 346 | -------------------------------------------------------------------------------- /libs/krabs/krabs/kernel_guids.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | #pragma once 5 | 6 | #include 7 | 8 | #include "compiler_check.hpp" 9 | 10 | namespace krabs { namespace guids { 11 | 12 | DEFINE_GUID ( /* 45d8cccd-539f-4b72-a8b7-5c683142609a */ 13 | alpc, 14 | 0x45d8cccd, 15 | 0x539f, 16 | 0x4b72, 17 | 0xa8, 0xb7, 0x5c, 0x68, 0x31, 0x42, 0x60, 0x9a 18 | ); 19 | 20 | DEFINE_GUID ( /* 13976d09-a327-438c-950b-7f03192815c7 */ 21 | debug, 22 | 0x13976d09, 23 | 0xa327, 24 | 0x438c, 25 | 0x95, 0x0b, 0x7f, 0x03, 0x19, 0x28, 0x15, 0xc7 26 | ); 27 | 28 | DEFINE_GUID ( /* 3d6fa8d4-fe05-11d0-9dda-00c04fd7ba7c */ 29 | disk_io, 30 | 0x3d6fa8d4, 31 | 0xfe05, 32 | 0x11d0, 33 | 0x9d, 0xda, 0x00, 0xc0, 0x4f, 0xd7, 0xba, 0x7c 34 | ); 35 | 36 | DEFINE_GUID ( /* 01853a65-418f-4f36-aefc-dc0f1d2fd235 */ 37 | event_trace_config, 38 | 0x01853a65, 39 | 0x418f, 40 | 0x4f36, 41 | 0xae, 0xfc, 0xdc, 0x0f, 0x1d, 0x2f, 0xd2, 0x35 42 | ); 43 | 44 | DEFINE_GUID ( /* 90cbdc39-4a3e-11d1-84f4-0000f80464e3 */ 45 | file_io, 46 | 0x90cbdc39, 47 | 0x4a3e, 48 | 0x11d1, 49 | 0x84, 0xf4, 0x00, 0x00, 0xf8, 0x04, 0x64, 0xe3 50 | ); 51 | 52 | DEFINE_GUID ( /* 2cb15d1d-5fc1-11d2-abe1-00a0c911f518 */ 53 | image_load, 54 | 0x2cb15d1d, 55 | 0x5fc1, 56 | 0x11d2, 57 | 0xab, 0xe1, 0x00, 0xa0, 0xc9, 0x11, 0xf5, 0x18 58 | ); 59 | 60 | DEFINE_GUID ( /* 3d6fa8d3-fe05-11d0-9dda-00c04fd7ba7c */ 61 | page_fault, 62 | 0x3d6fa8d3, 63 | 0xfe05, 64 | 0x11d0, 65 | 0x9d, 0xda, 0x00, 0xc0, 0x4f, 0xd7, 0xba, 0x7c 66 | ); 67 | 68 | DEFINE_GUID ( /* ce1dbfb4-137e-4da6-87b0-3f59aa102cbc */ 69 | perf_info, 70 | 0xce1dbfb4, 71 | 0x137e, 72 | 0x4da6, 73 | 0x87, 0xb0, 0x3f, 0x59, 0xaa, 0x10, 0x2c, 0xbc 74 | ); 75 | 76 | DEFINE_GUID ( /* 3d6fa8d0-fe05-11d0-9dda-00c04fd7ba7c */ 77 | process, 78 | 0x3d6fa8d0, 79 | 0xfe05, 80 | 0x11d0, 81 | 0x9d, 0xda, 0x00, 0xc0, 0x4f, 0xd7, 0xba, 0x7c 82 | ); 83 | 84 | DEFINE_GUID ( /* AE53722E-C863-11d2-8659-00C04FA321A1 */ 85 | registry, 86 | 0xae53722e, 87 | 0xc863, 88 | 0x11d2, 89 | 0x86, 0x59, 0x0, 0xc0, 0x4f, 0xa3, 0x21, 0xa1 90 | ); 91 | 92 | DEFINE_GUID ( /* d837ca92-12b9-44a5-ad6a-3a65b3578aa8 */ 93 | split_io, 94 | 0xd837ca92, 95 | 0x12b9, 96 | 0x44a5, 97 | 0xad, 0x6a, 0x3a, 0x65, 0xb3, 0x57, 0x8a, 0xa8 98 | ); 99 | 100 | DEFINE_GUID ( /* 9a280ac0-c8e0-11d1-84e2-00c04fb998a2 */ 101 | tcp_ip, 102 | 0x9a280ac0, 103 | 0xc8e0, 104 | 0x11d1, 105 | 0x84, 0xe2, 0x00, 0xc0, 0x4f, 0xb9, 0x98, 0xa2 106 | ); 107 | 108 | DEFINE_GUID ( /* 3d6fa8d1-fe05-11d0-9dda-00c04fd7ba7c */ 109 | thread, 110 | 0x3d6fa8d1, 111 | 0xfe05, 112 | 0x11d0, 113 | 0x9d, 0xda, 0x00, 0xc0, 0x4f, 0xd7, 0xba, 0x7c 114 | ); 115 | 116 | DEFINE_GUID ( /* bf3a50c5-a9c9-4988-a005-2df0b7c80f80 */ 117 | udp_ip, 118 | 0xbf3a50c5, 119 | 0xa9c9, 120 | 0x4988, 121 | 0xa0, 0x05, 0x2d, 0xf0, 0xb7, 0xc8, 0x0f, 0x80 122 | ); 123 | 124 | DEFINE_GUID ( /* 9e814aad-3204-11d2-9a82-006008a86939 */ 125 | system_trace, 126 | 0x9e814aad, 127 | 0x3204, 128 | 0x11d2, 129 | 0x9a, 0x82, 0x00, 0x60, 0x08, 0xa8, 0x69, 0x39); 130 | 131 | DEFINE_GUID( /* 89497f50-effe-4440-8cf2-ce6b1cdcaca7 */ 132 | ob_trace, 133 | 0x89497f50, 134 | 0xeffe, 135 | 0x4440, 136 | 0x8c, 0xf2, 0xce, 0x6b, 0x1c, 0xdc, 0xac, 0xa7); 137 | 138 | DEFINE_GUID( /* 0268a8b6-74fd-4302-9dd0-6e8f1795c0cf */ 139 | pool_trace, 140 | 0x0268a8b6, 141 | 0x74fd, 142 | 0x4302, 143 | 0x9d, 0xd0, 0x6e, 0x8f, 0x17, 0x95, 0xc0, 0xcf); 144 | 145 | DEFINE_GUID( /* 68fdd900-4a3e-11d1-84f4-0000f80464e3 */ 146 | event_trace, 147 | 0x68fdd900, 148 | 0x4a3e, 149 | 0x11d1, 150 | 0x84, 0xf4, 0x00, 0x00, 0xf8, 0x04, 0x64, 0xe3); 151 | 152 | DEFINE_GUID( /* 6a399ae0-4bc6-4de9-870b-3657f8947e7e */ 153 | lost_event, 154 | 0x6a399ae0, 155 | 0x4bc6, 156 | 0x4de9, 157 | 0x87, 0x0b, 0x36, 0x57, 0xf8, 0x94, 0x7e, 0x7e); 158 | 159 | DEFINE_GUID( /* 9aec974b-5b8e-4118-9b92-3186d8002ce5 */ 160 | ums_event, 161 | 0x9aec974b, 162 | 0x5b8e, 163 | 0x4118, 164 | 0x9b, 0x92, 0x31, 0x86, 0xd8, 0x00, 0x2c, 0xe5); 165 | 166 | DEFINE_GUID( /* def2fe46-7bd6-4b80-bd94-f57fe20d0ce3 */ 167 | stack_walk, 168 | 0xdef2fe46, 169 | 0x7bd6, 170 | 0x4b80, 171 | 0xbd, 0x94, 0xf5, 0x7f, 0xe2, 0x0d, 0x0c, 0xe3); 172 | 173 | DEFINE_GUID( /* e43445e0-0903-48c3-b878-ff0fccebdd04 */ 174 | power, 175 | 0xe43445e0, 176 | 0x0903, 177 | 0x48c3, 178 | 0xb8, 0x78, 0xff, 0x0f, 0xcc, 0xeb, 0xdd, 0x04); 179 | 180 | DEFINE_GUID( /* f8f10121-b617-4a56-868b-9df1b27fe32c */ 181 | mmcss_trace, 182 | 0xf8f10121, 183 | 0xb617, 184 | 0x4a56, 185 | 0x86, 0x8b, 0x9d, 0xf1, 0xb2, 0x7f, 0xe3, 0x2c); 186 | 187 | DEFINE_GUID( /* 3b9c9951-3480-4220-9377-9c8e5184f5cd */ 188 | rundown, 189 | 0x3b9c9951, 190 | 0x3480, 191 | 0x4220, 192 | 0x93, 0x77, 0x9c, 0x8e, 0x51, 0x84, 0xf5, 0xcd); 193 | 194 | } /* namespace guids */ } /* namespace krabs */ 195 | -------------------------------------------------------------------------------- /libs/krabs/krabs/kernel_providers.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | #pragma once 5 | 6 | #include "compiler_check.hpp" 7 | #include "kernel_guids.hpp" 8 | #include "perfinfo_groupmask.hpp" 9 | #include "provider.hpp" 10 | 11 | #define INITGUID 12 | #include 13 | 14 | namespace krabs { namespace kernel { 15 | 16 | #define CREATE_CONVENIENCE_KERNEL_PROVIDER(__name__, __value__, __guid__) \ 17 | struct __name__ : public krabs::kernel_provider \ 18 | { \ 19 | __name__() \ 20 | : krabs::kernel_provider(__value__, __guid__) \ 21 | {} \ 22 | }; 23 | 24 | #define CREATE_CONVENIENCE_KERNEL_PROVIDER_MASK(__name__, __guid__, __mask__) \ 25 | struct __name__ : public krabs::kernel_provider \ 26 | { \ 27 | __name__() \ 28 | : krabs::kernel_provider(__guid__, __mask__) \ 29 | {} \ 30 | }; 31 | 32 | /** 33 | * A provider that enables ALPC events. 34 | */ 35 | CREATE_CONVENIENCE_KERNEL_PROVIDER( 36 | alpc_provider, 37 | EVENT_TRACE_FLAG_ALPC, 38 | krabs::guids::alpc); 39 | 40 | /** 41 | * A provider that enables context switch events. 42 | */ 43 | CREATE_CONVENIENCE_KERNEL_PROVIDER( 44 | context_switch_provider, 45 | EVENT_TRACE_FLAG_CSWITCH, 46 | krabs::guids::thread); 47 | 48 | /** 49 | * A provider that enables debug print events. 50 | */ 51 | CREATE_CONVENIENCE_KERNEL_PROVIDER( 52 | debug_print_provider, 53 | EVENT_TRACE_FLAG_DBGPRINT, 54 | krabs::guids::debug); 55 | 56 | /** 57 | * A provider that enables file I/O name events. 58 | */ 59 | CREATE_CONVENIENCE_KERNEL_PROVIDER( 60 | disk_file_io_provider, 61 | EVENT_TRACE_FLAG_DISK_FILE_IO, 62 | krabs::guids::file_io); 63 | 64 | /** 65 | * A provider that enables disk I/O completion events. 66 | */ 67 | CREATE_CONVENIENCE_KERNEL_PROVIDER( 68 | disk_io_provider, 69 | EVENT_TRACE_FLAG_DISK_IO, 70 | krabs::guids::disk_io); 71 | 72 | /** 73 | * A provider that enables disk I/O start events. 74 | */ 75 | CREATE_CONVENIENCE_KERNEL_PROVIDER( 76 | disk_init_io_provider, 77 | EVENT_TRACE_FLAG_DISK_IO_INIT, 78 | krabs::guids::disk_io); 79 | 80 | /** 81 | * A provider that enables file I/O completion events. 82 | */ 83 | CREATE_CONVENIENCE_KERNEL_PROVIDER( 84 | file_io_provider, 85 | EVENT_TRACE_FLAG_FILE_IO, 86 | krabs::guids::file_io); 87 | 88 | /** 89 | * A provider that enables file I/O start events. 90 | */ 91 | CREATE_CONVENIENCE_KERNEL_PROVIDER( 92 | file_init_io_provider, 93 | EVENT_TRACE_FLAG_FILE_IO_INIT, 94 | krabs::guids::file_io); 95 | 96 | /** 97 | * A provider that enables thread dispatch events. 98 | */ 99 | CREATE_CONVENIENCE_KERNEL_PROVIDER( 100 | thread_dispatch_provider, 101 | EVENT_TRACE_FLAG_DISPATCHER, 102 | krabs::guids::thread); 103 | 104 | /** 105 | * A provider that enables device deferred procedure call events. 106 | */ 107 | CREATE_CONVENIENCE_KERNEL_PROVIDER( 108 | dpc_provider, 109 | EVENT_TRACE_FLAG_DPC, 110 | krabs::guids::perf_info); 111 | 112 | /** 113 | * A provider that enables driver events. 114 | */ 115 | CREATE_CONVENIENCE_KERNEL_PROVIDER( 116 | driver_provider, 117 | EVENT_TRACE_FLAG_DRIVER, 118 | krabs::guids::disk_io); 119 | 120 | /** 121 | * A provider that enables image load events. 122 | */ 123 | CREATE_CONVENIENCE_KERNEL_PROVIDER( 124 | image_load_provider, 125 | EVENT_TRACE_FLAG_IMAGE_LOAD, 126 | krabs::guids::image_load); 127 | 128 | /** 129 | * A provider that enables interrupt events. 130 | */ 131 | CREATE_CONVENIENCE_KERNEL_PROVIDER( 132 | interrupt_provider, 133 | EVENT_TRACE_FLAG_INTERRUPT, 134 | krabs::guids::perf_info); 135 | 136 | /** 137 | * A provider that enables memory hard fault events. 138 | */ 139 | CREATE_CONVENIENCE_KERNEL_PROVIDER( 140 | memory_hard_fault_provider, 141 | EVENT_TRACE_FLAG_MEMORY_HARD_FAULTS, 142 | krabs::guids::page_fault); 143 | 144 | /** 145 | * A provider that enables memory page fault events. 146 | */ 147 | CREATE_CONVENIENCE_KERNEL_PROVIDER( 148 | memory_page_fault_provider, 149 | EVENT_TRACE_FLAG_MEMORY_PAGE_FAULTS, 150 | krabs::guids::page_fault); 151 | 152 | /** 153 | * A provider that enables network tcp/ip events. 154 | */ 155 | CREATE_CONVENIENCE_KERNEL_PROVIDER( 156 | network_tcpip_provider, 157 | EVENT_TRACE_FLAG_NETWORK_TCPIP, 158 | krabs::guids::tcp_ip); 159 | 160 | /** 161 | * A provider that enables process events. 162 | */ 163 | CREATE_CONVENIENCE_KERNEL_PROVIDER( 164 | process_provider, 165 | EVENT_TRACE_FLAG_PROCESS, 166 | krabs::guids::process); 167 | 168 | /** 169 | * A provider that enables process counter events. 170 | */ 171 | CREATE_CONVENIENCE_KERNEL_PROVIDER( 172 | process_counter_provider, 173 | EVENT_TRACE_FLAG_PROCESS_COUNTERS, 174 | krabs::guids::process); 175 | 176 | /** 177 | * A provider that enables profiling events. 178 | */ 179 | CREATE_CONVENIENCE_KERNEL_PROVIDER( 180 | profile_provider, 181 | EVENT_TRACE_FLAG_PROFILE, 182 | krabs::guids::perf_info); 183 | 184 | /** 185 | * A provider that enables registry events. 186 | */ 187 | CREATE_CONVENIENCE_KERNEL_PROVIDER( 188 | registry_provider, 189 | EVENT_TRACE_FLAG_REGISTRY, 190 | krabs::guids::registry); 191 | 192 | /** 193 | * A provider that enables split I/O events. 194 | */ 195 | CREATE_CONVENIENCE_KERNEL_PROVIDER( 196 | split_io_provider, 197 | EVENT_TRACE_FLAG_SPLIT_IO, 198 | krabs::guids::split_io); 199 | 200 | /** 201 | * A provider that enables system call events. 202 | */ 203 | CREATE_CONVENIENCE_KERNEL_PROVIDER( 204 | system_call_provider, 205 | EVENT_TRACE_FLAG_SYSTEMCALL, 206 | krabs::guids::perf_info); 207 | 208 | /** 209 | * A provider that enables thread start and stop events. 210 | */ 211 | CREATE_CONVENIENCE_KERNEL_PROVIDER( 212 | thread_provider, 213 | EVENT_TRACE_FLAG_THREAD, 214 | krabs::guids::thread); 215 | 216 | /** 217 | * A provider that enables file map and unmap (excluding images) events. 218 | */ 219 | CREATE_CONVENIENCE_KERNEL_PROVIDER( 220 | vamap_provider, 221 | EVENT_TRACE_FLAG_VAMAP, 222 | krabs::guids::file_io); 223 | 224 | /** 225 | * A provider that enables VirtualAlloc and VirtualFree events. 226 | */ 227 | CREATE_CONVENIENCE_KERNEL_PROVIDER( 228 | virtual_alloc_provider, 229 | EVENT_TRACE_FLAG_VIRTUAL_ALLOC, 230 | krabs::guids::page_fault); 231 | 232 | /** 233 | * A provider that enables Object Manager events. 234 | */ 235 | CREATE_CONVENIENCE_KERNEL_PROVIDER_MASK( 236 | object_manager_provider, 237 | krabs::guids::ob_trace, 238 | PERF_OB_HANDLE); 239 | 240 | #undef CREATE_CONVENIENCE_KERNEL_PROVIDER 241 | #undef CREATE_CONVENIENCE_KERNEL_PROVIDER_MASK 242 | 243 | } /* namespace kernel */ } /* namespace krabs */ 244 | -------------------------------------------------------------------------------- /libs/krabs/krabs/kt.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | #pragma once 5 | 6 | #include "compiler_check.hpp" 7 | #include "kernel_guids.hpp" 8 | #include "perfinfo_groupmask.hpp" 9 | #include "provider.hpp" 10 | #include "trace.hpp" 11 | #include "ut.hpp" 12 | #include "version_helpers.hpp" 13 | 14 | #include 15 | 16 | namespace krabs { namespace details { 17 | 18 | /** 19 | * 20 | * Used as a template argument to a trace instance. This class implements 21 | * code paths for kernel traces. Should never be used or seen by client 22 | * code. 23 | * 24 | */ 25 | struct kt { 26 | 27 | typedef krabs::kernel_provider provider_type; 28 | 29 | /** 30 | * 31 | * Used to assign a name to the trace instance that is being 32 | * instantiated. 33 | * 34 | * 35 | * In pre-Win8 days, there could only be a single kernel trace 36 | * instance on an entire machine, and that instance had to be named 37 | * a particular name. This restriction was loosened in Win8, but 38 | * the trace still needs to do the right thing on older OSes. 39 | * 40 | */ 41 | static const std::wstring enforce_name_policy( 42 | const std::wstring &name); 43 | 44 | /** 45 | * 46 | * Generates a value that fills the EnableFlags field in an 47 | * EVENT_TRACE_PROPERTIES structure. This controls the providers that 48 | * get enabled for a kernel trace. 49 | * 50 | */ 51 | static const unsigned long construct_enable_flags( 52 | const krabs::trace &trace); 53 | 54 | /** 55 | * 56 | * Enables the providers that are attached to the given trace. 57 | * 58 | */ 59 | static void enable_providers( 60 | const krabs::trace &trace); 61 | 62 | /** 63 | * 64 | * Enables the configured kernel rundown flags. 65 | * 66 | * 67 | * This ETW feature is undocumented and should be used with caution. 68 | * 69 | */ 70 | static void enable_rundown( 71 | const krabs::trace& trace); 72 | 73 | /** 74 | * 75 | * Decides to forward an event to any of the providers in the trace. 76 | * 77 | */ 78 | static void forward_events( 79 | const EVENT_RECORD &record, 80 | const krabs::trace &trace); 81 | 82 | /** 83 | * 84 | * Sets the ETW trace log file mode. 85 | * 86 | */ 87 | static unsigned long augment_file_mode(); 88 | 89 | /** 90 | * 91 | * Returns the GUID of the trace session. 92 | * 93 | */ 94 | static krabs::guid get_trace_guid(); 95 | 96 | }; 97 | 98 | // Implementation 99 | // ------------------------------------------------------------------------ 100 | 101 | inline const std::wstring kt::enforce_name_policy( 102 | const std::wstring &name_hint) 103 | { 104 | if (IsWindows8OrGreater()) { 105 | return krabs::details::ut::enforce_name_policy(name_hint); 106 | } 107 | 108 | return KERNEL_LOGGER_NAME; 109 | } 110 | 111 | inline const unsigned long kt::construct_enable_flags( 112 | const krabs::trace &trace) 113 | { 114 | unsigned long flags = 0; 115 | for (auto &provider : trace.providers_) { 116 | flags |= provider.get().flags(); 117 | } 118 | 119 | return flags; 120 | } 121 | 122 | inline void kt::enable_providers( 123 | const krabs::trace &trace) 124 | { 125 | EVENT_TRACE_GROUPMASK_INFORMATION gmi = { 0 }; 126 | gmi.EventTraceInformationClass = EventTraceGroupMaskInformation; 127 | gmi.TraceHandle = trace.registrationHandle_; 128 | 129 | // initialise EventTraceGroupMasks to the values that have been enabled via the trace flags 130 | ULONG status = NtQuerySystemInformation(SystemPerformanceTraceInformation, &gmi, sizeof(gmi), nullptr); 131 | error_check_common_conditions(status); 132 | 133 | auto group_mask_set = false; 134 | for (auto& provider : trace.providers_) { 135 | auto group = provider.get().group_mask(); 136 | PERFINFO_OR_GROUP_WITH_GROUPMASK(group, &(gmi.EventTraceGroupMasks)); 137 | group_mask_set |= (group != 0); 138 | } 139 | 140 | if (group_mask_set) { 141 | // This will fail on Windows 7, so only call it if truly neccessary 142 | status = NtSetSystemInformation(SystemPerformanceTraceInformation, &gmi, sizeof(gmi)); 143 | error_check_common_conditions(status); 144 | } 145 | 146 | return; 147 | } 148 | 149 | inline void kt::enable_rundown( 150 | const krabs::trace& trace) 151 | { 152 | bool rundown_enabled = false; 153 | ULONG rundown_flags = 0; 154 | for (auto& provider : trace.providers_) { 155 | rundown_enabled |= provider.get().rundown_enabled(); 156 | rundown_flags |= provider.get().rundown_flags(); 157 | } 158 | 159 | if (rundown_enabled) { 160 | ULONG status = EnableTraceEx2(trace.registrationHandle_, 161 | &krabs::guids::rundown, 162 | EVENT_CONTROL_CODE_ENABLE_PROVIDER, 163 | 0, 164 | rundown_flags, 165 | 0, 166 | 0, 167 | NULL); 168 | error_check_common_conditions(status); 169 | } 170 | } 171 | 172 | 173 | inline void kt::forward_events( 174 | const EVENT_RECORD &record, 175 | const krabs::trace &trace) 176 | { 177 | for (auto &provider : trace.providers_) { 178 | if (provider.get().id() == record.EventHeader.ProviderId) { 179 | provider.get().on_event(record, trace.context_); 180 | return; 181 | } 182 | } 183 | 184 | if (trace.default_callback_ != nullptr) 185 | trace.default_callback_(record, trace.context_); 186 | } 187 | 188 | inline unsigned long kt::augment_file_mode() 189 | { 190 | if (IsWindows8OrGreater()) { 191 | return EVENT_TRACE_SYSTEM_LOGGER_MODE; 192 | } 193 | 194 | return 0; 195 | } 196 | 197 | inline krabs::guid kt::get_trace_guid() 198 | { 199 | if (IsWindows8OrGreater()) { 200 | return krabs::guid::random_guid(); 201 | } 202 | 203 | return krabs::guid(SystemTraceControlGuid); 204 | } 205 | 206 | } /* namespace details */ } /* namespace krabs */ 207 | -------------------------------------------------------------------------------- /libs/krabs/krabs/parse_types.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | #pragma once 5 | 6 | #ifndef WIN32_LEAN_AND_MEAN 7 | #define WIN32_LEAN_AND_MEAN 8 | #endif 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | #include 17 | 18 | #include "compiler_check.hpp" 19 | 20 | namespace krabs { 21 | 22 | /** 23 | * 24 | * Provided entirely for code clarity purposes. 25 | * Indicates that the number is intended to be used as an ID 26 | * 27 | * 28 | * This should be turned into an _id user defined literal when our 29 | * compiler decides to catch up to the times. 30 | * 31 | * 32 | * id(1000); 33 | * 34 | */ 35 | template 36 | T id(T n) 37 | { 38 | return n; 39 | } 40 | 41 | /** 42 | * 43 | * Provided entirely for code clarity purposes. 44 | * Indicates that the number is intended to be used as a version 45 | * 46 | * 47 | * This should be turned into a _vers user defined literal when our 48 | * compiler decides to catch up to the times. 49 | * 50 | * 51 | * id(1000); 52 | * 53 | */ 54 | template 55 | T version(T n) 56 | { 57 | return n; 58 | } 59 | 60 | /** 61 | * 62 | * Provided entirely for code clarity purposes. 63 | * Indicates that the number is intended to be used as an opcode 64 | * 65 | * 66 | * This should be turned into a _opcode user defined literal when our 67 | * compiler decides to catch up to the times. 68 | * 69 | * 70 | * opcode(1000); 71 | * 72 | */ 73 | template 74 | T opcode(T n) 75 | { 76 | return n; 77 | } 78 | 79 | 80 | /** 81 | * 82 | * Used to discriminate between hex ints and regular ints in ETW events. 83 | * 84 | * 85 | * Q: Why in the world? I can't even. 86 | * A: ETW differentiates between hexints and regular ints. When 87 | * record_builder validates that the input type matches the type 88 | * specified in the schema, getting this wrong will cause an 89 | * exception. A quick little type wrapper like this lets us 90 | * discriminate based on the type and everything turns out better. 91 | * 92 | */ 93 | struct hexint32 { 94 | hexint32(int v) 95 | : value(v) 96 | {} 97 | 98 | int value; 99 | }; 100 | 101 | struct hexint64 { 102 | hexint64(long long v) 103 | : value(v) 104 | {} 105 | 106 | long long value; 107 | }; 108 | 109 | /** 110 | * 111 | * Used to support parsing and creation of binary ETW fields. 112 | * 113 | */ 114 | struct binary { 115 | public: 116 | binary() : bytes_() { } 117 | 118 | binary(const BYTE* start, size_t n) 119 | : bytes_(start, start + n) 120 | { } 121 | 122 | const std::vector& bytes() const 123 | { 124 | return bytes_; 125 | } 126 | 127 | private: 128 | std::vector bytes_; 129 | }; 130 | 131 | template 132 | binary make_binary(const T& value, size_t n) 133 | { 134 | const auto start = (BYTE*)&value; 135 | return binary(start, n); 136 | } 137 | 138 | /** 139 | * 140 | * Used to handle parsing of IPv4 and IPv6 fields in an ETW record. 141 | * This is used in the parser class in a template specialization. 142 | * 143 | */ 144 | struct ip_address { 145 | union { 146 | DWORD v4; 147 | BYTE v6[16]; 148 | }; 149 | bool is_ipv6; 150 | 151 | static ip_address from_ipv6(const BYTE* bytes) 152 | { 153 | ip_address addr; 154 | addr.is_ipv6 = true; 155 | memcpy_s(addr.v6, 16, bytes, 16); 156 | return addr; 157 | } 158 | 159 | static ip_address from_ipv4(DWORD val) 160 | { 161 | ip_address addr; 162 | addr.is_ipv6 = false; 163 | addr.v4 = val; 164 | return addr; 165 | } 166 | 167 | ip_address() {} 168 | }; 169 | 170 | /** 171 | * 172 | * Used to handle parsing of socket addresses in 173 | * network order. This union is a convenient wrapper 174 | * around the type IPv4 and IPv6 types provided by 175 | * the Winsock (v2) APIs. 176 | * 177 | */ 178 | struct socket_address { 179 | union { 180 | struct sockaddr sa; 181 | struct sockaddr_in sa_in; 182 | struct sockaddr_in6 sa_in6; 183 | struct sockaddr_storage sa_stor; 184 | }; 185 | size_t size; 186 | 187 | static socket_address from_bytes(const BYTE* bytes, size_t size_in_bytes) 188 | { 189 | socket_address sa; 190 | memcpy_s(&(sa.sa_stor), sizeof sa.sa_stor, bytes, size_in_bytes); 191 | sa.size = size_in_bytes; 192 | return sa; 193 | } 194 | }; 195 | 196 | /** 197 | * 198 | * Holds information about an property extracted from the etw schema 199 | * 200 | */ 201 | struct property_info { 202 | const BYTE *pPropertyIndex_; 203 | const EVENT_PROPERTY_INFO *pEventPropertyInfo_; 204 | ULONG length_; 205 | 206 | property_info( 207 | const BYTE *offset, 208 | const EVENT_PROPERTY_INFO &evtPropInfo, 209 | ULONG length) 210 | : pPropertyIndex_(offset) 211 | , pEventPropertyInfo_(&evtPropInfo) 212 | , length_(length) 213 | { } 214 | 215 | property_info() 216 | : pPropertyIndex_(nullptr) 217 | , pEventPropertyInfo_(nullptr) 218 | , length_(0) 219 | { } 220 | 221 | inline bool found() const 222 | { 223 | return pPropertyIndex_ != nullptr; 224 | } 225 | }; 226 | 227 | /** 228 | * 229 | * Used to handle parsing of SIDs from either a 230 | * SID or WBEMSID property 231 | * 232 | */ 233 | struct sid { 234 | // SIDs are variable-length 235 | // So the 'best' way to store them is to convert to a string 236 | // The other-end can either print the string or call ConvertStringSidToSidA 237 | // to get the SID back 238 | std::string sid_string; 239 | 240 | static sid from_bytes(const BYTE* bytes, size_t size_in_bytes) 241 | { 242 | sid ws; 243 | LPSTR temp_sid_string; 244 | UNREFERENCED_PARAMETER(size_in_bytes); 245 | 246 | if (!ConvertSidToStringSidA((PSID)bytes, &temp_sid_string)) { 247 | throw std::runtime_error( 248 | "Failed to get a SID from a property"); 249 | } 250 | ws.sid_string = temp_sid_string; 251 | LocalFree(temp_sid_string); 252 | return ws; 253 | } 254 | 255 | private: 256 | }; 257 | 258 | /** 259 | * 260 | * Used to handle parsing of Pointer Address types. 261 | * 262 | */ 263 | struct pointer { 264 | /** 265 | * We store the pointer as an uint64_t, as it is highly unlikley 266 | * to be pointing to somewhere accessible to our process 267 | */ 268 | uint64_t address; 269 | 270 | static pointer from_bytes(const BYTE* bytes, size_t size_in_bytes) 271 | { 272 | pointer pt; 273 | 274 | // If 32-Bit, first parse as a uint32 275 | // Then we can 'cast' that to our uint64_t 276 | if (size_in_bytes == sizeof(uint32_t)) { 277 | pt.address = *reinterpret_cast(bytes); 278 | } 279 | else if (size_in_bytes == sizeof(uint64_t)) { 280 | pt.address = *reinterpret_cast(bytes); 281 | } 282 | else { 283 | throw std::runtime_error( 284 | "Failed to get a POINTER from a property"); 285 | } 286 | 287 | return pt; 288 | } 289 | 290 | private: 291 | }; 292 | 293 | 294 | /** 295 | * 296 | * Used to handle parsing of CountedStrings in an ETW Record. 297 | * This is used in the parser class in a template specialization. 298 | * 299 | */ 300 | #pragma pack(push,1) 301 | struct counted_string { 302 | using value_type = wchar_t; 303 | using reference = value_type&; 304 | using pointer = value_type*; 305 | using const_reference = const value_type&; 306 | using const_pointer = const value_type*; 307 | using iterator = value_type*; 308 | using const_iterator = const value_type*; 309 | 310 | /** 311 | * size of the string in bytes 312 | */ 313 | uint16_t size_; 314 | wchar_t string_[1]; 315 | 316 | const_pointer string() const 317 | { 318 | return string_; 319 | } 320 | 321 | size_t length() const 322 | { 323 | return size_ / sizeof(value_type); 324 | } 325 | }; 326 | #pragma pack(pop) 327 | 328 | static_assert(std::is_trivial::value && std::is_standard_layout::value , "Do not modify counted_string"); 329 | 330 | } /* namespace krabs */ 331 | -------------------------------------------------------------------------------- /libs/krabs/krabs/perfinfo_groupmask.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | #ifndef PERFINFO_GROUPMASK_HPP 5 | #define PERFINFO_GROUPMASK_HPP 6 | 7 | #include 8 | #pragma comment(lib, "ntdll") 9 | 10 | // https://geoffchappell.com/studies/windows/km/ntoskrnl/api/etw/tracesup/perfinfo_groupmask.htm 11 | 12 | #define PERF_MASK_INDEX (0xe0000000) 13 | #define PERF_MASK_GROUP (~PERF_MASK_INDEX) 14 | #define PERF_NUM_MASKS 8 15 | 16 | typedef ULONG PERFINFO_MASK; 17 | typedef struct _PERFINFO_GROUPMASK { 18 | ULONG Masks[PERF_NUM_MASKS]; 19 | } PERFINFO_GROUPMASK, *PPERFINFO_GROUPMASK; 20 | 21 | #define PERF_GET_MASK_INDEX(GM) (((GM) & PERF_MASK_INDEX) >> 29) 22 | #define PERF_GET_MASK_GROUP(GM) ((GM) & PERF_MASK_GROUP) 23 | #define PERFINFO_OR_GROUP_WITH_GROUPMASK(Group, pGroupMask) \ 24 | (pGroupMask)->Masks[PERF_GET_MASK_INDEX(Group)] |= PERF_GET_MASK_GROUP(Group); 25 | 26 | // Masks[0] 27 | #define PERF_PROCESS EVENT_TRACE_FLAG_PROCESS 28 | #define PERF_THREAD EVENT_TRACE_FLAG_THREAD 29 | #define PERF_PROC_THREAD EVENT_TRACE_FLAG_PROCESS | EVENT_TRACE_FLAG_THREAD 30 | #define PERF_LOADER EVENT_TRACE_FLAG_IMAGE_LOAD 31 | #define PERF_PERF_COUNTER EVENT_TRACE_FLAG_PROCESS_COUNTERS 32 | #define PERF_FILENAME EVENT_TRACE_FLAG_DISK_FILE_IO 33 | #define PERF_DISK_IO EVENT_TRACE_FLAG_DISK_FILE_IO | EVENT_TRACE_FLAG_DISK_IO 34 | #define PERF_DISK_IO_INIT EVENT_TRACE_FLAG_DISK_IO_INIT 35 | #define PERF_ALL_FAULTS EVENT_TRACE_FLAG_MEMORY_PAGE_FAULTS 36 | #define PERF_HARD_FAULTS EVENT_TRACE_FLAG_MEMORY_HARD_FAULTS 37 | #define PERF_VAMAP EVENT_TRACE_FLAG_VAMAP 38 | #define PERF_NETWORK EVENT_TRACE_FLAG_NETWORK_TCPIP 39 | #define PERF_REGISTRY EVENT_TRACE_FLAG_REGISTRY 40 | #define PERF_DBGPRINT EVENT_TRACE_FLAG_DBGPRINT 41 | #define PERF_JOB EVENT_TRACE_FLAG_JOB 42 | #define PERF_ALPC EVENT_TRACE_FLAG_ALPC 43 | #define PERF_SPLIT_IO EVENT_TRACE_FLAG_SPLIT_IO 44 | #define PERF_DEBUG_EVENTS EVENT_TRACE_FLAG_DEBUG_EVENTS 45 | #define PERF_FILE_IO EVENT_TRACE_FLAG_FILE_IO 46 | #define PERF_FILE_IO_INIT EVENT_TRACE_FLAG_FILE_IO_INIT 47 | #define PERF_NO_SYSCONFIG EVENT_TRACE_FLAG_NO_SYSCONFIG 48 | 49 | // Masks[1] 50 | #define PERF_MEMORY 0x20000001 51 | #define PERF_PROFILE 0x20000002 // equivalent to EVENT_TRACE_FLAG_PROFILE 52 | #define PERF_CONTEXT_SWITCH 0x20000004 // equivalent to EVENT_TRACE_FLAG_CSWITCH 53 | #define PERF_FOOTPRINT 0x20000008 54 | #define PERF_DRIVERS 0x20000010 // equivalent to EVENT_TRACE_FLAG_DRIVER 55 | #define PERF_REFSET 0x20000020 56 | #define PERF_POOL 0x20000040 57 | #define PERF_POOLTRACE 0x20000041 58 | #define PERF_DPC 0x20000080 // equivalent to EVENT_TRACE_FLAG_DPC 59 | #define PERF_COMPACT_CSWITCH 0x20000100 60 | #define PERF_DISPATCHER 0x20000200 // equivalent to EVENT_TRACE_FLAG_DISPATCHER 61 | #define PERF_PMC_PROFILE 0x20000400 62 | #define PERF_PROFILING 0x20000402 63 | #define PERF_PROCESS_INSWAP 0x20000800 64 | #define PERF_AFFINITY 0x20001000 65 | #define PERF_PRIORITY 0x20002000 66 | #define PERF_INTERRUPT 0x20004000 // equivalent to EVENT_TRACE_FLAG_INTERRUPT 67 | #define PERF_VIRTUAL_ALLOC 0x20008000 // equivalent to EVENT_TRACE_FLAG_VIRTUAL_ALLOC 68 | #define PERF_SPINLOCK 0x20010000 69 | #define PERF_SYNC_OBJECTS 0x20020000 70 | #define PERF_DPC_QUEUE 0x20040000 71 | #define PERF_MEMINFO 0x20080000 72 | #define PERF_CONTMEM_GEN 0x20100000 73 | #define PERF_SPINLOCK_CNTRS 0x20200000 74 | #define PERF_SPININSTR 0x20210000 75 | #define PERF_SESSION 0x20400000 76 | #define PERF_PFSECTION 0x20400000 77 | #define PERF_MEMINFO_WS 0x20800000 78 | #define PERF_KERNEL_QUEUE 0x21000000 79 | #define PERF_INTERRUPT_STEER 0x22000000 80 | #define PERF_SHOULD_YIELD 0x24000000 81 | #define PERF_WS 0x28000000 82 | 83 | // Masks[2] 84 | #define PERF_ANTI_STARVATION 0x40000001 85 | #define PERF_PROCESS_FREEZE 0x40000002 86 | #define PERF_PFN_LIST 0x40000004 87 | #define PERF_WS_DETAIL 0x40000008 88 | #define PERF_WS_ENTRY 0x40000010 89 | #define PERF_HEAP 0x40000020 90 | #define PERF_SYSCALL 0x40000040 // equivalent to EVENT_TRACE_FLAG_SYSTEMCALL 91 | #define PERF_UMS 0x40000080 92 | #define PERF_BACKTRACE 0x40000100 93 | #define PERF_VULCAN 0x40000200 94 | #define PERF_OBJECTS 0x40000400 95 | #define PERF_EVENTS 0x40000800 96 | #define PERF_FULLTRACE 0x40001000 97 | #define PERF_DFSS 0x40002000 98 | #define PERF_PREFETCH 0x40004000 99 | #define PERF_PROCESSOR_IDLE 0x40008000 100 | #define PERF_CPU_CONFIG 0x40010000 101 | #define PERF_TIMER 0x40020000 102 | #define PERF_CLOCK_INTERRUPT 0x40040000 103 | #define PERF_LOAD_BALANCER 0x40080000 104 | #define PERF_CLOCK_TIMER 0x40100000 105 | #define PERF_IDLE_SELECTION 0x40200000 106 | #define PERF_IPI 0x40400000 107 | #define PERF_IO_TIMER 0x40800000 108 | #define PERF_REG_HIVE 0x41000000 109 | #define PERF_REG_NOTIF 0x42000000 110 | #define PERF_PPM_EXIT_LATENCY 0x44000000 111 | #define PERF_WORKER_THREAD 0x48000000 112 | 113 | // Masks[4] 114 | #define PERF_OPTICAL_IO 0x80000001 115 | #define PERF_OPTICAL_IO_INIT 0x80000002 116 | #define PERF_DLL_INFO 0x80000008 117 | #define PERF_DLL_FLUSH_WS 0x80000010 118 | #define PERF_OB_HANDLE 0x80000040 119 | #define PERF_OB_OBJECT 0x80000080 120 | #define PERF_WAKE_DROP 0x80000200 121 | #define PERF_WAKE_EVENT 0x80000400 122 | #define PERF_DEBUGGER 0x80000800 123 | #define PERF_PROC_ATTACH 0x80001000 124 | #define PERF_WAKE_COUNTER 0x80002000 125 | #define PERF_POWER 0x80008000 126 | #define PERF_SOFT_TRIM 0x80010000 127 | #define PERF_CC 0x80020000 128 | #define PERF_FLT_IO_INIT 0x80080000 129 | #define PERF_FLT_IO 0x80100000 130 | #define PERF_FLT_FASTIO 0x80200000 131 | #define PERF_FLT_IO_FAILURE 0x80400000 132 | #define PERF_HV_PROFILE 0x80800000 133 | #define PERF_WDF_DPC 0x81000000 134 | #define PERF_WDF_INTERRUPT 0x82000000 135 | #define PERF_CACHE_FLUSH 0x84000000 136 | 137 | // Masks[5] 138 | #define PERF_HIBER_RUNDOWN 0xA0000001 139 | 140 | // Masks[6] 141 | #define PERF_SYSCFG_SYSTEM 0xC0000001 142 | #define PERF_SYSCFG_GRAPHICS 0xC0000002 143 | #define PERF_SYSCFG_STORAGE 0xC0000004 144 | #define PERF_SYSCFG_NETWORK 0xC0000008 145 | #define PERF_SYSCFG_SERVICES 0xC0000010 146 | #define PERF_SYSCFG_PNP 0xC0000020 147 | #define PERF_SYSCFG_OPTICAL 0xC0000040 148 | #define PERF_SYSCFG_ALL 0xDFFFFFFF 149 | 150 | // Masks[7] - Control Mask. All flags that change system behavior go here. 151 | #define PERF_CLUSTER_OFF 0xE0000001 152 | #define PERF_MEMORY_CONTROL 0xE0000002 153 | 154 | // TraceQueryInformation wasn't introduced until Windows 8, so we need to use 155 | // NtQuerySystemInformation instead in order to maintain support for Windows 7. 156 | // This requires the below additional definitions. 157 | 158 | typedef enum _EVENT_TRACE_INFORMATION_CLASS { 159 | EventTraceKernelVersionInformation, 160 | EventTraceGroupMaskInformation, 161 | EventTracePerformanceInformation, 162 | EventTraceTimeProfileInformation, 163 | EventTraceSessionSecurityInformation, 164 | EventTraceSpinlockInformation, 165 | EventTraceStackTracingInformation, 166 | EventTraceExecutiveResourceInformation, 167 | EventTraceHeapTracingInformation, 168 | EventTraceHeapSummaryTracingInformation, 169 | EventTracePoolTagFilterInformation, 170 | EventTracePebsTracingInformation, 171 | EventTraceProfileConfigInformation, 172 | EventTraceProfileSourceListInformation, 173 | EventTraceProfileEventListInformation, 174 | EventTraceProfileCounterListInformation, 175 | EventTraceStackCachingInformation, 176 | EventTraceObjectTypeFilterInformation, 177 | MaxEventTraceInfoClass 178 | } EVENT_TRACE_INFORMATION_CLASS; 179 | 180 | typedef struct _EVENT_TRACE_GROUPMASK_INFORMATION { 181 | EVENT_TRACE_INFORMATION_CLASS EventTraceInformationClass; 182 | TRACEHANDLE TraceHandle; 183 | PERFINFO_GROUPMASK EventTraceGroupMasks; 184 | } EVENT_TRACE_GROUPMASK_INFORMATION, * PEVENT_TRACE_GROUPMASK_INFORMATION; 185 | 186 | #ifndef _WINTERNL_ 187 | 188 | typedef enum _SYSTEM_INFORMATION_CLASS { 189 | } SYSTEM_INFORMATION_CLASS; 190 | 191 | typedef LONG NTSTATUS; 192 | 193 | extern "C" NTSTATUS NTAPI NtQuerySystemInformation( 194 | _In_ SYSTEM_INFORMATION_CLASS SystemInformationClass, 195 | _Out_writes_bytes_to_opt_(SystemInformationLength, *ReturnLength) PVOID SystemInformation, 196 | _In_ ULONG SystemInformationLength, 197 | _Out_opt_ PULONG ReturnLength 198 | ); 199 | 200 | #endif // _WINTERNL_ 201 | 202 | extern "C" NTSTATUS NTAPI NtSetSystemInformation( 203 | _In_ SYSTEM_INFORMATION_CLASS SystemInformationClass, 204 | _In_reads_bytes_opt_(SystemInformationLength) PVOID SystemInformation, 205 | _In_ ULONG SystemInformationLength 206 | ); 207 | 208 | constexpr auto SystemPerformanceTraceInformation{ static_cast(0x1f) }; 209 | 210 | #endif // PERFINFO_GROUPMASK_HPP -------------------------------------------------------------------------------- /libs/krabs/krabs/property.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | #pragma once 5 | 6 | #ifndef WIN32_LEAN_AND_MEAN 7 | #define WIN32_LEAN_AND_MEAN 8 | #endif 9 | 10 | #define INITGUID 11 | 12 | #include 13 | #include 14 | 15 | #include "compiler_check.hpp" 16 | #include "schema.hpp" 17 | #include "errors.hpp" 18 | 19 | #include 20 | #include 21 | #include 22 | 23 | #pragma comment(lib, "tdh.lib") 24 | 25 | namespace krabs { 26 | 27 | /** 28 | * 29 | * Represents a single property of the record schema. 30 | * 31 | * 32 | * Noticeably absent from this property is the ability to ask what its 33 | * value is. The reason for this is that this property instance is 34 | * intended to work with synth_records, which don't always have data to 35 | * correspond with properties. This class *cannot* return a value because 36 | * there isn't always a value to return. 37 | * 38 | */ 39 | class property { 40 | public: 41 | 42 | /** 43 | * 44 | * Constructs a property. 45 | * 46 | * 47 | * This should be instantiated by client code -- let the parser 48 | * object do this for you with its `properties` method. 49 | * 50 | */ 51 | property(const std::wstring &name, _TDH_IN_TYPE type); 52 | 53 | /** 54 | * 55 | * Retrieves the name of the property. 56 | * 57 | */ 58 | const std::wstring &name() const; 59 | 60 | /** 61 | * 62 | * Retrieves the Tdh type of the property. 63 | * 64 | */ 65 | _TDH_IN_TYPE type() const; 66 | 67 | private: 68 | std::wstring name_; 69 | _TDH_IN_TYPE type_; 70 | }; 71 | 72 | 73 | /** 74 | * 75 | * Iterates the properties in a given event record. 76 | * 77 | */ 78 | class property_iterator { 79 | public: 80 | 81 | /** 82 | * 83 | * Constructs a new iterator that lazily retrieves the properties of 84 | * the given event record. 85 | * 86 | * 87 | * Don't construct this yourself. Let the `parser` class do it for you. 88 | * 89 | */ 90 | property_iterator(const schema &s); 91 | 92 | /** 93 | * 94 | * Returns an iterator that hasn't yielded any properties yet. 95 | * 96 | */ 97 | std::vector::iterator begin(); 98 | 99 | /** 100 | * 101 | * Returns an iterator that has yielded all properties. 102 | * 103 | */ 104 | std::vector::iterator end(); 105 | 106 | private: 107 | 108 | /** 109 | * 110 | * Constructs a property instance out of the raw data of the 111 | * given property. 112 | * 113 | */ 114 | property get_property(size_t index) const; 115 | 116 | /** 117 | * 118 | * Collects the names of the properties in the schema. 119 | * 120 | * 121 | * This is a little lazy of us, as we end up iterating the properties 122 | * entirely before allowing enumeration by the client. Because this 123 | * code is most likely called in a non-critical path, there's not 124 | * much to worry about here. 125 | */ 126 | std::vector enum_properties() const; 127 | 128 | 129 | private: 130 | const krabs::schema &schema_; 131 | size_t numProperties_; 132 | std::vector properties_; 133 | std::vector::iterator beg_; 134 | std::vector::iterator end_; 135 | std::vector::iterator curr_; 136 | }; 137 | 138 | // Implementation 139 | // ------------------------------------------------------------------------ 140 | 141 | inline property::property(const std::wstring &name, _TDH_IN_TYPE type) 142 | : name_(name) 143 | , type_(type) 144 | {} 145 | 146 | inline const std::wstring &property::name() const 147 | { 148 | return name_; 149 | } 150 | 151 | inline _TDH_IN_TYPE property::type() const 152 | { 153 | return type_; 154 | } 155 | 156 | // ------------------------------------------------------------------------ 157 | 158 | inline property_iterator::property_iterator(const schema &s) 159 | : schema_(s) 160 | , numProperties_(s.pSchema_->TopLevelPropertyCount) 161 | , properties_(enum_properties()) 162 | , beg_(properties_.begin()) 163 | , end_(properties_.end()) 164 | , curr_(properties_.begin()) 165 | {} 166 | 167 | inline std::vector::iterator property_iterator::begin() 168 | { 169 | return beg_; 170 | } 171 | 172 | inline std::vector::iterator property_iterator::end() 173 | { 174 | return end_; 175 | } 176 | 177 | inline property property_iterator::get_property(size_t index) const 178 | { 179 | const auto &curr_prop = schema_.pSchema_->EventPropertyInfoArray[index]; 180 | 181 | const wchar_t *pName = reinterpret_cast( 182 | reinterpret_cast(schema_.pSchema_) + 183 | curr_prop.NameOffset); 184 | 185 | auto tdh_type = (_TDH_IN_TYPE)curr_prop.nonStructType.InType; 186 | 187 | return property(pName, tdh_type); 188 | } 189 | 190 | inline std::vector property_iterator::enum_properties() const 191 | { 192 | std::vector props; 193 | for (size_t i = 0; i < numProperties_; ++i) { 194 | props.emplace_back(get_property(i)); 195 | } 196 | 197 | return props; 198 | } 199 | 200 | 201 | } /* namespace krabs */ 202 | -------------------------------------------------------------------------------- /libs/krabs/krabs/schema_locator.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | #pragma once 5 | 6 | #ifndef WIN32_LEAN_AND_MEAN 7 | #define WIN32_LEAN_AND_MEAN 8 | #endif 9 | 10 | #define INITGUID 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | #include 17 | #include 18 | 19 | #include "compiler_check.hpp" 20 | #include "errors.hpp" 21 | #include "guid.hpp" 22 | 23 | #pragma comment(lib, "tdh.lib") 24 | 25 | namespace krabs { 26 | 27 | /** 28 | * 29 | * Type used as the key for cache lookup in a schema_locator. 30 | * 31 | */ 32 | struct schema_key 33 | { 34 | guid provider; 35 | uint16_t id; 36 | uint8_t opcode; 37 | uint8_t version; 38 | uint8_t level; 39 | 40 | schema_key(const EVENT_RECORD &record) 41 | : provider(record.EventHeader.ProviderId) 42 | , id(record.EventHeader.EventDescriptor.Id) 43 | , opcode(record.EventHeader.EventDescriptor.Opcode) 44 | , level(record.EventHeader.EventDescriptor.Level) 45 | , version(record.EventHeader.EventDescriptor.Version) { } 46 | 47 | bool operator==(const schema_key &rhs) const 48 | { 49 | return provider == rhs.provider && 50 | id == rhs.id && 51 | opcode == rhs.opcode && 52 | level == rhs.level && 53 | version == rhs.version; 54 | } 55 | 56 | bool operator!=(const schema_key &rhs) const { return !(*this == rhs); } 57 | }; 58 | } 59 | 60 | namespace std { 61 | 62 | /** 63 | * 64 | * Builds a hash code for a schema_key 65 | * 66 | */ 67 | template<> 68 | struct std::hash 69 | { 70 | size_t operator()(const krabs::schema_key &key) const 71 | { 72 | // Shift-Add-XOR hash - good enough for the small sets we deal with 73 | size_t h = 2166136261; 74 | 75 | h ^= (h << 5) + (h >> 2) + std::hash()(key.provider); 76 | h ^= (h << 5) + (h >> 2) + key.id; 77 | h ^= (h << 5) + (h >> 2) + key.opcode; 78 | h ^= (h << 5) + (h >> 2) + key.version; 79 | h ^= (h << 5) + (h >> 2) + key.level; 80 | 81 | return h; 82 | } 83 | }; 84 | } 85 | 86 | namespace krabs { 87 | 88 | /** 89 | * 90 | * Get event schema from TDH. 91 | * 92 | */ 93 | std::unique_ptr get_event_schema_from_tdh(const EVENT_RECORD &); 94 | 95 | /** 96 | * 97 | * Fetches and caches schemas from TDH. 98 | * NOTE: this cache also reduces the number of managed to native transitions 99 | * when krabs is compiled into a managed assembly. 100 | * 101 | */ 102 | class schema_locator { 103 | public: 104 | 105 | /** 106 | * 107 | * Retrieves the event schema from the cache or falls back to 108 | * TDH to load the schema. 109 | * 110 | */ 111 | const PTRACE_EVENT_INFO get_event_schema(const EVENT_RECORD &record) const; 112 | 113 | private: 114 | mutable std::unordered_map> cache_; 115 | }; 116 | 117 | // Implementation 118 | // ------------------------------------------------------------------------ 119 | 120 | inline const PTRACE_EVENT_INFO schema_locator::get_event_schema(const EVENT_RECORD &record) const 121 | { 122 | // check the cache 123 | auto key = schema_key(record); 124 | auto& buffer = cache_[key]; 125 | 126 | if (!buffer) { 127 | auto temp = get_event_schema_from_tdh(record); 128 | buffer.swap(temp); 129 | } 130 | 131 | return (PTRACE_EVENT_INFO)(buffer.get()); 132 | } 133 | 134 | inline std::unique_ptr get_event_schema_from_tdh(const EVENT_RECORD &record) 135 | { 136 | // get required size 137 | ULONG bufferSize = 0; 138 | ULONG status = TdhGetEventInformation( 139 | (PEVENT_RECORD)&record, 140 | 0, 141 | NULL, 142 | NULL, 143 | &bufferSize); 144 | 145 | if (status != ERROR_INSUFFICIENT_BUFFER) { 146 | error_check_common_conditions(status, record); 147 | } 148 | 149 | // allocate and fill the schema from TDH 150 | auto buffer = std::unique_ptr(new char[bufferSize]); 151 | 152 | error_check_common_conditions( 153 | TdhGetEventInformation( 154 | (PEVENT_RECORD)&record, 155 | 0, 156 | NULL, 157 | (PTRACE_EVENT_INFO)buffer.get(), 158 | &bufferSize), 159 | record); 160 | 161 | return buffer; 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /libs/krabs/krabs/size_provider.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | #pragma once 5 | 6 | #ifndef WIN32_LEAN_AND_MEAN 7 | #define WIN32_LEAN_AND_MEAN 8 | #endif 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | #include "compiler_check.hpp" 15 | 16 | namespace krabs { 17 | 18 | // TODO: I don't like this interface - it's too tightly 19 | // coupled to parser.hpp mainly because the code was 20 | // lifted directly out of parser::find_property. 21 | 22 | class size_provider { 23 | public: 24 | /** 25 | * 26 | * Get the size of the specified property from the specified record. 27 | * 28 | * BYTE* offset into the user data buffer where the property starts 29 | * wchar_t* name of the property to query 30 | * EVENT_RECORD& record to query 31 | * EVENT_PROPERTY_INFO& property info for the property to query 32 | */ 33 | static ULONG get_property_size( 34 | const BYTE*, 35 | const wchar_t*, 36 | const EVENT_RECORD&, 37 | const EVENT_PROPERTY_INFO&); 38 | 39 | private: 40 | static ULONG get_heuristic_size( 41 | const BYTE*, 42 | const EVENT_PROPERTY_INFO&, 43 | const EVENT_RECORD&); 44 | 45 | static ULONG get_tdh_size( 46 | const wchar_t*, 47 | const EVENT_RECORD&); 48 | }; 49 | 50 | // Implementation 51 | // ------------------------------------------------------------------------ 52 | 53 | inline ULONG size_provider::get_property_size( 54 | const BYTE* propertyStart, 55 | const wchar_t* propertyName, 56 | const EVENT_RECORD& record, 57 | const EVENT_PROPERTY_INFO& propertyInfo) 58 | { 59 | // The values of the event are essentially stored as an ad-hoc 60 | // variant. In order to determine how far we need to advance the 61 | // seeking pointer, we need to know the size of the property that 62 | // we've just looked at. For certain variable-sized types (like a 63 | // string), we need to ask Tdh* to determine the length of the 64 | // property. For others, the size is immediately accessible in 65 | // the schema structure. 66 | 67 | if ((propertyInfo.Flags & PropertyParamLength) == 0 && 68 | propertyInfo.length > 0) 69 | { 70 | // length is a union that may refer to another field for a length 71 | // value. In that case, defer to TDH for the value otherwise 72 | // use the length value directly. 73 | 74 | // For pointers check header instead of size, see PointerSize at 75 | // https://docs.microsoft.com/en-us/windows/win32/api/tdh/nf-tdh-tdhformatproperty 76 | // for details 77 | if (propertyInfo.nonStructType.InType == TDH_INTYPE_POINTER) 78 | { 79 | return record.EventHeader.Flags & EVENT_HEADER_FLAG_32_BIT_HEADER ? 4 : 8; 80 | } 81 | 82 | return propertyInfo.length; 83 | } 84 | 85 | ULONG propertyLength = 0; 86 | 87 | // If no flags are set on the property, attempt to use the length 88 | // field. If that field is 0, try using our heuristic. 89 | if (propertyInfo.Flags == 0) 90 | { 91 | if (propertyInfo.length > 0) 92 | propertyLength = propertyInfo.length; 93 | else 94 | propertyLength = get_heuristic_size(propertyStart, propertyInfo, record); 95 | } 96 | 97 | // Couldn't get the length from the 'length' field or 98 | // the heuristic for size failed -> ask Tdh. 99 | if (propertyLength == 0) 100 | propertyLength = get_tdh_size(propertyName, record); 101 | 102 | return propertyLength; 103 | } 104 | 105 | inline ULONG size_provider::get_heuristic_size( 106 | const BYTE* propertyStart, 107 | const EVENT_PROPERTY_INFO& propertyInfo, 108 | const EVENT_RECORD& record) 109 | { 110 | ULONG propertyLength = 0; 111 | PBYTE pRecordEnd = (PBYTE)record.UserData + record.UserDataLength; 112 | 113 | // The calls to Tdh are kind of expensive, especially when krabs is 114 | // included in a managed assembly as this call will be a thunk. 115 | // The following _very_ common property types can be short-circuited 116 | // to prevent the expensive call. 117 | 118 | // Be careful! Check IN and OUT types before making an assumption. 119 | 120 | // Strings that appear at the end of a record may not be null-terminated. 121 | // If a string is null-terminated, propertyLength includes the null character. 122 | // If a string is not-null terminated, propertyLength includes all bytes up 123 | // to the end of the record buffer. 124 | 125 | if (propertyInfo.nonStructType.OutType == TDH_OUTTYPE_STRING) 126 | { 127 | if (propertyInfo.nonStructType.InType == TDH_INTYPE_UNICODESTRING) 128 | { 129 | auto p = (const wchar_t*)propertyStart; 130 | auto pEnd = (const wchar_t*)pRecordEnd; 131 | while (p < pEnd) { 132 | if (!*p++) { 133 | break; 134 | } 135 | } 136 | propertyLength = static_cast(((PBYTE)p) - propertyStart); 137 | } 138 | else if (propertyInfo.nonStructType.InType == TDH_INTYPE_ANSISTRING) 139 | { 140 | auto p = (const char*)propertyStart; 141 | auto pEnd = (const char*)pRecordEnd; 142 | while (p < pEnd) { 143 | if (!*p++) { 144 | break; 145 | } 146 | 147 | } 148 | propertyLength = static_cast(((PBYTE)p) - propertyStart); 149 | } 150 | } 151 | 152 | return propertyLength; 153 | } 154 | 155 | inline ULONG size_provider::get_tdh_size( 156 | const wchar_t* propertyName, 157 | const EVENT_RECORD& record) 158 | { 159 | ULONG propertyLength = 0; 160 | 161 | PROPERTY_DATA_DESCRIPTOR desc; 162 | desc.PropertyName = (ULONGLONG)propertyName; 163 | desc.ArrayIndex = ULONG_MAX; 164 | 165 | TdhGetPropertySize((PEVENT_RECORD)&record, 0, NULL, 1, &desc, &propertyLength); 166 | 167 | return propertyLength; 168 | } 169 | } -------------------------------------------------------------------------------- /libs/krabs/krabs/tdh_helpers.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | #pragma once 5 | 6 | #define INITGUID 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #include "compiler_check.hpp" 13 | #include "parse_types.hpp" 14 | 15 | namespace krabs { 16 | 17 | #define CASE_TYPE(enum) case TDH_INTYPE_##enum: return #enum 18 | 19 | inline const char* in_type_to_string(_TDH_IN_TYPE type) 20 | { 21 | switch (type) 22 | { 23 | CASE_TYPE(NULL); 24 | CASE_TYPE(UNICODESTRING); 25 | CASE_TYPE(ANSISTRING); 26 | CASE_TYPE(INT8); 27 | CASE_TYPE(UINT8); 28 | CASE_TYPE(INT16); 29 | CASE_TYPE(UINT16); 30 | CASE_TYPE(INT32); 31 | CASE_TYPE(UINT32); 32 | CASE_TYPE(INT64); 33 | CASE_TYPE(UINT64); 34 | CASE_TYPE(FLOAT); 35 | CASE_TYPE(DOUBLE); 36 | CASE_TYPE(BOOLEAN); 37 | CASE_TYPE(BINARY); 38 | CASE_TYPE(GUID); 39 | CASE_TYPE(POINTER); 40 | CASE_TYPE(FILETIME); 41 | CASE_TYPE(SYSTEMTIME); 42 | CASE_TYPE(SID); 43 | CASE_TYPE(HEXINT32); 44 | CASE_TYPE(HEXINT64); 45 | CASE_TYPE(COUNTEDSTRING); 46 | CASE_TYPE(COUNTEDANSISTRING); 47 | CASE_TYPE(REVERSEDCOUNTEDSTRING); 48 | CASE_TYPE(REVERSEDCOUNTEDANSISTRING); 49 | CASE_TYPE(NONNULLTERMINATEDSTRING); 50 | CASE_TYPE(NONNULLTERMINATEDANSISTRING); 51 | CASE_TYPE(UNICODECHAR); 52 | CASE_TYPE(ANSICHAR); 53 | CASE_TYPE(SIZET); 54 | CASE_TYPE(HEXDUMP); 55 | CASE_TYPE(WBEMSID); 56 | default: return ""; 57 | } 58 | } 59 | 60 | #undef CASE_TYPE 61 | 62 | namespace debug { 63 | 64 | // this function provides a user-friendly compiler error 65 | // which shows the type in question in the error message. 66 | template 67 | inline void missing_assert_specialization_for() 68 | { 69 | static_assert(sizeof(T) == 0, __FUNCSIG__); 70 | } 71 | 72 | // The "catch-all" implementation of assert_valid_assignment just 73 | // throws in debug to let us know that we are trying to parse a 74 | // type that does not have any assignment validation. This compiles 75 | // to a no-op in release. 76 | template 77 | inline void assert_valid_assignment(const std::wstring&, const property_info&) 78 | { 79 | #ifndef NDEBUG 80 | 81 | // NOTE: if you want compile time assignment assertion define TYPEASSERT 82 | // in the preprocessor or undefine it to disable compilation errors 83 | 84 | #ifdef TYPEASSERT 85 | missing_assert_specialization_for(); 86 | #endif // TYPEASSERT 87 | #endif // NDEBUG 88 | } 89 | 90 | #ifndef NDEBUG 91 | 92 | // These specializations will be removed in release builds and compilation 93 | // will fall back to the unspecialized version which is a no-op in release. 94 | 95 | inline void throw_if_invalid( 96 | const std::wstring& name, 97 | const property_info& info, 98 | _TDH_IN_TYPE requested) 99 | { 100 | auto actual = (_TDH_IN_TYPE)info.pEventPropertyInfo_->nonStructType.InType; 101 | 102 | if (requested == actual) return; 103 | 104 | #pragma warning(push) 105 | #pragma warning(disable: 4244) // narrowing property name wchar_t to char for this error message 106 | std::string ansiName(name.begin(), name.end()); 107 | #pragma warning(pop) 108 | 109 | throw type_mismatch_assert( 110 | ansiName.c_str(), 111 | in_type_to_string(actual), 112 | in_type_to_string(requested)); 113 | } 114 | 115 | // The macro below generates a specialized version of assert_valid_assignment 116 | // only in debug builds. The specialized overload will be selected instead 117 | // of the unspecialized version defined above. This allows us to have 118 | // type-driven assertions only in debug builds. 119 | 120 | #define BUILD_ASSERT(type, tdh_type) \ 121 | template <> \ 122 | inline void assert_valid_assignment( \ 123 | const std::wstring& name, const property_info& info) \ 124 | { \ 125 | throw_if_invalid(name, info, tdh_type); \ 126 | } 127 | 128 | // NOTE: don't just blindly add assertions here, some types 129 | // that seem trivial (e.g. bool) are not because of differences 130 | // between the representation in C++ and the representation in ETW. 131 | // Ensure that type sizes match and that the ETW form isn't 132 | // a variant or variable length. A type that requires a specialized 133 | // assertion will also require a specialized parser. 134 | 135 | // strings 136 | BUILD_ASSERT(std::wstring, TDH_INTYPE_UNICODESTRING); 137 | BUILD_ASSERT(std::string, TDH_INTYPE_ANSISTRING); 138 | BUILD_ASSERT(const counted_string*, TDH_INTYPE_COUNTEDSTRING); 139 | 140 | // integers 141 | BUILD_ASSERT(int8_t, TDH_INTYPE_INT8); 142 | BUILD_ASSERT(uint8_t, TDH_INTYPE_UINT8); 143 | BUILD_ASSERT(int16_t, TDH_INTYPE_INT16); 144 | BUILD_ASSERT(uint16_t, TDH_INTYPE_UINT16); 145 | BUILD_ASSERT(int32_t, TDH_INTYPE_INT32); 146 | BUILD_ASSERT(uint32_t, TDH_INTYPE_UINT32); 147 | BUILD_ASSERT(int64_t, TDH_INTYPE_INT64); 148 | BUILD_ASSERT(uint64_t, TDH_INTYPE_UINT64); 149 | 150 | // floating 151 | BUILD_ASSERT(float, TDH_INTYPE_FLOAT); 152 | BUILD_ASSERT(double, TDH_INTYPE_DOUBLE); 153 | 154 | // FILETIME 155 | BUILD_ASSERT(::FILETIME, TDH_INTYPE_FILETIME); 156 | BUILD_ASSERT(::SYSTEMTIME, TDH_INTYPE_SYSTEMTIME); 157 | 158 | #undef BUILD_ASSERT 159 | 160 | template <> 161 | inline void assert_valid_assignment( 162 | const std::wstring&, const property_info& info) 163 | { 164 | auto outType = info.pEventPropertyInfo_->nonStructType.OutType; 165 | 166 | if (outType != TDH_OUTTYPE_IPV6 && 167 | outType != TDH_OUTTYPE_IPV4) { 168 | throw std::runtime_error( 169 | "Requested an IP address from non-IP address property"); 170 | } 171 | } 172 | 173 | template <> 174 | inline void assert_valid_assignment( 175 | const std::wstring&, const property_info& info) 176 | { 177 | auto outType = info.pEventPropertyInfo_->nonStructType.OutType; 178 | 179 | if (outType != TDH_OUTTYPE_SOCKETADDRESS) { 180 | throw std::runtime_error( 181 | "Requested a socket address from property that does not contain a socket address"); 182 | } 183 | } 184 | 185 | template <> 186 | inline void assert_valid_assignment( 187 | const std::wstring&, const property_info& info) 188 | { 189 | auto inType = info.pEventPropertyInfo_->nonStructType.InType; 190 | 191 | if (inType != TDH_INTYPE_WBEMSID && inType != TDH_INTYPE_SID) { 192 | throw std::runtime_error( 193 | "Requested a SID but was neither a SID nor WBEMSID"); 194 | } 195 | } 196 | 197 | template <> 198 | inline void assert_valid_assignment( 199 | const std::wstring&, const property_info& info) 200 | { 201 | auto inType = info.pEventPropertyInfo_->nonStructType.InType; 202 | 203 | if (inType != TDH_INTYPE_POINTER) { 204 | throw std::runtime_error( 205 | "Requested a POINTER from property that is not one"); 206 | } 207 | } 208 | 209 | template <> 210 | inline void assert_valid_assignment( 211 | const std::wstring&, const property_info& info) 212 | { 213 | auto inType = info.pEventPropertyInfo_->nonStructType.InType; 214 | 215 | if (inType != TDH_INTYPE_BOOLEAN) { 216 | throw std::runtime_error( 217 | "Requested a BOOLEAN from property that is not one"); 218 | } 219 | } 220 | 221 | #endif // NDEBUG 222 | 223 | } /* namespace debug */ 224 | 225 | } /* namespace krabs */ 226 | -------------------------------------------------------------------------------- /libs/krabs/krabs/testing/event_filter_proxy.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | #pragma once 5 | 6 | #define INITGUID 7 | 8 | 9 | #include "../compiler_check.hpp" 10 | #include "../filtering/event_filter.hpp" 11 | #include "synth_record.hpp" 12 | 13 | namespace krabs { namespace testing { 14 | 15 | /** 16 | * 17 | * Serves as a fill-in for the event_filter class for testing purposes. 18 | * It acts as a liason for the actual filter instance and allows for forced event 19 | * testing. 20 | * 21 | */ 22 | class event_filter_proxy { 23 | public: 24 | 25 | /** 26 | * 27 | * Constructs a proxy for the given event_filter. 28 | * 29 | * 30 | * krabs::event_filter event_filter; 31 | * krabs::testing::event_filter_proxy proxy(event_filter); 32 | * 33 | */ 34 | event_filter_proxy(krabs::event_filter &filter); 35 | 36 | /** 37 | * 38 | * Pushes an event through to the proxied filter instance. 39 | * 40 | * 41 | * krabs::event_filter event_filter; 42 | * krabs::testing::event_filter_proxy proxy(event_filter); 43 | * 44 | * krabs::guid powershell(L"{A0C1853B-5C40-4B15-8766-3CF1C58F985A}"); 45 | * krabs::testing::record_builder builder(powershell, krabs::id(7942), krabs::version(1)); 46 | * 47 | * builder.add_properties() 48 | * (L"ClassName", L"FakeETWEventForRealz") 49 | * (L"Message", L"This message is completely faked"); 50 | * 51 | * auto record = builder.pack_incomplete(); 52 | * proxy.push_event(record); 53 | * 54 | */ 55 | void push_event(const synth_record &record); 56 | 57 | private: 58 | krabs::event_filter &event_filter_; 59 | krabs::trace_context trace_context_; 60 | }; 61 | 62 | // Implementation 63 | // ------------------------------------------------------------------------ 64 | 65 | inline event_filter_proxy::event_filter_proxy(krabs::event_filter &event_filter) 66 | : event_filter_(event_filter) 67 | { 68 | } 69 | 70 | inline void event_filter_proxy::push_event(const synth_record &record) 71 | { 72 | event_filter_.on_event(record, trace_context_); 73 | } 74 | 75 | } /* namespace testing */ } /* namespace krabs */ 76 | -------------------------------------------------------------------------------- /libs/krabs/krabs/testing/extended_data_builder.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | #pragma once 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | // TODO: Remove this #define once Krabs starts using Windows SDK v. 10.0.19041.0 or later. 15 | // From evntcons.h starting in Windows SDK v. 10.0.19041.0. 16 | #ifndef EVENT_HEADER_EXT_TYPE_CONTAINER_ID 17 | #define EVENT_HEADER_EXT_TYPE_CONTAINER_ID 16 18 | #endif 19 | 20 | namespace krabs { namespace testing { 21 | class extended_data_builder; 22 | 23 | /** 24 | * 25 | * Since extended data items have to be packed later, we have to hold onto the data 26 | * until we're ready to pack it. 27 | * 28 | */ 29 | class extended_data_thunk 30 | { 31 | public: 32 | extended_data_thunk(USHORT ext_type, BYTE* data, size_t data_length); 33 | 34 | private: 35 | // Intentionally not defined. 36 | extended_data_thunk(); 37 | 38 | USHORT ext_type_; 39 | std::vector bytes_; 40 | 41 | friend class extended_data_builder; 42 | }; 43 | 44 | /** 45 | * 46 | * Generates fake packed EVENT_HEADER_EXTENDED_DATA_ITEM structures to later add into test 47 | * synth_record objects. These are not guaranteed to be indistinguishable from the real 48 | * thing, just good enough to unit test code that reads/interprets extended data. 49 | * 50 | * Note for testing: this builder just appends extended data structures, it won't stop you 51 | * from breaking any API invariants, such as only one of a specific extended data item type. 52 | * 53 | */ 54 | class extended_data_builder 55 | { 56 | public: 57 | static constexpr size_t GUID_STRING_LENGTH_NO_BRACES = 36; 58 | static constexpr size_t GUID_STRING_LENGTH_WITH_BRACES = GUID_STRING_LENGTH_NO_BRACES + 2; 59 | 60 | 61 | extended_data_builder() 62 | : items_() 63 | {} 64 | 65 | // Mocks a container ID type extended data item. 66 | void add_container_id(const GUID& container_id); 67 | 68 | // This generates a contiguous buffer holding all of the data for 69 | // the extended data items. Non-trivial because the actual structs 70 | // have to be a contiguous array, and they each contain pointers, 71 | // not offsets, to dynamically sized data buffers. 72 | std::pair, size_t> pack() const; 73 | 74 | // Returns the value that should correspond with EVENT_RECORD.ExtendedDataCount 75 | inline size_t count() const { return items_.size(); } 76 | 77 | private: 78 | std::vector items_; 79 | }; 80 | 81 | // Implementation 82 | // ------------------------------------------------------------------------ 83 | 84 | inline extended_data_thunk::extended_data_thunk(USHORT ext_type, BYTE* data, size_t data_length) 85 | : ext_type_(ext_type) 86 | , bytes_() 87 | { 88 | bytes_.assign(data, data + data_length); 89 | } 90 | 91 | inline void extended_data_builder::add_container_id(const GUID& container_id) 92 | { 93 | // With null terminator 94 | wchar_t wide_guid_buffer[GUID_STRING_LENGTH_WITH_BRACES + 1] = {}; 95 | 96 | // No null terminator 97 | BYTE guid_data[GUID_STRING_LENGTH_NO_BRACES] = {}; 98 | 99 | StringFromGUID2(container_id, wide_guid_buffer, GUID_STRING_LENGTH_WITH_BRACES + 1); 100 | 101 | for (int i = 0; i < GUID_STRING_LENGTH_NO_BRACES; i++) 102 | { 103 | // Offset by 1 to ignore the wrapping braces. 104 | guid_data[i] = static_cast(wide_guid_buffer[i + 1]); 105 | } 106 | 107 | items_.emplace_back(static_cast(EVENT_HEADER_EXT_TYPE_CONTAINER_ID), guid_data, GUID_STRING_LENGTH_NO_BRACES); 108 | } 109 | 110 | inline std::pair, size_t> extended_data_builder::pack() const 111 | { 112 | // Return null for buffer if there are no extended data items. 113 | if (items_.size() == 0) 114 | { 115 | return std::make_pair(std::shared_ptr(nullptr), 0); 116 | } 117 | 118 | BYTE* data_buffer = nullptr; 119 | size_t data_buffer_size = 0; 120 | 121 | // Step 1: compute the required buffer size 122 | size_t array_part_size = sizeof(EVENT_HEADER_EXTENDED_DATA_ITEM) * items_.size(); 123 | size_t data_part_size = 0; 124 | 125 | for (const extended_data_thunk& item : items_) 126 | { 127 | data_part_size += item.bytes_.size(); 128 | } 129 | 130 | // Allocate the buffer and zero it 131 | data_buffer = new BYTE[array_part_size + data_part_size]; 132 | data_buffer_size = array_part_size + data_part_size; 133 | ZeroMemory(data_buffer, data_buffer_size); 134 | 135 | // Step 2: Fill the buffer. For each extended data item, write the object into the buffer at the back. 136 | auto array_ptr = reinterpret_cast(data_buffer); 137 | auto data_ptr = data_buffer + array_part_size; 138 | 139 | for (int i = 0; i < items_.size(); i++) 140 | { 141 | // 2a: write the struct 142 | auto& destination = array_ptr[i]; 143 | const auto& thunk = items_[i]; 144 | const size_t thunk_size = thunk.bytes_.size(); 145 | 146 | destination.ExtType = thunk.ext_type_; 147 | destination.DataSize = static_cast(thunk_size); 148 | // Assert that the conversion did not truncate thunk_size. 149 | assert(static_cast(destination.DataSize) == thunk_size); 150 | 151 | // 2b: Write the data 152 | assert((data_buffer + data_buffer_size) > data_ptr); // prevent wraparound with unsigned int math 153 | size_t remaining = (data_buffer + data_buffer_size) - (data_ptr); 154 | // Assert that we will not truncate the data due to not allocating enough space in the buffer. 155 | assert(remaining >= thunk_size); 156 | // Make sure we rather not copy all of the data than overrun the buffer. 157 | memcpy_s(data_ptr, std::min(remaining, thunk_size), thunk.bytes_.data(), thunk_size); 158 | 159 | // 2c: point the DataPtr field at the data 160 | destination.DataPtr = reinterpret_cast(data_ptr); 161 | 162 | // 2d: increment the pointer for where to write the next piece of data 163 | data_ptr += destination.DataSize; 164 | } 165 | 166 | return std::make_pair(std::shared_ptr(data_buffer), data_buffer_size); 167 | } 168 | } } 169 | -------------------------------------------------------------------------------- /libs/krabs/krabs/testing/filler.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | #pragma once 5 | 6 | #include 7 | 8 | #include "../compiler_check.hpp" 9 | #include "../parse_types.hpp" 10 | 11 | namespace krabs { namespace testing { namespace details { 12 | 13 | /** 14 | * 15 | * Defines how much padding to inject into a synth_record when a property 16 | * is not filled by calling code. 17 | * 18 | */ 19 | inline size_t how_many_bytes_to_fill(_TDH_IN_TYPE type) 20 | { 21 | static_assert(sizeof(float) == 4, "sizeof(float) must be 4, defined on MSDN"); 22 | 23 | switch (type) { 24 | case TDH_INTYPE_NULL: throw std::runtime_error("supposed to be unusued -- something horrible is happening"); 25 | case TDH_INTYPE_UNICODESTRING: return sizeof(wchar_t); 26 | case TDH_INTYPE_ANSISTRING: return sizeof(char); 27 | case TDH_INTYPE_INT8: return sizeof(int8_t); 28 | case TDH_INTYPE_UINT8: return sizeof(uint8_t); 29 | case TDH_INTYPE_INT16: return sizeof(int16_t); 30 | case TDH_INTYPE_UINT16: return sizeof(uint16_t); 31 | case TDH_INTYPE_INT32: return sizeof(int32_t); 32 | case TDH_INTYPE_UINT32: return sizeof(uint32_t); 33 | case TDH_INTYPE_INT64: return sizeof(int64_t); 34 | case TDH_INTYPE_UINT64: return sizeof(int64_t); 35 | case TDH_INTYPE_FLOAT: return sizeof(float); 36 | case TDH_INTYPE_DOUBLE: return sizeof(double); 37 | case TDH_INTYPE_BOOLEAN: return sizeof(uint32_t); // 4-byte bool, defined on MSDN 38 | case TDH_INTYPE_BINARY: return sizeof(char); 39 | case TDH_INTYPE_GUID: return sizeof(GUID); 40 | case TDH_INTYPE_POINTER: return sizeof(char*); 41 | case TDH_INTYPE_FILETIME: return sizeof(FILETIME); 42 | case TDH_INTYPE_SYSTEMTIME: return sizeof(SYSTEMTIME); 43 | case TDH_INTYPE_SID: return sizeof(PSID); 44 | case TDH_INTYPE_HEXINT32: return sizeof(uint32_t); 45 | case TDH_INTYPE_HEXINT64: return sizeof(uint64_t); 46 | default: break; 47 | }; 48 | 49 | throw std::runtime_error("Unexpected fill type"); 50 | } 51 | 52 | /** 53 | * 54 | * Maps C++ types to TDH types. Used to do runtime type checking of packed 55 | * synthetic properties. 56 | * 57 | */ 58 | 59 | template 60 | struct tdh_morphism { 61 | // This doesn't have a value field, so compilation will fail when we 62 | // try to use a type in our record_builder that isn't recognized. 63 | }; 64 | 65 | template 66 | struct tdh_morphism { 67 | static const _TDH_IN_TYPE value = TDH_INTYPE_POINTER; 68 | }; 69 | 70 | template <> 71 | struct tdh_morphism { 72 | static const _TDH_IN_TYPE value = TDH_INTYPE_UNICODESTRING; 73 | }; 74 | 75 | template <> 76 | struct tdh_morphism { 77 | static const _TDH_IN_TYPE value = TDH_INTYPE_ANSISTRING; 78 | }; 79 | 80 | template <> 81 | struct tdh_morphism { 82 | static const _TDH_IN_TYPE value = TDH_INTYPE_INT8; 83 | }; 84 | 85 | template <> 86 | struct tdh_morphism { 87 | static const _TDH_IN_TYPE value = TDH_INTYPE_UINT8; 88 | }; 89 | 90 | template <> 91 | struct tdh_morphism { 92 | static const _TDH_IN_TYPE value = TDH_INTYPE_INT16; 93 | }; 94 | 95 | template <> 96 | struct tdh_morphism { 97 | static const _TDH_IN_TYPE value = TDH_INTYPE_UINT16; 98 | }; 99 | 100 | template <> 101 | struct tdh_morphism { 102 | static const _TDH_IN_TYPE value = TDH_INTYPE_INT32; 103 | }; 104 | 105 | template <> 106 | struct tdh_morphism { 107 | static const _TDH_IN_TYPE value = TDH_INTYPE_UINT32; 108 | }; 109 | 110 | template <> 111 | struct tdh_morphism { 112 | static const _TDH_IN_TYPE value = TDH_INTYPE_INT64; 113 | }; 114 | 115 | template <> 116 | struct tdh_morphism { 117 | static const _TDH_IN_TYPE value = TDH_INTYPE_UINT64; 118 | }; 119 | 120 | template <> 121 | struct tdh_morphism { 122 | static const _TDH_IN_TYPE value = TDH_INTYPE_FLOAT; 123 | }; 124 | 125 | template <> 126 | struct tdh_morphism { 127 | static const _TDH_IN_TYPE value = TDH_INTYPE_DOUBLE; 128 | }; 129 | 130 | template <> 131 | struct tdh_morphism { 132 | static const _TDH_IN_TYPE value = TDH_INTYPE_BOOLEAN; 133 | }; 134 | 135 | template <> 136 | struct tdh_morphism { 137 | static const _TDH_IN_TYPE value = TDH_INTYPE_GUID; 138 | }; 139 | 140 | template <> 141 | struct tdh_morphism { 142 | static const _TDH_IN_TYPE value = TDH_INTYPE_GUID; 143 | }; 144 | 145 | template <> 146 | struct tdh_morphism { 147 | static const _TDH_IN_TYPE value = TDH_INTYPE_FILETIME; 148 | }; 149 | 150 | template <> 151 | struct tdh_morphism { 152 | static const _TDH_IN_TYPE value = TDH_INTYPE_SYSTEMTIME; 153 | }; 154 | 155 | template <> 156 | struct tdh_morphism { 157 | static const _TDH_IN_TYPE value = TDH_INTYPE_HEXINT32; 158 | }; 159 | 160 | template <> 161 | struct tdh_morphism { 162 | static const _TDH_IN_TYPE value = TDH_INTYPE_HEXINT64; 163 | }; 164 | 165 | template <> 166 | struct tdh_morphism { 167 | static const _TDH_IN_TYPE value = TDH_INTYPE_SID; 168 | }; 169 | 170 | template <> 171 | struct tdh_morphism { 172 | static const _TDH_IN_TYPE value = TDH_INTYPE_BINARY; 173 | }; 174 | 175 | 176 | } /* namespace details */ } /* namespace testing */ } /* namespace krabs */ 177 | -------------------------------------------------------------------------------- /libs/krabs/krabs/testing/proxy.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | #pragma once 5 | 6 | #define INITGUID 7 | 8 | #include "../compiler_check.hpp" 9 | #include "../trace.hpp" 10 | #include "../client.hpp" 11 | #include "../testing/synth_record.hpp" 12 | 13 | namespace krabs { namespace testing { 14 | 15 | /** 16 | * 17 | * Serves as a fill-in for the trace class for testing purposes. It acts 18 | * as a liason for the actual trace instance and allows for forced event 19 | * testing. 20 | * 21 | */ 22 | template 23 | class trace_proxy { 24 | public: 25 | 26 | /** 27 | * 28 | * Constructs a proxy for the given trace. 29 | * 30 | * 31 | * krabs::user_trace trace; 32 | * krabs::testing::trace_proxy proxy(trace); 33 | * 34 | */ 35 | 36 | trace_proxy(T &trace); 37 | 38 | /** 39 | * 40 | * Mocks starting the underlying trace. 41 | * 42 | * 43 | * krabs::user_trace trace; 44 | * krabs::testing::trace_proxy proxy(trace); 45 | * proxy.start(); // do not call trace.start() 46 | * 47 | */ 48 | void start(); 49 | 50 | /** 51 | * 52 | * Pushes an event through to the proxied trace instance. 53 | * 54 | * 55 | * This is the primary mechanism for testing providers and their 56 | * callbacks. Create a fake event with an record_builder instance 57 | * and then push the created synth_record through the object 58 | * graph. 59 | * 60 | * 61 | * krabs::user_trace trace; 62 | * krabs::testing::trace_proxy proxy(trace); 63 | * proxy.start(); // do not call trace.start() 64 | * 65 | * krabs::guid powershell(L"{A0C1853B-5C40-4B15-8766-3CF1C58F985A}"); 66 | * krabs::testing::record_builder builder(powershell, krabs::id(7942), krabs::version(1)); 67 | * 68 | * builder.add_properties() 69 | * (L"ClassName", L"FakeETWEventForRealz") 70 | * (L"Message", L"This message is completely faked"); 71 | * 72 | * auto record = builder.pack_incomplete(); 73 | * proxy.push_event(record); 74 | * 75 | */ 76 | void push_event(const synth_record &record); 77 | 78 | private: 79 | T &trace_; 80 | }; 81 | 82 | /** 83 | * Specific instantiation for user traces. 84 | */ 85 | typedef trace_proxy user_trace_proxy; 86 | 87 | /** 88 | * Specific instantiation for kernel traces. 89 | */ 90 | typedef trace_proxy kernel_trace_proxy; 91 | 92 | // Implementation 93 | // ------------------------------------------------------------------------ 94 | 95 | template 96 | trace_proxy::trace_proxy(T &trace) 97 | : trace_(trace) 98 | { 99 | } 100 | 101 | template 102 | void trace_proxy::start() 103 | { 104 | } 105 | 106 | template 107 | void trace_proxy::push_event(const synth_record &record) 108 | { 109 | trace_.on_event(record); 110 | } 111 | 112 | } /* namespace testing */ } /* namespace krabs */ 113 | -------------------------------------------------------------------------------- /libs/krabs/krabs/testing/record_property_thunk.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | #pragma once 5 | 6 | #include "../compiler_check.hpp" 7 | #include "filler.hpp" 8 | 9 | namespace krabs { namespace testing { 10 | 11 | class record_builder; 12 | 13 | /** 14 | * 15 | * Takes any value and turns it into a sequence of serialized bytes. 16 | * 17 | * 18 | * When we're composing an event, we need to store heterogeneous types in 19 | * a collection while we wait until we know exactly how to pack the actual 20 | * event. Because the actual EVENT_RECORD structure properties are packed 21 | * into a byte collection, we take our cue from that and do similarly. We 22 | * keep all of the random property byte blobs separate until we know the 23 | * particular order to stash them in so we have less futzing to do later. 24 | * 25 | */ 26 | class record_property_thunk { 27 | public: 28 | 29 | template 30 | record_property_thunk(const std::wstring &property, const T &value); 31 | 32 | record_property_thunk(const std::wstring &property, const wchar_t *value); 33 | record_property_thunk(const std::wstring &property, const char *value); 34 | record_property_thunk(const std::wstring &property, bool value); 35 | 36 | const std::wstring &name() const; 37 | const std::vector &bytes() const; 38 | const _TDH_IN_TYPE type() const; 39 | 40 | private: 41 | 42 | // We need this because we don't have delegating constructors in VS 2012. 43 | template 44 | void common_string_init(const std::wstring &property, const T &value); 45 | 46 | template 47 | void common_init(const std::wstring &property, const T &value); 48 | 49 | private: 50 | std::wstring name_; 51 | std::vector bytes_; 52 | _TDH_IN_TYPE type_; 53 | 54 | friend class record_builder; 55 | }; 56 | 57 | // Implementation 58 | // ------------------------------------------------------------------------ 59 | template 60 | inline record_property_thunk::record_property_thunk( 61 | const std::wstring &property, 62 | const T &value) 63 | { 64 | common_init(property, value); 65 | } 66 | 67 | // Specialization for wstrings 68 | template <> 69 | inline record_property_thunk::record_property_thunk( 70 | const std::wstring &property, 71 | const std::wstring &value) 72 | { 73 | common_string_init(property, value); 74 | } 75 | 76 | // Specialization for strings 77 | template <> 78 | inline record_property_thunk::record_property_thunk( 79 | const std::wstring &property, 80 | const std::string &value) 81 | { 82 | common_string_init(property, value); 83 | } 84 | 85 | // Overload for wchar_t strings. 86 | inline record_property_thunk::record_property_thunk( 87 | const std::wstring &property, 88 | const wchar_t *value) 89 | { 90 | common_string_init(property, std::move(std::wstring(value))); 91 | } 92 | 93 | // Overload for char strings. 94 | inline record_property_thunk::record_property_thunk( 95 | const std::wstring &property, 96 | const char *value) 97 | { 98 | common_string_init(property, std::move(std::string(value))); 99 | } 100 | 101 | // Specialization for binary blobs 102 | template <> 103 | inline record_property_thunk::record_property_thunk( 104 | const std::wstring &property, 105 | const krabs::binary &bin) 106 | : name_(property) 107 | , bytes_(bin.bytes()) 108 | , type_(krabs::testing::details::tdh_morphism::value) 109 | { 110 | } 111 | 112 | // Overload for booleans 113 | inline record_property_thunk::record_property_thunk( 114 | const std::wstring &property, 115 | bool value) 116 | { 117 | common_init(property, (int)value); 118 | type_ = krabs::testing::details::tdh_morphism::value; 119 | } 120 | 121 | template 122 | void record_property_thunk::common_init( 123 | const std::wstring &property, 124 | const T &value) 125 | { 126 | name_ = property; 127 | bytes_ = std::move(std::vector((BYTE*)&value, (BYTE*)&value + sizeof(T))); 128 | type_ = krabs::testing::details::tdh_morphism::value; 129 | } 130 | 131 | template 132 | void record_property_thunk::common_string_init( 133 | const std::wstring &property, 134 | const T &value) 135 | { 136 | name_ = property; 137 | 138 | const size_t size = value.size() * sizeof(typename T::value_type); 139 | bytes_ = std::move(std::vector((BYTE*)&value[0], (BYTE*)&value[0] + size)); 140 | type_ = krabs::testing::details::tdh_morphism::value; 141 | 142 | // Null terminate the string 143 | for (size_t i = 0; i < sizeof(typename T::value_type); ++i) { 144 | bytes_.push_back('\0'); 145 | } 146 | } 147 | 148 | inline const std::wstring &record_property_thunk::name() const 149 | { 150 | return name_; 151 | } 152 | 153 | inline const std::vector &record_property_thunk::bytes() const 154 | { 155 | return bytes_; 156 | } 157 | 158 | inline const _TDH_IN_TYPE record_property_thunk::type() const 159 | { 160 | return type_; 161 | } 162 | 163 | } /* namespace testing */ } /* namespace krabs */ 164 | -------------------------------------------------------------------------------- /libs/krabs/krabs/testing/synth_record.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | #pragma once 5 | 6 | #define INITGUID 7 | 8 | #include 9 | #include 10 | 11 | #include "../compiler_check.hpp" 12 | #include "../guid.hpp" 13 | #include "../schema.hpp" 14 | #include "../parser.hpp" 15 | 16 | #include 17 | #include 18 | #include 19 | 20 | namespace krabs { namespace testing { 21 | 22 | /** 23 | * 24 | * Represents a property that is faked -- one that is built by hand for the 25 | * purpose of testing event reaction code. 26 | * 27 | */ 28 | class synth_record { 29 | public: 30 | 31 | /** 32 | * 33 | * Constructs a synthetic property, given a partially filled 34 | * EVENT_RECORD and a packed sequence of bytes that represent the 35 | * event's user data. 36 | * 37 | * 38 | * This class should not be directly instantiated -- an record_builder 39 | * should return this with its `pack` methods. 40 | * 41 | */ 42 | synth_record(const EVENT_RECORD& record, 43 | const std::vector& user_data); 44 | 45 | /** 46 | * 47 | * Constructs a synthetic property, given a partially filled 48 | * EVENT_RECORD and a packed sequence of bytes that represent the 49 | * event's user data. 50 | * 51 | * 52 | * This class should not be directly instantiated -- an record_builder 53 | * should return this with its `pack` methods. 54 | * 55 | */ 56 | synth_record(const EVENT_RECORD &record, 57 | const std::vector &user_data, 58 | const std::shared_ptr &extended_data); 59 | 60 | /** 61 | * 62 | * Copies a synth_record and updates the pointers 63 | * in the EVENT_RECORD appropriately. 64 | * 65 | */ 66 | synth_record(const synth_record& other); 67 | 68 | /** 69 | * 70 | * Moves a synth_record into a new instance. 71 | * 72 | */ 73 | synth_record(synth_record&& other); 74 | 75 | /** 76 | * 77 | * Assigns a synth_record to another. 78 | * 79 | * by value to take advantage of move ctor 80 | */ 81 | synth_record& operator=(synth_record); 82 | 83 | /** 84 | * 85 | * Allows implicit casts to an EVENT_RECORD. 86 | * 87 | */ 88 | operator const EVENT_RECORD&() const; 89 | 90 | /** 91 | * 92 | * Swaps two synth_records. 93 | * 94 | */ 95 | friend void swap(synth_record& left, synth_record& right) 96 | { 97 | using std::swap; // ADL 98 | 99 | swap(left.record_, right.record_); 100 | swap(left.data_, right.data_); 101 | swap(left.extended_data_, right.extended_data_); 102 | } 103 | 104 | private: 105 | synth_record() 106 | : record_() 107 | , data_() { } 108 | 109 | EVENT_RECORD record_; 110 | std::vector data_; 111 | 112 | // extended_data shared PTR is passed around to make sure that the data 113 | // buffer is only deleted after all dependent synth_records are deleted. 114 | // since the extended data structure uses direct pointers to data 115 | // instead of offsets, we can't pass around a vector unless we 116 | // also want to redo the pointers every time the buffer is copied. 117 | std::shared_ptr extended_data_; 118 | }; 119 | 120 | // Implementation 121 | // ------------------------------------------------------------------------ 122 | 123 | inline synth_record::synth_record(const EVENT_RECORD& record, 124 | const std::vector& user_data) 125 | : synth_record(record, user_data, std::shared_ptr()) 126 | { 127 | // Empty shared_ptr is fine here because there's no concern 128 | // about managing lifetime of an extended data buffer if there 129 | // is no extended data buffer. 130 | } 131 | 132 | inline synth_record::synth_record( 133 | const EVENT_RECORD &record, 134 | const std::vector &user_data, 135 | const std::shared_ptr &extended_data) 136 | : record_(record) 137 | , data_(user_data) 138 | , extended_data_(extended_data) 139 | { 140 | if (data_.size() > 0) { 141 | record_.UserData = &data_[0]; 142 | } else { 143 | record_.UserData = 0; 144 | } 145 | 146 | record_.UserDataLength = static_cast(data_.size()); 147 | } 148 | 149 | inline synth_record::synth_record(const synth_record& other) 150 | : synth_record(other.record_, other.data_, other.extended_data_) 151 | { } 152 | 153 | inline synth_record::synth_record(synth_record&& other) 154 | : synth_record() 155 | { 156 | swap(*this, other); 157 | } 158 | 159 | inline synth_record& synth_record::operator=(synth_record other) 160 | { 161 | swap(*this, other); 162 | return *this; 163 | } 164 | 165 | inline synth_record::operator const EVENT_RECORD&() const 166 | { 167 | return record_; 168 | } 169 | 170 | } /* namespace testing */ } /* namespace krabs */ 171 | -------------------------------------------------------------------------------- /libs/krabs/krabs/trace_context.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | #pragma once 5 | 6 | #include "schema_locator.hpp" 7 | 8 | namespace krabs { 9 | 10 | /** 11 | * 12 | * Additional ETW trace context passed to event callbacks 13 | * to enable processing. 14 | * 15 | */ 16 | struct trace_context 17 | { 18 | const schema_locator schema_locator; 19 | /* Add additional trace context here. */ 20 | }; 21 | 22 | } 23 | -------------------------------------------------------------------------------- /libs/krabs/krabs/ut.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | #pragma once 5 | 6 | #include 7 | 8 | #include "compiler_check.hpp" 9 | #include "trace.hpp" 10 | #include "provider.hpp" 11 | 12 | namespace krabs { namespace details { 13 | 14 | /** 15 | * 16 | * Used as a template argument to a trace instance. This class implements 17 | * code paths for user traces. Should never be used or seen by client 18 | * code. 19 | * 20 | */ 21 | struct ut { 22 | 23 | typedef krabs::provider<> provider_type; 24 | 25 | struct filter_flags { 26 | UCHAR level_; 27 | ULONGLONG any_; 28 | ULONGLONG all_; 29 | ULONG trace_flags_; 30 | }; 31 | 32 | struct filter_settings{ 33 | std::set provider_filter_event_ids_; 34 | filter_flags filter_flags_{}; 35 | bool rundown_enabled_ = false; 36 | }; 37 | 38 | typedef std::map provider_filter_settings; 39 | /** 40 | * 41 | * Used to assign a name to the trace instance that is being 42 | * instantiated. 43 | * 44 | * 45 | * There really isn't a name policy to enforce with user traces, but 46 | * kernel traces do have specific naming requirements. 47 | * 48 | */ 49 | static const std::wstring enforce_name_policy( 50 | const std::wstring &name); 51 | 52 | /** 53 | * 54 | * Generates a value that fills the EnableFlags field in an 55 | * EVENT_TRACE_PROPERTIES structure. This controls the providers that 56 | * get enabled for a kernel trace. For a user trace, it doesn't do 57 | * much of anything. 58 | * 59 | */ 60 | static const unsigned long construct_enable_flags( 61 | const krabs::trace &trace); 62 | 63 | /** 64 | * 65 | * Enables the providers that are attached to the given trace. 66 | * 67 | */ 68 | static void enable_providers( 69 | const krabs::trace &trace); 70 | 71 | /** 72 | * 73 | * Enables the configured rundown events for each provider. 74 | * Should be called immediately prior to ProcessTrace. 75 | * 76 | */ 77 | static void enable_rundown( 78 | const krabs::trace& trace); 79 | 80 | /** 81 | * 82 | * Decides to forward an event to any of the providers in the trace. 83 | * 84 | */ 85 | static void forward_events( 86 | const EVENT_RECORD &record, 87 | const krabs::trace &trace); 88 | 89 | /** 90 | * 91 | * Sets the ETW trace log file mode. 92 | * 93 | */ 94 | static unsigned long augment_file_mode(); 95 | 96 | /** 97 | * 98 | * Returns the GUID of the trace session. 99 | * 100 | */ 101 | static krabs::guid get_trace_guid(); 102 | }; 103 | 104 | 105 | // Implementation 106 | // ------------------------------------------------------------------------ 107 | 108 | inline const std::wstring ut::enforce_name_policy( 109 | const std::wstring &name_hint) 110 | { 111 | if (name_hint.empty()) { 112 | return std::to_wstring(krabs::guid::random_guid()); 113 | } 114 | 115 | return name_hint; 116 | } 117 | 118 | inline const unsigned long ut::construct_enable_flags( 119 | const krabs::trace &) 120 | { 121 | return 0; 122 | } 123 | 124 | inline void ut::enable_providers( 125 | const krabs::trace &trace) 126 | { 127 | if (trace.registrationHandle_ == INVALID_PROCESSTRACE_HANDLE) 128 | return; 129 | 130 | provider_filter_settings provider_flags; 131 | 132 | // This function essentially takes the union of all the provider flags 133 | // for a given provider GUID. This comes about when multiple providers 134 | // for the same GUID are provided and request different provider flags. 135 | // TODO: Only forward the calls that are requested to each provider. 136 | for (auto &provider : trace.providers_) { 137 | auto& settings = provider_flags[provider.get().guid_]; 138 | settings.filter_flags_.level_ |= provider.get().level_; 139 | settings.filter_flags_.any_ |= provider.get().any_; 140 | settings.filter_flags_.all_ |= provider.get().all_; 141 | settings.filter_flags_.trace_flags_ |= provider.get().trace_flags_; 142 | settings.rundown_enabled_ |= provider.get().rundown_enabled_; 143 | 144 | for (const auto& filter : provider.get().filters_) { 145 | settings.provider_filter_event_ids_.insert( 146 | filter.provider_filter_event_ids().begin(), 147 | filter.provider_filter_event_ids().end()); 148 | } 149 | } 150 | 151 | for (auto &provider : provider_flags) { 152 | ENABLE_TRACE_PARAMETERS parameters; 153 | parameters.ControlFlags = 0; 154 | parameters.Version = ENABLE_TRACE_PARAMETERS_VERSION_2; 155 | parameters.SourceId = provider.first; 156 | 157 | GUID guid = provider.first; 158 | auto& settings = provider.second; 159 | 160 | parameters.EnableProperty = settings.filter_flags_.trace_flags_; 161 | parameters.EnableFilterDesc = nullptr; 162 | parameters.FilterDescCount = 0; 163 | EVENT_FILTER_DESCRIPTOR filterDesc{}; 164 | std::vector filterEventIdBuffer; 165 | auto filterEventIdCount = settings.provider_filter_event_ids_.size(); 166 | 167 | if (filterEventIdCount > 0) { 168 | //event filters existing, set native filters using API 169 | parameters.FilterDescCount = 1; 170 | filterDesc.Type = EVENT_FILTER_TYPE_EVENT_ID; 171 | 172 | //allocate + size of expected events in filter 173 | DWORD size = FIELD_OFFSET(EVENT_FILTER_EVENT_ID, Events[filterEventIdCount]); 174 | filterEventIdBuffer.resize(size, 0); 175 | 176 | auto filterEventIds = reinterpret_cast(&(filterEventIdBuffer[0])); 177 | filterEventIds->FilterIn = TRUE; 178 | filterEventIds->Count = static_cast(filterEventIdCount); 179 | 180 | auto index = 0; 181 | for (auto filter : settings.provider_filter_event_ids_) { 182 | filterEventIds->Events[index] = filter; 183 | index++; 184 | } 185 | 186 | filterDesc.Ptr = reinterpret_cast(filterEventIds); 187 | filterDesc.Size = size; 188 | 189 | parameters.EnableFilterDesc = &filterDesc; 190 | } 191 | 192 | ULONG status = EnableTraceEx2(trace.registrationHandle_, 193 | &guid, 194 | EVENT_CONTROL_CODE_ENABLE_PROVIDER, 195 | settings.filter_flags_.level_, 196 | settings.filter_flags_.any_, 197 | settings.filter_flags_.all_, 198 | 0, 199 | ¶meters); 200 | error_check_common_conditions(status); 201 | } 202 | } 203 | 204 | inline void ut::enable_rundown( 205 | const krabs::trace& trace) 206 | { 207 | if (trace.registrationHandle_ == INVALID_PROCESSTRACE_HANDLE) 208 | return; 209 | 210 | for (auto& provider : trace.providers_) { 211 | if (!provider.get().rundown_enabled_) 212 | continue; 213 | 214 | ULONG status = EnableTraceEx2(trace.registrationHandle_, 215 | &provider.get().guid_, 216 | EVENT_CONTROL_CODE_CAPTURE_STATE, 217 | 0, 218 | 0, 219 | 0, 220 | 0, 221 | NULL); 222 | error_check_common_conditions(status); 223 | } 224 | } 225 | 226 | inline void ut::forward_events( 227 | const EVENT_RECORD &record, 228 | const krabs::trace &trace) 229 | { 230 | // for manifest providers, EventHeader.ProviderId is the Provider GUID 231 | for (auto& provider : trace.providers_) { 232 | if (record.EventHeader.ProviderId == provider.get().guid_) { 233 | provider.get().on_event(record, trace.context_); 234 | return; 235 | } 236 | } 237 | 238 | // for MOF providers, EventHeader.Provider is the *Message* GUID 239 | // we need to ask TDH for event information in order to determine the 240 | // correct provider to pass this event to 241 | auto schema = get_event_schema_from_tdh(record); 242 | auto eventInfo = reinterpret_cast(schema.get()); 243 | for (auto& provider : trace.providers_) { 244 | if (eventInfo->ProviderGuid == provider.get().guid_) { 245 | provider.get().on_event(record, trace.context_); 246 | return; 247 | } 248 | } 249 | 250 | if (trace.default_callback_ != nullptr) 251 | trace.default_callback_(record, trace.context_); 252 | } 253 | 254 | inline unsigned long ut::augment_file_mode() 255 | { 256 | return 0; 257 | } 258 | 259 | inline krabs::guid ut::get_trace_guid() 260 | { 261 | return krabs::guid::random_guid(); 262 | } 263 | 264 | } /* namespace details */ } /* namespace krabs */ 265 | -------------------------------------------------------------------------------- /libs/krabs/krabs/version_helpers.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | // We manually include this file because it doesn't exist for VS2012. 5 | 6 | #ifndef _versionhelpers_H_INCLUDED_ 7 | #define _versionhelpers_H_INCLUDED_ 8 | 9 | #include 10 | 11 | #ifdef _MSC_VER 12 | #pragma once 13 | #endif // _MSC_VER 14 | 15 | #pragma region Application Family 16 | #if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) 17 | 18 | #include // for _In_, etc. 19 | 20 | #if !defined(__midl) && !defined(SORTPP_PASS) 21 | 22 | #if (NTDDI_VERSION >= NTDDI_WINXP) 23 | 24 | #ifdef __cplusplus 25 | 26 | #define VERSIONHELPERAPI inline bool 27 | 28 | #else // __cplusplus 29 | 30 | #define VERSIONHELPERAPI FORCEINLINE BOOL 31 | 32 | #endif // __cplusplus 33 | 34 | #ifndef NTDDI_WINBLUE 35 | #define NTDDI_WINBLUE 0x06030000 36 | #endif 37 | 38 | #ifndef _WIN32_WINNT_WINBLUE 39 | #define _WIN32_WINNT_WINBLUE 0x0602 40 | #endif 41 | 42 | VERSIONHELPERAPI 43 | IsWindowsVersionOrGreater(WORD wMajorVersion, WORD wMinorVersion, WORD wServicePackMajor) 44 | { 45 | OSVERSIONINFOEXW osvi = { sizeof(osvi), 0, 0, 0, 0, {0}, 0, 0 }; 46 | DWORDLONG const dwlConditionMask = VerSetConditionMask( 47 | VerSetConditionMask( 48 | VerSetConditionMask( 49 | 0, VER_MAJORVERSION, VER_GREATER_EQUAL), 50 | VER_MINORVERSION, VER_GREATER_EQUAL), 51 | VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL); 52 | 53 | osvi.dwMajorVersion = wMajorVersion; 54 | osvi.dwMinorVersion = wMinorVersion; 55 | osvi.wServicePackMajor = wServicePackMajor; 56 | 57 | return VerifyVersionInfoW(&osvi, VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR, dwlConditionMask) != FALSE; 58 | } 59 | 60 | VERSIONHELPERAPI 61 | IsWindowsXPOrGreater() 62 | { 63 | return IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WINXP), LOBYTE(_WIN32_WINNT_WINXP), 0); 64 | } 65 | 66 | VERSIONHELPERAPI 67 | IsWindowsXPSP1OrGreater() 68 | { 69 | return IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WINXP), LOBYTE(_WIN32_WINNT_WINXP), 1); 70 | } 71 | 72 | VERSIONHELPERAPI 73 | IsWindowsXPSP2OrGreater() 74 | { 75 | return IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WINXP), LOBYTE(_WIN32_WINNT_WINXP), 2); 76 | } 77 | 78 | VERSIONHELPERAPI 79 | IsWindowsXPSP3OrGreater() 80 | { 81 | return IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WINXP), LOBYTE(_WIN32_WINNT_WINXP), 3); 82 | } 83 | 84 | VERSIONHELPERAPI 85 | IsWindowsVistaOrGreater() 86 | { 87 | return IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_VISTA), LOBYTE(_WIN32_WINNT_VISTA), 0); 88 | } 89 | 90 | VERSIONHELPERAPI 91 | IsWindowsVistaSP1OrGreater() 92 | { 93 | return IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_VISTA), LOBYTE(_WIN32_WINNT_VISTA), 1); 94 | } 95 | 96 | VERSIONHELPERAPI 97 | IsWindowsVistaSP2OrGreater() 98 | { 99 | return IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_VISTA), LOBYTE(_WIN32_WINNT_VISTA), 2); 100 | } 101 | 102 | VERSIONHELPERAPI 103 | IsWindows7OrGreater() 104 | { 105 | return IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WIN7), LOBYTE(_WIN32_WINNT_WIN7), 0); 106 | } 107 | 108 | VERSIONHELPERAPI 109 | IsWindows7SP1OrGreater() 110 | { 111 | return IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WIN7), LOBYTE(_WIN32_WINNT_WIN7), 1); 112 | } 113 | 114 | VERSIONHELPERAPI 115 | IsWindows8OrGreater() 116 | { 117 | return IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WIN8), LOBYTE(_WIN32_WINNT_WIN8), 0); 118 | } 119 | 120 | VERSIONHELPERAPI 121 | IsWindows8Point1OrGreater() 122 | { 123 | return IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WINBLUE), LOBYTE(_WIN32_WINNT_WINBLUE), 0); 124 | } 125 | 126 | VERSIONHELPERAPI 127 | IsWindowsServer() 128 | { 129 | OSVERSIONINFOEXW osvi = { sizeof(osvi), 0, 0, 0, 0, {0}, 0, 0, 0, VER_NT_WORKSTATION }; 130 | DWORDLONG const dwlConditionMask = VerSetConditionMask( 0, VER_PRODUCT_TYPE, VER_EQUAL ); 131 | 132 | return !VerifyVersionInfoW(&osvi, VER_PRODUCT_TYPE, dwlConditionMask); 133 | } 134 | 135 | #endif // NTDDI_VERSION 136 | 137 | #endif // defined(__midl) 138 | 139 | #endif /* WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) */ 140 | #pragma endregion 141 | 142 | #endif // _VERSIONHELPERS_H_INCLUDED_ 143 | -------------------------------------------------------------------------------- /libs/krabs/krabs/wstring_convert.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | #include 5 | 6 | namespace krabs { 7 | 8 | /** 9 | * Converts std::wstring argument to std::string using UTF-8 codepage 10 | * Returns empty string if translation fails or input string is empty 11 | * 12 | */ 13 | inline std::string from_wstring(const std::wstring& wstr, UINT codePage = CP_UTF8) 14 | { 15 | if (wstr.empty()) 16 | return {}; 17 | 18 | const auto requiredLen = WideCharToMultiByte(codePage, 0, wstr.data(), static_cast(wstr.size()), 19 | nullptr, 0, nullptr, nullptr); 20 | if (0 == requiredLen) 21 | return {}; 22 | 23 | std::string result(requiredLen, 0); 24 | const auto convertedLen = WideCharToMultiByte(codePage, 0, wstr.data(), static_cast(wstr.size()), 25 | &result[0], requiredLen, nullptr, nullptr); 26 | if (0 == convertedLen) 27 | return {}; 28 | 29 | return result; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /screens/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thefLink/Hunt-Weird-ImageLoads/c8b859950a69a9e3b042c9efb0ff547055066c10/screens/1.png --------------------------------------------------------------------------------