IDA Pro helper script for elf binarys

Benjamin Larsson <[email protected]> Tue, 08 Nov 2005 13:26:10 +0100
Newsgroups gmane.comp.video.xine.codec.devel
Message-ID <[email protected]>
Sometimes when you disassemble elf binarys with the freeware version of
IDA pro the software is not able to properly propagate the GOT table
(Global offset table) and thus failing to understand the meaning of
calls. PIC code can have a placeholder with a hexvalue fffffffc (-4).
This confuses IDA pro and fails to properly determine the start and stop
of function and fails to do a good autoanalysis.

Attached is a script that fills in real addresses in the placeholder so
that IDA pro can find the real call functions. External functions don't
have real addresses but IDA pro just adds them after the elf address
space in the order they are found in the symbol tables, this is emulated
in the script. This function might not work correctly depending which
tables (sym or dsym) are availible in the binary.

The script is run like this:

perl gotpropagate.pl -f libwma3.so

The output should look like this:

[...]
Patching offset 00046a40 with current address fcffffff to 73112
Patching offset 00046a78 with current address fcffffff to -7656
[...]

And a libwma3.sobak file should be in the local directory with the same
size as the original.

The code is quite hackish but worked on the file I needed to use it on.

MvH
Benjamin Larsson
gotpropagate.pl (text/plain, 5.4 KB)
#!/usr/bin/perl

# Init av variabler


#Argument parser

if (@ARGV){
  print "@ARGV\n";
  if ($ARGV[0] eq "-f"){
    $FILE1 = $ARGV[1];
  }
}
else{
  print "Usage:\n";
  print "./gotpropagate.pl [command] binary\n";
  print "-f input datafile\n";
  exit;
}

#get extern start address

@rows= `readelf -s libwma3.so | grep -i \"ABS _end\"`;
$externstartaddress = hex(substr $rows[0],8,8);
#print "$externstartaddress|\n";

# get SYMBOL TABLE

@fresource = `objdump -t $FILE1`;
$sym = 0;

@tabaddress = 0;
@tabname = 0;
@tablength = 0;
$tabindex = 1;

#parse out the tables
foreach $rad (@fresource) {
    if ($sym){
        $type = (substr $rad,17,5);
        if ($rad =~ m/text/) {
            #print $rad;
            $address = (substr $rad,0,8);
            #$address =~ m/0*/;              #get rid of starting 0's
            #$address = $';
            $tablelength = (substr $rad,23,8);
            $tablelength =~ m/0*/;
            $tablelength = $';
            $tablen = (substr $rad,32);
            chomp($tablen);
            $funcname = (substr $rad,45);
            chomp($funcname);

            #save the list
            $tabaddress[$tabindex] = hex($address);
            $tabname[$tabindex] = $tablen;
            $tablength[$tabindex] = hex($tablelength);
            $tabindex++;
            $function{$funcname} = $address;
            #print "$address $type $tablelength $funcname\n";
        }
        if ($type eq "*UND*"){
            $address = $externstartaddress;
            $externstartaddress = $externstartaddress+4;
            $funcname = (substr $rad,45);
            if ($funcname =~ m/@/){
                $funcname = $`;
            }
            chomp($funcname);
            $address =   sprintf("%X", "$address");
            $function{$funcname} = $address;
            #print "$address $type $funcname\n";
        }
    }
    if ($rad =~ m/SYMBOL/) {
        $sym = 1;
    }
}


# get DYNAMIC SYMBOL TABLE

@fresource = `objdump -T $FILE1`;
$dsym = 0;

@tabaddress = 0;
@tabname = 0;
@tablength = 0;
$tabindex = 1;

#parse out the tables
foreach $rad (@fresource) {
    if ($dsym){
        $type = (substr $rad,17,5);
        if ($rad =~ m/text/) {
            #print $rad;
            $address = (substr $rad,0,8);
            #$address =~ m/0*/;              #get rid of starting 0's
            #$address = $';
            $tablelength = (substr $rad,23,8);
            $tablelength =~ m/0*/;
            $tablelength = $';
            $tablen = (substr $rad,32);
            chomp($tablen);
            if ($rad =~ m/Base/) {
                $funcname = (substr $rad,45);
            } else {
                $funcname = (substr $rad,32);
            }
            chomp($funcname);

            #save the list
            $tabaddress[$tabindex] = hex($address);
            $tabname[$tabindex] = $tablen;
            $tablength[$tabindex] = hex($tablelength);
            $tabindex++;
            $function{$funcname} = $address;
            #print "$address $type $tablelength $funcname\n";
        }

    }
    if ($rad =~ m/SYMBOL/) {
        $dsym = 1;
    }
}


# File IO
# get the GOT table from the elf so

open(RESFILE, ">fresource.txt") or die "File doesn't exist\n";
@fresource = `objdump -R $FILE1`;
$ii = 0;
foreach $rad (@fresource)
{
    $address = (substr $rad,0,8);
    $type = (substr $rad,9,10);
    if ($type eq "R_386_PC32")
    {
        #print STDOUT "$rad";
        $fname = (substr $rad,27);
        chomp($fname);
        #$address =~ m/0*/;      #get rid of starting 0's
        #$address = $';
        #$address =~ tr/a-f/A-F/;
        $seekpos[$ii] = $address;
        $readdr[$ii] = $function{$fname};
        $ii++;
        $ofset = hex($function{$fname})- hex($address);
        print RESFILE "$address $function{$fname} - $fname $ofset\n";
        $functioncall{$address} = hex($function{$fname}) - hex($address)-4;
    }
}
close RESFILE;


#Get .data offset
$doffset = 0;
@offsets = `readelf -S $FILE1`;
foreach $rad (@offsets) {
    if ($rad=~ m/ \.text/) {
        $dataaddress = (substr $rad,41,8);
        $dataaddress =~ m/0*/;
        $dataaddress = $';
        $dataoffset = (substr $rad,50,6);
        $dataoffset =~ m/0*/;
        $dataoffset = $';
        #print "$dataaddress $dataoffset|\n";
        $doffset = hex($dataaddress) - hex($dataoffset);
    }
}


#Calculate real address
$seekaddress = hex($seekpos[0]) - $doffset;

#print "$seekpos[16] $readdr[16]\n";

open(rf, $FILE1) or die "can't open $FILE1: $!";
binmode rf;
open(wf, ">$FILE1"."bak") or die "open $FILE1  $!";
binmode wf;


#sysseek(rf, $seekaddress, 0) or die "seek:$!";
#seek(BINFILE,hex(c61),0) or die "seek:$!";
#sysread(rf, $BUFFER, 4);

#@fields = unpack("H*",$BUFFER);
$oldpos =0;

foreach $key (sort keys %functioncall) {
    if ($functioncall{$key} eq ""){
    } else {
        $pos = hex($key)-$oldpos; #-1;
        sysread(rf, $buf, $pos);
        syswrite(wf, $buf, length($buf));
        sysread(rf, $BUFFER, 4);
        $curvalue = unpack("H*",$BUFFER);
        print "Patching offset $key with current address $curvalue to $functioncall{$key}\n";
        $newjumpadr = pack("i*","$functioncall{$key}");
        syswrite(wf, $newjumpadr, 4);
        #@fields = unpack("H*",$newjumpadr);
        #print "|$fields[0]|\n";
        $oldpos = $oldpos+$pos+4;
    }
}

while (sysread(rf, $buf, 1024)){
    syswrite(wf, $buf, length($buf));
}
close rf;
close wf;

#$BUFFER = "DEADBEEF";
#$test = unpack("h*",pack("h*",$BUFFER));
#print $test;
print "Done!\n";