diff --git a/doc/NEWS b/doc/NEWS index a6fda198c55..764331fe031 100644 --- a/doc/NEWS +++ b/doc/NEWS @@ -210,7 +210,9 @@ Major changes from 1.9.0-jumbo-1 (May 2019) in this bleeding-edge version: - Add new option --catch-up=NAME, for running a new session only until it reaches the candidates tried count of a different, existing and paused - session, then exit. [magnum; 2021] + session, then exit. Unless the --no-catch-up-add option is also given, + the new input hashes (file names) used for the new session will be added + to the old session file once caught up. [magnum; 2021, 2026] - Add BestCrypt Volume Encryption V4 format. [Jean-Christophe Delaunay; 2021] diff --git a/doc/OPTIONS b/doc/OPTIONS index 7d99087ee88..2a7803efa59 100644 --- a/doc/OPTIONS +++ b/doc/OPTIONS @@ -229,9 +229,11 @@ numbers printed on the status line mean?" Limit the session to running only until it reaches the candidates tried count of a different, existing and paused session NAME. This can be used for adding more hashes to an existing job: After the new session -finishes, just concatenate the new hashes to the older session's hash -file and resume the old session. This will end up more or less as if -both sets of hashes were in the original session to start with. +finishes, the new hashes (file names) will be added to the older session +file so you can just resume that session. This will end up just as if both +sets of hashes were in the original session to start with, with no wasted +resources or time (assuming salted hashes). If you do not wish the original +session file to be touched, also add the --no-catch-up-add option. --make-charset=FILE make a charset, overwriting FILE diff --git a/src/john.c b/src/john.c index 6429867c3fd..27a5cf37495 100644 --- a/src/john.c +++ b/src/john.c @@ -1,7 +1,7 @@ /* * This file is part of John the Ripper password cracker, * Copyright (c) 1996-2024 by Solar Designer - * Copyright (c) 2009-2025, magnum + * Copyright (c) 2009-2026, magnum * Copyright (c) 2021, Claudio * Copyright (c) 2009-2018, JimF * @@ -1954,10 +1954,14 @@ static void john_done(void) mask_iter_warn); } if (event_abort && options.catchup && john_max_cands && status.cands >= john_max_cands) { + if (options.catchup_add) + rec_add_files(options.catchup); event_abort = 0; - log_event("Done catching up with '%s'", options.catchup); + log_event("Done catching up with '%s'%s", options.catchup, + options.catchup_add ? ", and added this session's password file(s) to it" : ""); if (john_main_process) - fprintf(stderr, "Done catching up with '%s'\n", options.catchup); + fprintf(stderr, "Done catching up with '%s'%s\n", options.catchup, + options.catchup_add ? ", and added this session's password file(s) to it" : ""); } if (event_abort) { char *abort_msg = (aborted_by_timer) ? diff --git a/src/options.c b/src/options.c index 70cba0a9270..ae65c7d418b 100644 --- a/src/options.c +++ b/src/options.c @@ -1,8 +1,8 @@ /* * This file is part of John the Ripper password cracker, * Copyright (c) 1996-2025 by Solar Designer - * - * ...with changes in the jumbo patch, by JimF and magnum (and various others?) + * Copyright (c) 2009-2026, magnum + * Copyright (c) 2009-2018, JimF * * Redistribution and use in source and binary forms, with or without * modification, are permitted. @@ -139,6 +139,7 @@ static struct opt_entry opt_list[] = { {"restore", FLG_RESTORE_SET, FLG_RESTORE_CHK, 0, ~FLG_RESTORE_SET & ~GETOPT_FLAGS, OPT_FMT_STR_ALLOC, &options.session}, {"session", FLG_SESSION, FLG_SESSION, FLG_CRACKING_SUP, OPT_REQ_PARAM, OPT_FMT_STR_ALLOC, &options.session}, {"catch-up", FLG_ONCE, 0, 0, OPT_REQ_PARAM, OPT_FMT_STR_ALLOC, &options.catchup}, + {"catch-up-add", FLG_ONCE, 0, 0, USUAL_REQ_CLR | OPT_TRISTATE, NULL, &options.catchup_add}, {"status", FLG_STATUS_SET, FLG_STATUS_CHK, 0, ~FLG_STATUS_SET & ~GETOPT_FLAGS, OPT_FMT_STR_ALLOC, &options.session}, {"make-charset", FLG_MAKECHR_SET, FLG_MAKECHR_CHK, 0, FLG_CRACKING_CHK | FLG_SESSION | OPT_REQ_PARAM, OPT_FMT_STR_ALLOC, &options.charset}, {"show", FLG_SHOW_SET, FLG_SHOW_CHK, 0, FLG_CRACKING_SUP | FLG_MAKECHR_CHK, OPT_FMT_STR_ALLOC, &show_uncracked_str}, @@ -332,6 +333,8 @@ JOHN_USAGE_FORK \ "--no-log Disables creation and writing to john.log file\n" \ "--bare-always-valid=Y Treat bare hashes as valid (Y/N)\n" \ "--catch-up=NAME Catch up with existing (paused) session NAME\n" \ +"--no-catch-up-add Do not add the new hashes (files) to NAME after\n" \ +" --catch-up=NAME caught up\n" \ "--config=FILE Use FILE instead of john.conf or john.ini\n" \ "--encoding=NAME Input encoding (eg. UTF-8, ISO-8859-1). See also\n" \ " doc/ENCODINGS.\n" \ @@ -623,6 +626,10 @@ void opt_init(char *name, int argc, char **argv) if (options.catchup && options.max_cands) error_msg("Can't combine --max-candidates and --catch-up options\n"); + if ((options.catchup_add != -1) && !options.catchup) + error_msg("The --%scatch-up-add option can only be used with --catch-up\n", + options.catchup_add ? "" : "no-"); + if (options.flags & FLG_STATUS_CHK) { #if OS_FORK char *rec_name_orig = rec_name; diff --git a/src/options.h b/src/options.h index 09d82605e49..f0b620e3987 100644 --- a/src/options.h +++ b/src/options.h @@ -1,8 +1,8 @@ /* * This file is part of John the Ripper password cracker, * Copyright (c) 1996-98,2003,2006,2013 by Solar Designer - * - * ...with changes in the jumbo patch, by JimF and magnum (and various others?) + * Copyright (c) 2009-2026, magnum + * Copyright (c) 2009-2015, JimF * * Redistribution and use in source and binary forms, with or without * modification, are permitted. @@ -457,6 +457,8 @@ struct options_main { int crack_status; /* --catch-up=oldsession */ char *catchup; +/* --catch-up-add (tri-state) */ + int catchup_add; #if defined(HAVE_OPENCL) || defined(HAVE_ZTEX) /* --mask-internal-target=N */ int req_int_cand_target; diff --git a/src/recovery.c b/src/recovery.c index e7048dffece..e46f343b5c7 100644 --- a/src/recovery.c +++ b/src/recovery.c @@ -1,8 +1,8 @@ /* * This file is part of John the Ripper password cracker, * Copyright (c) 1996-2003,2005,2006,2009,2010,2013,2017 by Solar Designer - * - * ...with changes in the jumbo patch, by JimF and magnum. + * Copyright (c) 2009-2026, magnum + * Copyright (c) 2009-2018, JimF * * Redistribution and use in source and binary forms, with or without * modification, are permitted. @@ -763,3 +763,82 @@ uint64_t rec_read_cands(char *session) return ret; } + +void rec_add_files(char *session) +{ + char *catchup_name; + FILE *catchup_file; + int64_t catchup_size = 0; + char suffix[1 + 20 + sizeof(RECOVERY_SUFFIX)]; + const char *sfx = RECOVERY_SUFFIX; + + if (!john_main_process && options.node_min) { + snprintf(suffix, sizeof(suffix), ".%u%s", options.node_min, RECOVERY_SUFFIX); + sfx = suffix; + } + catchup_name = path_session(session, sfx); + + if (!(catchup_file = fopen(catchup_name, "r+"))) + pexit("fopen catch-up file: '%s'", catchup_name); + +#if !(__MINGW32__ || _MSC_VER) + if (jtr_lock(fileno(catchup_file), F_SETLK, F_WRLCK, catchup_name)) + error_msg("Error: Catch-up session-file '%s' is locked\n", catchup_name); +#endif + + /* Check the original session-file's size */ + if (jtr_fseek64(catchup_file, 0, SEEK_END)) + pexit("fseek"); + if ((catchup_size = jtr_ftell64(catchup_file)) == -1) + pexit("ftell"); + if (jtr_fseek64(catchup_file, 0, SEEK_SET)) + pexit("fseek"); + if (catchup_size <= 0 || catchup_size > SIZE_MAX) + error_msg("Error: %s invalid size", catchup_name); + + /* Read original session-file data into memory */ + char *catchup_data = mem_alloc_tiny(catchup_size + 1, MEM_ALIGN_NONE); + catchup_data[catchup_size] = '\0'; + + if (fread(catchup_data, 1, (size_t)catchup_size, catchup_file) != catchup_size) { + if (ferror(catchup_file)) + pexit("fread"); + error_msg("Error: fread: Unexpected EOF in %s\n", rec_name); + } + + /* Parse session header */ + char magic[16]; + int argc, offset; + if (sscanf(catchup_data, "%15s\n%d\n%n", magic, &argc, &offset) != 2 || argc < 2) + error_msg("Error: %s invalid session-file format\n", rec_name); + + if (offset > catchup_size) + error_msg("Error: %s corrupt offset\n", rec_name); + + if (jtr_fseek64(catchup_file, 0, SEEK_SET)) + pexit("fseek"); + + /* Rewrite header */ + fprintf(catchup_file, "%s\n%d\n", magic, argc + (int)options.passwd->count); + + /* Append new file entries */ + struct list_entry *current; + for (current = options.passwd->head; current; current = current->next) + fprintf(catchup_file, "%s\n", current->data); + + /* Append original tail */ + fprintf(catchup_file, "%s", catchup_data + offset); + fclose(catchup_file); + + /* Delete this session's file */ + const char *full_rec_name = path_expand(rec_name); + if (unlink(full_rec_name)) + pexit("unlink: %s", full_rec_name); + + /* Close this session's file */ + if (rec_file) { + if (fclose(rec_file)) + pexit("fclose"); + rec_file = NULL; + } +} diff --git a/src/recovery.h b/src/recovery.h index ac431f17df0..f6582c23fb5 100644 --- a/src/recovery.h +++ b/src/recovery.h @@ -118,4 +118,10 @@ extern void rec_restore_mode(int (*restore_mode)(FILE *file)); */ extern uint64_t rec_read_cands(char *session); +/* + * Add the current session's password files to the file we caught up with. + * --catch-up=SESSION does this after successfully catching up. + */ +extern void rec_add_files(char *session); + #endif