0
# Memory Layout and Relocations
1
2
Functions for memory mapping PE files and handling base relocations for different load addresses. These capabilities enable proper loading and execution of PE files at arbitrary memory locations.
3
4
## Capabilities
5
6
### Memory Mapping
7
8
Create memory-mapped representation of the PE file as it would appear when loaded.
9
10
```python { .api }
11
def get_memory_mapped_image(self, max_virtual_address=268435456, ImageBase=None):
12
"""
13
Get memory layout of PE file as it would appear when loaded.
14
15
Args:
16
max_virtual_address (int): Maximum virtual address to map (default: 256MB)
17
ImageBase (int, optional): Base address to use. If None, uses PE's ImageBase.
18
19
Returns:
20
bytes: Memory-mapped image data
21
22
Note:
23
This creates a complete memory layout with sections mapped to their
24
virtual addresses, filling gaps with zeros as needed.
25
"""
26
```
27
28
### Base Relocations
29
30
Handle relocations needed when PE is loaded at different base address.
31
32
```python { .api }
33
def relocate_image(self, new_ImageBase):
34
"""
35
Apply base relocations for new ImageBase address.
36
37
Args:
38
new_ImageBase (int): New base address for the image
39
40
Note:
41
Modifies the PE file data to update all absolute addresses
42
based on the new base address. Requires relocation table.
43
"""
44
45
def has_relocs(self):
46
"""
47
Check if PE file has relocation table.
48
49
Returns:
50
bool: True if file has base relocations
51
"""
52
53
def has_dynamic_relocs(self):
54
"""
55
Check if PE file has dynamic relocations.
56
57
Returns:
58
bool: True if file has dynamic relocations
59
"""
60
```
61
62
### Overlay Operations
63
64
Handle data appended beyond the standard PE structure.
65
66
```python { .api }
67
def get_overlay(self):
68
"""
69
Get overlay data appended to PE file.
70
71
Returns:
72
bytes: Overlay data, or None if no overlay present
73
74
Note:
75
Overlay is data appended beyond the PE file structure,
76
often used by installers or self-extracting archives.
77
"""
78
79
def get_overlay_data_start_offset(self):
80
"""
81
Get file offset where overlay data begins.
82
83
Returns:
84
int: File offset of overlay start, or None if no overlay
85
"""
86
87
def trim(self):
88
"""
89
Remove overlay data from PE file.
90
91
Note:
92
Removes any data beyond the PE file structure,
93
effectively stripping overlay data.
94
"""
95
```
96
97
### Relocation Parsing
98
99
Parse and analyze relocation directory entries.
100
101
```python { .api }
102
def parse_relocations_directory(self, rva, size):
103
"""
104
Parse base relocations directory.
105
106
Args:
107
rva (int): RVA of relocations directory
108
size (int): Size of relocations directory
109
110
Populates:
111
self.DIRECTORY_ENTRY_BASERELOC: List of BaseRelocationData objects
112
"""
113
```
114
115
## Usage Examples
116
117
### Memory Image Generation
118
119
```python
120
import pefile
121
122
with pefile.PE('executable.exe') as pe:
123
# Get memory-mapped image at original base address
124
memory_image = pe.get_memory_mapped_image()
125
126
print(f"Memory image size: {len(memory_image)} bytes")
127
print(f"Original ImageBase: 0x{pe.OPTIONAL_HEADER.ImageBase:08x}")
128
129
# Save memory image
130
with open('memory_image.bin', 'wb') as f:
131
f.write(memory_image)
132
133
# Get memory image at different base address
134
new_base = 0x10000000
135
relocated_image = pe.get_memory_mapped_image(ImageBase=new_base)
136
137
with open('relocated_image.bin', 'wb') as f:
138
f.write(relocated_image)
139
```
140
141
### Relocation Analysis
142
143
```python
144
import pefile
145
146
with pefile.PE('executable.exe') as pe:
147
# Check if file has relocations
148
if pe.has_relocs():
149
print("File has base relocations")
150
151
if hasattr(pe, 'DIRECTORY_ENTRY_BASERELOC'):
152
total_relocs = 0
153
154
print("\nRelocation Blocks:")
155
print("-" * 40)
156
157
for reloc_block in pe.DIRECTORY_ENTRY_BASERELOC:
158
base_rva = reloc_block.struct.VirtualAddress
159
block_size = reloc_block.struct.SizeOfBlock
160
num_entries = len(reloc_block.entries)
161
162
print(f"Base RVA: 0x{base_rva:08x}")
163
print(f"Block Size: {block_size}")
164
print(f"Entries: {num_entries}")
165
166
# Show first few relocations in block
167
for i, entry in enumerate(reloc_block.entries[:5]):
168
if entry.type != 0: # Skip padding entries
169
rva = base_rva + entry.rva
170
print(f" 0x{rva:08x}: Type {entry.type}")
171
172
if len(reloc_block.entries) > 5:
173
print(f" ... and {len(reloc_block.entries) - 5} more")
174
175
total_relocs += num_entries
176
print()
177
178
print(f"Total relocations: {total_relocs}")
179
else:
180
print("File has no base relocations")
181
182
# Check for dynamic relocations
183
if pe.has_dynamic_relocs():
184
print("File has dynamic relocations")
185
```
186
187
### Address Space Layout
188
189
```python
190
import pefile
191
192
def analyze_address_space(pe):
193
"""Analyze PE address space layout."""
194
print("Address Space Layout:")
195
print("-" * 50)
196
197
image_base = pe.OPTIONAL_HEADER.ImageBase
198
size_of_image = pe.OPTIONAL_HEADER.SizeOfImage
199
200
print(f"ImageBase: 0x{image_base:08x}")
201
print(f"Size of Image: 0x{size_of_image:08x}")
202
print(f"Address Range: 0x{image_base:08x} - 0x{image_base + size_of_image:08x}")
203
print()
204
205
print("Section Virtual Layout:")
206
print(f"{'Section':<10} {'VirtAddr':<12} {'VirtSize':<12} {'End Addr':<12}")
207
print("-" * 50)
208
209
for section in pe.sections:
210
name = section.Name.decode('utf-8').strip('\x00')
211
virt_addr = image_base + section.VirtualAddress
212
virt_size = section.VirtualSize
213
end_addr = virt_addr + virt_size
214
215
print(f"{name:<10} 0x{virt_addr:08x} 0x{virt_size:08x} 0x{end_addr:08x}")
216
217
# Usage
218
with pefile.PE('executable.exe') as pe:
219
analyze_address_space(pe)
220
```
221
222
### Image Relocation
223
224
```python
225
import pefile
226
227
# Load PE file
228
pe = pefile.PE('executable.exe')
229
230
# Check current base address
231
original_base = pe.OPTIONAL_HEADER.ImageBase
232
print(f"Original ImageBase: 0x{original_base:08x}")
233
234
# Check if relocation is possible
235
if pe.has_relocs():
236
print("File supports relocation")
237
238
# Relocate to new base address
239
new_base = 0x20000000
240
print(f"Relocating to: 0x{new_base:08x}")
241
242
pe.relocate_image(new_base)
243
244
# Verify the change
245
updated_base = pe.OPTIONAL_HEADER.ImageBase
246
print(f"Updated ImageBase: 0x{updated_base:08x}")
247
248
# Save relocated PE
249
pe.write('relocated_executable.exe')
250
print("Relocated executable saved")
251
252
else:
253
print("File does not support relocation (no relocation table)")
254
255
pe.close()
256
```
257
258
### Virtual to Physical Address Translation
259
260
```python
261
import pefile
262
263
with pefile.PE('executable.exe') as pe:
264
def virtual_to_physical(virtual_addr):
265
"""Convert virtual address to file offset."""
266
# Convert VA to RVA
267
image_base = pe.OPTIONAL_HEADER.ImageBase
268
rva = virtual_addr - image_base
269
270
# Convert RVA to file offset
271
file_offset = pe.get_offset_from_rva(rva)
272
return file_offset
273
274
def physical_to_virtual(file_offset):
275
"""Convert file offset to virtual address."""
276
# Convert file offset to RVA
277
rva = pe.get_rva_from_offset(file_offset)
278
if rva is not None:
279
# Convert RVA to VA
280
image_base = pe.OPTIONAL_HEADER.ImageBase
281
virtual_addr = image_base + rva
282
return virtual_addr
283
return None
284
285
# Example translations
286
entry_point_va = pe.OPTIONAL_HEADER.ImageBase + pe.OPTIONAL_HEADER.AddressOfEntryPoint
287
entry_point_offset = virtual_to_physical(entry_point_va)
288
289
print(f"Entry Point VA: 0x{entry_point_va:08x}")
290
print(f"Entry Point File Offset: 0x{entry_point_offset:08x}")
291
292
# Verify round-trip conversion
293
back_to_va = physical_to_virtual(entry_point_offset)
294
print(f"Round-trip VA: 0x{back_to_va:08x}")
295
```
296
297
### Memory Protection Analysis
298
299
```python
300
import pefile
301
302
def analyze_memory_protection(pe):
303
"""Analyze section memory protection characteristics."""
304
print("Memory Protection Analysis:")
305
print("-" * 60)
306
print(f"{'Section':<10} {'Protection':<15} {'Characteristics':<30}")
307
print("-" * 60)
308
309
for section in pe.sections:
310
name = section.Name.decode('utf-8').strip('\x00')
311
chars = section.Characteristics
312
313
# Determine protection flags
314
protection = []
315
if chars & 0x20000000: # IMAGE_SCN_MEM_EXECUTE
316
protection.append('X')
317
if chars & 0x40000000: # IMAGE_SCN_MEM_READ
318
protection.append('R')
319
if chars & 0x80000000: # IMAGE_SCN_MEM_WRITE
320
protection.append('W')
321
322
prot_str = ''.join(protection) if protection else 'None'
323
324
# Decode other characteristics
325
char_flags = []
326
if chars & 0x00000020: # IMAGE_SCN_CNT_CODE
327
char_flags.append('CODE')
328
if chars & 0x00000040: # IMAGE_SCN_CNT_INITIALIZED_DATA
329
char_flags.append('INIT_DATA')
330
if chars & 0x00000080: # IMAGE_SCN_CNT_UNINITIALIZED_DATA
331
char_flags.append('UNINIT_DATA')
332
if chars & 0x02000000: # IMAGE_SCN_MEM_DISCARDABLE
333
char_flags.append('DISCARDABLE')
334
if chars & 0x10000000: # IMAGE_SCN_MEM_SHARED
335
char_flags.append('SHARED')
336
337
char_str = ', '.join(char_flags[:3]) # Limit for display
338
339
print(f"{name:<10} {prot_str:<15} {char_str:<30}")
340
341
# Usage
342
with pefile.PE('executable.exe') as pe:
343
analyze_memory_protection(pe)
344
```