mirror of
https://github.com/ilkyr/CustomRDI
synced 2026-06-08 14:48:48 +00:00
193 lines
6.5 KiB
C
193 lines
6.5 KiB
C
/*
|
|
* loader.c — outer loader for the reflective DLL injection demo.
|
|
*
|
|
* Rva2Offset() and GetReflectiveLoaderOffset() are from Stephen Fewer's
|
|
* reflective_dll_injection project (2012, BSD 3-clause). Kept verbatim
|
|
* because rewriting them isn't what this post is about.
|
|
*
|
|
* main() is the only part of this file that I wrote.
|
|
*/
|
|
|
|
#include <windows.h>
|
|
#include <stdio.h>
|
|
|
|
#if defined(_M_X64) || defined(__x86_64__)
|
|
#define WIN_X64
|
|
#endif
|
|
|
|
#define DEREF( name )*(UINT_PTR *)(name)
|
|
#define DEREF_64( name )*(DWORD64 *)(name)
|
|
#define DEREF_32( name )*(DWORD *)(name)
|
|
#define DEREF_16( name )*(WORD *)(name)
|
|
#define DEREF_8( name )*(BYTE *)(name)
|
|
|
|
#define REFLDR_NAME "CustomLoader"
|
|
|
|
DWORD Rva2Offset( DWORD dwRva, UINT_PTR uiBaseAddress )
|
|
{
|
|
WORD wIndex = 0;
|
|
PIMAGE_SECTION_HEADER pSectionHeader = NULL;
|
|
PIMAGE_NT_HEADERS pNtHeaders = NULL;
|
|
|
|
pNtHeaders = (PIMAGE_NT_HEADERS)(uiBaseAddress + ((PIMAGE_DOS_HEADER)uiBaseAddress)->e_lfanew);
|
|
|
|
pSectionHeader = (PIMAGE_SECTION_HEADER)((UINT_PTR)(&pNtHeaders->OptionalHeader) + pNtHeaders->FileHeader.SizeOfOptionalHeader);
|
|
|
|
if( dwRva < pSectionHeader[0].PointerToRawData )
|
|
return dwRva;
|
|
|
|
for( wIndex=0 ; wIndex < pNtHeaders->FileHeader.NumberOfSections ; wIndex++ )
|
|
{
|
|
if( dwRva >= pSectionHeader[wIndex].VirtualAddress && dwRva < (pSectionHeader[wIndex].VirtualAddress + pSectionHeader[wIndex].SizeOfRawData) )
|
|
return ( dwRva - pSectionHeader[wIndex].VirtualAddress + pSectionHeader[wIndex].PointerToRawData );
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
DWORD GetReflectiveLoaderOffset( VOID * lpReflectiveDllBuffer )
|
|
{
|
|
UINT_PTR uiBaseAddress = 0;
|
|
UINT_PTR uiExportDir = 0;
|
|
UINT_PTR uiNameArray = 0;
|
|
UINT_PTR uiAddressArray = 0;
|
|
UINT_PTR uiNameOrdinals = 0;
|
|
DWORD dwCounter = 0;
|
|
#ifdef WIN_X64
|
|
DWORD dwCompiledArch = 2;
|
|
#else
|
|
// This will catch Win32 and WinRT.
|
|
DWORD dwCompiledArch = 1;
|
|
#endif
|
|
|
|
uiBaseAddress = (UINT_PTR)lpReflectiveDllBuffer;
|
|
|
|
// get the File Offset of the modules NT Header
|
|
uiExportDir = uiBaseAddress + ((PIMAGE_DOS_HEADER)uiBaseAddress)->e_lfanew;
|
|
|
|
// currenlty we can only process a PE file which is the same type as the one this fuction has
|
|
// been compiled as, due to various offset in the PE structures being defined at compile time.
|
|
if( ((PIMAGE_NT_HEADERS)uiExportDir)->OptionalHeader.Magic == 0x010B ) // PE32
|
|
{
|
|
if( dwCompiledArch != 1 )
|
|
return 0;
|
|
}
|
|
else if( ((PIMAGE_NT_HEADERS)uiExportDir)->OptionalHeader.Magic == 0x020B ) // PE64
|
|
{
|
|
if( dwCompiledArch != 2 )
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
// uiNameArray = the address of the modules export directory entry
|
|
uiNameArray = (UINT_PTR)&((PIMAGE_NT_HEADERS)uiExportDir)->OptionalHeader.DataDirectory[ IMAGE_DIRECTORY_ENTRY_EXPORT ];
|
|
|
|
// get the File Offset of the export directory
|
|
uiExportDir = uiBaseAddress + Rva2Offset( ((PIMAGE_DATA_DIRECTORY)uiNameArray)->VirtualAddress, uiBaseAddress );
|
|
|
|
// get the File Offset for the array of name pointers
|
|
uiNameArray = uiBaseAddress + Rva2Offset( ((PIMAGE_EXPORT_DIRECTORY )uiExportDir)->AddressOfNames, uiBaseAddress );
|
|
|
|
// get the File Offset for the array of addresses
|
|
uiAddressArray = uiBaseAddress + Rva2Offset( ((PIMAGE_EXPORT_DIRECTORY )uiExportDir)->AddressOfFunctions, uiBaseAddress );
|
|
|
|
// get the File Offset for the array of name ordinals
|
|
uiNameOrdinals = uiBaseAddress + Rva2Offset( ((PIMAGE_EXPORT_DIRECTORY )uiExportDir)->AddressOfNameOrdinals, uiBaseAddress );
|
|
|
|
// get a counter for the number of exported functions...
|
|
dwCounter = ((PIMAGE_EXPORT_DIRECTORY )uiExportDir)->NumberOfNames;
|
|
|
|
// loop through all the exported functions to find the ReflectiveLoader
|
|
while( dwCounter-- )
|
|
{
|
|
char * cpExportedFunctionName = (char *)(uiBaseAddress + Rva2Offset( DEREF_32( uiNameArray ), uiBaseAddress ));
|
|
|
|
if( strstr( cpExportedFunctionName, REFLDR_NAME ) != NULL )
|
|
{
|
|
// get the File Offset for the array of addresses
|
|
uiAddressArray = uiBaseAddress + Rva2Offset( ((PIMAGE_EXPORT_DIRECTORY )uiExportDir)->AddressOfFunctions, uiBaseAddress );
|
|
|
|
// use the functions name ordinal as an index into the array of name pointers
|
|
uiAddressArray += ( DEREF_16( uiNameOrdinals ) * sizeof(DWORD) );
|
|
|
|
// return the File Offset to the ReflectiveLoader() functions code...
|
|
return Rva2Offset( DEREF_32( uiAddressArray ), uiBaseAddress );
|
|
}
|
|
// get the next exported function name
|
|
uiNameArray += sizeof(DWORD);
|
|
|
|
// get the next exported function name ordinal
|
|
uiNameOrdinals += sizeof(WORD);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int main(int argc, char *argv[]) {
|
|
if (argc < 2) {
|
|
printf("Usage: %s <payload.dll>\n", argv[0]);
|
|
return 1;
|
|
}
|
|
|
|
// Read the DLL from disk as raw bytes
|
|
printf("[+] Opening payload.dll\n");
|
|
HANDLE hFile = CreateFileA(argv[1], GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
|
|
if (hFile == INVALID_HANDLE_VALUE) {
|
|
printf("[-] CreateFileA failed (error %lu)\n", GetLastError());
|
|
return 1;
|
|
}
|
|
|
|
DWORD fileSize = GetFileSize(hFile, NULL);
|
|
printf("[+] File is %lu bytes\n", fileSize);
|
|
|
|
// Allocate RW memory and read the raw DLL into it
|
|
void *exec_mem = VirtualAlloc(NULL, fileSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
|
|
if (exec_mem == NULL) {
|
|
printf("[-] VirtualAlloc failed (error %lu)\n", GetLastError());
|
|
return 1;
|
|
}
|
|
printf("[+] Allocated buffer at %p\n", exec_mem);
|
|
|
|
DWORD bytesRead;
|
|
|
|
if (!ReadFile(hFile, exec_mem, fileSize, &bytesRead, NULL) || bytesRead != fileSize) {
|
|
printf("[-] ReadFile failed (error %lu)\n", GetLastError());
|
|
return 1;
|
|
}
|
|
printf("[+] Copied file into buffer\n");
|
|
|
|
CloseHandle(hFile);
|
|
|
|
// Find CustomLoader offset in the raw PE (disk layout)
|
|
DWORD loaderOffset = GetReflectiveLoaderOffset(exec_mem);
|
|
if (loaderOffset == 0) {
|
|
printf("[-] GetReflectiveLoadderOffset failed\n");
|
|
return 1;
|
|
}
|
|
printf("[+] Located CustomLoader at file offset: 0x%lX\n", loaderOffset);
|
|
|
|
// Make executable
|
|
DWORD oldProtect;
|
|
if (!VirtualProtect(exec_mem, fileSize, PAGE_EXECUTE_READWRITE, &oldProtect)) {
|
|
printf("[-] VirtualProtect failed (error %lu)\n", GetLastError());
|
|
return 1;
|
|
}
|
|
printf("[+] Marked buffer executable\n");
|
|
|
|
// Jump to CustomLoader within the raw bytes
|
|
printf("[+] Spawning thread at %p\n", (void *)((ULONG_PTR)exec_mem + loaderOffset));
|
|
HANDLE th = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)((ULONG_PTR)exec_mem + loaderOffset), NULL, 0, NULL);
|
|
if (th == NULL) {
|
|
printf("[-] CreateThread failed (error %lu)\n", GetLastError());
|
|
return 1;
|
|
}
|
|
|
|
WaitForSingleObject(th, INFINITE);
|
|
|
|
printf("[+] Thread returned, exiting\n");
|
|
|
|
return 0;
|
|
} |