Difference between revisions of "Talk:Sony Update Downloads"
From Exploitee.rs
Jump to navigationJump to search
(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";