@@ -60,9 +60,19 @@ typedef struct {
60
60
CustomFreeLibraryFunc freeLibrary ;
61
61
void * userdata ;
62
62
ExeEntryProc exeEntry ;
63
+ DWORD pageSize ;
63
64
} MEMORYMODULE , * PMEMORYMODULE ;
64
65
66
+ typedef struct {
67
+ LPVOID address ;
68
+ LPVOID alignedAddress ;
69
+ DWORD size ;
70
+ DWORD characteristics ;
71
+ BOOL last ;
72
+ } SECTIONFINALIZEDATA , * PSECTIONFINALIZEDATA ;
73
+
65
74
#define GET_HEADER_DICTIONARY (module , idx ) &(module)->headers->OptionalHeader.DataDirectory[idx]
75
+ #define ALIGN_DOWN (address , alignment ) (LPVOID)((uintptr_t)(address) & ~((alignment) - 1))
66
76
67
77
#ifdef DEBUG_OUTPUT
68
78
static void
@@ -98,6 +108,9 @@ CopySections(const unsigned char *data, PIMAGE_NT_HEADERS old_headers, PMEMORYMO
98
108
MEM_COMMIT ,
99
109
PAGE_READWRITE );
100
110
111
+ // Always use position from file to support alignments smaller
112
+ // than page size.
113
+ dest = codeBase + section -> VirtualAddress ;
101
114
section -> Misc .PhysicalAddress = (DWORD ) (uintptr_t ) dest ;
102
115
memset (dest , 0 , size );
103
116
}
@@ -111,6 +124,10 @@ CopySections(const unsigned char *data, PIMAGE_NT_HEADERS old_headers, PMEMORYMO
111
124
section -> SizeOfRawData ,
112
125
MEM_COMMIT ,
113
126
PAGE_READWRITE );
127
+
128
+ // Always use position from file to support alignments smaller
129
+ // than page size.
130
+ dest = codeBase + section -> VirtualAddress ;
114
131
memcpy (dest , data + section -> PointerToRawData , section -> SizeOfRawData );
115
132
section -> Misc .PhysicalAddress = (DWORD ) (uintptr_t ) dest ;
116
133
}
@@ -129,6 +146,63 @@ static int ProtectionFlags[2][2][2] = {
129
146
},
130
147
};
131
148
149
+ static DWORD
150
+ GetRealSectionSize (PMEMORYMODULE module , PIMAGE_SECTION_HEADER section ) {
151
+ DWORD size = section -> SizeOfRawData ;
152
+ if (size == 0 ) {
153
+ if (section -> Characteristics & IMAGE_SCN_CNT_INITIALIZED_DATA ) {
154
+ size = module -> headers -> OptionalHeader .SizeOfInitializedData ;
155
+ } else if (section -> Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA ) {
156
+ size = module -> headers -> OptionalHeader .SizeOfUninitializedData ;
157
+ }
158
+ }
159
+ return size ;
160
+ }
161
+
162
+ static BOOL
163
+ FinalizeSection (PMEMORYMODULE module , PSECTIONFINALIZEDATA sectionData ) {
164
+ DWORD protect , oldProtect ;
165
+ int executable ;
166
+ int readable ;
167
+ int writeable ;
168
+
169
+ if (sectionData -> size == 0 ) {
170
+ return TRUE;
171
+ }
172
+
173
+ if (sectionData -> characteristics & IMAGE_SCN_MEM_DISCARDABLE ) {
174
+ // section is not needed any more and can safely be freed
175
+ if (sectionData -> address == sectionData -> alignedAddress &&
176
+ (sectionData -> last ||
177
+ module -> headers -> OptionalHeader .SectionAlignment == module -> pageSize ||
178
+ (sectionData -> size % module -> pageSize ) == 0 )
179
+ ) {
180
+ // Only allowed to decommit whole pages
181
+ VirtualFree (sectionData -> address , sectionData -> size , MEM_DECOMMIT );
182
+ }
183
+ return TRUE;
184
+ }
185
+
186
+ // determine protection flags based on characteristics
187
+ executable = (sectionData -> characteristics & IMAGE_SCN_MEM_EXECUTE ) != 0 ;
188
+ readable = (sectionData -> characteristics & IMAGE_SCN_MEM_READ ) != 0 ;
189
+ writeable = (sectionData -> characteristics & IMAGE_SCN_MEM_WRITE ) != 0 ;
190
+ protect = ProtectionFlags [executable ][readable ][writeable ];
191
+ if (sectionData -> characteristics & IMAGE_SCN_MEM_NOT_CACHED ) {
192
+ protect |= PAGE_NOCACHE ;
193
+ }
194
+
195
+ // change memory access flags
196
+ if (VirtualProtect (sectionData -> address , sectionData -> size , protect , & oldProtect ) == 0 ) {
197
+ #ifdef DEBUG_OUTPUT
198
+ OutputLastError ("Error protecting memory page" )
199
+ #endif
200
+ return FALSE;
201
+ }
202
+
203
+ return TRUE;
204
+ }
205
+
132
206
static void
133
207
FinalizeSections (PMEMORYMODULE module )
134
208
{
@@ -139,45 +213,41 @@ FinalizeSections(PMEMORYMODULE module)
139
213
#else
140
214
#define imageOffset 0
141
215
#endif
216
+ SECTIONFINALIZEDATA sectionData ;
217
+ sectionData .address = (LPVOID )((uintptr_t )section -> Misc .PhysicalAddress | imageOffset );
218
+ sectionData .alignedAddress = ALIGN_DOWN (sectionData .address , module -> pageSize );
219
+ sectionData .size = GetRealSectionSize (module , section );
220
+ sectionData .characteristics = section -> Characteristics ;
221
+ sectionData .last = FALSE;
222
+ section ++ ;
142
223
143
224
// loop through all sections and change access flags
144
- for (i = 0 ; i < module -> headers -> FileHeader .NumberOfSections ; i ++ , section ++ ) {
145
- DWORD protect , oldProtect , size ;
146
- int executable = (section -> Characteristics & IMAGE_SCN_MEM_EXECUTE ) != 0 ;
147
- int readable = (section -> Characteristics & IMAGE_SCN_MEM_READ ) != 0 ;
148
- int writeable = (section -> Characteristics & IMAGE_SCN_MEM_WRITE ) != 0 ;
149
-
150
- if (section -> Characteristics & IMAGE_SCN_MEM_DISCARDABLE ) {
151
- // section is not needed any more and can safely be freed
152
- VirtualFree ((LPVOID )((uintptr_t )section -> Misc .PhysicalAddress | imageOffset ), section -> SizeOfRawData , MEM_DECOMMIT );
153
- continue ;
154
- }
155
-
156
- // determine protection flags based on characteristics
157
- protect = ProtectionFlags [executable ][readable ][writeable ];
158
- if (section -> Characteristics & IMAGE_SCN_MEM_NOT_CACHED ) {
159
- protect |= PAGE_NOCACHE ;
160
- }
161
-
162
- // determine size of region
163
- size = section -> SizeOfRawData ;
164
- if (size == 0 ) {
165
- if (section -> Characteristics & IMAGE_SCN_CNT_INITIALIZED_DATA ) {
166
- size = module -> headers -> OptionalHeader .SizeOfInitializedData ;
167
- } else if (section -> Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA ) {
168
- size = module -> headers -> OptionalHeader .SizeOfUninitializedData ;
225
+ for (i = 1 ; i < module -> headers -> FileHeader .NumberOfSections ; i ++ , section ++ ) {
226
+ LPVOID sectionAddress = (LPVOID )((uintptr_t )section -> Misc .PhysicalAddress | imageOffset );
227
+ LPVOID alignedAddress = ALIGN_DOWN (sectionAddress , module -> pageSize );
228
+ DWORD sectionSize = GetRealSectionSize (module , section );
229
+ // Combine access flags of all sections that share a page
230
+ // TODO(fancycode): We currently share flags of a trailing large section
231
+ // with the page of a first small section. This should be optimized.
232
+ if (sectionData .alignedAddress == alignedAddress || (uintptr_t ) sectionData .address + sectionData .size > (uintptr_t ) alignedAddress ) {
233
+ // Section shares page with previous
234
+ if ((section -> Characteristics & IMAGE_SCN_MEM_DISCARDABLE ) == 0 || (sectionData .characteristics & IMAGE_SCN_MEM_DISCARDABLE ) == 0 ) {
235
+ sectionData .characteristics = (sectionData .characteristics | section -> Characteristics ) & ~IMAGE_SCN_MEM_DISCARDABLE ;
236
+ } else {
237
+ sectionData .characteristics |= section -> Characteristics ;
169
238
}
239
+ sectionData .size = (((uintptr_t )sectionAddress ) + sectionSize ) - (uintptr_t ) sectionData .address ;
240
+ continue ;
170
241
}
171
242
172
- if (size > 0 ) {
173
- // change memory access flags
174
- if (VirtualProtect ((LPVOID )((uintptr_t )section -> Misc .PhysicalAddress | imageOffset ), size , protect , & oldProtect ) == 0 )
175
- #ifdef DEBUG_OUTPUT
176
- OutputLastError ("Error protecting memory page" )
177
- #endif
178
- ;
179
- }
243
+ FinalizeSection (module , & sectionData );
244
+ sectionData .address = sectionAddress ;
245
+ sectionData .alignedAddress = alignedAddress ;
246
+ sectionData .size = sectionSize ;
247
+ sectionData .characteristics = section -> Characteristics ;
180
248
}
249
+ sectionData .last = TRUE;
250
+ FinalizeSection (module , & sectionData );
181
251
#ifndef _WIN64
182
252
#undef imageOffset
183
253
#endif
@@ -358,6 +428,7 @@ HMEMORYMODULE MemoryLoadLibraryEx(const void *data,
358
428
unsigned char * code , * headers ;
359
429
SIZE_T locationDelta ;
360
430
BOOL successfull ;
431
+ SYSTEM_INFO sysInfo ;
361
432
362
433
dos_header = (PIMAGE_DOS_HEADER )data ;
363
434
if (dos_header -> e_magic != IMAGE_DOS_SIGNATURE ) {
@@ -380,6 +451,12 @@ HMEMORYMODULE MemoryLoadLibraryEx(const void *data,
380
451
return NULL ;
381
452
}
382
453
454
+ if (old_header -> OptionalHeader .SectionAlignment & 1 ) {
455
+ // Only support section alignments that are a multiple of 2
456
+ SetLastError (ERROR_BAD_EXE_FORMAT );
457
+ return NULL ;
458
+ }
459
+
383
460
// reserve memory for image of library
384
461
// XXX: is it correct to commit the complete memory region at once?
385
462
// calling DllEntry raises an exception if we don't...
@@ -417,6 +494,9 @@ HMEMORYMODULE MemoryLoadLibraryEx(const void *data,
417
494
result -> freeLibrary = freeLibrary ;
418
495
result -> userdata = userdata ;
419
496
497
+ GetNativeSystemInfo (& sysInfo );
498
+ result -> pageSize = sysInfo .dwPageSize ;
499
+
420
500
// commit memory for headers
421
501
headers = (unsigned char * )VirtualAlloc (code ,
422
502
old_header -> OptionalHeader .SizeOfHeaders ,
0 commit comments