mirror of
https://github.com/llvm/llvm-project.git
synced 2026-10-26 13:53:31 +08:00
It appears that both spellings are correct, but "null terminator" is far more common, while "NUL terminator" is technically precise regarding the ASCII character name. Most common in LLDB was "NULL terminated" which is the worst of both worlds. This rallies around "null-terminated". - Adjective -> null-terminated - Verb -> null-terminate - Nouns left unhyphenated but lowercased: null terminator, null termination
225 lines
7.9 KiB
Python
Executable File
225 lines
7.9 KiB
Python
Executable File
#!/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
|