/** Return true if the file looks like it might be a PE file according to the magic number. The file must contain what * appears to be a DOS File Header at address zero, and what appears to be a PE File Header at a file offset specified in * part of the DOS File Header (actually, in the bytes that follow the DOS File Header). */ bool SgAsmPEFileHeader::is_PE(SgAsmGenericFile *file) { /* Turn off byte reference tracking for the duration of this function. We don't want our testing the file contents to * affect the list of bytes that we've already referenced or which we might reference later. */ bool was_tracking = file->get_tracking_references(); file->set_tracking_references(false); try { /* Check DOS File Header magic number at beginning of the file */ unsigned char dos_magic[2]; file->read_content(0, dos_magic, sizeof dos_magic); if ('M'!=dos_magic[0] || 'Z'!=dos_magic[1]) throw 1; /* Read four-byte offset of potential PE File Header at offset 0x3c */ uint32_t lfanew_disk; file->read_content(0x3c, &lfanew_disk, sizeof lfanew_disk); rose_addr_t pe_offset = le_to_host(lfanew_disk); /* Look for the PE File Header magic number */ unsigned char pe_magic[4]; file->read_content(pe_offset, pe_magic, sizeof pe_magic); if ('P'!=pe_magic[0] || 'E'!=pe_magic[1] || '\0'!=pe_magic[2] || '\0'!=pe_magic[3]) throw 1; } catch (...) { file->set_tracking_references(was_tracking); return false; } file->set_tracking_references(was_tracking); return true; }
// comment from Stefan Schneider-Kennedy: // > 8 byte little endian. 2^(64) - number of 100ns intervals since timing // > started. Like Microsoft's FILETIME. Round to nearest second. static float convert_time(const char* p) { uint64_t d; memcpy(&d, p, sizeof(d)); le_to_host(&d, sizeof(d)); return (~d) * 1.0e-7f; }
// comment from Stefan Schneider-Kennedy: // > 8 byte little endian. Number of 100ns intervals since 1859. // > Like Microsoft's FILETIME with an offset. // // comment from Stephan Messlinger: // > The magic offset of 3506716800 seconds between the CNF time format to // > unixtime does not point to 1859 but to Nov 17, 1958, 00:00. This is the // > starting point for the 'Modified Julian Date', which is commonly used in // > astronomy. Together with resolution of 100 ns, this points out to the DEC // > VMS time format (which again fits to the PDP11 floating point format for // > the energy coefficients). // static string convert_date(const char* p) { uint64_t d; memcpy(&d, p, sizeof(d)); le_to_host(&d, sizeof(d)); time_t t = d / 10000000 - 3506716800u; // time since the Epoch char s[64]; int r = strftime(s, sizeof(s), "%a, %Y-%m-%d %H:%M:%S", gmtime(&t)); if (r == 0) throw FormatError("reading date failed."); return string(s); }
/** Initialize the header with information parsed from the file and construct and parse everything that's reachable from the * header. The PE File Header should have been constructed such that SgAsmPEFileHeader::ctor() was called. */ SgAsmPEFileHeader* SgAsmPEFileHeader::parse() { SgAsmGenericHeader::parse(); /* Read header, zero padding if the file isn't large enough */ PEFileHeader_disk fh; if (sizeof(fh)>get_size()) extend(sizeof(fh)-get_size()); if (sizeof(fh)!=read_content_local(0, &fh, sizeof fh, false)) fprintf(stderr, "SgAsmPEFileHeader::parse: warning: short read of PE header at byte 0x%08"PRIx64"\n", get_offset()); /* Check magic number before getting too far */ if (fh.e_magic[0]!='P' || fh.e_magic[1]!='E' || fh.e_magic[2]!='\0' || fh.e_magic[3]!='\0') throw FormatError("Bad PE magic number"); /* Decode COFF file header */ p_e_cpu_type = le_to_host(fh.e_cpu_type); p_e_nsections = le_to_host(fh.e_nsections); p_e_time = le_to_host(fh.e_time); p_e_coff_symtab = le_to_host(fh.e_coff_symtab); p_e_coff_nsyms = le_to_host(fh.e_coff_nsyms); p_e_nt_hdr_size = le_to_host(fh.e_nt_hdr_size); p_e_flags = le_to_host(fh.e_flags); /* Read the "Optional Header" (optional in the sense that not all files have one, but required for an executable), the * size of which is stored in the e_nt_hdr_size of the main PE file header. According to * http://www.phreedom.org/solar/code/tinype the Windows loader honors the e_nt_hdr_size even when set to smaller than the * smallest possible documented size of the optional header. Also it's possible for the optional header to extend beyond * the end of the file, in which case that part should be read as zero. */ PE32OptHeader_disk oh32; rose_addr_t need32 = sizeof(PEFileHeader_disk) + std::min(p_e_nt_hdr_size, (rose_addr_t)(sizeof oh32)); if (need32>get_size()) extend(need32-get_size()); if (sizeof(oh32)!=read_content_local(sizeof fh, &oh32, sizeof oh32, false)) fprintf(stderr, "SgAsmPEFileHeader::parse: warning: short read of PE Optional Header at byte 0x%08"PRIx64"\n", get_offset() + sizeof(fh)); p_e_opt_magic = le_to_host(oh32.e_opt_magic); /* File format changes from ctor() */ p_exec_format->set_purpose(p_e_flags & HF_PROGRAM ? PURPOSE_EXECUTABLE : PURPOSE_LIBRARY); p_exec_format->set_word_size(0x010b==p_e_opt_magic? 4 : 8); /* Decode the optional header. */ rose_addr_t entry_rva; if (4==p_exec_format->get_word_size()) { p_e_lmajor = le_to_host(oh32.e_lmajor); p_e_lminor = le_to_host(oh32.e_lminor); p_e_code_size = le_to_host(oh32.e_code_size); p_e_data_size = le_to_host(oh32.e_data_size); p_e_bss_size = le_to_host(oh32.e_bss_size); entry_rva = le_to_host(oh32.e_entrypoint_rva); p_e_code_rva = le_to_host(oh32.e_code_rva); p_e_data_rva = le_to_host(oh32.e_data_rva); p_base_va = le_to_host(oh32.e_image_base); p_e_section_align = le_to_host(oh32.e_section_align); p_e_file_align = le_to_host(oh32.e_file_align); p_e_os_major = le_to_host(oh32.e_os_major); p_e_os_minor = le_to_host(oh32.e_os_minor); p_e_user_major = le_to_host(oh32.e_user_major); p_e_user_minor = le_to_host(oh32.e_user_minor); p_e_subsys_major = le_to_host(oh32.e_subsys_major); p_e_subsys_minor = le_to_host(oh32.e_subsys_minor); p_e_reserved9 = le_to_host(oh32.e_reserved9); p_e_image_size = le_to_host(oh32.e_image_size); p_e_header_size = le_to_host(oh32.e_header_size); p_e_file_checksum = le_to_host(oh32.e_file_checksum); p_e_subsystem = le_to_host(oh32.e_subsystem); p_e_dll_flags = le_to_host(oh32.e_dll_flags); p_e_stack_reserve_size = le_to_host(oh32.e_stack_reserve_size); p_e_stack_commit_size = le_to_host(oh32.e_stack_commit_size); p_e_heap_reserve_size = le_to_host(oh32.e_heap_reserve_size); p_e_heap_commit_size = le_to_host(oh32.e_heap_commit_size); p_e_loader_flags = le_to_host(oh32.e_loader_flags); p_e_num_rvasize_pairs = le_to_host(oh32.e_num_rvasize_pairs); } else if (8==p_exec_format->get_word_size()) { /* We guessed wrong. This is a 64-bit header, not 32-bit. */ PE64OptHeader_disk oh64; rose_addr_t need64 = sizeof(PEFileHeader_disk) + std::min(p_e_nt_hdr_size, (rose_addr_t)(sizeof oh64)); if (need64>get_size()) extend(need64-get_size()); if (sizeof(oh64)!=read_content_local(sizeof fh, &oh64, sizeof oh64)) fprintf(stderr, "SgAsmPEFileHeader::parse: warning: short read of PE Optional Header at byte 0x%08"PRIx64"\n", get_offset() + sizeof(fh)); p_e_lmajor = le_to_host(oh64.e_lmajor); p_e_lminor = le_to_host(oh64.e_lminor); p_e_code_size = le_to_host(oh64.e_code_size); p_e_data_size = le_to_host(oh64.e_data_size); p_e_bss_size = le_to_host(oh64.e_bss_size); entry_rva = le_to_host(oh64.e_entrypoint_rva); p_e_code_rva = le_to_host(oh64.e_code_rva); // p_e_data_rva = le_to_host(oh.e_data_rva); /* not in PE32+ */ p_base_va = le_to_host(oh64.e_image_base); p_e_section_align = le_to_host(oh64.e_section_align); p_e_file_align = le_to_host(oh64.e_file_align); p_e_os_major = le_to_host(oh64.e_os_major); p_e_os_minor = le_to_host(oh64.e_os_minor); p_e_user_major = le_to_host(oh64.e_user_major); p_e_user_minor = le_to_host(oh64.e_user_minor); p_e_subsys_major = le_to_host(oh64.e_subsys_major); p_e_subsys_minor = le_to_host(oh64.e_subsys_minor); p_e_reserved9 = le_to_host(oh64.e_reserved9); p_e_image_size = le_to_host(oh64.e_image_size); p_e_header_size = le_to_host(oh64.e_header_size); p_e_file_checksum = le_to_host(oh64.e_file_checksum); p_e_subsystem = le_to_host(oh64.e_subsystem); p_e_dll_flags = le_to_host(oh64.e_dll_flags); p_e_stack_reserve_size = le_to_host(oh64.e_stack_reserve_size); p_e_stack_commit_size = le_to_host(oh64.e_stack_commit_size); p_e_heap_reserve_size = le_to_host(oh64.e_heap_reserve_size); p_e_heap_commit_size = le_to_host(oh64.e_heap_commit_size); p_e_loader_flags = le_to_host(oh64.e_loader_flags); p_e_num_rvasize_pairs = le_to_host(oh64.e_num_rvasize_pairs); } else { throw FormatError("unrecognized Windows PE optional header magic number"); } /* Magic number */ p_magic.clear(); for (size_t i = 0; i < sizeof(fh.e_magic); ++i) p_magic.push_back(fh.e_magic[i]); /* File format */ ROSE_ASSERT(p_e_lmajor <= 0xffff && p_e_lminor <= 0xffff); p_exec_format->set_version((p_e_lmajor << 16) | p_e_lminor); p_exec_format->set_is_current_version(true); /*FIXME*/ /* Target architecture */ switch (p_e_cpu_type) { case 0x0000: set_isa(ISA_UNSPECIFIED); break; case 0x014c: set_isa(ISA_IA32_386); break; case 0x014d: set_isa(ISA_IA32_486); break; case 0x014e: set_isa(ISA_IA32_Pentium); break; case 0x0162: set_isa(ISA_MIPS_MarkI); /* R2000, R3000 */ break; case 0x0163: set_isa(ISA_MIPS_MarkII); /* R6000 */ break; case 0x0166: set_isa(ISA_MIPS_MarkIII); /* R4000 */ break; case 0x01a2: /*Hitachi SH3*/ case 0x01a3: /*Hitachi SH3 with FPU*/ case 0x01a6: /*Hitachi SH4*/ case 0x01a8: /*Hitachi SH5*/ set_isa(ISA_Hitachi_SH); break; case 0x01c0: set_isa(ISA_ARM_Family); break; case 0x01d3: set_isa(ISA_Matsushita_AM33); break; case 0x01f0: /*w/o FPU*/ case 0x01f1: /*with FPU*/ set_isa(ISA_PowerPC); break; case 0x0200: set_isa(ISA_IA64_Family); break; case 0x0266: set_isa(ISA_MIPS_16); break; case 0x0366: set_isa(ISA_MIPS_FPU); break; case 0x0466: set_isa(ISA_MIPS_16FPU); break; case 0x0ebc: set_isa(ISA_EFI_ByteCode); break; case 0x8664: set_isa(ISA_X8664_Family); break; case 0x9041: set_isa(ISA_Mitsubishi_M32R); break; default: fprintf(stderr, "SgAsmPEFileHeader::parse: warning: unrecognized e_cputype = 0x%x (%u)\n", p_e_cpu_type, p_e_cpu_type); set_isa(ISA_OTHER); break; } /* The NT loader normally maps this file header at the header's base virtual address. */ set_mapped_preferred_rva(0); set_mapped_actual_va(0); /* will be assigned by BinaryLoader */ set_mapped_size(p_e_header_size); set_mapped_alignment(0); set_mapped_rperm(true); set_mapped_wperm(false); set_mapped_xperm(false); /* Entry point. We will eventually bind the entry point to a particular section (in SgAsmPEFileHeader::parse) so that if * sections are rearranged, extended, etc. the entry point will be updated automatically. */ add_entry_rva(entry_rva); /* The PE File Header has a fixed-size component followed by some number of RVA/Size pairs. The add_rvasize_pairs() will * extend the header and parse the RVA/Size pairs. */ if (get_e_num_rvasize_pairs() > 1000) { fprintf(stderr, "warning: PE File Header contains an unreasonable number of Rva/Size pairs. Limiting to 1000.\n"); set_e_num_rvasize_pairs(1000); } add_rvasize_pairs(); /* Construct the section table and its sections (non-synthesized sections). The specification says that the section table * comes after the optional (NT) header, which in turn comes after the fixed part of the PE header. The size of the * optional header is indicated in the fixed header. */ rose_addr_t secttab_offset = get_offset() + sizeof(PEFileHeader_disk) + get_e_nt_hdr_size(); rose_addr_t secttab_size = get_e_nsections() * sizeof(SgAsmPESectionTableEntry::PESectionTableEntry_disk); SgAsmPESectionTable *secttab = new SgAsmPESectionTable(this); secttab->set_offset(secttab_offset); secttab->set_size(secttab_size); secttab->parse(); set_section_table(secttab); /* Parse the COFF symbol table */ if (get_e_coff_symtab() && get_e_coff_nsyms()) { SgAsmCoffSymbolTable *symtab = new SgAsmCoffSymbolTable(this); symtab->set_offset(get_e_coff_symtab()); symtab->parse(); set_coff_symtab(symtab); } /* Associate RVAs with particular sections so that if a section's mapping is changed the RVA gets adjusted automatically. */ ROSE_ASSERT(get_entry_rvas().size()==1); get_entry_rvas()[0].bind(this); set_e_code_rva(get_e_code_rva().bind(this)); set_e_data_rva(get_e_data_rva().bind(this)); /* Turn header-specified tables (RVA/Size pairs) into generic sections */ create_table_sections(); return this; }