Skip to content

Instantly share code, notes, and snippets.

@CyberMonitor
Forked from nueh/plist2hashcat.py
Created August 15, 2019 01:57
Show Gist options
  • Save CyberMonitor/4149682b160ce13b7f4041571858b0ec to your computer and use it in GitHub Desktop.
Save CyberMonitor/4149682b160ce13b7f4041571858b0ec to your computer and use it in GitHub Desktop.

Revisions

  1. @nueh nueh renamed this gist Jan 4, 2014. 1 changed file with 0 additions and 0 deletions.
    File renamed without changes.
  2. @nueh nueh created this gist Jan 4, 2014.
    872 changes: 872 additions & 0 deletions ml2hashcat.py
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,872 @@
    #!/usr/bin/env python

    """Utilities for writing code that runs on Python 2 and 3"""

    import operator
    import sys
    import types

    __author__ = "Benjamin Peterson <[email protected]>"
    __version__ = "1.2.0"

    """
    six is under MIT License
    Copyright (c) 2010-2011 Benjamin Peterson
    Permission is hereby granted, free of charge, to any person obtaining a copy of
    this software and associated documentation files (the "Software"), to deal in
    the Software without restriction, including without limitation the rights to
    use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
    the Software, and to permit persons to whom the Software is furnished to do so,
    subject to the following conditions:
    The above copyright notice and this permission notice shall be included in all
    copies or substantial portions of the Software.
    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
    FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
    COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
    IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
    CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE."""


    # True if we are running on Python 3.
    PY3 = sys.version_info[0] == 3

    if PY3:
    string_types = str,
    integer_types = int,
    class_types = type,
    text_type = str
    binary_type = bytes

    MAXSIZE = sys.maxsize
    else:
    string_types = basestring,
    integer_types = (int, long)
    class_types = (type, types.ClassType)
    text_type = unicode
    binary_type = str

    if sys.platform == "java":
    # Jython always uses 32 bits.
    MAXSIZE = int((1 << 31) - 1)
    else:
    # It's possible to have sizeof(long) != sizeof(Py_ssize_t).
    class X(object):
    def __len__(self):
    return 1 << 31
    try:
    len(X())
    except OverflowError:
    # 32-bit
    MAXSIZE = int((1 << 31) - 1)
    else:
    # 64-bit
    MAXSIZE = int((1 << 63) - 1)
    del X


    def _add_doc(func, doc):
    """Add documentation to a function."""
    func.__doc__ = doc


    def _import_module(name):
    """Import module, returning the module after the last dot."""
    __import__(name)
    return sys.modules[name]


    class _LazyDescr(object):

    def __init__(self, name):
    self.name = name

    def __get__(self, obj, tp):
    result = self._resolve()
    setattr(obj, self.name, result)
    # This is a bit ugly, but it avoids running this again.
    delattr(tp, self.name)
    return result


    class MovedModule(_LazyDescr):

    def __init__(self, name, old, new=None):
    super(MovedModule, self).__init__(name)
    if PY3:
    if new is None:
    new = name
    self.mod = new
    else:
    self.mod = old

    def _resolve(self):
    return _import_module(self.mod)


    class MovedAttribute(_LazyDescr):

    def __init__(self, name, old_mod, new_mod, old_attr=None, new_attr=None):
    super(MovedAttribute, self).__init__(name)
    if PY3:
    if new_mod is None:
    new_mod = name
    self.mod = new_mod
    if new_attr is None:
    if old_attr is None:
    new_attr = name
    else:
    new_attr = old_attr
    self.attr = new_attr
    else:
    self.mod = old_mod
    if old_attr is None:
    old_attr = name
    self.attr = old_attr

    def _resolve(self):
    module = _import_module(self.mod)
    return getattr(module, self.attr)



    class _MovedItems(types.ModuleType):
    """Lazy loading of moved objects"""


    _moved_attributes = [
    MovedAttribute("cStringIO", "cStringIO", "io", "StringIO"),
    MovedAttribute("filter", "itertools", "builtins", "ifilter", "filter"),
    MovedAttribute("input", "__builtin__", "builtins", "raw_input", "input"),
    MovedAttribute("map", "itertools", "builtins", "imap", "map"),
    MovedAttribute("reload_module", "__builtin__", "imp", "reload"),
    MovedAttribute("reduce", "__builtin__", "functools"),
    MovedAttribute("StringIO", "StringIO", "io"),
    MovedAttribute("xrange", "__builtin__", "builtins", "xrange", "range"),
    MovedAttribute("zip", "itertools", "builtins", "izip", "zip"),

    MovedModule("builtins", "__builtin__"),
    MovedModule("configparser", "ConfigParser"),
    MovedModule("copyreg", "copy_reg"),
    MovedModule("http_cookiejar", "cookielib", "http.cookiejar"),
    MovedModule("http_cookies", "Cookie", "http.cookies"),
    MovedModule("html_entities", "htmlentitydefs", "html.entities"),
    MovedModule("html_parser", "HTMLParser", "html.parser"),
    MovedModule("http_client", "httplib", "http.client"),
    MovedModule("BaseHTTPServer", "BaseHTTPServer", "http.server"),
    MovedModule("CGIHTTPServer", "CGIHTTPServer", "http.server"),
    MovedModule("SimpleHTTPServer", "SimpleHTTPServer", "http.server"),
    MovedModule("cPickle", "cPickle", "pickle"),
    MovedModule("queue", "Queue"),
    MovedModule("reprlib", "repr"),
    MovedModule("socketserver", "SocketServer"),
    MovedModule("tkinter", "Tkinter"),
    MovedModule("tkinter_dialog", "Dialog", "tkinter.dialog"),
    MovedModule("tkinter_filedialog", "FileDialog", "tkinter.filedialog"),
    MovedModule("tkinter_scrolledtext", "ScrolledText", "tkinter.scrolledtext"),
    MovedModule("tkinter_simpledialog", "SimpleDialog", "tkinter.simpledialog"),
    MovedModule("tkinter_tix", "Tix", "tkinter.tix"),
    MovedModule("tkinter_constants", "Tkconstants", "tkinter.constants"),
    MovedModule("tkinter_dnd", "Tkdnd", "tkinter.dnd"),
    MovedModule("tkinter_colorchooser", "tkColorChooser",
    "tkinter.colorchooser"),
    MovedModule("tkinter_commondialog", "tkCommonDialog",
    "tkinter.commondialog"),
    MovedModule("tkinter_tkfiledialog", "tkFileDialog", "tkinter.filedialog"),
    MovedModule("tkinter_font", "tkFont", "tkinter.font"),
    MovedModule("tkinter_messagebox", "tkMessageBox", "tkinter.messagebox"),
    MovedModule("tkinter_tksimpledialog", "tkSimpleDialog",
    "tkinter.simpledialog"),
    MovedModule("urllib_robotparser", "robotparser", "urllib.robotparser"),
    MovedModule("winreg", "_winreg"),
    ]
    for attr in _moved_attributes:
    setattr(_MovedItems, attr.name, attr)
    del attr

    moves = sys.modules["six.moves"] = _MovedItems("moves")


    def add_move(move):
    """Add an item to six.moves."""
    setattr(_MovedItems, move.name, move)


    def remove_move(name):
    """Remove item from six.moves."""
    try:
    delattr(_MovedItems, name)
    except AttributeError:
    try:
    del moves.__dict__[name]
    except KeyError:
    raise AttributeError("no such move, %r" % (name,))


    if PY3:
    _meth_func = "__func__"
    _meth_self = "__self__"

    _func_code = "__code__"
    _func_defaults = "__defaults__"

    _iterkeys = "keys"
    _itervalues = "values"
    _iteritems = "items"
    else:
    _meth_func = "im_func"
    _meth_self = "im_self"

    _func_code = "func_code"
    _func_defaults = "func_defaults"

    _iterkeys = "iterkeys"
    _itervalues = "itervalues"
    _iteritems = "iteritems"


    try:
    advance_iterator = next
    except NameError:
    def advance_iterator(it):
    return it.next()
    next = advance_iterator


    if PY3:
    def get_unbound_function(unbound):
    return unbound

    Iterator = object

    def callable(obj):
    return any("__call__" in klass.__dict__ for klass in type(obj).__mro__)
    else:
    def get_unbound_function(unbound):
    return unbound.im_func

    class Iterator(object):

    def next(self):
    return type(self).__next__(self)

    callable = callable
    _add_doc(get_unbound_function,
    """Get the function out of a possibly unbound function""")


    get_method_function = operator.attrgetter(_meth_func)
    get_method_self = operator.attrgetter(_meth_self)
    get_function_code = operator.attrgetter(_func_code)
    get_function_defaults = operator.attrgetter(_func_defaults)


    def iterkeys(d):
    """Return an iterator over the keys of a dictionary."""
    return iter(getattr(d, _iterkeys)())

    def itervalues(d):
    """Return an iterator over the values of a dictionary."""
    return iter(getattr(d, _itervalues)())

    def iteritems(d):
    """Return an iterator over the (key, value) pairs of a dictionary."""
    return iter(getattr(d, _iteritems)())


    if PY3:
    def b(s):
    return s.encode("latin-1")
    def u(s):
    return s
    if sys.version_info[1] <= 1:
    def int2byte(i):
    return bytes((i,))
    else:
    # This is about 2x faster than the implementation above on 3.2+
    int2byte = operator.methodcaller("to_bytes", 1, "big")
    import io
    StringIO = io.StringIO
    BytesIO = io.BytesIO
    else:
    def b(s):
    return s
    def u(s):
    return unicode(s, "unicode_escape")
    int2byte = chr
    import StringIO
    StringIO = BytesIO = StringIO.StringIO
    _add_doc(b, """Byte literal""")
    _add_doc(u, """Text literal""")


    if PY3:
    import builtins
    exec_ = getattr(builtins, "exec")


    def reraise(tp, value, tb=None):
    if value.__traceback__ is not tb:
    raise value.with_traceback(tb)
    raise value


    print_ = getattr(builtins, "print")
    del builtins

    else:
    def exec_(code, globs=None, locs=None):
    """Execute code in a namespace."""
    if globs is None:
    frame = sys._getframe(1)
    globs = frame.f_globals
    if locs is None:
    locs = frame.f_locals
    del frame
    elif locs is None:
    locs = globs
    exec("""exec code in globs, locs""")


    exec_("""def reraise(tp, value, tb=None):
    raise tp, value, tb
    """)


    def print_(*args, **kwargs):
    """The new-style print function."""
    fp = kwargs.pop("file", sys.stdout)
    if fp is None:
    return
    def write(data):
    if not isinstance(data, basestring):
    data = str(data)
    fp.write(data)
    want_unicode = False
    sep = kwargs.pop("sep", None)
    if sep is not None:
    if isinstance(sep, unicode):
    want_unicode = True
    elif not isinstance(sep, str):
    raise TypeError("sep must be None or a string")
    end = kwargs.pop("end", None)
    if end is not None:
    if isinstance(end, unicode):
    want_unicode = True
    elif not isinstance(end, str):
    raise TypeError("end must be None or a string")
    if kwargs:
    raise TypeError("invalid keyword arguments to print()")
    if not want_unicode:
    for arg in args:
    if isinstance(arg, unicode):
    want_unicode = True
    break
    if want_unicode:
    newline = unicode("\n")
    space = unicode(" ")
    else:
    newline = "\n"
    space = " "
    if sep is None:
    sep = space
    if end is None:
    end = newline
    for i, arg in enumerate(args):
    if i:
    write(sep)
    write(arg)
    write(end)

    _add_doc(reraise, """Reraise an exception.""")


    def with_metaclass(meta, base=object):
    """Create a base class with a metaclass."""
    return meta("NewBase", (base,), {})


    """
    biplist is under BSD license
    Copyright (c) 2010, Andrew Wooster
    All rights reserved.
    Redistribution and use in source and binary forms, with or without
    modification, are permitted provided that the following conditions are met:
    * Redistributions of source code must retain the above copyright notice,
    this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
    notice, this list of conditions and the following disclaimer in the
    documentation and/or other materials provided with the distribution.
    * Neither the name of biplist nor the names of its contributors may be
    used to endorse or promote products derived from this software without
    specific prior written permission.
    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
    DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
    FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
    DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
    SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
    CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
    OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
    OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE"""

    """biplist -- a library for reading and writing binary property list files.
    Binary Property List (plist) files provide a faster and smaller serialization
    format for property lists on OS X. This is a library for generating binary
    plists which can be read by OS X, iOS, or other clients.
    The API models the plistlib API, and will call through to plistlib when
    XML serialization or deserialization is required.
    To generate plists with UID values, wrap the values with the Uid object. The
    value must be an int.
    To generate plists with NSData/CFData values, wrap the values with the
    Data object. The value must be a string.
    Date values can only be datetime.datetime objects.
    The exceptions InvalidPlistException and NotBinaryPlistException may be
    thrown to indicate that the data cannot be serialized or deserialized as
    a binary plist.
    Plist generation example:
    from biplist import *
    from datetime import datetime
    plist = {'aKey':'aValue',
    '0':1.322,
    'now':datetime.now(),
    'list':[1,2,3],
    'tuple':('a','b','c')
    }
    try:
    writePlist(plist, "example.plist")
    except (InvalidPlistException, NotBinaryPlistException), e:
    print "Something bad happened:", e
    Plist parsing example:
    from biplist import *
    try:
    plist = readPlist("example.plist")
    print plist
    except (InvalidPlistException, NotBinaryPlistException), e:
    print "Not a plist:", e
    """

    import sys
    from collections import namedtuple
    import calendar
    import datetime
    import math
    import plistlib
    from struct import pack, unpack
    import sys
    import time

    # import six

    __all__ = [
    'Uid', 'Data', 'readPlist', 'writePlist', 'readPlistFromString',
    'writePlistToString', 'InvalidPlistException', 'NotBinaryPlistException'
    ]

    apple_reference_date_offset = 978307200

    class Uid(int):
    """Wrapper around integers for representing UID values. This
    is used in keyed archiving."""
    def __repr__(self):
    return "Uid(%d)" % self

    class Data(binary_type):
    """Wrapper around str types for representing Data values."""
    pass

    class InvalidPlistException(Exception):
    """Raised when the plist is incorrectly formatted."""
    pass

    class NotBinaryPlistException(Exception):
    """Raised when a binary plist was expected but not encountered."""
    pass

    def readPlist(pathOrFile):
    """Raises NotBinaryPlistException, InvalidPlistException"""
    didOpen = False
    result = None
    if isinstance(pathOrFile, (binary_type, text_type)):
    pathOrFile = open(pathOrFile, 'rb')
    didOpen = True
    try:
    reader = PlistReader(pathOrFile)
    result = reader.parse()
    except NotBinaryPlistException as e:
    try:
    pathOrFile.seek(0)
    result = plistlib.readPlist(pathOrFile)
    result = wrapDataObject(result, for_binary=True)
    except Exception as e:
    raise InvalidPlistException(e)
    if didOpen:
    pathOrFile.close()
    return result

    def wrapDataObject(o, for_binary=False):
    if isinstance(o, Data) and not for_binary:
    o = plistlib.Data(o)
    elif isinstance(o, plistlib.Data) and for_binary:
    o = Data(o.data)
    elif isinstance(o, tuple):
    o = wrapDataObject(list(o), for_binary)
    o = tuple(o)
    elif isinstance(o, list):
    for i in range(len(o)):
    o[i] = wrapDataObject(o[i], for_binary)
    elif isinstance(o, dict):
    for k in o:
    o[k] = wrapDataObject(o[k], for_binary)
    return o

    def writePlist(rootObject, pathOrFile, binary=True):
    if not binary:
    rootObject = wrapDataObject(rootObject, binary)
    return plistlib.writePlist(rootObject, pathOrFile)
    else:
    didOpen = False
    if isinstance(pathOrFile, (six.binary_type, six.text_type)):
    pathOrFile = open(pathOrFile, 'wb')
    didOpen = True
    writer = PlistWriter(pathOrFile)
    result = writer.writeRoot(rootObject)
    if didOpen:
    pathOrFile.close()
    return result

    def readPlistFromString(data):
    return readPlist(six.BytesIO(data))

    def writePlistToString(rootObject, binary=True):
    if not binary:
    rootObject = wrapDataObject(rootObject, binary)
    if six.PY3:
    return plistlib.writePlistToBytes(rootObject)
    else:
    return plistlib.writePlistToString(rootObject)
    else:
    io = BytesIO()
    writer = PlistWriter(io)
    writer.writeRoot(rootObject)
    return io.getvalue()

    def is_stream_binary_plist(stream):
    stream.seek(0)
    header = stream.read(7)
    if header == b('bplist0'):
    return True
    else:
    return False

    PlistTrailer = namedtuple('PlistTrailer', 'offsetSize, objectRefSize, offsetCount, topLevelObjectNumber, offsetTableOffset')
    PlistByteCounts = namedtuple('PlistByteCounts', 'nullBytes, boolBytes, intBytes, realBytes, dateBytes, dataBytes, stringBytes, uidBytes, arrayBytes, setBytes, dictBytes')

    class PlistReader(object):
    file = None
    contents = ''
    offsets = None
    trailer = None
    currentOffset = 0

    def __init__(self, fileOrStream):
    """Raises NotBinaryPlistException."""
    self.reset()
    self.file = fileOrStream

    def parse(self):
    return self.readRoot()

    def reset(self):
    self.trailer = None
    self.contents = ''
    self.offsets = []
    self.currentOffset = 0

    def readRoot(self):
    result = None
    self.reset()
    # Get the header, make sure it's a valid file.
    if not is_stream_binary_plist(self.file):
    raise NotBinaryPlistException()
    self.file.seek(0)
    self.contents = self.file.read()
    if len(self.contents) < 32:
    raise InvalidPlistException("File is too short.")
    trailerContents = self.contents[-32:]
    try:
    self.trailer = PlistTrailer._make(unpack("!xxxxxxBBQQQ", trailerContents))
    offset_size = self.trailer.offsetSize * self.trailer.offsetCount
    offset = self.trailer.offsetTableOffset
    offset_contents = self.contents[offset:offset+offset_size]
    offset_i = 0
    while offset_i < self.trailer.offsetCount:
    begin = self.trailer.offsetSize*offset_i
    tmp_contents = offset_contents[begin:begin+self.trailer.offsetSize]
    tmp_sized = self.getSizedInteger(tmp_contents, self.trailer.offsetSize)
    self.offsets.append(tmp_sized)
    offset_i += 1
    self.setCurrentOffsetToObjectNumber(self.trailer.topLevelObjectNumber)
    result = self.readObject()
    except TypeError as e:
    raise InvalidPlistException(e)
    return result

    def setCurrentOffsetToObjectNumber(self, objectNumber):
    self.currentOffset = self.offsets[objectNumber]

    def readObject(self):
    result = None
    tmp_byte = self.contents[self.currentOffset:self.currentOffset+1]
    marker_byte = unpack("!B", tmp_byte)[0]
    format = (marker_byte >> 4) & 0x0f
    extra = marker_byte & 0x0f
    self.currentOffset += 1

    def proc_extra(extra):
    if extra == 0b1111:
    #self.currentOffset += 1
    extra = self.readObject()
    return extra

    # bool, null, or fill byte
    if format == 0b0000:
    if extra == 0b0000:
    result = None
    elif extra == 0b1000:
    result = False
    elif extra == 0b1001:
    result = True
    elif extra == 0b1111:
    pass # fill byte
    else:
    raise InvalidPlistException("Invalid object found at offset: %d" % (self.currentOffset - 1))
    # int
    elif format == 0b0001:
    extra = proc_extra(extra)
    result = self.readInteger(pow(2, extra))
    # real
    elif format == 0b0010:
    extra = proc_extra(extra)
    result = self.readReal(extra)
    # date
    elif format == 0b0011 and extra == 0b0011:
    result = self.readDate()
    # data
    elif format == 0b0100:
    extra = proc_extra(extra)
    result = self.readData(extra)
    # ascii string
    elif format == 0b0101:
    extra = proc_extra(extra)
    result = self.readAsciiString(extra)
    # Unicode string
    elif format == 0b0110:
    extra = proc_extra(extra)
    result = self.readUnicode(extra)
    # uid
    elif format == 0b1000:
    result = self.readUid(extra)
    # array
    elif format == 0b1010:
    extra = proc_extra(extra)
    result = self.readArray(extra)
    # set
    elif format == 0b1100:
    extra = proc_extra(extra)
    result = set(self.readArray(extra))
    # dict
    elif format == 0b1101:
    extra = proc_extra(extra)
    result = self.readDict(extra)
    else:
    raise InvalidPlistException("Invalid object found: {format: %s, extra: %s}" % (bin(format), bin(extra)))
    return result

    def readInteger(self, bytes):
    result = 0
    original_offset = self.currentOffset
    data = self.contents[self.currentOffset:self.currentOffset+bytes]
    result = self.getSizedInteger(data, bytes)
    self.currentOffset = original_offset + bytes
    return result

    def readReal(self, length):
    result = 0.0
    to_read = pow(2, length)
    data = self.contents[self.currentOffset:self.currentOffset+to_read]
    if length == 2: # 4 bytes
    result = unpack('>f', data)[0]
    elif length == 3: # 8 bytes
    result = unpack('>d', data)[0]
    else:
    raise InvalidPlistException("Unknown real of length %d bytes" % to_read)
    return result

    def readRefs(self, count):
    refs = []
    i = 0
    while i < count:
    fragment = self.contents[self.currentOffset:self.currentOffset+self.trailer.objectRefSize]
    ref = self.getSizedInteger(fragment, len(fragment))
    refs.append(ref)
    self.currentOffset += self.trailer.objectRefSize
    i += 1
    return refs

    def readArray(self, count):
    result = []
    values = self.readRefs(count)
    i = 0
    while i < len(values):
    self.setCurrentOffsetToObjectNumber(values[i])
    value = self.readObject()
    result.append(value)
    i += 1
    return result

    def readDict(self, count):
    result = {}
    keys = self.readRefs(count)
    values = self.readRefs(count)
    i = 0
    while i < len(keys):
    self.setCurrentOffsetToObjectNumber(keys[i])
    key = self.readObject()
    self.setCurrentOffsetToObjectNumber(values[i])
    value = self.readObject()
    result[key] = value
    i += 1
    return result

    def readAsciiString(self, length):
    result = unpack("!%ds" % length, self.contents[self.currentOffset:self.currentOffset+length])[0]
    self.currentOffset += length
    return result

    def readUnicode(self, length):
    actual_length = length*2
    data = self.contents[self.currentOffset:self.currentOffset+actual_length]
    # unpack not needed?!! data = unpack(">%ds" % (actual_length), data)[0]
    self.currentOffset += actual_length
    return data.decode('utf_16_be')

    def readDate(self):
    global apple_reference_date_offset
    result = unpack(">d", self.contents[self.currentOffset:self.currentOffset+8])[0]
    result = datetime.datetime.utcfromtimestamp(result + apple_reference_date_offset)
    self.currentOffset += 8
    return result

    def readData(self, length):
    result = self.contents[self.currentOffset:self.currentOffset+length]
    self.currentOffset += length
    return Data(result)

    def readUid(self, length):
    return Uid(self.readInteger(length+1))

    def getSizedInteger(self, data, bytes):
    result = 0
    # 1, 2, and 4 byte integers are unsigned
    if bytes == 1:
    result = unpack('>B', data)[0]
    elif bytes == 2:
    result = unpack('>H', data)[0]
    elif bytes == 4:
    result = unpack('>L', data)[0]
    elif bytes == 8:
    result = unpack('>q', data)[0]
    else:
    raise InvalidPlistException("Encountered integer longer than 8 bytes.")
    return result

    class HashableWrapper(object):
    def __init__(self, value):
    self.value = value
    def __repr__(self):
    return "<HashableWrapper: %s>" % [self.value]

    class BoolWrapper(object):
    def __init__(self, value):
    self.value = value
    def __repr__(self):
    return "<BoolWrapper: %s>" % self.value

    # Libraries end here, program starts
    # Written by Dhiru Kholia <dhiru at openwall.com> in September of 2012
    # My code is under "Simplified BSD License"
    # Adapted by Niklas Hennigs <[email protected]> for hashcat January 2014

    import binascii
    import getpass
    import os

    def process_file(filename):
    try:
    p1 = readPlist(filename)
    except IOError, e:
    print >> sys.stderr, "%s : %s" % (filename, str(e))
    return -1
    except InvalidPlistException:
    print >> sys.stderr, "%s is not a plist file!" % filename
    return -1

    s = StringIO(p1.get('ShadowHashData', [None])[0])
    if not s:
    sys.stderr.write("%s : could not find ShadowHashData\n" % filename)
    return -2

    try:
    p2 = readPlist(s)
    except Exception:
    e = sys.exc_info()[1]
    sys.stderr.write("%s : %s\n" % (filename, str(e)))
    return -3

    d = p2.get('SALTED-SHA512-PBKDF2', None)
    if not d:
    sys.stderr.write("%s does not contain SALTED-SHA512-PBKDF2\n" % filename)
    return -4

    salt = d.get('salt')
    entropy = d.get('entropy')
    iterations = d.get('iterations')
    salth = binascii.hexlify(salt)
    entropyh = binascii.hexlify(entropy)

    name = p1.get('name', ["user"])[0]

    sys.stdout.write("%s:$ml$%d$%s$%s\n" % \
    (name, iterations, salth, entropyh[0:128]))

    # from passlib.hash import grub_pbkdf2_sha512
    # hash = grub_pbkdf2_sha512.encrypt("password", rounds=iterations, salt=salt)
    # print hash

    if __name__ == "__main__":
    if len(sys.argv) < 2:
    print >> sys.stderr, "Usage: %s <Mountain Lion and later .plist files>\nUse with hashcat's '--username' option\nPlist: /private/var/db/dslocal/nodes/Default/users/%s.plist" % (os.path.basename(__file__), getpass.getuser())
    sys.exit(-1)

    for i in range(1, len(sys.argv)):
    process_file(sys.argv[i])