30 {
"JPEG",
".jpg", {0xFF,0xD8,0xFF}, {0xFF,0xD9},
true},
31 {
"PNG",
".png", {0x89,0x50,0x4E,0x47,0x0D,0x0A,0x1A,0x0A}, {0x49,0x45,0x4E,0x44,0xAE,0x42,0x60,0x82},
true},
32 {
"PDF",
".pdf", {0x25,0x50,0x44,0x46,0x2D}, {0x25,0x25,0x45,0x4F,0x46},
true},
33 {
"ZIP",
".zip", {0x50,0x4B,0x03,0x04}, {0x50,0x4B,0x05,0x06},
true},
34 {
"GIF",
".gif", {0x47,0x49,0x46,0x38}, {0x3B},
true},
35 {
"MP4",
".mp4", {0x00,0x00,0x00}, {
'f',
't',
'y',
'p'},
false}
39bool starts_with_at(
const std::vector<unsigned char>& buf,
size_t pos,
const std::vector<unsigned char>& pat) {
40 if (pos + pat.size() > buf.size())
return false;
41 return std::equal(pat.begin(), pat.end(), buf.begin() +
static_cast<std::ptrdiff_t
>(pos));
49bool copy_range(
DiskReader& reader, uint64_t offset, uint64_t size,
const fs::path& out_path, std::string& error) {
50 std::ofstream out(out_path, std::ios::binary);
52 error =
trs(
"Unable to create output file") + std::string(
": ") + out_path.string();
56 std::vector<unsigned char> buffer(1024 * 1024);
57 uint64_t remaining = size;
58 uint64_t cursor = offset;
59 while (remaining > 0) {
60 const size_t chunk =
static_cast<size_t>(std::min<uint64_t>(buffer.size(), remaining));
61 if (!reader.
read_at(cursor, buffer.data(), chunk)) {
62 error =
trs(
"Read error while exporting carved file");
65 out.write(
reinterpret_cast<const char*
>(buffer.data()),
static_cast<std::streamsize
>(chunk));
67 error =
trs(
"Write error while exporting carved file");
77 uint64_t range_offset,
79 const std::vector<Pattern>& pats,
80 const std::string& output_dir,
81 std::vector<CarvedFile>& carved,
82 std::vector<std::string>& logs,
84 if (range_size == 0)
return true;
86 const uint64_t file_size = reader.
size();
87 if (range_offset >= file_size)
return true;
88 const uint64_t range_end = std::min<uint64_t>(file_size, range_offset + range_size);
90 std::vector<unsigned char> buffer(
WINDOW);
92 uint64_t base = range_offset;
93 while (base < range_end) {
94 const size_t to_read =
static_cast<size_t>(std::min<uint64_t>(
WINDOW, range_end - base));
95 if (!reader.
read_at(base, buffer.data(), to_read)) {
96 error =
trs(
"Read error during file carving");
99 std::vector<unsigned char> chunk(buffer.begin(), buffer.begin() +
static_cast<std::ptrdiff_t
>(to_read));
101 for (
size_t i = 0; i < chunk.size(); ++i) {
102 for (
const auto& p : pats) {
104 if (std::string(p.type) ==
"MP4") {
109 if (!match)
continue;
111 const uint64_t abs_off = base + i;
112 if (abs_off >= range_end)
continue;
113 if (!carved.empty() && abs_off < carved.back().offset + carved.back().size) {
117 uint64_t found_size = 0;
118 if (p.footer_required) {
119 const uint64_t search_limit = std::min<uint64_t>(range_end - abs_off,
MAX_FILE_SIZE);
120 std::vector<unsigned char> probe(
static_cast<size_t>(search_limit));
121 if (!reader.
read_at(abs_off, probe.data(), probe.size()))
continue;
122 for (
size_t j = p.header.size(); j + p.footer.size() <= probe.size(); ++j) {
123 if (std::equal(p.footer.begin(), p.footer.end(), probe.begin() +
static_cast<std::ptrdiff_t
>(j))) {
124 found_size = j + p.footer.size();
128 if (found_size == 0)
continue;
130 found_size = std::min<uint64_t>(16ull *
MiB, range_end - abs_off);
133 std::ostringstream name;
134 name << std::string(p.type) <<
'_' << std::setw(12) << std::setfill(
'0') << abs_off << p.ext;
135 fs::path out_path = fs::path(output_dir) / name.str();
136 std::string copy_error;
137 if (!
copy_range(reader, abs_off, found_size, out_path, copy_error)) {
147 p.footer_required ? 0.72 : 0.45,
148 p.footer_required ?
trs(
"Recovered by header/footer carving") :
trs(
"Recovered by header-based carving"),
151 logs.push_back(
trs(
"Recovered carved file:") + std::string(
" ") + out_path.string());
155 if (base + to_read >= range_end)
break;