mirror of
https://github.com/wavestone-cdt/EDRSandblast.git
synced 2026-06-08 16:37:12 +00:00
48a75a7029
Co-authored-by: Maxime Meignan <maxime.meignan@wavestone.com>
205 lines
6.7 KiB
C
205 lines
6.7 KiB
C
#include <Windows.h>
|
|
#include "UserlandHooks.h"
|
|
#include "PEParser.h"
|
|
#include "PEBBrowse.h"
|
|
|
|
#define INVALID_SYSCALL_NUMBER 0xFFFFFFFF
|
|
|
|
DWORD GetSyscallNumberFromMemoryScanning(LPCSTR ntFunctionName) {
|
|
PE* ntdll_disk;
|
|
PE* ntdll_mem;
|
|
getNtdllPEs(&ntdll_mem, &ntdll_disk);
|
|
DWORD syscallNumber = INVALID_SYSCALL_NUMBER;
|
|
|
|
PBYTE scanner = PE_functionAddr(ntdll_disk, ntFunctionName);
|
|
for (int i = 0; i < 0x10; i++, scanner++) {
|
|
PDWORD pPotentialSycallNumber = (PDWORD)(scanner + 1);
|
|
if (*scanner == 0xB8 && *pPotentialSycallNumber < 0x10000) { //B8 : mov eax, imm32
|
|
syscallNumber = *pPotentialSycallNumber;
|
|
break;
|
|
}
|
|
}
|
|
return syscallNumber;
|
|
}
|
|
|
|
typedef struct SYSCALL_t {
|
|
LPCSTR Name;
|
|
DWORD RVA;
|
|
DWORD Number;
|
|
}SYSCALL;
|
|
|
|
int CmpSyscallsByRVA(SYSCALL const* a, SYSCALL const* b) {
|
|
if (a->RVA < b->RVA) {
|
|
return -1;
|
|
}
|
|
else if (a->RVA > b->RVA) {
|
|
return +1;
|
|
}
|
|
else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
int CmpSyscallsByName(SYSCALL const* a, SYSCALL const* b) {
|
|
return strcmp(a->Name, b->Name);
|
|
}
|
|
|
|
DWORD g_nbSyscalls = 0;
|
|
DWORD g_nbSyscallsMax = 0;
|
|
SYSCALL* g_syscalls = NULL;
|
|
|
|
SYSCALL* GetSyscallTable(PDWORD syscallTableSize) {
|
|
if (g_syscalls != NULL) {
|
|
*syscallTableSize = g_nbSyscalls;
|
|
return g_syscalls;
|
|
}
|
|
g_nbSyscallsMax = 0x10;
|
|
g_syscalls = calloc(g_nbSyscallsMax, sizeof(SYSCALL));
|
|
if (!g_syscalls) {
|
|
return NULL;
|
|
}
|
|
PE* ntdll_mem = NULL;
|
|
PE* ntdll_disk = NULL;
|
|
getNtdllPEs(&ntdll_mem, &ntdll_disk);
|
|
|
|
// Store all Zw* function as a syscall
|
|
for (DWORD nameOrdinal = 0; nameOrdinal < ntdll_mem->exportedNamesLength; nameOrdinal++) {
|
|
LPCSTR functionName = PE_RVA_to_Addr(ntdll_mem, ntdll_mem->exportedNames[nameOrdinal]);
|
|
if (*(WORD*)functionName == *((WORD*)"Zw")) {
|
|
if (g_nbSyscalls == g_nbSyscallsMax) {
|
|
g_nbSyscallsMax *= 2;
|
|
PVOID tmp = realloc(g_syscalls, g_nbSyscallsMax * sizeof(SYSCALL));
|
|
if (!tmp) {
|
|
return NULL;
|
|
}
|
|
g_syscalls = tmp;
|
|
}
|
|
g_syscalls[g_nbSyscalls].Name = functionName;
|
|
g_syscalls[g_nbSyscalls].RVA = PE_functionRVA(ntdll_mem, functionName);
|
|
g_nbSyscalls++;
|
|
}
|
|
}
|
|
PVOID tmp = realloc(g_syscalls, g_nbSyscalls * sizeof(SYSCALL));
|
|
if (!tmp || !g_nbSyscalls) {
|
|
return NULL;
|
|
}
|
|
g_syscalls = tmp;
|
|
g_nbSyscallsMax = g_nbSyscalls;
|
|
|
|
// Sort the Zw* functions by RVA
|
|
qsort(g_syscalls, g_nbSyscalls, sizeof(SYSCALL), CmpSyscallsByRVA);
|
|
|
|
// Deduce the syscall numbers from order in table
|
|
for (DWORD j = 0; j < g_nbSyscalls; j++) {
|
|
g_syscalls[j].Number = j;
|
|
}
|
|
// Sort the function back in alphabetical order
|
|
qsort(g_syscalls, g_nbSyscalls, sizeof(SYSCALL), CmpSyscallsByName);
|
|
|
|
*syscallTableSize = g_nbSyscalls;
|
|
return g_syscalls;
|
|
}
|
|
|
|
DWORD GetSyscallNumberFromHardcodedInformation(LPCSTR ntFunctionName) {
|
|
PE* ntdll_mem = NULL;
|
|
PE* ntdll_disk = NULL;
|
|
getNtdllPEs(&ntdll_mem, &ntdll_disk);
|
|
|
|
DWORD syscallNumber = INVALID_SYSCALL_NUMBER;
|
|
|
|
if (!strcmp(ntFunctionName, "NtProtectVirtualMemory")) {
|
|
pRtlGetVersion RtlGetVersion = (pRtlGetVersion)PE_functionAddr(ntdll_mem, "RtlGetVersion");
|
|
OSVERSIONINFOEXW versionInformation = { 0 };
|
|
RtlGetVersion(&versionInformation);
|
|
if (versionInformation.dwMajorVersion == 10 && versionInformation.dwMinorVersion == 0 && versionInformation.dwBuildNumber <= 19044) {
|
|
syscallNumber = 0x50; // win10
|
|
}
|
|
else if (versionInformation.dwMajorVersion == 6 && versionInformation.dwMinorVersion == 3) {
|
|
syscallNumber = 0x4F; // win8.1 / 2012 R2
|
|
}
|
|
else if (versionInformation.dwMajorVersion == 6 && versionInformation.dwMinorVersion == 2) {
|
|
syscallNumber = 0x4E; // win8 / 2012
|
|
}
|
|
else if (versionInformation.dwMajorVersion <= 6) {
|
|
syscallNumber = 0x4D; // win7 / 2008 R2 & before
|
|
}
|
|
}
|
|
return syscallNumber;
|
|
}
|
|
|
|
|
|
DWORD GetSyscallNumberFromExportOrdering(LPCSTR ntFunctionName) {
|
|
DWORD syscallTableSize;
|
|
SYSCALL* syscallTable = GetSyscallTable(&syscallTableSize);
|
|
if (syscallTable == NULL) {
|
|
return INVALID_SYSCALL_NUMBER;
|
|
}
|
|
LPSTR zwFunctionName = _strdup(ntFunctionName);
|
|
if (zwFunctionName == NULL) {
|
|
return INVALID_SYSCALL_NUMBER;
|
|
}
|
|
*(WORD*)zwFunctionName = *(WORD*)"Zw";
|
|
|
|
DWORD down = 0;
|
|
DWORD up = syscallTableSize;
|
|
while (up - down > 1) {
|
|
DWORD mid = (down + up) / 2;
|
|
if (strcmp(syscallTable[mid].Name, zwFunctionName) <= 0) {
|
|
down = mid;
|
|
}
|
|
else {
|
|
up = mid;
|
|
}
|
|
}
|
|
if (!strcmp(syscallTable[down].Name, zwFunctionName)) {
|
|
return syscallTable[down].Number;
|
|
}
|
|
else {
|
|
return INVALID_SYSCALL_NUMBER;
|
|
}
|
|
}
|
|
|
|
PVOID CreateSyscallStubWithVirtuallAlloc(LPCSTR ntFunctionName) {
|
|
BYTE mov_eax_syscall_number[] = { 0xB8, 0x42, 0x42, 0x42, 0x42 };
|
|
BYTE mov_r10_rcx[] = { 0x4C, 0x8B, 0xD1 };
|
|
BYTE syscall_ret[] = { 0x0F, 0x05, 0xC3 };
|
|
|
|
SIZE_T shellcode_len = sizeof(mov_eax_syscall_number) + sizeof(mov_r10_rcx) + sizeof(syscall_ret);
|
|
PBYTE shellcode = VirtualAlloc(NULL, shellcode_len, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
|
|
if (!shellcode) {
|
|
return NULL;
|
|
}
|
|
PBYTE pShellcode = shellcode;
|
|
|
|
// get the syscall number through different techniques and check they give the same result
|
|
DWORD syscallNumber = INVALID_SYSCALL_NUMBER;
|
|
DWORD(*GetSyscallNumberFunc[])(LPCSTR) = { GetSyscallNumberFromMemoryScanning , GetSyscallNumberFromExportOrdering , GetSyscallNumberFromHardcodedInformation };
|
|
|
|
for (DWORD i = 0; i < _countof(GetSyscallNumberFunc); i++) {
|
|
DWORD syscallNumberCandidate = GetSyscallNumberFunc[i](ntFunctionName);
|
|
if (syscallNumberCandidate != INVALID_SYSCALL_NUMBER) {
|
|
if (syscallNumber != INVALID_SYSCALL_NUMBER && syscallNumber != syscallNumberCandidate) {
|
|
return NULL;
|
|
}
|
|
syscallNumber = syscallNumberCandidate;
|
|
}
|
|
}
|
|
|
|
if (syscallNumber == INVALID_SYSCALL_NUMBER) {
|
|
return NULL;
|
|
}
|
|
|
|
*(DWORD*)&mov_eax_syscall_number[1] = syscallNumber;
|
|
memcpy(pShellcode, mov_eax_syscall_number, sizeof(mov_eax_syscall_number));
|
|
pShellcode += sizeof(mov_eax_syscall_number);
|
|
memcpy(pShellcode, mov_r10_rcx, sizeof(mov_r10_rcx));
|
|
pShellcode += sizeof(mov_r10_rcx);
|
|
memcpy(pShellcode, syscall_ret, sizeof(syscall_ret));
|
|
pShellcode += sizeof(syscall_ret);
|
|
|
|
DWORD oldProtect;
|
|
VirtualProtect(shellcode, shellcode_len, PAGE_EXECUTE_READ, &oldProtect);
|
|
|
|
return shellcode;
|
|
}
|