+/*\r
+ * PE Fixup Utility\r
+ * Copyright (C) 2005 Filip Navara\r
+ *\r
+ * The purpose of this utility is fix PE binaries generated by binutils and\r
+ * to manipulate flags that can't be set by binutils.\r
+ *\r
+ * Currently two features are implemented:\r
+ *\r
+ * - Setting flags on PE sections for use by drivers. The sections\r
+ * .text, .data, .idata, .bss are marked as non-pageable and\r
+ * non-discarable, section PAGE is marked as pageable and section\r
+ * INIT is marked as discaradable.\r
+ *\r
+ * - Sorting of export name table in executables. DLLTOOL has bug\r
+ * in sorting algorithm when the --kill-at flag is used. The exports\r
+ * are sorted in the decorated form and so the fastcall symbols are\r
+ * incorrectly put at the beginning of export table. This option\r
+ * allow to correct sort the table, so binary search can be used\r
+ * to process them.\r
+ */\r
+\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+#include <string.h>\r
+#include <fcntl.h>\r
+\r
+/* The following definitions are ripped from MinGW W32API headers. We don't\r
+ use these headers directly in order to allow compilation on Linux hosts. */\r
+\r
+typedef unsigned char BYTE;\r
+typedef unsigned short WORD;\r
+typedef unsigned int DWORD;\r
+typedef int LONG;\r
+\r
+#define IMAGE_NUMBEROF_DIRECTORY_ENTRIES 16\r
+#define IMAGE_SIZEOF_SHORT_NAME 8\r
+#define IMAGE_DOS_SIGNATURE 0x5A4D\r
+#define IMAGE_NT_SIGNATURE 0x00004550\r
+#define IMAGE_SCN_MEM_DISCARDABLE 0x2000000\r
+#define IMAGE_SCN_MEM_NOT_PAGED 0x8000000\r
+#define FIELD_OFFSET(t,f) ((LONG)&(((t*)0)->f))\r
+#define IMAGE_FIRST_SECTION(h) ((PIMAGE_SECTION_HEADER) ((DWORD)h+FIELD_OFFSET(IMAGE_NT_HEADERS,OptionalHeader)+((PIMAGE_NT_HEADERS)(h))->FileHeader.SizeOfOptionalHeader))\r
+#define IMAGE_DIRECTORY_ENTRY_EXPORT 0\r
+\r
+#pragma pack(push,2)\r
+typedef struct _IMAGE_DOS_HEADER {\r
+ WORD e_magic;\r
+ WORD e_cblp;\r
+ WORD e_cp;\r
+ WORD e_crlc;\r
+ WORD e_cparhdr;\r
+ WORD e_minalloc;\r
+ WORD e_maxalloc;\r
+ WORD e_ss;\r
+ WORD e_sp;\r
+ WORD e_csum;\r
+ WORD e_ip;\r
+ WORD e_cs;\r
+ WORD e_lfarlc;\r
+ WORD e_ovno;\r
+ WORD e_res[4];\r
+ WORD e_oemid;\r
+ WORD e_oeminfo;\r
+ WORD e_res2[10];\r
+ LONG e_lfanew;\r
+} IMAGE_DOS_HEADER,*PIMAGE_DOS_HEADER;\r
+#pragma pack(pop)\r
+#pragma pack(push,4)\r
+typedef struct _IMAGE_EXPORT_DIRECTORY {\r
+ DWORD Characteristics;\r
+ DWORD TimeDateStamp;\r
+ WORD MajorVersion;\r
+ WORD MinorVersion;\r
+ DWORD Name;\r
+ DWORD Base;\r
+ DWORD NumberOfFunctions;\r
+ DWORD NumberOfNames;\r
+ DWORD AddressOfFunctions;\r
+ DWORD AddressOfNames;\r
+ DWORD AddressOfNameOrdinals;\r
+} IMAGE_EXPORT_DIRECTORY,*PIMAGE_EXPORT_DIRECTORY;\r
+typedef struct _IMAGE_FILE_HEADER {\r
+ WORD Machine;\r
+ WORD NumberOfSections;\r
+ DWORD TimeDateStamp;\r
+ DWORD PointerToSymbolTable;\r
+ DWORD NumberOfSymbols;\r
+ WORD SizeOfOptionalHeader;\r
+ WORD Characteristics;\r
+} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;\r
+typedef struct _IMAGE_DATA_DIRECTORY {\r
+ DWORD VirtualAddress;\r
+ DWORD Size;\r
+} IMAGE_DATA_DIRECTORY,*PIMAGE_DATA_DIRECTORY;\r
+typedef struct _IMAGE_OPTIONAL_HEADER {\r
+ WORD Magic;\r
+ BYTE MajorLinkerVersion;\r
+ BYTE MinorLinkerVersion;\r
+ DWORD SizeOfCode;\r
+ DWORD SizeOfInitializedData;\r
+ DWORD SizeOfUninitializedData;\r
+ DWORD AddressOfEntryPoint;\r
+ DWORD BaseOfCode;\r
+ DWORD BaseOfData;\r
+ DWORD ImageBase;\r
+ DWORD SectionAlignment;\r
+ DWORD FileAlignment;\r
+ WORD MajorOperatingSystemVersion;\r
+ WORD MinorOperatingSystemVersion;\r
+ WORD MajorImageVersion;\r
+ WORD MinorImageVersion;\r
+ WORD MajorSubsystemVersion;\r
+ WORD MinorSubsystemVersion;\r
+ DWORD Reserved1;\r
+ DWORD SizeOfImage;\r
+ DWORD SizeOfHeaders;\r
+ DWORD CheckSum;\r
+ WORD Subsystem;\r
+ WORD DllCharacteristics;\r
+ DWORD SizeOfStackReserve;\r
+ DWORD SizeOfStackCommit;\r
+ DWORD SizeOfHeapReserve;\r
+ DWORD SizeOfHeapCommit;\r
+ DWORD LoaderFlags;\r
+ DWORD NumberOfRvaAndSizes;\r
+ IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];\r
+} IMAGE_OPTIONAL_HEADER,*PIMAGE_OPTIONAL_HEADER;\r
+typedef struct _IMAGE_NT_HEADERS {\r
+ DWORD Signature;\r
+ IMAGE_FILE_HEADER FileHeader;\r
+ IMAGE_OPTIONAL_HEADER OptionalHeader;\r
+} IMAGE_NT_HEADERS,*PIMAGE_NT_HEADERS;\r
+typedef struct _IMAGE_SECTION_HEADER {\r
+ BYTE Name[IMAGE_SIZEOF_SHORT_NAME];\r
+ union {\r
+ DWORD PhysicalAddress;\r
+ DWORD VirtualSize;\r
+ } Misc;\r
+ DWORD VirtualAddress;\r
+ DWORD SizeOfRawData;\r
+ DWORD PointerToRawData;\r
+ DWORD PointerToRelocations;\r
+ DWORD PointerToLinenumbers;\r
+ WORD NumberOfRelocations;\r
+ WORD NumberOfLinenumbers;\r
+ DWORD Characteristics;\r
+} IMAGE_SECTION_HEADER,*PIMAGE_SECTION_HEADER;\r
+#pragma pack(pop)\r
+\r
+/* End of ripped definitions */\r
+\r
+typedef struct _export_t {\r
+ DWORD name;\r
+ WORD ordinal;\r
+} export_t;\r
+\r
+unsigned char *buffer;\r
+PIMAGE_DOS_HEADER dos_header;\r
+PIMAGE_NT_HEADERS nt_header;\r
+\r
+void *rva_to_ptr(DWORD rva)\r
+{\r
+ PIMAGE_SECTION_HEADER section_header;\r
+ unsigned int i;\r
+\r
+ for (i = 0, section_header = IMAGE_FIRST_SECTION(nt_header);\r
+ i < nt_header->OptionalHeader.NumberOfRvaAndSizes;\r
+ i++, section_header++)\r
+ {\r
+ if (rva >= section_header->VirtualAddress &&\r
+ rva < section_header->VirtualAddress +\r
+ section_header->Misc.VirtualSize)\r
+ {\r
+ return buffer + rva - section_header->VirtualAddress +\r
+ section_header->PointerToRawData;\r
+ }\r
+ }\r
+\r
+ return NULL;\r
+}\r
+\r
+int export_compare_func(const void *a, const void *b)\r
+{\r
+ const export_t *ap = a;\r
+ const export_t *bp = b;\r
+ char *an = rva_to_ptr(ap->name);\r
+ char *bn = rva_to_ptr(bp->name);\r
+ return strcmp(an, bn);\r
+}\r
+\r
+int main(int argc, char **argv)\r
+{\r
+ int fd_in, fd_out;\r
+ long len;\r
+ PIMAGE_SECTION_HEADER section_header;\r
+ PIMAGE_DATA_DIRECTORY data_dir;\r
+ unsigned int i;\r
+ unsigned long checksum;\r
+ int fixup_exports = 0;\r
+ int fixup_sections = 0;\r
+\r
+ /*\r
+ * Process parameters.\r
+ */\r
+\r
+ if (argc < 2)\r
+ {\r
+ printf("Usage: %s <filename> <options>\n"\r
+ "Options:\n"\r
+ " -sections Sets section flags for PE image.\n"\r
+ " -exports Sort the names in export table.\n",\r
+ argv[0]);\r
+ return 1;\r
+ }\r
+\r
+ for (i = 2; i < argc; i++)\r
+ {\r
+ if (!strcmp(argv[i], "-sections"))\r
+ fixup_sections = 1;\r
+ else if (!strcmp(argv[i], "-exports"))\r
+ fixup_exports = 1;\r
+ else\r
+ { printf("Invalid option: %s\n", argv[i]); return 1; }\r
+ }\r
+\r
+ if (fixup_sections == 0 && fixup_exports == 0)\r
+ {\r
+ printf("Nothing to do.\n");\r
+ return 0;\r
+ }\r
+\r
+ /*\r
+ * Read the whole file to memory.\r
+ */\r
+\r
+ fd_in = open(argv[1], O_RDONLY | O_BINARY);\r
+ if (fd_in == 0)\r
+ {\r
+ printf("Can't open input file.\n");\r
+ return 1;\r
+ }\r
+\r
+ len = lseek(fd_in, 0, SEEK_END);\r
+ if (len < sizeof(IMAGE_DOS_HEADER))\r
+ {\r
+ close(fd_in);\r
+ printf("'%s' isn't a PE image.\n", argv[1]);\r
+ return 1;\r
+ }\r
+\r
+ buffer = malloc((len + 1) & ~1);\r
+ if (buffer == NULL)\r
+ {\r
+ close(fd_in);\r
+ printf("Not enough memory available.\n");\r
+ return 1;\r
+ }\r
+\r
+ /* Read the whole input file into a buffer */\r
+ lseek(fd_in, 0, SEEK_SET);\r
+ read(fd_in, buffer, len);\r
+ if (len & 1)\r
+ buffer[len] = 0;\r
+\r
+ close(fd_in);\r
+\r
+ /*\r
+ * Check the headers and save pointers to them.\r
+ */\r
+\r
+ dos_header = (PIMAGE_DOS_HEADER)buffer;\r
+ nt_header = (PIMAGE_NT_HEADERS)(buffer + dos_header->e_lfanew);\r
+ \r
+ if (dos_header->e_magic != IMAGE_DOS_SIGNATURE ||\r
+ nt_header->Signature != IMAGE_NT_SIGNATURE)\r
+ {\r
+ printf("'%s' isn't a PE image.\n", argv[1]);\r
+ free(buffer);\r
+ return 1;\r
+ }\r
+\r
+ if (fixup_exports)\r
+ {\r
+ /* Sort export directory */\r
+ data_dir = &nt_header->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];\r
+ if (data_dir->Size != 0)\r
+ {\r
+ PIMAGE_EXPORT_DIRECTORY export_directory;\r
+ DWORD *name_ptr;\r
+ WORD *ordinal_ptr;\r
+ export_t *exports;\r
+\r
+ export_directory = (PIMAGE_EXPORT_DIRECTORY)rva_to_ptr(data_dir->VirtualAddress);\r
+ if (export_directory != NULL)\r
+ {\r
+ exports = malloc(sizeof(export_t) * export_directory->NumberOfNames);\r
+ if (exports == NULL)\r
+ {\r
+ printf("Not enough memory.\n");\r
+ free(buffer);\r
+ return 1;\r
+ }\r
+\r
+ name_ptr = (DWORD *)rva_to_ptr(export_directory->AddressOfNames);\r
+ ordinal_ptr = (WORD *)rva_to_ptr(export_directory->AddressOfNameOrdinals);\r
+\r
+ for (i = 0; i < export_directory->NumberOfNames; i++)\r
+ {\r
+ exports[i].name = name_ptr[i];\r
+ exports[i].ordinal = ordinal_ptr[i];\r
+ }\r
+\r
+ qsort(exports, export_directory->NumberOfNames, sizeof(export_t),\r
+ export_compare_func);\r
+\r
+ for (i = 0; i < export_directory->NumberOfNames; i++)\r
+ {\r
+ name_ptr[i] = exports[i].name;\r
+ ordinal_ptr[i] = exports[i].ordinal;\r
+ }\r
+\r
+ free(exports);\r
+ }\r
+ }\r
+ }\r
+\r
+ if (fixup_sections)\r
+ {\r
+ /* Update section flags */\r
+ for (i = 0, section_header = IMAGE_FIRST_SECTION(nt_header);\r
+ i < nt_header->OptionalHeader.NumberOfRvaAndSizes;\r
+ i++, section_header++)\r
+ {\r
+ if (section_header->VirtualAddress)\r
+ break;\r
+\r
+ if (!strcmp(section_header->Name, ".text") ||\r
+ !strcmp(section_header->Name, ".data") ||\r
+ !strcmp(section_header->Name, ".idata") ||\r
+ !strcmp(section_header->Name, ".bss"))\r
+ {\r
+ section_header->Characteristics |= IMAGE_SCN_MEM_NOT_PAGED;\r
+ section_header->Characteristics &= ~IMAGE_SCN_MEM_DISCARDABLE;\r
+ }\r
+ else if (!strcmp(section_header->Name, "INIT"))\r
+ {\r
+ section_header->Characteristics |= IMAGE_SCN_MEM_DISCARDABLE;\r
+ }\r
+ else if (!strcmp(section_header->Name, "PAGE"))\r
+ {\r
+ section_header->Characteristics |= IMAGE_SCN_MEM_NOT_PAGED;\r
+ }\r
+ }\r
+ }\r
+\r
+ /* Recalculate checksum */\r
+ nt_header->OptionalHeader.CheckSum = 0;\r
+ checksum = 0;\r
+ for (i = 0; i < len; i += 2)\r
+ {\r
+ checksum += *(unsigned short *)(buffer + i);\r
+ checksum = (checksum + (checksum >> 16)) & 0xffff;\r
+ }\r
+ checksum += len;\r
+ nt_header->OptionalHeader.CheckSum = checksum;\r
+\r
+ /* Write the output file */\r
+ fd_out = open(argv[1], O_WRONLY | O_BINARY);\r
+ write(fd_out, buffer, len);\r
+ close(fd_out);\r
+\r
+ return 0;\r
+}\r