Remove submodule, just put Dependencies in ./libs
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
from hachoir_parser.program.elf import ElfFile
|
||||
from hachoir_parser.program.exe import ExeFile
|
||||
from hachoir_parser.program.python import PythonCompiledFile
|
||||
from hachoir_parser.program.java import JavaCompiledClassFile
|
||||
from hachoir_parser.program.prc import PRCFile
|
||||
|
||||
@@ -0,0 +1,187 @@
|
||||
"""
|
||||
ELF (Unix/BSD executable file format) parser.
|
||||
|
||||
Author: Victor Stinner
|
||||
Creation date: 08 may 2006
|
||||
"""
|
||||
|
||||
from hachoir_parser import Parser
|
||||
from hachoir_core.field import (FieldSet, ParserError,
|
||||
UInt8, UInt16, UInt32, Enum,
|
||||
String, Bytes)
|
||||
from hachoir_core.text_handler import textHandler, hexadecimal
|
||||
from hachoir_core.endian import LITTLE_ENDIAN, BIG_ENDIAN
|
||||
|
||||
class ElfHeader(FieldSet):
|
||||
static_size = 52*8
|
||||
LITTLE_ENDIAN_ID = 1
|
||||
BIG_ENDIAN_ID = 2
|
||||
MACHINE_NAME = {
|
||||
1: u"AT&T WE 32100",
|
||||
2: u"SPARC",
|
||||
3: u"Intel 80386",
|
||||
4: u"Motorola 68000",
|
||||
5: u"Motorola 88000",
|
||||
7: u"Intel 80860",
|
||||
8: u"MIPS RS3000"
|
||||
}
|
||||
CLASS_NAME = {
|
||||
1: u"32 bits",
|
||||
2: u"64 bits"
|
||||
}
|
||||
TYPE_NAME = {
|
||||
0: u"No file type",
|
||||
1: u"Relocatable file",
|
||||
2: u"Executable file",
|
||||
3: u"Shared object file",
|
||||
4: u"Core file",
|
||||
0xFF00: u"Processor-specific (0xFF00)",
|
||||
0xFFFF: u"Processor-specific (0xFFFF)"
|
||||
}
|
||||
ENDIAN_NAME = {
|
||||
LITTLE_ENDIAN_ID: "Little endian",
|
||||
BIG_ENDIAN_ID: "Big endian",
|
||||
}
|
||||
|
||||
def createFields(self):
|
||||
yield Bytes(self, "signature", 4, r'ELF signature ("\x7fELF")')
|
||||
yield Enum(UInt8(self, "class", "Class"), self.CLASS_NAME)
|
||||
yield Enum(UInt8(self, "endian", "Endian"), self.ENDIAN_NAME)
|
||||
yield UInt8(self, "file_version", "File version")
|
||||
yield String(self, "pad", 8, "Pad")
|
||||
yield UInt8(self, "nb_ident", "Size of ident[]")
|
||||
yield Enum(UInt16(self, "type", "File type"), self.TYPE_NAME)
|
||||
yield Enum(UInt16(self, "machine", "Machine type"), self.MACHINE_NAME)
|
||||
yield UInt32(self, "version", "ELF format version")
|
||||
yield UInt32(self, "entry", "Number of entries")
|
||||
yield UInt32(self, "phoff", "Program header offset")
|
||||
yield UInt32(self, "shoff", "Section header offset")
|
||||
yield UInt32(self, "flags", "Flags")
|
||||
yield UInt16(self, "ehsize", "Elf header size (this header)")
|
||||
yield UInt16(self, "phentsize", "Program header entry size")
|
||||
yield UInt16(self, "phnum", "Program header entry count")
|
||||
yield UInt16(self, "shentsize", "Section header entry size")
|
||||
yield UInt16(self, "shnum", "Section header entre count")
|
||||
yield UInt16(self, "shstrndx", "Section header strtab index")
|
||||
|
||||
def isValid(self):
|
||||
if self["signature"].value != "\x7FELF":
|
||||
return "Wrong ELF signature"
|
||||
if self["class"].value not in self.CLASS_NAME:
|
||||
return "Unknown class"
|
||||
if self["endian"].value not in self.ENDIAN_NAME:
|
||||
return "Unknown endian (%s)" % self["endian"].value
|
||||
return ""
|
||||
|
||||
class SectionHeader32(FieldSet):
|
||||
static_size = 40*8
|
||||
TYPE_NAME = {
|
||||
8: "BSS"
|
||||
}
|
||||
|
||||
def createFields(self):
|
||||
yield UInt32(self, "name", "Name")
|
||||
yield Enum(UInt32(self, "type", "Type"), self.TYPE_NAME)
|
||||
yield UInt32(self, "flags", "Flags")
|
||||
yield textHandler(UInt32(self, "VMA", "Virtual memory address"), hexadecimal)
|
||||
yield textHandler(UInt32(self, "LMA", "Logical memory address (in file)"), hexadecimal)
|
||||
yield textHandler(UInt32(self, "size", "Size"), hexadecimal)
|
||||
yield UInt32(self, "link", "Link")
|
||||
yield UInt32(self, "info", "Information")
|
||||
yield UInt32(self, "addr_align", "Address alignment")
|
||||
yield UInt32(self, "entry_size", "Entry size")
|
||||
|
||||
def createDescription(self):
|
||||
return "Section header (name: %s, type: %s)" % \
|
||||
(self["name"].value, self["type"].display)
|
||||
|
||||
class ProgramHeader32(FieldSet):
|
||||
TYPE_NAME = {
|
||||
3: "Dynamic library"
|
||||
}
|
||||
static_size = 32*8
|
||||
|
||||
def createFields(self):
|
||||
yield Enum(UInt16(self, "type", "Type"), ProgramHeader32.TYPE_NAME)
|
||||
yield UInt16(self, "flags", "Flags")
|
||||
yield UInt32(self, "offset", "Offset")
|
||||
yield textHandler(UInt32(self, "vaddr", "V. address"), hexadecimal)
|
||||
yield textHandler(UInt32(self, "paddr", "P. address"), hexadecimal)
|
||||
yield UInt32(self, "file_size", "File size")
|
||||
yield UInt32(self, "mem_size", "Memory size")
|
||||
yield UInt32(self, "align", "Alignment")
|
||||
yield UInt32(self, "xxx", "???")
|
||||
|
||||
def createDescription(self):
|
||||
return "Program Header (%s)" % self["type"].display
|
||||
|
||||
def sortSection(a, b):
|
||||
return int(a["offset"] - b["offset"])
|
||||
|
||||
#class Sections(FieldSet):
|
||||
# def createFields?(self, stream, parent, sections):
|
||||
# for section in sections:
|
||||
# ofs = section["offset"]
|
||||
# size = section["file_size"]
|
||||
# if size != 0:
|
||||
# sub = stream.createSub(ofs, size)
|
||||
# #yield DeflateFilter(self, "section[]", sub, size, Section, "Section"))
|
||||
# chunk = self.doRead("section[]", "Section", (Section,), {"stream": sub})
|
||||
# else:
|
||||
# chunk = self.doRead("section[]", "Section", (FormatChunk, "string[0]"))
|
||||
# chunk.description = "ELF section (in file: %s..%s)" % (ofs, ofs+size)
|
||||
|
||||
class ElfFile(Parser):
|
||||
PARSER_TAGS = {
|
||||
"id": "elf",
|
||||
"category": "program",
|
||||
"file_ext": ("so", ""),
|
||||
"min_size": ElfHeader.static_size, # At least one program header
|
||||
"mime": (
|
||||
u"application/x-executable",
|
||||
u"application/x-object",
|
||||
u"application/x-sharedlib",
|
||||
u"application/x-executable-file",
|
||||
u"application/x-coredump"),
|
||||
"magic": (("\x7FELF", 0),),
|
||||
"description": "ELF Unix/BSD program/library"
|
||||
}
|
||||
endian = LITTLE_ENDIAN
|
||||
|
||||
def validate(self):
|
||||
err = self["header"].isValid()
|
||||
if err:
|
||||
return err
|
||||
return True
|
||||
|
||||
def createFields(self):
|
||||
# Choose the right endian depending on endian specified in header
|
||||
if self.stream.readBits(5*8, 8, BIG_ENDIAN) == ElfHeader.BIG_ENDIAN_ID:
|
||||
self.endian = BIG_ENDIAN
|
||||
else:
|
||||
self.endian = LITTLE_ENDIAN
|
||||
|
||||
# Parse header and program headers
|
||||
yield ElfHeader(self, "header", "Header")
|
||||
for index in xrange(self["header/phnum"].value):
|
||||
yield ProgramHeader32(self, "prg_header[]")
|
||||
|
||||
if False:
|
||||
raise ParserError("TODO: Parse sections...")
|
||||
#sections = self.array("prg_header")
|
||||
#size = self["header/shoff"].value - self.current_size//8
|
||||
#chunk = self.doRead("data", "Data", (DeflateFilter, stream, size, Sections, sections))
|
||||
#chunk.description = "Sections (use an evil hack to manage share same data on differents parts)"
|
||||
#assert self.current_size//8 == self["header/shoff"].value
|
||||
else:
|
||||
raw = self.seekByte(self["header/shoff"].value, "raw[]", relative=False)
|
||||
if raw:
|
||||
yield raw
|
||||
|
||||
for index in xrange(self["header/shnum"].value):
|
||||
yield SectionHeader32(self, "section_header[]")
|
||||
|
||||
def createDescription(self):
|
||||
return "ELF Unix/BSD program/library: %s" % (
|
||||
self["header/class"].display)
|
||||
|
||||
@@ -0,0 +1,224 @@
|
||||
"""
|
||||
Microsoft Windows Portable Executable (PE) file parser.
|
||||
|
||||
Informations:
|
||||
- Microsoft Portable Executable and Common Object File Format Specification:
|
||||
http://www.microsoft.com/whdc/system/platform/firmware/PECOFF.mspx
|
||||
|
||||
Author: Victor Stinner
|
||||
Creation date: 2006-08-13
|
||||
"""
|
||||
|
||||
from hachoir_parser import HachoirParser
|
||||
from hachoir_core.endian import LITTLE_ENDIAN
|
||||
from hachoir_core.field import (FieldSet, RootSeekableFieldSet,
|
||||
UInt16, UInt32, String,
|
||||
RawBytes, PaddingBytes)
|
||||
from hachoir_core.text_handler import textHandler, hexadecimal
|
||||
from hachoir_parser.program.exe_ne import NE_Header
|
||||
from hachoir_parser.program.exe_pe import PE_Header, PE_OptHeader, SectionHeader
|
||||
from hachoir_parser.program.exe_res import PE_Resource, NE_VersionInfoNode
|
||||
|
||||
MAX_NB_SECTION = 50
|
||||
|
||||
class MSDosHeader(FieldSet):
|
||||
static_size = 64*8
|
||||
|
||||
def createFields(self):
|
||||
yield String(self, "header", 2, "File header (MZ)", charset="ASCII")
|
||||
yield UInt16(self, "size_mod_512", "File size in bytes modulo 512")
|
||||
yield UInt16(self, "size_div_512", "File size in bytes divide by 512")
|
||||
yield UInt16(self, "reloc_entries", "Number of relocation entries")
|
||||
yield UInt16(self, "code_offset", "Offset to the code in the file (divided by 16)")
|
||||
yield UInt16(self, "needed_memory", "Memory needed to run (divided by 16)")
|
||||
yield UInt16(self, "max_memory", "Maximum memory needed to run (divided by 16)")
|
||||
yield textHandler(UInt32(self, "init_ss_sp", "Initial value of SP:SS registers"), hexadecimal)
|
||||
yield UInt16(self, "checksum", "Checksum")
|
||||
yield textHandler(UInt32(self, "init_cs_ip", "Initial value of CS:IP registers"), hexadecimal)
|
||||
yield UInt16(self, "reloc_offset", "Offset in file to relocation table")
|
||||
yield UInt16(self, "overlay_number", "Overlay number")
|
||||
yield PaddingBytes(self, "reserved[]", 8, "Reserved")
|
||||
yield UInt16(self, "oem_id", "OEM id")
|
||||
yield UInt16(self, "oem_info", "OEM info")
|
||||
yield PaddingBytes(self, "reserved[]", 20, "Reserved")
|
||||
yield UInt32(self, "next_offset", "Offset to next header (PE or NE)")
|
||||
|
||||
def isValid(self):
|
||||
if 512 <= self["size_mod_512"].value:
|
||||
return "Invalid field 'size_mod_512' value"
|
||||
if self["code_offset"].value < 4:
|
||||
return "Invalid code offset"
|
||||
looks_pe = self["size_div_512"].value < 4
|
||||
if looks_pe:
|
||||
if self["checksum"].value != 0:
|
||||
return "Invalid value of checksum"
|
||||
if not (80 <= self["next_offset"].value <= 1024):
|
||||
return "Invalid value of next_offset"
|
||||
return ""
|
||||
|
||||
class ExeFile(HachoirParser, RootSeekableFieldSet):
|
||||
PARSER_TAGS = {
|
||||
"id": "exe",
|
||||
"category": "program",
|
||||
"file_ext": ("exe", "dll", "ocx"),
|
||||
"mime": (u"application/x-dosexec",),
|
||||
"min_size": 64*8,
|
||||
#"magic": (("MZ", 0),),
|
||||
"magic_regex": (("MZ.[\0\1].{4}[^\0\1\2\3]", 0),),
|
||||
"description": "Microsoft Windows Portable Executable"
|
||||
}
|
||||
endian = LITTLE_ENDIAN
|
||||
|
||||
def __init__(self, stream, **args):
|
||||
RootSeekableFieldSet.__init__(self, None, "root", stream, None, stream.askSize(self))
|
||||
HachoirParser.__init__(self, stream, **args)
|
||||
|
||||
def validate(self):
|
||||
if self.stream.readBytes(0, 2) != 'MZ':
|
||||
return "Wrong header"
|
||||
err = self["msdos"].isValid()
|
||||
if err:
|
||||
return "Invalid MSDOS header: "+err
|
||||
if self.isPE():
|
||||
if MAX_NB_SECTION < self["pe_header/nb_section"].value:
|
||||
return "Invalid number of section (%s)" \
|
||||
% self["pe_header/nb_section"].value
|
||||
return True
|
||||
|
||||
def createFields(self):
|
||||
yield MSDosHeader(self, "msdos", "MS-DOS program header")
|
||||
|
||||
if self.isPE() or self.isNE():
|
||||
offset = self["msdos/next_offset"].value
|
||||
self.seekByte(offset, relative=False)
|
||||
|
||||
if self.isPE():
|
||||
for field in self.parsePortableExecutable():
|
||||
yield field
|
||||
elif self.isNE():
|
||||
for field in self.parseNE_Executable():
|
||||
yield field
|
||||
else:
|
||||
offset = self["msdos/code_offset"].value * 16
|
||||
self.seekByte(offset, relative=False)
|
||||
|
||||
def parseNE_Executable(self):
|
||||
yield NE_Header(self, "ne_header")
|
||||
|
||||
# FIXME: Compute resource offset instead of using searchBytes()
|
||||
# Ugly hack to get find version info structure
|
||||
start = self.current_size
|
||||
addr = self.stream.searchBytes('VS_VERSION_INFO', start)
|
||||
if addr:
|
||||
self.seekBit(addr-32)
|
||||
yield NE_VersionInfoNode(self, "info")
|
||||
|
||||
def parsePortableExecutable(self):
|
||||
# Read PE header
|
||||
yield PE_Header(self, "pe_header")
|
||||
|
||||
# Read PE optional header
|
||||
size = self["pe_header/opt_hdr_size"].value
|
||||
rsrc_rva = None
|
||||
if size:
|
||||
yield PE_OptHeader(self, "pe_opt_header", size=size*8)
|
||||
if "pe_opt_header/resource/rva" in self:
|
||||
rsrc_rva = self["pe_opt_header/resource/rva"].value
|
||||
|
||||
# Read section headers
|
||||
sections = []
|
||||
for index in xrange(self["pe_header/nb_section"].value):
|
||||
section = SectionHeader(self, "section_hdr[]")
|
||||
yield section
|
||||
if section["phys_size"].value:
|
||||
sections.append(section)
|
||||
|
||||
# Read sections
|
||||
sections.sort(key=lambda field: field["phys_off"].value)
|
||||
for section in sections:
|
||||
self.seekByte(section["phys_off"].value)
|
||||
size = section["phys_size"].value
|
||||
if size:
|
||||
name = section.createSectionName()
|
||||
if rsrc_rva is not None and section["rva"].value == rsrc_rva:
|
||||
yield PE_Resource(self, name, section, size=size*8)
|
||||
else:
|
||||
yield RawBytes(self, name, size)
|
||||
|
||||
def isPE(self):
|
||||
if not hasattr(self, "_is_pe"):
|
||||
self._is_pe = False
|
||||
offset = self["msdos/next_offset"].value * 8
|
||||
if 2*8 <= offset \
|
||||
and (offset+PE_Header.static_size) <= self.size \
|
||||
and self.stream.readBytes(offset, 4) == 'PE\0\0':
|
||||
self._is_pe = True
|
||||
return self._is_pe
|
||||
|
||||
def isNE(self):
|
||||
if not hasattr(self, "_is_ne"):
|
||||
self._is_ne = False
|
||||
offset = self["msdos/next_offset"].value * 8
|
||||
if 64*8 <= offset \
|
||||
and (offset+NE_Header.static_size) <= self.size \
|
||||
and self.stream.readBytes(offset, 2) == 'NE':
|
||||
self._is_ne = True
|
||||
return self._is_ne
|
||||
|
||||
def getResource(self):
|
||||
# MS-DOS program: no resource
|
||||
if not self.isPE():
|
||||
return None
|
||||
|
||||
# Check if PE has resource or not
|
||||
if "pe_opt_header/resource/size" in self:
|
||||
if not self["pe_opt_header/resource/size"].value:
|
||||
return None
|
||||
if "section_rsrc" in self:
|
||||
return self["section_rsrc"]
|
||||
return None
|
||||
|
||||
def createDescription(self):
|
||||
if self.isPE():
|
||||
if self["pe_header/is_dll"].value:
|
||||
text = u"Microsoft Windows DLL"
|
||||
else:
|
||||
text = u"Microsoft Windows Portable Executable"
|
||||
info = [self["pe_header/cpu"].display]
|
||||
if "pe_opt_header" in self:
|
||||
hdr = self["pe_opt_header"]
|
||||
info.append(hdr["subsystem"].display)
|
||||
if self["pe_header/is_stripped"].value:
|
||||
info.append(u"stripped")
|
||||
return u"%s: %s" % (text, ", ".join(info))
|
||||
elif self.isNE():
|
||||
return u"New-style Executable (NE) for Microsoft MS Windows 3.x"
|
||||
else:
|
||||
return u"MS-DOS executable"
|
||||
|
||||
def createContentSize(self):
|
||||
if self.isPE():
|
||||
size = 0
|
||||
for index in xrange(self["pe_header/nb_section"].value):
|
||||
section = self["section_hdr[%u]" % index]
|
||||
section_size = section["phys_size"].value
|
||||
if not section_size:
|
||||
continue
|
||||
section_size = (section_size + section["phys_off"].value) * 8
|
||||
if size:
|
||||
size = max(size, section_size)
|
||||
else:
|
||||
size = section_size
|
||||
if size:
|
||||
return size
|
||||
else:
|
||||
return None
|
||||
elif self.isNE():
|
||||
# TODO: Guess NE size
|
||||
return None
|
||||
else:
|
||||
size = self["msdos/size_mod_512"].value + (self["msdos/size_div_512"].value-1) * 512
|
||||
if size < 0:
|
||||
return None
|
||||
return size*8
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
from hachoir_core.field import (FieldSet,
|
||||
Bit, UInt8, UInt16, UInt32, Bytes,
|
||||
PaddingBits, PaddingBytes, NullBits, NullBytes)
|
||||
from hachoir_core.text_handler import textHandler, hexadecimal, filesizeHandler
|
||||
|
||||
class NE_Header(FieldSet):
|
||||
static_size = 64*8
|
||||
def createFields(self):
|
||||
yield Bytes(self, "signature", 2, "New executable signature (NE)")
|
||||
yield UInt8(self, "link_ver", "Linker version number")
|
||||
yield UInt8(self, "link_rev", "Linker revision number")
|
||||
yield UInt16(self, "entry_table_ofst", "Offset to the entry table")
|
||||
yield UInt16(self, "entry_table_size", "Length (in bytes) of the entry table")
|
||||
yield PaddingBytes(self, "reserved[]", 4)
|
||||
|
||||
yield Bit(self, "is_dll", "Is a dynamic-link library (DLL)?")
|
||||
yield Bit(self, "is_win_app", "Is a Windows application?")
|
||||
yield PaddingBits(self, "reserved[]", 9)
|
||||
yield Bit(self, "first_seg_code", "First segment contains code that loads the application?")
|
||||
yield NullBits(self, "reserved[]", 1)
|
||||
yield Bit(self, "link_error", "Load even if linker detects errors?")
|
||||
yield NullBits(self, "reserved[]", 1)
|
||||
yield Bit(self, "is_lib", "Is a library module?")
|
||||
|
||||
yield UInt16(self, "auto_data_seg", "Automatic data segment number")
|
||||
yield filesizeHandler(UInt16(self, "local_heap_size", "Initial size (in bytes) of the local heap"))
|
||||
yield filesizeHandler(UInt16(self, "stack_size", "Initial size (in bytes) of the stack"))
|
||||
yield textHandler(UInt32(self, "cs_ip", "Value of CS:IP"), hexadecimal)
|
||||
yield textHandler(UInt32(self, "ss_sp", "Value of SS:SP"), hexadecimal)
|
||||
|
||||
yield UInt16(self, "nb_entry_seg_tab", "Number of entries in the segment table")
|
||||
yield UInt16(self, "nb_entry_modref_tab", "Number of entries in the module-reference table")
|
||||
yield filesizeHandler(UInt16(self, "size_nonres_name_tab", "Number of bytes in the nonresident-name table"))
|
||||
yield UInt16(self, "seg_tab_ofs", "Segment table offset")
|
||||
yield UInt16(self, "rsrc_ofs", "Resource offset")
|
||||
|
||||
yield UInt16(self, "res_name_tab_ofs", "Resident-name table offset")
|
||||
yield UInt16(self, "mod_ref_tab_ofs", "Module-reference table offset")
|
||||
yield UInt16(self, "import_tab_ofs", "Imported-name table offset")
|
||||
|
||||
yield UInt32(self, "non_res_name_tab_ofs", "Nonresident-name table offset")
|
||||
yield UInt16(self, "nb_mov_ent_pt", "Number of movable entry points")
|
||||
yield UInt16(self, "log2_sector_size", "Log2 of the segment sector size")
|
||||
yield UInt16(self, "nb_rsrc_seg", "Number of resource segments")
|
||||
|
||||
yield Bit(self, "unknown_os_format", "Operating system format is unknown")
|
||||
yield PaddingBits(self, "reserved[]", 1)
|
||||
yield Bit(self, "os_windows", "Operating system is Microsoft Windows")
|
||||
yield NullBits(self, "reserved[]", 6)
|
||||
yield Bit(self, "is_win20_prot", "Is Windows 2.x application running in version 3.x protected mode")
|
||||
yield Bit(self, "is_win20_font", "Is Windows 2.x application supporting proportional fonts")
|
||||
yield Bit(self, "fast_load", "Contains a fast-load area?")
|
||||
yield NullBits(self, "reserved[]", 4)
|
||||
|
||||
yield UInt16(self, "fastload_ofs", "Fast-load area offset (in sector)")
|
||||
yield UInt16(self, "fastload_size", "Fast-load area length (in sector)")
|
||||
|
||||
yield NullBytes(self, "reserved[]", 2)
|
||||
yield textHandler(UInt16(self, "win_version", "Expected Windows version number"), hexadecimal)
|
||||
|
||||
@@ -0,0 +1,221 @@
|
||||
from hachoir_core.field import (FieldSet, ParserError,
|
||||
Bit, UInt8, UInt16, UInt32, TimestampUnix32,
|
||||
Bytes, String, Enum,
|
||||
PaddingBytes, PaddingBits, NullBytes, NullBits)
|
||||
from hachoir_core.text_handler import textHandler, hexadecimal, filesizeHandler
|
||||
from hachoir_core.error import HACHOIR_ERRORS
|
||||
|
||||
class SectionHeader(FieldSet):
|
||||
static_size = 40 * 8
|
||||
def createFields(self):
|
||||
yield String(self, "name", 8, charset="ASCII", strip="\0 ")
|
||||
yield filesizeHandler(UInt32(self, "mem_size", "Size in memory"))
|
||||
yield textHandler(UInt32(self, "rva", "RVA (location) in memory"), hexadecimal)
|
||||
yield filesizeHandler(UInt32(self, "phys_size", "Physical size (on disk)"))
|
||||
yield filesizeHandler(UInt32(self, "phys_off", "Physical location (on disk)"))
|
||||
yield PaddingBytes(self, "reserved", 12)
|
||||
|
||||
# 0x0000000#
|
||||
yield NullBits(self, "reserved[]", 4)
|
||||
# 0x000000#0
|
||||
yield NullBits(self, "reserved[]", 1)
|
||||
yield Bit(self, "has_code", "Contains code")
|
||||
yield Bit(self, "has_init_data", "Contains initialized data")
|
||||
yield Bit(self, "has_uninit_data", "Contains uninitialized data")
|
||||
# 0x00000#00
|
||||
yield NullBits(self, "reserved[]", 1)
|
||||
yield Bit(self, "has_comment", "Contains comments?")
|
||||
yield NullBits(self, "reserved[]", 1)
|
||||
yield Bit(self, "remove", "Contents will not become part of image")
|
||||
# 0x0000#000
|
||||
yield Bit(self, "has_comdata", "Contains comdat?")
|
||||
yield NullBits(self, "reserved[]", 1)
|
||||
yield Bit(self, "no_defer_spec_exc", "Reset speculative exceptions handling bits in the TLB entries")
|
||||
yield Bit(self, "gp_rel", "Content can be accessed relative to GP")
|
||||
# 0x000#0000
|
||||
yield NullBits(self, "reserved[]", 4)
|
||||
# 0x00#00000
|
||||
yield NullBits(self, "reserved[]", 4)
|
||||
# 0x0#000000
|
||||
yield Bit(self, "ext_reloc", "Contains extended relocations?")
|
||||
yield Bit(self, "discarded", "Can be discarded?")
|
||||
yield Bit(self, "is_not_cached", "Is not cachable?")
|
||||
yield Bit(self, "is_not_paged", "Is not pageable?")
|
||||
# 0x#0000000
|
||||
yield Bit(self, "is_shareable", "Is shareable?")
|
||||
yield Bit(self, "is_executable", "Is executable?")
|
||||
yield Bit(self, "is_readable", "Is readable?")
|
||||
yield Bit(self, "is_writable", "Is writable?")
|
||||
|
||||
def rva2file(self, rva):
|
||||
return self["phys_off"].value + (rva - self["rva"].value)
|
||||
|
||||
def createDescription(self):
|
||||
rva = self["rva"].value
|
||||
size = self["mem_size"].value
|
||||
info = [
|
||||
"rva=0x%08x..0x%08x" % (rva, rva+size),
|
||||
"size=%s" % self["mem_size"].display,
|
||||
]
|
||||
if self["is_executable"].value:
|
||||
info.append("exec")
|
||||
if self["is_readable"].value:
|
||||
info.append("read")
|
||||
if self["is_writable"].value:
|
||||
info.append("write")
|
||||
return 'Section "%s": %s' % (self["name"].value, ", ".join(info))
|
||||
|
||||
def createSectionName(self):
|
||||
try:
|
||||
name = str(self["name"].value.strip("."))
|
||||
if name:
|
||||
return "section_%s" % name
|
||||
except HACHOIR_ERRORS, err:
|
||||
self.warning(unicode(err))
|
||||
return "section[]"
|
||||
|
||||
class DataDirectory(FieldSet):
|
||||
def createFields(self):
|
||||
yield textHandler(UInt32(self, "rva", "Virtual address"), hexadecimal)
|
||||
yield filesizeHandler(UInt32(self, "size"))
|
||||
|
||||
def createDescription(self):
|
||||
if self["size"].value:
|
||||
return "Directory at %s (%s)" % (
|
||||
self["rva"].display, self["size"].display)
|
||||
else:
|
||||
return "(empty directory)"
|
||||
|
||||
class PE_Header(FieldSet):
|
||||
static_size = 24*8
|
||||
cpu_name = {
|
||||
0x0184: u"Alpha AXP",
|
||||
0x01c0: u"ARM",
|
||||
0x014C: u"Intel 80386",
|
||||
0x014D: u"Intel 80486",
|
||||
0x014E: u"Intel Pentium",
|
||||
0x0200: u"Intel IA64",
|
||||
0x0268: u"Motorola 68000",
|
||||
0x0266: u"MIPS",
|
||||
0x0284: u"Alpha AXP 64 bits",
|
||||
0x0366: u"MIPS with FPU",
|
||||
0x0466: u"MIPS16 with FPU",
|
||||
0x01f0: u"PowerPC little endian",
|
||||
0x0162: u"R3000",
|
||||
0x0166: u"MIPS little endian (R4000)",
|
||||
0x0168: u"R10000",
|
||||
0x01a2: u"Hitachi SH3",
|
||||
0x01a6: u"Hitachi SH4",
|
||||
0x0160: u"R3000 (MIPS), big endian",
|
||||
0x0162: u"R3000 (MIPS), little endian",
|
||||
0x0166: u"R4000 (MIPS), little endian",
|
||||
0x0168: u"R10000 (MIPS), little endian",
|
||||
0x0184: u"DEC Alpha AXP",
|
||||
0x01F0: u"IBM Power PC, little endian",
|
||||
}
|
||||
|
||||
def createFields(self):
|
||||
yield Bytes(self, "header", 4, r"PE header signature (PE\0\0)")
|
||||
if self["header"].value != "PE\0\0":
|
||||
raise ParserError("Invalid PE header signature")
|
||||
yield Enum(UInt16(self, "cpu", "CPU type"), self.cpu_name)
|
||||
yield UInt16(self, "nb_section", "Number of sections")
|
||||
yield TimestampUnix32(self, "creation_date", "Creation date")
|
||||
yield UInt32(self, "ptr_to_sym", "Pointer to symbol table")
|
||||
yield UInt32(self, "nb_symbols", "Number of symbols")
|
||||
yield UInt16(self, "opt_hdr_size", "Optional header size")
|
||||
|
||||
yield Bit(self, "reloc_stripped", "If true, don't contain base relocations.")
|
||||
yield Bit(self, "exec_image", "Executable image?")
|
||||
yield Bit(self, "line_nb_stripped", "COFF line numbers stripped?")
|
||||
yield Bit(self, "local_sym_stripped", "COFF symbol table entries stripped?")
|
||||
yield Bit(self, "aggr_ws", "Aggressively trim working set")
|
||||
yield Bit(self, "large_addr", "Application can handle addresses greater than 2 GB")
|
||||
yield NullBits(self, "reserved", 1)
|
||||
yield Bit(self, "reverse_lo", "Little endian: LSB precedes MSB in memory")
|
||||
yield Bit(self, "32bit", "Machine based on 32-bit-word architecture")
|
||||
yield Bit(self, "is_stripped", "Debugging information removed?")
|
||||
yield Bit(self, "swap", "If image is on removable media, copy and run from swap file")
|
||||
yield PaddingBits(self, "reserved2", 1)
|
||||
yield Bit(self, "is_system", "It's a system file")
|
||||
yield Bit(self, "is_dll", "It's a dynamic-link library (DLL)")
|
||||
yield Bit(self, "up", "File should be run only on a UP machine")
|
||||
yield Bit(self, "reverse_hi", "Big endian: MSB precedes LSB in memory")
|
||||
|
||||
class PE_OptHeader(FieldSet):
|
||||
SUBSYSTEM_NAME = {
|
||||
1: u"Native",
|
||||
2: u"Windows GUI",
|
||||
3: u"Windows CUI",
|
||||
5: u"OS/2 CUI",
|
||||
7: u"POSIX CUI",
|
||||
8: u"Native Windows",
|
||||
9: u"Windows CE GUI",
|
||||
10: u"EFI application",
|
||||
11: u"EFI boot service driver",
|
||||
12: u"EFI runtime driver",
|
||||
13: u"EFI ROM",
|
||||
14: u"XBOX",
|
||||
16: u"Windows boot application",
|
||||
}
|
||||
DIRECTORY_NAME = {
|
||||
0: "export",
|
||||
1: "import",
|
||||
2: "resource",
|
||||
3: "exception",
|
||||
4: "certificate",
|
||||
5: "relocation",
|
||||
6: "debug",
|
||||
7: "description",
|
||||
8: "global_ptr",
|
||||
9: "tls", # Thread local storage
|
||||
10: "load_config",
|
||||
11: "bound_import",
|
||||
12: "import_address",
|
||||
}
|
||||
def createFields(self):
|
||||
yield UInt16(self, "signature", "PE optional header signature (0x010b)")
|
||||
# TODO: Support PE32+ (signature=0x020b)
|
||||
if self["signature"].value != 0x010b:
|
||||
raise ParserError("Invalid PE optional header signature")
|
||||
yield UInt8(self, "maj_lnk_ver", "Major linker version")
|
||||
yield UInt8(self, "min_lnk_ver", "Minor linker version")
|
||||
yield filesizeHandler(UInt32(self, "size_code", "Size of code"))
|
||||
yield filesizeHandler(UInt32(self, "size_init_data", "Size of initialized data"))
|
||||
yield filesizeHandler(UInt32(self, "size_uninit_data", "Size of uninitialized data"))
|
||||
yield textHandler(UInt32(self, "entry_point", "Address (RVA) of the code entry point"), hexadecimal)
|
||||
yield textHandler(UInt32(self, "base_code", "Base (RVA) of code"), hexadecimal)
|
||||
yield textHandler(UInt32(self, "base_data", "Base (RVA) of data"), hexadecimal)
|
||||
yield textHandler(UInt32(self, "image_base", "Image base (RVA)"), hexadecimal)
|
||||
yield filesizeHandler(UInt32(self, "sect_align", "Section alignment"))
|
||||
yield filesizeHandler(UInt32(self, "file_align", "File alignment"))
|
||||
yield UInt16(self, "maj_os_ver", "Major OS version")
|
||||
yield UInt16(self, "min_os_ver", "Minor OS version")
|
||||
yield UInt16(self, "maj_img_ver", "Major image version")
|
||||
yield UInt16(self, "min_img_ver", "Minor image version")
|
||||
yield UInt16(self, "maj_subsys_ver", "Major subsystem version")
|
||||
yield UInt16(self, "min_subsys_ver", "Minor subsystem version")
|
||||
yield NullBytes(self, "reserved", 4)
|
||||
yield filesizeHandler(UInt32(self, "size_img", "Size of image"))
|
||||
yield filesizeHandler(UInt32(self, "size_hdr", "Size of headers"))
|
||||
yield textHandler(UInt32(self, "checksum"), hexadecimal)
|
||||
yield Enum(UInt16(self, "subsystem"), self.SUBSYSTEM_NAME)
|
||||
yield UInt16(self, "dll_flags")
|
||||
yield filesizeHandler(UInt32(self, "size_stack_reserve"))
|
||||
yield filesizeHandler(UInt32(self, "size_stack_commit"))
|
||||
yield filesizeHandler(UInt32(self, "size_heap_reserve"))
|
||||
yield filesizeHandler(UInt32(self, "size_heap_commit"))
|
||||
yield UInt32(self, "loader_flags")
|
||||
yield UInt32(self, "nb_directory", "Number of RVA and sizes")
|
||||
for index in xrange(self["nb_directory"].value):
|
||||
try:
|
||||
name = self.DIRECTORY_NAME[index]
|
||||
except KeyError:
|
||||
name = "data_dir[%u]" % index
|
||||
yield DataDirectory(self, name)
|
||||
|
||||
def createDescription(self):
|
||||
return "PE optional header: %s, entry point %s" % (
|
||||
self["subsystem"].display,
|
||||
self["entry_point"].display)
|
||||
|
||||
@@ -0,0 +1,445 @@
|
||||
"""
|
||||
Parser for resource of Microsoft Windows Portable Executable (PE).
|
||||
|
||||
Documentation:
|
||||
- Wine project
|
||||
VS_FIXEDFILEINFO structure, file include/winver.h
|
||||
|
||||
Author: Victor Stinner
|
||||
Creation date: 2007-01-19
|
||||
"""
|
||||
|
||||
from hachoir_core.field import (FieldSet, ParserError, Enum,
|
||||
Bit, Bits, SeekableFieldSet,
|
||||
UInt16, UInt32, TimestampUnix32,
|
||||
RawBytes, PaddingBytes, NullBytes, NullBits,
|
||||
CString, String)
|
||||
from hachoir_core.text_handler import textHandler, filesizeHandler, hexadecimal
|
||||
from hachoir_core.tools import createDict, paddingSize, alignValue, makePrintable
|
||||
from hachoir_core.error import HACHOIR_ERRORS
|
||||
from hachoir_parser.common.win32 import BitmapInfoHeader
|
||||
|
||||
MAX_DEPTH = 5
|
||||
MAX_INDEX_PER_HEADER = 300
|
||||
MAX_NAME_PER_HEADER = MAX_INDEX_PER_HEADER
|
||||
|
||||
class Version(FieldSet):
|
||||
static_size = 32
|
||||
def createFields(self):
|
||||
yield textHandler(UInt16(self, "minor", "Minor version number"), hexadecimal)
|
||||
yield textHandler(UInt16(self, "major", "Major version number"), hexadecimal)
|
||||
def createValue(self):
|
||||
return self["major"].value + float(self["minor"].value) / 10000
|
||||
|
||||
MAJOR_OS_NAME = {
|
||||
1: "DOS",
|
||||
2: "OS/2 16-bit",
|
||||
3: "OS/2 32-bit",
|
||||
4: "Windows NT",
|
||||
}
|
||||
|
||||
MINOR_OS_BASE = 0
|
||||
MINOR_OS_NAME = {
|
||||
0: "Base",
|
||||
1: "Windows 16-bit",
|
||||
2: "Presentation Manager 16-bit",
|
||||
3: "Presentation Manager 32-bit",
|
||||
4: "Windows 32-bit",
|
||||
}
|
||||
|
||||
FILETYPE_DRIVER = 3
|
||||
FILETYPE_FONT = 4
|
||||
FILETYPE_NAME = {
|
||||
1: "Application",
|
||||
2: "DLL",
|
||||
3: "Driver",
|
||||
4: "Font",
|
||||
5: "VXD",
|
||||
7: "Static library",
|
||||
}
|
||||
|
||||
DRIVER_SUBTYPE_NAME = {
|
||||
1: "Printer",
|
||||
2: "Keyboard",
|
||||
3: "Language",
|
||||
4: "Display",
|
||||
5: "Mouse",
|
||||
6: "Network",
|
||||
7: "System",
|
||||
8: "Installable",
|
||||
9: "Sound",
|
||||
10: "Communications",
|
||||
}
|
||||
|
||||
FONT_SUBTYPE_NAME = {
|
||||
1: "Raster",
|
||||
2: "Vector",
|
||||
3: "TrueType",
|
||||
}
|
||||
|
||||
class VersionInfoBinary(FieldSet):
|
||||
def createFields(self):
|
||||
yield textHandler(UInt32(self, "magic", "File information magic (0xFEEF04BD)"), hexadecimal)
|
||||
if self["magic"].value != 0xFEEF04BD:
|
||||
raise ParserError("EXE resource: invalid file info magic")
|
||||
yield Version(self, "struct_ver", "Structure version (1.0)")
|
||||
yield Version(self, "file_ver_ms", "File version MS")
|
||||
yield Version(self, "file_ver_ls", "File version LS")
|
||||
yield Version(self, "product_ver_ms", "Product version MS")
|
||||
yield Version(self, "product_ver_ls", "Product version LS")
|
||||
yield textHandler(UInt32(self, "file_flags_mask"), hexadecimal)
|
||||
|
||||
yield Bit(self, "debug")
|
||||
yield Bit(self, "prerelease")
|
||||
yield Bit(self, "patched")
|
||||
yield Bit(self, "private_build")
|
||||
yield Bit(self, "info_inferred")
|
||||
yield Bit(self, "special_build")
|
||||
yield NullBits(self, "reserved", 26)
|
||||
|
||||
yield Enum(textHandler(UInt16(self, "file_os_major"), hexadecimal), MAJOR_OS_NAME)
|
||||
yield Enum(textHandler(UInt16(self, "file_os_minor"), hexadecimal), MINOR_OS_NAME)
|
||||
yield Enum(textHandler(UInt32(self, "file_type"), hexadecimal), FILETYPE_NAME)
|
||||
field = textHandler(UInt32(self, "file_subfile"), hexadecimal)
|
||||
if field.value == FILETYPE_DRIVER:
|
||||
field = Enum(field, DRIVER_SUBTYPE_NAME)
|
||||
elif field.value == FILETYPE_FONT:
|
||||
field = Enum(field, FONT_SUBTYPE_NAME)
|
||||
yield field
|
||||
yield TimestampUnix32(self, "date_ms")
|
||||
yield TimestampUnix32(self, "date_ls")
|
||||
|
||||
class VersionInfoNode(FieldSet):
|
||||
TYPE_STRING = 1
|
||||
TYPE_NAME = {
|
||||
0: "binary",
|
||||
1: "string",
|
||||
}
|
||||
|
||||
def __init__(self, parent, name, is_32bit=True):
|
||||
FieldSet.__init__(self, parent, name)
|
||||
self._size = alignValue(self["size"].value, 4) * 8
|
||||
self.is_32bit = is_32bit
|
||||
|
||||
def createFields(self):
|
||||
yield UInt16(self, "size", "Node size (in bytes)")
|
||||
yield UInt16(self, "data_size")
|
||||
yield Enum(UInt16(self, "type"), self.TYPE_NAME)
|
||||
yield CString(self, "name", charset="UTF-16-LE")
|
||||
|
||||
size = paddingSize(self.current_size//8, 4)
|
||||
if size:
|
||||
yield NullBytes(self, "padding[]", size)
|
||||
size = self["data_size"].value
|
||||
if size:
|
||||
if self["type"].value == self.TYPE_STRING:
|
||||
if self.is_32bit:
|
||||
size *= 2
|
||||
yield String(self, "value", size, charset="UTF-16-LE", truncate="\0")
|
||||
elif self["name"].value == "VS_VERSION_INFO":
|
||||
yield VersionInfoBinary(self, "value", size=size*8)
|
||||
if self["value/file_flags_mask"].value == 0:
|
||||
self.is_32bit = False
|
||||
else:
|
||||
yield RawBytes(self, "value", size)
|
||||
while 12 <= (self.size - self.current_size) // 8:
|
||||
yield VersionInfoNode(self, "node[]", self.is_32bit)
|
||||
size = (self.size - self.current_size) // 8
|
||||
if size:
|
||||
yield NullBytes(self, "padding[]", size)
|
||||
|
||||
|
||||
def createDescription(self):
|
||||
text = "Version info node: %s" % self["name"].value
|
||||
if self["type"].value == self.TYPE_STRING and "value" in self:
|
||||
text += "=%s" % self["value"].value
|
||||
return text
|
||||
|
||||
def parseVersionInfo(parent):
|
||||
yield VersionInfoNode(parent, "node[]")
|
||||
|
||||
def parseIcon(parent):
|
||||
yield BitmapInfoHeader(parent, "bmp_header")
|
||||
size = (parent.size - parent.current_size) // 8
|
||||
if size:
|
||||
yield RawBytes(parent, "raw", size)
|
||||
|
||||
class WindowsString(FieldSet):
|
||||
def createFields(self):
|
||||
yield UInt16(self, "length", "Number of 16-bit characters")
|
||||
size = self["length"].value * 2
|
||||
if size:
|
||||
yield String(self, "text", size, charset="UTF-16-LE")
|
||||
|
||||
def createValue(self):
|
||||
if "text" in self:
|
||||
return self["text"].value
|
||||
else:
|
||||
return u""
|
||||
|
||||
def createDisplay(self):
|
||||
return makePrintable(self.value, "UTF-8", to_unicode=True, quote='"')
|
||||
|
||||
def parseStringTable(parent):
|
||||
while not parent.eof:
|
||||
yield WindowsString(parent, "string[]")
|
||||
|
||||
RESOURCE_TYPE = {
|
||||
1: ("cursor[]", "Cursor", None),
|
||||
2: ("bitmap[]", "Bitmap", None),
|
||||
3: ("icon[]", "Icon", parseIcon),
|
||||
4: ("menu[]", "Menu", None),
|
||||
5: ("dialog[]", "Dialog", None),
|
||||
6: ("string_table[]", "String table", parseStringTable),
|
||||
7: ("font_dir[]", "Font directory", None),
|
||||
8: ("font[]", "Font", None),
|
||||
9: ("accelerators[]", "Accelerators", None),
|
||||
10: ("raw_res[]", "Unformatted resource data", None),
|
||||
11: ("message_table[]", "Message table", None),
|
||||
12: ("group_cursor[]", "Group cursor", None),
|
||||
14: ("group_icon[]", "Group icon", None),
|
||||
16: ("version_info", "Version information", parseVersionInfo),
|
||||
}
|
||||
|
||||
class Entry(FieldSet):
|
||||
static_size = 16*8
|
||||
|
||||
def __init__(self, parent, name, inode=None):
|
||||
FieldSet.__init__(self, parent, name)
|
||||
self.inode = inode
|
||||
|
||||
def createFields(self):
|
||||
yield textHandler(UInt32(self, "rva"), hexadecimal)
|
||||
yield filesizeHandler(UInt32(self, "size"))
|
||||
yield UInt32(self, "codepage")
|
||||
yield NullBytes(self, "reserved", 4)
|
||||
|
||||
def createDescription(self):
|
||||
return "Entry #%u: offset=%s size=%s" % (
|
||||
self.inode["offset"].value, self["rva"].display, self["size"].display)
|
||||
|
||||
class NameOffset(FieldSet):
|
||||
def createFields(self):
|
||||
yield UInt32(self, "name")
|
||||
yield Bits(self, "offset", 31)
|
||||
yield Bit(self, "is_name")
|
||||
|
||||
class IndexOffset(FieldSet):
|
||||
TYPE_DESC = createDict(RESOURCE_TYPE, 1)
|
||||
|
||||
def __init__(self, parent, name, res_type=None):
|
||||
FieldSet.__init__(self, parent, name)
|
||||
self.res_type = res_type
|
||||
|
||||
def createFields(self):
|
||||
yield Enum(UInt32(self, "type"), self.TYPE_DESC)
|
||||
yield Bits(self, "offset", 31)
|
||||
yield Bit(self, "is_subdir")
|
||||
|
||||
def createDescription(self):
|
||||
if self["is_subdir"].value:
|
||||
return "Sub-directory: %s at %s" % (self["type"].display, self["offset"].value)
|
||||
else:
|
||||
return "Index: ID %s at %s" % (self["type"].display, self["offset"].value)
|
||||
|
||||
class ResourceContent(FieldSet):
|
||||
def __init__(self, parent, name, entry, size=None):
|
||||
FieldSet.__init__(self, parent, name, size=entry["size"].value*8)
|
||||
self.entry = entry
|
||||
res_type = self.getResType()
|
||||
if res_type in RESOURCE_TYPE:
|
||||
self._name, description, self._parser = RESOURCE_TYPE[res_type]
|
||||
else:
|
||||
self._parser = None
|
||||
|
||||
def getResID(self):
|
||||
return self.entry.inode["offset"].value
|
||||
|
||||
def getResType(self):
|
||||
return self.entry.inode.res_type
|
||||
|
||||
def createFields(self):
|
||||
if self._parser:
|
||||
for field in self._parser(self):
|
||||
yield field
|
||||
else:
|
||||
yield RawBytes(self, "content", self.size//8)
|
||||
|
||||
def createDescription(self):
|
||||
return "Resource #%u content: type=%s" % (
|
||||
self.getResID(), self.getResType())
|
||||
|
||||
class Header(FieldSet):
|
||||
static_size = 16*8
|
||||
def createFields(self):
|
||||
yield NullBytes(self, "options", 4)
|
||||
yield TimestampUnix32(self, "creation_date")
|
||||
yield UInt16(self, "maj_ver", "Major version")
|
||||
yield UInt16(self, "min_ver", "Minor version")
|
||||
yield UInt16(self, "nb_name", "Number of named entries")
|
||||
yield UInt16(self, "nb_index", "Number of indexed entries")
|
||||
|
||||
def createDescription(self):
|
||||
text = "Resource header"
|
||||
info = []
|
||||
if self["nb_name"].value:
|
||||
info.append("%u name" % self["nb_name"].value)
|
||||
if self["nb_index"].value:
|
||||
info.append("%u index" % self["nb_index"].value)
|
||||
if self["creation_date"].value:
|
||||
info.append(self["creation_date"].display)
|
||||
if info:
|
||||
return "%s: %s" % (text, ", ".join(info))
|
||||
else:
|
||||
return text
|
||||
|
||||
class Name(FieldSet):
|
||||
def createFields(self):
|
||||
yield UInt16(self, "length")
|
||||
size = min(self["length"].value, 255)
|
||||
if size:
|
||||
yield String(self, "name", size, charset="UTF-16LE")
|
||||
|
||||
class Directory(FieldSet):
|
||||
def __init__(self, parent, name, res_type=None):
|
||||
FieldSet.__init__(self, parent, name)
|
||||
nb_entries = self["header/nb_name"].value + self["header/nb_index"].value
|
||||
self._size = Header.static_size + nb_entries * 64
|
||||
self.res_type = res_type
|
||||
|
||||
def createFields(self):
|
||||
yield Header(self, "header")
|
||||
|
||||
if MAX_NAME_PER_HEADER < self["header/nb_name"].value:
|
||||
raise ParserError("EXE resource: invalid number of name (%s)"
|
||||
% self["header/nb_name"].value)
|
||||
if MAX_INDEX_PER_HEADER < self["header/nb_index"].value:
|
||||
raise ParserError("EXE resource: invalid number of index (%s)"
|
||||
% self["header/nb_index"].value)
|
||||
|
||||
hdr = self["header"]
|
||||
for index in xrange(hdr["nb_name"].value):
|
||||
yield NameOffset(self, "name[]")
|
||||
for index in xrange(hdr["nb_index"].value):
|
||||
yield IndexOffset(self, "index[]", self.res_type)
|
||||
|
||||
def createDescription(self):
|
||||
return self["header"].description
|
||||
|
||||
class PE_Resource(SeekableFieldSet):
|
||||
def __init__(self, parent, name, section, size):
|
||||
SeekableFieldSet.__init__(self, parent, name, size=size)
|
||||
self.section = section
|
||||
|
||||
def parseSub(self, directory, name, depth):
|
||||
indexes = []
|
||||
for index in directory.array("index"):
|
||||
if index["is_subdir"].value:
|
||||
indexes.append(index)
|
||||
|
||||
#indexes.sort(key=lambda index: index["offset"].value)
|
||||
for index in indexes:
|
||||
self.seekByte(index["offset"].value)
|
||||
if depth == 1:
|
||||
res_type = index["type"].value
|
||||
else:
|
||||
res_type = directory.res_type
|
||||
yield Directory(self, name, res_type)
|
||||
|
||||
def createFields(self):
|
||||
# Parse directories
|
||||
depth = 0
|
||||
subdir = Directory(self, "root")
|
||||
yield subdir
|
||||
subdirs = [subdir]
|
||||
alldirs = [subdir]
|
||||
while subdirs:
|
||||
depth += 1
|
||||
if MAX_DEPTH < depth:
|
||||
self.error("EXE resource: depth too high (%s), stop parsing directories" % depth)
|
||||
break
|
||||
newsubdirs = []
|
||||
for index, subdir in enumerate(subdirs):
|
||||
name = "directory[%u][%u][]" % (depth, index)
|
||||
try:
|
||||
for field in self.parseSub(subdir, name, depth):
|
||||
if field.__class__ == Directory:
|
||||
newsubdirs.append(field)
|
||||
yield field
|
||||
except HACHOIR_ERRORS, err:
|
||||
self.error("Unable to create directory %s: %s" % (name, err))
|
||||
subdirs = newsubdirs
|
||||
alldirs.extend(subdirs)
|
||||
|
||||
# Create resource list
|
||||
resources = []
|
||||
for directory in alldirs:
|
||||
for index in directory.array("index"):
|
||||
if not index["is_subdir"].value:
|
||||
resources.append(index)
|
||||
|
||||
# Parse entries
|
||||
entries = []
|
||||
for resource in resources:
|
||||
offset = resource["offset"].value
|
||||
if offset is None:
|
||||
continue
|
||||
self.seekByte(offset)
|
||||
entry = Entry(self, "entry[]", inode=resource)
|
||||
yield entry
|
||||
entries.append(entry)
|
||||
entries.sort(key=lambda entry: entry["rva"].value)
|
||||
|
||||
# Parse resource content
|
||||
for entry in entries:
|
||||
try:
|
||||
offset = self.section.rva2file(entry["rva"].value)
|
||||
padding = self.seekByte(offset, relative=False)
|
||||
if padding:
|
||||
yield padding
|
||||
yield ResourceContent(self, "content[]", entry)
|
||||
except HACHOIR_ERRORS, err:
|
||||
self.warning("Error when parsing entry %s: %s" % (entry.path, err))
|
||||
|
||||
size = (self.size - self.current_size) // 8
|
||||
if size:
|
||||
yield PaddingBytes(self, "padding_end", size)
|
||||
|
||||
class NE_VersionInfoNode(FieldSet):
|
||||
TYPE_STRING = 1
|
||||
TYPE_NAME = {
|
||||
0: "binary",
|
||||
1: "string",
|
||||
}
|
||||
|
||||
def __init__(self, parent, name):
|
||||
FieldSet.__init__(self, parent, name)
|
||||
self._size = alignValue(self["size"].value, 4) * 8
|
||||
|
||||
def createFields(self):
|
||||
yield UInt16(self, "size", "Node size (in bytes)")
|
||||
yield UInt16(self, "data_size")
|
||||
yield CString(self, "name", charset="ISO-8859-1")
|
||||
|
||||
size = paddingSize(self.current_size//8, 4)
|
||||
if size:
|
||||
yield NullBytes(self, "padding[]", size)
|
||||
size = self["data_size"].value
|
||||
if size:
|
||||
if self["name"].value == "VS_VERSION_INFO":
|
||||
yield VersionInfoBinary(self, "value", size=size*8)
|
||||
else:
|
||||
yield String(self, "value", size, charset="ISO-8859-1")
|
||||
while 12 <= (self.size - self.current_size) // 8:
|
||||
yield NE_VersionInfoNode(self, "node[]")
|
||||
size = (self.size - self.current_size) // 8
|
||||
if size:
|
||||
yield NullBytes(self, "padding[]", size)
|
||||
|
||||
|
||||
def createDescription(self):
|
||||
text = "Version info node: %s" % self["name"].value
|
||||
# if self["type"].value == self.TYPE_STRING and "value" in self:
|
||||
# text += "=%s" % self["value"].value
|
||||
return text
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,82 @@
|
||||
"""
|
||||
PRC (Palm resource) parser.
|
||||
|
||||
Author: Sebastien Ponce
|
||||
Creation date: 29 october 2008
|
||||
"""
|
||||
|
||||
from hachoir_parser import Parser
|
||||
from hachoir_core.field import (FieldSet,
|
||||
UInt16, UInt32, TimestampMac32,
|
||||
String, RawBytes)
|
||||
from hachoir_core.endian import BIG_ENDIAN
|
||||
|
||||
class PRCHeader(FieldSet):
|
||||
static_size = 78*8
|
||||
|
||||
def createFields(self):
|
||||
yield String(self, "name", 32, "Name")
|
||||
yield UInt16(self, "flags", "Flags")
|
||||
yield UInt16(self, "version", "Version")
|
||||
yield TimestampMac32(self, "create_time", "Creation time")
|
||||
yield TimestampMac32(self, "mod_time", "Modification time")
|
||||
yield TimestampMac32(self, "backup_time", "Backup time")
|
||||
yield UInt32(self, "mod_num", "mod num")
|
||||
yield UInt32(self, "app_info", "app info")
|
||||
yield UInt32(self, "sort_info", "sort info")
|
||||
yield UInt32(self, "type", "type")
|
||||
yield UInt32(self, "id", "id")
|
||||
yield UInt32(self, "unique_id_seed", "unique_id_seed")
|
||||
yield UInt32(self, "next_record_list", "next_record_list")
|
||||
yield UInt16(self, "num_records", "num_records")
|
||||
|
||||
class ResourceHeader(FieldSet):
|
||||
static_size = 10*8
|
||||
|
||||
def createFields(self):
|
||||
yield String(self, "name", 4, "Name of the resource")
|
||||
yield UInt16(self, "flags", "ID number of the resource")
|
||||
yield UInt32(self, "offset", "Pointer to the resource data")
|
||||
|
||||
def createDescription(self):
|
||||
return "Resource Header (%s)" % self["name"]
|
||||
|
||||
class PRCFile(Parser):
|
||||
PARSER_TAGS = {
|
||||
"id": "prc",
|
||||
"category": "program",
|
||||
"file_ext": ("prc", ""),
|
||||
"min_size": ResourceHeader.static_size, # At least one program header
|
||||
"mime": (
|
||||
u"application/x-pilot-prc",
|
||||
u"application/x-palmpilot"),
|
||||
"description": "Palm Resource File"
|
||||
}
|
||||
endian = BIG_ENDIAN
|
||||
|
||||
def validate(self):
|
||||
# FIXME: Implement the validation function!
|
||||
return False
|
||||
|
||||
def createFields(self):
|
||||
# Parse header and program headers
|
||||
yield PRCHeader(self, "header", "Header")
|
||||
lens = []
|
||||
firstOne = True
|
||||
poff = 0
|
||||
for index in xrange(self["header/num_records"].value):
|
||||
r = ResourceHeader(self, "res_header[]")
|
||||
if firstOne:
|
||||
firstOne = False
|
||||
else:
|
||||
lens.append(r["offset"].value - poff)
|
||||
poff = r["offset"].value
|
||||
yield r
|
||||
lens.append(self.size/8 - poff)
|
||||
yield UInt16(self, "placeholder", "Place holder bytes")
|
||||
for i in range(len(lens)):
|
||||
yield RawBytes(self, "res[]", lens[i], '"'+self["res_header["+str(i)+"]/name"].value+"\" Resource")
|
||||
|
||||
def createDescription(self):
|
||||
return "Palm Resource file"
|
||||
|
||||
@@ -0,0 +1,334 @@
|
||||
"""
|
||||
Python compiled source code parser.
|
||||
|
||||
Informations:
|
||||
- Python 2.4.2 source code:
|
||||
files Python/marshal.c and Python/import.c
|
||||
|
||||
Author: Victor Stinner
|
||||
Creation: 25 march 2005
|
||||
"""
|
||||
|
||||
DISASSEMBLE = False
|
||||
|
||||
from hachoir_parser import Parser
|
||||
from hachoir_core.field import (FieldSet, UInt8,
|
||||
UInt16, Int32, UInt32, Int64, ParserError, Float64, Enum,
|
||||
Character, Bytes, RawBytes, PascalString8, TimestampUnix32)
|
||||
from hachoir_core.endian import LITTLE_ENDIAN
|
||||
from hachoir_core.bits import long2raw
|
||||
from hachoir_core.text_handler import textHandler, hexadecimal
|
||||
from hachoir_core.i18n import ngettext
|
||||
if DISASSEMBLE:
|
||||
from dis import dis
|
||||
|
||||
def disassembleBytecode(field):
|
||||
bytecode = field.value
|
||||
dis(bytecode)
|
||||
|
||||
# --- String and string reference ---
|
||||
def parseString(parent):
|
||||
yield UInt32(parent, "length", "Length")
|
||||
length = parent["length"].value
|
||||
if parent.name == "lnotab":
|
||||
bytecode_offset=0
|
||||
line_number=parent['../firstlineno'].value
|
||||
for i in range(0,length,2):
|
||||
bc_off_delta=UInt8(parent, 'bytecode_offset_delta[]')
|
||||
yield bc_off_delta
|
||||
bytecode_offset+=bc_off_delta.value
|
||||
bc_off_delta._description='Bytecode Offset %i'%bytecode_offset
|
||||
line_number_delta=UInt8(parent, 'line_number_delta[]')
|
||||
yield line_number_delta
|
||||
line_number+=line_number_delta.value
|
||||
line_number_delta._description='Line Number %i'%line_number
|
||||
elif 0 < length:
|
||||
yield RawBytes(parent, "text", length, "Content")
|
||||
if DISASSEMBLE and parent.name == "compiled_code":
|
||||
disassembleBytecode(parent["text"])
|
||||
|
||||
def parseStringRef(parent):
|
||||
yield textHandler(UInt32(parent, "ref"), hexadecimal)
|
||||
def createStringRefDesc(parent):
|
||||
return "String ref: %s" % parent["ref"].display
|
||||
|
||||
# --- Integers ---
|
||||
def parseInt32(parent):
|
||||
yield Int32(parent, "value")
|
||||
|
||||
def parseInt64(parent):
|
||||
yield Int64(parent, "value")
|
||||
|
||||
def parseLong(parent):
|
||||
yield Int32(parent, "digit_count")
|
||||
for index in xrange( abs(parent["digit_count"].value) ):
|
||||
yield UInt16(parent, "digit[]")
|
||||
|
||||
|
||||
# --- Float and complex ---
|
||||
def parseFloat(parent):
|
||||
yield PascalString8(parent, "value")
|
||||
def parseBinaryFloat(parent):
|
||||
yield Float64(parent, "value")
|
||||
def parseComplex(parent):
|
||||
yield PascalString8(parent, "real")
|
||||
yield PascalString8(parent, "complex")
|
||||
def parseBinaryComplex(parent):
|
||||
yield Float64(parent, "real")
|
||||
yield Float64(parent, "complex")
|
||||
|
||||
|
||||
# --- Tuple and list ---
|
||||
def parseTuple(parent):
|
||||
yield Int32(parent, "count", "Item count")
|
||||
count = parent["count"].value
|
||||
if count < 0:
|
||||
raise ParserError("Invalid tuple/list count")
|
||||
for index in xrange(count):
|
||||
yield Object(parent, "item[]")
|
||||
|
||||
def createTupleDesc(parent):
|
||||
count = parent["count"].value
|
||||
items = ngettext("%s item", "%s items", count) % count
|
||||
return "%s: %s" % (parent.code_info[2], items)
|
||||
|
||||
|
||||
# --- Dict ---
|
||||
def parseDict(parent):
|
||||
"""
|
||||
Format is: (key1, value1, key2, value2, ..., keyn, valuen, NULL)
|
||||
where each keyi and valuei is an object.
|
||||
"""
|
||||
parent.count = 0
|
||||
while True:
|
||||
key = Object(parent, "key[]")
|
||||
yield key
|
||||
if key["bytecode"].value == "0":
|
||||
break
|
||||
yield Object(parent, "value[]")
|
||||
parent.count += 1
|
||||
|
||||
def createDictDesc(parent):
|
||||
return "Dict: %s" % (ngettext("%s key", "%s keys", parent.count) % parent.count)
|
||||
|
||||
# --- Code ---
|
||||
def parseCode(parent):
|
||||
if 0x3000000 <= parent.root.getVersion():
|
||||
yield UInt32(parent, "arg_count", "Argument count")
|
||||
yield UInt32(parent, "kwonlyargcount", "Keyword only argument count")
|
||||
yield UInt32(parent, "nb_locals", "Number of local variables")
|
||||
yield UInt32(parent, "stack_size", "Stack size")
|
||||
yield UInt32(parent, "flags")
|
||||
elif 0x2030000 <= parent.root.getVersion():
|
||||
yield UInt32(parent, "arg_count", "Argument count")
|
||||
yield UInt32(parent, "nb_locals", "Number of local variables")
|
||||
yield UInt32(parent, "stack_size", "Stack size")
|
||||
yield UInt32(parent, "flags")
|
||||
else:
|
||||
yield UInt16(parent, "arg_count", "Argument count")
|
||||
yield UInt16(parent, "nb_locals", "Number of local variables")
|
||||
yield UInt16(parent, "stack_size", "Stack size")
|
||||
yield UInt16(parent, "flags")
|
||||
yield Object(parent, "compiled_code")
|
||||
yield Object(parent, "consts")
|
||||
yield Object(parent, "names")
|
||||
yield Object(parent, "varnames")
|
||||
if 0x2000000 <= parent.root.getVersion():
|
||||
yield Object(parent, "freevars")
|
||||
yield Object(parent, "cellvars")
|
||||
yield Object(parent, "filename")
|
||||
yield Object(parent, "name")
|
||||
if 0x2030000 <= parent.root.getVersion():
|
||||
yield UInt32(parent, "firstlineno", "First line number")
|
||||
else:
|
||||
yield UInt16(parent, "firstlineno", "First line number")
|
||||
yield Object(parent, "lnotab")
|
||||
|
||||
class Object(FieldSet):
|
||||
bytecode_info = {
|
||||
# Don't contains any data
|
||||
'0': ("null", None, "NULL", None),
|
||||
'N': ("none", None, "None", None),
|
||||
'F': ("false", None, "False", None),
|
||||
'T': ("true", None, "True", None),
|
||||
'S': ("stop_iter", None, "StopIter", None),
|
||||
'.': ("ellipsis", None, "ELLIPSIS", None),
|
||||
'?': ("unknown", None, "Unknown", None),
|
||||
|
||||
'i': ("int32", parseInt32, "Int32", None),
|
||||
'I': ("int64", parseInt64, "Int64", None),
|
||||
'f': ("float", parseFloat, "Float", None),
|
||||
'g': ("bin_float", parseBinaryFloat, "Binary float", None),
|
||||
'x': ("complex", parseComplex, "Complex", None),
|
||||
'y': ("bin_complex", parseBinaryComplex, "Binary complex", None),
|
||||
'l': ("long", parseLong, "Long", None),
|
||||
's': ("string", parseString, "String", None),
|
||||
't': ("interned", parseString, "Interned", None),
|
||||
'u': ("unicode", parseString, "Unicode", None),
|
||||
'R': ("string_ref", parseStringRef, "String ref", createStringRefDesc),
|
||||
'(': ("tuple", parseTuple, "Tuple", createTupleDesc),
|
||||
'[': ("list", parseTuple, "List", createTupleDesc),
|
||||
'<': ("set", parseTuple, "Set", createTupleDesc),
|
||||
'>': ("frozenset", parseTuple, "Frozen set", createTupleDesc),
|
||||
'{': ("dict", parseDict, "Dict", createDictDesc),
|
||||
'c': ("code", parseCode, "Code", None),
|
||||
}
|
||||
|
||||
def __init__(self, parent, name, **kw):
|
||||
FieldSet.__init__(self, parent, name, **kw)
|
||||
code = self["bytecode"].value
|
||||
if code not in self.bytecode_info:
|
||||
raise ParserError('Unknown bytecode: "%s"' % code)
|
||||
self.code_info = self.bytecode_info[code]
|
||||
if not name:
|
||||
self._name = self.code_info[0]
|
||||
if code == "l":
|
||||
self.createValue = self.createValueLong
|
||||
elif code in ("i", "I", "f", "g"):
|
||||
self.createValue = lambda: self["value"].value
|
||||
elif code == "T":
|
||||
self.createValue = lambda: True
|
||||
elif code == "F":
|
||||
self.createValue = lambda: False
|
||||
elif code in ("x", "y"):
|
||||
self.createValue = self.createValueComplex
|
||||
elif code in ("s", "t", "u"):
|
||||
self.createValue = self.createValueString
|
||||
self.createDisplay = self.createDisplayString
|
||||
if code == 't':
|
||||
if not hasattr(self.root,'string_table'):
|
||||
self.root.string_table=[]
|
||||
self.root.string_table.append(self)
|
||||
elif code == 'R':
|
||||
if hasattr(self.root,'string_table'):
|
||||
self.createValue = self.createValueStringRef
|
||||
|
||||
def createValueString(self):
|
||||
if "text" in self:
|
||||
return self["text"].value
|
||||
else:
|
||||
return ""
|
||||
|
||||
def createDisplayString(self):
|
||||
if "text" in self:
|
||||
return self["text"].display
|
||||
else:
|
||||
return "(empty)"
|
||||
|
||||
def createValueLong(self):
|
||||
is_negative = self["digit_count"].value < 0
|
||||
count = abs(self["digit_count"].value)
|
||||
total = 0
|
||||
for index in xrange(count-1, -1, -1):
|
||||
total <<= 15
|
||||
total += self["digit[%u]" % index].value
|
||||
if is_negative:
|
||||
total = -total
|
||||
return total
|
||||
|
||||
def createValueStringRef(self):
|
||||
return self.root.string_table[self['ref'].value].value
|
||||
|
||||
def createDisplayStringRef(self):
|
||||
return self.root.string_table[self['ref'].value].display
|
||||
|
||||
def createValueComplex(self):
|
||||
return complex(
|
||||
float(self["real"].value),
|
||||
float(self["complex"].value))
|
||||
|
||||
def createFields(self):
|
||||
yield Character(self, "bytecode", "Bytecode")
|
||||
parser = self.code_info[1]
|
||||
if parser:
|
||||
for field in parser(self):
|
||||
yield field
|
||||
|
||||
def createDescription(self):
|
||||
create = self.code_info[3]
|
||||
if create:
|
||||
return create(self)
|
||||
else:
|
||||
return self.code_info[2]
|
||||
|
||||
class PythonCompiledFile(Parser):
|
||||
PARSER_TAGS = {
|
||||
"id": "python",
|
||||
"category": "program",
|
||||
"file_ext": ("pyc", "pyo"),
|
||||
"min_size": 9*8,
|
||||
"description": "Compiled Python script (.pyc/.pyo files)"
|
||||
}
|
||||
endian = LITTLE_ENDIAN
|
||||
|
||||
# Dictionnary which associate the pyc signature (32-bit integer)
|
||||
# to a Python version string (eg. "m\xf2\r\n" => "Python 2.4b1").
|
||||
# This list comes from CPython source code, see "MAGIC"
|
||||
# and "pyc_magic" in file Python/import.c
|
||||
MAGIC = {
|
||||
# Python 1.x
|
||||
20121: ("1.5", 0x1050000),
|
||||
|
||||
# Python 2.x
|
||||
50823: ("2.0", 0x2000000),
|
||||
60202: ("2.1", 0x2010000),
|
||||
60717: ("2.2", 0x2020000),
|
||||
62011: ("2.3a0", 0x2030000),
|
||||
62021: ("2.3a0", 0x2030000),
|
||||
62041: ("2.4a0", 0x2040000),
|
||||
62051: ("2.4a3", 0x2040000),
|
||||
62061: ("2.4b1", 0x2040000),
|
||||
62071: ("2.5a0", 0x2050000),
|
||||
62081: ("2.5a0 (ast-branch)", 0x2050000),
|
||||
62091: ("2.5a0 (with)", 0x2050000),
|
||||
62092: ("2.5a0 (WITH_CLEANUP opcode)", 0x2050000),
|
||||
62101: ("2.5b3", 0x2050000),
|
||||
62111: ("2.5b3", 0x2050000),
|
||||
62121: ("2.5c1", 0x2050000),
|
||||
62131: ("2.5c2", 0x2050000),
|
||||
|
||||
# Python 3.x
|
||||
3000: ("3.0 (3000)", 0x3000000),
|
||||
3010: ("3.0 (3010)", 0x3000000),
|
||||
3020: ("3.0 (3020)", 0x3000000),
|
||||
3030: ("3.0 (3030)", 0x3000000),
|
||||
3040: ("3.0 (3040)", 0x3000000),
|
||||
3050: ("3.0 (3050)", 0x3000000),
|
||||
3060: ("3.0 (3060)", 0x3000000),
|
||||
3070: ("3.0 (3070)", 0x3000000),
|
||||
3080: ("3.0 (3080)", 0x3000000),
|
||||
3090: ("3.0 (3090)", 0x3000000),
|
||||
3100: ("3.0 (3100)", 0x3000000),
|
||||
3102: ("3.0 (3102)", 0x3000000),
|
||||
3110: ("3.0a4", 0x3000000),
|
||||
3130: ("3.0a5", 0x3000000),
|
||||
3131: ("3.0a5 unicode", 0x3000000),
|
||||
}
|
||||
|
||||
# Dictionnary which associate the pyc signature (4-byte long string)
|
||||
# to a Python version string (eg. "m\xf2\r\n" => "2.4b1")
|
||||
STR_MAGIC = dict( \
|
||||
(long2raw(magic | (ord('\r')<<16) | (ord('\n')<<24), LITTLE_ENDIAN), value[0]) \
|
||||
for magic, value in MAGIC.iteritems())
|
||||
|
||||
def validate(self):
|
||||
signature = self.stream.readBits(0, 16, self.endian)
|
||||
if signature not in self.MAGIC:
|
||||
return "Unknown version (%s)" % signature
|
||||
if self.stream.readBytes(2*8, 2) != "\r\n":
|
||||
return r"Wrong signature (\r\n)"
|
||||
if self.stream.readBytes(8*8, 1) != 'c':
|
||||
return "First object bytecode is not code"
|
||||
return True
|
||||
|
||||
def getVersion(self):
|
||||
if not hasattr(self, "version"):
|
||||
signature = self.stream.readBits(0, 16, self.endian)
|
||||
self.version = self.MAGIC[signature][1]
|
||||
return self.version
|
||||
|
||||
def createFields(self):
|
||||
yield Enum(Bytes(self, "signature", 4, "Python file signature and version"), self.STR_MAGIC)
|
||||
yield TimestampUnix32(self, "timestamp", "Timestamp")
|
||||
yield Object(self, "content")
|
||||
|
||||
Reference in New Issue
Block a user