123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546 |
- :author: Joachim Bauch
- :contact: mail@joachim-bauch.de
- :copyright: `Creative Commons License (by-sa)`__
- __ http://creativecommons.org/licenses/by-sa/2.5/
- .. contents::
- Overview
- =========
- The default windows API functions to load external libraries into a program
- (LoadLibrary, LoadLibraryEx) only work with files on the filesystem. It's
- therefore impossible to load a DLL from memory.
- But sometimes, you need exactly this functionality (e.g. you don't want to
- distribute a lot of files or want to make disassembling harder). Common
- workarounds for this problems are to write the DLL into a temporary file
- first and import it from there. When the program terminates, the temporary
- file gets deleted.
- In this tutorial, I will describe first, how DLL files are structured and
- will present some code that can be used to load a DLL completely from memory -
- without storing on the disk first.
- Windows executables - the PE format
- ====================================
- Most windows binaries that can contain executable code (.exe, .dll, .sys)
- share a common file format that consists of the following parts:
- +----------------+
- | DOS header |
- | |
- | DOS stub |
- +----------------+
- | PE header |
- +----------------+
- | Section header |
- +----------------+
- | Section 1 |
- +----------------+
- | Section 2 |
- +----------------+
- | . . . |
- +----------------+
- | Section n |
- +----------------+
- All structures given below can be found in the header file `winnt.h`.
- DOS header / stub
- ------------------
- The DOS header is only used for backwards compatibility. It precedes the DOS
- stub that normally just displays an error message about the program not being
- able to be run from DOS mode.
- Microsoft defines the DOS header as follows::
- typedef struct _IMAGE_DOS_HEADER { // DOS .EXE header
- WORD e_magic; // Magic number
- WORD e_cblp; // Bytes on last page of file
- WORD e_cp; // Pages in file
- WORD e_crlc; // Relocations
- WORD e_cparhdr; // Size of header in paragraphs
- WORD e_minalloc; // Minimum extra paragraphs needed
- WORD e_maxalloc; // Maximum extra paragraphs needed
- WORD e_ss; // Initial (relative) SS value
- WORD e_sp; // Initial SP value
- WORD e_csum; // Checksum
- WORD e_ip; // Initial IP value
- WORD e_cs; // Initial (relative) CS value
- WORD e_lfarlc; // File address of relocation table
- WORD e_ovno; // Overlay number
- WORD e_res[4]; // Reserved words
- WORD e_oemid; // OEM identifier (for e_oeminfo)
- WORD e_oeminfo; // OEM information; e_oemid specific
- WORD e_res2[10]; // Reserved words
- LONG e_lfanew; // File address of new exe header
- } IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;
- PE header
- ----------
- The PE header contains informations about the different sections inside the
- executable that are used to store code and data or to define imports from other
- libraries or exports this libraries provides.
- It's defined as follows::
- typedef struct _IMAGE_NT_HEADERS {
- DWORD Signature;
- IMAGE_FILE_HEADER FileHeader;
- IMAGE_OPTIONAL_HEADER32 OptionalHeader;
- } IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;
- The `FileHeader` describes the *physical* format of the file, i.e. contents, informations
- about symbols, etc::
- typedef struct _IMAGE_FILE_HEADER {
- WORD Machine;
- WORD NumberOfSections;
- DWORD TimeDateStamp;
- DWORD PointerToSymbolTable;
- DWORD NumberOfSymbols;
- WORD SizeOfOptionalHeader;
- WORD Characteristics;
- } IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;
- .. _OptionalHeader:
- The `OptionalHeader` contains informations about the *logical* format of the library,
- including required OS version, memory requirements and entry points::
- typedef struct _IMAGE_OPTIONAL_HEADER {
- //
- // Standard fields.
- //
- WORD Magic;
- BYTE MajorLinkerVersion;
- BYTE MinorLinkerVersion;
- DWORD SizeOfCode;
- DWORD SizeOfInitializedData;
- DWORD SizeOfUninitializedData;
- DWORD AddressOfEntryPoint;
- DWORD BaseOfCode;
- DWORD BaseOfData;
- //
- // NT additional fields.
- //
- DWORD ImageBase;
- DWORD SectionAlignment;
- DWORD FileAlignment;
- WORD MajorOperatingSystemVersion;
- WORD MinorOperatingSystemVersion;
- WORD MajorImageVersion;
- WORD MinorImageVersion;
- WORD MajorSubsystemVersion;
- WORD MinorSubsystemVersion;
- DWORD Win32VersionValue;
- DWORD SizeOfImage;
- DWORD SizeOfHeaders;
- DWORD CheckSum;
- WORD Subsystem;
- WORD DllCharacteristics;
- DWORD SizeOfStackReserve;
- DWORD SizeOfStackCommit;
- DWORD SizeOfHeapReserve;
- DWORD SizeOfHeapCommit;
- DWORD LoaderFlags;
- DWORD NumberOfRvaAndSizes;
- IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
- } IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;
- .. _DataDirectory:
- The `DataDirectory` contains 16 (`IMAGE_NUMBEROF_DIRECTORY_ENTRIES`) entries
- defining the logical components of the library:
- ===== ==========================
- Index Description
- ===== ==========================
- 0 Exported functions
- ----- --------------------------
- 1 Imported functions
- ----- --------------------------
- 2 Resources
- ----- --------------------------
- 3 Exception informations
- ----- --------------------------
- 4 Security informations
- ----- --------------------------
- 5 Base relocation table
- ----- --------------------------
- 6 Debug informations
- ----- --------------------------
- 7 Architecture specific data
- ----- --------------------------
- 8 Global pointer
- ----- --------------------------
- 9 Thread local storage
- ----- --------------------------
- 10 Load configuration
- ----- --------------------------
- 11 Bound imports
- ----- --------------------------
- 12 Import address table
- ----- --------------------------
- 13 Delay load imports
- ----- --------------------------
- 14 COM runtime descriptor
- ===== ==========================
- For importing the DLL we only need the entries describing the imports and the
- base relocation table. In order to provide access to the exported functions,
- the exports entry is required.
- Section header
- ---------------
- The section header is stored after the OptionalHeader_ structure in the PE
- header. Microsoft provides the macro `IMAGE_FIRST_SECTION` to get the start
- address based on the PE header.
- Actually, the section header is a list of informations about each section in
- the file::
- typedef struct _IMAGE_SECTION_HEADER {
- BYTE Name[IMAGE_SIZEOF_SHORT_NAME];
- union {
- DWORD PhysicalAddress;
- DWORD VirtualSize;
- } Misc;
- DWORD VirtualAddress;
- DWORD SizeOfRawData;
- DWORD PointerToRawData;
- DWORD PointerToRelocations;
- DWORD PointerToLinenumbers;
- WORD NumberOfRelocations;
- WORD NumberOfLinenumbers;
- DWORD Characteristics;
- } IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;
- A section can contain code, data, relocation informations, resources, export or
- import definitions, etc.
- Loading the library
- ====================
- To emulate the PE loader, we must first understand, which steps are neccessary
- to load the file to memory and prepare the structures so they can be called from
- other programs.
- When issuing the API call `LoadLibrary`, Windows basically performs these tasks:
- 1. Open the given file and check the DOS and PE headers.
- 2. Try to allocate a memory block of `PEHeader.OptionalHeader.SizeOfImage` bytes
- at position `PEHeader.OptionalHeader.ImageBase`.
- 3. Parse section headers and copy sections to their addresses. The destination
- address for each section, relative to the base of the allocated memory block,
- is stored in the `VirtualAddress` attribute of the `IMAGE_SECTION_HEADER`
- structure.
- 4. If the allocated memory block differs from `ImageBase`, various references in
- the code and/or data sections must be adjusted. This is called *Base
- relocation*.
- 5. The required imports for the library must be resolved by loading the
- corresponding libraries.
- 6. The memory regions of the different sections must be protected depending on
- the section's characteristics. Some sections are marked as *discardable*
- and therefore can be safely freed at this point. These sections normally
- contain temporary data that is only needed during the import, like the
- informations for the base relocation.
- 7. Now the library is loaded completely. It must be notified about this by
- calling the entry point using the flag `DLL_PROCESS_ATTACH`.
- In the following paragraphs, each step is described.
- Allocate memory
- ----------------
- All memory required for the library must be reserved / allocated using
- `VirtualAlloc`, as Windows provides functions to protect these memory blocks.
- This is required to restrict access to the memory, like blocking write access
- to the code or constant data.
- The OptionalHeader_ structure defines the size of the required memory block
- for the library. It must be reserved at the address specified by `ImageBase`
- if possible::
- memory = VirtualAlloc((LPVOID)(PEHeader->OptionalHeader.ImageBase),
- PEHeader->OptionalHeader.SizeOfImage,
- MEM_RESERVE,
- PAGE_READWRITE);
- If the reserved memory differs from the address given in `ImageBase`, base
- relocation as described below must be done.
- Copy sections
- --------------
- Once the memory has been reserved, the file contents can be copied to the
- system. The section header must get evaluated in order to determine the
- position in the file and the target area in memory.
- Before copying the data, the memory block must get committed::
- dest = VirtualAlloc(baseAddress + section->VirtualAddress,
- section->SizeOfRawData,
- MEM_COMMIT,
- PAGE_READWRITE);
- Sections without data in the file (like data sections for the used variables)
- have a `SizeOfRawData` of `0`, so you can use the `SizeOfInitializedData`
- or `SizeOfUninitializedData` of the OptionalHeader_. Which one must get
- choosen depending on the bit flags `IMAGE_SCN_CNT_INITIALIZED_DATA` and
- `IMAGE_SCN_CNT_UNINITIALIZED_DATA` that may be set in the section`s
- characteristics.
- Base relocation
- ----------------
- All memory addresses in the code / data sections of a library are stored relative
- to the address defined by `ImageBase` in the OptionalHeader_. If the library
- can't be imported to this memory address, the references must get adjusted
- => *relocated*. The file format helps for this by storing informations about
- all these references in the base relocation table, which can be found in the
- directory entry 5 of the DataDirectory_ in the OptionalHeader_.
- This table consists of a series of this structure
- ::
- typedef struct _IMAGE_BASE_RELOCATION {
- DWORD VirtualAddress;
- DWORD SizeOfBlock;
- } IMAGE_BASE_RELOCATION;
- It contains `(SizeOfBlock - IMAGE_SIZEOF_BASE_RELOCATION) / 2` entries of 16 bits
- each. The upper 4 bits define the type of relocation, the lower 12 bits define
- the offset relative to the `VirtualAddress`.
- The only types that seem to be used in DLLs are
- IMAGE_REL_BASED_ABSOLUTE
- No operation relocation. Used for padding.
- IMAGE_REL_BASED_HIGHLOW
- Add the delta between the `ImageBase` and the allocated memory block to the
- 32 bits found at the offset.
- Resolve imports
- ----------------
- The directory entry 1 of the DataDirectory_ in the OptionalHeader_ specifies
- a list of libraries to import symbols from. Each entry in this list is defined
- as follows::
- typedef struct _IMAGE_IMPORT_DESCRIPTOR {
- union {
- DWORD Characteristics; // 0 for terminating null import descriptor
- DWORD OriginalFirstThunk; // RVA to original unbound IAT (PIMAGE_THUNK_DATA)
- };
- DWORD TimeDateStamp; // 0 if not bound,
- // -1 if bound, and real date\time stamp
- // in IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (new BIND)
- // O.W. date/time stamp of DLL bound to (Old BIND)
- DWORD ForwarderChain; // -1 if no forwarders
- DWORD Name;
- DWORD FirstThunk; // RVA to IAT (if bound this IAT has actual addresses)
- } IMAGE_IMPORT_DESCRIPTOR;
- The `Name` entry describes the offset to the NULL-terminated string of the library
- name (e.g. `KERNEL32.DLL`). The `OriginalFirstThunk` entry points to a list
- of references to the function names to import from the external library.
- `FirstThunk` points to a list of addresses that gets filled with pointers to
- the imported symbols.
- When we resolve the imports, we walk both lists in parallel, import the function
- defined by the name in the first list and store the pointer to the symbol in the
- second list::
- nameRef = (DWORD *)(baseAddress + importDesc->OriginalFirstThunk);
- symbolRef = (DWORD *)(baseAddress + importDesc->FirstThunk);
- for (; *nameRef; nameRef++, symbolRef++)
- {
- PIMAGE_IMPORT_BY_NAME thunkData = (PIMAGE_IMPORT_BY_NAME)(codeBase + *nameRef);
- *symbolRef = (DWORD)GetProcAddress(handle, (LPCSTR)&thunkData->Name);
- if (*funcRef == 0)
- {
- handleImportError();
- return;
- }
- }
- Protect memory
- ---------------
- Every section specifies permission flags in it's `Characteristics` entry.
- These flags can be one or a combination of
- IMAGE_SCN_MEM_EXECUTE
- The section contains data that can be executed.
- IMAGE_SCN_MEM_READ
- The section contains data that is readable.
- IMAGE_SCN_MEM_WRITE
- The section contains data that is writeable.
- These flags must get mapped to the protection flags
- - PAGE_NOACCESS
- - PAGE_WRITECOPY
- - PAGE_READONLY
- - PAGE_READWRITE
- - PAGE_EXECUTE
- - PAGE_EXECUTE_WRITECOPY
- - PAGE_EXECUTE_READ
- - PAGE_EXECUTE_READWRITE
- Now, the function `VirtualProtect` can be used to limit access to the memory.
- If the program tries to access it in a unauthorized way, an exception gets
- raised by Windows.
- In addition the section flags above, the following can be added:
- IMAGE_SCN_MEM_DISCARDABLE
- The data in this section can be freed after the import. Usually this is
- specified for relocation data.
- IMAGE_SCN_MEM_NOT_CACHED
- The data in this section must not get cached by Windows. Add the bit
- flag `PAGE_NOCACHE` to the protection flags above.
- Notify library
- ---------------
- The last thing to do is to call the DLL entry point (defined by
- `AddressOfEntryPoint`) and so notifying the library about being attached
- to a process.
- The function at the entry point is defined as
- ::
- typedef BOOL (WINAPI *DllEntryProc)(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved);
- So the last code we need to execute is
- ::
- DllEntryProc entry = (DllEntryProc)(baseAddress + PEHeader->OptionalHeader.AddressOfEntryPoint);
- (*entry)((HINSTANCE)baseAddress, DLL_PROCESS_ATTACH, 0);
- Afterwards we can use the exported functions as with any normal library.
- Exported functions
- ===================
- If you want to access the functions that are exported by the library, you need to find the entry
- point to a symbol, i.e. the name of the function to call.
- The directory entry 0 of the DataDirectory_ in the OptionalHeader_ contains informations about
- the exported functions. It's defined as follows::
- typedef struct _IMAGE_EXPORT_DIRECTORY {
- DWORD Characteristics;
- DWORD TimeDateStamp;
- WORD MajorVersion;
- WORD MinorVersion;
- DWORD Name;
- DWORD Base;
- DWORD NumberOfFunctions;
- DWORD NumberOfNames;
- DWORD AddressOfFunctions; // RVA from base of image
- DWORD AddressOfNames; // RVA from base of image
- DWORD AddressOfNameOrdinals; // RVA from base of image
- } IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;
- First thing to do, is to map the name of the function to the ordinal number of the exported
- symbol. Therefore, just walk the arrays defined by `AddressOfNames` and `AddressOfNameOrdinals`
- parallel until you found the required name.
- Now you can use the ordinal number to read the address by evaluating the n-th element of the
- `AddressOfFunctions` array.
- Freeing the library
- ====================
- To free the custom loaded library, perform the steps
- - Call entry point to notify library about being detached::
- DllEntryProc entry = (DllEntryProc)(baseAddress + PEHeader->OptionalHeader.AddressOfEntryPoint);
- (*entry)((HINSTANCE)baseAddress, DLL_PROCESS_ATTACH, 0);
- - Free external libraries used to resolve imports.
- - Free allocated memory.
- MemoryModule
- =============
- MemoryModule is a C-library that can be used to load a DLL from memory.
- The interface is very similar to the standard methods for loading of libraries::
- typedef void *HMEMORYMODULE;
- HMEMORYMODULE MemoryLoadLibrary(const void *, size_t);
- FARPROC MemoryGetProcAddress(HMEMORYMODULE, const char *);
- void MemoryFreeLibrary(HMEMORYMODULE);
- Downloads
- ----------
- The latest development release can always be grabbed from Github at
- http://github.com/fancycode/MemoryModule/
- Known issues
- -------------
- - All memory that is not protected by section flags is gets committed using `PAGE_READWRITE`.
- I don't know if this is correct.
- License
- --------
- Since version 0.0.2, the MemoryModule library is released under the Mozilla Public License (MPL).
- Version 0.0.1 has been released unter the Lesser General Public License (LGPL).
- It is provided as-is without ANY warranty. You may use it at your own risk.
- Copyright
- ==========
- The MemoryModule library and this tutorial are
- Copyright (c) 2004-2015 by Joachim Bauch.
|