#!/usr/bin/perl
#
# dFdecode.pl - decodes certain popular javascript functions used to redirect
#               browsers to malware
#
$version = 1.0.0;
#
# Author: PinkFreud <pf .dash. javascript .at. mirkwood .dot. net>
# Date: 2007-08-11
#
# License: Public domain.  I take no responsibility for what may happen when
# anyone runs this script.  See disclaimer in usage() below (or run this script
# without any arguments).
#
# Dependencies:
#   * JavaScript module for perl.  See
#     http://search.cpan.org/~claesjac/JavaScript-1.03/
#
#   * HTML::Parser module.  See http://search.cpan.org/~gaas/HTML-Parser-3.56/
#
#   * Getopt::Long module.  This should be included with perl in most cases.
#     If not, see http://search.cpan.org/~jv/Getopt-Long-2.36/
#
# Debian users: apt-get install libjavascript-perl libhtml-parser-perl
# Getopt::Long is already included in Debian's perl-base
#
################################################################################


use JavaScript;
use HTML::Parser 3.00 ();
use Getopt::Long;


$max_recurse = 0;
$result = GetOptions ("maxrecurse=i" => \$max_recurse,
                      "help" => \&usage,
                      "i-agree" => \$agree);

&usage unless $agree == 1;
print "*** Maximum execution recursion set to $max_recurse.\n";


sub usage {
  $textversion = sprintf "%vd", $version;
  print <<_EOF_;
dfDecode.pl version $textversion by PinkFreud

Usage: $0 --i-agree [ --maxrecurse <num> ] index.html

--maxrecurse specifies how many times to recursively eval the javascript
taken from index.html


*** WARNING *** *** WARNING *** *** WARNING *** *** WARNING *** *** WARNING ***
This program actually *executes potentially harmful javascript*.  It's intended
for security researchers to deobfuscate javascript functions which attempt to
redirect browsers to harmful sites.  For this reason, if you do not know what
you're doing, do *not* run this utility!

I will not be responsible for any harmful effects of this script, including,
but not limited to, damage to your data, computer, your Aunt Matilda, or the
neighbor's dog.  In fact, I won't be responsible for *anything* this script
does or does not do.  Period.
*** WARNING *** *** WARNING *** *** WARNING *** *** WARNING *** *** WARNING ***

If you agree, you need to specify --i-agree as an argument to run this utility.

  -- PinkFreud, 2007-08-11

_EOF_

  exit 1;
}


### Pull JavaScript out of HTML ###
$html .= $line while ($line = <>);

my $p = HTML::Parser->new(api_version => 3,
  handlers    => [
    start => [\&tag, "tagname, '+1'"],
    end   => [\&tag, "tagname, '-1'"],
    text  => [\&text, "dtext"],
  ],
  marked_sections => 1,
);

$p->report_tags('script');
$p->parse($html) or die "$!\n";
$p->eof;


sub tag {
  my($tag, $num) = @_;
  $inside{$tag} += $num;
  return;
}

sub text {
  return unless $inside{script};
  push (@js, $_[0]);
  return;
}


### Bind Perl subs for use in JavaScript ###
$rt = new JavaScript::Runtime();
$cx = $rt->create_context();

$cx->bind_function(name => 'write', func => sub {
  my $js_out = join " ", @_;
  ($js_out) =~ s/<\/?script.*?>//ig;
  print "$js_out\n";
  if (++$recurse == $max_recurse) {
    print "*** Maximum recursion ($max_recurse) reached, not eval'ing js\n";
  } else {
    print "*** eval js\n";
    print " *** $js ***\n";
    $cx->eval($js_out);
  }
});

$cx->bind_function (name => 'winprint', func => sub {
  my @winprint = @_;
  print "*** Script attempted to open a window with the following arguments:\n";
  print "@winprint\n";
});


### Parse JavaScript ###
foreach $js (@js) {
  $recurse = 0;
  if ($max_recurse == 0) {
    print <<_EOF_;
*** Not executing javascript because you did not specify --maxrecurse!
*** The javascript I would try to execute is:
$js
_EOF_
  } else {
    $cx->eval(<<_EOF_
      document = new Object();
      document.write = write;
      window = new Object();
      window.open = winprint;
      $js;
_EOF_
    );
  }
}
