Skip to content
API Documentation
BastionGuard™
anonymous_namespace{file_carver.cpp} Namespace Reference

Classes

struct  Pattern

Functions

std::vector< Pattern > patterns ()
bool starts_with_at (const std::vector< unsigned char > &buf, size_t pos, const std::vector< unsigned char > &pat)
bool looks_like_mp4 (const std::vector< unsigned char > &buf, size_t pos)
bool copy_range (DiskReader &reader, uint64_t offset, uint64_t size, const fs::path &out_path, std::string &error)
bool carve_one_range (DiskReader &reader, uint64_t range_offset, uint64_t range_size, const std::vector< Pattern > &pats, const std::string &output_dir, std::vector< CarvedFile > &carved, std::vector< std::string > &logs, std::string &error)

Variables

constexpr uint64_t MiB = 1024ull * 1024ull
constexpr size_t WINDOW = 2 * 1024 * 1024
constexpr uint64_t MAX_FILE_SIZE = 128ull * 1024ull * 1024ull

Function Documentation

◆ carve_one_range()

bool anonymous_namespace{file_carver.cpp}::carve_one_range ( DiskReader & reader,
uint64_t range_offset,
uint64_t range_size,
const std::vector< Pattern > & pats,
const std::string & output_dir,
std::vector< CarvedFile > & carved,
std::vector< std::string > & logs,
std::string & error )

Definition at line 76 of file file_carver.cpp.

83 {
84 if (range_size == 0) return true;
85
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);
89
90 std::vector<unsigned char> buffer(WINDOW);
91
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");
97 return false;
98 }
99 std::vector<unsigned char> chunk(buffer.begin(), buffer.begin() + static_cast<std::ptrdiff_t>(to_read));
100
101 for (size_t i = 0; i < chunk.size(); ++i) {
102 for (const auto& p : pats) {
103 bool match = false;
104 if (std::string(p.type) == "MP4") {
105 match = looks_like_mp4(chunk, i);
106 } else {
107 match = starts_with_at(chunk, i, p.header);
108 }
109 if (!match) continue;
110
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) {
114 continue;
115 }
116
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();
125 break;
126 }
127 }
128 if (found_size == 0) continue;
129 } else {
130 found_size = std::min<uint64_t>(16ull * MiB, range_end - abs_off);
131 }
132
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)) {
138 error = copy_error;
139 return false;
140 }
141
142 carved.push_back(CarvedFile{
143 p.type,
144 p.ext,
145 abs_off,
146 found_size,
147 p.footer_required ? 0.72 : 0.45,
148 p.footer_required ? trs("Recovered by header/footer carving") : trs("Recovered by header-based carving"),
149 out_path.string()
150 });
151 logs.push_back(trs("Recovered carved file:") + std::string(" ") + out_path.string());
152 break;
153 }
154 }
155 if (base + to_read >= range_end) break;
156 base += MiB;
157 }
158
159 return true;
160}
bool read_at(uint64_t offset, void *buffer, size_t len)
uint64_t size() const
std::string trs(const char *msgid)
Definition i18n.cpp:22
bool copy_range(DiskReader &reader, uint64_t offset, uint64_t size, const fs::path &out_path, std::string &error)
bool starts_with_at(const std::vector< unsigned char > &buf, size_t pos, const std::vector< unsigned char > &pat)
bool looks_like_mp4(const std::vector< unsigned char > &buf, size_t pos)

References carve_one_range(), copy_range(), looks_like_mp4(), MAX_FILE_SIZE, MiB, DiskReader::read_at(), DiskReader::size(), starts_with_at(), trs(), and WINDOW.

Referenced by carve_one_range().

◆ copy_range()

bool anonymous_namespace{file_carver.cpp}::copy_range ( DiskReader & reader,
uint64_t offset,
uint64_t size,
const fs::path & out_path,
std::string & error )

Definition at line 49 of file file_carver.cpp.

49 {
50 std::ofstream out(out_path, std::ios::binary);
51 if (!out) {
52 error = trs("Unable to create output file") + std::string(": ") + out_path.string();
53 return false;
54 }
55
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");
63 return false;
64 }
65 out.write(reinterpret_cast<const char*>(buffer.data()), static_cast<std::streamsize>(chunk));
66 if (!out) {
67 error = trs("Write error while exporting carved file");
68 return false;
69 }
70 cursor += chunk;
71 remaining -= chunk;
72 }
73 return true;
74}

References copy_range(), DiskReader::read_at(), and trs().

Referenced by carve_one_range(), and copy_range().

◆ looks_like_mp4()

bool anonymous_namespace{file_carver.cpp}::looks_like_mp4 ( const std::vector< unsigned char > & buf,
size_t pos )

Definition at line 44 of file file_carver.cpp.

44 {
45 if (pos + 8 > buf.size()) return false;
46 return buf[pos+4] == 'f' && buf[pos+5] == 't' && buf[pos+6] == 'y' && buf[pos+7] == 'p';
47}

References looks_like_mp4().

Referenced by carve_one_range(), and looks_like_mp4().

◆ patterns()

std::vector< Pattern > anonymous_namespace{file_carver.cpp}::patterns ( )

Definition at line 28 of file file_carver.cpp.

28 {
29 return {
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}
36 };
37}

References patterns().

Referenced by patterns().

◆ starts_with_at()

bool anonymous_namespace{file_carver.cpp}::starts_with_at ( const std::vector< unsigned char > & buf,
size_t pos,
const std::vector< unsigned char > & pat )

Definition at line 39 of file file_carver.cpp.

39 {
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));
42}

References starts_with_at().

Referenced by carve_one_range(), and starts_with_at().

Variable Documentation

◆ MAX_FILE_SIZE

uint64_t anonymous_namespace{file_carver.cpp}::MAX_FILE_SIZE = 128ull * 1024ull * 1024ull
constexpr

Definition at line 18 of file file_carver.cpp.

Referenced by carve_one_range().

◆ MiB

uint64_t anonymous_namespace{file_carver.cpp}::MiB = 1024ull * 1024ull
constexpr

Definition at line 16 of file file_carver.cpp.

Referenced by carve_one_range().

◆ WINDOW

size_t anonymous_namespace{file_carver.cpp}::WINDOW = 2 * 1024 * 1024
constexpr

Definition at line 17 of file file_carver.cpp.

Referenced by carve_one_range().