From a92ab33ad54e8b4dc6daf64c44fd77bf0b2bd8fe Mon Sep 17 00:00:00 2001 From: "R. Yushaev" <44146334+Naufragous@users.noreply.github.com> Date: Thu, 20 Dec 2018 18:03:48 +0100 Subject: [PATCH] Add modularized test.pl --- tools/test.pl | 211 +++++++++++++++++++++++++++++++++++ tools/test_modules/README.md | 9 ++ 2 files changed, 220 insertions(+) create mode 100755 tools/test.pl create mode 100644 tools/test_modules/README.md diff --git a/tools/test.pl b/tools/test.pl new file mode 100755 index 000000000..548dde66e --- /dev/null +++ b/tools/test.pl @@ -0,0 +1,211 @@ +#!/usr/bin/env perl + +## +## Author......: See docs/credits.txt +## License.....: MIT +## + +use strict; +use warnings; + +use Data::Types qw (is_count is_whole); +use File::Basename; +use FindBin; + +# allows require by filename +use lib "$FindBin::Bin/test_modules"; + +my $TYPES = [ 'single', 'passthrough', 'verify' ]; + +my $TYPE = shift @ARGV; +my $MODE = shift @ARGV; + +is_in_array ($TYPE, $TYPES) or usage_exit (); + +is_whole ($MODE) or die "Mode must be a number\n"; + +eval { require "m$MODE.pm" } or die "Could not load test module:\n$@"; + +if ($TYPE eq 'single') +{ + single (@ARGV); +} +elsif ($TYPE eq 'passthrough') +{ + passthrough (); +} +elsif ($TYPE eq "verify") +{ + usage_exit () if scalar @ARGV != 3; + + verify (@ARGV); +} +else +{ + usage_exit (); +} + +sub single +{ + exists &{module_generate_hash} or die "Module function not found\n"; + + my $len = shift; + + undef $len unless is_count ($len); + + my $format = "echo -n %-32s | ./hashcat \${OPTS} -a0 -m%d '%s'\n"; + + for (my $i = 1; $i <= 32; $i++) + { + my $cur_len = $len // $i; + + my $word = random_numeric_string ($cur_len); + my $hash = module_generate_hash ($word); + + next unless defined $hash; + + print sprintf ($format, $word, $MODE, $hash); + } +} + +sub passthrough +{ + exists &{module_generate_hash} or die "Module function not found\n"; + + while (<>) + { + chomp $_; + + next if length $_ > 256; + + my $hash = module_generate_hash ($_); + + print "$hash\n"; + } +} + +sub verify +{ + exists &{module_verify_hash} or die "Module function not found\n"; + + my $hashes_file = shift; + my $cracks_file = shift; + my $out_file = shift; + + open (IN, '<', $hashes_file) or die "$hashes_file: $!\n"; + + my $hashlist; + + while () + { + s/[\n\r]*$//; + + push (@{$hashlist}, $_); + } + + close IN; + + open (IN, '<', $cracks_file) or die "$cracks_file: $!\n"; + open (OUT, '>', $out_file ) or die "$out_file: $!\n"; + + while () + { + s/[\n\r]*$//; + + my $hash = module_verify_hash ($_); + + next unless defined $hash; + + next unless is_in_array ($hash, $hashlist); + + print OUT "$_\n"; + } + + close IN; + close OUT; +} + +sub is_in_array +{ + my $value = shift; + my $array = shift; + + return unless defined $value; + return unless defined $array; + + return grep { $_ eq $value } @{$array}; +} + +sub pack_if_HEX_notation +{ + my $string = shift; + + return unless defined $string; + + if ($string =~ m/^\$HEX\[[0-9a-fA-F]*\]$/) + { + return pack ("H*", substr ($string, 5, -1)); + } + + return $string; +} + +sub random_bytes +{ + my $count = shift; + + return pack ("H*", random_hex_string (2 * $count)); +} + +sub random_hex_string +{ + my $count = shift; + + return if ! is_count ($count); + + my $string; + + $string .= sprintf("%x", rand 16) for (1 .. $count); + + return $string; +} + +sub random_numeric_string +{ + my $count = shift; + + return if ! is_count ($count); + + my $string; + + $string .= sprintf("%d", rand 10) for (1 .. $count); + + return $string; +} + +sub usage_exit +{ + my $f = basename ($0); + + print "\n" + . "Usage:\n" + . " $f single [length]\n" + . " $f passthrough \n" + . " $f verify \n" + . "\n" + . "Single:\n" + . " Generates up to 32 hashes of random numbers of incrementing length, or up to 32\n" + . " hashes of random numbers of exact [length]. Writes shell commands to stdout that\n" + . " can be processed by the test.sh script.\n" + . "\n" + . "Passthrough:\n" + . " Generates hashes for strings entered via stdin and prints them to stdout.\n" + . "\n" + . "Verify:\n" + . " Reads a list of hashes from and a list of hash:password pairs from\n" + . " . Hashes every password and compares the hash to the corresponding\n" + . " entry in the . If the hashes match and the hash is present in the\n" + . " list from , it will be written to the .\n"; + + exit 1; +} diff --git a/tools/test_modules/README.md b/tools/test_modules/README.md new file mode 100644 index 000000000..3732b42d6 --- /dev/null +++ b/tools/test_modules/README.md @@ -0,0 +1,9 @@ +### Hashcat test modules ### + +Each module provides the two functions `module_generate_hash` and `module_verify_hash`. The first parameter to `module_generate_hash` is the password, which can be either in ASCII or binary (packed) form. The `module_verify_hash` function accepts a line from the cracks file, without the newline characters. + +During `single` and `passthrough` tests the `module_generate_hash` function must provide random values (e.g. salt) for hash generation if necessary. The test.pl script offers a few handy functions like `random_hex_string`, `random_numeric_string` and `random_bytes`. You can implement your own salt generation functions, if your mode has specific requirements. + +During `verify` tests the `module_verify_hash` function must parse the hash:password line and to calculate a hash by passing all necessary data to `module_generate_hash`. How you pass it is up to you, as long as the first parameter is the password. + +**Important**: You have to call `pack_if_HEX_notation` as soon as you have parsed the password, or your tests will fail on passwords in the `$HEX[...]` format.