mirror of
https://github.com/e-fin/ADWS-BOF
synced 2026-06-23 09:33:17 +00:00
216 lines
10 KiB
C++
216 lines
10 KiB
C++
#include <windows.h>
|
|
#include <cassert>
|
|
|
|
extern "C" {
|
|
#ifdef _DEBUG
|
|
#undef DECLSPEC_IMPORT
|
|
#define DECLSPEC_IMPORT
|
|
#endif
|
|
#include "..\beacon.h"
|
|
}
|
|
|
|
namespace bof {
|
|
namespace mock {
|
|
namespace syscall {
|
|
/**
|
|
* @brief A function to find the first occurrence of a system call instruction in memory.
|
|
*
|
|
* This function searches for a specific system call instruction pattern within the
|
|
* memory block starting at the given address 'addr'. The pattern to search for
|
|
* depends on the target architecture:
|
|
*
|
|
* - On x64, the assembly pattern is: 'syscall; ret;' (0x0f, 0x05, 0xc3).
|
|
* - On x86, the assembly pattern is: 'sysenter; ret;' (0x0f, 0x34, 0xc3).
|
|
*
|
|
* @param addr A pointer to the starting address of the memory block.
|
|
* @return A pointer to the first occurrence of the system call instruction pattern
|
|
* if found, or NULL if the pattern is not found within the first 32 bytes.
|
|
*
|
|
*/
|
|
|
|
PBYTE FindSyscallInstruction(PBYTE addr) {
|
|
#if _M_X64
|
|
char syscallPattern[] = { '\x0f', '\x05', '\xc3' }; // syscall; ret;
|
|
#else
|
|
char syscallPattern[] = { '\x0f', '\x34', '\xc3' }; // sysenter; ret;
|
|
#endif
|
|
for (int offset = 0; offset < 32; ++offset) {
|
|
if (!memcmp(syscallPattern, (char*)addr + offset, sizeof(syscallPattern))) {
|
|
return addr + offset;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* @brief Find the system call number in the memory block
|
|
*
|
|
* This function searches for a specific pattern within the memory block starting at the
|
|
* given address 'addr' to identify the system call number. The pattern to search for
|
|
* depends on the target architecture:
|
|
*
|
|
* - On x64, the assembly pattern is: 'mov r10, rcx; mov eax, <syscall num>' (0x4c, 0x8b, 0xd1, 0xb8).
|
|
* - On x86, the assembly pattern is: 'mov eax, <syscall num>' (0xb8).
|
|
*
|
|
* @param addr A pointer to the starting address of the memory block.
|
|
* @return The system call number found in memory following the pattern, or 0 if the
|
|
* pattern is not found within the first 32 bytes.
|
|
*
|
|
*/
|
|
DWORD FindSyscallNumber(PBYTE addr) {
|
|
#if _M_X64
|
|
char syscallPattern[] = { '\x4c', '\x8b', '\xd1', '\xb8' };
|
|
#else
|
|
char syscallPattern[] = { '\xb8' };
|
|
#endif
|
|
for (int offset = 0; offset < 32; ++offset) {
|
|
if (!memcmp(syscallPattern, (char*)addr + offset, sizeof(syscallPattern))) {
|
|
DWORD* numAddress = (DWORD*)(addr + offset + sizeof(syscallPattern));
|
|
return *numAddress;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* A function to resolve the a system call number and function address.
|
|
*
|
|
* @param entry A pointer to a SYSCALL_API_ENTRY structure where resolved information will be stored.
|
|
* @param funcHash Hash value representing the target function to resolve.
|
|
* @return Returns TRUE if the resolution is successful; otherwise, returns FALSE.
|
|
*
|
|
*/
|
|
BOOL ResolveSyscallEntry(PSYSCALL_API_ENTRY entry, const char* funcName) {
|
|
// Resolve the NT function address
|
|
static HMODULE ntdll = LoadLibraryA("ntdll");
|
|
PVOID fnAddr = GetProcAddress(ntdll, funcName);
|
|
|
|
if (!fnAddr) {
|
|
return FALSE;
|
|
}
|
|
|
|
// Find the syscall number
|
|
DWORD sysnum = FindSyscallNumber((PBYTE)fnAddr);
|
|
|
|
// Find the address of the syscall instruction
|
|
PVOID jmpAddr = FindSyscallInstruction((PBYTE)fnAddr);
|
|
|
|
#ifdef _M_IX86
|
|
if (!jmpAddr) {
|
|
jmpAddr = (PVOID)__readfsdword(0xc0); // If WoW64, this returns wow64cpu!X86SwitchTo64BitMode
|
|
}
|
|
#endif
|
|
|
|
// We did not find the syscall
|
|
if (sysnum == 0 || jmpAddr == NULL) {
|
|
return FALSE;
|
|
}
|
|
|
|
// Fill the entry
|
|
entry->fnAddr = fnAddr;
|
|
entry->sysnum = sysnum;
|
|
entry->jmpAddr = jmpAddr;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* A helper macro for resolving an SYSCALL_API_ENTRY.
|
|
*
|
|
* @param field The field in the SYSCALL_API structure in which the resolved entry will be stored.
|
|
* @param name The function name used to generate a compile-time hash for entry lookup.
|
|
*/
|
|
#define RESOLVE_ENTRY(field, name) { \
|
|
if(!ResolveSyscallEntry(&field, name)) { assert(false && "Could not resolve the syscall entry"); } \
|
|
}
|
|
|
|
/**
|
|
* Resolve system call function addresses and syscall numbers.
|
|
*
|
|
* @param syscalls A pointer to a SYSCALL_API structure.
|
|
* @return TRUE if all system call entries are successfully resolved, FALSE otherwise.
|
|
*
|
|
*/
|
|
void ResolveSyscalls(PSYSCALL_API syscalls) {
|
|
RESOLVE_ENTRY(syscalls->ntAllocateVirtualMemory, "NtAllocateVirtualMemory");
|
|
RESOLVE_ENTRY(syscalls->ntAllocateVirtualMemory, "NtAllocateVirtualMemory");
|
|
RESOLVE_ENTRY(syscalls->ntProtectVirtualMemory, "NtProtectVirtualMemory");
|
|
RESOLVE_ENTRY(syscalls->ntFreeVirtualMemory, "NtFreeVirtualMemory");
|
|
RESOLVE_ENTRY(syscalls->ntGetContextThread, "NtGetContextThread");
|
|
RESOLVE_ENTRY(syscalls->ntSetContextThread, "NtSetContextThread");
|
|
RESOLVE_ENTRY(syscalls->ntResumeThread, "NtResumeThread");
|
|
RESOLVE_ENTRY(syscalls->ntCreateThreadEx, "NtCreateThreadEx");
|
|
RESOLVE_ENTRY(syscalls->ntOpenProcess, "NtOpenProcess");
|
|
RESOLVE_ENTRY(syscalls->ntOpenThread, "NtOpenThread");
|
|
RESOLVE_ENTRY(syscalls->ntClose, "NtClose");
|
|
RESOLVE_ENTRY(syscalls->ntCreateSection, "NtCreateSection");
|
|
RESOLVE_ENTRY(syscalls->ntMapViewOfSection, "NtMapViewOfSection");
|
|
RESOLVE_ENTRY(syscalls->ntUnmapViewOfSection, "NtUnmapViewOfSection");
|
|
RESOLVE_ENTRY(syscalls->ntQueryVirtualMemory, "NtQueryVirtualMemory");
|
|
RESOLVE_ENTRY(syscalls->ntDuplicateObject, "NtDuplicateObject");
|
|
RESOLVE_ENTRY(syscalls->ntReadVirtualMemory, "NtReadVirtualMemory");
|
|
RESOLVE_ENTRY(syscalls->ntWriteVirtualMemory, "NtWriteVirtualMemory");
|
|
RESOLVE_ENTRY(syscalls->ntReadFile, "NtReadFile");
|
|
RESOLVE_ENTRY(syscalls->ntWriteFile, "NtWriteFile");
|
|
RESOLVE_ENTRY(syscalls->ntCreateFile, "NtCreateFile");
|
|
|
|
/* Added in 411 */
|
|
RESOLVE_ENTRY(syscalls->ntQueueApcThread, "NtQueueApcThread");
|
|
RESOLVE_ENTRY( syscalls->ntCreateProcess, "NtCreateProcess");
|
|
RESOLVE_ENTRY(syscalls->ntOpenProcessToken, "NtOpenProcessToken");
|
|
RESOLVE_ENTRY(syscalls->ntQueryDirectoryFile, "NtQueryDirectoryFile");
|
|
RESOLVE_ENTRY(syscalls->ntTestAlert, "NtTestAlert");
|
|
RESOLVE_ENTRY(syscalls->ntSuspendProcess, "NtSuspendProcess");
|
|
RESOLVE_ENTRY(syscalls->ntResumeProcess, "NtResumeProcess");
|
|
RESOLVE_ENTRY(syscalls->ntQuerySystemInformation, "NtQuerySystemInformation");
|
|
RESOLVE_ENTRY(syscalls->ntSetInformationProcess, "NtSetInformationProcess");
|
|
RESOLVE_ENTRY(syscalls->ntSetInformationThread, "NtSetInformationThread");
|
|
RESOLVE_ENTRY(syscalls->ntQueryInformationProcess, "NtQueryInformationProcess");
|
|
RESOLVE_ENTRY(syscalls->ntQueryInformationThread, "NtQueryInformationThread");
|
|
RESOLVE_ENTRY(syscalls->ntOpenSection, "NtOpenSection");
|
|
RESOLVE_ENTRY(syscalls->ntAdjustPrivilegesToken, "NtAdjustPrivilegesToken");
|
|
RESOLVE_ENTRY(syscalls->ntDeviceIoControlFile, "NtDeviceIoControlFile");
|
|
RESOLVE_ENTRY(syscalls->ntWaitForMultipleObjects, "NtWaitForMultipleObjects");
|
|
}
|
|
|
|
/**
|
|
* A function to resolve the RTL function address.
|
|
*
|
|
* @param address A pointer to where the resolved information will be stored.
|
|
* @param funcHash Hash value representing the target function to resolve.
|
|
*
|
|
* @return Returns TRUE if the resolution is successful; otherwise, returns FALSE.
|
|
*
|
|
*/
|
|
BOOL ResolveNtdllFunc(PVOID* address, const char* funcName) {
|
|
static const HMODULE ntdll = LoadLibraryA("ntdll");
|
|
*address = GetProcAddress(ntdll, funcName);
|
|
return *address != NULL;
|
|
}
|
|
|
|
/**
|
|
* A helper macro for resolving a RTL function address.
|
|
*
|
|
* @param field The field in the RTL_API structure in which the resolved function address will be stored.
|
|
* @param name The function name used to generate a compile-time hash for entry lookup.
|
|
*/
|
|
#define RESOLVE_RTL_ENTRY(field, name) { \
|
|
if(!ResolveNtdllFunc(&field, name)) { assert(field && "Could not resolve RTL entry"); } \
|
|
}
|
|
|
|
/**
|
|
* Resolve RTL function addresses.
|
|
*
|
|
* @param rtls A pointer to a RTL_API structure.
|
|
*
|
|
*/
|
|
void ResolveRtls(PRTL_API rtls) {
|
|
/* Resolve the RTL function addresses */
|
|
RESOLVE_RTL_ENTRY(rtls->rtlDosPathNameToNtPathNameUWithStatusAddr, "RtlDosPathNameToNtPathName_U_WithStatus");
|
|
RESOLVE_RTL_ENTRY(rtls->rtlFreeHeapAddr, "RtlFreeHeap");
|
|
rtls->rtlGetProcessHeapAddr = GetProcessHeap();
|
|
assert(rtls->rtlGetProcessHeapAddr && "Could not get the process heap address");
|
|
}
|
|
}
|
|
}
|
|
} |