/* vim :set ts=4 sw=4 sts=4 et : */ /* pev - the PE file analyzer toolkit readpe.c - show PE file headers Copyright (C) 2013 - 2020 pev authors This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . In addition, as a special exception, the copyright holders give permission to link the code of portions of this program with the OpenSSL library under certain conditions as described in each individual source file, and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than OpenSSL. If you modify file(s) with this exception, you may extend this exception to your version of the file(s), but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. If you delete this exception statement from all source files in the program, then also delete it here. */ #include "common.h" #include "output.h" #include #include #define PROGRAM "readpe" typedef struct { bool all; bool dos; bool coff; bool opt; bool dirs; bool imports; bool exports; bool all_headers; bool all_sections; } options_t; static void usage(void) { static char formats[255]; output_available_formats(formats, sizeof(formats), '|'); printf("Usage: %s OPTIONS FILE\n" "Show PE file headers\n" "\nExample: %s --header optional winzip.exe\n" "\nOptions:\n" " -A, --all Full output (default).\n" " -H, --all-headers Show all PE headers.\n" " -S, --all-sections Show PE section headers.\n" " -f, --format <%s> Change output format (default: text).\n" " -d, --dirs Show data directories.\n" " -h, --header Show specific header. It can be used multiple times.\n" " -i, --imports Show imported functions.\n" " -e, --exports Show exported functions.\n" " -V, --version Show version.\n" " --help Show this help.\n", PROGRAM, PROGRAM, formats); } static void parse_headers(options_t *options, const char *optarg) { if (!strcmp(optarg, "dos")) options->dos = true; else if (!strcmp(optarg, "coff")) options->coff = true; else if (!strcmp(optarg, "optional")) options->opt = true; else EXIT_ERROR("invalid header option"); } static void free_options(options_t *options) { // FIX: Don't need to test for NULL pointer. //if (options == NULL) // return; free(options); } static options_t *parse_options(int argc, char *argv[]) { options_t *options = calloc_s(1, sizeof(options_t)); /* Parameters for getopt_long() function */ static const char short_options[] = "AHSh:dief:V"; static const struct option long_options[] = { { "help", no_argument, NULL, 1 }, { "all", no_argument, NULL, 'A' }, { "all-headers", no_argument, NULL, 'H' }, { "all-sections", no_argument, NULL, 'S' }, { "header", required_argument, NULL, 'h' }, { "imports", no_argument, NULL, 'i' }, { "exports", no_argument, NULL, 'e' }, { "dirs", no_argument, NULL, 'd' }, { "format", required_argument, NULL, 'f' }, { "version", no_argument, NULL, 'V' }, { NULL, 0, NULL, 0 } }; options->all = true; int c, ind; while ((c = getopt_long(argc, argv, short_options, long_options, &ind))) { if (c < 0) break; switch (c) { case 1: // --help option usage(); exit(EXIT_SUCCESS); case 'A': options->all = true; break; case 'H': options->all = false; options->all_headers = true; break; case 'd': options->all = false; options->dirs = true; break; case 'S': options->all = false; options->all_sections = true; break; case 'V': printf("%s %s\n%s\n", PROGRAM, TOOLKIT, COPY); exit(EXIT_SUCCESS); case 'h': options->all = false; parse_headers(options, optarg); break; case 'i': options->all = false; options->imports = true; break; case 'e': options->all = false; options->exports = true; break; case 'f': if (output_set_format_by_name(optarg) < 0) EXIT_ERROR("invalid format option"); break; default: fprintf(stderr, "%s: try '--help' for more information\n", PROGRAM); exit(EXIT_FAILURE); } } return options; } static void print_sections(pe_ctx_t *ctx) { #ifdef LIBPE_ENABLE_OUTPUT_COMPAT_WITH_V06 static const char * const flags_name[] = { "contains executable code", "contains initialized data", "contains uninitialized data", "contains data referenced through the GP", "contains extended relocations", "can be discarded as needed", "cannot be cached", "is not pageable", "can be shared in memory", "is executable", "is readable", "is writable" }; // valid flags only for executables referenced in pecoffv8 static const unsigned int valid_flags[] = { IMAGE_SCN_CNT_CODE, IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_CNT_UNINITIALIZED_DATA, IMAGE_SCN_GPREL, IMAGE_SCN_LNK_NRELOC_OVFL, IMAGE_SCN_MEM_DISCARDABLE, IMAGE_SCN_MEM_NOT_CACHED, IMAGE_SCN_MEM_NOT_PAGED, IMAGE_SCN_MEM_SHARED, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_WRITE }; static const size_t max_flags = LIBPE_SIZEOF_ARRAY(valid_flags); #endif output_open_scope("Sections", OUTPUT_SCOPE_TYPE_ARRAY); const uint32_t num_sections = pe_sections_count(ctx); if (num_sections == 0 || num_sections > MAX_SECTIONS) return; IMAGE_SECTION_HEADER **sections = pe_sections(ctx); if (sections == NULL) return; static char s[MAX_MSG]; static char section_name_buffer[SECTION_NAME_SIZE+1]; for (uint32_t i=0; i < num_sections; i++) { output_open_scope("Section", OUTPUT_SCOPE_TYPE_OBJECT); const char *section_name = pe_section_name(ctx, sections[i], section_name_buffer, sizeof(section_name_buffer)); output("Name", section_name); snprintf(s, MAX_MSG, "%#x (%" PRIu32 " bytes)", sections[i]->Misc.VirtualSize, sections[i]->Misc.VirtualSize); output("Virtual Size", s); snprintf(s, MAX_MSG, "%#x", sections[i]->VirtualAddress); output("Virtual Address", s); snprintf(s, MAX_MSG, "%#x (%" PRIu32 " bytes)", sections[i]->SizeOfRawData, sections[i]->SizeOfRawData); output("Size Of Raw Data", s); snprintf(s, MAX_MSG, "%#x", sections[i]->PointerToRawData); output("Pointer To Raw Data", s); snprintf(s, MAX_MSG, "%" PRIu16, sections[i]->NumberOfRelocations); output("Number Of Relocations", s); snprintf(s, MAX_MSG, "%#x", sections[i]->Characteristics); output("Characteristics", s); output_open_scope("Characteristic Names", OUTPUT_SCOPE_TYPE_ARRAY); #ifdef LIBPE_ENABLE_OUTPUT_COMPAT_WITH_V06 for (size_t j=0; j < max_flags; j++) { if (sections[i]->Characteristics & valid_flags[j]) { snprintf(s, MAX_MSG, "%s", flags_name[j]); output(NULL, s); } } #else if (pe_use_rom_section_characteristic(ctx)) { for (unsigned int flag = 1; flag != 0; flag <<= 1) { if (sections[i]->Characteristics & flag) { const char *characteristic_name = pe_rom_section_characteristic_name(flag); char formatted_characteristic_name[32]; if (characteristic_name == NULL) { snprintf(formatted_characteristic_name, sizeof(formatted_characteristic_name)-1, "UNKNOWN[%#x]", flag); characteristic_name = formatted_characteristic_name; } output(NULL, characteristic_name); } } } else { for (unsigned int flag = 1; flag != 0; flag <<= 1) { if (flag & 0x00F00000) continue; if (sections[i]->Characteristics & flag) { const char *characteristic_name = NULL; char formatted_characteristic_name[32]; if (pe_coff(ctx)->Machine == IMAGE_FILE_MACHINE_M68K) characteristic_name = pe_m68k_section_characteristic_name(flag); if (characteristic_name == NULL) characteristic_name = pe_section_characteristic_name(flag); if (characteristic_name == NULL) { snprintf(formatted_characteristic_name, sizeof(formatted_characteristic_name)-1, "UNKNOWN[%#x]", flag); characteristic_name = formatted_characteristic_name; } output(NULL, characteristic_name); } } if (sections[i]->Characteristics & 0x00F00000) { unsigned int flag = sections[i]->Characteristics & 0x00F00000; const char *characteristic_name = pe_section_characteristic_name(flag); char formatted_characteristic_name[32]; if (characteristic_name == NULL) { snprintf(formatted_characteristic_name, sizeof(formatted_characteristic_name)-1, "UNKNOWN[%#x]", flag); characteristic_name = formatted_characteristic_name; } output(NULL, characteristic_name); } } #endif output_close_scope(); // Characteristic Names output_close_scope(); // Section } output_close_scope(); // Sections } static void print_directories(pe_ctx_t *ctx) { #ifdef LIBPE_ENABLE_OUTPUT_COMPAT_WITH_V06 typedef struct { ImageDirectoryEntry entry; const char * const name; } ImageDirectoryEntryName; static const ImageDirectoryEntryName directoryEntryNames[] = { { IMAGE_DIRECTORY_ENTRY_EXPORT, "Export Table" }, // "Export directory", { IMAGE_DIRECTORY_ENTRY_IMPORT, "Import Table" }, // "Import directory", { IMAGE_DIRECTORY_ENTRY_RESOURCE, "Resource Table" }, // "Resource directory", { IMAGE_DIRECTORY_ENTRY_EXCEPTION, "Exception Table" }, // "Exception directory", { IMAGE_DIRECTORY_ENTRY_SECURITY, "Certificate Table" }, // "Security directory", { IMAGE_DIRECTORY_ENTRY_BASERELOC, "Base Relocation Table" }, // "Base relocation table", { IMAGE_DIRECTORY_ENTRY_DEBUG, "Debug" }, // "Debug directory", { IMAGE_DIRECTORY_ENTRY_ARCHITECTURE, "Architecture" }, // "Architecture-specific data", { IMAGE_DIRECTORY_ENTRY_GLOBALPTR, "Global Ptr" }, // "Global pointer", { IMAGE_DIRECTORY_ENTRY_TLS, "Thread Local Storage (TLS)"}, // "Thread local storage (TLS) directory", { IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG, "Load Config Table" }, // "Load configuration directory", { IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT, "Bound Import" }, // "Bound import directory", { IMAGE_DIRECTORY_ENTRY_IAT, "Import Address Table (IAT)"}, // "Import address table (IAT)", { IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT, "Delay Import Descriptor" }, // "Delay import table", { IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR, "CLR Runtime Header" }, // "COM descriptor table" { IMAGE_DIRECTORY_RESERVED, "" } // "Reserved" }; //static const size_t max_directory_entry = LIBPE_SIZEOF_ARRAY(names); #endif output_open_scope("Data directories", OUTPUT_SCOPE_TYPE_ARRAY); const uint32_t num_directories = pe_directories_count(ctx); if (num_directories == 0 || num_directories > MAX_DIRECTORIES) return; IMAGE_DATA_DIRECTORY **directories = pe_directories(ctx); if (directories == NULL) return; static char s[MAX_MSG]; for (uint32_t i=0; i < num_directories; i++) { if (directories[i]->Size) { output_open_scope("Directory", OUTPUT_SCOPE_TYPE_OBJECT); snprintf(s, MAX_MSG, "%#x (%" PRIu32 " bytes)", directories[i]->VirtualAddress, directories[i]->Size); #ifdef LIBPE_ENABLE_OUTPUT_COMPAT_WITH_V06 output(directoryEntryNames[i].name, s); #else output(pe_directory_name(i), s); #endif output_close_scope(); // Directory } } output_close_scope(); // Data directories } static void print_optional_header(pe_ctx_t *ctx, IMAGE_OPTIONAL_HEADER *header) { #ifdef LIBPE_ENABLE_OUTPUT_COMPAT_WITH_V06 typedef struct { WindowsSubsystem subsystem; const char * const name; } WindowsSubsystemName; static const WindowsSubsystemName subsystemNames[] = { { IMAGE_SUBSYSTEM_UNKNOWN, "Unknown subsystem" }, { IMAGE_SUBSYSTEM_NATIVE, "System native" }, { IMAGE_SUBSYSTEM_WINDOWS_GUI, "Windows GUI" }, { IMAGE_SUBSYSTEM_WINDOWS_CUI, "Windows CLI" }, { IMAGE_SUBSYSTEM_UNKNOWN, "Unknown subsystem" }, { IMAGE_SUBSYSTEM_UNKNOWN, "Unknown subsystem" }, { IMAGE_SUBSYSTEM_UNKNOWN, "Unknown subsystem" }, { IMAGE_SUBSYSTEM_POSIX_CUI, "Posix CLI" }, { IMAGE_SUBSYSTEM_WINDOWS_CE_GUI, "Windows CE GUI" }, { IMAGE_SUBSYSTEM_EFI_APPLICATION, "EFI application" }, { IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER, "EFI driver with boot" }, { IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER, "EFI run-time driver" }, { IMAGE_SUBSYSTEM_EFI_ROM, "EFI ROM" }, { IMAGE_SUBSYSTEM_XBOX, "XBOX" }, { IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION, "Boot application" } }; static const size_t max_subsystem = LIBPE_SIZEOF_ARRAY(subsystemNames); #endif if (!header) return; static char s[MAX_MSG]; output_open_scope("Optional/Image header", OUTPUT_SCOPE_TYPE_OBJECT); switch (header->type) { case MAGIC_ROM: { snprintf(s, MAX_MSG, "%#x (%s)", header->_rom->Magic, "ROM"); output("Magic number", s); snprintf(s, MAX_MSG, "%" PRIu8, header->_rom->MajorLinkerVersion); output("Linker major version", s); snprintf(s, MAX_MSG, "%" PRIu8, header->_rom->MinorLinkerVersion); output("Linker minor version", s); snprintf(s, MAX_MSG, "%#x", header->_rom->SizeOfCode); output("Size of .text section", s); snprintf(s, MAX_MSG, "%#x", header->_rom->SizeOfInitializedData); output("Size of .data section", s); snprintf(s, MAX_MSG, "%#x", header->_rom->SizeOfUninitializedData); output("Size of .bss section", s); snprintf(s, MAX_MSG, "%#x", header->_rom->AddressOfEntryPoint); output("Entrypoint", s); snprintf(s, MAX_MSG, "%#x", header->_rom->BaseOfCode); output("Address of .text section", s); snprintf(s, MAX_MSG, "%#x", header->_rom->BaseOfData); output("Address of .data section", s); snprintf(s, MAX_MSG, "%#x", header->_rom->BaseOfBss); output("Address of .bss section", s); snprintf(s, MAX_MSG, "%#x", header->_rom->GprMask); output("GprMask", s); snprintf(s, MAX_MSG, "%#x", header->_rom->CprMask[0]); output("CprMask[0]", s); snprintf(s, MAX_MSG, "%#x", header->_rom->CprMask[1]); output("CprMask[1]", s); snprintf(s, MAX_MSG, "%#x", header->_rom->CprMask[2]); output("CprMask[2]", s); snprintf(s, MAX_MSG, "%#x", header->_rom->CprMask[3]); output("CprMask[3]", s); snprintf(s, MAX_MSG, "%#x", header->_rom->GpValue); output("GpValue", s); break; } case MAGIC_PE32_0: case MAGIC_PE32: { snprintf(s, MAX_MSG, "%#x (%s)", header->_32->Magic, header->type == MAGIC_PE32_0 ? "PE32 ZERO" : "PE32"); output("Magic number", s); snprintf(s, MAX_MSG, "%" PRIu8, header->_32->MajorLinkerVersion); output("Linker major version", s); snprintf(s, MAX_MSG, "%" PRIu8, header->_32->MinorLinkerVersion); output("Linker minor version", s); snprintf(s, MAX_MSG, "%#x", header->_32->SizeOfCode); output("Size of .text section", s); snprintf(s, MAX_MSG, "%#x", header->_32->SizeOfInitializedData); output("Size of .data section", s); snprintf(s, MAX_MSG, "%#x", header->_32->SizeOfUninitializedData); output("Size of .bss section", s); snprintf(s, MAX_MSG, "%#x", header->_32->AddressOfEntryPoint); output("Entrypoint", s); snprintf(s, MAX_MSG, "%#x", header->_32->BaseOfCode); output("Address of .text section", s); snprintf(s, MAX_MSG, "%#x", header->_32->BaseOfData); output("Address of .data section", s); snprintf(s, MAX_MSG, "%#x", header->_32->ImageBase); output("ImageBase", s); snprintf(s, MAX_MSG, "%#x", header->_32->SectionAlignment); output("Alignment of sections", s); snprintf(s, MAX_MSG, "%#x", header->_32->FileAlignment); output("Alignment factor", s); snprintf(s, MAX_MSG, "%" PRIu16, header->_32->MajorOperatingSystemVersion); output("Major version of required OS", s); snprintf(s, MAX_MSG, "%" PRIu16, header->_32->MinorOperatingSystemVersion); output("Minor version of required OS", s); snprintf(s, MAX_MSG, "%" PRIu16, header->_32->MajorImageVersion); output("Major version of image", s); snprintf(s, MAX_MSG, "%" PRIu16, header->_32->MinorImageVersion); output("Minor version of image", s); snprintf(s, MAX_MSG, "%" PRIu16, header->_32->MajorSubsystemVersion); output("Major version of subsystem", s); snprintf(s, MAX_MSG, "%" PRIu16, header->_32->MinorSubsystemVersion); output("Minor version of subsystem", s); #ifndef LIBPE_ENABLE_OUTPUT_COMPAT_WITH_V06 snprintf(s, MAX_MSG, "Win32 version value: %#x", header->_32->Win32VersionValue); output_open_scope(s, OUTPUT_SCOPE_TYPE_OBJECT); if (header->_32->Win32VersionValue == 0) strcpy(s, "(default)"); else snprintf(s, MAX_MSG, "%u", header->_32->Win32VersionValue & 0xff); output("Overwrite OS major version", s); if (header->_32->Win32VersionValue == 0) strcpy(s, "(default)"); else snprintf(s, MAX_MSG, "%u", (header->_32->Win32VersionValue >> 8) & 0xff); output("Overwrite OS minor version", s); if (header->_32->Win32VersionValue == 0) strcpy(s, "(default)"); else snprintf(s, MAX_MSG, "%u", (header->_32->Win32VersionValue >> 16) & 0x3fff); output("Overwrite OS build number", s); if (header->_32->Win32VersionValue == 0) strcpy(s, "(default)"); else { uint8_t platform_id = header->_32->Win32VersionValue >> 30; static const char *const win32_version_value_platform_id[4] = { "NT", "CE", "Win32s", "Win9x" }; snprintf(s, MAX_MSG, "%u (%s)", platform_id, win32_version_value_platform_id[platform_id]); } output("Overwrite OS platform id", s); output_close_scope(); #endif snprintf(s, MAX_MSG, "%#x", header->_32->SizeOfImage); output("Size of image", s); snprintf(s, MAX_MSG, "%#x", header->_32->SizeOfHeaders); output("Size of headers", s); snprintf(s, MAX_MSG, "%#x", header->_32->CheckSum); output("Checksum", s); const uint16_t subsystem = header->_32->Subsystem; #ifdef LIBPE_ENABLE_OUTPUT_COMPAT_WITH_V06 const char *subsystem_name = "Unknown"; for (size_t i=0; i < max_subsystem; i++) { if (subsystem == subsystemNames[i].subsystem) subsystem_name = subsystemNames[i].name; } #else const char *subsystem_name = pe_windows_subsystem_name(subsystem); if (subsystem_name == NULL) subsystem_name = "Unknown"; #endif snprintf(s, MAX_MSG, "%#x (%s)", subsystem, subsystem_name); output("Subsystem required", s); snprintf(s, MAX_MSG, "%#x", header->_32->DllCharacteristics); output("DLL characteristics", s); #ifndef LIBPE_ENABLE_OUTPUT_COMPAT_WITH_V06 output_open_scope("DLL characteristics names", OUTPUT_SCOPE_TYPE_ARRAY); for (uint16_t i=0, flag=0x0001; i < 16; i++, flag <<= 1) { if (header->_32->DllCharacteristics & flag) { const char *characteristic_name = NULL; char formatted_characteristic_name[32]; if (pe_coff(ctx)->Characteristics & IMAGE_FILE_DLL) characteristic_name = pe_dll_image_dllcharacteristic_name(flag); if (characteristic_name == NULL) characteristic_name = pe_image_dllcharacteristic_name(flag); if (characteristic_name == NULL) { snprintf(formatted_characteristic_name, sizeof(formatted_characteristic_name)-1, "UNKNOWN[%#x]", flag); characteristic_name = formatted_characteristic_name; } output(NULL, characteristic_name); } } output_close_scope(); // DLL characteristics names #endif snprintf(s, MAX_MSG, "%#x", header->_32->SizeOfStackReserve); output("Size of stack to reserve", s); snprintf(s, MAX_MSG, "%#x", header->_32->SizeOfStackCommit); output("Size of stack to commit", s); snprintf(s, MAX_MSG, "%#x", header->_32->SizeOfHeapReserve); output("Size of heap space to reserve", s); snprintf(s, MAX_MSG, "%#x", header->_32->SizeOfHeapCommit); output("Size of heap space to commit", s); #ifndef LIBPE_ENABLE_OUTPUT_COMPAT_WITH_V06 snprintf(s, MAX_MSG, "%#x", header->_32->LoaderFlags); output("Loader Flags", s); output_open_scope("Loader Flags names", OUTPUT_SCOPE_TYPE_ARRAY); for (uint32_t i=0, flag=0x00000001; i < 32; i++, flag <<= 1) { if (header->_32->LoaderFlags & flag) { const char *flag_name = NULL; char formatted_flag_name[32]; if (pe_coff(ctx)->Characteristics & IMAGE_FILE_DLL) flag_name = pe_dll_image_loader_flags_name(flag); if (flag_name == NULL) flag_name = pe_image_loader_flags_name(flag); if (flag_name == NULL) { snprintf(formatted_flag_name, sizeof(formatted_flag_name)-1, "UNKNOWN[%#x]", flag); flag_name = formatted_flag_name; } output(NULL, flag_name); } } output_close_scope(); // Loader Flags names #endif break; } case MAGIC_PE64: { snprintf(s, MAX_MSG, "%#x (%s)", header->_64->Magic, "PE32+"); output("Magic number", s); snprintf(s, MAX_MSG, "%" PRIu8, header->_64->MajorLinkerVersion); output("Linker major version", s); snprintf(s, MAX_MSG, "%" PRIu8, header->_64->MinorLinkerVersion); output("Linker minor version", s); snprintf(s, MAX_MSG, "%#x", header->_64->SizeOfCode); output("Size of .text section", s); snprintf(s, MAX_MSG, "%#x", header->_64->SizeOfInitializedData); output("Size of .data section", s); snprintf(s, MAX_MSG, "%#x", header->_64->SizeOfUninitializedData); output("Size of .bss section", s); snprintf(s, MAX_MSG, "%#x", header->_64->AddressOfEntryPoint); output("Entrypoint", s); snprintf(s, MAX_MSG, "%#x", header->_64->BaseOfCode); output("Address of .text section", s); snprintf(s, MAX_MSG, "%#"PRIx64, header->_64->ImageBase); output("ImageBase", s); snprintf(s, MAX_MSG, "%#x", header->_64->SectionAlignment); output("Alignment of sections", s); snprintf(s, MAX_MSG, "%#x", header->_64->FileAlignment); output("Alignment factor", s); snprintf(s, MAX_MSG, "%" PRIu16, header->_64->MajorOperatingSystemVersion); output("Major version of required OS", s); snprintf(s, MAX_MSG, "%" PRIu16, header->_64->MinorOperatingSystemVersion); output("Minor version of required OS", s); snprintf(s, MAX_MSG, "%" PRIu16, header->_64->MajorImageVersion); output("Major version of image", s); snprintf(s, MAX_MSG, "%" PRIu16, header->_64->MinorImageVersion); output("Minor version of image", s); snprintf(s, MAX_MSG, "%" PRIu16, header->_64->MajorSubsystemVersion); output("Major version of subsystem", s); snprintf(s, MAX_MSG, "%" PRIu16, header->_64->MinorSubsystemVersion); output("Minor version of subsystem", s); #ifndef LIBPE_ENABLE_OUTPUT_COMPAT_WITH_V06 snprintf(s, MAX_MSG, "Win32 version value: %#x", header->_64->Win32VersionValue); output_open_scope(s, OUTPUT_SCOPE_TYPE_OBJECT); if (header->_64->Win32VersionValue == 0) strcpy(s, "(default)"); else snprintf(s, MAX_MSG, "%u", header->_64->Win32VersionValue & 0xff); output("Overwrite OS major version", s); if (header->_64->Win32VersionValue == 0) strcpy(s, "(default)"); else snprintf(s, MAX_MSG, "%u", (header->_64->Win32VersionValue >> 8) & 0xff); output("Overwrite OS minor version", s); if (header->_64->Win32VersionValue == 0) strcpy(s, "(default)"); else snprintf(s, MAX_MSG, "%u", (header->_64->Win32VersionValue >> 16) & 0x3fff); output("Overwrite OS build number", s); if (header->_64->Win32VersionValue == 0) strcpy(s, "(default)"); else { uint8_t platform_id = header->_64->Win32VersionValue >> 30; static const char *const win32_version_value_platform_id[4] = { "NT", "CE", "Win32s", "Win9x" }; snprintf(s, MAX_MSG, "%u (%s)", platform_id, win32_version_value_platform_id[platform_id]); } output("Overwrite OS platform id", s); output_close_scope(); #endif snprintf(s, MAX_MSG, "%#x", header->_64->SizeOfImage); output("Size of image", s); snprintf(s, MAX_MSG, "%#x", header->_64->SizeOfHeaders); output("Size of headers", s); snprintf(s, MAX_MSG, "%#x", header->_64->CheckSum); output("Checksum", s); const uint16_t subsystem = header->_64->Subsystem; #ifdef LIBPE_ENABLE_OUTPUT_COMPAT_WITH_V06 const char *subsystem_name = "Unknown"; for (size_t i=0; i < max_subsystem; i++) { if (subsystem == subsystemNames[i].subsystem) subsystem_name = subsystemNames[i].name; } #else const char *subsystem_name = pe_windows_subsystem_name(subsystem); if (subsystem_name == NULL) subsystem_name = "Unknown"; #endif snprintf(s, MAX_MSG, "%#x (%s)", subsystem, subsystem_name); output("Subsystem required", s); snprintf(s, MAX_MSG, "%#x", header->_64->DllCharacteristics); output("DLL characteristics", s); #ifndef LIBPE_ENABLE_OUTPUT_COMPAT_WITH_V06 output_open_scope("DLL characteristics names", OUTPUT_SCOPE_TYPE_ARRAY); for (uint16_t i=0, flag=0x0001; i < 16; i++, flag <<= 1) { if (header->_64->DllCharacteristics & flag) { const char *characteristic_name = NULL; char formatted_characteristic_name[32]; if (pe_coff(ctx)->Characteristics & IMAGE_FILE_DLL) characteristic_name = pe_dll_image_dllcharacteristic_name(flag); if (characteristic_name == NULL) characteristic_name = pe_image_dllcharacteristic_name(flag); if (characteristic_name == NULL) { snprintf(formatted_characteristic_name, sizeof(formatted_characteristic_name)-1, "UNKNOWN[%#x]", flag); characteristic_name = formatted_characteristic_name; } output(NULL, characteristic_name); } } output_close_scope(); // DLL characteristics names #endif snprintf(s, MAX_MSG, "%#"PRIx64, header->_64->SizeOfStackReserve); output("Size of stack to reserve", s); snprintf(s, MAX_MSG, "%#"PRIx64, header->_64->SizeOfStackCommit); output("Size of stack to commit", s); snprintf(s, MAX_MSG, "%#"PRIx64, header->_64->SizeOfHeapReserve); output("Size of heap space to reserve", s); snprintf(s, MAX_MSG, "%#"PRIx64, header->_64->SizeOfHeapCommit); output("Size of heap space to commit", s); #ifndef LIBPE_ENABLE_OUTPUT_COMPAT_WITH_V06 snprintf(s, MAX_MSG, "%#x", header->_64->LoaderFlags); output("Loader Flags", s); output_open_scope("Loader Flags names", OUTPUT_SCOPE_TYPE_ARRAY); for (uint32_t i=0, flag=0x00000001; i < 32; i++, flag <<= 1) { if (header->_64->LoaderFlags & flag) { const char *flag_name = NULL; char formatted_flag_name[32]; if (pe_coff(ctx)->Characteristics & IMAGE_FILE_DLL) flag_name = pe_dll_image_loader_flags_name(flag); if (flag_name == NULL) flag_name = pe_image_loader_flags_name(flag); if (flag_name == NULL) { snprintf(formatted_flag_name, sizeof(formatted_flag_name)-1, "UNKNOWN[%#x]", flag); flag_name = formatted_flag_name; } output(NULL, flag_name); } } output_close_scope(); // Loader Flags names #endif break; } } output_close_scope(); // Optional/Image heade } static void print_coff_header(pe_ctx_t *ctx, IMAGE_COFF_HEADER *header) { #ifdef LIBPE_ENABLE_OUTPUT_COMPAT_WITH_V06 typedef struct { ImageCharacteristics characteristic; const char * const name; } ImageCharacteristicsName; static const ImageCharacteristicsName characteristicsTable[] = { { IMAGE_FILE_RELOCS_STRIPPED, "base relocations stripped" }, { IMAGE_FILE_EXECUTABLE_IMAGE, "executable image" }, { IMAGE_FILE_LINE_NUMS_STRIPPED, "line numbers removed (deprecated)" }, { IMAGE_FILE_LOCAL_SYMS_STRIPPED, "local symbols removed (deprecated)" }, { IMAGE_FILE_AGGRESSIVE_WS_TRIM, "aggressively trim (deprecated for Windows 2000 and later)" }, { IMAGE_FILE_LARGE_ADDRESS_AWARE, "can handle more than 2 GB addresses" }, { IMAGE_FILE_16BIT_MACHINE, "" }, { IMAGE_FILE_BYTES_REVERSED_LO, "little-endian (deprecated)" }, { IMAGE_FILE_32BIT_MACHINE, "32-bit machine" }, { IMAGE_FILE_DEBUG_STRIPPED, "debugging information removed" }, { IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP, "copy to swap if it's on removable media" }, { IMAGE_FILE_NET_RUN_FROM_SWAP, "copy to swap if it's on network media" }, { IMAGE_FILE_SYSTEM, "system file" }, { IMAGE_FILE_DLL, "DLL image" }, { IMAGE_FILE_UP_SYSTEM_ONLY, "uniprocessor machine" }, { IMAGE_FILE_BYTES_REVERSED_HI, "big-endian (deprecated)" } }; typedef struct { MachineType type; const char * const name; } MachineTypeName; static const MachineTypeName machineTypeTable[] = { { IMAGE_FILE_MACHINE_UNKNOWN, "Any machine type" }, { IMAGE_FILE_MACHINE_AM33, "Matsushita AM33" }, { IMAGE_FILE_MACHINE_AMD64, "x86-64 (64-bits)" }, { IMAGE_FILE_MACHINE_ARM, "ARM little endian" }, { IMAGE_FILE_MACHINE_ARMV7, "ARMv7 (or higher) Thumb mode only" }, { IMAGE_FILE_MACHINE_CEE, "clr pure MSIL (object only)" }, { IMAGE_FILE_MACHINE_EBC, "EFI byte code" }, { IMAGE_FILE_MACHINE_I386, "Intel 386 and compatible (32-bits)"}, { IMAGE_FILE_MACHINE_IA64, "Intel Itanium" }, { IMAGE_FILE_MACHINE_M32R, "Mitsubishi M32R little endian" }, { IMAGE_FILE_MACHINE_MIPS16, "MIPS16" }, { IMAGE_FILE_MACHINE_MIPSFPU, "MIPS with FPU" }, { IMAGE_FILE_MACHINE_MIPSFPU16, "MIPS16 with FPU" }, { IMAGE_FILE_MACHINE_POWERPC, "Power PC little endian" }, { IMAGE_FILE_MACHINE_POWERPCFP, "Power PC with floating point support" }, { IMAGE_FILE_MACHINE_R4000, "MIPS little endian" }, { IMAGE_FILE_MACHINE_SH3, "Hitachi SH3" }, { IMAGE_FILE_MACHINE_SH3DSP, "Hitachi SH3 DSP" }, { IMAGE_FILE_MACHINE_SH4, "Hitachi SH4" }, { IMAGE_FILE_MACHINE_SH5, "Hitachi SH5" }, { IMAGE_FILE_MACHINE_THUMB, "ARM or Thumb (\"interworking\")" }, { IMAGE_FILE_MACHINE_WCEMIPSV2, "MIPS little-endian WCE v2" } }; static const size_t max_machine_type = LIBPE_SIZEOF_ARRAY(machineTypeTable); #endif output_open_scope("COFF/File header", OUTPUT_SCOPE_TYPE_OBJECT); #ifdef LIBPE_ENABLE_OUTPUT_COMPAT_WITH_V06 const char *machine = "Unknown machine type"; for (size_t i=0; i < max_machine_type; i++) { if (header->Machine == machineTypeTable[i].type) machine = machineTypeTable[i].name; } #else const char *machine = pe_machine_type_name(header->Machine); if (machine == NULL) machine = "Unknown machine type"; #endif static char s[MAX_MSG]; snprintf(s, MAX_MSG, "%#x %s", header->Machine, machine); output("Machine", s); snprintf(s, MAX_MSG, "%" PRIu16, header->NumberOfSections); output("Number of sections", s); if (pe_is_repro(ctx)) { snprintf(s, MAX_MSG, "0x%" PRIx32 " (reproducible hash)", header->TimeDateStamp); } else { char timestr[40] = "invalid"; const time_t timestamp = header->TimeDateStamp; struct tm *t = gmtime(×tamp); if (t) strftime(timestr, sizeof(timestr), "%a, %d %b %Y %H:%M:%S UTC", t); snprintf(s, MAX_MSG, "%" PRIu32 " (%s)", header->TimeDateStamp, timestr); } output("Date/time stamp", s); snprintf(s, MAX_MSG, "%#x", header->PointerToSymbolTable); output("Symbol Table offset", s); snprintf(s, MAX_MSG, "%" PRIu32, header->NumberOfSymbols); output("Number of symbols", s); snprintf(s, MAX_MSG, "%#x", header->SizeOfOptionalHeader); output("Size of optional header", s); snprintf(s, MAX_MSG, "%#x", header->Characteristics); output("Characteristics", s); output_open_scope("Characteristics names", OUTPUT_SCOPE_TYPE_ARRAY); for (uint16_t i=0, flag=0x0001; i < 16; i++, flag <<= 1) { if (header->Characteristics & flag) { #ifdef LIBPE_ENABLE_OUTPUT_COMPAT_WITH_V06 output(NULL, characteristicsTable[i].name); #else const char *characteristic_name = pe_image_characteristic_name(flag); char formatted_characteristic_name[32]; if (characteristic_name == NULL) { snprintf(formatted_characteristic_name, sizeof(formatted_characteristic_name)-1, "UNKNOWN[%#x]", flag); characteristic_name = formatted_characteristic_name; } output(NULL, characteristic_name); #endif } } output_close_scope(); // Characteristics names output_close_scope(); // COFF/File header } static void print_dos_header(IMAGE_DOS_HEADER *header) { char s[MAX_MSG]; output_open_scope("DOS Header", OUTPUT_SCOPE_TYPE_OBJECT); snprintf(s, MAX_MSG, "%#x (MZ)", header->e_magic); output("Magic number", s); snprintf(s, MAX_MSG, "%" PRIu16, header->e_cblp); output("Bytes in last page", s); snprintf(s, MAX_MSG, "%" PRIu16, header->e_cp); output("Pages in file", s); snprintf(s, MAX_MSG, "%" PRIu16, header->e_crlc); output("Relocations", s); snprintf(s, MAX_MSG, "%" PRIu16, header->e_cparhdr); output("Size of header in paragraphs", s); snprintf(s, MAX_MSG, "%" PRIu16, header->e_minalloc); output("Minimum extra paragraphs", s); snprintf(s, MAX_MSG, "%" PRIu16, header->e_maxalloc); output("Maximum extra paragraphs", s); snprintf(s, MAX_MSG, "%#x", header->e_ss); output("Initial (relative) SS value", s); snprintf(s, MAX_MSG, "%#x", header->e_sp); output("Initial SP value", s); snprintf(s, MAX_MSG, "%#x", header->e_ip); output("Initial IP value", s); snprintf(s, MAX_MSG, "%#x", header->e_cs); output("Initial (relative) CS value", s); snprintf(s, MAX_MSG, "%#x", header->e_lfarlc); output("Address of relocation table", s); snprintf(s, MAX_MSG, "%#x", header->e_ovno); output("Overlay number", s); snprintf(s, MAX_MSG, "%#x", header->e_oemid); output("OEM identifier", s); snprintf(s, MAX_MSG, "%#x", header->e_oeminfo); output("OEM information", s); snprintf(s, MAX_MSG, "%#x", header->e_lfanew); output("PE header offset", s); output_close_scope(); // DOS Header } static void print_exports(pe_ctx_t *ctx) { output_open_scope("Exported functions", OUTPUT_SCOPE_TYPE_ARRAY); const pe_exports_t *exports = pe_exports(ctx); if (exports->name || exports->functions_count > 0) { output_open_scope("Library", OUTPUT_SCOPE_TYPE_OBJECT); output("Name", exports->name); } if (exports->functions_count > 0) { output_open_scope("Functions", OUTPUT_SCOPE_TYPE_ARRAY); } for (size_t i=0; i < exports->functions_count; i++) { const pe_exported_function_t *func = &exports->functions[i]; if (func->address != 0) { output_open_scope("Function", OUTPUT_SCOPE_TYPE_OBJECT); char ordinal_str[32] = { 0 }; char address_str[16] = { 0 }; char hint_str[16] = { 0 }; snprintf(ordinal_str, sizeof(ordinal_str)-1, "%"PRIu32, func->ordinal); snprintf(address_str, sizeof(address_str)-1, "%#"PRIx32, func->address); snprintf(hint_str, sizeof(hint_str)-1, "0x%"PRIx32, func->hint); if (func->fwd_name != NULL) { char full_name[300 * 2 + 4]; snprintf(full_name, sizeof(full_name)-1, "%s -> %s", func->name, func->fwd_name); output("Ordinal", ordinal_str); output("Address", address_str); output("Hint", hint_str); output("Name", full_name); } else { output("Ordinal", ordinal_str); output("Address", address_str); output("Hint", hint_str); output("Name", func->name); } output_close_scope(); // Function } } if (exports->functions_count > 0) { output_close_scope(); // Functions } if (exports->name || exports->functions_count > 0) { output_close_scope(); // Library } output_close_scope(); // Exported functions } static void print_import_library(const pe_imported_dll_t *dll) { output("Name", dll->name); output_open_scope("Functions", OUTPUT_SCOPE_TYPE_ARRAY); for (size_t j=0; j < dll->functions_count; j++) { const pe_imported_function_t *func = &dll->functions[j]; output_open_scope("Function", OUTPUT_SCOPE_TYPE_OBJECT); { if (func->ordinal) { char ordinal_str[16]; snprintf(ordinal_str, sizeof(ordinal_str)-1, "%"PRIu16, func->ordinal); output("Ordinal", ordinal_str); } else { char hint_str[16]; snprintf(hint_str, sizeof(hint_str)-1, "0x%"PRIx16, func->hint); output("Hint", hint_str); output("Name", func->name); } } output_close_scope(); // Function } output_close_scope(); // Functions } static void print_imports(pe_ctx_t *ctx) { output_open_scope("Imported functions", OUTPUT_SCOPE_TYPE_ARRAY); const pe_imports_t *imports = pe_imports(ctx); for (size_t i=0; i < imports->dll_count; i++) { const pe_imported_dll_t *dll = &imports->dlls[i]; output_open_scope("Library", OUTPUT_SCOPE_TYPE_OBJECT); print_import_library(dll); output_close_scope(); // Library } for (size_t i=0; i < imports->delay_dll_count; i++) { const pe_imported_dll_t *dll = &imports->delay_dlls[i]; output_open_scope("Delay Loaded Library", OUTPUT_SCOPE_TYPE_OBJECT); print_import_library(dll); output_close_scope(); // Delay Loaded Library } output_close_scope(); // Imported functions } int main(int argc, char *argv[]) { pev_config_t config; PEV_INITIALIZE(&config); if (argc < 2) { usage(); return EXIT_FAILURE; } output_set_cmdline(argc, argv); options_t *options = parse_options(argc, argv); // opcoes pe_ctx_t ctx; pe_err_e err = pe_load_file(&ctx, argv[argc-1]); if (err != LIBPE_E_OK) { pe_error_print(stderr, err); return EXIT_FAILURE; } err = pe_parse(&ctx); if (err != LIBPE_E_OK) { pe_error_print(stderr, err); return EXIT_FAILURE; } if (!pe_is_pe(&ctx)) EXIT_ERROR("not a valid PE file"); output_open_document(); // dos header if (options->dos || options->all_headers || options->all) { IMAGE_DOS_HEADER *header_ptr = pe_dos(&ctx); if (header_ptr) print_dos_header(header_ptr); else if (pe_is_exec(&ctx)) { LIBPE_WARNING("unable to read DOS header"); } } // coff/file header if (options->coff || options->all_headers || options->all) { #ifndef LIBPE_ENABLE_OUTPUT_COMPAT_WITH_V06 if (ctx.pe.signature) { static char s[MAX_MSG]; output_open_scope("PE header", OUTPUT_SCOPE_TYPE_OBJECT); snprintf(s, MAX_MSG, "0x%08x (%.4s)", ctx.pe.signature, (const char *)&ctx.pe.signature); output("Signature", s); output_close_scope(); } #endif IMAGE_COFF_HEADER *header_ptr = pe_coff(&ctx); if (header_ptr) print_coff_header(&ctx, header_ptr); else { LIBPE_WARNING("unable to read COFF file header"); } } // optional header if (options->opt || options->all_headers || options->all) { IMAGE_OPTIONAL_HEADER *header_ptr = pe_optional(&ctx); if (header_ptr) print_optional_header(&ctx, header_ptr); else if (pe_is_exec(&ctx)) { LIBPE_WARNING("unable to read Optional (Image) file header"); } } IMAGE_DATA_DIRECTORY **directories = pe_directories(&ctx); bool directories_warned = false; // directories if (options->dirs || options->all) { if (directories != NULL) print_directories(&ctx); else if (pe_is_exec(&ctx) && !directories_warned) { LIBPE_WARNING("directories not found"); directories_warned = true; } } // imports if (options->imports || options->all) { if (directories != NULL) print_imports(&ctx); else if (pe_is_exec(&ctx) && !directories_warned) { LIBPE_WARNING("directories not found"); directories_warned = true; } } // exports if (options->exports || options->all) { if (directories != NULL) print_exports(&ctx); else if (pe_is_exec(&ctx) && !directories_warned) { LIBPE_WARNING("directories not found"); directories_warned = true; } } // sections if (options->all_sections || options->all) { if (pe_sections(&ctx) != NULL) print_sections(&ctx); else { LIBPE_WARNING("unable to read sections"); } } output_close_document(); // libera a memoria free_options(options); // free err = pe_unload(&ctx); if (err != LIBPE_E_OK) { pe_error_print(stderr, err); return EXIT_FAILURE; } PEV_FINALIZE(&config); return EXIT_SUCCESS; }