DonPAPI/lib/eater.py

129 lines
4.4 KiB
Python
Raw Permalink Normal View History

2021-09-27 09:20:43 +00:00
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#############################################################################
## ##
## This file is part of DPAPIck ##
## Windows DPAPI decryption & forensic toolkit ##
## ##
## ##
## Copyright (C) 2010, 2011 Cassidian SAS. All rights reserved. ##
## This document is the property of Cassidian SAS, it may not be copied or ##
## circulated without prior licence ##
## ##
## Author: Jean-Michel Picod <jmichel.p@gmail.com> ##
## ##
## This program is distributed under GPLv3 licence (see LICENCE.txt) ##
## ##
#############################################################################
import struct
class Eater(object):
"""This class is a helper for parsing binary structures."""
def __init__(self, raw, offset=0, end=None, endianness="<"):
self.raw = raw
self.ofs = offset
if end is None:
end = len(raw)
self.end = end
self.endianness = endianness
def prepare_fmt(self, fmt):
"""Internal use. Prepend endianness to the given format if it is not
already specified.
fmt is a format string for struct.unpack()
Returns a tuple of the format string and the corresponding data size.
"""
if fmt[0] not in ["<", ">", "!", "@"]:
fmt = self.endianness+fmt
return fmt, struct.calcsize(fmt)
def read(self, fmt):
"""Parses data with the given format string without taking away bytes.
Returns an array of elements or just one element depending on fmt.
"""
fmt, sz = self.prepare_fmt(fmt)
v = struct.unpack_from(fmt, self.raw, self.ofs)
if len(v) == 1:
v = v[0]
return v
def eat(self, fmt):
"""Parses data with the given format string.
Returns an array of elements or just one element depending on fmt.
"""
fmt, sz = self.prepare_fmt(fmt)
v = struct.unpack_from(fmt, self.raw, self.ofs)
if len(v) == 1:
v = v[0]
self.ofs += sz
return v
def eat_string(self, length):
"""Eats and returns a string of length characters"""
return self.eat("%us" % length)
def eat_length_and_string(self, fmt):
"""Eats and returns a string which length is obtained after eating
an integer represented by fmt
"""
l = self.eat(fmt)
return self.eat_string(l)
def pop(self, fmt):
"""Eats a structure represented by fmt from the end of raw data"""
fmt, sz = self.prepare_fmt(fmt)
self.end -= sz
v = struct.unpack_from(fmt, self.raw, self.end)
if len(v) == 1:
v = v[0]
return v
def pop_string(self, length):
"""Pops and returns a string of length characters"""
return self.pop("%us" % length)
def pop_length_and_string(self, fmt):
"""Pops and returns a string which length is obtained after poping an
integer represented by fmt.
"""
l = self.pop(fmt)
return self.pop_string(l)
def remain(self):
"""Returns all the bytes that have not been eated nor poped yet."""
return self.raw[self.ofs:self.end]
def eat_sub(self, length):
"""Eats a sub-structure that is contained in the next length bytes"""
sub = self.__class__(self.raw[self.ofs:self.ofs+length], endianness=self.endianness)
self.ofs += length
return sub
def __nonzero__(self):
return self.ofs < self.end
class DataStruct(object):
"""Don't use this class unless you know what you are doing!"""
def __init__(self, raw=None):
if raw is not None:
self.parse(Eater(raw, endianness="<"))
def parse(self, eater_obj):
raise NotImplementedError("This function must be implemented in subclasses")