#!/usr/bin/perl
# disassemble alien binary blobs
# look for "ldr .., [pc + #nn]" etc.
# and add strings and values it refers to
#
# (c) 2008 chr
# GPL V3+
#
# v0.2.1:
# * create labels for branch targets
# v0.2:
# * catch unaligned strings
# * note on strings
# * check for integer overflow
# use Data::Dumper;
# $Data::Dumper::Sortkeys = 1;
# Added to support execution of disassembler.pl
# when not in the same folder as binary file to
# be disassembled.
use Cwd;
$firmware_basepath = getcwd;
# adjust these for your needs (note final slash):
#$path = "$ENV{'HOME'}/gcc-4.1-arm/bin/";
$path = "";
# note on "strings": default is a minimum length of 4 chars.
# So if u are hunting for e.g. "FI2" add -n3
# However, it gives a lot of false positive.
$strdump = "strings -t x";
$objdump = "${path}arm-elf-objdump";
$objcopy = "${path}arm-elf-objcopy";
if (@ARGV != 2) {
die("Usage: $0 0x<offset> <dump.bin>");
}
$offset = $ARGV[0];
$binfile = $ARGV[1];
$firmware_file_path = "$firmware_basepath/$ARGV[1]";
# check if we wrap over
die "error stat($firmware_file_path): $!" unless ($flen = (stat($firmware_file_path))[7]);
if ( hex($offset) + $flen > 0xffffffff) {
die "offset + filesize > 0xffffffff. We can't wrap around!\n\ngame over"
}
#####
print "string dump\n";
my %strings;
open(IN, "$strdump \"$firmware_file_path\" |") or die "cannot start $strdump \"$firmware_file_path\": $!";
open(OUT,">$firmware_file_path.strings") or die "cannot write to $firmware_file_path.strings: $!";
while (<IN>) {
/^ *([[:xdigit:]]*) (.*)/;
my $addr = hex($1) + hex($offset);
my $addr_str = sprintf("%08x", $addr);
$strings{$addr_str} = $2;
print OUT "$addr_str $2\n";
# align string address so unaligned strings appears in disassemble
$addr_str = sprintf("%08x", $addr & ~0x3);
my $offs = $addr & 0x3;
$strings{$addr_str} = '.' x $offs . $2;
}
close IN;
close OUT;
#$strings{'ff810164'} = "TEST test";
#$strings{'ff810420'} = "add test";
#print Dumper(\%strings);
#exit;
#####
print "create elf file\n";
`$objcopy --change-addresses=$offset -I binary -O elf32-littlearm -B arm \"$firmware_file_path\" \"$firmware_file_path.elf\"`;
`$objcopy --set-section-flags .data=code,load,alloc,content \"$firmware_file_path.elf\"`;
#####
print "label scan\n";
my %labels;
open(IN, "$objdump -d \"$firmware_file_path.elf\" |")
or die "cannot start $objdump \"$firmware_file_path\": $!";
open(OUT,">$firmware_file_path.labels") or die "cannot write to $firmware_file_path.labels: $!";
while (<IN>) {
if (my ($addr, $dest) = $_ =~ /^ *([[:xdigit:]]+):[ \t]+[[:xdigit:]]+[ \t]+[Bb][[:alpha:]]*[ \t]+([[:xdigit:]]+)/) {
if ($labels{$dest} lt 1) {
print OUT "$dest ($addr)\n";
}
$labels{$dest} += 1;
print "\r0x$addr ";
}
}
close IN;
close OUT;
#####
print "\ndisassemble and string lookup\n";
open(IN, "$objdump -d \"$firmware_file_path.elf\" |")
or die "cannot start $objdump \"$firmware_file_path\": $!";
open(OUT,">$firmware_file_path.dis") or die "cannot write to $firmware_file_path.dis: $!";
open(BIN, "<$firmware_file_path") or die "cannot read $firmware_file_path";
binmode BIN;
while (<IN>) {
if ($_ eq " ...\n") { print OUT $_; next;}
my ($addr, $line) = $_ =~ /^ *([[:xdigit:]]*):(.*)/ or next;
# ff810b98: e51f2060 ldr r2, [pc, #-96] ; ff810b40 <_binary_dump_bin_start+0xb40>
# ff815dd4: e28f10dc add r1, pc, #220 ; 0xdc
if (
($line =~ /^(.*\tldr.*\[pc, #([-\d]+).*; )/) ||
($line =~ /^(.*\tadd.*pc, #([-\d]+).*; )/)
) {
$line = $1;
my $off = hex($addr) - hex($offset) + $2 + 8;
my $point = sprintf("%08x", hex($addr) + $2 + 8);
my $value = &get_word($off);
$line .= "$point: ($value) ";
if (my $str = $strings{$point}) {
# add pointed string
$line .= qq| *"$str"|;
}
elsif (my $str = $strings{$value}) {
# pointer to pointer ...
$line .= qq| **"$str"|;
}
}
# ff815e1c: e24f0090 sub r0, pc, #144 ; 0x90
elsif ($line =~ /^(.*\tsub.*pc, #([-\d]+).*; )/) {
$line = $1;
my $off = hex($addr) - hex($offset) - $2 + 8;
my $point = sprintf("%08x", hex($addr) - $2 + 8);
my $value = &get_word($off);
$line .= "$point: ($value) ";
if (my $str = $strings{$point}) {
$line .= qq| *"$str"|;
}
elsif (my $str = $strings{$value}) {
$line .= qq| **"$str"|;
}
}
# ff81015c: 3afffffc bcc ff810154 <_binary__blah...>
elsif ($line =~ /^([ \t]*[[:xdigit:]]+[ \t]+[Bb][[:alpha:]]*[ \t]+)([[:xdigit:]]+)/) {
$line = "$1loc_$2"
}
# insert label
if ($labels{$addr} gt 1) {
print OUT "loc_$addr: ; $labels{$addr} refs\n";
} elsif ($labels{$addr} gt 0) {
print OUT "loc_$addr:\n";
}
# add string comment
if (my $str = $strings{$addr}) {
print OUT qq|"$str":\n|;
}
print OUT "$addr: $line\n";
print "\r0x$addr ";
}
close IN;
close OUT;
#####
print "\njob complete!\n";
sub get_word {
my $off = shift;
my $ret;
seek(BIN, $off, 0);
my $c = read(BIN, $ret, 4);# or die "off: $off $! ($ret)";
return ($c > 0 ? sprintf("%08x", unpack("I", $ret)) : '???');
}
Advertisement
GPL:disassemble.pl
Advertisement