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);
            }
        }