#!/usr/bin/perl
#
# The official homepage of reap is at http://reap.cjb.net, for what that is worth....
# 
# This program is Copyright grugq && SiuL+Hacky, you can do all the stuff to it
# allowed under the GNU GPL
# 
# There is currently branch development of an objdump beautifier, you can
# get more information on it at http://xirr.com/~squeak.
#   
#
# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.

push(@INC,"/usr/lib/perl5");

use Tk;
require("flush.pl");
require Tk::TextUndo;
require Tk::FileSelect;
require Tk::Menubar;
use Tk::ErrorDialog;
use Getopt::Std;

my @strings;

$target_binary = undef;
$version = "v0.4B ";

getopts('hxql:t:o:');
use vars qw($opt_h $opt_x $opt_q $opt_l $opt_t $opt_o);

if( defined( $opt_h || $opt_x || $opt_q || $opt_l || $opt_t || $opt_o))
{
    if(defined $opt_h)
    {
	&help;
    }
    elsif(defined $opt_x)
    {
	$hex = $opt_x;	
    }
    elsif(defined $opt_l)
    {
	gui($opt_l);
    }
    elsif(defined $opt_q)
    {
	if(defined ($opt_t && $opt_o))
	{
	    dasm($opt_t,$opt_o);
	    exit(0);
	} else {
	    &help;
	}
    }
    elsif(defined $opt_t && $opt_o)
    {
	dasm($opt_t,$opt_o);
	gui($opt_s);
    } else {
	&help;
    }
} else  {
    &gui;
}



sub help {

    print "\t\t\t reap $version                                         \n";
    print "\tthe Reverse Engineer\'s Assembly Producer                  \n";
    print "\t\tby the grugq (&& SiuL+Hacky ;-)                          \n";
    print "\n                                                           \n";
    print " usage: reap < options > < target binary > < disassembly file >";
    print "\n                                                           \n";
    print "\tOptions:                                                   \n";
    print "\t  -h      Display this and exit                    \n";
    print "\t  -t      Target binary to diassemble              \n";
    print "\t          (you also need -o < file > )             \n";
    print "\t  -o      Disassembly file for saving to...        \n";
    print "\t          (implies -t < binary > )                 \n";
    print "\t  -l      Loads a disassembly file into the GUI    \n";
    print "\t  -x      Disassemble a binary and display opcodes \n";                                        
    print "\t  -q      Disassembles a binary to a disassembly   \n";
    print "\t          file and exit, ie no GUI                 \n";
    print "\t          (implies -t < binary > and -o < file > ) \n";
    print "\t                                                   \n";
    print "\t\t\t Nov 27 '99  -grugq                            \n";

    exit(0);
}


    
sub gui {
    $main = MainWindow->new();
    $main->optionAdd('*TextUndo.Background' => '#fff5e1');
    $main->configure( -title => "Reverse Engineer\'s Assembly Producer $version" );
    
    $text = $main->Scrolled(TextUndo, 
			    -wrap => 'none', 
			    -scrollbars => 'osre',
			    -selectbackground => 'yellow',
			    -selectforeground => 'black'
			   )
	->pack( -side => 'bottom', -expand => 'all' );
    ##
    ## If we were passed a file to load, we load it...
    if(defined $_[0])
    {
	$text->Load($_[0]);
    }


    
    my $mframe = $main->Frame()->pack( -side => 'top', -fill => 'x', -expand => 'all' );
    my $file_menu = $mframe->Menubutton(
					-text => 'File',
					-width => '10',
					-menuitems => [
						       ['Button' => '~Disassemble',
							-command => \&disassemble 
							],
						       [Button => '~Load file',
							-command => sub { 
							    my $file = $fs
								->Show(-popover => $top,
								       -create => 0, 
								       -verify => ['-r']
								       );
							    $text->Load($file) if (defined $file);
							}
							],
						       ['Button' => '~Save', 
							-command => [ $text , 'Save' ]
							],
						       ['Button' => 'Save ~As',
							-command => sub { 
							    my $file = $fs->Show(
										 -popover => $top, 
										 -create => 1, 
										 -verify => ['-w']
										 );
							    $text->Save($file) if (defined $file);
							}
							],
						       '-',
						       ['Button' => '~Exit',
							-command => sub { exit; }
							]
						       ]
					)
	->pack( -side => 'left' );
    my $edit_menu = $mframe->Menubutton(
					-text => 'Edit',
					-width => '10',
					-menuitems => 
					[
					 [
					  'Button' => '~Undo',
					  -command => [$text, 'undo']
					  ],
					 '',
					 [
					  'Button' => '~Copy',
					  -command => [$text, 'clipboardCopy']
					  ],
					 [
					  'Button' => 'Cu~t',
					  -command => [$text, 'clipboardCut']
					  ],
					 [
					  'Button' => '~Paste',
					  -command => [$text, 'clipboardPaste']
					  ],
					 '',
					 [
					  'Button' => 'Select All', 
					  -command => [$text, 'selectAll']
					  ],
					 ]
					)
	->pack( -side => 'left' );
    my $search_menu = $mframe->Menubutton(
					  -text => 'View',
					  -width => '10',
					  -menuitems => [
							 ['Button' => '~Strings',
							  -command => \&display_strings
							  ]
							 ]
					  )
	->pack( -side => 'left' );
    
    my $tools_menu = $mframe->Menubutton(
					 -text => 'Tools',
					 -width => 10,
					 -menuitems => [
							[ 'Button' => 'Hexcalc',
							  -command => sub {
							      system( "hexcalc &");
							  }
							  ],
							[ 'Button' => 'Offset',
							  -command => [\&offset, $text]
							  ],
							[ 'Button' => 'Biew',
							  -command => sub {
							      system("biew $target_binary &") if(defined($target_binary));
							  }
							  ],
							]
					 )
	->pack( -side => 'left' );
    
    my $help_menu = $mframe->Menubutton(
					-text => 'Help',
					-width => '10',
					-menuitems => [
						       ['Button' => 'Help...',
							-command => sub {
							}
							],
						       '-',
						       ['Button' => 'About',
							-command => sub {
							    about();
							}
							]
						       ]
					)
	->pack( -side => 'right');
    
######################
## The file select widget is configured...
##
    $fs = $main->Component(
			   FileSelect => 'fs',
			   -selectbackground => 'yellow',
			   -selectforeground => 'black',
			   -width => 25, 
			   -height => 8,
			   '-accept'   => sub 
			   { 
			       my $file = shift ; 
			       return 0 if (-s $file && !(stat(_))[12]);
			       return -x $file;
			       return (-r $file) && (-T $file);  
			   },
			   Name => 'fs', 
			   -filter => '*'
			   );
    MainLoop;
} # end sub gui

####################################################
##  The disassembly dialog.  we need a few things, just the minimum 
##
###################

sub disassemble {
    
    if(! Exists($dasm)) {
    $dasm = $main->Toplevel();
    $dasm->configure(
		     -title => 'Disassemble'
		     );
    ##
    ## The first frame with the target binary location
    ##
    $dasm_frame = $dasm->Frame()->pack( -side => 'top', -fill => 'x' );
    $dasm_frame->Label(
		       -width => 15,
		       -text => 'Target Binary :'
		       )
	->pack(-side => 'left' );
    $dasm_frame->Entry(
		       -selectforeground => 'black',
		       -selectbackground => 'yellow',
		       -width => 25,
		       -textvariable => \$target_binary
		       )
	->pack(
	       -side => 'left'
	       );
    $dasm_frame->Button(
			-text => 'Locate',
			-command => sub {
			    $target_binary = $fs->Show(
						       -popover => 'cursor',
						       -create => 1
						       );
			})
	->pack( -side => 'left' );
    ##
    ## The second frame in dasm, with where we save the diassembly
    ##
    $dasm_second_frame = $dasm->Frame()->pack( -side => 'top', -fill => 'x', -expand => 'all');
    $dasm_second_frame->Label(
			      -width => 15,
			      -text => 'Save to :'
			      )
	->pack( -side => 'left' );
    $dasm_second_frame->Entry(
			      -selectforeground => 'black',
			      -selectbackground => 'yellow',
			      -width => 25,
			      -textvariable => \$saved_file
			      )
	->pack( -side => 'left' );
    $dasm_second_frame->Button(
			       -text => 'Locate',
			       -command => sub {
				   $saved_file = $fs->Show(
							   -popover => $main,
							   -create => 1
							   );
			       })
	->pack( -side => 'left' );

    $dasm_third_frame = $dasm->Frame()->pack( -side => 'top', -fill => 'x' );
    $dasm_third_frame->Button(
			      -text => 'Dismiss',
			      -command => sub {
				  $dasm->withdraw();
			      }
			      )
	->pack( -side => 'left' );
    $dasm_third_frame->Button(
			      -text => 'Disassemble!',
			      -command => sub {
				  if( defined($target_binary) && defined($saved_file) ) {
				      $dasm->withdraw();
				      &dasm($target_binary,$saved_file);
				      $text->Load($saved_file);
				  } else {
				      if(! defined($saved_file)){
					  $saved_file =  "Enter something here!!";
				      } else {
					  $target_binary = "Enter something here!!";
				      }
				  }
			      }
			      )
	->pack( -side => 'right' );
    $dasm_fourth_frame = $dasm->Frame()->pack( -side => 'bottom', -fill => 'x', -expand => 'all' );

    $dasm_fourth_frame->Checkbutton(
				    -variable => \$hex,
				    -text => 'Display opcodes',
				    )
	->pack( -side => 'left' );

} else {
    $dasm->deiconify();
    $dasm->raise();
}
}
############################
##
## The about sub_function, greets, and all that cal
##
############################
sub about {

    if(! Exists($about_box))
    {
	$about_box=$main->Toplevel( -title => 'About');
	$about_text=$about_box->Text(
				     -width => 46,
				     -height => 15,
				     -padx => 2,
				     -pady => 2,
				     -relief => 'ridge'
				     
				     )
	    ->pack( -side => 'top', -expand => 'all' );

	my @about = (

	    ".oOo.___.oOo.___.oO(:reap:)Oo.___.oOo.___.oOo.\n",
	    "                                              \n",
	    "                  reap $version               \n", 
	    "   the Reverse Engineer's Assembly Producer   \n",
	    "                                              \n",
	    "           by the grugq                       \n",
	    "                                              \n",
	    "                                              \n",
	    "   Greets:                                    \n",
	    "          SiuL+Hacky, pug, Allan, Steve, Jon, \n",
	    "          Debrah, Volatility, +ORC, all +HCU, \n",
	    "          the Immortal Descendants, Kara,     \n",
	    "          the whole iST kREW... and bob       \n",
	    "                                              \n",
	    ".oOo.___.oOo.___.oOo.___.oOo.___.oOo.___.oOo. \n"
	    );
	foreach $line (@about)
	{
	    $about_text->insert('end', $line);
	}

	$about_text->configure(
			       -state => 'disabled'
			       );
	$about_box->Button(
			   -text => 'Dismiss',
			   -command => sub { $about_box->withdraw(); }
			   )
	    ->pack( -side => 'bottom', -expand => 'all');
    } else {

	$about_box->deiconify();
	$about_box->raise();
    }
}
	    

##
## This is the function that will show all the strings....
##
sub display_strings {

    if(! Exists($string_box))
    {
	$string_box = $main->Toplevel( -title => 'reap Strings' );
	$string_frame = $string_box->Frame()->pack(
						   -side => 'bottom',
						   -fill => 'x'
						   );
	$string_list = $string_box->Scrolled(
					     'Listbox',
					     -scrollbars => 'osoe',
					     -width => 45,
					     -height => 20
					     )
	    ->pack( -side => 'top', -expand => 'all' );
	$string_list->configure(
				-selectforeground => 'black',
				-selectbackground => 'yellow'
				);
	unless( defined(@strings))
	{
	    open(IN, "< $saved_file");
	    while(<IN>)
	    {
		if(/\"/)
		{
		    push(@strings, $_);
		}
	    }
	    close(IN);
	}
	foreach (@strings)
	{
	    $string_list->insert('end', $_ );
	}

	$string_list->bind('<Double-Button-1>',
			   sub {
			       my $junk = $string_list->curselection();
			       my $string = $string_list->get( $junk );
			       go( $string ) if(defined($string));
			   }
			   );
	
	$string_box->Button(
			    -text => 'Dismiss',
			    -command => sub {
				$string_box->withdraw();
			    }
			    )
	    ->pack( -side => 'bottom', -fill => 'x' );
    } else {
	$string_box->deiconify();
	$string_box->raise();
    }
##
## This is the go to function, no one elsee may use it!!! mine!!
## and I did not, rip it from somewhere.. ;) honest.. really.. I mean it.. true it is.. 
sub go {
    $phrase = shift;
    $index = $text->search(-nocase, "$phrase", '1.0');
    if (defined($index)){
	$text->SetCursor($index);
	$text->focus;
    } else {
	
	$main->Dialog(
		      -title  => 'String not found!!',
		      -text   => $OS_ERROR,
		      -bitmap => 'error',
		      )
	    ->Show;
    }
}
}

sub edit_with {

    if(! Exists($edit_box))
    {
	my $edit_box = $main->Toplevel();

	$edit_f = $edit_box->Frame()->pack( -side => 'top', -fill => 'x' );
	
	$edit_f->Label( -text => 'Open :' )->pack( -side => 'left' );
	$edit_f->Entry(
		       -selectforeground => 'black',
		       -selectbackground => 'yellow',
		       -width => 25,
		       -textvariable => \$file
		       )
	    ->pack( -side => 'left' );
	$edit_box->Button(
			  -text => 'Open',
			  -command => sub {
			      system("gvim $file");
			  }
			  )
	    ->pack( -side => bottom, -fill => 'x' );
    } else {
	$edit_box->deiconify();
	$edit_box->raise();
    }
}

sub offset {
  
    if(! defined($offset_box))
    {
	$offset_box = $main->Toplevel();
	$f_offset = $offset_box->Frame()->pack( -side => 'top', -fill => 'x' );
	$f_offset->Label(
			 -text => 'Line number:   '
			 )
	    ->pack( -side => 'left');
	$f_offset->Entry(
			 -width => 20,
			 -textvariable => \$offset
			 )
	    ->pack( -side => 'left');
	$f_offset->Button(
			  -text => 'Calculate',
			  -command => sub {
			      $gotton = calc( $offset );
			      }
			  )
	    ->pack();
	
	$ff_offset = $offset_box->Frame()->pack( -side => 'top', -fill => 'x' );
	$ff_offset->Label(
			  -border => '2',
			  -relief => 'ridge',
			  -textvariable => \$gotton
			  )
	    ->pack( -fill => 'x' );
	$offset_box->Button(
			-text => 'Dismiss',
			-command => sub
			{
			    $offset_box->withdraw();
			}
			    )
	    ->pack( -side => 'bottom', -fill => 'x' );

	sub calc {
	    
	    my( $text ) = shift;
	    
	    open( IN, "< $save_file" );
	    while(<IN>)
	    {
		if(/.text/)
		{
		    $string = $_;
		    close(IN);
		}
	    }
	    
	    @crap = split(/\s+/, $string);
	    
	    $offset = hex( $crap[ 6 ]);
	    $entry = hex($crap[ 5 ]);
	    $location = hex($text);
	    
	    
	    $tmp = ($location - $entry);
	    $tmp += $offset;
	    
	    return( hex($tmp));
	    
	} 
} else {
	    $offset_box->deiconify();
	    $offset_box->raise();
}
}




sub dasm 
{
    
##################################################################
######## LINUX DISASSEMBLER 2.0799
######## (C) SiuL+Hacky Jul 1999
######## You may copy, modify, distribute this program and
######## is up you to keep this header here
######## Usage: dasm exe_file dasm_file
##################################################################
    my $keys = \@strings;
    my( $f_input, $f_output ) = @_;
    &printflush(STDOUT, "\nCreating disassembled file ...");
    
    my $switches = " -d  -T  -x  --prefix-addresses ";
    if($hex) { $switches .= " --show-raw-insn "};
    
    if(system("objdump ". $switches . $f_input." > ".$f_output."tmp"))
    {
	print "Error: $!\n";
	print "You should see what is wrong, maybe you haven't got objdump in your path, or the target, or the permissions are wrong.\n";
	print "I would suggest you fix that, and then try again... -grugq\n";
	die "\n";
    }
    
    open(INPUT, "< ".$f_output."tmp");
    
    &printflush(STDOUT, "\nReading strings ...");
    $_=<INPUT>;
    while (!/\.rodata/){
	$_=<INPUT>;
	
    }
    ($rubbish, $rest)=split(/.rodata/,$_,2);
    ($rubbish, $rest)=split(/0/,$rest,2);
    @numbers=split(/  /,$rest,5);
    $size=hex($numbers[0]);
    $starting_address=hex($numbers[1]);
    $end_address=$starting_address+$size;
    $offset=hex($numbers[3]);
    open(CODIGO, "< ".$f_input);
    seek(CODIGO,$offset,0);
    read(CODIGO,$cadena,$size);
    close(CODIGO);
    
    
  SEARCH: while (<INPUT>){
      last SEARCH if (/SYMBOL TABLE/);
  }
    if (/SYMBOL TABLE/){ 
	&printflush(STDOUT, "\nProcessing symbol table ...");
	$_=<INPUT>;
	while (!/^\n/){
	    @st_element=split(/ /, $_);
	    $_=$st_element[$#st_element];
	    chop;
	    $symbol_table{$st_element[0]}=$_;
	    $_=<INPUT>;
	}
    }
    else {
	seek(INPUT,0,0);
    }
    
    while (!/\.text/){
	$_=<INPUT>;
    }
    &printflush(STDOUT,  "\nProcessing jmps and calls ...");
    
######### the regex gets rid of possible line information #############
    
    
    while (<INPUT>){
	if( $hex ) {
	    $_=~ s/([a-f0-9][a-f0-9]\s){2,6}/ /g;
	}

	$_=~ s/0x//g;
	$_=~ s/<.*?>//g;
	
	if (/j/){
	    ($direccion,$inst,$destino)=split(/\s+/,$_,3);
	    $destino=~s/ //g;
	    chomp($destino);
	    if($_=~/jmp/) {
		$salto{$destino}.=($direccion." (U)\; ");
	    } else {
		$salto{$destino}.=($direccion." (C)\; ");
	    }
	}
	elsif (/call/){
	    ($direccion,$inst,$destino)=split(/\s+/,$_,3);
	    $destino=~s/ //g;
	    chomp($destino);
	    $call{$destino}.=($direccion." \; ");
	}
    }
    
    seek(INPUT,0,0);
    &printflush(STDOUT, "\nWriting references ...\n");
    open(OUTPUT, "> ".$f_output) || die print "\nError opening write file\n";
    print OUTPUT "FILE REFERENCED: $f_input\n\n";

    
    while (!/Disassembly of section .text:/){
	$_=<INPUT>;
	print OUTPUT;
    }
    $char=".";
    $counter=0;
    while(<INPUT>){
	$counter++;
	if ( ($counter % 400)==0){
	    printflush(STDOUT,$char);
	    if ( ($counter % 4000)==0){
		printflush(STDOUT,"\r");
		if ($char eq "."){ $char=" ";}
		else { $char=".";}
	    }  
	}
	$copia=$_;

	if( $hex ) {
	    $_=~ s/([a-f0-9][a-f0-9]\s){2,6}/ /g;
	}
	
	$_=~s/0x//g;
	$_=~s/<.*?>//ge;

	($direccion, $inst, $destino)=split(/\s+/,$_,3);

	if ( defined( $symbol_table{$direccion} )){
	    print OUTPUT "\n ---- Function : ".$symbol_table{$direccion}." ----\n";
	}
	if (/call/){
	    $destino=~s/\s+//g;
	    chomp($destino);
	    if ( defined( $symbol_table{$destino} )){
		print OUTPUT "\nReference to function : ".$symbol_table{$destino}." \n\n";
	    }
	}  
	if ( defined( $salto{$direccion} ))
	{
	    print OUTPUT "\nReferenced from jump at ".$salto{$direccion}."\n\n";
	}
	if ( defined( $call{$direccion} ))
	{
	    print OUTPUT "Referenced from call at ".$call{$direccion}."\n\n";
	}
	if (/\$/)
	{
	    ($instruccion, $operand)=split(/\$/,$_,2);
	    if (!/push/){
		($operand, $rest)=split(/\,/,$operand,2);
	    }
	    chomp($operand);
	    $offset=hex($operand);
	    if ( ($offset <= $end_address) && ($offset >= $starting_address )
		 ){
		$auxiliar=substr($cadena, $offset-$starting_address);
		$length=index($auxiliar, pack("x") );
		$auxiliar=substr($auxiliar, 0, $length);       
		$auxiliar=~s/\n//g;
		print OUTPUT  "\n\nPossible reference to string: \n\"$auxiliar\"\n\n";
		
		push(@$keys, "$auxiliar");
	    }       
	}
	print OUTPUT $copia;
    }
    close(INPUT);
        
    print "\n";
    system("rm ".$f_output."tmp");
}









