void CopySections(byte[] data, IMAGE_NT_HEADERS *oldHeader)
        {
            if (data == null)
            {
                throw new ArgumentNullException("data");
            }

            if (oldHeader->Signature != Win.IMAGE_NT_SIGNATURE)
            {
                throw new BadImageFormatException("Invalid PE-Header");
            }

            uint  size;
            byte *dest;

            IMAGE_SECTION_HEADER *section = Win.IMAGE_FIRST_SECTION(_headers);

            for (int i = 0; i < _headers->FileHeader.NumberOfSections; i++, section++)
            {
                if (section->SizeOfRawData == 0)
                {
                    // section doesn't contain data in the dll itself, but may define
                    // uninitialized data
                    size = oldHeader->OptionalHeader.SectionAlignment;
                    if (size > 0)
                    {
                        dest = (byte *)Win.VirtualAlloc(
                            _codeBase + section->VirtualAddress,
                            size,
                            AllocationType.COMMIT,
                            MemoryProtection.READWRITE);

                        if (dest == null)
                        {
                            throw new NativeDllLoadException("Unable to allocate memory.");
                        }

                        dest = _codeBase + section->VirtualAddress;

                        section->PhysicalAddress = (uint)dest & 0xffffffff;
                        Win.MemSet(dest, 0, (void *)size);
                    }
                    continue;
                }

                dest = (byte *)Win.VirtualAlloc(
                    _codeBase + section->VirtualAddress,
                    section->SizeOfRawData,
                    AllocationType.COMMIT,
                    MemoryProtection.READWRITE);

                if (dest == null)
                {
                    throw new NativeDllLoadException("Out of memory.");
                }

                dest = _codeBase + section->VirtualAddress;
                Marshal.Copy(data, (int)section->PointerToRawData, (IntPtr)dest, (int)section->SizeOfRawData);
                section->PhysicalAddress = (uint)dest & 0xffffffff;
            }
        }
        private void MemoryLoadLibrary(byte[] data)
        {
            IMAGE_DOS_HEADER *    dosHeader;
            IMAGE_NT_HEADERS *    oldHeader;
            IMAGE_SECTION_HEADER *section;
            SYSTEM_INFO           systemInfo;
            void *   dllEntryPtr;
            void *   exeEntryPtr;
            byte *   headers, dataPtr, code;
            SizeT    optionalSectionSize;
            SizeT    lastSectionEnd = 0;
            SizeT    alignedImageSize;
            PtrDiffT locationDelta;

            _dataHandle = GCHandle.Alloc(data, GCHandleType.Pinned);
            if (!_dataHandle.IsAllocated)
            {
                throw new NativeDllLoadException("Can't allocate memory.");
            }

            dataPtr = (byte *)_dataHandle.AddrOfPinnedObject();

            dosHeader = (IMAGE_DOS_HEADER *)dataPtr;
            if (dosHeader->e_magic != Win.IMAGE_DOS_SIGNATURE)
            {
                throw new BadImageFormatException("Not a valid executable file.");
            }

            oldHeader = (IMAGE_NT_HEADERS *)(dataPtr + dosHeader->e_lfanew);
            if (oldHeader->Signature != Win.IMAGE_NT_SIGNATURE)
            {
                throw new BadImageFormatException("Not a valid PE file.");
            }

            if (oldHeader->FileHeader.Machine != GetMachineType())
            {
                throw new BadImageFormatException("Machine type doesn't fit. (i386 vs. AMD64)");
            }

            if ((oldHeader->OptionalHeader.SectionAlignment & 1) > 0)
            {
                throw new BadImageFormatException("Wrong section alignment");
            }

            section             = Win.IMAGE_FIRST_SECTION(oldHeader);
            optionalSectionSize = oldHeader->OptionalHeader.SectionAlignment;
            for (int i = 0; i < oldHeader->FileHeader.NumberOfSections; i++, section++)
            {
                SizeT endOfSection;
                if (section->SizeOfRawData == 0) // Section without data in the DLL
                {
                    endOfSection = section->VirtualAddress + optionalSectionSize;
                }
                else
                {
                    endOfSection = section->VirtualAddress + section->SizeOfRawData;
                }

                if (endOfSection > lastSectionEnd)
                {
                    lastSectionEnd = endOfSection;
                }
            }

            Win.GetNativeSystemInfo(&systemInfo);
            alignedImageSize = AlignValueUp(oldHeader->OptionalHeader.SizeOfImage, systemInfo.dwPageSize);
            if (alignedImageSize != AlignValueUp(lastSectionEnd, systemInfo.dwPageSize))
            {
                throw new BadImageFormatException("Wrong section alignment.");
            }

            code = (byte *)Win.VirtualAlloc(
                (void *)(oldHeader->OptionalHeader.ImageBase),
                oldHeader->OptionalHeader.SizeOfImage,
                AllocationType.RESERVE | AllocationType.COMMIT,
                MemoryProtection.READWRITE);

            if (code == null)
            {
                code = (byte *)Win.VirtualAlloc(
                    null,
                    oldHeader->OptionalHeader.SizeOfImage,
                    AllocationType.RESERVE | AllocationType.COMMIT,
                    MemoryProtection.READWRITE);
            }

            if (code == null)
            {
                throw new NativeDllLoadException("Out of Memory");
            }

#if WIN64
            while ((((ulong)code) >> 32) < (((ulong)(code + alignedImageSize)) >> 32))
            {
                _blockedMemory.Add((IntPtr)code);

                code = (byte *)Win.VirtualAlloc(
                    null,
                    alignedImageSize,
                    AllocationType.RESERVE | AllocationType.COMMIT,
                    MemoryProtection.READWRITE);

                if (code == null)
                {
                    throw new NativeDllLoadException("Out of Memory");
                }
            }
#endif

            _pageSize = systemInfo.dwPageSize;
            _codeBase = code;
            IsDll     = (oldHeader->FileHeader.Characteristics & Win.IMAGE_FILE_DLL) != 0;

            headers = (byte *)Win.VirtualAlloc(
                code,
                oldHeader->OptionalHeader.SizeOfHeaders,
                AllocationType.COMMIT,
                MemoryProtection.READWRITE);

            if (headers == null)
            {
                throw new NativeDllLoadException("Out of Memory");
            }

            Marshal.Copy(data, 0, (IntPtr)headers, (int)(dosHeader->e_lfanew + oldHeader->OptionalHeader.SizeOfHeaders));
            _headers = (IMAGE_NT_HEADERS *)&(headers)[dosHeader->e_lfanew];

            _headers->OptionalHeader.ImageBase = (UIntPtrT)code;

            CopySections(data, oldHeader);

            locationDelta = (PtrDiffT)(_headers->OptionalHeader.ImageBase - oldHeader->OptionalHeader.ImageBase);
            if (locationDelta != 0)
            {
                _isRelocated = PerformBaseRelocation(locationDelta);
            }
            else
            {
                _isRelocated = true;
            }

            BuildImportTable();
            FinalizeSections();
            ExecuteTLS();

            if (_headers->OptionalHeader.AddressOfEntryPoint == 0)
            {
                throw new NativeDllLoadException("DLL has no entry point");
            }

            if (IsDll)
            {
                dllEntryPtr = code + _headers->OptionalHeader.AddressOfEntryPoint;
                _dllEntry   = (DllEntryDelegate)Marshal.GetDelegateForFunctionPointer((IntPtr)dllEntryPtr, typeof(DllEntryDelegate));

                if (_dllEntry != null && _dllEntry(code, DllReason.DLL_PROCESS_ATTACH, null))
                {
                    _initialized = true;
                }
                else
                {
                    _initialized = false;
                    throw new NativeDllLoadException("Can't attach DLL to process.");
                }
            }
            else
            {
                exeEntryPtr = code + _headers->OptionalHeader.AddressOfEntryPoint;
                _exeEntry   = Marshal.GetDelegateForFunctionPointer <ExeEntryDelegate>((IntPtr)exeEntryPtr);
            }
        }