// dllmain.cpp : Defines the entry point for the DLL application. #include #include // Bytes needed for us to create a VTable and VTFixup #define VTALLOC_SIZE 12 // V-table constants #define COR_VTABLE_32BIT 0x01 // V-table slots are 32-bits in size. #define COR_VTABLE_64BIT 0x02 // V-table slots are 64-bits in size. #define COR_VTABLE_FROM_UNMANAGED 0x04 // If set, transition from unmanaged. #define COR_VTABLE_FROM_UNMANAGED_RETAIN_APPDOMAIN 0x08 // NEW #define COR_VTABLE_CALL_MOST_DERIVED 0x10 // Call most derived method described by typedef BOOL STDMETHODCALLTYPE _CorDllMain( HINSTANCE hInst, DWORD dwReason, LPVOID lpReserved ); typedef __int32 STDMETHODCALLTYPE _CorExeMain(); typedef struct IMAGE_COR_VTABLEFIXUP // From CoreCLR's corhdr.h { uint32_t RVA; // Offset of v-table array in image. uint16_t Count; // How many entries at location. uint16_t Type; // COR_VTABLE_xxx type of entries. } IMAGE_COR_VTABLEFIXUP; typedef void (*portal)(void); // Jumps to the _CorDLLMain function BOOL loadCOR(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved) { _CorDllMain* corInit; HMODULE cor = LoadLibraryA("mscoree.dll"); corInit = (_CorDllMain*)GetProcAddress(cor, "_CorDllMain"); return corInit(hinstDLL, fdwReason, lpReserved); } // Finds a section with enough space for us to inject our VTFixup and VTable entries IMAGE_SECTION_HEADER* findSlackSpace(IMAGE_SECTION_HEADER* sectionHeaders, int sectionCount) { for (int i = 0; i < sectionCount; i++) { if (sectionHeaders[i].Misc.VirtualSize - sectionHeaders[i].SizeOfRawData > VTALLOC_SIZE) { return §ionHeaders[i]; } } return NULL; } int main(int argc, char** argv) { IMAGE_DOS_HEADER* dosHeader; IMAGE_FILE_HEADER* fileHeader; IMAGE_OPTIONAL_HEADER64* optionalHeader; IMAGE_COR20_HEADER* corheader; IMAGE_SECTION_HEADER* sectionHeader, * slackSectionHeader; DWORD old, vtfixupAddr, vtableAddr; IMAGE_COR_VTABLEFIXUP* vtFixupEntry; printf(".NET Portal POC - by @_xpn_\n\n"); printf("[*] Loading our .NET assembly into memory\n"); char* assembly = (char*)LoadLibraryExA("C:\\Users\\Mak Loe\\msgbox.dll", 0, DONT_RESOLVE_DLL_REFERENCES); if (assembly == NULL) { printf("[x] Error loading assembly\n"); printf("[x] Reason : %i\n", GetLastError()); return 1; } dosHeader = (IMAGE_DOS_HEADER*)assembly; fileHeader = (IMAGE_FILE_HEADER*)((char*)assembly + dosHeader->e_lfanew + 4); optionalHeader = (IMAGE_OPTIONAL_HEADER64*)((char*)fileHeader + sizeof(IMAGE_FILE_HEADER)); if (optionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR].VirtualAddress == NULL) { printf("[x] .NET header offset NULL (make sure this is a .NET assembly)\n"); return 2; } corheader = (IMAGE_COR20_HEADER*)((char*)assembly + optionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR].VirtualAddress); sectionHeader = (IMAGE_SECTION_HEADER*)((char*)fileHeader + fileHeader->SizeOfOptionalHeader + sizeof(IMAGE_FILE_HEADER)); if ((corheader->Flags & 1) == 1) { printf("[x] Error: Assembly compiled with ILOnly flag, CorDllMain will fail, remove this flag with 'corflags.exe'\n"); return 4; } // Find a section for us to inject into printf("[*] Finding a section for us to inject into\n"); slackSectionHeader = findSlackSpace(sectionHeader + 1, fileHeader->NumberOfSections); if (slackSectionHeader == NULL) { printf("[x] Could not find an appropriate section to inject\n"); return 3; } printf("[*] Found section %s\n", slackSectionHeader->Name); // Give ourselves some of that slack space vtfixupAddr = slackSectionHeader->VirtualAddress + slackSectionHeader->Misc.VirtualSize; vtableAddr = vtfixupAddr + sizeof(IMAGE_COR_VTABLEFIXUP); // Update the IMAGE_SECTION_HEADER VirtualProtect(slackSectionHeader, sizeof(IMAGE_SECTION_HEADER), PAGE_READWRITE, &old); slackSectionHeader->Misc.VirtualSize += VTALLOC_SIZE; // Create our VTFixup table printf("[*] Creating our VTFixup\n"); VirtualProtect(assembly + vtfixupAddr, VTALLOC_SIZE, PAGE_READWRITE, &old); vtFixupEntry = (IMAGE_COR_VTABLEFIXUP*)((char*)assembly + vtfixupAddr); vtFixupEntry->Count = 1; vtFixupEntry->RVA = vtableAddr; vtFixupEntry->Type = COR_VTABLE_64BIT | COR_VTABLE_FROM_UNMANAGED; // Add the method token we want converting *(DWORD*)((char*)assembly + vtableAddr) = 0x06000001; // Update the IMAGE_COR20_HEADER to point to our new VTableFixup printf("[*] Pointing COR20 header to our VTFixup\n"); VirtualProtect(corheader, sizeof(IMAGE_COR20_HEADER), PAGE_READWRITE, &old); corheader->VTableFixups.VirtualAddress = vtfixupAddr; corheader->VTableFixups.Size = 8; //corheader->Flags = 0x0000; // Let the CLR create the portal printf("[*] Calling CorDllMain\n"); BOOL r = loadCOR((HMODULE)assembly, DLL_PROCESS_ATTACH, 0); if (r == FALSE) { printf("[x] Error: COR init failed - Make sure ILOnly flag isn't set\n"); return 4; } printf("[*] Jumping through the portal...\n"); // If we here, we can jump through the portal into managed land portal entry = (portal) * (DWORD*)((char*)assembly + vtableAddr); entry(); }