#!/usr/bin/perl
###############
##
# Name: msfdldebug
# Author: H D Moore <hdm [at] metasploit.com>
# Version: $Revision: 1890 $
# Description: Download debug symbols for a given DLL or EXE
# License:
#
# This file is part of the Metasploit Exploit Framework
# and is subject to the same licenses and copyrights as
# the rest of this package.
#
# Notes:
# This script has been tested with the DLL files that ship with Windows NT 4.0.
# Windows 2000, and Windows XP. It does not support the RSDS .NET type yet,
# nor does it actually extract any of the symbols from the files it downloads.
# Seperate extraction tools will be made available as they are completed. At
# this point I plan on releasing a symbol extractor for the unstripped images,
# NB09 debug files, and NB10 program database files. For the download to work
# you must already have wget and cabextract in your path, they available from:
#
# wget: http://wget.sunsite.dk/
# cabextract: http://www.kyz.uklinux.net/cabextract.php
#
##
require 5.6.0;
use strict;
use Getopt::Std;
use FindBin qw{$RealBin};
use lib "$RealBin/lib";
use Pex::PEInfo;
use Pex;
no utf8;
no locale;
my %opts;
getopts('h', \%opts);
if ($opts{'h'}) {
Usage();
}
my $target = shift() || Usage();
my $pe = Pex::PEInfo->new($target);
my $dbg_idx = 0;
my $dbg_skp = 0;
my $dbg_hsh;
my $dbg_raw;
my $pdb_hsh;
my ($exe_nb09, $exe_nb10, $exe_nb11);
my $valid_nb10 = 0;
if (! $pe)
{
print "[*] Error loading the executable image.\n";
exit(0);
}
my $filename = lc($target);
$filename =~ s/.*\/(.*)/$1/g;
my $raw = $pe->Raw();
$dbg_hsh = sprintf("%.8x%x", $pe->ImageHeader("TimeDateStamp"), $pe->OptImageHeader("SizeOfImage"));
if ($pe->ImageHeader("NumberOfSymbols") > 0 && $pe->ImageHeader("PointerToSymbolTable") > 0)
{
printf("[*] This file contains " . $pe->ImageHeader("NumberOfSymbols") .
" symbols at offset 0x%.8x.\n", $pe->ImageHeader("PointerToSymbolTable"));
}
my $debug_rva = $pe->Rva("debug");
if (! $debug_rva->[0])
{
print " [ RVA ] " . join("\n", $pe->Rvas()) . "\n";
print "[*] No debug directory found, something is seriously wrong...\n";
exit(0);
}
my $debug_off = $pe->VirtualToOffset($debug_rva->[0]);
my $debug_dat = substr($raw, $debug_off, $debug_rva->[1]);
my $debug_tds = substr($debug_dat, 4);
my $debug_typ = unpack("V", substr($debug_dat, 12));
my $debug_siz = unpack("V", substr($debug_dat, 16));
my $debug_dir = unpack("V", substr($debug_dat, 24));
# printf("DEBUG: TYPE=0x%.8x SIZE=0x%.8x OFFSET=0x%.8x\n", $debug_typ, $debug_siz, $debug_dir);
if ($debug_typ == 2 && substr($raw, $debug_dir, 4) eq "NB10")
{
print "[*] Executable contains a NB10 signature and does not require a DBG file.\n";
$dbg_skp++;
$dbg_idx = $exe_nb10;
}
if (! $dbg_skp)
{
my $dbg_fn = $filename;
$dbg_fn =~ s/(.*)\..../$1\.dbg/;
my $dbg_fn_com = $dbg_fn;
$dbg_fn_com =~ s/\.dbg/\.db\_/;
if (! -r $filename . "_" . $dbg_hsh . "._")
{
my $url = "http://msdl.microsoft.com/download/symbols/$dbg_fn/$dbg_hsh/$dbg_fn_com";
print "[*] Downloading DBG file: $url\n";
unlink($dbg_fn_com);
open(X, "wget -q $url|") || die "wget: $!";
while(<X>)
{
chomp;
next if !length($_);
print "[*] WGET> $_\n";
}
close(X);
if (! -r $dbg_fn_com)
{
print STDERR "[*] Download failed\n";
exit(0);
}
rename($dbg_fn_com, $filename . "_" . $dbg_hsh . "._");
unlink($dbg_fn_com);
}
unlink($dbg_fn);
open(X, "cabextract " . $filename . "_" . $dbg_hsh . "._|") || die "cabextract: $!";
while(<X>)
{
chomp;
next if !length($_);
print "[*] CAB> $_\n";
}
close(X);
if (! -f $dbg_fn)
{
print STDERR "[*] Could not extract debug file.\n";
exit(0);
}
open(X, "<$dbg_fn");
while (<X>) { $dbg_raw .= $_ }
close (X);
$dbg_idx = index($dbg_raw, "NB09");
if ($dbg_idx)
{
print "[*] This DBG file contains a NB09 symbol table, no PDB needed.\n";
exit(0);
}
$debug_dir = index($dbg_raw, "NB10");
if (! $debug_dir)
{
print "[*] No NB10 CodeView segment found in the debug file, giving up.\n";
exit(0);
}
} else {
$dbg_raw = $raw;
}
$pdb_hsh = sprintf("%.8x%x", unpack("V", substr($dbg_raw, $debug_dir + 8)), unpack("V",substr($dbg_raw, $debug_dir + 12)));
if ($pdb_hsh)
{
my $dbg_fn = $filename;
$dbg_fn =~ s/(.*)\..../$1\.pdb/;
my $dbg_fn_com = $dbg_fn;
$dbg_fn_com =~ s/\.pdb/\.pd\_/;
if (! -r $filename . "_" . $pdb_hsh . "._")
{
my $url = "http://msdl.microsoft.com/download/symbols/$dbg_fn/$pdb_hsh/$dbg_fn_com";
print "[*] Downloading PDB file: $url\n";
unlink($dbg_fn_com);
open(X, "wget -q $url|") || die "wget: $!";
while(<X>)
{
chomp;
next if !length($_);
print "[*] WGET> $_\n";
}
close(X);
rename($dbg_fn_com, $filename . "_" . $pdb_hsh . "._");
unlink($dbg_fn_com);
}
unlink($dbg_fn);
open(X, "cabextract " . $filename . "_" . $pdb_hsh . "._|") || die "cabextract: $!";
while(<X>)
{
chomp;
next if !length($_);
print "[*] CAB> $_\n";
}
close(X);
if (! -f $dbg_fn)
{
print STDERR "[*] Could not extract debug file.\n";
exit(0);
}
}
sub Usage {
print STDERR "Usage: $0 <file>\n";
exit(0);
}