github.com/saferwall/pe@v1.5.2/section.go (about) 1 // Copyright 2018 Saferwall. All rights reserved. 2 // Use of this source code is governed by Apache v2 license 3 // license that can be found in the LICENSE file. 4 5 package pe 6 7 import ( 8 "encoding/binary" 9 "math" 10 "sort" 11 "strings" 12 ) 13 14 const ( 15 // ImageSectionReserved1 for future use. 16 ImageSectionReserved1 = 0x00000000 17 18 // ImageSectionReserved2 for future use. 19 ImageSectionReserved2 = 0x00000001 20 21 // ImageSectionReserved3 for future use. 22 ImageSectionReserved3 = 0x00000002 23 24 // ImageSectionReserved4 for future use. 25 ImageSectionReserved4 = 0x00000004 26 27 // ImageSectionTypeNoPad indicates the section should not be padded to the next 28 // boundary. This flag is obsolete and is replaced by ImageSectionAlign1Bytes. 29 // This is valid only for object files. 30 ImageSectionTypeNoPad = 0x00000008 31 32 // ImageSectionReserved5 for future use. 33 ImageSectionReserved5 = 0x00000010 34 35 // ImageSectionCntCode indicates the section contains executable code. 36 ImageSectionCntCode = 0x00000020 37 38 // ImageSectionCntInitializedData indicates the section contains initialized 39 // data. 40 ImageSectionCntInitializedData = 0x00000040 41 42 // ImageSectionCntUninitializedData indicates the section contains uninitialized 43 // data. 44 ImageSectionCntUninitializedData = 0x00000080 45 46 // ImageSectionLnkOther is reserved for future use. 47 ImageSectionLnkOther = 0x00000100 48 49 // ImageSectionLnkInfo indicates the section contains comments or other 50 // information. The .drectve section has this type. This is valid for 51 // object files only. 52 ImageSectionLnkInfo = 0x00000200 53 54 // ImageSectionReserved6 for future use. 55 ImageSectionReserved6 = 0x00000400 56 57 // ImageSectionLnkRemove indicates the section will not become part of the image 58 // This is valid only for object files. 59 ImageSectionLnkRemove = 0x00000800 60 61 // ImageSectionLnkComdat indicates the section contains COMDAT data. For more 62 // information, see COMDAT Sections (Object Only). This is valid only for 63 // object files. 64 ImageSectionLnkCOMDAT = 0x00001000 65 66 // ImageSectionGpRel indicates the section contains data referenced through the 67 // global pointer (GP). 68 ImageSectionGpRel = 0x00008000 69 70 // ImageSectionMemPurgeable is reserved for future use. 71 ImageSectionMemPurgeable = 0x00020000 72 73 // ImageSectionMem16Bit is reserved for future use. 74 ImageSectionMem16Bit = 0x00020000 75 76 // ImageSectionMemLocked is reserved for future use. 77 ImageSectionMemLocked = 0x00040000 78 79 // ImageSectionMemPreload is reserved for future use. 80 ImageSectionMemPreload = 0x00080000 81 82 // ImageSectionAlign1Bytes indicates to align data on a 1-byte boundary. 83 // Valid only for object files. 84 ImageSectionAlign1Bytes = 0x00100000 85 86 // ImageSectionAlign2Bytes indicates to align data on a 2-byte boundary. 87 // Valid only for object files. 88 ImageSectionAlign2Bytes = 0x00200000 89 90 // ImageSectionAlign4Bytes indicates to align data on a 4-byte boundary. 91 // Valid only for object files. 92 ImageSectionAlign4Bytes = 0x00300000 93 94 // ImageSectionAlign8Bytes indicates to align data on a 8-byte boundary. 95 // Valid only for object files. 96 ImageSectionAlign8Bytes = 0x00400000 97 98 // ImageSectionAlign16Bytes indicates to align data on a 16-byte boundary. 99 // Valid only for object files. 100 ImageSectionAlign16Bytes = 0x00500000 101 102 // ImageSectionAlign32Bytes indicates to align data on a 32-byte boundary. 103 // Valid only for object files. 104 ImageSectionAlign32Bytes = 0x00600000 105 106 // ImageSectionAlign64Bytes indicates to align data on a 64-byte boundary. 107 // Valid only for object files. 108 ImageSectionAlign64Bytes = 0x00700000 109 110 // ImageSectionAlign128Bytes indicates to align data on a 128-byte boundary. 111 // Valid only for object files. 112 ImageSectionAlign128Bytes = 0x00800000 113 114 // ImageSectionAlign256Bytes indicates to align data on a 256-byte boundary. 115 // Valid only for object files. 116 ImageSectionAlign256Bytes = 0x00900000 117 118 // ImageSectionAlign512Bytes indicates to align data on a 512-byte boundary. 119 // Valid only for object files. 120 ImageSectionAlign512Bytes = 0x00A00000 121 122 // ImageSectionAlign1024Bytes indicates to align data on a 1024-byte boundary. 123 // Valid only for object files. 124 ImageSectionAlign1024Bytes = 0x00B00000 125 126 // ImageSectionAlign2048Bytes indicates to align data on a 2048-byte boundary. 127 // Valid only for object files. 128 ImageSectionAlign2048Bytes = 0x00C00000 129 130 // ImageSectionAlign4096Bytes indicates to align data on a 4096-byte boundary. 131 // Valid only for object files. 132 ImageSectionAlign4096Bytes = 0x00D00000 133 134 // ImageSectionAlign8192Bytes indicates to align data on a 8192-byte boundary. 135 // Valid only for object files. 136 ImageSectionAlign8192Bytes = 0x00E00000 137 138 // ImageSectionLnkMRelocOvfl indicates the section contains extended 139 // relocations. 140 ImageSectionLnkMRelocOvfl = 0x01000000 141 142 // ImageSectionMemDiscardable indicates the section can be discarded as needed. 143 ImageSectionMemDiscardable = 0x02000000 144 145 // ImageSectionMemNotCached indicates the section cannot be cached. 146 ImageSectionMemNotCached = 0x04000000 147 148 // ImageSectionMemNotPaged indicates the section is not pageable. 149 ImageSectionMemNotPaged = 0x08000000 150 151 // ImageSectionMemShared indicates the section can be shared in memory. 152 ImageSectionMemShared = 0x10000000 153 154 // ImageSectionMemExecute indicates the section can be executed as code. 155 ImageSectionMemExecute = 0x20000000 156 157 // ImageSectionMemRead indicates the section can be read. 158 ImageSectionMemRead = 0x40000000 159 160 // ImageSectionMemWrite indicates the section can be written to. 161 ImageSectionMemWrite = 0x80000000 162 ) 163 164 // ImageSectionHeader is part of the section table , in fact section table is an 165 // array of Image Section Header each contains information about one section of 166 // the whole file such as attribute,virtual offset. the array size is the number 167 // of sections in the file. 168 // Binary Spec : each struct is 40 byte and there is no padding . 169 type ImageSectionHeader struct { 170 171 // An 8-byte, null-padded UTF-8 encoded string. If the string is exactly 8 172 // characters long, there is no terminating null. For longer names, this 173 // field contains a slash (/) that is followed by an ASCII representation of 174 // a decimal number that is an offset into the string table. Executable 175 // images do not use a string table and do not support section names longer 176 // than 8 characters. Long names in object files are truncated if they are 177 // emitted to an executable file. 178 Name [8]uint8 `json:"name"` 179 180 // The total size of the section when loaded into memory. If this value is 181 // greater than SizeOfRawData, the section is zero-padded. This field is 182 // valid only for executable images and should be set to zero for object files. 183 VirtualSize uint32 `json:"virtual_size"` 184 185 // For executable images, the address of the first byte of the section 186 // relative to the image base when the section is loaded into memory. 187 // For object files, this field is the address of the first byte before 188 // relocation is applied; for simplicity, compilers should set this to zero. 189 // Otherwise, it is an arbitrary value that is subtracted from offsets during 190 // relocation. 191 VirtualAddress uint32 `json:"virtual_address"` 192 193 // The size of the section (for object files) or the size of the initialized 194 // data on disk (for image files). For executable images, this must be a 195 // multiple of FileAlignment from the optional header. If this is less than 196 // VirtualSize, the remainder of the section is zero-filled. Because the 197 // SizeOfRawData field is rounded but the VirtualSize field is not, it is 198 // possible for SizeOfRawData to be greater than VirtualSize as well. When 199 // a section contains only uninitialized data, this field should be zero. 200 SizeOfRawData uint32 `json:"size_of_raw_data"` 201 202 // The file pointer to the first page of the section within the COFF file. 203 // For executable images, this must be a multiple of FileAlignment from the 204 // optional header. For object files, the value should be aligned on a 205 // 4-byte boundary for best performance. When a section contains only 206 // uninitialized data, this field should be zero. 207 PointerToRawData uint32 `json:"pointer_to_raw_data"` 208 209 // The file pointer to the beginning of relocation entries for the section. 210 // This is set to zero for executable images or if there are no relocations. 211 PointerToRelocations uint32 `json:"pointer_to_relocations"` 212 213 // The file pointer to the beginning of line-number entries for the section. 214 // This is set to zero if there are no COFF line numbers. This value should 215 // be zero for an image because COFF debugging information is deprecated. 216 PointerToLineNumbers uint32 `json:"pointer_to_line_numbers"` 217 218 // The number of relocation entries for the section. 219 // This is set to zero for executable images. 220 NumberOfRelocations uint16 `json:"number_of_relocations"` 221 222 // The number of line-number entries for the section. This value should be 223 // zero for an image because COFF debugging information is deprecated. 224 NumberOfLineNumbers uint16 `json:"number_of_line_numbers"` 225 226 // The flags that describe the characteristics of the section. 227 Characteristics uint32 `json:"characteristics"` 228 } 229 230 // Section represents a PE section header, plus additional data like entropy. 231 type Section struct { 232 Header ImageSectionHeader `json:"header"` 233 // Entropy represents the section entropy. This field is not always populated 234 // depending on weather entropy calculation is enabled. The reason behind 235 // using a float64 pointer instead of a float64 is to distinguish between 236 // the case when the section entropy is equal to zero and the case when the 237 // entropy is equal to nil - meaning that it was never calculated. 238 Entropy *float64 `json:"entropy,omitempty"` 239 } 240 241 // ParseSectionHeader parses the PE section headers. Each row of the section 242 // table is, in effect, a section header. It must immediately follow the PE 243 // header. 244 func (pe *File) ParseSectionHeader() (err error) { 245 246 // Get the first section offset. 247 optionalHeaderOffset := pe.DOSHeader.AddressOfNewEXEHeader + 4 + 248 uint32(binary.Size(pe.NtHeader.FileHeader)) 249 offset := optionalHeaderOffset + 250 uint32(pe.NtHeader.FileHeader.SizeOfOptionalHeader) 251 252 // Track invalid/suspicious values while parsing sections. 253 maxErr := 3 254 255 secHeader := ImageSectionHeader{} 256 numberOfSections := pe.NtHeader.FileHeader.NumberOfSections 257 secHeaderSize := uint32(binary.Size(secHeader)) 258 259 // The section header indexing in the table is one-based, with the order of 260 // the sections defined by the linker. The sections follow one another 261 // contiguously in the order defined by the section header table, with 262 // starting RVAs aligned by the value of the SectionAlignment field of the 263 // PE header. 264 for i := uint16(0); i < numberOfSections; i++ { 265 err := pe.structUnpack(&secHeader, offset, secHeaderSize) 266 if err != nil { 267 return err 268 } 269 270 if secEnd := int64(secHeader.PointerToRawData) + int64(secHeader.SizeOfRawData); secEnd > pe.OverlayOffset { 271 pe.OverlayOffset = secEnd 272 } 273 274 countErr := 0 275 sec := Section{Header: secHeader} 276 secName := sec.String() 277 278 if (ImageSectionHeader{}) == secHeader { 279 pe.Anomalies = append(pe.Anomalies, "Section `"+secName+"` Contents are null-bytes") 280 countErr++ 281 } 282 283 if secHeader.SizeOfRawData+secHeader.PointerToRawData > pe.size { 284 pe.Anomalies = append(pe.Anomalies, "Section `"+secName+ 285 "` SizeOfRawData is larger than file") 286 countErr++ 287 } 288 289 if pe.adjustFileAlignment(secHeader.PointerToRawData) > pe.size { 290 pe.Anomalies = append(pe.Anomalies, "Section `"+secName+ 291 "` PointerToRawData points beyond the end of the file") 292 countErr++ 293 } 294 295 if secHeader.VirtualSize > 0x10000000 { 296 pe.Anomalies = append(pe.Anomalies, "Section `"+secName+ 297 "` VirtualSize is extremely large > 256MiB") 298 countErr++ 299 } 300 301 if pe.adjustSectionAlignment(secHeader.VirtualAddress) > 0x10000000 { 302 pe.Anomalies = append(pe.Anomalies, "Section `"+secName+ 303 "` VirtualAddress is beyond 0x10000000") 304 countErr++ 305 } 306 307 var fileAlignment uint32 308 switch pe.Is64 { 309 case true: 310 fileAlignment = pe.NtHeader.OptionalHeader.(ImageOptionalHeader64).FileAlignment 311 case false: 312 fileAlignment = pe.NtHeader.OptionalHeader.(ImageOptionalHeader32).FileAlignment 313 } 314 if fileAlignment != 0 && secHeader.PointerToRawData%fileAlignment != 0 { 315 pe.Anomalies = append(pe.Anomalies, "Section `"+secName+ 316 "` PointerToRawData is not multiple of FileAlignment") 317 countErr++ 318 } 319 320 if countErr >= maxErr { 321 break 322 } 323 324 // Append to the list of sections. 325 if pe.opts.SectionEntropy { 326 entropy := sec.CalculateEntropy(pe) 327 sec.Entropy = &entropy 328 } 329 pe.Sections = append(pe.Sections, sec) 330 331 offset += secHeaderSize 332 } 333 334 // Sort the sections by their VirtualAddress. This will allow to check 335 // for potentially overlapping sections in badly constructed PEs. 336 sort.Sort(byVirtualAddress(pe.Sections)) 337 338 if pe.NtHeader.FileHeader.NumberOfSections > 0 && len(pe.Sections) > 0 { 339 offset += secHeaderSize * uint32(pe.NtHeader.FileHeader.NumberOfSections) 340 } 341 342 // There could be a problem if there are no raw data sections 343 // greater than 0. Example: fc91013eb72529da005110a3403541b6 344 // Should this throw an exception in the minimum header offset 345 // can't be found? 346 var rawDataPointers []uint32 347 for _, sec := range pe.Sections { 348 if sec.Header.PointerToRawData > 0 { 349 rawDataPointers = append( 350 rawDataPointers, pe.adjustFileAlignment( 351 sec.Header.PointerToRawData)) 352 } 353 } 354 355 var lowestSectionOffset uint32 356 if len(rawDataPointers) > 0 { 357 lowestSectionOffset = Min(rawDataPointers) 358 } else { 359 lowestSectionOffset = 0 360 } 361 362 if lowestSectionOffset == 0 || lowestSectionOffset < offset { 363 if offset <= pe.size { 364 pe.Header = pe.data[:offset] 365 } 366 } else { 367 if lowestSectionOffset <= pe.size { 368 pe.Header = pe.data[:lowestSectionOffset] 369 } 370 } 371 372 pe.HasSections = true 373 return nil 374 } 375 376 // String stringifies the section name. 377 func (section *Section) String() string { 378 return strings.Replace(string(section.Header.Name[:]), "\x00", "", -1) 379 } 380 381 // NextHeaderAddr returns the VirtualAddress of the next section. 382 func (section *Section) NextHeaderAddr(pe *File) uint32 { 383 for i, currentSection := range pe.Sections { 384 if i == len(pe.Sections)-1 { 385 return 0 386 } 387 388 if section.Header == currentSection.Header { 389 return pe.Sections[i+1].Header.VirtualAddress 390 } 391 } 392 393 return 0 394 } 395 396 // Contains checks whether the section contains a given RVA. 397 func (section *Section) Contains(rva uint32, pe *File) bool { 398 399 // Check if the SizeOfRawData is realistic. If it's bigger than the size of 400 // the whole PE file minus the start address of the section it could be 401 // either truncated or the SizeOfRawData contains a misleading value. 402 // In either of those cases we take the VirtualSize. 403 404 var size uint32 405 adjustedPointer := pe.adjustFileAlignment(section.Header.PointerToRawData) 406 if uint32(len(pe.data))-adjustedPointer < section.Header.SizeOfRawData { 407 size = section.Header.VirtualSize 408 } else { 409 size = Max(section.Header.SizeOfRawData, section.Header.VirtualSize) 410 } 411 vaAdj := pe.adjustSectionAlignment(section.Header.VirtualAddress) 412 413 // Check whether there's any section after the current one that starts before 414 // the calculated end for the current one. If so, cut the current section's 415 // size to fit in the range up to where the next section starts. 416 if section.NextHeaderAddr(pe) != 0 && 417 section.NextHeaderAddr(pe) > section.Header.VirtualAddress && 418 vaAdj+size > section.NextHeaderAddr(pe) { 419 size = section.NextHeaderAddr(pe) - vaAdj 420 } 421 422 return vaAdj <= rva && rva < vaAdj+size 423 } 424 425 // Data returns a data chunk from a section. 426 func (section *Section) Data(start, length uint32, pe *File) []byte { 427 428 pointerToRawDataAdj := pe.adjustFileAlignment( 429 section.Header.PointerToRawData) 430 virtualAddressAdj := pe.adjustSectionAlignment( 431 section.Header.VirtualAddress) 432 433 var offset uint32 434 if start == 0 { 435 offset = pointerToRawDataAdj 436 } else { 437 offset = (start - virtualAddressAdj) + pointerToRawDataAdj 438 } 439 440 if offset > pe.size { 441 return nil 442 } 443 444 var end uint32 445 if length != 0 { 446 end = offset + length 447 } else { 448 end = offset + section.Header.SizeOfRawData 449 } 450 451 // PointerToRawData is not adjusted here as we might want to read any possible 452 // extra bytes that might get cut off by aligning the start (and hence cutting 453 // something off the end) 454 if end > section.Header.PointerToRawData+section.Header.SizeOfRawData && 455 section.Header.PointerToRawData+section.Header.SizeOfRawData > offset { 456 end = section.Header.PointerToRawData + section.Header.SizeOfRawData 457 } 458 459 if end > pe.size { 460 end = pe.size 461 } 462 463 return pe.data[offset:end] 464 } 465 466 // CalculateEntropy calculates section entropy. 467 func (section *Section) CalculateEntropy(pe *File) float64 { 468 sectionData := section.Data(0, 0, pe) 469 if sectionData == nil { 470 return 0.0 471 } 472 473 sectionSize := float64(len(sectionData)) 474 if sectionSize == 0.0 { 475 return 0.0 476 } 477 478 var frequencies [256]uint64 479 for _, v := range sectionData { 480 frequencies[v]++ 481 } 482 483 var entropy float64 484 for _, p := range frequencies { 485 if p > 0 { 486 freq := float64(p) / sectionSize 487 entropy += freq * math.Log2(freq) 488 } 489 } 490 491 return -entropy 492 } 493 494 // byVirtualAddress sorts all sections by Virtual Address. 495 type byVirtualAddress []Section 496 497 func (s byVirtualAddress) Len() int { return len(s) } 498 func (s byVirtualAddress) Swap(i, j int) { s[i], s[j] = s[j], s[i] } 499 func (s byVirtualAddress) Less(i, j int) bool { 500 return s[i].Header.VirtualAddress < s[j].Header.VirtualAddress 501 } 502 503 // byPointerToRawData sorts all sections by PointerToRawData. 504 type byPointerToRawData []Section 505 506 func (s byPointerToRawData) Len() int { return len(s) } 507 func (s byPointerToRawData) Swap(i, j int) { s[i], s[j] = s[j], s[i] } 508 func (s byPointerToRawData) Less(i, j int) bool { 509 return s[i].Header.PointerToRawData < s[j].Header.PointerToRawData 510 } 511 512 // PrettySectionFlags returns the string representations of the `Flags` field 513 // of section header. 514 func (section *Section) PrettySectionFlags() []string { 515 var values []string 516 517 sectionFlags := map[uint32]string{ 518 //ImageSectionReserved1: "Reserved1", 519 ImageSectionReserved2: "Reserved2", 520 ImageSectionReserved3: "Reserved3", 521 ImageSectionReserved4: "Reserved4", 522 ImageSectionTypeNoPad: "No Padd", 523 ImageSectionReserved5: "Reserved5", 524 ImageSectionCntCode: "Contains Code", 525 ImageSectionCntInitializedData: "Initialized Data", 526 ImageSectionCntUninitializedData: "Uninitialized Data", 527 ImageSectionLnkOther: "Lnk Other", 528 ImageSectionLnkInfo: "Lnk Info", 529 ImageSectionReserved6: "Reserved6", 530 ImageSectionLnkRemove: "LnkRemove", 531 ImageSectionLnkCOMDAT: "LnkCOMDAT", 532 ImageSectionGpRel: "GpReferenced", 533 ImageSectionMemPurgeable: "Purgeable", 534 ImageSectionMemLocked: "Locked", 535 ImageSectionMemPreload: "Preload", 536 ImageSectionAlign1Bytes: "Align1Bytes", 537 ImageSectionAlign2Bytes: "Align2Bytes", 538 ImageSectionAlign4Bytes: "Align4Bytes", 539 ImageSectionAlign8Bytes: "Align8Bytes", 540 ImageSectionAlign16Bytes: "Align16Bytes", 541 ImageSectionAlign32Bytes: "Align32Bytes", 542 ImageSectionAlign64Bytes: "Align64Bytes", 543 ImageSectionAlign128Bytes: "Align128Bytes", 544 ImageSectionAlign256Bytes: "Align256Bytes", 545 ImageSectionAlign512Bytes: "Align512Bytes", 546 ImageSectionAlign1024Bytes: "Align1024Bytes", 547 ImageSectionAlign2048Bytes: "Align2048Bytes", 548 ImageSectionAlign4096Bytes: "Align4096Bytes", 549 ImageSectionAlign8192Bytes: "Align8192Bytes", 550 ImageSectionLnkMRelocOvfl: "ExtendedReloc", 551 ImageSectionMemDiscardable: "Discardable", 552 ImageSectionMemNotCached: "NotCached", 553 ImageSectionMemNotPaged: "NotPaged", 554 ImageSectionMemShared: "Shared", 555 ImageSectionMemExecute: "Executable", 556 ImageSectionMemRead: "Readable", 557 ImageSectionMemWrite: "Writable", 558 } 559 560 flags := section.Header.Characteristics 561 for k, v := range sectionFlags { 562 if (k & flags) == k { 563 values = append(values, v) 564 } 565 } 566 567 return values 568 }