| #!/usr/bin/env python3 | |
| 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 |