SickGear/lib/hachoir/parser/program/dex.py
JackDandy 980e05cc99 Change Hachoir can't support PY2 so backport their PY3 to prevent a need for system dependant external binaries like mediainfo.
Backported 400 revisions from rev 1de4961-8897c5b (2018-2014).
Move core/benchmark, core/cmd_line, core/memory, core/profiler and core/timeout to core/optional/*
Remove metadata/qt*

PORT: Version 2.0a3 (inline with 3.0a3 @ f80c7d5).
Basic Support for XMP Packets.
tga: improvements to adhere more closely to the spec.
pdf: slightly improved parsing.
rar: fix TypeError on unknown block types.
Add MacRoman win32 codepage.
tiff/exif: support SubIFDs and tiled images.
Add method to export metadata in dictionary.
mpeg_video: don't attempt to parse Stream past length.
mpeg_video: parse ESCR correctly, add SCR value.
Change centralise CustomFragments.
field: don't set parser class if class is None, to enable autodetect.
field: add value/display for CustomFragment.
parser: inline warning to enable tracebacks in debug mode.
Fix empty bytestrings in makePrintable.
Fix contentSize in jpeg.py to account for image_data blocks.
Fix the ELF parser.
Enhance the AR archive parser.
elf parser: fix wrong wrong fields order in parsing little endian section flags.
elf parser: add s390 as a machine type.
Flesh out mp4 parser.

PORT: Version 2.0a1 (inline with 3.0a1).
Major refactoring and PEP8.
Fix ResourceWarning warnings on files. Add a close() method and support for the context manager protocol ("with obj: ...") to parsers, input and output streams.
metadata: get comment from ZIP.
Support for InputIOStream.read(0).
Fix sizeGe when size is None.
Remove unused new_seekable_field_set file.
Remove parser Mapsforge .map.
Remove parser Parallel Realities Starfighter .pak files.
sevenzip: fix for newer archives.
java: update access flags and modifiers for Java 1.7 and update description text for most recent Java.
Support ustar prefix field in tar archives.
Remove file_system* parsers.
Remove misc parsers 3d0, 3ds, gnome_keyring, msoffice*, mstask, ole*, word*.
Remove program parsers macho, nds, prc.
Support non-8bit Character subclasses.
Python parser supports Python 3.7.
Enhance mpeg_ts parser to support MTS/M2TS.
Support for creation date in tiff.
Change don't hardcode errno constant.

PORT: 1.9.1
Internal Only: The following are legacy reference to upstream commit messages.
Relevant changes up to b0a115f8.
Use integer division.
Replace HACHOIR_ERRORS with Exception.
Fix metadata.Data: make it sortable.
Import fixes from e7de492.
PORT: Version 2.0a1 (inline with 3.0a1 @ e9f8fad).
Replace hachoir.core.field with hachoir.field
Replace hachoir.core.stream with hachoir.stream
Remove the compatibility module for PY1.5 to PY2.5.
metadata: support TIFF picture.
metadata: fix string normalization.
metadata: fix datetime regex Fix hachoir bug #57.
FileFromInputStream: fix comparison between None and an int.
InputIOStream: open the file in binary mode.
2018-03-28 00:43:11 +01:00

274 lines
10 KiB
Python

'''
Dalvik Executable (dex) parser.
References:
- http://www.dalvikvm.com/
- http://code.google.com/p/androguard/source/browse/core/bytecodes/dvm.py
- http://androguard.googlecode.com/hg/specs/dalvik/dex-format.html
Author: Robert Xiao
Creation Date: May 29, 2011
'''
from hachoir.parser import HachoirParser
from hachoir.field import (SeekableFieldSet, RootSeekableFieldSet, FieldSet, ParserError,
String, RawBytes, GenericVector,
UInt8, UInt16, UInt32, NullBits, Bit)
from hachoir.core.text_handler import textHandler, hexadecimal, filesizeHandler
from hachoir.core.endian import LITTLE_ENDIAN
from hachoir.parser.program.java import eat_descriptor
class DexHeader(FieldSet):
def createFields(self):
yield String(self, "magic", 4)
yield String(self, "version", 4, strip='\0')
yield textHandler(UInt32(self, "checksum"), hexadecimal)
yield RawBytes(self, "signature", 20, description="SHA1 sum over all subsequent data")
yield filesizeHandler(UInt32(self, "filesize"))
yield UInt32(self, "size", description="Header size")
self._size = self['size'].value * 8
yield textHandler(UInt32(self, "endian"), hexadecimal)
yield UInt32(self, "link_count")
yield UInt32(self, "link_offset")
yield UInt32(self, "map_offset", description="offset to map footer")
yield UInt32(self, "string_count", description="number of entries in string table")
yield UInt32(self, "string_offset", description="offset to string table")
yield UInt32(self, "type_desc_count", description="number of entries in type descriptor table")
yield UInt32(self, "type_desc_offset", description="offset to type descriptor table")
yield UInt32(self, "meth_desc_count", description="number of entries in method descriptor table")
yield UInt32(self, "meth_desc_offset", description="offset to method descriptor table")
yield UInt32(self, "field_count", description="number of entries in field table")
yield UInt32(self, "field_offset", description="offset to field table")
yield UInt32(self, "method_count", description="number of entries in method table")
yield UInt32(self, "method_offset", description="offset to method table")
yield UInt32(self, "class_count", description="number of entries in class table")
yield UInt32(self, "class_offset", description="offset to class table")
yield UInt32(self, "data_size", description="size of data region")
yield UInt32(self, "data_offset", description="offset to data region")
def stringIndex(field):
return field['/string_table/item[%d]' % field.value].display
def classDisplay(field):
disp, tail = eat_descriptor(stringIndex(field))
return disp
def classIndex(field):
return field['/type_desc_table/item[%d]' % field.value].display
# modified from java.py
code_to_type_name = {
'B': "byte",
'C': "char",
'D': "double",
'F': "float",
'I': "int",
'J': "long",
'L': "object",
'S': "short",
'Z': "boolean",
}
def argumentDisplay(field):
# parse "shorty" descriptors (these start with the return code, which is redundant)
text = stringIndex(field)[1:]
return [code_to_type_name.get(c, c) for c in text]
def signatureIndex(field):
return field['/meth_desc_table/item[%d]' % field.value].display
class PascalCString(FieldSet):
def createFields(self):
yield UInt8(self, "size")
self._size = (self['size'].value + 2) * 8
yield String(self, "string", self['size'].value + 1, strip='\0')
def createValue(self):
return self['string'].value
class StringTable(SeekableFieldSet):
def createFields(self):
for item in self['/string_offsets'].array('item'):
self.seekByte(item.value, relative=False)
yield PascalCString(self, "item[]")
class TypeDescriptorEntry(FieldSet):
static_size = 32
def createFields(self):
yield textHandler(UInt32(self, "desc", description="Type descriptor"), classDisplay)
def createValue(self):
return (self['desc'].value,)
def createDisplay(self):
return self['desc'].display
class MethodDescriptorEntry(FieldSet):
static_size = 96
def createFields(self):
yield textHandler(UInt32(self, "args", description="Argument type"), argumentDisplay)
yield textHandler(UInt32(self, "return", description="Return type"), classIndex)
yield UInt32(self, "param_offset", "Offset to parameter detail list")
def createValue(self):
return (self['args'].value, self['return'].value)
def createDisplay(self):
return "%s (%s)" % (self['return'].display, ', '.join(self['args'].display))
class FieldEntry(FieldSet):
static_size = 64
def createFields(self):
yield textHandler(UInt16(self, "class", description="Class containing this field"), classIndex)
yield textHandler(UInt16(self, "type", description="Field type"), classIndex)
yield textHandler(UInt32(self, "name", description="Field name"), stringIndex)
def createValue(self):
return (self['class'].value, self['type'].value, self['name'].value)
def createDisplay(self):
return "%s %s.%s" % (self['type'].display, self['class'].display, self['name'].display)
class MethodEntry(FieldSet):
static_size = 64
def createFields(self):
yield textHandler(UInt16(self, "class", description="Class containing this method"), classIndex)
yield textHandler(UInt16(self, "sig", description="Method signature"), signatureIndex)
yield textHandler(UInt32(self, "name", description="Method name"), stringIndex)
def createValue(self):
return (self['class'].value, self['sig'].value, self['name'].value)
def createDisplay(self):
sig = self['/meth_desc_table/item[%d]' % self['sig'].value]
return "%s %s.%s(%s)" % (
sig['return'].display, self['class'].display, self['name'].display, ', '.join(sig['args'].display))
class AccessFlags(FieldSet):
static_size = 32
def createFields(self):
yield Bit(self, "public")
yield Bit(self, "private")
yield Bit(self, "protected")
yield Bit(self, "static")
yield Bit(self, "final")
yield Bit(self, "synchronized")
yield Bit(self, "volatile")
yield Bit(self, "transient")
yield Bit(self, "native")
yield Bit(self, "interface")
yield Bit(self, "abstract")
yield Bit(self, "strictfp")
yield Bit(self, "synthetic")
yield Bit(self, "annotation")
yield Bit(self, "enum")
yield NullBits(self, "reserved[]", 1)
yield Bit(self, "constructor")
yield NullBits(self, "reserved[]", 15)
def createValue(self):
return tuple(f for f in self if f.value is True)
def createDisplay(self):
return ' '.join(f.name for f in self if f.value is True)
class ClassEntry(FieldSet):
static_size = 8 * 32
def createFields(self):
yield textHandler(UInt32(self, "class", description="Class being described"), classIndex)
yield AccessFlags(self, "flags")
yield textHandler(UInt32(self, "superclass", description="Superclass"), classIndex)
yield UInt32(self, "interfaces_offset", description="Offset to interface list")
yield textHandler(UInt32(self, "filename", description="Filename"), stringIndex)
yield UInt32(self, "annotations_offset")
yield UInt32(self, "class_data_offset")
yield UInt32(self, "static_values_offset")
def createValue(self):
return tuple(f.value for f in self)
def createDisplay(self):
disp = self['flags'].display
if not self['flags/interface'].value:
if disp:
disp += ' '
disp += 'class'
disp += ' ' + self['class'].display
if self['superclass'].display != 'java.lang.Object':
disp += ' extends ' + self['superclass'].display
return disp
class DexFile(HachoirParser, RootSeekableFieldSet):
MAGIC = "dex\n"
PARSER_TAGS = {
"id": "dex",
"category": "program",
"file_ext": ("dex",),
"min_size": 80 * 8,
"magic": ((MAGIC, 0),),
"description": "Dalvik VM 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, len(self.MAGIC)) != self.MAGIC:
return "Invalid magic"
if self['header/version'].value != '035':
return "Unknown version"
return True
def createFields(self):
yield DexHeader(self, "header")
self.seekByte(self['header/string_offset'].value)
yield GenericVector(self, "string_offsets", self['header/string_count'].value, UInt32,
description="Offsets for string table")
self.seekByte(self['string_offsets/item[0]'].value)
yield StringTable(self, "string_table",
description="String table")
self.seekByte(self['header/type_desc_offset'].value)
yield GenericVector(self, "type_desc_table", self['header/type_desc_count'].value, TypeDescriptorEntry,
description="Type descriptor table")
self.seekByte(self['header/meth_desc_offset'].value)
yield GenericVector(self, "meth_desc_table", self['header/meth_desc_count'].value, MethodDescriptorEntry,
description="Method descriptor table")
self.seekByte(self['header/field_offset'].value)
yield GenericVector(self, "field_table", self['header/field_count'].value, FieldEntry,
description="Field definition table")
self.seekByte(self['header/method_offset'].value)
yield GenericVector(self, "method_table", self['header/method_count'].value, MethodEntry,
description="Method definition table")
self.seekByte(self['header/class_offset'].value)
yield GenericVector(self, "class_table", self['header/class_count'].value, ClassEntry,
description="Class definition table")