private static void FreePointerList(POINTER_LIST head, CustomFreeFunc freeMemory, void* userdata) { POINTER_LIST node = head; while (node != null) { POINTER_LIST next; freeMemory(node.address, null, MEM_RELEASE, userdata); next = node.next; node = next; } }
internal static MEMORYMODULE MemoryLoadLibraryEx(void* data, void* size, CustomAllocFunc allocMemory, CustomFreeFunc freeMemory, CustomLoadLibraryFunc loadLibrary, CustomGetProcAddressFunc getProcAddress, CustomFreeLibraryFunc freeLibrary, void* userdata) { MEMORYMODULE result = null; IMAGE_DOS_HEADER* dos_header; void* old_header; byte* code, headers; void* locationDelta; SYSTEM_INFO sysInfo; IMAGE_SECTION_HEADER* section; uint i; void* optionalSectionSize; void* lastSectionEnd = null; void* alignedImageSize; POINTER_LIST blockedMemory = null; if (!CheckSize(size, (void*)IMAGE_DOS_HEADER.UnmanagedSize)) return null; dos_header = (IMAGE_DOS_HEADER*)data; if (dos_header->e_magic != IMAGE_DOS_SIGNATURE) return null; if (!CheckSize(size, (void*)(dos_header->e_lfanew + (WIN64 ? IMAGE_NT_HEADERS64.UnmanagedSize : IMAGE_NT_HEADERS32.UnmanagedSize)))) return null; old_header = &((byte*)data)[dos_header->e_lfanew]; if ((WIN64 ? ((IMAGE_NT_HEADERS64*)old_header)->Signature : ((IMAGE_NT_HEADERS32*)old_header)->Signature) != IMAGE_NT_SIGNATURE) return null; if ((WIN64 ? ((IMAGE_NT_HEADERS64*)old_header)->FileHeader.Machine : ((IMAGE_NT_HEADERS32*)old_header)->FileHeader.Machine) != HOST_MACHINE) return null; if (((WIN64 ? ((IMAGE_NT_HEADERS64*)old_header)->OptionalHeader.SectionAlignment : ((IMAGE_NT_HEADERS32*)old_header)->OptionalHeader.SectionAlignment) & 1) != 0) // Only support section alignments that are a multiple of 2 return null; section = IMAGE_FIRST_SECTION(old_header); optionalSectionSize = (void*)((WIN64 ? ((IMAGE_NT_HEADERS64*)old_header)->OptionalHeader.SectionAlignment : ((IMAGE_NT_HEADERS32*)old_header)->OptionalHeader.SectionAlignment)); for (i = 0; i < (WIN64 ? ((IMAGE_NT_HEADERS64*)old_header)->FileHeader.NumberOfSections : ((IMAGE_NT_HEADERS32*)old_header)->FileHeader.NumberOfSections); i++, section++) { void* endOfSection; if (section->SizeOfRawData == 0) { // Section without data in the DLL endOfSection = (void*)(section->VirtualAddress + (ulong)optionalSectionSize); } else { endOfSection = (void*)(section->VirtualAddress + (ulong)section->SizeOfRawData); } if ((ulong)endOfSection > (ulong)lastSectionEnd) lastSectionEnd = endOfSection; } GetNativeSystemInfo(&sysInfo); alignedImageSize = AlignValueUp((void*)(WIN64 ? ((IMAGE_NT_HEADERS64*)old_header)->OptionalHeader.SizeOfImage : ((IMAGE_NT_HEADERS32*)old_header)->OptionalHeader.SizeOfImage), (void*)sysInfo.dwPageSize); if (alignedImageSize != AlignValueUp(lastSectionEnd, (void*)sysInfo.dwPageSize)) return null; // reserve memory for image of library // XXX: is it correct to commit the complete memory region at once? // calling DllEntry raises an exception if we don't... code = (byte*)allocMemory((void*)(WIN64 ? ((IMAGE_NT_HEADERS64*)old_header)->OptionalHeader.ImageBase : ((IMAGE_NT_HEADERS32*)old_header)->OptionalHeader.ImageBase), alignedImageSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE, userdata); if (code == null) { // try to allocate memory at arbitrary position code = (byte*)allocMemory(null, alignedImageSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE, userdata); if (code == null) return null; } if (WIN64) { // Memory block may not span 4 GB boundaries. while ((ulong)code >> 32 < ((ulong)code + (ulong)alignedImageSize) >> 32) { POINTER_LIST node = new POINTER_LIST { next = blockedMemory, address = code }; blockedMemory = node; code = (byte*)allocMemory(null, alignedImageSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE, userdata); if (code == null) { FreePointerList(blockedMemory, freeMemory, userdata); return null; } } } result = new MEMORYMODULE { codeBase = code, isDLL = ((WIN64 ? ((IMAGE_NT_HEADERS64*)old_header)->FileHeader.Characteristics : ((IMAGE_NT_HEADERS32*)old_header)->FileHeader.Characteristics) & IMAGE_FILE_DLL) != 0, alloc = allocMemory, free = freeMemory, loadLibrary = loadLibrary, getProcAddress = getProcAddress, freeLibrary = freeLibrary, userdata = userdata, pageSize = sysInfo.dwPageSize }; if (WIN64) result.blockedMemory = blockedMemory; if (!CheckSize(size, (void*)(WIN64 ? ((IMAGE_NT_HEADERS64*)old_header)->OptionalHeader.SizeOfHeaders : ((IMAGE_NT_HEADERS32*)old_header)->OptionalHeader.SizeOfHeaders))) goto error; // commit memory for headers headers = (byte*)allocMemory(code, (void*)(WIN64 ? ((IMAGE_NT_HEADERS64*)old_header)->OptionalHeader.SizeOfHeaders : ((IMAGE_NT_HEADERS32*)old_header)->OptionalHeader.SizeOfHeaders), MEM_COMMIT, PAGE_READWRITE, userdata); // copy PE header to code memcpy(headers, dos_header, (void*)(WIN64 ? ((IMAGE_NT_HEADERS64*)old_header)->OptionalHeader.SizeOfHeaders : ((IMAGE_NT_HEADERS32*)old_header)->OptionalHeader.SizeOfHeaders)); result.headers = &headers[dos_header->e_lfanew]; // update position if (WIN64) ((IMAGE_NT_HEADERS64*)result.headers)->OptionalHeader.ImageBase = (ulong)code; else ((IMAGE_NT_HEADERS32*)result.headers)->OptionalHeader.ImageBase = (uint)code; // copy sections from DLL file block to new memory location if (!CopySections((byte*)data, size, old_header, result)) goto error; // adjust base address of imported data locationDelta = (void*)((WIN64 ? ((IMAGE_NT_HEADERS64*)result.headers)->OptionalHeader.ImageBase : ((IMAGE_NT_HEADERS32*)result.headers)->OptionalHeader.ImageBase) - (WIN64 ? ((IMAGE_NT_HEADERS64*)old_header)->OptionalHeader.ImageBase : ((IMAGE_NT_HEADERS32*)old_header)->OptionalHeader.ImageBase)); if ((ulong)locationDelta != 0) result.isRelocated = PerformBaseRelocation(result, locationDelta); else result.isRelocated = true; // load required dlls and adjust function table of imports if (!BuildImportTable(result)) goto error; // mark memory pages depending on section headers and release // sections that are marked as "discardable" if (!FinalizeSections(result)) goto error; // TLS callbacks are executed BEFORE the main loading if (!ExecuteTLS(result)) goto error; // get entry point of loaded library if ((WIN64 ? ((IMAGE_NT_HEADERS64*)result.headers)->OptionalHeader.AddressOfEntryPoint : ((IMAGE_NT_HEADERS32*)result.headers)->OptionalHeader.AddressOfEntryPoint) != 0) { if (result.isDLL) { DllEntryProc DllEntry = (DllEntryProc)Marshal.GetDelegateForFunctionPointer((IntPtr)(code + (WIN64 ? ((IMAGE_NT_HEADERS64*)result.headers)->OptionalHeader.AddressOfEntryPoint : ((IMAGE_NT_HEADERS32*)result.headers)->OptionalHeader.AddressOfEntryPoint)), typeof(DllEntryProc)); // notify library about attaching to process bool successfull = DllEntry(code, DLL_PROCESS_ATTACH, null); if (!successfull) goto error; result.initialized = true; } else result.exeEntry = (ExeEntryProc)Marshal.GetDelegateForFunctionPointer((IntPtr)(code + (WIN64 ? ((IMAGE_NT_HEADERS64*)result.headers)->OptionalHeader.AddressOfEntryPoint : ((IMAGE_NT_HEADERS32*)result.headers)->OptionalHeader.AddressOfEntryPoint)), typeof(ExeEntryProc)); } else result.exeEntry = null; return result; error: // cleanup MemoryFreeLibrary(result); return null; }