Exemple #1
0
def test_mft_4k_resident_data():
    f = open(MFT_4K, 'rb')
    mft = MFT.MasterFileTableParser(f)

    cnt = 0
    for fr in mft.file_records():
        if mft.build_full_paths(fr) == ['/1.txt']:
            cnt += 1

            for attr in fr.attributes():
                v = attr.value_decoded()
                if type(v) is Attributes.Data:
                    data = v.value

                    md5 = hashlib.md5()
                    md5.update(data)
                    assert md5.hexdigest(
                    ) == 'a75a25c964f50df4ea9398d8ccf6afbd'

                    assert data.replace(b'ABC', b'') == b''

        elif mft.build_full_paths(fr) == ['/2.txt']:
            cnt += 1

            for attr in fr.attributes():
                v = attr.value_decoded()
                if type(v) is Attributes.Data:
                    md5 = hashlib.md5()
                    md5.update(v.value)
                    assert md5.hexdigest(
                    ) == 'd6fbed685c98416fb7388dad7503811c'

    assert cnt == 2

    f.close()
Exemple #2
0
def test_open_file_by_child_frs():
    f = open(MFT_NHC, 'rb')
    mft = MFT.MasterFileTableParser(f)

    for file_number in [8508, 8533, 8535, 8553]:
        file_record = mft.get_file_record_by_number(file_number)
        assert file_record.get_master_file_table_number() == 8508

    with pytest.raises(MFT.MasterFileTableException):
        file_record = mft.get_file_record_by_number(8533, None, False)

    f.close()
Exemple #3
0
def test_different_la():
    f = open(MFT_DIFFERENT_LA, 'rb')

    c_1 = 0
    c_2 = 0

    mft = MFT.MasterFileTableParser(f)
    for file_record in mft.file_records():
        paths = mft.build_full_paths(file_record)

        if len(paths) == 0:
            continue

        assert len(paths) == 1
        path = paths[0]

        if path == '/ts_la/test_la.txt':
            for attr in file_record.attributes():
                attr_value = attr.value_decoded()

                if type(attr_value) is not Attributes.StandardInformation:
                    continue

                c_1 += 1
                ts_m_1 = attr_value.get_mtime()
                ts_a_1 = attr_value.get_atime()
                ts_c_1 = attr_value.get_ctime()
                ts_e_1 = attr_value.get_etime()

        elif path == '/ts_la':
            for attr in file_record.attributes():
                attr_value = attr.value_decoded()

                if type(attr_value) is not Attributes.IndexRoot:
                    continue

                for index_entry in attr_value.index_entries():
                    attr_value = Attributes.FileName(
                        index_entry.get_attribute())

                    c_2 += 1
                    ts_m_2 = attr_value.get_mtime()
                    ts_a_2 = attr_value.get_atime()
                    ts_c_2 = attr_value.get_ctime()
                    ts_e_2 = attr_value.get_etime()

    assert c_1 == 1 and c_2 == 1
    assert ts_m_1 == ts_m_2 and ts_c_1 == ts_c_2 and ts_e_1 == ts_e_2 and ts_a_1 != ts_a_2
    assert ts_a_2 < ts_a_1

    f.close()
Exemple #4
0
def test_mft_mirr():
    f = open(MFT_MIRR, 'rb')

    file_names = ['$MFT', '$MFTMirr', '$LogFile', '$Volume']

    mft = MFT.MasterFileTableParser(f)
    for file_record in mft.file_records():
        for attribute in file_record.attributes():
            if type(attribute) is MFT.AttributeRecordNonresident:
                continue

            value = attribute.value_decoded()
            if type(value) is not Attributes.FileName:
                continue

            assert value.get_file_name() == file_names.pop(0)

    assert len(file_names) == 0
    f.close()

    f = open(MFT_MIRR_4K, 'rb')

    file_names = ['$MFT', '$MFTMirr', '$LogFile', '$Volume']

    mft = MFT.MasterFileTableParser(f)
    for file_record in mft.file_records():
        for attribute in file_record.attributes():
            if type(attribute) is MFT.AttributeRecordNonresident:
                continue

            value = attribute.value_decoded()
            if type(value) is not Attributes.FileName:
                continue

            assert value.get_file_name() == file_names.pop(0)

    assert len(file_names) == 0
    f.close()
Exemple #5
0
def test_mft_unicode_volume_name():
    f = open(MFT_ORHPAN, 'rb')
    mft = MFT.MasterFileTableParser(f)

    volume_name = None
    fr_vol = mft.get_file_record_by_number(MFT.FILE_NUMBER_VOLUME)
    for attr in fr_vol.attributes():
        v = attr.value_decoded()
        if type(v) is Attributes.VolumeName:
            volume_name = v.get_name()
            break

    assert volume_name == 'тест-test'

    f.close()
Exemple #6
0
def test_frs():
    with open(FRS, 'rb') as f:
        frs_raw = f.read()

    frs = MFT.FileRecordSegment(frs_raw)

    assert frs.is_in_use()
    assert frs.get_sequence_number() == 2
    assert frs.get_reference_count() == 1
    assert frs.is_base_file_record_segment()
    assert frs.get_logfile_sequence_number() == 31832129
    assert frs.get_master_file_table_number() == 11072

    attr_list = None
    i = 0
    for attr in frs.attributes():
        if i == 0:
            assert type(attr.value_decoded()) is Attributes.StandardInformation
        elif i == 1:
            assert type(attr.value_decoded()) is Attributes.AttributeList
            attr_list = attr.value_decoded()
        elif i == 2:
            assert type(attr.value_decoded()) is Attributes.FileName
        elif i == 3:
            assert type(attr.value_decoded()) is Attributes.ObjectID
        else:
            assert False

        i += 1

    assert i == 4

    i = 0
    for attr_entry in attr_list.entries():
        assert attr_entry.attribute_name is None

        if i == 0:
            assert attr_entry.attribute_type_code == 0x10
        elif i == 1:
            assert attr_entry.attribute_type_code == 0x30
        elif i == 2:
            assert attr_entry.attribute_type_code == 0x40
        else:
            assert attr_entry.attribute_type_code == 0x80

        i += 1

    assert i == 6
Exemple #7
0
def test_deleted():
    f = open(MFT_DELETED, 'rb')

    found = False

    mft = MFT.MasterFileTableParser(f)
    for file_record in mft.file_records():
        paths = mft.build_full_paths(file_record)

        if len(paths) > 0 and paths[
                0] == '/1/2/3/4/file.txt' and not file_record.is_in_use():
            found = True

    f.close()

    assert found
Exemple #8
0
def test_mft_unicode_file_names():
    f = open(MFT_UNICODE, 'rb')
    mft = MFT.MasterFileTableParser(f)

    cnt = 0
    for fr in mft.file_records():
        paths = mft.build_full_paths(fr)
        assert len(paths) == 0 or len(paths) == 1

        if len(paths) > 0:
            path = paths[0]
            if path == '/Привет' or path == '/Привет/привет.txt':
                cnt += 1

    assert cnt == 2

    f.close()
Exemple #9
0
def test_compressed_sparse():
    f = open(MFT_COMPRESSED_SPARSE, 'rb')

    tested_cnt = 0
    mft = MFT.MasterFileTableParser(f)

    for file_record in mft.file_records():
        paths = mft.build_full_paths(file_record)

        if len(paths) == 0:
            continue

        assert len(paths) == 1
        path = paths[0]

        if path.endswith('/compressed.txt'):
            tested_cnt += 1

            c = 0
            for attr in file_record.attributes():
                if type(attr) is MFT.AttributeRecordResident:
                    continue

                c += 1
                assert attr.type_code == 0x80 and attr.name is None and attr.file_size == 22308

            assert c == 1

        elif path.endswith('/sparse'):
            tested_cnt += 1

            c = 0
            for attr in file_record.attributes():
                if type(attr) is MFT.AttributeRecordResident:
                    continue

                c += 1
                assert attr.type_code == 0x80 and attr.name is None and attr.file_size == 1048582

            assert c == 1

    assert tested_cnt == 2

    f.close()
Exemple #10
0
def test_ea_sizes():
    f = open(FRS_EA, 'rb')
    frs_raw = f.read()

    frs = MFT.FileRecordSegment(frs_raw)
    for attribute in frs.attributes():
        attribute_value = attribute.value_decoded()
        if type(attribute_value) is Attributes.EA:
            assert len(attribute_value.value) == 160

        if type(attribute_value) is Attributes.EAInformation:
            assert attribute_value.get_packed_ea_size(
            ) == 149 and attribute_value.get_unpacked_ea_size() == 160

        elif type(attribute_value) is Attributes.FileName:
            assert attribute_value.get_packed_ea_size(
            ) == 149 and attribute_value.get_unpacked_ea_size_difference(
            ) == 11

    f.close()
Exemple #11
0
def test_rp_in_frs():
    with open(FRS_RP, 'rb') as f:
        frs_raw = f.read()

    frs = MFT.FileRecordSegment(frs_raw)
    for attribute in frs.attributes():
        assert type(attribute) is MFT.AttributeRecordResident

        v = attribute.value_decoded()

        if type(v) is not Attributes.ReparsePoint:
            continue

        assert v.is_reparse_tag_microsoft()

        rp_buf = v.get_reparse_buffer()

        md5 = hashlib.md5()
        md5.update(rp_buf)
        assert md5.hexdigest() == 'a8ac63b71e1af29121c6d3c3c438926b'
Exemple #12
0
def test_wsl_in_frs():
    with open(FRS_EA_WSL, 'rb') as f:
        frs_raw = f.read()

    frs = MFT.FileRecordSegment(frs_raw)
    for attribute in frs.attributes():
        assert type(attribute) is MFT.AttributeRecordResident

        v = attribute.value_decoded()

        if type(v) is not Attributes.EA:
            continue

        c = 0
        for name, flags, value in v.data_parsed():
            c += 1

            assert flags == 0

            if name == b'LXATTRB\x00':
                lxattrb = WSL.LXATTRB(value)

                mtime = lxattrb.get_mtime()
                assert mtime.year == 2019 and mtime.month == 1 and mtime.day == 21
            elif name == b'LXXATTR\x00':
                lxxattr = WSL.LXXATTR(value)

                xattr_list = []
                for xname, xvalue in lxxattr.extended_attributes():
                    xattr_list.append((xname, xvalue))

                xattr_list.remove((b'user.1', b'11'))
                xattr_list.remove((b'user.2', b'22'))
                xattr_list.remove((b'user.3', b'33'))
                xattr_list.remove((b'user.4444', b'attrval'))
                assert len(xattr_list) == 0
            else:
                assert False

        assert c == 2
Exemple #13
0
def test_mft_orphan_files():
    f = open(MFT_ORHPAN, 'rb')
    mft = MFT.MasterFileTableParser(f)

    cnt = 0
    orphans_seen = []
    for fr in mft.file_records():
        paths = mft.build_full_paths(fr)
        if len(paths) == 0:
            continue

        assert len(paths) == 1
        path = paths[0]

        if path.startswith('<Orphan>/'):
            cnt += 1
            orphans_seen.append(path)

    assert cnt == 4
    assert sorted(orphans_seen) == [
        '<Orphan>/2.txt', '<Orphan>/3.txt', '<Orphan>/4.txt', '<Orphan>/5.txt'
    ]

    f.close()
Exemple #14
0
def test_mft_allocated_files():
    def compare_against_fls_line(fr, path, fls_line):
        # This function must return False if a file record and its path do not apply to an FLS line.
        # If they do, an assertion error (or another exception) must be raised if something is invalid.
        # If everything is okay, this function must return True.

        fls_entries = fls_line.split('\t')

        file_name_fls = fls_entries[1]
        if '/' + file_name_fls != path and not (
                '/' + file_name_fls).startswith(
                    path + ':$'
                ):  # A file record and its path do not apply to this FLS line.
            return False

        # Run the checks.
        file_type_fls, inode_fls = fls_entries[0].rstrip(':').split(' ')

        if fr.get_flags() & MFT.FILE_FILE_NAME_INDEX_PRESENT > 0:
            assert file_type_fls == 'd/d'
        else:
            assert file_type_fls == 'r/r'

        assert inode_fls.startswith(
            str(fr.get_master_file_table_number()) + '-')

        mod_time_fls, acc_time_fls, chg_time_fls, cre_time_fls = fls_entries[
            2:6]
        if mod_time_fls.endswith(' (UTC)'):
            use_msk = False
        elif mod_time_fls.endswith(' (MSK)'):
            use_msk = True
        else:
            assert False

        for attr in fr.attributes():
            if type(attr) is MFT.AttributeRecordNonresident:
                continue

            v = attr.value_decoded()
            if type(v) is Attributes.StandardInformation:
                if not use_msk:
                    mod_time = v.get_mtime().strftime(
                        '%Y-%m-%d %H:%M:%S (UTC)')
                else:
                    mod_time = (v.get_mtime() + datetime.timedelta(hours=3)
                                ).strftime('%Y-%m-%d %H:%M:%S (MSK)')

                assert mod_time == mod_time_fls

                if not use_msk:
                    acc_time = v.get_atime().strftime(
                        '%Y-%m-%d %H:%M:%S (UTC)')
                else:
                    acc_time = (v.get_atime() + datetime.timedelta(hours=3)
                                ).strftime('%Y-%m-%d %H:%M:%S (MSK)')

                assert acc_time == acc_time_fls

                if not use_msk:
                    chg_time = v.get_etime().strftime(
                        '%Y-%m-%d %H:%M:%S (UTC)')
                else:
                    chg_time = (v.get_etime() + datetime.timedelta(hours=3)
                                ).strftime('%Y-%m-%d %H:%M:%S (MSK)')

                assert chg_time == chg_time_fls

                if not use_msk:
                    cre_time = v.get_ctime().strftime(
                        '%Y-%m-%d %H:%M:%S (UTC)')
                else:
                    cre_time = (v.get_ctime() + datetime.timedelta(hours=3)
                                ).strftime('%Y-%m-%d %H:%M:%S (MSK)')

                assert cre_time == cre_time_fls

        size_fls, uid_fls, gid_fls = fls_entries[6:9]
        assert int(size_fls) >= 0 and int(uid_fls) >= 0 and int(gid_fls) >= 0

        return True

    for mft_filename, mft_parsed_filename in MFT_ALLOCATED_TEST_LIST:
        f = open(mft_filename, 'rb')
        mft = MFT.MasterFileTableParser(f)

        with open(mft_parsed_filename, 'rb') as fls:
            fls_output = fls.read().decode('utf-8').splitlines()

        not_found_list = []
        for fr in mft.file_records(True):
            paths = mft.build_full_paths(fr)
            if len(paths) == 0:  # A file with no name, skip it.
                continue

            i_paths = []
            for i_path, i_file_name in mft.build_full_paths(fr, True):
                i_paths.append(i_path)
                assert i_path.endswith('/' + i_file_name.get_file_name())

            assert sorted(paths) == sorted(i_paths)

            for path in paths:
                found = False
                for fls_line in fls_output[:]:
                    if compare_against_fls_line(fr, path, fls_line):
                        found = True
                        fls_output.remove(fls_line)

                if not found:
                    not_found_list.append(path)

        assert len(
            fls_output) == 1  # A virtual directory ("$OrphanFiles") is left.

        not_found_list.remove('/.')  # This is not present in FLS lines.
        for path in not_found_list:
            # Check that we did not find short file names only.
            try:
                file_name, file_extension = path.split('/')[-1].split('.')
            except ValueError:
                file_name = path.split('/')[-1]
                assert len(file_name) <= 8 and file_name.upper() == file_name
            else:
                assert len(file_name) <= 8 and file_name.upper() == file_name
                assert len(file_extension) <= 3 and file_extension.upper(
                ) == file_extension

        f.close()