libPeConv
A library to load, manipulate, dump PE files.
Loading...
Searching...
No Matches
relocate.cpp
Go to the documentation of this file.
1#include "peconv/relocate.h"
2
4#include <stdio.h>
5#include "peconv/logger.h"
6
7using namespace peconv;
8
9#define RELOC_32BIT_FIELD 3
10#define RELOC_64BIT_FIELD 0xA
11
13{
14public:
15 ApplyRelocCallback(bool _is64bit, ULONGLONG _oldBase, ULONGLONG _newBase)
16 : RelocBlockCallback(_is64bit), oldBase(_oldBase), newBase(_newBase)
17 {
18 }
19
20 virtual bool processRelocField(ULONG_PTR relocField)
21 {
22 if (is64bit) {
23 ULONGLONG* relocateAddr = (ULONGLONG*)((ULONG_PTR)relocField);
24 ULONGLONG rva = (*relocateAddr) - oldBase;
25 (*relocateAddr) = rva + newBase;
26 }
27 else {
28 DWORD* relocateAddr = (DWORD*)((ULONG_PTR)relocField);
29 ULONGLONG rva = ULONGLONG(*relocateAddr) - oldBase;
30 (*relocateAddr) = static_cast<DWORD>(rva + newBase);
31 }
32 return true;
33 }
34
35protected:
36 ULONGLONG oldBase;
37 ULONGLONG newBase;
38};
39
40bool is_empty_reloc_block(BASE_RELOCATION_ENTRY *block, SIZE_T entriesNum, DWORD page, PVOID modulePtr, SIZE_T moduleSize)
41{
42 if (entriesNum == 0) {
43 return true; // nothing to process
44 }
45 BASE_RELOCATION_ENTRY* entry = block;
46 for (SIZE_T i = 0; i < entriesNum; i++) {
47 if (!validate_ptr(modulePtr, moduleSize, entry, sizeof(BASE_RELOCATION_ENTRY))) {
48 return false;
49 }
50 DWORD type = entry->Type;
51 if (type != 0) {
52 //non empty block found
53 return false;
54 }
55 entry = (BASE_RELOCATION_ENTRY*)((ULONG_PTR)entry + sizeof(WORD));
56 }
57 return true;
58}
59
60namespace {
61 bool validate_reloc_field(PVOID modulePtr, SIZE_T moduleSize, bool is64bit, const DWORD reloc_field)
62 {
63 const size_t field_width = is64bit ? sizeof(ULONGLONG) : sizeof(DWORD);
64 const ULONG_PTR reloc_ptr = (ULONG_PTR)modulePtr + reloc_field;
65 return peconv::validate_ptr(modulePtr, moduleSize, (LPVOID)reloc_ptr, field_width);
66 }
67};
68
69bool process_reloc_block(BASE_RELOCATION_ENTRY *block, SIZE_T entriesNum, DWORD page, PVOID modulePtr, SIZE_T moduleSize, bool is64bit, RelocBlockCallback *callback)
70{
71 if (entriesNum == 0) {
72 return true; // nothing to process
73 }
74 BASE_RELOCATION_ENTRY* entry = block;
75 SIZE_T i = 0;
76 for (i = 0; i < entriesNum; i++) {
77 if (!validate_ptr(modulePtr, moduleSize, entry, sizeof(BASE_RELOCATION_ENTRY))) {
78 break;
79 }
80 DWORD offset = entry->Offset;
81 DWORD type = entry->Type;
82 if (type == 0) {
83 entry = (BASE_RELOCATION_ENTRY*)((ULONG_PTR)entry + sizeof(WORD));
84 continue; // skip padding
85 }
86 if (type != RELOC_32BIT_FIELD && type != RELOC_64BIT_FIELD) {
87 if (callback) { //print debug messages only if the callback function was set
88 LOG_ERROR("Not supported relocation format at %d: %d.", (int)i, (int)type);
89 }
90 return false;
91 }
92 const DWORD reloc_field = page + offset;
93 if (!validate_reloc_field(modulePtr, moduleSize, is64bit, reloc_field)) {
94 if (callback) { //print debug messages only if the callback function was set
95 LOG_ERROR("Malformed reloc field: 0x%lx.", reloc_field);
96 }
97 return false;
98 }
99 if (callback) {
100 bool isOk = callback->processRelocField(((ULONG_PTR)modulePtr + reloc_field));
101 if (!isOk) {
102 LOG_ERROR("Failed processing reloc field at: 0x%lx.", reloc_field);
103 return false;
104 }
105 }
106 entry = (BASE_RELOCATION_ENTRY*)((ULONG_PTR)entry + sizeof(WORD));
107 }
108 return (i != 0);
109}
110
111bool peconv::process_relocation_table(IN PVOID modulePtr, IN SIZE_T moduleSize, IN RelocBlockCallback *callback)
112{
113 IMAGE_DATA_DIRECTORY* relocDir = peconv::get_directory_entry((const BYTE*)modulePtr, IMAGE_DIRECTORY_ENTRY_BASERELOC);
114 if (relocDir == NULL) {
115 LOG_DEBUG("No relocation table found.");
116 return false;
117 }
118 if (!validate_ptr(modulePtr, moduleSize, relocDir, sizeof(IMAGE_DATA_DIRECTORY))) {
119 LOG_ERROR("Invalid relocDir pointer.");
120 return false;
121 }
122 DWORD maxSize = relocDir->Size;
123 DWORD relocAddr = relocDir->VirtualAddress;
124 bool is64b = is64bit((BYTE*)modulePtr);
125
126 IMAGE_BASE_RELOCATION* reloc = NULL;
127
128 DWORD parsedSize = 0;
129 while (parsedSize < maxSize) {
130 reloc = (IMAGE_BASE_RELOCATION*)(relocAddr + parsedSize + (ULONG_PTR)modulePtr);
131 if (!validate_ptr(modulePtr, moduleSize, reloc, sizeof(IMAGE_BASE_RELOCATION))) {
132 LOG_ERROR("Invalid address of relocations.");
133 return false;
134 }
135 if (reloc->SizeOfBlock < (2 * sizeof(DWORD))) {
136 LOG_ERROR("Malformed relocation block: SizeOfBlock too small.");
137 return false;
138 }
139 const size_t entriesNum = (reloc->SizeOfBlock - 2 * sizeof(DWORD)) / sizeof(WORD);
140 const DWORD page = reloc->VirtualAddress;
141
142 BASE_RELOCATION_ENTRY* block = (BASE_RELOCATION_ENTRY*)((ULONG_PTR)reloc + sizeof(DWORD) + sizeof(DWORD));
143 if (!validate_ptr(modulePtr, moduleSize, block, sizeof(BASE_RELOCATION_ENTRY))) {
144 LOG_ERROR("Invalid address of relocations block.");
145 return false;
146 }
147 if (!is_empty_reloc_block(block, entriesNum, page, modulePtr, moduleSize)) {
148 if (!process_reloc_block(block, entriesNum, page, modulePtr, moduleSize, is64b, callback)) {
149 // the block was malformed
150 return false;
151 }
152 }
153 parsedSize += reloc->SizeOfBlock;
154 }
155 return true;
156}
157
158bool apply_relocations(PVOID modulePtr, SIZE_T moduleSize, ULONGLONG newBase, ULONGLONG oldBase)
159{
160 const bool is64b = is64bit((BYTE*)modulePtr);
161 ApplyRelocCallback callback(is64b, oldBase, newBase);
162 return process_relocation_table(modulePtr, moduleSize, &callback);
163}
164
165bool peconv::relocate_module(IN BYTE* modulePtr, IN SIZE_T moduleSize, IN ULONGLONG newBase, IN ULONGLONG oldBase)
166{
167 if (modulePtr == NULL) {
168 return false;
169 }
170 if (oldBase == 0) {
171 oldBase = get_image_base(modulePtr);
172 }
173 LOG_DEBUG("New Base: 0x%llx Old Base: 0x%llx.", (unsigned long long)newBase, (unsigned long long)oldBase);
174 if (newBase == oldBase) {
175 LOG_DEBUG("Nothing to relocate: oldBase equals newBase.");
176 return true; //nothing to relocate
177 }
178 if (apply_relocations(modulePtr, moduleSize, newBase, oldBase)) {
179 return true;
180 }
181 LOG_ERROR("Could not relocate the module.");
182 return false;
183}
184
185bool peconv::has_valid_relocation_table(IN const PBYTE modulePtr, IN const size_t moduleSize)
186{
187 return process_relocation_table(modulePtr, moduleSize, nullptr);
188}
189
virtual bool processRelocField(ULONG_PTR relocField)
Definition relocate.cpp:20
ApplyRelocCallback(bool _is64bit, ULONGLONG _oldBase, ULONGLONG _newBase)
Definition relocate.cpp:15
ULONGLONG oldBase
Definition relocate.cpp:36
ULONGLONG newBase
Definition relocate.cpp:37
RelocBlockCallback(bool _is64bit)
Definition relocate.h:20
virtual bool processRelocField(ULONG_PTR relocField)=0
#define LOG_DEBUG(fmt,...)
Definition logger.h:56
#define LOG_ERROR(fmt,...)
Definition logger.h:36
ULONGLONG get_image_base(IN const BYTE *pe_buffer)
bool validate_ptr(IN const void *buffer_bgn, IN size_t buffer_size, IN const void *field_bgn, IN size_t field_size)
bool process_relocation_table(IN PVOID modulePtr, IN SIZE_T moduleSize, IN RelocBlockCallback *callback)
Definition relocate.cpp:111
bool is64bit(IN const BYTE *pe_buffer)
bool relocate_module(IN BYTE *modulePtr, IN SIZE_T moduleSize, IN ULONGLONG newBase, IN ULONGLONG oldBase=0)
Definition relocate.cpp:165
struct peconv::_BASE_RELOCATION_ENTRY BASE_RELOCATION_ENTRY
IMAGE_DATA_DIRECTORY * get_directory_entry(IN const BYTE *pe_buffer, IN DWORD dir_id, IN bool allow_empty=false)
bool has_valid_relocation_table(IN const PBYTE modulePtr, IN const size_t moduleSize)
Definition relocate.cpp:185
Wrappers over various fields in the PE header. Read, write, parse PE headers.
bool process_reloc_block(BASE_RELOCATION_ENTRY *block, SIZE_T entriesNum, DWORD page, PVOID modulePtr, SIZE_T moduleSize, bool is64bit, RelocBlockCallback *callback)
Definition relocate.cpp:69
#define RELOC_64BIT_FIELD
Definition relocate.cpp:10
bool apply_relocations(PVOID modulePtr, SIZE_T moduleSize, ULONGLONG newBase, ULONGLONG oldBase)
Definition relocate.cpp:158
bool is_empty_reloc_block(BASE_RELOCATION_ENTRY *block, SIZE_T entriesNum, DWORD page, PVOID modulePtr, SIZE_T moduleSize)
Definition relocate.cpp:40
#define RELOC_32BIT_FIELD
Definition relocate.cpp:9
Operating on PE file's relocations table.