private static void *GetRealSectionSize(MEMORYMODULE module, IMAGE_SECTION_HEADER *section) { uint size = section->SizeOfRawData; if (size == 0) { if ((section->Characteristics & IMAGE_SCN_CNT_INITIALIZED_DATA) != 0) { size = (WIN64 ? ((IMAGE_NT_HEADERS64 *)module.headers)->OptionalHeader.SizeOfInitializedData : ((IMAGE_NT_HEADERS32 *)module.headers)->OptionalHeader.SizeOfInitializedData); } else if ((section->Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA) != 0) { size = (WIN64 ? ((IMAGE_NT_HEADERS64 *)module.headers)->OptionalHeader.SizeOfUninitializedData : ((IMAGE_NT_HEADERS32 *)module.headers)->OptionalHeader.SizeOfUninitializedData); } } return((void *)size); }
public static void MemoryFreeLibrary(MEMORYMODULE mod) { MEMORYMODULE module = mod; if (module == null) { return; } if (module.initialized) { // notify library about detaching from process DllEntryProc DllEntry = (DllEntryProc)Marshal.GetDelegateForFunctionPointer((IntPtr)(module.codeBase + (WIN64 ? ((IMAGE_NT_HEADERS64 *)module.headers)->OptionalHeader.AddressOfEntryPoint : ((IMAGE_NT_HEADERS32 *)module.headers)->OptionalHeader.AddressOfEntryPoint)), typeof(DllEntryProc)); DllEntry(module.codeBase, DLL_PROCESS_DETACH, null); } if (module.modules != null) { // free previously opened libraries int i; for (i = 0; i < module.numModules; i++) { if ((void *)((IntPtr *)module.modules)[i] != null) { module.freeLibrary((void *)((IntPtr *)module.modules)[i], module.userdata); } } free(module.modules); } if (module.codeBase != null) { // release memory of library module.free(module.codeBase, null, MEM_RELEASE, module.userdata); } if (WIN64) { FreePointerList(module.blockedMemory, module.free, module.userdata); } }
private static bool FinalizeSection(MEMORYMODULE module, SECTIONFINALIZEDATA sectionData) { uint protect, oldProtect; bool executable; bool readable; bool writeable; if (sectionData.size == null) { return(true); } if ((sectionData.characteristics & IMAGE_SCN_MEM_DISCARDABLE) != 0) { // section is not needed any more and can safely be freed if (sectionData.address == sectionData.alignedAddress && (sectionData.last || (WIN64 ? ((IMAGE_NT_HEADERS64 *)module.headers)->OptionalHeader.SectionAlignment : ((IMAGE_NT_HEADERS32 *)module.headers)->OptionalHeader.SectionAlignment) == module.pageSize || ((ulong)sectionData.size % module.pageSize) == 0)) { // Only allowed to decommit whole pages module.free(sectionData.address, sectionData.size, MEM_DECOMMIT, module.userdata); } return(true); } // determine protection flags based on characteristics executable = (sectionData.characteristics & IMAGE_SCN_MEM_EXECUTE) != 0; readable = (sectionData.characteristics & IMAGE_SCN_MEM_READ) != 0; writeable = (sectionData.characteristics & IMAGE_SCN_MEM_WRITE) != 0; protect = ProtectionFlags[executable ? 1 : 0, readable ? 1 : 0, writeable ? 1 : 0]; if ((sectionData.characteristics & IMAGE_SCN_MEM_NOT_CACHED) != 0) { protect |= PAGE_NOCACHE; } // change memory access flags if (!VirtualProtect(sectionData.address, sectionData.size, protect, &oldProtect)) { return(false); } return(true); }
public static void *MemoryFindResourceEx(MEMORYMODULE module, string s_name, string s_type, ushort language, bool unicode) { byte *name; byte *type; if (unicode) { fixed(char *p = s_name.ToCharArray()) name = (byte *)p; fixed(char *p = s_type.ToCharArray()) type = (byte *)p; } else { fixed(byte *p = Encoding.Convert(Encoding.Unicode, Encoding.ASCII, Encoding.Unicode.GetBytes(s_name))) name = p; fixed(byte *p = Encoding.Convert(Encoding.Unicode, Encoding.ASCII, Encoding.Unicode.GetBytes(s_type))) type = p; } return(MemoryFindResourceEx(module, name, type, language, unicode)); }
private static bool ExecuteTLS(MEMORYMODULE module) { byte* codeBase = module.codeBase; IMAGE_TLS_DIRECTORY tls; void** callback; IMAGE_DATA_DIRECTORY* directory = GET_HEADER_DICTIONARY(module, IMAGE_DIRECTORY_ENTRY_TLS); if (directory->VirtualAddress == 0) { return true; } tls = *(IMAGE_TLS_DIRECTORY*)((ulong)codeBase + directory->VirtualAddress); callback = (void**)tls.AddressOfCallBacks; if (callback != null) { while (*callback != null) { ((IMAGE_TLS_CALLBACK)Marshal.GetDelegateForFunctionPointer((IntPtr)(*callback), typeof(IMAGE_TLS_CALLBACK)))((void*)codeBase, DLL_PROCESS_ATTACH, null); callback++; } } return true; }
internal static void* MemoryGetProcAddress(MEMORYMODULE mod, byte* name) { MEMORYMODULE module = mod; byte* codeBase = module.codeBase; uint idx = 0; IMAGE_EXPORT_DIRECTORY* exports; IMAGE_DATA_DIRECTORY* directory = GET_HEADER_DICTIONARY(module, IMAGE_DIRECTORY_ENTRY_EXPORT); if (directory->Size == 0) // no export table found return null; exports = (IMAGE_EXPORT_DIRECTORY*)(codeBase + directory->VirtualAddress); if (exports->NumberOfNames == 0 || exports->NumberOfFunctions == 0) // DLL doesn't export anything return null; if (HIWORD(name) == 0) { // load function by ordinal value if (LOWORD(name) < exports->Base) return null; idx = LOWORD(name) - exports->Base; } else if (exports->NumberOfNames == 0) return null; else { ExportNameEntry found; // Lazily build name table and sort it by names if (module.nameExportsTable == null) { uint i; uint* nameRef = (uint*)(codeBase + exports->AddressOfNames); ushort* ordinal = (ushort*)(codeBase + exports->AddressOfNameOrdinals); ExportNameEntry[] entry = new ExportNameEntry[exports->NumberOfNames]; module.nameExportsTable = entry; for (i = 0; i < exports->NumberOfNames; i++, nameRef++, ordinal++) entry[i] = new ExportNameEntry { name = codeBase + (*nameRef), idx = *ordinal }; Array.Sort(module.nameExportsTable, _compare); } // search function name in list of exported names with binary search ExportNameEntry tmp = new ExportNameEntry { name = name }; int foundIndex = Array.BinarySearch(module.nameExportsTable, tmp, tmp); found = foundIndex < 0 ? null : module.nameExportsTable[foundIndex]; if (found == null) // exported symbol not found return null; idx = found.idx; } if (idx > exports->NumberOfFunctions) // name <. ordinal number don't match return null; // AddressOfFunctions contains the RVAs to the "real" functions return (void*)(codeBase + (*(uint*)(codeBase + exports->AddressOfFunctions + (idx * 4)))); }
internal static void* MemoryGetProcAddress(MEMORYMODULE mod, string s_name) { fixed (byte* p = Encoding.Convert(Encoding.Unicode, Encoding.ASCII, Encoding.Unicode.GetBytes(s_name))) return MemoryGetProcAddress(mod, p); }
internal static MEMORYMODULE MemoryLoadLibraryEx(void* data, void* size, CustomAllocFunc allocMemory, CustomFreeFunc freeMemory, CustomLoadLibraryFunc loadLibrary, CustomGetProcAddressFunc getProcAddress, CustomFreeLibraryFunc freeLibrary, void* userdata) { MEMORYMODULE result = null; IMAGE_DOS_HEADER* dos_header; void* old_header; byte* code, headers; void* locationDelta; SYSTEM_INFO sysInfo; IMAGE_SECTION_HEADER* section; uint i; void* optionalSectionSize; void* lastSectionEnd = null; void* alignedImageSize; POINTER_LIST blockedMemory = null; if (!CheckSize(size, (void*)IMAGE_DOS_HEADER.UnmanagedSize)) return null; dos_header = (IMAGE_DOS_HEADER*)data; if (dos_header->e_magic != IMAGE_DOS_SIGNATURE) return null; if (!CheckSize(size, (void*)(dos_header->e_lfanew + (WIN64 ? IMAGE_NT_HEADERS64.UnmanagedSize : IMAGE_NT_HEADERS32.UnmanagedSize)))) return null; old_header = &((byte*)data)[dos_header->e_lfanew]; if ((WIN64 ? ((IMAGE_NT_HEADERS64*)old_header)->Signature : ((IMAGE_NT_HEADERS32*)old_header)->Signature) != IMAGE_NT_SIGNATURE) return null; if ((WIN64 ? ((IMAGE_NT_HEADERS64*)old_header)->FileHeader.Machine : ((IMAGE_NT_HEADERS32*)old_header)->FileHeader.Machine) != HOST_MACHINE) return null; if (((WIN64 ? ((IMAGE_NT_HEADERS64*)old_header)->OptionalHeader.SectionAlignment : ((IMAGE_NT_HEADERS32*)old_header)->OptionalHeader.SectionAlignment) & 1) != 0) // Only support section alignments that are a multiple of 2 return null; section = IMAGE_FIRST_SECTION(old_header); optionalSectionSize = (void*)((WIN64 ? ((IMAGE_NT_HEADERS64*)old_header)->OptionalHeader.SectionAlignment : ((IMAGE_NT_HEADERS32*)old_header)->OptionalHeader.SectionAlignment)); for (i = 0; i < (WIN64 ? ((IMAGE_NT_HEADERS64*)old_header)->FileHeader.NumberOfSections : ((IMAGE_NT_HEADERS32*)old_header)->FileHeader.NumberOfSections); i++, section++) { void* endOfSection; if (section->SizeOfRawData == 0) { // Section without data in the DLL endOfSection = (void*)(section->VirtualAddress + (ulong)optionalSectionSize); } else { endOfSection = (void*)(section->VirtualAddress + (ulong)section->SizeOfRawData); } if ((ulong)endOfSection > (ulong)lastSectionEnd) lastSectionEnd = endOfSection; } GetNativeSystemInfo(&sysInfo); alignedImageSize = AlignValueUp((void*)(WIN64 ? ((IMAGE_NT_HEADERS64*)old_header)->OptionalHeader.SizeOfImage : ((IMAGE_NT_HEADERS32*)old_header)->OptionalHeader.SizeOfImage), (void*)sysInfo.dwPageSize); if (alignedImageSize != AlignValueUp(lastSectionEnd, (void*)sysInfo.dwPageSize)) return null; // reserve memory for image of library // XXX: is it correct to commit the complete memory region at once? // calling DllEntry raises an exception if we don't... code = (byte*)allocMemory((void*)(WIN64 ? ((IMAGE_NT_HEADERS64*)old_header)->OptionalHeader.ImageBase : ((IMAGE_NT_HEADERS32*)old_header)->OptionalHeader.ImageBase), alignedImageSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE, userdata); if (code == null) { // try to allocate memory at arbitrary position code = (byte*)allocMemory(null, alignedImageSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE, userdata); if (code == null) return null; } if (WIN64) { // Memory block may not span 4 GB boundaries. while ((ulong)code >> 32 < ((ulong)code + (ulong)alignedImageSize) >> 32) { POINTER_LIST node = new POINTER_LIST { next = blockedMemory, address = code }; blockedMemory = node; code = (byte*)allocMemory(null, alignedImageSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE, userdata); if (code == null) { FreePointerList(blockedMemory, freeMemory, userdata); return null; } } } result = new MEMORYMODULE { codeBase = code, isDLL = ((WIN64 ? ((IMAGE_NT_HEADERS64*)old_header)->FileHeader.Characteristics : ((IMAGE_NT_HEADERS32*)old_header)->FileHeader.Characteristics) & IMAGE_FILE_DLL) != 0, alloc = allocMemory, free = freeMemory, loadLibrary = loadLibrary, getProcAddress = getProcAddress, freeLibrary = freeLibrary, userdata = userdata, pageSize = sysInfo.dwPageSize }; if (WIN64) result.blockedMemory = blockedMemory; if (!CheckSize(size, (void*)(WIN64 ? ((IMAGE_NT_HEADERS64*)old_header)->OptionalHeader.SizeOfHeaders : ((IMAGE_NT_HEADERS32*)old_header)->OptionalHeader.SizeOfHeaders))) goto error; // commit memory for headers headers = (byte*)allocMemory(code, (void*)(WIN64 ? ((IMAGE_NT_HEADERS64*)old_header)->OptionalHeader.SizeOfHeaders : ((IMAGE_NT_HEADERS32*)old_header)->OptionalHeader.SizeOfHeaders), MEM_COMMIT, PAGE_READWRITE, userdata); // copy PE header to code memcpy(headers, dos_header, (void*)(WIN64 ? ((IMAGE_NT_HEADERS64*)old_header)->OptionalHeader.SizeOfHeaders : ((IMAGE_NT_HEADERS32*)old_header)->OptionalHeader.SizeOfHeaders)); result.headers = &headers[dos_header->e_lfanew]; // update position if (WIN64) ((IMAGE_NT_HEADERS64*)result.headers)->OptionalHeader.ImageBase = (ulong)code; else ((IMAGE_NT_HEADERS32*)result.headers)->OptionalHeader.ImageBase = (uint)code; // copy sections from DLL file block to new memory location if (!CopySections((byte*)data, size, old_header, result)) goto error; // adjust base address of imported data locationDelta = (void*)((WIN64 ? ((IMAGE_NT_HEADERS64*)result.headers)->OptionalHeader.ImageBase : ((IMAGE_NT_HEADERS32*)result.headers)->OptionalHeader.ImageBase) - (WIN64 ? ((IMAGE_NT_HEADERS64*)old_header)->OptionalHeader.ImageBase : ((IMAGE_NT_HEADERS32*)old_header)->OptionalHeader.ImageBase)); if ((ulong)locationDelta != 0) result.isRelocated = PerformBaseRelocation(result, locationDelta); else result.isRelocated = true; // load required dlls and adjust function table of imports if (!BuildImportTable(result)) goto error; // mark memory pages depending on section headers and release // sections that are marked as "discardable" if (!FinalizeSections(result)) goto error; // TLS callbacks are executed BEFORE the main loading if (!ExecuteTLS(result)) goto error; // get entry point of loaded library if ((WIN64 ? ((IMAGE_NT_HEADERS64*)result.headers)->OptionalHeader.AddressOfEntryPoint : ((IMAGE_NT_HEADERS32*)result.headers)->OptionalHeader.AddressOfEntryPoint) != 0) { if (result.isDLL) { DllEntryProc DllEntry = (DllEntryProc)Marshal.GetDelegateForFunctionPointer((IntPtr)(code + (WIN64 ? ((IMAGE_NT_HEADERS64*)result.headers)->OptionalHeader.AddressOfEntryPoint : ((IMAGE_NT_HEADERS32*)result.headers)->OptionalHeader.AddressOfEntryPoint)), typeof(DllEntryProc)); // notify library about attaching to process bool successfull = DllEntry(code, DLL_PROCESS_ATTACH, null); if (!successfull) goto error; result.initialized = true; } else result.exeEntry = (ExeEntryProc)Marshal.GetDelegateForFunctionPointer((IntPtr)(code + (WIN64 ? ((IMAGE_NT_HEADERS64*)result.headers)->OptionalHeader.AddressOfEntryPoint : ((IMAGE_NT_HEADERS32*)result.headers)->OptionalHeader.AddressOfEntryPoint)), typeof(ExeEntryProc)); } else result.exeEntry = null; return result; error: // cleanup MemoryFreeLibrary(result); return null; }
private static bool BuildImportTable(MEMORYMODULE module) { byte* codeBase = module.codeBase; IMAGE_IMPORT_DESCRIPTOR* importDesc; bool result = true; IMAGE_DATA_DIRECTORY* directory = GET_HEADER_DICTIONARY(module, IMAGE_DIRECTORY_ENTRY_IMPORT); if (directory->Size == 0) { return true; } importDesc = (IMAGE_IMPORT_DESCRIPTOR*)(codeBase + directory->VirtualAddress); for (; !IsBadReadPtr(importDesc, (void*)IMAGE_IMPORT_DESCRIPTOR.UnmanagedSize) && importDesc->Name != 0; importDesc++) { void** thunkRef; void** funcRef; void** tmp; void* handle = module.loadLibrary(codeBase + importDesc->Name, module.userdata); if (handle == null) { result = false; break; } tmp = (void**)realloc(module.modules, (void*)((module.numModules + 1) * sizeof(void*))); if (tmp == null) { module.freeLibrary(handle, module.userdata); result = false; break; } module.modules = tmp; module.modules[module.numModules++] = handle; if (importDesc->OriginalFirstThunk != 0) { thunkRef = (void**)(codeBase + importDesc->OriginalFirstThunk); funcRef = (void**)(codeBase + importDesc->FirstThunk); } else { // no hint table thunkRef = (void**)(codeBase + importDesc->FirstThunk); funcRef = (void**)(codeBase + importDesc->FirstThunk); } for (; *thunkRef != null; thunkRef++, funcRef++) { if (IMAGE_SNAP_BY_ORDINAL(*thunkRef)) { *funcRef = module.getProcAddress(handle, (byte*)IMAGE_ORDINAL(*thunkRef), module.userdata); } else { IMAGE_IMPORT_BY_NAME* thunkData = (IMAGE_IMPORT_BY_NAME*)(codeBase + (ulong)(*thunkRef)); *funcRef = module.getProcAddress(handle, thunkData->Name, module.userdata); } if (*funcRef == null) { result = false; break; } } if (!result) { module.freeLibrary(handle, module.userdata); break; } } return result; }
private static IMAGE_DATA_DIRECTORY* GET_HEADER_DICTIONARY(MEMORYMODULE module, uint idx) { return WIN64 ? (IMAGE_DATA_DIRECTORY*)&((IMAGE_NT_HEADERS64*)module.headers)->OptionalHeader.DataDirectory[idx] : (IMAGE_DATA_DIRECTORY*)&((IMAGE_NT_HEADERS32*)module.headers)->OptionalHeader.DataDirectory[idx]; }
private MemoryModule(MEMORYMODULE internalModule) => _internalModule = internalModule;
public static int MemoryLoadString(MEMORYMODULE module, uint id, out string buffer, int maxsize, bool unicode) { return(MemoryLoadStringEx(module, id, out buffer, maxsize, DEFAULT_LANGUAGE, unicode)); }
public static void *MemoryFindResource(MEMORYMODULE module, string name, string type, bool unicode) { return(MemoryFindResourceEx(module, name, type, DEFAULT_LANGUAGE, unicode)); }