1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155
|
/*
* The Sleuth Kit
*
* Brian Carrier [carrier <at> sleuthkit [dot] org]
* Copyright (c) 2019-2020 Brian Carrier. All Rights reserved
* Copyright (c) 2018-2019 BlackBag Technologies. All Rights reserved
*
* This software is distributed under the Common Public License 1.0
*/
#include "tsk_apfs.hpp"
#include "../libtsk.h"
#include "../fs/tsk_apfs.hpp"
#include "../fs/tsk_fs_i.h"
#include <stdexcept>
APFSPool::APFSPool(std::vector<img_t>&& imgs, apfs_block_num nx_block_num)
: TSKPool(std::forward<std::vector<img_t>>(imgs)),
_nx_block_num{nx_block_num} {
if (_members.size() != 1) {
throw std::runtime_error(
"Only single physical store APFS pools are currently supported");
}
// If we're scanning for the latest NXSB then we need to start with the
// last known good NXSB first
if (_nx_block_num == APFS_POOL_NX_BLOCK_LATEST) {
_nx_block_num = APFS_POOL_NX_BLOCK_LAST_KNOWN_GOOD;
}
std::tie(_img, _offset) = _members[0];
auto nxsb = nx(true);
// Update the base members
_uuid = nxsb->uuid();
_block_size = nxsb->block_size();
_dev_block_size = _img->sector_size;
_num_blocks = nxsb->num_blocks();
// Check to see if we need to scan for a newer pool
if (nx_block_num == APFS_POOL_NX_BLOCK_LATEST) {
const auto versions = known_versions();
if (versions.empty()) {
_nx_block_num = APFS_POOL_NX_BLOCK_LAST_KNOWN_GOOD;
if (tsk_verbose) {
tsk_fprintf(stderr,
"APFSPool: No checkpoint superblocks found. Attempting to "
"fall back to last known good superblock\n");
}
} else {
const auto highest = std::max_element(
versions.begin(), versions.end(),
[](const auto& a, const auto& b) { return a.xid < b.xid; });
// No need to do anything, we're already the highest version
if (highest->xid != nxsb->xid()) {
_nx_block_num = highest->nx_block_num;
try {
nxsb = nx(true);
} catch (const std::runtime_error&) {
// Fallback to last known good block if the latest block is not valid
_nx_block_num = APFS_POOL_NX_BLOCK_LAST_KNOWN_GOOD;
nxsb = nx(true);
}
}
}
}
_vol_blocks = nxsb->volume_blocks();
_num_vols = static_cast<int>(_vol_blocks.size());
// If the software crypto bit is not set, then either hardware crypto is used
// or there are no volumes that are encrypted.
if (bit_is_set(nxsb->sb()->flags, APFS_NXSB_FLAGS_CRYPTO_SW) == false) {
// We need to check each volume to determine if any of them have encryption
// enabled.
for (const auto& volume : volumes()) {
if (volume.encrypted()) {
_hw_crypto = true;
break;
}
}
}
}
std::unique_ptr<APFSSuperblock> APFSPool::nx(bool validate) const {
auto nxsb = std::make_unique<APFSSuperblock>((*this), _nx_block_num);
if (validate && nxsb->validate_checksum() == false) {
throw std::runtime_error("NXSB object checksum failed");
}
return nxsb;
}
std::vector<APFSFileSystem> APFSPool::volumes() const {
std::vector<APFSFileSystem> v{};
v.reserve(_vol_blocks.size());
for (const auto block : _vol_blocks) {
v.emplace_back((*this), block);
}
return v;
}
ssize_t APFSPool::read(uint64_t address, char* buf, size_t buf_size) const
noexcept {
return tsk_img_read(_img, address + _offset, buf, buf_size);
}
const std::vector<APFSPool::nx_version> APFSPool::known_versions() const {
std::vector<nx_version> vers{};
const auto nxsb = nx();
const auto sb = nxsb->sb();
for (auto addr = sb->chkpt_desc_base_addr;
addr < sb->chkpt_desc_base_addr + sb->chkpt_desc_block_count; addr++) {
APFSObject obj{(*this), addr};
if (obj.obj_type() != APFS_OBJ_TYPE_SUPERBLOCK ||
obj.oid() != nxsb->oid()) {
continue;
}
if (!obj.validate_checksum()) {
continue;
}
vers.emplace_back(nx_version{addr, obj.xid()});
}
return vers;
}
const std::vector<APFSPool::range> APFSPool::unallocated_ranges() const {
return nx()->unallocated_ranges();
}
void APFSPool::clear_cache() noexcept {
_block_cache.clear();
tsk_take_lock(&(_img->cache_lock));
// Setting the lengths to zero should invalidate the cache.
memset(_img->cache_len, 0, sizeof(_img->cache_len));
tsk_release_lock(&(_img->cache_lock));
}
|