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 }