diff --git a/MemoryModule/LoadDllMemoryApi.h b/MemoryModule/LoadDllMemoryApi.h new file mode 100644 index 0000000..9987f57 --- /dev/null +++ b/MemoryModule/LoadDllMemoryApi.h @@ -0,0 +1,109 @@ +#pragma once +#include + +typedef PVOID HMEMORYMODULE, PLDR_DATA_TABLE_ENTRY, HMEMORYRSRC; + +/** + * Load DLL from memory location with the given size. + * + * All dependencies are resolved using default LoadLibrary/GetProcAddress + * calls through the Windows API. + */ +HMEMORYMODULE MemoryLoadLibrary(const void*, size_t); + +/** + * Get address of exported method. Supports loading both by name and by + * ordinal value. + */ +FARPROC MemoryGetProcAddress(HMEMORYMODULE, LPCSTR); + +/** + * Free previously loaded DLL. + */ +bool MemoryFreeLibrary(HMEMORYMODULE); + +/** + * Find the location of a resource with the specified type and name. + */ +HMEMORYRSRC MemoryFindResource(HMEMORYMODULE, LPCTSTR, LPCTSTR); + +/** + * Find the location of a resource with the specified type, name and language. + */ +HMEMORYRSRC MemoryFindResourceEx(HMEMORYMODULE, LPCTSTR, LPCTSTR, WORD); + +/** + * Get the size of the resource in bytes. + */ +DWORD MemorySizeofResource(HMEMORYMODULE, HMEMORYRSRC); + +/** + * Get a pointer to the contents of the resource. + */ +LPVOID MemoryLoadResource(HMEMORYMODULE, HMEMORYRSRC); + +/** + * Load a string resource. + */ +int MemoryLoadString(HMEMORYMODULE, UINT, LPTSTR, int); + +/** + * Load a string resource with a given language. + */ +int MemoryLoadStringEx(HMEMORYMODULE, UINT, LPTSTR, int, WORD); + +NTSTATUS NTAPI NtLoadDllMemory( + OUT HMEMORYMODULE* BaseAddress, + IN LPVOID BufferAddress, + IN size_t BufferSize +); + +/* + NtLoadDllMemoryEx dwFlags +*/ + +//If this flag is specified, all subsequent flags will be ignored. +//Also, will be incompatible with Win32 API. +#define LOAD_FLAGS_NOT_MAP_DLL 0x10000000 + +//If this flag is specified, exception handling will not be supported. +#define LOAD_FLAGS_NOT_ADD_INVERTED_FUNCTION 0x00000001 + +//If this flag is specified, NtLoadDllMemory and NtUnloadDllMemory will not use reference counting. +//If you try to load the same module, it will fail. When you unload the module, +// it will be unloaded without checking the reference count. +#define LOAD_FLAGS_NOT_USE_REFERENCE_COUNT 0x00000002 + +//If this flag is specified, DllName and DllFullName cannot be nullptr, +// they can be arbitrary strings without having to be correct file names and paths. +//Otherwise, DllName and DllFullName will use random names if they are nullptr. +//For compatibility with GetModuleHandle, DllName and DllFullName should be guaranteed to always end in .dll +#define LOAD_FLAGS_USE_DLL_NAME 0x00000004 + +NTSTATUS NTAPI NtLoadDllMemoryExW( + OUT HMEMORYMODULE* BaseAddress, + OUT PLDR_DATA_TABLE_ENTRY* LdrEntry OPTIONAL, + IN DWORD dwFlags, + IN LPVOID BufferAddress, + IN size_t BufferSize, + IN LPCWSTR DllName OPTIONAL, + IN LPCWSTR DllFullName OPTIONAL +); +NTSTATUS NTAPI NtLoadDllMemoryExA( + OUT HMEMORYMODULE* BaseAddress, + OUT PLDR_DATA_TABLE_ENTRY* LdrEntry OPTIONAL, + IN DWORD dwFlags, + IN LPVOID BufferAddress, + IN size_t BufferSize, + IN LPCSTR DllName OPTIONAL, + IN LPCSTR DllFullName OPTIONAL +); + +#ifdef UNICODE +#define NtLoadDllMemoryEx NtLoadDllMemoryExW +#else +#define NtLoadDllMemoryEx NtLoadDllMemoryExA +#endif + + +NTSTATUS NTAPI NtUnloadDllMemory(IN HMEMORYMODULE BaseAddress); diff --git a/MemoryModule/MemoryModule.cpp b/MemoryModule/MemoryModule.cpp index b09d159..85a7e86 100644 --- a/MemoryModule/MemoryModule.cpp +++ b/MemoryModule/MemoryModule.cpp @@ -5,9 +5,6 @@ #include "rtltype.h" #include "ntstatus.h" #include -#ifdef DEBUG_OUTPUT -#include -#endif #if _MSC_VER #pragma warning(disable:4055) @@ -17,10 +14,6 @@ #define inline __inline #endif -#ifndef IMAGE_SIZEOF_BASE_RELOCATION -#define IMAGE_SIZEOF_BASE_RELOCATION (sizeof(IMAGE_BASE_RELOCATION)) -#endif - #ifdef _WIN64 #define HOST_MACHINE IMAGE_FILE_MACHINE_AMD64 #else @@ -549,7 +542,7 @@ static int _find(const void* a, const void* b) { FARPROC MemoryGetProcAddress(HMEMORYMODULE mod, LPCSTR name) { PMEMORYMODULE module = MapMemoryModuleHandle(mod); - unsigned char* codeBase = module->codeBase - module->headers_align; + unsigned char* codeBase = module->codeBase; DWORD idx = 0; PIMAGE_EXPORT_DIRECTORY exports; PIMAGE_NT_HEADERS headers = GetImageNtHeaders(module); @@ -632,6 +625,7 @@ bool MemoryFreeLibrary(HMEMORYMODULE mod) { PIMAGE_NT_HEADERS headers = module ? GetImageNtHeaders(module) : nullptr; if (!module || module->Signature != MEMORY_MODULE_SIGNATURE || !headers) return false; + if (module->loadFromNtLoadDllMemory && !module->underUnload)return false; if (module->initialized) { DllEntryProc DllEntry = (DllEntryProc)(LPVOID)(module->codeBase + headers->OptionalHeader.AddressOfEntryPoint); (*DllEntry)((HINSTANCE)module->codeBase, DLL_PROCESS_DETACH, 0); diff --git a/MemoryModule/MemoryModule.h b/MemoryModule/MemoryModule.h index 851345a..17f1b71 100644 --- a/MemoryModule/MemoryModule.h +++ b/MemoryModule/MemoryModule.h @@ -44,28 +44,28 @@ typedef struct _MEMORYMODULE { ULONG64 Signature; __declspec(align(sizeof(size_t))) struct { DWORD SizeofHeaders; - - //Not implemented - struct { - union { + union { + struct { //Status Flags BYTE initialized : 1; - BYTE reservedStatusFlags : 7; + BYTE loadFromNtLoadDllMemory : 1; + BYTE underUnload : 1; + BYTE reservedStatusFlags : 5; BYTE cbFlagsReserved; //Load Flags - WORD notMapDll : 1; - WORD notInsertLdrEntry : 1; - WORD notInsertInvertedFunctionTableEntry : 1; - WORD notUseReferenceCount : 1; - WORD reservedLoadFlags : 12; + WORD MappedDll : 1; + WORD InsertInvertedFunctionTableEntry : 1; + WORD UseReferenceCount : 1; + WORD reservedLoadFlags : 13; + }; - DWORD dwModuleFlags; + DWORD dwFlags; }; }; - LPBYTE codeBase; //codeBase == ImageBase + OptionalHeader.BaseOfCode; + LPBYTE codeBase; //codeBase == ImageBase __declspec(align(sizeof(size_t))) struct { PVOID lpReserved; }; @@ -146,6 +146,8 @@ extern "C" { bool WINAPI IsValidMemoryModuleHandle(HMEMORYMODULE hModule); + PMEMORYMODULE WINAPI MapMemoryModuleHandle(HMEMORYMODULE hModule); + #ifdef __cplusplus } #endif diff --git a/MemoryModule/MemoryModule.vcxproj b/MemoryModule/MemoryModule.vcxproj index bee9cb2..50fcccf 100644 --- a/MemoryModule/MemoryModule.vcxproj +++ b/MemoryModule/MemoryModule.vcxproj @@ -24,6 +24,7 @@ + diff --git a/MemoryModule/MemoryModule.vcxproj.filters b/MemoryModule/MemoryModule.vcxproj.filters index 48e8d7c..e397dd5 100644 --- a/MemoryModule/MemoryModule.vcxproj.filters +++ b/MemoryModule/MemoryModule.vcxproj.filters @@ -38,5 +38,8 @@ Header Files + + Header Files + \ No newline at end of file diff --git a/MemoryModule/NativeFunctionsInternal.cpp b/MemoryModule/NativeFunctionsInternal.cpp index e077d4d..557890e 100644 --- a/MemoryModule/NativeFunctionsInternal.cpp +++ b/MemoryModule/NativeFunctionsInternal.cpp @@ -556,6 +556,14 @@ NTSTATUS NTAPI NtLoadDllMemoryExW( IN LPCWSTR DllFullName OPTIONAL) { if (IsBadReadPtr(BufferAddress, BufferSize) || IsBadWritePtr(BaseAddress, sizeof(HMEMORYMODULE)))return STATUS_ACCESS_VIOLATION; *BaseAddress = nullptr; + PMEMORYMODULE module = nullptr; + NTSTATUS status = STATUS_SUCCESS; + + if (dwFlags & LOAD_FLAGS_NOT_MAP_DLL) { + dwFlags &= LOAD_FLAGS_NOT_MAP_DLL; + DllName = DllFullName = nullptr; + } + if (dwFlags & LOAD_FLAGS_USE_DLL_NAME && (!DllName || !DllFullName))return STATUS_INVALID_PARAMETER_3; if (DllName) { PLIST_ENTRY ListHead, ListEntry; @@ -573,9 +581,11 @@ NTSTATUS NTAPI NtLoadDllMemoryExW( !wcsnicmp(DllName, CurEntry->BaseDllName.Buffer, CurEntry->BaseDllName.Length / sizeof(wchar_t))) { /* Let's compare their headers */ if (!(h2 = RtlImageNtHeader(CurEntry->DllBase)))continue; + if (!(module = MapMemoryModuleHandle(CurEntry->DllBase)))continue; if ((h1->OptionalHeader.SizeOfCode == h2->OptionalHeader.SizeOfCode) && (h1->OptionalHeader.SizeOfHeaders == h2->OptionalHeader.SizeOfHeaders)) { /* This is our entry!, update load count and return success */ + if (!module->UseReferenceCount || dwFlags & LOAD_FLAGS_NOT_USE_REFERENCE_COUNT)return STATUS_INVALID_PARAMETER_3; NtUpdateReferenceCount(CurEntry, FLAG_REFERENCE); *BaseAddress = CurEntry->DllBase; return STATUS_SUCCESS; @@ -596,23 +606,68 @@ NTSTATUS NTAPI NtLoadDllMemoryExW( return STATUS_UNSUCCESSFUL; } } + if (!(module = MapMemoryModuleHandle(*BaseAddress))) { + __fastfail(STATUS_INVALID_ADDRESS); + return STATUS_INVALID_ADDRESS; + } + module->loadFromNtLoadDllMemory = true; + if (dwFlags & LOAD_FLAGS_NOT_MAP_DLL) return STATUS_SUCCESS; + status = NtMapDllMemory(*BaseAddress, DllName, DllFullName, LdrEntry); + if (!NT_SUCCESS(status)) { + NtUnloadDllMemory(*BaseAddress); + *BaseAddress = nullptr; + return status; + } + module->MappedDll = true; + if (!(dwFlags & LOAD_FLAGS_NOT_USE_REFERENCE_COUNT))module->UseReferenceCount = true; + if (dwFlags & LOAD_FLAGS_NOT_ADD_INVERTED_FUNCTION)return STATUS_SUCCESS; + status = RtlInsertInvertedFunctionTable((PVOID)module->codeBase, RtlImageNtHeader(*BaseAddress)->OptionalHeader.SizeOfImage); + if (!NT_SUCCESS(status)) { + NtUnloadDllMemory(*BaseAddress); + *BaseAddress = nullptr; + return status; + } + module->InsertInvertedFunctionTableEntry = true; + return STATUS_SUCCESS; +} - NTSTATUS status = NtMapDllMemory(*BaseAddress, DllName, DllFullName, LdrEntry); - if (!NT_SUCCESS(status)) MemoryFreeLibrary(*BaseAddress); - status = RtlInsertInvertedFunctionTable((PVOID)RtlImageNtHeader(*BaseAddress)->OptionalHeader.ImageBase, RtlImageNtHeader(*BaseAddress)->OptionalHeader.SizeOfImage); - if (!NT_SUCCESS(status)) MemoryFreeLibrary(*BaseAddress); +NTSTATUS NtLoadDllMemoryExA( + OUT HMEMORYMODULE* BaseAddress, + OUT PLDR_DATA_TABLE_ENTRY* LdrEntry OPTIONAL, + IN DWORD dwFlags, + IN LPVOID BufferAddress, + IN size_t BufferSize, + IN LPCSTR DllName OPTIONAL, + IN LPCSTR DllFullName OPTIONAL){ + LPWSTR _DllName = nullptr, _DllFullName = nullptr; + size_t size; + NTSTATUS status; + if (DllName) { + size = strlen(DllName) + 1; + _DllName = new wchar_t[size]; + mbstowcs(_DllName, DllName, size); + } + if (DllFullName) { + size = strlen(DllFullName) + 1; + _DllFullName = new wchar_t[size]; + mbstowcs(_DllFullName, DllFullName, size); + } + status = NtLoadDllMemoryExW(BaseAddress, LdrEntry, dwFlags, BufferAddress, BufferSize, _DllName, _DllFullName); + if (_DllName)delete[]_DllName; + if (_DllFullName)delete[]_DllFullName; return status; } NTSTATUS NTAPI NtUnloadDllMemory(IN HMEMORYMODULE BaseAddress) { if (IsBadReadPtr(BaseAddress, sizeof(size_t)))return STATUS_ACCESS_VIOLATION; - if (!IsValidMemoryModuleHandle(BaseAddress))return STATUS_INVALID_HANDLE; - + PLIST_ENTRY ListHead, ListEntry; PLDR_DATA_TABLE_ENTRY CurEntry; ULONG count = 0; NTSTATUS status = STATUS_SUCCESS; + PMEMORYMODULE module = MapMemoryModuleHandle(BaseAddress); + if (!module || !module->loadFromNtLoadDllMemory)return STATUS_INVALID_HANDLE; ListHead = &NtCurrentPeb()->Ldr->InLoadOrderModuleList; ListEntry = ListHead->Flink; while (ListEntry != ListHead) { @@ -623,13 +678,20 @@ NTSTATUS NTAPI NtUnloadDllMemory(IN HMEMORYMODULE BaseAddress) { /* Check if name matches */ if (CurEntry->DllBase == BaseAddress) { if (RtlImageNtHeader(BaseAddress)->OptionalHeader.SizeOfImage == CurEntry->SizeOfImage) { - status = NtGetReferenceCount(CurEntry, &count); - if (!NT_SUCCESS(status))return status; + if (module->UseReferenceCount) { + status = NtGetReferenceCount(CurEntry, &count); + if (!NT_SUCCESS(status))return status; + } if (!count) { - status = RtlRemoveInvertedFunctionTable(BaseAddress); - if (!NT_SUCCESS(status))__fastfail(status); + module->underUnload = true; + if (module->MappedDll) { + if (module->InsertInvertedFunctionTableEntry) { + status = RtlRemoveInvertedFunctionTable(BaseAddress); + if (!NT_SUCCESS(status))__fastfail(status); + } + if (!NtFreeLdrDataTableEntry(CurEntry))__fastfail(STATUS_NOT_SUPPORTED); + } if (!MemoryFreeLibrary(BaseAddress))__fastfail(STATUS_UNSUCCESSFUL); - if (!NtFreeLdrDataTableEntry(CurEntry))__fastfail(STATUS_NOT_SUPPORTED); return STATUS_SUCCESS; } else { diff --git a/MemoryModule/NativeFunctionsInternal.h b/MemoryModule/NativeFunctionsInternal.h index 32a60f7..672a3ce 100644 --- a/MemoryModule/NativeFunctionsInternal.h +++ b/MemoryModule/NativeFunctionsInternal.h @@ -389,10 +389,28 @@ NTSTATUS NTAPI NtLoadDllMemory( IN size_t BufferSize ); -#define LOAD_FLAGS_NOT_ADD_LDR_ENTRY -#define LOAD_FLAGS_NOT_ADD_INVERTED_FUNCTION -#define LOAD_FLAGS_NOT_MAP_DLL +/* + NtLoadDllMemoryEx dwFlags +*/ +//If this flag is specified, all subsequent flags will be ignored. +//Also, will be incompatible with Win32 API. +#define LOAD_FLAGS_NOT_MAP_DLL 0x10000000 + +//If this flag is specified, exception handling will not be supported. +#define LOAD_FLAGS_NOT_ADD_INVERTED_FUNCTION 0x00000001 + +//If this flag is specified, NtLoadDllMemory and NtUnloadDllMemory will not use reference counting. +//If you try to load the same module, it will fail. When you unload the module, +// it will be unloaded without checking the reference count. +#define LOAD_FLAGS_NOT_USE_REFERENCE_COUNT 0x00000002 + +//If this flag is specified, DllName and DllFullName cannot be nullptr, +// they can be arbitrary strings without having to be correct file names and paths. +//Otherwise, DllName and DllFullName will use random names if they are nullptr. +//For compatibility with GetModuleHandle, DllName and DllFullName should be guaranteed to always end in .dll +#define LOAD_FLAGS_USE_DLL_NAME 0x00000004 + NTSTATUS NTAPI NtLoadDllMemoryExW( OUT HMEMORYMODULE* BaseAddress, diff --git a/README.md b/README.md index 7e37128..4e56457 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ MemoryModulePP, used to load a DLL from memory. MemoryModulePP is compatible with Win32 API and supports exception handling. -**MemoryModulePP is developed based on [MemoryModule].** +**MemoryModulePP is developed based on [MemoryModule][ref1].** **This repository is under development.** @@ -16,23 +16,26 @@ MemoryModulePP, used to load a DLL from memory. MemoryModulePP is compatible wit - Use reference counting, repeated loading of the same module will update the reference counting, please refer to NtLoadDllMemoryExW - The above features can be turned off through the dwFlags parameter of NtLoadDllMemoryExW -### Tech +## Tech MemoryModulePP uses many open source projects and references to work properly: -* [Vergilius Project][ref1] - Some windows kernel structure reference. -* [MemoryModule] - Load dll from memory, reference and improve part of this repository's code. +* [Vergilius Project][ref0] - Some windows kernel structure reference. +* [MemoryModule][ref1] - Load dll from memory, reference and improve part of this repository's code. * [Blackbone][ref2] - Windows memory hacking library, Referenced the idea of exception handling. * [Exceptions on Windows x64][ref3] - How Windows x64 Exception Handling Works. (Russian) +* [Reactos][ref4] - How WIndows loads dll. -### Todos +## Todos - Compatible with Win8 and x86 architecture - Improve MEMORYPODULE structure + - Improve NtLoadDllMemoryExW function - [MemoryModule]: - [ref1]: + [ref0]: + [ref1]: [ref2]: [ref3]: + [ref4]: diff --git a/test/d.dll b/test/d.dll deleted file mode 100644 index aff8e77..0000000 Binary files a/test/d.dll and /dev/null differ diff --git a/test/test.cpp b/test/test.cpp index 39de8d3..7b20cf7 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -31,7 +31,7 @@ int main() { if (hModule)test = GetProcAddress(hModule, "test"); printf("m1:\n\tHMEMORYMODULE\t= 0x%p\n\tHMODULE\t\t= 0x%p\n\tModuleFileName\t= %s\n\ttest\t\t= 0x%p\n\n", m1, hModule, name, test); if (test)test(); - + GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, (LPCSTR)test, &hModule); GetModuleFileNameA(hModule, name, MAX_PATH); test = GetProcAddress(hModule, "test");