github.com/MerlinKodo/sing-tun@v0.1.15/internal/wintun/memmod/memmod_windows.go (about)

     1  /* SPDX-License-Identifier: MIT
     2   *
     3   * Copyright (C) 2017-2021 WireGuard LLC. All Rights Reserved.
     4   */
     5  
     6  package memmod
     7  
     8  import (
     9  	"errors"
    10  	"fmt"
    11  	"strings"
    12  	"sync"
    13  	"syscall"
    14  	"unsafe"
    15  
    16  	"golang.org/x/sys/windows"
    17  )
    18  
    19  type addressList struct {
    20  	next    *addressList
    21  	address uintptr
    22  }
    23  
    24  func (head *addressList) free() {
    25  	for node := head; node != nil; node = node.next {
    26  		windows.VirtualFree(node.address, 0, windows.MEM_RELEASE)
    27  	}
    28  }
    29  
    30  type Module struct {
    31  	headers       *IMAGE_NT_HEADERS
    32  	codeBase      uintptr
    33  	modules       []windows.Handle
    34  	initialized   bool
    35  	isDLL         bool
    36  	isRelocated   bool
    37  	nameExports   map[string]uint16
    38  	entry         uintptr
    39  	blockedMemory *addressList
    40  }
    41  
    42  func (module *Module) BaseAddr() uintptr {
    43  	return module.codeBase
    44  }
    45  
    46  func (module *Module) headerDirectory(idx int) *IMAGE_DATA_DIRECTORY {
    47  	return &module.headers.OptionalHeader.DataDirectory[idx]
    48  }
    49  
    50  func (module *Module) copySections(address, size uintptr, oldHeaders *IMAGE_NT_HEADERS) error {
    51  	sections := module.headers.Sections()
    52  	for i := range sections {
    53  		if sections[i].SizeOfRawData == 0 {
    54  			// Section doesn't contain data in the dll itself, but may define uninitialized data.
    55  			sectionSize := oldHeaders.OptionalHeader.SectionAlignment
    56  			if sectionSize == 0 {
    57  				continue
    58  			}
    59  			dest, err := windows.VirtualAlloc(module.codeBase+uintptr(sections[i].VirtualAddress),
    60  				uintptr(sectionSize),
    61  				windows.MEM_COMMIT,
    62  				windows.PAGE_READWRITE)
    63  			if err != nil {
    64  				return fmt.Errorf("Error allocating section: %w", err)
    65  			}
    66  
    67  			// Always use position from file to support alignments smaller than page size (allocation above will align to page size).
    68  			dest = module.codeBase + uintptr(sections[i].VirtualAddress)
    69  			// NOTE: On 64bit systems we truncate to 32bit here but expand again later when "PhysicalAddress" is used.
    70  			sections[i].SetPhysicalAddress((uint32)(dest & 0xffffffff))
    71  			dst := unsafe.Slice((*byte)(a2p(dest)), sectionSize)
    72  			for j := range dst {
    73  				dst[j] = 0
    74  			}
    75  			continue
    76  		}
    77  
    78  		if size < uintptr(sections[i].PointerToRawData+sections[i].SizeOfRawData) {
    79  			return errors.New("Incomplete section")
    80  		}
    81  
    82  		// Commit memory block and copy data from dll.
    83  		dest, err := windows.VirtualAlloc(module.codeBase+uintptr(sections[i].VirtualAddress),
    84  			uintptr(sections[i].SizeOfRawData),
    85  			windows.MEM_COMMIT,
    86  			windows.PAGE_READWRITE)
    87  		if err != nil {
    88  			return fmt.Errorf("Error allocating memory block: %w", err)
    89  		}
    90  
    91  		// Always use position from file to support alignments smaller than page size (allocation above will align to page size).
    92  		memcpy(
    93  			module.codeBase+uintptr(sections[i].VirtualAddress),
    94  			address+uintptr(sections[i].PointerToRawData),
    95  			uintptr(sections[i].SizeOfRawData))
    96  		// NOTE: On 64bit systems we truncate to 32bit here but expand again later when "PhysicalAddress" is used.
    97  		sections[i].SetPhysicalAddress((uint32)(dest & 0xffffffff))
    98  	}
    99  
   100  	return nil
   101  }
   102  
   103  func (module *Module) realSectionSize(section *IMAGE_SECTION_HEADER) uintptr {
   104  	size := section.SizeOfRawData
   105  	if size != 0 {
   106  		return uintptr(size)
   107  	}
   108  	if (section.Characteristics & IMAGE_SCN_CNT_INITIALIZED_DATA) != 0 {
   109  		return uintptr(module.headers.OptionalHeader.SizeOfInitializedData)
   110  	}
   111  	if (section.Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA) != 0 {
   112  		return uintptr(module.headers.OptionalHeader.SizeOfUninitializedData)
   113  	}
   114  	return 0
   115  }
   116  
   117  type sectionFinalizeData struct {
   118  	address         uintptr
   119  	alignedAddress  uintptr
   120  	size            uintptr
   121  	characteristics uint32
   122  	last            bool
   123  }
   124  
   125  func (module *Module) finalizeSection(sectionData *sectionFinalizeData) error {
   126  	if sectionData.size == 0 {
   127  		return nil
   128  	}
   129  
   130  	if (sectionData.characteristics & IMAGE_SCN_MEM_DISCARDABLE) != 0 {
   131  		// Section is not needed any more and can safely be freed.
   132  		if sectionData.address == sectionData.alignedAddress &&
   133  			(sectionData.last ||
   134  				(sectionData.size%uintptr(module.headers.OptionalHeader.SectionAlignment)) == 0) {
   135  			// Only allowed to decommit whole pages.
   136  			windows.VirtualFree(sectionData.address, sectionData.size, windows.MEM_DECOMMIT)
   137  		}
   138  		return nil
   139  	}
   140  
   141  	// determine protection flags based on characteristics
   142  	ProtectionFlags := [8]uint32{
   143  		windows.PAGE_NOACCESS,          // not writeable, not readable, not executable
   144  		windows.PAGE_EXECUTE,           // not writeable, not readable, executable
   145  		windows.PAGE_READONLY,          // not writeable, readable, not executable
   146  		windows.PAGE_EXECUTE_READ,      // not writeable, readable, executable
   147  		windows.PAGE_WRITECOPY,         // writeable, not readable, not executable
   148  		windows.PAGE_EXECUTE_WRITECOPY, // writeable, not readable, executable
   149  		windows.PAGE_READWRITE,         // writeable, readable, not executable
   150  		windows.PAGE_EXECUTE_READWRITE, // writeable, readable, executable
   151  	}
   152  	protect := ProtectionFlags[sectionData.characteristics>>29]
   153  	if (sectionData.characteristics & IMAGE_SCN_MEM_NOT_CACHED) != 0 {
   154  		protect |= windows.PAGE_NOCACHE
   155  	}
   156  
   157  	// Change memory access flags.
   158  	var oldProtect uint32
   159  	err := windows.VirtualProtect(sectionData.address, sectionData.size, protect, &oldProtect)
   160  	if err != nil {
   161  		return fmt.Errorf("Error protecting memory page: %w", err)
   162  	}
   163  
   164  	return nil
   165  }
   166  
   167  func (module *Module) registerExceptionHandlers() {
   168  	directory := module.headerDirectory(IMAGE_DIRECTORY_ENTRY_EXCEPTION)
   169  	if directory.Size == 0 || directory.VirtualAddress == 0 {
   170  		return
   171  	}
   172  	runtimeFuncs := (*windows.RUNTIME_FUNCTION)(unsafe.Pointer(module.codeBase + uintptr(directory.VirtualAddress)))
   173  	windows.RtlAddFunctionTable(runtimeFuncs, uint32(uintptr(directory.Size)/unsafe.Sizeof(*runtimeFuncs)), module.codeBase)
   174  }
   175  
   176  func (module *Module) finalizeSections() error {
   177  	sections := module.headers.Sections()
   178  	imageOffset := module.headers.OptionalHeader.imageOffset()
   179  	sectionData := sectionFinalizeData{}
   180  	sectionData.address = uintptr(sections[0].PhysicalAddress()) | imageOffset
   181  	sectionData.alignedAddress = alignDown(sectionData.address, uintptr(module.headers.OptionalHeader.SectionAlignment))
   182  	sectionData.size = module.realSectionSize(&sections[0])
   183  	sections[0].SetVirtualSize(uint32(sectionData.size))
   184  	sectionData.characteristics = sections[0].Characteristics
   185  
   186  	// Loop through all sections and change access flags.
   187  	for i := uint16(1); i < module.headers.FileHeader.NumberOfSections; i++ {
   188  		sectionAddress := uintptr(sections[i].PhysicalAddress()) | imageOffset
   189  		alignedAddress := alignDown(sectionAddress, uintptr(module.headers.OptionalHeader.SectionAlignment))
   190  		sectionSize := module.realSectionSize(&sections[i])
   191  		sections[i].SetVirtualSize(uint32(sectionSize))
   192  		// Combine access flags of all sections that share a page.
   193  		// TODO: We currently share flags of a trailing large section with the page of a first small section. This should be optimized.
   194  		if sectionData.alignedAddress == alignedAddress || sectionData.address+sectionData.size > alignedAddress {
   195  			// Section shares page with previous.
   196  			if (sections[i].Characteristics&IMAGE_SCN_MEM_DISCARDABLE) == 0 || (sectionData.characteristics&IMAGE_SCN_MEM_DISCARDABLE) == 0 {
   197  				sectionData.characteristics = (sectionData.characteristics | sections[i].Characteristics) &^ IMAGE_SCN_MEM_DISCARDABLE
   198  			} else {
   199  				sectionData.characteristics |= sections[i].Characteristics
   200  			}
   201  			sectionData.size = sectionAddress + sectionSize - sectionData.address
   202  			continue
   203  		}
   204  
   205  		err := module.finalizeSection(&sectionData)
   206  		if err != nil {
   207  			return fmt.Errorf("Error finalizing section: %w", err)
   208  		}
   209  		sectionData.address = sectionAddress
   210  		sectionData.alignedAddress = alignedAddress
   211  		sectionData.size = sectionSize
   212  		sectionData.characteristics = sections[i].Characteristics
   213  	}
   214  	sectionData.last = true
   215  	err := module.finalizeSection(&sectionData)
   216  	if err != nil {
   217  		return fmt.Errorf("Error finalizing section: %w", err)
   218  	}
   219  	return nil
   220  }
   221  
   222  func (module *Module) executeTLS() {
   223  	directory := module.headerDirectory(IMAGE_DIRECTORY_ENTRY_TLS)
   224  	if directory.VirtualAddress == 0 {
   225  		return
   226  	}
   227  
   228  	tls := (*IMAGE_TLS_DIRECTORY)(a2p(module.codeBase + uintptr(directory.VirtualAddress)))
   229  	callback := tls.AddressOfCallbacks
   230  	if callback != 0 {
   231  		for {
   232  			f := *(*uintptr)(a2p(callback))
   233  			if f == 0 {
   234  				break
   235  			}
   236  			syscall.SyscallN(f, module.codeBase, DLL_PROCESS_ATTACH, 0)
   237  			callback += unsafe.Sizeof(f)
   238  		}
   239  	}
   240  }
   241  
   242  func (module *Module) performBaseRelocation(delta uintptr) (relocated bool, err error) {
   243  	directory := module.headerDirectory(IMAGE_DIRECTORY_ENTRY_BASERELOC)
   244  	if directory.Size == 0 {
   245  		return delta == 0, nil
   246  	}
   247  
   248  	relocationHdr := (*IMAGE_BASE_RELOCATION)(a2p(module.codeBase + uintptr(directory.VirtualAddress)))
   249  	for relocationHdr.VirtualAddress > 0 {
   250  		dest := module.codeBase + uintptr(relocationHdr.VirtualAddress)
   251  
   252  		relInfos := unsafe.Slice(
   253  			(*uint16)(a2p(uintptr(unsafe.Pointer(relocationHdr))+unsafe.Sizeof(*relocationHdr))),
   254  			(uintptr(relocationHdr.SizeOfBlock)-unsafe.Sizeof(*relocationHdr))/unsafe.Sizeof(uint16(0)))
   255  		for _, relInfo := range relInfos {
   256  			// The upper 4 bits define the type of relocation.
   257  			relType := relInfo >> 12
   258  			// The lower 12 bits define the offset.
   259  			relOffset := uintptr(relInfo & 0xfff)
   260  
   261  			switch relType {
   262  			case IMAGE_REL_BASED_ABSOLUTE:
   263  				// Skip relocation.
   264  
   265  			case IMAGE_REL_BASED_LOW:
   266  				*(*uint16)(a2p(dest + relOffset)) += uint16(delta & 0xffff)
   267  				break
   268  
   269  			case IMAGE_REL_BASED_HIGH:
   270  				*(*uint16)(a2p(dest + relOffset)) += uint16(uint32(delta) >> 16)
   271  				break
   272  
   273  			case IMAGE_REL_BASED_HIGHLOW:
   274  				*(*uint32)(a2p(dest + relOffset)) += uint32(delta)
   275  
   276  			case IMAGE_REL_BASED_DIR64:
   277  				*(*uint64)(a2p(dest + relOffset)) += uint64(delta)
   278  
   279  			case IMAGE_REL_BASED_THUMB_MOV32:
   280  				inst := *(*uint32)(a2p(dest + relOffset))
   281  				imm16 := ((inst << 1) & 0x0800) + ((inst << 12) & 0xf000) +
   282  					((inst >> 20) & 0x0700) + ((inst >> 16) & 0x00ff)
   283  				if (inst & 0x8000fbf0) != 0x0000f240 {
   284  					return false, fmt.Errorf("Wrong Thumb2 instruction %08x, expected MOVW", inst)
   285  				}
   286  				imm16 += uint32(delta) & 0xffff
   287  				hiDelta := (uint32(delta&0xffff0000) >> 16) + ((imm16 & 0xffff0000) >> 16)
   288  				*(*uint32)(a2p(dest + relOffset)) = (inst & 0x8f00fbf0) + ((imm16 >> 1) & 0x0400) +
   289  					((imm16 >> 12) & 0x000f) +
   290  					((imm16 << 20) & 0x70000000) +
   291  					((imm16 << 16) & 0xff0000)
   292  				if hiDelta != 0 {
   293  					inst = *(*uint32)(a2p(dest + relOffset + 4))
   294  					imm16 = ((inst << 1) & 0x0800) + ((inst << 12) & 0xf000) +
   295  						((inst >> 20) & 0x0700) + ((inst >> 16) & 0x00ff)
   296  					if (inst & 0x8000fbf0) != 0x0000f2c0 {
   297  						return false, fmt.Errorf("Wrong Thumb2 instruction %08x, expected MOVT", inst)
   298  					}
   299  					imm16 += hiDelta
   300  					if imm16 > 0xffff {
   301  						return false, fmt.Errorf("Resulting immediate value won't fit: %08x", imm16)
   302  					}
   303  					*(*uint32)(a2p(dest + relOffset + 4)) = (inst & 0x8f00fbf0) +
   304  						((imm16 >> 1) & 0x0400) +
   305  						((imm16 >> 12) & 0x000f) +
   306  						((imm16 << 20) & 0x70000000) +
   307  						((imm16 << 16) & 0xff0000)
   308  				}
   309  
   310  			default:
   311  				return false, fmt.Errorf("Unsupported relocation: %v", relType)
   312  			}
   313  		}
   314  
   315  		// Advance to next relocation block.
   316  		relocationHdr = (*IMAGE_BASE_RELOCATION)(a2p(uintptr(unsafe.Pointer(relocationHdr)) + uintptr(relocationHdr.SizeOfBlock)))
   317  	}
   318  	return true, nil
   319  }
   320  
   321  func (module *Module) buildImportTable() error {
   322  	directory := module.headerDirectory(IMAGE_DIRECTORY_ENTRY_IMPORT)
   323  	if directory.Size == 0 {
   324  		return nil
   325  	}
   326  
   327  	module.modules = make([]windows.Handle, 0, 16)
   328  	importDesc := (*IMAGE_IMPORT_DESCRIPTOR)(a2p(module.codeBase + uintptr(directory.VirtualAddress)))
   329  	for importDesc.Name != 0 {
   330  		handle, err := windows.LoadLibraryEx(windows.BytePtrToString((*byte)(a2p(module.codeBase+uintptr(importDesc.Name)))), 0, windows.LOAD_LIBRARY_SEARCH_SYSTEM32)
   331  		if err != nil {
   332  			return fmt.Errorf("Error loading module: %w", err)
   333  		}
   334  		var thunkRef, funcRef *uintptr
   335  		if importDesc.OriginalFirstThunk() != 0 {
   336  			thunkRef = (*uintptr)(a2p(module.codeBase + uintptr(importDesc.OriginalFirstThunk())))
   337  			funcRef = (*uintptr)(a2p(module.codeBase + uintptr(importDesc.FirstThunk)))
   338  		} else {
   339  			// No hint table.
   340  			thunkRef = (*uintptr)(a2p(module.codeBase + uintptr(importDesc.FirstThunk)))
   341  			funcRef = (*uintptr)(a2p(module.codeBase + uintptr(importDesc.FirstThunk)))
   342  		}
   343  		for *thunkRef != 0 {
   344  			if IMAGE_SNAP_BY_ORDINAL(*thunkRef) {
   345  				*funcRef, err = windows.GetProcAddressByOrdinal(handle, IMAGE_ORDINAL(*thunkRef))
   346  			} else {
   347  				thunkData := (*IMAGE_IMPORT_BY_NAME)(a2p(module.codeBase + *thunkRef))
   348  				*funcRef, err = windows.GetProcAddress(handle, windows.BytePtrToString(&thunkData.Name[0]))
   349  			}
   350  			if err != nil {
   351  				windows.FreeLibrary(handle)
   352  				return fmt.Errorf("Error getting function address: %w", err)
   353  			}
   354  			thunkRef = (*uintptr)(a2p(uintptr(unsafe.Pointer(thunkRef)) + unsafe.Sizeof(*thunkRef)))
   355  			funcRef = (*uintptr)(a2p(uintptr(unsafe.Pointer(funcRef)) + unsafe.Sizeof(*funcRef)))
   356  		}
   357  		module.modules = append(module.modules, handle)
   358  		importDesc = (*IMAGE_IMPORT_DESCRIPTOR)(a2p(uintptr(unsafe.Pointer(importDesc)) + unsafe.Sizeof(*importDesc)))
   359  	}
   360  	return nil
   361  }
   362  
   363  func (module *Module) buildNameExports() error {
   364  	directory := module.headerDirectory(IMAGE_DIRECTORY_ENTRY_EXPORT)
   365  	if directory.Size == 0 {
   366  		return errors.New("No export table found")
   367  	}
   368  	exports := (*IMAGE_EXPORT_DIRECTORY)(a2p(module.codeBase + uintptr(directory.VirtualAddress)))
   369  	if exports.NumberOfNames == 0 || exports.NumberOfFunctions == 0 {
   370  		return errors.New("No functions exported")
   371  	}
   372  	if exports.NumberOfNames == 0 {
   373  		return errors.New("No functions exported by name")
   374  	}
   375  	nameRefs := unsafe.Slice((*uint32)(a2p(module.codeBase+uintptr(exports.AddressOfNames))), exports.NumberOfNames)
   376  	ordinals := unsafe.Slice((*uint16)(a2p(module.codeBase+uintptr(exports.AddressOfNameOrdinals))), exports.NumberOfNames)
   377  	module.nameExports = make(map[string]uint16)
   378  	for i := range nameRefs {
   379  		nameArray := windows.BytePtrToString((*byte)(a2p(module.codeBase + uintptr(nameRefs[i]))))
   380  		module.nameExports[nameArray] = ordinals[i]
   381  	}
   382  	return nil
   383  }
   384  
   385  type addressRange struct {
   386  	start uintptr
   387  	end   uintptr
   388  }
   389  
   390  var (
   391  	loadedAddressRanges         []addressRange
   392  	loadedAddressRangesMu       sync.RWMutex
   393  	haveHookedRtlPcToFileHeader sync.Once
   394  	hookRtlPcToFileHeaderResult error
   395  )
   396  
   397  func hookRtlPcToFileHeader() error {
   398  	var kernelBase windows.Handle
   399  	err := windows.GetModuleHandleEx(windows.GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, windows.StringToUTF16Ptr("kernelbase.dll"), &kernelBase)
   400  	if err != nil {
   401  		return err
   402  	}
   403  	imageBase := unsafe.Pointer(kernelBase)
   404  	dosHeader := (*IMAGE_DOS_HEADER)(imageBase)
   405  	ntHeaders := (*IMAGE_NT_HEADERS)(unsafe.Add(imageBase, dosHeader.E_lfanew))
   406  	importsDirectory := ntHeaders.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]
   407  	importDescriptor := (*IMAGE_IMPORT_DESCRIPTOR)(unsafe.Add(imageBase, importsDirectory.VirtualAddress))
   408  	for ; importDescriptor.Name != 0; importDescriptor = (*IMAGE_IMPORT_DESCRIPTOR)(unsafe.Add(unsafe.Pointer(importDescriptor), unsafe.Sizeof(*importDescriptor))) {
   409  		libraryName := windows.BytePtrToString((*byte)(unsafe.Add(imageBase, importDescriptor.Name)))
   410  		if strings.EqualFold(libraryName, "ntdll.dll") {
   411  			break
   412  		}
   413  	}
   414  	if importDescriptor.Name == 0 {
   415  		return errors.New("ntdll.dll not found")
   416  	}
   417  	originalThunk := (*uintptr)(unsafe.Add(imageBase, importDescriptor.OriginalFirstThunk()))
   418  	thunk := (*uintptr)(unsafe.Add(imageBase, importDescriptor.FirstThunk))
   419  	for ; *originalThunk != 0; originalThunk = (*uintptr)(unsafe.Add(unsafe.Pointer(originalThunk), unsafe.Sizeof(*originalThunk))) {
   420  		if *originalThunk&IMAGE_ORDINAL_FLAG == 0 {
   421  			function := (*IMAGE_IMPORT_BY_NAME)(unsafe.Add(imageBase, *originalThunk))
   422  			name := windows.BytePtrToString(&function.Name[0])
   423  			if name == "RtlPcToFileHeader" {
   424  				break
   425  			}
   426  		}
   427  		thunk = (*uintptr)(unsafe.Add(unsafe.Pointer(thunk), unsafe.Sizeof(*thunk)))
   428  	}
   429  	if *originalThunk == 0 {
   430  		return errors.New("RtlPcToFileHeader not found")
   431  	}
   432  	var oldProtect uint32
   433  	err = windows.VirtualProtect(uintptr(unsafe.Pointer(thunk)), unsafe.Sizeof(*thunk), windows.PAGE_READWRITE, &oldProtect)
   434  	if err != nil {
   435  		return err
   436  	}
   437  	originalRtlPcToFileHeader := *thunk
   438  	*thunk = windows.NewCallback(func(pcValue uintptr, baseOfImage *uintptr) uintptr {
   439  		loadedAddressRangesMu.RLock()
   440  		for i := range loadedAddressRanges {
   441  			if pcValue >= loadedAddressRanges[i].start && pcValue < loadedAddressRanges[i].end {
   442  				pcValue = *thunk
   443  				break
   444  			}
   445  		}
   446  		loadedAddressRangesMu.RUnlock()
   447  		ret, _, _ := syscall.SyscallN(originalRtlPcToFileHeader, pcValue, uintptr(unsafe.Pointer(baseOfImage)))
   448  		return ret
   449  	})
   450  	err = windows.VirtualProtect(uintptr(unsafe.Pointer(thunk)), unsafe.Sizeof(*thunk), oldProtect, &oldProtect)
   451  	if err != nil {
   452  		return err
   453  	}
   454  	return nil
   455  }
   456  
   457  // LoadLibrary loads module image to memory.
   458  func LoadLibrary(data []byte) (module *Module, err error) {
   459  	addr := uintptr(unsafe.Pointer(&data[0]))
   460  	size := uintptr(len(data))
   461  	if size < unsafe.Sizeof(IMAGE_DOS_HEADER{}) {
   462  		return nil, errors.New("Incomplete IMAGE_DOS_HEADER")
   463  	}
   464  	dosHeader := (*IMAGE_DOS_HEADER)(a2p(addr))
   465  	if dosHeader.E_magic != IMAGE_DOS_SIGNATURE {
   466  		return nil, fmt.Errorf("Not an MS-DOS binary (provided: %x, expected: %x)", dosHeader.E_magic, IMAGE_DOS_SIGNATURE)
   467  	}
   468  	if (size < uintptr(dosHeader.E_lfanew)+unsafe.Sizeof(IMAGE_NT_HEADERS{})) {
   469  		return nil, errors.New("Incomplete IMAGE_NT_HEADERS")
   470  	}
   471  	oldHeader := (*IMAGE_NT_HEADERS)(a2p(addr + uintptr(dosHeader.E_lfanew)))
   472  	if oldHeader.Signature != IMAGE_NT_SIGNATURE {
   473  		return nil, fmt.Errorf("Not an NT binary (provided: %x, expected: %x)", oldHeader.Signature, IMAGE_NT_SIGNATURE)
   474  	}
   475  	if oldHeader.FileHeader.Machine != imageFileProcess {
   476  		return nil, fmt.Errorf("Foreign platform (provided: %x, expected: %x)", oldHeader.FileHeader.Machine, imageFileProcess)
   477  	}
   478  	if (oldHeader.OptionalHeader.SectionAlignment & 1) != 0 {
   479  		return nil, errors.New("Unaligned section")
   480  	}
   481  	lastSectionEnd := uintptr(0)
   482  	sections := oldHeader.Sections()
   483  	optionalSectionSize := oldHeader.OptionalHeader.SectionAlignment
   484  	for i := range sections {
   485  		var endOfSection uintptr
   486  		if sections[i].SizeOfRawData == 0 {
   487  			// Section without data in the DLL
   488  			endOfSection = uintptr(sections[i].VirtualAddress) + uintptr(optionalSectionSize)
   489  		} else {
   490  			endOfSection = uintptr(sections[i].VirtualAddress) + uintptr(sections[i].SizeOfRawData)
   491  		}
   492  		if endOfSection > lastSectionEnd {
   493  			lastSectionEnd = endOfSection
   494  		}
   495  	}
   496  	alignedImageSize := alignUp(uintptr(oldHeader.OptionalHeader.SizeOfImage), uintptr(oldHeader.OptionalHeader.SectionAlignment))
   497  	if alignedImageSize != alignUp(lastSectionEnd, uintptr(oldHeader.OptionalHeader.SectionAlignment)) {
   498  		return nil, errors.New("Section is not page-aligned")
   499  	}
   500  
   501  	module = &Module{isDLL: (oldHeader.FileHeader.Characteristics & IMAGE_FILE_DLL) != 0}
   502  	defer func() {
   503  		if err != nil {
   504  			module.Free()
   505  			module = nil
   506  		}
   507  	}()
   508  
   509  	// Reserve memory for image of library.
   510  	// TODO: Is it correct to commit the complete memory region at once? Calling DllEntry raises an exception if we don't.
   511  	module.codeBase, err = windows.VirtualAlloc(oldHeader.OptionalHeader.ImageBase,
   512  		alignedImageSize,
   513  		windows.MEM_RESERVE|windows.MEM_COMMIT,
   514  		windows.PAGE_READWRITE)
   515  	if err != nil {
   516  		// Try to allocate memory at arbitrary position.
   517  		module.codeBase, err = windows.VirtualAlloc(0,
   518  			alignedImageSize,
   519  			windows.MEM_RESERVE|windows.MEM_COMMIT,
   520  			windows.PAGE_READWRITE)
   521  		if err != nil {
   522  			err = fmt.Errorf("Error allocating code: %w", err)
   523  			return
   524  		}
   525  	}
   526  	err = module.check4GBBoundaries(alignedImageSize)
   527  	if err != nil {
   528  		err = fmt.Errorf("Error reallocating code: %w", err)
   529  		return
   530  	}
   531  
   532  	if size < uintptr(oldHeader.OptionalHeader.SizeOfHeaders) {
   533  		err = errors.New("Incomplete headers")
   534  		return
   535  	}
   536  	// Commit memory for headers.
   537  	headers, err := windows.VirtualAlloc(module.codeBase,
   538  		uintptr(oldHeader.OptionalHeader.SizeOfHeaders),
   539  		windows.MEM_COMMIT,
   540  		windows.PAGE_READWRITE)
   541  	if err != nil {
   542  		err = fmt.Errorf("Error allocating headers: %w", err)
   543  		return
   544  	}
   545  	// Copy PE header to code.
   546  	memcpy(headers, addr, uintptr(oldHeader.OptionalHeader.SizeOfHeaders))
   547  	module.headers = (*IMAGE_NT_HEADERS)(a2p(headers + uintptr(dosHeader.E_lfanew)))
   548  
   549  	// Update position.
   550  	module.headers.OptionalHeader.ImageBase = module.codeBase
   551  
   552  	// Copy sections from DLL file block to new memory location.
   553  	err = module.copySections(addr, size, oldHeader)
   554  	if err != nil {
   555  		err = fmt.Errorf("Error copying sections: %w", err)
   556  		return
   557  	}
   558  
   559  	// Adjust base address of imported data.
   560  	locationDelta := module.headers.OptionalHeader.ImageBase - oldHeader.OptionalHeader.ImageBase
   561  	if locationDelta != 0 {
   562  		module.isRelocated, err = module.performBaseRelocation(locationDelta)
   563  		if err != nil {
   564  			err = fmt.Errorf("Error relocating module: %w", err)
   565  			return
   566  		}
   567  	} else {
   568  		module.isRelocated = true
   569  	}
   570  
   571  	// Load required dlls and adjust function table of imports.
   572  	err = module.buildImportTable()
   573  	if err != nil {
   574  		err = fmt.Errorf("Error building import table: %w", err)
   575  		return
   576  	}
   577  
   578  	// Mark memory pages depending on section headers and release sections that are marked as "discardable".
   579  	err = module.finalizeSections()
   580  	if err != nil {
   581  		err = fmt.Errorf("Error finalizing sections: %w", err)
   582  		return
   583  	}
   584  
   585  	// Register exception tables, if they exist.
   586  	module.registerExceptionHandlers()
   587  
   588  	// Register function PCs.
   589  	loadedAddressRangesMu.Lock()
   590  	loadedAddressRanges = append(loadedAddressRanges, addressRange{module.codeBase, module.codeBase + alignedImageSize})
   591  	loadedAddressRangesMu.Unlock()
   592  	haveHookedRtlPcToFileHeader.Do(func() {
   593  		hookRtlPcToFileHeaderResult = hookRtlPcToFileHeader()
   594  	})
   595  	err = hookRtlPcToFileHeaderResult
   596  	if err != nil {
   597  		return
   598  	}
   599  
   600  	// TLS callbacks are executed BEFORE the main loading.
   601  	module.executeTLS()
   602  
   603  	// Get entry point of loaded module.
   604  	if module.headers.OptionalHeader.AddressOfEntryPoint != 0 {
   605  		module.entry = module.codeBase + uintptr(module.headers.OptionalHeader.AddressOfEntryPoint)
   606  		if module.isDLL {
   607  			// Notify library about attaching to process.
   608  			r0, _, _ := syscall.SyscallN(module.entry, module.codeBase, DLL_PROCESS_ATTACH, 0)
   609  			successful := r0 != 0
   610  			if !successful {
   611  				err = windows.ERROR_DLL_INIT_FAILED
   612  				return
   613  			}
   614  			module.initialized = true
   615  		}
   616  	}
   617  
   618  	module.buildNameExports()
   619  	return
   620  }
   621  
   622  // Free releases module resources and unloads it.
   623  func (module *Module) Free() {
   624  	if module.initialized {
   625  		// Notify library about detaching from process.
   626  		syscall.SyscallN(module.entry, module.codeBase, DLL_PROCESS_DETACH, 0)
   627  		module.initialized = false
   628  	}
   629  	if module.modules != nil {
   630  		// Free previously opened libraries.
   631  		for _, handle := range module.modules {
   632  			windows.FreeLibrary(handle)
   633  		}
   634  		module.modules = nil
   635  	}
   636  	if module.codeBase != 0 {
   637  		windows.VirtualFree(module.codeBase, 0, windows.MEM_RELEASE)
   638  		module.codeBase = 0
   639  	}
   640  	if module.blockedMemory != nil {
   641  		module.blockedMemory.free()
   642  		module.blockedMemory = nil
   643  	}
   644  }
   645  
   646  // ProcAddressByName returns function address by exported name.
   647  func (module *Module) ProcAddressByName(name string) (uintptr, error) {
   648  	directory := module.headerDirectory(IMAGE_DIRECTORY_ENTRY_EXPORT)
   649  	if directory.Size == 0 {
   650  		return 0, errors.New("No export table found")
   651  	}
   652  	exports := (*IMAGE_EXPORT_DIRECTORY)(a2p(module.codeBase + uintptr(directory.VirtualAddress)))
   653  	if module.nameExports == nil {
   654  		return 0, errors.New("No functions exported by name")
   655  	}
   656  	if idx, ok := module.nameExports[name]; ok {
   657  		if uint32(idx) > exports.NumberOfFunctions {
   658  			return 0, errors.New("Ordinal number too high")
   659  		}
   660  		// AddressOfFunctions contains the RVAs to the "real" functions.
   661  		return module.codeBase + uintptr(*(*uint32)(a2p(module.codeBase + uintptr(exports.AddressOfFunctions) + uintptr(idx)*4))), nil
   662  	}
   663  	return 0, errors.New("Function not found by name")
   664  }
   665  
   666  // ProcAddressByOrdinal returns function address by exported ordinal.
   667  func (module *Module) ProcAddressByOrdinal(ordinal uint16) (uintptr, error) {
   668  	directory := module.headerDirectory(IMAGE_DIRECTORY_ENTRY_EXPORT)
   669  	if directory.Size == 0 {
   670  		return 0, errors.New("No export table found")
   671  	}
   672  	exports := (*IMAGE_EXPORT_DIRECTORY)(a2p(module.codeBase + uintptr(directory.VirtualAddress)))
   673  	if uint32(ordinal) < exports.Base {
   674  		return 0, errors.New("Ordinal number too low")
   675  	}
   676  	idx := ordinal - uint16(exports.Base)
   677  	if uint32(idx) > exports.NumberOfFunctions {
   678  		return 0, errors.New("Ordinal number too high")
   679  	}
   680  	// AddressOfFunctions contains the RVAs to the "real" functions.
   681  	return module.codeBase + uintptr(*(*uint32)(a2p(module.codeBase + uintptr(exports.AddressOfFunctions) + uintptr(idx)*4))), nil
   682  }
   683  
   684  func alignDown(value, alignment uintptr) uintptr {
   685  	return value & ^(alignment - 1)
   686  }
   687  
   688  func alignUp(value, alignment uintptr) uintptr {
   689  	return (value + alignment - 1) & ^(alignment - 1)
   690  }
   691  
   692  func a2p(addr uintptr) unsafe.Pointer {
   693  	return unsafe.Pointer(addr)
   694  }
   695  
   696  func memcpy(dst, src, size uintptr) {
   697  	copy(unsafe.Slice((*byte)(a2p(dst)), size), unsafe.Slice((*byte)(a2p(src)), size))
   698  }