=pod perl -w xor-bigint.pl --debug=1 --dir='/home/guest/xor-test' --re='test/5\.txt$' --key='xyz0 % ^ &88**' --rotate_key='<<' --area_size='10%' --area_offset='30%' perl -w xor-bigint.pl --debug=1 --dir='/home/guest/xor-test' --re='test/5\.txt$' --key='xyz0 % ^ &88**' --rotate_key='<<' --area_size=1000 --area_offset=4000 =cut use strict; use Getopt::Long; use File::Find; use Math::BigInt try => 'GMP'; #~ use POSIX ":sys_wait_h"; # perldoc -f waitpid #~ use Fcntl; # for SEEK_SET and SEEK_CUR # 0 to set the new position to POSITION; SEEK_SET # 1 to set the it to the current position plus POSITION; SEEK_CUR my %arg=(forks=>1, dir=>'.', area_offset=>0, count=>1, test=>0, rotate_start_bit=>1,); # GetOptions( 'forks=s'=>\$arg{forks}, # 'dir=s'=>\$arg{dir}, # default='.' full path!! 're=s'=>\$arg{re}, # регулярное выражение для полного имени файла (пусто - все файлы) default=undef 'key=s'=>\$arg{key}, #mandatory 'area_offset=s'=>\$arg{area_offset}, # в байтах сместиться от начала файла default=0 (можно в %) 'area_size=s'=>\$arg{area_size}, # в байтах undef -> до конца (можно в %) # 4 связанных параметра при вращении битов ключа 'rotate_key=s'=>\$arg{rotate_key}, # куда вращать ключ << влево >> вправо в цикле count (см вызов rot()) Если не укаазть - не будет вращения ключа default=undef 'rotate_count=i'=>\$arg{rotate_count}, # сколько раз вращать default=count bits of key 'rotate_start_bit=i'=>\$arg{rotate_start_bit},# (см вызов rotate()) default=1 'rotate_mask_bits=i'=>\$arg{rotate_mask_bits},# количество битов в маске (см вызов rotate()) default=count bits of key (не больше!!!) 'debug=i'=>\$arg{debug},# default=undef 'test=i'=>\$arg{test},# default=0 ); my $key_len = length($arg{key}) or die "Empty --key= option"; my $key_bits = $key_len*8; my $key_bigint = str2b($arg{key}); $arg{rotate_count} //= $key_bits; my $mask = Math::BigInt->new('0b'.(1 x ($arg{rotate_mask_bits} || $key_bits)));# хо-хо, может быстрее #~ print "$key_len\tlen\n$key_bits\tkey_bits\n", unpack('B*', 'xz'), "\txz\n", $key_bigint->badd(1)->as_bin, "\tkey+1\n", $mask->as_bin, "\tmask\n"; # генерация шифра из ключа и его трансформации my $cypher; map { my $place = $arg{rotate_start_bit}+$_-1; my $rot = rotate($key_bigint, $arg{rotate_key}, $place, $mask); #~ print $rot->as_bin, "\trot[$arg{rotate_key} $place]\t", ($rot->is_nan && ""), "\tnan\n"; $cypher .= b2str($rot) unless $rot->is_nan; } (1..($arg{rotate_count})) if $arg{rotate_key}; my $cypher_len = length($cypher); $arg{debug} && print #unpack('b*', $cypher), "\tcypher\n", "$cypher_len\t bit cypher len\n"; #~ exit; my $offset_coeff = ($arg{area_offset} =~ /(\d+)/)[0]/100 if $arg{area_offset} =~ /%/; print "Offset k=[$offset_coeff][$arg{area_offset}]\n" if $offset_coeff; my $size_coeff = ($arg{area_size} =~ /(\d+)/)[0]/100 if $arg{area_size} && $arg{area_size} =~ /%/; print "Size k=[$size_coeff][$arg{area_size}]\n" if $size_coeff; do { print "WARN!!! IT IS STRONGLY RECOMEND TO COPY/SAVE THIS COMMAND LINE FOR NEXT TIME (ok?)"; my $ans = <>; chomp($ans); exit unless lc($ans) eq 'ok'; print "WARN!!! STRONGLY BE SURE THAT YOU DID [\$ unset HISTFILE]. (y/n):"; $ans = <>; chomp($ans); print "PLEASE DO [\$ unset HISTFILE]!\n" and exit unless lc($ans) eq 'y'; #~ exec('bash -c "unset HISTFILE"'); }; #~ exit; my %pids; find({ wanted => sub { =pod $File::Find::dir is the current directory name, $_ is the current filename within that directory $File::Find::name is the complete pathname to the file. for each directory found, it will "chdir()" into that directory and continue the search =cut return if -d; # пропуск директорий ($arg{debug} && print "Skip: [$File::Find::name]\n") || 1 and return unless $File::Find::name =~ /$arg{re}/; if( scalar keys %pids == $arg{forks}) { my $pid = wait(); delete $pids{$pid}; } make_child($File::Find::name); } , follow => 1, # symlinks }, $arg{dir}); waitpid $_, 0 for keys %pids; sub make_child { my $fn = shift;# @find or return undef; my $pid = fork(); if ($pid) {# parent $pids{$pid}++; #~ print "Запущен процесс [$pid] для файла [$fn]\n"; return 1; } elsif ($pid == 0) {# child print "Process[$$] [$File::Find::name]...";# if $arg{debug}; process($fn); print " done\n";# if $arg{debug}; exit; } else { die "Не может fork: $!\n"; } } sub process { #~ return; my $fn = shift; open(FH, "+<", $fn) or warn "[$fn] opening: $!\n" and return; binmode(FH); my $fsize = (stat($fn))[7]; print "\t skip size == 0" and (close FH or warn "[$fn] closing: $!") and return if $fsize == 0; my $foffset = defined $offset_coeff ? int($fsize*$offset_coeff) : $arg{area_offset}; # отмотать вперед sysseek(FH, $foffset, 0) or warn "[$fn] forward seeking: $!" # SEEK_SET and (close FH or warn "[$fn] closing: $!") and return; my $count = defined $size_coeff ?# убывающий счетчик считываний int(($fsize-$foffset)*$size_coeff)# int((size*%/100)/len) : $arg{area_size} // ($fsize-$foffset); my $len = $cypher_len > $count ? $count : $cypher_len; while ($count && (my $read = sysread(FH, my $buffer, $len))) { $count -= $read; $len = $cypher_len > $count ? $count : $cypher_len; #~ push my @zero, @- while $buffer =~ /(\0)/g; #~ print "Found zero @zero" if @zero; #~ my $read = sysread(FH, my $buffer, $len); #~ warn "[$fn] reading whole chunk[$_] [$read] != [$len]" #~ and (close FH #~ or warn "[$fn] closing: $!") #~ and return #~ unless $read == $len; #~ print " after read POS: ", sysseek(FH, 0, 1) if $arg{debug}; # отмотать обратно sysseek(FH, -$read, 1) #SEEK_CUR or warn "[$fn] back seeking: $!" and (close FH or warn "[$fn] closing: $!") and return; #~ print FH $BUFFER ^ $cypher ; print "write pos: ", sysseek(FH, 0, 1), "; " if $arg{debug}; my $write = $buffer ^ substr($cypher, 0, $read); #~ substr($write, $_, 1, chr(0x00)) for @zero; syswrite FH, $write or warn "[$fn] writing: $!" and (close FH or warn "[$fn] closing: $!") and return; } close FH or warn "[$fn] closing: $!"; } sub str2b {# convert string to Math::BigInt Math::BigInt->new("0b".unpack('B*', $_[0])); } sub b2str {# pack Math::BigInt to string, (is not ->bstr) pack('B*', substr($_[0]->as_bin , 2)); } sub rotate {# rotate($bigint, $direction, 1, $mask) - $direction(<<|>>); на 1 бит, $mask - my $rev = $_[1] eq '<<' ? '>>' : '<<'; my $mask_len = length(substr($_[3]->as_bin, 2)); my $places = ($_[2] % $mask_len) || $_[2]; my $shift1 = eval "\$_[0] $_[1] \$places"; my $shift2 = eval "\$_[0] $rev (\$mask_len - \$places)"; #~ my $mask = Math::BigInt->new(2) **$_[2] - 1; #~ my $mask = Math::BigInt->new('0b'.(1 x $_[3]));# хо-хо, может быстрее return ($shift1 | $shift2) & $_[3]; } #~ sub rol { return ($_[0]<<$_[1] | $_[0]>>($_[2]-$_[1])) & (2**$_[2]-1); } #~ sub ror { return ($_[0]>>$_[1] | $_[0]<<($_[2]-$_[1])) & (2**$_[2]-1); } #~ substr($BUFFER ^ $cypher, 0, $read); __END__ #~ use Math::BigInt; use Math::BigInt try => 'GMP'; my $key = "r\0 4r"; my $up = unpack('b*', $key); my $bits = length($up); my $x =Math::BigInt->new("0b$up"); my $y; print "$key key\t", length($key)," len\n", $x->as_bin(), " as is\t", length(bpack($x)), " len\t", "$bits bits\n", map {$y = rot($x, '>>', $_+0, $bits+0); $y->as_bin." rot[$_]\t".length(substr($y->as_bin , 2))." bits\t".length(bpack($y))." bytes\t".($x cmp $y)." cmp\t".(bpack($x) eq bpack($y))." eq\n";} (1..100);