1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
|
#!/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")
|