50 {
51 std::vector<ScanResult> out;
52 std::set<uint64_t> seen_offsets;
53
54 logs.push_back(
trs(
"NTFS probe: analysis started."));
55
56 for (const auto& seed : seed_results) {
57 const bool interesting_seed =
61 if (!interesting_seed || seen_offsets.count(seed.offset_bytes) != 0) {
62 continue;
63 }
64 seen_offsets.insert(seed.offset_bytes);
65
66 std::array<unsigned char, 512> sector{};
68 continue;
69 }
70
71 const uint16_t bytes_per_sector =
le16(sector.data() + 11);
72 const uint8_t sectors_per_cluster = sector[13];
73 const uint64_t total_sectors =
le64(sector.data() + 40);
74 const uint64_t mft_lcn =
le64(sector.data() + 48);
75 const uint64_t mftmirr_lcn =
le64(sector.data() + 56);
76 const int8_t record_descriptor = static_cast<int8_t>(sector[64]);
77
78 if (bytes_per_sector == 0 || sectors_per_cluster == 0 || total_sectors == 0) {
79 continue;
80 }
81
82 const uint64_t cluster_size = static_cast<uint64_t>(bytes_per_sector) * sectors_per_cluster;
83 const uint64_t volume_size = total_sectors * bytes_per_sector;
85
86 if (record_size == 0) {
87 continue;
88 }
89 const uint64_t mft_offset = seed.offset_bytes + (mft_lcn * cluster_size);
90 const uint64_t mftmirr_offset = seed.offset_bytes + (mftmirr_lcn * cluster_size);
91
92 std::array<char, 4> mft_sig{};
93 bool mft_ok = reader.
read_at(mft_offset, mft_sig.data(), mft_sig.size()) &&
94 std::string(mft_sig.data(), mft_sig.size()) == "FILE";
95
96 std::array<char, 4> mftmirr_sig{};
97 bool mftmirr_ok = reader.
read_at(mftmirr_offset, mftmirr_sig.data(), mftmirr_sig.size()) &&
98 std::string(mftmirr_sig.data(), mftmirr_sig.size()) == "FILE";
99
100 std::ostringstream detail;
101 detail <<
trs(
"Bytes/sector") <<
": " << bytes_per_sector
102 <<
", " <<
trs(
"Sectors/cluster") <<
": " <<
static_cast<unsigned>(sectors_per_cluster)
103 <<
", " <<
trs(
"MFT record size") <<
": " << record_size
104 <<
", " <<
trs(
"MFT") <<
": " << (mft_ok ?
"OK" :
"missing")
105 <<
", " <<
trs(
"MFTMirr") <<
": " << (mftmirr_ok ?
"OK" :
"missing");
106
107 double confidence = 0.85;
108 if (mft_ok) confidence += 0.10;
109 if (mftmirr_ok) confidence += 0.03;
110 if (confidence > 0.99) confidence = 0.99;
111
113 seed.offset_bytes,
114 volume_size,
115 "NTFS volume",
116 "NTFS probe",
117 detail.str(),
118 confidence});
119
120 if (mft_ok) {
122 mft_offset,
123 record_size,
124 "NTFS $MFT",
125 "NTFS probe",
126 trs(
"Primary MFT record signature validated"),
127 0.96});
128 }
129 if (mftmirr_ok) {
131 mftmirr_offset,
132 record_size,
133 "NTFS $MFTMirr",
134 "NTFS probe",
135 trs(
"MFT mirror record signature validated"),
136 0.93});
137 }
138 }
139
140 logs.push_back(
trs(
"NTFS probe: analysis completed."));
141 return out;
142}
bool read_at(uint64_t offset, void *buffer, size_t len)
std::string trs(const char *msgid)
uint64_t le64(const unsigned char *p)
uint16_t le16(const unsigned char *p)
uint64_t record_size_from_descriptor(int8_t descriptor, uint64_t cluster_size)
bool is_ntfs_boot_sector(const std::array< unsigned char, 512 > §or)