2
Fork 0

feat: Implement brute-forcing the raw size

Oodle must know the size of the uncompressed data when decompressing
data. If you don't know that size, the only option is to guess.
And since it needs to be the exact number, you simply have to try
every number.
This commit is contained in:
Lucas Schwiderski 2022-10-20 17:47:42 +02:00
parent 36edf93538
commit 2bc7751d3b
Signed by: lucas
GPG key ID: AA12679AAA6DF4D8

View file

@ -25,15 +25,21 @@ const char* LIB_FILE = "oo2core_8_win64.dll";
typedef decltype(OodleLZ_Decompress)* decompress_func; typedef decltype(OodleLZ_Decompress)* decompress_func;
typedef decltype(OodleCore_Plugins_SetPrintf)* setprintf_func; typedef decltype(OodleCore_Plugins_SetPrintf)* setprintf_func;
#define OK 0
#define GENERIC_ERROR -1
#define DECOMPRESS_ERROR -2
void usage() { void usage() {
std::cerr << std::cerr <<
"Usage: oodle-cli [OPTIONS] [--] <in_file> <out_file> <uncompressed_size>\n" "Usage: oodle-cli [OPTIONS] [--] <in_file> <out_file> <uncompressed_size>\n"
"Decompress a <in_file> to <out_file> using the Oodle algorithm.\n" "Decompress a <in_file> to <out_file> using the Oodle algorithm.\n"
"The size of the uncompressed data must be known.\n" "The size of the uncompressed data must be known. If 0 is given, the tool will\n"
"attempt to brute-force the correct number by incrementing from the compressed size.\n"
"\n" "\n"
"Options:\n" "Options:\n"
" -v Increase Oodle's verbosity. May be specified up to three times.\n" " -v Increase Oodle's verbosity. May be specified up to three times.\n"
" -c Only check if the library can be found and used.\n" " -c Only check if the library can be found and used.\n"
" --guess <number> When guessing the uncompressed size, a maximum number as multiple of the compressed size prevents an infinite loop. This changes the factor. Defaults to 10."
"\n" "\n"
"The following environmental variables are recognized:\n" "The following environmental variables are recognized:\n"
" DLL_SEARCH_PATH: A color (':') separated list of directories to search for the Oodle library. May be relative to the current working directory.\n"; " DLL_SEARCH_PATH: A color (':') separated list of directories to search for the Oodle library. May be relative to the current working directory.\n";
@ -60,10 +66,51 @@ void printf_callback(int verboseLevel, const char* file, int line, const char* f
vfprintf(stdout, fmt, args); vfprintf(stdout, fmt, args);
} }
int do_decompress(decompress_func decompress, std::vector<char> compressed_buffer, std::string out_name, size_t raw_buffer_size, OodleLZ_Verbosity verbosity) {
std::vector<char> raw_buffer(raw_buffer_size);
intptr_t res = decompress(
compressed_buffer.data(),
compressed_buffer.size(),
raw_buffer.data(),
raw_buffer_size,
OodleLZ_FuzzSafe_Yes,
OodleLZ_CheckCRC_No,
verbosity,
nullptr,
0,
nullptr,
nullptr,
nullptr,
0,
OodleLZ_Decode_Unthreaded
);
if (res != raw_buffer_size) {
return DECOMPRESS_ERROR;
}
std::ofstream out_file(out_name, std::ios::binary);
if (!out_file) {
std::cerr << "ERROR: Failed to open output file!\n";
return GENERIC_ERROR;
}
out_file.write(raw_buffer.data(), res);
if (out_file.fail()) {
std::cerr << "ERROR: Failed to write output file!\n";
return GENERIC_ERROR;
}
return OK;
}
int main(int argc, char* argv[]) int main(int argc, char* argv[])
{ {
int verbosity = 0; int verbosity = 0;
bool check_lib = FALSE; bool check_lib = FALSE;
int guess_factor = 10;
int i = 1; int i = 1;
for (; i < argc; i++) { for (; i < argc; i++) {
@ -83,6 +130,10 @@ int main(int argc, char* argv[])
else if (strcmp(arg, "-v") == 0) { else if (strcmp(arg, "-v") == 0) {
verbosity++; verbosity++;
} }
else if (strcmp(arg, "--guess") == 0) {
i++;
guess_factor = std::stoi(argv[i]);
}
else { else {
std::cerr << "WARN: Unknown option '" << arg << "'!\n\n"; std::cerr << "WARN: Unknown option '" << arg << "'!\n\n";
} }
@ -174,36 +225,41 @@ int main(int argc, char* argv[])
return 1; return 1;
} }
std::vector<char> raw_buffer(raw_buffer_size); if (raw_buffer_size > 0) {
int res = do_decompress(decompress, compressed_buffer, out_name, raw_buffer_size, (OodleLZ_Verbosity)verbosity);
int res = decompress( if (res == DECOMPRESS_ERROR) {
compressed_buffer.data(), std::cerr << "ERROR: Failed to decompress!\n";
compressed_buffer_size,
raw_buffer.data(),
raw_buffer_size,
OodleLZ_FuzzSafe_Yes,
OodleLZ_CheckCRC_No,
(OodleLZ_Verbosity)verbosity,
nullptr,
0,
nullptr,
nullptr,
nullptr,
0,
OodleLZ_Decode_Unthreaded
);
out_file.write(raw_buffer.data(), res);
if (out_file.fail()) {
std::cerr << "Failed to write output file!\n";
return 1; return 1;
} }
else if (res == GENERIC_ERROR) {
if (res != raw_buffer_size) {
std::cerr << "Failed to decompress. Expected " << raw_buffer_size << " bytes, got " << res << "!\n";
return 1; return 1;
} }
}
else {
size_t guessed_size = compressed_buffer.size();
// I need some maximum value to avoid infinite loops. The factor is configurable.
size_t max = guessed_size * guess_factor;
while (true) {
int res = do_decompress(decompress, compressed_buffer, out_name, guessed_size, (OodleLZ_Verbosity)verbosity);
if (res == GENERIC_ERROR) {
return 1;
}
else if (res == OK) {
std::cout << "INFO: Found decompressed size as " << guessed_size << "\n";
break;
}
guessed_size++;
if (guessed_size > max) {
std::cerr << "ERROR: Exceeded maximum value for guessing uncompressed size!\n";
return 1;
}
}
}
std::cout << "INFO: Done!\n"; std::cout << "INFO: Done!\n";
} }