def ReadBytes(params, ctxt, scope, stream, coord): if len(params) != 3: raise errors.InvalidArguments(coord, "3 arguments (buffer, pos, n)", "{} args".format(len(params))) if not isinstance(params[0], pfp.fields.Array): raise errors.InvalidArguments(coord, "buffer must be an array", params[0].__class__.__name__) if params[0].field_cls not in [pfp.fields.UChar, pfp.fields.Char]: raise errors.InvalidArguments( coord, "buffer must be an array of uchar or char", params[0].field_cls.__name__) if not isinstance(params[1], pfp.fields.IntBase): raise errors.InvalidArguments(coord, "pos must be an integer", params[1].__class__.__name__) if not isinstance(params[2], pfp.fields.IntBase): raise errors.InvalidArguments(coord, "n must be an integer", params[2].__class__.__name__) bits = stream._bits curr_pos = stream.tell() vals = [ params[0].field_cls(stream) for x in six.moves.range(PYVAL(params[2])) ] stream.seek(curr_pos, 0) stream._bits = bits params[0]._pfp__set_value(vals)
def packer_gzip(params, ctxt, scope, stream, coord): """``PackerGZip`` - implements both unpacking and packing. Can be used as the ``packer`` for a field. When packing, concats the build output of all params and gzip-compresses the result. When unpacking, concats the build output of all params and gzip-decompresses the result. Example: The code below specifies that the ``data`` field is gzipped and that once decompressed, should be parsed with ``PACK_TYPE``. When building the ``PACK_TYPE`` structure, ``data`` will be updated with the compressed data.:: char data[0x100]<packer=PackerGZip, packtype=PACK_TYPE>; :pack: True if the data should be packed, false if it should be unpacked :data: The data to operate on :returns: An array """ if len(params) <= 1: raise errors.InvalidArguments(coord, "{} args".format(len(params)), "at least two arguments") # to gzip it (pack it) if params[0]: return pack_gzip(params[1:], ctxt, scope, stream, coord) else: return unpack_gzip(params[1:], ctxt, scope, stream, coord)
def Stricmp(params, ctxt, scope, stream, coord): if len(params) != 2: raise errors.InvalidArguments(coord, "{} args".format(len(params)), "2 arguments") str1 = PYSTR(params[0]).lower() str2 = PYSTR(params[1]).lower() return _cmp(str1, str2)
def Strncpy(params, ctxt, scope, stream, coord): if len(params) != 3: raise errors.InvalidArguments(coord, "{} args".format(len(params)), "3 arguments") max_len = PYVAL(params[2]) params[0]._pfp__set_value(PYSTR(params[1])[:max_len])
def Strnicmp(params, ctxt, scope, stream, coord): if len(params) != 3: raise errors.InvalidArguments(coord, "{} args".format(len(params)), "3 arguments") max_chars = PYVAL(params[2]) str1 = PYSTR(params[0])[:max_chars].lower() str2 = PYSTR(params[1])[:max_chars].lower() return _cmp(str1, str2)
def IsLittleEndian(params, ctxt, scope, stream, coord): if len(params) > 0: raise errors.InvalidArguments(coord, "0 arguments", "{} args".format(len(params))) if pfp.fields.NumberBase.endian == pfp.fields.LITTLE_ENDIAN: return 0 else: return 1
def Strlen(params, ctxt, scope, stream, coord): if len(params) != 1: raise errors.InvalidArguments(coord, "1 argument", "{} args".format(len(params))) val = params[0] if isinstance(val, pfp.fields.Array): val = val._array_to_str() else: val = PYVAL(val) return len(val)
def FSkip(params, ctxt, scope, stream, coord): """Returns 0 if successful or -1 if the address is out of range """ if len(params) != 1: raise errors.InvalidArguments(coord, "{} args".format(len(params)), "FSkip accepts only one argument") skip_amt = PYVAL(params[0]) pos = stream.tell() return FSeek([pos + skip_amt], ctxt, scope, stream, coord)
def Strcmp(params, ctxt, scope, stream, coord): if len(params) != 2: raise errors.InvalidArguments(coord, "{} args".format(len(params)), "2 arguments") str1 = PYSTR(params[0]) str2 = PYSTR(params[1]) if params[0] is None or params[1] is None: return 0 return _cmp(str1, str2)
def FEof(params, ctxt, scope, stream, coord): if len(params) > 0: raise errors.InvalidArguments(coord, "0 arguments", "{} args".format(len(params))) # now that streams are _ALL_ BitwrappedStreams, we can use BitwrappedStream-specific # functions if stream.is_eof(): return 1 else: return 0
def Memcpy(params, ctxt, scope, stream, coord): if params[0]._pfp__interp._generate: return if len(params) < 3: raise errors.InvalidArguments(coord, "{} args".format(len(params)), "at least 3 args") if len(params) > 5: raise errors.InvalidArguments(coord, "{} args".format(len(params)), "at most 5 args") dest = params[0] src = params[1] n = PYVAL(params[2]) if len(params) > 3: dest_offset = PYVAL(params[3]) else: dest_offset = 0 if len(params) > 4: src_offset = PYVAL(params[4]) else: src_offset = 0 if not isinstance(dest, pfp.fields.Array): raise errors.InvalidArguments(coord, dest.__class__.__name__, "an array") if not isinstance(src, pfp.fields.Array): raise errors.InvalidArguments(coord, src.__class__.__name__, "an array") count = 0 while n > 0: val = dest.field_cls() val._pfp__set_value(src[src_offset + count]._pfp__value) # TODO clone it dest[dest_offset + count] = val count += 1 n -= 1
def Strstr(params, ctxt, scope, stream, coord): if len(params) != 2: raise errors.InvalidArguments(coord, "{} args".format(len(params)), "2 arguments") haystack = PYSTR(params[0]) needle = PYSTR(params[1]) try: return haystack.index(needle) # expected condition when the substring doesn't exist except ValueError as e: return -1
def FSeek(params, ctxt, scope, stream, coord): """Returns 0 if successful or -1 if the address is out of range """ if len(params) != 1: raise errors.InvalidArguments( coord, "{} args".format(len(params)), "FSeek accepts only one argument", ) if params[0] is None: return 0 pos = PYVAL(params[0]) curr_pos = stream.tell() fsize = stream.size() if pos > fsize: stream.seek(fsize) return -1 elif pos < 0: stream.seek(0) return -1 diff = pos - curr_pos if diff < 0: stream.seek(pos) return 0 data = stream.read(diff) # let the ctxt automatically append numbers, as needed, unless the previous # child was also a skipped field skipped_name = "_skipped" if len(ctxt._pfp__children ) > 0 and ctxt._pfp__children[-1]._pfp__name.startswith("_skipped"): old_name = ctxt._pfp__children[-1]._pfp__name data = ctxt._pfp__children[-1].raw_data + data skipped_name = old_name ctxt._pfp__children = ctxt._pfp__children[:-1] del ctxt._pfp__children_map[old_name] tmp_stream = bitwrap.BitwrappedStream(six.BytesIO(data)) new_field = pfp.fields.Array(len(data), pfp.fields.Char, tmp_stream) ctxt._pfp__add_child(skipped_name, new_field, stream) scope.add_var(skipped_name, new_field) return 0
def instantiate(self, scope, args, interp): """Create a ParamList instance for actual interpretation :args: TODO :returns: A ParamList object """ param_instances = [] BYREF = "byref" # TODO are default values for function parameters allowed in 010? #print 'self._params=',self._params,type(self._params) for x in self._params: if x == pfp.fields.Void: continue param_name, param_cls = x # we don't instantiate a copy of byref params if getattr(param_cls, "byref", False): param_instances.append(BYREF) else: field = param_cls() field._pfp__name = param_name param_instances.append(field) if len(args) != len(param_instances): raise errors.InvalidArguments( self._coords, [x.__class__.__name__ for x in args], [x.__class__.__name__ for x in param_instances]) # TODO type checking on provided types for x in six.moves.range(len(args)): param = param_instances[x] # arrays are simply passed through into the function. We shouldn't # have to worry about frozenness/unfrozenness at this point if param is BYREF or isinstance(param, pfp.fields.Array): param = args[x] param_instances[x] = param scope.add_local(self._params[x][0], param) else: param._pfp__set_value(args[x]) scope.add_local(param._pfp__name, param) param._pfp__interp = interp return ParamList(param_instances)
def SubStr(params, ctxt, scope, stream, coord): if len(params) < 2: raise errors.InvalidArguments(coord, "2 arguments", "{} args".format(len(params))) string = PYSTR(params[0]) start = PYVAL(params[1]) count = -1 if len(params) > 2: count = PYVAL(params[2]) if count < 0: count = -1 if count == -1: return string[start:] else: return string[start:start+count]
def _read_data(params, stream, cls, coord): bits = stream._bits curr_pos = stream.tell() if len(params) == 1: pos = PYVAL(params[0]) stream.seek(pos, 0) elif len(params) > 1: raise errors.InvalidArguments(coord, "at most 1 arguments", "{} args".format(len(params))) res = cls(stream=stream) # reset the stream stream.seek(curr_pos, 0) stream._bits = bits return res
def SPrintf(params, ctxt, scope, stream, coord): if len(params) < 2: raise errors.InvalidArguments(coord, "{} args".format(len(params)), "at least 2 args") if len(params) == 2: params[0]._pfp__set_value(PYSTR(params[1])) return len(PYSTR(params[1])) parts = [] for part in params[2:]: if isinstance(part, pfp.fields.Array) or isinstance(part, pfp.fields.String): parts.append(PYSTR(part)) else: parts.append(PYVAL(part)) new_value = PYSTR(params[1]) % tuple(parts) params[0]._pfp__set_value(new_value) return len(new_value)
def unpack_gzip(params, ctxt, scope, stream, coord): """``UnpackGZip`` - Concats the build output of all params and gunzips the resulting data, returning a char array. Example: :: char data[0x100]<pack=UnpackGZip, ...>; """ if len(params) == 0: raise errors.InvalidArguments(coord, "{} args".format(len(params)), "at least one argument") built = utils.binary("") for param in params: if isinstance(param, pfp.fields.Field): built += param._pfp__build() else: built += param return zlib.decompress(built)
def watch_length(params, ctxt, scope, stream, coord): """WatchLength - Watch the total length of each of the params. Example: The code below uses the ``WatchLength`` update function to update the ``length`` field to the length of the ``data`` field :: int length<watch=data, update=WatchLength>; char data[length]; """ if len(params) <= 1: raise errors.InvalidArguments(coord, "{} args".format(len(params)), "at least two arguments") to_update = params[0] total_size = 0 for param in params[1:]: total_size += param._pfp__width() to_update._pfp__set_value(total_size)
def watch_crc(params, ctxt, scope, stream, coord): """WatchCrc32 - Watch the total crc32 of the params. Example: The code below uses the ``WatchCrc32`` update function to update the ``crc`` field to the crc of the ``length`` and ``data`` fields :: char length; char data[length]; int crc<watch=length;data, update=WatchCrc32>; """ if len(params) <= 1: raise errors.InvalidArguments(coord, "{} args".format(len(params)), "at least two arguments") to_update = params[0] total_data = utils.binary("") for param in params[1:]: total_data += param._pfp__build() to_update._pfp__set_value(binascii.crc32(total_data))
def instantiate(self, scope, args, interp): """Create a ParamList instance for actual interpretation :args: TODO :returns: A ParamList object """ param_instances = [] BYREF = "byref" # TODO are default values for function parameters allowed in 010? for param_name, param_cls in self._params: # we don't instantiate a copy of byref params if getattr(param_cls, "byref", False): param_instances.append(BYREF) else: field = param_cls() field._pfp__name = param_name param_instances.append(field) if len(args) != len(param_instances): raise errors.InvalidArguments( self._coords, [x.__class__.__name__ for x in args], [x.__class__.__name__ for x in param_instances]) # TODO type checking on provided types for x in six.moves.range(len(args)): param = param_instances[x] if param is BYREF: param = args[x] param_instances[x] = param scope.add_local(self._params[x][0], param) else: param._pfp__set_value(args[x]) scope.add_local(param._pfp__name, param) param._pfp__interp = interp return ParamList(param_instances)
def Memcmp(params, ctxt, scope, stream, coord): """ int Memcmp( const uchar s1[], const uchar s2[], int n ) Compares the first n bytes of s1 and s2. Returns a value less than zero if s1 is less than s2, zero if they are equal, or a value greater than zero if s1 is greater than s2. """ if len(params) < 3: raise errors.InvalidArguments( coord, "{} args".format(len(params)), "3 arguments", ) s1 = PYSTR(params[0]) s2 = PYSTR(params[1]) n = PYVAL(params[2]) s1_sub = s1[:n] s2_sub = s2[:n] return _cmp(s1_sub, s2_sub)
def Atoi(params, ctxt, scope, stream, coord): if len(params) < 1: raise errors.InvalidArguments(coord, "{} args".format(len(params)), "one arg") return int(PYSTR(params[0]))
def Strcpy(params, ctxt, scope, stream, coord): if len(params) != 2: raise errors.InvalidArguments(coord, "{} args".format(len(params)), "2 arguments") params[0]._pfp__set_value(PYSTR(params[1]))
def ToUpper(params, ctxt, scope, stream, coord): if len(params) != 1: raise errors.InvalidArguments(coord, "{} args".format(len(params)), "1 argument") return ord(chr(PYVAL(params[0])).upper())
def BitfieldRightToLeft(params, ctxt, scope, stream, coord, interp): if len(params) > 0: raise errors.InvalidArguments(coord, "0 arguments", "{} args".format(len(params))) interp.set_bitfield_right_left()
def BitfieldEnablePadding(params, ctxt, scope, stream, coord, interp): if len(params) > 0: raise errors.InvalidArguments(coord, "0 arguments", "{} args".format(len(params))) interp.set_bitfield_padded(True)
def BigEndian(params, ctxt, scope, stream, coord): if len(params) > 0: raise errors.InvalidArguments(coord, "0 arguments", "{} args".format(len(params))) pfp.fields.NumberBase.endian = pfp.fields.BIG_ENDIAN
def Exit(params, ctxt, scope, stream, coord): if len(params) != 1: raise errors.InvalidArguments(coord, "1 arguments", "{} args".format(len(params))) error_code = PYVAL(params[0]) raise errors.InterpExit(error_code)
def FTell(params, ctxt, scope, stream, coord): if len(params) > 0: raise errors.InvalidArguments(coord, "0 arguments", "{} args".format(len(params))) return stream.tell()