llvm/lldb/examples/python/file_extract.py

#!/usr/bin/env python

import string
import struct
import sys


class FileExtract:
    """Decode binary data from a file"""

    def __init__(self, f, b="="):
        """Initialize with an open binary file and optional byte order"""

        self.file = f
        self.byte_order = b
        self.offsets = list()

    def set_byte_order(self, b):
        '''Set the byte order, valid values are "big", "little", "swap", "native", "<", ">", "@", "="'''
        if b == "big":
            self.byte_order = ">"
        elif b == "little":
            self.byte_order = "<"
        elif b == "swap":
            # swap what ever the current byte order is
            self.byte_order = swap_unpack_char()
        elif b == "native":
            self.byte_order = "="
        elif b == "<" or b == ">" or b == "@" or b == "=":
            self.byte_order = b
        else:
            print("error: invalid byte order specified: '%s'" % b)

    def is_in_memory(self):
        return False

    def seek(self, offset, whence=0):
        if self.file:
            return self.file.seek(offset, whence)
        raise ValueError

    def tell(self):
        if self.file:
            return self.file.tell()
        raise ValueError

    def read_size(self, byte_size):
        s = self.file.read(byte_size)
        if len(s) != byte_size:
            return None
        return s

    def push_offset_and_seek(self, offset):
        '''Push the current file offset and seek to "offset"'''
        self.offsets.append(self.file.tell())
        self.file.seek(offset, 0)

    def pop_offset_and_seek(self):
        """Pop a previously pushed file offset, or do nothing if there were no previously pushed offsets"""
        if len(self.offsets) > 0:
            self.file.seek(self.offsets.pop())

    def get_sint8(self, fail_value=0):
        """Extract a single int8_t from the binary file at the current file position, returns a single integer"""
        s = self.read_size(1)
        if s:
            (v,) = struct.unpack(self.byte_order + "b", s)
            return v
        else:
            return fail_value

    def get_uint8(self, fail_value=0):
        """Extract a single uint8_t from the binary file at the current file position, returns a single integer"""
        s = self.read_size(1)
        if s:
            (v,) = struct.unpack(self.byte_order + "B", s)
            return v
        else:
            return fail_value

    def get_sint16(self, fail_value=0):
        """Extract a single int16_t from the binary file at the current file position, returns a single integer"""
        s = self.read_size(2)
        if s:
            (v,) = struct.unpack(self.byte_order + "h", s)
            return v
        else:
            return fail_value

    def get_uint16(self, fail_value=0):
        """Extract a single uint16_t from the binary file at the current file position, returns a single integer"""
        s = self.read_size(2)
        if s:
            (v,) = struct.unpack(self.byte_order + "H", s)
            return v
        else:
            return fail_value

    def get_sint32(self, fail_value=0):
        """Extract a single int32_t from the binary file at the current file position, returns a single integer"""
        s = self.read_size(4)
        if s:
            (v,) = struct.unpack(self.byte_order + "i", s)
            return v
        else:
            return fail_value

    def get_uint32(self, fail_value=0):
        """Extract a single uint32_t from the binary file at the current file position, returns a single integer"""
        s = self.read_size(4)
        if s:
            (v,) = struct.unpack(self.byte_order + "I", s)
            return v
        else:
            return fail_value

    def get_sint64(self, fail_value=0):
        """Extract a single int64_t from the binary file at the current file position, returns a single integer"""
        s = self.read_size(8)
        if s:
            (v,) = struct.unpack(self.byte_order + "q", s)
            return v
        else:
            return fail_value

    def get_uint64(self, fail_value=0):
        """Extract a single uint64_t from the binary file at the current file position, returns a single integer"""
        s = self.read_size(8)
        if s:
            (v,) = struct.unpack(self.byte_order + "Q", s)
            return v
        else:
            return fail_value

    def get_fixed_length_c_string(
        self, n, fail_value="", isprint_only_with_space_padding=False
    ):
        """Extract a single fixed length C string from the binary file at the current file position, returns a single C string"""
        s = self.read_size(n)
        if s:
            (cstr,) = struct.unpack(self.byte_order + ("%i" % n) + "s", s)
            # Strip trialing NULLs
            cstr = string.strip(cstr, "\0")
            if isprint_only_with_space_padding:
                for c in cstr:
                    if c in string.printable or ord(c) == 0:
                        continue
                    return fail_value
            return cstr
        else:
            return fail_value

    def get_c_string(self):
        """Extract a single NULL terminated C string from the binary file at the current file position, returns a single C string"""
        cstr = ""
        byte = self.get_uint8()
        while byte != 0:
            cstr += "%c" % byte
            byte = self.get_uint8()
        return cstr

    def get_n_sint8(self, n, fail_value=0):
        """Extract "n" int8_t integers from the binary file at the current file position, returns a list of integers"""
        s = self.read_size(n)
        if s:
            return struct.unpack(self.byte_order + ("%u" % n) + "b", s)
        else:
            return (fail_value,) * n

    def get_n_uint8(self, n, fail_value=0):
        """Extract "n" uint8_t integers from the binary file at the current file position, returns a list of integers"""
        s = self.read_size(n)
        if s:
            return struct.unpack(self.byte_order + ("%u" % n) + "B", s)
        else:
            return (fail_value,) * n

    def get_n_sint16(self, n, fail_value=0):
        """Extract "n" int16_t integers from the binary file at the current file position, returns a list of integers"""
        s = self.read_size(2 * n)
        if s:
            return struct.unpack(self.byte_order + ("%u" % n) + "h", s)
        else:
            return (fail_value,) * n

    def get_n_uint16(self, n, fail_value=0):
        """Extract "n" uint16_t integers from the binary file at the current file position, returns a list of integers"""
        s = self.read_size(2 * n)
        if s:
            return struct.unpack(self.byte_order + ("%u" % n) + "H", s)
        else:
            return (fail_value,) * n

    def get_n_sint32(self, n, fail_value=0):
        """Extract "n" int32_t integers from the binary file at the current file position, returns a list of integers"""
        s = self.read_size(4 * n)
        if s:
            return struct.unpack(self.byte_order + ("%u" % n) + "i", s)
        else:
            return (fail_value,) * n

    def get_n_uint32(self, n, fail_value=0):
        """Extract "n" uint32_t integers from the binary file at the current file position, returns a list of integers"""
        s = self.read_size(4 * n)
        if s:
            return struct.unpack(self.byte_order + ("%u" % n) + "I", s)
        else:
            return (fail_value,) * n

    def get_n_sint64(self, n, fail_value=0):
        """Extract "n" int64_t integers from the binary file at the current file position, returns a list of integers"""
        s = self.read_size(8 * n)
        if s:
            return struct.unpack(self.byte_order + ("%u" % n) + "q", s)
        else:
            return (fail_value,) * n

    def get_n_uint64(self, n, fail_value=0):
        """Extract "n" uint64_t integers from the binary file at the current file position, returns a list of integers"""
        s = self.read_size(8 * n)
        if s:
            return struct.unpack(self.byte_order + ("%u" % n) + "Q", s)
        else:
            return (fail_value,) * n