diff --git a/MemoryModule.c b/MemoryModule.c index 6877e61..e918e9c 100644 --- a/MemoryModule.c +++ b/MemoryModule.c @@ -1,471 +1,471 @@ -/* - * Memory DLL loading code - * Version 0.0.2 - * - * Copyright (c) 2004-2005 by Joachim Bauch / mail@joachim-bauch.de - * http://www.joachim-bauch.de - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is MemoryModule.c - * - * The Initial Developer of the Original Code is Joachim Bauch. - * - * Portions created by Joachim Bauch are Copyright (C) 2004-2005 - * Joachim Bauch. All Rights Reserved. - * - */ - -// disable warnings about pointer <-> DWORD conversions -#pragma warning( disable : 4311 4312 ) - -#include -#include -#ifdef DEBUG_OUTPUT -#include -#endif - -#include "MemoryModule.h" - -typedef struct { - PIMAGE_NT_HEADERS headers; - unsigned char *codeBase; - HMODULE *modules; - int numModules; - int initialized; -} MEMORYMODULE, *PMEMORYMODULE; - -typedef BOOL (WINAPI *DllEntryProc)(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved); - -#define GET_HEADER_DICTIONARY(module, idx) &(module)->headers->OptionalHeader.DataDirectory[idx] -#define CALCULATE_ADDRESS(base, offset) (((DWORD)(base)) + (offset)) - -#ifdef DEBUG_OUTPUT -static void -OutputLastError(const char *msg) -{ - LPVOID tmp; - char *tmpmsg; - FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&tmp, 0, NULL); - tmpmsg = (char *)LocalAlloc(LPTR, strlen(msg) + strlen(tmp) + 3); - sprintf(tmpmsg, "%s: %s", msg, tmp); - OutputDebugString(tmpmsg); - LocalFree(tmpmsg); - LocalFree(tmp); -} -#endif - -static void -CopySections(const unsigned char *data, PIMAGE_NT_HEADERS old_headers, PMEMORYMODULE module) -{ - int i, size; - unsigned char *codeBase = module->codeBase; - unsigned char *dest; - PIMAGE_SECTION_HEADER section = IMAGE_FIRST_SECTION(module->headers); - for (i=0; iheaders->FileHeader.NumberOfSections; i++, section++) - { - if (section->SizeOfRawData == 0) - { - // section doesn't contain data in the dll itself, but may define - // uninitialized data - size = old_headers->OptionalHeader.SectionAlignment; - if (size > 0) - { - dest = (unsigned char *)VirtualAlloc((unsigned char *)CALCULATE_ADDRESS(codeBase, section->VirtualAddress), - size, - MEM_COMMIT, - PAGE_READWRITE); - - section->Misc.PhysicalAddress = (DWORD)dest; - memset(dest, 0, size); - } - - // section is empty - continue; - } - - // commit memory block and copy data from dll - dest = (unsigned char *)VirtualAlloc((unsigned char *)CALCULATE_ADDRESS(codeBase, section->VirtualAddress), - section->SizeOfRawData, - MEM_COMMIT, - PAGE_READWRITE); - memcpy(dest, (unsigned char *)CALCULATE_ADDRESS(data, section->PointerToRawData), section->SizeOfRawData); - section->Misc.PhysicalAddress = (DWORD)dest; - } -} - -// Protection flags for memory pages (Executable, Readable, Writeable) -static int ProtectionFlags[2][2][2] = { - { - // not executable - {PAGE_NOACCESS, PAGE_WRITECOPY}, - {PAGE_READONLY, PAGE_READWRITE}, - }, { - // executable - {PAGE_EXECUTE, PAGE_EXECUTE_WRITECOPY}, - {PAGE_EXECUTE_READ, PAGE_EXECUTE_READWRITE}, - }, -}; - -static void -FinalizeSections(PMEMORYMODULE module) -{ - int i; - PIMAGE_SECTION_HEADER section = IMAGE_FIRST_SECTION(module->headers); - - // loop through all sections and change access flags - for (i=0; iheaders->FileHeader.NumberOfSections; i++, section++) - { - DWORD protect, oldProtect, size; - int executable = (section->Characteristics & IMAGE_SCN_MEM_EXECUTE) != 0; - int readable = (section->Characteristics & IMAGE_SCN_MEM_READ) != 0; - int writeable = (section->Characteristics & IMAGE_SCN_MEM_WRITE) != 0; - - if (section->Characteristics & IMAGE_SCN_MEM_DISCARDABLE) - { - // section is not needed any more and can safely be freed - VirtualFree((LPVOID)section->Misc.PhysicalAddress, section->SizeOfRawData, MEM_DECOMMIT); - continue; - } - - // determine protection flags based on characteristics - protect = ProtectionFlags[executable][readable][writeable]; - if (section->Characteristics & IMAGE_SCN_MEM_NOT_CACHED) - protect |= PAGE_NOCACHE; - - // determine size of region - size = section->SizeOfRawData; - if (size == 0) - { - if (section->Characteristics & IMAGE_SCN_CNT_INITIALIZED_DATA) - size = module->headers->OptionalHeader.SizeOfInitializedData; - else if (section->Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA) - size = module->headers->OptionalHeader.SizeOfUninitializedData; - } - - if (size > 0) - { - // change memory access flags - if (VirtualProtect((LPVOID)section->Misc.PhysicalAddress, section->SizeOfRawData, protect, &oldProtect) == 0) -#ifdef DEBUG_OUTPUT - OutputLastError("Error protecting memory page") -#endif - ; - } - } -} - -static void -PerformBaseRelocation(PMEMORYMODULE module, DWORD delta) -{ - DWORD i; - unsigned char *codeBase = module->codeBase; - - PIMAGE_DATA_DIRECTORY directory = GET_HEADER_DICTIONARY(module, IMAGE_DIRECTORY_ENTRY_BASERELOC); - if (directory->Size > 0) - { - PIMAGE_BASE_RELOCATION relocation = (PIMAGE_BASE_RELOCATION)CALCULATE_ADDRESS(codeBase, directory->VirtualAddress); - for (; relocation->VirtualAddress > 0; ) - { - unsigned char *dest = (unsigned char *)CALCULATE_ADDRESS(codeBase, relocation->VirtualAddress); - unsigned short *relInfo = (unsigned short *)((unsigned char *)relocation + IMAGE_SIZEOF_BASE_RELOCATION); - for (i=0; i<((relocation->SizeOfBlock-IMAGE_SIZEOF_BASE_RELOCATION) / 2); i++, relInfo++) - { - DWORD *patchAddrHL; - int type, offset; - - // the upper 4 bits define the type of relocation - type = *relInfo >> 12; - // the lower 12 bits define the offset - offset = *relInfo & 0xfff; - - switch (type) - { - case IMAGE_REL_BASED_ABSOLUTE: - // skip relocation - break; - - case IMAGE_REL_BASED_HIGHLOW: - // change complete 32 bit address - patchAddrHL = (DWORD *)CALCULATE_ADDRESS(dest, offset); - *patchAddrHL += delta; - break; - - default: - //printf("Unknown relocation: %d\n", type); - break; - } - } - - // advance to next relocation block - relocation = (PIMAGE_BASE_RELOCATION)CALCULATE_ADDRESS(relocation, relocation->SizeOfBlock); - } - } -} - -static int -BuildImportTable(PMEMORYMODULE module) -{ - int result=1; - unsigned char *codeBase = module->codeBase; - - PIMAGE_DATA_DIRECTORY directory = GET_HEADER_DICTIONARY(module, IMAGE_DIRECTORY_ENTRY_IMPORT); - if (directory->Size > 0) - { - PIMAGE_IMPORT_DESCRIPTOR importDesc = (PIMAGE_IMPORT_DESCRIPTOR)CALCULATE_ADDRESS(codeBase, directory->VirtualAddress); - for (; !IsBadReadPtr(importDesc, sizeof(IMAGE_IMPORT_DESCRIPTOR)) && importDesc->Name; importDesc++) - { - DWORD *thunkRef, *funcRef; - HMODULE handle = LoadLibrary((LPCSTR)CALCULATE_ADDRESS(codeBase, importDesc->Name)); - if (handle == INVALID_HANDLE_VALUE) - { -#if DEBUG_OUTPUT - OutputLastError("Can't load library"); -#endif - result = 0; - break; - } - - module->modules = (HMODULE *)realloc(module->modules, (module->numModules+1)*(sizeof(HMODULE))); - if (module->modules == NULL) - { - result = 0; - break; - } - - module->modules[module->numModules++] = handle; - if (importDesc->OriginalFirstThunk) - { - thunkRef = (DWORD *)CALCULATE_ADDRESS(codeBase, importDesc->OriginalFirstThunk); - funcRef = (DWORD *)CALCULATE_ADDRESS(codeBase, importDesc->FirstThunk); - } else { - // no hint table - thunkRef = (DWORD *)CALCULATE_ADDRESS(codeBase, importDesc->FirstThunk); - funcRef = (DWORD *)CALCULATE_ADDRESS(codeBase, importDesc->FirstThunk); - } - for (; *thunkRef; thunkRef++, funcRef++) - { - if IMAGE_SNAP_BY_ORDINAL(*thunkRef) - *funcRef = (DWORD)GetProcAddress(handle, (LPCSTR)IMAGE_ORDINAL(*thunkRef)); - else { - PIMAGE_IMPORT_BY_NAME thunkData = (PIMAGE_IMPORT_BY_NAME)CALCULATE_ADDRESS(codeBase, *thunkRef); - *funcRef = (DWORD)GetProcAddress(handle, (LPCSTR)&thunkData->Name); - } - if (*funcRef == 0) - { - result = 0; - break; - } - } - - if (!result) - break; - } - } - - return result; -} - -HMEMORYMODULE MemoryLoadLibrary(const void *data) -{ - PMEMORYMODULE result; - PIMAGE_DOS_HEADER dos_header; - PIMAGE_NT_HEADERS old_header; - unsigned char *code, *headers; - DWORD locationDelta; - DllEntryProc DllEntry; - BOOL successfull; - - dos_header = (PIMAGE_DOS_HEADER)data; - if (dos_header->e_magic != IMAGE_DOS_SIGNATURE) - { -#if DEBUG_OUTPUT - OutputDebugString("Not a valid executable file.\n"); -#endif - return NULL; - } - - old_header = (PIMAGE_NT_HEADERS)&((const unsigned char *)(data))[dos_header->e_lfanew]; - if (old_header->Signature != IMAGE_NT_SIGNATURE) - { -#if DEBUG_OUTPUT - OutputDebugString("No PE header found.\n"); -#endif - return NULL; - } - - // reserve memory for image of library - code = (unsigned char *)VirtualAlloc((LPVOID)(old_header->OptionalHeader.ImageBase), - old_header->OptionalHeader.SizeOfImage, - MEM_RESERVE, - PAGE_READWRITE); - - if (code == NULL) - // try to allocate memory at arbitrary position - code = (unsigned char *)VirtualAlloc(NULL, - old_header->OptionalHeader.SizeOfImage, - MEM_RESERVE, - PAGE_READWRITE); - - if (code == NULL) - { -#if DEBUG_OUTPUT - OutputLastError("Can't reserve memory"); -#endif - return NULL; - } - - result = (PMEMORYMODULE)HeapAlloc(GetProcessHeap(), 0, sizeof(MEMORYMODULE)); - result->codeBase = code; - result->numModules = 0; - result->modules = NULL; - result->initialized = 0; - - // XXX: is it correct to commit the complete memory region at once? - // calling DllEntry raises an exception if we don't... - VirtualAlloc(code, - old_header->OptionalHeader.SizeOfImage, - MEM_COMMIT, - PAGE_READWRITE); - - // commit memory for headers - headers = (unsigned char *)VirtualAlloc(code, - old_header->OptionalHeader.SizeOfHeaders, - MEM_COMMIT, - PAGE_READWRITE); - - // copy PE header to code - memcpy(headers, dos_header, dos_header->e_lfanew + old_header->OptionalHeader.SizeOfHeaders); - result->headers = (PIMAGE_NT_HEADERS)&((const unsigned char *)(headers))[dos_header->e_lfanew]; - - // update position - result->headers->OptionalHeader.ImageBase = (DWORD)code; - - // copy sections from DLL file block to new memory location - CopySections(data, old_header, result); - - // adjust base address of imported data - locationDelta = (DWORD)(code - old_header->OptionalHeader.ImageBase); - if (locationDelta != 0) - PerformBaseRelocation(result, locationDelta); - - // load required dlls and adjust function table of imports - if (!BuildImportTable(result)) - goto error; - - // mark memory pages depending on section headers and release - // sections that are marked as "discardable" - FinalizeSections(result); - - // get entry point of loaded library - if (result->headers->OptionalHeader.AddressOfEntryPoint != 0) - { - DllEntry = (DllEntryProc)CALCULATE_ADDRESS(code, result->headers->OptionalHeader.AddressOfEntryPoint); - if (DllEntry == 0) - { -#if DEBUG_OUTPUT - OutputDebugString("Library has no entry point.\n"); -#endif - goto error; - } - - // notify library about attaching to process - successfull = (*DllEntry)((HINSTANCE)code, DLL_PROCESS_ATTACH, 0); - if (!successfull) - { -#if DEBUG_OUTPUT - OutputDebugString("Can't attach library.\n"); -#endif - goto error; - } - result->initialized = 1; - } - - return (HMEMORYMODULE)result; - -error: - // cleanup - MemoryFreeLibrary(result); - return NULL; -} - -FARPROC MemoryGetProcAddress(HMEMORYMODULE module, const char *name) -{ - unsigned char *codeBase = ((PMEMORYMODULE)module)->codeBase; - int idx=-1; - DWORD i, *nameRef; - WORD *ordinal; - PIMAGE_EXPORT_DIRECTORY exports; - PIMAGE_DATA_DIRECTORY directory = GET_HEADER_DICTIONARY((PMEMORYMODULE)module, IMAGE_DIRECTORY_ENTRY_EXPORT); - if (directory->Size == 0) - // no export table found - return NULL; - - exports = (PIMAGE_EXPORT_DIRECTORY)CALCULATE_ADDRESS(codeBase, directory->VirtualAddress); - if (exports->NumberOfNames == 0 || exports->NumberOfFunctions == 0) - // DLL doesn't export anything - return NULL; - - // search function name in list of exported names - nameRef = (DWORD *)CALCULATE_ADDRESS(codeBase, exports->AddressOfNames); - ordinal = (WORD *)CALCULATE_ADDRESS(codeBase, exports->AddressOfNameOrdinals); - for (i=0; iNumberOfNames; i++, nameRef++, ordinal++) - if (stricmp(name, (const char *)CALCULATE_ADDRESS(codeBase, *nameRef)) == 0) - { - idx = *ordinal; - break; - } - - if (idx == -1) - // exported symbol not found - return NULL; - - if ((DWORD)idx > exports->NumberOfFunctions) - // name <-> ordinal number don't match - return NULL; - - // AddressOfFunctions contains the RVAs to the "real" functions - return (FARPROC)CALCULATE_ADDRESS(codeBase, *(DWORD *)CALCULATE_ADDRESS(codeBase, exports->AddressOfFunctions + (idx*4))); -} - -void MemoryFreeLibrary(HMEMORYMODULE mod) -{ - int i; - PMEMORYMODULE module = (PMEMORYMODULE)mod; - - if (module != NULL) - { - if (module->initialized != 0) - { - // notify library about detaching from process - DllEntryProc DllEntry = (DllEntryProc)CALCULATE_ADDRESS(module->codeBase, module->headers->OptionalHeader.AddressOfEntryPoint); - (*DllEntry)((HINSTANCE)module->codeBase, DLL_PROCESS_DETACH, 0); - module->initialized = 0; - } - - if (module->modules != NULL) - { - // free previously opened libraries - for (i=0; inumModules; i++) - if (module->modules[i] != INVALID_HANDLE_VALUE) - FreeLibrary(module->modules[i]); - - free(module->modules); - } - - if (module->codeBase != NULL) - // release memory of library - VirtualFree(module->codeBase, 0, MEM_RELEASE); - - HeapFree(GetProcessHeap(), 0, module); - } -} +/* + * Memory DLL loading code + * Version 0.0.2 + * + * Copyright (c) 2004-2005 by Joachim Bauch / mail@joachim-bauch.de + * http://www.joachim-bauch.de + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is MemoryModule.c + * + * The Initial Developer of the Original Code is Joachim Bauch. + * + * Portions created by Joachim Bauch are Copyright (C) 2004-2005 + * Joachim Bauch. All Rights Reserved. + * + */ + +// disable warnings about pointer <-> DWORD conversions +#pragma warning( disable : 4311 4312 ) + +#include +#include +#ifdef DEBUG_OUTPUT +#include +#endif + +#include "MemoryModule.h" + +typedef struct { + PIMAGE_NT_HEADERS headers; + unsigned char *codeBase; + HMODULE *modules; + int numModules; + int initialized; +} MEMORYMODULE, *PMEMORYMODULE; + +typedef BOOL (WINAPI *DllEntryProc)(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved); + +#define GET_HEADER_DICTIONARY(module, idx) &(module)->headers->OptionalHeader.DataDirectory[idx] +#define CALCULATE_ADDRESS(base, offset) (((DWORD)(base)) + (offset)) + +#ifdef DEBUG_OUTPUT +static void +OutputLastError(const char *msg) +{ + LPVOID tmp; + char *tmpmsg; + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&tmp, 0, NULL); + tmpmsg = (char *)LocalAlloc(LPTR, strlen(msg) + strlen(tmp) + 3); + sprintf(tmpmsg, "%s: %s", msg, tmp); + OutputDebugString(tmpmsg); + LocalFree(tmpmsg); + LocalFree(tmp); +} +#endif + +static void +CopySections(const unsigned char *data, PIMAGE_NT_HEADERS old_headers, PMEMORYMODULE module) +{ + int i, size; + unsigned char *codeBase = module->codeBase; + unsigned char *dest; + PIMAGE_SECTION_HEADER section = IMAGE_FIRST_SECTION(module->headers); + for (i=0; iheaders->FileHeader.NumberOfSections; i++, section++) + { + if (section->SizeOfRawData == 0) + { + // section doesn't contain data in the dll itself, but may define + // uninitialized data + size = old_headers->OptionalHeader.SectionAlignment; + if (size > 0) + { + dest = (unsigned char *)VirtualAlloc((unsigned char *)CALCULATE_ADDRESS(codeBase, section->VirtualAddress), + size, + MEM_COMMIT, + PAGE_READWRITE); + + section->Misc.PhysicalAddress = (DWORD)dest; + memset(dest, 0, size); + } + + // section is empty + continue; + } + + // commit memory block and copy data from dll + dest = (unsigned char *)VirtualAlloc((unsigned char *)CALCULATE_ADDRESS(codeBase, section->VirtualAddress), + section->SizeOfRawData, + MEM_COMMIT, + PAGE_READWRITE); + memcpy(dest, (unsigned char *)CALCULATE_ADDRESS(data, section->PointerToRawData), section->SizeOfRawData); + section->Misc.PhysicalAddress = (DWORD)dest; + } +} + +// Protection flags for memory pages (Executable, Readable, Writeable) +static int ProtectionFlags[2][2][2] = { + { + // not executable + {PAGE_NOACCESS, PAGE_WRITECOPY}, + {PAGE_READONLY, PAGE_READWRITE}, + }, { + // executable + {PAGE_EXECUTE, PAGE_EXECUTE_WRITECOPY}, + {PAGE_EXECUTE_READ, PAGE_EXECUTE_READWRITE}, + }, +}; + +static void +FinalizeSections(PMEMORYMODULE module) +{ + int i; + PIMAGE_SECTION_HEADER section = IMAGE_FIRST_SECTION(module->headers); + + // loop through all sections and change access flags + for (i=0; iheaders->FileHeader.NumberOfSections; i++, section++) + { + DWORD protect, oldProtect, size; + int executable = (section->Characteristics & IMAGE_SCN_MEM_EXECUTE) != 0; + int readable = (section->Characteristics & IMAGE_SCN_MEM_READ) != 0; + int writeable = (section->Characteristics & IMAGE_SCN_MEM_WRITE) != 0; + + if (section->Characteristics & IMAGE_SCN_MEM_DISCARDABLE) + { + // section is not needed any more and can safely be freed + VirtualFree((LPVOID)section->Misc.PhysicalAddress, section->SizeOfRawData, MEM_DECOMMIT); + continue; + } + + // determine protection flags based on characteristics + protect = ProtectionFlags[executable][readable][writeable]; + if (section->Characteristics & IMAGE_SCN_MEM_NOT_CACHED) + protect |= PAGE_NOCACHE; + + // determine size of region + size = section->SizeOfRawData; + if (size == 0) + { + if (section->Characteristics & IMAGE_SCN_CNT_INITIALIZED_DATA) + size = module->headers->OptionalHeader.SizeOfInitializedData; + else if (section->Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA) + size = module->headers->OptionalHeader.SizeOfUninitializedData; + } + + if (size > 0) + { + // change memory access flags + if (VirtualProtect((LPVOID)section->Misc.PhysicalAddress, section->SizeOfRawData, protect, &oldProtect) == 0) +#ifdef DEBUG_OUTPUT + OutputLastError("Error protecting memory page") +#endif + ; + } + } +} + +static void +PerformBaseRelocation(PMEMORYMODULE module, DWORD delta) +{ + DWORD i; + unsigned char *codeBase = module->codeBase; + + PIMAGE_DATA_DIRECTORY directory = GET_HEADER_DICTIONARY(module, IMAGE_DIRECTORY_ENTRY_BASERELOC); + if (directory->Size > 0) + { + PIMAGE_BASE_RELOCATION relocation = (PIMAGE_BASE_RELOCATION)CALCULATE_ADDRESS(codeBase, directory->VirtualAddress); + for (; relocation->VirtualAddress > 0; ) + { + unsigned char *dest = (unsigned char *)CALCULATE_ADDRESS(codeBase, relocation->VirtualAddress); + unsigned short *relInfo = (unsigned short *)((unsigned char *)relocation + IMAGE_SIZEOF_BASE_RELOCATION); + for (i=0; i<((relocation->SizeOfBlock-IMAGE_SIZEOF_BASE_RELOCATION) / 2); i++, relInfo++) + { + DWORD *patchAddrHL; + int type, offset; + + // the upper 4 bits define the type of relocation + type = *relInfo >> 12; + // the lower 12 bits define the offset + offset = *relInfo & 0xfff; + + switch (type) + { + case IMAGE_REL_BASED_ABSOLUTE: + // skip relocation + break; + + case IMAGE_REL_BASED_HIGHLOW: + // change complete 32 bit address + patchAddrHL = (DWORD *)CALCULATE_ADDRESS(dest, offset); + *patchAddrHL += delta; + break; + + default: + //printf("Unknown relocation: %d\n", type); + break; + } + } + + // advance to next relocation block + relocation = (PIMAGE_BASE_RELOCATION)CALCULATE_ADDRESS(relocation, relocation->SizeOfBlock); + } + } +} + +static int +BuildImportTable(PMEMORYMODULE module) +{ + int result=1; + unsigned char *codeBase = module->codeBase; + + PIMAGE_DATA_DIRECTORY directory = GET_HEADER_DICTIONARY(module, IMAGE_DIRECTORY_ENTRY_IMPORT); + if (directory->Size > 0) + { + PIMAGE_IMPORT_DESCRIPTOR importDesc = (PIMAGE_IMPORT_DESCRIPTOR)CALCULATE_ADDRESS(codeBase, directory->VirtualAddress); + for (; !IsBadReadPtr(importDesc, sizeof(IMAGE_IMPORT_DESCRIPTOR)) && importDesc->Name; importDesc++) + { + DWORD *thunkRef, *funcRef; + HMODULE handle = LoadLibrary((LPCSTR)CALCULATE_ADDRESS(codeBase, importDesc->Name)); + if (handle == INVALID_HANDLE_VALUE) + { +#if DEBUG_OUTPUT + OutputLastError("Can't load library"); +#endif + result = 0; + break; + } + + module->modules = (HMODULE *)realloc(module->modules, (module->numModules+1)*(sizeof(HMODULE))); + if (module->modules == NULL) + { + result = 0; + break; + } + + module->modules[module->numModules++] = handle; + if (importDesc->OriginalFirstThunk) + { + thunkRef = (DWORD *)CALCULATE_ADDRESS(codeBase, importDesc->OriginalFirstThunk); + funcRef = (DWORD *)CALCULATE_ADDRESS(codeBase, importDesc->FirstThunk); + } else { + // no hint table + thunkRef = (DWORD *)CALCULATE_ADDRESS(codeBase, importDesc->FirstThunk); + funcRef = (DWORD *)CALCULATE_ADDRESS(codeBase, importDesc->FirstThunk); + } + for (; *thunkRef; thunkRef++, funcRef++) + { + if IMAGE_SNAP_BY_ORDINAL(*thunkRef) + *funcRef = (DWORD)GetProcAddress(handle, (LPCSTR)IMAGE_ORDINAL(*thunkRef)); + else { + PIMAGE_IMPORT_BY_NAME thunkData = (PIMAGE_IMPORT_BY_NAME)CALCULATE_ADDRESS(codeBase, *thunkRef); + *funcRef = (DWORD)GetProcAddress(handle, (LPCSTR)&thunkData->Name); + } + if (*funcRef == 0) + { + result = 0; + break; + } + } + + if (!result) + break; + } + } + + return result; +} + +HMEMORYMODULE MemoryLoadLibrary(const void *data) +{ + PMEMORYMODULE result; + PIMAGE_DOS_HEADER dos_header; + PIMAGE_NT_HEADERS old_header; + unsigned char *code, *headers; + DWORD locationDelta; + DllEntryProc DllEntry; + BOOL successfull; + + dos_header = (PIMAGE_DOS_HEADER)data; + if (dos_header->e_magic != IMAGE_DOS_SIGNATURE) + { +#if DEBUG_OUTPUT + OutputDebugString("Not a valid executable file.\n"); +#endif + return NULL; + } + + old_header = (PIMAGE_NT_HEADERS)&((const unsigned char *)(data))[dos_header->e_lfanew]; + if (old_header->Signature != IMAGE_NT_SIGNATURE) + { +#if DEBUG_OUTPUT + OutputDebugString("No PE header found.\n"); +#endif + return NULL; + } + + // reserve memory for image of library + code = (unsigned char *)VirtualAlloc((LPVOID)(old_header->OptionalHeader.ImageBase), + old_header->OptionalHeader.SizeOfImage, + MEM_RESERVE, + PAGE_READWRITE); + + if (code == NULL) + // try to allocate memory at arbitrary position + code = (unsigned char *)VirtualAlloc(NULL, + old_header->OptionalHeader.SizeOfImage, + MEM_RESERVE, + PAGE_READWRITE); + + if (code == NULL) + { +#if DEBUG_OUTPUT + OutputLastError("Can't reserve memory"); +#endif + return NULL; + } + + result = (PMEMORYMODULE)HeapAlloc(GetProcessHeap(), 0, sizeof(MEMORYMODULE)); + result->codeBase = code; + result->numModules = 0; + result->modules = NULL; + result->initialized = 0; + + // XXX: is it correct to commit the complete memory region at once? + // calling DllEntry raises an exception if we don't... + VirtualAlloc(code, + old_header->OptionalHeader.SizeOfImage, + MEM_COMMIT, + PAGE_READWRITE); + + // commit memory for headers + headers = (unsigned char *)VirtualAlloc(code, + old_header->OptionalHeader.SizeOfHeaders, + MEM_COMMIT, + PAGE_READWRITE); + + // copy PE header to code + memcpy(headers, dos_header, dos_header->e_lfanew + old_header->OptionalHeader.SizeOfHeaders); + result->headers = (PIMAGE_NT_HEADERS)&((const unsigned char *)(headers))[dos_header->e_lfanew]; + + // update position + result->headers->OptionalHeader.ImageBase = (DWORD)code; + + // copy sections from DLL file block to new memory location + CopySections(data, old_header, result); + + // adjust base address of imported data + locationDelta = (DWORD)(code - old_header->OptionalHeader.ImageBase); + if (locationDelta != 0) + PerformBaseRelocation(result, locationDelta); + + // load required dlls and adjust function table of imports + if (!BuildImportTable(result)) + goto error; + + // mark memory pages depending on section headers and release + // sections that are marked as "discardable" + FinalizeSections(result); + + // get entry point of loaded library + if (result->headers->OptionalHeader.AddressOfEntryPoint != 0) + { + DllEntry = (DllEntryProc)CALCULATE_ADDRESS(code, result->headers->OptionalHeader.AddressOfEntryPoint); + if (DllEntry == 0) + { +#if DEBUG_OUTPUT + OutputDebugString("Library has no entry point.\n"); +#endif + goto error; + } + + // notify library about attaching to process + successfull = (*DllEntry)((HINSTANCE)code, DLL_PROCESS_ATTACH, 0); + if (!successfull) + { +#if DEBUG_OUTPUT + OutputDebugString("Can't attach library.\n"); +#endif + goto error; + } + result->initialized = 1; + } + + return (HMEMORYMODULE)result; + +error: + // cleanup + MemoryFreeLibrary(result); + return NULL; +} + +FARPROC MemoryGetProcAddress(HMEMORYMODULE module, const char *name) +{ + unsigned char *codeBase = ((PMEMORYMODULE)module)->codeBase; + int idx=-1; + DWORD i, *nameRef; + WORD *ordinal; + PIMAGE_EXPORT_DIRECTORY exports; + PIMAGE_DATA_DIRECTORY directory = GET_HEADER_DICTIONARY((PMEMORYMODULE)module, IMAGE_DIRECTORY_ENTRY_EXPORT); + if (directory->Size == 0) + // no export table found + return NULL; + + exports = (PIMAGE_EXPORT_DIRECTORY)CALCULATE_ADDRESS(codeBase, directory->VirtualAddress); + if (exports->NumberOfNames == 0 || exports->NumberOfFunctions == 0) + // DLL doesn't export anything + return NULL; + + // search function name in list of exported names + nameRef = (DWORD *)CALCULATE_ADDRESS(codeBase, exports->AddressOfNames); + ordinal = (WORD *)CALCULATE_ADDRESS(codeBase, exports->AddressOfNameOrdinals); + for (i=0; iNumberOfNames; i++, nameRef++, ordinal++) + if (stricmp(name, (const char *)CALCULATE_ADDRESS(codeBase, *nameRef)) == 0) + { + idx = *ordinal; + break; + } + + if (idx == -1) + // exported symbol not found + return NULL; + + if ((DWORD)idx > exports->NumberOfFunctions) + // name <-> ordinal number don't match + return NULL; + + // AddressOfFunctions contains the RVAs to the "real" functions + return (FARPROC)CALCULATE_ADDRESS(codeBase, *(DWORD *)CALCULATE_ADDRESS(codeBase, exports->AddressOfFunctions + (idx*4))); +} + +void MemoryFreeLibrary(HMEMORYMODULE mod) +{ + int i; + PMEMORYMODULE module = (PMEMORYMODULE)mod; + + if (module != NULL) + { + if (module->initialized != 0) + { + // notify library about detaching from process + DllEntryProc DllEntry = (DllEntryProc)CALCULATE_ADDRESS(module->codeBase, module->headers->OptionalHeader.AddressOfEntryPoint); + (*DllEntry)((HINSTANCE)module->codeBase, DLL_PROCESS_DETACH, 0); + module->initialized = 0; + } + + if (module->modules != NULL) + { + // free previously opened libraries + for (i=0; inumModules; i++) + if (module->modules[i] != INVALID_HANDLE_VALUE) + FreeLibrary(module->modules[i]); + + free(module->modules); + } + + if (module->codeBase != NULL) + // release memory of library + VirtualFree(module->codeBase, 0, MEM_RELEASE); + + HeapFree(GetProcessHeap(), 0, module); + } +} diff --git a/MemoryModule.h b/MemoryModule.h index f314e5d..acece0d 100644 --- a/MemoryModule.h +++ b/MemoryModule.h @@ -1,48 +1,48 @@ -/* - * Memory DLL loading code - * Version 0.0.2 - * - * Copyright (c) 2004-2005 by Joachim Bauch / mail@joachim-bauch.de - * http://www.joachim-bauch.de - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is MemoryModule.h - * - * The Initial Developer of the Original Code is Joachim Bauch. - * - * Portions created by Joachim Bauch are Copyright (C) 2004-2005 - * Joachim Bauch. All Rights Reserved. - * - */ - -#ifndef __MEMORY_MODULE_HEADER -#define __MEMORY_MODULE_HEADER - -#include - -typedef void *HMEMORYMODULE; - -#ifdef __cplusplus -extern "C" { -#endif - -HMEMORYMODULE MemoryLoadLibrary(const void *); - -FARPROC MemoryGetProcAddress(HMEMORYMODULE, const char *); - -void MemoryFreeLibrary(HMEMORYMODULE); - -#ifdef __cplusplus -} -#endif - -#endif // __MEMORY_MODULE_HEADER +/* + * Memory DLL loading code + * Version 0.0.2 + * + * Copyright (c) 2004-2005 by Joachim Bauch / mail@joachim-bauch.de + * http://www.joachim-bauch.de + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is MemoryModule.h + * + * The Initial Developer of the Original Code is Joachim Bauch. + * + * Portions created by Joachim Bauch are Copyright (C) 2004-2005 + * Joachim Bauch. All Rights Reserved. + * + */ + +#ifndef __MEMORY_MODULE_HEADER +#define __MEMORY_MODULE_HEADER + +#include + +typedef void *HMEMORYMODULE; + +#ifdef __cplusplus +extern "C" { +#endif + +HMEMORYMODULE MemoryLoadLibrary(const void *); + +FARPROC MemoryGetProcAddress(HMEMORYMODULE, const char *); + +void MemoryFreeLibrary(HMEMORYMODULE); + +#ifdef __cplusplus +} +#endif + +#endif // __MEMORY_MODULE_HEADER diff --git a/doc/readme.txt b/doc/readme.txt index 50e7c83..a6cb906 100644 --- a/doc/readme.txt +++ b/doc/readme.txt @@ -1,554 +1,554 @@ -:author: Joachim Bauch -:contact: mail@joachim-bauch.de -:copyright: `Creative Commons License (by-sa)`__ - -__ http://creativecommons.org/licenses/by-sa/2.5/ - - -.. contents:: - - -Overview -========= - -The default windows API functions to load external libraries into a program -(LoadLibrary, LoadLibraryEx) only work with files on the filesystem. It's -therefore impossible to load a DLL from memory. -But sometimes, you need exactly this functionality (e.g. you don't want to -distribute a lot of files or want to make disassembling harder). Common -workarounds for this problems are to write the DLL into a temporary file -first and import it from there. When the program terminates, the temporary -file gets deleted. - -In this tutorial, I will describe first, how DLL files are structured and -will present some code that can be used to load a DLL completely from memory - -without storing on the disk first. - - -Windows executables - the PE format -==================================== - -Most windows binaries that can contain executable code (.exe, .dll, .sys) -share a common file format that consists of the following parts: - -+----------------+ -| DOS header | -| | -| DOS stub | -+----------------+ -| PE header | -+----------------+ -| Section header | -+----------------+ -| Section 1 | -+----------------+ -| Section 2 | -+----------------+ -| . . . | -+----------------+ -| Section n | -+----------------+ - -All structures given below can be found in the header file `winnt.h`. - - -DOS header / stub ------------------- - -The DOS header is only used for backwards compatibility. It precedes the DOS -stub that normally just displays an error message about the program not being -able to be run from DOS mode. - -Microsoft defines the DOS header as follows:: - - typedef struct _IMAGE_DOS_HEADER { // DOS .EXE header - WORD e_magic; // Magic number - WORD e_cblp; // Bytes on last page of file - WORD e_cp; // Pages in file - WORD e_crlc; // Relocations - WORD e_cparhdr; // Size of header in paragraphs - WORD e_minalloc; // Minimum extra paragraphs needed - WORD e_maxalloc; // Maximum extra paragraphs needed - WORD e_ss; // Initial (relative) SS value - WORD e_sp; // Initial SP value - WORD e_csum; // Checksum - WORD e_ip; // Initial IP value - WORD e_cs; // Initial (relative) CS value - WORD e_lfarlc; // File address of relocation table - WORD e_ovno; // Overlay number - WORD e_res[4]; // Reserved words - WORD e_oemid; // OEM identifier (for e_oeminfo) - WORD e_oeminfo; // OEM information; e_oemid specific - WORD e_res2[10]; // Reserved words - LONG e_lfanew; // File address of new exe header - } IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER; - - -PE header ----------- - -The PE header contains informations about the different sections inside the -executable that are used to store code and data or to define imports from other -libraries or exports this libraries provides. - -It's defined as follows:: - - typedef struct _IMAGE_NT_HEADERS { - DWORD Signature; - IMAGE_FILE_HEADER FileHeader; - IMAGE_OPTIONAL_HEADER32 OptionalHeader; - } IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32; - -The `FileHeader` describes the *physical* format of the file, i.e. contents, informations -about symbols, etc:: - - typedef struct _IMAGE_FILE_HEADER { - WORD Machine; - WORD NumberOfSections; - DWORD TimeDateStamp; - DWORD PointerToSymbolTable; - DWORD NumberOfSymbols; - WORD SizeOfOptionalHeader; - WORD Characteristics; - } IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER; - -.. _OptionalHeader: - -The `OptionalHeader` contains informations about the *logical* format of the library, -including required OS version, memory requirements and entry points:: - - typedef struct _IMAGE_OPTIONAL_HEADER { - // - // Standard fields. - // - - WORD Magic; - BYTE MajorLinkerVersion; - BYTE MinorLinkerVersion; - DWORD SizeOfCode; - DWORD SizeOfInitializedData; - DWORD SizeOfUninitializedData; - DWORD AddressOfEntryPoint; - DWORD BaseOfCode; - DWORD BaseOfData; - - // - // NT additional fields. - // - - DWORD ImageBase; - DWORD SectionAlignment; - DWORD FileAlignment; - WORD MajorOperatingSystemVersion; - WORD MinorOperatingSystemVersion; - WORD MajorImageVersion; - WORD MinorImageVersion; - WORD MajorSubsystemVersion; - WORD MinorSubsystemVersion; - DWORD Win32VersionValue; - DWORD SizeOfImage; - DWORD SizeOfHeaders; - DWORD CheckSum; - WORD Subsystem; - WORD DllCharacteristics; - DWORD SizeOfStackReserve; - DWORD SizeOfStackCommit; - DWORD SizeOfHeapReserve; - DWORD SizeOfHeapCommit; - DWORD LoaderFlags; - DWORD NumberOfRvaAndSizes; - IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; - } IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32; - -.. _DataDirectory: - -The `DataDirectory` contains 16 (`IMAGE_NUMBEROF_DIRECTORY_ENTRIES`) entries -defining the logical components of the library: - -===== ========================== -Index Description -===== ========================== -0 Exported functions ------ -------------------------- -1 Imported functions ------ -------------------------- -2 Resources ------ -------------------------- -3 Exception informations ------ -------------------------- -4 Security informations ------ -------------------------- -5 Base relocation table ------ -------------------------- -6 Debug informations ------ -------------------------- -7 Architecture specific data ------ -------------------------- -8 Global pointer ------ -------------------------- -9 Thread local storage ------ -------------------------- -10 Load configuration ------ -------------------------- -11 Bound imports ------ -------------------------- -12 Import address table ------ -------------------------- -13 Delay load imports ------ -------------------------- -14 COM runtime descriptor -===== ========================== - -For importing the DLL we only need the entries describing the imports and the -base relocation table. In order to provide access to the exported functions, -the exports entry is required. - - -Section header ---------------- - -The section header is stored after the OptionalHeader_ structure in the PE -header. Microsoft provides the macro `IMAGE_FIRST_SECTION` to get the start -address based on the PE header. - -Actually, the section header is a list of informations about each section in -the file:: - - typedef struct _IMAGE_SECTION_HEADER { - BYTE Name[IMAGE_SIZEOF_SHORT_NAME]; - union { - DWORD PhysicalAddress; - DWORD VirtualSize; - } Misc; - DWORD VirtualAddress; - DWORD SizeOfRawData; - DWORD PointerToRawData; - DWORD PointerToRelocations; - DWORD PointerToLinenumbers; - WORD NumberOfRelocations; - WORD NumberOfLinenumbers; - DWORD Characteristics; - } IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER; - -A section can contain code, data, relocation informations, resources, export or -import definitions, etc. - - -Loading the library -==================== - -To emulate the PE loader, we must first understand, which steps are neccessary -to load the file to memory and prepare the structures so they can be called from -other programs. - -When issuing the API call `LoadLibrary`, Windows basically performs these tasks: - -1. Open the given file and check the DOS and PE headers. - -2. Try to allocate a memory block of `PEHeader.OptionalHeader.SizeOfImage` bytes - at position `PEHeader.OptionalHeader.ImageBase`. - -3. Parse section headers and copy sections to their addresses. The destination - address for each section, relative to the base of the allocated memory block, - is stored in the `VirtualAddress` attribute of the `IMAGE_SECTION_HEADER` - structure. - -4. If the allocated memory block differs from `ImageBase`, various references in - the code and/or data sections must be adjusted. This is called *Base - relocation*. - -5. The required imports for the library must be resolved by loading the - corresponding libraries. - -6. The memory regions of the different sections must be protected depending on - the section's characteristics. Some sections are marked as *discardable* - and therefore can be safely freed at this point. These sections normally - contain temporary data that is only needed during the import, like the - informations for the base relocation. - -7. Now the library is loaded completely. It must be notified about this by - calling the entry point using the flag `DLL_PROCESS_ATTACH`. - -In the following paragraphs, each step is described. - - -Allocate memory ----------------- - -All memory required for the library must be reserved / allocated using -`VirtualAlloc`, as Windows provides functions to protect these memory blocks. -This is required to restrict access to the memory, like blocking write access -to the code or constant data. - -The OptionalHeader_ structure defines the size of the required memory block -for the library. It must be reserved at the address specified by `ImageBase` -if possible:: - - memory = VirtualAlloc((LPVOID)(PEHeader->OptionalHeader.ImageBase), - PEHeader->OptionalHeader.SizeOfImage, - MEM_RESERVE, - PAGE_READWRITE); - -If the reserved memory differs from the address given in `ImageBase`, base -relocation as described below must be done. - - -Copy sections --------------- - -Once the memory has been reserved, the file contents can be copied to the -system. The section header must get evaluated in order to determine the -position in the file and the target area in memory. - -Before copying the data, the memory block must get committed:: - - dest = VirtualAlloc(baseAddress + section->VirtualAddress, - section->SizeOfRawData, - MEM_COMMIT, - PAGE_READWRITE); - -Sections without data in the file (like data sections for the used variables) -have a `SizeOfRawData` of `0`, so you can use the `SizeOfInitializedData` -or `SizeOfUninitializedData` of the OptionalHeader_. Which one must get -choosen depending on the bit flags `IMAGE_SCN_CNT_INITIALIZED_DATA` and -`IMAGE_SCN_CNT_UNINITIALIZED_DATA` that may be set in the section`s -characteristics. - - -Base relocation ----------------- - -All memory addresses in the code / data sections of a library are stored relative -to the address defined by `ImageBase` in the OptionalHeader_. If the library -can't be imported to this memory address, the references must get adjusted -=> *relocated*. The file format helps for this by storing informations about -all these references in the base relocation table, which can be found in the -directory entry 5 of the DataDirectory_ in the OptionalHeader_. - -This table consists of a series of this structure - -:: - - typedef struct _IMAGE_BASE_RELOCATION { - DWORD VirtualAddress; - DWORD SizeOfBlock; - } IMAGE_BASE_RELOCATION; - -It contains `(SizeOfBlock - IMAGE_SIZEOF_BASE_RELOCATION) / 2` entries of 16 bits -each. The upper 4 bits define the type of relocation, the lower 12 bits define -the offset relative to the `VirtualAddress`. - -The only types that seem to be used in DLLs are - -IMAGE_REL_BASED_ABSOLUTE - No operation relocation. Used for padding. -IMAGE_REL_BASED_HIGHLOW - Add the delta between the `ImageBase` and the allocated memory block to the - 32 bits found at the offset. - - -Resolve imports ----------------- - -The directory entry 1 of the DataDirectory_ in the OptionalHeader_ specifies -a list of libraries to import symbols from. Each entry in this list is defined -as follows:: - - typedef struct _IMAGE_IMPORT_DESCRIPTOR { - union { - DWORD Characteristics; // 0 for terminating null import descriptor - DWORD OriginalFirstThunk; // RVA to original unbound IAT (PIMAGE_THUNK_DATA) - }; - DWORD TimeDateStamp; // 0 if not bound, - // -1 if bound, and real date\time stamp - // in IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (new BIND) - // O.W. date/time stamp of DLL bound to (Old BIND) - - DWORD ForwarderChain; // -1 if no forwarders - DWORD Name; - DWORD FirstThunk; // RVA to IAT (if bound this IAT has actual addresses) - } IMAGE_IMPORT_DESCRIPTOR; - -The `Name` entry describes the offset to the NULL-terminated string of the library -name (e.g. `KERNEL32.DLL`). The `OriginalFirstThunk` entry points to a list -of references to the function names to import from the external library. -`FirstThunk` points to a list of addresses that gets filled with pointers to -the imported symbols. - -When we resolve the imports, we walk both lists in parallel, import the function -defined by the name in the first list and store the pointer to the symbol in the -second list:: - - nameRef = (DWORD *)(baseAddress + importDesc->OriginalFirstThunk); - symbolRef = (DWORD *)(baseAddress + importDesc->FirstThunk); - for (; *nameRef; nameRef++, symbolRef++) - { - PIMAGE_IMPORT_BY_NAME thunkData = (PIMAGE_IMPORT_BY_NAME)(codeBase + *nameRef); - *symbolRef = (DWORD)GetProcAddress(handle, (LPCSTR)&thunkData->Name); - if (*funcRef == 0) - { - handleImportError(); - return; - } - } - - -Protect memory ---------------- - -Every section specifies permission flags in it's `Characteristics` entry. -These flags can be one or a combination of - -IMAGE_SCN_MEM_EXECUTE - The section contains data that can be executed. - -IMAGE_SCN_MEM_READ - The section contains data that is readable. - -IMAGE_SCN_MEM_WRITE - The section contains data that is writeable. - -These flags must get mapped to the protection flags - -- PAGE_NOACCESS -- PAGE_WRITECOPY -- PAGE_READONLY -- PAGE_READWRITE -- PAGE_EXECUTE -- PAGE_EXECUTE_WRITECOPY -- PAGE_EXECUTE_READ -- PAGE_EXECUTE_READWRITE - -Now, the function `VirtualProtect` can be used to limit access to the memory. -If the program tries to access it in a unauthorized way, an exception gets -raised by Windows. - -In addition the section flags above, the following can be added: - -IMAGE_SCN_MEM_DISCARDABLE - The data in this section can be freed after the import. Usually this is - specified for relocation data. - -IMAGE_SCN_MEM_NOT_CACHED - The data in this section must not get cached by Windows. Add the bit - flag `PAGE_NOCACHE` to the protection flags above. - - -Notify library ---------------- - -The last thing to do is to call the DLL entry point (defined by -`AddressOfEntryPoint`) and so notifying the library about being attached -to a process. - -The function at the entry point is defined as - -:: - - typedef BOOL (WINAPI *DllEntryProc)(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved); - -So the last code we need to execute is - -:: - - DllEntryProc entry = (DllEntryProc)(baseAddress + PEHeader->OptionalHeader.AddressOfEntryPoint); - (*entry)((HINSTANCE)baseAddress, DLL_PROCESS_ATTACH, 0); - -Afterwards we can use the exported functions as with any normal library. - - -Exported functions -=================== - -If you want to access the functions that are exported by the library, you need to find the entry -point to a symbol, i.e. the name of the function to call. - -The directory entry 0 of the DataDirectory_ in the OptionalHeader_ contains informations about -the exported functions. It's defined as follows:: - - typedef struct _IMAGE_EXPORT_DIRECTORY { - DWORD Characteristics; - DWORD TimeDateStamp; - WORD MajorVersion; - WORD MinorVersion; - DWORD Name; - DWORD Base; - DWORD NumberOfFunctions; - DWORD NumberOfNames; - DWORD AddressOfFunctions; // RVA from base of image - DWORD AddressOfNames; // RVA from base of image - DWORD AddressOfNameOrdinals; // RVA from base of image - } IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY; - -First thing to do, is to map the name of the function to the ordinal number of the exported -symbol. Therefore, just walk the arrays defined by `AddressOfNames` and `AddressOfNameOrdinals` -parallel until you found the required name. - -Now you can use the ordinal number to read the address by evaluating the n-th element of the -`AddressOfFunctions` array. - - -Freeing the library -==================== - -To free the custom loaded library, perform the steps - -- Call entry point to notify library about being detached:: - - DllEntryProc entry = (DllEntryProc)(baseAddress + PEHeader->OptionalHeader.AddressOfEntryPoint); - (*entry)((HINSTANCE)baseAddress, DLL_PROCESS_ATTACH, 0); - -- Free external libraries used to resolve imports. -- Free allocated memory. - - -MemoryModule -============= - -MemoryModule is a C-library that can be used to load a DLL from memory. - -The interface is very similar to the standard methods for loading of libraries:: - - typedef void *HMEMORYMODULE; - - HMEMORYMODULE MemoryLoadLibrary(const void *); - FARPROC MemoryGetProcAddress(HMEMORYMODULE, const char *); - void MemoryFreeLibrary(HMEMORYMODULE); - - -Downloads ----------- - -The latest development release can always be grabbed from my development subversion server at -http://fancycode.com/viewcvs/MemoryModule/trunk/ - -All released versions can be downloaded from the list below. - -Version 0.0.2 (MPL release) - http://www.joachim-bauch.de/tutorials/downloads/MemoryModule-0.0.2.zip - -Version 0.0.1 (first public release) - http://www.joachim-bauch.de/tutorials/downloads/MemoryModule-0.0.1.zip - - -Known issues -------------- - -- All memory that is not protected by section flags is gets committed using `PAGE_READWRITE`. - I don't know if this is correct. - - -License --------- - -Since version 0.0.2, the MemoryModule library is released under the Mozilla Public License (MPL). -Version 0.0.1 has been released unter the Lesser General Public License (LGPL). - -It is provided as-is without ANY warranty. You may use it at your own risk. - - -Copyright -========== - -The MemoryModule library and this tutorial are -Copyright (c) 2004-2006 by Joachim Bauch. +:author: Joachim Bauch +:contact: mail@joachim-bauch.de +:copyright: `Creative Commons License (by-sa)`__ + +__ http://creativecommons.org/licenses/by-sa/2.5/ + + +.. contents:: + + +Overview +========= + +The default windows API functions to load external libraries into a program +(LoadLibrary, LoadLibraryEx) only work with files on the filesystem. It's +therefore impossible to load a DLL from memory. +But sometimes, you need exactly this functionality (e.g. you don't want to +distribute a lot of files or want to make disassembling harder). Common +workarounds for this problems are to write the DLL into a temporary file +first and import it from there. When the program terminates, the temporary +file gets deleted. + +In this tutorial, I will describe first, how DLL files are structured and +will present some code that can be used to load a DLL completely from memory - +without storing on the disk first. + + +Windows executables - the PE format +==================================== + +Most windows binaries that can contain executable code (.exe, .dll, .sys) +share a common file format that consists of the following parts: + ++----------------+ +| DOS header | +| | +| DOS stub | ++----------------+ +| PE header | ++----------------+ +| Section header | ++----------------+ +| Section 1 | ++----------------+ +| Section 2 | ++----------------+ +| . . . | ++----------------+ +| Section n | ++----------------+ + +All structures given below can be found in the header file `winnt.h`. + + +DOS header / stub +------------------ + +The DOS header is only used for backwards compatibility. It precedes the DOS +stub that normally just displays an error message about the program not being +able to be run from DOS mode. + +Microsoft defines the DOS header as follows:: + + typedef struct _IMAGE_DOS_HEADER { // DOS .EXE header + WORD e_magic; // Magic number + WORD e_cblp; // Bytes on last page of file + WORD e_cp; // Pages in file + WORD e_crlc; // Relocations + WORD e_cparhdr; // Size of header in paragraphs + WORD e_minalloc; // Minimum extra paragraphs needed + WORD e_maxalloc; // Maximum extra paragraphs needed + WORD e_ss; // Initial (relative) SS value + WORD e_sp; // Initial SP value + WORD e_csum; // Checksum + WORD e_ip; // Initial IP value + WORD e_cs; // Initial (relative) CS value + WORD e_lfarlc; // File address of relocation table + WORD e_ovno; // Overlay number + WORD e_res[4]; // Reserved words + WORD e_oemid; // OEM identifier (for e_oeminfo) + WORD e_oeminfo; // OEM information; e_oemid specific + WORD e_res2[10]; // Reserved words + LONG e_lfanew; // File address of new exe header + } IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER; + + +PE header +---------- + +The PE header contains informations about the different sections inside the +executable that are used to store code and data or to define imports from other +libraries or exports this libraries provides. + +It's defined as follows:: + + typedef struct _IMAGE_NT_HEADERS { + DWORD Signature; + IMAGE_FILE_HEADER FileHeader; + IMAGE_OPTIONAL_HEADER32 OptionalHeader; + } IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32; + +The `FileHeader` describes the *physical* format of the file, i.e. contents, informations +about symbols, etc:: + + typedef struct _IMAGE_FILE_HEADER { + WORD Machine; + WORD NumberOfSections; + DWORD TimeDateStamp; + DWORD PointerToSymbolTable; + DWORD NumberOfSymbols; + WORD SizeOfOptionalHeader; + WORD Characteristics; + } IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER; + +.. _OptionalHeader: + +The `OptionalHeader` contains informations about the *logical* format of the library, +including required OS version, memory requirements and entry points:: + + typedef struct _IMAGE_OPTIONAL_HEADER { + // + // Standard fields. + // + + WORD Magic; + BYTE MajorLinkerVersion; + BYTE MinorLinkerVersion; + DWORD SizeOfCode; + DWORD SizeOfInitializedData; + DWORD SizeOfUninitializedData; + DWORD AddressOfEntryPoint; + DWORD BaseOfCode; + DWORD BaseOfData; + + // + // NT additional fields. + // + + DWORD ImageBase; + DWORD SectionAlignment; + DWORD FileAlignment; + WORD MajorOperatingSystemVersion; + WORD MinorOperatingSystemVersion; + WORD MajorImageVersion; + WORD MinorImageVersion; + WORD MajorSubsystemVersion; + WORD MinorSubsystemVersion; + DWORD Win32VersionValue; + DWORD SizeOfImage; + DWORD SizeOfHeaders; + DWORD CheckSum; + WORD Subsystem; + WORD DllCharacteristics; + DWORD SizeOfStackReserve; + DWORD SizeOfStackCommit; + DWORD SizeOfHeapReserve; + DWORD SizeOfHeapCommit; + DWORD LoaderFlags; + DWORD NumberOfRvaAndSizes; + IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; + } IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32; + +.. _DataDirectory: + +The `DataDirectory` contains 16 (`IMAGE_NUMBEROF_DIRECTORY_ENTRIES`) entries +defining the logical components of the library: + +===== ========================== +Index Description +===== ========================== +0 Exported functions +----- -------------------------- +1 Imported functions +----- -------------------------- +2 Resources +----- -------------------------- +3 Exception informations +----- -------------------------- +4 Security informations +----- -------------------------- +5 Base relocation table +----- -------------------------- +6 Debug informations +----- -------------------------- +7 Architecture specific data +----- -------------------------- +8 Global pointer +----- -------------------------- +9 Thread local storage +----- -------------------------- +10 Load configuration +----- -------------------------- +11 Bound imports +----- -------------------------- +12 Import address table +----- -------------------------- +13 Delay load imports +----- -------------------------- +14 COM runtime descriptor +===== ========================== + +For importing the DLL we only need the entries describing the imports and the +base relocation table. In order to provide access to the exported functions, +the exports entry is required. + + +Section header +--------------- + +The section header is stored after the OptionalHeader_ structure in the PE +header. Microsoft provides the macro `IMAGE_FIRST_SECTION` to get the start +address based on the PE header. + +Actually, the section header is a list of informations about each section in +the file:: + + typedef struct _IMAGE_SECTION_HEADER { + BYTE Name[IMAGE_SIZEOF_SHORT_NAME]; + union { + DWORD PhysicalAddress; + DWORD VirtualSize; + } Misc; + DWORD VirtualAddress; + DWORD SizeOfRawData; + DWORD PointerToRawData; + DWORD PointerToRelocations; + DWORD PointerToLinenumbers; + WORD NumberOfRelocations; + WORD NumberOfLinenumbers; + DWORD Characteristics; + } IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER; + +A section can contain code, data, relocation informations, resources, export or +import definitions, etc. + + +Loading the library +==================== + +To emulate the PE loader, we must first understand, which steps are neccessary +to load the file to memory and prepare the structures so they can be called from +other programs. + +When issuing the API call `LoadLibrary`, Windows basically performs these tasks: + +1. Open the given file and check the DOS and PE headers. + +2. Try to allocate a memory block of `PEHeader.OptionalHeader.SizeOfImage` bytes + at position `PEHeader.OptionalHeader.ImageBase`. + +3. Parse section headers and copy sections to their addresses. The destination + address for each section, relative to the base of the allocated memory block, + is stored in the `VirtualAddress` attribute of the `IMAGE_SECTION_HEADER` + structure. + +4. If the allocated memory block differs from `ImageBase`, various references in + the code and/or data sections must be adjusted. This is called *Base + relocation*. + +5. The required imports for the library must be resolved by loading the + corresponding libraries. + +6. The memory regions of the different sections must be protected depending on + the section's characteristics. Some sections are marked as *discardable* + and therefore can be safely freed at this point. These sections normally + contain temporary data that is only needed during the import, like the + informations for the base relocation. + +7. Now the library is loaded completely. It must be notified about this by + calling the entry point using the flag `DLL_PROCESS_ATTACH`. + +In the following paragraphs, each step is described. + + +Allocate memory +---------------- + +All memory required for the library must be reserved / allocated using +`VirtualAlloc`, as Windows provides functions to protect these memory blocks. +This is required to restrict access to the memory, like blocking write access +to the code or constant data. + +The OptionalHeader_ structure defines the size of the required memory block +for the library. It must be reserved at the address specified by `ImageBase` +if possible:: + + memory = VirtualAlloc((LPVOID)(PEHeader->OptionalHeader.ImageBase), + PEHeader->OptionalHeader.SizeOfImage, + MEM_RESERVE, + PAGE_READWRITE); + +If the reserved memory differs from the address given in `ImageBase`, base +relocation as described below must be done. + + +Copy sections +-------------- + +Once the memory has been reserved, the file contents can be copied to the +system. The section header must get evaluated in order to determine the +position in the file and the target area in memory. + +Before copying the data, the memory block must get committed:: + + dest = VirtualAlloc(baseAddress + section->VirtualAddress, + section->SizeOfRawData, + MEM_COMMIT, + PAGE_READWRITE); + +Sections without data in the file (like data sections for the used variables) +have a `SizeOfRawData` of `0`, so you can use the `SizeOfInitializedData` +or `SizeOfUninitializedData` of the OptionalHeader_. Which one must get +choosen depending on the bit flags `IMAGE_SCN_CNT_INITIALIZED_DATA` and +`IMAGE_SCN_CNT_UNINITIALIZED_DATA` that may be set in the section`s +characteristics. + + +Base relocation +---------------- + +All memory addresses in the code / data sections of a library are stored relative +to the address defined by `ImageBase` in the OptionalHeader_. If the library +can't be imported to this memory address, the references must get adjusted +=> *relocated*. The file format helps for this by storing informations about +all these references in the base relocation table, which can be found in the +directory entry 5 of the DataDirectory_ in the OptionalHeader_. + +This table consists of a series of this structure + +:: + + typedef struct _IMAGE_BASE_RELOCATION { + DWORD VirtualAddress; + DWORD SizeOfBlock; + } IMAGE_BASE_RELOCATION; + +It contains `(SizeOfBlock - IMAGE_SIZEOF_BASE_RELOCATION) / 2` entries of 16 bits +each. The upper 4 bits define the type of relocation, the lower 12 bits define +the offset relative to the `VirtualAddress`. + +The only types that seem to be used in DLLs are + +IMAGE_REL_BASED_ABSOLUTE + No operation relocation. Used for padding. +IMAGE_REL_BASED_HIGHLOW + Add the delta between the `ImageBase` and the allocated memory block to the + 32 bits found at the offset. + + +Resolve imports +---------------- + +The directory entry 1 of the DataDirectory_ in the OptionalHeader_ specifies +a list of libraries to import symbols from. Each entry in this list is defined +as follows:: + + typedef struct _IMAGE_IMPORT_DESCRIPTOR { + union { + DWORD Characteristics; // 0 for terminating null import descriptor + DWORD OriginalFirstThunk; // RVA to original unbound IAT (PIMAGE_THUNK_DATA) + }; + DWORD TimeDateStamp; // 0 if not bound, + // -1 if bound, and real date\time stamp + // in IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (new BIND) + // O.W. date/time stamp of DLL bound to (Old BIND) + + DWORD ForwarderChain; // -1 if no forwarders + DWORD Name; + DWORD FirstThunk; // RVA to IAT (if bound this IAT has actual addresses) + } IMAGE_IMPORT_DESCRIPTOR; + +The `Name` entry describes the offset to the NULL-terminated string of the library +name (e.g. `KERNEL32.DLL`). The `OriginalFirstThunk` entry points to a list +of references to the function names to import from the external library. +`FirstThunk` points to a list of addresses that gets filled with pointers to +the imported symbols. + +When we resolve the imports, we walk both lists in parallel, import the function +defined by the name in the first list and store the pointer to the symbol in the +second list:: + + nameRef = (DWORD *)(baseAddress + importDesc->OriginalFirstThunk); + symbolRef = (DWORD *)(baseAddress + importDesc->FirstThunk); + for (; *nameRef; nameRef++, symbolRef++) + { + PIMAGE_IMPORT_BY_NAME thunkData = (PIMAGE_IMPORT_BY_NAME)(codeBase + *nameRef); + *symbolRef = (DWORD)GetProcAddress(handle, (LPCSTR)&thunkData->Name); + if (*funcRef == 0) + { + handleImportError(); + return; + } + } + + +Protect memory +--------------- + +Every section specifies permission flags in it's `Characteristics` entry. +These flags can be one or a combination of + +IMAGE_SCN_MEM_EXECUTE + The section contains data that can be executed. + +IMAGE_SCN_MEM_READ + The section contains data that is readable. + +IMAGE_SCN_MEM_WRITE + The section contains data that is writeable. + +These flags must get mapped to the protection flags + +- PAGE_NOACCESS +- PAGE_WRITECOPY +- PAGE_READONLY +- PAGE_READWRITE +- PAGE_EXECUTE +- PAGE_EXECUTE_WRITECOPY +- PAGE_EXECUTE_READ +- PAGE_EXECUTE_READWRITE + +Now, the function `VirtualProtect` can be used to limit access to the memory. +If the program tries to access it in a unauthorized way, an exception gets +raised by Windows. + +In addition the section flags above, the following can be added: + +IMAGE_SCN_MEM_DISCARDABLE + The data in this section can be freed after the import. Usually this is + specified for relocation data. + +IMAGE_SCN_MEM_NOT_CACHED + The data in this section must not get cached by Windows. Add the bit + flag `PAGE_NOCACHE` to the protection flags above. + + +Notify library +--------------- + +The last thing to do is to call the DLL entry point (defined by +`AddressOfEntryPoint`) and so notifying the library about being attached +to a process. + +The function at the entry point is defined as + +:: + + typedef BOOL (WINAPI *DllEntryProc)(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved); + +So the last code we need to execute is + +:: + + DllEntryProc entry = (DllEntryProc)(baseAddress + PEHeader->OptionalHeader.AddressOfEntryPoint); + (*entry)((HINSTANCE)baseAddress, DLL_PROCESS_ATTACH, 0); + +Afterwards we can use the exported functions as with any normal library. + + +Exported functions +=================== + +If you want to access the functions that are exported by the library, you need to find the entry +point to a symbol, i.e. the name of the function to call. + +The directory entry 0 of the DataDirectory_ in the OptionalHeader_ contains informations about +the exported functions. It's defined as follows:: + + typedef struct _IMAGE_EXPORT_DIRECTORY { + DWORD Characteristics; + DWORD TimeDateStamp; + WORD MajorVersion; + WORD MinorVersion; + DWORD Name; + DWORD Base; + DWORD NumberOfFunctions; + DWORD NumberOfNames; + DWORD AddressOfFunctions; // RVA from base of image + DWORD AddressOfNames; // RVA from base of image + DWORD AddressOfNameOrdinals; // RVA from base of image + } IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY; + +First thing to do, is to map the name of the function to the ordinal number of the exported +symbol. Therefore, just walk the arrays defined by `AddressOfNames` and `AddressOfNameOrdinals` +parallel until you found the required name. + +Now you can use the ordinal number to read the address by evaluating the n-th element of the +`AddressOfFunctions` array. + + +Freeing the library +==================== + +To free the custom loaded library, perform the steps + +- Call entry point to notify library about being detached:: + + DllEntryProc entry = (DllEntryProc)(baseAddress + PEHeader->OptionalHeader.AddressOfEntryPoint); + (*entry)((HINSTANCE)baseAddress, DLL_PROCESS_ATTACH, 0); + +- Free external libraries used to resolve imports. +- Free allocated memory. + + +MemoryModule +============= + +MemoryModule is a C-library that can be used to load a DLL from memory. + +The interface is very similar to the standard methods for loading of libraries:: + + typedef void *HMEMORYMODULE; + + HMEMORYMODULE MemoryLoadLibrary(const void *); + FARPROC MemoryGetProcAddress(HMEMORYMODULE, const char *); + void MemoryFreeLibrary(HMEMORYMODULE); + + +Downloads +---------- + +The latest development release can always be grabbed from my development subversion server at +http://fancycode.com/viewcvs/MemoryModule/trunk/ + +All released versions can be downloaded from the list below. + +Version 0.0.2 (MPL release) + http://www.joachim-bauch.de/tutorials/downloads/MemoryModule-0.0.2.zip + +Version 0.0.1 (first public release) + http://www.joachim-bauch.de/tutorials/downloads/MemoryModule-0.0.1.zip + + +Known issues +------------- + +- All memory that is not protected by section flags is gets committed using `PAGE_READWRITE`. + I don't know if this is correct. + + +License +-------- + +Since version 0.0.2, the MemoryModule library is released under the Mozilla Public License (MPL). +Version 0.0.1 has been released unter the Lesser General Public License (LGPL). + +It is provided as-is without ANY warranty. You may use it at your own risk. + + +Copyright +========== + +The MemoryModule library and this tutorial are +Copyright (c) 2004-2006 by Joachim Bauch. diff --git a/example/DllLoader/DllLoader.cpp b/example/DllLoader/DllLoader.cpp index 4fa8ca3..f51c31b 100644 --- a/example/DllLoader/DllLoader.cpp +++ b/example/DllLoader/DllLoader.cpp @@ -1,70 +1,70 @@ -#define WIN32_LEAN_AND_MEAN - -#include -#include -#include - -#include "../../MemoryModule.h" - -typedef int (*addNumberProc)(int, int); - -#define DLL_FILE "..\\..\\SampleDLL\\Debug\\SampleDLL.dll" - -void LoadFromFile(void) -{ - addNumberProc addNumber; - HINSTANCE handle = LoadLibrary(DLL_FILE); - if (handle == INVALID_HANDLE_VALUE) - return; - - addNumber = (addNumberProc)GetProcAddress(handle, "addNumbers"); - printf("From file: %d\n", addNumber(1, 2)); - FreeLibrary(handle); -} - -void LoadFromMemory(void) -{ - FILE *fp; - unsigned char *data=NULL; - size_t size; - HMEMORYMODULE module; - addNumberProc addNumber; - - fp = fopen(DLL_FILE, "rb"); - if (fp == NULL) - { - printf("Can't open DLL file \"%s\".", DLL_FILE); - goto exit; - } - - fseek(fp, 0, SEEK_END); - size = ftell(fp); - data = (unsigned char *)malloc(size); - fseek(fp, 0, SEEK_SET); - fread(data, 1, size, fp); - fclose(fp); - - module = MemoryLoadLibrary(data); - if (module == NULL) - { - printf("Can't load library from memory.\n"); - goto exit; - } - - addNumber = (addNumberProc)MemoryGetProcAddress(module, "addNumbers"); - printf("From memory: %d\n", addNumber(1, 2)); - MemoryFreeLibrary(module); - -exit: - if (data) - free(data); -} - -int main(int argc, char* argv[]) -{ - LoadFromFile(); - printf("\n\n"); - LoadFromMemory(); - return 0; -} - +#define WIN32_LEAN_AND_MEAN + +#include +#include +#include + +#include "../../MemoryModule.h" + +typedef int (*addNumberProc)(int, int); + +#define DLL_FILE "..\\..\\SampleDLL\\Debug\\SampleDLL.dll" + +void LoadFromFile(void) +{ + addNumberProc addNumber; + HINSTANCE handle = LoadLibrary(DLL_FILE); + if (handle == INVALID_HANDLE_VALUE) + return; + + addNumber = (addNumberProc)GetProcAddress(handle, "addNumbers"); + printf("From file: %d\n", addNumber(1, 2)); + FreeLibrary(handle); +} + +void LoadFromMemory(void) +{ + FILE *fp; + unsigned char *data=NULL; + size_t size; + HMEMORYMODULE module; + addNumberProc addNumber; + + fp = fopen(DLL_FILE, "rb"); + if (fp == NULL) + { + printf("Can't open DLL file \"%s\".", DLL_FILE); + goto exit; + } + + fseek(fp, 0, SEEK_END); + size = ftell(fp); + data = (unsigned char *)malloc(size); + fseek(fp, 0, SEEK_SET); + fread(data, 1, size, fp); + fclose(fp); + + module = MemoryLoadLibrary(data); + if (module == NULL) + { + printf("Can't load library from memory.\n"); + goto exit; + } + + addNumber = (addNumberProc)MemoryGetProcAddress(module, "addNumbers"); + printf("From memory: %d\n", addNumber(1, 2)); + MemoryFreeLibrary(module); + +exit: + if (data) + free(data); +} + +int main(int argc, char* argv[]) +{ + LoadFromFile(); + printf("\n\n"); + LoadFromMemory(); + return 0; +} + diff --git a/example/DllLoader/DllLoader.vcproj b/example/DllLoader/DllLoader.vcproj index e6e24dd..e743a68 100644 --- a/example/DllLoader/DllLoader.vcproj +++ b/example/DllLoader/DllLoader.vcproj @@ -1,138 +1,138 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/example/DllMemory.sln b/example/DllMemory.sln index ef94099..812654b 100644 --- a/example/DllMemory.sln +++ b/example/DllMemory.sln @@ -1,30 +1,30 @@ -Microsoft Visual Studio Solution File, Format Version 8.00 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SampleDLL", "SampleDLL\SampleDLL.vcproj", "{B293DAC4-5BCA-4413-9B7B-92CB56459875}" - ProjectSection(ProjectDependencies) = postProject - {D0226BB5-3A02-4C91-893A-F36567AED5C5} = {D0226BB5-3A02-4C91-893A-F36567AED5C5} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DllLoader", "DllLoader\DllLoader.vcproj", "{D0226BB5-3A02-4C91-893A-F36567AED5C5}" - ProjectSection(ProjectDependencies) = postProject - EndProjectSection -EndProject -Global - GlobalSection(SolutionConfiguration) = preSolution - Debug = Debug - Release = Release - EndGlobalSection - GlobalSection(ProjectConfiguration) = postSolution - {B293DAC4-5BCA-4413-9B7B-92CB56459875}.Debug.ActiveCfg = Debug|Win32 - {B293DAC4-5BCA-4413-9B7B-92CB56459875}.Debug.Build.0 = Debug|Win32 - {B293DAC4-5BCA-4413-9B7B-92CB56459875}.Release.ActiveCfg = Release|Win32 - {B293DAC4-5BCA-4413-9B7B-92CB56459875}.Release.Build.0 = Release|Win32 - {D0226BB5-3A02-4C91-893A-F36567AED5C5}.Debug.ActiveCfg = Debug|Win32 - {D0226BB5-3A02-4C91-893A-F36567AED5C5}.Debug.Build.0 = Debug|Win32 - {D0226BB5-3A02-4C91-893A-F36567AED5C5}.Release.ActiveCfg = Release|Win32 - {D0226BB5-3A02-4C91-893A-F36567AED5C5}.Release.Build.0 = Release|Win32 - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - EndGlobalSection - GlobalSection(ExtensibilityAddIns) = postSolution - EndGlobalSection -EndGlobal +Microsoft Visual Studio Solution File, Format Version 8.00 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SampleDLL", "SampleDLL\SampleDLL.vcproj", "{B293DAC4-5BCA-4413-9B7B-92CB56459875}" + ProjectSection(ProjectDependencies) = postProject + {D0226BB5-3A02-4C91-893A-F36567AED5C5} = {D0226BB5-3A02-4C91-893A-F36567AED5C5} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DllLoader", "DllLoader\DllLoader.vcproj", "{D0226BB5-3A02-4C91-893A-F36567AED5C5}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfiguration) = preSolution + Debug = Debug + Release = Release + EndGlobalSection + GlobalSection(ProjectConfiguration) = postSolution + {B293DAC4-5BCA-4413-9B7B-92CB56459875}.Debug.ActiveCfg = Debug|Win32 + {B293DAC4-5BCA-4413-9B7B-92CB56459875}.Debug.Build.0 = Debug|Win32 + {B293DAC4-5BCA-4413-9B7B-92CB56459875}.Release.ActiveCfg = Release|Win32 + {B293DAC4-5BCA-4413-9B7B-92CB56459875}.Release.Build.0 = Release|Win32 + {D0226BB5-3A02-4C91-893A-F36567AED5C5}.Debug.ActiveCfg = Debug|Win32 + {D0226BB5-3A02-4C91-893A-F36567AED5C5}.Debug.Build.0 = Debug|Win32 + {D0226BB5-3A02-4C91-893A-F36567AED5C5}.Release.ActiveCfg = Release|Win32 + {D0226BB5-3A02-4C91-893A-F36567AED5C5}.Release.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + EndGlobalSection + GlobalSection(ExtensibilityAddIns) = postSolution + EndGlobalSection +EndGlobal diff --git a/example/SampleDLL/SampleDLL.cpp b/example/SampleDLL/SampleDLL.cpp index 662efcd..bc21282 100644 --- a/example/SampleDLL/SampleDLL.cpp +++ b/example/SampleDLL/SampleDLL.cpp @@ -1,10 +1,10 @@ -#include "SampleDLL.h" - -extern "C" { - -SAMPLEDLL_API int addNumbers(int a, int b) -{ - return a + b; -} - +#include "SampleDLL.h" + +extern "C" { + +SAMPLEDLL_API int addNumbers(int a, int b) +{ + return a + b; +} + } \ No newline at end of file diff --git a/example/SampleDLL/SampleDLL.h b/example/SampleDLL/SampleDLL.h index b77f135..9bc0f94 100644 --- a/example/SampleDLL/SampleDLL.h +++ b/example/SampleDLL/SampleDLL.h @@ -1,11 +1,11 @@ -extern "C" { - -#ifdef SAMPLEDLL_EXPORTS -#define SAMPLEDLL_API __declspec(dllexport) -#else -#define SAMPLEDLL_API __declspec(dllimport) -#endif - -SAMPLEDLL_API int addNumbers(int a, int b); - +extern "C" { + +#ifdef SAMPLEDLL_EXPORTS +#define SAMPLEDLL_API __declspec(dllexport) +#else +#define SAMPLEDLL_API __declspec(dllimport) +#endif + +SAMPLEDLL_API int addNumbers(int a, int b); + } \ No newline at end of file diff --git a/example/SampleDLL/SampleDLL.vcproj b/example/SampleDLL/SampleDLL.vcproj index 9a0b722..0213210 100644 --- a/example/SampleDLL/SampleDLL.vcproj +++ b/example/SampleDLL/SampleDLL.vcproj @@ -1,137 +1,137 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +