#!/usr/bin/perl
# generate clamav sigs from variant virus files

use strict;
use warnings;
use Data::Dumper;

my $trimextra = 0;
my $reffile = shift @ARGV;
my $refsig = qx { sigtool --hex-dump < "$reffile" };

my $baseoffset = 0;
# work from the front or or the back of the file?
my $fromstart = 0;

my $hs = 0;
# number of different bytes to consider the string "many" instead of "limited or"
my $ndiff = 1;

if ($fromstart) {
  $refsig =~ s/^.{1024}// while $hs++ < $baseoffset;
} else {
  $refsig =~ s/.{1024}$// while $hs++ < $baseoffset;
}
#while ($refsig =~ /^000000000000000000000000/) {
#  $refsig =~ s/^.{1024}//;
#  $trimextra++;
#}
if ($fromstart) {
  $refsig =~ s/^(.{8192}).+/$1/;
} else {
  $refsig =~ s/.+(.{8192})$/$1/;
}

my @refbytes = ($refsig =~ /../g);

my @basesigs;
foreach my $vfile (@ARGV) {
  my $sig = qx { sigtool --hex-dump < "$vfile" };
  $hs = 0;
  if ($fromstart) {
    $sig =~ s/^.{1024}// while $hs++ < $baseoffset;
  } else {
    $sig =~ s/.{1024}$// while $hs++ < $baseoffset;
  }
#  for (my $i = 0; $i <$trimextra; $i++) { $sig =~ s/^.{1024}//; }
##sig =~ /^000000000000000000000000/;
  if ($fromstart) {
    $sig =~ s/^(.{8192}).+/$1/;
  } else {
    $sig =~ s/.+(.{8192})$/$1/;
  }
#print "$sig\n";
  my @foo = ($sig =~ /../g);
  push @basesigs, \@foo;
}

my @outsig;
#for (my $i = 0; $i < 256; $i++) {
#print "'$i: $refbytes[$i]', ";
for (my $i = 0; $i < 4096; $i++) {
  my @tmp;
  push @tmp, $refbytes[$i];
  foreach my $b (@basesigs) {
    next if !defined($b->[$i]);
    push @tmp, $b->[$i] if !grep /^$b->[$i]$/, @tmp;
  }
  if ($#tmp) {
    if ($#tmp >= $ndiff) {
      push @outsig, '{1}';
    } else {
      push @outsig, '('.join('|',@tmp).')';
    }
  } else {
    push @outsig, $tmp[0];
  }
}

#no warnings qw(uninitialized);
my $n = 0;
my $i = 0;
#my $origbytes;
foreach my $byte (@outsig) {
#  print "'$byte'";
#  print "$i test: '$byte'\n" if $i > 100;
  next if !defined($byte);
  if ($byte eq '{1}') {
#    print "anybyte\n";
    $n++;
    next;
  } else {
#    print " $i $n?\n";
    if ($n) {
      print "{$n}" if $n > 1;
      print '??' if $n == 1;
#$origbytes .= ' ';
    }
#$origbytes .= chr($byte);
    print $byte;
    $n = 0;
  }
#  last if $i++ > 200;
}
#print join '', @outsig;
print "\n";
#print "$origbytes\n";
