From f90a6ada5e1f1778fe9178a6378b2dd2451db77c Mon Sep 17 00:00:00 2001 From: CarterPerez-dev Date: Sun, 22 Mar 2026 10:39:07 -0400 Subject: [PATCH] feat: CLI with Boost.program_options and hash type dispatch Full argument parsing for all attack modes, hash auto-detection, salt support, thread count, and JSON output. Dispatch resolves hasher + attack at compile time via template instantiation. --- PROJECTS/beginner/hash-cracker/main.cpp | 180 +++++++++++++++++++++++- 1 file changed, 178 insertions(+), 2 deletions(-) diff --git a/PROJECTS/beginner/hash-cracker/main.cpp b/PROJECTS/beginner/hash-cracker/main.cpp index e696711c..68380d53 100644 --- a/PROJECTS/beginner/hash-cracker/main.cpp +++ b/PROJECTS/beginner/hash-cracker/main.cpp @@ -1,6 +1,182 @@ // ©AngelaMos | 2026 // main.cpp -int main() { - return 0; +#include +#include +#include +#include +#include +#include "src/attack/BruteForceAttack.hpp" +#include "src/attack/DictionaryAttack.hpp" +#include "src/attack/RuleAttack.hpp" +#include "src/config/Config.hpp" +#include "src/core/Concepts.hpp" +#include "src/core/Engine.hpp" +#include "src/hash/HashDetector.hpp" +#include "src/hash/MD5Hasher.hpp" +#include "src/hash/SHA1Hasher.hpp" +#include "src/hash/SHA256Hasher.hpp" +#include "src/hash/SHA512Hasher.hpp" + +namespace po = boost::program_options; + +static std::string build_charset(const std::string& spec) { + std::string result; + + auto has = [&](std::string_view token) { + return spec.find(token) != std::string::npos; + }; + + if (has("lower")) { result += config::CHARSET_LOWER; } + if (has("upper")) { result += config::CHARSET_UPPER; } + if (has("digits")) { result += config::CHARSET_DIGITS; } + if (has("special")) { result += config::CHARSET_SPECIAL; } + + if (result.empty()) { + result += config::CHARSET_LOWER; + result += config::CHARSET_DIGITS; + } + + return result; +} + +template +static auto dispatch_attack(const CrackConfig& cfg) + -> std::expected { + if (cfg.bruteforce) { + return Engine::crack(cfg); + } + if (cfg.use_rules) { + return Engine::crack(cfg); + } + return Engine::crack(cfg); +} + +static auto dispatch_hasher(HashType type, const CrackConfig& cfg) + -> std::expected { + switch (type) { + case HashType::MD5: return dispatch_attack(cfg); + case HashType::SHA1: return dispatch_attack(cfg); + case HashType::SHA256: return dispatch_attack(cfg); + case HashType::SHA512: return dispatch_attack(cfg); + } + return std::unexpected(CrackError::UnsupportedAlgorithm); +} + +static void write_json_result(const std::string& path, + const CrackResult& result) { + auto* f = std::fopen(path.c_str(), "w"); + if (!f) { return; } + + std::fprintf(f, + "{\n" + " \"plaintext\": \"%s\",\n" + " \"hash\": \"%s\",\n" + " \"algorithm\": \"%s\",\n" + " \"elapsed_seconds\": %.4f,\n" + " \"candidates_tested\": %zu,\n" + " \"hashes_per_second\": %.2f\n" + "}\n", + result.plaintext.c_str(), + result.hash.c_str(), + result.algorithm.c_str(), + result.elapsed_seconds, + result.candidates_tested, + result.hashes_per_second); + + std::fclose(f); +} + +int main(int argc, char* argv[]) { + po::options_description desc("hashcracker - Multi-threaded hash cracking tool"); + desc.add_options() + ("help,h", "Show help message") + ("hash", po::value(), "Target hash to crack") + ("type", po::value()->default_value("auto"), + "Hash type: md5, sha1, sha256, sha512, auto") + ("wordlist,w", po::value(), "Path to wordlist file") + ("bruteforce,b", "Use brute-force attack mode") + ("charset", po::value()->default_value("lower,digits"), + "Character sets: lower,upper,digits,special") + ("max-length", po::value()->default_value( + config::DEFAULT_MAX_BRUTE_LENGTH), "Max password length for brute-force") + ("rules,r", "Apply mutation rules to dictionary words") + ("chain-rules", "Chain mutation rules in combination") + ("salt", po::value(), "Salt value to prepend/append") + ("salt-position", po::value()->default_value("prepend"), + "Salt position: prepend or append") + ("threads,t", po::value()->default_value( + config::DEFAULT_THREAD_COUNT), "Thread count (0 = auto)") + ("output,o", po::value(), "Write JSON result to file"); + + po::variables_map vm; + try { + po::store(po::parse_command_line(argc, argv, desc), vm); + po::notify(vm); + } catch (const po::error& e) { + std::println(stderr, "Error: {}", e.what()); + return 1; + } + + if (vm.count("help") || !vm.count("hash")) { + std::cout << desc << std::endl; + return vm.count("help") ? 0 : 1; + } + + CrackConfig cfg; + cfg.target_hash = vm["hash"].as(); + cfg.hash_type = vm["type"].as(); + cfg.thread_count = vm["threads"].as(); + cfg.bruteforce = vm.count("bruteforce") > 0; + cfg.use_rules = vm.count("rules") > 0; + cfg.chain_rules = vm.count("chain-rules") > 0; + cfg.max_length = vm["max-length"].as(); + + if (vm.count("wordlist")) { + cfg.wordlist_path = vm["wordlist"].as(); + } + if (vm.count("salt")) { + cfg.salt = vm["salt"].as(); + } + cfg.salt_position = vm["salt-position"].as(); + if (vm.count("output")) { + cfg.output_path = vm["output"].as(); + } + + if (cfg.bruteforce) { + cfg.charset = build_charset(vm["charset"].as()); + } else if (cfg.wordlist_path.empty()) { + std::println(stderr, "Error: --wordlist required for dictionary/rule attacks"); + return 1; + } + + HashType hash_type; + if (cfg.hash_type == "auto") { + auto detected = HashDetector::detect(cfg.target_hash); + if (!detected.has_value()) { + std::println(stderr, "Error: {}", + crack_error_message(detected.error())); + return 1; + } + hash_type = *detected; + } else if (cfg.hash_type == "md5") { + hash_type = HashType::MD5; + } else if (cfg.hash_type == "sha1") { + hash_type = HashType::SHA1; + } else if (cfg.hash_type == "sha256") { + hash_type = HashType::SHA256; + } else if (cfg.hash_type == "sha512") { + hash_type = HashType::SHA512; + } else { + std::println(stderr, "Error: Unknown hash type '{}'", cfg.hash_type); + return 1; + } + + auto result = dispatch_hasher(hash_type, cfg); + + if (result.has_value() && !cfg.output_path.empty()) { + write_json_result(cfg.output_path, *result); + } + + return result.has_value() ? 0 : 1; }