Beispiel #1
0
def DecompressChunkData(chunk_data, data_len):
    '''Decompress an individual compressed chunk (tag=0x600D)'''
    uncompressed = b''
    if chunk_data[0:4] in [b'bv41', b'bv4-']:
        last_uncompressed = b''
        comp_start = 0  # bv** offset
        comp_header = chunk_data[comp_start:comp_start + 4]
        while (data_len > comp_start) and (comp_header != b'bv4$'):
            if comp_header == b'bv41':
                uncompressed_size, compressed_size = struct.unpack(
                    '<II', chunk_data[comp_start + 4:comp_start + 12])
                last_uncompressed = lz4.block.decompress(
                    chunk_data[comp_start + 12:comp_start + 12 +
                               compressed_size],
                    uncompressed_size,
                    dict=last_uncompressed)
                comp_start += 12 + compressed_size
                uncompressed += last_uncompressed
            elif comp_header == b'bv4-':
                uncompressed_size = struct.unpack(
                    '<I', chunk_data[comp_start + 4:comp_start + 8])[0]
                uncompressed += chunk_data[comp_start + 8:comp_start + 8 +
                                           uncompressed_size]
                comp_start += 8 + uncompressed_size
            else:
                logger.error(
                    'Unknown compression value {} @ 0x{:X} - {}'.format(
                        binascii.hexlify(comp_header), begin_pos + comp_start,
                        comp_header))
                break
            comp_header = chunk_data[comp_start:comp_start + 4]
    else:
        logger.error('Unknown compression type {}'.format(
            binascii.hexlify(chunk_data[16:20])))
    return uncompressed
Beispiel #2
0
    def ReadFmtStringFromVirtualOffset(self, v_offset):
        '''Reads a format string for a specific virtual offset.

        Args:
          v_offset (int): virtual offset.

        Returns:
          str: a format string, '%s' if the 32-bit MSB (0x80000000) is set or
              '<compose failure [UUID]>' if the uuidtext file could not be
              parsed or there is no entry corresponding with the virtual offset.
        '''
        if not self._file.is_valid:
            # This is the value returned by the MacOS 'log' program if uuidtext
            # is not found.
            return '<compose failure [UUID]>'

        if v_offset & 0x80000000:
            return '%s'

        for range_start_offset, data_offset, data_len in self._entries:
            range_end_offset = range_start_offset + data_len
            if range_start_offset <= v_offset < range_end_offset:
                rel_offset = v_offset - range_start_offset

                file_object = self._file.file_pointer
                file_object.seek(data_offset + rel_offset)
                format_string_data = file_object.read(data_len - rel_offset)
                return self._ReadCString(format_string_data,
                                         data_len - rel_offset)

        # This is the value returned by the MacOS 'log' program if the uuidtext
        # entry is not found.
        logger.error('Invalid bounds 0x{0:X} for {1!s}'.format(
            v_offset, self.Uuid))
        return '<compose failure [UUID]>'
Beispiel #3
0
 def GetSubSystemAndCategory(self, sc_id):
     sc = self.items.get(sc_id, None)
     if sc:
         return (sc[0], sc[1])
     # Not found!
     logger.error("Could not find subsystem_category_id={}".format(sc_id))
     return ('', '')
Beispiel #4
0
 def GetProcInfoById(self, id):
     for proc_info in self.ProcInfos:
         if proc_info.id == id:
             return proc_info
     # Not found!
     logger.error("ProcInfo with id={} not found".format(id))
     return None
Beispiel #5
0
 def GetUuidEntryFromVirtualOffset(self, v_offset):
     '''Returns uuid_entry where uuid_entry[xx].v_off <= v_offset and falls within allowed size'''
     for b in self.uuid_entries:
         if (b[0] <= v_offset) and ((b[0] + b[1]) > v_offset):
             rel_offset = v_offset - b[0]
             return b
     #Not found
     logger.error('Failed to find uuid_entry for v_offset 0x{:X} in Dsc!'.format(v_offset))
     return None
Beispiel #6
0
 def FindVirtualOffsetEntries(self, v_offset):
     '''Return tuple (range_entry, uuid_entry) where range_entry[xx].size <= v_offset'''
     ret_range_entry = None
     ret_uuid_entry = None
     for a in self.range_entries:
         if (a[1] <= v_offset) and ((a[1] + a[3]) > v_offset):
             ret_range_entry = a
             ret_uuid_entry = self.uuid_entries[a[0]]
             return (ret_range_entry, ret_uuid_entry)
     #Not found
     logger.error('Failed to find v_offset in Dsc!')
     return (None, None)
Beispiel #7
0
def ReadTimesyncFile(buffer, ts_list):
    try:
        pos = 0
        size = len(buffer)
        while pos < size:
            sig, header_size, unk1 = struct.unpack("<HHI", buffer[pos:pos + 8])
            if sig != 0xBBB0:
                logger.error(
                    "not the right signature for Timesync header, got 0x{:04X} instead of 0x{:04X}, pos was 0x{:08X}"
                    .format(sig, 0x0030BBB0, pos))
                break
            uuid = UUID(bytes=buffer[pos + 8:pos + 24])
            ts_numer, ts_denom, t_stamp, tz, is_dst = struct.unpack(
                "<IIqiI", buffer[pos + 24:pos + 48])
            ts_header = resources.TimesyncHeader(sig, unk1, uuid, ts_numer,
                                                 ts_denom, t_stamp, tz, is_dst)
            pos += header_size  # 0x30 (48) by default
            if header_size != 0x30:
                logger.info(
                    "Timesync header was 0x{:X} bytes instead of 0x30(48) bytes!"
                    .format(size))
            logger.debug("TIMEHEAD {}  0x{:016X}  {} {}".format(
                uuid, t_stamp, ReadAPFSTime(t_stamp), 'boot'))
            #TODO - TEST search ts_list for existing, not seen so far
            existing_ts = None
            for ts in ts_list:
                if ts.header.boot_uuid == uuid:
                    existing_ts = ts
                    break
            if existing_ts:
                ts_obj = existing_ts
            else:
                ts_obj = resources.Timesync(ts_header)
                ts_list.append(ts_obj)
                # Adding header timestamp as Ts type too with cont_time = 0
                timesync_item = resources.TimesyncItem(0, 0, t_stamp, tz,
                                                       is_dst)
                ts_obj.items.append(timesync_item)
            while pos < size:
                if buffer[pos:pos + 4] == b'Ts \x00':
                    ts_unknown, cont_time, t_stamp, bias, is_dst = struct.unpack(
                        "<IqqiI", buffer[pos + 4:pos + 32])
                    timesync_item = resources.TimesyncItem(
                        ts_unknown, cont_time, t_stamp, bias, is_dst)
                    ts_obj.items.append(timesync_item)
                    logger.debug("TIMESYNC {}  0x{:016X}  {} {}".format(
                        uuid, t_stamp, ReadAPFSTime(t_stamp), ts_unknown))
                else:
                    break  # break this loop, parse as header
                pos += 32
    except Exception as ex:
        logger.exception("Exception reading TimesyncFile")
Beispiel #8
0
def DecompressTraceV3(trace_file, out_file):
    ''' Creates an uncompressed version of the .traceV3 file.
        Input parameters:
        trace_file = file pointer to .traceV3 file (opened as 'rb')
        out_file   = file pointer to blank file (opened as 'wb')
        Returns True/False
    '''
    try:
        index = 0
        tag = trace_file.read(4)
        while tag:
            begin_pos = trace_file.tell() - 4
            trace_file.seek(begin_pos + 8)
            struct_len = struct.unpack('<Q', trace_file.read(8))[0]
            logger.debug("index={} pos=0x{:X} tag=0x{}".format(
                index, begin_pos,
                binascii.hexlify(tag)[::-1]))

            trace_file.seek(begin_pos)
            chunk_data_incl_header = trace_file.read(16 + struct_len)
            if tag == b'\x00\x10\x00\x00':  # header
                out_file.write(chunk_data_incl_header
                               )  # boot_uuid header, write to output directly
            elif tag[0] == b'\x0B':
                out_file.write(chunk_data_incl_header
                               )  # uncompressed, write to output directly
            elif tag[0] == b'\x0D':
                uncompressed = DecompressChunkData(chunk_data_incl_header[16:],
                                                   struct_len)
                out_file.write(chunk_data_incl_header[0:8])  # Same Header !
                out_file.write(struct.pack('<Q',
                                           len(uncompressed)))  # New size
                out_file.write(uncompressed)
            else:
                logger.error('Unknown chunk tag value encountered : {}'.format(
                    binascii.hexlify(tag)))
                out_file.write(chunk_data_incl_header)
            if struct_len % 8:  # Go to QWORD boundary
                struct_len += 8 - (struct_len % 8)
            if out_file.tell() % 8:  # Go to QWORD boundary on output
                out_file.write(
                    b'\x00\x00\x00\x00\x00\x00\x00'[0:(8 -
                                                       out_file.tell() % 8)])
            trace_file.seek(begin_pos + 16 + struct_len)
            tag = trace_file.read(4)
            index += 1
    except Exception as ex:
        logger.exception('')
        return False
    return True
Beispiel #9
0
def ReadAPFSTime(
    mac_apfs_time
):  # Mac APFS timestamp is nano second time epoch beginning 1970/1/1
    '''Returns datetime object, or empty string upon error'''
    if mac_apfs_time not in (0, None, ''):
        try:
            if isinstance(mac_apfs_time, str):
                mac_apfs_time = float(mac_apfs_time)
            return datetime.datetime(1970, 1, 1) + datetime.timedelta(
                seconds=mac_apfs_time / 1000000000.)
        except Exception as ex:
            logger.error(
                "ReadAPFSTime() Failed to convert timestamp from value " +
                str(mac_apfs_time) + " Error was: " + str(ex))
    return ''
Beispiel #10
0
    def _ReadNtSid(self, data):
        '''Reads a windows SID from its raw binary form'''
        sid = ''
        size = len(data)
        if size < 8:
            logger.error('Not a windows sid')
        rev = struct.unpack("<B", data[0])[0]
        num_sub_auth = struct.unpack("<B", data[1])[0]
        authority = struct.unpack(">I", data[4:8])[0]

        if size < (8 + (num_sub_auth * 4)):
            logger.error(
                'buffer too small or truncated - cant fit all sub_auth')
            return ''
        sub_authorities = struct.unpack('<{}I'.format(num_sub_auth),
                                        data[8:8 * num_sub_auth])
        sid = 'S-{}-{}-'.format(rev, authority) + '-'.join(
            [str(sa) for sa in sub_authorities])
        return sid
Beispiel #11
0
def ReadTimesyncFolder(path, ts_list, vfs):
    '''Reads files in the timesync folder specified by 'path' and populates ts_list 
       with timesync entries.
       vfs = VirtualFileSystem object
    '''
    try:
        entries = vfs.listdir(path)
        for entry in sorted(
                entries
        ):  # sort the files by name, so continuous time will be sequential automatically
            if entry.endswith(".timesync"):
                file_path = vfs.path_join(path, entry)
                logger.debug(
                    'Trying to read timesync file {}'.format(file_path))
                f = vfs.get_virtual_file(file_path, 'TimeSync').open()
                if f:
                    buffer = f.read()  # should be a fairly small file!
                    ReadTimesyncFile(buffer, ts_list)
                    f.close()
            else:
                logger.error(
                    "In Timesync folder, found non-ts file {}".format(entry))
    except Exception:
        logger.exception('')