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