-
Notifications
You must be signed in to change notification settings - Fork 4
/
opt.go
337 lines (312 loc) · 10.4 KB
/
opt.go
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
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
package pe
import (
"encoding/binary"
"fmt"
"io"
"log"
"strings"
)
// Maximum optional header size, which includes 16 data directories.
const maxOptHdrSize = 224
// OptHeader represents an optional header.
type OptHeader struct {
OptHeader32
// Data directories contains the location and size of various data
// structures. The following is a list of data directories as specified by
// index.
//
// 0: Export table.
// 1: Import table.
// 2: Resource table.
// 3: Exception table.
// 4: Certificate table.
// 5: Base relocation table.
// 6: Debugging information.
// 7: Architecture-specific data.
// 8: Global pointer register.
// 9: Thread local storage (TLS) table.
// 10: Load configuration table.
// 11: Bound import table.
// 12: Import address table.
// 13: Delay import descriptor.
// 14: CLR header.
// 15: Reserved.
DataDirs []DataDirectory
}
// Data directory indices.
const (
DataDirExportTable = 0 // Export table.
DataDirImportTable = 1 // Import table.
DataDirResourceTable = 2 // Resource table.
DataDirExceptionTable = 3 // Exception table.
DataDirCertificateTable = 4 // Certificate table.
DataDirBaseRelocationTable = 5 // Base relocation table.
DataDirDebug = 6 // Debugging information.
DataDirArchitecture = 7 // Architecture-specific data.
DataDirGlobalPtr = 8 // Global pointer register.
DataDirTLSTable = 9 // Thread local storage (TLS) table.
DataDirLoadConfigTable = 10 // Load configuration table.
DataDirBoundImport = 11 // Bound import table.
DataDirIAT = 12 // Import address table.
DataDirDelayImportDescriptor = 13 // Delay import descriptor.
DataDirCLRHeader = 14 // CLR header.
DataDirReserved = 15 // Reserved.
)
// OptHeader32 represents a 32-bit optional header.
type OptHeader32 struct {
// The state of the image file.
State OptState
// Major linker version.
MajorLinkVer uint8
// Minor linker version.
MinorLinkVer uint8
// Size of the code section in bytes, or the sum of all such sections if
// there are multiple code sections.
CodeSize uint32
// Size of the data section in bytes, or the sum of all such sections if
// there are multiple data sections.
DataSize uint32
// Size of the uninitialized data section in bytes, or the sum of all such
// sections if there are multiple uninitialized data sections.
BSSSize uint32
// Pointer to the entry point function, relative to the image base.
EntryRelAddr uint32
// Pointer to the beginning of the code section, relative to the image base.
CodeBase uint32
// Pointer to the beginning of the data section, relative to the image base.
DataBase uint32
// The base address is the starting-address of a memory-mapped EXE or DLL.
// The default value for DLLs is 0x10000000 and the default value for
// applications is 0x00400000.
ImageBase uint32
// The virtual address of each section is aligned to a multiple of this
// value. The default section alignment is the page size of the system.
SectAlign uint32
// The file offset of each section is aligned to a multiple of this value.
// The default file alignment is 512.
FileAlign uint32
// Major operating system version.
MajorOSVer uint16
// Minor operating system version.
MinorOSVer uint16
// Major image version.
MajorImageVer uint16
// Minor image version.
MinorImageVer uint16
// Major subsystem version.
MajorSubsystemVer uint16
// Minor subsystem version.
MinorSubsystemVer uint16
// Reserved.
Res uint32
// Size of the image, in bytes, including all headers. Must be a multiple of
// SectAlign.
ImageSize uint32
// The combined size of the following items, rounded to a multiple of
// FileAlign.
// * The PEHdrOffset member of the DOSHeader.
// * The 4 byte PE-signature.
// * The FileHeader.
// * The OptHeader.
// * All section headers.
HdrSize uint32
// The checksum is an additive checksum of the file.
Checksum uint32
// The subsystem required to run an image.
Subsystem Subsystem
// A bitfield which specifies the DLL characteristics of the image.
Flags DLLFlag
// The number of bytes to reserve for the stack.
ReserveStackSize uint32
// The size of the stack at load time.
InitStackSize uint32
// The number of bytes to reserve for the heap.
ReserveHeapSize uint32
// The size of the heap at load time.
InitHeapSize uint32
// Obsolete.
LoaderFlags uint32
// Number of data directories.
NDataDir uint32
}
// OptState specifies the state of the image file.
type OptState uint16
const (
// OptState32 represents a 32-bit executable image.
OptState32 OptState = 0x010B
// OptState64 represents a 64-bit executable image.
OptState64 OptState = 0x020B
// OptStateROM represents a ROM image.
OptStateROM OptState = 0x0107
)
// optStateName is a map from OptState to string description.
var optStateName = map[OptState]string{
OptState32: "32-bit",
OptState64: "64-bit",
OptStateROM: "ROM",
}
func (state OptState) String() string {
if s, ok := optStateName[state]; ok {
return s
}
return fmt.Sprintf("unknown state: 0x%04X", uint16(state))
}
// Subsystem specifies the subsystem required to run an image.
type Subsystem uint16
// Subsystems.
const (
// SubsystemUnknown represents an unknown subsystem.
SubsystemUnknown Subsystem = iota
// SubsystemNative represents a device driver or native system process; no
// subsystem required.
SubsystemNative
// SubsystemWinGUI represents a Windows graphical user interface (GUI)
// subsystem.
SubsystemWinGUI
// SubsystemWinCLI represents a Window command line interface (CLI)
// subsystem.
SubsystemWinCLI
_
// SubsystemOS2CLI represents a OS/2 CLI subsystem.
SubsystemOS2CLI
_
// SubsystemPOSIXCLI represents a POSIX CLI subsystem.
SubsystemPOSIXCLI
_
// SubsystemWinCEGUI represents a Windows CE GUI subsystem.
SubsystemWinCEGUI
// SubsystemEFIApp represents an Extensible Firmware Interface (EFI)
// application.
SubsystemEFIApp
// SubsystemEFIBootDriver represents an EFI driver with boot services.
SubsystemEFIBootDriver
// SubsystemEFIRuntimeDriver represents an EFI driver with run-time services.
SubsystemEFIRuntimeDriver
// SubsystemEFIROM represents an EFI ROM image.
SubsystemEFIROM
// SubsystemXbox represents an Xbox system.
SubsystemXbox
_
// SubsystemWinBootApp represents a boot application.
SubsystemWinBootApp
)
// subsystemName is a map from Subsystem to string description.
var subsystemName = map[Subsystem]string{
SubsystemUnknown: "unknown",
SubsystemNative: "native",
SubsystemWinGUI: "Windows GUI",
SubsystemWinCLI: "Windows CLI",
SubsystemOS2CLI: "OS/2 CLI",
SubsystemPOSIXCLI: "POSIX CLI",
SubsystemWinCEGUI: "Windows CE GUI",
SubsystemEFIApp: "EFI application",
SubsystemEFIBootDriver: "EFI boot driver",
SubsystemEFIRuntimeDriver: "EFI runtime driver",
SubsystemEFIROM: "EFI ROM",
SubsystemXbox: "Xbox",
SubsystemWinBootApp: "boot application",
}
func (subsystem Subsystem) String() string {
if s, ok := subsystemName[subsystem]; ok {
return s
}
return fmt.Sprintf("unknown subsystem: 0x%04X", uint16(subsystem))
}
// DLLFlag is a bitfield which specifies the DLL characteristics of an image.
type DLLFlag uint16
// DLL characteristics.
const (
// DLLFlagDynBase indicates that the DLL can be relocated at load time.
DLLFlagDynBase DLLFlag = 0x0040
// DLLFlagForceIntegrity forces code integrity checks.
DLLFlagForceIntegrity DLLFlag = 0x0080
// DLLFlagCanDEP indicates that the image is compatible with data execution
// prevention (DEP).
DLLFlagCanDEP DLLFlag = 0x0100
// DLLFlagNoIsolation indicates that the image shouldn't be isolated.
DLLFlagNoIsolation DLLFlag = 0x0200
// DLLFlagNoSEH indicates that the image doesn't use structured exception
// handling (SEH). No handlers can be called in this image.
DLLFlagNoSEH DLLFlag = 0x0400
// DLLFlagNoBind specifies that the linker shouldn't bind the image.
DLLFlagNoBind DLLFlag = 0x0800
// DLLFlagWDMDriver represents a Windows Driver Model (WDM) driver.
DLLFlagWDMDriver DLLFlag = 0x2000
// DLLFlagCanRDS indicates that the image is remove desktop service (RDS)
// aware.
DLLFlagCanRDS DLLFlag = 0x8000
)
// dllFlagName is a map from DLLFlag to string description.
var dllFlagName = map[DLLFlag]string{
DLLFlagDynBase: "dynamic base",
DLLFlagForceIntegrity: "force integrity",
DLLFlagCanDEP: "can DEP",
DLLFlagNoIsolation: "no isolation",
DLLFlagNoSEH: "no SEH",
DLLFlagNoBind: "no bind",
DLLFlagWDMDriver: "WDM driver",
DLLFlagCanRDS: "can RDS",
}
func (flags DLLFlag) String() string {
var ss []string
for i := uint(0); i < 16; i++ {
mask := DLLFlag(1 << i)
if flags&mask != 0 {
flags &^= mask
s, ok := dllFlagName[mask]
if !ok {
s = fmt.Sprintf("unknown flag: 0x%04X", uint16(mask))
}
ss = append(ss, s)
}
}
if len(ss) == 0 {
return "none"
}
return strings.Join(ss, "|")
}
// A DataDirectory contains the location and size of various data structures.
type DataDirectory struct {
// Relative address of the table.
RelAddr uint32
// Size of the table in bytes.
Size uint32
}
// OptHeader returns the optional header of file.
func (file *File) OptHeader() (opthdr *OptHeader, err error) {
if file.opthdr == nil {
err = file.parseOptHeader()
if err != nil {
return nil, err
}
}
return file.opthdr, nil
}
// parseOptHeader parses the optional header of file.
func (file *File) parseOptHeader() error {
doshdr, err := file.DOSHeader()
if err != nil {
return err
}
optoff := int64(doshdr.PEHdrOffset) + fileHdrSize
sr := io.NewSectionReader(file.r, optoff, maxOptHdrSize)
// Parse optional header.
file.opthdr = new(OptHeader)
opthdr := file.opthdr
err = binary.Read(sr, binary.LittleEndian, &opthdr.OptHeader32)
if err != nil {
return fmt.Errorf("pe.File.parseOptHeader: unable to read optional header; %v", err)
}
// Verify that the reserved field is zero.
if opthdr.Res != 0 {
log.Printf("pe.File.parseOptHeader: invalid reserved field; expected 0, got %d.\n", opthdr.Res)
}
// Parse data directories.
// TODO(u): Ignore void/zero data directories (using a for loop).
opthdr.DataDirs = make([]DataDirectory, opthdr.NDataDir)
err = binary.Read(sr, binary.LittleEndian, &opthdr.DataDirs)
if err != nil {
return fmt.Errorf("pe.File.parseOptHeader: unable to read data directories; %v", err)
}
return nil
}