├── example ├── DllLoader │ ├── .gitignore │ ├── CMakeLists.txt │ ├── Makefile │ ├── DllLoaderLoader.cpp │ ├── DllLoader.vcproj │ └── DllLoader.cpp ├── SampleDLL │ ├── .gitignore │ ├── SampleDLL.cpp │ ├── SampleDLL.h │ ├── CMakeLists.txt │ ├── Makefile │ ├── SampleDLL.rc │ └── SampleDLL.vcproj ├── CMakeLists.txt ├── Makefile └── DllMemory.sln ├── .gitignore ├── tests ├── SampleDLL.cpp ├── SampleDLL.h ├── runtests.sh ├── runwine.sh ├── CMakeLists.txt ├── TestSuite.c ├── generate-exports.sh ├── SampleDLL.rc ├── Makefile └── LoadDll.cpp ├── .gitattributes ├── Makefile ├── appveyor.yml ├── readme.md ├── scripts └── run-appveyor.bat ├── CMakeLists.txt ├── .travis.yml ├── MemoryModule.h ├── LICENSE.txt ├── doc └── readme.rst └── MemoryModule.c /example/DllLoader/.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.exe -------------------------------------------------------------------------------- /example/SampleDLL/.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.dll 3 | *.res 4 | -------------------------------------------------------------------------------- /example/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_subdirectory (DllLoader) 2 | add_subdirectory (SampleDLL) 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.obj 3 | *.exe 4 | tests/*.dll 5 | tests/*.res 6 | 7 | tests/SampleExports.cpp 8 | tests/SampleExports.h 9 | -------------------------------------------------------------------------------- /tests/SampleDLL.cpp: -------------------------------------------------------------------------------- 1 | #include "SampleDLL.h" 2 | 3 | extern "C" { 4 | 5 | SAMPLEDLL_API int addNumbers(int a, int b) 6 | { 7 | return a + b; 8 | } 9 | 10 | } 11 | -------------------------------------------------------------------------------- /example/SampleDLL/SampleDLL.cpp: -------------------------------------------------------------------------------- 1 | #include "SampleDLL.h" 2 | 3 | extern "C" { 4 | 5 | SAMPLEDLL_API int addNumbers(int a, int b) 6 | { 7 | return a + b; 8 | } 9 | 10 | } 11 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | 3 | # Native line endings. 4 | *.c text 5 | *.cpp text 6 | *.h text 7 | *.rc text 8 | 9 | # Windows line endings. 10 | *.bat text eol=crlf 11 | *.eln text eol=crlf 12 | -------------------------------------------------------------------------------- /tests/SampleDLL.h: -------------------------------------------------------------------------------- 1 | extern "C" { 2 | 3 | #ifdef SAMPLEDLL_EXPORTS 4 | #define SAMPLEDLL_API __declspec(dllexport) 5 | #else 6 | #define SAMPLEDLL_API __declspec(dllimport) 7 | #endif 8 | 9 | SAMPLEDLL_API int addNumbers(int a, int b); 10 | 11 | } 12 | -------------------------------------------------------------------------------- /example/SampleDLL/SampleDLL.h: -------------------------------------------------------------------------------- 1 | extern "C" { 2 | 3 | #ifdef SAMPLEDLL_EXPORTS 4 | #define SAMPLEDLL_API __declspec(dllexport) 5 | #else 6 | #define SAMPLEDLL_API __declspec(dllimport) 7 | #endif 8 | 9 | SAMPLEDLL_API int addNumbers(int a, int b); 10 | 11 | } 12 | -------------------------------------------------------------------------------- /example/Makefile: -------------------------------------------------------------------------------- 1 | SUBDIRS = DllLoader SampleDLL 2 | 3 | .PHONY: subdirs $(SUBDIRS) 4 | 5 | subdirs: $(SUBDIRS) 6 | 7 | $(SUBDIRS): 8 | $(MAKE) -C $@ 9 | 10 | CLEANDIRS = $(SUBDIRS:%=clean-%) 11 | 12 | clean: $(CLEANDIRS) 13 | $(CLEANDIRS): 14 | $(MAKE) -C $(@:clean-%=%) clean 15 | 16 | .PHONY: subdirs $(INSTALLDIRS) 17 | .PHONY: clean 18 | -------------------------------------------------------------------------------- /example/SampleDLL/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set (sources 2 | SampleDLL.cpp 3 | SampleDLL.h 4 | SampleDLL.rc 5 | ) 6 | 7 | add_definitions (-DSAMPLEDLL_EXPORTS) 8 | add_library (SampleDLL MODULE ${sources}) 9 | if (NOT MSVC) 10 | set_target_properties ("SampleDLL" PROPERTIES PREFIX "") 11 | set_target_properties ("SampleDLL" PROPERTIES SUFFIX ".dll") 12 | endif () 13 | -------------------------------------------------------------------------------- /tests/runtests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | PLATFORM=$1 3 | 4 | read -a TEST_DLLS <<< $2 5 | 6 | for filename in "${TEST_DLLS[@]}" 7 | do 8 | : 9 | echo "Testing $filename" 10 | ./runwine.sh "${PLATFORM}" ./LoadDll.exe $filename 11 | if [ "$?" != "0" ]; then 12 | exit 1 13 | fi 14 | done 15 | 16 | echo "${#TEST_DLLS[@]} tests completed successfully" 17 | -------------------------------------------------------------------------------- /tests/runwine.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -eu 3 | PLATFORM=$1 4 | shift 5 | 6 | if [ "${PLATFORM}" = "x86_64" ]; then 7 | export WINEPREFIX=${HOME}/.wine64/ 8 | WINE=wine64 9 | else 10 | export WINEPREFIX=${HOME}/.wine/ 11 | WINE=wine 12 | fi 13 | export WINEPATH=/usr/lib/gcc/${PLATFORM}-w64-mingw32/4.8/:/usr/${PLATFORM}-w64-mingw32/lib 14 | 15 | exec ${WINE} $@ 16 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | SUBDIRS = example tests 2 | 3 | .PHONY: subdirs $(SUBDIRS) 4 | 5 | subdirs: $(SUBDIRS) 6 | 7 | $(SUBDIRS): 8 | $(MAKE) -C $@ 9 | 10 | CLEANDIRS = $(SUBDIRS:%=clean-%) 11 | 12 | clean: $(CLEANDIRS) 13 | $(CLEANDIRS): 14 | $(MAKE) -C $(@:clean-%=%) clean 15 | 16 | test: 17 | $(MAKE) -C tests test 18 | 19 | .PHONY: subdirs $(INSTALLDIRS) 20 | .PHONY: clean test 21 | -------------------------------------------------------------------------------- /tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set (sources_testsuite 2 | TestSuite.c 3 | ) 4 | 5 | if (NOT MSVC) 6 | set (CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "-static") 7 | set (CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS "-static") 8 | endif () 9 | 10 | add_executable (TestSuite ${sources_testsuite}) 11 | target_link_libraries ("TestSuite" "MemoryModule") 12 | if (NOT MSVC) 13 | set_target_properties ("TestSuite" PROPERTIES SUFFIX ".exe") 14 | endif () 15 | -------------------------------------------------------------------------------- /tests/TestSuite.c: -------------------------------------------------------------------------------- 1 | #define WIN32_LEAN_AND_MEAN 2 | #ifndef _CRT_SECURE_NO_WARNINGS 3 | #define _CRT_SECURE_NO_WARNINGS 4 | #endif 5 | 6 | #include 7 | 8 | extern BOOL MemoryModuleTestsuite(); 9 | 10 | int main(int argc, char* argv[]) 11 | { 12 | UNREFERENCED_PARAMETER(argc); 13 | UNREFERENCED_PARAMETER(argv); 14 | if (!MemoryModuleTestsuite()) { 15 | return 1; 16 | } 17 | 18 | return 0; 19 | } 20 | -------------------------------------------------------------------------------- /example/SampleDLL/Makefile: -------------------------------------------------------------------------------- 1 | UNAME := $(shell uname) 2 | 3 | ifeq ($(UNAME), Linux) 4 | ifndef PLATFORM 5 | PLATFORM = i686 6 | endif 7 | CC = $(PLATFORM)-w64-mingw32-g++ 8 | CXX = $(PLATFORM)-w64-mingw32-g++ 9 | LINK = $(PLATFORM)-w64-mingw32-g++ 10 | RC = $(PLATFORM)-w64-mingw32-windres 11 | else 12 | CC = g++ 13 | CXX = g++ 14 | LINK = ld 15 | RC = rc 16 | endif 17 | 18 | RM = rm 19 | CFLAGS = -Wall -g -DSAMPLEDLL_EXPORTS 20 | LDFLAGS = -shared 21 | RCFLAGS = -O coff 22 | 23 | ifdef UNICODE 24 | CFLAGS += -DUNICODE -D_UNICODE 25 | endif 26 | 27 | OBJ = SampleDLL.o SampleDLL.res 28 | 29 | SampleDLL.dll: $(OBJ) 30 | $(LINK) $(LDFLAGS) -o SampleDLL.dll $(OBJ) 31 | 32 | %.o: %.cpp 33 | $(CXX) $(CFLAGS) -c $< 34 | 35 | %.res: %.rc 36 | $(RC) $(RCFLAGS) -o $*.res $< 37 | 38 | clean: 39 | $(RM) -rf $(OBJ) SampleDLL.dll 40 | -------------------------------------------------------------------------------- /example/SampleDLL/SampleDLL.rc: -------------------------------------------------------------------------------- 1 | 1 VERSIONINFO 2 | FILEVERSION 1,0,0,0 3 | PRODUCTVERSION 1,0,0,0 4 | BEGIN 5 | BLOCK "StringFileInfo" 6 | BEGIN 7 | BLOCK "040904E4" 8 | BEGIN 9 | VALUE "CompanyName", "fancy.code" 10 | VALUE "FileDescription", "SampleDLL" 11 | VALUE "FileVersion", "1.0" 12 | VALUE "InternalName", "SampleDLL" 13 | VALUE "LegalCopyright", "Copyright (c) 2004-2015 Joachim Bauch" 14 | VALUE "OriginalFilename", "SampleDLL.dll" 15 | VALUE "ProductName", "MemoryModule" 16 | VALUE "ProductVersion", "0.0.4" 17 | END 18 | END 19 | 20 | BLOCK "VarFileInfo" 21 | BEGIN 22 | VALUE "Translation", 0x409, 1252 23 | END 24 | END 25 | 26 | 27 | #define IDS_HELLO 1 28 | #define IDS_WORLD 20 29 | 30 | STRINGTABLE 31 | { 32 | IDS_HELLO, "Hello" 33 | IDS_WORLD, "World!" 34 | } 35 | -------------------------------------------------------------------------------- /tests/generate-exports.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | ## 4 | ## Generate header file. 5 | ## 6 | 7 | cat > SampleExports.h << EOF 8 | extern "C" { 9 | 10 | #ifdef SAMPLEDLL_EXPORTS 11 | #define SAMPLEDLL_API __declspec(dllexport) 12 | #else 13 | #define SAMPLEDLL_API __declspec(dllimport) 14 | #endif 15 | 16 | EOF 17 | 18 | for i in `seq 1 100`; 19 | do 20 | cat >> SampleExports.h << EOF 21 | SAMPLEDLL_API int add$i(int a); 22 | EOF 23 | done 24 | 25 | cat >> SampleExports.h << EOF 26 | } 27 | EOF 28 | 29 | 30 | ## 31 | ## Generate source file. 32 | ## 33 | 34 | cat > SampleExports.cpp << EOF 35 | #include "SampleExports.h" 36 | 37 | extern "C" { 38 | EOF 39 | 40 | for i in `seq 1 100 | sort -R`; 41 | do 42 | cat >> SampleExports.cpp << EOF 43 | SAMPLEDLL_API int add$i(int a) 44 | { 45 | return a + $i; 46 | } 47 | EOF 48 | done 49 | 50 | cat >> SampleExports.cpp << EOF 51 | } 52 | EOF 53 | -------------------------------------------------------------------------------- /example/DllLoader/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set (sources_dllloader 2 | DllLoader.cpp 3 | ) 4 | 5 | set (sources_dllloaderloader 6 | DllLoaderLoader.cpp 7 | ) 8 | 9 | if (NOT MSVC) 10 | set (CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "-static") 11 | set (CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS "-static") 12 | endif () 13 | 14 | add_executable (DllLoader ${sources_dllloader}) 15 | target_link_libraries ("DllLoader" "MemoryModule") 16 | if (NOT MSVC) 17 | set_target_properties ("DllLoader" PROPERTIES SUFFIX ".exe") 18 | set_target_properties ("DllLoader" PROPERTIES LINK_FLAGS "-Wl,--image-base -Wl,0x20000000") 19 | endif () 20 | 21 | add_executable (DllLoaderLoader ${sources_dllloaderloader}) 22 | target_link_libraries ("DllLoaderLoader" "MemoryModule") 23 | if (NOT MSVC) 24 | set_target_properties ("DllLoaderLoader" PROPERTIES SUFFIX ".exe") 25 | set_target_properties ("DllLoaderLoader" PROPERTIES LINK_FLAGS "-Wl,--image-base -Wl,0x10000000") 26 | endif () 27 | -------------------------------------------------------------------------------- /example/DllLoader/Makefile: -------------------------------------------------------------------------------- 1 | UNAME := $(shell uname) 2 | 3 | ifeq ($(UNAME), Linux) 4 | ifndef PLATFORM 5 | PLATFORM = i686 6 | endif 7 | CC = $(PLATFORM)-w64-mingw32-g++ 8 | CXX = $(PLATFORM)-w64-mingw32-g++ 9 | LINK = $(PLATFORM)-w64-mingw32-ld 10 | else 11 | CC = g++ 12 | CXX = g++ 13 | LINK = ld 14 | endif 15 | 16 | RM = rm 17 | CFLAGS = -Wall -g 18 | LDFLAGS = -static 19 | 20 | ifdef UNICODE 21 | CFLAGS += -DUNICODE -D_UNICODE 22 | endif 23 | 24 | OBJ = DllLoader.o ../../MemoryModule.o 25 | OBJ_LOADER = DllLoaderLoader.o ../../MemoryModule.o 26 | 27 | all: DllLoader.exe DllLoaderLoader.exe 28 | 29 | DllLoader.exe: $(OBJ) 30 | $(CC) $(LDFLAGS) -Wl,--image-base -Wl,0x20000000 -o DllLoader.exe $(OBJ) 31 | 32 | DllLoaderLoader.exe: $(OBJ_LOADER) 33 | $(CC) $(LDFLAGS) -Wl,--image-base -Wl,0x10000000 -o DllLoaderLoader.exe $(OBJ_LOADER) 34 | 35 | %.o: %.cpp 36 | $(CXX) $(CFLAGS) -c $< 37 | 38 | %.o: %.cc 39 | $(CC) $(CFLAGS) -c $< 40 | 41 | clean: 42 | $(RM) -rf $(OBJ) $(OBJ_LOADER) DllLoader.exe DllLoaderLoader.exe 43 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | # Status available at 2 | # https://ci.appveyor.com/project/fancycode/memorymodule 3 | version: 1.0.{build} 4 | 5 | os: 6 | - Visual Studio 2015 7 | 8 | environment: 9 | matrix: 10 | - GENERATOR: "Visual Studio 9 2008" 11 | UNICODE: ON 12 | - GENERATOR: "Visual Studio 9 2008" 13 | UNICODE: OFF 14 | - GENERATOR: "Visual Studio 10 2010" 15 | UNICODE: ON 16 | - GENERATOR: "Visual Studio 10 2010" 17 | UNICODE: OFF 18 | - GENERATOR: "Visual Studio 11 2012" 19 | UNICODE: ON 20 | - GENERATOR: "Visual Studio 11 2012" 21 | UNICODE: OFF 22 | - GENERATOR: "Visual Studio 12 2013" 23 | UNICODE: ON 24 | - GENERATOR: "Visual Studio 12 2013" 25 | UNICODE: OFF 26 | - GENERATOR: "Visual Studio 14 2015" 27 | UNICODE: ON 28 | - GENERATOR: "Visual Studio 14 2015" 29 | UNICODE: OFF 30 | 31 | platform: 32 | - x86 33 | - x64 34 | 35 | configuration: 36 | - Debug 37 | 38 | install: 39 | - call scripts\run-appveyor.bat 40 | 41 | build: off 42 | test: off 43 | deploy: off 44 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | MemoryModule 2 | ============ 3 | 4 | [![Build Status](https://travis-ci.org/fancycode/MemoryModule.svg?branch=master)](https://travis-ci.org/fancycode/MemoryModule)[![Build status](https://ci.appveyor.com/api/projects/status/qcrfxbno0jbbl9cx/branch/master?svg=true)](https://ci.appveyor.com/project/fancycode/memorymodule) 5 | 6 | The default windows API functions to load external libraries into a program 7 | (`LoadLibrary`, `LoadLibraryEx`) only work with files on the filesystem. It's 8 | therefore impossible to load a DLL from memory. 9 | 10 | But sometimes, you need exactly this functionality (e.g. you don't want to 11 | distribute a lot of files or want to make disassembling harder). Common 12 | workarounds for this problems are to write the DLL into a temporary file 13 | first and import it from there. When the program terminates, the temporary 14 | file gets deleted. 15 | 16 | `MemoryModule` is a library that can be used to load a DLL completely from 17 | memory - without storing on the disk first. 18 | 19 | See `doc/readme.rst` for more informations about the format of a DLL file and 20 | a tutorial how they can be loaded directly. 21 | -------------------------------------------------------------------------------- /tests/SampleDLL.rc: -------------------------------------------------------------------------------- 1 | 1 VERSIONINFO 2 | FILEVERSION 1,0,0,0 3 | PRODUCTVERSION 1,0,0,0 4 | BEGIN 5 | BLOCK "StringFileInfo" 6 | BEGIN 7 | BLOCK "040904E4" 8 | BEGIN 9 | VALUE "CompanyName", "fancy.code" 10 | VALUE "FileDescription", "SampleDLL" 11 | VALUE "FileVersion", "1.0" 12 | VALUE "InternalName", "SampleDLL" 13 | VALUE "LegalCopyright", "Copyright (c) 2004-2015 Joachim Bauch" 14 | VALUE "OriginalFilename", "SampleDLL.dll" 15 | VALUE "ProductName", "MemoryModule" 16 | VALUE "ProductVersion", "0.0.4" 17 | END 18 | END 19 | 20 | BLOCK "VarFileInfo" 21 | BEGIN 22 | VALUE "Translation", 0x409, 1252 23 | END 24 | END 25 | 26 | 27 | #define IDS_HELLO 1 28 | #define IDS_WORLD 20 29 | 30 | STRINGTABLE 31 | { 32 | IDS_HELLO, "Hello" 33 | IDS_WORLD, "World!" 34 | } 35 | 36 | STRINGRES RCDATA 37 | { 38 | "This is a ANSI string\0", 39 | L"This is a UNICODE string\0", 40 | } 41 | 42 | STRINGRES1 RCDATA 43 | { 44 | "This is ANSI string 1\0", 45 | L"This is UNICODE string 1\0", 46 | } 47 | 48 | STRINGRES2 RCDATA 49 | { 50 | "This is ANSI string 2\0", 51 | L"This is UNICODE string 2\0", 52 | } 53 | 54 | STRINGRES3 RCDATA 55 | { 56 | "This is ANSI string 3\0", 57 | L"This is UNICODE string 3\0", 58 | } 59 | -------------------------------------------------------------------------------- /example/DllLoader/DllLoaderLoader.cpp: -------------------------------------------------------------------------------- 1 | #define WIN32_LEAN_AND_MEAN 2 | #ifndef _CRT_SECURE_NO_WARNINGS 3 | #define _CRT_SECURE_NO_WARNINGS 4 | #endif 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "../../MemoryModule.h" 13 | 14 | #define EXE_FILE TEXT("DllLoader.exe") 15 | 16 | int RunFromMemory(void) 17 | { 18 | FILE *fp; 19 | unsigned char *data=NULL; 20 | long size; 21 | size_t read; 22 | HMEMORYMODULE handle; 23 | int result = -1; 24 | 25 | fp = _tfopen(EXE_FILE, _T("rb")); 26 | if (fp == NULL) 27 | { 28 | _tprintf(_T("Can't open executable \"%s\"."), EXE_FILE); 29 | goto exit; 30 | } 31 | 32 | fseek(fp, 0, SEEK_END); 33 | size = ftell(fp); 34 | assert(size >= 0); 35 | data = (unsigned char *)malloc(size); 36 | assert(data != NULL); 37 | fseek(fp, 0, SEEK_SET); 38 | read = fread(data, 1, size, fp); 39 | assert(read == static_cast(size)); 40 | fclose(fp); 41 | 42 | handle = MemoryLoadLibrary(data, size); 43 | if (handle == NULL) 44 | { 45 | _tprintf(_T("Can't load library from memory.\n")); 46 | goto exit; 47 | } 48 | 49 | result = MemoryCallEntryPoint(handle); 50 | if (result < 0) { 51 | _tprintf(_T("Could not execute entry point: %d\n"), result); 52 | } 53 | MemoryFreeLibrary(handle); 54 | 55 | exit: 56 | free(data); 57 | return result; 58 | } 59 | 60 | int main() 61 | { 62 | return RunFromMemory(); 63 | } 64 | 65 | -------------------------------------------------------------------------------- /example/DllMemory.sln: -------------------------------------------------------------------------------- 1 | Microsoft Visual Studio Solution File, Format Version 8.00 2 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SampleDLL", "SampleDLL\SampleDLL.vcproj", "{B293DAC4-5BCA-4413-9B7B-92CB56459875}" 3 | ProjectSection(ProjectDependencies) = postProject 4 | {D0226BB5-3A02-4C91-893A-F36567AED5C5} = {D0226BB5-3A02-4C91-893A-F36567AED5C5} 5 | EndProjectSection 6 | EndProject 7 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DllLoader", "DllLoader\DllLoader.vcproj", "{D0226BB5-3A02-4C91-893A-F36567AED5C5}" 8 | ProjectSection(ProjectDependencies) = postProject 9 | EndProjectSection 10 | EndProject 11 | Global 12 | GlobalSection(SolutionConfiguration) = preSolution 13 | Debug = Debug 14 | Release = Release 15 | EndGlobalSection 16 | GlobalSection(ProjectConfiguration) = postSolution 17 | {B293DAC4-5BCA-4413-9B7B-92CB56459875}.Debug.ActiveCfg = Debug|Win32 18 | {B293DAC4-5BCA-4413-9B7B-92CB56459875}.Debug.Build.0 = Debug|Win32 19 | {B293DAC4-5BCA-4413-9B7B-92CB56459875}.Release.ActiveCfg = Release|Win32 20 | {B293DAC4-5BCA-4413-9B7B-92CB56459875}.Release.Build.0 = Release|Win32 21 | {D0226BB5-3A02-4C91-893A-F36567AED5C5}.Debug.ActiveCfg = Debug|Win32 22 | {D0226BB5-3A02-4C91-893A-F36567AED5C5}.Debug.Build.0 = Debug|Win32 23 | {D0226BB5-3A02-4C91-893A-F36567AED5C5}.Release.ActiveCfg = Release|Win32 24 | {D0226BB5-3A02-4C91-893A-F36567AED5C5}.Release.Build.0 = Release|Win32 25 | EndGlobalSection 26 | GlobalSection(ExtensibilityGlobals) = postSolution 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityAddIns) = postSolution 29 | EndGlobalSection 30 | EndGlobal 31 | -------------------------------------------------------------------------------- /scripts/run-appveyor.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | setlocal 3 | 4 | if /I "%PLATFORM%" == "x64" ( 5 | set CMAKE_GEN_SUFFIX= Win64 6 | if /I "%GENERATOR%" == "Visual Studio 9 2008" ( 7 | echo Skipping %CONFIGURATION% build using %GENERATOR%%CMAKE_GEN_SUFFIX% on %PLATFORM% 8 | exit 0 9 | ) 10 | ) else ( 11 | set CMAKE_GEN_SUFFIX= 12 | ) 13 | 14 | echo. 15 | echo Preparing %CONFIGURATION% build environment for %GENERATOR%%CMAKE_GEN_SUFFIX% ... 16 | cmake "-G%GENERATOR%%CMAKE_GEN_SUFFIX%" -DPLATFORM=%PLATFORM% -DUNICODE=%UNICODE% -DTESTSUITE=ON -H. -Bbuild 17 | if %errorlevel% neq 0 exit /b %errorlevel% 18 | 19 | echo. 20 | echo Building ... 21 | cmake --build build --config %CONFIGURATION% 22 | if %errorlevel% neq 0 exit /b %errorlevel% 23 | 24 | echo. 25 | echo Copying generated files ... 26 | copy /y build\example\DllLoader\%CONFIGURATION%\DllLoader.exe build\example\DllLoader\ > NUL 27 | copy /y build\example\DllLoader\%CONFIGURATION%\DllLoaderLoader.exe build\example\DllLoader\ > NUL 28 | copy /y build\example\SampleDLL\%CONFIGURATION%\SampleDLL.dll build\example\SampleDLL\ > NUL 29 | copy /y build\tests\%CONFIGURATION%\TestSuite.exe build\tests\ > NUL 30 | 31 | cd build\example\DllLoader 32 | 33 | echo. 34 | echo Running DllLoader.exe ... 35 | DllLoader.exe 36 | if %errorlevel% neq 0 exit /b %errorlevel% 37 | 38 | echo. 39 | echo Running DllLoaderLoader.exe ... 40 | DllLoaderLoader.exe 41 | if %errorlevel% neq 0 exit /b %errorlevel% 42 | 43 | cd ..\..\tests 44 | 45 | echo. 46 | echo Running TestSuite.exe ... 47 | TestSuite.exe 48 | if %errorlevel% neq 0 exit /b %errorlevel% 49 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project (MemoryModule) 2 | cmake_minimum_required (VERSION 2.8.7) 3 | 4 | set (PLATFORM "x86_64" CACHE STRING "Platform to compile for") 5 | message (STATUS "Compile for ${PLATFORM} platform") 6 | 7 | if (NOT MSVC) 8 | set (CMAKE_SYSTEM_NAME Windows) 9 | set (CMAKE_POSITION_INDEPENDENT_CODE False) 10 | 11 | set (COMPILER_PREFIX "${PLATFORM}-w64-mingw32") 12 | set (CMAKE_C_COMPILER "${COMPILER_PREFIX}-gcc") 13 | set (CMAKE_CXX_COMPILER "${COMPILER_PREFIX}-g++") 14 | set (CMAKE_RC_COMPILER "${COMPILER_PREFIX}-windres") 15 | set (CMAKE_AR "${COMPILER_PREFIX}-ar") 16 | set (CMAKE_RANLIB "${COMPILER_PREFIX}-ranlib") 17 | 18 | set (CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) 19 | set (CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) 20 | set (CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) 21 | 22 | set (CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "") 23 | set (CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS "") 24 | 25 | set (CMAKE_RC_COMPILE_OBJECT "${CMAKE_RC_COMPILER} -O coff -I${CMAKE_CURRENT_SOURCE_DIR} ") 26 | endif () 27 | 28 | if (NOT MSVC) 29 | add_definitions ("-Wall") 30 | else () 31 | # Show level 4 warnings. 32 | add_definitions ("-W4") 33 | endif () 34 | 35 | option(UNICODE "Compile with UNICODE support" OFF) 36 | if (UNICODE) 37 | message (STATUS "Compile with UNICODE support") 38 | add_definitions ("-DUNICODE" "-D_UNICODE") 39 | else () 40 | message (STATUS "Compile without UNICODE support") 41 | endif () 42 | 43 | option(TESTSUITE "Compile with TESTSUITE support" OFF) 44 | if (TESTSUITE) 45 | message (STATUS "Compile with TESTSUITE support") 46 | add_definitions ("-DTESTSUITE") 47 | else () 48 | message (STATUS "Compile without TESTSUITE support") 49 | endif () 50 | 51 | add_library (MemoryModule STATIC MemoryModule.c MemoryModule.h) 52 | target_include_directories(MemoryModule PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}") 53 | if (NOT MSVC) 54 | set_target_properties ("MemoryModule" PROPERTIES PREFIX "") 55 | endif () 56 | 57 | add_subdirectory (example) 58 | add_subdirectory (tests) 59 | 60 | enable_language (RC) 61 | -------------------------------------------------------------------------------- /tests/Makefile: -------------------------------------------------------------------------------- 1 | UNAME := $(shell uname) 2 | 3 | ifeq ($(UNAME), Linux) 4 | ifndef PLATFORM 5 | PLATFORM = i686 6 | endif 7 | CC = $(PLATFORM)-w64-mingw32-g++ 8 | CXX = $(PLATFORM)-w64-mingw32-g++ 9 | LD = $(PLATFORM)-w64-mingw32-ld 10 | RC = $(PLATFORM)-w64-mingw32-windres 11 | else 12 | CC = g++ 13 | CXX = g++ 14 | LD = ld 15 | RC = rc 16 | endif 17 | 18 | RM = rm 19 | CFLAGS = -Wall -g -DTESTSUITE 20 | LDFLAGS = 21 | RCFLAGS = -O coff 22 | 23 | ifdef UNICODE 24 | CFLAGS += -DUNICODE -D_UNICODE 25 | endif 26 | 27 | CFLAGS_DLL = -DSAMPLEDLL_EXPORTS 28 | CFLAGS_EXE = 29 | LDFLAGS_DLL = -shared 30 | LDFLAGS_EXE = -static 31 | 32 | TEST_DLLS = \ 33 | test-align-128.dll \ 34 | test-align-256.dll \ 35 | test-align-512.dll \ 36 | test-align-768.dll \ 37 | test-align-1024.dll \ 38 | test-align-2048.dll \ 39 | test-align-3072.dll \ 40 | test-align-4096.dll \ 41 | test-align-100.dll \ 42 | test-align-200.dll \ 43 | test-align-300.dll \ 44 | test-align-400.dll \ 45 | test-align-500.dll \ 46 | test-align-600.dll \ 47 | test-align-800.dll \ 48 | test-align-900.dll \ 49 | test-relocate.dll \ 50 | test-exports.dll 51 | 52 | LOADDLL_OBJ = LoadDll.o ../MemoryModule.o 53 | TESTSUITE_OBJ = TestSuite.o ../MemoryModule.o 54 | DLL_OBJ = SampleDLL.o SampleDLL.res 55 | 56 | all: prepare_testsuite LoadDll.exe TestSuite.exe $(TEST_DLLS) 57 | 58 | prepare_testsuite: 59 | rm -f $(TESTSUITE_OBJ) 60 | 61 | LoadDll.exe: $(LOADDLL_OBJ) 62 | $(CC) $(LDFLAGS_EXE) $(LDFLAGS) -Wl,--image-base -Wl,0x20000000 -o LoadDll.exe $(LOADDLL_OBJ) 63 | 64 | TestSuite.exe: $(TESTSUITE_OBJ) 65 | $(CC) $(LDFLAGS_EXE) $(LDFLAGS) -o TestSuite.exe $(TESTSUITE_OBJ) 66 | 67 | LoadDll.o: LoadDll.cpp 68 | $(CXX) $(CFLAGS) $(CFLAGS_EXE) -c $< 69 | 70 | test-align-%.dll: $(DLL_OBJ) 71 | $(LD) $(LDFLAGS_DLL) $(LDFLAGS) --file-alignment $* --section-alignment $* -o $@ $(DLL_OBJ) 72 | 73 | test-relocate.dll: $(DLL_OBJ) 74 | $(CXX) $(LDFLAGS_DLL) $(LDFLAGS) -Wl,--image-base -Wl,0x20000000 -o $@ $(DLL_OBJ) 75 | 76 | test-exports.dll: SampleExports.o 77 | $(CXX) $(LDFLAGS_DLL) $(LDFLAGS) -o $@ SampleExports.o 78 | 79 | SampleExports.cpp: generate-exports.sh 80 | ./generate-exports.sh 81 | 82 | %.o: %.cpp 83 | $(CXX) $(CFLAGS) $(CFLAGS_DLL) -c $< 84 | 85 | %.o: %.cc 86 | $(CC) $(CFLAGS) $(CFLAGS_DLL) -c $< 87 | 88 | %.res: %.rc 89 | $(RC) $(RCFLAGS) -o $*.res $< 90 | 91 | clean: 92 | $(RM) -rf LoadDll.exe $(TEST_DLLS) $(LOADDLL_OBJ) $(DLL_OBJ) $(TESTSUITE_OBJ) SampleExports.o 93 | 94 | test: all 95 | ./runwine.sh $(PLATFORM) TestSuite.exe 96 | ./runtests.sh $(PLATFORM) "$(TEST_DLLS)" 97 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: true 2 | 3 | matrix: 4 | include: 5 | - env: PLATFORM=x86_64 UNICODE= CMAKE= WINE_PACKAGE=winehq-stable:amd64 6 | addons: 7 | apt: 8 | packages: 9 | - binutils-mingw-w64-x86-64 10 | - mingw-w64-x86-64-dev 11 | - g++-mingw-w64-x86-64 12 | - gcc-mingw-w64-x86-64 13 | - env: PLATFORM=i686 UNICODE= CMAKE= WINE_PACKAGE=winehq-stable:i386 14 | addons: 15 | apt: 16 | packages: 17 | - binutils-mingw-w64-i686 18 | - mingw-w64-i686-dev 19 | - g++-mingw-w64-i686 20 | - gcc-mingw-w64-i686 21 | - env: PLATFORM=x86_64 UNICODE=1 CMAKE= WINE_PACKAGE=winehq-stable:amd64 22 | addons: 23 | apt: 24 | packages: 25 | - binutils-mingw-w64-x86-64 26 | - mingw-w64-x86-64-dev 27 | - g++-mingw-w64-x86-64 28 | - gcc-mingw-w64-x86-64 29 | - env: PLATFORM=i686 UNICODE=1 CMAKE= WINE_PACKAGE=winehq-stable:i386 30 | addons: 31 | apt: 32 | packages: 33 | - binutils-mingw-w64-i686 34 | - mingw-w64-i686-dev 35 | - g++-mingw-w64-i686 36 | - gcc-mingw-w64-i686 37 | - env: PLATFORM=x86_64 UNICODE= CMAKE=1 WINE_PACKAGE=winehq-stable:amd64 38 | addons: 39 | apt: 40 | packages: 41 | - binutils-mingw-w64-x86-64 42 | - mingw-w64-x86-64-dev 43 | - g++-mingw-w64-x86-64 44 | - gcc-mingw-w64-x86-64 45 | - env: PLATFORM=i686 UNICODE= CMAKE=1 WINE_PACKAGE=winehq-stable:i386 46 | addons: 47 | apt: 48 | packages: 49 | - binutils-mingw-w64-i686 50 | - cmake 51 | - mingw-w64-i686-dev 52 | - g++-mingw-w64-i686 53 | - gcc-mingw-w64-i686 54 | - env: PLATFORM=x86_64 UNICODE=1 CMAKE=1 WINE_PACKAGE=winehq-stable:amd64 55 | addons: 56 | apt: 57 | packages: 58 | - binutils-mingw-w64-x86-64 59 | - cmake 60 | - mingw-w64-x86-64-dev 61 | - g++-mingw-w64-x86-64 62 | - gcc-mingw-w64-x86-64 63 | - env: PLATFORM=i686 UNICODE=1 CMAKE=1 WINE_PACKAGE=winehq-stable:i386 64 | addons: 65 | apt: 66 | packages: 67 | - binutils-mingw-w64-i686 68 | - cmake 69 | - mingw-w64-i686-dev 70 | - g++-mingw-w64-i686 71 | - gcc-mingw-w64-i686 72 | 73 | language: cpp 74 | 75 | dist: xenial 76 | 77 | cache: 78 | - apt 79 | - ccache 80 | 81 | before_script: 82 | - curl https://dl.winehq.org/wine-builds/winehq.key | sudo apt-key add - 83 | - echo "deb https://dl.winehq.org/wine-builds/ubuntu/ xenial main" | sudo tee /etc/apt/sources.list.d/winehq.list 84 | - sudo apt-get -y update && sudo apt-get -y install --install-recommends $WINE_PACKAGE 85 | - if [ ! -z "$CMAKE" ]; then cmake -DPLATFORM=$PLATFORM -DUNICODE=$UNICODE -DTESTSUITE=ON -H. -B.; fi 86 | 87 | script: 88 | - if [ -z "$CMAKE" ]; then make PLATFORM=$PLATFORM UNICODE=$UNICODE; fi 89 | - if [ -z "$CMAKE" ]; then make test PLATFORM=$PLATFORM UNICODE=$UNICODE; fi 90 | - if [ ! -z "$CMAKE" ]; then cmake --build .; fi 91 | - cd example/DllLoader 92 | - ../../tests/runwine.sh $PLATFORM ./DllLoader.exe 93 | - ../../tests/runwine.sh $PLATFORM ./DllLoaderLoader.exe 94 | - cd ../../tests 95 | - ./runwine.sh $PLATFORM ./TestSuite.exe 96 | -------------------------------------------------------------------------------- /example/DllLoader/DllLoader.vcproj: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 11 | 12 | 13 | 19 | 30 | 32 | 40 | 42 | 44 | 46 | 48 | 50 | 52 | 54 | 56 | 58 | 60 | 61 | 67 | 75 | 77 | 86 | 88 | 90 | 92 | 94 | 96 | 98 | 100 | 102 | 104 | 106 | 107 | 108 | 109 | 110 | 111 | 115 | 117 | 118 | 120 | 121 | 122 | 126 | 128 | 129 | 130 | 134 | 135 | 136 | 137 | 138 | 139 | -------------------------------------------------------------------------------- /example/SampleDLL/SampleDLL.vcproj: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 11 | 12 | 13 | 19 | 30 | 32 | 41 | 43 | 45 | 47 | 49 | 51 | 53 | 55 | 57 | 59 | 61 | 62 | 68 | 76 | 78 | 88 | 90 | 92 | 94 | 96 | 98 | 100 | 102 | 104 | 106 | 108 | 109 | 110 | 111 | 112 | 113 | 117 | 119 | 120 | 121 | 125 | 127 | 128 | 129 | 133 | 134 | 135 | 136 | 137 | 138 | -------------------------------------------------------------------------------- /MemoryModule.h: -------------------------------------------------------------------------------- 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.h 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 | 27 | #ifndef __MEMORY_MODULE_HEADER 28 | #define __MEMORY_MODULE_HEADER 29 | 30 | #include 31 | 32 | typedef void *HMEMORYMODULE; 33 | 34 | typedef void *HMEMORYRSRC; 35 | 36 | typedef void *HCUSTOMMODULE; 37 | 38 | #ifdef __cplusplus 39 | extern "C" { 40 | #endif 41 | 42 | typedef LPVOID (*CustomAllocFunc)(LPVOID, SIZE_T, DWORD, DWORD, void*); 43 | typedef BOOL (*CustomFreeFunc)(LPVOID, SIZE_T, DWORD, void*); 44 | typedef HCUSTOMMODULE (*CustomLoadLibraryFunc)(LPCSTR, void *); 45 | typedef FARPROC (*CustomGetProcAddressFunc)(HCUSTOMMODULE, LPCSTR, void *); 46 | typedef void (*CustomFreeLibraryFunc)(HCUSTOMMODULE, void *); 47 | 48 | /** 49 | * Load EXE/DLL from memory location with the given size. 50 | * 51 | * All dependencies are resolved using default LoadLibrary/GetProcAddress 52 | * calls through the Windows API. 53 | */ 54 | HMEMORYMODULE MemoryLoadLibrary(const void *, size_t); 55 | 56 | /** 57 | * Load EXE/DLL from memory location with the given size using custom dependency 58 | * resolvers. 59 | * 60 | * Dependencies will be resolved using passed callback methods. 61 | */ 62 | HMEMORYMODULE MemoryLoadLibraryEx(const void *, size_t, 63 | CustomAllocFunc, 64 | CustomFreeFunc, 65 | CustomLoadLibraryFunc, 66 | CustomGetProcAddressFunc, 67 | CustomFreeLibraryFunc, 68 | void *); 69 | 70 | /** 71 | * Get address of exported method. Supports loading both by name and by 72 | * ordinal value. 73 | */ 74 | FARPROC MemoryGetProcAddress(HMEMORYMODULE, LPCSTR); 75 | 76 | /** 77 | * Free previously loaded EXE/DLL. 78 | */ 79 | void MemoryFreeLibrary(HMEMORYMODULE); 80 | 81 | /** 82 | * Execute entry point (EXE only). The entry point can only be executed 83 | * if the EXE has been loaded to the correct base address or it could 84 | * be relocated (i.e. relocation information have not been stripped by 85 | * the linker). 86 | * 87 | * Important: calling this function will not return, i.e. once the loaded 88 | * EXE finished running, the process will terminate. 89 | * 90 | * Returns a negative value if the entry point could not be executed. 91 | */ 92 | int MemoryCallEntryPoint(HMEMORYMODULE); 93 | 94 | /** 95 | * Find the location of a resource with the specified type and name. 96 | */ 97 | HMEMORYRSRC MemoryFindResource(HMEMORYMODULE, LPCTSTR, LPCTSTR); 98 | 99 | /** 100 | * Find the location of a resource with the specified type, name and language. 101 | */ 102 | HMEMORYRSRC MemoryFindResourceEx(HMEMORYMODULE, LPCTSTR, LPCTSTR, WORD); 103 | 104 | /** 105 | * Get the size of the resource in bytes. 106 | */ 107 | DWORD MemorySizeofResource(HMEMORYMODULE, HMEMORYRSRC); 108 | 109 | /** 110 | * Get a pointer to the contents of the resource. 111 | */ 112 | LPVOID MemoryLoadResource(HMEMORYMODULE, HMEMORYRSRC); 113 | 114 | /** 115 | * Load a string resource. 116 | */ 117 | int MemoryLoadString(HMEMORYMODULE, UINT, LPTSTR, int); 118 | 119 | /** 120 | * Load a string resource with a given language. 121 | */ 122 | int MemoryLoadStringEx(HMEMORYMODULE, UINT, LPTSTR, int, WORD); 123 | 124 | /** 125 | * Default implementation of CustomAllocFunc that calls VirtualAlloc 126 | * internally to allocate memory for a library 127 | * 128 | * This is the default as used by MemoryLoadLibrary. 129 | */ 130 | LPVOID MemoryDefaultAlloc(LPVOID, SIZE_T, DWORD, DWORD, void *); 131 | 132 | /** 133 | * Default implementation of CustomFreeFunc that calls VirtualFree 134 | * internally to free the memory used by a library 135 | * 136 | * This is the default as used by MemoryLoadLibrary. 137 | */ 138 | BOOL MemoryDefaultFree(LPVOID, SIZE_T, DWORD, void *); 139 | 140 | /** 141 | * Default implementation of CustomLoadLibraryFunc that calls LoadLibraryA 142 | * internally to load an additional libary. 143 | * 144 | * This is the default as used by MemoryLoadLibrary. 145 | */ 146 | HCUSTOMMODULE MemoryDefaultLoadLibrary(LPCSTR, void *); 147 | 148 | /** 149 | * Default implementation of CustomGetProcAddressFunc that calls GetProcAddress 150 | * internally to get the address of an exported function. 151 | * 152 | * This is the default as used by MemoryLoadLibrary. 153 | */ 154 | FARPROC MemoryDefaultGetProcAddress(HCUSTOMMODULE, LPCSTR, void *); 155 | 156 | /** 157 | * Default implementation of CustomFreeLibraryFunc that calls FreeLibrary 158 | * internally to release an additional libary. 159 | * 160 | * This is the default as used by MemoryLoadLibrary. 161 | */ 162 | void MemoryDefaultFreeLibrary(HCUSTOMMODULE, void *); 163 | 164 | #ifdef __cplusplus 165 | } 166 | #endif 167 | 168 | #endif // __MEMORY_MODULE_HEADER 169 | -------------------------------------------------------------------------------- /tests/LoadDll.cpp: -------------------------------------------------------------------------------- 1 | #define WIN32_LEAN_AND_MEAN 2 | #ifndef _CRT_SECURE_NO_WARNINGS 3 | #define _CRT_SECURE_NO_WARNINGS 4 | #endif 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "../MemoryModule.h" 13 | 14 | typedef int (*addProc)(int); 15 | typedef int (*addNumberProc)(int, int); 16 | 17 | // Thanks to Tim Cooper (from http://stackoverflow.com/a/8584708) 18 | const char *sstrstr(const char *haystack, const char *needle, size_t length) { 19 | size_t needle_length = strlen(needle); 20 | size_t i; 21 | 22 | for (i = 0; i < length; i++) { 23 | if (i + needle_length > length) { 24 | return NULL; 25 | } 26 | 27 | if (strncmp(&haystack[i], needle, needle_length) == 0) { 28 | return &haystack[i]; 29 | } 30 | } 31 | return NULL; 32 | } 33 | 34 | const wchar_t *swcsstr(const wchar_t *haystack, const wchar_t *needle, size_t length) { 35 | size_t needle_length = wcslen(needle); 36 | size_t i; 37 | 38 | for (i = 0; i < length; i++) { 39 | if (i + needle_length > length) { 40 | return NULL; 41 | } 42 | 43 | if (wcsncmp(&haystack[i], needle, needle_length) == 0) { 44 | return &haystack[i]; 45 | } 46 | } 47 | return NULL; 48 | } 49 | 50 | BOOL CheckResourceStrings(LPVOID data, DWORD size, const char *first, const wchar_t *second) { 51 | const char *first_pos; 52 | const wchar_t *second_pos; 53 | const wchar_t *src; 54 | 55 | if (data == NULL || size == 0) { 56 | return FALSE; 57 | } 58 | 59 | first_pos = sstrstr((const char *) data, first, size); 60 | if (first_pos == NULL) { 61 | fprintf(stderr, "ERROR: data doesn't start with %s\n", first); 62 | return FALSE; 63 | } 64 | 65 | src = (const wchar_t *) (((const char *) data) + strlen(first) + 1); 66 | second_pos = swcsstr(src, second, (size - strlen(first) - 1) / sizeof(wchar_t)); 67 | if (second_pos == NULL) { 68 | fwprintf(stderr, L"ERROR: data doesn't continue with %s\n", second); 69 | return FALSE; 70 | } 71 | 72 | return TRUE; 73 | } 74 | 75 | BOOL LoadFromMemory(char *filename) 76 | { 77 | FILE *fp; 78 | unsigned char *data=NULL; 79 | long size; 80 | size_t read; 81 | HMEMORYMODULE handle = NULL; 82 | addNumberProc addNumber; 83 | addNumberProc addNumber2; 84 | HMEMORYRSRC resourceInfo; 85 | DWORD resourceSize; 86 | LPVOID resourceData; 87 | TCHAR buffer[100]; 88 | BOOL result = TRUE; 89 | 90 | fp = fopen(filename, "rb"); 91 | if (fp == NULL) 92 | { 93 | printf("Can't open DLL file \"%s\".", filename); 94 | result = FALSE; 95 | goto exit; 96 | } 97 | 98 | fseek(fp, 0, SEEK_END); 99 | size = ftell(fp); 100 | assert(size > 0); 101 | data = (unsigned char *)malloc(size); 102 | assert(data != NULL); 103 | fseek(fp, 0, SEEK_SET); 104 | read = fread(data, 1, size, fp); 105 | assert(read == static_cast(size)); 106 | fclose(fp); 107 | 108 | handle = MemoryLoadLibrary(data, size); 109 | if (handle == NULL) 110 | { 111 | _tprintf(_T("Can't load library from memory.\n")); 112 | result = FALSE; 113 | goto exit; 114 | } 115 | 116 | addNumber = (addNumberProc)MemoryGetProcAddress(handle, NULL); 117 | if (addNumber != NULL) { 118 | _tprintf(_T("MemoryGetProcAddress(NULL) returned %p\n"), addNumber); 119 | result = FALSE; 120 | goto exit; 121 | } 122 | 123 | addNumber = (addNumberProc)MemoryGetProcAddress(handle, reinterpret_cast(0xff)); 124 | if (addNumber != NULL) { 125 | _tprintf(_T("MemoryGetProcAddress(0xff) returned %p\n"), addNumber); 126 | result = FALSE; 127 | goto exit; 128 | } 129 | 130 | addNumber = (addNumberProc)MemoryGetProcAddress(handle, "addNumbers"); 131 | if (!addNumber) { 132 | _tprintf(_T("MemoryGetProcAddress(\"addNumber\") returned NULL\n")); 133 | result = FALSE; 134 | goto exit; 135 | } 136 | _tprintf(_T("From memory: %d\n"), addNumber(1, 2)); 137 | 138 | // the DLL only exports one function, try to load by ordinal value 139 | addNumber2 = (addNumberProc)MemoryGetProcAddress(handle, reinterpret_cast(0x01)); 140 | if (addNumber != addNumber2) { 141 | _tprintf(_T("MemoryGetProcAddress(0x01) returned %p (expected %p)\n"), addNumber2, addNumber); 142 | result = FALSE; 143 | goto exit; 144 | } 145 | 146 | resourceInfo = MemoryFindResource(handle, MAKEINTRESOURCE(VS_VERSION_INFO), RT_VERSION); 147 | _tprintf(_T("MemoryFindResource returned 0x%p\n"), resourceInfo); 148 | 149 | if (resourceInfo != NULL) { 150 | resourceSize = MemorySizeofResource(handle, resourceInfo); 151 | resourceData = MemoryLoadResource(handle, resourceInfo); 152 | _tprintf(_T("Memory resource data: %ld bytes at 0x%p\n"), resourceSize, resourceData); 153 | 154 | MemoryLoadString(handle, 1, buffer, sizeof(buffer)); 155 | _tprintf(_T("String1: %s\n"), buffer); 156 | 157 | MemoryLoadString(handle, 20, buffer, sizeof(buffer)); 158 | _tprintf(_T("String2: %s\n"), buffer); 159 | } else { 160 | result = FALSE; 161 | } 162 | 163 | resourceInfo = MemoryFindResource(handle, _T("stringres"), RT_RCDATA); 164 | _tprintf(_T("MemoryFindResource returned 0x%p\n"), resourceInfo); 165 | if (resourceInfo != NULL) { 166 | resourceSize = MemorySizeofResource(handle, resourceInfo); 167 | resourceData = MemoryLoadResource(handle, resourceInfo); 168 | 169 | _tprintf(_T("Memory resource data: %ld bytes at 0x%p\n"), resourceSize, resourceData); 170 | if (!CheckResourceStrings(resourceData, resourceSize, "This is a ANSI string", L"This is a UNICODE string")) { 171 | result = FALSE; 172 | } 173 | } else { 174 | result = FALSE; 175 | } 176 | 177 | resourceInfo = MemoryFindResource(handle, _T("stringres1"), RT_RCDATA); 178 | _tprintf(_T("MemoryFindResource returned 0x%p\n"), resourceInfo); 179 | if (resourceInfo != NULL) { 180 | resourceSize = MemorySizeofResource(handle, resourceInfo); 181 | resourceData = MemoryLoadResource(handle, resourceInfo); 182 | 183 | _tprintf(_T("Memory resource data: %ld bytes at 0x%p\n"), resourceSize, resourceData); 184 | if (!CheckResourceStrings(resourceData, resourceSize, "This is ANSI string 1", L"This is UNICODE string 1")) { 185 | result = FALSE; 186 | } 187 | } else { 188 | result = FALSE; 189 | } 190 | 191 | 192 | resourceInfo = MemoryFindResource(handle, _T("stringres2"), RT_RCDATA); 193 | _tprintf(_T("MemoryFindResource returned 0x%p\n"), resourceInfo); 194 | if (resourceInfo != NULL) { 195 | resourceSize = MemorySizeofResource(handle, resourceInfo); 196 | resourceData = MemoryLoadResource(handle, resourceInfo); 197 | 198 | _tprintf(_T("Memory resource data: %ld bytes at 0x%p\n"), resourceSize, resourceData); 199 | if (!CheckResourceStrings(resourceData, resourceSize, "This is ANSI string 2", L"This is UNICODE string 2")) { 200 | result = FALSE; 201 | } 202 | } else { 203 | result = FALSE; 204 | } 205 | 206 | 207 | resourceInfo = MemoryFindResource(handle, _T("stringres3"), RT_RCDATA); 208 | _tprintf(_T("MemoryFindResource returned 0x%p\n"), resourceInfo); 209 | if (resourceInfo != NULL) { 210 | resourceSize = MemorySizeofResource(handle, resourceInfo); 211 | resourceData = MemoryLoadResource(handle, resourceInfo); 212 | 213 | _tprintf(_T("Memory resource data: %ld bytes at 0x%p\n"), resourceSize, resourceData); 214 | if (!CheckResourceStrings(resourceData, resourceSize, "This is ANSI string 3", L"This is UNICODE string 3")) { 215 | result = FALSE; 216 | } 217 | } else { 218 | result = FALSE; 219 | } 220 | 221 | exit: 222 | MemoryFreeLibrary(handle); 223 | free(data); 224 | return result; 225 | } 226 | 227 | BOOL LoadExportsFromMemory(char *filename) 228 | { 229 | FILE *fp; 230 | unsigned char *data=NULL; 231 | long size; 232 | size_t read; 233 | HMEMORYMODULE handle = NULL; 234 | int i; 235 | BOOL result = TRUE; 236 | 237 | fp = fopen(filename, "rb"); 238 | if (fp == NULL) 239 | { 240 | printf("Can't open DLL file \"%s\".", filename); 241 | result = FALSE; 242 | goto exit; 243 | } 244 | 245 | fseek(fp, 0, SEEK_END); 246 | size = ftell(fp); 247 | assert(size > 0); 248 | data = (unsigned char *)malloc(size); 249 | assert(data != NULL); 250 | fseek(fp, 0, SEEK_SET); 251 | read = fread(data, 1, size, fp); 252 | assert(read == static_cast(size)); 253 | fclose(fp); 254 | 255 | handle = MemoryLoadLibrary(data, size); 256 | if (handle == NULL) 257 | { 258 | _tprintf(_T("Can't load library from memory.\n")); 259 | result = FALSE; 260 | goto exit; 261 | } 262 | 263 | for (i = 1; i <= 100; i++) { 264 | char name[100]; 265 | sprintf(name, "add%d", i); 266 | addProc addNumber = (addProc)MemoryGetProcAddress(handle, name); 267 | if (!addNumber) { 268 | _tprintf(_T("MemoryGetProcAddress(\"%s\") returned NULL\n"), name); 269 | result = FALSE; 270 | goto exit; 271 | } 272 | int result = addNumber(1); 273 | if (result != 1 + i) { 274 | _tprintf(_T("(\"%s\") returned %d, expected %d\n"), name, result, 1 + i); 275 | result = FALSE; 276 | goto exit; 277 | } 278 | _tprintf(_T("%s: %d\n"), name, result); 279 | } 280 | exit: 281 | MemoryFreeLibrary(handle); 282 | free(data); 283 | return result; 284 | } 285 | 286 | int main(int argc, char* argv[]) 287 | { 288 | if (argc < 2) { 289 | fprintf(stderr, "USAGE: %s \n", argv[0]); 290 | return 1; 291 | } 292 | 293 | if (!strstr((const char *) argv[1], "exports")) { 294 | if (!LoadFromMemory(argv[1])) { 295 | return 2; 296 | } 297 | } else { 298 | if (!LoadExportsFromMemory(argv[1])) { 299 | return 2; 300 | } 301 | } 302 | 303 | return 0; 304 | } 305 | -------------------------------------------------------------------------------- /example/DllLoader/DllLoader.cpp: -------------------------------------------------------------------------------- 1 | #define WIN32_LEAN_AND_MEAN 2 | #ifndef _CRT_SECURE_NO_WARNINGS 3 | #define _CRT_SECURE_NO_WARNINGS 4 | #endif 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "../../MemoryModule.h" 13 | 14 | typedef int (*addNumberProc)(int, int); 15 | 16 | #define DLL_FILE TEXT("..\\SampleDLL\\SampleDLL.dll") 17 | 18 | void LoadFromFile(void) 19 | { 20 | addNumberProc addNumber; 21 | HRSRC resourceInfo; 22 | DWORD resourceSize; 23 | LPVOID resourceData; 24 | TCHAR buffer[100]; 25 | 26 | HINSTANCE handle = LoadLibrary(DLL_FILE); 27 | if (handle == NULL) 28 | return; 29 | 30 | addNumber = (addNumberProc)GetProcAddress(handle, "addNumbers"); 31 | _tprintf(_T("From file: %d\n"), addNumber(1, 2)); 32 | 33 | resourceInfo = FindResource(handle, MAKEINTRESOURCE(VS_VERSION_INFO), RT_VERSION); 34 | _tprintf(_T("FindResource returned 0x%p\n"), resourceInfo); 35 | 36 | resourceSize = SizeofResource(handle, resourceInfo); 37 | resourceData = LoadResource(handle, resourceInfo); 38 | _tprintf(_T("Resource data: %ld bytes at 0x%p\n"), resourceSize, resourceData); 39 | 40 | LoadString(handle, 1, buffer, sizeof(buffer)); 41 | _tprintf(_T("String1: %s\n"), buffer); 42 | 43 | LoadString(handle, 20, buffer, sizeof(buffer)); 44 | _tprintf(_T("String2: %s\n"), buffer); 45 | 46 | FreeLibrary(handle); 47 | } 48 | 49 | void* ReadLibrary(size_t* pSize) { 50 | size_t read; 51 | void* result; 52 | FILE* fp; 53 | 54 | fp = _tfopen(DLL_FILE, _T("rb")); 55 | if (fp == NULL) 56 | { 57 | _tprintf(_T("Can't open DLL file \"%s\"."), DLL_FILE); 58 | return NULL; 59 | } 60 | 61 | fseek(fp, 0, SEEK_END); 62 | *pSize = static_cast(ftell(fp)); 63 | if (*pSize == 0) 64 | { 65 | fclose(fp); 66 | return NULL; 67 | } 68 | 69 | result = (unsigned char *)malloc(*pSize); 70 | if (result == NULL) 71 | { 72 | return NULL; 73 | } 74 | 75 | fseek(fp, 0, SEEK_SET); 76 | read = fread(result, 1, *pSize, fp); 77 | fclose(fp); 78 | if (read != *pSize) 79 | { 80 | free(result); 81 | return NULL; 82 | } 83 | 84 | return result; 85 | } 86 | 87 | void LoadFromMemory(void) 88 | { 89 | void *data; 90 | size_t size; 91 | HMEMORYMODULE handle; 92 | addNumberProc addNumber; 93 | HMEMORYRSRC resourceInfo; 94 | DWORD resourceSize; 95 | LPVOID resourceData; 96 | TCHAR buffer[100]; 97 | 98 | data = ReadLibrary(&size); 99 | if (data == NULL) 100 | { 101 | return; 102 | } 103 | 104 | handle = MemoryLoadLibrary(data, size); 105 | if (handle == NULL) 106 | { 107 | _tprintf(_T("Can't load library from memory.\n")); 108 | goto exit; 109 | } 110 | 111 | addNumber = (addNumberProc)MemoryGetProcAddress(handle, "addNumbers"); 112 | _tprintf(_T("From memory: %d\n"), addNumber(1, 2)); 113 | 114 | resourceInfo = MemoryFindResource(handle, MAKEINTRESOURCE(VS_VERSION_INFO), RT_VERSION); 115 | _tprintf(_T("MemoryFindResource returned 0x%p\n"), resourceInfo); 116 | 117 | resourceSize = MemorySizeofResource(handle, resourceInfo); 118 | resourceData = MemoryLoadResource(handle, resourceInfo); 119 | _tprintf(_T("Memory resource data: %ld bytes at 0x%p\n"), resourceSize, resourceData); 120 | 121 | MemoryLoadString(handle, 1, buffer, sizeof(buffer)); 122 | _tprintf(_T("String1: %s\n"), buffer); 123 | 124 | MemoryLoadString(handle, 20, buffer, sizeof(buffer)); 125 | _tprintf(_T("String2: %s\n"), buffer); 126 | 127 | MemoryFreeLibrary(handle); 128 | 129 | exit: 130 | free(data); 131 | } 132 | 133 | #define MAX_CALLS 20 134 | 135 | struct CallList { 136 | int current_alloc_call, current_free_call; 137 | CustomAllocFunc alloc_calls[MAX_CALLS]; 138 | CustomFreeFunc free_calls[MAX_CALLS]; 139 | }; 140 | 141 | LPVOID MemoryFailingAlloc(LPVOID address, SIZE_T size, DWORD allocationType, DWORD protect, void* userdata) 142 | { 143 | UNREFERENCED_PARAMETER(address); 144 | UNREFERENCED_PARAMETER(size); 145 | UNREFERENCED_PARAMETER(allocationType); 146 | UNREFERENCED_PARAMETER(protect); 147 | UNREFERENCED_PARAMETER(userdata); 148 | return NULL; 149 | } 150 | 151 | LPVOID MemoryMockAlloc(LPVOID address, SIZE_T size, DWORD allocationType, DWORD protect, void* userdata) 152 | { 153 | CallList* calls = (CallList*)userdata; 154 | CustomAllocFunc current_func = calls->alloc_calls[calls->current_alloc_call++]; 155 | assert(current_func != NULL); 156 | return current_func(address, size, allocationType, protect, NULL); 157 | } 158 | 159 | BOOL MemoryMockFree(LPVOID lpAddress, SIZE_T dwSize, DWORD dwFreeType, void* userdata) 160 | { 161 | CallList* calls = (CallList*)userdata; 162 | CustomFreeFunc current_func = calls->free_calls[calls->current_free_call++]; 163 | assert(current_func != NULL); 164 | return current_func(lpAddress, dwSize, dwFreeType, NULL); 165 | } 166 | 167 | void InitFuncs(void** funcs, va_list args) { 168 | for (int i = 0; ; i++) { 169 | assert(i < MAX_CALLS); 170 | funcs[i] = va_arg(args, void*); 171 | if (funcs[i] == NULL) break; 172 | } 173 | } 174 | 175 | void InitAllocFuncs(CallList* calls, ...) { 176 | va_list args; 177 | va_start(args, calls); 178 | InitFuncs((void**)calls->alloc_calls, args); 179 | va_end(args); 180 | calls->current_alloc_call = 0; 181 | } 182 | 183 | void InitFreeFuncs(CallList* calls, ...) { 184 | va_list args; 185 | va_start(args, calls); 186 | InitFuncs((void**)calls->free_calls, args); 187 | va_end(args); 188 | calls->current_free_call = 0; 189 | } 190 | 191 | void InitFreeFunc(CallList* calls, CustomFreeFunc freeFunc) { 192 | for (int i = 0; i < MAX_CALLS; i++) { 193 | calls->free_calls[i] = freeFunc; 194 | } 195 | calls->current_free_call = 0; 196 | } 197 | 198 | void TestFailingAllocation(void *data, size_t size) { 199 | CallList expected_calls; 200 | HMEMORYMODULE handle; 201 | 202 | InitAllocFuncs(&expected_calls, MemoryFailingAlloc, MemoryFailingAlloc, NULL); 203 | InitFreeFuncs(&expected_calls, NULL); 204 | 205 | handle = MemoryLoadLibraryEx( 206 | data, size, MemoryMockAlloc, MemoryMockFree, MemoryDefaultLoadLibrary, 207 | MemoryDefaultGetProcAddress, MemoryDefaultFreeLibrary, &expected_calls); 208 | 209 | assert(handle == NULL); 210 | assert(GetLastError() == ERROR_OUTOFMEMORY); 211 | assert(expected_calls.current_free_call == 0); 212 | 213 | MemoryFreeLibrary(handle); 214 | assert(expected_calls.current_free_call == 0); 215 | } 216 | 217 | void TestCleanupAfterFailingAllocation(void *data, size_t size) { 218 | CallList expected_calls; 219 | HMEMORYMODULE handle; 220 | int free_calls_after_loading; 221 | 222 | InitAllocFuncs(&expected_calls, 223 | MemoryDefaultAlloc, 224 | MemoryDefaultAlloc, 225 | MemoryDefaultAlloc, 226 | MemoryDefaultAlloc, 227 | MemoryFailingAlloc, 228 | NULL); 229 | InitFreeFuncs(&expected_calls, MemoryDefaultFree, NULL); 230 | 231 | handle = MemoryLoadLibraryEx( 232 | data, size, MemoryMockAlloc, MemoryMockFree, MemoryDefaultLoadLibrary, 233 | MemoryDefaultGetProcAddress, MemoryDefaultFreeLibrary, &expected_calls); 234 | 235 | free_calls_after_loading = expected_calls.current_free_call; 236 | 237 | MemoryFreeLibrary(handle); 238 | assert(expected_calls.current_free_call == free_calls_after_loading); 239 | } 240 | 241 | void TestFreeAfterDefaultAlloc(void *data, size_t size) { 242 | CallList expected_calls; 243 | HMEMORYMODULE handle; 244 | int free_calls_after_loading; 245 | 246 | // Note: free might get called internally multiple times 247 | InitFreeFunc(&expected_calls, MemoryDefaultFree); 248 | 249 | handle = MemoryLoadLibraryEx( 250 | data, size, MemoryDefaultAlloc, MemoryMockFree, MemoryDefaultLoadLibrary, 251 | MemoryDefaultGetProcAddress, MemoryDefaultFreeLibrary, &expected_calls); 252 | 253 | assert(handle != NULL); 254 | free_calls_after_loading = expected_calls.current_free_call; 255 | 256 | MemoryFreeLibrary(handle); 257 | assert(expected_calls.current_free_call == free_calls_after_loading + 1); 258 | } 259 | 260 | #ifdef _WIN64 261 | 262 | LPVOID MemoryAllocHigh(LPVOID address, SIZE_T size, DWORD allocationType, DWORD protect, void* userdata) 263 | { 264 | int* counter = static_cast(userdata); 265 | if (*counter == 0) { 266 | // Make sure the image gets loaded to an address above 32bit. 267 | uintptr_t offset = 0x10000000000; 268 | address = (LPVOID) ((uintptr_t) address + offset); 269 | } 270 | (*counter)++; 271 | return MemoryDefaultAlloc(address, size, allocationType, protect, NULL); 272 | } 273 | 274 | void TestAllocHighMemory(void *data, size_t size) { 275 | HMEMORYMODULE handle; 276 | int counter = 0; 277 | addNumberProc addNumber; 278 | HMEMORYRSRC resourceInfo; 279 | DWORD resourceSize; 280 | LPVOID resourceData; 281 | TCHAR buffer[100]; 282 | 283 | handle = MemoryLoadLibraryEx( 284 | data, size, MemoryAllocHigh, MemoryDefaultFree, MemoryDefaultLoadLibrary, 285 | MemoryDefaultGetProcAddress, MemoryDefaultFreeLibrary, &counter); 286 | 287 | assert(handle != NULL); 288 | 289 | addNumber = (addNumberProc)MemoryGetProcAddress(handle, "addNumbers"); 290 | _tprintf(_T("From memory: %d\n"), addNumber(1, 2)); 291 | 292 | resourceInfo = MemoryFindResource(handle, MAKEINTRESOURCE(VS_VERSION_INFO), RT_VERSION); 293 | _tprintf(_T("MemoryFindResource returned 0x%p\n"), resourceInfo); 294 | 295 | resourceSize = MemorySizeofResource(handle, resourceInfo); 296 | resourceData = MemoryLoadResource(handle, resourceInfo); 297 | _tprintf(_T("Memory resource data: %ld bytes at 0x%p\n"), resourceSize, resourceData); 298 | 299 | MemoryLoadString(handle, 1, buffer, sizeof(buffer)); 300 | _tprintf(_T("String1: %s\n"), buffer); 301 | 302 | MemoryLoadString(handle, 20, buffer, sizeof(buffer)); 303 | _tprintf(_T("String2: %s\n"), buffer); 304 | 305 | MemoryFreeLibrary(handle); 306 | } 307 | #endif // _WIN64 308 | 309 | void TestCustomAllocAndFree(void) 310 | { 311 | void *data; 312 | size_t size; 313 | 314 | data = ReadLibrary(&size); 315 | if (data == NULL) 316 | { 317 | return; 318 | } 319 | 320 | _tprintf(_T("Test MemoryLoadLibraryEx after initially failing allocation function\n")); 321 | TestFailingAllocation(data, size); 322 | _tprintf(_T("Test cleanup after MemoryLoadLibraryEx with failing allocation function\n")); 323 | TestCleanupAfterFailingAllocation(data, size); 324 | _tprintf(_T("Test custom free function after MemoryLoadLibraryEx\n")); 325 | TestFreeAfterDefaultAlloc(data, size); 326 | #ifdef _WIN64 327 | _tprintf(_T("Test allocating in high memory\n")); 328 | TestAllocHighMemory(data, size); 329 | #endif 330 | 331 | free(data); 332 | } 333 | 334 | int main() 335 | { 336 | LoadFromFile(); 337 | printf("\n\n"); 338 | LoadFromMemory(); 339 | printf("\n\n"); 340 | TestCustomAllocAndFree(); 341 | return 0; 342 | } 343 | 344 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Mozilla Public License Version 2.0 2 | ================================== 3 | 4 | 1. Definitions 5 | -------------- 6 | 7 | 1.1. "Contributor" 8 | means each individual or legal entity that creates, contributes to 9 | the creation of, or owns Covered Software. 10 | 11 | 1.2. "Contributor Version" 12 | means the combination of the Contributions of others (if any) used 13 | by a Contributor and that particular Contributor's Contribution. 14 | 15 | 1.3. "Contribution" 16 | means Covered Software of a particular Contributor. 17 | 18 | 1.4. "Covered Software" 19 | means Source Code Form to which the initial Contributor has attached 20 | the notice in Exhibit A, the Executable Form of such Source Code 21 | Form, and Modifications of such Source Code Form, in each case 22 | including portions thereof. 23 | 24 | 1.5. "Incompatible With Secondary Licenses" 25 | means 26 | 27 | (a) that the initial Contributor has attached the notice described 28 | in Exhibit B to the Covered Software; or 29 | 30 | (b) that the Covered Software was made available under the terms of 31 | version 1.1 or earlier of the License, but not also under the 32 | terms of a Secondary License. 33 | 34 | 1.6. "Executable Form" 35 | means any form of the work other than Source Code Form. 36 | 37 | 1.7. "Larger Work" 38 | means a work that combines Covered Software with other material, in 39 | a separate file or files, that is not Covered Software. 40 | 41 | 1.8. "License" 42 | means this document. 43 | 44 | 1.9. "Licensable" 45 | means having the right to grant, to the maximum extent possible, 46 | whether at the time of the initial grant or subsequently, any and 47 | all of the rights conveyed by this License. 48 | 49 | 1.10. "Modifications" 50 | means any of the following: 51 | 52 | (a) any file in Source Code Form that results from an addition to, 53 | deletion from, or modification of the contents of Covered 54 | Software; or 55 | 56 | (b) any new file in Source Code Form that contains any Covered 57 | Software. 58 | 59 | 1.11. "Patent Claims" of a Contributor 60 | means any patent claim(s), including without limitation, method, 61 | process, and apparatus claims, in any patent Licensable by such 62 | Contributor that would be infringed, but for the grant of the 63 | License, by the making, using, selling, offering for sale, having 64 | made, import, or transfer of either its Contributions or its 65 | Contributor Version. 66 | 67 | 1.12. "Secondary License" 68 | means either the GNU General Public License, Version 2.0, the GNU 69 | Lesser General Public License, Version 2.1, the GNU Affero General 70 | Public License, Version 3.0, or any later versions of those 71 | licenses. 72 | 73 | 1.13. "Source Code Form" 74 | means the form of the work preferred for making modifications. 75 | 76 | 1.14. "You" (or "Your") 77 | means an individual or a legal entity exercising rights under this 78 | License. For legal entities, "You" includes any entity that 79 | controls, is controlled by, or is under common control with You. For 80 | purposes of this definition, "control" means (a) the power, direct 81 | or indirect, to cause the direction or management of such entity, 82 | whether by contract or otherwise, or (b) ownership of more than 83 | fifty percent (50%) of the outstanding shares or beneficial 84 | ownership of such entity. 85 | 86 | 2. License Grants and Conditions 87 | -------------------------------- 88 | 89 | 2.1. Grants 90 | 91 | Each Contributor hereby grants You a world-wide, royalty-free, 92 | non-exclusive license: 93 | 94 | (a) under intellectual property rights (other than patent or trademark) 95 | Licensable by such Contributor to use, reproduce, make available, 96 | modify, display, perform, distribute, and otherwise exploit its 97 | Contributions, either on an unmodified basis, with Modifications, or 98 | as part of a Larger Work; and 99 | 100 | (b) under Patent Claims of such Contributor to make, use, sell, offer 101 | for sale, have made, import, and otherwise transfer either its 102 | Contributions or its Contributor Version. 103 | 104 | 2.2. Effective Date 105 | 106 | The licenses granted in Section 2.1 with respect to any Contribution 107 | become effective for each Contribution on the date the Contributor first 108 | distributes such Contribution. 109 | 110 | 2.3. Limitations on Grant Scope 111 | 112 | The licenses granted in this Section 2 are the only rights granted under 113 | this License. No additional rights or licenses will be implied from the 114 | distribution or licensing of Covered Software under this License. 115 | Notwithstanding Section 2.1(b) above, no patent license is granted by a 116 | Contributor: 117 | 118 | (a) for any code that a Contributor has removed from Covered Software; 119 | or 120 | 121 | (b) for infringements caused by: (i) Your and any other third party's 122 | modifications of Covered Software, or (ii) the combination of its 123 | Contributions with other software (except as part of its Contributor 124 | Version); or 125 | 126 | (c) under Patent Claims infringed by Covered Software in the absence of 127 | its Contributions. 128 | 129 | This License does not grant any rights in the trademarks, service marks, 130 | or logos of any Contributor (except as may be necessary to comply with 131 | the notice requirements in Section 3.4). 132 | 133 | 2.4. Subsequent Licenses 134 | 135 | No Contributor makes additional grants as a result of Your choice to 136 | distribute the Covered Software under a subsequent version of this 137 | License (see Section 10.2) or under the terms of a Secondary License (if 138 | permitted under the terms of Section 3.3). 139 | 140 | 2.5. Representation 141 | 142 | Each Contributor represents that the Contributor believes its 143 | Contributions are its original creation(s) or it has sufficient rights 144 | to grant the rights to its Contributions conveyed by this License. 145 | 146 | 2.6. Fair Use 147 | 148 | This License is not intended to limit any rights You have under 149 | applicable copyright doctrines of fair use, fair dealing, or other 150 | equivalents. 151 | 152 | 2.7. Conditions 153 | 154 | Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted 155 | in Section 2.1. 156 | 157 | 3. Responsibilities 158 | ------------------- 159 | 160 | 3.1. Distribution of Source Form 161 | 162 | All distribution of Covered Software in Source Code Form, including any 163 | Modifications that You create or to which You contribute, must be under 164 | the terms of this License. You must inform recipients that the Source 165 | Code Form of the Covered Software is governed by the terms of this 166 | License, and how they can obtain a copy of this License. You may not 167 | attempt to alter or restrict the recipients' rights in the Source Code 168 | Form. 169 | 170 | 3.2. Distribution of Executable Form 171 | 172 | If You distribute Covered Software in Executable Form then: 173 | 174 | (a) such Covered Software must also be made available in Source Code 175 | Form, as described in Section 3.1, and You must inform recipients of 176 | the Executable Form how they can obtain a copy of such Source Code 177 | Form by reasonable means in a timely manner, at a charge no more 178 | than the cost of distribution to the recipient; and 179 | 180 | (b) You may distribute such Executable Form under the terms of this 181 | License, or sublicense it under different terms, provided that the 182 | license for the Executable Form does not attempt to limit or alter 183 | the recipients' rights in the Source Code Form under this License. 184 | 185 | 3.3. Distribution of a Larger Work 186 | 187 | You may create and distribute a Larger Work under terms of Your choice, 188 | provided that You also comply with the requirements of this License for 189 | the Covered Software. If the Larger Work is a combination of Covered 190 | Software with a work governed by one or more Secondary Licenses, and the 191 | Covered Software is not Incompatible With Secondary Licenses, this 192 | License permits You to additionally distribute such Covered Software 193 | under the terms of such Secondary License(s), so that the recipient of 194 | the Larger Work may, at their option, further distribute the Covered 195 | Software under the terms of either this License or such Secondary 196 | License(s). 197 | 198 | 3.4. Notices 199 | 200 | You may not remove or alter the substance of any license notices 201 | (including copyright notices, patent notices, disclaimers of warranty, 202 | or limitations of liability) contained within the Source Code Form of 203 | the Covered Software, except that You may alter any license notices to 204 | the extent required to remedy known factual inaccuracies. 205 | 206 | 3.5. Application of Additional Terms 207 | 208 | You may choose to offer, and to charge a fee for, warranty, support, 209 | indemnity or liability obligations to one or more recipients of Covered 210 | Software. However, You may do so only on Your own behalf, and not on 211 | behalf of any Contributor. You must make it absolutely clear that any 212 | such warranty, support, indemnity, or liability obligation is offered by 213 | You alone, and You hereby agree to indemnify every Contributor for any 214 | liability incurred by such Contributor as a result of warranty, support, 215 | indemnity or liability terms You offer. You may include additional 216 | disclaimers of warranty and limitations of liability specific to any 217 | jurisdiction. 218 | 219 | 4. Inability to Comply Due to Statute or Regulation 220 | --------------------------------------------------- 221 | 222 | If it is impossible for You to comply with any of the terms of this 223 | License with respect to some or all of the Covered Software due to 224 | statute, judicial order, or regulation then You must: (a) comply with 225 | the terms of this License to the maximum extent possible; and (b) 226 | describe the limitations and the code they affect. Such description must 227 | be placed in a text file included with all distributions of the Covered 228 | Software under this License. Except to the extent prohibited by statute 229 | or regulation, such description must be sufficiently detailed for a 230 | recipient of ordinary skill to be able to understand it. 231 | 232 | 5. Termination 233 | -------------- 234 | 235 | 5.1. The rights granted under this License will terminate automatically 236 | if You fail to comply with any of its terms. However, if You become 237 | compliant, then the rights granted under this License from a particular 238 | Contributor are reinstated (a) provisionally, unless and until such 239 | Contributor explicitly and finally terminates Your grants, and (b) on an 240 | ongoing basis, if such Contributor fails to notify You of the 241 | non-compliance by some reasonable means prior to 60 days after You have 242 | come back into compliance. Moreover, Your grants from a particular 243 | Contributor are reinstated on an ongoing basis if such Contributor 244 | notifies You of the non-compliance by some reasonable means, this is the 245 | first time You have received notice of non-compliance with this License 246 | from such Contributor, and You become compliant prior to 30 days after 247 | Your receipt of the notice. 248 | 249 | 5.2. If You initiate litigation against any entity by asserting a patent 250 | infringement claim (excluding declaratory judgment actions, 251 | counter-claims, and cross-claims) alleging that a Contributor Version 252 | directly or indirectly infringes any patent, then the rights granted to 253 | You by any and all Contributors for the Covered Software under Section 254 | 2.1 of this License shall terminate. 255 | 256 | 5.3. In the event of termination under Sections 5.1 or 5.2 above, all 257 | end user license agreements (excluding distributors and resellers) which 258 | have been validly granted by You or Your distributors under this License 259 | prior to termination shall survive termination. 260 | 261 | ************************************************************************ 262 | * * 263 | * 6. Disclaimer of Warranty * 264 | * ------------------------- * 265 | * * 266 | * Covered Software is provided under this License on an "as is" * 267 | * basis, without warranty of any kind, either expressed, implied, or * 268 | * statutory, including, without limitation, warranties that the * 269 | * Covered Software is free of defects, merchantable, fit for a * 270 | * particular purpose or non-infringing. The entire risk as to the * 271 | * quality and performance of the Covered Software is with You. * 272 | * Should any Covered Software prove defective in any respect, You * 273 | * (not any Contributor) assume the cost of any necessary servicing, * 274 | * repair, or correction. This disclaimer of warranty constitutes an * 275 | * essential part of this License. No use of any Covered Software is * 276 | * authorized under this License except under this disclaimer. * 277 | * * 278 | ************************************************************************ 279 | 280 | ************************************************************************ 281 | * * 282 | * 7. Limitation of Liability * 283 | * -------------------------- * 284 | * * 285 | * Under no circumstances and under no legal theory, whether tort * 286 | * (including negligence), contract, or otherwise, shall any * 287 | * Contributor, or anyone who distributes Covered Software as * 288 | * permitted above, be liable to You for any direct, indirect, * 289 | * special, incidental, or consequential damages of any character * 290 | * including, without limitation, damages for lost profits, loss of * 291 | * goodwill, work stoppage, computer failure or malfunction, or any * 292 | * and all other commercial damages or losses, even if such party * 293 | * shall have been informed of the possibility of such damages. This * 294 | * limitation of liability shall not apply to liability for death or * 295 | * personal injury resulting from such party's negligence to the * 296 | * extent applicable law prohibits such limitation. Some * 297 | * jurisdictions do not allow the exclusion or limitation of * 298 | * incidental or consequential damages, so this exclusion and * 299 | * limitation may not apply to You. * 300 | * * 301 | ************************************************************************ 302 | 303 | 8. Litigation 304 | ------------- 305 | 306 | Any litigation relating to this License may be brought only in the 307 | courts of a jurisdiction where the defendant maintains its principal 308 | place of business and such litigation shall be governed by laws of that 309 | jurisdiction, without reference to its conflict-of-law provisions. 310 | Nothing in this Section shall prevent a party's ability to bring 311 | cross-claims or counter-claims. 312 | 313 | 9. Miscellaneous 314 | ---------------- 315 | 316 | This License represents the complete agreement concerning the subject 317 | matter hereof. If any provision of this License is held to be 318 | unenforceable, such provision shall be reformed only to the extent 319 | necessary to make it enforceable. Any law or regulation which provides 320 | that the language of a contract shall be construed against the drafter 321 | shall not be used to construe this License against a Contributor. 322 | 323 | 10. Versions of the License 324 | --------------------------- 325 | 326 | 10.1. New Versions 327 | 328 | Mozilla Foundation is the license steward. Except as provided in Section 329 | 10.3, no one other than the license steward has the right to modify or 330 | publish new versions of this License. Each version will be given a 331 | distinguishing version number. 332 | 333 | 10.2. Effect of New Versions 334 | 335 | You may distribute the Covered Software under the terms of the version 336 | of the License under which You originally received the Covered Software, 337 | or under the terms of any subsequent version published by the license 338 | steward. 339 | 340 | 10.3. Modified Versions 341 | 342 | If you create software not governed by this License, and you want to 343 | create a new license for such software, you may create and use a 344 | modified version of this License if you rename the license and remove 345 | any references to the name of the license steward (except to note that 346 | such modified license differs from this License). 347 | 348 | 10.4. Distributing Source Code Form that is Incompatible With Secondary 349 | Licenses 350 | 351 | If You choose to distribute Source Code Form that is Incompatible With 352 | Secondary Licenses under the terms of this version of the License, the 353 | notice described in Exhibit B of this License must be attached. 354 | 355 | Exhibit A - Source Code Form License Notice 356 | ------------------------------------------- 357 | 358 | This Source Code Form is subject to the terms of the Mozilla Public 359 | License, v. 2.0. If a copy of the MPL was not distributed with this 360 | file, You can obtain one at http://mozilla.org/MPL/2.0/. 361 | 362 | If it is not possible or desirable to put the notice in a particular 363 | file, then You may include the notice in a location (such as a LICENSE 364 | file in a relevant directory) where a recipient would be likely to look 365 | for such a notice. 366 | 367 | You may add additional accurate notices of copyright ownership. 368 | 369 | Exhibit B - "Incompatible With Secondary Licenses" Notice 370 | --------------------------------------------------------- 371 | 372 | This Source Code Form is "Incompatible With Secondary Licenses", as 373 | defined by the Mozilla Public License, v. 2.0. 374 | -------------------------------------------------------------------------------- /doc/readme.rst: -------------------------------------------------------------------------------- 1 | :author: Joachim Bauch 2 | :contact: mail@joachim-bauch.de 3 | :copyright: `Creative Commons License (by-sa)`__ 4 | 5 | __ http://creativecommons.org/licenses/by-sa/2.5/ 6 | 7 | 8 | .. contents:: 9 | 10 | 11 | Overview 12 | ========= 13 | 14 | The default windows API functions to load external libraries into a program 15 | (LoadLibrary, LoadLibraryEx) only work with files on the filesystem. It's 16 | therefore impossible to load a DLL from memory. 17 | But sometimes, you need exactly this functionality (e.g. you don't want to 18 | distribute a lot of files or want to make disassembling harder). Common 19 | workarounds for this problems are to write the DLL into a temporary file 20 | first and import it from there. When the program terminates, the temporary 21 | file gets deleted. 22 | 23 | In this tutorial, I will describe first, how DLL files are structured and 24 | will present some code that can be used to load a DLL completely from memory - 25 | without storing on the disk first. 26 | 27 | 28 | Windows executables - the PE format 29 | ==================================== 30 | 31 | Most windows binaries that can contain executable code (.exe, .dll, .sys) 32 | share a common file format that consists of the following parts: 33 | 34 | +----------------+ 35 | | DOS header | 36 | | | 37 | | DOS stub | 38 | +----------------+ 39 | | PE header | 40 | +----------------+ 41 | | Section header | 42 | +----------------+ 43 | | Section 1 | 44 | +----------------+ 45 | | Section 2 | 46 | +----------------+ 47 | | . . . | 48 | +----------------+ 49 | | Section n | 50 | +----------------+ 51 | 52 | All structures given below can be found in the header file `winnt.h`. 53 | 54 | 55 | DOS header / stub 56 | ------------------ 57 | 58 | The DOS header is only used for backwards compatibility. It precedes the DOS 59 | stub that normally just displays an error message about the program not being 60 | able to be run from DOS mode. 61 | 62 | Microsoft defines the DOS header as follows:: 63 | 64 | typedef struct _IMAGE_DOS_HEADER { // DOS .EXE header 65 | WORD e_magic; // Magic number 66 | WORD e_cblp; // Bytes on last page of file 67 | WORD e_cp; // Pages in file 68 | WORD e_crlc; // Relocations 69 | WORD e_cparhdr; // Size of header in paragraphs 70 | WORD e_minalloc; // Minimum extra paragraphs needed 71 | WORD e_maxalloc; // Maximum extra paragraphs needed 72 | WORD e_ss; // Initial (relative) SS value 73 | WORD e_sp; // Initial SP value 74 | WORD e_csum; // Checksum 75 | WORD e_ip; // Initial IP value 76 | WORD e_cs; // Initial (relative) CS value 77 | WORD e_lfarlc; // File address of relocation table 78 | WORD e_ovno; // Overlay number 79 | WORD e_res[4]; // Reserved words 80 | WORD e_oemid; // OEM identifier (for e_oeminfo) 81 | WORD e_oeminfo; // OEM information; e_oemid specific 82 | WORD e_res2[10]; // Reserved words 83 | LONG e_lfanew; // File address of new exe header 84 | } IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER; 85 | 86 | 87 | PE header 88 | ---------- 89 | 90 | The PE header contains informations about the different sections inside the 91 | executable that are used to store code and data or to define imports from other 92 | libraries or exports this libraries provides. 93 | 94 | It's defined as follows:: 95 | 96 | typedef struct _IMAGE_NT_HEADERS { 97 | DWORD Signature; 98 | IMAGE_FILE_HEADER FileHeader; 99 | IMAGE_OPTIONAL_HEADER32 OptionalHeader; 100 | } IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32; 101 | 102 | The `FileHeader` describes the *physical* format of the file, i.e. contents, informations 103 | about symbols, etc:: 104 | 105 | typedef struct _IMAGE_FILE_HEADER { 106 | WORD Machine; 107 | WORD NumberOfSections; 108 | DWORD TimeDateStamp; 109 | DWORD PointerToSymbolTable; 110 | DWORD NumberOfSymbols; 111 | WORD SizeOfOptionalHeader; 112 | WORD Characteristics; 113 | } IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER; 114 | 115 | .. _OptionalHeader: 116 | 117 | The `OptionalHeader` contains informations about the *logical* format of the library, 118 | including required OS version, memory requirements and entry points:: 119 | 120 | typedef struct _IMAGE_OPTIONAL_HEADER { 121 | // 122 | // Standard fields. 123 | // 124 | 125 | WORD Magic; 126 | BYTE MajorLinkerVersion; 127 | BYTE MinorLinkerVersion; 128 | DWORD SizeOfCode; 129 | DWORD SizeOfInitializedData; 130 | DWORD SizeOfUninitializedData; 131 | DWORD AddressOfEntryPoint; 132 | DWORD BaseOfCode; 133 | DWORD BaseOfData; 134 | 135 | // 136 | // NT additional fields. 137 | // 138 | 139 | DWORD ImageBase; 140 | DWORD SectionAlignment; 141 | DWORD FileAlignment; 142 | WORD MajorOperatingSystemVersion; 143 | WORD MinorOperatingSystemVersion; 144 | WORD MajorImageVersion; 145 | WORD MinorImageVersion; 146 | WORD MajorSubsystemVersion; 147 | WORD MinorSubsystemVersion; 148 | DWORD Win32VersionValue; 149 | DWORD SizeOfImage; 150 | DWORD SizeOfHeaders; 151 | DWORD CheckSum; 152 | WORD Subsystem; 153 | WORD DllCharacteristics; 154 | DWORD SizeOfStackReserve; 155 | DWORD SizeOfStackCommit; 156 | DWORD SizeOfHeapReserve; 157 | DWORD SizeOfHeapCommit; 158 | DWORD LoaderFlags; 159 | DWORD NumberOfRvaAndSizes; 160 | IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; 161 | } IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32; 162 | 163 | .. _DataDirectory: 164 | 165 | The `DataDirectory` contains 16 (`IMAGE_NUMBEROF_DIRECTORY_ENTRIES`) entries 166 | defining the logical components of the library: 167 | 168 | ===== ========================== 169 | Index Description 170 | ===== ========================== 171 | 0 Exported functions 172 | ----- -------------------------- 173 | 1 Imported functions 174 | ----- -------------------------- 175 | 2 Resources 176 | ----- -------------------------- 177 | 3 Exception informations 178 | ----- -------------------------- 179 | 4 Security informations 180 | ----- -------------------------- 181 | 5 Base relocation table 182 | ----- -------------------------- 183 | 6 Debug informations 184 | ----- -------------------------- 185 | 7 Architecture specific data 186 | ----- -------------------------- 187 | 8 Global pointer 188 | ----- -------------------------- 189 | 9 Thread local storage 190 | ----- -------------------------- 191 | 10 Load configuration 192 | ----- -------------------------- 193 | 11 Bound imports 194 | ----- -------------------------- 195 | 12 Import address table 196 | ----- -------------------------- 197 | 13 Delay load imports 198 | ----- -------------------------- 199 | 14 COM runtime descriptor 200 | ===== ========================== 201 | 202 | For importing the DLL we only need the entries describing the imports and the 203 | base relocation table. In order to provide access to the exported functions, 204 | the exports entry is required. 205 | 206 | 207 | Section header 208 | --------------- 209 | 210 | The section header is stored after the OptionalHeader_ structure in the PE 211 | header. Microsoft provides the macro `IMAGE_FIRST_SECTION` to get the start 212 | address based on the PE header. 213 | 214 | Actually, the section header is a list of informations about each section in 215 | the file:: 216 | 217 | typedef struct _IMAGE_SECTION_HEADER { 218 | BYTE Name[IMAGE_SIZEOF_SHORT_NAME]; 219 | union { 220 | DWORD PhysicalAddress; 221 | DWORD VirtualSize; 222 | } Misc; 223 | DWORD VirtualAddress; 224 | DWORD SizeOfRawData; 225 | DWORD PointerToRawData; 226 | DWORD PointerToRelocations; 227 | DWORD PointerToLinenumbers; 228 | WORD NumberOfRelocations; 229 | WORD NumberOfLinenumbers; 230 | DWORD Characteristics; 231 | } IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER; 232 | 233 | A section can contain code, data, relocation informations, resources, export or 234 | import definitions, etc. 235 | 236 | 237 | Loading the library 238 | ==================== 239 | 240 | To emulate the PE loader, we must first understand, which steps are neccessary 241 | to load the file to memory and prepare the structures so they can be called from 242 | other programs. 243 | 244 | When issuing the API call `LoadLibrary`, Windows basically performs these tasks: 245 | 246 | 1. Open the given file and check the DOS and PE headers. 247 | 248 | 2. Try to allocate a memory block of `PEHeader.OptionalHeader.SizeOfImage` bytes 249 | at position `PEHeader.OptionalHeader.ImageBase`. 250 | 251 | 3. Parse section headers and copy sections to their addresses. The destination 252 | address for each section, relative to the base of the allocated memory block, 253 | is stored in the `VirtualAddress` attribute of the `IMAGE_SECTION_HEADER` 254 | structure. 255 | 256 | 4. If the allocated memory block differs from `ImageBase`, various references in 257 | the code and/or data sections must be adjusted. This is called *Base 258 | relocation*. 259 | 260 | 5. The required imports for the library must be resolved by loading the 261 | corresponding libraries. 262 | 263 | 6. The memory regions of the different sections must be protected depending on 264 | the section's characteristics. Some sections are marked as *discardable* 265 | and therefore can be safely freed at this point. These sections normally 266 | contain temporary data that is only needed during the import, like the 267 | informations for the base relocation. 268 | 269 | 7. Now the library is loaded completely. It must be notified about this by 270 | calling the entry point using the flag `DLL_PROCESS_ATTACH`. 271 | 272 | In the following paragraphs, each step is described. 273 | 274 | 275 | Allocate memory 276 | ---------------- 277 | 278 | All memory required for the library must be reserved / allocated using 279 | `VirtualAlloc`, as Windows provides functions to protect these memory blocks. 280 | This is required to restrict access to the memory, like blocking write access 281 | to the code or constant data. 282 | 283 | The OptionalHeader_ structure defines the size of the required memory block 284 | for the library. It must be reserved at the address specified by `ImageBase` 285 | if possible:: 286 | 287 | memory = VirtualAlloc((LPVOID)(PEHeader->OptionalHeader.ImageBase), 288 | PEHeader->OptionalHeader.SizeOfImage, 289 | MEM_RESERVE, 290 | PAGE_READWRITE); 291 | 292 | If the reserved memory differs from the address given in `ImageBase`, base 293 | relocation as described below must be done. 294 | 295 | 296 | Copy sections 297 | -------------- 298 | 299 | Once the memory has been reserved, the file contents can be copied to the 300 | system. The section header must get evaluated in order to determine the 301 | position in the file and the target area in memory. 302 | 303 | Before copying the data, the memory block must get committed:: 304 | 305 | dest = VirtualAlloc(baseAddress + section->VirtualAddress, 306 | section->SizeOfRawData, 307 | MEM_COMMIT, 308 | PAGE_READWRITE); 309 | 310 | Sections without data in the file (like data sections for the used variables) 311 | have a `SizeOfRawData` of `0`, so you can use the `SizeOfInitializedData` 312 | or `SizeOfUninitializedData` of the OptionalHeader_. Which one must get 313 | choosen depending on the bit flags `IMAGE_SCN_CNT_INITIALIZED_DATA` and 314 | `IMAGE_SCN_CNT_UNINITIALIZED_DATA` that may be set in the section`s 315 | characteristics. 316 | 317 | 318 | Base relocation 319 | ---------------- 320 | 321 | All memory addresses in the code / data sections of a library are stored relative 322 | to the address defined by `ImageBase` in the OptionalHeader_. If the library 323 | can't be imported to this memory address, the references must get adjusted 324 | => *relocated*. The file format helps for this by storing informations about 325 | all these references in the base relocation table, which can be found in the 326 | directory entry 5 of the DataDirectory_ in the OptionalHeader_. 327 | 328 | This table consists of a series of this structure 329 | 330 | :: 331 | 332 | typedef struct _IMAGE_BASE_RELOCATION { 333 | DWORD VirtualAddress; 334 | DWORD SizeOfBlock; 335 | } IMAGE_BASE_RELOCATION; 336 | 337 | It contains `(SizeOfBlock - IMAGE_SIZEOF_BASE_RELOCATION) / 2` entries of 16 bits 338 | each. The upper 4 bits define the type of relocation, the lower 12 bits define 339 | the offset relative to the `VirtualAddress`. 340 | 341 | The only types that seem to be used in DLLs are 342 | 343 | IMAGE_REL_BASED_ABSOLUTE 344 | No operation relocation. Used for padding. 345 | IMAGE_REL_BASED_HIGHLOW 346 | Add the delta between the `ImageBase` and the allocated memory block to the 347 | 32 bits found at the offset. 348 | 349 | 350 | Resolve imports 351 | ---------------- 352 | 353 | The directory entry 1 of the DataDirectory_ in the OptionalHeader_ specifies 354 | a list of libraries to import symbols from. Each entry in this list is defined 355 | as follows:: 356 | 357 | typedef struct _IMAGE_IMPORT_DESCRIPTOR { 358 | union { 359 | DWORD Characteristics; // 0 for terminating null import descriptor 360 | DWORD OriginalFirstThunk; // RVA to original unbound IAT (PIMAGE_THUNK_DATA) 361 | }; 362 | DWORD TimeDateStamp; // 0 if not bound, 363 | // -1 if bound, and real date\time stamp 364 | // in IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (new BIND) 365 | // O.W. date/time stamp of DLL bound to (Old BIND) 366 | 367 | DWORD ForwarderChain; // -1 if no forwarders 368 | DWORD Name; 369 | DWORD FirstThunk; // RVA to IAT (if bound this IAT has actual addresses) 370 | } IMAGE_IMPORT_DESCRIPTOR; 371 | 372 | The `Name` entry describes the offset to the NULL-terminated string of the library 373 | name (e.g. `KERNEL32.DLL`). The `OriginalFirstThunk` entry points to a list 374 | of references to the function names to import from the external library. 375 | `FirstThunk` points to a list of addresses that gets filled with pointers to 376 | the imported symbols. 377 | 378 | When we resolve the imports, we walk both lists in parallel, import the function 379 | defined by the name in the first list and store the pointer to the symbol in the 380 | second list:: 381 | 382 | nameRef = (DWORD *)(baseAddress + importDesc->OriginalFirstThunk); 383 | symbolRef = (DWORD *)(baseAddress + importDesc->FirstThunk); 384 | for (; *nameRef; nameRef++, symbolRef++) 385 | { 386 | PIMAGE_IMPORT_BY_NAME thunkData = (PIMAGE_IMPORT_BY_NAME)(codeBase + *nameRef); 387 | *symbolRef = (DWORD)GetProcAddress(handle, (LPCSTR)&thunkData->Name); 388 | if (*funcRef == 0) 389 | { 390 | handleImportError(); 391 | return; 392 | } 393 | } 394 | 395 | 396 | Protect memory 397 | --------------- 398 | 399 | Every section specifies permission flags in it's `Characteristics` entry. 400 | These flags can be one or a combination of 401 | 402 | IMAGE_SCN_MEM_EXECUTE 403 | The section contains data that can be executed. 404 | 405 | IMAGE_SCN_MEM_READ 406 | The section contains data that is readable. 407 | 408 | IMAGE_SCN_MEM_WRITE 409 | The section contains data that is writeable. 410 | 411 | These flags must get mapped to the protection flags 412 | 413 | - PAGE_NOACCESS 414 | - PAGE_WRITECOPY 415 | - PAGE_READONLY 416 | - PAGE_READWRITE 417 | - PAGE_EXECUTE 418 | - PAGE_EXECUTE_WRITECOPY 419 | - PAGE_EXECUTE_READ 420 | - PAGE_EXECUTE_READWRITE 421 | 422 | Now, the function `VirtualProtect` can be used to limit access to the memory. 423 | If the program tries to access it in a unauthorized way, an exception gets 424 | raised by Windows. 425 | 426 | In addition the section flags above, the following can be added: 427 | 428 | IMAGE_SCN_MEM_DISCARDABLE 429 | The data in this section can be freed after the import. Usually this is 430 | specified for relocation data. 431 | 432 | IMAGE_SCN_MEM_NOT_CACHED 433 | The data in this section must not get cached by Windows. Add the bit 434 | flag `PAGE_NOCACHE` to the protection flags above. 435 | 436 | 437 | Notify library 438 | --------------- 439 | 440 | The last thing to do is to call the DLL entry point (defined by 441 | `AddressOfEntryPoint`) and so notifying the library about being attached 442 | to a process. 443 | 444 | The function at the entry point is defined as 445 | 446 | :: 447 | 448 | typedef BOOL (WINAPI *DllEntryProc)(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved); 449 | 450 | So the last code we need to execute is 451 | 452 | :: 453 | 454 | DllEntryProc entry = (DllEntryProc)(baseAddress + PEHeader->OptionalHeader.AddressOfEntryPoint); 455 | (*entry)((HINSTANCE)baseAddress, DLL_PROCESS_ATTACH, 0); 456 | 457 | Afterwards we can use the exported functions as with any normal library. 458 | 459 | 460 | Exported functions 461 | =================== 462 | 463 | If you want to access the functions that are exported by the library, you need to find the entry 464 | point to a symbol, i.e. the name of the function to call. 465 | 466 | The directory entry 0 of the DataDirectory_ in the OptionalHeader_ contains informations about 467 | the exported functions. It's defined as follows:: 468 | 469 | typedef struct _IMAGE_EXPORT_DIRECTORY { 470 | DWORD Characteristics; 471 | DWORD TimeDateStamp; 472 | WORD MajorVersion; 473 | WORD MinorVersion; 474 | DWORD Name; 475 | DWORD Base; 476 | DWORD NumberOfFunctions; 477 | DWORD NumberOfNames; 478 | DWORD AddressOfFunctions; // RVA from base of image 479 | DWORD AddressOfNames; // RVA from base of image 480 | DWORD AddressOfNameOrdinals; // RVA from base of image 481 | } IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY; 482 | 483 | First thing to do, is to map the name of the function to the ordinal number of the exported 484 | symbol. Therefore, just walk the arrays defined by `AddressOfNames` and `AddressOfNameOrdinals` 485 | parallel until you found the required name. 486 | 487 | Now you can use the ordinal number to read the address by evaluating the n-th element of the 488 | `AddressOfFunctions` array. 489 | 490 | 491 | Freeing the library 492 | ==================== 493 | 494 | To free the custom loaded library, perform the steps 495 | 496 | - Call entry point to notify library about being detached:: 497 | 498 | DllEntryProc entry = (DllEntryProc)(baseAddress + PEHeader->OptionalHeader.AddressOfEntryPoint); 499 | (*entry)((HINSTANCE)baseAddress, DLL_PROCESS_ATTACH, 0); 500 | 501 | - Free external libraries used to resolve imports. 502 | - Free allocated memory. 503 | 504 | 505 | MemoryModule 506 | ============= 507 | 508 | MemoryModule is a C-library that can be used to load a DLL from memory. 509 | 510 | The interface is very similar to the standard methods for loading of libraries:: 511 | 512 | typedef void *HMEMORYMODULE; 513 | 514 | HMEMORYMODULE MemoryLoadLibrary(const void *, size_t); 515 | FARPROC MemoryGetProcAddress(HMEMORYMODULE, const char *); 516 | void MemoryFreeLibrary(HMEMORYMODULE); 517 | 518 | 519 | Downloads 520 | ---------- 521 | 522 | The latest development release can always be grabbed from Github at 523 | http://github.com/fancycode/MemoryModule/ 524 | 525 | 526 | Known issues 527 | ------------- 528 | 529 | - All memory that is not protected by section flags is gets committed using `PAGE_READWRITE`. 530 | I don't know if this is correct. 531 | 532 | 533 | License 534 | -------- 535 | 536 | Since version 0.0.2, the MemoryModule library is released under the Mozilla Public License (MPL). 537 | Version 0.0.1 has been released unter the Lesser General Public License (LGPL). 538 | 539 | It is provided as-is without ANY warranty. You may use it at your own risk. 540 | 541 | 542 | Copyright 543 | ========== 544 | 545 | The MemoryModule library and this tutorial are 546 | Copyright (c) 2004-2015 by Joachim Bauch. 547 | -------------------------------------------------------------------------------- /MemoryModule.c: -------------------------------------------------------------------------------- 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 | * THeller: Added binary search in MemoryGetProcAddress function 27 | * (#define USE_BINARY_SEARCH to enable it). This gives a very large 28 | * speedup for libraries that exports lots of functions. 29 | * 30 | * These portions are Copyright (C) 2013 Thomas Heller. 31 | */ 32 | 33 | #include 34 | #include 35 | #include 36 | #include 37 | #ifdef DEBUG_OUTPUT 38 | #include 39 | #endif 40 | 41 | #if _MSC_VER 42 | // Disable warning about data -> function pointer conversion 43 | #pragma warning(disable:4055) 44 | // C4244: conversion from 'uintptr_t' to 'DWORD', possible loss of data. 45 | #pragma warning(error: 4244) 46 | // C4267: conversion from 'size_t' to 'int', possible loss of data. 47 | #pragma warning(error: 4267) 48 | 49 | #define inline __inline 50 | #endif 51 | 52 | #ifndef IMAGE_SIZEOF_BASE_RELOCATION 53 | // Vista SDKs no longer define IMAGE_SIZEOF_BASE_RELOCATION!? 54 | #define IMAGE_SIZEOF_BASE_RELOCATION (sizeof(IMAGE_BASE_RELOCATION)) 55 | #endif 56 | 57 | #ifdef _WIN64 58 | #define HOST_MACHINE IMAGE_FILE_MACHINE_AMD64 59 | #else 60 | #define HOST_MACHINE IMAGE_FILE_MACHINE_I386 61 | #endif 62 | 63 | #include "MemoryModule.h" 64 | 65 | struct ExportNameEntry { 66 | LPCSTR name; 67 | WORD idx; 68 | }; 69 | 70 | typedef BOOL (WINAPI *DllEntryProc)(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved); 71 | typedef int (WINAPI *ExeEntryProc)(void); 72 | 73 | #ifdef _WIN64 74 | typedef struct POINTER_LIST { 75 | struct POINTER_LIST *next; 76 | void *address; 77 | } POINTER_LIST; 78 | #endif 79 | 80 | typedef struct { 81 | PIMAGE_NT_HEADERS headers; 82 | unsigned char *codeBase; 83 | HCUSTOMMODULE *modules; 84 | int numModules; 85 | BOOL initialized; 86 | BOOL isDLL; 87 | BOOL isRelocated; 88 | CustomAllocFunc alloc; 89 | CustomFreeFunc free; 90 | CustomLoadLibraryFunc loadLibrary; 91 | CustomGetProcAddressFunc getProcAddress; 92 | CustomFreeLibraryFunc freeLibrary; 93 | struct ExportNameEntry *nameExportsTable; 94 | void *userdata; 95 | ExeEntryProc exeEntry; 96 | DWORD pageSize; 97 | #ifdef _WIN64 98 | POINTER_LIST *blockedMemory; 99 | #endif 100 | } MEMORYMODULE, *PMEMORYMODULE; 101 | 102 | typedef struct { 103 | LPVOID address; 104 | LPVOID alignedAddress; 105 | SIZE_T size; 106 | DWORD characteristics; 107 | BOOL last; 108 | } SECTIONFINALIZEDATA, *PSECTIONFINALIZEDATA; 109 | 110 | #define GET_HEADER_DICTIONARY(module, idx) &(module)->headers->OptionalHeader.DataDirectory[idx] 111 | 112 | static inline uintptr_t 113 | AlignValueDown(uintptr_t value, uintptr_t alignment) { 114 | return value & ~(alignment - 1); 115 | } 116 | 117 | static inline LPVOID 118 | AlignAddressDown(LPVOID address, uintptr_t alignment) { 119 | return (LPVOID) AlignValueDown((uintptr_t) address, alignment); 120 | } 121 | 122 | static inline size_t 123 | AlignValueUp(size_t value, size_t alignment) { 124 | return (value + alignment - 1) & ~(alignment - 1); 125 | } 126 | 127 | static inline void* 128 | OffsetPointer(void* data, ptrdiff_t offset) { 129 | return (void*) ((uintptr_t) data + offset); 130 | } 131 | 132 | static inline void 133 | OutputLastError(const char *msg) 134 | { 135 | #ifndef DEBUG_OUTPUT 136 | UNREFERENCED_PARAMETER(msg); 137 | #else 138 | LPVOID tmp; 139 | char *tmpmsg; 140 | FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, 141 | NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&tmp, 0, NULL); 142 | tmpmsg = (char *)LocalAlloc(LPTR, strlen(msg) + strlen(tmp) + 3); 143 | sprintf(tmpmsg, "%s: %s", msg, tmp); 144 | OutputDebugString(tmpmsg); 145 | LocalFree(tmpmsg); 146 | LocalFree(tmp); 147 | #endif 148 | } 149 | 150 | #ifdef _WIN64 151 | static void 152 | FreePointerList(POINTER_LIST *head, CustomFreeFunc freeMemory, void *userdata) 153 | { 154 | POINTER_LIST *node = head; 155 | while (node) { 156 | POINTER_LIST *next; 157 | freeMemory(node->address, 0, MEM_RELEASE, userdata); 158 | next = node->next; 159 | free(node); 160 | node = next; 161 | } 162 | } 163 | #endif 164 | 165 | static BOOL 166 | CheckSize(size_t size, size_t expected) { 167 | if (size < expected) { 168 | SetLastError(ERROR_INVALID_DATA); 169 | return FALSE; 170 | } 171 | 172 | return TRUE; 173 | } 174 | 175 | static BOOL 176 | CopySections(const unsigned char *data, size_t size, PIMAGE_NT_HEADERS old_headers, PMEMORYMODULE module) 177 | { 178 | int i, section_size; 179 | unsigned char *codeBase = module->codeBase; 180 | unsigned char *dest; 181 | PIMAGE_SECTION_HEADER section = IMAGE_FIRST_SECTION(module->headers); 182 | for (i=0; iheaders->FileHeader.NumberOfSections; i++, section++) { 183 | if (section->SizeOfRawData == 0) { 184 | // section doesn't contain data in the dll itself, but may define 185 | // uninitialized data 186 | section_size = old_headers->OptionalHeader.SectionAlignment; 187 | if (section_size > 0) { 188 | dest = (unsigned char *)module->alloc(codeBase + section->VirtualAddress, 189 | section_size, 190 | MEM_COMMIT, 191 | PAGE_READWRITE, 192 | module->userdata); 193 | if (dest == NULL) { 194 | return FALSE; 195 | } 196 | 197 | // Always use position from file to support alignments smaller 198 | // than page size (allocation above will align to page size). 199 | dest = codeBase + section->VirtualAddress; 200 | // NOTE: On 64bit systems we truncate to 32bit here but expand 201 | // again later when "PhysicalAddress" is used. 202 | section->Misc.PhysicalAddress = (DWORD) ((uintptr_t) dest & 0xffffffff); 203 | memset(dest, 0, section_size); 204 | } 205 | 206 | // section is empty 207 | continue; 208 | } 209 | 210 | if (!CheckSize(size, section->PointerToRawData + section->SizeOfRawData)) { 211 | return FALSE; 212 | } 213 | 214 | // commit memory block and copy data from dll 215 | dest = (unsigned char *)module->alloc(codeBase + section->VirtualAddress, 216 | section->SizeOfRawData, 217 | MEM_COMMIT, 218 | PAGE_READWRITE, 219 | module->userdata); 220 | if (dest == NULL) { 221 | return FALSE; 222 | } 223 | 224 | // Always use position from file to support alignments smaller 225 | // than page size (allocation above will align to page size). 226 | dest = codeBase + section->VirtualAddress; 227 | memcpy(dest, data + section->PointerToRawData, section->SizeOfRawData); 228 | // NOTE: On 64bit systems we truncate to 32bit here but expand 229 | // again later when "PhysicalAddress" is used. 230 | section->Misc.PhysicalAddress = (DWORD) ((uintptr_t) dest & 0xffffffff); 231 | } 232 | 233 | return TRUE; 234 | } 235 | 236 | // Protection flags for memory pages (Executable, Readable, Writeable) 237 | static int ProtectionFlags[2][2][2] = { 238 | { 239 | // not executable 240 | {PAGE_NOACCESS, PAGE_WRITECOPY}, 241 | {PAGE_READONLY, PAGE_READWRITE}, 242 | }, { 243 | // executable 244 | {PAGE_EXECUTE, PAGE_EXECUTE_WRITECOPY}, 245 | {PAGE_EXECUTE_READ, PAGE_EXECUTE_READWRITE}, 246 | }, 247 | }; 248 | 249 | static SIZE_T 250 | GetRealSectionSize(PMEMORYMODULE module, PIMAGE_SECTION_HEADER section) { 251 | DWORD size = section->SizeOfRawData; 252 | if (size == 0) { 253 | if (section->Characteristics & IMAGE_SCN_CNT_INITIALIZED_DATA) { 254 | size = module->headers->OptionalHeader.SizeOfInitializedData; 255 | } else if (section->Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA) { 256 | size = module->headers->OptionalHeader.SizeOfUninitializedData; 257 | } 258 | } 259 | return (SIZE_T) size; 260 | } 261 | 262 | static BOOL 263 | FinalizeSection(PMEMORYMODULE module, PSECTIONFINALIZEDATA sectionData) { 264 | DWORD protect, oldProtect; 265 | BOOL executable; 266 | BOOL readable; 267 | BOOL writeable; 268 | 269 | if (sectionData->size == 0) { 270 | return TRUE; 271 | } 272 | 273 | if (sectionData->characteristics & IMAGE_SCN_MEM_DISCARDABLE) { 274 | // section is not needed any more and can safely be freed 275 | if (sectionData->address == sectionData->alignedAddress && 276 | (sectionData->last || 277 | module->headers->OptionalHeader.SectionAlignment == module->pageSize || 278 | (sectionData->size % module->pageSize) == 0) 279 | ) { 280 | // Only allowed to decommit whole pages 281 | module->free(sectionData->address, sectionData->size, MEM_DECOMMIT, module->userdata); 282 | } 283 | return TRUE; 284 | } 285 | 286 | // determine protection flags based on characteristics 287 | executable = (sectionData->characteristics & IMAGE_SCN_MEM_EXECUTE) != 0; 288 | readable = (sectionData->characteristics & IMAGE_SCN_MEM_READ) != 0; 289 | writeable = (sectionData->characteristics & IMAGE_SCN_MEM_WRITE) != 0; 290 | protect = ProtectionFlags[executable][readable][writeable]; 291 | if (sectionData->characteristics & IMAGE_SCN_MEM_NOT_CACHED) { 292 | protect |= PAGE_NOCACHE; 293 | } 294 | 295 | // change memory access flags 296 | if (VirtualProtect(sectionData->address, sectionData->size, protect, &oldProtect) == 0) { 297 | OutputLastError("Error protecting memory page"); 298 | return FALSE; 299 | } 300 | 301 | return TRUE; 302 | } 303 | 304 | static BOOL 305 | FinalizeSections(PMEMORYMODULE module) 306 | { 307 | int i; 308 | PIMAGE_SECTION_HEADER section = IMAGE_FIRST_SECTION(module->headers); 309 | #ifdef _WIN64 310 | // "PhysicalAddress" might have been truncated to 32bit above, expand to 311 | // 64bits again. 312 | uintptr_t imageOffset = ((uintptr_t) module->headers->OptionalHeader.ImageBase & 0xffffffff00000000); 313 | #else 314 | static const uintptr_t imageOffset = 0; 315 | #endif 316 | SECTIONFINALIZEDATA sectionData; 317 | sectionData.address = (LPVOID)((uintptr_t)section->Misc.PhysicalAddress | imageOffset); 318 | sectionData.alignedAddress = AlignAddressDown(sectionData.address, module->pageSize); 319 | sectionData.size = GetRealSectionSize(module, section); 320 | sectionData.characteristics = section->Characteristics; 321 | sectionData.last = FALSE; 322 | section++; 323 | 324 | // loop through all sections and change access flags 325 | for (i=1; iheaders->FileHeader.NumberOfSections; i++, section++) { 326 | LPVOID sectionAddress = (LPVOID)((uintptr_t)section->Misc.PhysicalAddress | imageOffset); 327 | LPVOID alignedAddress = AlignAddressDown(sectionAddress, module->pageSize); 328 | SIZE_T sectionSize = GetRealSectionSize(module, section); 329 | // Combine access flags of all sections that share a page 330 | // TODO(fancycode): We currently share flags of a trailing large section 331 | // with the page of a first small section. This should be optimized. 332 | if (sectionData.alignedAddress == alignedAddress || (uintptr_t) sectionData.address + sectionData.size > (uintptr_t) alignedAddress) { 333 | // Section shares page with previous 334 | if ((section->Characteristics & IMAGE_SCN_MEM_DISCARDABLE) == 0 || (sectionData.characteristics & IMAGE_SCN_MEM_DISCARDABLE) == 0) { 335 | sectionData.characteristics = (sectionData.characteristics | section->Characteristics) & ~IMAGE_SCN_MEM_DISCARDABLE; 336 | } else { 337 | sectionData.characteristics |= section->Characteristics; 338 | } 339 | sectionData.size = (((uintptr_t)sectionAddress) + ((uintptr_t) sectionSize)) - (uintptr_t) sectionData.address; 340 | continue; 341 | } 342 | 343 | if (!FinalizeSection(module, §ionData)) { 344 | return FALSE; 345 | } 346 | sectionData.address = sectionAddress; 347 | sectionData.alignedAddress = alignedAddress; 348 | sectionData.size = sectionSize; 349 | sectionData.characteristics = section->Characteristics; 350 | } 351 | sectionData.last = TRUE; 352 | if (!FinalizeSection(module, §ionData)) { 353 | return FALSE; 354 | } 355 | return TRUE; 356 | } 357 | 358 | static BOOL 359 | ExecuteTLS(PMEMORYMODULE module) 360 | { 361 | unsigned char *codeBase = module->codeBase; 362 | PIMAGE_TLS_DIRECTORY tls; 363 | PIMAGE_TLS_CALLBACK* callback; 364 | 365 | PIMAGE_DATA_DIRECTORY directory = GET_HEADER_DICTIONARY(module, IMAGE_DIRECTORY_ENTRY_TLS); 366 | if (directory->VirtualAddress == 0) { 367 | return TRUE; 368 | } 369 | 370 | tls = (PIMAGE_TLS_DIRECTORY) (codeBase + directory->VirtualAddress); 371 | callback = (PIMAGE_TLS_CALLBACK *) tls->AddressOfCallBacks; 372 | if (callback) { 373 | while (*callback) { 374 | (*callback)((LPVOID) codeBase, DLL_PROCESS_ATTACH, NULL); 375 | callback++; 376 | } 377 | } 378 | return TRUE; 379 | } 380 | 381 | static BOOL 382 | PerformBaseRelocation(PMEMORYMODULE module, ptrdiff_t delta) 383 | { 384 | unsigned char *codeBase = module->codeBase; 385 | PIMAGE_BASE_RELOCATION relocation; 386 | 387 | PIMAGE_DATA_DIRECTORY directory = GET_HEADER_DICTIONARY(module, IMAGE_DIRECTORY_ENTRY_BASERELOC); 388 | if (directory->Size == 0) { 389 | return (delta == 0); 390 | } 391 | 392 | relocation = (PIMAGE_BASE_RELOCATION) (codeBase + directory->VirtualAddress); 393 | for (; relocation->VirtualAddress > 0; ) { 394 | DWORD i; 395 | unsigned char *dest = codeBase + relocation->VirtualAddress; 396 | unsigned short *relInfo = (unsigned short*) OffsetPointer(relocation, IMAGE_SIZEOF_BASE_RELOCATION); 397 | for (i=0; i<((relocation->SizeOfBlock-IMAGE_SIZEOF_BASE_RELOCATION) / 2); i++, relInfo++) { 398 | // the upper 4 bits define the type of relocation 399 | int type = *relInfo >> 12; 400 | // the lower 12 bits define the offset 401 | int offset = *relInfo & 0xfff; 402 | 403 | switch (type) 404 | { 405 | case IMAGE_REL_BASED_ABSOLUTE: 406 | // skip relocation 407 | break; 408 | 409 | case IMAGE_REL_BASED_HIGHLOW: 410 | // change complete 32 bit address 411 | { 412 | DWORD *patchAddrHL = (DWORD *) (dest + offset); 413 | *patchAddrHL += (DWORD) delta; 414 | } 415 | break; 416 | 417 | #ifdef _WIN64 418 | case IMAGE_REL_BASED_DIR64: 419 | { 420 | ULONGLONG *patchAddr64 = (ULONGLONG *) (dest + offset); 421 | *patchAddr64 += (ULONGLONG) delta; 422 | } 423 | break; 424 | #endif 425 | 426 | default: 427 | //printf("Unknown relocation: %d\n", type); 428 | break; 429 | } 430 | } 431 | 432 | // advance to next relocation block 433 | relocation = (PIMAGE_BASE_RELOCATION) OffsetPointer(relocation, relocation->SizeOfBlock); 434 | } 435 | return TRUE; 436 | } 437 | 438 | static BOOL 439 | BuildImportTable(PMEMORYMODULE module) 440 | { 441 | unsigned char *codeBase = module->codeBase; 442 | PIMAGE_IMPORT_DESCRIPTOR importDesc; 443 | BOOL result = TRUE; 444 | 445 | PIMAGE_DATA_DIRECTORY directory = GET_HEADER_DICTIONARY(module, IMAGE_DIRECTORY_ENTRY_IMPORT); 446 | if (directory->Size == 0) { 447 | return TRUE; 448 | } 449 | 450 | importDesc = (PIMAGE_IMPORT_DESCRIPTOR) (codeBase + directory->VirtualAddress); 451 | for (; !IsBadReadPtr(importDesc, sizeof(IMAGE_IMPORT_DESCRIPTOR)) && importDesc->Name; importDesc++) { 452 | uintptr_t *thunkRef; 453 | FARPROC *funcRef; 454 | HCUSTOMMODULE *tmp; 455 | HCUSTOMMODULE handle = module->loadLibrary((LPCSTR) (codeBase + importDesc->Name), module->userdata); 456 | if (handle == NULL) { 457 | SetLastError(ERROR_MOD_NOT_FOUND); 458 | result = FALSE; 459 | break; 460 | } 461 | 462 | tmp = (HCUSTOMMODULE *) realloc(module->modules, (module->numModules+1)*(sizeof(HCUSTOMMODULE))); 463 | if (tmp == NULL) { 464 | module->freeLibrary(handle, module->userdata); 465 | SetLastError(ERROR_OUTOFMEMORY); 466 | result = FALSE; 467 | break; 468 | } 469 | module->modules = tmp; 470 | 471 | module->modules[module->numModules++] = handle; 472 | if (importDesc->OriginalFirstThunk) { 473 | thunkRef = (uintptr_t *) (codeBase + importDesc->OriginalFirstThunk); 474 | funcRef = (FARPROC *) (codeBase + importDesc->FirstThunk); 475 | } else { 476 | // no hint table 477 | thunkRef = (uintptr_t *) (codeBase + importDesc->FirstThunk); 478 | funcRef = (FARPROC *) (codeBase + importDesc->FirstThunk); 479 | } 480 | for (; *thunkRef; thunkRef++, funcRef++) { 481 | if (IMAGE_SNAP_BY_ORDINAL(*thunkRef)) { 482 | *funcRef = module->getProcAddress(handle, (LPCSTR)IMAGE_ORDINAL(*thunkRef), module->userdata); 483 | } else { 484 | PIMAGE_IMPORT_BY_NAME thunkData = (PIMAGE_IMPORT_BY_NAME) (codeBase + (*thunkRef)); 485 | *funcRef = module->getProcAddress(handle, (LPCSTR)&thunkData->Name, module->userdata); 486 | } 487 | if (*funcRef == 0) { 488 | result = FALSE; 489 | break; 490 | } 491 | } 492 | 493 | if (!result) { 494 | module->freeLibrary(handle, module->userdata); 495 | SetLastError(ERROR_PROC_NOT_FOUND); 496 | break; 497 | } 498 | } 499 | 500 | return result; 501 | } 502 | 503 | LPVOID MemoryDefaultAlloc(LPVOID address, SIZE_T size, DWORD allocationType, DWORD protect, void* userdata) 504 | { 505 | UNREFERENCED_PARAMETER(userdata); 506 | return VirtualAlloc(address, size, allocationType, protect); 507 | } 508 | 509 | BOOL MemoryDefaultFree(LPVOID lpAddress, SIZE_T dwSize, DWORD dwFreeType, void* userdata) 510 | { 511 | UNREFERENCED_PARAMETER(userdata); 512 | return VirtualFree(lpAddress, dwSize, dwFreeType); 513 | } 514 | 515 | HCUSTOMMODULE MemoryDefaultLoadLibrary(LPCSTR filename, void *userdata) 516 | { 517 | HMODULE result; 518 | UNREFERENCED_PARAMETER(userdata); 519 | result = LoadLibraryA(filename); 520 | if (result == NULL) { 521 | return NULL; 522 | } 523 | 524 | return (HCUSTOMMODULE) result; 525 | } 526 | 527 | FARPROC MemoryDefaultGetProcAddress(HCUSTOMMODULE module, LPCSTR name, void *userdata) 528 | { 529 | UNREFERENCED_PARAMETER(userdata); 530 | return (FARPROC) GetProcAddress((HMODULE) module, name); 531 | } 532 | 533 | void MemoryDefaultFreeLibrary(HCUSTOMMODULE module, void *userdata) 534 | { 535 | UNREFERENCED_PARAMETER(userdata); 536 | FreeLibrary((HMODULE) module); 537 | } 538 | 539 | HMEMORYMODULE MemoryLoadLibrary(const void *data, size_t size) 540 | { 541 | return MemoryLoadLibraryEx(data, size, MemoryDefaultAlloc, MemoryDefaultFree, MemoryDefaultLoadLibrary, MemoryDefaultGetProcAddress, MemoryDefaultFreeLibrary, NULL); 542 | } 543 | 544 | HMEMORYMODULE MemoryLoadLibraryEx(const void *data, size_t size, 545 | CustomAllocFunc allocMemory, 546 | CustomFreeFunc freeMemory, 547 | CustomLoadLibraryFunc loadLibrary, 548 | CustomGetProcAddressFunc getProcAddress, 549 | CustomFreeLibraryFunc freeLibrary, 550 | void *userdata) 551 | { 552 | PMEMORYMODULE result = NULL; 553 | PIMAGE_DOS_HEADER dos_header; 554 | PIMAGE_NT_HEADERS old_header; 555 | unsigned char *code, *headers; 556 | ptrdiff_t locationDelta; 557 | SYSTEM_INFO sysInfo; 558 | PIMAGE_SECTION_HEADER section; 559 | DWORD i; 560 | size_t optionalSectionSize; 561 | size_t lastSectionEnd = 0; 562 | size_t alignedImageSize; 563 | #ifdef _WIN64 564 | POINTER_LIST *blockedMemory = NULL; 565 | #endif 566 | 567 | if (!CheckSize(size, sizeof(IMAGE_DOS_HEADER))) { 568 | return NULL; 569 | } 570 | dos_header = (PIMAGE_DOS_HEADER)data; 571 | if (dos_header->e_magic != IMAGE_DOS_SIGNATURE) { 572 | SetLastError(ERROR_BAD_EXE_FORMAT); 573 | return NULL; 574 | } 575 | 576 | if (!CheckSize(size, dos_header->e_lfanew + sizeof(IMAGE_NT_HEADERS))) { 577 | return NULL; 578 | } 579 | old_header = (PIMAGE_NT_HEADERS)&((const unsigned char *)(data))[dos_header->e_lfanew]; 580 | if (old_header->Signature != IMAGE_NT_SIGNATURE) { 581 | SetLastError(ERROR_BAD_EXE_FORMAT); 582 | return NULL; 583 | } 584 | 585 | if (old_header->FileHeader.Machine != HOST_MACHINE) { 586 | SetLastError(ERROR_BAD_EXE_FORMAT); 587 | return NULL; 588 | } 589 | 590 | if (old_header->OptionalHeader.SectionAlignment & 1) { 591 | // Only support section alignments that are a multiple of 2 592 | SetLastError(ERROR_BAD_EXE_FORMAT); 593 | return NULL; 594 | } 595 | 596 | section = IMAGE_FIRST_SECTION(old_header); 597 | optionalSectionSize = old_header->OptionalHeader.SectionAlignment; 598 | for (i=0; iFileHeader.NumberOfSections; i++, section++) { 599 | size_t endOfSection; 600 | if (section->SizeOfRawData == 0) { 601 | // Section without data in the DLL 602 | endOfSection = section->VirtualAddress + optionalSectionSize; 603 | } else { 604 | endOfSection = section->VirtualAddress + section->SizeOfRawData; 605 | } 606 | 607 | if (endOfSection > lastSectionEnd) { 608 | lastSectionEnd = endOfSection; 609 | } 610 | } 611 | 612 | GetNativeSystemInfo(&sysInfo); 613 | alignedImageSize = AlignValueUp(old_header->OptionalHeader.SizeOfImage, sysInfo.dwPageSize); 614 | if (alignedImageSize != AlignValueUp(lastSectionEnd, sysInfo.dwPageSize)) { 615 | SetLastError(ERROR_BAD_EXE_FORMAT); 616 | return NULL; 617 | } 618 | 619 | // reserve memory for image of library 620 | // XXX: is it correct to commit the complete memory region at once? 621 | // calling DllEntry raises an exception if we don't... 622 | code = (unsigned char *)allocMemory((LPVOID)(old_header->OptionalHeader.ImageBase), 623 | alignedImageSize, 624 | MEM_RESERVE | MEM_COMMIT, 625 | PAGE_READWRITE, 626 | userdata); 627 | 628 | if (code == NULL) { 629 | // try to allocate memory at arbitrary position 630 | code = (unsigned char *)allocMemory(NULL, 631 | alignedImageSize, 632 | MEM_RESERVE | MEM_COMMIT, 633 | PAGE_READWRITE, 634 | userdata); 635 | if (code == NULL) { 636 | SetLastError(ERROR_OUTOFMEMORY); 637 | return NULL; 638 | } 639 | } 640 | 641 | #ifdef _WIN64 642 | // Memory block may not span 4 GB boundaries. 643 | while ((((uintptr_t) code) >> 32) < (((uintptr_t) (code + alignedImageSize)) >> 32)) { 644 | POINTER_LIST *node = (POINTER_LIST*) malloc(sizeof(POINTER_LIST)); 645 | if (!node) { 646 | freeMemory(code, 0, MEM_RELEASE, userdata); 647 | FreePointerList(blockedMemory, freeMemory, userdata); 648 | SetLastError(ERROR_OUTOFMEMORY); 649 | return NULL; 650 | } 651 | 652 | node->next = blockedMemory; 653 | node->address = code; 654 | blockedMemory = node; 655 | 656 | code = (unsigned char *)allocMemory(NULL, 657 | alignedImageSize, 658 | MEM_RESERVE | MEM_COMMIT, 659 | PAGE_READWRITE, 660 | userdata); 661 | if (code == NULL) { 662 | FreePointerList(blockedMemory, freeMemory, userdata); 663 | SetLastError(ERROR_OUTOFMEMORY); 664 | return NULL; 665 | } 666 | } 667 | #endif 668 | 669 | result = (PMEMORYMODULE)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(MEMORYMODULE)); 670 | if (result == NULL) { 671 | freeMemory(code, 0, MEM_RELEASE, userdata); 672 | #ifdef _WIN64 673 | FreePointerList(blockedMemory, freeMemory, userdata); 674 | #endif 675 | SetLastError(ERROR_OUTOFMEMORY); 676 | return NULL; 677 | } 678 | 679 | result->codeBase = code; 680 | result->isDLL = (old_header->FileHeader.Characteristics & IMAGE_FILE_DLL) != 0; 681 | result->alloc = allocMemory; 682 | result->free = freeMemory; 683 | result->loadLibrary = loadLibrary; 684 | result->getProcAddress = getProcAddress; 685 | result->freeLibrary = freeLibrary; 686 | result->userdata = userdata; 687 | result->pageSize = sysInfo.dwPageSize; 688 | #ifdef _WIN64 689 | result->blockedMemory = blockedMemory; 690 | #endif 691 | 692 | if (!CheckSize(size, old_header->OptionalHeader.SizeOfHeaders)) { 693 | goto error; 694 | } 695 | 696 | // commit memory for headers 697 | headers = (unsigned char *)allocMemory(code, 698 | old_header->OptionalHeader.SizeOfHeaders, 699 | MEM_COMMIT, 700 | PAGE_READWRITE, 701 | userdata); 702 | 703 | // copy PE header to code 704 | memcpy(headers, dos_header, old_header->OptionalHeader.SizeOfHeaders); 705 | result->headers = (PIMAGE_NT_HEADERS)&((const unsigned char *)(headers))[dos_header->e_lfanew]; 706 | 707 | // update position 708 | result->headers->OptionalHeader.ImageBase = (uintptr_t)code; 709 | 710 | // copy sections from DLL file block to new memory location 711 | if (!CopySections((const unsigned char *) data, size, old_header, result)) { 712 | goto error; 713 | } 714 | 715 | // adjust base address of imported data 716 | locationDelta = (ptrdiff_t)(result->headers->OptionalHeader.ImageBase - old_header->OptionalHeader.ImageBase); 717 | if (locationDelta != 0) { 718 | result->isRelocated = PerformBaseRelocation(result, locationDelta); 719 | } else { 720 | result->isRelocated = TRUE; 721 | } 722 | 723 | // load required dlls and adjust function table of imports 724 | if (!BuildImportTable(result)) { 725 | goto error; 726 | } 727 | 728 | // mark memory pages depending on section headers and release 729 | // sections that are marked as "discardable" 730 | if (!FinalizeSections(result)) { 731 | goto error; 732 | } 733 | 734 | // TLS callbacks are executed BEFORE the main loading 735 | if (!ExecuteTLS(result)) { 736 | goto error; 737 | } 738 | 739 | // get entry point of loaded library 740 | if (result->headers->OptionalHeader.AddressOfEntryPoint != 0) { 741 | if (result->isDLL) { 742 | DllEntryProc DllEntry = (DllEntryProc)(LPVOID)(code + result->headers->OptionalHeader.AddressOfEntryPoint); 743 | // notify library about attaching to process 744 | BOOL successfull = (*DllEntry)((HINSTANCE)code, DLL_PROCESS_ATTACH, 0); 745 | if (!successfull) { 746 | SetLastError(ERROR_DLL_INIT_FAILED); 747 | goto error; 748 | } 749 | result->initialized = TRUE; 750 | } else { 751 | result->exeEntry = (ExeEntryProc)(LPVOID)(code + result->headers->OptionalHeader.AddressOfEntryPoint); 752 | } 753 | } else { 754 | result->exeEntry = NULL; 755 | } 756 | 757 | return (HMEMORYMODULE)result; 758 | 759 | error: 760 | // cleanup 761 | MemoryFreeLibrary(result); 762 | return NULL; 763 | } 764 | 765 | static int _compare(const void *a, const void *b) 766 | { 767 | const struct ExportNameEntry *p1 = (const struct ExportNameEntry*) a; 768 | const struct ExportNameEntry *p2 = (const struct ExportNameEntry*) b; 769 | return strcmp(p1->name, p2->name); 770 | } 771 | 772 | static int _find(const void *a, const void *b) 773 | { 774 | LPCSTR *name = (LPCSTR *) a; 775 | const struct ExportNameEntry *p = (const struct ExportNameEntry*) b; 776 | return strcmp(*name, p->name); 777 | } 778 | 779 | FARPROC MemoryGetProcAddress(HMEMORYMODULE mod, LPCSTR name) 780 | { 781 | PMEMORYMODULE module = (PMEMORYMODULE)mod; 782 | unsigned char *codeBase = module->codeBase; 783 | DWORD idx = 0; 784 | PIMAGE_EXPORT_DIRECTORY exports; 785 | PIMAGE_DATA_DIRECTORY directory = GET_HEADER_DICTIONARY(module, IMAGE_DIRECTORY_ENTRY_EXPORT); 786 | if (directory->Size == 0) { 787 | // no export table found 788 | SetLastError(ERROR_PROC_NOT_FOUND); 789 | return NULL; 790 | } 791 | 792 | exports = (PIMAGE_EXPORT_DIRECTORY) (codeBase + directory->VirtualAddress); 793 | if (exports->NumberOfNames == 0 || exports->NumberOfFunctions == 0) { 794 | // DLL doesn't export anything 795 | SetLastError(ERROR_PROC_NOT_FOUND); 796 | return NULL; 797 | } 798 | 799 | if (HIWORD(name) == 0) { 800 | // load function by ordinal value 801 | if (LOWORD(name) < exports->Base) { 802 | SetLastError(ERROR_PROC_NOT_FOUND); 803 | return NULL; 804 | } 805 | 806 | idx = LOWORD(name) - exports->Base; 807 | } else if (!exports->NumberOfNames) { 808 | SetLastError(ERROR_PROC_NOT_FOUND); 809 | return NULL; 810 | } else { 811 | const struct ExportNameEntry *found; 812 | 813 | // Lazily build name table and sort it by names 814 | if (!module->nameExportsTable) { 815 | DWORD i; 816 | DWORD *nameRef = (DWORD *) (codeBase + exports->AddressOfNames); 817 | WORD *ordinal = (WORD *) (codeBase + exports->AddressOfNameOrdinals); 818 | struct ExportNameEntry *entry = (struct ExportNameEntry*) malloc(exports->NumberOfNames * sizeof(struct ExportNameEntry)); 819 | module->nameExportsTable = entry; 820 | if (!entry) { 821 | SetLastError(ERROR_OUTOFMEMORY); 822 | return NULL; 823 | } 824 | for (i=0; iNumberOfNames; i++, nameRef++, ordinal++, entry++) { 825 | entry->name = (const char *) (codeBase + (*nameRef)); 826 | entry->idx = *ordinal; 827 | } 828 | qsort(module->nameExportsTable, 829 | exports->NumberOfNames, 830 | sizeof(struct ExportNameEntry), _compare); 831 | } 832 | 833 | // search function name in list of exported names with binary search 834 | found = (const struct ExportNameEntry*) bsearch(&name, 835 | module->nameExportsTable, 836 | exports->NumberOfNames, 837 | sizeof(struct ExportNameEntry), _find); 838 | if (!found) { 839 | // exported symbol not found 840 | SetLastError(ERROR_PROC_NOT_FOUND); 841 | return NULL; 842 | } 843 | 844 | idx = found->idx; 845 | } 846 | 847 | if (idx > exports->NumberOfFunctions) { 848 | // name <-> ordinal number don't match 849 | SetLastError(ERROR_PROC_NOT_FOUND); 850 | return NULL; 851 | } 852 | 853 | // AddressOfFunctions contains the RVAs to the "real" functions 854 | return (FARPROC)(LPVOID)(codeBase + (*(DWORD *) (codeBase + exports->AddressOfFunctions + (idx*4)))); 855 | } 856 | 857 | void MemoryFreeLibrary(HMEMORYMODULE mod) 858 | { 859 | PMEMORYMODULE module = (PMEMORYMODULE)mod; 860 | 861 | if (module == NULL) { 862 | return; 863 | } 864 | if (module->initialized) { 865 | // notify library about detaching from process 866 | DllEntryProc DllEntry = (DllEntryProc)(LPVOID)(module->codeBase + module->headers->OptionalHeader.AddressOfEntryPoint); 867 | (*DllEntry)((HINSTANCE)module->codeBase, DLL_PROCESS_DETACH, 0); 868 | } 869 | 870 | free(module->nameExportsTable); 871 | if (module->modules != NULL) { 872 | // free previously opened libraries 873 | int i; 874 | for (i=0; inumModules; i++) { 875 | if (module->modules[i] != NULL) { 876 | module->freeLibrary(module->modules[i], module->userdata); 877 | } 878 | } 879 | 880 | free(module->modules); 881 | } 882 | 883 | if (module->codeBase != NULL) { 884 | // release memory of library 885 | module->free(module->codeBase, 0, MEM_RELEASE, module->userdata); 886 | } 887 | 888 | #ifdef _WIN64 889 | FreePointerList(module->blockedMemory, module->free, module->userdata); 890 | #endif 891 | HeapFree(GetProcessHeap(), 0, module); 892 | } 893 | 894 | int MemoryCallEntryPoint(HMEMORYMODULE mod) 895 | { 896 | PMEMORYMODULE module = (PMEMORYMODULE)mod; 897 | 898 | if (module == NULL || module->isDLL || module->exeEntry == NULL || !module->isRelocated) { 899 | return -1; 900 | } 901 | 902 | return module->exeEntry(); 903 | } 904 | 905 | #define DEFAULT_LANGUAGE MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL) 906 | 907 | HMEMORYRSRC MemoryFindResource(HMEMORYMODULE module, LPCTSTR name, LPCTSTR type) 908 | { 909 | return MemoryFindResourceEx(module, name, type, DEFAULT_LANGUAGE); 910 | } 911 | 912 | static PIMAGE_RESOURCE_DIRECTORY_ENTRY _MemorySearchResourceEntry( 913 | void *root, 914 | PIMAGE_RESOURCE_DIRECTORY resources, 915 | LPCTSTR key) 916 | { 917 | PIMAGE_RESOURCE_DIRECTORY_ENTRY entries = (PIMAGE_RESOURCE_DIRECTORY_ENTRY) (resources + 1); 918 | PIMAGE_RESOURCE_DIRECTORY_ENTRY result = NULL; 919 | DWORD start; 920 | DWORD end; 921 | DWORD middle; 922 | 923 | if (!IS_INTRESOURCE(key) && key[0] == TEXT('#')) { 924 | // special case: resource id given as string 925 | TCHAR *endpos = NULL; 926 | long int tmpkey = (WORD) _tcstol((TCHAR *) &key[1], &endpos, 10); 927 | if (tmpkey <= 0xffff && lstrlen(endpos) == 0) { 928 | key = MAKEINTRESOURCE(tmpkey); 929 | } 930 | } 931 | 932 | // entries are stored as ordered list of named entries, 933 | // followed by an ordered list of id entries - we can do 934 | // a binary search to find faster... 935 | if (IS_INTRESOURCE(key)) { 936 | WORD check = (WORD) (uintptr_t) key; 937 | start = resources->NumberOfNamedEntries; 938 | end = start + resources->NumberOfIdEntries; 939 | 940 | while (end > start) { 941 | WORD entryName; 942 | middle = (start + end) >> 1; 943 | entryName = (WORD) entries[middle].Name; 944 | if (check < entryName) { 945 | end = (end != middle ? middle : middle-1); 946 | } else if (check > entryName) { 947 | start = (start != middle ? middle : middle+1); 948 | } else { 949 | result = &entries[middle]; 950 | break; 951 | } 952 | } 953 | } else { 954 | LPCWSTR searchKey; 955 | size_t searchKeyLen = _tcslen(key); 956 | #if defined(UNICODE) 957 | searchKey = key; 958 | #else 959 | // Resource names are always stored using 16bit characters, need to 960 | // convert string we search for. 961 | #define MAX_LOCAL_KEY_LENGTH 2048 962 | // In most cases resource names are short, so optimize for that by 963 | // using a pre-allocated array. 964 | wchar_t _searchKeySpace[MAX_LOCAL_KEY_LENGTH+1]; 965 | LPWSTR _searchKey; 966 | if (searchKeyLen > MAX_LOCAL_KEY_LENGTH) { 967 | size_t _searchKeySize = (searchKeyLen + 1) * sizeof(wchar_t); 968 | _searchKey = (LPWSTR) malloc(_searchKeySize); 969 | if (_searchKey == NULL) { 970 | SetLastError(ERROR_OUTOFMEMORY); 971 | return NULL; 972 | } 973 | } else { 974 | _searchKey = &_searchKeySpace[0]; 975 | } 976 | 977 | mbstowcs(_searchKey, key, searchKeyLen); 978 | _searchKey[searchKeyLen] = 0; 979 | searchKey = _searchKey; 980 | #endif 981 | start = 0; 982 | end = resources->NumberOfNamedEntries; 983 | while (end > start) { 984 | int cmp; 985 | PIMAGE_RESOURCE_DIR_STRING_U resourceString; 986 | middle = (start + end) >> 1; 987 | resourceString = (PIMAGE_RESOURCE_DIR_STRING_U) OffsetPointer(root, entries[middle].Name & 0x7FFFFFFF); 988 | cmp = _wcsnicmp(searchKey, resourceString->NameString, resourceString->Length); 989 | if (cmp == 0) { 990 | // Handle partial match 991 | if (searchKeyLen > resourceString->Length) { 992 | cmp = 1; 993 | } else if (searchKeyLen < resourceString->Length) { 994 | cmp = -1; 995 | } 996 | } 997 | if (cmp < 0) { 998 | end = (middle != end ? middle : middle-1); 999 | } else if (cmp > 0) { 1000 | start = (middle != start ? middle : middle+1); 1001 | } else { 1002 | result = &entries[middle]; 1003 | break; 1004 | } 1005 | } 1006 | #if !defined(UNICODE) 1007 | if (searchKeyLen > MAX_LOCAL_KEY_LENGTH) { 1008 | free(_searchKey); 1009 | } 1010 | #undef MAX_LOCAL_KEY_LENGTH 1011 | #endif 1012 | } 1013 | 1014 | return result; 1015 | } 1016 | 1017 | HMEMORYRSRC MemoryFindResourceEx(HMEMORYMODULE module, LPCTSTR name, LPCTSTR type, WORD language) 1018 | { 1019 | unsigned char *codeBase = ((PMEMORYMODULE) module)->codeBase; 1020 | PIMAGE_DATA_DIRECTORY directory = GET_HEADER_DICTIONARY((PMEMORYMODULE) module, IMAGE_DIRECTORY_ENTRY_RESOURCE); 1021 | PIMAGE_RESOURCE_DIRECTORY rootResources; 1022 | PIMAGE_RESOURCE_DIRECTORY nameResources; 1023 | PIMAGE_RESOURCE_DIRECTORY typeResources; 1024 | PIMAGE_RESOURCE_DIRECTORY_ENTRY foundType; 1025 | PIMAGE_RESOURCE_DIRECTORY_ENTRY foundName; 1026 | PIMAGE_RESOURCE_DIRECTORY_ENTRY foundLanguage; 1027 | if (directory->Size == 0) { 1028 | // no resource table found 1029 | SetLastError(ERROR_RESOURCE_DATA_NOT_FOUND); 1030 | return NULL; 1031 | } 1032 | 1033 | if (language == DEFAULT_LANGUAGE) { 1034 | // use language from current thread 1035 | language = LANGIDFROMLCID(GetThreadLocale()); 1036 | } 1037 | 1038 | // resources are stored as three-level tree 1039 | // - first node is the type 1040 | // - second node is the name 1041 | // - third node is the language 1042 | rootResources = (PIMAGE_RESOURCE_DIRECTORY) (codeBase + directory->VirtualAddress); 1043 | foundType = _MemorySearchResourceEntry(rootResources, rootResources, type); 1044 | if (foundType == NULL) { 1045 | SetLastError(ERROR_RESOURCE_TYPE_NOT_FOUND); 1046 | return NULL; 1047 | } 1048 | 1049 | typeResources = (PIMAGE_RESOURCE_DIRECTORY) (codeBase + directory->VirtualAddress + (foundType->OffsetToData & 0x7fffffff)); 1050 | foundName = _MemorySearchResourceEntry(rootResources, typeResources, name); 1051 | if (foundName == NULL) { 1052 | SetLastError(ERROR_RESOURCE_NAME_NOT_FOUND); 1053 | return NULL; 1054 | } 1055 | 1056 | nameResources = (PIMAGE_RESOURCE_DIRECTORY) (codeBase + directory->VirtualAddress + (foundName->OffsetToData & 0x7fffffff)); 1057 | foundLanguage = _MemorySearchResourceEntry(rootResources, nameResources, (LPCTSTR) (uintptr_t) language); 1058 | if (foundLanguage == NULL) { 1059 | // requested language not found, use first available 1060 | if (nameResources->NumberOfIdEntries == 0) { 1061 | SetLastError(ERROR_RESOURCE_LANG_NOT_FOUND); 1062 | return NULL; 1063 | } 1064 | 1065 | foundLanguage = (PIMAGE_RESOURCE_DIRECTORY_ENTRY) (nameResources + 1); 1066 | } 1067 | 1068 | return (codeBase + directory->VirtualAddress + (foundLanguage->OffsetToData & 0x7fffffff)); 1069 | } 1070 | 1071 | DWORD MemorySizeofResource(HMEMORYMODULE module, HMEMORYRSRC resource) 1072 | { 1073 | PIMAGE_RESOURCE_DATA_ENTRY entry; 1074 | UNREFERENCED_PARAMETER(module); 1075 | entry = (PIMAGE_RESOURCE_DATA_ENTRY) resource; 1076 | if (entry == NULL) { 1077 | return 0; 1078 | } 1079 | 1080 | return entry->Size; 1081 | } 1082 | 1083 | LPVOID MemoryLoadResource(HMEMORYMODULE module, HMEMORYRSRC resource) 1084 | { 1085 | unsigned char *codeBase = ((PMEMORYMODULE) module)->codeBase; 1086 | PIMAGE_RESOURCE_DATA_ENTRY entry = (PIMAGE_RESOURCE_DATA_ENTRY) resource; 1087 | if (entry == NULL) { 1088 | return NULL; 1089 | } 1090 | 1091 | return codeBase + entry->OffsetToData; 1092 | } 1093 | 1094 | int 1095 | MemoryLoadString(HMEMORYMODULE module, UINT id, LPTSTR buffer, int maxsize) 1096 | { 1097 | return MemoryLoadStringEx(module, id, buffer, maxsize, DEFAULT_LANGUAGE); 1098 | } 1099 | 1100 | int 1101 | MemoryLoadStringEx(HMEMORYMODULE module, UINT id, LPTSTR buffer, int maxsize, WORD language) 1102 | { 1103 | HMEMORYRSRC resource; 1104 | PIMAGE_RESOURCE_DIR_STRING_U data; 1105 | DWORD size; 1106 | if (maxsize == 0) { 1107 | return 0; 1108 | } 1109 | 1110 | resource = MemoryFindResourceEx(module, MAKEINTRESOURCE((id >> 4) + 1), RT_STRING, language); 1111 | if (resource == NULL) { 1112 | buffer[0] = 0; 1113 | return 0; 1114 | } 1115 | 1116 | data = (PIMAGE_RESOURCE_DIR_STRING_U) MemoryLoadResource(module, resource); 1117 | id = id & 0x0f; 1118 | while (id--) { 1119 | data = (PIMAGE_RESOURCE_DIR_STRING_U) OffsetPointer(data, (data->Length + 1) * sizeof(WCHAR)); 1120 | } 1121 | if (data->Length == 0) { 1122 | SetLastError(ERROR_RESOURCE_NAME_NOT_FOUND); 1123 | buffer[0] = 0; 1124 | return 0; 1125 | } 1126 | 1127 | size = data->Length; 1128 | if (size >= (DWORD) maxsize) { 1129 | size = maxsize; 1130 | } else { 1131 | buffer[size] = 0; 1132 | } 1133 | #if defined(UNICODE) 1134 | wcsncpy(buffer, data->NameString, size); 1135 | #else 1136 | wcstombs(buffer, data->NameString, size); 1137 | #endif 1138 | return size; 1139 | } 1140 | 1141 | #ifdef TESTSUITE 1142 | #include 1143 | 1144 | #ifndef PRIxPTR 1145 | #ifdef _WIN64 1146 | #define PRIxPTR "I64x" 1147 | #else 1148 | #define PRIxPTR "x" 1149 | #endif 1150 | #endif 1151 | 1152 | static const uintptr_t AlignValueDownTests[][3] = { 1153 | {16, 16, 16}, 1154 | {17, 16, 16}, 1155 | {32, 16, 32}, 1156 | {33, 16, 32}, 1157 | #ifdef _WIN64 1158 | {0x12345678abcd1000, 0x1000, 0x12345678abcd1000}, 1159 | {0x12345678abcd101f, 0x1000, 0x12345678abcd1000}, 1160 | #endif 1161 | {0, 0, 0}, 1162 | }; 1163 | 1164 | static const uintptr_t AlignValueUpTests[][3] = { 1165 | {16, 16, 16}, 1166 | {17, 16, 32}, 1167 | {32, 16, 32}, 1168 | {33, 16, 48}, 1169 | #ifdef _WIN64 1170 | {0x12345678abcd1000, 0x1000, 0x12345678abcd1000}, 1171 | {0x12345678abcd101f, 0x1000, 0x12345678abcd2000}, 1172 | #endif 1173 | {0, 0, 0}, 1174 | }; 1175 | 1176 | BOOL MemoryModuleTestsuite() { 1177 | BOOL success = TRUE; 1178 | size_t idx; 1179 | for (idx = 0; AlignValueDownTests[idx][0]; ++idx) { 1180 | const uintptr_t* tests = AlignValueDownTests[idx]; 1181 | uintptr_t value = AlignValueDown(tests[0], tests[1]); 1182 | if (value != tests[2]) { 1183 | printf("AlignValueDown failed for 0x%" PRIxPTR "/0x%" PRIxPTR ": expected 0x%" PRIxPTR ", got 0x%" PRIxPTR "\n", 1184 | tests[0], tests[1], tests[2], value); 1185 | success = FALSE; 1186 | } 1187 | } 1188 | for (idx = 0; AlignValueDownTests[idx][0]; ++idx) { 1189 | const uintptr_t* tests = AlignValueUpTests[idx]; 1190 | uintptr_t value = AlignValueUp(tests[0], tests[1]); 1191 | if (value != tests[2]) { 1192 | printf("AlignValueUp failed for 0x%" PRIxPTR "/0x%" PRIxPTR ": expected 0x%" PRIxPTR ", got 0x%" PRIxPTR "\n", 1193 | tests[0], tests[1], tests[2], value); 1194 | success = FALSE; 1195 | } 1196 | } 1197 | if (success) { 1198 | printf("OK\n"); 1199 | } 1200 | return success; 1201 | } 1202 | #endif 1203 | --------------------------------------------------------------------------------