in reply to Brute Force Algorithm with Perl reg exprs?

You mentioned you wanted to recursion. Recursion (or more specifically, a stack) is the tool of choice to use if you want to do a depth-first search. However, a depth-first search is not optimal here, since people usually choose passwords much smaller then the maximum length. For example,

# Depth-first search # Max length = 20 1234 1234a 1234aa 1234aaa 1234aaaa ... 1234aaaaaaaaaaaaaaaa 1234b 1234bb ...

A breadth-first search would be more appropriate. For example,

# Breadth-first search 1234 1234a 1234b 1234c ... 12348 12349 1234aa 1234ab 1234ac ...

Breadth-first search uses a queue, not a stack, so recursion is not appropriate. Regexps are also inappropriate because they strength is in backtracking, which is done using stacks. So let's look at a breadth-first solution:

use strict; use warnings; sub crack { my $hint = shift; my $target = shift; my $salt = substr($target, 0, 2); my @chars = @_; my @to_process = ( '' ); my $passwd; while (@to_process) { my $pad = shift(@to_process); push(@to_process, $pad . $_) foreach @chars; for my $pos (0..length($pad)) { $passwd = $pad; substr($passwd, $pos, 0, $hint); return $passwd if crypt($passwd, $salt) eq $target; } } } my @chars = ('a'..'z', 'A'..'Z', '0'..'9'); print crack('12', crypt('d12e', 'AA'), @chars), "\n"; print crack('1234', crypt('aa12345', 'AA'), @chars), "\n";

Unfortunately, the above takes way too much memory. We need to find an interator which generates the next item to process on the fly. Since the to-process list is a simple incrementing sequence, it's not only possible to generate the next item on the fly, it's rather simple. The following code performs a breadth-first search using virtually no stack or heap memory:

Working solution withheld for a week, since you said this was for class.

Replies are listed 'Best First'.
Re^2: Brute Force Algorithm with Perl reg exprs?
by ikegami (Patriarch) on May 10, 2006 at 20:26 UTC
    Oops, it's been more than a week. Here's the promised code
    use strict; use warnings; sub crack { my $hint = shift; my $target = shift; my $salt = substr($target, 0, 2); my @chars = @_; my $base = @chars; my $passwd; use integer; for (my $num_chars=0; ; $num_chars++) { for (my $enc=0; ; $enc++) { my $i = $enc; my $pad = ''; for (1..$num_chars) { my $idx = $i % $base; $i = $i / $base; $pad .= $chars[$idx]; } # Go add a character. last if $i; #$pad = reverse $pad; for my $pos (0..$num_chars) { $passwd = $pad; substr($passwd, $pos, 0, $hint); return $passwd if crypt($passwd, $salt) eq $target; } } } } my @chars = ('a'..'z', 'A'..'Z', '0'..'9'); print crack('12', crypt('d12e', 'AA'), @chars), "\n"; print crack('1234', crypt('aa12345', 'AA'), @chars), "\n";