-
Notifications
You must be signed in to change notification settings - Fork 175
/
relocate.cpp
193 lines (177 loc) · 6.3 KB
/
relocate.cpp
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
#include "peconv/relocate.h"
#include "peconv/pe_hdrs_helper.h"
#include <stdio.h>
#include <iostream>
using namespace peconv;
#define RELOC_32BIT_FIELD 3
#define RELOC_64BIT_FIELD 0xA
class ApplyRelocCallback : public RelocBlockCallback
{
public:
ApplyRelocCallback(bool _is64bit, ULONGLONG _oldBase, ULONGLONG _newBase)
: RelocBlockCallback(_is64bit), oldBase(_oldBase), newBase(_newBase)
{
}
virtual bool processRelocField(ULONG_PTR relocField)
{
if (is64bit) {
ULONGLONG* relocateAddr = (ULONGLONG*)((ULONG_PTR)relocField);
ULONGLONG rva = (*relocateAddr) - oldBase;
(*relocateAddr) = rva + newBase;
}
else {
DWORD* relocateAddr = (DWORD*)((ULONG_PTR)relocField);
ULONGLONG rva = ULONGLONG(*relocateAddr) - oldBase;
(*relocateAddr) = static_cast<DWORD>(rva + newBase);
}
return true;
}
protected:
ULONGLONG oldBase;
ULONGLONG newBase;
};
bool is_empty_reloc_block(BASE_RELOCATION_ENTRY *block, SIZE_T entriesNum, DWORD page, PVOID modulePtr, SIZE_T moduleSize)
{
if (entriesNum == 0) {
return true; // nothing to process
}
BASE_RELOCATION_ENTRY* entry = block;
for (SIZE_T i = 0; i < entriesNum; i++) {
if (!validate_ptr(modulePtr, moduleSize, entry, sizeof(BASE_RELOCATION_ENTRY))) {
return false;
}
DWORD type = entry->Type;
if (type != 0) {
//non empty block found
return false;
}
entry = (BASE_RELOCATION_ENTRY*)((ULONG_PTR)entry + sizeof(WORD));
}
return true;
}
bool process_reloc_block(BASE_RELOCATION_ENTRY *block, SIZE_T entriesNum, DWORD page, PVOID modulePtr, SIZE_T moduleSize, bool is64bit, RelocBlockCallback *callback)
{
if (entriesNum == 0) {
return true; // nothing to process
}
BASE_RELOCATION_ENTRY* entry = block;
SIZE_T i = 0;
for (i = 0; i < entriesNum; i++) {
if (!validate_ptr(modulePtr, moduleSize, entry, sizeof(BASE_RELOCATION_ENTRY))) {
break;
}
DWORD offset = entry->Offset;
DWORD type = entry->Type;
if (type == 0) {
break;
}
if (type != RELOC_32BIT_FIELD && type != RELOC_64BIT_FIELD) {
if (callback) { //print debug messages only if the callback function was set
printf("[-] Not supported relocations format at %d: %d\n", (int)i, (int)type);
}
return false;
}
DWORD reloc_field = page + offset;
if (reloc_field >= moduleSize) {
if (callback) { //print debug messages only if the callback function was set
printf("[-] Malformed field: %lx\n", reloc_field);
}
return false;
}
if (callback) {
bool isOk = callback->processRelocField(((ULONG_PTR)modulePtr + reloc_field));
if (!isOk) {
std::cout << "[-] Failed processing reloc field at: " << std::hex << reloc_field << "\n";
return false;
}
}
entry = (BASE_RELOCATION_ENTRY*)((ULONG_PTR)entry + sizeof(WORD));
}
return (i != 0);
}
bool peconv::process_relocation_table(IN PVOID modulePtr, IN SIZE_T moduleSize, IN RelocBlockCallback *callback)
{
IMAGE_DATA_DIRECTORY* relocDir = peconv::get_directory_entry((const BYTE*)modulePtr, IMAGE_DIRECTORY_ENTRY_BASERELOC);
if (relocDir == NULL) {
#ifdef _DEBUG
std::cout << "[!] WARNING: no relocation table found!\n";
#endif
return false;
}
if (!validate_ptr(modulePtr, moduleSize, relocDir, sizeof(IMAGE_DATA_DIRECTORY))) {
std::cerr << "[!] Invalid relocDir pointer\n";
return false;
}
DWORD maxSize = relocDir->Size;
DWORD relocAddr = relocDir->VirtualAddress;
bool is64b = is64bit((BYTE*)modulePtr);
IMAGE_BASE_RELOCATION* reloc = NULL;
DWORD parsedSize = 0;
DWORD validBlocks = 0;
while (parsedSize < maxSize) {
reloc = (IMAGE_BASE_RELOCATION*)(relocAddr + parsedSize + (ULONG_PTR)modulePtr);
if (!validate_ptr(modulePtr, moduleSize, reloc, sizeof(IMAGE_BASE_RELOCATION))) {
#ifdef _DEBUG
std::cerr << "[-] Invalid address of relocations\n";
#endif
return false;
}
if (reloc->SizeOfBlock == 0) {
break;
}
size_t entriesNum = (reloc->SizeOfBlock - 2 * sizeof(DWORD)) / sizeof(WORD);
DWORD page = reloc->VirtualAddress;
BASE_RELOCATION_ENTRY* block = (BASE_RELOCATION_ENTRY*)((ULONG_PTR)reloc + sizeof(DWORD) + sizeof(DWORD));
if (!validate_ptr(modulePtr, moduleSize, block, sizeof(BASE_RELOCATION_ENTRY))) {
std::cerr << "[-] Invalid address of relocations block\n";
return false;
}
if (!is_empty_reloc_block(block, entriesNum, page, modulePtr, moduleSize)) {
if (process_reloc_block(block, entriesNum, page, modulePtr, moduleSize, is64b, callback)) {
validBlocks++;
}
else {
// the block was malformed
return false;
}
}
parsedSize += reloc->SizeOfBlock;
}
return (validBlocks != 0);
}
bool apply_relocations(PVOID modulePtr, SIZE_T moduleSize, ULONGLONG newBase, ULONGLONG oldBase)
{
const bool is64b = is64bit((BYTE*)modulePtr);
ApplyRelocCallback callback(is64b, oldBase, newBase);
return process_relocation_table(modulePtr, moduleSize, &callback);
}
bool peconv::relocate_module(IN BYTE* modulePtr, IN SIZE_T moduleSize, IN ULONGLONG newBase, IN ULONGLONG oldBase)
{
if (modulePtr == NULL) {
return false;
}
if (oldBase == 0) {
oldBase = get_image_base(modulePtr);
}
#ifdef _DEBUG
printf("New Base: %llx\n", newBase);
printf("Old Base: %llx\n", oldBase);
#endif
if (newBase == oldBase) {
#ifdef _DEBUG
printf("Nothing to relocate! oldBase is the same as the newBase!\n");
#endif
return true; //nothing to relocate
}
if (apply_relocations(modulePtr, moduleSize, newBase, oldBase)) {
return true;
}
#ifdef _DEBUG
printf("Could not relocate the module!\n");
#endif
return false;
}
bool peconv::has_valid_relocation_table(IN const PBYTE modulePtr, IN const size_t moduleSize)
{
return process_relocation_table(modulePtr, moduleSize, nullptr);
}