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 // Package pe implements access to PE (Microsoft Windows Portable Executable) files. 6 package pe 7 8 import ( 9 "debug/dwarf" 10 "encoding/binary" 11 "errors" 12 "fmt" 13 "io" 14 "os" 15 "strconv" 16 ) 17 18 // A File represents an open PE file. 19 type File struct { 20 FileHeader 21 OptionalHeader interface{} // of type *OptionalHeader32 or *OptionalHeader64 22 Sections []*Section 23 Symbols []*Symbol 24 25 closer io.Closer 26 } 27 28 type SectionHeader struct { 29 Name string 30 VirtualSize uint32 31 VirtualAddress uint32 32 Size uint32 33 Offset uint32 34 PointerToRelocations uint32 35 PointerToLineNumbers uint32 36 NumberOfRelocations uint16 37 NumberOfLineNumbers uint16 38 Characteristics uint32 39 } 40 41 type Section struct { 42 SectionHeader 43 44 // Embed ReaderAt for ReadAt method. 45 // Do not embed SectionReader directly 46 // to avoid having Read and Seek. 47 // If a client wants Read and Seek it must use 48 // Open() to avoid fighting over the seek offset 49 // with other clients. 50 io.ReaderAt 51 sr *io.SectionReader 52 } 53 54 type Symbol struct { 55 Name string 56 Value uint32 57 SectionNumber int16 58 Type uint16 59 StorageClass uint8 60 } 61 62 type ImportDirectory struct { 63 OriginalFirstThunk uint32 64 TimeDateStamp uint32 65 ForwarderChain uint32 66 Name uint32 67 FirstThunk uint32 68 69 dll string 70 } 71 72 // Data reads and returns the contents of the PE section. 73 func (s *Section) Data() ([]byte, error) { 74 dat := make([]byte, s.sr.Size()) 75 n, err := s.sr.ReadAt(dat, 0) 76 if n == len(dat) { 77 err = nil 78 } 79 return dat[0:n], err 80 } 81 82 // Open returns a new ReadSeeker reading the PE section. 83 func (s *Section) Open() io.ReadSeeker { return io.NewSectionReader(s.sr, 0, 1<<63-1) } 84 85 type FormatError struct { 86 off int64 87 msg string 88 val interface{} 89 } 90 91 func (e *FormatError) Error() string { 92 msg := e.msg 93 if e.val != nil { 94 msg += fmt.Sprintf(" '%v'", e.val) 95 } 96 msg += fmt.Sprintf(" in record at byte %#x", e.off) 97 return msg 98 } 99 100 // Open opens the named file using os.Open and prepares it for use as a PE binary. 101 func Open(name string) (*File, error) { 102 f, err := os.Open(name) 103 if err != nil { 104 return nil, err 105 } 106 ff, err := NewFile(f) 107 if err != nil { 108 f.Close() 109 return nil, err 110 } 111 ff.closer = f 112 return ff, nil 113 } 114 115 // Close closes the File. 116 // If the File was created using NewFile directly instead of Open, 117 // Close has no effect. 118 func (f *File) Close() error { 119 var err error 120 if f.closer != nil { 121 err = f.closer.Close() 122 f.closer = nil 123 } 124 return err 125 } 126 127 var ( 128 sizeofOptionalHeader32 = uint16(binary.Size(OptionalHeader32{})) 129 sizeofOptionalHeader64 = uint16(binary.Size(OptionalHeader64{})) 130 ) 131 132 // NewFile creates a new File for accessing a PE binary in an underlying reader. 133 func NewFile(r io.ReaderAt) (*File, error) { 134 f := new(File) 135 sr := io.NewSectionReader(r, 0, 1<<63-1) 136 137 var dosheader [96]byte 138 if _, err := r.ReadAt(dosheader[0:], 0); err != nil { 139 return nil, err 140 } 141 var base int64 142 if dosheader[0] == 'M' && dosheader[1] == 'Z' { 143 signoff := int64(binary.LittleEndian.Uint32(dosheader[0x3c:])) 144 var sign [4]byte 145 r.ReadAt(sign[:], signoff) 146 if !(sign[0] == 'P' && sign[1] == 'E' && sign[2] == 0 && sign[3] == 0) { 147 return nil, errors.New("Invalid PE File Format.") 148 } 149 base = signoff + 4 150 } else { 151 base = int64(0) 152 } 153 sr.Seek(base, os.SEEK_SET) 154 if err := binary.Read(sr, binary.LittleEndian, &f.FileHeader); err != nil { 155 return nil, err 156 } 157 if f.FileHeader.Machine != IMAGE_FILE_MACHINE_UNKNOWN && f.FileHeader.Machine != IMAGE_FILE_MACHINE_AMD64 && f.FileHeader.Machine != IMAGE_FILE_MACHINE_I386 { 158 return nil, errors.New("Invalid PE File Format.") 159 } 160 161 var ss []byte 162 if f.FileHeader.NumberOfSymbols > 0 { 163 // Get COFF string table, which is located at the end of the COFF symbol table. 164 sr.Seek(int64(f.FileHeader.PointerToSymbolTable+COFFSymbolSize*f.FileHeader.NumberOfSymbols), os.SEEK_SET) 165 var l uint32 166 if err := binary.Read(sr, binary.LittleEndian, &l); err != nil { 167 return nil, err 168 } 169 ss = make([]byte, l) 170 if _, err := r.ReadAt(ss, int64(f.FileHeader.PointerToSymbolTable+COFFSymbolSize*f.FileHeader.NumberOfSymbols)); err != nil { 171 return nil, err 172 } 173 174 // Process COFF symbol table. 175 sr.Seek(int64(f.FileHeader.PointerToSymbolTable), os.SEEK_SET) 176 aux := uint8(0) 177 for i := 0; i < int(f.FileHeader.NumberOfSymbols); i++ { 178 cs := new(COFFSymbol) 179 if err := binary.Read(sr, binary.LittleEndian, cs); err != nil { 180 return nil, err 181 } 182 if aux > 0 { 183 aux-- 184 continue 185 } 186 var name string 187 if cs.Name[0] == 0 && cs.Name[1] == 0 && cs.Name[2] == 0 && cs.Name[3] == 0 { 188 si := int(binary.LittleEndian.Uint32(cs.Name[4:])) 189 name, _ = getString(ss, si) 190 } else { 191 name = cstring(cs.Name[:]) 192 } 193 aux = cs.NumberOfAuxSymbols 194 s := &Symbol{ 195 Name: name, 196 Value: cs.Value, 197 SectionNumber: cs.SectionNumber, 198 Type: cs.Type, 199 StorageClass: cs.StorageClass, 200 } 201 f.Symbols = append(f.Symbols, s) 202 } 203 } 204 205 // Read optional header. 206 sr.Seek(base, os.SEEK_SET) 207 if err := binary.Read(sr, binary.LittleEndian, &f.FileHeader); err != nil { 208 return nil, err 209 } 210 var oh32 OptionalHeader32 211 var oh64 OptionalHeader64 212 switch f.FileHeader.SizeOfOptionalHeader { 213 case sizeofOptionalHeader32: 214 if err := binary.Read(sr, binary.LittleEndian, &oh32); err != nil { 215 return nil, err 216 } 217 if oh32.Magic != 0x10b { // PE32 218 return nil, fmt.Errorf("pe32 optional header has unexpected Magic of 0x%x", oh32.Magic) 219 } 220 f.OptionalHeader = &oh32 221 case sizeofOptionalHeader64: 222 if err := binary.Read(sr, binary.LittleEndian, &oh64); err != nil { 223 return nil, err 224 } 225 if oh64.Magic != 0x20b { // PE32+ 226 return nil, fmt.Errorf("pe32+ optional header has unexpected Magic of 0x%x", oh64.Magic) 227 } 228 f.OptionalHeader = &oh64 229 } 230 231 // Process sections. 232 f.Sections = make([]*Section, f.FileHeader.NumberOfSections) 233 for i := 0; i < int(f.FileHeader.NumberOfSections); i++ { 234 sh := new(SectionHeader32) 235 if err := binary.Read(sr, binary.LittleEndian, sh); err != nil { 236 return nil, err 237 } 238 var name string 239 if sh.Name[0] == '\x2F' { 240 si, _ := strconv.Atoi(cstring(sh.Name[1:])) 241 name, _ = getString(ss, si) 242 } else { 243 name = cstring(sh.Name[0:]) 244 } 245 s := new(Section) 246 s.SectionHeader = SectionHeader{ 247 Name: name, 248 VirtualSize: sh.VirtualSize, 249 VirtualAddress: sh.VirtualAddress, 250 Size: sh.SizeOfRawData, 251 Offset: sh.PointerToRawData, 252 PointerToRelocations: sh.PointerToRelocations, 253 PointerToLineNumbers: sh.PointerToLineNumbers, 254 NumberOfRelocations: sh.NumberOfRelocations, 255 NumberOfLineNumbers: sh.NumberOfLineNumbers, 256 Characteristics: sh.Characteristics, 257 } 258 s.sr = io.NewSectionReader(r, int64(s.SectionHeader.Offset), int64(s.SectionHeader.Size)) 259 s.ReaderAt = s.sr 260 f.Sections[i] = s 261 } 262 return f, nil 263 } 264 265 func cstring(b []byte) string { 266 var i int 267 for i = 0; i < len(b) && b[i] != 0; i++ { 268 } 269 return string(b[0:i]) 270 } 271 272 // getString extracts a string from symbol string table. 273 func getString(section []byte, start int) (string, bool) { 274 if start < 0 || start >= len(section) { 275 return "", false 276 } 277 278 for end := start; end < len(section); end++ { 279 if section[end] == 0 { 280 return string(section[start:end]), true 281 } 282 } 283 return "", false 284 } 285 286 // Section returns the first section with the given name, or nil if no such 287 // section exists. 288 func (f *File) Section(name string) *Section { 289 for _, s := range f.Sections { 290 if s.Name == name { 291 return s 292 } 293 } 294 return nil 295 } 296 297 func (f *File) DWARF() (*dwarf.Data, error) { 298 // There are many other DWARF sections, but these 299 // are the ones the debug/dwarf package uses. 300 // Don't bother loading others. 301 var names = [...]string{"abbrev", "info", "line", "str"} 302 var dat [len(names)][]byte 303 for i, name := range names { 304 name = ".debug_" + name 305 s := f.Section(name) 306 if s == nil { 307 continue 308 } 309 b, err := s.Data() 310 if err != nil && uint32(len(b)) < s.Size { 311 return nil, err 312 } 313 if 0 < s.VirtualSize && s.VirtualSize < s.Size { 314 b = b[:s.VirtualSize] 315 } 316 dat[i] = b 317 } 318 319 abbrev, info, line, str := dat[0], dat[1], dat[2], dat[3] 320 return dwarf.New(abbrev, nil, nil, info, line, nil, nil, str) 321 } 322 323 // ImportedSymbols returns the names of all symbols 324 // referred to by the binary f that are expected to be 325 // satisfied by other libraries at dynamic load time. 326 // It does not return weak symbols. 327 func (f *File) ImportedSymbols() ([]string, error) { 328 pe64 := f.Machine == IMAGE_FILE_MACHINE_AMD64 329 ds := f.Section(".idata") 330 if ds == nil { 331 // not dynamic, so no libraries 332 return nil, nil 333 } 334 d, err := ds.Data() 335 if err != nil { 336 return nil, err 337 } 338 var ida []ImportDirectory 339 for len(d) > 0 { 340 var dt ImportDirectory 341 dt.OriginalFirstThunk = binary.LittleEndian.Uint32(d[0:4]) 342 dt.Name = binary.LittleEndian.Uint32(d[12:16]) 343 dt.FirstThunk = binary.LittleEndian.Uint32(d[16:20]) 344 d = d[20:] 345 if dt.OriginalFirstThunk == 0 { 346 break 347 } 348 ida = append(ida, dt) 349 } 350 names, _ := ds.Data() 351 var all []string 352 for _, dt := range ida { 353 dt.dll, _ = getString(names, int(dt.Name-ds.VirtualAddress)) 354 d, _ = ds.Data() 355 // seek to OriginalFirstThunk 356 d = d[dt.OriginalFirstThunk-ds.VirtualAddress:] 357 for len(d) > 0 { 358 if pe64 { // 64bit 359 va := binary.LittleEndian.Uint64(d[0:8]) 360 d = d[8:] 361 if va == 0 { 362 break 363 } 364 if va&0x8000000000000000 > 0 { // is Ordinal 365 // TODO add dynimport ordinal support. 366 } else { 367 fn, _ := getString(names, int(uint32(va)-ds.VirtualAddress+2)) 368 all = append(all, fn+":"+dt.dll) 369 } 370 } else { // 32bit 371 va := binary.LittleEndian.Uint32(d[0:4]) 372 d = d[4:] 373 if va == 0 { 374 break 375 } 376 if va&0x80000000 > 0 { // is Ordinal 377 // TODO add dynimport ordinal support. 378 //ord := va&0x0000FFFF 379 } else { 380 fn, _ := getString(names, int(va-ds.VirtualAddress+2)) 381 all = append(all, fn+":"+dt.dll) 382 } 383 } 384 } 385 } 386 387 return all, nil 388 } 389 390 // ImportedLibraries returns the names of all libraries 391 // referred to by the binary f that are expected to be 392 // linked with the binary at dynamic link time. 393 func (f *File) ImportedLibraries() ([]string, error) { 394 // TODO 395 // cgo -dynimport don't use this for windows PE, so just return. 396 return nil, nil 397 } 398