-
-
Save xpn/8af9b78e07b9565ce21949a4438d42bb to your computer and use it in GitHub Desktop.
A POC to show how VTableFixup and VTables works to allow .NET methods to be called from unmanaged code.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <iostream> | |
#include <windows.h> | |
// 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 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_HEADER32* 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:\\L33tMalwarez.dll", 0, DONT_RESOLVE_DLL_REFERENCES); | |
if (assembly == NULL) { | |
printf("[x] Error loading assembly\n"); | |
return 1; | |
} | |
dosHeader = (IMAGE_DOS_HEADER*)assembly; | |
fileHeader = (IMAGE_FILE_HEADER *)((char *)assembly + dosHeader->e_lfanew + 4); | |
optionalHeader = (IMAGE_OPTIONAL_HEADER32 *)((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_32BIT | COR_VTABLE_FROM_UNMANAGED; | |
// Add the method token we want converting | |
*(DWORD *)((char *)assembly + vtableAddr) = 0x06000002; | |
// 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 = 0x20000; | |
// 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(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hi, it looks like that i've some problem with the "portal". Here's the code that i used, i've changed some stuff cause the DLL that i want to load is 64-bit. There is no problem at the process, it all went well, but when it comes to the execution of the portal, the process craches with an AccessViolation error, do you know what caused the problem?