diff --git a/.gitignore b/.gitignore index dca28b06..eba49931 100644 --- a/.gitignore +++ b/.gitignore @@ -12,9 +12,11 @@ Debug *.dylib *.dll *.o +*.dSYM *.log .settings src/ofs2rva +src/output src/pedis src/pehash src/pepack diff --git a/lib/libpe/dir_entry_security.h b/lib/libpe/dir_entry_security.h new file mode 100644 index 00000000..b277675e --- /dev/null +++ b/lib/libpe/dir_entry_security.h @@ -0,0 +1,59 @@ +#ifndef LIBPE_DIR_ENTRY_SECURITY_H +#define LIBPE_DIR_ENTRY_SECURITY_H + +#include "types.h" + +#define ANYSIZE_ARRAY 1 + +// #define WIN_TRUST_MAJOR_REVISION_MASK 0xFFFF0000 +// #define WIN_TRUST_MINOR_REVISION_MASK 0x0000FFFF +// #define WIN_TRUST_REVISION_1_0 0x00010000 + +typedef enum { + // Version 1, legacy version of the Win_Certificate + // structure. It is supported only for purposes of + // verifying legacy Authenticode signatures + WIN_CERT_REVISION_1_0 = 0x0100, + // Version 2 is the current version of the Win_Certificate structure. + WIN_CERT_REVISION_2_0 = 0x0200 +} CertRevision; + +typedef enum { + WIN_CERT_TYPE_X509 = 0x0001, // bCertificate contains an X.509 (Certificate) + WIN_CERT_TYPE_PKCS_SIGNED_DATA = 0x0002, // bCertificate contains a PKCS#7 (SignedData structure) + WIN_CERT_TYPE_RESERVED_1 = 0x0003, // Reserved + WIN_CERT_TYPE_TS_STACK_SIGNED = 0x0004, // Terminal Server Protocol Stack (Certificate signing) + WIN_CERT_TYPE_EFI_PKCS115 = 0x0EF0, + WIN_CERT_TYPE_EFI_GUID = 0x0EF1 +} CertType; + +#pragma pack(4) + +// Originally declared in Wintrust.h +typedef struct { + // Specified the size, in bytes, of the WIN_CERTIFICATE structure, + // including the data in bCertificate. + DWORD dwLength; + // Indicates the revision of the structure. + WORD wRevision; + // Specifies the type of certificate. + // This member can be one of the following values: + // Value Meaning + // ---------------------------------------------------------------------------------------- + // WIN_CERT_TYPE_X509 The certificate contains an X.509 Certificate. + // WIN_CERT_TYPE_PKCS_SIGNED_DATA The certificate contains a PKCS SignedData structure. + // WIN_CERT_TYPE_RESERVED_1 Reserved. + // WIN_CERT_TYPE_TS_STACK_SIGNED + WORD wCertificateType; + // A variable-sized array of bytes that contains the certificate data. + BYTE bCertificate[ANYSIZE_ARRAY]; +} WIN_CERTIFICATE; + +typedef struct { + DWORD cbData; + BYTE *pbData; +} CRYPT_DATA_BLOB; + +#pragma pack() + +#endif diff --git a/lib/libpe/pe.c b/lib/libpe/pe.c index d3c4f4ea..68fb9064 100644 --- a/lib/libpe/pe.c +++ b/lib/libpe/pe.c @@ -34,7 +34,7 @@ void *xmalloc(size_t size) return new_mem; } - + // return a rva of given offset DWORD ofs2rva(PE_FILE *pe, DWORD ofs) { @@ -48,7 +48,7 @@ DWORD ofs2rva(PE_FILE *pe, DWORD ofs) ofs < (pe->sections_ptr[i]->PointerToRawData + pe->sections_ptr[i]->SizeOfRawData)) return ofs + pe->sections_ptr[i]->VirtualAddress; } - return 0; + return 0; } QWORD rva2ofs(PE_FILE *pe, QWORD rva) @@ -57,7 +57,7 @@ QWORD rva2ofs(PE_FILE *pe, QWORD rva) return 0; for (unsigned int i=0; i < pe->num_sections; i++) - { + { if (rva >= pe->sections_ptr[i]->VirtualAddress && rva < (pe->sections_ptr[i]->VirtualAddress + pe->sections_ptr[i]->SizeOfRawData)) return rva - pe->sections_ptr[i]->VirtualAddress + pe->sections_ptr[i]->PointerToRawData; @@ -107,7 +107,7 @@ IMAGE_SECTION_HEADER* pe_get_section(PE_FILE *pe, const char *section_name) if (!pe->num_sections || pe->num_sections > MAX_SECTIONS) return NULL; - + for (unsigned int i=0; i < pe->num_sections; i++) { if (memcmp(pe->sections_ptr[i]->Name, section_name, strlen(section_name)) == 0) @@ -122,10 +122,10 @@ bool pe_get_resource_entries(PE_FILE *pe) if (!pe) return false; - + if (pe->rsrc_entries_ptr) return pe->rsrc_entries_ptr; - + if (!pe_get_resource_directory(pe, &dir)) return false; @@ -135,11 +135,11 @@ bool pe_get_resource_entries(PE_FILE *pe) return false; pe->rsrc_entries_ptr = xmalloc(sizeof(IMAGE_RESOURCE_DIRECTORY_ENTRY) * pe->num_rsrc_entries); - + for (unsigned int i=0; i < pe->num_rsrc_entries; i++) { pe->rsrc_entries_ptr[i] = xmalloc(sizeof(IMAGE_RESOURCE_DIRECTORY_ENTRY)); - + if (!fread(pe->rsrc_entries_ptr[i], sizeof(IMAGE_RESOURCE_DIRECTORY_ENTRY), 1, pe->handle)) return false; } @@ -155,9 +155,13 @@ bool pe_get_resource_directory(PE_FILE *pe, IMAGE_RESOURCE_DIRECTORY *dir) if (!pe_get_directories(pe)) return false; - if (pe->directories_ptr[IMAGE_DIRECTORY_ENTRY_RESOURCE]->Size > 0) + IMAGE_DATA_DIRECTORY *directory = pe_get_data_directory(pe, IMAGE_DIRECTORY_ENTRY_RESOURCE); + if (!directory) + return false; + + if (directory->Size > 0) { - pe->addr_rsrc_dir = rva2ofs(pe, pe->directories_ptr[IMAGE_DIRECTORY_ENTRY_RESOURCE]->VirtualAddress); + pe->addr_rsrc_dir = rva2ofs(pe, directory->VirtualAddress); if (fseek(pe->handle, pe->addr_rsrc_dir, SEEK_SET)) return false; @@ -169,6 +173,15 @@ bool pe_get_resource_directory(PE_FILE *pe, IMAGE_RESOURCE_DIRECTORY *dir) return false; } +IMAGE_DATA_DIRECTORY *pe_get_data_directory(PE_FILE *pe, ImageDirectoryEntry entry) +{ + if (!pe || !pe->directories_ptr || entry > (WORD)(pe->num_directories - 1)) + return NULL; + + return pe->directories_ptr[entry]; +} + + bool pe_get_sections(PE_FILE *pe) { IMAGE_SECTION_HEADER **sections; @@ -210,8 +223,6 @@ bool pe_get_sections(PE_FILE *pe) bool pe_get_directories(PE_FILE *pe) { - IMAGE_DATA_DIRECTORY **dirs; - if (!pe) return false; @@ -230,19 +241,27 @@ bool pe_get_directories(PE_FILE *pe) if (pe->num_directories > 32) return false; - dirs = xmalloc(sizeof(IMAGE_DATA_DIRECTORY *) * pe->num_directories); + const size_t directories_size = sizeof(IMAGE_DATA_DIRECTORY *) * pe->num_directories; + + pe->directories_ptr = xmalloc(directories_size); + if (!pe->directories_ptr) + return false; + + // Zero out the entire block, otherwise if one allocation fails for dirs[i], + // pe_deinit() will try to free wild pointers. This of course does not take + // into consideration that an allocation failure will make the process die. + memset(pe->directories_ptr, 0, directories_size); for (unsigned int i=0; i < pe->num_directories; i++) { - dirs[i] = xmalloc(sizeof(IMAGE_DATA_DIRECTORY)); - if (!fread(dirs[i], sizeof(IMAGE_DATA_DIRECTORY), 1, pe->handle)) + pe->directories_ptr[i] = xmalloc(sizeof(IMAGE_DATA_DIRECTORY)); + if (!fread(pe->directories_ptr[i], sizeof(IMAGE_DATA_DIRECTORY), 1, pe->handle)) return false; } pe->addr_sections = ftell(pe->handle); - pe->directories_ptr = dirs; - if (!pe->addr_sections || !pe->directories_ptr) + if (!pe->addr_sections) return false; return true; @@ -269,7 +288,11 @@ bool pe_get_optional(PE_FILE *pe) if (fseek(pe->handle, pe->addr_optional, SEEK_SET)) return false; - header = xmalloc(sizeof(IMAGE_OPTIONAL_HEADER)); + pe->optional_ptr = xmalloc(sizeof(IMAGE_OPTIONAL_HEADER)); + if (!pe->optional_ptr) + return false; + + header = pe->optional_ptr; switch (pe->architecture) { @@ -298,10 +321,9 @@ bool pe_get_optional(PE_FILE *pe) return false; } - pe->optional_ptr = header; pe->addr_directories = ftell(pe->handle); - if (!pe->optional_ptr || !pe->addr_directories) + if (!pe->addr_directories) return false; return true; @@ -346,7 +368,7 @@ bool pe_get_dos(PE_FILE *pe, IMAGE_DOS_HEADER *header) { if (!pe) return false; - + if (!pe->handle) return false; @@ -364,10 +386,10 @@ QWORD pe_get_size(PE_FILE *pe) { if (pe->size) return pe->size; - + if (fseek(pe->handle, 0, SEEK_END)) return 0; - + pe->size = ftell(pe->handle); rewind(pe->handle); return pe->size; @@ -384,6 +406,7 @@ bool is_pe(PE_FILE *pe) if (pe->handle == NULL) return false; + rewind(pe->handle); if (!fread(&header, sizeof(WORD), 1, pe->handle)) return false; @@ -405,9 +428,9 @@ bool is_pe(PE_FILE *pe) return false; if (pesig != 0x4550) // "PE\0\0" - return false; + return false; - rewind(pe->handle); + rewind(pe->handle); return true; } diff --git a/lib/libpe/pe.h b/lib/libpe/pe.h index b6c688ca..a3a8a30d 100644 --- a/lib/libpe/pe.h +++ b/lib/libpe/pe.h @@ -20,22 +20,16 @@ #ifndef LIBPE_H #define LIBPE_H -#include #include -#include #include #include +#include "types.h" +#include "dir_entry_security.h" #define PE32 0x10b #define PE64 0x20b #define MZ 0x5a4d -typedef uint32_t DWORD; -typedef int32_t LONG; -typedef uint8_t BYTE; -typedef uint16_t WORD; -typedef uint64_t QWORD; - #define MAX_SECTIONS 96 // section name size @@ -44,52 +38,54 @@ typedef uint64_t QWORD; #define IMAGE_ORDINAL_FLAG64 0x8000000000000000ULL // resources types -#define RT_CURSOR 1 // cursor image -#define RT_BITMAP 2 // bitmap (.bmp) -#define RT_ICON 3 // icon -#define RT_MENU 4 // menu -#define RT_DIALOG 5 // dialog window -#define RT_STRING 6 // unicode string -#define RT_FONTDIR 7 // font directory -#define RT_FONT 8 // font -#define RT_ACCELERATOR 9 // hot keys -#define RT_RCDATA 10 // data -#define RT_MESSAGETABLE 11 // string table -#define RT_GROUP_CURSOR 12 // cursor group -#define RT_GROUP_ICON 14 // icon group -#define RT_VERSION 16 // version information -#define RT_DLGINCLUDE 17 // names of header files for dialogs (*.h) used by compiler -#define RT_PLUGPLAY 19 // data determined by application -#define RT_VXD 20 // vxd info -#define RT_ANICURSOR 21 // animated cursor -#define RT_ANIICON 22 // animated icon -#define RT_HTML 23 // html page -#define RT_MANIFEST 24 // manifest of Windows XP build -#define RT_DLGINIT 240 // strings used for initiating some controls in dialogs -#define RT_TOOLBAR 241 // configuration of toolbars +#define RT_CURSOR 1 // cursor image +#define RT_BITMAP 2 // bitmap (.bmp) +#define RT_ICON 3 // icon +#define RT_MENU 4 // menu +#define RT_DIALOG 5 // dialog window +#define RT_STRING 6 // unicode string +#define RT_FONTDIR 7 // font directory +#define RT_FONT 8 // font +#define RT_ACCELERATOR 9 // hot keys +#define RT_RCDATA 10 // data +#define RT_MESSAGETABLE 11 // string table +#define RT_GROUP_CURSOR 12 // cursor group +#define RT_GROUP_ICON 14 // icon group +#define RT_VERSION 16 // version information +#define RT_DLGINCLUDE 17 // names of header files for dialogs (*.h) used by compiler +#define RT_PLUGPLAY 19 // data determined by application +#define RT_VXD 20 // vxd info +#define RT_ANICURSOR 21 // animated cursor +#define RT_ANIICON 22 // animated icon +#define RT_HTML 23 // html page +#define RT_MANIFEST 24 // manifest of Windows XP build +#define RT_DLGINIT 240 // strings used for initiating some controls in dialogs +#define RT_TOOLBAR 241 // configuration of toolbars // directory Entries -#define IMAGE_DIRECTORY_ENTRY_EXPORT 0 // Export Directory -#define IMAGE_DIRECTORY_ENTRY_IMPORT 1 // Import Directory -#define IMAGE_DIRECTORY_ENTRY_RESOURCE 2 // Resource Directory -#define IMAGE_DIRECTORY_ENTRY_EXCEPTION 3 // Exception Directory -#define IMAGE_DIRECTORY_ENTRY_SECURITY 4 // Security Directory -#define IMAGE_DIRECTORY_ENTRY_BASERELOC 5 // Base Relocation Table -#define IMAGE_DIRECTORY_ENTRY_DEBUG 6 // Debug Directory -// IMAGE_DIRECTORY_ENTRY_COPYRIGHT 7 // (X86 usage) -#define IMAGE_DIRECTORY_ENTRY_ARCHITECTURE 7 // Architecture Specific Data -#define IMAGE_DIRECTORY_ENTRY_GLOBALPTR 8 // RVA of GP -#define IMAGE_DIRECTORY_ENTRY_TLS 9 // TLS Directory -#define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG 10 // Load Configuration Directory -#define IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT 11 // Bound Import Directory in headers -#define IMAGE_DIRECTORY_ENTRY_IAT 12 // Import Address Table -#define IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT 13 // Delay Load Import Descriptors -#define IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR 14 // COM Runtime descriptor +typedef enum { + // FIXME: Quoting pecoff_v8.docx: "Entries in the section table are numbered starting from one (1)". + IMAGE_DIRECTORY_ENTRY_EXPORT = 0, // Export Directory + IMAGE_DIRECTORY_ENTRY_IMPORT = 1, // Import Directory + IMAGE_DIRECTORY_ENTRY_RESOURCE = 2, // Resource Directory + IMAGE_DIRECTORY_ENTRY_EXCEPTION = 3, // Exception Directory + IMAGE_DIRECTORY_ENTRY_SECURITY = 4, // Security Directory + IMAGE_DIRECTORY_ENTRY_BASERELOC = 5, // Base Relocation Table + IMAGE_DIRECTORY_ENTRY_DEBUG = 6, // Debug Directory + //IMAGE_DIRECTORY_ENTRY_COPYRIGHT = 7, // (X86 usage) + IMAGE_DIRECTORY_ENTRY_ARCHITECTURE = 7, // Architecture Specific Data + IMAGE_DIRECTORY_ENTRY_GLOBALPTR = 8, // RVA of GP + IMAGE_DIRECTORY_ENTRY_TLS = 9, // TLS Directory + IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG = 10, // Load Configuration Directory + IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT = 11, // Bound Import Directory in headers + IMAGE_DIRECTORY_ENTRY_IAT = 12, // Import Address Table + IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT = 13, // Delay Load Import Descriptors + IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR = 14 // COM Runtime descriptor +} ImageDirectoryEntry; #pragma pack(push, 1) -typedef struct _MACHINE_ENTRY -{ +typedef struct _MACHINE_ENTRY { char name[40]; WORD code; } MACHINE_ENTRY; @@ -229,31 +225,26 @@ typedef struct _IMAGE_RESOURCE_DIRECTORY { WORD NumberOfIdEntries; } IMAGE_RESOURCE_DIRECTORY; -typedef struct _IMAGE_RESOURCE_DIRECTORY_ENTRY -{ - union - { - struct - { - DWORD NameOffset:31; - DWORD NameIsString:1; - } name; - DWORD Name; - } DirectoryName; - union - { - DWORD OffsetToData; - struct - { - DWORD OffsetToDirectory:31; - DWORD DataIsDirectory:1; - } data; - } DirectoryData; +typedef struct _IMAGE_RESOURCE_DIRECTORY_ENTRY { + union { + struct { + DWORD NameOffset:31; + DWORD NameIsString:1; + } name; + DWORD Name; + } DirectoryName; + union { + DWORD OffsetToData; + struct { + DWORD OffsetToDirectory:31; + DWORD DataIsDirectory:1; + } data; + } DirectoryData; } IMAGE_RESOURCE_DIRECTORY_ENTRY; typedef struct _IMAGE_RESOURCE_DATA_STRING { - WORD length; - WORD string[1]; + WORD length; + WORD string[1]; } IMAGE_RESOURCE_DATA_STRING; typedef struct _IMAGE_RESOURCE_DATA_ENTRY { @@ -263,8 +254,7 @@ typedef struct _IMAGE_RESOURCE_DATA_ENTRY { DWORD reserved; } IMAGE_RESOURCE_DATA_ENTRY; -typedef struct _RESOURCE_ENTRY -{ +typedef struct _RESOURCE_ENTRY { char name[20]; DWORD nameOffset; char extension[20]; @@ -306,29 +296,29 @@ typedef struct _IMAGE_TLS_DIRECTORY64 { } IMAGE_TLS_DIRECTORY64; typedef struct _IMAGE_EXPORT_DIRECTORY { - DWORD Characteristics; - DWORD TimeDateStamp; - WORD MajorVersion; - WORD MinorVersion; - DWORD Name; - DWORD Base; - DWORD NumberOfFunctions; - DWORD NumberOfNames; - DWORD AddressOfFunctions; - DWORD AddressOfNames; - DWORD AddressOfNameOrdinals; - } IMAGE_EXPORT_DIRECTORY; + DWORD Characteristics; + DWORD TimeDateStamp; + WORD MajorVersion; + WORD MinorVersion; + DWORD Name; + DWORD Base; + DWORD NumberOfFunctions; + DWORD NumberOfNames; + DWORD AddressOfFunctions; + DWORD AddressOfNames; + DWORD AddressOfNameOrdinals; +} IMAGE_EXPORT_DIRECTORY; typedef struct _IMAGE_IMPORT_DESCRIPTOR { - union { - DWORD Characteristics; // 0 for terminating null import descriptor - DWORD OriginalFirstThunk; // RVA to original unbound IAT - } u1; - DWORD TimeDateStamp; - DWORD ForwarderChain; // -1 if no forwarders - DWORD Name; - // RVA to IAT (if bound this IAT has actual addresses) - DWORD FirstThunk; + union { + DWORD Characteristics; // 0 for terminating null import descriptor + DWORD OriginalFirstThunk; // RVA to original unbound IAT + } u1; + DWORD TimeDateStamp; + DWORD ForwarderChain; // -1 if no forwarders + DWORD Name; + // RVA to IAT (if bound this IAT has actual addresses) + DWORD FirstThunk; } IMAGE_IMPORT_DESCRIPTOR; // import name entry @@ -355,21 +345,20 @@ typedef struct _IMAGE_THUNK_DATA32 { } u1; } IMAGE_THUNK_DATA32; -typedef struct _PE_FILE -{ +typedef struct _PE_FILE { FILE *handle; - + bool isdll; WORD e_lfanew; WORD architecture; QWORD entrypoint; QWORD imagebase; QWORD size; - + WORD num_sections; WORD num_directories; WORD num_rsrc_entries; - + WORD addr_sections; WORD addr_directories; WORD addr_dos; @@ -377,7 +366,7 @@ typedef struct _PE_FILE WORD addr_coff; WORD addr_rsrc_sec; WORD addr_rsrc_dir; - + // pointers (will be freed if needed) IMAGE_OPTIONAL_HEADER *optional_ptr; IMAGE_SECTION_HEADER **sections_ptr; @@ -385,7 +374,6 @@ typedef struct _PE_FILE //IMAGE_TLS_DIRECTORY32 *tls_ptr; IMAGE_RESOURCE_DIRECTORY *rsrc_ptr; IMAGE_RESOURCE_DIRECTORY_ENTRY **rsrc_entries_ptr; - } PE_FILE; #pragma pack(pop) @@ -403,7 +391,7 @@ QWORD pe_get_size(PE_FILE *pe); // header functions bool pe_init(PE_FILE *pe, FILE *handle); bool pe_get_sections(PE_FILE *pe); -IMAGE_SECTION_HEADER* pe_get_section(PE_FILE *pe, const char* section_name); +IMAGE_SECTION_HEADER *pe_get_section(PE_FILE *pe, const char *section_name); bool pe_get_directories(PE_FILE *pe); bool pe_get_optional(PE_FILE *pe); bool pe_get_coff(PE_FILE *pe, IMAGE_COFF_HEADER *header); @@ -412,7 +400,8 @@ bool pe_get_dos(PE_FILE *pe, IMAGE_DOS_HEADER *header); //bool pe_get_tls_callbacks(PE_FILE *pe); bool pe_get_resource_directory(PE_FILE *pe, IMAGE_RESOURCE_DIRECTORY *dir); bool pe_get_resource_entries(PE_FILE *pe); +IMAGE_DATA_DIRECTORY *pe_get_data_directory(PE_FILE *pe, ImageDirectoryEntry entry); -IMAGE_SECTION_HEADER* pe_rva2section(PE_FILE *pe, QWORD rva); +IMAGE_SECTION_HEADER *pe_rva2section(PE_FILE *pe, QWORD rva); #endif diff --git a/lib/libpe/types.h b/lib/libpe/types.h new file mode 100644 index 00000000..dba5e769 --- /dev/null +++ b/lib/libpe/types.h @@ -0,0 +1,13 @@ +#ifndef LIBPE_TYPES_H +#define LIBPE_TYPES_H + +#include +#include + +typedef uint32_t DWORD; +typedef int32_t LONG; +typedef uint8_t BYTE; +typedef uint16_t WORD; +typedef uint64_t QWORD; + +#endif diff --git a/readme b/readme index 8c02365f..622c0379 100644 --- a/readme +++ b/readme @@ -73,7 +73,7 @@ 4.4. Building in Windows (via Cygwin) - Under Cygwin, you'll need following packages + Under Cygwin, you'll need to install the following packages in order to compile pev: * gcc @@ -94,6 +94,32 @@ You can't call another targets (like make install) in Cygwin environment. + 4.5. Building in Mac OS X + + Under Mac OS X, you'll need to install the following packages + in order to compile pev: + + * libpcre + + We recommend using one of the following package managers to + install this package: + + * MacPorts - http://www.macports.org + * Homebrew - http://mxcl.github.com/homebrew + + For MacPorts, install the dependency and compile pev with: + + $ sudo port install pcre + $ make CFLAGS="-I/opt/local/include" + + For Homebrew, install the dependency and compile pev with: + + $ sudo brew install pcre + $ make CFLAGS="-I/usr/local/include" + + NOTE: The paths above may change depending on how you installed the + package manager. + 5. Included tools * pehash - calculate PE file hashes diff --git a/src/Makefile b/src/Makefile index d8afed8f..c733a6ee 100644 --- a/src/Makefile +++ b/src/Makefile @@ -29,10 +29,14 @@ MANDIR = /usr/share/man/man1 # 'make' will compile all binaries all: $(PROGS) +pesec: LDFLAGS += -lcrypto + pestr: CPPFLAGS += -D_GNU_SOURCE=1 pestr: LDFLAGS += -lpcre pestr: pestr.c +peres: peres.c + pehash: LDFLAGS += -lssl -lcrypto pehash: pehash.c diff --git a/src/output.c b/src/output.c index fe1f7c80..dabf5e8a 100644 --- a/src/output.c +++ b/src/output.c @@ -23,7 +23,7 @@ #include "output.h" #include "common.h" -extern format_e format; +format_e format; void parse_format(const char *optarg) { diff --git a/src/output.h b/src/output.h index 99758de2..6e69fddf 100644 --- a/src/output.h +++ b/src/output.h @@ -32,6 +32,6 @@ typedef enum { FORMAT_CSV = 4 } format_e; -format_e format; +extern format_e format; #endif diff --git a/src/peres.c b/src/peres.c index 46a9ed73..85bade8d 100644 --- a/src/peres.c +++ b/src/peres.c @@ -1,6 +1,6 @@ /* pev - the PE file analyzer toolkit - + peres.c - retrive informations and binary data of resources Copyright (C) 2012 pev authors @@ -51,13 +51,13 @@ static void parse_options(int argc, char **argv) static const struct option long_options[] = { - {"all", required_argument, NULL, 'a'}, - {"extract", no_argument, NULL, 'x'}, - {"info", no_argument, NULL, 'i'}, - {"statistics", no_argument, NULL, 's'}, - {"version", no_argument, NULL, 'v'}, - {"help", no_argument, NULL, 1 }, - { NULL, 0, NULL, 0 } + {"all", required_argument, NULL, 'a'}, + {"extract", no_argument, NULL, 'x'}, + {"info", no_argument, NULL, 'i'}, + {"statistics", no_argument, NULL, 's'}, + {"version", no_argument, NULL, 'v'}, + {"help", no_argument, NULL, 1 }, + { NULL, 0, NULL, 0 } }; //memset(&config, false, sizeof(config)); @@ -145,7 +145,7 @@ static void showNode(NODE_PERES *nodePeres) snprintf(value, MAX_MSG, "%d", nodePeres->node.dataString.length); output("String len", value); - snprintf(value, MAX_MSG, "%d", (int) nodePeres->node.dataString.string); + snprintf(value, MAX_MSG, "%d", nodePeres->node.dataString.string[0]); output("String", value); break; @@ -174,8 +174,8 @@ static void showNode(NODE_PERES *nodePeres) static NODE_PERES * createNode(NODE_PERES *currentNode, NODE_TYPE_PERES typeOfNextNode) { currentNode->nextNode = xmalloc(sizeof(NODE_PERES)); - ((NODE_PERES *) currentNode->nextNode)->lastNode = currentNode; - currentNode = (NODE_PERES *) currentNode->nextNode; + currentNode->nextNode->lastNode = currentNode; + currentNode = currentNode->nextNode; currentNode->nodeType = typeOfNextNode; currentNode->nextNode = NULL; return currentNode; @@ -254,13 +254,12 @@ static void freeNodes(NODE_PERES *currentNode) } } -static RESOURCE_ENTRY * getResourceEntryByNameOffset(DWORD nameOffset) +static const RESOURCE_ENTRY * getResourceEntryByNameOffset(DWORD nameOffset) { - unsigned int i; - for(i = 0; i < (sizeof(resourceTypes)/sizeof(RESOURCE_ENTRY)); i++) + for (size_t i = 0; i < (sizeof(resource_types) / sizeof(RESOURCE_ENTRY)); i++) { - if(resourceTypes[i].nameOffset == nameOffset) - return (RESOURCE_ENTRY *)&resourceTypes[i]; + if (resource_types[i].nameOffset == nameOffset) + return &resource_types[i]; } return NULL; @@ -274,7 +273,6 @@ static void saveResource(PE_FILE *pe, NODE_PERES *nodePeres, int count) char fileName[100]; DWORD nameOffset; QWORD offsetData; - struct stat statDir = {0}; buffer = xmalloc(lastNodeByType(nodePeres, RDT_DATA_ENTRY)->node.dataEntry.size); memset(buffer, 0, lastNodeByType(nodePeres, RDT_DATA_ENTRY)->node.dataEntry.size); @@ -282,23 +280,24 @@ static void saveResource(PE_FILE *pe, NODE_PERES *nodePeres, int count) fseek(pe->handle, offsetData, SEEK_SET); memset(&fileName, 0, 100); memset(&dirName, 0, 100); - nameOffset = ((NODE_PERES *)nodePeres->rootNode)->node.directoryEntry.DirectoryName.name.NameOffset; + nameOffset = nodePeres->rootNode->node.directoryEntry.DirectoryName.name.NameOffset; if(fread(buffer, lastNodeByType(nodePeres, RDT_DATA_ENTRY)->node.dataEntry.size + 1, 1, pe->handle)) { + struct stat statDir; if (stat(resourceDir, &statDir) == -1) mkdir(resourceDir, 0700); - snprintf(&dirName, 100, "%s/%s", resourceDir, getResourceEntryByNameOffset(nameOffset)->dirName); + snprintf(dirName, sizeof(dirName), "%s/%s", resourceDir, getResourceEntryByNameOffset(nameOffset)->dirName); if (stat(dirName, &statDir) == -1) mkdir(dirName, 0700); if(getResourceEntryByNameOffset(nameOffset) != NULL) - snprintf(&fileName, 100, "%s/%d%s", dirName, count, getResourceEntryByNameOffset(nameOffset)->extension); + snprintf(fileName, sizeof(fileName), "%s/%d%s", dirName, count, getResourceEntryByNameOffset(nameOffset)->extension); else - snprintf(&fileName, 100, "%s/%d.bin", dirName, count); + snprintf(fileName, sizeof(fileName), "%s/%d.bin", dirName, count); - fpSave = fopen(&fileName, "wb+"); + fpSave = fopen(fileName, "wb+"); fwrite(buffer, lastNodeByType(nodePeres, RDT_DATA_ENTRY)->node.dataEntry.size, 1, fpSave); fclose(fpSave); output("Save On", fileName); @@ -473,18 +472,18 @@ static NODE_PERES * discoveryNodesPeres(PE_FILE *pe) fread(&nodePeres->node, sizeof(IMAGE_RESOURCE_DIRECTORY_ENTRY), 1, pe->handle); //showNode(nodePeres); - if (lastNodeByTypeAndLevel(nodePeres, RDT_DIRECTORY_ENTRY, RDT_LEVEL1)->node.directoryEntry.DirectoryData.data.DataIsDirectory) - { - fseek(pe->handle, (raiz + lastNodeByTypeAndLevel(nodePeres, RDT_DIRECTORY_ENTRY, RDT_LEVEL1)->node.directoryEntry.DirectoryData.data.OffsetToDirectory), SEEK_SET); - nodePeres = createNode(nodePeres, RDT_RESOURCE_DIRECTORY); - nodePeres->nodeLevel = RDT_LEVEL2; - nodePeres->rootNode = lastNodeByTypeAndLevel(nodePeres, RDT_DIRECTORY_ENTRY, RDT_LEVEL1); - fread(&nodePeres->node, sizeof(IMAGE_RESOURCE_DIRECTORY), 1, pe->handle); - //showNode(nodePeres); - - for(j = 1, offsetDirectory2 = 0; j <= (lastNodeByTypeAndLevel(nodePeres, RDT_RESOURCE_DIRECTORY, RDT_LEVEL2)->node.resourceDirectory.NumberOfNamedEntries + - lastNodeByTypeAndLevel(nodePeres, RDT_RESOURCE_DIRECTORY, RDT_LEVEL2)->node.resourceDirectory.NumberOfIdEntries); j++) - { + if (lastNodeByTypeAndLevel(nodePeres, RDT_DIRECTORY_ENTRY, RDT_LEVEL1)->node.directoryEntry.DirectoryData.data.DataIsDirectory) + { + fseek(pe->handle, (raiz + lastNodeByTypeAndLevel(nodePeres, RDT_DIRECTORY_ENTRY, RDT_LEVEL1)->node.directoryEntry.DirectoryData.data.OffsetToDirectory), SEEK_SET); + nodePeres = createNode(nodePeres, RDT_RESOURCE_DIRECTORY); + nodePeres->nodeLevel = RDT_LEVEL2; + nodePeres->rootNode = lastNodeByTypeAndLevel(nodePeres, RDT_DIRECTORY_ENTRY, RDT_LEVEL1); + fread(&nodePeres->node, sizeof(IMAGE_RESOURCE_DIRECTORY), 1, pe->handle); + //showNode(nodePeres); + + for(j = 1, offsetDirectory2 = 0; j <= (lastNodeByTypeAndLevel(nodePeres, RDT_RESOURCE_DIRECTORY, RDT_LEVEL2)->node.resourceDirectory.NumberOfNamedEntries + + lastNodeByTypeAndLevel(nodePeres, RDT_RESOURCE_DIRECTORY, RDT_LEVEL2)->node.resourceDirectory.NumberOfIdEntries); j++) + { if(j == 1) { offsetDirectory2 += 16; @@ -525,7 +524,7 @@ static NODE_PERES * discoveryNodesPeres(PE_FILE *pe) fread(&nodePeres->node, sizeof(IMAGE_RESOURCE_DATA_STRING), 1, pe->handle); //showNode(nodePeres); - fseek(pe->handle, (raiz + ((NODE_PERES *)nodePeres->lastNode)->node.directoryEntry.DirectoryData.data.OffsetToDirectory), SEEK_SET); + fseek(pe->handle, (raiz + nodePeres->lastNode->node.directoryEntry.DirectoryData.data.OffsetToDirectory), SEEK_SET); nodePeres = createNode(nodePeres, RDT_DATA_ENTRY); nodePeres->nodeLevel = RDT_LEVEL3; nodePeres->rootNode = rootNodePeres; @@ -533,7 +532,7 @@ static NODE_PERES * discoveryNodesPeres(PE_FILE *pe) //showNode(nodePeres); } } - } + } } return nodePeres; } diff --git a/src/peres.h b/src/peres.h index b397ce71..8af8670e 100644 --- a/src/peres.h +++ b/src/peres.h @@ -1,6 +1,6 @@ /* pev - the PE file analyzer toolkit - + peres.h - definitions for peres.c Copyright (C) 2012 pev authors @@ -37,63 +37,60 @@ struct options { }; typedef enum { - RDT_LEVEL1 = 1, - RDT_LEVEL2 = 2, - RDT_LEVEL3 = 3 + RDT_LEVEL1 = 1, + RDT_LEVEL2 = 2, + RDT_LEVEL3 = 3 } NODE_LEVEL_PERES; typedef enum { - RDT_RESOURCE_DIRECTORY = 1, - RDT_DIRECTORY_ENTRY = 2, - RDT_DATA_STRING = 3, - RDT_DATA_ENTRY = 4 + RDT_RESOURCE_DIRECTORY = 1, + RDT_DIRECTORY_ENTRY = 2, + RDT_DATA_STRING = 3, + RDT_DATA_ENTRY = 4 } NODE_TYPE_PERES; -typedef struct _NODE_PERES -{ +typedef struct _NODE_PERES { NODE_TYPE_PERES nodeType; NODE_LEVEL_PERES nodeLevel; - union - { + union { IMAGE_RESOURCE_DIRECTORY resourceDirectory; // nodeType == 1 IMAGE_RESOURCE_DIRECTORY_ENTRY directoryEntry; // nodeType == 2 IMAGE_RESOURCE_DATA_STRING dataString; // nodeType == 3 IMAGE_RESOURCE_DATA_ENTRY dataEntry; // nodeType == 4 } node; - struct NODE_PERES *nextNode; - struct NODE_PERES *lastNode; - struct NODE_PERES *rootNode; + struct _NODE_PERES *nextNode; + struct _NODE_PERES *lastNode; + struct _NODE_PERES *rootNode; } NODE_PERES; -char *resourceDir = "resources"; - -static const RESOURCE_ENTRY resourceTypes[] = -{ - {"RT_CURSOR", 1, ".cur", "cursors"}, - {"RT_BITMAP", 2, ".bmp", "bitmaps"}, - {"RT_ICON", 3, ".ico", "icons"}, - {"RT_MENU", 4, ".rc", "menus"}, - {"RT_DIALOG", 5, ".dlg", "dialogs"}, - {"RT_STRING", 6, ".rc", "strings"}, - {"RT_FONTDIR", 7, ".fnt", "fontdirs"}, - {"RT_FONT", 8, ".fnt", "fonts"}, - {"RT_ACCELERATOR", 9, ".rc", "accelerators"}, - {"RT_RCDATA", 10, ".rc", "rcdatas"}, - {"RT_MESSAGETABLE", 11, ".mc", "messagetables"}, - {"RT_GROUP_CURSOR", 12, ".cur", "groupcursors"}, - {"RT_GROUP_ICON", 14, ".ico", "groupicons"}, - {"RT_VERSION", 16, ".rc", "versions"}, - {"RT_DLGINCLUDE", 17, ".rc", "dlgincludes"}, - {"RT_PLUGPLAY", 19, ".rc", "plugplays"}, - {"RT_VXD", 20, ".rc", "xvds"}, - {"RT_ANICURSOR", 21, ".rc", "anicursors"}, - {"RT_ANIICON", 22, ".rc", "aniicons"}, - {"RT_HTML", 23, ".html", "htmls"}, - {"RT_MANIFEST", 24, ".xml", "manifests"}, - {"RT_DLGINIT", 240, ".rc", "dlginits"}, - {"RT_TOOLBAR", 241, ".rc", "toolbars"} +static const RESOURCE_ENTRY resource_types[] = { + { "RT_CURSOR", 1, ".cur", "cursors" }, + { "RT_BITMAP", 2, ".bmp", "bitmaps" }, + { "RT_ICON", 3, ".ico", "icons" }, + { "RT_MENU", 4, ".rc", "menus" }, + { "RT_DIALOG", 5, ".dlg", "dialogs" }, + { "RT_STRING", 6, ".rc", "strings" }, + { "RT_FONTDIR", 7, ".fnt", "fontdirs" }, + { "RT_FONT", 8, ".fnt", "fonts" }, + { "RT_ACCELERATOR", 9, ".rc", "accelerators" }, + { "RT_RCDATA", 10, ".rc", "rcdatas" }, + { "RT_MESSAGETABLE", 11, ".mc", "messagetables" }, + { "RT_GROUP_CURSOR", 12, ".cur", "groupcursors" }, + { "RT_GROUP_ICON", 14, ".ico", "groupicons" }, + { "RT_VERSION", 16, ".rc", "versions" }, + { "RT_DLGINCLUDE", 17, ".rc", "dlgincludes" }, + { "RT_PLUGPLAY", 19, ".rc", "plugplays" }, + { "RT_VXD", 20, ".rc", "xvds" }, + { "RT_ANICURSOR", 21, ".rc", "anicursors" }, + { "RT_ANIICON", 22, ".rc", "aniicons" }, + { "RT_HTML", 23, ".html", "htmls" }, + { "RT_MANIFEST", 24, ".xml", "manifests" }, + { "RT_DLGINIT", 240, ".rc", "dlginits" }, + { "RT_TOOLBAR", 241, ".rc", "toolbars" } }; +const char *resourceDir = "resources"; + struct options config; #endif diff --git a/src/pescan.c b/src/pescan.c index e12a2023..e85353b6 100644 --- a/src/pescan.c +++ b/src/pescan.c @@ -1,6 +1,6 @@ /* pev - the PE file analyzer toolkit - + pescan.c - search for suspicious things in PE files Copyright (C) 2012 pev authors @@ -49,7 +49,7 @@ static void parse_options(int argc, char *argv[]) {"verbose", no_argument, NULL, 'v'}, { NULL, 0, NULL, 0 } }; - + memset(&config, 0, sizeof(config)); while ((c = getopt_long(argc, argv, short_options, @@ -63,13 +63,13 @@ static void parse_options(int argc, char *argv[]) case 1: // --help option usage(); exit(EXIT_SUCCESS); - + case 'f': parse_format(optarg); break; - + case 'v': config.verbose = true; break; - + default: fprintf(stderr, "%s: try '--help' for more information\n", PROGRAM); exit(EXIT_FAILURE); @@ -104,7 +104,7 @@ static bool normal_dos_stub(PE_FILE *pe, DWORD *stub_offset) if (!fread(&data, sizeof(data), 1, pe->handle)) EXIT_ERROR("unable to read DOS stub"); - + if (memcmp(dos_stub, data, sizeof(data))==0) return true; @@ -143,11 +143,13 @@ static DWORD pe_get_tls_directory(PE_FILE *pe) if (pe->num_directories > 32) return 0; - for (unsigned int i=0; (i < pe->num_directories && pe->directories_ptr[i]); i++) - { - if ((i == IMAGE_DIRECTORY_ENTRY_TLS) && pe->directories_ptr[i]->Size > 0) - return pe->directories_ptr[i]->VirtualAddress; - } + IMAGE_DATA_DIRECTORY *directory = pe_get_data_directory(pe, IMAGE_DIRECTORY_ENTRY_TLS); + if (!directory) + return false; + + if (directory->Size > 0) + return directory->VirtualAddress; + return 0; } @@ -160,12 +162,12 @@ static int pe_get_tls_callbacks(PE_FILE *pe) { QWORD tls_addr = 0; int ret = 0; - + if (!pe) return 0; tls_addr = pe_get_tls_directory(pe); - + if (!tls_addr || !pe_get_sections(pe)) return 0; @@ -187,7 +189,7 @@ static int pe_get_tls_callbacks(PE_FILE *pe) IMAGE_TLS_DIRECTORY32 tlsdir32; if (!fread(&tlsdir32, sizeof(tlsdir32), 1, pe->handle)) - return 0; + return 0; if (! (tlsdir32.AddressOfCallBacks & pe->optional_ptr->_32->ImageBase)) break; @@ -201,7 +203,7 @@ static int pe_get_tls_callbacks(PE_FILE *pe) IMAGE_TLS_DIRECTORY64 tlsdir64; if (!fread(&tlsdir64, sizeof(tlsdir64), 1, pe->handle)) - return 0; + return 0; if (! (tlsdir64.AddressOfCallBacks & pe->optional_ptr->_64->ImageBase)) break; @@ -215,14 +217,14 @@ static int pe_get_tls_callbacks(PE_FILE *pe) ret = -1; // tls directory and section exists do - { + { fread(&funcaddr, sizeof(int), 1, pe->handle); if (funcaddr) { char value[MAX_MSG]; ret = ++j; // function found - + if (config.verbose) { snprintf(value, MAX_MSG, "%#x", funcaddr); @@ -284,7 +286,7 @@ static void print_strange_sections(PE_FILE *pe) for (unsigned i=0; i < pe->num_sections && i <= 65535; i++, aux=false) { memset(&value, 0, sizeof(value)); - + if (!strisprint((const char *)pe->sections_ptr[i]->Name)) stradd(value, "suspicious name", &aux); @@ -311,7 +313,7 @@ static bool normal_imagebase(PE_FILE *pe) if (!pe->imagebase) pe_get_optional(pe); - return (pe->imagebase == 0x100000000 || + return (pe->imagebase == 0x100000000 || pe->imagebase == 0x1000000 || pe->imagebase == 0x400000); } @@ -349,13 +351,13 @@ double calculate_entropy(const unsigned int byte_count[256], const int total_len { double entropy = 0.; const double log_2 = 1.44269504088896340736; - + for(unsigned int i = 0; i < 256; i++) { double temp = (double)byte_count[i] / total_length; if(temp > 0.) entropy += fabs(temp * (log(temp) * log_2)); - + } return entropy; @@ -407,7 +409,7 @@ int main(int argc, char *argv[]) // File entropy entropy = calculate_entropy_file(&pe); - + if(entropy < 7.0) snprintf(value, MAX_MSG, "normal (%f)", entropy); else @@ -434,7 +436,7 @@ int main(int argc, char *argv[]) snprintf(value, MAX_MSG, "normal - va: %#x - raw: %#"PRIx64, ep, rva2ofs(&pe, ep)); else snprintf(value, MAX_MSG, "normal"); - + output("entrypoint", value); // dos stub @@ -448,19 +450,19 @@ int main(int argc, char *argv[]) } else snprintf(value, MAX_MSG, "normal"); - + output("DOS stub", value); // tls callbacks callbacks = pe_get_tls_callbacks(&pe); - + if (callbacks == 0) snprintf(value, MAX_MSG, "not found"); else if (callbacks == -1) snprintf(value, MAX_MSG, "found - no functions"); else if (callbacks >0) snprintf(value, MAX_MSG, "found - %d function(s)", callbacks); - + output("TLS directory", value); memset(&value, 0, sizeof(value)); diff --git a/src/pesec.c b/src/pesec.c index 953f07e5..28dc3320 100644 --- a/src/pesec.c +++ b/src/pesec.c @@ -1,6 +1,6 @@ /* pev - the PE file analyzer toolkit - + pesec.c - Check for security features in PE files Copyright (C) 2012 pev authors @@ -20,39 +20,104 @@ */ #include "pesec.h" +#include +#include +#include +#include +#include -static int ind; - +typedef enum { + CERT_FORMAT_TEXT = 1, + CERT_FORMAT_PEM = 2, + CERT_FORMAT_DER = 3 +} cert_format_e; +typedef struct { + cert_format_e certoutform; + BIO *certout; +} options_t; static void usage() { printf("Usage: %s [OPTIONS] FILE\n" - "Check for security features in PE files\n" - "\nExample: %s wordpad.exe\n" - "\nOptions:\n" - " -f, --format change output format (default: text)\n" - " -v, --version show version and exit\n" - " --help show this help and exit\n", - PROGRAM, PROGRAM); + "Check for security features in PE files\n" + "\nExample: %s wordpad.exe\n" + "\nOptions:\n" + " -f, --format change output format (default: text)\n" + " -c, --certoutform specifies the certificate output format (default: text)\n" + " -o, --certout filename specifies the output filename to write certificates to (default: stdout)\n" + " -v, --version show version and exit\n" + " --help show this help and exit\n", + PROGRAM, PROGRAM); +} + +static cert_format_e parse_certoutform(const char *optarg) +{ + cert_format_e result; + if (strcmp(optarg, "text") == 0) + result = CERT_FORMAT_TEXT; + else if (strcmp(optarg, "pem") == 0) + result = CERT_FORMAT_PEM; + else if (strcmp(optarg, "der") == 0) + result = CERT_FORMAT_DER; + else + EXIT_ERROR("invalid cert_format option"); + return result; +} + +static BIO *parse_certout(const char *optarg) +{ + BIO *bio = BIO_new(BIO_s_file()); + if (bio == NULL) { + EXIT_ERROR("could not allocate BIO"); + } + + if (strcmp(optarg, "stdout") == 0) { + BIO_set_fp(bio, stdout, BIO_NOCLOSE); + } else if (strcmp(optarg, "stderr") == 0) { + BIO_set_fp(bio, stderr, BIO_NOCLOSE); + } else { + int ret = BIO_write_filename(bio, (char *)optarg); + if (ret == 0) { + BIO_free(bio); + EXIT_ERROR("failed to open file"); + } + } + + return bio; } -static void parse_options(int argc, char *argv[]) +static void free_options(options_t *options) { - int c; + if (options == NULL) + return; + + if (options->certout != NULL) + BIO_free(options->certout); + + free(options); +} + +static options_t *parse_options(int argc, char *argv[]) +{ + options_t *options = xmalloc(sizeof(options_t)); + memset(options, 0, sizeof(options_t)); /* Parameters for getopt_long() function */ - static const char short_options[] = "f:v"; + static const char short_options[] = "f:c:o:v"; static const struct option long_options[] = { - {"format", required_argument, NULL, 'f'}, - {"help", no_argument, NULL, 1 }, - {"version", no_argument, NULL, 'v'}, - { NULL, 0, NULL, 0 } + {"format", required_argument, NULL, 'f'}, + {"certoutform", required_argument, NULL, 'c'}, + {"certout", required_argument, NULL, 'o'}, + {"help", no_argument, NULL, 1 }, + {"version", no_argument, NULL, 'v'}, + { NULL, 0, NULL, 0 } }; - while ((c = getopt_long(argc, argv, short_options, - long_options, &ind))) + int c, ind; + + while ((c = getopt_long(argc, argv, short_options, long_options, &ind))) { if (c < 0) break; @@ -62,19 +127,25 @@ static void parse_options(int argc, char *argv[]) case 1: // --help option usage(); exit(EXIT_SUCCESS); - case 'f': - parse_format(optarg); break; - + parse_format(optarg); + break; case 'v': printf("%s %s\n%s\n", PROGRAM, TOOLKIT, COPY); exit(EXIT_SUCCESS); - + case 'c': + options->certoutform = parse_certoutform(optarg); + break; + case 'o': + options->certout = parse_certout(optarg); + break; default: fprintf(stderr, "%s: try '--help' for more information\n", PROGRAM); exit(EXIT_FAILURE); } } + + return options; } /* @@ -85,10 +156,11 @@ static bool stack_cookies(PE_FILE *pe) { unsigned int i, found = 0; unsigned char buff; - const unsigned char mvs2010[] = - {0x55, 0x8b, 0xec, 0x83, - 0x33, 0xc5, 0x33, 0xcd, - 0xe8, 0xc3}; + const unsigned char mvs2010[] = { + 0x55, 0x8b, 0xec, 0x83, + 0x33, 0xc5, 0x33, 0xcd, + 0xe8, 0xc3 + }; if (!pe) return false; @@ -107,28 +179,233 @@ static bool stack_cookies(PE_FILE *pe) found++; } } - + return (found == sizeof(mvs2010)); } -int main(int argc, char *argv[]) +static int round_up(int numToRound, int multiple) { - PE_FILE pe; - FILE *fp = NULL; - WORD dllchar = 0; - char field[MAX_MSG]; - - if (argc < 2) + if (multiple == 0) + return 0; + return (numToRound + multiple - 1) / multiple * multiple; +} + +static void print_certificate(BIO *out, cert_format_e format, X509 *cert) +{ + if (out == NULL) + return; + switch (format) { + default: + case CERT_FORMAT_TEXT: + X509_print(out, cert); + break; + case CERT_FORMAT_PEM: + PEM_write_bio_X509(out, cert); + break; + case CERT_FORMAT_DER: + EXIT_ERROR("DER format is not yet supported for output"); + break; + } +} + +static int parse_pkcs7_data(const options_t *options, const CRYPT_DATA_BLOB *blob) +{ + int result = 0; + const cert_format_e input_fmt = CERT_FORMAT_DER; + PKCS7 *p7 = NULL; + BIO *in = NULL; + + CRYPTO_malloc_init(); + ERR_load_crypto_strings(); + OpenSSL_add_all_algorithms(); + + in = BIO_new_mem_buf(blob->pbData, blob->cbData); + if (in == NULL) { + result = -2; + goto error; + } + + switch (input_fmt) { + default: EXIT_ERROR("unhandled input format for certificate"); + case CERT_FORMAT_DER: + p7 = d2i_PKCS7_bio(in, NULL); + break; + case CERT_FORMAT_PEM: + p7 = PEM_read_bio_PKCS7(in, NULL, NULL, NULL); + break; + } + if (p7 == NULL) { + ERR_print_errors_fp(stderr); + result = -3; + goto error; + } + + STACK_OF(X509) *certs = NULL; + + int type = OBJ_obj2nid(p7->type); + switch (type) { + default: break; + case NID_pkcs7_signed: // PKCS7_type_is_signed(p7) + certs = p7->d.sign->cert; + break; + case NID_pkcs7_signedAndEnveloped: // PKCS7_type_is_signedAndEnveloped(p7) + certs = p7->d.signed_and_enveloped->cert; + break; + } + + const int numcerts = certs != NULL ? sk_X509_num(certs) : 0; + for (int i = 0; i < numcerts; i++) { + X509 *cert = sk_X509_value(certs, i); + print_certificate(options->certout, options->certoutform, cert); + // NOTE: Calling X509_free(cert) is unnecessary. + } + + // Print whether certificate signature is valid + if (numcerts > 0) { + X509 *subject = sk_X509_value(certs, 0); + X509 *issuer = sk_X509_value(certs, numcerts - 1); + int valid_sig = X509_verify(subject, X509_get_pubkey(issuer)); + output("Signature", valid_sig == 1 ? "valid" : "invalid"); + } + + // Print signers + if (numcerts > 0) + output("Signers", NULL); + for (int i = 0; i < numcerts; i++) { + X509 *cert = sk_X509_value(certs, i); + X509_NAME *name = X509_get_subject_name(cert); + + int issuer_name_len = X509_NAME_get_text_by_NID(name, NID_commonName, NULL, 0); + if (issuer_name_len > 0) { + char issuer_name[issuer_name_len + 1]; + X509_NAME_get_text_by_NID(name, NID_commonName, issuer_name, issuer_name_len + 1); + output(NULL, issuer_name); + } + } + +error: + if (p7 != NULL) + PKCS7_free(p7); + if (in != NULL) + BIO_free(in); + + // Deallocate everything from OpenSSL_add_all_algorithms + EVP_cleanup(); + // Deallocate everything from ERR_load_crypto_strings + ERR_free_strings(); + + return result; +} + +static void parse_certificates(const options_t *options, PE_FILE *pe) +{ + if (!pe_get_directories(pe)) + EXIT_ERROR("unable to read the Directories entry from Optional header"); + + const IMAGE_DATA_DIRECTORY * const directory = pe_get_data_directory(pe, IMAGE_DIRECTORY_ENTRY_SECURITY); + if (directory == NULL) + return; + + if (directory->VirtualAddress == 0 || directory->Size == 0) + return; + + DWORD fileOffset = directory->VirtualAddress; // This a file pointer rather than a common RVA. + + output("Certificates", NULL); + while (fileOffset - directory->VirtualAddress < directory->Size) { + if (fseek(pe->handle, fileOffset, SEEK_SET)) + EXIT_ERROR("unable to seek"); + + DWORD dwCertLen = 0; + + // Read the size of this WIN_CERTIFICATE + if (!fread(&dwCertLen, sizeof(DWORD), 1, pe->handle)) + EXIT_ERROR("unable to read"); + + if (fseek(pe->handle, fileOffset, SEEK_SET)) + EXIT_ERROR("unable to seek"); + + WIN_CERTIFICATE *cert = xmalloc(dwCertLen); + + // Read the whole WIN_CERTIFICATE based on the previously read size + if (!fread(cert, dwCertLen, 1, pe->handle)) + EXIT_ERROR("unable to read"); + + static char value[MAX_MSG]; + + snprintf(value, MAX_MSG, "%u bytes", cert->dwLength); + output("Length", value); + + snprintf(value, MAX_MSG, "0x%x (%s)", cert->wRevision, + cert->wRevision == WIN_CERT_REVISION_1_0 ? "1" : + cert->wRevision == WIN_CERT_REVISION_2_0 ? "2" : "unknown"); + output("Revision", value); + + snprintf(value, MAX_MSG, "0x%x", cert->wCertificateType); + switch (cert->wCertificateType) { + default: strlcat(value, " (UNKNOWN)", MAX_MSG); break; + case WIN_CERT_TYPE_X509: strlcat(value, " (X509)", MAX_MSG); break; + case WIN_CERT_TYPE_PKCS_SIGNED_DATA: strlcat(value, " (PKCS_SIGNED_DATA)", MAX_MSG); break; + case WIN_CERT_TYPE_TS_STACK_SIGNED: strlcat(value, " (TS_STACK_SIGNED)", MAX_MSG); break; + } + output("Type", value); + + fileOffset += round_up(cert->dwLength, 8); // Offset to the next certificate. + + if (fileOffset - directory->VirtualAddress > directory->Size) + EXIT_ERROR("either the attribute certificate table or the Size field is corrupted"); + + switch (cert->wRevision) { + default: + EXIT_ERROR("unknown wRevision"); + case WIN_CERT_REVISION_1_0: + EXIT_ERROR("WIN_CERT_REVISION_1_0 is not supported"); + case WIN_CERT_REVISION_2_0: + break; + } + + switch (cert->wCertificateType) { + default: + EXIT_ERROR("unknown wCertificateType"); + case WIN_CERT_TYPE_X509: + EXIT_ERROR("WIN_CERT_TYPE_X509 is not supported"); + case WIN_CERT_TYPE_PKCS_SIGNED_DATA: + { + CRYPT_DATA_BLOB p7data; + p7data.cbData = cert->dwLength - offsetof(WIN_CERTIFICATE, bCertificate); + p7data.pbData = cert->bCertificate; + parse_pkcs7_data(options, &p7data); + break; + } + case WIN_CERT_TYPE_TS_STACK_SIGNED: + EXIT_ERROR("WIN_CERT_TYPE_TS_STACK_SIGNED is not supported"); + case WIN_CERT_TYPE_EFI_PKCS115: + EXIT_ERROR("WIN_CERT_TYPE_EFI_PKCS115 is not supported"); + case WIN_CERT_TYPE_EFI_GUID: + EXIT_ERROR("WIN_CERT_TYPE_EFI_GUID is not supported"); + } + + free(cert); + } +} + +int main(int argc, char *argv[]) +{ + if (argc < 2) { usage(); exit(1); } - parse_options(argc, argv); // opcoes - - if ((fp = fopen(argv[argc-1], "rb")) == NULL) + options_t *options = parse_options(argc, argv); // opcoes + + const char *path = argv[argc-1]; + + FILE *fp = NULL; + if ((fp = fopen(path, "rb")) == NULL) EXIT_ERROR("file not found or unreadable"); + PE_FILE pe; pe_init(&pe, fp); // inicializa o struct pe if (!is_pe(&pe)) @@ -137,6 +414,7 @@ int main(int argc, char *argv[]) if (!pe_get_optional(&pe)) return 1; + WORD dllchar = 0; if (pe.architecture == PE32) dllchar = pe.optional_ptr->_32->DllCharacteristics; else if (pe.architecture == PE64) @@ -144,24 +422,31 @@ int main(int argc, char *argv[]) else return 1; + char field[MAX_MSG]; + // aslr - snprintf(field, MAX_MSG, "ASLR"); + snprintf(field, MAX_MSG, "ASLR"); output(field, (dllchar & 0x40) ? "yes" : "no"); // dep/nx - snprintf(field, MAX_MSG, "DEP/NX"); + snprintf(field, MAX_MSG, "DEP/NX"); output(field, (dllchar & 0x100) ? "yes" : "no"); // seh - snprintf(field, MAX_MSG, "SEH"); + snprintf(field, MAX_MSG, "SEH"); output(field, (dllchar & 0x400) ? "no" : "yes"); // stack cookies snprintf(field, MAX_MSG, "Stack cookies (EXPERIMENTAL)"); output(field, stack_cookies(&pe) ? "yes" : "no"); - + + // certificados + parse_certificates(options, &pe); + // libera a memoria pe_deinit(&pe); - + + free_options(options); + return 0; } diff --git a/src/readpe.c b/src/readpe.c index de75e212..8cc1b438 100644 --- a/src/readpe.c +++ b/src/readpe.c @@ -1,6 +1,6 @@ /* pev - the PE file analyzer toolkit - + readpe.c - show PE file headers Copyright (C) 2012 pev authors @@ -116,7 +116,7 @@ void parse_options(int argc, char *argv[]) case 'h': config.all = false; parse_headers(optarg); break; - + case 'i': config.all = false; config.imports = true; break; @@ -225,7 +225,7 @@ static void print_directories(PE_FILE *pe) output("Data directories", NULL); - if (! pe->directories_ptr) + if (!pe->directories_ptr) return; for (i=0; i < pe->num_directories && i < 16; i++) @@ -583,18 +583,18 @@ static void print_imported_functions(PE_FILE *pe, long offset) char fname[MAX_FUNCTION_NAME]; char hintstr[16]; unsigned int i; - + if (fseek(pe->handle, offset, SEEK_SET)) return; memset(&fname, 0, sizeof(fname)); memset(&hintstr, 0, sizeof(hintstr)); - + while (1) { if (!fread(&fptr, (pe->architecture == PE64) ? sizeof(QWORD) : sizeof(DWORD), 1, pe->handle)) return; - + if (!fptr) break; @@ -605,26 +605,26 @@ static void print_imported_functions(PE_FILE *pe, long offset) { // save file pointer in functions array aux2 = ftell(pe->handle); - + if (fseek(pe->handle, rva2ofs(pe, fptr), SEEK_SET)) return; // follow function pointer if (!fread(&hint, sizeof(hint), 1, pe->handle)) return; - + for (i=0; ihandle)) return; - + if (!isprint((int)c)) // 0 and non-printable break; - + fname[i] = c; } snprintf(hintstr, 15, "%d", hint); - + // restore file pointer to functions array if (fseek(pe->handle, aux2, SEEK_SET)) return; @@ -635,7 +635,7 @@ static void print_imported_functions(PE_FILE *pe, long offset) memset(&fname, 0, sizeof(fname)); memset(&hintstr, 0, sizeof(hintstr)); } - + fseek(pe->handle, aux, SEEK_SET); } @@ -645,11 +645,16 @@ static void print_exports(PE_FILE *pe) IMAGE_EXPORT_DIRECTORY exp; DWORD rva, aux, faddr = 0; - va = pe->directories_ptr[IMAGE_DIRECTORY_ENTRY_EXPORT] ? - pe->directories_ptr[IMAGE_DIRECTORY_ENTRY_EXPORT]->VirtualAddress : 0; + IMAGE_DATA_DIRECTORY *directory = pe_get_data_directory(pe, IMAGE_DIRECTORY_ENTRY_EXPORT); + if (!directory) + EXIT_ERROR("export directory not found") + va = directory->VirtualAddress; if (!va) - EXIT_ERROR("export directory not found"); + { + fprintf(stderr, "export directory not found\n"); + return; + } if (fseek(pe->handle, rva2ofs(pe, va), SEEK_SET)) EXIT_ERROR("unable to seek until export directory"); @@ -697,15 +702,17 @@ static void print_imports(PE_FILE *pe) char dllname[MAX_DLL_NAME]; unsigned int i; - va = pe->directories_ptr[IMAGE_DIRECTORY_ENTRY_IMPORT] ? - pe->directories_ptr[IMAGE_DIRECTORY_ENTRY_IMPORT]->VirtualAddress : 0; + IMAGE_DATA_DIRECTORY *directory = pe_get_data_directory(pe, IMAGE_DIRECTORY_ENTRY_IMPORT); + if (!directory) + EXIT_ERROR("import directory not found") + va = directory->VirtualAddress; if (!va) EXIT_ERROR("import directory not found"); if (fseek(pe->handle, rva2ofs(pe, va), SEEK_SET)) EXIT_ERROR("error seeking file"); - + memset(&id, 0, sizeof(id)); memset(&dllname, 0, sizeof(dllname)); @@ -720,7 +727,7 @@ static void print_imports(PE_FILE *pe) aux = ftell(pe->handle); va = rva2ofs(pe, id.Name); - + if (!va) return; @@ -732,16 +739,16 @@ static void print_imports(PE_FILE *pe) for (i=0; i < MAX_DLL_NAME; i++) { fread(&c, sizeof(c), 1, pe->handle); - + if (!c) break; - + dllname[i] = c; } - + output(dllname, NULL); memset(&dllname, 0, sizeof(dllname)); - + if (fseek(pe->handle, aux, SEEK_SET)) // restore file pointer return; @@ -759,7 +766,7 @@ int main(int argc, char *argv[]) { PE_FILE pe; FILE *fp = NULL; - + if (argc < 2) { usage(); @@ -811,14 +818,14 @@ int main(int argc, char *argv[]) print_directories(&pe); else { EXIT_ERROR("unable to read the Directories entry from Optional header"); } } - + // imports if (config.imports || config.all) { if (pe_get_directories(&pe)) print_imports(&pe); else { EXIT_ERROR("unable to read the Directories entry from Optional header"); } - } + } // exports if (config.exports || config.all)