github.com/twelsh-aw/go/src@v0.0.0-20230516233729-a56fe86a7c81/debug/pe/file.go (about) 1 // Copyright 2009 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 /* 6 Package pe implements access to PE (Microsoft Windows Portable Executable) files. 7 8 # Security 9 10 This package is not designed to be hardened against adversarial inputs, and is 11 outside the scope of https://go.dev/security/policy. In particular, only basic 12 validation is done when parsing object files. As such, care should be taken when 13 parsing untrusted inputs, as parsing malformed files may consume significant 14 resources, or cause panics. 15 */ 16 package pe 17 18 import ( 19 "bytes" 20 "compress/zlib" 21 "debug/dwarf" 22 "encoding/binary" 23 "errors" 24 "fmt" 25 "io" 26 "os" 27 "strings" 28 ) 29 30 // Avoid use of post-Go 1.4 io features, to make safe for toolchain bootstrap. 31 const seekStart = 0 32 33 // A File represents an open PE file. 34 type File struct { 35 FileHeader 36 OptionalHeader any // of type *OptionalHeader32 or *OptionalHeader64 37 Sections []*Section 38 Symbols []*Symbol // COFF symbols with auxiliary symbol records removed 39 COFFSymbols []COFFSymbol // all COFF symbols (including auxiliary symbol records) 40 StringTable StringTable 41 42 closer io.Closer 43 } 44 45 // Open opens the named file using os.Open and prepares it for use as a PE binary. 46 func Open(name string) (*File, error) { 47 f, err := os.Open(name) 48 if err != nil { 49 return nil, err 50 } 51 ff, err := NewFile(f) 52 if err != nil { 53 f.Close() 54 return nil, err 55 } 56 ff.closer = f 57 return ff, nil 58 } 59 60 // Close closes the File. 61 // If the File was created using NewFile directly instead of Open, 62 // Close has no effect. 63 func (f *File) Close() error { 64 var err error 65 if f.closer != nil { 66 err = f.closer.Close() 67 f.closer = nil 68 } 69 return err 70 } 71 72 // TODO(brainman): add Load function, as a replacement for NewFile, that does not call removeAuxSymbols (for performance) 73 74 // NewFile creates a new File for accessing a PE binary in an underlying reader. 75 func NewFile(r io.ReaderAt) (*File, error) { 76 f := new(File) 77 sr := io.NewSectionReader(r, 0, 1<<63-1) 78 79 var dosheader [96]byte 80 if _, err := r.ReadAt(dosheader[0:], 0); err != nil { 81 return nil, err 82 } 83 var base int64 84 if dosheader[0] == 'M' && dosheader[1] == 'Z' { 85 signoff := int64(binary.LittleEndian.Uint32(dosheader[0x3c:])) 86 var sign [4]byte 87 r.ReadAt(sign[:], signoff) 88 if !(sign[0] == 'P' && sign[1] == 'E' && sign[2] == 0 && sign[3] == 0) { 89 return nil, fmt.Errorf("invalid PE file signature: % x", sign) 90 } 91 base = signoff + 4 92 } else { 93 base = int64(0) 94 } 95 sr.Seek(base, seekStart) 96 if err := binary.Read(sr, binary.LittleEndian, &f.FileHeader); err != nil { 97 return nil, err 98 } 99 switch f.FileHeader.Machine { 100 case IMAGE_FILE_MACHINE_AMD64, 101 IMAGE_FILE_MACHINE_ARM64, 102 IMAGE_FILE_MACHINE_ARMNT, 103 IMAGE_FILE_MACHINE_I386, 104 IMAGE_FILE_MACHINE_RISCV32, 105 IMAGE_FILE_MACHINE_RISCV64, 106 IMAGE_FILE_MACHINE_RISCV128, 107 IMAGE_FILE_MACHINE_UNKNOWN: 108 // ok 109 default: 110 return nil, fmt.Errorf("unrecognized PE machine: %#x", f.FileHeader.Machine) 111 } 112 113 var err error 114 115 // Read string table. 116 f.StringTable, err = readStringTable(&f.FileHeader, sr) 117 if err != nil { 118 return nil, err 119 } 120 121 // Read symbol table. 122 f.COFFSymbols, err = readCOFFSymbols(&f.FileHeader, sr) 123 if err != nil { 124 return nil, err 125 } 126 f.Symbols, err = removeAuxSymbols(f.COFFSymbols, f.StringTable) 127 if err != nil { 128 return nil, err 129 } 130 131 // Seek past file header. 132 _, err = sr.Seek(base+int64(binary.Size(f.FileHeader)), seekStart) 133 if err != nil { 134 return nil, err 135 } 136 137 // Read optional header. 138 f.OptionalHeader, err = readOptionalHeader(sr, f.FileHeader.SizeOfOptionalHeader) 139 if err != nil { 140 return nil, err 141 } 142 143 // Process sections. 144 f.Sections = make([]*Section, f.FileHeader.NumberOfSections) 145 for i := 0; i < int(f.FileHeader.NumberOfSections); i++ { 146 sh := new(SectionHeader32) 147 if err := binary.Read(sr, binary.LittleEndian, sh); err != nil { 148 return nil, err 149 } 150 name, err := sh.fullName(f.StringTable) 151 if err != nil { 152 return nil, err 153 } 154 s := new(Section) 155 s.SectionHeader = SectionHeader{ 156 Name: name, 157 VirtualSize: sh.VirtualSize, 158 VirtualAddress: sh.VirtualAddress, 159 Size: sh.SizeOfRawData, 160 Offset: sh.PointerToRawData, 161 PointerToRelocations: sh.PointerToRelocations, 162 PointerToLineNumbers: sh.PointerToLineNumbers, 163 NumberOfRelocations: sh.NumberOfRelocations, 164 NumberOfLineNumbers: sh.NumberOfLineNumbers, 165 Characteristics: sh.Characteristics, 166 } 167 r2 := r 168 if sh.PointerToRawData == 0 { // .bss must have all 0s 169 r2 = &nobitsSectionReader{} 170 } 171 s.sr = io.NewSectionReader(r2, int64(s.SectionHeader.Offset), int64(s.SectionHeader.Size)) 172 s.ReaderAt = s.sr 173 f.Sections[i] = s 174 } 175 for i := range f.Sections { 176 var err error 177 f.Sections[i].Relocs, err = readRelocs(&f.Sections[i].SectionHeader, sr) 178 if err != nil { 179 return nil, err 180 } 181 } 182 183 return f, nil 184 } 185 186 type nobitsSectionReader struct{} 187 188 func (*nobitsSectionReader) ReadAt(p []byte, off int64) (n int, err error) { 189 return 0, errors.New("unexpected read from section with uninitialized data") 190 } 191 192 // getString extracts a string from symbol string table. 193 func getString(section []byte, start int) (string, bool) { 194 if start < 0 || start >= len(section) { 195 return "", false 196 } 197 198 for end := start; end < len(section); end++ { 199 if section[end] == 0 { 200 return string(section[start:end]), true 201 } 202 } 203 return "", false 204 } 205 206 // Section returns the first section with the given name, or nil if no such 207 // section exists. 208 func (f *File) Section(name string) *Section { 209 for _, s := range f.Sections { 210 if s.Name == name { 211 return s 212 } 213 } 214 return nil 215 } 216 217 func (f *File) DWARF() (*dwarf.Data, error) { 218 dwarfSuffix := func(s *Section) string { 219 switch { 220 case strings.HasPrefix(s.Name, ".debug_"): 221 return s.Name[7:] 222 case strings.HasPrefix(s.Name, ".zdebug_"): 223 return s.Name[8:] 224 default: 225 return "" 226 } 227 228 } 229 230 // sectionData gets the data for s and checks its size. 231 sectionData := func(s *Section) ([]byte, error) { 232 b, err := s.Data() 233 if err != nil && uint32(len(b)) < s.Size { 234 return nil, err 235 } 236 237 if 0 < s.VirtualSize && s.VirtualSize < s.Size { 238 b = b[:s.VirtualSize] 239 } 240 241 if len(b) >= 12 && string(b[:4]) == "ZLIB" { 242 dlen := binary.BigEndian.Uint64(b[4:12]) 243 dbuf := make([]byte, dlen) 244 r, err := zlib.NewReader(bytes.NewBuffer(b[12:])) 245 if err != nil { 246 return nil, err 247 } 248 if _, err := io.ReadFull(r, dbuf); err != nil { 249 return nil, err 250 } 251 if err := r.Close(); err != nil { 252 return nil, err 253 } 254 b = dbuf 255 } 256 return b, nil 257 } 258 259 // There are many other DWARF sections, but these 260 // are the ones the debug/dwarf package uses. 261 // Don't bother loading others. 262 var dat = map[string][]byte{"abbrev": nil, "info": nil, "str": nil, "line": nil, "ranges": nil} 263 for _, s := range f.Sections { 264 suffix := dwarfSuffix(s) 265 if suffix == "" { 266 continue 267 } 268 if _, ok := dat[suffix]; !ok { 269 continue 270 } 271 272 b, err := sectionData(s) 273 if err != nil { 274 return nil, err 275 } 276 dat[suffix] = b 277 } 278 279 d, err := dwarf.New(dat["abbrev"], nil, nil, dat["info"], dat["line"], nil, dat["ranges"], dat["str"]) 280 if err != nil { 281 return nil, err 282 } 283 284 // Look for DWARF4 .debug_types sections and DWARF5 sections. 285 for i, s := range f.Sections { 286 suffix := dwarfSuffix(s) 287 if suffix == "" { 288 continue 289 } 290 if _, ok := dat[suffix]; ok { 291 // Already handled. 292 continue 293 } 294 295 b, err := sectionData(s) 296 if err != nil { 297 return nil, err 298 } 299 300 if suffix == "types" { 301 err = d.AddTypes(fmt.Sprintf("types-%d", i), b) 302 } else { 303 err = d.AddSection(".debug_"+suffix, b) 304 } 305 if err != nil { 306 return nil, err 307 } 308 } 309 310 return d, nil 311 } 312 313 // TODO(brainman): document ImportDirectory once we decide what to do with it. 314 315 type ImportDirectory struct { 316 OriginalFirstThunk uint32 317 TimeDateStamp uint32 318 ForwarderChain uint32 319 Name uint32 320 FirstThunk uint32 321 322 dll string 323 } 324 325 // ImportedSymbols returns the names of all symbols 326 // referred to by the binary f that are expected to be 327 // satisfied by other libraries at dynamic load time. 328 // It does not return weak symbols. 329 func (f *File) ImportedSymbols() ([]string, error) { 330 if f.OptionalHeader == nil { 331 return nil, nil 332 } 333 334 _, pe64 := f.OptionalHeader.(*OptionalHeader64) 335 336 // grab the number of data directory entries 337 var dd_length uint32 338 if pe64 { 339 dd_length = f.OptionalHeader.(*OptionalHeader64).NumberOfRvaAndSizes 340 } else { 341 dd_length = f.OptionalHeader.(*OptionalHeader32).NumberOfRvaAndSizes 342 } 343 344 // check that the length of data directory entries is large 345 // enough to include the imports directory. 346 if dd_length < IMAGE_DIRECTORY_ENTRY_IMPORT+1 { 347 return nil, nil 348 } 349 350 // grab the import data directory entry 351 var idd DataDirectory 352 if pe64 { 353 idd = f.OptionalHeader.(*OptionalHeader64).DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT] 354 } else { 355 idd = f.OptionalHeader.(*OptionalHeader32).DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT] 356 } 357 358 // figure out which section contains the import directory table 359 var ds *Section 360 ds = nil 361 for _, s := range f.Sections { 362 if s.Offset == 0 { 363 continue 364 } 365 // We are using distance between s.VirtualAddress and idd.VirtualAddress 366 // to avoid potential overflow of uint32 caused by addition of s.VirtualSize 367 // to s.VirtualAddress. 368 if s.VirtualAddress <= idd.VirtualAddress && idd.VirtualAddress-s.VirtualAddress < s.VirtualSize { 369 ds = s 370 break 371 } 372 } 373 374 // didn't find a section, so no import libraries were found 375 if ds == nil { 376 return nil, nil 377 } 378 379 d, err := ds.Data() 380 if err != nil { 381 return nil, err 382 } 383 384 // seek to the virtual address specified in the import data directory 385 d = d[idd.VirtualAddress-ds.VirtualAddress:] 386 387 // start decoding the import directory 388 var ida []ImportDirectory 389 for len(d) >= 20 { 390 var dt ImportDirectory 391 dt.OriginalFirstThunk = binary.LittleEndian.Uint32(d[0:4]) 392 dt.TimeDateStamp = binary.LittleEndian.Uint32(d[4:8]) 393 dt.ForwarderChain = binary.LittleEndian.Uint32(d[8:12]) 394 dt.Name = binary.LittleEndian.Uint32(d[12:16]) 395 dt.FirstThunk = binary.LittleEndian.Uint32(d[16:20]) 396 d = d[20:] 397 if dt.OriginalFirstThunk == 0 { 398 break 399 } 400 ida = append(ida, dt) 401 } 402 // TODO(brainman): this needs to be rewritten 403 // ds.Data() returns contents of section containing import table. Why store in variable called "names"? 404 // Why we are retrieving it second time? We already have it in "d", and it is not modified anywhere. 405 // getString does not extracts a string from symbol string table (as getString doco says). 406 // Why ds.Data() called again and again in the loop? 407 // Needs test before rewrite. 408 names, _ := ds.Data() 409 var all []string 410 for _, dt := range ida { 411 dt.dll, _ = getString(names, int(dt.Name-ds.VirtualAddress)) 412 d, _ = ds.Data() 413 // seek to OriginalFirstThunk 414 d = d[dt.OriginalFirstThunk-ds.VirtualAddress:] 415 for len(d) > 0 { 416 if pe64 { // 64bit 417 va := binary.LittleEndian.Uint64(d[0:8]) 418 d = d[8:] 419 if va == 0 { 420 break 421 } 422 if va&0x8000000000000000 > 0 { // is Ordinal 423 // TODO add dynimport ordinal support. 424 } else { 425 fn, _ := getString(names, int(uint32(va)-ds.VirtualAddress+2)) 426 all = append(all, fn+":"+dt.dll) 427 } 428 } else { // 32bit 429 va := binary.LittleEndian.Uint32(d[0:4]) 430 d = d[4:] 431 if va == 0 { 432 break 433 } 434 if va&0x80000000 > 0 { // is Ordinal 435 // TODO add dynimport ordinal support. 436 //ord := va&0x0000FFFF 437 } else { 438 fn, _ := getString(names, int(va-ds.VirtualAddress+2)) 439 all = append(all, fn+":"+dt.dll) 440 } 441 } 442 } 443 } 444 445 return all, nil 446 } 447 448 // ImportedLibraries returns the names of all libraries 449 // referred to by the binary f that are expected to be 450 // linked with the binary at dynamic link time. 451 func (f *File) ImportedLibraries() ([]string, error) { 452 // TODO 453 // cgo -dynimport don't use this for windows PE, so just return. 454 return nil, nil 455 } 456 457 // FormatError is unused. 458 // The type is retained for compatibility. 459 type FormatError struct { 460 } 461 462 func (e *FormatError) Error() string { 463 return "unknown error" 464 } 465 466 // readOptionalHeader accepts an io.ReadSeeker pointing to optional header in the PE file 467 // and its size as seen in the file header. 468 // It parses the given size of bytes and returns optional header. It infers whether the 469 // bytes being parsed refer to 32 bit or 64 bit version of optional header. 470 func readOptionalHeader(r io.ReadSeeker, sz uint16) (any, error) { 471 // If optional header size is 0, return empty optional header. 472 if sz == 0 { 473 return nil, nil 474 } 475 476 var ( 477 // First couple of bytes in option header state its type. 478 // We need to read them first to determine the type and 479 // validity of optional header. 480 ohMagic uint16 481 ohMagicSz = binary.Size(ohMagic) 482 ) 483 484 // If optional header size is greater than 0 but less than its magic size, return error. 485 if sz < uint16(ohMagicSz) { 486 return nil, fmt.Errorf("optional header size is less than optional header magic size") 487 } 488 489 // read reads from io.ReadSeeke, r, into data. 490 var err error 491 read := func(data any) bool { 492 err = binary.Read(r, binary.LittleEndian, data) 493 return err == nil 494 } 495 496 if !read(&ohMagic) { 497 return nil, fmt.Errorf("failure to read optional header magic: %v", err) 498 499 } 500 501 switch ohMagic { 502 case 0x10b: // PE32 503 var ( 504 oh32 OptionalHeader32 505 // There can be 0 or more data directories. So the minimum size of optional 506 // header is calculated by subtracting oh32.DataDirectory size from oh32 size. 507 oh32MinSz = binary.Size(oh32) - binary.Size(oh32.DataDirectory) 508 ) 509 510 if sz < uint16(oh32MinSz) { 511 return nil, fmt.Errorf("optional header size(%d) is less minimum size (%d) of PE32 optional header", sz, oh32MinSz) 512 } 513 514 // Init oh32 fields 515 oh32.Magic = ohMagic 516 if !read(&oh32.MajorLinkerVersion) || 517 !read(&oh32.MinorLinkerVersion) || 518 !read(&oh32.SizeOfCode) || 519 !read(&oh32.SizeOfInitializedData) || 520 !read(&oh32.SizeOfUninitializedData) || 521 !read(&oh32.AddressOfEntryPoint) || 522 !read(&oh32.BaseOfCode) || 523 !read(&oh32.BaseOfData) || 524 !read(&oh32.ImageBase) || 525 !read(&oh32.SectionAlignment) || 526 !read(&oh32.FileAlignment) || 527 !read(&oh32.MajorOperatingSystemVersion) || 528 !read(&oh32.MinorOperatingSystemVersion) || 529 !read(&oh32.MajorImageVersion) || 530 !read(&oh32.MinorImageVersion) || 531 !read(&oh32.MajorSubsystemVersion) || 532 !read(&oh32.MinorSubsystemVersion) || 533 !read(&oh32.Win32VersionValue) || 534 !read(&oh32.SizeOfImage) || 535 !read(&oh32.SizeOfHeaders) || 536 !read(&oh32.CheckSum) || 537 !read(&oh32.Subsystem) || 538 !read(&oh32.DllCharacteristics) || 539 !read(&oh32.SizeOfStackReserve) || 540 !read(&oh32.SizeOfStackCommit) || 541 !read(&oh32.SizeOfHeapReserve) || 542 !read(&oh32.SizeOfHeapCommit) || 543 !read(&oh32.LoaderFlags) || 544 !read(&oh32.NumberOfRvaAndSizes) { 545 return nil, fmt.Errorf("failure to read PE32 optional header: %v", err) 546 } 547 548 dd, err := readDataDirectories(r, sz-uint16(oh32MinSz), oh32.NumberOfRvaAndSizes) 549 if err != nil { 550 return nil, err 551 } 552 553 copy(oh32.DataDirectory[:], dd) 554 555 return &oh32, nil 556 case 0x20b: // PE32+ 557 var ( 558 oh64 OptionalHeader64 559 // There can be 0 or more data directories. So the minimum size of optional 560 // header is calculated by subtracting oh64.DataDirectory size from oh64 size. 561 oh64MinSz = binary.Size(oh64) - binary.Size(oh64.DataDirectory) 562 ) 563 564 if sz < uint16(oh64MinSz) { 565 return nil, fmt.Errorf("optional header size(%d) is less minimum size (%d) for PE32+ optional header", sz, oh64MinSz) 566 } 567 568 // Init oh64 fields 569 oh64.Magic = ohMagic 570 if !read(&oh64.MajorLinkerVersion) || 571 !read(&oh64.MinorLinkerVersion) || 572 !read(&oh64.SizeOfCode) || 573 !read(&oh64.SizeOfInitializedData) || 574 !read(&oh64.SizeOfUninitializedData) || 575 !read(&oh64.AddressOfEntryPoint) || 576 !read(&oh64.BaseOfCode) || 577 !read(&oh64.ImageBase) || 578 !read(&oh64.SectionAlignment) || 579 !read(&oh64.FileAlignment) || 580 !read(&oh64.MajorOperatingSystemVersion) || 581 !read(&oh64.MinorOperatingSystemVersion) || 582 !read(&oh64.MajorImageVersion) || 583 !read(&oh64.MinorImageVersion) || 584 !read(&oh64.MajorSubsystemVersion) || 585 !read(&oh64.MinorSubsystemVersion) || 586 !read(&oh64.Win32VersionValue) || 587 !read(&oh64.SizeOfImage) || 588 !read(&oh64.SizeOfHeaders) || 589 !read(&oh64.CheckSum) || 590 !read(&oh64.Subsystem) || 591 !read(&oh64.DllCharacteristics) || 592 !read(&oh64.SizeOfStackReserve) || 593 !read(&oh64.SizeOfStackCommit) || 594 !read(&oh64.SizeOfHeapReserve) || 595 !read(&oh64.SizeOfHeapCommit) || 596 !read(&oh64.LoaderFlags) || 597 !read(&oh64.NumberOfRvaAndSizes) { 598 return nil, fmt.Errorf("failure to read PE32+ optional header: %v", err) 599 } 600 601 dd, err := readDataDirectories(r, sz-uint16(oh64MinSz), oh64.NumberOfRvaAndSizes) 602 if err != nil { 603 return nil, err 604 } 605 606 copy(oh64.DataDirectory[:], dd) 607 608 return &oh64, nil 609 default: 610 return nil, fmt.Errorf("optional header has unexpected Magic of 0x%x", ohMagic) 611 } 612 } 613 614 // readDataDirectories accepts an io.ReadSeeker pointing to data directories in the PE file, 615 // its size and number of data directories as seen in optional header. 616 // It parses the given size of bytes and returns given number of data directories. 617 func readDataDirectories(r io.ReadSeeker, sz uint16, n uint32) ([]DataDirectory, error) { 618 ddSz := uint64(binary.Size(DataDirectory{})) 619 if uint64(sz) != uint64(n)*ddSz { 620 return nil, fmt.Errorf("size of data directories(%d) is inconsistent with number of data directories(%d)", sz, n) 621 } 622 623 dd := make([]DataDirectory, n) 624 if err := binary.Read(r, binary.LittleEndian, dd); err != nil { 625 return nil, fmt.Errorf("failure to read data directories: %v", err) 626 } 627 628 return dd, nil 629 }