Package pype32 :: Module utils
[hide private]
[frames] | no frames]

Source Code for Module pype32.utils

  1  #!/usr/bin/python 
  2  # -*- coding: utf-8 -*-  
  3   
  4  # Copyright (c) 2013, Nahuel Riva  
  5  # All rights reserved.  
  6  #  
  7  # Redistribution and use in source and binary forms, with or without  
  8  # modification, are permitted provided that the following conditions are met:  
  9  #  
 10  #     * Redistributions of source code must retain the above copyright notice,  
 11  #       this list of conditions and the following disclaimer.  
 12  #     * Redistributions in binary form must reproduce the above copyright  
 13  #       notice,this list of conditions and the following disclaimer in the  
 14  #       documentation and/or other materials provided with the distribution.  
 15  #     * Neither the name of the copyright holder nor the names of its  
 16  #       contributors may be used to endorse or promote products derived from  
 17  #       this software without specific prior written permission.  
 18  #  
 19  # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"  
 20  # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE  
 21  # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE  
 22  # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE  
 23  # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR  
 24  # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF  
 25  # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS  
 26  # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN  
 27  # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)  
 28  # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE  
 29  # POSSIBILITY OF SUCH DAMAGE.  
 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   
52 -def powerOfTwo(value):
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
65 -def allZero(buffer):
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
83 -class WriteData(object):
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
100 - def __len__(self):
101 return len(self.data.buf[self.data.tell():])
102
103 - def __str__(self):
104 return self.data.getvalue()
105
106 - def writeByte(self, byte):
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
115 - def writeWord(self, word):
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
124 - def writeDword(self, dword):
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
133 - def writeQword(self, qword):
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
151 - def setOffset(self, value):
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
164 - def skipBytes(self, nroBytes):
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
173 - def tell(self):
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
182 - def __del__(self):
183 self.data.close() 184 del self.data
185
186 -class ReadData(object):
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
206 - def __len__(self):
207 return self.length - self.offset
208
209 - def readDword(self):
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
220 - def readWord(self):
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
231 - def readByte(self):
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
242 - def readQword(self):
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
253 - def readString(self):
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
266 - def readAlignedString(self, align = 4):
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
305 - def skipBytes(self, nroBytes):
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
314 - def setOffset(self, value):
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 #if value >= len(self.data): 324 # raise excep.WrongOffsetValueException("Wrong offset value. Must be less than %d" % len(self.data)) 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
350 - def tell(self):
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
359 - def readFields(self, fields):
360 result = {} 361 #if not isinstance(fields, (list, tuple)): raise Exception("Invalid field list '{0}'.".format(type(fields).__name__)) 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 #print type(v).__name__ 367 parsed = v.parse(self) 368 result.update({ k: parsed.value }) 369 break # only process the first 370 return result
371
372 - def read7BitEncodedInteger(self):
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
388 - def readDotNetString(self):
389 string = self.readString() 390 self.skipBytes(1) 391 return string
392
393 - def readDotNetUnicodeString(self):
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
404 - def readDotNetGuid(self):
405 guid = str(uuid.UUID(bytes=self.read(16))) 406 return guid
407
408 - def readDotNetBlob(self):
409 length = self.read7BitEncodedInteger() 410 blob = self.read(length) 411 return blob
412