def __init__(self): super(AudioHandler, self).__init__() self.header_base = construct.BitStruct( 'ASTRMBaseHeader', construct.BitField('fmt', 3), construct.Bit('channel'), construct.Flag('vibrate'), construct.Bit('packet_type'), construct.BitField('seq_id', 10), construct.BitField('payload_size', 16)) self.header_aud = construct.Struct('ASTRMAudioHeader', construct.ULInt32('timestamp')) self.header_msg = construct.Struct( 'ASTRMMsgHeader', # This is kind of a hack, (there are two timestamp fields, which one is used # depends on packet_type construct.ULInt32('timestamp_audio'), construct.ULInt32('timestamp'), construct.Array(2, construct.ULInt32('freq_0')), # -> mc_video construct.Array(2, construct.ULInt32('freq_1')), # -> mc_sync construct.ULInt8('vid_format'), construct.Padding(3)) self.header = construct.Struct( 'ASTRMHeader', construct.Embed(self.header_base), construct.Switch('format_hdr', lambda ctx: ctx.packet_type, { 0: construct.Embed(self.header_aud), 1: construct.Embed(self.header_msg), }, default=construct.Pass))
def MakeAssemblyRefOSRow(): return construct.Struct('AssemblyRefOSRow', construct.ULInt32('OSPlatformID'), construct.ULInt32('OSMajorVersion'), construct.ULInt32('OSMinorVersion'), MDTag.AssemblyRefRId.parse('AssemblyRef') )
class Wdigest_x64(Wdigest, Mimikatz_x64): """TODO: add description.""" WDIGEST_LIST_ENTRY = construct.Struct('WdigestListEntry', construct.ULInt64('previous'), construct.ULInt64('next'), construct.ULInt32('usage_count'), construct.ULInt32('align1'), construct.ULInt64('this_entry'), construct.ULInt64('luid'), construct.ULInt64('flag'), construct.ULInt16('user_len'), construct.ULInt16('user_max_len'), construct.ULInt32('align2'), construct.ULInt64('user_string_ptr'), construct.ULInt16('domain_len'), construct.ULInt16('domain_max_len'), construct.ULInt32('align3'), construct.ULInt64('domain_string_ptr'), construct.ULInt16('password_len'), construct.ULInt16('password_max_len'), construct.ULInt32('align4'), construct.ULInt64('password_encrypted_ptr')) def __init__(self, lsass_task, credentials_obj): Mimikatz_x64.__init__(self, lsass_task) Wdigest.__init__(self, credentials_obj)
def MakeManifestResourceRow(): return construct.Struct('ManifestResourceRow', construct.ULInt32('Offset'), construct.ULInt32('Flags'), MDTag.StringHeapRef.parse('Name'), MDTag.Implementation.parse('Implementation') )
def MakeExportedTypeRow(): return construct.Struct('ExportedTypeRow', construct.ULInt32('Flags'), construct.ULInt32('TypeDefId'), MDTag.StringHeapRef.parse('TypeName'), MDTag.StringHeapRef.parse('TypeNamespace'), MDTag.Implementation.parse('Implementation') )
def _decode(self, obj, context): el = [ int(construct.Byte('foo').parse(obj[0:1])), (int(construct.ULInt32('foo').parse(obj[1:5])) + (int(construct.ULInt16('foo').parse(obj[5:7])) << 32))] auth_sub_count = construct.Byte('foo').parse(obj[7:8]) for i in range(0, auth_sub_count): el.append(construct.ULInt32('foo').parse(obj[8+i*4:])) return 'S-' + '-'.join([str(x) for x in el])
def MakeAssemblyRow(): return construct.Struct('AssemblyRow', construct.ULInt32('HashAlgId'), construct.ULInt16('MajorVersion'), construct.ULInt16('MinorVersion'), construct.ULInt16('BuildNumber'), construct.ULInt16('RevisionNumber'), construct.ULInt32('Flags'), MDTag.BlobHeapRef.parse('PublicKey'), MDTag.StringHeapRef.parse('Name'), MDTag.StringHeapRef.parse('Culture') )
def CheckSignature(self, value_data): """Parses the signature. Args: value_data: a binary string containing the value data. Returns: The format type if successful or None otherwise. """ signature = construct.ULInt32('signature').parse(value_data) if signature == self._HEADER_SIGNATURE_XP: return self.FORMAT_TYPE_XP elif signature == self._HEADER_SIGNATURE_2003: # TODO: determine which format version is used (2003 or Vista). return self.FORMAT_TYPE_2003 elif signature == self._HEADER_SIGNATURE_7: return self.FORMAT_TYPE_7 elif signature == self._HEADER_SIGNATURE_8: if value_data[signature:signature + 4] in [ self._CACHED_ENTRY_SIGNATURE_8_0, self._CACHED_ENTRY_SIGNATURE_8_1 ]: return self.FORMAT_TYPE_8
class LsaDecryptor_Vista_x64(LsaDecryptor_x64): """Class for Vista x64.""" SIGNATURE = '\x83\x64\x24\x30\x00\x44\x8b\x4c\x24\x48\x48\x8b\x0d' PTR_IV_OFFSET = 63; PTR_AES_KEY_OFFSET = 25; PTR_DES_KEY_OFFSET = -69; BCRYPT_HANDLE_KEY = construct.Struct('KIWI_BCRYPT_HANDLE_KEY', construct.ULInt32('size'), construct.ULInt32('tag'), # Tag 'UUUR', 0x55555552. construct.ULInt64('ptr_void_algorithm'), construct.ULInt64('ptr_kiwi_bcrypt_key'), construct.ULInt64('ptr_unknown')) def __init__(self, lsass_task): LsaDecryptor_x64.__init__(self, lsass_task)
def _decode(self, obj, context): return '{:08x}-{:04x}-{:04x}-{:04x}-{:s}'.format( construct.ULInt32('foo').parse(obj[0:4]), construct.ULInt16('foo').parse(obj[4:6]), construct.ULInt16('foo').parse(obj[6:8]), construct.UBInt16('foo').parse(obj[8:10]), obj[10:16].encode('hex'))
def _CheckSignature(self, value_data): """Parses and validates the signature. Args: value_data (bytes): value data. Returns: int: format type or None if format could not be determined. """ signature = construct.ULInt32(u'signature').parse(value_data) if signature == self._HEADER_SIGNATURE_XP: return self._FORMAT_TYPE_XP elif signature == self._HEADER_SIGNATURE_2003: # TODO: determine which format version is used (2003 or Vista). return self._FORMAT_TYPE_2003 elif signature == self._HEADER_SIGNATURE_7: return self._FORMAT_TYPE_7 elif signature == self._HEADER_SIGNATURE_8: if value_data[signature:signature + 4] in (self._CACHED_ENTRY_SIGNATURE_8_0, self._CACHED_ENTRY_SIGNATURE_8_1): return self._FORMAT_TYPE_8 elif signature in self._HEADER_SIGNATURES_10: # Windows 10 uses the same cache entry signature as Windows 8.1 if value_data[signature:signature + 4] in [self._CACHED_ENTRY_SIGNATURE_8_1]: return self._FORMAT_TYPE_10
class LsaDecryptor_Win7_x64(LsaDecryptor_x64): """Class for Windows 7 x64.""" # MIMIKATZ x64: BYTE PTRN_WNO8_LsaInitializeProtectedMemory_KEY[] SIGNATURE = '\x83\x64\x24\x30\x00\x44\x8b\x4c\x24\x48\x48\x8b\x0d' PTR_IV_OFFSET = 59; PTR_AES_KEY_OFFSET = 25; PTR_DES_KEY_OFFSET = -61; BCRYPT_HANDLE_KEY = construct.Struct('KIWI_BCRYPT_HANDLE_KEY', construct.ULInt32('size'), construct.ULInt32('tag'), # Tag 'UUUR', 0x55555552. construct.ULInt64('ptr_void_algorithm'), construct.ULInt64('ptr_kiwi_bcrypt_key'), construct.ULInt64('ptr_unknown')) def __init__(self, lsass_task): LsaDecryptor_x64.__init__(self, lsass_task)
class LsaDecryptor_Win7_x86(LsaDecryptor_x86): """Class for Windows 7 x86.""" # MIMIKATZ x86: BYTE PTRN_WNO8_LsaInitializeProtectedMemory_KEY[] SIGNATURE = '\x8b\xf0\x3b\xf3\x7c\x2c\x6a\x02\x6a\x10\x68' PTR_IV_OFFSET = 11; PTR_AES_KEY_OFFSET = -15; PTR_DES_KEY_OFFSET = -70; BCRYPT_HANDLE_KEY = construct.Struct('KIWI_BCRYPT_HANDLE_KEY', construct.ULInt32('size'), construct.ULInt32('tag'), # Tag 'UUUR', 0x55555552. construct.ULInt32('ptr_void_algorithm'), construct.ULInt32('ptr_kiwi_bcrypt_key'), construct.ULInt32('ptr_unknown')) def __init__(self, lsass_task): LsaDecryptor_x86.__init__(self, lsass_task)
def decode_itempos(itempos): """ Decodes a single itempos and returns extracted information """ itempos_io = StringIO.StringIO(itempos) itempos_struct = construct.Struct("itempos", construct.ULInt16("itempos_size"), construct.Padding(2), construct.ULInt32("filesize"), construct.Bytes("dos_date", 2), construct.Bytes("dos_time", 2), construct.ULInt16("file_attr"), construct.CString("filename") ) parse_res = itempos_struct.parse_stream(itempos_io) if itempos_io.pos % 2 == 1: itempos_io.read(1) ext_struct = construct.Struct("ext", construct.ULInt16("ext_size"), construct.ULInt16("ext_version") ) parse_ext = ext_struct.parse_stream(itempos_io) if parse_ext["ext_version"] >= 0x3: itempos2_struct = construct.Struct("itempos2", construct.Padding(2), # 0004 construct.Padding(2), # BEEF construct.Bytes("creation_dos_date", 2), construct.Bytes("creation_dos_time", 2), construct.Bytes("access_dos_date", 2), construct.Bytes("access_dos_time", 2), construct.Padding(4) ) parse_res2 = itempos2_struct.parse_stream(itempos_io) unicode_filename = "" if parse_ext["ext_version"] >= 0x7: itempos3_struct = construct.Struct("itempos3", construct.ULInt64("file_ref"), construct.Padding(8), construct.Padding(2), construct.Padding(4) ) parse_res3 = itempos3_struct.parse_stream(itempos_io) unicode_filename = itempos_io.read().decode("utf16") if not unicode_filename.endswith("\0"): unicode_filename = unicode_filename[:-2] # ditch last unused 2 bytes and \0 char elif parse_ext["ext_version"] >= 0x3: unicode_filename = itempos_io.read().decode("utf16") if not unicode_filename.endswith("\0"): unicode_filename = unicode_filename[:-2] # ditch last unused 2 bytes and \0 char timestamp_modified = dosdate(parse_res["dos_date"], parse_res["dos_time"]).strftime("%d/%m/%Y %H:%M:%S") timestamp_created = dosdate(parse_res2["creation_dos_date"], parse_res2["creation_dos_time"]).strftime( "%d/%m/%Y %H:%M:%S") timestamp_access = dosdate(parse_res2["access_dos_date"], parse_res2["access_dos_time"]).strftime( "%d/%m/%Y %H:%M:%S") return [unicode(parse_res["itempos_size"]), unicode(parse_res["filesize"]), timestamp_modified, parse_res["filename"], timestamp_created, timestamp_access, unicode_filename]
def MakeTypeDefRow(): return construct.Struct('TypeDefRow', construct.ULInt32('Flags'), MDTag.StringHeapRef.parse('Name'), MDTag.StringHeapRef.parse('Namespace'), MDTag.TypeDefOrRef.parse('Extends'), MDTag.FieldRef.parse('FieldList'), MDTag.MethodRef.parse('MethodList') )
class WinVerPlugin(interface.KeyPlugin): """Plug-in to collect information about the Windows version.""" NAME = 'winreg_winver' REG_KEYS = [u'\\Microsoft\\Windows NT\\CurrentVersion'] REG_TYPE = 'SOFTWARE' URLS = [] INT_STRUCT = construct.ULInt32('install') # TODO: Refactor remove this function in a later CL. def GetValueString(self, key, value_name): """Retrieves a specific string value from the Registry key. Args: key: A Windows Registry key (instance of WinRegKey). value_name: The name of the value. Returns: A string value if one is available, otherwise an empty string. """ value = key.GetValue(value_name) if not value: return '' if not value.data or not value.DataIsString(): return '' return value.data def GetEntries(self, key, **unused_kwargs): """Gather minimal information about system install and return an event.""" text_dict = {} text_dict[u'Owner'] = self.GetValueString(key, 'RegisteredOwner') text_dict[u'sp'] = self.GetValueString(key, 'CSDBuildNumber') text_dict[u'Product name'] = self.GetValueString(key, 'ProductName') text_dict[u' Windows Version Information'] = u'' install_raw = key.GetValue('InstallDate').raw_data # TODO: move this to a function in utils with a more descriptive name # e.g. CopyByteStreamToInt32BigEndian. try: install = self.INT_STRUCT.parse(install_raw) except construct.FieldError: install = 0 event_object = event.WinRegistryEvent( key.path, text_dict, timestamp=timelib.Timestamp.FromPosixTime(install)) event_object.prodname = text_dict[u'Product name'] event_object.source_long = 'SOFTWARE WinVersion key' if text_dict[u'Owner']: event_object.owner = text_dict[u'Owner'] yield event_object
def _ReadIndexTable(self): """Reads the index table.""" cache_address_data = self._file_object.read(4) while len(cache_address_data) == 4: value = construct.ULInt32(u'cache_address').parse(cache_address_data) if value: cache_address = CacheAddress(value) self.index_table.append(cache_address) cache_address_data = self._file_object.read(4)
def __init__(s): super(ServiceASTRM, s).__init__() s.header_base = construct.BitStruct('ASTRMBaseHeader', construct.BitField('fmt', 3), construct.Bit('channel'), construct.Flag('vibrate'), construct.Bit('packet_type'), construct.BitField('seq_id', 10), construct.BitField('payload_size', 16) ) s.header_aud = construct.Struct('ASTRMAudioHeader', construct.ULInt32('timestamp'), # construct.Array(lambda ctx: ctx.payload_size, construct.UBInt8("data")) ) s.header_msg = construct.Struct('ASTRMMsgHeader', # This is kind of a hack, (there are two timestamp fields, which one is used depends on packet_type construct.ULInt32('timestamp_audio'), construct.ULInt32('timestamp'), construct.Array(2, construct.ULInt32('freq_0')), # -> mc_video construct.Array(2, construct.ULInt32('freq_1')), # -> mc_sync construct.ULInt8('vid_format'), construct.Padding(3) ) s.header = construct.Struct('ASTRMHeader', construct.Embed(s.header_base), construct.Switch('format_hdr', lambda ctx: ctx.packet_type, { 0 : construct.Embed(s.header_aud), 1 : construct.Embed(s.header_msg), }, default = construct.Pass ) ) s.is_streaming = False s.p = pyaudio.PyAudio() s.stream = None s.pa_num_bufs = 15 s.pa_ring = [array.array('H', '\0' * 416 * 2)] * s.pa_num_bufs s.pa_wpos = s.pa_rpos = 0
def get_key(self, pos, key_offset): ptr_key = self.get_ptr_with_offset(pos + key_offset) if ptr_key: ptr_key = self.get_ptr(ptr_key) if ptr_key: size = self.BCRYPT_HANDLE_KEY.sizeof() data = self.get_data(ptr_key, size) if data: kbhk = self.BCRYPT_HANDLE_KEY.parse(data) if kbhk.tag == self.UUUR_TAG: ptr_key = kbhk.ptr_kiwi_bcrypt_key size = self.BCRYPT_KEY.sizeof() data = self.get_data(ptr_key, size) if data: kbk = self.BCRYPT_KEY.parse(data) if kbk.tag == self.MSSK_TAG: adjust = construct.ULInt32('').sizeof() size = kbk.cbSecret + adjust ptr_key = ptr_key + self.BCRYPT_KEY.sizeof( ) - adjust data = self.get_data(ptr_key, size) if data: khk = self.HARD_KEY.parse(data) return khk.data else: debug.warning( 'get_key() unable to get HARD_KEY.') else: debug.warning( 'get_key() BCRYPT_KEY invalid tag') else: debug.warning( 'get_key() unable to read BCRYPT_KEY data.') else: debug.warning( 'get_key() BCRYPT_HANDLE_KEY invalid tag') debug.warning(kbhk) else: debug.warning( 'get_key() unable to read BCRYPT_HANDLE_KEY data.') else: debug.warning( 'get_key() unable to get BCRYPT_HANDLE_KEY pointer.') else: debug.warning('get_key()unable to get first pointer.')
def _ReadUtmpEvent(self, file_object): """Returns an UtmpEvent from a single UTMP entry. Args: file_object: a file-like object that points to an UTMP file. Returns: An event object constructed from a single UTMP record or None if we have reached the end of the file (or EOF). """ offset = file_object.tell() data = file_object.read(self.LINUX_UTMP_ENTRY_SIZE) if not data or len(data) != self.LINUX_UTMP_ENTRY_SIZE: return try: entry = self.LINUX_UTMP_ENTRY.parse(data) except (IOError, construct.FieldError): logging.warning( (u'UTMP entry at 0x{:x} couldn\'t be parsed.').format(offset)) return self._ReadUtmpEvent(file_object) user = self._GetTextFromNullTerminatedString(entry.username) terminal = self._GetTextFromNullTerminatedString(entry.terminal) if terminal == '~': terminal = u'system boot' computer_name = self._GetTextFromNullTerminatedString(entry.hostname) if computer_name == u'N/A' or computer_name == u':0': computer_name = u'localhost' status = self.STATUS_TYPE.get(entry.type, u'N/A') if not entry.address_b: try: ip_address = socket.inet_ntoa( construct.ULInt32('int').build(entry.address_a)) if ip_address == '0.0.0.0': ip_address = u'localhost' except (IOError, construct.FieldError, socket.error): ip_address = u'N/A' else: ip_address = u'{0:d}.{1:d}.{2:d}.{3:d}'.format( entry.address_a, entry.address_b, entry.address_c, entry.address_d) return UtmpEvent(entry.timestamp, entry.microsecond, user, computer_name, terminal, status, ip_address, entry)
def __init__(s): s.header_cmd0 = construct.Struct('CMD0Header', construct.UBInt8('magic'), construct.UBInt8('unk_0'), construct.UBInt8('unk_1'), construct.UBInt8('unk_2'), construct.UBInt8('unk_3'), construct.UBInt8('flags'), construct.UBInt8('id_primary'), construct.UBInt8('id_secondary'), construct.UBInt16('error_code'), construct.UBInt16('payload_size_cmd0') ) s.header_cmd1 = construct.Struct('CMD1Header', construct.Padding(48) ) s.header_cmd2 = construct.Struct('CMD2Header', construct.ULInt16('JDN_base'), construct.Padding(2), construct.ULInt32('seconds') ) s.header = construct.Struct('CMDHeader', construct.ULInt16('packet_type'), construct.ULInt16('cmd_id'), construct.ULInt16('payload_size'), construct.ULInt16('seq_id'), construct.Switch('cmd_hdr', lambda ctx: ctx.cmd_id, { 0 : construct.If(lambda ctx: ctx.payload_size >= s.header_cmd0.sizeof(), construct.Embed(s.header_cmd0)), 1 : construct.If(lambda ctx: ctx.payload_size == s.header_cmd1.sizeof(), construct.Embed(s.header_cmd1)), 2 : construct.If(lambda ctx: ctx.payload_size == s.header_cmd2.sizeof(), construct.Embed(s.header_cmd2)) }, default = construct.Pass ) ) s.cmd_handlers = { 0 : s.cmd0, 1 : s.cmd1, 2 : s.cmd2 } s.cmd0_handlers = { 5 : { 6 : s.cmd0_5_6 }, }
def _ReadIndexTable(self): """Reads the index table.""" cache_address_index = 0 cache_address_data = self._file_object.read(4) while len(cache_address_data) == 4: value = construct.ULInt32(u'cache_address').parse(cache_address_data) if value: cache_address = CacheAddress(value) if self._debug: print(u'Cache address: {0:d}\t\t\t\t\t\t\t: {1:s}'.format( cache_address_index, cache_address.GetDebugString())) self.index_table[cache_address_index] = cache_address cache_address_index += 1 cache_address_data = self._file_object.read(4) if self._debug: print(u'')
def _ParseItem(self, file_object, offset): """Parses a Systemd journal DATA object. This method will read, and decompress if needed, the content of a DATA object. Args: file_object (dfvfs.FileIO): a file-like object. offset (int): offset to the DATA object. Returns: tuple[str, str]: key and value of this item. Raises: ParseError: When an unexpected object type is parsed. """ object_header, payload_size = self._ParseObjectHeader( file_object, offset) file_object.read(self._DATA_OBJECT_SIZE) if object_header.type != 'DATA': raise errors.ParseError( 'Expected an object of type DATA, but got {0:s}'.format( object_header.type)) event_data = file_object.read(payload_size - self._DATA_OBJECT_SIZE) if object_header.flags & self._OBJECT_COMPRESSED_FLAG_XZ: event_data = lzma.decompress(event_data) event_string = event_data.decode('utf-8') elif object_header.flags & self._OBJECT_COMPRESSED_FLAG_LZ4: decompressed_size = construct.ULInt32('size').parse( event_data[0:7]) event_data = lz4.block.decompress( event_data[8:], uncompressed_size=decompressed_size) event_string = event_data.decode('utf-8') event_key, event_value = event_string.split('=', 1) return (event_key, event_value)
class UtmpParser(interface.SingleFileBaseParser): """Parser for Linux/Unix UTMP files.""" _INITIAL_FILE_OFFSET = None NAME = 'utmp' DESCRIPTION = u'Parser for Linux/Unix UTMP files.' LINUX_UTMP_ENTRY = construct.Struct('utmp_linux', construct.ULInt32('type'), construct.ULInt32('pid'), construct.String('terminal', 32), construct.ULInt32('terminal_id'), construct.String('username', 32), construct.String('hostname', 256), construct.ULInt16('termination'), construct.ULInt16('exit'), construct.ULInt32('session'), construct.ULInt32('timestamp'), construct.ULInt32('microsecond'), construct.ULInt32('address_a'), construct.ULInt32('address_b'), construct.ULInt32('address_c'), construct.ULInt32('address_d'), construct.Padding(20)) LINUX_UTMP_ENTRY_SIZE = LINUX_UTMP_ENTRY.sizeof() STATUS_TYPE = { 0: 'EMPTY', 1: 'RUN_LVL', 2: 'BOOT_TIME', 3: 'NEW_TIME', 4: 'OLD_TIME', 5: 'INIT_PROCESS', 6: 'LOGIN_PROCESS', 7: 'USER_PROCESS', 8: 'DEAD_PROCESS', 9: 'ACCOUNTING' } # Set a default test value for few fields, this is supposed to be a text # that is highly unlikely to be seen in a terminal field, or a username field. # It is important that this value does show up in such fields, but otherwise # it can be a free flowing text field. _DEFAULT_TEST_VALUE = u'Ekki Fraedilegur Moguleiki, thetta er bull ! = + _<>' def ParseFileObject(self, parser_mediator, file_object, **kwargs): """Parses an UTMP file-like object. Args: parser_mediator: A parser mediator object (instance of ParserMediator). file_object: The file-like object to extract data from. Raises: UnableToParseFile: when the file cannot be parsed. """ file_object.seek(0, os.SEEK_SET) try: structure = self.LINUX_UTMP_ENTRY.parse_stream(file_object) except (IOError, construct.FieldError) as exception: raise errors.UnableToParseFile( u'Unable to parse UTMP Header with error: {0:s}'.format( exception)) if structure.type not in self.STATUS_TYPE: raise errors.UnableToParseFile( (u'Not an UTMP file, unknown type ' u'[{0:d}].').format(structure.type)) if not self._VerifyTextField(structure.terminal): raise errors.UnableToParseFile( u'Not an UTMP file, unknown terminal.') if not self._VerifyTextField(structure.username): raise errors.UnableToParseFile( u'Not an UTMP file, unknown username.') if not self._VerifyTextField(structure.hostname): raise errors.UnableToParseFile( u'Not an UTMP file, unknown hostname.') # Check few values. terminal = self._GetTextFromNullTerminatedString( structure.terminal, self._DEFAULT_TEST_VALUE) if terminal == self._DEFAULT_TEST_VALUE: raise errors.UnableToParseFile( u'Not an UTMP file, no terminal set.') username = self._GetTextFromNullTerminatedString( structure.username, self._DEFAULT_TEST_VALUE) if username == self._DEFAULT_TEST_VALUE: raise errors.UnableToParseFile( u'Not an UTMP file, no username set.') if not structure.timestamp: raise errors.UnableToParseFile( u'Not an UTMP file, no timestamp set in the first record.') file_object.seek(0, os.SEEK_SET) event_object = self._ReadUtmpEvent(file_object) while event_object: event_object.offset = file_object.tell() parser_mediator.ProduceEvent(event_object) event_object = self._ReadUtmpEvent(file_object) def _VerifyTextField(self, text): """Check if a byte stream is a null terminated string. Args: event_object: text field from the structure. Return: True if it is a null terminated string, False otherwise. """ _, _, null_chars = text.partition(b'\x00') if not null_chars: return False return len(null_chars) == null_chars.count(b'\x00') def _ReadUtmpEvent(self, file_object): """Returns an UtmpEvent from a single UTMP entry. Args: file_object: a file-like object that points to an UTMP file. Returns: An event object constructed from a single UTMP record or None if we have reached the end of the file (or EOF). """ offset = file_object.tell() data = file_object.read(self.LINUX_UTMP_ENTRY_SIZE) if not data or len(data) != self.LINUX_UTMP_ENTRY_SIZE: return try: entry = self.LINUX_UTMP_ENTRY.parse(data) except (IOError, construct.FieldError): logging.warning( (u'UTMP entry at 0x{:x} couldn\'t be parsed.').format(offset)) return self._ReadUtmpEvent(file_object) user = self._GetTextFromNullTerminatedString(entry.username) terminal = self._GetTextFromNullTerminatedString(entry.terminal) if terminal == '~': terminal = u'system boot' computer_name = self._GetTextFromNullTerminatedString(entry.hostname) if computer_name == u'N/A' or computer_name == u':0': computer_name = u'localhost' status = self.STATUS_TYPE.get(entry.type, u'N/A') if not entry.address_b: try: ip_address = socket.inet_ntoa( construct.ULInt32('int').build(entry.address_a)) if ip_address == '0.0.0.0': ip_address = u'localhost' except (IOError, construct.FieldError, socket.error): ip_address = u'N/A' else: ip_address = u'{0:d}.{1:d}.{2:d}.{3:d}'.format( entry.address_a, entry.address_b, entry.address_c, entry.address_d) return UtmpEvent(entry.timestamp, entry.microsecond, user, computer_name, terminal, status, ip_address, entry) def _GetTextFromNullTerminatedString(self, null_terminated_string, default_string=u'N/A'): """Get a UTF-8 text from a raw null terminated string. Args: null_terminated_string: Raw string terminated with null character. default_string: The default string returned if the parser fails. Returns: A decoded UTF-8 string or if unable to decode, the supplied default string. """ text, _, _ = null_terminated_string.partition('\x00') try: text = text.decode('utf-8') except UnicodeDecodeError: logging.warning( u'[UTMP] Decode UTF8 failed, the message string may be cut short.' ) text = text.decode('utf-8', 'ignore') if not text: return default_string return text
class BagMRUPlugin(interface.KeyPlugin): """Class that defines a BagMRU Windows Registry plugin.""" NAME = 'winreg_bagmru' DESCRIPTION = u'Parser for BagMRU Registry data.' # TODO: remove REG_TYPE and use HKEY_CURRENT_USER instead. REG_TYPE = 'any' REG_KEYS = frozenset([ u'\\Software\\Microsoft\\Windows\\Shell\\BagMRU', u'\\Software\\Microsoft\\Windows\\ShellNoRoam\\BagMRU', (u'\\Local Settings\\Software\\Microsoft\\Windows\\' u'Shell\\BagMRU'), (u'\\Local Settings\\Software\\Microsoft\\Windows\\' u'ShellNoRoam\\BagMRU'), (u'\\Wow6432Node\\Local Settings\\Software\\' u'Microsoft\\Windows\\Shell\\BagMRU'), (u'\\Wow6432Node\\Local Settings\\Software\\' u'Microsoft\\Windows\\ShellNoRoam\\BagMRU') ]) URLS = [u'https://code.google.com/p/winreg-kb/wiki/MRUKeys'] _MRULISTEX_ENTRY = construct.ULInt32(u'entry_number') def _ParseMRUListExEntryValue(self, parser_mediator, key, entry_index, entry_number, text_dict, value_strings, parent_path_segments, codepage='cp1252', **unused_kwargs): """Parses the MRUListEx entry value. Args: parser_mediator: A parser mediator object (instance of ParserMediator). key: the Registry key (instance of winreg.WinRegKey) that contains the MRUListEx value. entry_index: integer value representing the MRUListEx entry index. entry_number: integer value representing the entry number. text_dict: text dictionary object to append textual strings. value_strings: value string dictionary object to append value strings. parent_path_segments: list containing the parent shell item path segments. codepage: Optional extended ASCII string codepage. The default is cp1252. Returns: The path segment of the shell item. """ value = key.GetValue(u'{0:d}'.format(entry_number)) path_segment = u'N/A' value_string = u'' if value is None: parser_mediator.ProduceParseError( u'Missing MRUListEx entry value: {0:d} in key: {1:s}.'.format( entry_number, key.path)) elif not value.DataIsBinaryData(): parser_mediator.ProduceParseError( u'Non-binary MRUListEx entry value: {0:d} in key: {1:s}.'. format(entry_number, key.path)) elif value.data: shell_items_parser = shell_items.ShellItemsParser(key.path) shell_items_parser.UpdateChainAndParse(parser_mediator, value.data, parent_path_segments, codepage=codepage) path_segment = shell_items_parser.GetUpperPathSegment() value_string = shell_items_parser.CopyToPath() value_strings[entry_number] = value_string value_string = u'Shell item path: {0:s}'.format(value_string) value_text = u'Index: {0:d} [MRU Value {1:d}]'.format( entry_index + 1, entry_number) text_dict[value_text] = value_string return path_segment def _ParseMRUListExValue(self, parser_mediator, key): """Parses the MRUListEx value in a given Registry key. Args: parser_mediator: A parser mediator object (instance of ParserMediator). key: the Registry key (instance of winreg.WinRegKey) that contains the MRUListEx value. Yields: A tuple of the MRUListEx index and entry number, where 0 is the first index value. """ mru_list_value = key.GetValue(u'MRUListEx') if mru_list_value: mrulistex_data = mru_list_value.data data_size = len(mrulistex_data) _, remainder = divmod(data_size, 4) if remainder != 0: parser_mediator.ProduceParseError( (u'MRUListEx value data size is not a multitude of 4 ' u'in MRU key: {0:s}').format(key.path)) data_size -= remainder entry_index = 0 data_offset = 0 while data_offset < data_size: try: entry_number = self._MRULISTEX_ENTRY.parse( mrulistex_data[data_offset:]) yield entry_index, entry_number except construct.FieldError: parser_mediator.ProduceParseError(( u'Unable to parse MRUListEx value data at offset: {0:d} ' u'in MRU key: {1:s}').format(data_offset, key.path)) entry_index += 1 data_offset += 4 def _ParseSubKey(self, parser_mediator, key, parent_path_segments, registry_type=None, codepage='cp1252'): """Extract event objects from a MRUListEx Registry key. Args: parser_mediator: A parser mediator object (instance of ParserMediator). key: the Registry key (instance of winreg.WinRegKey). parent_path_segments: list containing the parent shell item path segments. registry_type: Optional Registry type string. The default is None. codepage: Optional extended ASCII string codepage. The default is cp1252. """ entry_numbers = {} text_dict = {} value_strings = {} found_terminator = False for index, entry_number in self._ParseMRUListExValue( parser_mediator, key): if entry_number == 0xffffffff: found_terminator = True continue if found_terminator: parser_mediator.ProduceParseError( (u'Found additional MRUListEx entries after terminator ' u'in key: {0:s}.').format(key.path)) # Only create one parser error per terminator. found_terminator = False path_segment = self._ParseMRUListExEntryValue(parser_mediator, key, index, entry_number, text_dict, value_strings, parent_path_segments, codepage=codepage) entry_numbers[entry_number] = path_segment event_object = windows_events.WindowsRegistryEvent( key.last_written_timestamp, key.path, text_dict, offset=key.offset, registry_type=registry_type, urls=self.URLS, source_append=u': BagMRU') parser_mediator.ProduceEvent(event_object) for entry_number, path_segment in entry_numbers.iteritems(): sub_key = key.GetSubkey(u'{0:d}'.format(entry_number)) if not sub_key: parser_mediator.ProduceParseError( u'Missing BagMRU sub key: {0:d} in key: {1:s}.'.format( entry_number, key.path)) continue parent_path_segments.append(path_segment) self._ParseSubKey(parser_mediator, sub_key, parent_path_segments, codepage=codepage) _ = parent_path_segments.pop() def GetEntries(self, parser_mediator, key=None, registry_type=None, codepage='cp1252', **unused_kwargs): """Extract event objects from a Registry key containing a MRUListEx value. Args: parser_mediator: A parser mediator object (instance of ParserMediator). key: Optional Registry key (instance of winreg.WinRegKey). The default is None. registry_type: Optional Registry type string. The default is None. codepage: Optional extended ASCII string codepage. The default is cp1252. """ self._ParseSubKey(parser_mediator, key, [], registry_type=registry_type, codepage=codepage)
def DetermineCacheEntrySize(self, format_type, value_data, cached_entry_offset): """Parses a cached entry. Args: format_type: integer value that contains the format type. value_data: a binary string containing the value data. cached_entry_offset: integer value that contains the offset of the first cached entry data relative to the start of the value data. Returns: The cached entry size if successful or None otherwise. Raises: RuntimeError: if the format type is not supported. """ if format_type not in [ self.FORMAT_TYPE_XP, self.FORMAT_TYPE_2003, self.FORMAT_TYPE_VISTA, self.FORMAT_TYPE_7, self.FORMAT_TYPE_8 ]: raise RuntimeError( u'Unsupported format type: {0:d}'.format(format_type)) cached_entry_data = value_data[cached_entry_offset:] cached_entry_size = 0 if format_type == self.FORMAT_TYPE_XP: cached_entry_size = self._CACHED_ENTRY_XP_32BIT_STRUCT.sizeof() elif format_type in [ self.FORMAT_TYPE_2003, self.FORMAT_TYPE_VISTA, self.FORMAT_TYPE_7 ]: path_size = construct.ULInt16('path_size').parse( cached_entry_data[0:2]) maximum_path_size = construct.ULInt16('maximum_path_size').parse( cached_entry_data[2:4]) path_offset_32bit = construct.ULInt32('path_offset').parse( cached_entry_data[4:8]) path_offset_64bit = construct.ULInt32('path_offset').parse( cached_entry_data[8:16]) if maximum_path_size < path_size: logging.error(u'Path size value out of bounds.') return path_end_of_string_size = maximum_path_size - path_size if path_size == 0 or path_end_of_string_size != 2: logging.error(u'Unsupported path size values.') return # Assume the entry is 64-bit if the 32-bit path offset is 0 and # the 64-bit path offset is set. if path_offset_32bit == 0 and path_offset_64bit != 0: if format_type == self.FORMAT_TYPE_2003: cached_entry_size = self._CACHED_ENTRY_2003_64BIT_STRUCT.sizeof( ) elif format_type == self.FORMAT_TYPE_VISTA: cached_entry_size = self._CACHED_ENTRY_VISTA_64BIT_STRUCT.sizeof( ) elif format_type == self.FORMAT_TYPE_7: cached_entry_size = self._CACHED_ENTRY_7_64BIT_STRUCT.sizeof( ) else: if format_type == self.FORMAT_TYPE_2003: cached_entry_size = self._CACHED_ENTRY_2003_32BIT_STRUCT.sizeof( ) elif format_type == self.FORMAT_TYPE_VISTA: cached_entry_size = self._CACHED_ENTRY_VISTA_32BIT_STRUCT.sizeof( ) elif format_type == self.FORMAT_TYPE_7: cached_entry_size = self._CACHED_ENTRY_7_32BIT_STRUCT.sizeof( ) elif format_type == self.FORMAT_TYPE_8: cached_entry_size = self._CACHED_ENTRY_HEADER_8_STRUCT.sizeof() return cached_entry_size
class CustomDestinationsParser(interface.FileObjectParser): """Parses .customDestinations-ms files.""" _INITIAL_FILE_OFFSET = None NAME = u'custom_destinations' DESCRIPTION = u'Parser for *.customDestinations-ms files.' # We cannot use the parser registry here since winlnk could be disabled. # TODO: see if there is a more elegant solution for this. _WINLNK_PARSER = winlnk.WinLnkParser() _LNK_GUID = ( b'\x01\x14\x02\x00\x00\x00\x00\x00\xc0\x00\x00\x00\x00\x00\x00\x46') _FOOTER_SIGNATURE = 0xbabffbab _FILE_HEADER = construct.Struct(u'file_header', construct.ULInt32(u'unknown1'), construct.ULInt32(u'unknown2'), construct.ULInt32(u'unknown3'), construct.ULInt32(u'header_values_type')) _HEADER_VALUE_TYPE_0 = construct.Struct( u'header_value_type_0', construct.ULInt32(u'number_of_characters'), construct.String(u'string', lambda ctx: ctx.number_of_characters * 2), construct.ULInt32(u'unknown1')) _HEADER_VALUE_TYPE_1_OR_2 = construct.Struct( u'header_value_type_1_or_2', construct.ULInt32(u'unknown1')) _ENTRY_HEADER = construct.Struct(u'entry_header', construct.String(u'guid', 16)) _FILE_FOOTER = construct.Struct(u'file_footer', construct.ULInt32(u'signature')) def _ParseLNKFile(self, parser_mediator, file_entry, file_offset, remaining_file_size): """Parses a LNK file stored within the .customDestinations-ms file. Args: parser_mediator: A parser mediator object (instance of ParserMediator). file_entry: A file entry object (instance of dfvfs.FileEntry). file_offset: The offset of the LNK file data. remaining_file_size: The size of the data remaining in the .customDestinations-ms file. Returns: The size of the LNK file data or 0 if the LNK file could not be read. """ path_spec = path_spec_factory.Factory.NewPathSpec( definitions.TYPE_INDICATOR_DATA_RANGE, range_offset=file_offset, range_size=remaining_file_size, parent=file_entry.path_spec) display_name = u'{0:s} # 0x{1:08x}'.format(file_entry.name, file_offset) try: lnk_file_object = resolver.Resolver.OpenFileObject(path_spec) except (dfvfs_errors.BackEndError, RuntimeError) as exception: message = ( u'Unable to open LNK file: {0:s} with error {1:s}').format( display_name, exception) parser_mediator.ProduceParseError(message) return 0 self._WINLNK_PARSER.Parse(parser_mediator, lnk_file_object, display_name=display_name) # We cannot trust the file size in the LNK data so we get the last offset # that was read instead. lnk_file_size = lnk_file_object.get_offset() lnk_file_object.close() return lnk_file_size def ParseFileObject(self, parser_mediator, file_object, **kwargs): """Parses a .customDestinations-ms file-like object. Args: parser_mediator: A parser mediator object (instance of ParserMediator). file_object: A file-like object. Raises: UnableToParseFile: when the file cannot be parsed. """ file_entry = parser_mediator.GetFileEntry() display_name = parser_mediator.GetDisplayName() file_object.seek(0, os.SEEK_SET) try: file_header = self._FILE_HEADER.parse_stream(file_object) except (IOError, construct.FieldError) as exception: raise errors.UnableToParseFile( (u'Invalid Custom Destination: {0:s} - unable to parse ' u'file header with error: {1:s}').format( display_name, exception)) if file_header.unknown1 != 2: raise errors.UnableToParseFile(( u'Unsupported Custom Destination file: {0:s} - invalid unknown1: ' u'{1:d}.').format(display_name, file_header.unknown1)) if file_header.header_values_type > 2: raise errors.UnableToParseFile(( u'Unsupported Custom Destination file: {0:s} - invalid header value ' u'type: {1:d}.').format(display_name, file_header.header_values_type)) if file_header.header_values_type == 0: data_structure = self._HEADER_VALUE_TYPE_0 else: data_structure = self._HEADER_VALUE_TYPE_1_OR_2 try: _ = data_structure.parse_stream(file_object) except (IOError, construct.FieldError) as exception: raise errors.UnableToParseFile( (u'Invalid Custom Destination file: {0:s} - unable to parse ' u'header value with error: {1:s}').format( display_name, exception)) file_size = file_object.get_size() file_offset = file_object.get_offset() remaining_file_size = file_size - file_offset # The Custom Destination file does not have a unique signature in # the file header that is why we use the first LNK class identifier (GUID) # as a signature. first_guid_checked = False while remaining_file_size > 4: try: entry_header = self._ENTRY_HEADER.parse_stream(file_object) except (IOError, construct.FieldError) as exception: error_message = ( u'Invalid Custom Destination file: {0:s} - unable to parse ' u'entry header with error: {1:s}').format( display_name, exception) if not first_guid_checked: raise errors.UnableToParseFile(error_message) logging.warning(error_message) break if entry_header.guid != self._LNK_GUID: error_message = ( u'Unsupported Custom Destination file: {0:s} - invalid entry ' u'header.').format(display_name) if not first_guid_checked: raise errors.UnableToParseFile(error_message) file_object.seek(-16, os.SEEK_CUR) try: file_footer = self._FILE_FOOTER.parse_stream(file_object) except (IOError, construct.FieldError) as exception: raise IOError( (u'Unable to parse file footer at offset: 0x{0:08x} ' u'with error: {1:s}').format(file_offset, exception)) if file_footer.signature != self._FOOTER_SIGNATURE: logging.warning(error_message) file_object.seek(-4, os.SEEK_CUR) # TODO: add support for Jump List LNK file recovery. break first_guid_checked = True file_offset += 16 remaining_file_size -= 16 lnk_file_size = self._ParseLNKFile(parser_mediator, file_entry, file_offset, remaining_file_size) file_offset += lnk_file_size remaining_file_size -= lnk_file_size file_object.seek(file_offset, os.SEEK_SET) try: file_footer = self._FILE_FOOTER.parse_stream(file_object) except (IOError, construct.FieldError) as exception: logging.warning( (u'Invalid Custom Destination file: {0:s} - unable to parse ' u'footer with error: {1:s}').format(display_name, exception)) if file_footer.signature != self._FOOTER_SIGNATURE: logging.warning(( u'Unsupported Custom Destination file: {0:s} - invalid footer ' u'signature.').format(display_name))
class NTFSUsnJrnlParser(interface.FileObjectParser): """Parses a NTFS USN change journal.""" _INITIAL_FILE_OFFSET = None NAME = u'usnjrnl' DESCRIPTION = u'Parser for NTFS USN change journal ($UsnJrnl).' _USN_RECORD_V2 = construct.Struct( u'usn_record_v2', construct.ULInt32(u'size'), construct.ULInt16(u'major_version'), construct.ULInt16(u'minor_version'), construct.ULInt64(u'file_reference'), construct.ULInt64(u'parent_file_reference'), construct.ULInt64(u'update_sequence_number'), construct.ULInt64(u'update_date_time'), construct.ULInt32(u'update_reason_flags'), construct.ULInt32(u'update_source_flags'), construct.ULInt32(u'security_descriptor_identifier'), construct.ULInt32(u'file_attribute_flags'), construct.ULInt16(u'name_size'), construct.ULInt16(u'name_offset'), construct.String(u'name', lambda ctx: ctx.size - 60)) # TODO: add support for USN_RECORD_V3 when actually seen to be used. def _ParseUSNChangeJournal(self, parser_mediator, usn_change_journal): """Parses an USN change journal. Args: parser_mediator: A parser mediator object (instance of ParserMediator). usn_change_journal: An USN change journal object (instance of pyfsntsfs.usn_change_journal). """ if not usn_change_journal: return usn_record_data = usn_change_journal.read_usn_record() while usn_record_data: current_offset = usn_change_journal.get_offset() try: usn_record_struct = self._USN_RECORD_V2.parse(usn_record_data) except (IOError, construct.FieldError) as exception: parser_mediator.ProduceParseError(( u'unable to parse USN record at offset: 0x{0:08x} ' u'with error: {1:s}').format(current_offset, exception)) continue name_offset = usn_record_struct.name_offset - 60 utf16_stream = usn_record_struct.name[ name_offset:usn_record_struct.name_size] try: name_string = utf16_stream.decode(u'utf-16-le') except (UnicodeDecodeError, UnicodeEncodeError) as exception: name_string = utf16_stream.decode(u'utf-16-le', errors=u'replace') parser_mediator.ProduceParseError(( u'unable to decode USN record name string with error: ' u'{0:s}. Characters that cannot be decoded will be replaced ' u'with "?" or "\\ufffd".').format(exception)) event_object = file_system_events.NTFSUSNChangeEvent( usn_record_struct.update_date_time, current_offset, name_string, usn_record_struct.file_reference, usn_record_struct.update_sequence_number, usn_record_struct.update_source_flags, usn_record_struct.update_reason_flags, file_attribute_flags=usn_record_struct.file_attribute_flags, parent_file_reference=usn_record_struct.parent_file_reference) parser_mediator.ProduceEvent(event_object) usn_record_data = usn_change_journal.read_usn_record() def ParseFileObject(self, parser_mediator, file_object, **kwargs): """Parses a NTFS $UsnJrnl metadata file-like object. Args: parser_mediator: A parser mediator object (instance of ParserMediator). file_object: A file-like object. """ volume = pyfsntfs.volume() try: volume.open_file_object(file_object) except IOError as exception: parser_mediator.ProduceParseError( u'unable to open NTFS volume with error: {0:s}'.format(exception)) try: usn_change_journal = volume.get_usn_change_journal() self._ParseUSNChangeJournal(parser_mediator, usn_change_journal) finally: volume.close()
class AppCompatCacheKeyParser(object): """Class that parses the Application Compatibility Cache data.""" FORMAT_TYPE_2000 = 1 FORMAT_TYPE_XP = 2 FORMAT_TYPE_2003 = 3 FORMAT_TYPE_VISTA = 4 FORMAT_TYPE_7 = 5 FORMAT_TYPE_8 = 6 # AppCompatCache format signature used in Windows XP. _HEADER_SIGNATURE_XP = 0xdeadbeef # AppCompatCache format used in Windows XP. _HEADER_XP_32BIT_STRUCT = construct.Struct( 'appcompatcache_header_xp', construct.ULInt32('signature'), construct.ULInt32('number_of_cached_entries'), construct.ULInt32('unknown1'), construct.ULInt32('unknown2'), construct.Padding(384)) _CACHED_ENTRY_XP_32BIT_STRUCT = construct.Struct( 'appcompatcache_cached_entry_xp_32bit', construct.Array(528, construct.Byte('path')), construct.ULInt64('last_modification_time'), construct.ULInt64('file_size'), construct.ULInt64('last_update_time')) # AppCompatCache format signature used in Windows 2003, Vista and 2008. _HEADER_SIGNATURE_2003 = 0xbadc0ffe # AppCompatCache format used in Windows 2003. _HEADER_2003_STRUCT = construct.Struct( 'appcompatcache_header_2003', construct.ULInt32('signature'), construct.ULInt32('number_of_cached_entries')) _CACHED_ENTRY_2003_32BIT_STRUCT = construct.Struct( 'appcompatcache_cached_entry_2003_32bit', construct.ULInt16('path_size'), construct.ULInt16('maximum_path_size'), construct.ULInt32('path_offset'), construct.ULInt64('last_modification_time'), construct.ULInt64('file_size')) _CACHED_ENTRY_2003_64BIT_STRUCT = construct.Struct( 'appcompatcache_cached_entry_2003_64bit', construct.ULInt16('path_size'), construct.ULInt16('maximum_path_size'), construct.ULInt32('unknown1'), construct.ULInt64('path_offset'), construct.ULInt64('last_modification_time'), construct.ULInt64('file_size')) # AppCompatCache format used in Windows Vista and 2008. _CACHED_ENTRY_VISTA_32BIT_STRUCT = construct.Struct( 'appcompatcache_cached_entry_vista_32bit', construct.ULInt16('path_size'), construct.ULInt16('maximum_path_size'), construct.ULInt32('path_offset'), construct.ULInt64('last_modification_time'), construct.ULInt32('insertion_flags'), construct.ULInt32('shim_flags')) _CACHED_ENTRY_VISTA_64BIT_STRUCT = construct.Struct( 'appcompatcache_cached_entry_vista_64bit', construct.ULInt16('path_size'), construct.ULInt16('maximum_path_size'), construct.ULInt32('unknown1'), construct.ULInt64('path_offset'), construct.ULInt64('last_modification_time'), construct.ULInt32('insertion_flags'), construct.ULInt32('shim_flags')) # AppCompatCache format signature used in Windows 7 and 2008 R2. _HEADER_SIGNATURE_7 = 0xbadc0fee # AppCompatCache format used in Windows 7 and 2008 R2. _HEADER_7_STRUCT = construct.Struct( 'appcompatcache_header_7', construct.ULInt32('signature'), construct.ULInt32('number_of_cached_entries'), construct.Padding(120)) _CACHED_ENTRY_7_32BIT_STRUCT = construct.Struct( 'appcompatcache_cached_entry_7_32bit', construct.ULInt16('path_size'), construct.ULInt16('maximum_path_size'), construct.ULInt32('path_offset'), construct.ULInt64('last_modification_time'), construct.ULInt32('insertion_flags'), construct.ULInt32('shim_flags'), construct.ULInt32('data_size'), construct.ULInt32('data_offset')) _CACHED_ENTRY_7_64BIT_STRUCT = construct.Struct( 'appcompatcache_cached_entry_7_64bit', construct.ULInt16('path_size'), construct.ULInt16('maximum_path_size'), construct.ULInt32('unknown1'), construct.ULInt64('path_offset'), construct.ULInt64('last_modification_time'), construct.ULInt32('insertion_flags'), construct.ULInt32('shim_flags'), construct.ULInt64('data_size'), construct.ULInt64('data_offset')) # AppCompatCache format used in Windows 8.0 and 8.1. _HEADER_SIGNATURE_8 = 0x00000080 _HEADER_8_STRUCT = construct.Struct('appcompatcache_header_8', construct.ULInt32('signature'), construct.Padding(124)) _CACHED_ENTRY_HEADER_8_STRUCT = construct.Struct( 'appcompatcache_cached_entry_header_8', construct.ULInt32('signature'), construct.ULInt32('unknown1'), construct.ULInt32('cached_entry_data_size'), construct.ULInt16('path_size')) # AppCompatCache format used in Windows 8.0. _CACHED_ENTRY_SIGNATURE_8_0 = '00ts' # AppCompatCache format used in Windows 8.1. _CACHED_ENTRY_SIGNATURE_8_1 = '10ts' def CheckSignature(self, value_data): """Parses the signature. Args: value_data: a binary string containing the value data. Returns: The format type if successful or None otherwise. """ signature = construct.ULInt32('signature').parse(value_data) if signature == self._HEADER_SIGNATURE_XP: return self.FORMAT_TYPE_XP elif signature == self._HEADER_SIGNATURE_2003: # TODO: determine which format version is used (2003 or Vista). return self.FORMAT_TYPE_2003 elif signature == self._HEADER_SIGNATURE_7: return self.FORMAT_TYPE_7 elif signature == self._HEADER_SIGNATURE_8: if value_data[signature:signature + 4] in [ self._CACHED_ENTRY_SIGNATURE_8_0, self._CACHED_ENTRY_SIGNATURE_8_1 ]: return self.FORMAT_TYPE_8 def ParseHeader(self, format_type, value_data): """Parses the header. Args: format_type: integer value that contains the format type. value_data: a binary string containing the value data. Returns: A header object (instance of AppCompatCacheHeader). Raises: RuntimeError: if the format type is not supported. """ if format_type not in [ self.FORMAT_TYPE_XP, self.FORMAT_TYPE_2003, self.FORMAT_TYPE_VISTA, self.FORMAT_TYPE_7, self.FORMAT_TYPE_8 ]: raise RuntimeError( u'Unsupported format type: {0:d}'.format(format_type)) # TODO: change to collections.namedtuple or use __slots__ if the overhead # of a regular object becomes a problem. header_object = AppCompatCacheHeader() if format_type == self.FORMAT_TYPE_XP: header_object.header_size = self._HEADER_XP_32BIT_STRUCT.sizeof() header_struct = self._HEADER_XP_32BIT_STRUCT.parse(value_data) elif format_type == self.FORMAT_TYPE_2003: header_object.header_size = self._HEADER_2003_STRUCT.sizeof() header_struct = self._HEADER_2003_STRUCT.parse(value_data) elif format_type == self.FORMAT_TYPE_VISTA: header_object.header_size = self._HEADER_VISTA_STRUCT.sizeof() header_struct = self._HEADER_VISTA_STRUCT.parse(value_data) elif format_type == self.FORMAT_TYPE_7: header_object.header_size = self._HEADER_7_STRUCT.sizeof() header_struct = self._HEADER_7_STRUCT.parse(value_data) elif format_type == self.FORMAT_TYPE_8: header_object.header_size = self._HEADER_8_STRUCT.sizeof() header_struct = self._HEADER_8_STRUCT.parse(value_data) if format_type in [ self.FORMAT_TYPE_XP, self.FORMAT_TYPE_2003, self.FORMAT_TYPE_VISTA, self.FORMAT_TYPE_7 ]: header_object.number_of_cached_entries = header_struct.get( 'number_of_cached_entries') return header_object def DetermineCacheEntrySize(self, format_type, value_data, cached_entry_offset): """Parses a cached entry. Args: format_type: integer value that contains the format type. value_data: a binary string containing the value data. cached_entry_offset: integer value that contains the offset of the first cached entry data relative to the start of the value data. Returns: The cached entry size if successful or None otherwise. Raises: RuntimeError: if the format type is not supported. """ if format_type not in [ self.FORMAT_TYPE_XP, self.FORMAT_TYPE_2003, self.FORMAT_TYPE_VISTA, self.FORMAT_TYPE_7, self.FORMAT_TYPE_8 ]: raise RuntimeError( u'Unsupported format type: {0:d}'.format(format_type)) cached_entry_data = value_data[cached_entry_offset:] cached_entry_size = 0 if format_type == self.FORMAT_TYPE_XP: cached_entry_size = self._CACHED_ENTRY_XP_32BIT_STRUCT.sizeof() elif format_type in [ self.FORMAT_TYPE_2003, self.FORMAT_TYPE_VISTA, self.FORMAT_TYPE_7 ]: path_size = construct.ULInt16('path_size').parse( cached_entry_data[0:2]) maximum_path_size = construct.ULInt16('maximum_path_size').parse( cached_entry_data[2:4]) path_offset_32bit = construct.ULInt32('path_offset').parse( cached_entry_data[4:8]) path_offset_64bit = construct.ULInt32('path_offset').parse( cached_entry_data[8:16]) if maximum_path_size < path_size: logging.error(u'Path size value out of bounds.') return path_end_of_string_size = maximum_path_size - path_size if path_size == 0 or path_end_of_string_size != 2: logging.error(u'Unsupported path size values.') return # Assume the entry is 64-bit if the 32-bit path offset is 0 and # the 64-bit path offset is set. if path_offset_32bit == 0 and path_offset_64bit != 0: if format_type == self.FORMAT_TYPE_2003: cached_entry_size = self._CACHED_ENTRY_2003_64BIT_STRUCT.sizeof( ) elif format_type == self.FORMAT_TYPE_VISTA: cached_entry_size = self._CACHED_ENTRY_VISTA_64BIT_STRUCT.sizeof( ) elif format_type == self.FORMAT_TYPE_7: cached_entry_size = self._CACHED_ENTRY_7_64BIT_STRUCT.sizeof( ) else: if format_type == self.FORMAT_TYPE_2003: cached_entry_size = self._CACHED_ENTRY_2003_32BIT_STRUCT.sizeof( ) elif format_type == self.FORMAT_TYPE_VISTA: cached_entry_size = self._CACHED_ENTRY_VISTA_32BIT_STRUCT.sizeof( ) elif format_type == self.FORMAT_TYPE_7: cached_entry_size = self._CACHED_ENTRY_7_32BIT_STRUCT.sizeof( ) elif format_type == self.FORMAT_TYPE_8: cached_entry_size = self._CACHED_ENTRY_HEADER_8_STRUCT.sizeof() return cached_entry_size def ParseCachedEntry(self, format_type, value_data, cached_entry_offset, cached_entry_size): """Parses a cached entry. Args: format_type: integer value that contains the format type. value_data: a binary string containing the value data. cached_entry_offset: integer value that contains the offset of the cached entry data relative to the start of the value data. cached_entry_size: integer value that contains the cached entry data size. Returns: A cached entry object (instance of AppCompatCacheCachedEntry). Raises: RuntimeError: if the format type is not supported. """ if format_type not in [ self.FORMAT_TYPE_XP, self.FORMAT_TYPE_2003, self.FORMAT_TYPE_VISTA, self.FORMAT_TYPE_7, self.FORMAT_TYPE_8 ]: raise RuntimeError( u'Unsupported format type: {0:d}'.format(format_type)) cached_entry_data = value_data[ cached_entry_offset:cached_entry_offset + cached_entry_size] cached_entry_struct = None if format_type == self.FORMAT_TYPE_XP: if cached_entry_size == self._CACHED_ENTRY_XP_32BIT_STRUCT.sizeof( ): cached_entry_struct = self._CACHED_ENTRY_XP_32BIT_STRUCT.parse( cached_entry_data) elif format_type == self.FORMAT_TYPE_2003: if cached_entry_size == self._CACHED_ENTRY_2003_32BIT_STRUCT.sizeof( ): cached_entry_struct = self._CACHED_ENTRY_2003_32BIT_STRUCT.parse( cached_entry_data) elif cached_entry_size == self._CACHED_ENTRY_2003_64BIT_STRUCT.sizeof( ): cached_entry_struct = self._CACHED_ENTRY_2003_64BIT_STRUCT.parse( cached_entry_data) elif format_type == self.FORMAT_TYPE_VISTA: if cached_entry_size == self._CACHED_ENTRY_VISTA_32BIT_STRUCT.sizeof( ): cached_entry_struct = self._CACHED_ENTRY_VISTA_32BIT_STRUCT.parse( cached_entry_data) elif cached_entry_size == self._CACHED_ENTRY_VISTA_64BIT_STRUCT.sizeof( ): cached_entry_struct = self._CACHED_ENTRY_VISTA_64BIT_STRUCT.parse( cached_entry_data) elif format_type == self.FORMAT_TYPE_7: if cached_entry_size == self._CACHED_ENTRY_7_32BIT_STRUCT.sizeof(): cached_entry_struct = self._CACHED_ENTRY_7_32BIT_STRUCT.parse( cached_entry_data) elif cached_entry_size == self._CACHED_ENTRY_7_64BIT_STRUCT.sizeof( ): cached_entry_struct = self._CACHED_ENTRY_7_64BIT_STRUCT.parse( cached_entry_data) elif format_type == self.FORMAT_TYPE_8: if cached_entry_data[0:4] not in [ self._CACHED_ENTRY_SIGNATURE_8_0, self._CACHED_ENTRY_SIGNATURE_8_1 ]: raise RuntimeError(u'Unsupported cache entry signature') if cached_entry_size == self._CACHED_ENTRY_HEADER_8_STRUCT.sizeof( ): cached_entry_struct = self._CACHED_ENTRY_HEADER_8_STRUCT.parse( cached_entry_data) cached_entry_data_size = cached_entry_struct.get( 'cached_entry_data_size') cached_entry_size = 12 + cached_entry_data_size cached_entry_data = value_data[ cached_entry_offset:cached_entry_offset + cached_entry_size] if not cached_entry_struct: raise RuntimeError(u'Unsupported cache entry size: {0:d}'.format( cached_entry_size)) cached_entry_object = AppCompatCacheCachedEntry() cached_entry_object.cached_entry_size = cached_entry_size path_offset = 0 data_size = 0 if format_type == self.FORMAT_TYPE_XP: string_size = 0 for string_index in xrange(0, 528, 2): if (ord(cached_entry_data[string_index]) == 0 and ord(cached_entry_data[string_index + 1]) == 0): break string_size += 2 cached_entry_object.path = binary.Ut16StreamCopyToString( cached_entry_data[0:string_size]) elif format_type in [ self.FORMAT_TYPE_2003, self.FORMAT_TYPE_VISTA, self.FORMAT_TYPE_7 ]: path_size = cached_entry_struct.get('path_size') path_offset = cached_entry_struct.get('path_offset') elif format_type == self.FORMAT_TYPE_8: path_size = cached_entry_struct.get('path_size') cached_entry_data_offset = 14 + path_size cached_entry_object.path = binary.Ut16StreamCopyToString( cached_entry_data[14:cached_entry_data_offset]) remaining_data = cached_entry_data[cached_entry_data_offset:] cached_entry_object.insertion_flags = construct.ULInt32( 'insertion_flags').parse(remaining_data[0:4]) cached_entry_object.shim_flags = construct.ULInt32( 'shim_flags').parse(remaining_data[4:8]) if cached_entry_data[0:4] == self._CACHED_ENTRY_SIGNATURE_8_0: cached_entry_data_offset += 8 elif cached_entry_data[0:4] == self._CACHED_ENTRY_SIGNATURE_8_1: cached_entry_data_offset += 10 remaining_data = cached_entry_data[cached_entry_data_offset:] if format_type in [ self.FORMAT_TYPE_XP, self.FORMAT_TYPE_2003, self.FORMAT_TYPE_VISTA, self.FORMAT_TYPE_7 ]: cached_entry_object.last_modification_time = cached_entry_struct.get( 'last_modification_time') elif format_type == self.FORMAT_TYPE_8: cached_entry_object.last_modification_time = construct.ULInt64( 'last_modification_time').parse(remaining_data[0:8]) if format_type in [self.FORMAT_TYPE_XP, self.FORMAT_TYPE_2003]: cached_entry_object.file_size = cached_entry_struct.get( 'file_size') elif format_type in [self.FORMAT_TYPE_VISTA, self.FORMAT_TYPE_7]: cached_entry_object.insertion_flags = cached_entry_struct.get( 'insertion_flags') cached_entry_object.shim_flags = cached_entry_struct.get( 'shim_flags') if format_type == self.FORMAT_TYPE_XP: cached_entry_object.last_update_time = cached_entry_struct.get( 'last_update_time') if format_type == self.FORMAT_TYPE_7: data_offset = cached_entry_struct.get('data_offset') data_size = cached_entry_struct.get('data_size') elif format_type == self.FORMAT_TYPE_8: data_offset = cached_entry_offset + cached_entry_data_offset + 12 data_size = construct.ULInt32('data_size').parse( remaining_data[8:12]) if path_offset > 0 and path_size > 0: path_size += path_offset cached_entry_object.path = binary.Ut16StreamCopyToString( value_data[path_offset:path_size]) if data_size > 0: data_size += data_offset cached_entry_object.data = value_data[data_offset:data_size] return cached_entry_object
def ParseCachedEntry(self, format_type, value_data, cached_entry_offset, cached_entry_size): """Parses a cached entry. Args: format_type: integer value that contains the format type. value_data: a binary string containing the value data. cached_entry_offset: integer value that contains the offset of the cached entry data relative to the start of the value data. cached_entry_size: integer value that contains the cached entry data size. Returns: A cached entry object (instance of AppCompatCacheCachedEntry). Raises: RuntimeError: if the format type is not supported. """ if format_type not in [ self.FORMAT_TYPE_XP, self.FORMAT_TYPE_2003, self.FORMAT_TYPE_VISTA, self.FORMAT_TYPE_7, self.FORMAT_TYPE_8 ]: raise RuntimeError( u'Unsupported format type: {0:d}'.format(format_type)) cached_entry_data = value_data[ cached_entry_offset:cached_entry_offset + cached_entry_size] cached_entry_struct = None if format_type == self.FORMAT_TYPE_XP: if cached_entry_size == self._CACHED_ENTRY_XP_32BIT_STRUCT.sizeof( ): cached_entry_struct = self._CACHED_ENTRY_XP_32BIT_STRUCT.parse( cached_entry_data) elif format_type == self.FORMAT_TYPE_2003: if cached_entry_size == self._CACHED_ENTRY_2003_32BIT_STRUCT.sizeof( ): cached_entry_struct = self._CACHED_ENTRY_2003_32BIT_STRUCT.parse( cached_entry_data) elif cached_entry_size == self._CACHED_ENTRY_2003_64BIT_STRUCT.sizeof( ): cached_entry_struct = self._CACHED_ENTRY_2003_64BIT_STRUCT.parse( cached_entry_data) elif format_type == self.FORMAT_TYPE_VISTA: if cached_entry_size == self._CACHED_ENTRY_VISTA_32BIT_STRUCT.sizeof( ): cached_entry_struct = self._CACHED_ENTRY_VISTA_32BIT_STRUCT.parse( cached_entry_data) elif cached_entry_size == self._CACHED_ENTRY_VISTA_64BIT_STRUCT.sizeof( ): cached_entry_struct = self._CACHED_ENTRY_VISTA_64BIT_STRUCT.parse( cached_entry_data) elif format_type == self.FORMAT_TYPE_7: if cached_entry_size == self._CACHED_ENTRY_7_32BIT_STRUCT.sizeof(): cached_entry_struct = self._CACHED_ENTRY_7_32BIT_STRUCT.parse( cached_entry_data) elif cached_entry_size == self._CACHED_ENTRY_7_64BIT_STRUCT.sizeof( ): cached_entry_struct = self._CACHED_ENTRY_7_64BIT_STRUCT.parse( cached_entry_data) elif format_type == self.FORMAT_TYPE_8: if cached_entry_data[0:4] not in [ self._CACHED_ENTRY_SIGNATURE_8_0, self._CACHED_ENTRY_SIGNATURE_8_1 ]: raise RuntimeError(u'Unsupported cache entry signature') if cached_entry_size == self._CACHED_ENTRY_HEADER_8_STRUCT.sizeof( ): cached_entry_struct = self._CACHED_ENTRY_HEADER_8_STRUCT.parse( cached_entry_data) cached_entry_data_size = cached_entry_struct.get( 'cached_entry_data_size') cached_entry_size = 12 + cached_entry_data_size cached_entry_data = value_data[ cached_entry_offset:cached_entry_offset + cached_entry_size] if not cached_entry_struct: raise RuntimeError(u'Unsupported cache entry size: {0:d}'.format( cached_entry_size)) cached_entry_object = AppCompatCacheCachedEntry() cached_entry_object.cached_entry_size = cached_entry_size path_offset = 0 data_size = 0 if format_type == self.FORMAT_TYPE_XP: string_size = 0 for string_index in xrange(0, 528, 2): if (ord(cached_entry_data[string_index]) == 0 and ord(cached_entry_data[string_index + 1]) == 0): break string_size += 2 cached_entry_object.path = binary.Ut16StreamCopyToString( cached_entry_data[0:string_size]) elif format_type in [ self.FORMAT_TYPE_2003, self.FORMAT_TYPE_VISTA, self.FORMAT_TYPE_7 ]: path_size = cached_entry_struct.get('path_size') path_offset = cached_entry_struct.get('path_offset') elif format_type == self.FORMAT_TYPE_8: path_size = cached_entry_struct.get('path_size') cached_entry_data_offset = 14 + path_size cached_entry_object.path = binary.Ut16StreamCopyToString( cached_entry_data[14:cached_entry_data_offset]) remaining_data = cached_entry_data[cached_entry_data_offset:] cached_entry_object.insertion_flags = construct.ULInt32( 'insertion_flags').parse(remaining_data[0:4]) cached_entry_object.shim_flags = construct.ULInt32( 'shim_flags').parse(remaining_data[4:8]) if cached_entry_data[0:4] == self._CACHED_ENTRY_SIGNATURE_8_0: cached_entry_data_offset += 8 elif cached_entry_data[0:4] == self._CACHED_ENTRY_SIGNATURE_8_1: cached_entry_data_offset += 10 remaining_data = cached_entry_data[cached_entry_data_offset:] if format_type in [ self.FORMAT_TYPE_XP, self.FORMAT_TYPE_2003, self.FORMAT_TYPE_VISTA, self.FORMAT_TYPE_7 ]: cached_entry_object.last_modification_time = cached_entry_struct.get( 'last_modification_time') elif format_type == self.FORMAT_TYPE_8: cached_entry_object.last_modification_time = construct.ULInt64( 'last_modification_time').parse(remaining_data[0:8]) if format_type in [self.FORMAT_TYPE_XP, self.FORMAT_TYPE_2003]: cached_entry_object.file_size = cached_entry_struct.get( 'file_size') elif format_type in [self.FORMAT_TYPE_VISTA, self.FORMAT_TYPE_7]: cached_entry_object.insertion_flags = cached_entry_struct.get( 'insertion_flags') cached_entry_object.shim_flags = cached_entry_struct.get( 'shim_flags') if format_type == self.FORMAT_TYPE_XP: cached_entry_object.last_update_time = cached_entry_struct.get( 'last_update_time') if format_type == self.FORMAT_TYPE_7: data_offset = cached_entry_struct.get('data_offset') data_size = cached_entry_struct.get('data_size') elif format_type == self.FORMAT_TYPE_8: data_offset = cached_entry_offset + cached_entry_data_offset + 12 data_size = construct.ULInt32('data_size').parse( remaining_data[8:12]) if path_offset > 0 and path_size > 0: path_size += path_offset cached_entry_object.path = binary.Ut16StreamCopyToString( value_data[path_offset:path_size]) if data_size > 0: data_size += data_offset cached_entry_object.data = value_data[data_offset:data_size] return cached_entry_object