github.com/saferwall/pe@v1.5.2/reloc.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  	"errors"
    10  )
    11  
    12  var (
    13  	// ErrInvalidBaseRelocVA is reposed when base reloc lies outside of the image.
    14  	ErrInvalidBaseRelocVA = errors.New("invalid relocation information." +
    15  		" Base Relocation VirtualAddress is outside of PE Image")
    16  
    17  	// ErrInvalidBasicRelocSizeOfBloc is reposed when base reloc is too large.
    18  	ErrInvalidBasicRelocSizeOfBloc = errors.New("invalid relocation " +
    19  		"information. Base Relocation SizeOfBlock too large")
    20  )
    21  
    22  // ImageBaseRelocationEntryType represents the type of an in image base relocation entry.
    23  type ImageBaseRelocationEntryType uint8
    24  
    25  // The Type field of the relocation record indicates what kind of relocation
    26  // should be performed. Different relocation types are defined for each type
    27  // of machine.
    28  const (
    29  	// The base relocation is skipped. This type can be used to pad a block.
    30  	ImageRelBasedAbsolute = 0
    31  
    32  	// The base relocation adds the high 16 bits of the difference to the 16-bit
    33  	// field at offset. The 16-bit field represents the high value of a 32-bit word.
    34  	ImageRelBasedHigh = 1
    35  
    36  	// The base relocation adds the low 16 bits of the difference to the 16-bit
    37  	// field at offset. The 16-bit field represents the low half of a 32-bit word.
    38  	ImageRelBasedLow = 2
    39  
    40  	// The base relocation applies all 32 bits of the difference to the 32-bit
    41  	// field at offset.
    42  	ImageRelBasedHighLow = 3
    43  
    44  	// The base relocation adds the high 16 bits of the difference to the 16-bit
    45  	// field at offset. The 16-bit field represents the high value of a 32-bit
    46  	// word. The low 16 bits of the 32-bit value are stored in the 16-bit word
    47  	// that follows this base relocation. This means that this base relocation
    48  	// occupies two slots.
    49  	ImageRelBasedHighAdj = 4
    50  
    51  	// The relocation interpretation is dependent on the machine type.
    52  	// When the machine type is MIPS, the base relocation applies to a MIPS jump
    53  	// instruction.
    54  	ImageRelBasedMIPSJmpAddr = 5
    55  
    56  	// This relocation is meaningful only when the machine type is ARM or Thumb.
    57  	// The base relocation applies the 32-bit address of a symbol across a
    58  	// consecutive MOVW/MOVT instruction pair.
    59  	ImageRelBasedARMMov32 = 5
    60  
    61  	// This relocation is only meaningful when the machine type is RISC-V. The
    62  	// base relocation applies to the high 20 bits of a 32-bit absolute address.
    63  	ImageRelBasedRISCVHigh20 = 5
    64  
    65  	// Reserved, must be zero.
    66  	ImageRelReserved = 6
    67  
    68  	// This relocation is meaningful only when the machine type is Thumb.
    69  	// The base relocation applies the 32-bit address of a symbol to a
    70  	// consecutive MOVW/MOVT instruction pair.
    71  	ImageRelBasedThumbMov32 = 7
    72  
    73  	// This relocation is only meaningful when the machine type is RISC-V.
    74  	// The base relocation applies to the low 12 bits of a 32-bit absolute
    75  	// address formed in RISC-V I-type instruction format.
    76  	ImageRelBasedRISCVLow12i = 7
    77  
    78  	// This relocation is only meaningful when the machine type is RISC-V.
    79  	// The base relocation applies to the low 12 bits of a 32-bit absolute
    80  	// address formed in RISC-V S-type instruction format.
    81  	ImageRelBasedRISCVLow12s = 8
    82  
    83  	// The relocation is only meaningful when the machine type is MIPS.
    84  	// The base relocation applies to a MIPS16 jump instruction.
    85  	ImageRelBasedMIPSJmpAddr16 = 9
    86  
    87  	// The base relocation applies the difference to the 64-bit field at offset.
    88  	ImageRelBasedDir64 = 10
    89  )
    90  
    91  const (
    92  	// MaxDefaultRelocEntriesCount represents the default maximum number of
    93  	// relocations entries to parse. Some malware uses a fake huge reloc entries that
    94  	// can slow significantly the parser.
    95  	// Example:  01008963d32f5cc17b64c31446386ee5b36a7eab6761df87a2989ba9394d8f3d
    96  	MaxDefaultRelocEntriesCount = 0x1000
    97  )
    98  
    99  // ImageBaseRelocation represents the IMAGE_BASE_RELOCATION structure.
   100  // Each chunk of base relocation data begins with an IMAGE_BASE_RELOCATION structure.
   101  type ImageBaseRelocation struct {
   102  	// The image base plus the page RVA is added to each offset to create the
   103  	// VA where the base relocation must be applied.
   104  	VirtualAddress uint32 `json:"virtual_address"`
   105  
   106  	// The total number of bytes in the base relocation block, including the
   107  	// Page RVA and Block Size fields and the Type/Offset fields that follow.
   108  	SizeOfBlock uint32 `json:"size_of_block"`
   109  }
   110  
   111  // ImageBaseRelocationEntry represents an image base relocation entry.
   112  type ImageBaseRelocationEntry struct {
   113  	// Locate data that must be reallocated in buffer (data being an address
   114  	// we use pointer of pointer).
   115  	Data uint16 `json:"data"`
   116  
   117  	// The offset of the relocation. This value plus the VirtualAddress
   118  	// in IMAGE_BASE_RELOCATION is the complete RVA.
   119  	Offset uint16 `json:"offset"`
   120  
   121  	// A value that indicates the kind of relocation that should be performed.
   122  	// Valid relocation types depend on machine type.
   123  	Type ImageBaseRelocationEntryType `json:"type"`
   124  }
   125  
   126  // Relocation represents the relocation table which holds the data that needs to
   127  // be relocated.
   128  type Relocation struct {
   129  	// Points to the ImageBaseRelocation structure.
   130  	Data ImageBaseRelocation `json:"data"`
   131  
   132  	// holds the list of entries for each chunk.
   133  	Entries []ImageBaseRelocationEntry `json:"entries"`
   134  }
   135  
   136  func (pe *File) parseRelocations(dataRVA, rva, size uint32) ([]ImageBaseRelocationEntry, error) {
   137  	var relocEntries []ImageBaseRelocationEntry
   138  
   139  	relocEntriesCount := size / 2
   140  	if relocEntriesCount > pe.opts.MaxRelocEntriesCount {
   141  		pe.Anomalies = append(pe.Anomalies, AnoAddressOfDataBeyondLimits)
   142  	}
   143  
   144  	offset := pe.GetOffsetFromRva(dataRVA)
   145  	var err error
   146  	for i := uint32(0); i < relocEntriesCount; i++ {
   147  		entry := ImageBaseRelocationEntry{}
   148  		entry.Data, err = pe.ReadUint16(offset + (i * 2))
   149  		if err != nil {
   150  			break
   151  		}
   152  		entry.Type = ImageBaseRelocationEntryType(entry.Data >> 12)
   153  		entry.Offset = entry.Data & 0x0fff
   154  		relocEntries = append(relocEntries, entry)
   155  	}
   156  
   157  	return relocEntries, nil
   158  }
   159  
   160  func (pe *File) parseRelocDirectory(rva, size uint32) error {
   161  	var sizeOfImage uint32
   162  	switch pe.Is64 {
   163  	case true:
   164  		sizeOfImage = pe.NtHeader.OptionalHeader.(ImageOptionalHeader64).SizeOfImage
   165  	case false:
   166  		sizeOfImage = pe.NtHeader.OptionalHeader.(ImageOptionalHeader32).SizeOfImage
   167  	}
   168  
   169  	relocSize := uint32(binary.Size(ImageBaseRelocation{}))
   170  	end := rva + size
   171  	for rva < end {
   172  		baseReloc := ImageBaseRelocation{}
   173  		offset := pe.GetOffsetFromRva(rva)
   174  		err := pe.structUnpack(&baseReloc, offset, relocSize)
   175  		if err != nil {
   176  			return err
   177  		}
   178  
   179  		// VirtualAddress must lie within the Image.
   180  		if baseReloc.VirtualAddress > sizeOfImage {
   181  			return ErrInvalidBaseRelocVA
   182  		}
   183  
   184  		// SizeOfBlock must be less or equal than the size of the image.
   185  		// It's a rather loose sanity test.
   186  		if baseReloc.SizeOfBlock > sizeOfImage {
   187  			return ErrInvalidBasicRelocSizeOfBloc
   188  		}
   189  
   190  		relocEntries, err := pe.parseRelocations(rva+relocSize,
   191  			baseReloc.VirtualAddress, baseReloc.SizeOfBlock-relocSize)
   192  		if err != nil {
   193  			return err
   194  		}
   195  
   196  		pe.Relocations = append(pe.Relocations, Relocation{
   197  			Data:    baseReloc,
   198  			Entries: relocEntries,
   199  		})
   200  
   201  		if baseReloc.SizeOfBlock == 0 {
   202  			break
   203  		}
   204  		rva += baseReloc.SizeOfBlock
   205  	}
   206  
   207  	if len(pe.Relocations) > 0 {
   208  		pe.HasReloc = true
   209  	}
   210  
   211  	return nil
   212  }
   213  
   214  // String returns the string representation of the `Type` field of a base reloc entry.
   215  func (t ImageBaseRelocationEntryType) String(pe *File) string {
   216  	relocTypesMap := map[ImageBaseRelocationEntryType]string{
   217  		ImageRelBasedAbsolute:      "Absolute",
   218  		ImageRelBasedHigh:          "High",
   219  		ImageRelBasedLow:           "Low",
   220  		ImageRelBasedHighLow:       "HighLow",
   221  		ImageRelBasedHighAdj:       "HighAdj",
   222  		ImageRelReserved:           "Reserved",
   223  		ImageRelBasedRISCVLow12s:   "RISC-V Low12s",
   224  		ImageRelBasedMIPSJmpAddr16: "MIPS Jmp Addr16",
   225  		ImageRelBasedDir64:         "DIR64",
   226  	}
   227  
   228  	if value, ok := relocTypesMap[t]; ok {
   229  		return value
   230  	}
   231  
   232  	switch pe.NtHeader.FileHeader.Machine {
   233  	case ImageFileMachineMIPS16, ImageFileMachineMIPSFPU, ImageFileMachineMIPSFPU16, ImageFileMachineWCEMIPSv2:
   234  		if t == ImageRelBasedMIPSJmpAddr {
   235  			return "MIPS JMP Addr"
   236  		}
   237  
   238  	case ImageFileMachineARM, ImageFileMachineARM64, ImageFileMachineARMNT:
   239  		if t == ImageRelBasedARMMov32 {
   240  			return "ARM MOV 32"
   241  		}
   242  
   243  		if t == ImageRelBasedThumbMov32 {
   244  			return "Thumb MOV 32"
   245  		}
   246  	case ImageFileMachineRISCV32, ImageFileMachineRISCV64, ImageFileMachineRISCV128:
   247  		if t == ImageRelBasedRISCVHigh20 {
   248  			return "RISC-V High 20"
   249  		}
   250  
   251  		if t == ImageRelBasedRISCVLow12i {
   252  			return "RISC-V Low 12"
   253  		}
   254  	}
   255  
   256  	return "?"
   257  }