-
-
Notifications
You must be signed in to change notification settings - Fork 58
Expand file tree
/
Copy pathreloc.go
More file actions
290 lines (242 loc) · 10.3 KB
/
Copy pathreloc.go
File metadata and controls
290 lines (242 loc) · 10.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
// Copyright 2018 Saferwall. All rights reserved.
// Use of this source code is governed by Apache v2 license
// license that can be found in the LICENSE file.
package pe
import (
"encoding/binary"
"errors"
)
var (
// ErrInvalidBaseRelocVA is reposed when base reloc lies outside of the image.
ErrInvalidBaseRelocVA = errors.New("invalid relocation information." +
" Base Relocation VirtualAddress is outside of PE Image")
// ErrInvalidBasicRelocSizeOfBloc is reposed when base reloc is too large.
ErrInvalidBasicRelocSizeOfBloc = errors.New("invalid relocation " +
"information. Base Relocation SizeOfBlock too large")
)
// ImageBaseRelocationEntryType represents the type of an in image base relocation entry.
type ImageBaseRelocationEntryType uint8
// The Type field of the relocation record indicates what kind of relocation
// should be performed. Different relocation types are defined for each type
// of machine.
const (
// The base relocation is skipped. This type can be used to pad a block.
ImageRelBasedAbsolute = 0
// The base relocation adds the high 16 bits of the difference to the 16-bit
// field at offset. The 16-bit field represents the high value of a 32-bit word.
ImageRelBasedHigh = 1
// The base relocation adds the low 16 bits of the difference to the 16-bit
// field at offset. The 16-bit field represents the low half of a 32-bit word.
ImageRelBasedLow = 2
// The base relocation applies all 32 bits of the difference to the 32-bit
// field at offset.
ImageRelBasedHighLow = 3
// The base relocation adds the high 16 bits of the difference to the 16-bit
// field at offset. The 16-bit field represents the high value of a 32-bit
// word. The low 16 bits of the 32-bit value are stored in the 16-bit word
// that follows this base relocation. This means that this base relocation
// occupies two slots.
ImageRelBasedHighAdj = 4
// The relocation interpretation is dependent on the machine type.
// When the machine type is MIPS, the base relocation applies to a MIPS jump
// instruction.
ImageRelBasedMIPSJmpAddr = 5
// This relocation is meaningful only when the machine type is ARM or Thumb.
// The base relocation applies the 32-bit address of a symbol across a
// consecutive MOVW/MOVT instruction pair.
ImageRelBasedARMMov32 = 5
// This relocation is only meaningful when the machine type is RISC-V. The
// base relocation applies to the high 20 bits of a 32-bit absolute address.
ImageRelBasedRISCVHigh20 = 5
// Reserved, must be zero.
ImageRelReserved = 6
// This relocation is meaningful only when the machine type is Thumb.
// The base relocation applies the 32-bit address of a symbol to a
// consecutive MOVW/MOVT instruction pair.
ImageRelBasedThumbMov32 = 7
// This relocation is only meaningful when the machine type is RISC-V.
// The base relocation applies to the low 12 bits of a 32-bit absolute
// address formed in RISC-V I-type instruction format.
ImageRelBasedRISCVLow12i = 7
// This relocation is only meaningful when the machine type is RISC-V.
// The base relocation applies to the low 12 bits of a 32-bit absolute
// address formed in RISC-V S-type instruction format.
ImageRelBasedRISCVLow12s = 8
// The relocation is only meaningful when the machine type is MIPS.
// The base relocation applies to a MIPS16 jump instruction.
ImageRelBasedMIPSJmpAddr16 = 9
// The base relocation applies the difference to the 64-bit field at offset.
ImageRelBasedDir64 = 10
)
const (
// MaxDefaultRelocEntriesCount represents the default maximum number of
// relocations entries to parse. Some malware uses a fake huge reloc entries that
// can slow significantly the parser.
// Example: 01008963d32f5cc17b64c31446386ee5b36a7eab6761df87a2989ba9394d8f3d
MaxDefaultRelocEntriesCount = 0x1000
)
// ImageBaseRelocation represents the IMAGE_BASE_RELOCATION structure.
// Each chunk of base relocation data begins with an IMAGE_BASE_RELOCATION structure.
type ImageBaseRelocation struct {
// The image base plus the page RVA is added to each offset to create the
// VA where the base relocation must be applied.
VirtualAddress uint32 `json:"virtual_address"`
// The total number of bytes in the base relocation block, including the
// Page RVA and Block Size fields and the Type/Offset fields that follow.
SizeOfBlock uint32 `json:"size_of_block"`
}
// ImageBaseRelocationEntry represents an image base relocation entry.
type ImageBaseRelocationEntry struct {
// Locate data that must be reallocated in buffer (data being an address
// we use pointer of pointer).
Data uint16 `json:"data"`
// The offset of the relocation. This value plus the VirtualAddress
// in IMAGE_BASE_RELOCATION is the complete RVA.
Offset uint16 `json:"offset"`
// A value that indicates the kind of relocation that should be performed.
// Valid relocation types depend on machine type.
Type ImageBaseRelocationEntryType `json:"type"`
}
// Relocation represents the relocation table which holds the data that needs to
// be relocated.
type Relocation struct {
// Points to the ImageBaseRelocation structure.
Data ImageBaseRelocation `json:"data"`
// holds the list of entries for each chunk.
Entries []ImageBaseRelocationEntry `json:"entries"`
}
func (pe *File) parseRelocations(dataRVA, rva, size uint32) ([]ImageBaseRelocationEntry, error) {
var relocEntries []ImageBaseRelocationEntry
relocEntriesCount := size / 2
if relocEntriesCount > pe.opts.MaxRelocEntriesCount {
pe.Anomalies = append(pe.Anomalies, AnoAddressOfDataBeyondLimits)
// Defense-in-depth: cap the iteration. A block with a genuinely huge
// (but still smaller than SizeOfImage) SizeOfBlock would otherwise
// have us decoding tens of thousands of meaningless WORDs as fake
// entries — see MaxDefaultRelocEntriesCount above for the reference
// malware sample that motivated this cap.
relocEntriesCount = pe.opts.MaxRelocEntriesCount
}
offset := pe.GetOffsetFromRva(dataRVA)
var err error
for i := uint32(0); i < relocEntriesCount; i++ {
entry := ImageBaseRelocationEntry{}
entry.Data, err = pe.ReadUint16(offset + (i * 2))
if err != nil {
break
}
entry.Type = ImageBaseRelocationEntryType(entry.Data >> 12)
entry.Offset = entry.Data & 0x0fff
relocEntries = append(relocEntries, entry)
}
return relocEntries, nil
}
func (pe *File) parseRelocDirectory(rva, size uint32) error {
var sizeOfImage uint32
switch pe.Is64 {
case true:
sizeOfImage = pe.NtHeader.OptionalHeader.(ImageOptionalHeader64).SizeOfImage
case false:
sizeOfImage = pe.NtHeader.OptionalHeader.(ImageOptionalHeader32).SizeOfImage
}
relocSize := uint32(binary.Size(ImageBaseRelocation{}))
end := rva + size
for rva < end {
baseReloc := ImageBaseRelocation{}
offset := pe.GetOffsetFromRva(rva)
err := pe.structUnpack(&baseReloc, offset, relocSize)
if err != nil {
return err
}
// Implicit end-of-table on a {VirtualAddress=0, SizeOfBlock=0} block.
//
// The PE/COFF spec does not define this sentinel — it states only that
// the data-directory Size field bounds the table (see "The .reloc
// Section (Image Only)" in the PE Format spec). In practice though,
// many real binaries declare BaseReloc.Size larger than the actual
// reloc data: e.g. when the .reloc section's VirtualSize exceeds its
// RawSize, or when the linker rounds the directory to a section/page
// boundary, the slack is zero-filled. Walking into that slack reads
// {0, 0} — and continuing would (a) loop forever (zero size advances
// rva by 0) and (b) make parseRelocations underflow on
// SizeOfBlock - relocSize and synthesise millions of phantom entries
// from whatever bytes happen to sit past the real reloc data.
//
// The Windows loader and every major PE parser (pefile, LIEF, ...)
// treat {0, 0} as table termination for the same reasons; we follow
// suit. The MaxRelocEntriesCount cap in parseRelocations remains as
// the spec-strict backstop for blocks with a non-zero but bogus size.
if baseReloc.SizeOfBlock == 0 {
break
}
// Per the spec, Block Size is "the total number of bytes in the base
// relocation block, including the Page RVA and Block Size fields"
// — so the minimum legitimate value is the 8-byte header alone (zero
// entries). Anything smaller is malformed and would underflow the
// SizeOfBlock - relocSize calculation passed to parseRelocations.
if baseReloc.SizeOfBlock < relocSize {
return ErrInvalidBasicRelocSizeOfBloc
}
// VirtualAddress must lie within the Image.
if baseReloc.VirtualAddress > sizeOfImage {
return ErrInvalidBaseRelocVA
}
// SizeOfBlock must be less or equal than the size of the image.
// It's a rather loose sanity test.
if baseReloc.SizeOfBlock > sizeOfImage {
return ErrInvalidBasicRelocSizeOfBloc
}
relocEntries, err := pe.parseRelocations(rva+relocSize,
baseReloc.VirtualAddress, baseReloc.SizeOfBlock-relocSize)
if err != nil {
return err
}
pe.Relocations = append(pe.Relocations, Relocation{
Data: baseReloc,
Entries: relocEntries,
})
rva += baseReloc.SizeOfBlock
}
if len(pe.Relocations) > 0 {
pe.HasReloc = true
}
return nil
}
// String returns the string representation of the `Type` field of a base reloc entry.
func (t ImageBaseRelocationEntryType) String(pe *File) string {
relocTypesMap := map[ImageBaseRelocationEntryType]string{
ImageRelBasedAbsolute: "Absolute",
ImageRelBasedHigh: "High",
ImageRelBasedLow: "Low",
ImageRelBasedHighLow: "HighLow",
ImageRelBasedHighAdj: "HighAdj",
ImageRelReserved: "Reserved",
ImageRelBasedRISCVLow12s: "RISC-V Low12s",
ImageRelBasedMIPSJmpAddr16: "MIPS Jmp Addr16",
ImageRelBasedDir64: "DIR64",
}
if value, ok := relocTypesMap[t]; ok {
return value
}
switch pe.NtHeader.FileHeader.Machine {
case ImageFileMachineMIPS16, ImageFileMachineMIPSFPU, ImageFileMachineMIPSFPU16, ImageFileMachineWCEMIPSv2:
if t == ImageRelBasedMIPSJmpAddr {
return "MIPS JMP Addr"
}
case ImageFileMachineARM, ImageFileMachineARM64, ImageFileMachineARMNT:
if t == ImageRelBasedARMMov32 {
return "ARM MOV 32"
}
if t == ImageRelBasedThumbMov32 {
return "Thumb MOV 32"
}
case ImageFileMachineRISCV32, ImageFileMachineRISCV64, ImageFileMachineRISCV128:
if t == ImageRelBasedRISCVHigh20 {
return "RISC-V High 20"
}
if t == ImageRelBasedRISCVLow12i {
return "RISC-V Low 12"
}
}
return "?"
}