diff --git a/MemoryModule/LoadDllMemoryApi.h b/MemoryModule/LoadDllMemoryApi.h index f31a078..5cb787e 100644 --- a/MemoryModule/LoadDllMemoryApi.h +++ b/MemoryModule/LoadDllMemoryApi.h @@ -1,64 +1,98 @@ #pragma once #include +typedef PVOID HMEMORYMODULE, HMEMORYRSRC; -typedef PVOID HMEMORYMODULE, PLDR_DATA_TABLE_ENTRY, HMEMORYRSRC; - +//Deprecated API +#ifndef _DEPRECATED /** * 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); +NOT_BUILD_WINDOWS_DEPRECATE +__drv_preferredFunction("NtLoadDllMemory*", "Deprecated. Use NtLoadDllMemory or NtLoadDllMemoryEx.") +HMEMORYMODULE MemoryLoadLibrary(const void*); /** * Get address of exported method. Supports loading both by name and by * ordinal value. */ +NOT_BUILD_WINDOWS_DEPRECATE +__drv_preferredFunction("GetProcAddress", "Deprecated. Use Win32API GetProcAddress.") FARPROC MemoryGetProcAddress(HMEMORYMODULE, LPCSTR); /** * Free previously loaded DLL. */ +NOT_BUILD_WINDOWS_DEPRECATE +__drv_preferredFunction("NtUnloadDllMemory", "Deprecated. Use NtUnloadDllMemory.") bool MemoryFreeLibrary(HMEMORYMODULE); /** * Find the location of a resource with the specified type and name. */ +NOT_BUILD_WINDOWS_DEPRECATE +__drv_preferredFunction("FindResource", "Deprecated. Use Win32API FindResource.") HMEMORYRSRC MemoryFindResource(HMEMORYMODULE, LPCTSTR, LPCTSTR); /** * Find the location of a resource with the specified type, name and language. */ +NOT_BUILD_WINDOWS_DEPRECATE +__drv_preferredFunction("FindResourceEx", "Deprecated. Use Win32API FindResourceEx.") HMEMORYRSRC MemoryFindResourceEx(HMEMORYMODULE, LPCTSTR, LPCTSTR, WORD); /** * Get the size of the resource in bytes. */ +NOT_BUILD_WINDOWS_DEPRECATE +__drv_preferredFunction("SizeofResource", "Deprecated. Use Win32API SizeofResource.") DWORD MemorySizeofResource(HMEMORYMODULE, HMEMORYRSRC); /** * Get a pointer to the contents of the resource. */ +NOT_BUILD_WINDOWS_DEPRECATE +__drv_preferredFunction("LoadResource", "Deprecated. Use Win32API LoadResource.") LPVOID MemoryLoadResource(HMEMORYMODULE, HMEMORYRSRC); /** * Load a string resource. */ +NOT_BUILD_WINDOWS_DEPRECATE +__drv_preferredFunction("LoadString*", "Deprecated. Use Win32API LoadStringA or LoadStringW.") int MemoryLoadString(HMEMORYMODULE, UINT, LPTSTR, int); /** * Load a string resource with a given language. */ +NOT_BUILD_WINDOWS_DEPRECATE +__drv_preferredFunction("LoadString*", "Deprecated. Use Win32API LoadStringA or LoadStringW.") int MemoryLoadStringEx(HMEMORYMODULE, UINT, LPTSTR, int, WORD); +#endif +#define MEMORY_FEATURE_SUPPORT_VERSION 0x00000001 +#define MEMORY_FEATURE_MODULE_BASEADDRESS_INDEX 0x00000002 /* Windows8 and greater */ +#define MEMORY_FEATURE_LDRP_HEAP 0x00000004 +#define MEMORY_FEATURE_LDRP_HASH_TABLE 0x00000008 +#define MEMORY_FEATURE_INVERTED_FUNCTION_TABLE 0x00000010 +#define MEMORY_FEATURE_LDRP_HANDLE_TLS_DATA 0x00000020 +#define MEMORY_FEATURE_ALL 0x0000003f + +//Get the implementation of the currently running operating system. +NTSTATUS NTAPI NtQuerySystemMemoryModuleFeatures(OUT PDWORD pFeatures); + + +//Load dll from the provided buffer. NTSTATUS NTAPI NtLoadDllMemory( - OUT HMEMORYMODULE* BaseAddress, - IN LPVOID BufferAddress, - IN size_t BufferSize + OUT HMEMORYMODULE* BaseAddress, // Output module base address + IN LPVOID BufferAddress, // Pointer to the dll file data buffer + IN size_t Reserved // Reserved parameter, must be 0 ); + /* NtLoadDllMemoryEx dwFlags */ @@ -67,6 +101,9 @@ NTSTATUS NTAPI NtLoadDllMemory( //Also, will be incompatible with Win32 API. #define LOAD_FLAGS_NOT_MAP_DLL 0x10000000 +//If this flag is specified, this routine will not fail even if the call to LdrpTlsData fails. +#define LOAD_FLAGS_NOT_FAIL_IF_HANDLE_TLS 0x20000000 + //If this flag is specified, exception handling will not be supported. #define LOAD_FLAGS_NOT_ADD_INVERTED_FUNCTION 0x00000001 @@ -85,29 +122,51 @@ NTSTATUS NTAPI NtLoadDllMemory( #define LOAD_FLAGS_NOT_HANDLE_TLS 0x00000008 NTSTATUS NTAPI NtLoadDllMemoryExW( - OUT HMEMORYMODULE* BaseAddress, - OUT PVOID* LdrEntry OPTIONAL, - IN DWORD dwFlags, - IN LPVOID BufferAddress, - IN size_t BufferSize, - IN LPCWSTR DllName OPTIONAL, - IN LPCWSTR DllFullName OPTIONAL + OUT HMEMORYMODULE* BaseAddress, // Output module base address + OUT PVOID* LdrEntry OPTIONAL, // Receive a pointer to the LDR node of the module + IN DWORD dwFlags, // Flags + IN LPVOID BufferAddress, // Pointer to the dll file data buffer + IN size_t Reserved, // Reserved parameter, must be 0 + IN LPCWSTR DllName OPTIONAL, // Module file name + IN LPCWSTR DllFullName OPTIONAL // Module file full path ); + NTSTATUS NTAPI NtLoadDllMemoryExA( OUT HMEMORYMODULE* BaseAddress, OUT PVOID* LdrEntry OPTIONAL, IN DWORD dwFlags, IN LPVOID BufferAddress, - IN size_t BufferSize, + IN size_t Reserved, IN LPCSTR DllName OPTIONAL, IN LPCSTR DllFullName OPTIONAL ); +//Unload modules previously loaded from memory +NTSTATUS NTAPI NtUnloadDllMemory(IN HMEMORYMODULE BaseAddress); + +#ifdef _WIN64 +#pragma comment(linker,"/export:NtUnloadDllMemoryAndExitThread") +#pragma comment(linker,"/export:FreeLibraryMemoryAndExitThread=NtUnloadDllMemoryAndExitThread") +#else +#pragma comment(linker,"/export:NtUnloadDllMemoryAndExitThread=_NtUnloadDllMemoryAndExitThread@8") +#pragma comment(linker,"/export:FreeLibraryMemoryAndExitThread=_NtUnloadDllMemoryAndExitThread@8") +#endif +//FreeLibraryMemoryAndExitThread = GetProcAddress(GetModuleHandleW(nullptr), "FreeLibraryMemoryAndExitThread"); +//FreeLibraryMemoryAndExitThread(hModule, 0); +extern "C" { + __declspec(noreturn) VOID NTAPI NtUnloadDllMemoryAndExitThread(IN HMEMORYMODULE BaseAddress, IN DWORD dwExitCode); +} + +#define LoadLibraryMemory NtLoadDllMemory +#define FreeLibraryMemory NtUnloadDllMemory +#define FreeLibraryMemoryAndExitThread NtUnloadDllMemoryAndExitThread #ifdef UNICODE #define NtLoadDllMemoryEx NtLoadDllMemoryExW +#define LoadLibraryMemoryEx NtLoadDllMemoryExW #else #define NtLoadDllMemoryEx NtLoadDllMemoryExA +#define LoadLibraryMemoryEx NtLoadDllMemoryExA #endif -NTSTATUS NTAPI NtUnloadDllMemory(IN HMEMORYMODULE BaseAddress); + diff --git a/MemoryModule/MemoryModule.cpp b/MemoryModule/MemoryModule.cpp index 6d1be7e..80f0c63 100644 --- a/MemoryModule/MemoryModule.cpp +++ b/MemoryModule/MemoryModule.cpp @@ -33,13 +33,19 @@ static PIMAGE_NT_HEADERS WINAPI GetImageNtHeaders(PMEMORYMODULE pModule) { } PMEMORYMODULE WINAPI MapMemoryModuleHandle(HMEMORYMODULE hModule) { - PIMAGE_DOS_HEADER dos = (PIMAGE_DOS_HEADER)hModule; - if (!dos)return nullptr; - PIMAGE_NT_HEADERS nt = (PIMAGE_NT_HEADERS)((LPBYTE)hModule + dos->e_lfanew); - if (!nt)return nullptr; - PMEMORYMODULE pModule = (PMEMORYMODULE)((LPBYTE)hModule + nt->OptionalHeader.SizeOfHeaders); - if (pModule->Signature != MEMORY_MODULE_SIGNATURE || (size_t)pModule->codeBase != nt->OptionalHeader.ImageBase)return nullptr; - return pModule; + __try { + PIMAGE_DOS_HEADER dos = (PIMAGE_DOS_HEADER)hModule; + if (!dos)return nullptr; + PIMAGE_NT_HEADERS nt = (PIMAGE_NT_HEADERS)((LPBYTE)hModule + dos->e_lfanew); + if (!nt)return nullptr; + PMEMORYMODULE pModule = (PMEMORYMODULE)((LPBYTE)hModule + nt->OptionalHeader.SizeOfHeaders); + if (!_ProbeForRead(pModule, sizeof(MEMORYMODULE)))return nullptr; + if (pModule->Signature != MEMORY_MODULE_SIGNATURE || (size_t)pModule->codeBase != nt->OptionalHeader.ImageBase)return nullptr; + return pModule; + } + __except (EXCEPTION_EXECUTE_HANDLER) { + return nullptr; + } } bool WINAPI IsValidMemoryModuleHandle(HMEMORYMODULE hModule) { @@ -126,15 +132,6 @@ static VOID FinalSectionsProtect(PMEMORYMODULE module) { return; } -static BOOL CheckSize(size_t size, size_t expected) { - if (size < expected) { - SetLastError(ERROR_INVALID_DATA); - return FALSE; - } - - return TRUE; -} - static BOOL CopySections(const unsigned char* data, PMEMORYMODULE module) { LPBYTE codeBase = module->codeBase; LPVOID dest; @@ -244,7 +241,7 @@ static BOOL ExecuteTLS(PMEMORYMODULE module) { unsigned char* codeBase = module->codeBase; PIMAGE_TLS_DIRECTORY tls; PIMAGE_TLS_CALLBACK* callback; - PIMAGE_NT_HEADERS headers = GetImageNtHeaders(module); + PIMAGE_NT_HEADERS headers = RtlImageNtHeader(codeBase); PIMAGE_DATA_DIRECTORY directory = GET_HEADER_DICTIONARY(headers, IMAGE_DIRECTORY_ENTRY_TLS); if (directory->VirtualAddress == 0) return TRUE; @@ -360,15 +357,6 @@ static BOOL BuildImportTable(PMEMORYMODULE module) { return result; } -//static BOOL PerformForwardExport(PMEMORYMODULE module) { -// PIMAGE_EXPORT_DIRECTORY exports; -// PIMAGE_NT_HEADERS headers = GetImageNtHeaders(module); -// PIMAGE_DATA_DIRECTORY directory = GET_HEADER_DICTIONARY(headers, IMAGE_DIRECTORY_ENTRY_EXPORT); -// if (!directory->Size)return TRUE; -// return FALSE; -// -//} - HMEMORYMODULE MemoryLoadLibrary(const void* data) { PMEMORYMODULE hMemoryModule = nullptr; PIMAGE_DOS_HEADER dos_header, new_dos_header; @@ -534,18 +522,48 @@ error: return nullptr; } +bool MemoryFreeLibrary(HMEMORYMODULE mod) { + PMEMORYMODULE module = MapMemoryModuleHandle(mod); + PIMAGE_NT_HEADERS headers = RtlImageNtHeader(mod); + + if (!module) 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); + } + if (module->nameExportsTable)delete[] module->nameExportsTable; + if (module->hModulesList != nullptr) { + int i; + for (i = 0; i < module->dwModulesCount; i++) { + if (module->hModulesList[i]) { + FreeLibrary(module->hModulesList[i]); + } + } + free(module->hModulesList); + } +#ifdef _WIN64 + FreePointerList(module->blockedMemory); +#endif + if (module->codeBase != nullptr) VirtualFree(mod, 0, MEM_RELEASE); + return true; +} + + + +/* + Deprecated API +*/ static int _compare(const void* a, const void* b) { const struct ExportNameEntry* p1 = (const struct ExportNameEntry*) a; const struct ExportNameEntry* p2 = (const struct ExportNameEntry*) b; return strcmp(p1->name, p2->name); } - static int _find(const void* a, const void* b) { LPCSTR* name = (LPCSTR*)a; const struct ExportNameEntry* p = (const struct ExportNameEntry*) b; return strcmp(*name, p->name); } - FARPROC MemoryGetProcAddress(HMEMORYMODULE mod, LPCSTR name) { PMEMORYMODULE module = MapMemoryModuleHandle(mod); unsigned char* codeBase = module->codeBase; @@ -625,40 +643,10 @@ FARPROC MemoryGetProcAddress(HMEMORYMODULE mod, LPCSTR name) { // AddressOfFunctions contains the RVAs to the "real" functions return (FARPROC)(LPVOID)(codeBase + (*(DWORD*)(codeBase + exports->AddressOfFunctions + (static_cast(idx) * 4)))); } - -bool MemoryFreeLibrary(HMEMORYMODULE mod) { - PMEMORYMODULE module = MapMemoryModuleHandle(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); - } - if (module->nameExportsTable)delete[] module->nameExportsTable; - if (module->hModulesList != nullptr) { - int i; - for (i = 0; i < module->dwModulesCount; i++) { - if (module->hModulesList[i]) { - FreeLibrary(module->hModulesList[i]); - } - } - free(module->hModulesList); - } -#ifdef _WIN64 - FreePointerList(module->blockedMemory); -#endif - if (module->codeBase != nullptr) VirtualFree(mod, 0, MEM_RELEASE); - return true; -} - #define DEFAULT_LANGUAGE MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL) - HMEMORYRSRC MemoryFindResource(HMEMORYMODULE module, LPCTSTR name, LPCTSTR type) { return MemoryFindResourceEx(module, name, type, DEFAULT_LANGUAGE); } - static PIMAGE_RESOURCE_DIRECTORY_ENTRY _MemorySearchResourceEntry(void* root, PIMAGE_RESOURCE_DIRECTORY resources, LPCTSTR key) { PIMAGE_RESOURCE_DIRECTORY_ENTRY entries = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(resources + 1); PIMAGE_RESOURCE_DIRECTORY_ENTRY result = nullptr; @@ -765,7 +753,6 @@ static PIMAGE_RESOURCE_DIRECTORY_ENTRY _MemorySearchResourceEntry(void* root, PI return result; } - HMEMORYRSRC MemoryFindResourceEx(HMEMORYMODULE module, LPCTSTR name, LPCTSTR type, WORD language) { PMEMORYMODULE mod = MapMemoryModuleHandle(module); unsigned char* codeBase = mod->codeBase; @@ -819,7 +806,6 @@ HMEMORYRSRC MemoryFindResourceEx(HMEMORYMODULE module, LPCTSTR name, LPCTSTR typ return (codeBase + directory->VirtualAddress + (foundLanguage->OffsetToData & 0x7fffffff)); } - DWORD MemorySizeofResource(HMEMORYMODULE module, HMEMORYRSRC resource) { PIMAGE_RESOURCE_DATA_ENTRY entry; UNREFERENCED_PARAMETER(module); @@ -830,7 +816,6 @@ DWORD MemorySizeofResource(HMEMORYMODULE module, HMEMORYRSRC resource) { return entry->Size; } - LPVOID MemoryLoadResource(HMEMORYMODULE module, HMEMORYRSRC resource) { unsigned char* codeBase = MapMemoryModuleHandle(module)->codeBase; PIMAGE_RESOURCE_DATA_ENTRY entry = (PIMAGE_RESOURCE_DATA_ENTRY)resource; @@ -840,11 +825,9 @@ LPVOID MemoryLoadResource(HMEMORYMODULE module, HMEMORYRSRC resource) { return codeBase + entry->OffsetToData; } - int MemoryLoadString(HMEMORYMODULE module, UINT id, LPTSTR buffer, int maxsize) { return MemoryLoadStringEx(module, id, buffer, maxsize, DEFAULT_LANGUAGE); } - int MemoryLoadStringEx(HMEMORYMODULE module, UINT id, LPTSTR buffer, int maxsize, WORD language) { HMEMORYRSRC resource; PIMAGE_RESOURCE_DIR_STRING_U data; diff --git a/MemoryModule/Native.cpp b/MemoryModule/Native.cpp index 51feaec..ac851f6 100644 --- a/MemoryModule/Native.cpp +++ b/MemoryModule/Native.cpp @@ -75,10 +75,6 @@ PVOID RtlCreateHeap(ULONG Flags, PVOID HeapBase, SIZE_T ReserveSize, SIZE_T Comm Flags, HeapBase, ReserveSize, CommitSize, Lock, Parameters); } -PVOID BsRtlCreateHeap(ULONG Flags, SIZE_T ReserveSize, SIZE_T CommitSize) { - return RtlCreateHeap(Flags | HEAP_GROWABLE, NULL, ReserveSize, CommitSize, NULL, NULL); -} - PVOID RtlDestroyHeap(PVOID HeapHandle) { return ((PVOID(__stdcall*)(PVOID))RtlGetNtProcAddress("RtlDestroyHeap"))(HeapHandle); } @@ -365,12 +361,20 @@ PVOID NTAPI RtlDecodeSystemPointer(PVOID Pointer) { return decltype(&RtlDecodeSystemPointer)(RtlGetNtProcAddress("RtlDecodeSystemPointer"))(Pointer); } +BOOLEAN NTAPI VirtualAccessCheckNoException(LPCVOID pBuffer, size_t size, ACCESS_MASK protect) { + if (size) { + MEMORY_BASIC_INFORMATION mbi{}; + SIZE_T len = 0; + if (!NT_SUCCESS(NtQueryVirtualMemory(NtCurrentProcess(), const_cast(pBuffer), MemoryBasicInformation, &mbi, sizeof(mbi), &len)) || + !(mbi.Protect & protect)) { + return FALSE; + } + } + return TRUE; +} BOOLEAN NTAPI VirtualAccessCheck(LPCVOID pBuffer, size_t size, ACCESS_MASK protect) { - MEMORY_BASIC_INFORMATION mbi{}; - SIZE_T len = 0; - if (!NT_SUCCESS(NtQueryVirtualMemory(NtCurrentProcess(), const_cast(pBuffer), MemoryBasicInformation, &mbi, sizeof(mbi), &len)) || - !(mbi.Protect & protect)) { - RaiseException(EXCEPTION_ACCESS_VIOLATION, 0, 0, nullptr); + if (!VirtualAccessCheckNoException(pBuffer, size, protect)) { + RtlRaiseStatus(STATUS_ACCESS_VIOLATION); return FALSE; } return TRUE; @@ -385,3 +389,8 @@ NTSTATUS NTAPI LdrUnlockLoaderLock(size_t Flags, size_t Cookie) { NTSTATUS NTAPI LdrUnloadDll(IN HANDLE ModuleHandle) { return (decltype(&LdrUnloadDll)(RtlGetNtProcAddress("LdrUnloadDll")))(ModuleHandle); } + +DECLSPEC_NORETURN VOID NTAPI RtlExitUserThread(IN NTSTATUS ExitStatus) { + (decltype(&RtlExitUserThread)(RtlGetNtProcAddress("RtlExitUserThread")))(ExitStatus); +} + diff --git a/MemoryModule/Native.h b/MemoryModule/Native.h index b758285..c80f2e5 100644 --- a/MemoryModule/Native.h +++ b/MemoryModule/Native.h @@ -1225,11 +1225,6 @@ PVOID RtlCreateHeap( PVOID Lock, PRTL_HEAP_PARAMETERS Parameters ); -PVOID BsRtlCreateHeap( - ULONG Flags, - SIZE_T ReserveSize, - SIZE_T CommitSize -); PVOID RtlDestroyHeap( PVOID HeapHandle ); @@ -1406,10 +1401,15 @@ PVOID NTAPI RtlDecodeSystemPointer(PVOID Pointer); #define NtCurrentThread() (HANDLE)-2 BOOLEAN NTAPI VirtualAccessCheck(LPCVOID pBuffer, size_t size, ACCESS_MASK protect); +BOOLEAN NTAPI VirtualAccessCheckNoException(LPCVOID pBuffer, size_t size, ACCESS_MASK protect); #define ProbeForRead(pBuffer, size) VirtualAccessCheck(pBuffer, size, PAGE_READONLY | PAGE_READWRITE | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE) #define ProbeForWrite(pBuffer, size) VirtualAccessCheck(pBuffer, size, PAGE_READWRITE | PAGE_EXECUTE_WRITECOPY | PAGE_WRITECOPY | PAGE_EXECUTE_READWRITE) #define ProbeForReadWrite(pBuffer, size) VirtualAccessCheck(pBuffer, size, PAGE_EXECUTE_READWRITE | PAGE_READWRITE) #define ProbeForExecute(pBuffer, size) VirtualAccessCheck(pBuffer, size, PAGE_EXECUTE | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY) +#define _ProbeForRead(pBuffer, size) VirtualAccessCheckNoException(pBuffer, size, PAGE_READONLY | PAGE_READWRITE | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE) +#define _ProbeForWrite(pBuffer, size) VirtualAccessCheckNoException(pBuffer, size, PAGE_READWRITE | PAGE_EXECUTE_WRITECOPY | PAGE_WRITECOPY | PAGE_EXECUTE_READWRITE) +#define _ProbeForReadWrite(pBuffer, size) VirtualAccessCheckNoException(pBuffer, size, PAGE_EXECUTE_READWRITE | PAGE_READWRITE) +#define _ProbeForExecute(pBuffer, size) VirtualAccessCheckNoException(pBuffer, size, PAGE_EXECUTE | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY) //Flags #define LOCK_RAISE_EXCEPTION 1 @@ -1421,3 +1421,7 @@ BOOLEAN NTAPI VirtualAccessCheck(LPCVOID pBuffer, size_t size, ACCESS_MASK prote NTSTATUS NTAPI LdrLockLoaderLock(size_t Flags, size_t* State, size_t* Cookie); NTSTATUS NTAPI LdrUnlockLoaderLock(size_t Flags, size_t Cookie); NTSTATUS NTAPI LdrUnloadDll(IN HANDLE ModuleHandle); + +#define RtlRaiseStatus(_Status_) ((VOID(NTAPI*)(NTSTATUS Status))(RtlGetNtProcAddress("RtlRaiseStatus")))(_Status_) + +DECLSPEC_NORETURN VOID NTAPI RtlExitUserThread(IN NTSTATUS ExitStatus); diff --git a/MemoryModule/NativeFunctionsInternal.cpp b/MemoryModule/NativeFunctionsInternal.cpp index 3e5acc6..02ff20b 100644 --- a/MemoryModule/NativeFunctionsInternal.cpp +++ b/MemoryModule/NativeFunctionsInternal.cpp @@ -26,14 +26,48 @@ static bool NTAPI RtlVerifyVersion(IN DWORD MajorVersion, IN DWORD MinorVersion return false; } static bool NTAPI RtlIsWindowsVersionOrGreater(IN DWORD MajorVersion, IN DWORD MinorVersion, IN DWORD BuildNumber) { - DWORD Versions[3]; - RtlGetNtVersionNumbers(Versions, Versions + 1, Versions + 2); + static DWORD Versions[3]{}; + if (!Versions[0])RtlGetNtVersionNumbers(Versions, Versions + 1, Versions + 2); + if (Versions[0] == MajorVersion) { if (Versions[1] == MinorVersion) return Versions[2] >= BuildNumber; else return (Versions[1] > MinorVersion); } else return Versions[0] > MajorVersion; } +static bool NTAPI RtlIsWindowsVersionInScope( + IN DWORD MinMajorVersion, IN DWORD MinMinorVersion, IN DWORD MinBuildNumber, + IN DWORD MaxMajorVersion, IN DWORD MaxMinorVersion, IN DWORD MaxBuildNumber) { + return RtlIsWindowsVersionOrGreater(MinMajorVersion, MinMinorVersion, MinBuildNumber) && + !RtlIsWindowsVersionOrGreater(MaxMajorVersion, MaxMinorVersion, MaxBuildNumber); +} + +static PLDR_DATA_TABLE_ENTRY NTAPI RtlFindLdrTableEntryByHandle(PVOID BaseAddress) { + PLIST_ENTRY ListHead = &NtCurrentPeb()->Ldr->InLoadOrderModuleList, ListEntry = ListHead->Flink; + PLDR_DATA_TABLE_ENTRY CurEntry; + while (ListEntry != ListHead) { + CurEntry = CONTAINING_RECORD(ListEntry, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks); + ListEntry = ListEntry->Flink; + if (CurEntry->DllBase == BaseAddress) { + return CurEntry; + } + } + return nullptr; +} +static PLDR_DATA_TABLE_ENTRY NTAPI RtlFindLdrTableEntryByBaseName(PCWSTR BaseName) { + PLIST_ENTRY ListHead = &NtCurrentPeb()->Ldr->InLoadOrderModuleList, ListEntry = ListHead->Flink; + PLDR_DATA_TABLE_ENTRY CurEntry; + while (ListEntry != ListHead) { + CurEntry = CONTAINING_RECORD(ListEntry, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks); + ListEntry = ListEntry->Flink; + if (!wcsnicmp(BaseName, CurEntry->BaseDllName.Buffer, (CurEntry->BaseDllName.Length / sizeof(wchar_t)) - 4) || + !wcsnicmp(BaseName, CurEntry->BaseDllName.Buffer, CurEntry->BaseDllName.Length / sizeof(wchar_t))) { + return CurEntry; + } + } + return nullptr; +} +#define RtlFindNtdllLdrEntry() RtlFindLdrTableEntryByBaseName(L"ntdll.dll") static ULONG NTAPI LdrHashEntry(IN const UNICODE_STRING& str, IN bool _xor = true) { ULONG result = 0; @@ -62,22 +96,6 @@ static HANDLE NTAPI RtlFindtLdrpHeap() { NtQueryVirtualMemory(NtCurrentProcess(), CurEntry, MemoryBasicInformation, &mbi, sizeof(mbi), (PSIZE_T)&ListHead); return result = mbi.AllocationBase; } -static PLDR_DATA_TABLE_ENTRY NTAPI RtlFindNtdllLdrEntry() { - PLIST_ENTRY ListHead, ListEntry; - static PLDR_DATA_TABLE_ENTRY CurEntry = nullptr; - if (CurEntry)return CurEntry; - - ListHead = &NtCurrentPeb()->Ldr->InInitializationOrderModuleList; - ListEntry = ListHead->Flink; - while (ListHead != ListEntry) { - CurEntry = CONTAINING_RECORD(ListEntry, LDR_DATA_TABLE_ENTRY, InInitializationOrderLinks); - ListEntry = ListEntry->Flink; - if (0 == wcsnicmp(CurEntry->BaseDllName.Buffer, L"ntdll.dll", CurEntry->BaseDllName.Length)) - return CurEntry; - } - - return CurEntry = nullptr; -} static PLIST_ENTRY NTAPI RtlFindLdrpHashTable() { static PLIST_ENTRY list = nullptr; if (list) return list; @@ -271,28 +289,29 @@ static NTSTATUS NTAPI NtRemoveModuleBaseAddressIndexNode(IN PLDR_DATA_TABLE_ENTR return STATUS_SUCCESS; } -static NTSTATUS NTAPI NtFreeDependencies(IN PLDR_DATA_TABLE_ENTRY_WIN8 LdrEntry) { +static NTSTATUS NTAPI NtFreeDependencies(IN PLDR_DATA_TABLE_ENTRY_WIN10 LdrEntry) { _LDR_DDAG_NODE* DependentDdgeNode = nullptr; - PLDR_DATA_TABLE_ENTRY_WIN8 ModuleEntry = nullptr; + PLDR_DATA_TABLE_ENTRY_WIN10 ModuleEntry = nullptr; _LDRP_CSLIST* head = (decltype(head))LdrEntry->DdagNode->Dependencies, *entry = head; + const static bool IsWin8 = RtlIsWindowsVersionInScope(6, 2, 0, 6, 3, -1); if (!LdrEntry->DdagNode->Dependencies)return STATUS_SUCCESS; - + //find all dependencies and free do { DependentDdgeNode = entry->Dependent.DependentDdagNode; - if (DependentDdgeNode->Modules.Flink->Flink != &DependentDdgeNode->Modules) RaiseException(-1, EXCEPTION_NONCONTINUABLE, 0, nullptr); + if (DependentDdgeNode->Modules.Flink->Flink != &DependentDdgeNode->Modules) __fastfail(FAST_FAIL_CORRUPT_LIST_ENTRY); ModuleEntry = decltype(ModuleEntry)((size_t)DependentDdgeNode->Modules.Flink - offsetof(_LDR_DATA_TABLE_ENTRY_WIN8, NodeModuleLink)); - if (ModuleEntry->DdagNode != DependentDdgeNode) RaiseException(-1, EXCEPTION_NONCONTINUABLE, 0, nullptr); - if (!DependentDdgeNode->IncomingDependencies) RaiseException(-1, EXCEPTION_NONCONTINUABLE, 0, nullptr); + if (ModuleEntry->DdagNode != DependentDdgeNode) __fastfail(FAST_FAIL_CORRUPT_LIST_ENTRY); + if (!DependentDdgeNode->IncomingDependencies) __fastfail(FAST_FAIL_CORRUPT_LIST_ENTRY); _LDRP_CSLIST::_LDRP_CSLIST_INCOMMING* _last = DependentDdgeNode->IncomingDependencies, *_entry = _last; _LDR_DDAG_NODE* CurrentDdagNode; size_t State = 0, Cookies; //Acquire LoaderLock do { - if (!NT_SUCCESS(LdrLockLoaderLock(LOCK_NO_WAIT_IF_BUSY, &State, &Cookies))) - RaiseException(-1, EXCEPTION_NONCONTINUABLE, 0, nullptr); + if (!NT_SUCCESS(LdrLockLoaderLock(LOCK_NO_WAIT_IF_BUSY, &State, &Cookies))) __fastfail(FAST_FAIL_FATAL_APP_EXIT); } while (State != LOCK_STATE_ENTERED); + do { CurrentDdagNode = (decltype(CurrentDdagNode))((size_t)_entry->IncommingDdagNode & ~1); if (CurrentDdagNode == LdrEntry->DdagNode) { @@ -316,19 +335,32 @@ static NTSTATUS NTAPI NtFreeDependencies(IN PLDR_DATA_TABLE_ENTRY_WIN8 LdrEntry) } break; } + //save the last entry if (_last != _entry)_last = (decltype(_last))_last->NextIncommingEntry; _entry = (decltype(_entry))_entry->NextIncommingEntry; } while (_entry != _last); //free LoaderLock LdrUnlockLoaderLock(0, Cookies); + entry = (decltype(entry))entry->Dependent.NextDependentEntry; //free it - LdrUnloadDll(ModuleEntry->DllBase); + if (IsWin8) { + //Update win8 dep count + _LDR_DDAG_NODE_WIN8* win8_node = (decltype(win8_node))ModuleEntry->DdagNode; + if (!win8_node->DependencyCount)__fastfail(FAST_FAIL_CORRUPT_LIST_ENTRY); + --win8_node->DependencyCount; + if (!ModuleEntry->DdagNode->LoadCount && win8_node->ReferenceCount == 1 && !win8_node->DependencyCount) { + win8_node->LoadCount = 1; + LdrUnloadDll(ModuleEntry->DllBase); + } + } + else { + LdrUnloadDll(ModuleEntry->DllBase); + } NtFreeLdrpHeap(LdrEntry->DdagNode->Dependencies); //lookup next dependent. - entry = (decltype(entry))entry->Dependent.NextDependentEntry; LdrEntry->DdagNode->Dependencies = (_LDRP_CSLIST::_LDRP_CSLIST_DEPENDENT*)(entry == head ? nullptr : entry); } while (entry != head); @@ -355,12 +387,14 @@ static bool NTAPI NtInitializeLdrDataTableEntry( case win8: case win8_1: { auto entry = (PLDR_DATA_TABLE_ENTRY_WIN8)LdrEntry; + const static bool IsWin8 = RtlIsWindowsVersionInScope(6, 2, 0, 6, 3, -1); NtQuerySystemTime(&entry->LoadTime); entry->OriginalBase = headers->OptionalHeader.ImageBase; entry->BaseNameHashValue = LdrHashEntry(DllBaseName, false); entry->LoadReason = LoadReasonDynamicLoad; if (!NT_SUCCESS(NtInsertModuleBaseAddressIndexNode(LdrEntry, BaseAddress)))return false; - if (!(entry->DdagNode = (decltype(entry->DdagNode))NtAllocateLdrpHeap(sizeof(_LDR_DDAG_NODE))))return false; + if (!(entry->DdagNode = (decltype(entry->DdagNode)) + NtAllocateLdrpHeap(IsWin8 ? sizeof(_LDR_DDAG_NODE_WIN8) : sizeof(_LDR_DDAG_NODE))))return false; //NtInitializeListEntry(&entry->NodeModuleLink); //NtInitializeListEntry(&entry->DdagNode->Modules); //NtInitializeSingleEntry(&entry->DdagNode->CondenseLink); @@ -370,6 +404,7 @@ static bool NTAPI NtInitializeLdrDataTableEntry( entry->DdagNode->Modules.Blink = &entry->NodeModuleLink; entry->DdagNode->State = LdrModulesReadyToRun; entry->DdagNode->LoadCount = 1; + if (IsWin8) ((_LDR_DDAG_NODE_WIN8*)(entry->DdagNode))->ReferenceCount = 1; entry->ImageDll = entry->LoadNotificationsSent = entry->EntryProcessed = entry->InLegacyLists = entry->InIndexes = entry->ProcessAttachCalled = true; entry->InExceptionTable = !(dwFlags & LOAD_FLAGS_NOT_ADD_INVERTED_FUNCTION); @@ -414,7 +449,7 @@ static bool NTAPI NtFreeLdrDataTableEntry(IN PLDR_DATA_TABLE_ENTRY LdrEntry) { case win10_2: case win8: case win8_1: { - auto entry = (PLDR_DATA_TABLE_ENTRY_WIN8)LdrEntry; + auto entry = (PLDR_DATA_TABLE_ENTRY_WIN10)LdrEntry; NtFreeDependencies(entry); NtFreeLdrpHeap(entry->DdagNode); NtRemoveModuleBaseAddressIndexNode(LdrEntry); @@ -630,7 +665,8 @@ NTSTATUS NTAPI NtLoadDllMemoryExW( UNREFERENCED_PARAMETER(BufferSize); __try { - if (IsBadReadPtr(BufferAddress, BufferSize))status = STATUS_ACCESS_VIOLATION; + //ProbeForRead(BufferAddress, BufferSize); + if (BufferSize)status = STATUS_INVALID_PARAMETER_5; *BaseAddress = nullptr; if (LdrEntry)*LdrEntry = nullptr; } @@ -668,6 +704,7 @@ NTSTATUS NTAPI NtLoadDllMemoryExW( if (!module->UseReferenceCount || dwFlags & LOAD_FLAGS_NOT_USE_REFERENCE_COUNT)return STATUS_INVALID_PARAMETER_3; NtUpdateReferenceCount(CurEntry, FLAG_REFERENCE); *BaseAddress = CurEntry->DllBase; + if (LdrEntry)*LdrEntry = CurEntry; return STATUS_SUCCESS; } } @@ -687,8 +724,10 @@ NTSTATUS NTAPI NtLoadDllMemoryExW( } } if (!(module = MapMemoryModuleHandle(*BaseAddress))) { - __fastfail(STATUS_INVALID_ADDRESS); - return STATUS_INVALID_ADDRESS; + __fastfail(FAST_FAIL_FATAL_APP_EXIT); + DebugBreak(); + ExitProcess(STATUS_INVALID_ADDRESS); + TerminateProcess(NtCurrentProcess(), STATUS_INVALID_ADDRESS); } module->loadFromNtLoadDllMemory = true; headers = RtlImageNtHeader(*BaseAddress); @@ -765,9 +804,13 @@ NTSTATUS NTAPI NtLoadDllMemoryExA( } NTSTATUS NTAPI NtUnloadDllMemory(IN HMEMORYMODULE BaseAddress) { - if (IsBadReadPtr(BaseAddress, sizeof(size_t)))return STATUS_ACCESS_VIOLATION; + __try { + ProbeForRead(BaseAddress, sizeof(size_t)); + } + __except (EXCEPTION_EXECUTE_HANDLER) { + return GetExceptionCode(); + } - PLIST_ENTRY ListHead, ListEntry; PLDR_DATA_TABLE_ENTRY CurEntry; ULONG count = 0; NTSTATUS status = STATUS_SUCCESS; @@ -779,36 +822,29 @@ NTSTATUS NTAPI NtUnloadDllMemory(IN HMEMORYMODULE BaseAddress) { //Mapping dll failed if (module->loadFromNtLoadDllMemory && !module->MappedDll) { module->underUnload = true; - MemoryFreeLibrary(BaseAddress); - return STATUS_SUCCESS; + return MemoryFreeLibrary(BaseAddress) ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL; } - ListHead = &NtCurrentPeb()->Ldr->InLoadOrderModuleList; - ListEntry = ListHead->Flink; - while (ListEntry != ListHead) { - CurEntry = CONTAINING_RECORD(ListEntry, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks); - ListEntry = ListEntry->Flink; - if (CurEntry->DllBase == BaseAddress) { - if (RtlImageNtHeader(BaseAddress)->OptionalHeader.SizeOfImage == CurEntry->SizeOfImage) { - if (module->UseReferenceCount) { - status = NtGetReferenceCount(CurEntry, &count); - if (!NT_SUCCESS(status))return status; - } - if (!(count & ~1)) { - 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 (CurEntry = RtlFindLdrTableEntryByHandle(BaseAddress)) { + if (RtlImageNtHeader(BaseAddress)->OptionalHeader.SizeOfImage == CurEntry->SizeOfImage) { + if (module->UseReferenceCount) { + status = NtGetReferenceCount(CurEntry, &count); + if (!NT_SUCCESS(status))return status; + } + if (!(count & ~1)) { + module->underUnload = true; + if (module->MappedDll) { + if (module->InsertInvertedFunctionTableEntry) { + status = RtlRemoveInvertedFunctionTable(BaseAddress); + if (!NT_SUCCESS(status))__fastfail(FAST_FAIL_CORRUPT_LIST_ENTRY); } - if (!MemoryFreeLibrary(BaseAddress))__fastfail(STATUS_UNSUCCESSFUL); - return STATUS_SUCCESS; - } - else { - return NtUpdateReferenceCount(CurEntry, FLAG_DEREFERENCE); + if (!NtFreeLdrDataTableEntry(CurEntry))__fastfail(FAST_FAIL_FATAL_APP_EXIT); } + if (!MemoryFreeLibrary(BaseAddress))__fastfail(FAST_FAIL_FATAL_APP_EXIT); + return STATUS_SUCCESS; + } + else { + return NtUpdateReferenceCount(CurEntry, FLAG_DEREFERENCE); } } } @@ -816,6 +852,11 @@ NTSTATUS NTAPI NtUnloadDllMemory(IN HMEMORYMODULE BaseAddress) { return STATUS_INVALID_HANDLE; } +VOID NTAPI NtUnloadDllMemoryAndExitThread(IN HMEMORYMODULE BaseAddress, IN DWORD dwExitCode) { + NtUnloadDllMemory(BaseAddress); + RtlExitUserThread(dwExitCode); +} + @@ -841,7 +882,7 @@ static VOID NTAPI RtlpInsertInvertedFunctionTable(IN PRTL_INVERTED_FUNCTION_TABL ULONG SizeOfTable = 0; PIMAGE_NT_HEADERS headers = RtlImageNtHeader(ImageBase); PIMAGE_DATA_DIRECTORY dir = &headers->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION]; - bool need = RtlIsWindowsVersionOrGreater(10, 0, 0); + bool need = RtlIsWindowsVersionOrGreater(6, 2, 0); Index = (ULONG)need; CurrentSize = InvertedTable->Count; @@ -879,22 +920,23 @@ static VOID NTAPI RtlpInsertInvertedFunctionTable(IN PRTL_INVERTED_FUNCTION_TABL #else DWORD ptr, count; - bool IsWin10 = RtlIsWindowsVersionOrGreater(10, 0, 0); - ULONG Index = IsWin10 ? 1 : 0; + bool IsWin8OrGreater = RtlIsWindowsVersionOrGreater(6, 2, 0); + ULONG Index = IsWin8OrGreater ? 1 : 0; if (InvertedTable->Count == InvertedTable->MaxCount) { - InvertedTable->Overflow = TRUE; + if (IsWin8OrGreater)InvertedTable->NextEntrySEHandlerTableEncoded = TRUE; + else InvertedTable->Overflow = TRUE; return; } while (Index < InvertedTable->Count) { - if (ImageBase < (IsWin10 ? + if (ImageBase < (IsWin8OrGreater ? ((PRTL_INVERTED_FUNCTION_TABLE_ENTRY_64)&InvertedTable->Entries[Index])->ImageBase : InvertedTable->Entries[Index].ImageBase)) break; Index++; } if (Index != InvertedTable->Count) { - if (IsWin10) { + if (IsWin8OrGreater) { RtlMoveMemory(&InvertedTable->Entries[Index + 1], &InvertedTable->Entries[Index], (InvertedTable->Count - Index) * sizeof(RTL_INVERTED_FUNCTION_TABLE_ENTRY)); } @@ -906,7 +948,7 @@ static VOID NTAPI RtlpInsertInvertedFunctionTable(IN PRTL_INVERTED_FUNCTION_TABL } RtlCaptureImageExceptionValues(ImageBase, &ptr, &count); - if (IsWin10) { + if (IsWin8OrGreater) { //memory layout is same as x64 PRTL_INVERTED_FUNCTION_TABLE_ENTRY_64 entry = (decltype(entry))&InvertedTable->Entries[Index]; entry->ExceptionDirectory = (PIMAGE_RUNTIME_FUNCTION_ENTRY)RtlEncodeSystemPointer((PVOID)ptr); @@ -930,11 +972,11 @@ static VOID NTAPI RtlpRemoveInvertedFunctionTable(IN PRTL_INVERTED_FUNCTION_TABL ULONG CurrentSize; ULONG Index; //bool need = RtlIsWindowsVersionOrGreater(6, 2, 0); - bool IsWin10 = RtlIsWindowsVersionOrGreater(10, 0, 0); + bool IsWin8OrGreater = RtlIsWindowsVersionOrGreater(6, 2, 0); CurrentSize = InvertedTable->Count; for (Index = 0; Index < CurrentSize; Index += 1) { - if (ImageBase == (IsWin10 ? + if (ImageBase == (IsWin8OrGreater ? ((PRTL_INVERTED_FUNCTION_TABLE_ENTRY_64)&InvertedTable->Entries[Index])->ImageBase : InvertedTable->Entries[Index].ImageBase)) break; @@ -948,7 +990,7 @@ static VOID NTAPI RtlpRemoveInvertedFunctionTable(IN PRTL_INVERTED_FUNCTION_TABL &InvertedTable->Entries[Index + 1], (CurrentSize - Index - 1) * sizeof(RTL_INVERTED_FUNCTION_TABLE_ENTRY)); #else - if (IsWin10) { + if (IsWin8OrGreater) { RtlMoveMemory(&InvertedTable->Entries[Index], &InvertedTable->Entries[Index + 1], (CurrentSize - Index) * sizeof(RTL_INVERTED_FUNCTION_TABLE_ENTRY)); } @@ -964,6 +1006,19 @@ static VOID NTAPI RtlpRemoveInvertedFunctionTable(IN PRTL_INVERTED_FUNCTION_TABL //if (need)_InterlockedIncrement(&InvertedTable->Epoch); } + if (InvertedTable->Count != InvertedTable->MaxCount) { +#ifdef _WIN64 + //InvertedTable->Overflow = FALSE; +#else + if (IsWin8OrGreater) { + InvertedTable->NextEntrySEHandlerTableEncoded = FALSE; + } + else { + InvertedTable->Overflow = FALSE; + } +#endif + } + return; } @@ -1046,6 +1101,14 @@ static NTSTATUS NTAPI RtlFindMemoryBlockFromModuleSection( return status; } +static __forceinline bool NTAPI RtlIsModuleUnloaded(PLDR_DATA_TABLE_ENTRY entry) { + if (RtlIsWindowsVersionOrGreater(6, 2, 0)) { + return PLDR_DATA_TABLE_ENTRY_WIN8(entry)->DdagNode->State == LdrModulesUnloaded; + } + else { + return entry->DllBase == nullptr; + } +} static PVOID FindLdrpInvertedFunctionTable32() { // _RTL_INVERTED_FUNCTION_TABLE x86 // Count +0x0 ???????? @@ -1064,20 +1127,20 @@ static PVOID FindLdrpInvertedFunctionTable32() { _RTL_INVERTED_FUNCTION_TABLE_ENTRY_WIN7_32 entry{}; LPCSTR lpSectionName = ".data"; SEARCH_CONTEXT SearchContext{ SearchContext.MemoryBuffer = &entry,SearchContext.BufferLength = sizeof(entry) }; - BYTE Offset = 0xC; PLIST_ENTRY ListHead = &NtCurrentPeb()->Ldr->InMemoryOrderModuleList, ListEntry = ListHead->Flink; PLDR_DATA_TABLE_ENTRY CurEntry = nullptr; DWORD SEHTable, SEHCount; + BYTE Offset = 0x20; //sizeof(_RTL_INVERTED_FUNCTION_TABLE_ENTRY)*2 + + if (RtlIsWindowsVersionOrGreater(10, 0, 0)) lpSectionName = ".mrdata"; + else if (!RtlIsWindowsVersionOrGreater(6, 2, 0)) Offset = 0xC; - //Does Windows 8 need fix? - if (RtlIsWindowsVersionOrGreater(10, 0, 0)) { - Offset = 0x20; //sizeof(_RTL_INVERTED_FUNCTION_TABLE_ENTRY)*2 - lpSectionName = ".mrdata"; - } while (ListEntry != ListHead) { CurEntry = CONTAINING_RECORD(ListEntry, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks); ListEntry = ListEntry->Flink; + if (RtlIsModuleUnloaded(CurEntry))continue; //skip unloaded module + if (IsValidMemoryModuleHandle(CurEntry->DllBase))continue; //skip our memory module. if (CurEntry->DllBase == hNtdll && Offset == 0x20)continue; //Win10 skip first entry, if the base of ntdll is smallest. hModule = (HMODULE)(hModule ? min(hModule, CurEntry->DllBase) : CurEntry->DllBase); } @@ -1117,7 +1180,14 @@ static PVOID FindLdrpInvertedFunctionTable64() { PIMAGE_DATA_DIRECTORY dir = nullptr; SEARCH_CONTEXT SearchContext{ SearchContext.MemoryBuffer = &entry,SearchContext.BufferLength = sizeof(entry) }; - if (RtlIsWindowsVersionOrGreater(10, 0, 0)) { + //Windows 8 + if (RtlVerifyVersion(6, 2, 0, RTL_VERIFY_FLAGS_MAJOR_VERSION | RTL_VERIFY_FLAGS_MINOR_VERSION)) { + hModule = hNtdll; + ModuleHeaders = NtdllHeaders; + //lpSectionName = ".data"; + } + //Windows 8.1 ~ Windows 10 + else if (RtlIsWindowsVersionOrGreater(6, 3, 0)) { hModule = hNtdll; ModuleHeaders = NtdllHeaders; lpSectionName = ".mrdata"; @@ -1129,6 +1199,8 @@ static PVOID FindLdrpInvertedFunctionTable64() { while (ListEntry != ListHead) { CurEntry = CONTAINING_RECORD(ListEntry, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks); ListEntry = ListEntry->Flink; + //Make sure the smallest base address is not our memory module + if (IsValidMemoryModuleHandle(CurEntry->DllBase))continue; hModule = (HMODULE)(hModule ? min(hModule, CurEntry->DllBase) : CurEntry->DllBase); } ModuleHeaders = RtlImageNtHeader(hModule); @@ -1403,7 +1475,39 @@ int NTAPI RtlCaptureImageExceptionValues(PVOID BaseAddress, PDWORD SEHandlerTabl return 0; } +#define MEMORY_FEATURE_SUPPORT_VERSION 0x00000001 +#define MEMORY_FEATURE_MODULE_BASEADDRESS_INDEX 0x00000002 +#define MEMORY_FEATURE_LDRP_HEAP 0x00000004 +#define MEMORY_FEATURE_LDRP_HASH_TABLE 0x00000008 +#define MEMORY_FEATURE_INVERTED_FUNCTION_TABLE 0x00000010 +#define MEMORY_FEATURE_LDRP_HANDLE_TLS_DATA 0x00000020 +NTSTATUS NTAPI NtQuerySystemMemoryModuleFeatures(OUT PDWORD pFeatures) { + static DWORD features = 0; + NTSTATUS status = STATUS_SUCCESS; + PVOID pfn = nullptr; + bool value = false; + __try { + if (features) { + *pFeatures = features; + return status; + } + + if (RtlFindLdrpModuleBaseAddressIndex())features |= MEMORY_FEATURE_MODULE_BASEADDRESS_INDEX; + if (RtlFindtLdrpHeap())features |= MEMORY_FEATURE_LDRP_HEAP; + if (RtlFindLdrpHashTable())features |= MEMORY_FEATURE_LDRP_HASH_TABLE; + if (RtlFindLdrpInvertedFunctionTable())features |= MEMORY_FEATURE_INVERTED_FUNCTION_TABLE; + if (NT_SUCCESS(RtlFindLdrpHandleTlsData(&pfn, &value)) && pfn)features |= MEMORY_FEATURE_LDRP_HANDLE_TLS_DATA; + + if (features)features |= MEMORY_FEATURE_SUPPORT_VERSION; + *pFeatures = features; + } + __except (EXCEPTION_EXECUTE_HANDLER) { + status = GetExceptionCode(); + } + return status; +} + #ifndef _WIN64 #undef RtlCompareMemory -#undef FindLdrpInvertedFunctionTable #endif +#undef FindLdrpInvertedFunctionTable diff --git a/MemoryModule/NativeFunctionsInternal.h b/MemoryModule/NativeFunctionsInternal.h index e6a26a6..bc6a8e1 100644 --- a/MemoryModule/NativeFunctionsInternal.h +++ b/MemoryModule/NativeFunctionsInternal.h @@ -121,6 +121,19 @@ struct _LDR_DDAG_NODE { _SINGLE_LIST_ENTRY CondenseLink; //0x40 ULONG PreorderNumber; //0x48 }; +struct _LDR_DDAG_NODE_WIN8 { + _LIST_ENTRY Modules; //0x0 + _LDR_SERVICE_TAG_RECORD* ServiceTagList; //0x10 + ULONG LoadCount; //0x18 + ULONG ReferenceCount; //0x1c + ULONG DependencyCount; //0x20 + _LDRP_CSLIST::_LDRP_CSLIST_DEPENDENT* Dependencies; //0x28 + _LDRP_CSLIST::_LDRP_CSLIST_INCOMMING* IncomingDependencies; //0x30 + _LDR_DDAG_STATE State; //0x38 + _SINGLE_LIST_ENTRY CondenseLink; //0x40 + ULONG PreorderNumber; //0x48 + ULONG LowestLink; //0x4c +}; //5.1.2600 Windows XP SP3 //5.2.3790 Windows XP | 2003 SP2 @@ -219,7 +232,7 @@ typedef struct _LDR_DATA_TABLE_ENTRY_WIN8 { ULONG TimeDateStamp; //0x80 _ACTIVATION_CONTEXT* EntryPointActivationContext; //0x88 VOID* PatchInformation; //0x90 - _LDR_DDAG_NODE* DdagNode; //0x98 + _LDR_DDAG_NODE_WIN8* DdagNode; //0x98 _LIST_ENTRY NodeModuleLink; //0xa0 VOID* SnapContext; //0xb0 VOID* ParentDllBase; //0xb8 @@ -438,6 +451,11 @@ NTSTATUS NTAPI NtLoadDllMemoryExW( NTSTATUS NTAPI NtUnloadDllMemory(IN HMEMORYMODULE BaseAddress); +extern "C" { + __declspec(noreturn) VOID NTAPI NtUnloadDllMemoryAndExitThread(IN HMEMORYMODULE BaseAddress, IN DWORD dwExitCode); +} + + typedef struct _RTL_RB_TREE { PRTL_BALANCED_NODE Root; PRTL_BALANCED_NODE Min; diff --git a/README.md b/README.md index 3a04f8e..4ba59bf 100644 --- a/README.md +++ b/README.md @@ -8,8 +8,12 @@ MemoryModulePP, used to load a DLL from memory. MemoryModulePP is compatible wit **This repository is under development.** + +> In order to support 32-bit dll exception handling, the dll should enable the /SAFESEH linker option, +> otherwise the exception handler cannot pass the RtlIsValidHandler () check when an exception occurs + ## New Features - - Support Win10 forward export + - Support Win8 ## Features - Compatible with Win32 API (GetModuleHandleA/W/Ex GetModuleFileNameA/W/Ex GetProcAddress and any Resource API) @@ -20,6 +24,7 @@ MemoryModulePP, used to load a DLL from memory. MemoryModulePP is compatible wit - The above features can be turned off through the dwFlags parameter of NtLoadDllMemoryExW - Support for TLS(Thread Local Storage) - DllMain can receive four types of notifications + - Support Win10 forward export ## Tech diff --git a/a/a.rc b/a/a.rc index 7080dca..2b900ca 100644 --- a/a/a.rc +++ b/a/a.rc @@ -45,6 +45,14 @@ END #endif // APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// BINARY +// + +IDR_BINARY1 BINARY "binary1.bin" + + ///////////////////////////////////////////////////////////////////////////// // // String Table diff --git a/a/a.vcxproj b/a/a.vcxproj index b6603b7..eca2992 100644 --- a/a/a.vcxproj +++ b/a/a.vcxproj @@ -31,6 +31,7 @@ true v142 Unicode + false DynamicLibrary @@ -91,14 +92,16 @@ true - MultiThreaded + MultiThreadedDebug + Sync Windows true false m.def + true @@ -178,6 +181,7 @@ + diff --git a/a/a.vcxproj.filters b/a/a.vcxproj.filters index 4bd388e..a959bc2 100644 --- a/a/a.vcxproj.filters +++ b/a/a.vcxproj.filters @@ -36,5 +36,8 @@ Resource Files + + Resource Files + \ No newline at end of file diff --git a/a/binary1.bin b/a/binary1.bin new file mode 100644 index 0000000..cc65721 Binary files /dev/null and b/a/binary1.bin differ diff --git a/a/resource.h b/a/resource.h index c345b02..9c21f76 100644 --- a/a/resource.h +++ b/a/resource.h @@ -3,12 +3,13 @@ // Used by a.rc // #define IDS_STRING101 101 +#define IDR_BINARY1 102 // Next default values for new objects // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 102 +#define _APS_NEXT_RESOURCE_VALUE 103 #define _APS_NEXT_COMMAND_VALUE 40001 #define _APS_NEXT_CONTROL_VALUE 1001 #define _APS_NEXT_SYMED_VALUE 101 diff --git a/test/test.cpp b/test/test.cpp index c1563cf..8866f26 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -1,21 +1,13 @@ -#include "../MemoryModule/NativeFunctionsInternal.h" -//#include "../MemoryModule/LoadDllMemoryApi.h" +//#include "../MemoryModule/NativeFunctionsInternal.h" +#include "../MemoryModule/LoadDllMemoryApi.h" #ifndef NT_SUCCESS #define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0) #endif #include #pragma warning(disable:4996) -PLDR_DATA_TABLE_ENTRY_WIN10_2 RtlFindDllLdrEntry(LPCWSTR DllName) { - PLIST_ENTRY head = &NtCurrentPeb()->Ldr->InMemoryOrderModuleList, entry = head->Flink; - PLDR_DATA_TABLE_ENTRY_WIN10_2 cur = nullptr; - while (entry != head) { - cur = CONTAINING_RECORD(entry, LDR_DATA_TABLE_ENTRY_WIN10_2, InMemoryOrderLinks); - entry = entry->Flink; - if (!wcsicmp(DllName, cur->BaseDllName.Buffer))return cur; - } - return nullptr; -} + int main() { + //return ((int(*)(int))GetProcAddress(LoadLibraryW(L"a.dll"), "exception"))(0); LPVOID buffer; size_t size; FILE* f = fopen("a.dll", "rb"); @@ -28,25 +20,82 @@ int main() { _fseeki64(f, 0, SEEK_SET); fread(buffer = new char[size], 1, size, f); fclose(f); - + HMEMORYMODULE m1 = nullptr, m2 = m1; HMODULE hModule = nullptr; FARPROC pfn = nullptr; + DWORD MemoryModuleFeatures = 0; - if (!NT_SUCCESS(NtLoadDllMemoryExW(&m1, nullptr, 0, buffer, size, L"kernel64", nullptr))) goto end; - if (!NT_SUCCESS(NtLoadDllMemoryExW(&m2, nullptr, 0, buffer, size, L"kernel128", nullptr))) goto end; + typedef int(* _exception)(int code); + _exception exception = nullptr; + HRSRC hRsrc; + DWORD SizeofRes; + HGLOBAL gRes; + char str[10]; + + NtQuerySystemMemoryModuleFeatures(&MemoryModuleFeatures); + if (MemoryModuleFeatures != MEMORY_FEATURE_ALL) { + printf("not support all features on this version of windows.\n"); + } + + if (!NT_SUCCESS(NtLoadDllMemoryExW(&m1, nullptr, 0, buffer, 0, L"kernel64", nullptr))) goto end; + LoadLibraryW(L"wininet.dll"); + if (!NT_SUCCESS(NtLoadDllMemoryExW(&m2, nullptr, 0, buffer, 0, L"kernel128", nullptr))) goto end; + + //forward export hModule = (HMODULE)m1; pfn = (decltype(pfn))(GetProcAddress(hModule, "Socket")); //ws2_32.WSASocketW pfn = (decltype(pfn))(GetProcAddress(hModule, "VerifyTruse")); //wintrust.WinVerifyTrust hModule = (HMODULE)m2; pfn = (decltype(pfn))(GetProcAddress(hModule, "Socket")); pfn = (decltype(pfn))(GetProcAddress(hModule, "VerifyTruse")); - printf("pfn = %p\n", pfn); + + //exception + hModule = (HMODULE)m1; + exception = (_exception)GetProcAddress(hModule, "exception"); + if (exception) { + for (int i = 0; i < 4; ++i)exception(i); + } + + //tls + pfn = GetProcAddress(hModule, "thread"); + if (pfn && pfn()) { + printf("thread test failed.\n"); + } + + //resource + if (!LoadStringA(hModule, 101, str, 10)) { + printf("load string failed.\n"); + } + else { + printf("%s\n", str); + } + if (!(hRsrc = FindResourceA(hModule, MAKEINTRESOURCEA(102), "BINARY"))) { + printf("find binary resource failed.\n"); + } + else { + if ((SizeofRes = SizeofResource(hModule, hRsrc)) != 0x10) { + printf("invalid res size.\n"); + } + else { + if (!(gRes = LoadResource(hModule, hRsrc))) { + printf("load res failed.\n"); + } + else { + if (!LockResource(gRes))printf("lock res failed.\n"); + else { + printf("resource test success.\n"); + } + } + } + } end: delete[]buffer; if (m1)NtUnloadDllMemory(m1); + FreeLibrary(GetModuleHandleW(L"wininet.dll")); if (m2)NtUnloadDllMemory(m2); + return 0; }