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