-
Notifications
You must be signed in to change notification settings - Fork 19
/
loader.cpp
233 lines (209 loc) · 6.25 KB
/
loader.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
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
#include "common.h"
#include <algorithm>
#include <errno.h>
#include <memory>
#include <sys/mman.h>
#include <sys/syscall.h>
#include <unistd.h>
struct PEHeader {
uint8_t magic[4]; // "PE\0\0"
uint16_t machine;
uint16_t numberOfSections;
uint32_t timeDateStamp;
uint32_t pointerToSymbolTable;
uint32_t numberOfSymbols;
uint16_t sizeOfOptionalHeader;
uint16_t characteristics;
};
struct PEImageDataDirectory {
uint32_t virtualAddress;
uint32_t size;
};
struct PE32Header {
uint16_t magic; // 0x10B for PE32
uint8_t majorLinkerVersion;
uint8_t minorLinkerVersion;
uint32_t sizeOfCode;
uint32_t sizeOfInitializedData;
uint32_t sizeOfUninitializedData;
uint32_t addressOfEntryPoint;
uint32_t baseOfCode;
uint32_t baseOfData;
uint32_t imageBase;
uint32_t sectionAlignment;
uint32_t fileAlignment;
uint16_t majorOperatingSystemVersion;
uint16_t minorOperatingSystemVersion;
uint16_t majorImageVersion;
uint16_t minorImageVersion;
uint16_t majorSubsystemVersion;
uint16_t minorSubsystemVersion;
uint32_t win32VersionValue;
uint32_t sizeOfImage;
uint32_t sizeOfHeaders;
uint32_t checkSum;
uint16_t subsystem;
uint16_t dllCharacteristics;
uint32_t sizeOfStackReserve;
uint32_t sizeOfStackCommit;
uint32_t sizeOfHeapReserve;
uint32_t sizeOfHeapCommit;
uint32_t loaderFlags;
uint32_t numberOfRvaAndSizes;
PEImageDataDirectory exportTable;
PEImageDataDirectory importTable; // *
PEImageDataDirectory resourceTable; // *
PEImageDataDirectory exceptionTable;
PEImageDataDirectory certificateTable;
PEImageDataDirectory baseRelocationTable; // *
PEImageDataDirectory debug; // *
PEImageDataDirectory architecture;
PEImageDataDirectory globalPtr;
PEImageDataDirectory tlsTable;
PEImageDataDirectory loadConfigTable;
PEImageDataDirectory boundImport;
PEImageDataDirectory iat;
PEImageDataDirectory delayImportDescriptor;
PEImageDataDirectory clrRuntimeHeader;
PEImageDataDirectory reserved;
};
struct PESectionHeader {
char name[8];
uint32_t virtualSize;
uint32_t virtualAddress;
uint32_t sizeOfRawData;
uint32_t pointerToRawData;
uint32_t pointerToRelocations;
uint32_t pointerToLinenumbers;
uint16_t numberOfRelocations;
uint16_t numberOfLinenumbers;
uint32_t characteristics;
};
struct PEImportDirectoryEntry {
uint32_t *importLookupTable;
uint32_t timeDateStamp;
uint32_t forwarderChain;
char *name;
uint32_t *importAddressTable;
};
struct PEHintNameTableEntry {
uint16_t hint;
char name[1]; // variable length
};
uint16_t read16(FILE *file) {
uint16_t v = 0;
fread(&v, 2, 1, file);
return v;
}
uint32_t read32(FILE *file) {
uint32_t v = 0;
fread(&v, 4, 1, file);
return v;
}
wibo::Executable::Executable() {
imageBuffer = nullptr;
imageSize = 0;
entryPoint = nullptr;
rsrcBase = 0;
}
wibo::Executable::~Executable() {
if (imageBuffer) {
munmap(imageBuffer, imageSize);
}
}
/**
* Load a PE file into memory.
*
* @param file The file to load.
* @param exec Whether to make the loaded image executable.
*/
bool wibo::Executable::loadPE(FILE *file, bool exec) {
// Skip to PE header
fseek(file, 0x3C, SEEK_SET);
uint32_t offsetToPE = read32(file);
fseek(file, offsetToPE, SEEK_SET);
// Read headers
PEHeader header;
fread(&header, sizeof header, 1, file);
if (memcmp(header.magic, "PE\0\0", 4) != 0)
return false;
if (header.machine != 0x14C) // i386
return false;
DEBUG_LOG("Sections: %d / Size of optional header: %x\n", header.numberOfSections, header.sizeOfOptionalHeader);
PE32Header header32;
memset(&header32, 0, sizeof header32);
fread(&header32, std::min(sizeof(header32), (size_t) header.sizeOfOptionalHeader), 1, file);
if (header32.magic != 0x10B)
return false;
DEBUG_LOG("Image Base: %x / Size: %x\n", header32.imageBase, header32.sizeOfImage);
long pageSize = sysconf(_SC_PAGE_SIZE);
DEBUG_LOG("Page size: %x\n", (unsigned int)pageSize);
// Build buffer
imageSize = header32.sizeOfImage;
if (exec) {
imageBuffer = mmap((void *)header32.imageBase, header32.sizeOfImage, PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_ANONYMOUS | MAP_FIXED | MAP_PRIVATE, -1, 0);
} else {
imageBuffer = mmap(nullptr, header32.sizeOfImage, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
}
memset(imageBuffer, 0, header32.sizeOfImage);
if (imageBuffer == MAP_FAILED) {
perror("Image mapping failed!");
imageBuffer = 0;
return false;
}
// Read the sections
fseek(file, offsetToPE + sizeof header + header.sizeOfOptionalHeader, SEEK_SET);
for (int i = 0; i < header.numberOfSections; i++) {
PESectionHeader section;
fread(§ion, sizeof section, 1, file);
char name[9];
memcpy(name, section.name, 8);
name[8] = 0;
DEBUG_LOG("Section %d: name=%s addr=%x size=%x (raw=%x) ptr=%x\n", i, name, section.virtualAddress, section.virtualSize, section.sizeOfRawData, section.pointerToRawData);
void *sectionBase = (void *) ((uintptr_t) imageBuffer + section.virtualAddress);
if (section.pointerToRawData > 0 && section.sizeOfRawData > 0) {
// Grab this data
long savePos = ftell(file);
fseek(file, section.pointerToRawData, SEEK_SET);
fread(sectionBase, section.sizeOfRawData, 1, file);
fseek(file, savePos, SEEK_SET);
}
if (strcmp(name, ".rsrc") == 0) {
rsrcBase = sectionBase;
}
}
if (!exec) {
// No need to resolve imports
return true;
}
// Handle imports
PEImportDirectoryEntry *dir = fromRVA<PEImportDirectoryEntry>(header32.importTable.virtualAddress);
while (dir->name) {
char *dllName = fromRVA(dir->name);
DEBUG_LOG("DLL Name: %s\n", dllName);
uint32_t *lookupTable = fromRVA(dir->importLookupTable);
uint32_t *addressTable = fromRVA(dir->importAddressTable);
HMODULE module = loadModule(dllName);
while (*lookupTable) {
uint32_t lookup = *lookupTable;
if (lookup & 0x80000000) {
// Import by ordinal
uint16_t ordinal = lookup & 0xFFFF;
DEBUG_LOG(" Ordinal: %d\n", ordinal);
*addressTable = reinterpret_cast<uintptr_t>(resolveFuncByOrdinal(module, ordinal));
} else {
// Import by name
PEHintNameTableEntry *hintName = fromRVA<PEHintNameTableEntry>(lookup);
DEBUG_LOG(" Name: %s\n", hintName->name);
*addressTable = reinterpret_cast<uintptr_t>(resolveFuncByName(module, hintName->name));
}
++lookupTable;
++addressTable;
}
freeModule(module);
++dir;
}
entryPoint = fromRVA<void>(header32.addressOfEntryPoint);
return true;
}