-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdecompressor.cpp
More file actions
144 lines (118 loc) · 5.35 KB
/
Copy pathdecompressor.cpp
File metadata and controls
144 lines (118 loc) · 5.35 KB
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
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <vector>
#include <iomanip>
#include <zlib.h>
#include <openssl/sha.h>
#include <filesystem>
namespace fs = std::filesystem;
// Computes salted SHA-256 hash of filename for consistent hashed output name
std::string sha256(const std::string& input) {
const std::string salt = "$packerx_";
std::string salted = salt + input;
unsigned char hash[SHA256_DIGEST_LENGTH];
SHA256(reinterpret_cast<const unsigned char*>(salted.c_str()), salted.size(), hash);
std::ostringstream ss;
for (int i = 0; i < SHA256_DIGEST_LENGTH; ++i)
// minimum 2 width, pads with 0, 0x12 as 12
ss << std::hex << std::setw(2) << std::setfill('0') << (int)hash[i];
return ss.str();
}
// Decompresses file, extracts extension, validates CRC32 checksum
std::vector<unsigned char> decompressFile(const std::string& fullPath, std::string& recoveredExtension, bool& validCRC) {
// opens in binary -- why? so that no automatic transformation like new line thing happens.
std::ifstream file(fullPath, std::ios::binary);
if (!file) {
std::cerr << "Failed to open: " << fullPath << std::endl;
return {};
}
// Read full binary content -- reads byte by byte and {} is the end of line -- reads the file in one line without using iterators
// when you want to do read the file, without whitespace handling or any sort of parsing
std::vector<unsigned char> compressedData((std::istreambuf_iterator<char>(file)), {});
file.close();
if (compressedData.size() < 5) {
std::cerr << "File too small or corrupted." << std::endl;
return {};
}
// First byte stores extension length
size_t extLength = compressedData[0];
// Sanity check on extension and CRC presence
if (compressedData.size() < 1 + extLength + 4) {
std::cerr << "Invalid or corrupted metadata." << std::endl;
return {};
}
// Extract original file extension
recoveredExtension = std::string(compressedData.begin() + 1, compressedData.begin() + 1 + extLength);
// Extract embedded CRC32 from last 4 bytes
size_t crcPos = compressedData.size() - 4;
uLong extractedCRC = 0;
extractedCRC |= static_cast<uLong>(compressedData[crcPos]); // first 1 byte -- LSB
extractedCRC |= static_cast<uLong>(compressedData[crcPos + 1]) << 8; // first 1 byte
extractedCRC |= static_cast<uLong>(compressedData[crcPos + 2]) << 16; // first 1 byte
extractedCRC |= static_cast<uLong>(compressedData[crcPos + 3]) << 24; // first 1 byte -- MSB
// Isolate the compressed payload
std::vector<unsigned char> actualData(compressedData.begin() + 1 + extLength, compressedData.begin() + crcPos);
// Prepare buffer for decompressed output
uLongf destSize = actualData.size() * 4;
std::vector<unsigned char> uncompressedData(destSize);
int result = uncompress(uncompressedData.data(), &destSize, actualData.data(), actualData.size());
// Retry with larger buffer if necessary
if (result == Z_BUF_ERROR) {
destSize *= 2;
uncompressedData.resize(destSize);
result = uncompress(uncompressedData.data(), &destSize, actualData.data(), actualData.size());
}
if (result != Z_OK) {
std::cerr << "Decompression failed with code: " << result << std::endl;
return {};
}
// Trim buffer to actual size
uncompressedData.resize(destSize);
// Compute CRC32 of decompressed data -- same thing we did in compressfile
uLong actualCRC = crc32(0L, Z_NULL, 0);
actualCRC = crc32(actualCRC, uncompressedData.data(), uncompressedData.size());
//Debugging
std::cout<<"Debug --> Actual CRC32: "<<actualCRC<<", Extracted CRC32: "<<extractedCRC<<"\n";
// Compare computed and embedded CRCs
validCRC = (actualCRC == extractedCRC);
return uncompressedData;
}
int main(int argc, char* argv[]) {
if (argc < 2) {
std::cerr << "Usage: decompress <original_filename>" << std::endl;
return 1;
}
// Extract filename from argument and compute salted SHA-256
std::string Filename = argv[1];
std::string ogFilename = fs::path(Filename).stem().string();
std::string hashedName = sha256(ogFilename);
// Locate compressed file
std::string inputFile = "compressed_output/" + hashedName + ".bin";
std::string recoveredExt;
bool crcMatch = false;
// Perform decompression
std::vector<unsigned char> decompressedData = decompressFile(inputFile, recoveredExt, crcMatch);
if (decompressedData.empty()) {
std::cerr << "Decompression failed." << std::endl;
return 1;
}
// Abort if CRC32 check fails
if (!crcMatch) {
std::cerr << "CRC32 check FAILED. File may be corrupted." << std::endl;
return 1;
}
// Build output path
std::string outPath = "decompressed_output/" + ogFilename + "_restored." + recoveredExt;
// Save restored file
std::ofstream out(outPath, std::ios::binary);
out.write(reinterpret_cast<const char*>(decompressedData.data()), decompressedData.size());
out.close();
// Final status
std::cout << "Decompression successful!" << std::endl;
std::cout << "Restored file: " << outPath << std::endl;
std::cout << "Extension: ." << recoveredExt << std::endl;
std::cout << "Size: " << decompressedData.size() << " bytes" << std::endl;
return 0;
}