/// <summary> /// Adjusts base address of the imported data. /// </summary> /// <param name="memory_module">Pointer to a memory module.</param> /// <param name="delta">Adjustment delta value.</param> private void PerformBaseRelocation(MEMORY_MODULE* memory_module, ulong delta) { WinNT.IMAGE_DATA_DIRECTORY* directory = this.GET_HEADER_DIRECTORY(memory_module, WinNT.IMAGE_DIRECTORY_ENTRY_BASERELOC); if (directory->Size > 0) { WinNT.IMAGE_BASE_RELOCATION* relocation = (WinNT.IMAGE_BASE_RELOCATION*)(memory_module->codeBase + directory->VirtualAddress); int sizeOfBaseRelocation = sizeof(WinNT.IMAGE_BASE_RELOCATION); int index; for (; relocation->VirtualAddress > 0; ) { byte* dest = (byte*)(memory_module->codeBase + relocation->VirtualAddress); ushort* relInfo = (ushort*)((byte*)relocation + sizeOfBaseRelocation); for (index = 0; index < ((relocation->SizeOfBlock - sizeOfBaseRelocation) / 2); index++, relInfo++) { uint* patchAddrHL32; ulong* patchAddrHL64; uint type, offset; // the upper 4 bits define the type of relocation type = (uint)(*relInfo >> 12); // the lower 12 bits define the offset offset = (uint)(*relInfo & 0xfff); switch (type) { case WinNT.IMAGE_REL_BASED_ABSOLUTE: break; case WinNT.IMAGE_REL_BASED_HIGHLOW: patchAddrHL32 = (uint*)((uint)dest + offset); *patchAddrHL32 += (uint)delta; break; case WinNT.IMAGE_REL_BASED_DIR64: patchAddrHL64 = (ulong*)((ulong)dest + offset); *patchAddrHL64 += delta; break; default: break; } } relocation = (WinNT.IMAGE_BASE_RELOCATION*)((byte*)relocation + relocation->SizeOfBlock); } } }
private WinNT.IMAGE_DATA_DIRECTORY* GET_HEADER_DIRECTORY(MEMORY_MODULE* memory_module, uint index) { if (Environment.Is64BitProcess) { WinNT.IMAGE_NT_HEADERS64* headers = (WinNT.IMAGE_NT_HEADERS64*)memory_module->headers; return (WinNT.IMAGE_DATA_DIRECTORY*)(&headers->OptionalHeader.DataDirectory[index]); } else { WinNT.IMAGE_NT_HEADERS32* headers = (WinNT.IMAGE_NT_HEADERS32*)memory_module->headers; return (WinNT.IMAGE_DATA_DIRECTORY*)(&headers->OptionalHeader.DataDirectory[index]); } }
/// <summary> /// Marks memory pages depending on section headers and release sections that are marked as "discardable". /// </summary> /// <param name="memory_module">Pointer to a memory module.</param> private void FinalizeSections(MEMORY_MODULE* memory_module) { WinNT.IMAGE_SECTION_HEADER* section = WinNT.IMAGE_FIRST_SECTION(memory_module->headers); ; ushort number_of_sections; uint size_of_initialized_data; uint size_of_uninitialized_data; long image_offset = 0; if (Environment.Is64BitProcess) { WinNT.IMAGE_NT_HEADERS64* headers = (WinNT.IMAGE_NT_HEADERS64*)memory_module->headers; number_of_sections = headers->FileHeader.NumberOfSections; size_of_initialized_data = headers->OptionalHeader.SizeOfInitializedData; size_of_uninitialized_data = headers->OptionalHeader.SizeOfUninitializedData; image_offset = (long)((ulong)headers->OptionalHeader.ImageBase & 0xffffffff00000000); } else { WinNT.IMAGE_NT_HEADERS32* headers = (WinNT.IMAGE_NT_HEADERS32*)memory_module->headers; number_of_sections = headers->FileHeader.NumberOfSections; size_of_initialized_data = headers->OptionalHeader.SizeOfInitializedData; size_of_uninitialized_data = headers->OptionalHeader.SizeOfUninitializedData; } for (int i = 0; i < number_of_sections; i++, section++) { uint protect, oldProtect, rawDataSize; uint executable = Convert.ToUInt32((section->Characteristics & WinNT.IMAGE_SCN_MEM_EXECUTE) != 0); uint readable = Convert.ToUInt32((section->Characteristics & WinNT.IMAGE_SCN_MEM_READ) != 0); uint writeable = Convert.ToUInt32((section->Characteristics & WinNT.IMAGE_SCN_MEM_WRITE) != 0); if ((section->Characteristics & WinNT.IMAGE_SCN_MEM_DISCARDABLE) != 0) { // section is not needed any more and can safely be freed WinBase.VirtualFree((IntPtr)((long)section->PhysicalAddress | (long)image_offset), section->SizeOfRawData, WinNT.MEM_DECOMMIT); continue; } protect = _protectionFlags[executable, readable, writeable]; if ((section->Characteristics & WinNT.IMAGE_SCN_MEM_NOT_CACHED) != 0) protect |= WinNT.PAGE_NOCACHE; // determine size of region rawDataSize = section->SizeOfRawData; if (rawDataSize == 0) { if ((section->Characteristics & WinNT.IMAGE_SCN_CNT_INITIALIZED_DATA) != 0) rawDataSize = size_of_initialized_data; else if ((section->Characteristics & WinNT.IMAGE_SCN_CNT_UNINITIALIZED_DATA) != 0) rawDataSize = size_of_uninitialized_data; } if (rawDataSize > 0) { // change memory access flags WinBase.VirtualProtect((IntPtr)((long)section->PhysicalAddress | (long)image_offset), rawDataSize, protect, &oldProtect); } } }
/// <summary> /// Copies sections from a native module file block to the new memory location. /// </summary> /// <param name="ptr_data">Pointer to a native module byte array.</param> /// <param name="ptr_old_headers">Pointer to a source native module headers.</param> /// <param name="memory_module">Pointer to a memory module.</param> private void CopySections(byte* ptr_data, byte* ptr_old_headers, MEMORY_MODULE* memory_module) { byte* codeBase = memory_module->codeBase; WinNT.IMAGE_SECTION_HEADER* section = WinNT.IMAGE_FIRST_SECTION(memory_module->headers); ushort numberOfSections; uint sectionAlignment; if (Environment.Is64BitProcess) { WinNT.IMAGE_NT_HEADERS64* new_headers = (WinNT.IMAGE_NT_HEADERS64*)memory_module->headers; numberOfSections = new_headers->FileHeader.NumberOfSections; WinNT.IMAGE_NT_HEADERS64* old_headers = (WinNT.IMAGE_NT_HEADERS64*)ptr_old_headers; sectionAlignment = old_headers->OptionalHeader.SectionAlignment; } else { WinNT.IMAGE_NT_HEADERS32* new_headers = (WinNT.IMAGE_NT_HEADERS32*)memory_module->headers; numberOfSections = new_headers->FileHeader.NumberOfSections; WinNT.IMAGE_NT_HEADERS32* old_headers = (WinNT.IMAGE_NT_HEADERS32*)ptr_old_headers; sectionAlignment = old_headers->OptionalHeader.SectionAlignment; } uint index; byte* dest; for (index = 0; index < numberOfSections; index++, section++) { if (section->SizeOfRawData == 0) { if (sectionAlignment > 0) { dest = (byte*)WinBase.VirtualAlloc((IntPtr)(codeBase + section->VirtualAddress), sectionAlignment, WinNT.MEM_COMMIT, WinNT.PAGE_READWRITE); section->PhysicalAddress = (uint)dest; memory.memset(dest, 0, sectionAlignment); } continue; } // commit memory block and copy data from dll dest = (byte*)WinBase.VirtualAlloc((IntPtr)(codeBase + section->VirtualAddress), section->SizeOfRawData, WinNT.MEM_COMMIT, WinNT.PAGE_READWRITE); memory.memcpy(dest, ptr_data + section->PointerToRawData, section->SizeOfRawData); section->PhysicalAddress = (uint)dest; } }
/// <summary> /// Calls module entry point. /// </summary> /// <param name="memory_module">Pointer to a memory module.</param> /// <param name="fdwReason"></param> /// <returns>If the function succeeds or if there is no entry point, the return value is true.</returns> private bool CallDllEntryPoint(MEMORY_MODULE* memory_module, uint fdwReason) { uint addressOfEntryPoint; if (Environment.Is64BitProcess) { WinNT.IMAGE_NT_HEADERS64* headers = (WinNT.IMAGE_NT_HEADERS64*)memory_module->headers; addressOfEntryPoint = headers->OptionalHeader.AddressOfEntryPoint; } else { WinNT.IMAGE_NT_HEADERS32* headers = (WinNT.IMAGE_NT_HEADERS32*)memory_module->headers; addressOfEntryPoint = headers->OptionalHeader.AddressOfEntryPoint; } if (addressOfEntryPoint != 0) { IntPtr dllEntry = (IntPtr)(memory_module->codeBase + addressOfEntryPoint); if (dllEntry == IntPtr.Zero) { return false; } DllEntryProc dllEntryProc = (DllEntryProc)Marshal.GetDelegateForFunctionPointer(dllEntry, typeof(DllEntryProc)); if (dllEntryProc((IntPtr)memory_module->codeBase, fdwReason, 0)) { if (fdwReason == WinNT.DLL_PROCESS_ATTACH) { memory_module->initialized = 1; } else if (fdwReason == WinNT.DLL_PROCESS_DETACH) { memory_module->initialized = 0; } return true; } else { return false; } } return true; }
/// <summary> /// Loads required dlls and adjust function table of the imports. /// </summary> /// <param name="memory_module">Pointer to a memory module.</param> /// <returns>If the function succeeds, the return value is true.</returns> private bool BuildImportTable(MEMORY_MODULE* memory_module) { bool result = true; WinNT.IMAGE_DATA_DIRECTORY* directory = this.GET_HEADER_DIRECTORY(memory_module, WinNT.IMAGE_DIRECTORY_ENTRY_IMPORT); if (directory->Size > 0) { WinNT.IMAGE_IMPORT_DESCRIPTOR* importDesc = (WinNT.IMAGE_IMPORT_DESCRIPTOR*)(memory_module->codeBase + directory->VirtualAddress); for (; importDesc->Name != 0; importDesc++) { IntPtr* thunkRef; IntPtr* funcRef; string moduleName = Marshal.PtrToStringAnsi((IntPtr)(memory_module->codeBase + importDesc->Name)); IntPtr handle = WinBase.LoadLibrary(moduleName); if (handle == IntPtr.Zero) { result = false; break; } int size_of_pointer = sizeof(IntPtr); memory_module->modules = (IntPtr*)memory.realloc((byte*)memory_module->modules, (uint)((memory_module->numModules) * size_of_pointer), (uint)((memory_module->numModules + 1) * size_of_pointer)); if (memory_module->modules == null) { result = false; break; } memory_module->modules[memory_module->numModules++] = handle; if (importDesc->Characteristics != 0) { thunkRef = (IntPtr*)(memory_module->codeBase + importDesc->Characteristics); funcRef = (IntPtr*)(memory_module->codeBase + importDesc->FirstThunk); } else { thunkRef = (IntPtr*)(memory_module->codeBase + importDesc->FirstThunk); funcRef = (IntPtr*)(memory_module->codeBase + importDesc->FirstThunk); } for (; *thunkRef != IntPtr.Zero; thunkRef++, funcRef++) { if (WinNT.IMAGE_SNAP_BY_ORDINAL(thunkRef)) { *funcRef = WinBase.GetProcAddress(handle, (byte*)WinNT.IMAGE_ORDINAL(thunkRef)); } else { WinNT.IMAGE_IMPORT_BY_NAME* thunkData = (WinNT.IMAGE_IMPORT_BY_NAME*)(memory_module->codeBase + (ulong)*thunkRef); string procName = Marshal.PtrToStringAnsi((IntPtr)(byte*)(thunkData) + 2); *funcRef = WinBase.GetProcAddress(handle, procName); } if (*funcRef == IntPtr.Zero) { result = false; break; } } if (!result) break; } } return result; }