private WinNT.IMAGE_DATA_DIRECTORY *GET_HEADER_DIRECTORY(MEMORY_MODULE * memory_module, uint index) { if (Environment.Is64BitProcess) { WinNT.IMAGE_NT_HEADERS64 *headers = (WinNT.IMAGE_NT_HEADERS64 *)memory_module->headers; return((WinNT.IMAGE_DATA_DIRECTORY *)(&headers->OptionalHeader.DataDirectory[index])); } else { WinNT.IMAGE_NT_HEADERS32 *headers = (WinNT.IMAGE_NT_HEADERS32 *)memory_module->headers; return((WinNT.IMAGE_DATA_DIRECTORY *)(&headers->OptionalHeader.DataDirectory[index])); } }
/// <summary> /// Copies sections from a native module file block to the new memory location. /// </summary> /// <param name="ptr_data">Pointer to a native module byte array.</param> /// <param name="ptr_old_headers">Pointer to a source native module headers.</param> /// <param name="memory_module">Pointer to a memory module.</param> private void CopySections(byte *ptr_data, byte *ptr_old_headers, MEMORY_MODULE *memory_module) { byte *codeBase = memory_module->codeBase; WinNT.IMAGE_SECTION_HEADER *section = WinNT.IMAGE_FIRST_SECTION(memory_module->headers); ushort numberOfSections; uint sectionAlignment; if (Environment.Is64BitProcess) { WinNT.IMAGE_NT_HEADERS64 *new_headers = (WinNT.IMAGE_NT_HEADERS64 *)memory_module->headers; numberOfSections = new_headers->FileHeader.NumberOfSections; WinNT.IMAGE_NT_HEADERS64 *old_headers = (WinNT.IMAGE_NT_HEADERS64 *)ptr_old_headers; sectionAlignment = old_headers->OptionalHeader.SectionAlignment; } else { WinNT.IMAGE_NT_HEADERS32 *new_headers = (WinNT.IMAGE_NT_HEADERS32 *)memory_module->headers; numberOfSections = new_headers->FileHeader.NumberOfSections; WinNT.IMAGE_NT_HEADERS32 *old_headers = (WinNT.IMAGE_NT_HEADERS32 *)ptr_old_headers; sectionAlignment = old_headers->OptionalHeader.SectionAlignment; } uint index; byte *dest; for (index = 0; index < numberOfSections; index++, section++) { if (section->SizeOfRawData == 0) { if (sectionAlignment > 0) { dest = (byte *)WinBase.VirtualAlloc((IntPtr)(codeBase + section->VirtualAddress), sectionAlignment, WinNT.MEM_COMMIT, WinNT.PAGE_READWRITE); section->PhysicalAddress = (uint)dest; memory.memset(dest, 0, sectionAlignment); } continue; } // commit memory block and copy data from dll dest = (byte *)WinBase.VirtualAlloc((IntPtr)(codeBase + section->VirtualAddress), section->SizeOfRawData, WinNT.MEM_COMMIT, WinNT.PAGE_READWRITE); memory.memcpy(dest, ptr_data + section->PointerToRawData, section->SizeOfRawData); section->PhysicalAddress = (uint)dest; } }
/// <summary> /// Calls module entry point. /// </summary> /// <param name="memory_module">Pointer to a memory module.</param> /// <param name="fdwReason"></param> /// <returns>If the function succeeds or if there is no entry point, the return value is true.</returns> private bool CallDllEntryPoint(MEMORY_MODULE *memory_module, uint fdwReason) { uint addressOfEntryPoint; if (Environment.Is64BitProcess) { WinNT.IMAGE_NT_HEADERS64 *headers = (WinNT.IMAGE_NT_HEADERS64 *)memory_module->headers; addressOfEntryPoint = headers->OptionalHeader.AddressOfEntryPoint; } else { WinNT.IMAGE_NT_HEADERS32 *headers = (WinNT.IMAGE_NT_HEADERS32 *)memory_module->headers; addressOfEntryPoint = headers->OptionalHeader.AddressOfEntryPoint; } if (addressOfEntryPoint != 0) { IntPtr dllEntry = (IntPtr)(memory_module->codeBase + addressOfEntryPoint); if (dllEntry == IntPtr.Zero) { return(false); } DllEntryProc dllEntryProc = (DllEntryProc)Marshal.GetDelegateForFunctionPointer(dllEntry, typeof(DllEntryProc)); if (dllEntryProc((IntPtr)memory_module->codeBase, fdwReason, 0)) { if (fdwReason == WinNT.DLL_PROCESS_ATTACH) { memory_module->initialized = 1; } else if (fdwReason == WinNT.DLL_PROCESS_DETACH) { memory_module->initialized = 0; } return(true); } else { return(false); } } return(true); }
/// <summary> /// Marks memory pages depending on section headers and release sections that are marked as "discardable". /// </summary> /// <param name="memory_module">Pointer to a memory module.</param> private void FinalizeSections(MEMORY_MODULE *memory_module) { WinNT.IMAGE_SECTION_HEADER *section = WinNT.IMAGE_FIRST_SECTION(memory_module->headers);; ushort number_of_sections; uint size_of_initialized_data; uint size_of_uninitialized_data; long image_offset = 0; if (Environment.Is64BitProcess) { WinNT.IMAGE_NT_HEADERS64 *headers = (WinNT.IMAGE_NT_HEADERS64 *)memory_module->headers; number_of_sections = headers->FileHeader.NumberOfSections; size_of_initialized_data = headers->OptionalHeader.SizeOfInitializedData; size_of_uninitialized_data = headers->OptionalHeader.SizeOfUninitializedData; image_offset = (long)((ulong)headers->OptionalHeader.ImageBase & 0xffffffff00000000); } else { WinNT.IMAGE_NT_HEADERS32 *headers = (WinNT.IMAGE_NT_HEADERS32 *)memory_module->headers; number_of_sections = headers->FileHeader.NumberOfSections; size_of_initialized_data = headers->OptionalHeader.SizeOfInitializedData; size_of_uninitialized_data = headers->OptionalHeader.SizeOfUninitializedData; } for (int i = 0; i < number_of_sections; i++, section++) { uint protect, oldProtect, rawDataSize; uint executable = Convert.ToUInt32((section->Characteristics & WinNT.IMAGE_SCN_MEM_EXECUTE) != 0); uint readable = Convert.ToUInt32((section->Characteristics & WinNT.IMAGE_SCN_MEM_READ) != 0); uint writeable = Convert.ToUInt32((section->Characteristics & WinNT.IMAGE_SCN_MEM_WRITE) != 0); if ((section->Characteristics & WinNT.IMAGE_SCN_MEM_DISCARDABLE) != 0) { // section is not needed any more and can safely be freed WinBase.VirtualFree((IntPtr)(void *)((long)section->PhysicalAddress | (long)image_offset), section->SizeOfRawData, WinNT.MEM_DECOMMIT); continue; } protect = _protectionFlags[executable, readable, writeable]; if ((section->Characteristics & WinNT.IMAGE_SCN_MEM_NOT_CACHED) != 0) { protect |= WinNT.PAGE_NOCACHE; } // determine size of region rawDataSize = section->SizeOfRawData; if (rawDataSize == 0) { if ((section->Characteristics & WinNT.IMAGE_SCN_CNT_INITIALIZED_DATA) != 0) { rawDataSize = size_of_initialized_data; } else if ((section->Characteristics & WinNT.IMAGE_SCN_CNT_UNINITIALIZED_DATA) != 0) { rawDataSize = size_of_uninitialized_data; } } if (rawDataSize > 0) { // change memory access flags WinBase.VirtualProtect((IntPtr)(void *)((long)section->PhysicalAddress | (long)image_offset), rawDataSize, protect, &oldProtect); } } }
/// <summary> /// Loads the specified native module from a byte array into the address space of the calling process. /// </summary> /// <param name="data">Native module byte array.</param> /// <returns>If the function succeeds, the return value is a handle to the module.</returns> private IntPtr MemoryLoadLibrary(byte[] data) { fixed(byte *ptr_data = data) { WinNT.IMAGE_DOS_HEADER *dos_header = (WinNT.IMAGE_DOS_HEADER *)ptr_data; if (dos_header->e_magic != WinNT.IMAGE_DOS_SIGNATURE) { throw new NotSupportedException(); } byte * ptr_old_header; uint old_header_oh_sizeOfImage; uint old_header_oh_sizeOfHeaders; int image_nt_headers_Size; IntPtr old_header_oh_imageBase; if (Environment.Is64BitProcess) { WinNT.IMAGE_NT_HEADERS64 *old_header = (WinNT.IMAGE_NT_HEADERS64 *)(ptr_data + dos_header->e_lfanew); if (old_header->Signature != WinNT.IMAGE_NT_SIGNATURE) { throw new NotSupportedException(); } old_header_oh_sizeOfImage = old_header->OptionalHeader.SizeOfImage; old_header_oh_sizeOfHeaders = old_header->OptionalHeader.SizeOfHeaders; old_header_oh_imageBase = old_header->OptionalHeader.ImageBase; ptr_old_header = (byte *)old_header; image_nt_headers_Size = sizeof(WinNT.IMAGE_NT_HEADERS64); } else { WinNT.IMAGE_NT_HEADERS32 *old_header = (WinNT.IMAGE_NT_HEADERS32 *)(ptr_data + dos_header->e_lfanew); if (old_header->Signature != WinNT.IMAGE_NT_SIGNATURE) { throw new NotSupportedException(); } old_header_oh_sizeOfImage = old_header->OptionalHeader.SizeOfImage; old_header_oh_sizeOfHeaders = old_header->OptionalHeader.SizeOfHeaders; old_header_oh_imageBase = old_header->OptionalHeader.ImageBase; ptr_old_header = (byte *)old_header; image_nt_headers_Size = sizeof(WinNT.IMAGE_NT_HEADERS32); } IntPtr codeBase = IntPtr.Zero; if (!Environment.Is64BitProcess) { codeBase = WinBase.VirtualAlloc(old_header_oh_imageBase, old_header_oh_sizeOfImage, WinNT.MEM_RESERVE, WinNT.PAGE_READWRITE); } if (codeBase == IntPtr.Zero) { codeBase = WinBase.VirtualAlloc(IntPtr.Zero, old_header_oh_sizeOfImage, WinNT.MEM_RESERVE, WinNT.PAGE_READWRITE); } if (codeBase == IntPtr.Zero) { return(IntPtr.Zero); } MEMORY_MODULE *memory_module = (MEMORY_MODULE *)Marshal.AllocHGlobal(sizeof(MEMORY_MODULE)); memory_module->codeBase = (byte *)codeBase; memory_module->numModules = 0; memory_module->modules = null; memory_module->initialized = 0; WinBase.VirtualAlloc(codeBase, old_header_oh_sizeOfImage, WinNT.MEM_COMMIT, WinNT.PAGE_READWRITE); IntPtr headers = WinBase.VirtualAlloc(codeBase, old_header_oh_sizeOfHeaders, WinNT.MEM_COMMIT, WinNT.PAGE_READWRITE); // copy PE header to code memory.memcpy((byte *)headers, (byte *)dos_header, dos_header->e_lfanew + old_header_oh_sizeOfHeaders); memory_module->headers = &((byte *)(headers))[dos_header->e_lfanew]; if (Environment.Is64BitProcess) { WinNT.IMAGE_NT_HEADERS64 *mm_headers_64 = (WinNT.IMAGE_NT_HEADERS64 *)(memory_module->headers); mm_headers_64->OptionalHeader.ImageBase = codeBase; } else { WinNT.IMAGE_NT_HEADERS32 *mm_headers_32 = (WinNT.IMAGE_NT_HEADERS32 *)(memory_module->headers); mm_headers_32->OptionalHeader.ImageBase = codeBase; } this.CopySections(ptr_data, ptr_old_header, memory_module); ulong locationDelta = (ulong)((ulong)codeBase - (ulong)old_header_oh_imageBase); if (locationDelta != 0) { this.PerformBaseRelocation(memory_module, locationDelta); } if (!this.BuildImportTable(memory_module)) { goto error; } this.FinalizeSections(memory_module); if (!this.CallDllEntryPoint(memory_module, WinNT.DLL_PROCESS_ATTACH)) { goto error; } return((IntPtr)memory_module); error: MemoryFreeLibrary((IntPtr)memory_module); return(IntPtr.Zero); } }