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 Auxiliary classes and functions.
33
34 @group Read/Write data stream objects:
35 ReadData, WriteData
36 """
37
38 __revision__ = "$Id$"
39
40 __all__ = [
41 "ReadData",
42 "WriteData",
43 ]
44
45 import excep
46
47 from cStringIO import StringIO as cstringio
48 from StringIO import StringIO
49 from struct import pack, unpack
50 import uuid
51
53 """
54 Tries to determine if a given value is a power of two.
55
56 @type value: int
57 @param value: Value to test if it is power of two.
58
59 @rtype: bool
60 @return: C{True} if the value is power of two,
61 C{False} if it doesn't.
62 """
63 return value != 0 and (value & (value - 1)) == 0
64
66 """
67 Tries to determine if a buffer is empty.
68
69 @type buffer: str
70 @param buffer: Buffer to test if it is empty.
71
72 @rtype: bool
73 @return: C{True} if the given buffer is empty, i.e. full of zeros,
74 C{False} if it doesn't.
75 """
76 allZero = True
77 for byte in buffer:
78 if byte != "\x00":
79 allZero = False
80 break
81 return allZero
82
84 """Return a WriteData-like stream object for writing."""
85 - def __init__(self, data, endianness = "<", signed = False):
86 """
87 @type data: str
88 @param data: Data to create the L{WriteData} object.
89
90 @type endianness: str
91 @param endianness: (Optional) Indicates the endianness used to write the data. The C{<} indicates little-endian while C{>} indicates big-endian.
92
93 @type signed: bool
94 @param signed: (Optional) If set to C{True} the data will be treated as signed. If set to C{False} it will be treated as unsigned.
95 """
96 self.data = StringIO(data)
97 self.endianness = endianness
98 self.signed = signed
99
101 return len(self.data.buf[self.data.tell():])
102
104 return self.data.getvalue()
105
107 """
108 Writes a byte into the L{WriteData} stream object.
109
110 @type byte: int
111 @param byte: Byte value to write into the stream.
112 """
113 self.data.write(pack("B" if not self.signed else "b", byte))
114
116 """
117 Writes a word value into the L{WriteData} stream object.
118
119 @type word: int
120 @param word: Word value to write into the stream.
121 """
122 self.data.write(pack(self.endianness + ("H" if not self.signed else "h"), word))
123
125 """
126 Writes a dword value into the L{WriteData} stream object.
127
128 @type dword: int
129 @param dword: Dword value to write into the stream.
130 """
131 self.data.write(pack(self.endianness + ("L" if not self.signed else "l"), dword))
132
134 """
135 Writes a qword value into the L{WriteData} stream object.
136
137 @type qword: int
138 @param qword: Qword value to write into the stream.
139 """
140 self.data.write(pack(self.endianness + ("Q" if not self.signed else "q"), qword))
141
142 - def write(self, dataToWrite):
143 """
144 Writes data into the L{WriteData} stream object.
145
146 @type dataToWrite: str
147 @param dataToWrite: Data to write into the stream.
148 """
149 self.data.write(dataToWrite)
150
152 """
153 Sets the offset of the L{WriteData} stream object in wich the data is written.
154
155 @type value: int
156 @param value: Integer value that represent the offset we want to start writing in the L{WriteData} stream.
157
158 @raise WrongOffsetValueException: The value is beyond the total length of the data.
159 """
160 if value >= len(self.data.getvalue()):
161 raise excep.WrongOffsetValueException("Wrong offset value. Must be less than %d" % len(self.data))
162 self.data.seek(value)
163
165 """
166 Skips the specified number as parameter to the current value of the L{WriteData} stream.
167
168 @type nroBytes: int
169 @param nroBytes: The number of bytes to skip.
170 """
171 self.data.seek(nroBytes + self.data.tell())
172
174 """
175 Returns the current position of the offset in the L{WriteData} sream object.
176
177 @rtype: int
178 @return: The value of the current offset in the stream.
179 """
180 return self.data.tell()
181
183 self.data.close()
184 del self.data
185
187 """Returns a ReadData-like stream object."""
188 - def __init__(self, data, endianness = "<", signed = False):
189 """
190 @type data: str
191 @param data: The data from which we want to read.
192
193 @type endianness: str
194 @param endianness: (Optional) Indicates the endianness used to read the data. The C{<} indicates little-endian while C{>} indicates big-endian.
195
196 @type signed: bool
197 @param signed: (Optional) If set to C{True} the data will be treated as signed. If set to C{False} it will be treated as unsigned.
198 """
199 self.data = data
200 self.offset = 0
201 self.endianness = endianness
202 self.signed = signed
203 self.log = False
204 self.length = len(data)
205
207 return self.length - self.offset
208
210 """
211 Reads a dword value from the L{ReadData} stream object.
212
213 @rtype: int
214 @return: The dword value read from the L{ReadData} stream.
215 """
216 dword = unpack(self.endianness + ('L' if not self.signed else 'l'), self.readAt(self.offset, 4))[0]
217 self.offset += 4
218 return dword
219
221 """
222 Reads a word value from the L{ReadData} stream object.
223
224 @rtype: int
225 @return: The word value read from the L{ReadData} stream.
226 """
227 word = unpack(self.endianness + ('H' if not self.signed else 'h'), self.readAt(self.offset, 2))[0]
228 self.offset += 2
229 return word
230
232 """
233 Reads a byte value from the L{ReadData} stream object.
234
235 @rtype: int
236 @return: The byte value read from the L{ReadData} stream.
237 """
238 byte = unpack('B' if not self.signed else 'b', self.readAt(self.offset, 1))[0]
239 self.offset += 1
240 return byte
241
243 """
244 Reads a qword value from the L{ReadData} stream object.
245
246 @rtype: int
247 @return: The qword value read from the L{ReadData} stream.
248 """
249 qword = unpack(self.endianness + ('Q' if not self.signed else 'b'), self.readAt(self.offset, 8))[0]
250 self.offset += 8
251 return qword
252
254 """
255 Reads an ASCII string from the L{ReadData} stream object.
256
257 @rtype: str
258 @return: An ASCII string read form the stream.
259 """
260 resultStr = ""
261 while self.data[self.offset] != "\x00":
262 resultStr += self.data[self.offset]
263 self.offset += 1
264 return resultStr
265
267 """
268 Reads an ASCII string aligned to the next align-bytes boundary.
269
270 @type align: int
271 @param align: (Optional) The value we want the ASCII string to be aligned.
272
273 @rtype: str
274 @return: A 4-bytes aligned (default) ASCII string.
275 """
276 s = self.readString()
277 r = align - len(s) % align
278 while r:
279 s += self.data[self.offset]
280 self.offset += 1
281 r -= 1
282 return s.rstrip("\x00")
283
284 - def read(self, nroBytes):
285 """
286 Reads data from the L{ReadData} stream object.
287
288 @type nroBytes: int
289 @param nroBytes: The number of bytes to read.
290
291 @rtype: str
292 @return: A string containing the read data from the L{ReadData} stream object.
293
294 @raise DataLengthException: The number of bytes tried to be read are more than the remaining in the L{ReadData} stream.
295 """
296 if nroBytes > self.length - self.offset:
297 if self.log:
298 print "Warning: Trying to read: %d bytes - only %d bytes left" % (nroBytes, self.length - self.offset)
299 nroBytes = self.length - self.offset
300
301 resultStr = self.data[self.offset:self.offset + nroBytes]
302 self.offset += nroBytes
303 return resultStr
304
306 """
307 Skips the specified number as parameter to the current value of the L{ReadData} stream.
308
309 @type nroBytes: int
310 @param nroBytes: The number of bytes to skip.
311 """
312 self.offset += nroBytes
313
315 """
316 Sets the offset of the L{ReadData} stream object in wich the data is read.
317
318 @type value: int
319 @param value: Integer value that represent the offset we want to start reading in the L{ReadData} stream.
320
321 @raise WrongOffsetValueException: The value is beyond the total length of the data.
322 """
323
324
325 self.offset = value
326
327 - def readAt(self, offset, size):
328 """
329 Reads as many bytes indicated in the size parameter at the specific offset.
330
331 @type offset: int
332 @param offset: Offset of the value to be read.
333
334 @type size: int
335 @param size: This parameter indicates how many bytes are going to be read from a given offset.
336
337 @rtype: str
338 @return: A packed string containing the read data.
339 """
340 if offset > self.length:
341 if self.log:
342 print "Warning: Trying to read: %d bytes - only %d bytes left" % (nroBytes, self.length - self.offset)
343 offset = self.length - self.offset
344 tmpOff = self.tell()
345 self.setOffset(offset)
346 r = self.read(size)
347 self.setOffset(tmpOff)
348 return r
349
351 """
352 Returns the current position of the offset in the L{ReadData} sream object.
353
354 @rtype: int
355 @return: The value of the current offset in the stream.
356 """
357 return self.offset
358
360 result = {}
361
362 if not isinstance(fields, (list, tuple)): return result
363 for field in fields:
364 if not isinstance(field, dict): raise Exception("Invalid field definition '{0}'.".format(type(field).__name__))
365 for k, v in field.iteritems():
366
367 parsed = v.parse(self)
368 result.update({ k: parsed.value })
369 break
370 return result
371
373 b = self.readByte()
374 if not b & 0x80:
375 result = b
376 elif not b & 0x40:
377 result = b & 0x3f
378 result = result << 8 | self.readByte()
379 elif not b & 0x20:
380 result = b & 0x1f
381 result = result << 8 | self.readByte()
382 result = result << 8 | self.readByte()
383 result = result << 8 | self.readByte()
384 else:
385 raise Exception("Invalid 7-bit encoded number.")
386 return result
387
392
394 length = self.read7BitEncodedInteger()
395 flag = False
396 if length % 2:
397 string = self.read(length - 1)
398 flag = bool(self.readByte())
399 else:
400 string = self.read(length)
401 string = string.decode('utf_16')
402 return string
403
405 guid = str(uuid.UUID(bytes=self.read(16)))
406 return guid
407
412