Futile attempt of spreading malware: 3wPlayer vs. perl hackers

If you happen to be a voracious torrent user you may have found yourself in the situation where instead of a film your video player displays a still text inviting you to download their stupid trojan-horse infected video proprietary video player.

Basically the video file is there, but it's been encrypted so that only that particular crappy video player allows you to see it. Or is it? Apparently 3wplayer are not very bright, all they did was using standard exclusive or (XOR) with a short string, which makes sense. It's really easy to implement, saves space since the function for encoding and decoding is the same and most importantly it's fast, wich it has to be when dealing with video. What those morons who created that stupid player forgot, was that if you have a lot of the same bytes in the file you want to encrypt, the XOR key shines through. (hacking code below!)

That means that with a simple perl script you may circumvent that problem.

#!/usr/bin/perl
# Turn of output buffer
$|++;

# The key for XOR decryption
my $key = 'UIERYQWORTWEHLKDNKDBISGLZNCBZCVNBADFIEYLJ' . chr(0);

print "Reading from \"$ARGV[0]\":\n";
$insize = -s $ARGV[0];
# Open the bogus AVI file
open(IN, $ARGV[0]) or die $!;
binmode IN;

# Read Header to check
read(IN, $buffer, 4);
if ($buffer ne 'RIFF') {
    print "  ERROR: \"$ARGV[0]\" is not an AVI\n";
    close IN;
    exit(1);
}
# Get Length of the unencrypted movie
read(IN, $buffer, 4);
$offset = unpack 'L', $buffer;
print "  End of the unencrypted movie is at byte offset $offset\n";

# Jump to the read offset
seek(IN, $offset, 0);

# The next 4 or 8 Bytes seem to be either an unsinged long
# or an unsigned quad. This is another offset to jump
# over some filler bytes. Right now I can't really tell if
# it's 4 or 8 bytes, because I only have 1 file to test with.
# I assume it's a quad.

# low word
read(IN, $buffer, 4);
$offlo = unpack 'L', $buffer;
# high word
read(IN, $buffer, 4);
$offhi = unpack 'L', $buffer;
# Calculate offset
$offset = $offhi * 4294967296 + $offlo;

print "  Offset after the unencrypted movie is $offset\n";
seek(IN, $offset, 0);

# Then there seem to be another 100 filler bytes
# with value 0xff. Jump over those too, to get
# to the offset where the real movie starts.
printf "  Adding extra filler bytes, final offset is %s\n", $offset+100;
seek(IN, 100, 1);

# Update the size
$insize -= $offset+100;

# Open a file for writing the decrypted data to
print "Decrypting to \"$ARGV[1]\":\n";
open(OUT, ">$ARGV[1]");
binmode OUT;
truncate OUT, 0;

$bytes = 0;
$klen = length($key);
# Read key length bytes, decrypt them and
# write them to the output file untill you reach
# the end of the file
while ( read(IN, $buffer, $klen) ) {
    $buffer ^= $key;
    print OUT $buffer;
    $bytes += $klen;
    # print the status
    printf "\r  %d written (% .1f %%)", $bytes, ($bytes / $insize * 100);
}
# Close both files
close OUT;
close IN;
print "\n\nDONE!\n";

Save the code as decode.pl and start the script on the command line with:

perl decode.pl ENCRYPTED_FILE.avi DECRYPTED_FILE.avi

And don't forget to put the file names in quotes if you have spaces in them. decode.pl + VLC or MPlayer worked just fine.

Many thanks to the mininova forums' hackers.