• XSS.stack #1 – первый литературный журнал от юзеров форума

Статья RecycledGate Technique and Windows API Unhooking overview

Remio

HDD-drive
Пользователь
Регистрация
13.06.2025
Сообщения
42
Реакции
35
Author Remio
Source https://xss.pro


REMY Q | 17/06/2025 | RecycledGate Technique and Windows API Unhooking overview




In this here post I shall demonstrate and explain some classic anti-hooking techniques for WindowsAPI. In order to understand this concept we must first familiarize ourselves with API hooking.

When we make a program it is that case that it will need to interact with the OS, and will inevitably be calling some functions. It is also the case that some of these functions may be ‘hooked’, meaning they will be intercepted or redirected by AV/ EDRs for analysis at runtime. These hooks are used to analyse and monitor your programs behaviour.

Anti-hooking techniques aims to detect or bypass these hooks, allowing your program to interact directly with the intended system functions. Each type of hook has the purpose of intercepting or redirecting the flow of execution for your program, all for the undesirable purpose of debugging, analyziing and monotoring its operation.

I will go into details about the workings of each type of hook in future posts, for I shall keep it simple and briefly describe the most common types of hook you will encounter being placed on functions by EDR:

IAT Hooking (Import Address Table hooking): Modifies the addresses in a program’s Import Address Table to point to custom functions. Often used by injected DLLs to hijack API calls.

Export Address Table (EAT) Hooking: Alters the export table of a DLL (like ntdll.dll) so that functions resolve to alternate addresses. Less common, but still used in advanced hooking frameworks.

Syscall Hooking: Patches syscall stubs in user-mode DLLs (like ntdll) or modifies the kernel’s syscall table. Often used by rootkits or advanced EDRs. Can be bypassed with direct syscalls.

VEH/SEH Hooking: Uses exception handlers to control execution flow without modifying code directly. More stealthy, but detectable by checking for registered handlers in the process.

Inline Hooking: It overwrites the first bytes of a function with a JMP instruction. This allows execution of our program to be redirected before the function does any processing. Most hooking methods use a 32bit relative jump (opcode 0xE9), which takes up 5 bytes of space. Again, very commonly used by most standard EDRs and debuggers. We can be detected this hooks by comparing in memory bytes to a clean copy on disk, we will explore this later on in this post.


MessageBoxA inline hooking example

Standard (Hooked) API Call:
Код:
Your Code → MessageBoxA → JMP to EDR Hook in user32.dll → Monitored or Blocked

RecycledGate Flow for MessageBoxA:
Код:
Your Code → RG_MessageBoxA → RecycledGate → RecycledGateDescent → Clean syscall stub (e.g., NtUserMessageCall via unused function) → Kernel → MessageBox window appears



”All hope abandon, ye who enter here.” Over the gate to Hell, they should be made to crawl on their bellies to enter the kingdom of darkness…

Now we have an idea of what hooks are and how they work, we can start to think about methods & techniques to counter them. For this write up, I shall focus on the RecycledGate technique. To understand the RecycledGate technique , we must first understand the evolution of syscall based evasion methods, starting with Hell’s Gate.

Hell’s Gate is essentially a technique for dynamically resolving syscall IDs by parsing the in-memory export table of NTDLL, and constructing a clean syscall stub (a small piece of code that loads the syscall number and executes the syscall instruction to transition from user mode to kernel mode) in runtime memory. This bypassed inline hooks placed by usermode EDRs on functions like NtAllocateVirtualMemory or NtOpenProcess.

But as detection improved, security products started flagging manually crafted syscall stubs and memory with RWX protections. That led to the evolution of Halo’s Gate. Rather than crafting a syscall stub manually, Halo’s Gate searched for an unhooked syscall instruction (usually from a neighboring function), walked backwards from that point, and used that instead. This eliminated the need for shellcode-style allocations and reduced the footprint.

But Halo’s Gate still relies on assumptions, like static distances between syscalls, which made it susceptible to detection via behavioural patterns analysis. This lead to the development of Tartarus’ Gate. Tartarus’ Gate aims to be a more aggressive/flexible implementation, that scans the entire .text section of NTDLL for valid syscall instruction sequences that have not been overwritten by inline hooks. It introduces a generic mechanism to dynamically locate valid syscall instructions even when the target functions are hooked.

This leads us onto the technique we shall be examining in this paper: RecycledGate.

Essentially we can think of RecycledGate as a clean, reusable implementation of the Tartarus’ Gate concept. Rather than relying on offset assumptions or static signatures, it performs discovery of unhooked syscall instructions and maps them to specific API calls. What does this mean? think of it like this, instead of relying on hardcoded offsets or trying to guess where there is a a clean syscall, RecycledGate actively locates untouched syscall stubs: code sequences inside ntdll.dll that still contain the original mov r10, rcx; mov eax, SYSID; syscall pattern. These are usually from less commonly hooked functions that EDRs ignore. Once found, the stub is copied and the syscall number is patched to match the desired API (NtAllocateVirtualMemory, NtOpenProcess, etc.).

This allows you to bypass the inline hook entirely by using a ‘clean’ syscall gateway for a different syscall. It’s like rerouting the wires behind a access control panel, the button says “Open door” or something but behind the scenes, it can be rewired to unlock a vault. The watchers see nothing unusual, but the outcome is completely different.

The main takeaway here is that RecycledGate does not rely on shellcode or any fixed offsets. Instead it’s dynamically locating clean syscalls by walking ntdll in memory and searching for specific instruction patterns. It then recycles syscall stubs from unrelated or unhooked functions to better evade EDR, hence the name RecycledGate.

In the next part we shall explore an implimentation of this technique.



RecycledGate x64 demonstration Program

This demonstration tests 26 Windows APIs by detecting inline hooking and rerouting calls through recycled syscall stubs. It performs basic syscall chains across memory operations, file I/O, and handle management, logging which APIs succeed or fail when bypassing hooks. It provides you with a quick indication / validation of RecycledGate’s effectiveness in console log.

Github Link to Demo Program: https://github.com/RemyQue/RecycledGate-Demo

RecycledGate Project Source Files:

Код:
|—RecycledGate/
|- main.c: our main entry point
|- RecycledGate.h:function prototypes & constants
|- RecycledGate.c: Core implementation logic
|- recycledgate.asm: Assembly trampoline functions
|- structs.h: Our Windows internal structures

Assembly Engine (recycledgate.asm)

The syscall execution mechanism uses x64 AMD assembler to manage clean gate discovery and execution:
Код:
; Global syscall state storage
wSystemCall DWORD 000h                    ; Current syscall number
qRecycledGate QWORD 0000000000000000h     ; Address of clean syscall instructions

RecycledGate PROC
   mov wSystemCall, 000h       ; Clear previous state
   mov wSystemCall, ecx        ; Store syscall number (param 1)
   mov qRecycledGate, rdx      ; Store clean gate address (param 2)
   ret
RecycledGate ENDP

RecycledGateDescent PROC
   mov r10, rcx                ; Follow x64 syscall convention
   mov eax, wSystemCall        ; Load our syscall number
   jmp qword ptr [qRecycledGate] ; Execute through clean gate
RecycledGateDescent ENDP

Data Architecture (RecycledGate.c)

We track syscall info through structured tables:
Код:
typedef struct _RECYCLED_TABLE_ENTRY {
   PVOID   pAddress;        // Original function address
   DWORD64 dwHash;          // Function name hash
   WORD    wSystemCall;     // Syscall number
   PVOID   pRecycledGate;   // Clean syscall gate address
   BOOL    bHooked;         // Hook detection flag
} RECYCLED_TABLE_ENTRY;

typedef struct _RECYCLED_TABLE {
   RECYCLED_TABLE_ENTRY NtAllocateVirtualMemory;
   RECYCLED_TABLE_ENTRY NtProtectVirtualMemory;
   RECYCLED_TABLE_ENTRY NtCreateThreadEx;
   // … 23 additional APIs
} RECYCLED_TABLE;

Hook Detection Engine

See below the multiple detection mechanisms:
Код:
// Inline hook detection via JMP instruction scanning
for (INT j = 0; j < SYS_STUB_SIZE; j++) {
   if (pFunctionAddress[j] == 0xE9) {  // JMP rel32
       pTableEntry->bHooked = TRUE;
       break;
   }
}

// Syscall number extraction from mov eax, imm32
if (pFunctionAddress[j] == 0xB8) {
   WORD wSystemCall = *((PWORD)(pFunctionAddress + j + 1));
   pTableEntry->wSystemCall = wSystemCall;
}

// Clean gate discovery via pattern matching
if (pFunctionAddress[k] == 0x0F &&      // syscall
   pFunctionAddress[k + 1] == 0x05 && 
   pFunctionAddress[k + 2] == 0xC3) {  // ret
   pGate = (PVOID)(pFunctionAddress + k);
}

Intelligent Gate Recycling

When our target functions are hooked, the system searches for alternative clean gates:
Код:
// Bidirectional neighbor search for clean gates
if (pTableEntry->bHooked) {
   // Search forward through higher syscall numbers
   for (INT offset = 1; offset <= 10; offset++) {
       PBYTE pAltFunc = pFunctionAddress + (offset * 32);
       // Pattern match for clean syscall; ret sequence
   }
  
   // If unsuccessful, search backward through lower syscall numbers
   for (INT offset = 1; offset <= 10; offset++) {
       PBYTE pAltFunc = pFunctionAddress - (offset * 32);
       // Pattern match for clean syscall; ret sequence
   }
}

Stealth PE Parsing

Function resolution avoids monitored APIs through manual export table parsing:
Код:
// Direct export directory access
PIMAGE_EXPORT_DIRECTORY pImageExportDirectory =
   (PIMAGE_EXPORT_DIRECTORY)((PBYTE)pModuleBase +
 pImageNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]. VirtualAddress);

// Hash-based function identification
DWORD64 HashFunctionName(BYTE* str) {
   DWORD64 hash = 5381;
   INT c;
   while ((c = *str++))
       hash = ((hash << 5) + hash) + c;  // djb2 algorithm
   return hash;
}

Simple API Wrappers

RecycledGate provides dropin replacements for standard Windows APIs:
Код:
NTSTATUS RG_NtAllocateVirtualMemory(
   HANDLE ProcessHandle,
   PVOID* BaseAddress,
   ULONG_PTR ZeroBits,
   PSIZE_T RegionSize,
   ULONG AllocationType,
   ULONG Protect
) {
   // Configure syscall parameters
   RecycledGate(g_RecycledTable.NtAllocateVirtualMemory.wSystemCall,
                g_RecycledTable.NtAllocateVirtualMemory.pRecycledGate);
  
   // Execute through clean gate
   return RecycledGateDescent(ProcessHandle, BaseAddress, ZeroBits,
                             RegionSize, AllocationType, Protect);
}

Demo Console Interface

The demo runs in as a x64 console app that provides feedback during hook bypass testing on execution of the program.



  • Syscall Info Table: Lists each API with its syscall number, gate address, and hook status (YES/NO).
  • Hook Summary: Displays total, hooked, and clean function counts.
  • Summarizes all test outcomes.





Final Overview of method & Demo

The RecycledGate method represents a substantial evolution in syscall ‘gate’ evasion techniques, addressing the prior weaknesses that earlier methods like Hell’s Gate and Halo’s Gate had rather effectively. By dynamically locating and repurposing clean syscall instructions from unhooked functions, it eliminates the usual signatures that modern EDRs detect.

This is indeed a favourable technique to this day due to its elegance and simplicity, working within the security implementation itself. Every syscall executed properly through RecycledGate uses legitimate ntdll.dll code paths, making it nearly indistinguishable from normal applications, therefore (in theory, more often than not we hope) EDR sees what appears to be a standard function call, while your code achieves direct kernel access through an entirely different route.

As EDRs adapt their detection strategies techniques like RecycledGate will keep on evolving. The fundamental principle however, that is to leverage legitimate system components against themselves, will remain a a powerful approach that’s difficult to defend against without breaking normal application functionality for the foreseeable future in my opinion at least.

The source code and demo provide everything needed to understand and test this technique for yourself. Remember to use these methods responsibly and only in authorized testing environments.

If this demonstrations is well recieved I shall mabe expand on this more and provide examples for hells gate and more explanations. Thanks for reading.

- Remy

Github Link to project: https://github.com/RemyQue/RecycledGate-Demo


”As above, so below. As I believe the world to be, so it is.”
 
Последнее редактирование модератором:


Напишите ответ...
  • Вставить:
Прикрепить файлы
Верх