http://qs1969.pair.com?node_id=406786
Category: Miscellaneous
Author/Contact Info X-3mE'89 - <exxtreme(a)altervista.org> http://exxtreme.altervista.org
Description: Extract a C shellcode from any binary program -- you must specify the function and the start/stop addresses
#!/usr/bin/perl
######################################################################
+#######
#                                                                     
+      #
# mksc.pl v0.1 - ShellCode creator - written for Perlmonks.org        
+      #
# Copyright (c) 2004 X-3mE'89 <exxtreme@altervista.org>               
+      #
#                                                                     
+      #
# This program is free software; you can redistribute it and/or modify
+      #
# it under the terms of the GNU General Public License as published by
+      #
# the Free Software Foundation; either version 2 of the License, or   
+      #
# (at your option) any later version.                                 
+      #
#                                                                     
+      #
# This program is distributed in the hope that it will be useful,     
+      #
# but WITHOUT ANY WARRANTY; without even the implied warranty of      
+      #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the       
+      #
# GNU General Public License for more details.                        
+      #
#                                                                     
+      #
# You should have received a copy of the GNU General Public License   
+      #
# along with this program; if not, write to the Free Software         
+      #
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
+  USA #
#                                                                     
+      #
######################################################################
+#######
#                                                                     
+      #
# Usage example:                                                      
+      #
# $ mksc.pl   stupid_program   main   16 72                           
+      #
#        ,_______/           ,__/      /  \_,                         
+      #
#   program containing    function  ,_/      stop address (e.g main+72
+)     #
#     our shellcode         start address                             
+      #
#                           (e.g main+16)                             
+      #
######################################################################
+#######

use strict;

if($ARGV[2] eq '')
{
    die "Usage: $0 <executable> <function> <start_address> <stop_addre
+ss>\n".
        "start_address and stop_address must be in numeric form\n".
        "Example:\n $0 a.out main 16 73\nextracts bytes from main+16 t
+o main+73\n";
}

#
# Our variables
#

my $toexec = $ARGV[0];
my $func   = $ARGV[1];
my $addr   = $ARGV[2];
my $saddr  = $ARGV[3];
my $sc;
my $i;
my $x;
my @data;

#
# Open a file and write instructions
# for gdb to it.
#

open(TEMP,">/tmp/sc.tmp") or die "Couldn't open /tmp/sc.tmp\n";

#
# Start extracting shellcode.
#
print TEMP "x/bx $func+$addr\n";

#
# Continue extracting shellcode
#

for($i=$addr;$i<$saddr;$i++)
{
    print TEMP "\n"
}
#
# Quit gdb.
#
print TEMP "q\n";

#
# Close gdb "script" file.
#

close TEMP;

#
# Run gdb.
#

system("gdb -q $toexec </tmp/sc.tmp>/tmp/scresult.tmp");

#
# Initialize $sc and $i
# ($i is set to -1 so that
# the shellcode will appear
# "regular" if you don't
# understand try changing $i's
# value...)
#

$sc="char ".$func."[]=\n\t\"";
$i=-1;

#
# Read gdb's output.
#

open(ITEMP,"/tmp/scresult.tmp");
@data=<ITEMP>;
close ITEMP;

#
# Get the shellcodes from gdb's
# output using regexps.
#

foreach $x(@data)
{
    if($x=~/^\(gdb\)/)
    {
        $x=~s/\(gdb\) 0x.+ <.+>:\s+//g;
        $x=~s/0x/\\x/g;
        $x=~s/\n//g;
        $x=~s/\(gdb\)//g;
        $x=~s/\s+//g;
        $sc.=$x;
    }
    $i++;
#
# "Indent" the shellcode
# ($i exists only for this)
#
    if(($i%12)==0) 
    {
        $sc.="\"\n\t\""
    }
}

$sc.="\";\n";

#
# Save our shellcode.
#

open(SHELLCODE,">shellcoded.c") or die "Couldn't open shellcoded.c\n";
print SHELLCODE $sc;
close SHELLCODE;

#
# Disk clean-up.
#

system("rm -rf /tmp/sc.tmp /tmp/scresult.tmp");

#
# End.
#
Replies are listed 'Best First'.
Re: mksc.pl
by dragonchild (Archbishop) on Nov 10, 2004 at 21:02 UTC
    Looks cool! A few notes:
    1. Most Perl programs that are meant to be run from the shell use Getopt::Long and Pod::Usage for interfacing with the user. You might want to look at those as they can really simplify your life. Plus, they allow you to easily handle options that are, well, optional. :-)
    2. You use a bunch of tempfiles. The way you're using it means that you cannot safely have two versions of this program running, plus it's not OS-portable the way you've written it. (In fact, it's barely Unix-portable.) I'd recommend using File::Temp which handles all that crap for you.
    3. Although you don't actually use directories and pathnames, I'd also look at File::Spec (and File::Spec::Functions if you don't like the OO interface) for that need.
    4. You use 'rm -rf' instead of the built-in unlink. Why? (Plus, if you use File::Temp, it's taken care of for you.)
    5. It would probably be better to write
      for($i=$addr;$i<$saddr;$i++) { print TEMP "\n" }
      as
      print TEMP $/ for $addr .. $saddr - 1;
      Not only is it more Perlish, but it better documents what it is you're attempting to do.
    6. You don't check the return value for any of your system calls. That's not good ...
    7. Are you charged for your whitespace?
      $x=~s/...//g;
      is hard to read. Why not do something like
      $x =~ s/...//g;
      instead? The compiler doesn't care, but I will. Reading code is hard - you probably want to make it as easy as possible. (Plus, see below for more help in readability.)
    8. Your main loop
      open(ITEMP,"/tmp/scresult.tmp"); @data=<ITEMP>; close ITEMP; foreach $x(@data) { if($x=~/^\(gdb\)/) { $x=~s/\(gdb\) 0x.+ <.+>:\s+//g; $x=~s/0x/\\x/g; $x=~s/\n//g; $x=~s/\(gdb\)//g; $x=~s/\s+//g; $sc.=$x; } $i++; # # "Indent" the shellcode # ($i exists only for this) # if(($i%12)==0) { $sc.="\"\n\t\"" } }
      is probably better written as:
      open( TEMP, "</tmp/scresult.tmp" ) or die "Cannot open: $!\"; my $sc="char ".$func."[]=\n\t\""; my $i = -1; while (<TEMP>) { if(/^\(gdb\)/) { s/\(gdb\) 0x.+ <.+>:\s+//g; s/0x/\\x/g; s/\n//g; s/\(gdb\)//g; s/\s+//g; $sc .= $_; } # "Indent" the shellcode # ($i exists only for this) $i++; unless (++$i%12) { $sc.="\"\n\t\"" } } close TEMP;

      Not only is it clearer what you're doing, but you don't slurp the whole file into memory. What if you're going for 1G of data? Also, you should indent your comments to line up with your code. Otherwise, the whole point of indenting - to show logical groupings - is ruined. When I skimmed your original code, I didn't immediately see the fact that the modulo-check on $i had something to do with the loop.

      Plus, you shouldn't declare a variable until you intend to use it. You declare $sc way up at the top, but don't use it until nearly the end. If you declare it at the top, I'm going to look for usage near the top. If I don't see it, I'm going to start looking for bugs that may not be there.

    Being right, does not endow the right to be rude; politeness costs nothing.
    Being unknowing, is not the same as being stupid.
    Expressing a contrary opinion, whether to the individual or the group, is more often a sign of deeper thought than of cantankerous belligerence.
    Do not mistake your goals as the only goals; your opinion as the only opinion; your confidence as correctness. Saying you know better is not the same as explaining you know better.

Re: mksc.pl
by hv (Prior) on Nov 11, 2004 at 13:28 UTC