标签:
The most important steps of DLL loading are:
DLL_PROCESS_ATTACH
parameter.I wrote the code that performed these steps but then quickly found out something is not OK: This loaded DLL doesn‘t have a valid HMODULE
/HINSTANCE
handle and many windows functions expect you to specify one (for example, GetProcAddress()
, CreateDialog()
, and so on...). Actually the HINSTANCE
handle of a module is nothing more than the address of the DOS/PE header of the loaded DLL in memory.I tried to pass this address to the functions but it didn‘t work because windows checks whether this handle is really a handle and not only the contents of memory! This makes using manually loaded DLLs a bit harder!I had to write my own GetProcAddress()
because the windows version didn‘t work with my DLLs.Later I found out that I want to use dialog resources in the DLL and CreateDialog()
also requires a module handle to get the dialog resources from the DLL. For this reason I invented my custom FindResource()
function that works with manually loaded DLLs and it can be used to find dialog resources that can be passed to the CreateDialogIndirect()
function. You can use other types of resources as well in manually loaded DLLs if you find a function for that resource that cooperates with FindResource()
. In this tip you get the code for the manual DLL loader and GetProcAddress()
, but I post here the resource related functions in another tip.
HMODULE
so it makes life harder especially when its about resources.DllMain()
doesn‘t receive DLL_THREAD_ATTACH
and DLL_THREAD_DETACH
notifications. You could simulate this by creating a small DLL that you load with normal LoadLibrary()
and from the DllMain()
of this normally loaded DLL you could call the entrypoint of your manually loaded DLLs in case of DLL_THREAD_ATTACH/DLL_THREAD_DETACH
.(建立一个普通的用LoadLibrary()加载起来的dll,从这个正常加载起来的dll的DLLMain(),你可以调用手动加载的DLL的入口点)LoadLibrary()
. This is actually not a limitation, just mentioned it for your information. Actually it would be useless to start loading for example kernel32.dll with manual dll loading, most system DLLs would probably disfunction/crash!ntdll.dll!RtlIsValidHandler()
doesn‘t accept exception handler routines from the memory area of our manually loaded DLL (because this memory area isn‘t mapped from a PE file). This is a problem only if an exception is raised inside a __try block of the DLL (because windows can‘t run the exception handler of the DLL and raises another exception that escapes the exception handler of the DLL - the result is usually a crash).定义一个结构体DLLInterface类型:里边是两个函数类型的指针AddNumbers和MyMessageBox
typedef struct DLLInterface { int (*AddNumbers)(int a, int b); void (*MyMessageBox)(const char* message); } DLLInterface;
对应的cpp文件中定义了两个函数:
int AddNumbers(int a, int b) { printf("DLL: AddNumbers(%d, %d)\n", a, b); return a + b; } void MyMessageBox(const char* message) { printf("DLL: MyMessageBox(\"%s\")\n", message); MessageBoxA(NULL, message, "DLL MessageBox!", MB_OK); }
然后将这两个函数作为指针传给上面定义的这样的一个结构体:
DLLInterface g_Interface =
{
AddNumbers,
MyMessageBox
};
将函数存储在dll文件的导出表中,__declspec(dllexport)表示被包含这个函数的程序之外的程序调用:
__declspec(dllexport) const DLLInterface* GetDLLInterface() { return &g_Interface; }
DLLMain:
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) { // With manual DLL loading you can not use DLL_THREAD_ATTACH and DLL_THREAD_DETACH. switch (fdwReason) { case DLL_PROCESS_ATTACH: printf("DLL: DLL_PROCESS_ATTACH\n"); // TODO break; case DLL_PROCESS_DETACH: printf("DLL: DLL_PROCESS_DETACH\n"); // TODO break; default: break; } return TRUE; }
这里是模拟的从内存中加载DLL:
MODULE_HANDLE LoadModule(const char* dll_path) { LOAD_DLL_INFO* p = new LOAD_DLL_INFO; DWORD res = LoadDLLFromFileName(dll_path, 0, p); if (res != ELoadDLLResult_OK) { delete p; return NULL; } return p; }
LoadDLLFromFileName中调用了:
ELoadDLLResult LoadDLLFromFileNameOffset(const char* filename, size_t dll_offset, size_t dll_size, int flags, LOAD_DLL_INFO* info) { ELoadDLLResult res; FILE* f = fopen(filename, "rb");//filename就是testDLL的路径 if (!f) return ELoadDLLResult_DLLFileNotFound; res = LoadDLLFromCFile(f, dll_offset, dll_size, flags, info); fclose(f); return res; }
再看LoadDLLFromCFile中的内容,这里值得说明:
ELoadDLLResult LoadDLLFromCFile(FILE* f, size_t dll_offset, size_t dll_size, int flags, LOAD_DLL_INFO* info) { LOAD_DLL_FROM_FILE_STRUCT ldffs = { f, dll_offset, dll_size }; return LoadDLL((LOAD_DLL_READPROC)&LoadDLLFromFileCallback, &ldffs, flags, info); }
LOAD_DLL_READPROC是一个函数指针类型,定义如下:
//LOAD_DLL_READPROC是一个函数指针
typedef BOOL (*LOAD_DLL_READPROC)(void* buff, size_t position, size_t size, void* param);
将LoadDLLFromFileCallback作为参数传递给LoadDLL."LoadDLLFromFileCallback"这个函数起名的用意是显然的,就是为了说明这是一个CALLBACK函数
这个CALLBACK函数的源码如下:
static BOOL LoadDLLFromFileCallback(void* buff, size_t position, size_t size, LOAD_DLL_FROM_FILE_STRUCT* param) { if (!size) return TRUE; if ((position + size) > param->dll_size) return FALSE; fseek(param->f, param->dll_offset + position, SEEK_SET); return fread(buff, 1, size, param->f) == size;//用fread的形式把数据读入内存 }
说白了,CALLBACK之所以叫回调,是因为他是为了“被(作为参数)调用”而存在的,而不是为了来调用别的函数。
模拟的FreeLibrary:
bool UnloadModule(MODULE_HANDLE handle) { bool res = FALSE != UnloadDLL(handle); delete handle; return res; }
模拟的GetProcAddress:
void* GetModuleFunction(MODULE_HANDLE handle, const char* func_name) { return (void*)myGetProcAddress_LoadDLLInfo(handle, func_name); }
源码下载、说明:
http://www.codeproject.com/Tips/430684/Loading-Win-DLLs-manually-without-LoadLibrary
下面是一个德国人写的Loading a DLL from memory
This tutorial describes a technique how a dynamic link library (DLL) can be loaded from memory without storing it on the hard-disk first.
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:
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:
If the reserved memory differs from the address given in ImageBase, base relocation as described below must be done.
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:
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
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
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:
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:
Every section specifies permission flags in it’s Characteristics entry. These flags can be one or a combination of
These flags must get mapped to the protection flags
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:
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);
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.
LoadLibrary执行的任务就到此结束了。
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:
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.
To free the custom loaded library, perform the steps
DllEntryProc entry = (DllEntryProc)(baseAddress + PEHeader->OptionalHeader.AddressOfEntryPoint);
(*entry)((HINSTANCE)baseAddress, DLL_PROCESS_ATTACH, 0);
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:
原文参考:
http://www.joachim-bauch.de/tutorials/loading-a-dll-from-memory/
源码参考:
https://github.com/fancycode/MemoryModule
标签:
原文地址:http://www.cnblogs.com/predator-wang/p/4892720.html