|
1 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
2 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
4 |
|
5 #ifndef __GNUC__ |
|
6 // disable warnings about pointer <-> DWORD conversions |
|
7 #pragma warning( disable : 4311 4312 ) |
|
8 #endif |
|
9 |
|
10 #ifdef _WIN64 |
|
11 #define POINTER_TYPE ULONGLONG |
|
12 #else |
|
13 #define POINTER_TYPE DWORD |
|
14 #endif |
|
15 |
|
16 #include <windows.h> |
|
17 #include <winnt.h> |
|
18 #include <stdlib.h> |
|
19 #ifdef DEBUG_OUTPUT |
|
20 #include <stdio.h> |
|
21 #endif |
|
22 |
|
23 #include "nsWindowsHelpers.h" |
|
24 |
|
25 typedef const unsigned char* FileView; |
|
26 |
|
27 template<> |
|
28 class nsAutoRefTraits<FileView> |
|
29 { |
|
30 public: |
|
31 typedef FileView RawRef; |
|
32 static FileView Void() |
|
33 { |
|
34 return nullptr; |
|
35 } |
|
36 |
|
37 static void Release(RawRef aView) |
|
38 { |
|
39 if (nullptr != aView) |
|
40 UnmapViewOfFile(aView); |
|
41 } |
|
42 }; |
|
43 |
|
44 #ifndef IMAGE_SIZEOF_BASE_RELOCATION |
|
45 // Vista SDKs no longer define IMAGE_SIZEOF_BASE_RELOCATION!? |
|
46 #define IMAGE_SIZEOF_BASE_RELOCATION (sizeof(IMAGE_BASE_RELOCATION)) |
|
47 #endif |
|
48 |
|
49 #include "LoadLibraryRemote.h" |
|
50 |
|
51 typedef struct { |
|
52 PIMAGE_NT_HEADERS headers; |
|
53 unsigned char *localCodeBase; |
|
54 unsigned char *remoteCodeBase; |
|
55 HMODULE *modules; |
|
56 int numModules; |
|
57 } MEMORYMODULE, *PMEMORYMODULE; |
|
58 |
|
59 typedef BOOL (WINAPI *DllEntryProc)(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved); |
|
60 |
|
61 #define GET_HEADER_DICTIONARY(module, idx) &(module)->headers->OptionalHeader.DataDirectory[idx] |
|
62 |
|
63 #ifdef DEBUG_OUTPUT |
|
64 static void |
|
65 OutputLastError(const char *msg) |
|
66 { |
|
67 char* tmp; |
|
68 char *tmpmsg; |
|
69 FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, |
|
70 nullptr, GetLastError(), |
|
71 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), |
|
72 (LPSTR) &tmp, 0, nullptr); |
|
73 tmpmsg = (char *)LocalAlloc(LPTR, strlen(msg) + strlen(tmp) + 3); |
|
74 sprintf(tmpmsg, "%s: %s", msg, tmp); |
|
75 OutputDebugStringA(tmpmsg); |
|
76 LocalFree(tmpmsg); |
|
77 LocalFree(tmp); |
|
78 } |
|
79 #endif |
|
80 |
|
81 static void |
|
82 CopySections(const unsigned char *data, PIMAGE_NT_HEADERS old_headers, PMEMORYMODULE module) |
|
83 { |
|
84 int i; |
|
85 unsigned char *codeBase = module->localCodeBase; |
|
86 unsigned char *dest; |
|
87 PIMAGE_SECTION_HEADER section = IMAGE_FIRST_SECTION(module->headers); |
|
88 for (i=0; i<module->headers->FileHeader.NumberOfSections; i++, section++) { |
|
89 dest = codeBase + section->VirtualAddress; |
|
90 memset(dest, 0, section->Misc.VirtualSize); |
|
91 if (section->SizeOfRawData) { |
|
92 memcpy(dest, data + section->PointerToRawData, section->SizeOfRawData); |
|
93 } |
|
94 // section->Misc.PhysicalAddress = (POINTER_TYPE) module->remoteCodeBase + section->VirtualAddress; |
|
95 } |
|
96 } |
|
97 |
|
98 // Protection flags for memory pages (Executable, Readable, Writeable) |
|
99 static int ProtectionFlags[2][2][2] = { |
|
100 { |
|
101 // not executable |
|
102 {PAGE_NOACCESS, PAGE_WRITECOPY}, |
|
103 {PAGE_READONLY, PAGE_READWRITE}, |
|
104 }, { |
|
105 // executable |
|
106 {PAGE_EXECUTE, PAGE_EXECUTE_WRITECOPY}, |
|
107 {PAGE_EXECUTE_READ, PAGE_EXECUTE_READWRITE}, |
|
108 }, |
|
109 }; |
|
110 |
|
111 static bool |
|
112 FinalizeSections(PMEMORYMODULE module, HANDLE hRemoteProcess) |
|
113 { |
|
114 #ifdef DEBUG_OUTPUT |
|
115 fprintf(stderr, "Finalizing sections: local base %p, remote base %p\n", |
|
116 module->localCodeBase, module->remoteCodeBase); |
|
117 #endif |
|
118 |
|
119 int i; |
|
120 PIMAGE_SECTION_HEADER section = IMAGE_FIRST_SECTION(module->headers); |
|
121 |
|
122 // loop through all sections and change access flags |
|
123 for (i=0; i<module->headers->FileHeader.NumberOfSections; i++, section++) { |
|
124 DWORD protect, oldProtect, size; |
|
125 int executable = (section->Characteristics & IMAGE_SCN_MEM_EXECUTE) != 0; |
|
126 int readable = (section->Characteristics & IMAGE_SCN_MEM_READ) != 0; |
|
127 int writeable = (section->Characteristics & IMAGE_SCN_MEM_WRITE) != 0; |
|
128 |
|
129 // determine protection flags based on characteristics |
|
130 protect = ProtectionFlags[executable][readable][writeable]; |
|
131 if (section->Characteristics & IMAGE_SCN_MEM_NOT_CACHED) { |
|
132 protect |= PAGE_NOCACHE; |
|
133 } |
|
134 |
|
135 // determine size of region |
|
136 size = section->Misc.VirtualSize; |
|
137 if (size > 0) { |
|
138 void* remoteAddress = module->remoteCodeBase + section->VirtualAddress; |
|
139 void* localAddress = module->localCodeBase + section->VirtualAddress; |
|
140 |
|
141 #ifdef DEBUG_OUTPUT |
|
142 fprintf(stderr, "Copying section %s to %p, size %x, executable %i readable %i writeable %i\n", |
|
143 section->Name, remoteAddress, size, executable, readable, writeable); |
|
144 #endif |
|
145 |
|
146 // Copy the data from local->remote and set the memory protection |
|
147 if (!VirtualAllocEx(hRemoteProcess, remoteAddress, size, MEM_COMMIT, PAGE_READWRITE)) |
|
148 return false; |
|
149 |
|
150 if (!WriteProcessMemory(hRemoteProcess, |
|
151 remoteAddress, |
|
152 localAddress, |
|
153 size, |
|
154 nullptr)) { |
|
155 #ifdef DEBUG_OUTPUT |
|
156 OutputLastError("Error writing remote memory.\n"); |
|
157 #endif |
|
158 return false; |
|
159 } |
|
160 |
|
161 if (VirtualProtectEx(hRemoteProcess, remoteAddress, size, protect, &oldProtect) == 0) { |
|
162 #ifdef DEBUG_OUTPUT |
|
163 OutputLastError("Error protecting memory page"); |
|
164 #endif |
|
165 return false; |
|
166 } |
|
167 } |
|
168 } |
|
169 return true; |
|
170 } |
|
171 |
|
172 static void |
|
173 PerformBaseRelocation(PMEMORYMODULE module, SIZE_T delta) |
|
174 { |
|
175 DWORD i; |
|
176 unsigned char *codeBase = module->localCodeBase; |
|
177 |
|
178 PIMAGE_DATA_DIRECTORY directory = GET_HEADER_DICTIONARY(module, IMAGE_DIRECTORY_ENTRY_BASERELOC); |
|
179 if (directory->Size > 0) { |
|
180 PIMAGE_BASE_RELOCATION relocation = (PIMAGE_BASE_RELOCATION) (codeBase + directory->VirtualAddress); |
|
181 for (; relocation->VirtualAddress > 0; ) { |
|
182 unsigned char *dest = codeBase + relocation->VirtualAddress; |
|
183 unsigned short *relInfo = (unsigned short *)((unsigned char *)relocation + IMAGE_SIZEOF_BASE_RELOCATION); |
|
184 for (i=0; i<((relocation->SizeOfBlock-IMAGE_SIZEOF_BASE_RELOCATION) / 2); i++, relInfo++) { |
|
185 DWORD *patchAddrHL; |
|
186 #ifdef _WIN64 |
|
187 ULONGLONG *patchAddr64; |
|
188 #endif |
|
189 int type, offset; |
|
190 |
|
191 // the upper 4 bits define the type of relocation |
|
192 type = *relInfo >> 12; |
|
193 // the lower 12 bits define the offset |
|
194 offset = *relInfo & 0xfff; |
|
195 |
|
196 switch (type) |
|
197 { |
|
198 case IMAGE_REL_BASED_ABSOLUTE: |
|
199 // skip relocation |
|
200 break; |
|
201 |
|
202 case IMAGE_REL_BASED_HIGHLOW: |
|
203 // change complete 32 bit address |
|
204 patchAddrHL = (DWORD *) (dest + offset); |
|
205 *patchAddrHL += delta; |
|
206 break; |
|
207 |
|
208 #ifdef _WIN64 |
|
209 case IMAGE_REL_BASED_DIR64: |
|
210 patchAddr64 = (ULONGLONG *) (dest + offset); |
|
211 *patchAddr64 += delta; |
|
212 break; |
|
213 #endif |
|
214 |
|
215 default: |
|
216 //printf("Unknown relocation: %d\n", type); |
|
217 break; |
|
218 } |
|
219 } |
|
220 |
|
221 // advance to next relocation block |
|
222 relocation = (PIMAGE_BASE_RELOCATION) (((char *) relocation) + relocation->SizeOfBlock); |
|
223 } |
|
224 } |
|
225 } |
|
226 |
|
227 static int |
|
228 BuildImportTable(PMEMORYMODULE module) |
|
229 { |
|
230 int result=1; |
|
231 unsigned char *codeBase = module->localCodeBase; |
|
232 |
|
233 PIMAGE_DATA_DIRECTORY directory = GET_HEADER_DICTIONARY(module, IMAGE_DIRECTORY_ENTRY_IMPORT); |
|
234 if (directory->Size > 0) { |
|
235 PIMAGE_IMPORT_DESCRIPTOR importDesc = (PIMAGE_IMPORT_DESCRIPTOR) (codeBase + directory->VirtualAddress); |
|
236 PIMAGE_IMPORT_DESCRIPTOR importEnd = (PIMAGE_IMPORT_DESCRIPTOR) (codeBase + directory->VirtualAddress + directory->Size); |
|
237 |
|
238 for (; importDesc < importEnd && importDesc->Name; importDesc++) { |
|
239 POINTER_TYPE *thunkRef; |
|
240 FARPROC *funcRef; |
|
241 HMODULE handle = GetModuleHandleA((LPCSTR) (codeBase + importDesc->Name)); |
|
242 if (handle == nullptr) { |
|
243 #if DEBUG_OUTPUT |
|
244 OutputLastError("Can't load library"); |
|
245 #endif |
|
246 result = 0; |
|
247 break; |
|
248 } |
|
249 |
|
250 module->modules = (HMODULE *)realloc(module->modules, (module->numModules+1)*(sizeof(HMODULE))); |
|
251 if (module->modules == nullptr) { |
|
252 result = 0; |
|
253 break; |
|
254 } |
|
255 |
|
256 module->modules[module->numModules++] = handle; |
|
257 if (importDesc->OriginalFirstThunk) { |
|
258 thunkRef = (POINTER_TYPE *) (codeBase + importDesc->OriginalFirstThunk); |
|
259 funcRef = (FARPROC *) (codeBase + importDesc->FirstThunk); |
|
260 } else { |
|
261 // no hint table |
|
262 thunkRef = (POINTER_TYPE *) (codeBase + importDesc->FirstThunk); |
|
263 funcRef = (FARPROC *) (codeBase + importDesc->FirstThunk); |
|
264 } |
|
265 for (; *thunkRef; thunkRef++, funcRef++) { |
|
266 if (IMAGE_SNAP_BY_ORDINAL(*thunkRef)) { |
|
267 *funcRef = (FARPROC)GetProcAddress(handle, (LPCSTR)IMAGE_ORDINAL(*thunkRef)); |
|
268 } else { |
|
269 PIMAGE_IMPORT_BY_NAME thunkData = (PIMAGE_IMPORT_BY_NAME) (codeBase + (*thunkRef)); |
|
270 *funcRef = (FARPROC)GetProcAddress(handle, (LPCSTR)&thunkData->Name); |
|
271 } |
|
272 if (*funcRef == 0) { |
|
273 result = 0; |
|
274 break; |
|
275 } |
|
276 } |
|
277 |
|
278 if (!result) { |
|
279 break; |
|
280 } |
|
281 } |
|
282 } |
|
283 |
|
284 return result; |
|
285 } |
|
286 |
|
287 static void* MemoryGetProcAddress(PMEMORYMODULE module, const char *name); |
|
288 |
|
289 void* LoadRemoteLibraryAndGetAddress(HANDLE hRemoteProcess, |
|
290 const WCHAR* library, |
|
291 const char* symbol) |
|
292 { |
|
293 // Map the DLL into memory |
|
294 nsAutoHandle hLibrary( |
|
295 CreateFile(library, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, |
|
296 FILE_ATTRIBUTE_NORMAL, nullptr)); |
|
297 if (INVALID_HANDLE_VALUE == hLibrary) { |
|
298 #if DEBUG_OUTPUT |
|
299 OutputLastError("Couldn't CreateFile the library.\n"); |
|
300 #endif |
|
301 return nullptr; |
|
302 } |
|
303 |
|
304 nsAutoHandle hMapping( |
|
305 CreateFileMapping(hLibrary, nullptr, PAGE_READONLY, 0, 0, nullptr)); |
|
306 if (!hMapping) { |
|
307 #if DEBUG_OUTPUT |
|
308 OutputLastError("Couldn't CreateFileMapping.\n"); |
|
309 #endif |
|
310 return nullptr; |
|
311 } |
|
312 |
|
313 nsAutoRef<FileView> data( |
|
314 (const unsigned char*) MapViewOfFile(hMapping, FILE_MAP_READ, 0, 0, 0)); |
|
315 if (!data) { |
|
316 #if DEBUG_OUTPUT |
|
317 OutputLastError("Couldn't MapViewOfFile.\n"); |
|
318 #endif |
|
319 return nullptr; |
|
320 } |
|
321 |
|
322 SIZE_T locationDelta; |
|
323 |
|
324 PIMAGE_DOS_HEADER dos_header = (PIMAGE_DOS_HEADER)data.get(); |
|
325 if (dos_header->e_magic != IMAGE_DOS_SIGNATURE) { |
|
326 #if DEBUG_OUTPUT |
|
327 OutputDebugStringA("Not a valid executable file.\n"); |
|
328 #endif |
|
329 return nullptr; |
|
330 } |
|
331 |
|
332 PIMAGE_NT_HEADERS old_header = (PIMAGE_NT_HEADERS)(data + dos_header->e_lfanew); |
|
333 if (old_header->Signature != IMAGE_NT_SIGNATURE) { |
|
334 #if DEBUG_OUTPUT |
|
335 OutputDebugStringA("No PE header found.\n"); |
|
336 #endif |
|
337 return nullptr; |
|
338 } |
|
339 |
|
340 // reserve memory for image of library in this process and the target process |
|
341 unsigned char* localCode = (unsigned char*) VirtualAlloc(nullptr, |
|
342 old_header->OptionalHeader.SizeOfImage, |
|
343 MEM_RESERVE | MEM_COMMIT, |
|
344 PAGE_READWRITE); |
|
345 if (!localCode) { |
|
346 #if DEBUG_OUTPUT |
|
347 OutputLastError("Can't reserve local memory."); |
|
348 #endif |
|
349 } |
|
350 |
|
351 unsigned char* remoteCode = (unsigned char*) VirtualAllocEx(hRemoteProcess, nullptr, |
|
352 old_header->OptionalHeader.SizeOfImage, |
|
353 MEM_RESERVE, |
|
354 PAGE_EXECUTE_READ); |
|
355 if (!remoteCode) { |
|
356 #if DEBUG_OUTPUT |
|
357 OutputLastError("Can't reserve remote memory."); |
|
358 #endif |
|
359 } |
|
360 |
|
361 MEMORYMODULE result; |
|
362 result.localCodeBase = localCode; |
|
363 result.remoteCodeBase = remoteCode; |
|
364 result.numModules = 0; |
|
365 result.modules = nullptr; |
|
366 |
|
367 // copy PE header to code |
|
368 memcpy(localCode, dos_header, dos_header->e_lfanew + old_header->OptionalHeader.SizeOfHeaders); |
|
369 result.headers = reinterpret_cast<PIMAGE_NT_HEADERS>(localCode + dos_header->e_lfanew); |
|
370 |
|
371 // update position |
|
372 result.headers->OptionalHeader.ImageBase = (POINTER_TYPE)remoteCode; |
|
373 |
|
374 // copy sections from DLL file block to new memory location |
|
375 CopySections(data, old_header, &result); |
|
376 |
|
377 // adjust base address of imported data |
|
378 locationDelta = (SIZE_T)(remoteCode - old_header->OptionalHeader.ImageBase); |
|
379 if (locationDelta != 0) { |
|
380 PerformBaseRelocation(&result, locationDelta); |
|
381 } |
|
382 |
|
383 // load required dlls and adjust function table of imports |
|
384 if (!BuildImportTable(&result)) { |
|
385 return nullptr; |
|
386 } |
|
387 |
|
388 // mark memory pages depending on section headers and release |
|
389 // sections that are marked as "discardable" |
|
390 if (!FinalizeSections(&result, hRemoteProcess)) { |
|
391 return nullptr; |
|
392 } |
|
393 |
|
394 return MemoryGetProcAddress(&result, symbol); |
|
395 } |
|
396 |
|
397 static void* MemoryGetProcAddress(PMEMORYMODULE module, const char *name) |
|
398 { |
|
399 unsigned char *localCodeBase = module->localCodeBase; |
|
400 int idx=-1; |
|
401 DWORD i, *nameRef; |
|
402 WORD *ordinal; |
|
403 PIMAGE_EXPORT_DIRECTORY exports; |
|
404 PIMAGE_DATA_DIRECTORY directory = GET_HEADER_DICTIONARY(module, IMAGE_DIRECTORY_ENTRY_EXPORT); |
|
405 if (directory->Size == 0) { |
|
406 // no export table found |
|
407 return nullptr; |
|
408 } |
|
409 |
|
410 exports = (PIMAGE_EXPORT_DIRECTORY) (localCodeBase + directory->VirtualAddress); |
|
411 if (exports->NumberOfNames == 0 || exports->NumberOfFunctions == 0) { |
|
412 // DLL doesn't export anything |
|
413 return nullptr; |
|
414 } |
|
415 |
|
416 // search function name in list of exported names |
|
417 nameRef = (DWORD *) (localCodeBase + exports->AddressOfNames); |
|
418 ordinal = (WORD *) (localCodeBase + exports->AddressOfNameOrdinals); |
|
419 for (i=0; i<exports->NumberOfNames; i++, nameRef++, ordinal++) { |
|
420 if (stricmp(name, (const char *) (localCodeBase + (*nameRef))) == 0) { |
|
421 idx = *ordinal; |
|
422 break; |
|
423 } |
|
424 } |
|
425 |
|
426 if (idx == -1) { |
|
427 // exported symbol not found |
|
428 return nullptr; |
|
429 } |
|
430 |
|
431 if ((DWORD)idx > exports->NumberOfFunctions) { |
|
432 // name <-> ordinal number don't match |
|
433 return nullptr; |
|
434 } |
|
435 |
|
436 // AddressOfFunctions contains the RVAs to the "real" functions |
|
437 return module->remoteCodeBase + (*(DWORD *) (localCodeBase + exports->AddressOfFunctions + (idx*4))); |
|
438 } |