3 * Copyright (C) 2005 Filip Navara
5 * The purpose of this utility is fix PE binaries generated by binutils and
6 * to manipulate flags that can't be set by binutils.
8 * Currently two features are implemented:
10 * - Setting flags on PE sections for use by drivers. The sections
11 * .text, .data, .idata, .bss are marked as non-pageable and
12 * non-discarable, section PAGE is marked as pageable and section
13 * INIT is marked as discaradable.
15 * - Sorting of export name table in executables. DLLTOOL has bug
16 * in sorting algorithm when the --kill-at flag is used. The exports
17 * are sorted in the decorated form and so the fastcall symbols are
18 * incorrectly put at the beginning of export table. This option
19 * allow to correct sort the table, so binary search can be used
28 /* The following definitions are ripped from MinGW W32API headers. We don't
29 use these headers directly in order to allow compilation on Linux hosts. */
31 typedef unsigned char BYTE
;
32 typedef unsigned short WORD
;
33 typedef unsigned int DWORD
;
36 #define IMAGE_NUMBEROF_DIRECTORY_ENTRIES 16
37 #define IMAGE_SIZEOF_SHORT_NAME 8
38 #define IMAGE_DOS_SIGNATURE 0x5A4D
39 #define IMAGE_NT_SIGNATURE 0x00004550
40 #define IMAGE_SCN_MEM_DISCARDABLE 0x2000000
41 #define IMAGE_SCN_MEM_NOT_PAGED 0x8000000
42 #define FIELD_OFFSET(t,f) ((LONG)&(((t*)0)->f))
43 #define IMAGE_FIRST_SECTION(h) ((PIMAGE_SECTION_HEADER) ((DWORD)h+FIELD_OFFSET(IMAGE_NT_HEADERS,OptionalHeader)+((PIMAGE_NT_HEADERS)(h))->FileHeader.SizeOfOptionalHeader))
44 #define IMAGE_DIRECTORY_ENTRY_EXPORT 0
47 typedef struct _IMAGE_DOS_HEADER
{
67 } IMAGE_DOS_HEADER
,*PIMAGE_DOS_HEADER
;
70 typedef struct _IMAGE_EXPORT_DIRECTORY
{
71 DWORD Characteristics
;
77 DWORD NumberOfFunctions
;
79 DWORD AddressOfFunctions
;
81 DWORD AddressOfNameOrdinals
;
82 } IMAGE_EXPORT_DIRECTORY
,*PIMAGE_EXPORT_DIRECTORY
;
83 typedef struct _IMAGE_FILE_HEADER
{
85 WORD NumberOfSections
;
87 DWORD PointerToSymbolTable
;
88 DWORD NumberOfSymbols
;
89 WORD SizeOfOptionalHeader
;
91 } IMAGE_FILE_HEADER
, *PIMAGE_FILE_HEADER
;
92 typedef struct _IMAGE_DATA_DIRECTORY
{
95 } IMAGE_DATA_DIRECTORY
,*PIMAGE_DATA_DIRECTORY
;
96 typedef struct _IMAGE_OPTIONAL_HEADER
{
98 BYTE MajorLinkerVersion
;
99 BYTE MinorLinkerVersion
;
101 DWORD SizeOfInitializedData
;
102 DWORD SizeOfUninitializedData
;
103 DWORD AddressOfEntryPoint
;
107 DWORD SectionAlignment
;
109 WORD MajorOperatingSystemVersion
;
110 WORD MinorOperatingSystemVersion
;
111 WORD MajorImageVersion
;
112 WORD MinorImageVersion
;
113 WORD MajorSubsystemVersion
;
114 WORD MinorSubsystemVersion
;
120 WORD DllCharacteristics
;
121 DWORD SizeOfStackReserve
;
122 DWORD SizeOfStackCommit
;
123 DWORD SizeOfHeapReserve
;
124 DWORD SizeOfHeapCommit
;
126 DWORD NumberOfRvaAndSizes
;
127 IMAGE_DATA_DIRECTORY DataDirectory
[IMAGE_NUMBEROF_DIRECTORY_ENTRIES
];
128 } IMAGE_OPTIONAL_HEADER
,*PIMAGE_OPTIONAL_HEADER
;
129 typedef struct _IMAGE_NT_HEADERS
{
131 IMAGE_FILE_HEADER FileHeader
;
132 IMAGE_OPTIONAL_HEADER OptionalHeader
;
133 } IMAGE_NT_HEADERS
,*PIMAGE_NT_HEADERS
;
134 typedef struct _IMAGE_SECTION_HEADER
{
135 BYTE Name
[IMAGE_SIZEOF_SHORT_NAME
];
137 DWORD PhysicalAddress
;
140 DWORD VirtualAddress
;
142 DWORD PointerToRawData
;
143 DWORD PointerToRelocations
;
144 DWORD PointerToLinenumbers
;
145 WORD NumberOfRelocations
;
146 WORD NumberOfLinenumbers
;
147 DWORD Characteristics
;
148 } IMAGE_SECTION_HEADER
,*PIMAGE_SECTION_HEADER
;
151 /* End of ripped definitions */
153 typedef struct _export_t
{
158 unsigned char *buffer
;
159 PIMAGE_DOS_HEADER dos_header
;
160 PIMAGE_NT_HEADERS nt_header
;
162 void *rva_to_ptr(DWORD rva
)
164 PIMAGE_SECTION_HEADER section_header
;
167 for (i
= 0, section_header
= IMAGE_FIRST_SECTION(nt_header
);
168 i
< nt_header
->OptionalHeader
.NumberOfRvaAndSizes
;
169 i
++, section_header
++)
171 if (rva
>= section_header
->VirtualAddress
&&
172 rva
< section_header
->VirtualAddress
+
173 section_header
->Misc
.VirtualSize
)
175 return buffer
+ rva
- section_header
->VirtualAddress
+
176 section_header
->PointerToRawData
;
183 int export_compare_func(const void *a
, const void *b
)
185 const export_t
*ap
= a
;
186 const export_t
*bp
= b
;
187 char *an
= rva_to_ptr(ap
->name
);
188 char *bn
= rva_to_ptr(bp
->name
);
189 return strcmp(an
, bn
);
192 int main(int argc
, char **argv
)
196 PIMAGE_SECTION_HEADER section_header
;
197 PIMAGE_DATA_DIRECTORY data_dir
;
199 unsigned long checksum
;
200 int fixup_exports
= 0;
201 int fixup_sections
= 0;
204 * Process parameters.
209 printf("Usage: %s <filename> <options>\n"
211 " -sections Sets section flags for PE image.\n"
212 " -exports Sort the names in export table.\n",
217 for (i
= 2; i
< argc
; i
++)
219 if (!strcmp(argv
[i
], "-sections"))
221 else if (!strcmp(argv
[i
], "-exports"))
224 { printf("Invalid option: %s\n", argv
[i
]); return 1; }
227 if (fixup_sections
== 0 && fixup_exports
== 0)
229 printf("Nothing to do.\n");
234 * Read the whole file to memory.
237 fd_in
= open(argv
[1], O_RDONLY
| O_BINARY
);
240 printf("Can't open input file.\n");
244 len
= lseek(fd_in
, 0, SEEK_END
);
245 if (len
< sizeof(IMAGE_DOS_HEADER
))
248 printf("'%s' isn't a PE image.\n", argv
[1]);
252 buffer
= malloc((len
+ 1) & ~1);
256 printf("Not enough memory available.\n");
260 /* Read the whole input file into a buffer */
261 lseek(fd_in
, 0, SEEK_SET
);
262 read(fd_in
, buffer
, len
);
269 * Check the headers and save pointers to them.
272 dos_header
= (PIMAGE_DOS_HEADER
)buffer
;
273 nt_header
= (PIMAGE_NT_HEADERS
)(buffer
+ dos_header
->e_lfanew
);
275 if (dos_header
->e_magic
!= IMAGE_DOS_SIGNATURE
||
276 nt_header
->Signature
!= IMAGE_NT_SIGNATURE
)
278 printf("'%s' isn't a PE image.\n", argv
[1]);
285 /* Sort export directory */
286 data_dir
= &nt_header
->OptionalHeader
.DataDirectory
[IMAGE_DIRECTORY_ENTRY_EXPORT
];
287 if (data_dir
->Size
!= 0)
289 PIMAGE_EXPORT_DIRECTORY export_directory
;
294 export_directory
= (PIMAGE_EXPORT_DIRECTORY
)rva_to_ptr(data_dir
->VirtualAddress
);
295 if (export_directory
!= NULL
)
297 exports
= malloc(sizeof(export_t
) * export_directory
->NumberOfNames
);
300 printf("Not enough memory.\n");
305 name_ptr
= (DWORD
*)rva_to_ptr(export_directory
->AddressOfNames
);
306 ordinal_ptr
= (WORD
*)rva_to_ptr(export_directory
->AddressOfNameOrdinals
);
308 for (i
= 0; i
< export_directory
->NumberOfNames
; i
++)
310 exports
[i
].name
= name_ptr
[i
];
311 exports
[i
].ordinal
= ordinal_ptr
[i
];
314 qsort(exports
, export_directory
->NumberOfNames
, sizeof(export_t
),
315 export_compare_func
);
317 for (i
= 0; i
< export_directory
->NumberOfNames
; i
++)
319 name_ptr
[i
] = exports
[i
].name
;
320 ordinal_ptr
[i
] = exports
[i
].ordinal
;
330 /* Update section flags */
331 for (i
= 0, section_header
= IMAGE_FIRST_SECTION(nt_header
);
332 i
< nt_header
->OptionalHeader
.NumberOfRvaAndSizes
;
333 i
++, section_header
++)
335 if (!strcmp(section_header
->Name
, ".text") ||
336 !strcmp(section_header
->Name
, ".data") ||
337 !strcmp(section_header
->Name
, ".idata") ||
338 !strcmp(section_header
->Name
, ".bss"))
340 section_header
->Characteristics
|= IMAGE_SCN_MEM_NOT_PAGED
;
341 section_header
->Characteristics
&= ~IMAGE_SCN_MEM_DISCARDABLE
;
343 else if (!strcmp(section_header
->Name
, "INIT"))
345 section_header
->Characteristics
|= IMAGE_SCN_MEM_DISCARDABLE
;
347 else if (!strcmp(section_header
->Name
, "PAGE"))
349 section_header
->Characteristics
|= IMAGE_SCN_MEM_NOT_PAGED
;
354 /* Recalculate checksum */
355 nt_header
->OptionalHeader
.CheckSum
= 0;
357 for (i
= 0; i
< len
; i
+= 2)
359 checksum
+= *(unsigned short *)(buffer
+ i
);
360 checksum
= (checksum
+ (checksum
>> 16)) & 0xffff;
363 nt_header
->OptionalHeader
.CheckSum
= checksum
;
365 /* Write the output file */
366 fd_out
= open(argv
[1], O_WRONLY
| O_BINARY
);
367 write(fd_out
, buffer
, len
);