From 82dff7e53b3d5e92693de2189f6f36ea98782128 Mon Sep 17 00:00:00 2001 From: gdesmar <75089569+gdesmar@users.noreply.github.com> Date: Mon, 26 Aug 2024 14:20:36 +0000 Subject: [PATCH] Add Crack option for password-protected files --- src/cli/extract.cpp | 59 +++++++++++++++++++++++++++++++++++++-------- src/cli/extract.hpp | 1 + src/cli/main.cpp | 2 ++ 3 files changed, 52 insertions(+), 10 deletions(-) diff --git a/src/cli/extract.cpp b/src/cli/extract.cpp index 098fc77c..3da7d944 100644 --- a/src/cli/extract.cpp +++ b/src/cli/extract.cpp @@ -25,6 +25,8 @@ #include #include #include +#include +#include #include #include #include @@ -918,6 +920,17 @@ void create_output_directory(const extract_options & o) { } +bool test_password(const std::string & password, const setup::info & info) { + + if(info.header.options & setup::header::Password) { + crypto::hasher checksum(info.header.password.type); + checksum.update(info.header.password_salt.c_str(), info.header.password_salt.length()); + checksum.update(password.c_str(), password.length()); + return (checksum.finalize() == info.header.password); + } + return false; +} + } // anonymous namespace void process_file(const fs::path & installer, const extract_options & o) { @@ -1025,6 +1038,37 @@ void process_file(const fs::path & installer, const extract_options & o) { throw format_error(oss.str()); } + if (o.crack) { + if (!(info.header.options & setup::header::EncryptionUsed)) { + log_warning << "File is not password protected, cannot crack"; + return; + } + std::string compiledcode = info.header.compiled_code; + std::regex possible_password_regex("(([\x20-\x7e]\\0?){6,})"); + auto passwords_begin = std::sregex_iterator(compiledcode.begin(), compiledcode.end(), possible_password_regex); + auto passwords_end = std::sregex_iterator(); + std::vector possible_passwords; + + for (std::sregex_iterator i = passwords_begin; i != passwords_end; ++i) + { + possible_passwords.push_back((*i).str()); + } + + std::string password; + std::regex null_re("\\0"); + for (std::vector::reverse_iterator riter = possible_passwords.rbegin(); riter != possible_passwords.rend(); ++riter) { + std::string non_null_password = std::regex_replace(*riter, null_re, ""); + util::from_utf8(non_null_password, password, info.codepage); + if (test_password(password, info)) { + std::cout << "Password found: " << non_null_password << '\n'; + return; + } + password.clear(); + } + std::cout << "Password not found, think of opening an issue if you have an idea why\n"; + return; + } + if(o.gog_galaxy && (o.list || o.test || o.extract || o.list_languages || o.list_components)) { gog::parse_galaxy_files(info, o.gog); } @@ -1038,17 +1082,12 @@ void process_file(const fs::path & installer, const extract_options & o) { } } else { util::from_utf8(o.password, password, info.codepage); - if(info.header.options & setup::header::Password) { - crypto::hasher checksum(info.header.password.type); - checksum.update(info.header.password_salt.c_str(), info.header.password_salt.length()); - checksum.update(password.c_str(), password.length()); - if(checksum.finalize() != info.header.password) { - if(o.check_password) { - throw std::runtime_error("Incorrect password provided"); - } - log_error << "Incorrect password provided"; - password.clear(); + if (!test_password(password, info)) { + if(o.check_password) { + throw std::runtime_error("Incorrect password provided"); } + log_error << "Incorrect password provided"; + password.clear(); } #if !INNOEXTRACT_HAVE_ARC4 if((o.extract || o.test) && (info.header.options & setup::header::EncryptionUsed)) { diff --git a/src/cli/extract.hpp b/src/cli/extract.hpp index e37f030a..edf6a8a3 100644 --- a/src/cli/extract.hpp +++ b/src/cli/extract.hpp @@ -60,6 +60,7 @@ struct extract_options { #ifdef DEBUG bool dump_headers; //!< Dump setup headers #endif + bool crack; //!< Crack passworded file bool list; //!< List files bool test; //!< Test files (but don't extract) bool extract; //!< Extract files diff --git a/src/cli/main.cpp b/src/cli/main.cpp index d9e3edc5..217741f8 100644 --- a/src/cli/main.cpp +++ b/src/cli/main.cpp @@ -129,6 +129,7 @@ int main(int argc, char * argv[]) { po::options_description action("Actions"); action.add_options() + ("crack", "Crack passworded file") ("test,t", "Only verify checksums, don't write anything") ("extract,e", "Extract files (default action)") ("list,l", "Only list files, don't write anything") @@ -261,6 +262,7 @@ int main(int argc, char * argv[]) { bool explicit_list = (options.count("list") != 0); o.list = explicit_list || o.list_sizes || o.list_checksums; o.extract = (options.count("extract") != 0); + o.crack = (options.count("crack") != 0); o.test = (options.count("test") != 0); o.list_languages = (options.count("list-languages") != 0); o.list_components = (options.count("list-components") != 0);