mirror of
https://github.com/vxcontrol/MemoryModule.git
synced 2026-07-21 06:05:55 -04:00
added readme.txt
This commit is contained in:
+512
@@ -0,0 +1,512 @@
|
||||
:Author: Joachim Bauch
|
||||
:Contact: mail@joachim-bauch.de
|
||||
|
||||
|
||||
.. 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;
|
||||
|
||||
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;
|
||||
|
||||
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 0 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
|
||||
===================
|
||||
|
||||
TODO
|
||||
|
||||
|
||||
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 *, const size_t);
|
||||
FARPROC MemoryGetProcAddress(HMEMORYMODULE, const char *);
|
||||
void MemoryFreeLibrary(HMEMORYMODULE);
|
||||
|
||||
|
||||
Downloads
|
||||
----------
|
||||
|
||||
Currently, MemoryModule is only available from my SVN server at
|
||||
https://leviathan.joachim-bauch.de/cgi-bin/viewcvs.cgi/MemoryModule/trunk/?root=misc
|
||||
|
||||
|
||||
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
|
||||
--------
|
||||
|
||||
The MemoryModule library is released under 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 by Joachim Bauch.
|
||||
Reference in New Issue
Block a user