mirror of
https://github.com/fancycode/MemoryModule
synced 2026-06-06 15:44:28 +00:00
Support section alignments that are smaller than the memory page size (fixes #20/#21).
This commit is contained in:
+113
-33
@@ -60,9 +60,19 @@ typedef struct {
|
|||||||
CustomFreeLibraryFunc freeLibrary;
|
CustomFreeLibraryFunc freeLibrary;
|
||||||
void *userdata;
|
void *userdata;
|
||||||
ExeEntryProc exeEntry;
|
ExeEntryProc exeEntry;
|
||||||
|
DWORD pageSize;
|
||||||
} MEMORYMODULE, *PMEMORYMODULE;
|
} MEMORYMODULE, *PMEMORYMODULE;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
LPVOID address;
|
||||||
|
LPVOID alignedAddress;
|
||||||
|
DWORD size;
|
||||||
|
DWORD characteristics;
|
||||||
|
BOOL last;
|
||||||
|
} SECTIONFINALIZEDATA, *PSECTIONFINALIZEDATA;
|
||||||
|
|
||||||
#define GET_HEADER_DICTIONARY(module, idx) &(module)->headers->OptionalHeader.DataDirectory[idx]
|
#define GET_HEADER_DICTIONARY(module, idx) &(module)->headers->OptionalHeader.DataDirectory[idx]
|
||||||
|
#define ALIGN_DOWN(address, alignment) (LPVOID)((uintptr_t)(address) & ~((alignment) - 1))
|
||||||
|
|
||||||
#ifdef DEBUG_OUTPUT
|
#ifdef DEBUG_OUTPUT
|
||||||
static void
|
static void
|
||||||
@@ -98,6 +108,9 @@ CopySections(const unsigned char *data, PIMAGE_NT_HEADERS old_headers, PMEMORYMO
|
|||||||
MEM_COMMIT,
|
MEM_COMMIT,
|
||||||
PAGE_READWRITE);
|
PAGE_READWRITE);
|
||||||
|
|
||||||
|
// Always use position from file to support alignments smaller
|
||||||
|
// than page size.
|
||||||
|
dest = codeBase + section->VirtualAddress;
|
||||||
section->Misc.PhysicalAddress = (DWORD) (uintptr_t) dest;
|
section->Misc.PhysicalAddress = (DWORD) (uintptr_t) dest;
|
||||||
memset(dest, 0, size);
|
memset(dest, 0, size);
|
||||||
}
|
}
|
||||||
@@ -111,6 +124,10 @@ CopySections(const unsigned char *data, PIMAGE_NT_HEADERS old_headers, PMEMORYMO
|
|||||||
section->SizeOfRawData,
|
section->SizeOfRawData,
|
||||||
MEM_COMMIT,
|
MEM_COMMIT,
|
||||||
PAGE_READWRITE);
|
PAGE_READWRITE);
|
||||||
|
|
||||||
|
// Always use position from file to support alignments smaller
|
||||||
|
// than page size.
|
||||||
|
dest = codeBase + section->VirtualAddress;
|
||||||
memcpy(dest, data + section->PointerToRawData, section->SizeOfRawData);
|
memcpy(dest, data + section->PointerToRawData, section->SizeOfRawData);
|
||||||
section->Misc.PhysicalAddress = (DWORD) (uintptr_t) dest;
|
section->Misc.PhysicalAddress = (DWORD) (uintptr_t) dest;
|
||||||
}
|
}
|
||||||
@@ -129,6 +146,63 @@ static int ProtectionFlags[2][2][2] = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static DWORD
|
||||||
|
GetRealSectionSize(PMEMORYMODULE module, PIMAGE_SECTION_HEADER section) {
|
||||||
|
DWORD size = section->SizeOfRawData;
|
||||||
|
if (size == 0) {
|
||||||
|
if (section->Characteristics & IMAGE_SCN_CNT_INITIALIZED_DATA) {
|
||||||
|
size = module->headers->OptionalHeader.SizeOfInitializedData;
|
||||||
|
} else if (section->Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA) {
|
||||||
|
size = module->headers->OptionalHeader.SizeOfUninitializedData;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
static BOOL
|
||||||
|
FinalizeSection(PMEMORYMODULE module, PSECTIONFINALIZEDATA sectionData) {
|
||||||
|
DWORD protect, oldProtect;
|
||||||
|
int executable;
|
||||||
|
int readable;
|
||||||
|
int writeable;
|
||||||
|
|
||||||
|
if (sectionData->size == 0) {
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sectionData->characteristics & IMAGE_SCN_MEM_DISCARDABLE) {
|
||||||
|
// section is not needed any more and can safely be freed
|
||||||
|
if (sectionData->address == sectionData->alignedAddress &&
|
||||||
|
(sectionData->last ||
|
||||||
|
module->headers->OptionalHeader.SectionAlignment == module->pageSize ||
|
||||||
|
(sectionData->size % module->pageSize) == 0)
|
||||||
|
) {
|
||||||
|
// Only allowed to decommit whole pages
|
||||||
|
VirtualFree(sectionData->address, sectionData->size, MEM_DECOMMIT);
|
||||||
|
}
|
||||||
|
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][readable][writeable];
|
||||||
|
if (sectionData->characteristics & IMAGE_SCN_MEM_NOT_CACHED) {
|
||||||
|
protect |= PAGE_NOCACHE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// change memory access flags
|
||||||
|
if (VirtualProtect(sectionData->address, sectionData->size, protect, &oldProtect) == 0) {
|
||||||
|
#ifdef DEBUG_OUTPUT
|
||||||
|
OutputLastError("Error protecting memory page")
|
||||||
|
#endif
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
FinalizeSections(PMEMORYMODULE module)
|
FinalizeSections(PMEMORYMODULE module)
|
||||||
{
|
{
|
||||||
@@ -139,45 +213,41 @@ FinalizeSections(PMEMORYMODULE module)
|
|||||||
#else
|
#else
|
||||||
#define imageOffset 0
|
#define imageOffset 0
|
||||||
#endif
|
#endif
|
||||||
|
SECTIONFINALIZEDATA sectionData;
|
||||||
|
sectionData.address = (LPVOID)((uintptr_t)section->Misc.PhysicalAddress | imageOffset);
|
||||||
|
sectionData.alignedAddress = ALIGN_DOWN(sectionData.address, module->pageSize);
|
||||||
|
sectionData.size = GetRealSectionSize(module, section);
|
||||||
|
sectionData.characteristics = section->Characteristics;
|
||||||
|
sectionData.last = FALSE;
|
||||||
|
section++;
|
||||||
|
|
||||||
// loop through all sections and change access flags
|
// loop through all sections and change access flags
|
||||||
for (i=0; i<module->headers->FileHeader.NumberOfSections; i++, section++) {
|
for (i=1; i<module->headers->FileHeader.NumberOfSections; i++, section++) {
|
||||||
DWORD protect, oldProtect, size;
|
LPVOID sectionAddress = (LPVOID)((uintptr_t)section->Misc.PhysicalAddress | imageOffset);
|
||||||
int executable = (section->Characteristics & IMAGE_SCN_MEM_EXECUTE) != 0;
|
LPVOID alignedAddress = ALIGN_DOWN(sectionAddress, module->pageSize);
|
||||||
int readable = (section->Characteristics & IMAGE_SCN_MEM_READ) != 0;
|
DWORD sectionSize = GetRealSectionSize(module, section);
|
||||||
int writeable = (section->Characteristics & IMAGE_SCN_MEM_WRITE) != 0;
|
// Combine access flags of all sections that share a page
|
||||||
|
// TODO(fancycode): We currently share flags of a trailing large section
|
||||||
if (section->Characteristics & IMAGE_SCN_MEM_DISCARDABLE) {
|
// with the page of a first small section. This should be optimized.
|
||||||
// section is not needed any more and can safely be freed
|
if (sectionData.alignedAddress == alignedAddress || (uintptr_t) sectionData.address + sectionData.size > (uintptr_t) alignedAddress) {
|
||||||
VirtualFree((LPVOID)((uintptr_t)section->Misc.PhysicalAddress | imageOffset), section->SizeOfRawData, MEM_DECOMMIT);
|
// Section shares page with previous
|
||||||
|
if ((section->Characteristics & IMAGE_SCN_MEM_DISCARDABLE) == 0 || (sectionData.characteristics & IMAGE_SCN_MEM_DISCARDABLE) == 0) {
|
||||||
|
sectionData.characteristics = (sectionData.characteristics | section->Characteristics) & ~IMAGE_SCN_MEM_DISCARDABLE;
|
||||||
|
} else {
|
||||||
|
sectionData.characteristics |= section->Characteristics;
|
||||||
|
}
|
||||||
|
sectionData.size = (((uintptr_t)sectionAddress) + sectionSize) - (uintptr_t) sectionData.address;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// determine protection flags based on characteristics
|
FinalizeSection(module, §ionData);
|
||||||
protect = ProtectionFlags[executable][readable][writeable];
|
sectionData.address = sectionAddress;
|
||||||
if (section->Characteristics & IMAGE_SCN_MEM_NOT_CACHED) {
|
sectionData.alignedAddress = alignedAddress;
|
||||||
protect |= PAGE_NOCACHE;
|
sectionData.size = sectionSize;
|
||||||
}
|
sectionData.characteristics = section->Characteristics;
|
||||||
|
|
||||||
// determine size of region
|
|
||||||
size = section->SizeOfRawData;
|
|
||||||
if (size == 0) {
|
|
||||||
if (section->Characteristics & IMAGE_SCN_CNT_INITIALIZED_DATA) {
|
|
||||||
size = module->headers->OptionalHeader.SizeOfInitializedData;
|
|
||||||
} else if (section->Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA) {
|
|
||||||
size = module->headers->OptionalHeader.SizeOfUninitializedData;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (size > 0) {
|
|
||||||
// change memory access flags
|
|
||||||
if (VirtualProtect((LPVOID)((uintptr_t)section->Misc.PhysicalAddress | imageOffset), size, protect, &oldProtect) == 0)
|
|
||||||
#ifdef DEBUG_OUTPUT
|
|
||||||
OutputLastError("Error protecting memory page")
|
|
||||||
#endif
|
|
||||||
;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
sectionData.last = TRUE;
|
||||||
|
FinalizeSection(module, §ionData);
|
||||||
#ifndef _WIN64
|
#ifndef _WIN64
|
||||||
#undef imageOffset
|
#undef imageOffset
|
||||||
#endif
|
#endif
|
||||||
@@ -358,6 +428,7 @@ HMEMORYMODULE MemoryLoadLibraryEx(const void *data,
|
|||||||
unsigned char *code, *headers;
|
unsigned char *code, *headers;
|
||||||
SIZE_T locationDelta;
|
SIZE_T locationDelta;
|
||||||
BOOL successfull;
|
BOOL successfull;
|
||||||
|
SYSTEM_INFO sysInfo;
|
||||||
|
|
||||||
dos_header = (PIMAGE_DOS_HEADER)data;
|
dos_header = (PIMAGE_DOS_HEADER)data;
|
||||||
if (dos_header->e_magic != IMAGE_DOS_SIGNATURE) {
|
if (dos_header->e_magic != IMAGE_DOS_SIGNATURE) {
|
||||||
@@ -380,6 +451,12 @@ HMEMORYMODULE MemoryLoadLibraryEx(const void *data,
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (old_header->OptionalHeader.SectionAlignment & 1) {
|
||||||
|
// Only support section alignments that are a multiple of 2
|
||||||
|
SetLastError(ERROR_BAD_EXE_FORMAT);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
// reserve memory for image of library
|
// reserve memory for image of library
|
||||||
// XXX: is it correct to commit the complete memory region at once?
|
// XXX: is it correct to commit the complete memory region at once?
|
||||||
// calling DllEntry raises an exception if we don't...
|
// calling DllEntry raises an exception if we don't...
|
||||||
@@ -417,6 +494,9 @@ HMEMORYMODULE MemoryLoadLibraryEx(const void *data,
|
|||||||
result->freeLibrary = freeLibrary;
|
result->freeLibrary = freeLibrary;
|
||||||
result->userdata = userdata;
|
result->userdata = userdata;
|
||||||
|
|
||||||
|
GetNativeSystemInfo(&sysInfo);
|
||||||
|
result->pageSize = sysInfo.dwPageSize;
|
||||||
|
|
||||||
// commit memory for headers
|
// commit memory for headers
|
||||||
headers = (unsigned char *)VirtualAlloc(code,
|
headers = (unsigned char *)VirtualAlloc(code,
|
||||||
old_header->OptionalHeader.SizeOfHeaders,
|
old_header->OptionalHeader.SizeOfHeaders,
|
||||||
|
|||||||
Reference in New Issue
Block a user