Source code for plaso.parsers.bsm

"""Basic Security Module (BSM) event auditing file parser."""

import os

from dfdatetime import posix_time as dfdatetime_posix_time

from plaso.containers import events
from plaso.lib import definitions
from plaso.lib import dtfabric_helper
from plaso.lib import errors
from plaso.parsers import interface
from plaso.parsers import manager


[docs] class BSMEventData(events.EventData): """Basic Security Module (BSM) audit event data. Attributes: event_type (int): identifier that represents the type of the event. extra_tokens (list[dict[str, dict[str, str]]]): event extra tokens, which is a list of dictionaries that contain: {token type: {token values}} offset (int): offset of the BSM record relative to the start of the file, from which the event data was extracted. record_length (int): record length in bytes (trailer number). return_value (str): processed return value and exit status. written_time (dfdatetime.DateTimeValues): entry written date and time. """ DATA_TYPE = "bsm:entry"
[docs] def __init__(self): """Initializes event data.""" super().__init__(data_type=self.DATA_TYPE) self.event_type = None self.extra_tokens = None self.offset = None self.record_length = None self.return_value = None self.written_time = None
[docs] class BSMParser(interface.FileObjectParser, dtfabric_helper.DtFabricHelper): """Parser for Basic Security Module (BSM) event auditing files.""" NAME = "bsm_log" DATA_FORMAT = "Basic Security Module (BSM) event auditing file" _DEFINITION_FILE = os.path.join(os.path.dirname(__file__), "bsm.yaml") _TOKEN_TYPE_AUT_TRAILER = 0x13 _TOKEN_TYPE_AUT_HEADER32 = 0x14 _TOKEN_TYPE_AUT_HEADER32_EX = 0x15 _TOKEN_TYPE_AUT_RETURN32 = 0x27 _TOKEN_TYPE_AUT_RETURN64 = 0x72 _TOKEN_TYPE_AUT_HEADER64 = 0x74 _TOKEN_TYPE_AUT_HEADER64_EX = 0x79 _HEADER_TOKEN_TYPES = frozenset( [ _TOKEN_TYPE_AUT_HEADER32, _TOKEN_TYPE_AUT_HEADER32_EX, _TOKEN_TYPE_AUT_HEADER64, _TOKEN_TYPE_AUT_HEADER64_EX, ] ) _TOKEN_TYPES = { 0x00: "AUT_INVALID", 0x11: "AUT_OTHER_FILE32", 0x12: "AUT_OHEADER", 0x13: "AUT_TRAILER", 0x14: "AUT_HEADER32", 0x15: "AUT_HEADER32_EX", 0x21: "AUT_DATA", 0x22: "AUT_IPC", 0x23: "AUT_PATH", 0x24: "AUT_SUBJECT32", 0x25: "AUT_XATPATH", 0x26: "AUT_PROCESS32", 0x27: "AUT_RETURN32", 0x28: "AUT_TEXT", 0x29: "AUT_OPAQUE", 0x2A: "AUT_IN_ADDR", 0x2B: "AUT_IP", 0x2C: "AUT_IPORT", 0x2D: "AUT_ARG32", 0x2E: "AUT_SOCKET", 0x2F: "AUT_SEQ", 0x30: "AUT_ACL", 0x31: "AUT_ATTR", 0x32: "AUT_IPC_PERM", 0x33: "AUT_LABEL", 0x34: "AUT_GROUPS", 0x35: "AUT_ACE", 0x36: "AUT_SLABEL", 0x37: "AUT_CLEAR", 0x38: "AUT_PRIV", 0x39: "AUT_UPRIV", 0x3A: "AUT_LIAISON", 0x3B: "AUT_NEWGROUPS", 0x3C: "AUT_EXEC_ARGS", 0x3D: "AUT_EXEC_ENV", 0x3E: "AUT_ATTR32", 0x3F: "AUT_UNAUTH", 0x40: "AUT_XATOM", 0x41: "AUT_XOBJ", 0x42: "AUT_XPROTO", 0x43: "AUT_XSELECT", 0x44: "AUT_XCOLORMAP", 0x45: "AUT_XCURSOR", 0x46: "AUT_XFONT", 0x47: "AUT_XGC", 0x48: "AUT_XPIXMAP", 0x49: "AUT_XPROPERTY", 0x4A: "AUT_XWINDOW", 0x4B: "AUT_XCLIENT", 0x51: "AUT_CMD", 0x52: "AUT_EXIT", 0x60: "AUT_ZONENAME", 0x70: "AUT_HOST", 0x71: "AUT_ARG64", 0x72: "AUT_RETURN64", 0x73: "AUT_ATTR64", 0x74: "AUT_HEADER64", 0x75: "AUT_SUBJECT64", 0x76: "AUT_SERVER64", 0x77: "AUT_PROCESS64", 0x78: "AUT_OTHER_FILE64", 0x79: "AUT_HEADER64_EX", 0x7A: "AUT_SUBJECT32_EX", 0x7B: "AUT_PROCESS32_EX", 0x7C: "AUT_SUBJECT64_EX", 0x7D: "AUT_PROCESS64_EX", 0x7E: "AUT_IN_ADDR_EX", 0x7F: "AUT_SOCKET_EX", 0x80: "AUT_SOCKINET32", 0x81: "AUT_SOCKINET128", 0x82: "AUT_SOCKUNIX", } _DATA_TYPE_MAP_PER_TOKEN_TYPE = { 0x11: "bsm_token_data_other_file32", 0x13: "bsm_token_data_trailer", 0x14: "bsm_token_data_header32", 0x15: "bsm_token_data_header32_ex", 0x21: "bsm_token_data_data", 0x22: "bsm_token_data_ipc", 0x23: "bsm_token_data_path", 0x24: "bsm_token_data_subject32", 0x26: "bsm_token_data_subject32", 0x27: "bsm_token_data_return32", 0x28: "bsm_token_data_text", 0x29: "bsm_token_data_opaque", 0x2A: "bsm_token_data_in_addr", 0x2B: "bsm_token_data_ip", 0x2C: "bsm_token_data_iport", 0x2D: "bsm_token_data_arg32", 0x2F: "bsm_token_data_seq", 0x32: "bsm_token_data_ipc_perm", 0x34: "bsm_token_data_groups", 0x3B: "bsm_token_data_groups", 0x3C: "bsm_token_data_exec_args", 0x3D: "bsm_token_data_exec_args", 0x3E: "bsm_token_data_attr32", 0x52: "bsm_token_data_exit", 0x60: "bsm_token_data_zonename", 0x71: "bsm_token_data_arg64", 0x72: "bsm_token_data_return64", 0x73: "bsm_token_data_attr64", 0x74: "bsm_token_data_header64", 0x75: "bsm_token_data_subject64", 0x77: "bsm_token_data_subject64", 0x79: "bsm_token_data_header64_ex", 0x7A: "bsm_token_data_subject32_ex", 0x7B: "bsm_token_data_subject32_ex", 0x7C: "bsm_token_data_subject64_ex", 0x7D: "bsm_token_data_subject64_ex", 0x7E: "bsm_token_data_in_addr_ex", 0x7F: "bsm_token_data_socket_ex", 0x80: "bsm_token_data_sockinet32", 0x81: "bsm_token_data_sockinet64", 0x82: "bsm_token_data_sockunix", } _TOKEN_DATA_FORMAT_FUNCTIONS = { 0x11: "_FormatOtherFileToken", 0x21: "_FormatDataToken", 0x22: "_FormatIPCToken", 0x23: "_FormatPathToken", 0x24: "_FormatSubjectOrProcessToken", 0x26: "_FormatSubjectOrProcessToken", 0x27: "_FormatReturnOrExitToken", 0x28: "_FormatTextToken", 0x29: "_FormatOpaqueToken", 0x2A: "_FormatInAddrToken", 0x2B: "_FormatIPToken", 0x2C: "_FormatIPortToken", 0x2D: "_FormatArgToken", 0x2F: "_FormatSeqToken", 0x32: "_FormatIPCPermToken", 0x34: "_FormatGroupsToken", 0x3B: "_FormatGroupsToken", 0x3C: "_FormatExecArgsToken", 0x3D: "_FormatExecArgsToken", 0x3E: "_FormatAttrToken", 0x52: "_FormatReturnOrExitToken", 0x60: "_FormatZonenameToken", 0x71: "_FormatArgToken", 0x72: "_FormatReturnOrExitToken", 0x73: "_FormatAttrToken", 0x75: "_FormatSubjectOrProcessToken", 0x77: "_FormatSubjectOrProcessToken", 0x7A: "_FormatSubjectExOrProcessExToken", 0x7B: "_FormatSubjectExOrProcessExToken", 0x7C: "_FormatSubjectExOrProcessExToken", 0x7D: "_FormatSubjectExOrProcessExToken", 0x7E: "_FormatInAddrExToken", 0x7F: "_FormatSocketExToken", 0x80: "_FormatSocketInet32Token", 0x81: "_FormatSocketInet128Token", 0x82: "_FormatSocketUnixToken", } _DATA_TOKEN_FORMAT = { 0: "Binary", 1: "Octal", 2: "Decimal", 3: "Hexadecimal", 4: "String", } # BSM identification errors. _ERRORS = { 0: "Success", 1: "Operation not permitted", 2: "No such file or directory", 3: "No such process", 4: "Interrupted system call", 5: "Input/output error", 6: "Device not configured", 7: "Argument list too long", 8: "Exec format error", 9: "Bad file descriptor", 10: "No child processes", 11: "Resource temporarily unavailable", 12: "Cannot allocate memory", 13: "Permission denied", 14: "Bad address", 15: "Block device required", 16: "Device busy", 17: "File exists", 18: "ross-device link", 19: "Operation not supported by device", 20: "Not a directory", 21: "Is a directory", 22: "Invalid argument", 23: "Too many open files in system", 24: "Too many open files", 25: "Inappropriate ioctl for device", 26: "Text file busy", 27: "File too large", 28: "No space left on device", 29: "Illegal seek", 30: "Read-only file system", 31: "Too many links", 32: "Broken pipe", 33: "Numerical argument out of domain", 34: "Result too large", 35: "No message of desired type", 36: "Identifier removed", 45: "Resource deadlock avoided", 46: "No locks available", 47: "Operation canceled", 48: "Operation not supported", 49: "Disc quota exceeded", 66: "Too many levels of remote in path", 67: "Link has been severed", 71: "Protocol error", 74: "Multihop attempted", 77: "Bad message", 78: "File name too long", 79: "Value too large to be stored in data type", 88: "Illegal byte sequence", 89: "Function not implemented", 90: "Too many levels of symbolic links", 91: "Restart syscall", 93: "Directory not empty", 94: "Too many users", 95: "Socket operation on non-socket", 96: "Destination address required", 97: "Message too long", 98: "Protocol wrong type for socket", 99: "Protocol not available", 120: "Protocol not supported", 121: "Socket type not supported", 122: "Operation not supported", 123: "Protocol family not supported", 124: "Address family not supported by protocol family", 125: "Address already in use", 126: "Can't assign requested address", 127: "Network is down", 128: "Network unreachable", 129: "Network dropped connection on reset", 130: "Software caused connection abort", 131: "Connection reset by peer", 132: "No buffer space available", 133: "Socket is already connected", 134: "Socket is not connected", 143: "Can't send after socket shutdown", 144: "Too many references: can't splice", 145: "Operation timed out", 146: "Connection refused", 147: "Host is down", 148: "No route to host", 149: "Operation already in progress", 150: "Operation now in progress", 151: "Stale NFS file handle", 190: "PROCLIM", 191: "BADRPC", 192: "RPCMISMATCH", 193: "PROGUNAVAIL", 194: "PROGMISMATCH", 195: "PROCUNAVAIL", 196: "FTYPE", 197: "AUTH", 198: "NEEDAUTH", 199: "NOATTR", 200: "DOOFUS", 201: "USTRETURN", 202: "NOIOCTL", 203: "DIRIOCTL", 204: "PWROFF", 205: "DEVERR", 206: "BADEXEC", 207: "BADARCH", 208: "SHLIBVERS", 209: "BADMACHO", 210: "POLICY", } # BSM network protocolsb based on information from OpenBSD. _NETWORK_PROTOCOLS = { 0: "UNSPEC", 1: "LOCAL", 2: "INET", 3: "IMPLINK", 4: "PUP", 5: "CHAOS", 6: "NS", 8: "ECMA", 9: "DATAKIT", 10: "CCITT", 11: "SNA", 12: "DECnet", 13: "DLI", 14: "LAT", 15: "HYLINK", 16: "APPLETALK", 19: "OSI", 23: "IPX", 24: "ROUTE", 25: "LINK", 26: "INET6", 27: "KEY", 500: "NETBIOS", 501: "ISO", 502: "XTP", 503: "COIP", 504: "CNT", 505: "RTIP", 506: "SIP", 507: "PIP", 508: "ISDN", 509: "E164", 510: "NATM", 511: "ATM", 512: "NETGRAPH", 513: "SLOW", 514: "CLUSTER", 515: "ARP", 516: "BLUETOOTH", } def _FormatArgToken(self, token_data): """Formats an argument token as a dictionary of values. Args: token_data (bsm_token_data_arg32|bsm_token_data_arg64): AUT_ARG32 or AUT_ARG64 token data. Returns: dict[str, str]: token values. """ return { "string": token_data.argument_value.rstrip("\x00"), "num_arg": token_data.argument_index, "is": token_data.argument_name, } def _FormatAttrToken(self, token_data): """Formats an attribute token as a dictionary of values. Args: token_data (bsm_token_data_attr32|bsm_token_data_attr64): AUT_ATTR32 or AUT_ATTR64 token data. Returns: dict[str, str]: token values. """ return { "mode": token_data.file_mode, "uid": token_data.user_identifier, "gid": token_data.group_identifier, "system_id": token_data.file_system_identifier, "node_id": token_data.file_identifier, "device": token_data.device, } def _FormatDataToken(self, token_data): """Formats a data token as a dictionary of values. Args: token_data (bsm_token_data_data): AUT_DATA token data. Returns: dict[str, str]: token values. """ format_string = self._DATA_TOKEN_FORMAT.get(token_data.data_format, "UNKNOWN") if token_data.data_format == 4: data = bytes(bytearray(token_data.data)).split(b"\x00", maxsplit=1)[0] data = data.decode("utf-8") else: data = "".join([f"{byte:02x}" for byte in token_data.data]) return {"format": format_string, "data": data} def _FormatInAddrExToken(self, token_data): """Formats an extended IPv4 address token as a dictionary of values. Args: token_data (bsm_token_data_in_addr_ex): AUT_IN_ADDR_EX token data. Returns: dict[str, str]: token values. """ protocol = self._NETWORK_PROTOCOLS.get(token_data.net_type, "UNKNOWN") if token_data.net_type == 4: ip_address = self._FormatPackedIPv6Address(token_data.ip_address[:4]) elif token_data.net_type == 16: ip_address = self._FormatPackedIPv6Address(token_data.ip_address) else: ip_address = None return { "protocols": protocol, "net_type": token_data.net_type, "address": ip_address, } def _FormatInAddrToken(self, token_data): """Formats an IPv4 address token as a dictionary of values. Args: token_data (bsm_token_data_in_addr): AUT_IN_ADDR token data. Returns: dict[str, str]: token values. """ ip_address = self._FormatPackedIPv4Address(token_data.ip_address) return {"ip": ip_address} def _FormatIPCPermToken(self, token_data): """Formats an IPC permissions token as a dictionary of values. Args: token_data (bsm_token_data_ipc_perm): AUT_IPC_PERM token data. Returns: dict[str, str]: token values. """ return { "user_id": token_data.user_identifier, "group_id": token_data.group_identifier, "creator_user_id": token_data.creator_user_identifier, "creator_group_id": token_data.creator_group_identifier, "access": token_data.access_mode, } def _FormatIPCToken(self, token_data): """Formats an IPC token as a dictionary of values. Args: token_data (bsm_token_data_ipc): AUT_IPC token data. Returns: dict[str, str]: token values. """ return { "object_type": token_data.object_type, "object_id": token_data.object_identifier, } def _FormatGroupsToken(self, token_data): """Formats a groups token as a dictionary of values. Args: token_data (bsm_token_data_groups): AUT_GROUPS or AUT_NEWGROUPS token data. Returns: dict[str, str]: token values. """ return { "number_of_groups": token_data.number_of_groups, "groups": ", ".join(token_data.groups), } def _FormatExecArgsToken(self, token_data): """Formats an execution arguments token as a dictionary of values. Args: token_data (bsm_token_data_exec_args): AUT_EXEC_ARGS or AUT_EXEC_ENV token data. Returns: dict[str, str]: token values. """ return { "number_of_strings": token_data.number_of_strings, "strings": ", ".join(token_data.strings), } def _FormatIPortToken(self, token_data): """Formats an IP port token as a dictionary of values. Args: token_data (bsm_token_data_iport): AUT_IPORT token data. Returns: dict[str, str]: token values. """ return {"port_number": token_data.port_number} def _FormatIPToken(self, token_data): """Formats an IPv4 packet header token as a dictionary of values. Args: token_data (bsm_token_data_ip): AUT_IP token data. Returns: dict[str, str]: token values. """ data = "".join([f"{byte:02x}" for byte in token_data.data]) return {"IPv4_Header": data} def _FormatOpaqueToken(self, token_data): """Formats an opaque token as a dictionary of values. Args: token_data (bsm_token_data_opaque): AUT_OPAQUE token data. Returns: dict[str, str]: token values. """ data = "".join([f"{byte:02x}" for byte in token_data.data]) return {"data": data} def _FormatOtherFileToken(self, token_data): """Formats an other file token as a dictionary of values. Args: token_data (bsm_token_data_other_file32): AUT_OTHER_FILE32 token data. Returns: dict[str, str]: token values. """ # TODO: if this timestamp is useful, it must be extracted as a separate # event object. timestamp = token_data.microseconds + ( token_data.timestamp * definitions.MICROSECONDS_PER_SECOND ) date_time = dfdatetime_posix_time.PosixTimeInMicroseconds(timestamp=timestamp) date_time_string = date_time.CopyToDateTimeString() return {"string": token_data.name.rstrip("\x00"), "timestamp": date_time_string} def _FormatPathToken(self, token_data): """Formats a path token as a dictionary of values. Args: token_data (bsm_token_data_path): AUT_PATH token data. Returns: dict[str, str]: token values. """ return {"path": token_data.path.rstrip("\x00")} def _FormatReturnOrExitToken(self, token_data): """Formats a return or exit token as a dictionary of values. Args: token_data (bsm_token_data_exit|bsm_token_data_return32| bsm_token_data_return64): AUT_EXIT, AUT_RETURN32 or AUT_RETURN64 token data. Returns: dict[str, str]: token values. """ error_string = self._ERRORS.get(token_data.status, "UNKNOWN") return { "error": error_string, "token_status": token_data.status, "call_status": token_data.return_value, } def _FormatSeqToken(self, token_data): """Formats a sequence token as a dictionary of values. Args: token_data (bsm_token_data_seq): AUT_SEQ token data. Returns: dict[str, str]: token values. """ return {"sequence_number": token_data.sequence_number} def _FormatSocketExToken(self, token_data): """Formats an extended socket token as a dictionary of values. Args: token_data (bsm_token_data_socket_ex): AUT_SOCKET_EX token data. Returns: dict[str, str]: token values. """ if token_data.socket_domain == 10: local_ip_address = self._FormatPackedIPv6Address( token_data.local_ip_address ) remote_ip_address = self._FormatPackedIPv6Address( token_data.remote_ip_address ) else: local_ip_address = self._FormatPackedIPv4Address( token_data.local_ip_address ) remote_ip_address = self._FormatPackedIPv4Address( token_data.remote_ip_address ) return { "from": local_ip_address, "from_port": token_data.local_port, "to": remote_ip_address, "to_port": token_data.remote_port, } def _FormatSocketInet32Token(self, token_data): """Formats an Internet socket token as a dictionary of values. Args: token_data (bsm_token_data_sockinet32): AUT_SOCKINET32 token data. Returns: dict[str, str]: token values. """ protocol = self._NETWORK_PROTOCOLS.get(token_data.socket_family, "UNKNOWN") ip_address = self._FormatPackedIPv4Address(token_data.ip_addresss) return { "protocols": protocol, "family": token_data.socket_family, "port": token_data.port_number, "address": ip_address, } def _FormatSocketInet128Token(self, token_data): """Formats an Internet socket token as a dictionary of values. Args: token_data (bsm_token_data_sockinet64): AUT_SOCKINET128 token data. Returns: dict[str, str]: token values. """ protocol = self._NETWORK_PROTOCOLS.get(token_data.socket_family, "UNKNOWN") ip_address = self._FormatPackedIPv6Address(token_data.ip_addresss) return { "protocols": protocol, "family": token_data.socket_family, "port": token_data.port_number, "address": ip_address, } def _FormatSocketUnixToken(self, token_data): """Formats an Unix socket token as a dictionary of values. Args: token_data (bsm_token_data_sockunix): AUT_SOCKUNIX token data. Returns: dict[str, str]: token values. """ protocol = self._NETWORK_PROTOCOLS.get(token_data.socket_family, "UNKNOWN") return { "protocols": protocol, "family": token_data.socket_family, "path": token_data.socket_path, } def _FormatSubjectOrProcessToken(self, token_data): """Formats a subject or process token as a dictionary of values. Args: token_data (bsm_token_data_subject32|bsm_token_data_subject64): AUT_SUBJECT32, AUT_PROCESS32, AUT_SUBJECT64 or AUT_PROCESS64 token data. Returns: dict[str, str]: token values. """ ip_address = self._FormatPackedIPv4Address(token_data.ip_address) return { "aid": token_data.audit_user_identifier, "euid": token_data.effective_user_identifier, "egid": token_data.effective_group_identifier, "uid": token_data.real_user_identifier, "gid": token_data.real_group_identifier, "pid": token_data.process_identifier, "session_id": token_data.session_identifier, "terminal_port": token_data.terminal_port, "terminal_ip": ip_address, } def _FormatSubjectExOrProcessExToken(self, token_data): """Formats a subject or process token as a dictionary of values. Args: token_data (bsm_token_data_subject32_ex|bsm_token_data_subject64_ex): AUT_SUBJECT32_EX, AUT_PROCESS32_EX, AUT_SUBJECT64_EX or AUT_PROCESS64_EX token data. Returns: dict[str, str]: token values. """ if token_data.net_type == 4: ip_address = self._FormatPackedIPv4Address(token_data.ip_address) elif token_data.net_type == 16: ip_address = self._FormatPackedIPv6Address(token_data.ip_address) else: ip_address = "unknown" return { "aid": token_data.audit_user_identifier, "euid": token_data.effective_user_identifier, "egid": token_data.effective_group_identifier, "uid": token_data.real_user_identifier, "gid": token_data.real_group_identifier, "pid": token_data.process_identifier, "session_id": token_data.session_identifier, "terminal_port": token_data.terminal_port, "terminal_ip": ip_address, } def _FormatTextToken(self, token_data): """Formats a text token as a dictionary of values. Args: token_data (bsm_token_data_text): AUT_TEXT token data. Returns: dict[str, str]: token values. """ return {"text": token_data.text.rstrip("\x00")} def _FormatTokenData(self, token_type, token_data): """Formats the token data as a dictionary of values. Args: token_type (int): token type. token_data (object): token data. Returns: dict[str, str]: formatted token values or an empty dictionary if no formatted token values could be determined. """ token_data_format_function = self._TOKEN_DATA_FORMAT_FUNCTIONS.get(token_type) if token_data_format_function: token_data_format_function = getattr(self, token_data_format_function, None) if not token_data_format_function: return {} return token_data_format_function(token_data) def _FormatZonenameToken(self, token_data): """Formats a time zone name token as a dictionary of values. Args: token_data (bsm_token_data_zonename): AUT_ZONENAME token data. Returns: dict[str, str]: token values. """ return {"name": token_data.name.rstrip("\x00")} def _ParseRecord(self, parser_mediator, file_object): """Parses an event record. Args: parser_mediator (ParserMediator): mediates interactions between parsers and other components, such as storage and dfVFS. file_object (dfvfs.FileIO): file-like object. Raises: ParseError: if the event record cannot be read. """ header_record_offset = file_object.tell() # Check the header token type before reading the token data to prevent # variable size tokens to consume a large amount of memory. token_type = self._ParseTokenType(file_object, header_record_offset) if token_type not in self._HEADER_TOKEN_TYPES: raise errors.ParseError( f"Unsupported header token type: 0x{token_type:02x}" ) token_type, token_data = self._ParseToken(file_object, header_record_offset) if token_data.format_version != 11: raise errors.ParseError( f"Unsupported format version type: {token_data.format_version:d}" ) timestamp = token_data.microseconds + ( token_data.timestamp * definitions.MICROSECONDS_PER_SECOND ) event_type = token_data.event_type header_record_size = token_data.record_size record_end_offset = header_record_offset + header_record_size event_tokens = [] return_token_values = None file_offset = file_object.tell() while file_offset < record_end_offset: token_type, token_data = self._ParseToken(file_object, file_offset) if not token_data: raise errors.ParseError(f"Unsupported token type: 0x{token_type:02x}") file_offset = file_object.tell() if token_type == self._TOKEN_TYPE_AUT_TRAILER: break token_type_string = self._TOKEN_TYPES.get(token_type, "UNKNOWN") token_values = self._FormatTokenData(token_type, token_data) event_tokens.append({token_type_string: token_values}) if token_type in ( self._TOKEN_TYPE_AUT_RETURN32, self._TOKEN_TYPE_AUT_RETURN64, ): # Make sure return_token_values is a string. error = token_values["error"] token_status = token_values["token_status"] call_status = token_values["call_status"] return_token_values = ( f"{{'error': '{error:s}', " f"'token_status': {token_status:d}, " f"'call_status': {call_status:d}}}" ) if token_data.record_size != header_record_size: raise errors.ParseError( "Mismatch of event record size between header and trailer token." ) event_data = BSMEventData() event_data.event_type = event_type event_data.extra_tokens = event_tokens event_data.offset = header_record_offset event_data.record_length = header_record_size event_data.return_value = return_token_values event_data.written_time = dfdatetime_posix_time.PosixTimeInMicroseconds( timestamp=timestamp ) parser_mediator.ProduceEventData(event_data) def _ParseToken(self, file_object, file_offset): """Parses a token. Args: file_object (dfvfs.FileIO): file-like object. file_offset (int): offset of the token relative to the start of the file-like object. Returns: tuple: containing: int: token type object: token data or None if the token type is not supported. """ token_type = self._ParseTokenType(file_object, file_offset) token_data = None token_data_map_name = self._DATA_TYPE_MAP_PER_TOKEN_TYPE.get(token_type, None) if token_data_map_name: token_data_map = self._GetDataTypeMap(token_data_map_name) token_data, _ = self._ReadStructureFromFileObject( file_object, file_offset + 1, token_data_map ) return token_type, token_data def _ParseTokenType(self, file_object, file_offset): """Parses a token type. Args: file_object (dfvfs.FileIO): file-like object. file_offset (int): offset of the token relative to the start of the file-like object. Returns: int: token type """ token_type_map = self._GetDataTypeMap("uint8") token_type, _ = self._ReadStructureFromFileObject( file_object, file_offset, token_type_map ) return token_type
[docs] def ParseFileObject(self, parser_mediator, file_object): """Parses a BSM file-like object. Args: parser_mediator (ParserMediator): mediates interactions between parsers and other components, such as storage and dfVFS. file_object (dfvfs.FileIO): a file-like object. Raises: WrongParser: when the file cannot be parsed. """ file_offset = file_object.get_offset() file_size = file_object.get_size() while file_offset < file_size: try: self._ParseRecord(parser_mediator, file_object) except errors.ParseError as exception: if file_offset == 0: raise errors.WrongParser( f"Unable to parse first event record with error: {exception!s}" ) # TODO: skip to next event record. file_offset = file_object.get_offset()
manager.ParsersManager.RegisterParser(BSMParser)