Rusty Hell's Gate / Halo's Gate / Tartarus' Gate and FreshyCalls / SysWhispers1 / SysWhispers2 / SysWhispers3 Library

Overview

Hell's Gate / Halo's Gate / Tartarus' Gate and FreshyCalls / SysWhispers1 / SysWhispers2 / SysWhispers3 in Rust

I named this project Mordor because Hell's Gate / Halo's Gate / Tartarus' Gate remind me of the Black Gate of Mordor in The Lord of the Rings for some weird reason haha and the project needs a cool name so why not?

BlackGate Credits to Middle-earth: Shadow of War Wiki

TODO

  • Use an egghunter like Syswhispers3
  • Add similar syscall functionality for hells_halos_tartarus_gate
  • Add functionality in hells_halos_tartarus_gate to unhook if all function in ntdll.dll are hooked aka Veles' Reek at SEKTOR7. (I would just use freshycalls_syswhispers instead).

Usage

The morder-rs project comes with 2 sub-projects called freshycalls_syswhispers and hells_halos_tartarus_gate. The difference between the Rust version of freshycalls_syswhispers and C/C++/Python version of Syswhispers1/Syswhispers2/Syswhispers3 is that this project does not generate header/ASM files and output like it but utilizes the same techniques and is to be used as a library.

  1. Add the library to your Rust Cargo.toml file by setting the git repository or local path and choosing the direct or indirect system call feature by setting _DIRECT_ or _INDIRECT_ as a feature. Please note you can only choose direct _DIRECT_ or _INDIRECT_ not both.
[dependencies]
freshycalls_syswhispers = { path = "../mordor-rs/freshycalls_syswhispers",  features = ["_DIRECT_"] }
[dependencies]
freshycalls_syswhispers = { path = "../mordor-rs/freshycalls_syswhispers",  features = ["_INDIRECT_"] }

or

[dependencies]
freshycalls_syswhispers = { git = "https://github.com/memN0ps/mordor-rs/tree/main/freshycalls_syswhispers", features = ["_DIRECT_"] }
[dependencies]
freshycalls_syswhispers = { git = "https://github.com/memN0ps/mordor-rs/tree/main/freshycalls_syswhispers", features = ["_INDIRECT_"] }
  1. Make use of the library
use freshycalls_syswhispers;
  1. Dynamically retrieve the SSN and/or syscall instruction from ntdll.dll even if functions are hooked and call any function using direct and/or indirect syscall. Note that when calling a function using the syscall macro the string will be obfuscated by hashing (NtClose in this example).
unsafe { syscall!("NtClose", process_handle) };

Example

Cargo.toml file:

[package]
name = "testing-rs"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
#freshycalls_syswhispers = { path = "../mordor-rs/freshycalls_syswhispers",  features = ["_DIRECT_"] }
freshycalls_syswhispers = { path = "../mordor-rs/freshycalls_syswhispers",  features = ["_INDIRECT_"] }

env_logger = "0.9.0"
log = "0.4.17"
sysinfo = "0.20.4"
obfstr = "0.3.0"
ntapi = { version = "0.4.0", features = ["impl-default"] }

[dependencies.windows-sys]
version = "0.36.1"
features = [
    "Win32_Foundation",
    "Win32_Security",
    "Win32_System_Threading",
    "Win32_UI_WindowsAndMessaging",
    "Win32_System_Memory",
    "Win32_System_Diagnostics_Debug",
    "Win32_System_SystemServices",
    "Win32_System_WindowsProgramming",
    "Win32_System_LibraryLoader",
]

main.rs file:

use std::{os::windows::raw::HANDLE, ptr::null_mut};

use freshycalls_syswhispers::{self, syscall, syscall_resolve::get_process_id_by_name};
use ntapi::{
    ntapi_base::CLIENT_ID,
    winapi::{
        shared::ntdef::{NT_SUCCESS, OBJECT_ATTRIBUTES},
        um::winnt::{PROCESS_VM_READ, PROCESS_VM_WRITE},
    },
};

fn main() {
    env_logger::init();

    let mut oa = OBJECT_ATTRIBUTES::default();

    let process_id = get_process_id_by_name("notepad.exe");
    let mut process_handle = process_id as HANDLE;

    let mut ci = CLIENT_ID {
        UniqueProcess: process_handle,
        UniqueThread: null_mut(),
    };

    let status = unsafe {
        syscall!(
            "NtOpenProcess",
            &mut process_handle,
            PROCESS_VM_WRITE | PROCESS_VM_READ,
            &mut oa,
            &mut ci
        )
    };

    log::debug!("status: {:#x}", status);

    if !NT_SUCCESS(status) {
        unsafe { syscall!("NtClose", process_handle) };
        panic!("Failed to get a handle to the target process");
    }

    log::debug!("Process Handle: {:?}", process_handle);
    unsafe { syscall!("NtClose", process_handle) };
}

Unit Tests

The project has been tested with both _DIRECT_ and _INDIRECT_ system call features enabled and passed the unit tests even with hooks in place.

[features]
default = ["_INDIRECT_"]
$ cargo test
<...redacted...>
     Running unittests src\lib.rs (target\debug\deps\freshycalls_syswhispers-8106a187dc70ecbf.exe)

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

     Running tests\syscaller.rs (target\debug\deps\syscaller-60e96237a57b1d9a.exe)

running 1 test
test tests::test_open_process ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.37s

   Doc-tests freshycalls_syswhispers

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
[features]
default = ["_DIRECT_"]
$ cargo test
<...redacted...>
     Running unittests src\lib.rs (target\debug\deps\freshycalls_syswhispers-7888fd45359c60a6.exe)

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

     Running tests\syscaller.rs (target\debug\deps\syscaller-546ab9a58b0eaa56.exe)

running 1 test
test tests::test_open_process ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.28s

   Doc-tests freshycalls_syswhispers

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

Description

Hooking

Hooking is a technique used to intercept calls to pre-existing functions or redirect the execution flow of a legitimate API to another location in memory. This memory location could be controlled by an attacker, anti-virus (AV), end-point detection and response (EDR), or anti-cheat (AC).

Inline Hooking: This allows us to replace the targeted function's first few bytes (assembly instructions) with a jump instruction to redirect execution flow to another location in memory.

Import Address Table Hooking (IAT hooking): The Import Address table is a lookup table of function pointers for functions imported from DLLs or executables. IAT hooking allows us to replace the function address in the Import Address Table with another to redirect the program's execution flow.

BlackGate Credits to Kyle Mistele

What is Hell's Gate / Halo's Gate / Tartarus' Gate?

Hell's Gate is a process injection technique that allows us to search for a number of bytes called the syscall stub, from the ntdll.dll module to extract the system call numbers and save them in a dedicated memory table, which is then used to call system APIs directly. However, the limitation of Hell's Gate is that it needs access to a clean ntdll.dll module if the functions are hooked. Otherwise, it cannot populate the needed syscall numbers and eventually fails to deliver native API calls. To address this problem, a twin sister was born called Halo's Gate, which is just a patch to Hell's Gate based on a very simple observation.

When a hooked is placed on a function (jmp <address>) we won't be able to dynamically retrieve the syscall numbers, so to address this problem we can look at the system call numbers of the neighboring functions and adjust the calculations accordingly to get our system call number, because syscall ID in the syscall stub follow each other incrementally.

Hell's Gate only checks for a sequence of bytes in the following order 4c8bd1b8, which looks like this in assembly:

mov r10, rcx
mov eax, <syscall>

Halo's gate does the same as Hell's Gate but with an additional check to see if there is a hook in place by checking if the first byte of the export is e9 (jmp) and if there is a hook in place then Halo's gate starts to look at the neighboring functions and adjust the calculations accordingly to get our system call number, since the syscall ID in the syscall stub follows each other incrementally.

However, not all EDRs hook in the same location (at the start of the function). What if EDRs hooks right after mov r10, rcx (4c8bd1)? This will break our code.

Tartarus' Gate solves this issue by adding an additional check to Halo's gate by searching for these bytes sequentially 4c8bd1e9 as well, which looks like this in assembly.

mov r10, rcx
jmp <address>

However, if all functions are hooked then we can find the first one and unhook all one by one starting from call ID 0. This method is called Veles' Reek at SEKTOR7.

FreshyCalls / SysWhispers1 / SysWhispers2 / SysWhispers3

The FreshyCalls technique searches the Export Directory for functions starting with Nt (excluding Ntdll) and sorts them by addresses. Surprisingly the lowest address is syscall number 0 and the next one will be syscall numbers 1 and 2 and 3... You can verify this in x64 dbg or Windbg yourself.

Syswhispers2 does the same thing but instead of searching for the Nt functions inside the export directory of ntdll.dll it searches for functions starting with Zw. Surprisingly Zw functions and Nt functions point to the same syscall stubs and will have the same system call number.

Here we can verify that: syswhisper2

syswhisper2

The difference between Zw anmd Nt functions are explained by Microsoft here:

"*The Windows native operating system services API is implemented as a set of routines that run in kernel mode. These routines have names that begin with the prefix Nt or Zw. Kernel-mode drivers can call these routines directly. User-mode applications can access these routines by using system calls.

With a few exceptions, each native system services routine has two slightly different versions that have similar names but different prefixes. For example, calls to NtCreateFile and ZwCreateFile perform similar operations and are, in fact, serviced by the same kernel-mode system routine.

For system calls from user mode, the Nt and Zw versions of a routine behave identically. For calls from a kernel-mode driver, the Nt and Zw versions of a routine differ in how they handle the parameter values that the caller passes to the routine.

A kernel-mode driver calls the Zw version of a native system services routine to inform the routine that the parameters come from a trusted, kernel-mode source. In this case, the routine assumes that it can safely use the parameters without first validating them. However, if the parameters might be from either a user-mode source or a kernel-mode source, the driver instead calls the Nt version of the routine, which determines, based on the history of the calling thread, whether the parameters originated in user mode or kernel mode*"

In a nutshell:

Hell's Gate: This will parse ntdll.dll to find the starting of the syscall stub (4c8bd1b8) and then retrieve the syscall ID. However, if the syscall stub is hooked then our code will break.

Halo's Gate: The same as Hell’s Gate, but adds an additional check to see if the first hooks are in place by checking if the first byte is e9 (jmp) and if there is a hook in place then Halo's gate starts to look at the neighboring functions and adjust the calculations accordingly to get our system call number since the syscall ID in the syscall stub follows each other incrementally.

Tartarus' Gate: The same Halo's Gate but adds an additional check to see if the syscall stub is hooked on the second line, after the assembly instructions mov r10, rcx by searching the following sequence of bytes: `4c8bd1e9.

FreshyCalls: This will search functions starting with Nt in the Export Directory and sorts them by addresses and the lowest address is the syscall identifier 0.

SysWhispers1: Uses the OS version information to select the correct system call number.

Syswhispers2: The same as FreshyCalls, but this will search for Zw functions in the Export Directory and store the name by replacing Zw with Nt.

SysWhispers3 This is very similar to SysWhispers2 with the exception that it also supports x86/WoW64, syscalls instruction replacement with an EGG (to be dynamically replaced), direct jumps to syscalls in x86/x64 mode (in WOW64 it's almost standard), direct jumps to random syscalls (borrowing @ElephantSeal's idea).

Exercise for the reader by: An excellent blog by Alice Climent-Pommeret and Kelzvirus

References and Credits

You might also like...
Cogo is a high-performance library for programming stackful coroutines with which you can easily develop and maintain massive concurrent programs.
Cogo is a high-performance library for programming stackful coroutines with which you can easily develop and maintain massive concurrent programs.

Cogo is a high-performance library for programming stackful coroutines with which you can easily develop and maintain massive concurrent programs.

A simple thread schedule and priority library for rust

thread-priority A simple library to control thread schedule policies and thread priority. If your operating system isn't yet supported, please, create

A simple library to get all pairs from any Dex and sync reserves.

pair_sync A simple library to get all pairs from any supported Dex and sync reserves. Crates.io Documentation in progress Filename: examples/sync-pair

Library and proc macro to analyze memory usage of data structures in rust.
Library and proc macro to analyze memory usage of data structures in rust.

Allocative: memory profiler for Rust This crate implements a lightweight memory profiler which allows object traversal and memory size introspection.

An actors library for Rust and Tokio designed to work with async / await message handlers out of the box.

Akt An actors framework for Rust and Tokio. It is heavily inspired by Actix and right now it has very similar look and feel. The main difference is th

Rust library for scheduling, managing resources, and running DAGs 🌙

🌙 moongraph 📈 moongraph is a Rust library for scheduling, managing resources, and running directed acyclic graphs. In moongraph, graph nodes are nor

Rust library for compiling and running other programs.

Exers 💻 Exers is a rust library for compiling and running code in different languages and runtimes. Usage example fn main() { // Imports...

Rust library for concurrent data access, using memory-mapped files, zero-copy deserialization, and wait-free synchronization.

mmap-sync mmap-sync is a Rust crate designed to manage high-performance, concurrent data access between a single writer process and multiple reader pr

This rust library is to get lottery monthly result loto6, numbers3 and numbers4

Scraping Japanese Lottery Site this library is to get lottery monthly result loto6, numbers3 and numbers4. scraping japanese lottery site from https:/

Owner
Pentesting | Red Teaming | Offensive Tool Development in Rust.
null
🔀 Rusty flow graph processing library

flowing flowing is a flow graph processing library written in Rust. It shall serve as a general-purpose building block for all kinds of dataflow progr

Micha Hanselmann 2 Oct 4, 2022
rusty-riscy is a performance testing and system resource monitoring tool written in Rust to benchmark RISC-V processors.

rusty-riscy rusty-riscy is a performance testing and system resource monitoring tool written in Rust to benchmark RISC-V processors. Objectives To cre

Suhas KV 4 May 3, 2022
🦀 My Rusty Playground

rust playground A language empowering everyone to build reliable and efficient software. Resources The Book Rustlings Rust by Example Take your first

Eliaz Bobadilla 5 Apr 6, 2022
Rust 核心库和标准库的源码级中文翻译,可作为 IDE 工具的智能提示 (Rust core library and standard library translation. can be used as IntelliSense for IDE tools)

Rust 标准库中文版 这是翻译 Rust 库 的地方, 相关源代码来自于 https://github.com/rust-lang/rust。 如果您不会说英语,那么拥有使用中文的文档至关重要,即使您会说英语,使用母语也仍然能让您感到愉快。Rust 标准库是高质量的,不管是新手还是老手,都可以从中

wtklbm 493 Jan 4, 2023
Omeglib, a portmanteau of "omegle" and "library", is a crate for interacting with omegle, simply and asynchronously

Omeglib, a portmanteau of "omegle" and "library", is a crate for interacting with omegle, simply and asynchronously. It is intended to suit one's every requirement regarding chat on omegle.

null 1 May 25, 2022
A tool and library to losslessly join multiple .mp4 files shot with same camera and settings

mp4-merge A tool and library to losslessly join multiple .mp4 files shot with same camera and settings. This is useful to merge multiple files that ar

Gyroflow 7 Jan 2, 2023
Rust library for hardware accelerated drawing of 2D shapes, images, and text, with an easy to use API.

Speedy2D Hardware-accelerated drawing of shapes, images, and text, with an easy to use API. Speedy2D aims to be: The simplest Rust API for creating a

null 223 Dec 26, 2022
Comprehensive DSP graph and synthesis library for developing a modular synthesizer in Rust, such as HexoSynth.

HexoDSP - Comprehensive DSP graph and synthesis library for developing a modular synthesizer in Rust, such as HexoSynth. This project contains the com

Weird Constructor 45 Dec 17, 2022
A library for transcoding between bytes in Astro Notation Format and Native Rust data types.

Rust Astro Notation A library for transcoding between hexadecimal strings in Astro Notation Format and Native Rust data types. Usage In your Cargo.tom

Stelar Software 1 Feb 4, 2022
A rust library that provides pseudo-reflection for structs and enums

Treeflection treeflection_derive Treeflection runs a command stored as a string on a tree of structs, collections and primitive types. Commands A comm

Lucas Kent 21 Nov 17, 2022