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  }