├── .gitignore ├── README.md ├── SKREAM.sln └── SKREAM ├── Config.cpp ├── Config.h ├── DetoursKernel.cpp ├── DetoursKernel.h ├── Entry.cpp ├── NativeStructs7.h ├── NativeStructs8.h ├── PoolBloaterMitigation.cpp ├── PoolBloaterMitigation.h ├── PoolDefs.h ├── PoolSliderMitigation.cpp ├── PoolSliderMitigation.h ├── Random.cpp ├── Random.h ├── SKREAM.inf ├── SKREAM.vcxproj ├── SKREAM.vcxproj.filters ├── SKREAM.vcxproj.user ├── TypeOverwriteMitigation.cpp ├── TypeOverwriteMitigation.h ├── VadUtils.cpp ├── VadUtils.h └── bad0b0b0 ├── SKREAM_tester.cpp ├── bad0b0b0.vcxproj ├── bad0b0b0.vcxproj.filters ├── bad0b0b0.vcxproj.user ├── stdafx.cpp ├── stdafx.h └── targetver.h /.gitignore: -------------------------------------------------------------------------------- 1 | *.ipch 2 | *.opendb 3 | *.db 4 | *.suo 5 | *.obj 6 | *.tlog 7 | *.pdb 8 | *.sys 9 | *.cat 10 | *.log 11 | *.inf 12 | *.idb 13 | *.db-shm 14 | .vs/ 15 | *.cer 16 | *.stackdump 17 | *.exe 18 | *.pch 19 | *.ilk 20 | *.iobj 21 | *.ipdb 22 | *.id0 23 | *.id1 24 | *.til 25 | *.id2 26 | *.nam 27 | *.i64 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SKREAM 2 | 3 | **S**entinelOne's **K**e**R**nel **E**xploits **A**dvanced **M**itigations 4 | 5 | This kit contains the following mitigations: 6 | 1. **Preallocate 0xbad0b0b0** 7 | This mitigation will block exploits using TypeIndex overwrite techniques on Windows 7 and 8 (this specific technique was mitigated by Microsoft in Windows 8.1). 8 | 9 | 2. **PoolSlider** and **PoolBloater** 10 | Both of these mitigations will randomize pool allocations to break pool overflow exploits. 11 | PoolSlider uses the extra padding added to allocations whose size doesn't match the pool granularity to randomize the base address returned to the caller. 12 | PoolBloater adds a random number of pool blocks to each pool allocation, to randomize its size. 13 | 14 | The mitigations included in SKREAM are explained in detail in these blog posts: 15 | https://www.sentinelone.com/blog/skream-kernel-mode-exploits-mitigations-rest-us/ 16 | https://www.sentinelone.com/blog/skream-reloaded-randomizing-kernel-pool-allocations/ 17 | 18 | The configuration of the driver can be controlled through the config.h file, where you can enable/disable each mitigation and change default values for some of the mitigations. 19 | 20 | Notice: 21 | 1. You can't enable both PoolBloater and PoolSlider at the same time. 22 | 2. If PoolSlider is enabled, the driver can't be loaded early in the boot (start_type= system), since it will conflict with some system drivers and crash the system. 23 | 3. Use SKREAM at your own risk! 24 | -------------------------------------------------------------------------------- /SKREAM.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.27703.2000 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SKREAM", "SKREAM\SKREAM.vcxproj", "{C41E94E8-8E60-4BDD-8DA6-DEA9B9779351}" 7 | EndProject 8 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SKREAM_tester", "SKREAM\bad0b0b0\bad0b0b0.vcxproj", "{11AAF0D3-A0B7-4D7F-9566-72681A62AF83}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|x64 = Debug|x64 13 | Debug|x86 = Debug|x86 14 | Release|x64 = Release|x64 15 | Release|x86 = Release|x86 16 | EndGlobalSection 17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 18 | {C41E94E8-8E60-4BDD-8DA6-DEA9B9779351}.Debug|x64.ActiveCfg = Debug|x64 19 | {C41E94E8-8E60-4BDD-8DA6-DEA9B9779351}.Debug|x64.Build.0 = Debug|x64 20 | {C41E94E8-8E60-4BDD-8DA6-DEA9B9779351}.Debug|x64.Deploy.0 = Debug|x64 21 | {C41E94E8-8E60-4BDD-8DA6-DEA9B9779351}.Debug|x86.ActiveCfg = Debug|Win32 22 | {C41E94E8-8E60-4BDD-8DA6-DEA9B9779351}.Debug|x86.Build.0 = Debug|Win32 23 | {C41E94E8-8E60-4BDD-8DA6-DEA9B9779351}.Debug|x86.Deploy.0 = Debug|Win32 24 | {C41E94E8-8E60-4BDD-8DA6-DEA9B9779351}.Release|x64.ActiveCfg = Release|x64 25 | {C41E94E8-8E60-4BDD-8DA6-DEA9B9779351}.Release|x64.Build.0 = Release|x64 26 | {C41E94E8-8E60-4BDD-8DA6-DEA9B9779351}.Release|x64.Deploy.0 = Release|x64 27 | {C41E94E8-8E60-4BDD-8DA6-DEA9B9779351}.Release|x86.ActiveCfg = Release|Win32 28 | {C41E94E8-8E60-4BDD-8DA6-DEA9B9779351}.Release|x86.Build.0 = Release|Win32 29 | {C41E94E8-8E60-4BDD-8DA6-DEA9B9779351}.Release|x86.Deploy.0 = Release|Win32 30 | {11AAF0D3-A0B7-4D7F-9566-72681A62AF83}.Debug|x64.ActiveCfg = Debug|x64 31 | {11AAF0D3-A0B7-4D7F-9566-72681A62AF83}.Debug|x64.Build.0 = Debug|x64 32 | {11AAF0D3-A0B7-4D7F-9566-72681A62AF83}.Debug|x64.Deploy.0 = Debug|x64 33 | {11AAF0D3-A0B7-4D7F-9566-72681A62AF83}.Debug|x86.ActiveCfg = Debug|Win32 34 | {11AAF0D3-A0B7-4D7F-9566-72681A62AF83}.Debug|x86.Build.0 = Debug|Win32 35 | {11AAF0D3-A0B7-4D7F-9566-72681A62AF83}.Release|x64.ActiveCfg = Release|x64 36 | {11AAF0D3-A0B7-4D7F-9566-72681A62AF83}.Release|x64.Build.0 = Release|x64 37 | {11AAF0D3-A0B7-4D7F-9566-72681A62AF83}.Release|x86.ActiveCfg = Release|Win32 38 | {11AAF0D3-A0B7-4D7F-9566-72681A62AF83}.Release|x86.Build.0 = Release|Win32 39 | EndGlobalSection 40 | GlobalSection(SolutionProperties) = preSolution 41 | HideSolutionNode = FALSE 42 | EndGlobalSection 43 | GlobalSection(ExtensibilityGlobals) = postSolution 44 | SolutionGuid = {A5083929-E0C6-43E4-90C8-249495BC7187} 45 | EndGlobalSection 46 | EndGlobal 47 | -------------------------------------------------------------------------------- /SKREAM/Config.cpp: -------------------------------------------------------------------------------- 1 | #include "Config.h" 2 | 3 | static_assert(!(USE_POOL_BLOATER_MITIGATION && USE_POOL_SLIDER_MITIGATION), 4 | "USE_POOL_BLOATER_MITIGATION and USE_POOL_SLIDER_MITIGATION are mutually exclusive"); 5 | 6 | static_assert(MIN_POOL_CHUNKS_TO_ADD >= 1, 7 | "MIN_POOL_CHUNKS_TO_ADD must be greater than or equal to 1"); 8 | 9 | static_assert(MAX_POOL_CHUNKS_TO_ADD >= MIN_POOL_CHUNKS_TO_ADD, 10 | "MAX_POOL_CHUNKS_TO_ADD must be greater than or equal to MIN_POOL_CHUNKS_TO_ADD"); -------------------------------------------------------------------------------- /SKREAM/Config.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // 4 | // Params for enabling/disabling specific mitigations. 5 | // 6 | 7 | #define USE_POOL_BLOATER_MITIGATION (0) 8 | #define USE_POOL_SLIDER_MITIGATION (1) 9 | #define USE_TYPE_INDEX_OVERWRITE_MITIGATION (1) 10 | 11 | // 12 | // Params for the PoolBloater mitigation. 13 | // 14 | 15 | #define MIN_POOL_CHUNKS_TO_ADD (1) 16 | #define MAX_POOL_CHUNKS_TO_ADD (5) 17 | 18 | // 19 | // Normally we only wish to hook drivers which are not an integral part of the OS so as not to anger PatchGuard. 20 | // 21 | 22 | #define MAX_SIGNING_LEVEL_TO_HOOK (SE_SIGNING_LEVEL_MICROSOFT) 23 | -------------------------------------------------------------------------------- /SKREAM/DetoursKernel.cpp: -------------------------------------------------------------------------------- 1 | #include "DetoursKernel.h" 2 | #include 3 | 4 | static inline 5 | PUCHAR 6 | RvaAdjust(_Pre_notnull_ PIMAGE_DOS_HEADER pDosHeader, _In_ ULONG raddr) 7 | { 8 | if (raddr != NULL) { 9 | return ((PUCHAR)pDosHeader) + raddr; 10 | } 11 | return NULL; 12 | } 13 | 14 | NTSTATUS 15 | NTAPI 16 | DetourEnumerateImportsEx( 17 | _In_opt_ PVOID hModule, 18 | _In_opt_ PVOID pContext, 19 | _In_opt_ PF_DETOUR_IMPORT_FILE_CALLBACK pfImportFile, 20 | _In_opt_ PF_DETOUR_IMPORT_FUNC_CALLBACK_EX pfImportFunc) 21 | { 22 | PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)hModule; 23 | 24 | __try { 25 | #pragma warning(suppress:6011) 26 | if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE) { 27 | return STATUS_INVALID_IMAGE_NOT_MZ; 28 | } 29 | 30 | PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)((PUCHAR)pDosHeader + 31 | pDosHeader->e_lfanew); 32 | if (pNtHeader->Signature != IMAGE_NT_SIGNATURE) { 33 | return STATUS_INVALID_IMAGE_FORMAT; 34 | } 35 | if (pNtHeader->FileHeader.SizeOfOptionalHeader == 0) { 36 | return STATUS_INVALID_IMAGE_FORMAT; 37 | } 38 | 39 | PIMAGE_IMPORT_DESCRIPTOR iidp 40 | = (PIMAGE_IMPORT_DESCRIPTOR) 41 | RvaAdjust(pDosHeader, 42 | pNtHeader->OptionalHeader 43 | .DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress); 44 | 45 | if (iidp == NULL) { 46 | return STATUS_INVALID_IMAGE_FORMAT; 47 | } 48 | 49 | for (; iidp->OriginalFirstThunk != 0; iidp++) { 50 | 51 | PCSTR pszName = (PCHAR)RvaAdjust(pDosHeader, iidp->Name); 52 | if (pszName == NULL) { 53 | return STATUS_INVALID_IMAGE_FORMAT; 54 | } 55 | 56 | PIMAGE_THUNK_DATA pThunks = (PIMAGE_THUNK_DATA) 57 | RvaAdjust(pDosHeader, iidp->OriginalFirstThunk); 58 | PVOID * pAddrs = (PVOID *) 59 | RvaAdjust(pDosHeader, iidp->FirstThunk); 60 | 61 | PVOID hFile = NULL; 62 | 63 | if (pfImportFile != NULL) { 64 | if (!pfImportFile(pContext, hFile, pszName)) { 65 | break; 66 | } 67 | } 68 | 69 | ULONG nNames = 0; 70 | if (pThunks) { 71 | for (; pThunks[nNames].u1.Ordinal; nNames++) { 72 | ULONG nOrdinal = 0; 73 | PCSTR pszFunc = NULL; 74 | 75 | if (IMAGE_SNAP_BY_ORDINAL(pThunks[nNames].u1.Ordinal)) { 76 | nOrdinal = (ULONG)IMAGE_ORDINAL(pThunks[nNames].u1.Ordinal); 77 | } 78 | else { 79 | pszFunc = (PCSTR)RvaAdjust(pDosHeader, 80 | (ULONG)pThunks[nNames].u1.AddressOfData + 2); 81 | } 82 | 83 | if (pfImportFunc != NULL) { 84 | if (!pfImportFunc(pContext, 85 | nOrdinal, 86 | pszFunc, 87 | &pAddrs[nNames])) { 88 | break; 89 | } 90 | } 91 | } 92 | if (pfImportFunc != NULL) { 93 | pfImportFunc(pContext, 0, NULL, NULL); 94 | } 95 | } 96 | } 97 | if (pfImportFile != NULL) { 98 | pfImportFile(pContext, NULL, NULL); 99 | } 100 | return STATUS_SUCCESS; 101 | } 102 | __except (GetExceptionCode() == STATUS_ACCESS_VIOLATION ? 103 | EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) { 104 | return STATUS_INVALID_IMAGE_FORMAT; 105 | } 106 | } 107 | 108 | -------------------------------------------------------------------------------- /SKREAM/DetoursKernel.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | typedef BOOLEAN (NTAPI *PF_DETOUR_IMPORT_FILE_CALLBACK)( 6 | _In_opt_ PVOID pContext, 7 | _In_opt_ PVOID hModule, 8 | _In_opt_ LPCSTR pszFile); 9 | 10 | // Same as PF_DETOUR_IMPORT_FUNC_CALLBACK but extra indirection on last parameter. 11 | typedef BOOLEAN (NTAPI *PF_DETOUR_IMPORT_FUNC_CALLBACK_EX)( 12 | _In_opt_ PVOID pContext, 13 | _In_ ULONG nOrdinal, 14 | _In_opt_ LPCSTR pszFunc, 15 | _In_opt_ PVOID* ppvFunc); 16 | 17 | // 18 | // Borrowed from the MS Detours library (https://github.com/Microsoft/Detours) and adapted to the kernel environment. 19 | // 20 | 21 | NTSTATUS 22 | NTAPI 23 | DetourEnumerateImportsEx( 24 | _In_opt_ PVOID hModule, 25 | _In_opt_ PVOID pContext, 26 | _In_opt_ PF_DETOUR_IMPORT_FILE_CALLBACK pfImportFile, 27 | _In_opt_ PF_DETOUR_IMPORT_FUNC_CALLBACK_EX pfImportFunc); -------------------------------------------------------------------------------- /SKREAM/Entry.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "TypeOverwriteMitigation.h" 3 | #include "PoolSliderMitigation.h" 4 | #include "PoolBloaterMitigation.h" 5 | #include "Config.h" 6 | 7 | extern "C" { 8 | DRIVER_INITIALIZE DriverEntry; 9 | } 10 | 11 | static 12 | VOID 13 | CreateProcessNotifyEx( 14 | _Inout_ PEPROCESS Process, 15 | _In_ HANDLE ProcessId, 16 | _In_opt_ PPS_CREATE_NOTIFY_INFO pCreateInfo 17 | ) 18 | { 19 | PAGED_CODE(); 20 | 21 | #if USE_TYPE_INDEX_OVERWRITE_MITIGATION && defined(_AMD64_) 22 | if (pCreateInfo == nullptr) { 23 | // The process is being terminated. 24 | return; 25 | } 26 | 27 | NTSTATUS status = MitigateObjectTypeOverwrite(ProcessId, Process); 28 | if (!NT_SUCCESS(status)) { 29 | DbgPrint("Failed to harden process %u against object type overwrite attack\n", HandleToULong(ProcessId)); 30 | } 31 | #else // _X86_ 32 | UNREFERENCED_PARAMETER(Process); 33 | UNREFERENCED_PARAMETER(ProcessId); 34 | UNREFERENCED_PARAMETER(pCreateInfo); 35 | #endif // USE_TYPE_INDEX_OVERWRITE_MITIGATION && defined(_AMD64_) 36 | 37 | } 38 | 39 | static 40 | VOID 41 | LoadImageNotify( 42 | _In_ PUNICODE_STRING FullImageName, 43 | _In_ HANDLE ProcessId, 44 | _In_ PIMAGE_INFO ImageInfo 45 | ) 46 | { 47 | #if USE_POOL_BLOATER_MITIGATION 48 | PoolBloaterLoadImageNotify(FullImageName, ProcessId, ImageInfo); 49 | #elif USE_POOL_SLIDER_MITIGATION 50 | PoolSliderLoadImageNotify(FullImageName, ProcessId, ImageInfo); 51 | #endif // USE_POOL_BLOATER_MITIGATION 52 | } 53 | 54 | VOID 55 | Unload( 56 | _In_ PDRIVER_OBJECT DriverObject 57 | ) 58 | { 59 | UNREFERENCED_PARAMETER(DriverObject); 60 | 61 | PsRemoveLoadImageNotifyRoutine(LoadImageNotify); 62 | PsSetCreateProcessNotifyRoutineEx(CreateProcessNotifyEx, TRUE); 63 | } 64 | 65 | NTSTATUS 66 | DriverEntry( 67 | _In_ PDRIVER_OBJECT DriverObject, 68 | _In_ PUNICODE_STRING RegistryPath 69 | ) 70 | { 71 | NTSTATUS status = STATUS_SUCCESS; 72 | 73 | UNREFERENCED_PARAMETER(RegistryPath); 74 | 75 | if (MmIsDriverVerifying(DriverObject)) { 76 | DbgPrint("*** WARNING: SKREAM might be incompatible with driver verifier! ***\n"); 77 | } 78 | 79 | DriverObject->DriverUnload = Unload; 80 | 81 | status = PsSetCreateProcessNotifyRoutineEx(CreateProcessNotifyEx, FALSE); 82 | if (!NT_SUCCESS(status)) { 83 | DbgPrint("Failed to register process creation notify routine, status = %08x\n", status); 84 | goto Exit; 85 | } 86 | 87 | status = PsSetLoadImageNotifyRoutine(LoadImageNotify); 88 | if (!NT_SUCCESS(status)) { 89 | DbgPrint("Failed to register load image notify routine, status = %08x\n", status); 90 | goto Exit; 91 | } 92 | 93 | DbgPrint("SKREAM was successfully loaded!\n"); 94 | 95 | Exit: 96 | if (!NT_SUCCESS(status)) { 97 | PsRemoveLoadImageNotifyRoutine(LoadImageNotify); 98 | PsSetCreateProcessNotifyRoutineEx(CreateProcessNotifyEx, TRUE); 99 | } 100 | 101 | return status; 102 | } 103 | -------------------------------------------------------------------------------- /SKREAM/NativeStructs7.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace win7 { 4 | 5 | // 6 | // Native structures W7 x64 SP1 7 | // 8 | #pragma warning(disable : 4214 4201) 9 | 10 | struct _MMVAD_FLAGS // Size=8 11 | { 12 | unsigned __int64 CommitCharge : 51; // Size=8 Offset=0 BitOffset=0 BitCount=51 13 | unsigned __int64 NoChange : 1; // Size=8 Offset=0 BitOffset=51 BitCount=1 14 | unsigned __int64 VadType : 3; // Size=8 Offset=0 BitOffset=52 BitCount=3 15 | unsigned __int64 MemCommit : 1; // Size=8 Offset=0 BitOffset=55 BitCount=1 16 | unsigned __int64 Protection : 5; // Size=8 Offset=0 BitOffset=56 BitCount=5 17 | unsigned __int64 Spare : 2; // Size=8 Offset=0 BitOffset=61 BitCount=2 18 | unsigned __int64 PrivateMemory : 1; // Size=8 Offset=0 BitOffset=63 BitCount=1 19 | }; 20 | 21 | struct _MMVAD_FLAGS3 // Size=8 22 | { 23 | unsigned __int64 PreferredNode : 6; // Size=8 Offset=0 BitOffset=0 BitCount=6 24 | unsigned __int64 Teb : 1; // Size=8 Offset=0 BitOffset=6 BitCount=1 25 | unsigned __int64 Spare : 1; // Size=8 Offset=0 BitOffset=7 BitCount=1 26 | unsigned __int64 SequentialAccess : 1; // Size=8 Offset=0 BitOffset=8 BitCount=1 27 | unsigned __int64 LastSequentialTrim : 15; // Size=8 Offset=0 BitOffset=9 BitCount=15 28 | unsigned __int64 Spare2 : 8; // Size=8 Offset=0 BitOffset=24 BitCount=8 29 | unsigned __int64 LargePageCreating : 1; // Size=8 Offset=0 BitOffset=32 BitCount=1 30 | unsigned __int64 Spare3 : 31; // Size=8 Offset=0 BitOffset=33 BitCount=31 31 | }; 32 | 33 | struct _MMVAD_FLAGS2 // Size=4 34 | { 35 | unsigned int FileOffset : 24; // Size=4 Offset=0 BitOffset=0 BitCount=24 36 | unsigned int SecNoChange : 1; // Size=4 Offset=0 BitOffset=24 BitCount=1 37 | unsigned int OneSecured : 1; // Size=4 Offset=0 BitOffset=25 BitCount=1 38 | unsigned int MultipleSecured : 1; // Size=4 Offset=0 BitOffset=26 BitCount=1 39 | unsigned int Spare : 1; // Size=4 Offset=0 BitOffset=27 BitCount=1 40 | unsigned int LongVad : 1; // Size=4 Offset=0 BitOffset=28 BitCount=1 41 | unsigned int ExtendableFile : 1; // Size=4 Offset=0 BitOffset=29 BitCount=1 42 | unsigned int Inherit : 1; // Size=4 Offset=0 BitOffset=30 BitCount=1 43 | unsigned int CopyOnWrite : 1; // Size=4 Offset=0 BitOffset=31 BitCount=1 44 | }; 45 | 46 | struct _MMSECURE_FLAGS // Size=4 47 | { 48 | unsigned long ReadOnly : 1; // Size=4 Offset=0 BitOffset=0 BitCount=1 49 | unsigned long NoWrite : 1; // Size=4 Offset=0 BitOffset=1 BitCount=1 50 | unsigned long Spare : 10; // Size=4 Offset=0 BitOffset=2 BitCount=10 51 | }; 52 | 53 | union ___unnamed710 // Size=8 54 | { 55 | struct 56 | { 57 | __int64 Balance : 2; // Size=8 Offset=0 BitOffset=0 BitCount=2 58 | }; 59 | struct _MMADDRESS_NODE * Parent; // Size=8 Offset=0 60 | }; 61 | 62 | union ___unnamed712 // Size=8 63 | { 64 | unsigned __int64 LongFlags; // Size=8 Offset=0 65 | struct _MMVAD_FLAGS VadFlags; // Size=8 Offset=0 66 | }; 67 | union ___unnamed713 // Size=8 68 | { 69 | unsigned __int64 LongFlags3; // Size=8 Offset=0 70 | struct _MMVAD_FLAGS3 VadFlags3; // Size=8 Offset=0 71 | }; 72 | 73 | union ___unnamed715 // Size=4 74 | { 75 | unsigned long LongFlags2; // Size=4 Offset=0 76 | struct _MMVAD_FLAGS2 VadFlags2; // Size=4 Offset=0 77 | }; 78 | 79 | union ___unnamed1322 // Size=8 80 | { 81 | struct _MMSECURE_FLAGS Flags; // Size=4 Offset=0 82 | void * StartVa; // Size=8 Offset=0 83 | }; 84 | 85 | struct _MMADDRESS_LIST // Size=16 86 | { 87 | union ___unnamed1322 u1; // Size=8 Offset=0 88 | void * EndVa; // Size=8 Offset=8 89 | }; 90 | 91 | union ___unnamed1319 // Size=16 92 | { 93 | struct _LIST_ENTRY List; // Size=16 Offset=0 94 | struct _MMADDRESS_LIST Secured; // Size=16 Offset=0 95 | }; 96 | 97 | union ___unnamed1320 // Size=8 98 | { 99 | struct _MMBANKED_SECTION * Banked; // Size=8 Offset=0 100 | struct _MMEXTEND_INFO * ExtendedInfo; // Size=8 Offset=0 101 | }; 102 | 103 | typedef struct _MMADDRESS_NODE // Size=40 104 | { 105 | union ___unnamed710 u1; 106 | struct _MMADDRESS_NODE * LeftChild; // Size=8 Offset=8 107 | struct _MMADDRESS_NODE * RightChild; // Size=8 Offset=16 108 | unsigned __int64 StartingVpn; // Size=8 Offset=24 109 | unsigned __int64 EndingVpn; // Size=8 Offset=32 110 | 111 | } MMADDRESS_NODE, *PMMADDRESS_NODE, *PMM_AVL_NODE; 112 | 113 | typedef struct _MM_AVL_TABLE // Size=64 114 | { 115 | struct _MMADDRESS_NODE BalancedRoot; // Size=40 Offset=0 116 | struct 117 | { 118 | unsigned __int64 DepthOfTree : 5; // Size=8 Offset=40 BitOffset=0 BitCount=5 119 | unsigned __int64 Unused : 3; // Size=8 Offset=40 BitOffset=5 BitCount=3 120 | unsigned __int64 NumberGenericTableElements : 56; // Size=8 Offset=40 BitOffset=8 BitCount=56 121 | }; 122 | void * NodeHint; // Size=8 Offset=48 123 | void * NodeFreeHint; // Size=8 Offset=56 124 | 125 | } MM_AVL_TABLE, *PMM_AVL_TABLE; 126 | 127 | typedef struct _MMVAD_SHORT // Size=64 128 | { 129 | union ___unnamed710 u1; // Size=8 Offset=0 130 | struct _MMVAD * LeftChild; // Size=8 Offset=8 131 | struct _MMVAD * RightChild; // Size=8 Offset=16 132 | unsigned __int64 StartingVpn; // Size=8 Offset=24 133 | unsigned __int64 EndingVpn; // Size=8 Offset=32 134 | union ___unnamed712 u; // Size=8 Offset=40 135 | void * PushLock; // Size=8 Offset=48 136 | union ___unnamed713 u5; // Size=8 Offset=56 137 | } MMVAD_SHORT, *PMMVAD_SHORT; 138 | 139 | typedef struct _MMVAD // Size=120 140 | { 141 | MMVAD_SHORT vadShort; 142 | union ___unnamed715 u2; // Size=4 Offset=64 143 | unsigned long pad0; // Size=4 Offset=68 144 | struct _SUBSECTION * Subsection; // Size=8 Offset=72 145 | struct _MMPTE * FirstPrototypePte; // Size=8 Offset=80 146 | struct _MMPTE * LastContiguousPte; // Size=8 Offset=88 147 | struct _LIST_ENTRY ViewLinks; // Size=16 Offset=96 148 | struct _EPROCESS * VadsProcess; // Size=8 Offset=112 149 | } MMVAD, *PMMVAD; 150 | 151 | typedef struct _MMVAD_LONG // Size=144 152 | { 153 | MMVAD vad; 154 | union ___unnamed1319 u3; // Size=16 Offset=120 155 | union ___unnamed1320 u4; // Size=8 Offset=136 156 | } MMVAD_LONG, *PMMVAD_LONG; 157 | 158 | typedef struct _POOL_HEADER // Size=16 159 | { 160 | union 161 | { 162 | struct 163 | { 164 | unsigned long PreviousSize : 8; // Size=4 Offset=0 BitOffset=0 BitCount=8 165 | unsigned long PoolIndex : 8; // Size=4 Offset=0 BitOffset=8 BitCount=8 166 | unsigned long BlockSize : 8; // Size=4 Offset=0 BitOffset=16 BitCount=8 167 | unsigned long PoolType : 8; // Size=4 Offset=0 BitOffset=24 BitCount=8 168 | }; 169 | unsigned long Ulong1; // Size=4 Offset=0 170 | }; 171 | unsigned long PoolTag; // Size=4 Offset=4 172 | union 173 | { 174 | struct _EPROCESS * ProcessBilled; // Size=8 Offset=8 175 | struct 176 | { 177 | unsigned short AllocatorBackTraceIndex; // Size=2 Offset=8 178 | unsigned short PoolTagHash; // Size=2 Offset=10 179 | }; 180 | }; 181 | } POOL_HEADER, *PPOOL_HEADER; 182 | 183 | typedef struct _HANDLE_TABLE 184 | { 185 | ULONG_PTR TableCode; 186 | struct _EPROCESS *QuotaProcess; 187 | HANDLE UniqueProcessId; 188 | void* HandleLock; 189 | struct _LIST_ENTRY HandleTableList; 190 | EX_PUSH_LOCK HandleContentionEvent; 191 | struct _HANDLE_TRACE_DEBUG_INFO *DebugInfo; 192 | int ExtraInfoPages; 193 | ULONG Flags; 194 | ULONG FirstFreeHandle; 195 | struct _HANDLE_TABLE_ENTRY *LastFreeHandleEntry; 196 | ULONG HandleCount; 197 | ULONG NextHandleNeedingPool; 198 | // More fields here... 199 | } HANDLE_TABLE, *PHANDLE_TABLE; 200 | 201 | #pragma warning(default : 4214 4201) 202 | 203 | inline auto GET_VAD_ROOT(PMM_AVL_TABLE Table) 204 | { 205 | return &Table->BalancedRoot; 206 | } 207 | 208 | } 209 | -------------------------------------------------------------------------------- /SKREAM/NativeStructs8.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace win8 { 4 | 5 | // 6 | // Native structures W8 x64 7 | // 8 | #pragma warning(disable : 4214 4201) 9 | 10 | struct _MMVAD_FLAGS // Size=4 11 | { 12 | unsigned long VadType : 3; // Size=4 Offset=0 BitOffset=0 BitCount=3 13 | unsigned long Protection : 5; // Size=4 Offset=0 BitOffset=3 BitCount=5 14 | unsigned long PreferredNode : 6; // Size=4 Offset=0 BitOffset=8 BitCount=6 15 | unsigned long NoChange : 1; // Size=4 Offset=0 BitOffset=14 BitCount=1 16 | unsigned long PrivateMemory : 1; // Size=4 Offset=0 BitOffset=15 BitCount=1 17 | unsigned long Teb : 1; // Size=4 Offset=0 BitOffset=16 BitCount=1 18 | unsigned long PrivateFixup : 1; // Size=4 Offset=0 BitOffset=17 BitCount=1 19 | unsigned long Spare : 13; // Size=4 Offset=0 BitOffset=18 BitCount=13 20 | unsigned long DeleteInProgress : 1; // Size=4 Offset=0 BitOffset=31 BitCount=1 21 | }; 22 | 23 | struct _MMVAD_FLAGS1 // Size=4 24 | { 25 | unsigned long CommitCharge : 31; // Size=4 Offset=0 BitOffset=0 BitCount=31 26 | unsigned long MemCommit : 1; // Size=4 Offset=0 BitOffset=31 BitCount=1 27 | }; 28 | 29 | struct _MMVAD_FLAGS2 // Size=4 30 | { 31 | unsigned long FileOffset : 24; // Size=4 Offset=0 BitOffset=0 BitCount=24 32 | unsigned long Large : 1; // Size=4 Offset=0 BitOffset=24 BitCount=1 33 | unsigned long TrimBehind : 1; // Size=4 Offset=0 BitOffset=25 BitCount=1 34 | unsigned long Inherit : 1; // Size=4 Offset=0 BitOffset=26 BitCount=1 35 | unsigned long CopyOnWrite : 1; // Size=4 Offset=0 BitOffset=27 BitCount=1 36 | unsigned long NoValidationNeeded : 1; // Size=4 Offset=0 BitOffset=28 BitCount=1 37 | unsigned long Spare : 3; // Size=4 Offset=0 BitOffset=29 BitCount=3 38 | }; 39 | 40 | struct _MMVAD_FLAGS3 // Size=8 41 | { 42 | unsigned __int64 PreferredNode : 6; // Size=8 Offset=0 BitOffset=0 BitCount=6 43 | unsigned __int64 Teb : 1; // Size=8 Offset=0 BitOffset=6 BitCount=1 44 | unsigned __int64 Spare : 1; // Size=8 Offset=0 BitOffset=7 BitCount=1 45 | unsigned __int64 SequentialAccess : 1; // Size=8 Offset=0 BitOffset=8 BitCount=1 46 | unsigned __int64 LastSequentialTrim : 15; // Size=8 Offset=0 BitOffset=9 BitCount=15 47 | unsigned __int64 Spare2 : 8; // Size=8 Offset=0 BitOffset=24 BitCount=8 48 | unsigned __int64 LargePageCreating : 1; // Size=8 Offset=0 BitOffset=32 BitCount=1 49 | unsigned __int64 Spare3 : 31; // Size=8 Offset=0 BitOffset=33 BitCount=31 50 | }; 51 | 52 | struct _MMSECURE_FLAGS // Size=4 53 | { 54 | unsigned long ReadOnly : 1; // Size=4 Offset=0 BitOffset=0 BitCount=1 55 | unsigned long NoWrite : 1; // Size=4 Offset=0 BitOffset=1 BitCount=1 56 | unsigned long SecNoChange : 1; 57 | unsigned long Spare : 10; // Size=4 Offset=0 BitOffset=2 BitCount=10 58 | }; 59 | 60 | struct _MI_VAD_SEQUENTIAL_INFO // Size=8 61 | { 62 | unsigned __int64 Length : 12; // Size=8 Offset=0 BitOffset=0 BitCount=12 63 | unsigned __int64 Vpn : 52; // Size=8 Offset=0 BitOffset=12 BitCount=52 64 | }; 65 | 66 | union ___unnamed1666 // Size=8 67 | { 68 | struct 69 | { 70 | __int64 Balance : 2; // Size=8 Offset=0 BitOffset=0 BitCount=2 71 | }; 72 | struct _MM_AVL_NODE * Parent; // Size=8 Offset=0 73 | }; 74 | 75 | union ___unnamed1784 // Size=4 76 | { 77 | unsigned long LongFlags; // Size=4 Offset=0 78 | struct _MMVAD_FLAGS VadFlags; // Size=4 Offset=0 79 | }; 80 | union ___unnamed1785 // Size=4 81 | { 82 | unsigned long LongFlags1; // Size=4 Offset=0 83 | struct _MMVAD_FLAGS1 VadFlags1; // Size=4 Offset=0 84 | }; 85 | 86 | union ___unnamed1883 // Size=4 87 | { 88 | unsigned long LongFlags2; // Size=4 Offset=0 89 | struct _MMVAD_FLAGS2 VadFlags2; // Size=4 Offset=0 90 | }; 91 | 92 | union ___unnamed1885 // Size=8 93 | { 94 | struct _MI_VAD_SEQUENTIAL_INFO SequentialVa; // Size=8 Offset=0 95 | struct _MMEXTEND_INFO * ExtendedInfo; // Size=8 Offset=0 96 | }; 97 | 98 | typedef struct _MM_AVL_NODE // Size=24 99 | { 100 | union ___unnamed1666 u1; // Size=8 Offset=0 101 | struct _MM_AVL_NODE * LeftChild; // Size=8 Offset=8 102 | struct _MM_AVL_NODE * RightChild; // Size=8 Offset=16 103 | } MM_AVL_NODE, *PMM_AVL_NODE, *PMMADDRESS_NODE; 104 | 105 | typedef struct _MM_AVL_TABLE // Size=48 106 | { 107 | struct _MM_AVL_NODE BalancedRoot; // Size=24 Offset=0 108 | struct 109 | { 110 | unsigned __int64 DepthOfTree : 5; // Size=8 Offset=24 BitOffset=0 BitCount=5 111 | unsigned __int64 TableType : 3; // Size=8 Offset=24 BitOffset=5 BitCount=3 112 | unsigned __int64 NumberGenericTableElements : 56; // Size=8 Offset=24 BitOffset=8 BitCount=56 113 | }; 114 | void * NodeHint; // Size=8 Offset=32 115 | void * NodeFreeHint; // Size=8 Offset=40 116 | } MM_AVL_TABLE, *PMM_AVL_TABLE; 117 | 118 | union ___unnamed1322 // Size=8 119 | { 120 | struct _MMSECURE_FLAGS Flags; // Size=4 Offset=0 121 | void * StartVa; // Size=8 Offset=0 122 | }; 123 | 124 | struct _MMADDRESS_LIST // Size=16 125 | { 126 | union ___unnamed1322 u1; // Size=8 Offset=0 127 | void * EndVa; // Size=8 Offset=8 128 | }; 129 | 130 | typedef struct _MI_VAD_EVENT_BLOCK 131 | { 132 | struct _MI_VAD_EVENT_BLOCK * Next; 133 | ULONG WaitReason; 134 | struct _MMADDRESS_LIST SecureInfo; 135 | } MI_VAD_EVENT_BLOCK, *PMI_VAD_EVENT_BLOCK; 136 | 137 | typedef struct _MMVAD_SHORT // Size=64 138 | { 139 | struct _MM_AVL_NODE VadNode; // Size=24 Offset=0 140 | unsigned long StartingVpn; // Size=4 Offset=24 141 | unsigned long EndingVpn; // Size=4 Offset=28 142 | void * PushLock; // Size=8 Offset=32 143 | union ___unnamed1784 u; // Size=4 Offset=40 144 | union ___unnamed1785 u1; // Size=4 Offset=44 145 | struct _MI_VAD_EVENT_BLOCK * EventList; // Size=8 Offset=48 146 | long ReferenceCount; // Size=4 Offset=56 147 | } MMVAD_SHORT, *PMMVAD_SHORT; 148 | 149 | typedef struct _MMVAD // Size=128 150 | { 151 | struct _MMVAD_SHORT Core; // Size=64 Offset=0 152 | union ___unnamed1883 u2; // Size=4 Offset=64 153 | struct _SUBSECTION * Subsection; // Size=8 Offset=72 154 | struct _MMPTE * FirstPrototypePte; // Size=8 Offset=80 155 | struct _MMPTE * LastContiguousPte; // Size=8 Offset=88 156 | struct _LIST_ENTRY ViewLinks; // Size=16 Offset=96 157 | struct _EPROCESS * VadsProcess; // Size=8 Offset=112 158 | union ___unnamed1885 u4; // Size=8 Offset=120 159 | } MMVAD, *PMMVAD; 160 | 161 | typedef struct _POOL_HEADER // Size=16 162 | { 163 | union 164 | { 165 | struct 166 | { 167 | unsigned long PreviousSize : 8; // Size=4 Offset=0 BitOffset=0 BitCount=8 168 | unsigned long PoolIndex : 8; // Size=4 Offset=0 BitOffset=8 BitCount=8 169 | unsigned long BlockSize : 8; // Size=4 Offset=0 BitOffset=16 BitCount=8 170 | unsigned long PoolType : 8; // Size=4 Offset=0 BitOffset=24 BitCount=8 171 | }; 172 | unsigned long Ulong1; // Size=4 Offset=0 173 | }; 174 | unsigned long PoolTag; // Size=4 Offset=4 175 | union 176 | { 177 | struct _EPROCESS * ProcessBilled; // Size=8 Offset=8 178 | struct 179 | { 180 | unsigned short AllocatorBackTraceIndex; // Size=2 Offset=8 181 | unsigned short PoolTagHash; // Size=2 Offset=10 182 | }; 183 | }; 184 | } POOL_HEADER, *PPOOL_HEADER; 185 | 186 | typedef struct _HANDLE_TABLE 187 | { 188 | ULONG NextHandleNeedingPool; 189 | long ExtraInfoPages; 190 | ULONG_PTR TableCode; 191 | struct _EPROCESS * QuotaProcess; 192 | LIST_ENTRY HandleTableList; 193 | ULONG UniqueProcessId; 194 | ULONG Flags; 195 | EX_PUSH_LOCK HandleContentionEvent; 196 | EX_PUSH_LOCK HandleTableLock; 197 | // More fields here... 198 | } HANDLE_TABLE, *PHANDLE_TABLE; 199 | 200 | #pragma warning(default : 4214 4201) 201 | 202 | inline auto GET_VAD_ROOT(PMM_AVL_TABLE Table) 203 | { 204 | return (Table->BalancedRoot.RightChild); 205 | } 206 | 207 | } 208 | -------------------------------------------------------------------------------- /SKREAM/PoolBloaterMitigation.cpp: -------------------------------------------------------------------------------- 1 | #include "PoolBloaterMitigation.h" 2 | #include "DetoursKernel.h" 3 | #include "Random.h" 4 | #include 5 | #include 6 | #include "Config.h" 7 | #include "PoolDefs.h" 8 | 9 | static 10 | PVOID 11 | NTAPI 12 | ExAllocatePoolWithTag_Hook( 13 | _In_ POOL_TYPE PoolType, 14 | _In_ SIZE_T NumberOfBytes, 15 | _In_ ULONG Tag 16 | ) 17 | { 18 | // 19 | // Currently we don't do anything to allocations bigger than a page size. 20 | // 21 | 22 | if (NumberOfBytes > BIG_POOL_ALLOCATION_THRESHOLD) { 23 | goto Exit; 24 | } 25 | 26 | // 27 | // Add a random number of chunks to the pool allocation without changing its base address or breaking its alignment. 28 | // 29 | 30 | auto PoolChunksToAdd = RNG::get().rand(MIN_POOL_CHUNKS_TO_ADD, MAX_POOL_CHUNKS_TO_ADD); 31 | NumberOfBytes += (PoolChunksToAdd * POOL_GRANULARITY); 32 | 33 | Exit: 34 | return ExAllocatePoolWithTag(PoolType, NumberOfBytes, Tag); 35 | } 36 | 37 | static 38 | PVOID 39 | NTAPI 40 | ExAllocatePool_Hook( 41 | _In_ POOL_TYPE PoolType, 42 | _In_ SIZE_T NumberOfBytes 43 | ) 44 | { 45 | // 46 | // ExAllocatePool is a mere wrapper for ExAllocatePoolWithTag. 47 | // 48 | 49 | return ExAllocatePoolWithTag_Hook(PoolType, NumberOfBytes, DEFAULT_ALLOCATION_TAG); 50 | } 51 | 52 | static 53 | BOOLEAN 54 | NTAPI 55 | ImportFuncCallbackEx( 56 | _In_opt_ PVOID pContext, 57 | _In_ ULONG nOrdinal, 58 | _In_opt_ PCSTR pszName, 59 | _In_opt_ PVOID *pvFunc) 60 | { 61 | UNREFERENCED_PARAMETER(nOrdinal); 62 | 63 | if (pvFunc && pszName) { 64 | 65 | // 66 | // Check if we encountered a function we wish to hook. 67 | // 68 | 69 | ULONG_PTR hookFunc = 0; 70 | 71 | if (strcmp(pszName, "ExAllocatePoolWithTag") == 0) { 72 | hookFunc = reinterpret_cast(ExAllocatePoolWithTag_Hook); 73 | } 74 | else if (strcmp(pszName, "ExAllocatePool") == 0) { 75 | hookFunc = reinterpret_cast(ExAllocatePool_Hook); 76 | } 77 | 78 | if (hookFunc) { 79 | auto pDriverName = reinterpret_cast(pContext); 80 | DbgPrint("Hooking function %s in driver %wZ\n", pszName, pDriverName); 81 | } 82 | else { 83 | goto Exit; 84 | } 85 | 86 | PMDL pImportEntryMdl = nullptr; 87 | BOOLEAN LockedPages = FALSE; 88 | 89 | __try { 90 | 91 | // 92 | // Patch the IAT entry. 93 | // 94 | 95 | pImportEntryMdl = IoAllocateMdl(pvFunc, sizeof(ULONG_PTR), FALSE, FALSE, nullptr); 96 | if (!pImportEntryMdl) { 97 | DbgPrint("Could not allocate an MDL for import patch, insufficient resources."); 98 | __leave; 99 | } 100 | 101 | // 102 | // Although the imported entry is expected to be in system address space this could still throw, 103 | // for example an in-page error. 104 | // 105 | 106 | __try { 107 | MmProbeAndLockPages(pImportEntryMdl, KernelMode, IoWriteAccess); 108 | } 109 | __except (EXCEPTION_EXECUTE_HANDLER) { 110 | DbgPrint("Exception while trying to probe and lock the import entry, status: 0x%08x", GetExceptionCode()); 111 | __leave; 112 | } 113 | 114 | LockedPages = TRUE; 115 | 116 | PVOID pWritableImportEntry = MmGetSystemAddressForMdlSafe(pImportEntryMdl, NormalPagePriority | MdlMappingNoExecute); 117 | if (!pWritableImportEntry) { 118 | DbgPrint("Failed acquiring a system VA for MDL, insufficient resources\n"); 119 | __leave; 120 | } 121 | 122 | NTSTATUS status = MmProtectMdlSystemAddress(pImportEntryMdl, PAGE_READWRITE); 123 | if (!NT_SUCCESS(status)) { 124 | DbgPrint("Failed protecting the MDL system address, status: 0x%08x", status); 125 | __leave; 126 | } 127 | 128 | *reinterpret_cast(pWritableImportEntry) = hookFunc; 129 | 130 | } 131 | __finally { 132 | if (pImportEntryMdl) { 133 | if (LockedPages) { 134 | MmUnlockPages(pImportEntryMdl); 135 | } 136 | 137 | IoFreeMdl(pImportEntryMdl); 138 | } 139 | } 140 | } 141 | 142 | Exit: 143 | return TRUE; 144 | } 145 | 146 | VOID 147 | PoolBloaterLoadImageNotify( 148 | _In_ PUNICODE_STRING FullImageName, 149 | _In_ HANDLE ProcessId, 150 | _In_ PIMAGE_INFO ImageInfo 151 | ) 152 | { 153 | UNREFERENCED_PARAMETER(FullImageName); 154 | UNREFERENCED_PARAMETER(ProcessId); 155 | 156 | if (!ImageInfo->SystemModeImage) { 157 | 158 | // 159 | // We only care about kernel-mode drivers. 160 | // 161 | 162 | return; 163 | } 164 | 165 | if (ImageInfo->ImageSignatureLevel > MAX_SIGNING_LEVEL_TO_HOOK) { 166 | 167 | // 168 | // We only wish to hook 3rd party products. 169 | // 170 | 171 | return; 172 | } 173 | 174 | // 175 | // Hook ExAllocatePool and ExAllocatePoolWithTag. 176 | // 177 | 178 | DetourEnumerateImportsEx(ImageInfo->ImageBase, FullImageName, nullptr, ImportFuncCallbackEx); 179 | } -------------------------------------------------------------------------------- /SKREAM/PoolBloaterMitigation.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | VOID 6 | PoolBloaterLoadImageNotify( 7 | _In_ PUNICODE_STRING FullImageName, 8 | _In_ HANDLE ProcessId, 9 | _In_ PIMAGE_INFO ImageInfo); 10 | 11 | -------------------------------------------------------------------------------- /SKREAM/PoolDefs.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // 4 | // POOL_HEADER structure taken from ReactOS. 5 | // This isn't the most up-to-date definition, but for our purposes it will do. 6 | // 7 | 8 | #pragma warning(disable : 4201) 9 | typedef struct _POOL_HEADER 10 | { 11 | union 12 | { 13 | #ifdef _AMD64_ 14 | struct 15 | { 16 | USHORT PreviousSize : 8; 17 | USHORT PoolIndex : 8; 18 | USHORT BlockSize : 8; 19 | USHORT PoolType : 8; 20 | }; 21 | #else // _X86_ 22 | struct 23 | { 24 | USHORT PreviousSize : 9; 25 | USHORT PoolIndex : 7; 26 | USHORT BlockSize : 9; 27 | USHORT PoolType : 7; 28 | }; 29 | #endif // _AMD64_ 30 | ULONG Ulong1; 31 | }; 32 | #ifdef _M_AMD64 33 | ULONG PoolTag; 34 | #endif 35 | union 36 | { 37 | #ifdef _M_AMD64 38 | PEPROCESS ProcessBilled; 39 | #else 40 | ULONG PoolTag; 41 | #endif 42 | struct 43 | { 44 | USHORT AllocatorBackTraceIndex; 45 | USHORT PoolTagHash; 46 | }; 47 | }; 48 | } POOL_HEADER, *PPOOL_HEADER; 49 | #pragma warning(default : 4201) 50 | 51 | #ifdef _AMD64_ 52 | #define POOL_GRANULARITY (0x10) 53 | #else // _X86_ 54 | #define POOL_GRANULARITY (0x8) 55 | #endif 56 | 57 | static_assert(sizeof(POOL_HEADER) == POOL_GRANULARITY, "Bad POOL_HEADER definition"); 58 | 59 | // 60 | // Evaluates to 0xfffffff8 on 32-bits and to 0xfffffffffffffff0 on 64-bits. 61 | // 62 | 63 | #define POOL_ALIGNMENT_MASK (MAXULONG_PTR - POOL_GRANULARITY + 1) 64 | 65 | // 66 | // Default pool tags used by ExAllocatePool and ExFreePool. 67 | // 68 | 69 | #define DEFAULT_ALLOCATION_TAG ('enoN') 70 | #define DEFAULT_FREE_TAG (0) 71 | 72 | // 73 | // Pool allocations greater than 4080 bytes (requiring a page or more) are handled by nt!ExpAllocateBigPool. 74 | // See https://media.blackhat.com/bh-dc-11/Mandt/BlackHat_DC_2011_Mandt_kernelpool-wp.pdf fore more details. 75 | // 76 | 77 | #define BIG_POOL_ALLOCATION_THRESHOLD (4080) 78 | -------------------------------------------------------------------------------- /SKREAM/PoolSliderMitigation.cpp: -------------------------------------------------------------------------------- 1 | #include "PoolSliderMitigation.h" 2 | #include "DetoursKernel.h" 3 | #include "Random.h" 4 | #include 5 | #include 6 | #include "PoolDefs.h" 7 | #include "Config.h" 8 | 9 | static 10 | ULONG 11 | GetPoolBlockSizeInBytes(_In_ PVOID pBlock) 12 | { 13 | auto pBlockHeader = reinterpret_cast(reinterpret_cast(pBlock) - sizeof(POOL_HEADER)); 14 | return (pBlockHeader->BlockSize * POOL_GRANULARITY); 15 | } 16 | 17 | static 18 | PVOID 19 | NTAPI 20 | ExAllocatePoolWithTag_Hook( 21 | _In_ POOL_TYPE PoolType, 22 | _In_ SIZE_T NumberOfBytes, 23 | _In_ ULONG Tag 24 | ) 25 | { 26 | // 27 | // Call original pool routine. 28 | // 29 | 30 | PVOID p = ExAllocatePoolWithTag(PoolType, NumberOfBytes, Tag); 31 | 32 | if (!p) { 33 | 34 | // 35 | // There is nothing we can do to failed allocations. 36 | // 37 | 38 | goto Exit; 39 | } 40 | 41 | if (NumberOfBytes > BIG_POOL_ALLOCATION_THRESHOLD) { 42 | 43 | // 44 | // Allocations bigger than a page size are handled separately. 45 | // 46 | 47 | goto Exit; 48 | } 49 | 50 | // 51 | // Retrieve the block size. 52 | // 53 | 54 | auto BlockSizeInBytes = GetPoolBlockSizeInBytes(p); 55 | 56 | // 57 | // Calculate the amount of padding we have. 58 | // 59 | 60 | ULONG Padding = BlockSizeInBytes - sizeof(POOL_HEADER) - static_cast(NumberOfBytes); 61 | if (Padding == 0) { 62 | 63 | // 64 | // We currently don't handle cases when the requested size is an exact multiple of the pool granularity. 65 | // Refer to the accompanying blog post for some ideas about handling this class of cases. 66 | // 67 | 68 | goto Exit; 69 | } 70 | 71 | if (Padding > POOL_GRANULARITY - 1) { 72 | 73 | // 74 | // This could happen when the specified pool type is CacheAligned. 75 | // In this case we'll only use the first 15 bytes of padding, 76 | // so it'll be easier to align the address when the allocation is freed. 77 | // 78 | 79 | Padding = POOL_GRANULARITY - 1; 80 | } 81 | 82 | // 83 | // Add a random delta to the allocation. 84 | // 85 | 86 | auto delta = RNG::get().rand(1, Padding); 87 | p = Add2Ptr(p, delta); 88 | 89 | Exit: 90 | return p; 91 | } 92 | 93 | static 94 | VOID 95 | ExFreePoolWithTag_Hook( 96 | _In_ PVOID P, 97 | _In_ ULONG Tag 98 | ) 99 | { 100 | // 101 | // Align back to normal pool granularity. 102 | // 103 | 104 | P = reinterpret_cast(reinterpret_cast(P) & (POOL_ALIGNMENT_MASK)); 105 | ExFreePoolWithTag(P, Tag); 106 | } 107 | 108 | static 109 | PVOID 110 | NTAPI 111 | ExAllocatePool_Hook( 112 | _In_ POOL_TYPE PoolType, 113 | _In_ SIZE_T NumberOfBytes 114 | ) 115 | { 116 | // 117 | // ExAllocatePool is a mere wrapper for ExAllocatePoolWithTag. 118 | // 119 | 120 | return ExAllocatePoolWithTag_Hook(PoolType, NumberOfBytes, DEFAULT_ALLOCATION_TAG); 121 | } 122 | 123 | static 124 | VOID 125 | ExFreePool_Hook( 126 | _In_ PVOID P 127 | ) 128 | { 129 | // 130 | // ExFreePool is a mere wrapper for ExFreePoolWithTag. 131 | // 132 | 133 | ExFreePoolWithTag_Hook(P, DEFAULT_FREE_TAG); 134 | } 135 | 136 | static 137 | VOID 138 | RtlFreeAnsiString_Hook( 139 | _In_ PANSI_STRING AnsiString 140 | ) 141 | { 142 | // 143 | // The documentation for RtlFreeAnsiString (see https://docs.microsoft.com/en-us/windows/desktop/api/winternl/nf-winternl-rtlfreeansistring) 144 | // clearly states that the purpose of this function is to "Free the string buffer allocated by RtlUnicodeStringToAnsiString." 145 | // 146 | // Unfortunately, some misbehaving drivers use this DDI to free a string buffer which was allocated directly by a 147 | // previous call to ExAllocatePool(WithTag). To overcome this discrepancy we chose to hook RtlFreeAnsiString and 148 | // align the string buffer ourselves if needed. Failure to do so will result in ExFreePool(WithTag) being called with 149 | // an unaligned pointer, thus leading to a inevitable BSOD. 150 | // 151 | 152 | auto P = reinterpret_cast(AnsiString->Buffer); 153 | P = reinterpret_cast(reinterpret_cast(P) & POOL_ALIGNMENT_MASK); 154 | AnsiString->Buffer = reinterpret_cast(P); 155 | RtlFreeAnsiString(AnsiString); 156 | } 157 | 158 | static 159 | BOOLEAN 160 | NTAPI 161 | ImportFuncCallbackEx( 162 | _In_opt_ PVOID pContext, 163 | _In_ ULONG nOrdinal, 164 | _In_opt_ PCSTR pszName, 165 | _In_opt_ PVOID *pvFunc) 166 | { 167 | UNREFERENCED_PARAMETER(nOrdinal); 168 | 169 | if (pvFunc && pszName) { 170 | 171 | // 172 | // Check if we encountered a function we wish to hook. 173 | // 174 | 175 | ULONG_PTR hookFunc = NULL; 176 | 177 | if (strcmp(pszName, "ExAllocatePoolWithTag") == 0) { 178 | hookFunc = reinterpret_cast(ExAllocatePoolWithTag_Hook); 179 | } 180 | else if (strcmp(pszName, "ExFreePoolWithTag") == 0) { 181 | hookFunc = reinterpret_cast(ExFreePoolWithTag_Hook); 182 | } 183 | else if (strcmp(pszName, "ExAllocatePool") == 0) { 184 | hookFunc = reinterpret_cast(ExAllocatePool_Hook); 185 | } 186 | else if (strcmp(pszName, "ExFreePool") == 0) { 187 | hookFunc = reinterpret_cast(ExFreePool_Hook); 188 | } 189 | else if (strcmp(pszName, "RtlFreeAnsiString") == 0 || strcmp(pszName, "RtlFreeUnicodeString") == 0) { 190 | hookFunc = reinterpret_cast(RtlFreeAnsiString_Hook); 191 | } 192 | 193 | if (hookFunc) { 194 | auto pDriverName = reinterpret_cast(pContext); 195 | DbgPrint("Hooking function %s in driver %wZ\n", pszName, pDriverName); 196 | } 197 | else { 198 | goto Exit; 199 | } 200 | 201 | PMDL pImportEntryMdl = nullptr; 202 | BOOLEAN LockedPages = FALSE; 203 | 204 | __try { 205 | 206 | // 207 | // Patch the IAT entry. 208 | // 209 | 210 | pImportEntryMdl = IoAllocateMdl(pvFunc, sizeof(ULONG_PTR), FALSE, FALSE, nullptr); 211 | if (!pImportEntryMdl) { 212 | DbgPrint("Could not allocate an MDL for import patch, insufficient resources."); 213 | __leave; 214 | } 215 | 216 | // 217 | // Although the import entry is expected to be in system address space this could still throw, 218 | // for example an in-page error. 219 | // 220 | 221 | __try { 222 | MmProbeAndLockPages(pImportEntryMdl, KernelMode, IoWriteAccess); 223 | } 224 | __except (EXCEPTION_EXECUTE_HANDLER) { 225 | DbgPrint("Exception while trying to probe and lock the import entry, status: 0x%08x", GetExceptionCode()); 226 | __leave; 227 | } 228 | 229 | LockedPages = TRUE; 230 | 231 | PVOID pWritableImportEntry = MmGetSystemAddressForMdlSafe(pImportEntryMdl, NormalPagePriority | MdlMappingNoExecute); 232 | if (!pWritableImportEntry) { 233 | DbgPrint("Failed acquiring a system VA for MDL, insufficient resources\n"); 234 | __leave; 235 | } 236 | 237 | NTSTATUS status = MmProtectMdlSystemAddress(pImportEntryMdl, PAGE_READWRITE); 238 | if (!NT_SUCCESS(status)) { 239 | DbgPrint("Failed protecting the MDL system address, status: 0x%08x", status); 240 | __leave; 241 | } 242 | 243 | *reinterpret_cast(pWritableImportEntry) = hookFunc; 244 | 245 | } 246 | __finally { 247 | if (pImportEntryMdl) { 248 | if (LockedPages) { 249 | MmUnlockPages(pImportEntryMdl); 250 | } 251 | 252 | IoFreeMdl(pImportEntryMdl); 253 | } 254 | } 255 | } 256 | 257 | Exit: 258 | return TRUE; 259 | } 260 | 261 | VOID 262 | PoolSliderLoadImageNotify( 263 | _In_ PUNICODE_STRING FullImageName, 264 | _In_ HANDLE ProcessId, 265 | _In_ PIMAGE_INFO ImageInfo 266 | ) 267 | { 268 | UNREFERENCED_PARAMETER(FullImageName); 269 | UNREFERENCED_PARAMETER(ProcessId); 270 | 271 | if (!ImageInfo->SystemModeImage) { 272 | 273 | // 274 | // We only care about kernel-mode drivers. 275 | // 276 | 277 | return; 278 | } 279 | 280 | if (ImageInfo->ImageSignatureLevel > MAX_SIGNING_LEVEL_TO_HOOK) { 281 | 282 | // 283 | // We only wish to hook 3rd party products. 284 | // 285 | 286 | return; 287 | } 288 | 289 | // 290 | // Hook some pool related routines. 291 | // 292 | 293 | DetourEnumerateImportsEx(ImageInfo->ImageBase, FullImageName, nullptr, ImportFuncCallbackEx); 294 | } 295 | -------------------------------------------------------------------------------- /SKREAM/PoolSliderMitigation.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | 7 | 8 | VOID 9 | PoolSliderLoadImageNotify( 10 | _In_ PUNICODE_STRING FullImageName, 11 | _In_ HANDLE ProcessId, 12 | _In_ PIMAGE_INFO ImageInfo); 13 | -------------------------------------------------------------------------------- /SKREAM/Random.cpp: -------------------------------------------------------------------------------- 1 | #include "Random.h" 2 | 3 | auto RNG::get() -> RNG& 4 | { 5 | static RNG instance; 6 | return instance; 7 | } 8 | 9 | ULONG RNG::rand() 10 | { 11 | // 12 | // We are not using RtlRandom(Ex) since these functions can only operate at IRQL <= APC_LEVEL, and we want our rand() 13 | // implementation to work at DISPATCH_LEVEL as well. 14 | // 15 | 16 | m_Seed = m_Seed * 1103515245 + 12345; 17 | return static_cast((m_Seed / 65536) % 32768); 18 | } 19 | 20 | ULONG RNG::rand(_In_ ULONG max) 21 | { 22 | return rand() % max; 23 | } 24 | 25 | ULONG RNG::rand(_In_ ULONG min, _In_ ULONG max) 26 | { 27 | return rand() % (max + 1 - min) + min; 28 | } 29 | 30 | RNG::RNG() 31 | { 32 | LARGE_INTEGER Counter = KeQueryPerformanceCounter(nullptr); 33 | m_Seed = Counter.LowPart ^ Counter.HighPart; 34 | } 35 | 36 | -------------------------------------------------------------------------------- /SKREAM/Random.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | class RNG 6 | { 7 | public: 8 | static RNG& get(); 9 | 10 | ULONG rand(); 11 | ULONG rand(_In_ ULONG max); 12 | ULONG rand(_In_ ULONG min, _In_ ULONG max); 13 | 14 | private: 15 | RNG(); 16 | ULONG m_Seed = 0; 17 | }; 18 | 19 | -------------------------------------------------------------------------------- /SKREAM/SKREAM.inf: -------------------------------------------------------------------------------- 1 | ; 2 | ; SKREAM.inf 3 | ; 4 | 5 | [Version] 6 | Signature="$WINDOWS NT$" 7 | Class=System 8 | ClassGuid={4d36e97d-e325-11ce-bfc1-08002be10318} 9 | Provider=%ManufacturerName% 10 | DriverVer= 11 | CatalogFile=SKREAM.cat 12 | 13 | [DestinationDirs] 14 | DefaultDestDir = 12 15 | 16 | 17 | [SourceDisksNames] 18 | 1 = %DiskName%,,,"" 19 | 20 | [SourceDisksFiles] 21 | 22 | 23 | [Manufacturer] 24 | %ManufacturerName%=Standard,NT$ARCH$ 25 | 26 | [Standard.NT$ARCH$] 27 | 28 | 29 | [Strings] 30 | ManufacturerName="" ;TODO: Replace with your manufacturer name 31 | ClassName="" 32 | DiskName="SKREAM Source Disk" -------------------------------------------------------------------------------- /SKREAM/SKREAM.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 | Debug 22 | ARM 23 | 24 | 25 | Release 26 | ARM 27 | 28 | 29 | Debug 30 | ARM64 31 | 32 | 33 | Release 34 | ARM64 35 | 36 | 37 | 38 | {C41E94E8-8E60-4BDD-8DA6-DEA9B9779351} 39 | {dd38f7fc-d7bd-488b-9242-7d8754cde80d} 40 | v4.5 41 | 12.0 42 | Debug 43 | Win32 44 | SKREAM 45 | 46 | 47 | 48 | Windows7 49 | true 50 | WindowsKernelModeDriver10.0 51 | Driver 52 | WDM 53 | Desktop 54 | <_NT_TARGET_VERSION>$(_NT_TARGET_VERSION_WIN7) 55 | 56 | 57 | Windows7 58 | false 59 | WindowsKernelModeDriver10.0 60 | Driver 61 | WDM 62 | Desktop 63 | <_NT_TARGET_VERSION>$(_NT_TARGET_VERSION_WIN7) 64 | 65 | 66 | Windows7 67 | true 68 | WindowsKernelModeDriver10.0 69 | Driver 70 | WDM 71 | 72 | 73 | Windows7 74 | false 75 | WindowsKernelModeDriver10.0 76 | Driver 77 | WDM 78 | 79 | 80 | Windows10 81 | true 82 | WindowsKernelModeDriver10.0 83 | Driver 84 | WDM 85 | 86 | 87 | Windows10 88 | false 89 | WindowsKernelModeDriver10.0 90 | Driver 91 | WDM 92 | 93 | 94 | Windows10 95 | true 96 | WindowsKernelModeDriver10.0 97 | Driver 98 | WDM 99 | 100 | 101 | Windows10 102 | false 103 | WindowsKernelModeDriver10.0 104 | Driver 105 | WDM 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | DbgengKernelDebugger 117 | 118 | 119 | DbgengKernelDebugger 120 | 121 | 122 | DbgengKernelDebugger 123 | 124 | 125 | DbgengKernelDebugger 126 | 127 | 128 | DbgengKernelDebugger 129 | 130 | 131 | DbgengKernelDebugger 132 | 133 | 134 | DbgengKernelDebugger 135 | 136 | 137 | DbgengKernelDebugger 138 | 139 | 140 | 141 | true 142 | false 143 | 144 | 145 | /integritycheck %(AdditionalOptions) 146 | 147 | 148 | 149 | 150 | true 151 | 152 | 153 | 154 | 155 | false 156 | 157 | 158 | /integritycheck %(AdditionalOptions) 159 | 160 | 161 | 162 | 163 | /integritycheck %(AdditionalOptions) 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | true 181 | true 182 | 183 | 184 | true 185 | true 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | -------------------------------------------------------------------------------- /SKREAM/SKREAM.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hpp;hxx;hm;inl;inc;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 | {8E41214B-6785-4CFE-B992-037D68949A14} 18 | inf;inv;inx;mof;mc; 19 | 20 | 21 | {6c89a815-0388-4927-a1c8-bccdc9e20cbc} 22 | 23 | 24 | {b0cb89aa-649e-4cf2-9132-a36bb1af8db9} 25 | 26 | 27 | {73810331-e83c-4863-9099-64ceea8035aa} 28 | 29 | 30 | {c0f1d137-0507-40b1-bb8e-c475e96a802a} 31 | 32 | 33 | {10996397-6cfa-4f40-8054-62e93d3e036f} 34 | 35 | 36 | {7a50e05a-19f2-47b0-99fd-844b1c1bafe5} 37 | 38 | 39 | 40 | 41 | Driver Files 42 | 43 | 44 | 45 | 46 | Source Files 47 | 48 | 49 | Source Files 50 | 51 | 52 | TypeOverwrite Mitigation\Source Files 53 | 54 | 55 | TypeOverwrite Mitigation\Source Files 56 | 57 | 58 | PoolOverflow Mitigation\Source Files 59 | 60 | 61 | PoolOverflow Mitigation\Source Files 62 | 63 | 64 | PoolOverflow Mitigation\Source Files 65 | 66 | 67 | PoolOverflow Mitigation\Source Files 68 | 69 | 70 | 71 | 72 | Header Files 73 | 74 | 75 | TypeOverwrite Mitigation\Header Files 76 | 77 | 78 | TypeOverwrite Mitigation\Header Files 79 | 80 | 81 | TypeOverwrite Mitigation\Header Files 82 | 83 | 84 | TypeOverwrite Mitigation\Header Files 85 | 86 | 87 | PoolOverflow Mitigation\Header Files 88 | 89 | 90 | PoolOverflow Mitigation\Header Files 91 | 92 | 93 | PoolOverflow Mitigation\Header Files 94 | 95 | 96 | PoolOverflow Mitigation\Header Files 97 | 98 | 99 | PoolOverflow Mitigation\Header Files 100 | 101 | 102 | -------------------------------------------------------------------------------- /SKREAM/SKREAM.vcxproj.user: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | TestSign 5 | CN=SentinelTestCA | 2EB334A249C3D3407ACA5D5AC8BD5592F4027A7E 6 | 7 | -------------------------------------------------------------------------------- /SKREAM/TypeOverwriteMitigation.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "NativeStructs7.h" 5 | #include "NativeStructs8.h" 6 | #include "VadUtils.h" 7 | 8 | enum PROCESS_ACCESS_MASK : ACCESS_MASK { 9 | PROCESS_TERMINATE = 0x0001, 10 | PROCESS_CREATE_THREAD = 0x0002, 11 | PROCESS_VM_OPERATION = 0x0008, 12 | PROCESS_VM_WRITE = 0x0020, 13 | PROCESS_CREATE_PROCESS = 0x0080, 14 | PROCESS_SET_QUOTA = 0x0100, 15 | PROCESS_SET_INFORMATION = 0x0200, 16 | PROCESS_SUSPEND_RESUME = 0x0800, 17 | PROCESS_QUERY_LIMITED_INFORMATION = 0x1000, 18 | PROCESS_SET_LIMITED_INFORMATION = 0x2000, 19 | PROCESS_QUERY_INFORMATION = 0x0400, 20 | }; 21 | 22 | enum VAD_POOL_TAGS : ULONG { 23 | LONG_VAD_POOL_TAG = 'ldaV', 24 | SHORT_VAD_POOL_TAG = 'SdaV', 25 | }; 26 | 27 | /** 28 | * kd> dps nt!ObTypeIndexTable 29 | * fffff800`02c75d40 00000000`00000000 30 | * fffff800`02c75d48 00000000`bad0b0b0 31 | * fffff800`02c75d50 fffffa80`01848b30 32 | * fffff800`02c75d58 fffffa80`018489e0 33 | * fffff800`02c75d60 fffffa80`018e5080 34 | * fffff800`02c75d68 fffffa80`018e5e50 35 | * fffff800`02c75d70 fffffa80`018e5c30 36 | * fffff800`02c75d78 fffffa80`018e5ae0 37 | * fffff800`02c75d80 fffffa80`018e5990 38 | * fffff800`02c75d88 fffffa80`018e5840 39 | * fffff800`02c75d90 fffffa80`018e56f0 40 | * fffff800`02c75d98 fffffa80`018e54b0 41 | * fffff800`02c75da0 fffffa80`01966270 42 | * fffff800`02c75da8 fffffa80`018e8210 43 | * fffff800`02c75db0 fffffa80`0197b5c0 44 | * fffff800`02c75db8 fffffa80`01982620 45 | */ 46 | 47 | static constexpr ULONG MM_ALLOCATION_GRANULARITY = 64 * 1024; 48 | static constexpr ULONG_PTR OBJECT_TYPE_BAD0B0B0 = 0x00000000bad0b0b0; 49 | 50 | EXTERN_C PVOID PsGetProcessWow64Process(_In_ PEPROCESS); 51 | 52 | static 53 | NTSTATUS 54 | MitigateObjectTypeOverwriteWin8( 55 | _In_ PEPROCESS processObject 56 | ) 57 | { 58 | NTSTATUS status = STATUS_SUCCESS; 59 | 60 | // 61 | // Find the VAD node in the tree representing the preceding allocation. 62 | // 63 | 64 | win8::PMMVAD_SHORT shortVad = nullptr; 65 | status = BBFindVAD(processObject, OBJECT_TYPE_BAD0B0B0, &shortVad); 66 | 67 | if (!NT_SUCCESS(status)) { 68 | DbgPrint("Failed to find VAD, status = %08x\n", status); 69 | return status; 70 | } 71 | 72 | // 73 | // Allocate a longer VAD. 74 | // 75 | 76 | win8::PMMVAD longerVad = nullptr; 77 | longerVad = static_cast(ExAllocatePoolWithTag(NonPagedPool, sizeof(win8::MMVAD), LONG_VAD_POOL_TAG)); 78 | 79 | if (!longerVad) { 80 | DbgPrint("Failed to allocate longer VAD, insufficient resources\n"); 81 | status = STATUS_INSUFFICIENT_RESOURCES; 82 | return status; 83 | } 84 | 85 | RtlZeroMemory(longerVad, sizeof(*longerVad)); 86 | 87 | // 88 | // Copy the short VAD into the long VAD. 89 | // 90 | 91 | longerVad->Core = *shortVad; 92 | 93 | // 94 | // Secure the VAD against malicious attempts to free, unprotect or modify it in any way. 95 | // There is no need to explicitly set 'MemCommit', 'PrivateMemory' and 'Protection' as they already had their 96 | // correct values when the short VAD was allocated. 97 | // 98 | // longerVad->Core.u1.VadFlags1.MemCommit = FALSE; 99 | // longerVad->Core.u.VadFlags.PrivateMemory = TRUE; 100 | // longerVad->Core.u.VadFlags.Protection = MM_NOACCESS; 101 | // 102 | 103 | status = SecureVAD(longerVad); 104 | if (!NT_SUCCESS(status)) { 105 | DbgPrint("Failed to secure VAD, status = %08x\n", status); 106 | return status; 107 | } 108 | 109 | // 110 | // Setup child nodes. 111 | // 112 | 113 | if (longerVad->Core.VadNode.LeftChild) { 114 | 115 | // 116 | // Our node has a left child, make its parent the new, long VAD. 117 | // 118 | 119 | longerVad->Core.VadNode.LeftChild->u1.Parent = reinterpret_cast(longerVad); 120 | } 121 | 122 | if (longerVad->Core.VadNode.RightChild) { 123 | 124 | // 125 | // Our node has a right child, make its parent the new, long VAD. 126 | // 127 | 128 | longerVad->Core.VadNode.RightChild->u1.Parent = reinterpret_cast(longerVad); 129 | } 130 | 131 | // 132 | // Link the new node back to the VAD tree. 133 | // 134 | 135 | PVOID oldShortVad = nullptr; 136 | 137 | if (shortVad->VadNode.u1.Parent->LeftChild == reinterpret_cast(shortVad)) { 138 | 139 | // 140 | // The short VAD is the left child of the parent node, so replace it with the corresponding long VAD. 141 | // 142 | 143 | oldShortVad = InterlockedExchangePointer( 144 | reinterpret_cast(&shortVad->VadNode.u1.Parent->LeftChild), 145 | reinterpret_cast(longerVad)); 146 | } 147 | else if (shortVad->VadNode.u1.Parent->RightChild == reinterpret_cast(shortVad)) { 148 | 149 | // 150 | // The short VAD is the right child of the parent node, so replace it with the corresponding long VAD. 151 | // 152 | 153 | oldShortVad = InterlockedExchangePointer( 154 | reinterpret_cast(&shortVad->VadNode.u1.Parent->RightChild), 155 | reinterpret_cast(longerVad)); 156 | } 157 | else { 158 | 159 | // 160 | // Anomaly! 161 | // 162 | 163 | DbgPrint("Couldn't link the long VAD back to the VAD tree.\n"); 164 | NT_ASSERT(FALSE); 165 | } 166 | 167 | // 168 | // Free the old VAD entry. 169 | // 170 | 171 | NT_ASSERT(oldShortVad == shortVad); 172 | ExFreePoolWithTag(oldShortVad, SHORT_VAD_POOL_TAG); 173 | 174 | return status; 175 | } 176 | 177 | static 178 | NTSTATUS 179 | MitigateObjectTypeOverwriteWin7( 180 | _In_ PEPROCESS processObject 181 | ) 182 | { 183 | NTSTATUS status = STATUS_SUCCESS; 184 | 185 | // 186 | // Find the VAD node in the tree representing the preceding allocation. 187 | // 188 | 189 | win7::PMMVAD_SHORT shortVad = nullptr; 190 | status = BBFindVAD(processObject, OBJECT_TYPE_BAD0B0B0, &shortVad); 191 | 192 | if (!NT_SUCCESS(status)) { 193 | DbgPrint("Failed to find VAD, status = %08x\n", status); 194 | return status; 195 | } 196 | 197 | // 198 | // Allocate a long VAD. 199 | // 200 | 201 | win7::PMMVAD_LONG longVad = nullptr; 202 | longVad = reinterpret_cast(ExAllocatePoolWithTag(NonPagedPool, sizeof(*longVad), LONG_VAD_POOL_TAG)); 203 | 204 | if (!longVad) { 205 | DbgPrint("Failed to allocate long VAD, insufficient resources\n"); 206 | status = STATUS_INSUFFICIENT_RESOURCES; 207 | return status; 208 | } 209 | 210 | RtlZeroMemory(longVad, sizeof(*longVad)); 211 | 212 | // 213 | // Copy the short VAD into the long VAD. 214 | // 215 | 216 | longVad->vad.vadShort = *shortVad; 217 | 218 | // 219 | // Secure the VAD against malicious attempts to free, unprotect or modify it in any way. 220 | // There is no need to explicitly set 'MemCommit', 'PrivateMemory' and 'Protection' as they already had their 221 | // correct values when the short VAD was allocated. 222 | // 223 | // longVad->vad.vadShort.u.VadFlags.MemCommit = FALSE; 224 | // longVad->vad.vadShort.u.VadFlags.PrivateMemory = TRUE; 225 | // longVad->vad.vadShort.u.VadFlags.Protection = MM_NOACCESS; 226 | // 227 | 228 | status = SecureVAD(longVad); 229 | if (!NT_SUCCESS(status)) { 230 | DbgPrint("Failed to secure VAD, status = %08x\n", status); 231 | return status; 232 | } 233 | 234 | // 235 | // Setup child nodes. 236 | // 237 | 238 | if (longVad->vad.vadShort.LeftChild) { 239 | 240 | // 241 | // Our node has a left child, make its parent the new, long VAD. 242 | // 243 | 244 | longVad->vad.vadShort.LeftChild->vadShort.u1.Parent = reinterpret_cast(longVad); 245 | } 246 | 247 | if (longVad->vad.vadShort.RightChild) { 248 | 249 | // 250 | // Our node has a right child, make its parent the new, long VAD. 251 | // 252 | 253 | longVad->vad.vadShort.RightChild->vadShort.u1.Parent = reinterpret_cast(longVad); 254 | } 255 | 256 | // 257 | // Link the new node back to the VAD tree. 258 | // 259 | 260 | PVOID oldShortVad = nullptr; 261 | 262 | if (shortVad->u1.Parent->LeftChild == reinterpret_cast(shortVad)) { 263 | 264 | // 265 | // The short VAD is the left child of the parent node, so replace it with the corresponding long VAD. 266 | // 267 | 268 | oldShortVad = InterlockedExchangePointer( 269 | reinterpret_cast(&shortVad->u1.Parent->LeftChild), 270 | reinterpret_cast(longVad)); 271 | } 272 | else if (shortVad->u1.Parent->RightChild == reinterpret_cast(shortVad)) { 273 | 274 | // 275 | // The short VAD is the right child of the parent node, so replace it with the corresponding long VAD. 276 | // 277 | 278 | oldShortVad = InterlockedExchangePointer( 279 | reinterpret_cast(&shortVad->u1.Parent->RightChild), 280 | reinterpret_cast(longVad)); 281 | } 282 | else { 283 | 284 | // 285 | // Anomaly! 286 | // 287 | 288 | DbgPrint("Couldn't link the long VAD back to the VAD tree.\n"); 289 | NT_ASSERT(FALSE); 290 | } 291 | 292 | // 293 | // Free the old VAD entry. 294 | // 295 | 296 | NT_ASSERT(oldShortVad == shortVad); 297 | ExFreePoolWithTag(oldShortVad, SHORT_VAD_POOL_TAG); 298 | 299 | return status; 300 | } 301 | 302 | NTSTATUS 303 | MitigateObjectTypeOverwrite( 304 | _In_ HANDLE processId, 305 | _In_ PEPROCESS processObject 306 | ) 307 | { 308 | NTSTATUS status = STATUS_SUCCESS; 309 | 310 | // 311 | // Check OS version. 312 | // 313 | 314 | if (RtlIsNtDdiVersionAvailable(NTDDI_WINBLUE) || !RtlIsNtDdiVersionAvailable(NTDDI_WIN7)) { 315 | DbgPrint("Unsuitable Windows version: this mitigation is only relevant for Windows 7 and 8.\n"); 316 | return STATUS_SUCCESS; 317 | } 318 | 319 | // 320 | // Open the target process. 321 | // 322 | 323 | OBJECT_ATTRIBUTES procAttrs{}; 324 | InitializeObjectAttributes(&procAttrs, nullptr, OBJ_KERNEL_HANDLE, nullptr, nullptr); 325 | 326 | CLIENT_ID cid{}; 327 | cid.UniqueProcess = processId; 328 | 329 | HANDLE hProcess = nullptr; 330 | 331 | status = ZwOpenProcess(&hProcess, PROCESS_VM_OPERATION, &procAttrs, &cid); 332 | if (!NT_SUCCESS(status)) { 333 | DbgPrint("Failed to open process with PID %u, status = %08x\n", HandleToULong(cid.UniqueProcess), status); 334 | return status; 335 | } 336 | 337 | PVOID baseAddress = nullptr; 338 | SIZE_T regionSize = 0; 339 | 340 | __try { 341 | 342 | // 343 | // Allocate the 0xbad0b0b0 region. 344 | // 345 | 346 | baseAddress = ALIGN_DOWN_POINTER_BY(OBJECT_TYPE_BAD0B0B0, MM_ALLOCATION_GRANULARITY); 347 | regionSize = MM_ALLOCATION_GRANULARITY; 348 | 349 | status = ZwAllocateVirtualMemory(hProcess, &baseAddress, 0, ®ionSize, MEM_RESERVE, PAGE_NOACCESS); 350 | if (!NT_SUCCESS(status)) { 351 | if (!PsGetProcessWow64Process(processObject)) { 352 | 353 | // 354 | // For WoW64 processes this failure is for the most time expected as normally all address space above 355 | // the 2GB boundary is reserved and can't be allocated. 356 | // 357 | 358 | DbgPrint("Failed to allocate 0xbad0b0b0 region, status = %08x\n", status); 359 | } 360 | 361 | baseAddress = nullptr; 362 | __leave; 363 | } 364 | 365 | // 366 | // Prevent the 0xbad0b0b0 region from being allocated, freed or unmapped. 367 | // 368 | 369 | bool isWindows7 = !RtlIsNtDdiVersionAvailable(NTDDI_WIN8); 370 | 371 | if (isWindows7) { 372 | status = MitigateObjectTypeOverwriteWin7(processObject); 373 | } 374 | else { 375 | status = MitigateObjectTypeOverwriteWin8(processObject); 376 | } 377 | } 378 | __finally { 379 | if (!NT_SUCCESS(status)) { 380 | 381 | // 382 | // Cleanup. 383 | // 384 | 385 | if (baseAddress) { 386 | regionSize = 0; 387 | ZwFreeVirtualMemory(hProcess, &baseAddress, ®ionSize, MEM_RELEASE); 388 | } 389 | } 390 | 391 | if (hProcess) { 392 | ZwClose(hProcess); 393 | } 394 | } 395 | 396 | return status; 397 | } 398 | -------------------------------------------------------------------------------- /SKREAM/TypeOverwriteMitigation.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | NTSTATUS 6 | MitigateObjectTypeOverwrite( 7 | _In_ HANDLE processId, 8 | _In_ PEPROCESS processObject 9 | ); -------------------------------------------------------------------------------- /SKREAM/VadUtils.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "NativeStructs7.h" 3 | #include "NativeStructs8.h" 4 | 5 | enum MM_MAX_COMMIT : ULONG_PTR { 6 | Windows7 = 0x7ffffffffffff, 7 | Windows8 = 0x7fffffff, 8 | }; 9 | 10 | static constexpr ULONG VAD_EVENT_BLOCK_POOL_TAG = 'MmSe'; 11 | 12 | namespace win7 { 13 | TABLE_SEARCH_RESULT 14 | MiFindNodeOrParent( 15 | _In_ PMM_AVL_TABLE Table, 16 | _In_ ULONG_PTR StartingVpn, 17 | _Out_ PMMADDRESS_NODE * NodeOrParent 18 | ) 19 | 20 | /*++ 21 | 22 | Routine Description: 23 | 24 | This routine is used by all of the routines of the generic 25 | table package to locate the a node in the tree. It will 26 | find and return (via the NodeOrParent parameter) the node 27 | with the given key, or if that node is not in the tree it 28 | will return (via the NodeOrParent parameter) a pointer to 29 | the parent. 30 | 31 | Arguments: 32 | 33 | Table - The generic table to search for the key. 34 | 35 | StartingVpn - The starting virtual page number. 36 | 37 | NodeOrParent - Will be set to point to the node containing the 38 | the key or what should be the parent of the node 39 | if it were in the tree. Note that this will *NOT* 40 | be set if the search result is TableEmptyTree. 41 | 42 | Return Value: 43 | 44 | TABLE_SEARCH_RESULT - TableEmptyTree: The tree was empty. NodeOrParent 45 | is *not* altered. 46 | 47 | TableFoundNode: A node with the key is in the tree. 48 | NodeOrParent points to that node. 49 | 50 | TableInsertAsLeft: Node with key was not found. 51 | NodeOrParent points to what would 52 | be parent. The node would be the 53 | left child. 54 | 55 | TableInsertAsRight: Node with key was not found. 56 | NodeOrParent points to what would 57 | be parent. The node would be 58 | the right child. 59 | 60 | Environment: 61 | 62 | Kernel mode. The PFN lock is held for some of the tables. 63 | 64 | --*/ 65 | 66 | { 67 | PMMADDRESS_NODE Child; 68 | PMMADDRESS_NODE NodeToExamine; 69 | PMMVAD_SHORT VpnCompare; 70 | ULONG_PTR startVpn; 71 | ULONG_PTR endVpn; 72 | 73 | if (Table->NumberGenericTableElements == 0) { 74 | return TableEmptyTree; 75 | } 76 | 77 | NodeToExamine = (PMMADDRESS_NODE)GET_VAD_ROOT(Table); 78 | 79 | for (;;) { 80 | 81 | VpnCompare = (PMMVAD_SHORT)NodeToExamine; 82 | startVpn = VpnCompare->StartingVpn; 83 | endVpn = VpnCompare->EndingVpn; 84 | 85 | #if defined( _WIN81_ ) || defined( _WIN10_ ) 86 | startVpn |= (ULONG_PTR)VpnCompare->StartingVpnHigh << 32; 87 | endVpn |= (ULONG_PTR)VpnCompare->EndingVpnHigh << 32; 88 | #endif 89 | 90 | // 91 | // Compare the buffer with the key in the tree element. 92 | // 93 | 94 | if (StartingVpn < startVpn) { 95 | 96 | Child = NodeToExamine->LeftChild; 97 | 98 | if (Child != NULL) { 99 | NodeToExamine = Child; 100 | } 101 | else { 102 | 103 | // 104 | // Node is not in the tree. Set the output 105 | // parameter to point to what would be its 106 | // parent and return which child it would be. 107 | // 108 | 109 | *NodeOrParent = NodeToExamine; 110 | return TableInsertAsLeft; 111 | } 112 | } 113 | else if (StartingVpn <= endVpn) { 114 | 115 | // 116 | // This is the node. 117 | // 118 | 119 | *NodeOrParent = NodeToExamine; 120 | return TableFoundNode; 121 | } 122 | else { 123 | 124 | Child = NodeToExamine->RightChild; 125 | 126 | if (Child != NULL) { 127 | NodeToExamine = Child; 128 | } 129 | else { 130 | 131 | // 132 | // Node is not in the tree. Set the output 133 | // parameter to point to what would be its 134 | // parent and return which child it would be. 135 | // 136 | 137 | *NodeOrParent = NodeToExamine; 138 | return TableInsertAsRight; 139 | } 140 | } 141 | 142 | }; 143 | } 144 | } 145 | 146 | namespace win8 { 147 | TABLE_SEARCH_RESULT 148 | MiFindNodeOrParent( 149 | _In_ PMM_AVL_TABLE Table, 150 | _In_ ULONG_PTR StartingVpn, 151 | _Out_ PMMADDRESS_NODE * NodeOrParent 152 | ) 153 | 154 | /*++ 155 | 156 | Routine Description: 157 | 158 | This routine is used by all of the routines of the generic 159 | table package to locate the a node in the tree. It will 160 | find and return (via the NodeOrParent parameter) the node 161 | with the given key, or if that node is not in the tree it 162 | will return (via the NodeOrParent parameter) a pointer to 163 | the parent. 164 | 165 | Arguments: 166 | 167 | Table - The generic table to search for the key. 168 | 169 | StartingVpn - The starting virtual page number. 170 | 171 | NodeOrParent - Will be set to point to the node containing the 172 | the key or what should be the parent of the node 173 | if it were in the tree. Note that this will *NOT* 174 | be set if the search result is TableEmptyTree. 175 | 176 | Return Value: 177 | 178 | TABLE_SEARCH_RESULT - TableEmptyTree: The tree was empty. NodeOrParent 179 | is *not* altered. 180 | 181 | TableFoundNode: A node with the key is in the tree. 182 | NodeOrParent points to that node. 183 | 184 | TableInsertAsLeft: Node with key was not found. 185 | NodeOrParent points to what would 186 | be parent. The node would be the 187 | left child. 188 | 189 | TableInsertAsRight: Node with key was not found. 190 | NodeOrParent points to what would 191 | be parent. The node would be 192 | the right child. 193 | 194 | Environment: 195 | 196 | Kernel mode. The PFN lock is held for some of the tables. 197 | 198 | --*/ 199 | 200 | { 201 | PMMADDRESS_NODE Child; 202 | PMMADDRESS_NODE NodeToExamine; 203 | PMMVAD_SHORT VpnCompare; 204 | ULONG_PTR startVpn; 205 | ULONG_PTR endVpn; 206 | 207 | if (Table->NumberGenericTableElements == 0) { 208 | return TableEmptyTree; 209 | } 210 | 211 | NodeToExamine = (PMMADDRESS_NODE)GET_VAD_ROOT(Table); 212 | 213 | for (;;) { 214 | 215 | VpnCompare = (PMMVAD_SHORT)NodeToExamine; 216 | startVpn = VpnCompare->StartingVpn; 217 | endVpn = VpnCompare->EndingVpn; 218 | 219 | #if defined( _WIN81_ ) || defined( _WIN10_ ) 220 | startVpn |= (ULONG_PTR)VpnCompare->StartingVpnHigh << 32; 221 | endVpn |= (ULONG_PTR)VpnCompare->EndingVpnHigh << 32; 222 | #endif 223 | 224 | // 225 | // Compare the buffer with the key in the tree element. 226 | // 227 | 228 | if (StartingVpn < startVpn) { 229 | 230 | Child = NodeToExamine->LeftChild; 231 | 232 | if (Child != NULL) { 233 | NodeToExamine = Child; 234 | } 235 | else { 236 | 237 | // 238 | // Node is not in the tree. Set the output 239 | // parameter to point to what would be its 240 | // parent and return which child it would be. 241 | // 242 | 243 | *NodeOrParent = NodeToExamine; 244 | return TableInsertAsLeft; 245 | } 246 | } 247 | else if (StartingVpn <= endVpn) { 248 | 249 | // 250 | // This is the node. 251 | // 252 | 253 | *NodeOrParent = NodeToExamine; 254 | return TableFoundNode; 255 | } 256 | else { 257 | 258 | Child = NodeToExamine->RightChild; 259 | 260 | if (Child != NULL) { 261 | NodeToExamine = Child; 262 | } 263 | else { 264 | 265 | // 266 | // Node is not in the tree. Set the output 267 | // parameter to point to what would be its 268 | // parent and return which child it would be. 269 | // 270 | 271 | *NodeOrParent = NodeToExamine; 272 | return TableInsertAsRight; 273 | } 274 | } 275 | 276 | }; 277 | } 278 | } 279 | 280 | 281 | /// 282 | /// Find VAD that describes target address 283 | /// 284 | /// Target process object 285 | /// Address to find 286 | /// Found VAD. NULL if not found 287 | /// Status code 288 | NTSTATUS BBFindVAD(_In_ PEPROCESS pProcess, _In_ ULONG_PTR address, _Out_ win7::PMMVAD_SHORT * pResult) 289 | { 290 | NTSTATUS status = STATUS_SUCCESS; 291 | ULONG_PTR vpnStart = address >> PAGE_SHIFT; 292 | 293 | ASSERT(pProcess != NULL && pResult != NULL); 294 | if (pProcess == NULL || pResult == NULL) 295 | return STATUS_INVALID_PARAMETER; 296 | 297 | win7::PMM_AVL_TABLE pTable = (win7::PMM_AVL_TABLE)((PUCHAR)pProcess + 0x448/*dynData.VadRoot*/); 298 | win7::PMM_AVL_NODE pNode = win7::GET_VAD_ROOT(pTable); 299 | 300 | // Search VAD 301 | if (win7::MiFindNodeOrParent(pTable, vpnStart, &pNode) == TableFoundNode) 302 | { 303 | *pResult = (win7::PMMVAD_SHORT)pNode; 304 | } 305 | else 306 | { 307 | DbgPrint("BlackBone: %s: VAD entry for address 0x%p not found\n", __FUNCTION__, address); 308 | status = STATUS_NOT_FOUND; 309 | } 310 | 311 | return status; 312 | } 313 | 314 | 315 | /// 316 | /// Find VAD that describes target address 317 | /// 318 | /// Target process object 319 | /// Address to find 320 | /// Found VAD. NULL if not found 321 | /// Status code 322 | NTSTATUS BBFindVAD(_In_ PEPROCESS pProcess, _In_ ULONG_PTR address, _Out_ win8::PMMVAD_SHORT * pResult) 323 | { 324 | NTSTATUS status = STATUS_SUCCESS; 325 | ULONG_PTR vpnStart = address >> PAGE_SHIFT; 326 | 327 | ASSERT(pProcess != NULL && pResult != NULL); 328 | if (pProcess == NULL || pResult == NULL) 329 | return STATUS_INVALID_PARAMETER; 330 | 331 | win8::PMM_AVL_TABLE pTable = (win8::PMM_AVL_TABLE)((PUCHAR)pProcess + 0x590 /*dynData.VadRoot*/); 332 | win8::PMM_AVL_NODE pNode = win8::GET_VAD_ROOT(pTable); 333 | 334 | // Search VAD 335 | if (win8::MiFindNodeOrParent(pTable, vpnStart, &pNode) == TableFoundNode) 336 | { 337 | *pResult = (win8::PMMVAD_SHORT)pNode; 338 | } 339 | else 340 | { 341 | DbgPrint("BlackBone: %s: VAD entry for address 0x%p not found\n", __FUNCTION__, address); 342 | status = STATUS_NOT_FOUND; 343 | } 344 | 345 | return status; 346 | } 347 | 348 | NTSTATUS SecureVAD(_Out_ win7::PMMVAD_LONG pLongVad) 349 | { 350 | // 351 | // Setup VAD flags. 352 | // 353 | 354 | pLongVad->vad.vadShort.u.VadFlags.CommitCharge = MM_MAX_COMMIT::Windows7; 355 | pLongVad->vad.vadShort.u.VadFlags.NoChange = TRUE; 356 | 357 | pLongVad->vad.u2.VadFlags2.OneSecured = TRUE; 358 | pLongVad->vad.u2.VadFlags2.LongVad = TRUE; 359 | 360 | // 361 | // Make the entire range secure. 362 | // 363 | 364 | pLongVad->u3.Secured.u1.StartVa = reinterpret_cast(pLongVad->vad.vadShort.StartingVpn << PAGE_SHIFT); 365 | pLongVad->u3.Secured.EndVa = reinterpret_cast(((pLongVad->vad.vadShort.EndingVpn + 1) << PAGE_SHIFT) - 1); 366 | 367 | return STATUS_SUCCESS; 368 | } 369 | 370 | NTSTATUS SecureVAD(_Out_ win8::PMMVAD pVad) 371 | { 372 | NTSTATUS status = STATUS_SUCCESS; 373 | 374 | // 375 | // Setup VAD flags. 376 | // 377 | 378 | pVad->Core.u.VadFlags.NoChange = TRUE; 379 | pVad->Core.u1.VadFlags1.CommitCharge = MM_MAX_COMMIT::Windows8; 380 | 381 | // 382 | // Setup the VAD event block, which contains the memory range to secure. 383 | // 384 | 385 | auto pVadEventBlock = static_cast( 386 | ExAllocatePoolWithTag(NonPagedPool, sizeof(win8::MI_VAD_EVENT_BLOCK), VAD_EVENT_BLOCK_POOL_TAG)); 387 | 388 | if (!pVadEventBlock) { 389 | DbgPrint("Failed to allocate long VAD event block, insufficient resources\n"); 390 | status = STATUS_INSUFFICIENT_RESOURCES; 391 | goto Exit; 392 | } 393 | 394 | pVadEventBlock->Next = nullptr; 395 | pVadEventBlock->WaitReason = 2; // kd> !poolfind -tag "MmSe" -x "dt nt!_MI_VAD_EVENT_BLOCK @$extret WaitReason" 396 | 397 | // 398 | // Make the entire range secure. 399 | // 400 | 401 | pVadEventBlock->SecureInfo.u1.StartVa = reinterpret_cast(pVad->Core.StartingVpn << PAGE_SHIFT); 402 | pVadEventBlock->SecureInfo.EndVa = reinterpret_cast(((pVad->Core.EndingVpn + 1) << PAGE_SHIFT) - 1); 403 | 404 | pVad->Core.EventList = pVadEventBlock; 405 | 406 | Exit: 407 | return status; 408 | } -------------------------------------------------------------------------------- /SKREAM/VadUtils.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "NativeStructs7.h" 4 | #include "NativeStructs8.h" 5 | 6 | NTSTATUS BBFindVAD(_In_ PEPROCESS pProcess, _In_ ULONG_PTR address, _Out_ win7::PMMVAD_SHORT * pResult); 7 | NTSTATUS BBFindVAD(_In_ PEPROCESS pProcess, _In_ ULONG_PTR address, _Out_ win8::PMMVAD_SHORT * pResult); 8 | 9 | NTSTATUS SecureVAD(_Out_ win7::PMMVAD_LONG pLongVad); 10 | NTSTATUS SecureVAD(_Out_ win8::PMMVAD pVad); -------------------------------------------------------------------------------- /SKREAM/bad0b0b0/SKREAM_tester.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sentinel-One/SKREAM/7f9ea66e62c605215420afb2992bfe469f31d485/SKREAM/bad0b0b0/SKREAM_tester.cpp -------------------------------------------------------------------------------- /SKREAM/bad0b0b0/bad0b0b0.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 | 15.0 23 | {11AAF0D3-A0B7-4D7F-9566-72681A62AF83} 24 | Win32Proj 25 | bad0b0b0 26 | 10.0.17134.0 27 | SKREAM_tester 28 | 29 | 30 | 31 | Application 32 | true 33 | v141 34 | Unicode 35 | 36 | 37 | Application 38 | false 39 | v141 40 | true 41 | Unicode 42 | 43 | 44 | Application 45 | true 46 | v141 47 | Unicode 48 | 49 | 50 | Application 51 | false 52 | v141 53 | true 54 | Unicode 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | true 76 | 77 | 78 | true 79 | 80 | 81 | false 82 | 83 | 84 | false 85 | 86 | 87 | 88 | Use 89 | Level3 90 | Disabled 91 | true 92 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 93 | true 94 | 95 | 96 | Console 97 | true 98 | 99 | 100 | 101 | 102 | Use 103 | Level3 104 | Disabled 105 | true 106 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 107 | true 108 | MultiThreadedDebug 109 | 110 | 111 | Console 112 | true 113 | 114 | 115 | 116 | 117 | Use 118 | Level3 119 | MaxSpeed 120 | true 121 | true 122 | true 123 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 124 | true 125 | 126 | 127 | Console 128 | true 129 | true 130 | true 131 | 132 | 133 | 134 | 135 | Use 136 | Level3 137 | MaxSpeed 138 | true 139 | true 140 | true 141 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 142 | true 143 | 144 | 145 | Console 146 | true 147 | true 148 | true 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | Create 159 | Create 160 | Create 161 | Create 162 | 163 | 164 | 165 | 166 | 167 | -------------------------------------------------------------------------------- /SKREAM/bad0b0b0/bad0b0b0.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;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 | Header Files 20 | 21 | 22 | Header Files 23 | 24 | 25 | 26 | 27 | Source Files 28 | 29 | 30 | Source Files 31 | 32 | 33 | -------------------------------------------------------------------------------- /SKREAM/bad0b0b0/bad0b0b0.vcxproj.user: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /SKREAM/bad0b0b0/stdafx.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sentinel-One/SKREAM/7f9ea66e62c605215420afb2992bfe469f31d485/SKREAM/bad0b0b0/stdafx.cpp -------------------------------------------------------------------------------- /SKREAM/bad0b0b0/stdafx.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sentinel-One/SKREAM/7f9ea66e62c605215420afb2992bfe469f31d485/SKREAM/bad0b0b0/stdafx.h -------------------------------------------------------------------------------- /SKREAM/bad0b0b0/targetver.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sentinel-One/SKREAM/7f9ea66e62c605215420afb2992bfe469f31d485/SKREAM/bad0b0b0/targetver.h --------------------------------------------------------------------------------