MemoryModule.c 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974
  1. /*
  2. * Memory DLL loading code
  3. * Version 0.0.4
  4. *
  5. * Copyright (c) 2004-2015 by Joachim Bauch / mail@joachim-bauch.de
  6. * http://www.joachim-bauch.de
  7. *
  8. * The contents of this file are subject to the Mozilla Public License Version
  9. * 2.0 (the "License"); you may not use this file except in compliance with
  10. * the License. You may obtain a copy of the License at
  11. * http://www.mozilla.org/MPL/
  12. *
  13. * Software distributed under the License is distributed on an "AS IS" basis,
  14. * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  15. * for the specific language governing rights and limitations under the
  16. * License.
  17. *
  18. * The Original Code is MemoryModule.c
  19. *
  20. * The Initial Developer of the Original Code is Joachim Bauch.
  21. *
  22. * Portions created by Joachim Bauch are Copyright (C) 2004-2015
  23. * Joachim Bauch. All Rights Reserved.
  24. *
  25. */
  26. #include <windows.h>
  27. #include <winnt.h>
  28. #include <stddef.h>
  29. #include <stdint.h>
  30. #include <tchar.h>
  31. #ifdef DEBUG_OUTPUT
  32. #include <stdio.h>
  33. #endif
  34. #if _MSC_VER
  35. // Disable warning about data -> function pointer conversion
  36. #pragma warning(disable:4055)
  37. #endif
  38. #ifndef IMAGE_SIZEOF_BASE_RELOCATION
  39. // Vista SDKs no longer define IMAGE_SIZEOF_BASE_RELOCATION!?
  40. #define IMAGE_SIZEOF_BASE_RELOCATION (sizeof(IMAGE_BASE_RELOCATION))
  41. #endif
  42. #include "MemoryModule.h"
  43. typedef BOOL (WINAPI *DllEntryProc)(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved);
  44. typedef int (WINAPI *ExeEntryProc)(void);
  45. typedef struct {
  46. PIMAGE_NT_HEADERS headers;
  47. unsigned char *codeBase;
  48. HCUSTOMMODULE *modules;
  49. int numModules;
  50. BOOL initialized;
  51. BOOL isDLL;
  52. BOOL isRelocated;
  53. CustomLoadLibraryFunc loadLibrary;
  54. CustomGetProcAddressFunc getProcAddress;
  55. CustomFreeLibraryFunc freeLibrary;
  56. void *userdata;
  57. ExeEntryProc exeEntry;
  58. DWORD pageSize;
  59. } MEMORYMODULE, *PMEMORYMODULE;
  60. typedef struct {
  61. LPVOID address;
  62. LPVOID alignedAddress;
  63. DWORD size;
  64. DWORD characteristics;
  65. BOOL last;
  66. } SECTIONFINALIZEDATA, *PSECTIONFINALIZEDATA;
  67. #define GET_HEADER_DICTIONARY(module, idx) &(module)->headers->OptionalHeader.DataDirectory[idx]
  68. #define ALIGN_DOWN(address, alignment) (LPVOID)((uintptr_t)(address) & ~((alignment) - 1))
  69. #define ALIGN_VALUE_UP(value, alignment) (((value) + (alignment) - 1) & ~((alignment) - 1))
  70. #ifdef DEBUG_OUTPUT
  71. static void
  72. OutputLastError(const char *msg)
  73. {
  74. LPVOID tmp;
  75. char *tmpmsg;
  76. FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
  77. NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&tmp, 0, NULL);
  78. tmpmsg = (char *)LocalAlloc(LPTR, strlen(msg) + strlen(tmp) + 3);
  79. sprintf(tmpmsg, "%s: %s", msg, tmp);
  80. OutputDebugString(tmpmsg);
  81. LocalFree(tmpmsg);
  82. LocalFree(tmp);
  83. }
  84. #endif
  85. static BOOL
  86. CheckSize(size_t size, size_t expected) {
  87. if (size < expected) {
  88. SetLastError(ERROR_INVALID_DATA);
  89. return FALSE;
  90. }
  91. return TRUE;
  92. }
  93. static BOOL
  94. CopySections(const unsigned char *data, size_t size, PIMAGE_NT_HEADERS old_headers, PMEMORYMODULE module)
  95. {
  96. int i, section_size;
  97. unsigned char *codeBase = module->codeBase;
  98. unsigned char *dest;
  99. PIMAGE_SECTION_HEADER section = IMAGE_FIRST_SECTION(module->headers);
  100. for (i=0; i<module->headers->FileHeader.NumberOfSections; i++, section++) {
  101. if (section->SizeOfRawData == 0) {
  102. // section doesn't contain data in the dll itself, but may define
  103. // uninitialized data
  104. section_size = old_headers->OptionalHeader.SectionAlignment;
  105. if (section_size > 0) {
  106. dest = (unsigned char *)VirtualAlloc(codeBase + section->VirtualAddress,
  107. section_size,
  108. MEM_COMMIT,
  109. PAGE_READWRITE);
  110. if (dest == NULL) {
  111. return FALSE;
  112. }
  113. // Always use position from file to support alignments smaller
  114. // than page size.
  115. dest = codeBase + section->VirtualAddress;
  116. section->Misc.PhysicalAddress = (DWORD) (uintptr_t) dest;
  117. memset(dest, 0, section_size);
  118. }
  119. // section is empty
  120. continue;
  121. }
  122. if (!CheckSize(size, section->PointerToRawData + section->SizeOfRawData)) {
  123. return FALSE;
  124. }
  125. // commit memory block and copy data from dll
  126. dest = (unsigned char *)VirtualAlloc(codeBase + section->VirtualAddress,
  127. section->SizeOfRawData,
  128. MEM_COMMIT,
  129. PAGE_READWRITE);
  130. if (dest == NULL) {
  131. return FALSE;
  132. }
  133. // Always use position from file to support alignments smaller
  134. // than page size.
  135. dest = codeBase + section->VirtualAddress;
  136. memcpy(dest, data + section->PointerToRawData, section->SizeOfRawData);
  137. section->Misc.PhysicalAddress = (DWORD) (uintptr_t) dest;
  138. }
  139. return TRUE;
  140. }
  141. // Protection flags for memory pages (Executable, Readable, Writeable)
  142. static int ProtectionFlags[2][2][2] = {
  143. {
  144. // not executable
  145. {PAGE_NOACCESS, PAGE_WRITECOPY},
  146. {PAGE_READONLY, PAGE_READWRITE},
  147. }, {
  148. // executable
  149. {PAGE_EXECUTE, PAGE_EXECUTE_WRITECOPY},
  150. {PAGE_EXECUTE_READ, PAGE_EXECUTE_READWRITE},
  151. },
  152. };
  153. static DWORD
  154. GetRealSectionSize(PMEMORYMODULE module, PIMAGE_SECTION_HEADER section) {
  155. DWORD size = section->SizeOfRawData;
  156. if (size == 0) {
  157. if (section->Characteristics & IMAGE_SCN_CNT_INITIALIZED_DATA) {
  158. size = module->headers->OptionalHeader.SizeOfInitializedData;
  159. } else if (section->Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA) {
  160. size = module->headers->OptionalHeader.SizeOfUninitializedData;
  161. }
  162. }
  163. return size;
  164. }
  165. static BOOL
  166. FinalizeSection(PMEMORYMODULE module, PSECTIONFINALIZEDATA sectionData) {
  167. DWORD protect, oldProtect;
  168. BOOL executable;
  169. BOOL readable;
  170. BOOL writeable;
  171. if (sectionData->size == 0) {
  172. return TRUE;
  173. }
  174. if (sectionData->characteristics & IMAGE_SCN_MEM_DISCARDABLE) {
  175. // section is not needed any more and can safely be freed
  176. if (sectionData->address == sectionData->alignedAddress &&
  177. (sectionData->last ||
  178. module->headers->OptionalHeader.SectionAlignment == module->pageSize ||
  179. (sectionData->size % module->pageSize) == 0)
  180. ) {
  181. // Only allowed to decommit whole pages
  182. VirtualFree(sectionData->address, sectionData->size, MEM_DECOMMIT);
  183. }
  184. return TRUE;
  185. }
  186. // determine protection flags based on characteristics
  187. executable = (sectionData->characteristics & IMAGE_SCN_MEM_EXECUTE) != 0;
  188. readable = (sectionData->characteristics & IMAGE_SCN_MEM_READ) != 0;
  189. writeable = (sectionData->characteristics & IMAGE_SCN_MEM_WRITE) != 0;
  190. protect = ProtectionFlags[executable][readable][writeable];
  191. if (sectionData->characteristics & IMAGE_SCN_MEM_NOT_CACHED) {
  192. protect |= PAGE_NOCACHE;
  193. }
  194. // change memory access flags
  195. if (VirtualProtect(sectionData->address, sectionData->size, protect, &oldProtect) == 0) {
  196. #ifdef DEBUG_OUTPUT
  197. OutputLastError("Error protecting memory page")
  198. #endif
  199. return FALSE;
  200. }
  201. return TRUE;
  202. }
  203. static BOOL
  204. FinalizeSections(PMEMORYMODULE module)
  205. {
  206. int i;
  207. PIMAGE_SECTION_HEADER section = IMAGE_FIRST_SECTION(module->headers);
  208. #ifdef _WIN64
  209. uintptr_t imageOffset = (module->headers->OptionalHeader.ImageBase & 0xffffffff00000000);
  210. #else
  211. #define imageOffset 0
  212. #endif
  213. SECTIONFINALIZEDATA sectionData;
  214. sectionData.address = (LPVOID)((uintptr_t)section->Misc.PhysicalAddress | imageOffset);
  215. sectionData.alignedAddress = ALIGN_DOWN(sectionData.address, module->pageSize);
  216. sectionData.size = GetRealSectionSize(module, section);
  217. sectionData.characteristics = section->Characteristics;
  218. sectionData.last = FALSE;
  219. section++;
  220. // loop through all sections and change access flags
  221. for (i=1; i<module->headers->FileHeader.NumberOfSections; i++, section++) {
  222. LPVOID sectionAddress = (LPVOID)((uintptr_t)section->Misc.PhysicalAddress | imageOffset);
  223. LPVOID alignedAddress = ALIGN_DOWN(sectionAddress, module->pageSize);
  224. DWORD sectionSize = GetRealSectionSize(module, section);
  225. // Combine access flags of all sections that share a page
  226. // TODO(fancycode): We currently share flags of a trailing large section
  227. // with the page of a first small section. This should be optimized.
  228. if (sectionData.alignedAddress == alignedAddress || (uintptr_t) sectionData.address + sectionData.size > (uintptr_t) alignedAddress) {
  229. // Section shares page with previous
  230. if ((section->Characteristics & IMAGE_SCN_MEM_DISCARDABLE) == 0 || (sectionData.characteristics & IMAGE_SCN_MEM_DISCARDABLE) == 0) {
  231. sectionData.characteristics = (sectionData.characteristics | section->Characteristics) & ~IMAGE_SCN_MEM_DISCARDABLE;
  232. } else {
  233. sectionData.characteristics |= section->Characteristics;
  234. }
  235. sectionData.size = (((uintptr_t)sectionAddress) + sectionSize) - (uintptr_t) sectionData.address;
  236. continue;
  237. }
  238. if (!FinalizeSection(module, &sectionData)) {
  239. return FALSE;
  240. }
  241. sectionData.address = sectionAddress;
  242. sectionData.alignedAddress = alignedAddress;
  243. sectionData.size = sectionSize;
  244. sectionData.characteristics = section->Characteristics;
  245. }
  246. sectionData.last = TRUE;
  247. if (!FinalizeSection(module, &sectionData)) {
  248. return FALSE;
  249. }
  250. #ifndef _WIN64
  251. #undef imageOffset
  252. #endif
  253. return TRUE;
  254. }
  255. static BOOL
  256. ExecuteTLS(PMEMORYMODULE module)
  257. {
  258. unsigned char *codeBase = module->codeBase;
  259. PIMAGE_TLS_DIRECTORY tls;
  260. PIMAGE_TLS_CALLBACK* callback;
  261. PIMAGE_DATA_DIRECTORY directory = GET_HEADER_DICTIONARY(module, IMAGE_DIRECTORY_ENTRY_TLS);
  262. if (directory->VirtualAddress == 0) {
  263. return TRUE;
  264. }
  265. tls = (PIMAGE_TLS_DIRECTORY) (codeBase + directory->VirtualAddress);
  266. callback = (PIMAGE_TLS_CALLBACK *) tls->AddressOfCallBacks;
  267. if (callback) {
  268. while (*callback) {
  269. (*callback)((LPVOID) codeBase, DLL_PROCESS_ATTACH, NULL);
  270. callback++;
  271. }
  272. }
  273. return TRUE;
  274. }
  275. static BOOL
  276. PerformBaseRelocation(PMEMORYMODULE module, ptrdiff_t delta)
  277. {
  278. unsigned char *codeBase = module->codeBase;
  279. PIMAGE_BASE_RELOCATION relocation;
  280. PIMAGE_DATA_DIRECTORY directory = GET_HEADER_DICTIONARY(module, IMAGE_DIRECTORY_ENTRY_BASERELOC);
  281. if (directory->Size == 0) {
  282. return (delta == 0);
  283. }
  284. relocation = (PIMAGE_BASE_RELOCATION) (codeBase + directory->VirtualAddress);
  285. for (; relocation->VirtualAddress > 0; ) {
  286. DWORD i;
  287. unsigned char *dest = codeBase + relocation->VirtualAddress;
  288. unsigned short *relInfo = (unsigned short *)((unsigned char *)relocation + IMAGE_SIZEOF_BASE_RELOCATION);
  289. for (i=0; i<((relocation->SizeOfBlock-IMAGE_SIZEOF_BASE_RELOCATION) / 2); i++, relInfo++) {
  290. DWORD *patchAddrHL;
  291. #ifdef _WIN64
  292. ULONGLONG *patchAddr64;
  293. #endif
  294. int type, offset;
  295. // the upper 4 bits define the type of relocation
  296. type = *relInfo >> 12;
  297. // the lower 12 bits define the offset
  298. offset = *relInfo & 0xfff;
  299. switch (type)
  300. {
  301. case IMAGE_REL_BASED_ABSOLUTE:
  302. // skip relocation
  303. break;
  304. case IMAGE_REL_BASED_HIGHLOW:
  305. // change complete 32 bit address
  306. patchAddrHL = (DWORD *) (dest + offset);
  307. *patchAddrHL += (DWORD) delta;
  308. break;
  309. #ifdef _WIN64
  310. case IMAGE_REL_BASED_DIR64:
  311. patchAddr64 = (ULONGLONG *) (dest + offset);
  312. *patchAddr64 += (ULONGLONG) delta;
  313. break;
  314. #endif
  315. default:
  316. //printf("Unknown relocation: %d\n", type);
  317. break;
  318. }
  319. }
  320. // advance to next relocation block
  321. relocation = (PIMAGE_BASE_RELOCATION) (((char *) relocation) + relocation->SizeOfBlock);
  322. }
  323. return TRUE;
  324. }
  325. static BOOL
  326. BuildImportTable(PMEMORYMODULE module)
  327. {
  328. unsigned char *codeBase = module->codeBase;
  329. PIMAGE_IMPORT_DESCRIPTOR importDesc;
  330. BOOL result = TRUE;
  331. PIMAGE_DATA_DIRECTORY directory = GET_HEADER_DICTIONARY(module, IMAGE_DIRECTORY_ENTRY_IMPORT);
  332. if (directory->Size == 0) {
  333. return TRUE;
  334. }
  335. importDesc = (PIMAGE_IMPORT_DESCRIPTOR) (codeBase + directory->VirtualAddress);
  336. for (; !IsBadReadPtr(importDesc, sizeof(IMAGE_IMPORT_DESCRIPTOR)) && importDesc->Name; importDesc++) {
  337. uintptr_t *thunkRef;
  338. FARPROC *funcRef;
  339. HCUSTOMMODULE *tmp;
  340. HCUSTOMMODULE handle = module->loadLibrary((LPCSTR) (codeBase + importDesc->Name), module->userdata);
  341. if (handle == NULL) {
  342. SetLastError(ERROR_MOD_NOT_FOUND);
  343. result = FALSE;
  344. break;
  345. }
  346. tmp = (HCUSTOMMODULE *) realloc(module->modules, (module->numModules+1)*(sizeof(HCUSTOMMODULE)));
  347. if (tmp == NULL) {
  348. module->freeLibrary(handle, module->userdata);
  349. SetLastError(ERROR_OUTOFMEMORY);
  350. result = FALSE;
  351. break;
  352. }
  353. module->modules = tmp;
  354. module->modules[module->numModules++] = handle;
  355. if (importDesc->OriginalFirstThunk) {
  356. thunkRef = (uintptr_t *) (codeBase + importDesc->OriginalFirstThunk);
  357. funcRef = (FARPROC *) (codeBase + importDesc->FirstThunk);
  358. } else {
  359. // no hint table
  360. thunkRef = (uintptr_t *) (codeBase + importDesc->FirstThunk);
  361. funcRef = (FARPROC *) (codeBase + importDesc->FirstThunk);
  362. }
  363. for (; *thunkRef; thunkRef++, funcRef++) {
  364. if (IMAGE_SNAP_BY_ORDINAL(*thunkRef)) {
  365. *funcRef = module->getProcAddress(handle, (LPCSTR)IMAGE_ORDINAL(*thunkRef), module->userdata);
  366. } else {
  367. PIMAGE_IMPORT_BY_NAME thunkData = (PIMAGE_IMPORT_BY_NAME) (codeBase + (*thunkRef));
  368. *funcRef = module->getProcAddress(handle, (LPCSTR)&thunkData->Name, module->userdata);
  369. }
  370. if (*funcRef == 0) {
  371. result = FALSE;
  372. break;
  373. }
  374. }
  375. if (!result) {
  376. module->freeLibrary(handle, module->userdata);
  377. SetLastError(ERROR_PROC_NOT_FOUND);
  378. break;
  379. }
  380. }
  381. return result;
  382. }
  383. HCUSTOMMODULE MemoryDefaultLoadLibrary(LPCSTR filename, void *userdata)
  384. {
  385. HMODULE result;
  386. UNREFERENCED_PARAMETER(userdata);
  387. result = LoadLibraryA(filename);
  388. if (result == NULL) {
  389. return NULL;
  390. }
  391. return (HCUSTOMMODULE) result;
  392. }
  393. FARPROC MemoryDefaultGetProcAddress(HCUSTOMMODULE module, LPCSTR name, void *userdata)
  394. {
  395. UNREFERENCED_PARAMETER(userdata);
  396. return (FARPROC) GetProcAddress((HMODULE) module, name);
  397. }
  398. void MemoryDefaultFreeLibrary(HCUSTOMMODULE module, void *userdata)
  399. {
  400. UNREFERENCED_PARAMETER(userdata);
  401. FreeLibrary((HMODULE) module);
  402. }
  403. HMEMORYMODULE MemoryLoadLibrary(const void *data, size_t size)
  404. {
  405. return MemoryLoadLibraryEx(data, size, MemoryDefaultLoadLibrary, MemoryDefaultGetProcAddress, MemoryDefaultFreeLibrary, NULL);
  406. }
  407. HMEMORYMODULE MemoryLoadLibraryEx(const void *data, size_t size,
  408. CustomLoadLibraryFunc loadLibrary,
  409. CustomGetProcAddressFunc getProcAddress,
  410. CustomFreeLibraryFunc freeLibrary,
  411. void *userdata)
  412. {
  413. PMEMORYMODULE result = NULL;
  414. PIMAGE_DOS_HEADER dos_header;
  415. PIMAGE_NT_HEADERS old_header;
  416. unsigned char *code, *headers;
  417. ptrdiff_t locationDelta;
  418. SYSTEM_INFO sysInfo;
  419. PIMAGE_SECTION_HEADER section;
  420. DWORD i;
  421. size_t optionalSectionSize;
  422. size_t lastSectionEnd = 0;
  423. size_t alignedImageSize;
  424. if (!CheckSize(size, sizeof(IMAGE_DOS_HEADER))) {
  425. return NULL;
  426. }
  427. dos_header = (PIMAGE_DOS_HEADER)data;
  428. if (dos_header->e_magic != IMAGE_DOS_SIGNATURE) {
  429. SetLastError(ERROR_BAD_EXE_FORMAT);
  430. return NULL;
  431. }
  432. if (!CheckSize(size, dos_header->e_lfanew + sizeof(IMAGE_NT_HEADERS))) {
  433. return NULL;
  434. }
  435. old_header = (PIMAGE_NT_HEADERS)&((const unsigned char *)(data))[dos_header->e_lfanew];
  436. if (old_header->Signature != IMAGE_NT_SIGNATURE) {
  437. SetLastError(ERROR_BAD_EXE_FORMAT);
  438. return NULL;
  439. }
  440. #ifdef _WIN64
  441. if (old_header->FileHeader.Machine != IMAGE_FILE_MACHINE_AMD64) {
  442. #else
  443. if (old_header->FileHeader.Machine != IMAGE_FILE_MACHINE_I386) {
  444. #endif
  445. SetLastError(ERROR_BAD_EXE_FORMAT);
  446. return NULL;
  447. }
  448. if (old_header->OptionalHeader.SectionAlignment & 1) {
  449. // Only support section alignments that are a multiple of 2
  450. SetLastError(ERROR_BAD_EXE_FORMAT);
  451. return NULL;
  452. }
  453. section = IMAGE_FIRST_SECTION(old_header);
  454. optionalSectionSize = old_header->OptionalHeader.SectionAlignment;
  455. for (i=0; i<old_header->FileHeader.NumberOfSections; i++, section++) {
  456. size_t endOfSection;
  457. if (section->SizeOfRawData == 0) {
  458. // Section without data in the DLL
  459. endOfSection = section->VirtualAddress + optionalSectionSize;
  460. } else {
  461. endOfSection = section->VirtualAddress + section->SizeOfRawData;
  462. }
  463. if (endOfSection > lastSectionEnd) {
  464. lastSectionEnd = endOfSection;
  465. }
  466. }
  467. GetNativeSystemInfo(&sysInfo);
  468. alignedImageSize = ALIGN_VALUE_UP(old_header->OptionalHeader.SizeOfImage, sysInfo.dwPageSize);
  469. if (alignedImageSize != ALIGN_VALUE_UP(lastSectionEnd, sysInfo.dwPageSize)) {
  470. SetLastError(ERROR_BAD_EXE_FORMAT);
  471. return NULL;
  472. }
  473. // reserve memory for image of library
  474. // XXX: is it correct to commit the complete memory region at once?
  475. // calling DllEntry raises an exception if we don't...
  476. code = (unsigned char *)VirtualAlloc((LPVOID)(old_header->OptionalHeader.ImageBase),
  477. alignedImageSize,
  478. MEM_RESERVE | MEM_COMMIT,
  479. PAGE_READWRITE);
  480. if (code == NULL) {
  481. // try to allocate memory at arbitrary position
  482. code = (unsigned char *)VirtualAlloc(NULL,
  483. alignedImageSize,
  484. MEM_RESERVE | MEM_COMMIT,
  485. PAGE_READWRITE);
  486. if (code == NULL) {
  487. SetLastError(ERROR_OUTOFMEMORY);
  488. return NULL;
  489. }
  490. }
  491. result = (PMEMORYMODULE)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(MEMORYMODULE));
  492. if (result == NULL) {
  493. VirtualFree(code, 0, MEM_RELEASE);
  494. SetLastError(ERROR_OUTOFMEMORY);
  495. return NULL;
  496. }
  497. result->codeBase = code;
  498. result->isDLL = (old_header->FileHeader.Characteristics & IMAGE_FILE_DLL) != 0;
  499. result->loadLibrary = loadLibrary;
  500. result->getProcAddress = getProcAddress;
  501. result->freeLibrary = freeLibrary;
  502. result->userdata = userdata;
  503. result->pageSize = sysInfo.dwPageSize;
  504. if (!CheckSize(size, old_header->OptionalHeader.SizeOfHeaders)) {
  505. goto error;
  506. }
  507. // commit memory for headers
  508. headers = (unsigned char *)VirtualAlloc(code,
  509. old_header->OptionalHeader.SizeOfHeaders,
  510. MEM_COMMIT,
  511. PAGE_READWRITE);
  512. // copy PE header to code
  513. memcpy(headers, dos_header, old_header->OptionalHeader.SizeOfHeaders);
  514. result->headers = (PIMAGE_NT_HEADERS)&((const unsigned char *)(headers))[dos_header->e_lfanew];
  515. // update position
  516. result->headers->OptionalHeader.ImageBase = (uintptr_t)code;
  517. // copy sections from DLL file block to new memory location
  518. if (!CopySections((const unsigned char *) data, size, old_header, result)) {
  519. goto error;
  520. }
  521. // adjust base address of imported data
  522. locationDelta = (ptrdiff_t)(result->headers->OptionalHeader.ImageBase - old_header->OptionalHeader.ImageBase);
  523. if (locationDelta != 0) {
  524. result->isRelocated = PerformBaseRelocation(result, locationDelta);
  525. } else {
  526. result->isRelocated = TRUE;
  527. }
  528. // load required dlls and adjust function table of imports
  529. if (!BuildImportTable(result)) {
  530. goto error;
  531. }
  532. // mark memory pages depending on section headers and release
  533. // sections that are marked as "discardable"
  534. if (!FinalizeSections(result)) {
  535. goto error;
  536. }
  537. // TLS callbacks are executed BEFORE the main loading
  538. if (!ExecuteTLS(result)) {
  539. goto error;
  540. }
  541. // get entry point of loaded library
  542. if (result->headers->OptionalHeader.AddressOfEntryPoint != 0) {
  543. if (result->isDLL) {
  544. DllEntryProc DllEntry = (DllEntryProc)(LPVOID)(code + result->headers->OptionalHeader.AddressOfEntryPoint);
  545. // notify library about attaching to process
  546. BOOL successfull = (*DllEntry)((HINSTANCE)code, DLL_PROCESS_ATTACH, 0);
  547. if (!successfull) {
  548. SetLastError(ERROR_DLL_INIT_FAILED);
  549. goto error;
  550. }
  551. result->initialized = TRUE;
  552. } else {
  553. result->exeEntry = (ExeEntryProc)(LPVOID)(code + result->headers->OptionalHeader.AddressOfEntryPoint);
  554. }
  555. } else {
  556. result->exeEntry = NULL;
  557. }
  558. return (HMEMORYMODULE)result;
  559. error:
  560. // cleanup
  561. MemoryFreeLibrary(result);
  562. return NULL;
  563. }
  564. FARPROC MemoryGetProcAddress(HMEMORYMODULE module, LPCSTR name)
  565. {
  566. unsigned char *codeBase = ((PMEMORYMODULE)module)->codeBase;
  567. DWORD idx = 0;
  568. PIMAGE_EXPORT_DIRECTORY exports;
  569. PIMAGE_DATA_DIRECTORY directory = GET_HEADER_DICTIONARY((PMEMORYMODULE)module, IMAGE_DIRECTORY_ENTRY_EXPORT);
  570. if (directory->Size == 0) {
  571. // no export table found
  572. SetLastError(ERROR_PROC_NOT_FOUND);
  573. return NULL;
  574. }
  575. exports = (PIMAGE_EXPORT_DIRECTORY) (codeBase + directory->VirtualAddress);
  576. if (exports->NumberOfNames == 0 || exports->NumberOfFunctions == 0) {
  577. // DLL doesn't export anything
  578. SetLastError(ERROR_PROC_NOT_FOUND);
  579. return NULL;
  580. }
  581. if (HIWORD(name) == 0) {
  582. // load function by ordinal value
  583. if (LOWORD(name) < exports->Base) {
  584. SetLastError(ERROR_PROC_NOT_FOUND);
  585. return NULL;
  586. }
  587. idx = LOWORD(name) - exports->Base;
  588. } else {
  589. // search function name in list of exported names
  590. DWORD i;
  591. DWORD *nameRef = (DWORD *) (codeBase + exports->AddressOfNames);
  592. WORD *ordinal = (WORD *) (codeBase + exports->AddressOfNameOrdinals);
  593. BOOL found = FALSE;
  594. for (i=0; i<exports->NumberOfNames; i++, nameRef++, ordinal++) {
  595. if (_stricmp(name, (const char *) (codeBase + (*nameRef))) == 0) {
  596. idx = *ordinal;
  597. found = TRUE;
  598. break;
  599. }
  600. }
  601. if (!found) {
  602. // exported symbol not found
  603. SetLastError(ERROR_PROC_NOT_FOUND);
  604. return NULL;
  605. }
  606. }
  607. if (idx > exports->NumberOfFunctions) {
  608. // name <-> ordinal number don't match
  609. SetLastError(ERROR_PROC_NOT_FOUND);
  610. return NULL;
  611. }
  612. // AddressOfFunctions contains the RVAs to the "real" functions
  613. return (FARPROC)(LPVOID)(codeBase + (*(DWORD *) (codeBase + exports->AddressOfFunctions + (idx*4))));
  614. }
  615. void MemoryFreeLibrary(HMEMORYMODULE mod)
  616. {
  617. PMEMORYMODULE module = (PMEMORYMODULE)mod;
  618. if (module == NULL) {
  619. return;
  620. }
  621. if (module->initialized) {
  622. // notify library about detaching from process
  623. DllEntryProc DllEntry = (DllEntryProc)(LPVOID)(module->codeBase + module->headers->OptionalHeader.AddressOfEntryPoint);
  624. (*DllEntry)((HINSTANCE)module->codeBase, DLL_PROCESS_DETACH, 0);
  625. }
  626. if (module->modules != NULL) {
  627. // free previously opened libraries
  628. int i;
  629. for (i=0; i<module->numModules; i++) {
  630. if (module->modules[i] != NULL) {
  631. module->freeLibrary(module->modules[i], module->userdata);
  632. }
  633. }
  634. free(module->modules);
  635. }
  636. if (module->codeBase != NULL) {
  637. // release memory of library
  638. VirtualFree(module->codeBase, 0, MEM_RELEASE);
  639. }
  640. HeapFree(GetProcessHeap(), 0, module);
  641. }
  642. int MemoryCallEntryPoint(HMEMORYMODULE mod)
  643. {
  644. PMEMORYMODULE module = (PMEMORYMODULE)mod;
  645. if (module == NULL || module->isDLL || module->exeEntry == NULL || !module->isRelocated) {
  646. return -1;
  647. }
  648. return module->exeEntry();
  649. }
  650. #define DEFAULT_LANGUAGE MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL)
  651. HMEMORYRSRC MemoryFindResource(HMEMORYMODULE module, LPCTSTR name, LPCTSTR type)
  652. {
  653. return MemoryFindResourceEx(module, name, type, DEFAULT_LANGUAGE);
  654. }
  655. static PIMAGE_RESOURCE_DIRECTORY_ENTRY _MemorySearchResourceEntry(
  656. void *root,
  657. PIMAGE_RESOURCE_DIRECTORY resources,
  658. LPCTSTR key)
  659. {
  660. PIMAGE_RESOURCE_DIRECTORY_ENTRY entries = (PIMAGE_RESOURCE_DIRECTORY_ENTRY) (resources + 1);
  661. PIMAGE_RESOURCE_DIRECTORY_ENTRY result = NULL;
  662. DWORD start;
  663. DWORD end;
  664. DWORD middle;
  665. if (!IS_INTRESOURCE(key) && key[0] == TEXT('#')) {
  666. // special case: resource id given as string
  667. TCHAR *endpos = NULL;
  668. long int tmpkey = (WORD) _tcstol((TCHAR *) &key[1], &endpos, 10);
  669. if (tmpkey <= 0xffff && lstrlen(endpos) == 0) {
  670. key = MAKEINTRESOURCE(tmpkey);
  671. }
  672. }
  673. // entries are stored as ordered list of named entries,
  674. // followed by an ordered list of id entries - we can do
  675. // a binary search to find faster...
  676. if (IS_INTRESOURCE(key)) {
  677. WORD check = (WORD) (uintptr_t) key;
  678. start = resources->NumberOfNamedEntries;
  679. end = start + resources->NumberOfIdEntries;
  680. while (end > start) {
  681. WORD entryName;
  682. middle = (start + end) >> 1;
  683. entryName = (WORD) entries[middle].Name;
  684. if (check < entryName) {
  685. end = (end != middle ? middle : middle-1);
  686. } else if (check > entryName) {
  687. start = (start != middle ? middle : middle+1);
  688. } else {
  689. result = &entries[middle];
  690. break;
  691. }
  692. }
  693. } else {
  694. LPCWSTR searchKey;
  695. size_t searchKeyLen = _tcslen(key);
  696. #if defined(UNICODE)
  697. searchKey = key;
  698. #else
  699. // Resource names are always stored using 16bit characters, need to
  700. // convert string we search for.
  701. #define MAX_LOCAL_KEY_LENGTH 2048
  702. // In most cases resource names are short, so optimize for that by
  703. // using a pre-allocated array.
  704. wchar_t _searchKeySpace[MAX_LOCAL_KEY_LENGTH+1];
  705. LPWSTR _searchKey;
  706. if (searchKeyLen > MAX_LOCAL_KEY_LENGTH) {
  707. size_t _searchKeySize = (searchKeyLen + 1) * sizeof(wchar_t);
  708. _searchKey = (LPWSTR) malloc(_searchKeySize);
  709. if (_searchKey == NULL) {
  710. SetLastError(ERROR_OUTOFMEMORY);
  711. return NULL;
  712. }
  713. } else {
  714. _searchKey = &_searchKeySpace[0];
  715. }
  716. mbstowcs(_searchKey, key, searchKeyLen);
  717. _searchKey[searchKeyLen] = 0;
  718. searchKey = _searchKey;
  719. #endif
  720. start = 0;
  721. end = resources->NumberOfNamedEntries;
  722. while (end > start) {
  723. int cmp;
  724. PIMAGE_RESOURCE_DIR_STRING_U resourceString;
  725. middle = (start + end) >> 1;
  726. resourceString = (PIMAGE_RESOURCE_DIR_STRING_U) (((char *) root) + (entries[middle].Name & 0x7FFFFFFF));
  727. cmp = _wcsnicmp(searchKey, resourceString->NameString, resourceString->Length);
  728. if (cmp == 0) {
  729. // Handle partial match
  730. cmp = searchKeyLen - resourceString->Length;
  731. }
  732. if (cmp < 0) {
  733. end = (middle != end ? middle : middle-1);
  734. } else if (cmp > 0) {
  735. start = (middle != start ? middle : middle+1);
  736. } else {
  737. result = &entries[middle];
  738. break;
  739. }
  740. }
  741. #if !defined(UNICODE)
  742. if (searchKeyLen > MAX_LOCAL_KEY_LENGTH) {
  743. free(_searchKey);
  744. }
  745. #undef MAX_LOCAL_KEY_LENGTH
  746. #endif
  747. }
  748. return result;
  749. }
  750. HMEMORYRSRC MemoryFindResourceEx(HMEMORYMODULE module, LPCTSTR name, LPCTSTR type, WORD language)
  751. {
  752. unsigned char *codeBase = ((PMEMORYMODULE) module)->codeBase;
  753. PIMAGE_DATA_DIRECTORY directory = GET_HEADER_DICTIONARY((PMEMORYMODULE) module, IMAGE_DIRECTORY_ENTRY_RESOURCE);
  754. PIMAGE_RESOURCE_DIRECTORY rootResources;
  755. PIMAGE_RESOURCE_DIRECTORY nameResources;
  756. PIMAGE_RESOURCE_DIRECTORY typeResources;
  757. PIMAGE_RESOURCE_DIRECTORY_ENTRY foundType;
  758. PIMAGE_RESOURCE_DIRECTORY_ENTRY foundName;
  759. PIMAGE_RESOURCE_DIRECTORY_ENTRY foundLanguage;
  760. if (directory->Size == 0) {
  761. // no resource table found
  762. SetLastError(ERROR_RESOURCE_DATA_NOT_FOUND);
  763. return NULL;
  764. }
  765. if (language == DEFAULT_LANGUAGE) {
  766. // use language from current thread
  767. language = LANGIDFROMLCID(GetThreadLocale());
  768. }
  769. // resources are stored as three-level tree
  770. // - first node is the type
  771. // - second node is the name
  772. // - third node is the language
  773. rootResources = (PIMAGE_RESOURCE_DIRECTORY) (codeBase + directory->VirtualAddress);
  774. foundType = _MemorySearchResourceEntry(rootResources, rootResources, type);
  775. if (foundType == NULL) {
  776. SetLastError(ERROR_RESOURCE_TYPE_NOT_FOUND);
  777. return NULL;
  778. }
  779. typeResources = (PIMAGE_RESOURCE_DIRECTORY) (codeBase + directory->VirtualAddress + (foundType->OffsetToData & 0x7fffffff));
  780. foundName = _MemorySearchResourceEntry(rootResources, typeResources, name);
  781. if (foundName == NULL) {
  782. SetLastError(ERROR_RESOURCE_NAME_NOT_FOUND);
  783. return NULL;
  784. }
  785. nameResources = (PIMAGE_RESOURCE_DIRECTORY) (codeBase + directory->VirtualAddress + (foundName->OffsetToData & 0x7fffffff));
  786. foundLanguage = _MemorySearchResourceEntry(rootResources, nameResources, (LPCTSTR) (uintptr_t) language);
  787. if (foundLanguage == NULL) {
  788. // requested language not found, use first available
  789. if (nameResources->NumberOfIdEntries == 0) {
  790. SetLastError(ERROR_RESOURCE_LANG_NOT_FOUND);
  791. return NULL;
  792. }
  793. foundLanguage = (PIMAGE_RESOURCE_DIRECTORY_ENTRY) (nameResources + 1);
  794. }
  795. return (codeBase + directory->VirtualAddress + (foundLanguage->OffsetToData & 0x7fffffff));
  796. }
  797. DWORD MemorySizeofResource(HMEMORYMODULE module, HMEMORYRSRC resource)
  798. {
  799. PIMAGE_RESOURCE_DATA_ENTRY entry;
  800. UNREFERENCED_PARAMETER(module);
  801. entry = (PIMAGE_RESOURCE_DATA_ENTRY) resource;
  802. if (entry == NULL) {
  803. return 0;
  804. }
  805. return entry->Size;
  806. }
  807. LPVOID MemoryLoadResource(HMEMORYMODULE module, HMEMORYRSRC resource)
  808. {
  809. unsigned char *codeBase = ((PMEMORYMODULE) module)->codeBase;
  810. PIMAGE_RESOURCE_DATA_ENTRY entry = (PIMAGE_RESOURCE_DATA_ENTRY) resource;
  811. if (entry == NULL) {
  812. return NULL;
  813. }
  814. return codeBase + entry->OffsetToData;
  815. }
  816. int
  817. MemoryLoadString(HMEMORYMODULE module, UINT id, LPTSTR buffer, int maxsize)
  818. {
  819. return MemoryLoadStringEx(module, id, buffer, maxsize, DEFAULT_LANGUAGE);
  820. }
  821. int
  822. MemoryLoadStringEx(HMEMORYMODULE module, UINT id, LPTSTR buffer, int maxsize, WORD language)
  823. {
  824. HMEMORYRSRC resource;
  825. PIMAGE_RESOURCE_DIR_STRING_U data;
  826. DWORD size;
  827. if (maxsize == 0) {
  828. return 0;
  829. }
  830. resource = MemoryFindResourceEx(module, MAKEINTRESOURCE((id >> 4) + 1), RT_STRING, language);
  831. if (resource == NULL) {
  832. buffer[0] = 0;
  833. return 0;
  834. }
  835. data = (PIMAGE_RESOURCE_DIR_STRING_U) MemoryLoadResource(module, resource);
  836. id = id & 0x0f;
  837. while (id--) {
  838. data = (PIMAGE_RESOURCE_DIR_STRING_U) (((char *) data) + (data->Length + 1) * sizeof(WCHAR));
  839. }
  840. if (data->Length == 0) {
  841. SetLastError(ERROR_RESOURCE_NAME_NOT_FOUND);
  842. buffer[0] = 0;
  843. return 0;
  844. }
  845. size = data->Length;
  846. if (size >= (DWORD) maxsize) {
  847. size = maxsize;
  848. } else {
  849. buffer[size] = 0;
  850. }
  851. #if defined(UNICODE)
  852. wcsncpy(buffer, data->NameString, size);
  853. #else
  854. wcstombs(buffer, data->NameString, size);
  855. #endif
  856. return size;
  857. }