Difference between revisions of "Talk:Sony Update Downloads"

From Exploitee.rs
(contributing more chars of the pad (up to 134) and my script for building it out.)
 
(Add my approach to the hash cracker script.)
 
Line 100: Line 100:
  
 
I checked the checksums in the .hex file and they all validate so far.
 
I checked the checksums in the .hex file and they all validate so far.
 +
 +
Catrane 2011.02.11:
 +
Nice work, Abliss.    It sounds like we're running similar approaches.  Here's my script.  It prints one line for each byte, listing all the possibilities.  I then do a human search for anything that looks predictable and work it by hand.  The 756 byte key I posted was also verified using the .hex checksums in addition to visual inspection.  The typos in the code make visual inspection a little tricky.
 +
 +
<pre>
 +
#!/usr/bin/perl
 +
 +
use strict;
 +
use warnings;
 +
 +
use IO::File;
 +
 +
my @files = ();
 +
my @filters = ();
 +
my @rules = ();
 +
my %ruleinfo = ();
 +
my $files_left = 0;
 +
 +
#my $textfilter = "[[:print:]\x0a\x0b\t\n\r\f ]";
 +
my $textfilter = "[[:print:]\x0a\t ]";
 +
my $intelhexfilter = "[0-9A-F:\x0d\x0a]";
 +
 +
my $knownmasks = "";
 +
if ( -f 'knownmasks' )
 +
{
 +
$knownmasks = `cat knownmasks`;
 +
}
 +
 +
sub checkRules
 +
{
 +
my ($rule,$char) = @_;
 +
if ( "text" eq $ruleinfo{$rule}{'type'} )
 +
{
 +
} elsif ( "intelhex" eq $ruleinfo{$rule}{'type'} ) {
 +
if ( $ruleinfo{$rule}{'is_colon'} )
 +
{
 +
return 0 unless ( ":" eq $char );
 +
}
 +
if ( $ruleinfo{$rule}{'was_colon'} > 0 )
 +
{
 +
return 0 if ( ($ruleinfo{$rule}{'was_colon'} < 12) && ("\x0a" eq $char) );
 +
return 0 if ( ($ruleinfo{$rule}{'was_colon'} < 11) && (("\x0a" eq $char) || ("\x0d" eq $char)) );
 +
return 0 if ( ":" eq $char );
 +
}
 +
return 0 if ( ( ":" eq $char ) && ! $ruleinfo{$rule}{'was_maybe_0a'} );
 +
return 0 if ( ( "\x0a" eq $char ) && ! $ruleinfo{$rule}{'was_maybe_0d'} );
 +
 +
}
 +
return 1;
 +
}
 +
 +
sub advanceRules
 +
{
 +
my ($rule,$options) = @_;
 +
if ( "text" eq $ruleinfo{$rule}{'type'} )
 +
{
 +
} elsif ( "intelhex" eq $ruleinfo{$rule}{'type'} ) {
 +
my $is_nr = ( $options =~ m/^\\[nr]\\[nr]$/ );
 +
# 0d0a == \r\n
 +
my $is_maybe_0d = ( $options =~ m/\\r/ );
 +
my $is_maybe_0a = ( $options =~ m/\\n/ );
 +
if ( $ruleinfo{$rule}{'was_colon'} )
 +
{
 +
$ruleinfo{$rule}{'was_colon'}++;
 +
if ( $ruleinfo{$rule}{'was_colon'} > 12 )
 +
{
 +
$ruleinfo{$rule}{'was_colon'} = 0;
 +
}
 +
}
 +
if ( $ruleinfo{$rule}{'is_colon'} )
 +
{
 +
$ruleinfo{$rule}{'is_colon'} = 0;
 +
$ruleinfo{$rule}{'was_colon'} = 1;
 +
} elsif ( $ruleinfo{$rule}{'was_nr'} ) {
 +
$ruleinfo{$rule}{'is_colon'} = $is_nr;
 +
}
 +
$ruleinfo{$rule}{'was_nr'} = $is_nr;
 +
$ruleinfo{$rule}{'was_maybe_0d'} = $is_maybe_0d;
 +
$ruleinfo{$rule}{'was_maybe_0a'} = $is_maybe_0a;
 +
}
 +
}
 +
 +
while ( my $filter = shift )
 +
{
 +
if ( "text" eq $filter )
 +
{
 +
push(@filters,$textfilter);
 +
push(@rules,$files_left);
 +
$ruleinfo{$files_left}{'type'} = "text";
 +
} elsif ( "intelhex" eq $filter ) {
 +
push(@filters,$intelhexfilter);
 +
push(@rules,$files_left);
 +
$ruleinfo{$files_left}{'type'} = "intelhex";
 +
$ruleinfo{$files_left}{'is_colon'} = 1; # First must be colon.
 +
$ruleinfo{$files_left}{'was_colon'} = 0;
 +
$ruleinfo{$files_left}{'was_nr'} = 1; # Make like normal colon setup.
 +
$ruleinfo{$files_left}{'was_maybe_0d'} = 0;
 +
$ruleinfo{$files_left}{'was_maybe_0a'} = 1; # Make like normal colon setup.
 +
} elsif ( "test_text" eq $filter ) {
 +
my $counter = 0;
 +
my $fh = IO::File->new("< ".shift) or die "Unable to open file.\n";
 +
while ( defined( my $char = getc($fh) ) )
 +
{
 +
die "failed test at index $counter.\n" unless ( $char =~ m/$textfilter/ );
 +
$counter++;
 +
}
 +
print "test passed.\n";
 +
exit;
 +
} elsif ( "test_intelhex" eq $filter ) {
 +
my $counter = 0;
 +
my $fh = IO::File->new("< ".shift) or die "Unable to open file.\n";
 +
while ( defined( my $char = getc($fh) ) )
 +
{
 +
die "failed test at index $counter.\n" unless ( $char =~ m/$intelhexfilter/ );
 +
$counter++;
 +
}
 +
print "test passed.\n";
 +
exit;
 +
} else {
 +
die "Invalid filter '$filter'.\n";
 +
}
 +
my $file = shift;
 +
die "Missing filename parameter.\n" unless defined $file;
 +
die "File '$file' does not exist.\n" unless ( -f $file );
 +
my $fh = IO::File->new("< $file") or die "Unable to open file '$file'.\n";
 +
push(@files,$fh);
 +
$files_left++;
 +
}
 +
 +
while ( $files_left )
 +
{
 +
my @options = ("\x00","\x01","\x02","\x03","\x04","\x05","\x06","\x07","\x08","\x09","\x0a","\x0b","\x0c","\x0d","\x0e","\x0f",
 +
              "\x10","\x11","\x12","\x13","\x14","\x15","\x16","\x17","\x18","\x19","\x1a","\x1b","\x1c","\x1d","\x1e","\x1f",
 +
              "\x20","\x21","\x22","\x23","\x24","\x25","\x26","\x27","\x28","\x29","\x2a","\x2b","\x2c","\x2d","\x2e","\x2f",
 +
              "\x30","\x31","\x32","\x33","\x34","\x35","\x36","\x37","\x38","\x39","\x3a","\x3b","\x3c","\x3d","\x3e","\x3f",
 +
              "\x40","\x41","\x42","\x43","\x44","\x45","\x46","\x47","\x48","\x49","\x4a","\x4b","\x4c","\x4d","\x4e","\x4f",
 +
              "\x50","\x51","\x52","\x53","\x54","\x55","\x56","\x57","\x58","\x59","\x5a","\x5b","\x5c","\x5d","\x5e","\x5f",
 +
              "\x60","\x61","\x62","\x63","\x64","\x65","\x66","\x67","\x68","\x69","\x6a","\x6b","\x6c","\x6d","\x6e","\x6f",
 +
              "\x70","\x71","\x72","\x73","\x74","\x75","\x76","\x77","\x78","\x79","\x7a","\x7b","\x7c","\x7d","\x7e","\x7f",
 +
              "\x80","\x81","\x82","\x83","\x84","\x85","\x86","\x87","\x88","\x89","\x8a","\x8b","\x8c","\x8d","\x8e","\x8f",
 +
              "\x90","\x91","\x92","\x93","\x94","\x95","\x96","\x97","\x98","\x99","\x9a","\x9b","\x9c","\x9d","\x9e","\x9f",
 +
              "\xa0","\xa1","\xa2","\xa3","\xa4","\xa5","\xa6","\xa7","\xa8","\xa9","\xaa","\xab","\xac","\xad","\xae","\xaf",
 +
              "\xb0","\xb1","\xb2","\xb3","\xb4","\xb5","\xb6","\xb7","\xb8","\xb9","\xba","\xbb","\xbc","\xbd","\xbe","\xbf",
 +
              "\xc0","\xc1","\xc2","\xc3","\xc4","\xc5","\xc6","\xc7","\xc8","\xc9","\xca","\xcb","\xcc","\xcd","\xce","\xcf",
 +
              "\xd0","\xd1","\xd2","\xd3","\xd4","\xd5","\xd6","\xd7","\xd8","\xd9","\xda","\xdb","\xdc","\xdd","\xde","\xdf",
 +
              "\xe0","\xe1","\xe2","\xe3","\xe4","\xe5","\xe6","\xe7","\xe8","\xe9","\xea","\xeb","\xec","\xed","\xee","\xef",
 +
              "\xf0","\xf1","\xf2","\xf3","\xf4","\xf5","\xf6","\xf7","\xf8","\xf9","\xfa","\xfb","\xfc","\xfd","\xfe","\xff");
 +
my $options_left = 256;
 +
if ( $knownmasks =~ s/^(..)\n// )
 +
{
 +
my $knownmask = $1;
 +
if ( $knownmask =~ m/[0-9a-f]{2}/i )
 +
{
 +
my $val;
 +
eval('$val = "\\x'.$knownmask.'";');
 +
$options[0] = $val;
 +
$options_left = 1;
 +
}
 +
}
 +
 +
my @filechars = ();
 +
 +
for ( my $i = 0; $i < $files_left; $i++ )
 +
{
 +
my $char = getc($files[$i]);
 +
if ( defined($char) )
 +
{
 +
$filechars[$i] = $char;
 +
my $filter = $filters[$i];
 +
 +
for ( my $x = 0; $x < $options_left; $x++ )
 +
{
 +
#print "$x vs $options_left\n";
 +
# print "char is $char\n";
 +
# print "option is $options[$x]\n";
 +
my $realchar = $char ^ $options[$x];
 +
unless ( ($realchar =~ m/$filter/) && checkRules($rules[$i],$realchar) )
 +
{
 +
splice(@options,$x,1);
 +
$x--;
 +
$options_left--;
 +
}
 +
}
 +
} else {
 +
$files[$i]->close;
 +
splice(@files,$i,1);
 +
splice(@filters,$i,1);
 +
splice(@rules,$i,1);
 +
$i--;
 +
$files_left--;
 +
}
 +
}
 +
 +
last unless $files_left;
 +
 +
die "Failed to solve.\n" unless $options_left;
 +
for ( my $i = 0; $i < $files_left; $i++ )
 +
{
 +
my $options = "";
 +
for ( my $x = 0; $x < $options_left; $x++ )
 +
{
 +
my $realchar = $filechars[$i] ^ $options[$x];
 +
$realchar = "\\t" if ( $realchar eq "\t" );
 +
$realchar = "\\r" if ( $realchar eq "\r" );
 +
$realchar = "\\n" if ( $realchar eq "\n" );
 +
#$realchar = "\\v" if ( $realchar eq "\v" );
 +
$realchar = "\\v" if ( $realchar eq "\x0b" );
 +
$realchar = "\\a" if ( $realchar eq "\x0a" );
 +
$realchar = "\\f" if ( $realchar eq "\f" );
 +
$realchar = "\\s" if ( $realchar eq " " );
 +
$options .= $realchar;
 +
}
 +
print $options."\n" unless $i;
 +
advanceRules($rules[$i],$options);
 +
}
 +
}
 +
print "Done\n";
 +
</pre>

Latest revision as of 04:05, 12 February 2011

I worked on expanding the pad a bit. Here's the script I use.

#!/usr/bin/perl
use strict;
package abliss;
my $start = shift;

open HEX, "<./history/other/RfHid_v0156_2010091601_NL.hex" or die;
open OUT, ">>pad.bin";
open IN, "<pad.bin";
my @files = (
    "./history/NBL/batch_sync-vfat.sh",
    "./history/board_conf.sh",
    "./history/other/check_spectra1_20100929.sh",
    "./history/other/factory_reset_conditional_keepremote_20101012.sh",
    "./history/other/format_sda_20100514.sh");
my @fds;
for (my $i = 0; $i <= $#files; $i++) {
    open (my $fd, $files[$i]) or die "can't open " . $files[$i];
    push(@fds, $fd);
}
my @contents;

my $hexbyte;
my @hexchars = qw(0 1 2 3 4 5 6 7 8 9 A B C D E F :);
push(@hexchars, "\r");
push(@hexchars, "\n");
my @output = ();
our $xorbyte;
while (read(HEX, $hexbyte, 1)) {
    for (my $i = 0; $i <= $#files; $i++) {
        my $char;
        if (read($fds[$i], $char, 1)) {
            $contents[$i] .= $char;
            if (length($contents[$i]) > 30) {
                $contents[$i] = substr($contents[$i], 1);
            }
        }
    }
    if ($start-- > 0) {
        my $char;
        read(IN, $char, 1);
        $xorbyte = ord($char);
    } else {
        for (my $j = 0; $j <= $#hexchars; $j++) {
            $xorbyte = ord($hexbyte) ^ ord($hexchars[$j]);
            my $choices = "";
            my $ok = 1;
            for (my $i = 0; $i <= $#files; $i++) {
                my $neword = (ord(substr($contents[$i],length($contents[$i]) - 1)) ^ $xorbyte);
                if ($neword > 127 || 
                    ($neword < 32 && 
                     $neword != 9 && # tab
                     $neword != 10 && # LF
                     $neword != 13 # CR
                    )) {
                    $ok = 0;
                    #printf "==== %2d ====\n%s\n", $j, xorlastbyte($contents[$i]);
                    last;
                }
            }
            if ($ok) {
                printf "==== %2d ====\n%s\n", $j, join("\n--\n", map {xorlastbyte($_)} @contents);
            }
        }
        my $answer = <STDIN>;
        chomp $answer;
        if ($answer eq "q") {
            close OUT;
            die;
        }
        $xorbyte = ord($hexbyte) ^ ord($hexchars[$answer]);
        print OUT chr($xorbyte);
    }
    @contents = map {xorlastbyte($_)} @contents;
}

sub xorlastbyte {
    my $content = shift;
    if ($content) {
        my @chars = split(//, $content);
        $chars[-1] = chr(ord($chars[-1])^ $xorbyte);
        return join('', @chars);
    }
}

Here's my pad, 134 chars:

00000000  38 cf 4f aa 7a 8a 2e 3e  2b 41 82 9a ad 31 e9 dc  |8.O.z..>+A...1..|
00000010  ef 47 2f 0b 26 76 12 fe  5f 5b 58 e1 10 18 7d e6  |.G/.&v.._[X...}.|
00000020  ad 92 1b 91 8e 90 69 f7  8a 9b 68 d8 98 58 fa 95  |......i...h..X..|
00000030  63 81 d6 5f 04 7d 29 8b  09 cf b9 21 b8 d9 df dd  |c.._.})....!....|
00000040  c4 7e 71 d9 3f 35 ea 7b  0d ec 7f d1 a3 76 64 88  |.~q.?5.{.....vd.|
00000050  a5 8e 27 49 60 c0 a0 bc  77 54 31 e3 d6 6a bf e5  |..'I`...wT1..j..|
00000060  1b 42 25 da a3 97 b8 e1  ba 54 13 5b 68 31 da ff  |.B%......T.[h1..|
00000070  1c 5c 15 46 4e 32 f1 76  50 e0 4e f3 ab 9a 28 bb  |.\.FN2.vP.N...(.|
00000080  b5 cf 2f 50 24 45                                 |../P$E|

I checked the checksums in the .hex file and they all validate so far.

Catrane 2011.02.11: Nice work, Abliss. It sounds like we're running similar approaches. Here's my script. It prints one line for each byte, listing all the possibilities. I then do a human search for anything that looks predictable and work it by hand. The 756 byte key I posted was also verified using the .hex checksums in addition to visual inspection. The typos in the code make visual inspection a little tricky.

#!/usr/bin/perl

use strict;
use warnings;

use IO::File;

my @files = ();
my @filters = ();
my @rules = ();
my %ruleinfo = ();
my $files_left = 0;

#my $textfilter = "[[:print:]\x0a\x0b\t\n\r\f ]";
my $textfilter = "[[:print:]\x0a\t ]";
my $intelhexfilter = "[0-9A-F:\x0d\x0a]";

my $knownmasks = "";
if ( -f 'knownmasks' )
{
	$knownmasks = `cat knownmasks`;
}

sub checkRules
{
	my ($rule,$char) = @_;
	if ( "text" eq $ruleinfo{$rule}{'type'} )
	{
	} elsif ( "intelhex" eq $ruleinfo{$rule}{'type'} ) {
		if ( $ruleinfo{$rule}{'is_colon'} )
		{
			return 0 unless ( ":" eq $char );
		}
		if ( $ruleinfo{$rule}{'was_colon'} > 0 )
		{
			return 0 if ( ($ruleinfo{$rule}{'was_colon'} < 12) && ("\x0a" eq $char) );
			return 0 if ( ($ruleinfo{$rule}{'was_colon'} < 11) && (("\x0a" eq $char) || ("\x0d" eq $char)) );
			return 0 if ( ":" eq $char );
		}
		return 0 if ( ( ":" eq $char ) && ! $ruleinfo{$rule}{'was_maybe_0a'} );
		return 0 if ( ( "\x0a" eq $char ) && ! $ruleinfo{$rule}{'was_maybe_0d'} );
		
	}
	return 1;
}

sub advanceRules
{
	my ($rule,$options) = @_;
	if ( "text" eq $ruleinfo{$rule}{'type'} )
	{
	} elsif ( "intelhex" eq $ruleinfo{$rule}{'type'} ) {
		my $is_nr = ( $options =~ m/^\\[nr]\\[nr]$/ );
		# 0d0a == \r\n
		my $is_maybe_0d = ( $options =~ m/\\r/ );
		my $is_maybe_0a = ( $options =~ m/\\n/ );
		if ( $ruleinfo{$rule}{'was_colon'} )
		{
			$ruleinfo{$rule}{'was_colon'}++;
			if ( $ruleinfo{$rule}{'was_colon'} > 12 )
			{
				$ruleinfo{$rule}{'was_colon'} = 0;
			}
		}
		if ( $ruleinfo{$rule}{'is_colon'} )
		{
			$ruleinfo{$rule}{'is_colon'} = 0;
			$ruleinfo{$rule}{'was_colon'} = 1;
		} elsif ( $ruleinfo{$rule}{'was_nr'} ) {
			$ruleinfo{$rule}{'is_colon'} = $is_nr;
		}
		$ruleinfo{$rule}{'was_nr'} = $is_nr;
		$ruleinfo{$rule}{'was_maybe_0d'} = $is_maybe_0d;
		$ruleinfo{$rule}{'was_maybe_0a'} = $is_maybe_0a;
	}
}

while ( my $filter = shift )
{
	if ( "text" eq $filter )
	{
		push(@filters,$textfilter);
		push(@rules,$files_left);
		$ruleinfo{$files_left}{'type'} = "text";
	} elsif ( "intelhex" eq $filter ) {
		push(@filters,$intelhexfilter);
		push(@rules,$files_left);
		$ruleinfo{$files_left}{'type'} = "intelhex";
		$ruleinfo{$files_left}{'is_colon'} = 1; # First must be colon.
		$ruleinfo{$files_left}{'was_colon'} = 0;
		$ruleinfo{$files_left}{'was_nr'} = 1; # Make like normal colon setup.
		$ruleinfo{$files_left}{'was_maybe_0d'} = 0;
		$ruleinfo{$files_left}{'was_maybe_0a'} = 1; # Make like normal colon setup.
	} elsif ( "test_text" eq $filter ) {
		my $counter = 0;
		my $fh = IO::File->new("< ".shift) or die "Unable to open file.\n";
		while ( defined( my $char = getc($fh) ) )
		{
			die "failed test at index $counter.\n" unless ( $char =~ m/$textfilter/ );
			$counter++;
		}
		print "test passed.\n";
		exit;
	} elsif ( "test_intelhex" eq $filter ) {
		my $counter = 0;
		my $fh = IO::File->new("< ".shift) or die "Unable to open file.\n";
		while ( defined( my $char = getc($fh) ) )
		{
			die "failed test at index $counter.\n" unless ( $char =~ m/$intelhexfilter/ );
			$counter++;
		}
		print "test passed.\n";
		exit;
	} else {
		die "Invalid filter '$filter'.\n";
	}
	my $file = shift;
	die "Missing filename parameter.\n" unless defined $file;
	die "File '$file' does not exist.\n" unless ( -f $file );
	my $fh = IO::File->new("< $file") or die "Unable to open file '$file'.\n";
	push(@files,$fh);
	$files_left++;
}

while ( $files_left )
{
	my @options = ("\x00","\x01","\x02","\x03","\x04","\x05","\x06","\x07","\x08","\x09","\x0a","\x0b","\x0c","\x0d","\x0e","\x0f",
	               "\x10","\x11","\x12","\x13","\x14","\x15","\x16","\x17","\x18","\x19","\x1a","\x1b","\x1c","\x1d","\x1e","\x1f",
	               "\x20","\x21","\x22","\x23","\x24","\x25","\x26","\x27","\x28","\x29","\x2a","\x2b","\x2c","\x2d","\x2e","\x2f",
	               "\x30","\x31","\x32","\x33","\x34","\x35","\x36","\x37","\x38","\x39","\x3a","\x3b","\x3c","\x3d","\x3e","\x3f",
	               "\x40","\x41","\x42","\x43","\x44","\x45","\x46","\x47","\x48","\x49","\x4a","\x4b","\x4c","\x4d","\x4e","\x4f",
	               "\x50","\x51","\x52","\x53","\x54","\x55","\x56","\x57","\x58","\x59","\x5a","\x5b","\x5c","\x5d","\x5e","\x5f",
	               "\x60","\x61","\x62","\x63","\x64","\x65","\x66","\x67","\x68","\x69","\x6a","\x6b","\x6c","\x6d","\x6e","\x6f",
	               "\x70","\x71","\x72","\x73","\x74","\x75","\x76","\x77","\x78","\x79","\x7a","\x7b","\x7c","\x7d","\x7e","\x7f",
	               "\x80","\x81","\x82","\x83","\x84","\x85","\x86","\x87","\x88","\x89","\x8a","\x8b","\x8c","\x8d","\x8e","\x8f",
	               "\x90","\x91","\x92","\x93","\x94","\x95","\x96","\x97","\x98","\x99","\x9a","\x9b","\x9c","\x9d","\x9e","\x9f",
	               "\xa0","\xa1","\xa2","\xa3","\xa4","\xa5","\xa6","\xa7","\xa8","\xa9","\xaa","\xab","\xac","\xad","\xae","\xaf",
	               "\xb0","\xb1","\xb2","\xb3","\xb4","\xb5","\xb6","\xb7","\xb8","\xb9","\xba","\xbb","\xbc","\xbd","\xbe","\xbf",
	               "\xc0","\xc1","\xc2","\xc3","\xc4","\xc5","\xc6","\xc7","\xc8","\xc9","\xca","\xcb","\xcc","\xcd","\xce","\xcf",
	               "\xd0","\xd1","\xd2","\xd3","\xd4","\xd5","\xd6","\xd7","\xd8","\xd9","\xda","\xdb","\xdc","\xdd","\xde","\xdf",
	               "\xe0","\xe1","\xe2","\xe3","\xe4","\xe5","\xe6","\xe7","\xe8","\xe9","\xea","\xeb","\xec","\xed","\xee","\xef",
	               "\xf0","\xf1","\xf2","\xf3","\xf4","\xf5","\xf6","\xf7","\xf8","\xf9","\xfa","\xfb","\xfc","\xfd","\xfe","\xff");
	my $options_left = 256;
	if ( $knownmasks =~ s/^(..)\n// )
	{
		my $knownmask = $1;
		if ( $knownmask =~ m/[0-9a-f]{2}/i )
		{
			my $val;
			eval('$val = "\\x'.$knownmask.'";');
			$options[0] = $val;
			$options_left = 1;
		}
	}

	my @filechars = ();

	for ( my $i = 0; $i < $files_left; $i++ )
	{
		my $char = getc($files[$i]);
		if ( defined($char) )
		{
			$filechars[$i] = $char;
			my $filter = $filters[$i];

			for ( my $x = 0; $x < $options_left; $x++ )
			{
#print "$x vs $options_left\n";
#				print "char is $char\n";
#				print "option is $options[$x]\n";
				my $realchar = $char ^ $options[$x];
				unless ( ($realchar =~ m/$filter/) && checkRules($rules[$i],$realchar) )
				{
					splice(@options,$x,1);
					$x--;
					$options_left--;
				}
			}
		} else {
			$files[$i]->close;
			splice(@files,$i,1);
			splice(@filters,$i,1);
			splice(@rules,$i,1);
			$i--;
			$files_left--;
		}
	}

	last unless $files_left;

	die "Failed to solve.\n" unless $options_left;
	for ( my $i = 0; $i < $files_left; $i++ )
	{
		my $options = "";
		for ( my $x = 0; $x < $options_left; $x++ )
		{
			my $realchar = $filechars[$i] ^ $options[$x];
			$realchar = "\\t" if ( $realchar eq "\t" );
			$realchar = "\\r" if ( $realchar eq "\r" );
			$realchar = "\\n" if ( $realchar eq "\n" );
			#$realchar = "\\v" if ( $realchar eq "\v" );
			$realchar = "\\v" if ( $realchar eq "\x0b" );
			$realchar = "\\a" if ( $realchar eq "\x0a" );
			$realchar = "\\f" if ( $realchar eq "\f" );
			$realchar = "\\s" if ( $realchar eq " " );
			$options .= $realchar;
		}
		print $options."\n" unless $i;
		advanceRules($rules[$i],$options);
	}
}
print "Done\n";