File Coverage

blib/lib/App/dumpbin.pm
Criterion Covered Total %
statement 50 50 100.0
branch 13 16 81.2
condition 3 3 100.0
subroutine 6 6 100.0
pod 0 2 0.0
total 72 77 93.5


line stmt bran cond sub pod time code
1             package App::dumpbin 0.02 {
2 3     3   133985 use 5.008001;
  3         34  
3 3     3   16 use strict;
  3         4  
  3         63  
4 3     3   14 use warnings;
  3         6  
  3         80  
5 3     3   2296 use Path::Tiny;
  3         37922  
  3         1754  
6             #
7             my %sections;
8              
9             sub exports {
10 2     2 0 782 my $file = shift;
11 2         6 my $raw = Path::Tiny->new($file)->slurp_raw;
12             #
13 2 50       522 exit 99 if 0x5A4D != unpack 'v', substr $raw, 0, 2; # check signature
14 2         8 my $peo = unpack 'V', substr $raw, 0x3C, 4;
15 2 50       9 exit 99 if "PE\0\0" ne substr $raw, $peo, 4;
16             #
17 2         10 my ( $sizeOfOptionalHeader, undef, $magic ) = unpack 'vvv', substr $raw, $peo + 20, 8;
18 2 50       6 return if !$sizeOfOptionalHeader; # No optional COFF and thus no exports
19 2         6 my $pe32plus = $magic == 0x20b; # 32bit: Ox10b 64bit: 0x20b ROM?: 0x107
20 2         7 my $opt_header = substr $raw, $peo + 24, $sizeOfOptionalHeader;
21              
22             # COFF header
23 2         7 my $numberOfSections = unpack 'v', substr $raw, $peo + 6, 2;
24              
25             # Windows "optional" header
26 2 100       11 my $imageBase = $pe32plus ? unpack 'Q', substr $opt_header, 24, 8 : unpack 'V',
27             substr $opt_header, 28, 4;
28 2 100       7 my $numberOfRVAandSizes = unpack 'V', substr $opt_header, ( $pe32plus ? 108 : 112 ), 4;
29             {
30 2         4 %sections = ();
  2         5  
31 2         6 my $sec_begin = $peo + 24 + $sizeOfOptionalHeader;
32 2         6 my $sec_data = substr $raw, $sec_begin, $numberOfSections * 40;
33 2         10 for my $x ( 0 .. $numberOfSections - 1 ) {
34 13         22 my $sec_head = $sec_begin + ( $x * 40 );
35 13         29 my $sec_name = unpack 'Z*', substr $raw, $sec_head, 8;
36 13         99 $sections{$sec_name} = [ unpack 'VV VVVV vv V', substr $raw, $sec_head + 8 ];
37             }
38             }
39              
40             # dig into directory
41 2 100       10 my ( $edata_pos, $edata_len ) = unpack 'VV', substr $opt_header, $pe32plus ? 112 : 96, 8;
42 2         10 my @fields = unpack 'V10', substr $raw, rva2offset($edata_pos), 40;
43 2         8 my ( $ptr_func, $ptr_name, $ptr_ord ) = map { rva2offset( $fields[$_] ) } 7 .. 9;
  6         12  
44 2         8 my %retval = ( name => unpack 'Z*', substr $raw, rva2offset( $fields[3] ), 256 );
45 2         14 my @ord = unpack 'V' x $fields[5], substr $raw, $ptr_func, 4 * $fields[5];
46 2         7 for my $idx ( 0 .. $fields[5] ) {
47 6         16 my $ord_cur = unpack 'v', substr $raw, $ptr_ord + ( 2 * $idx ), 2;
48 6         9 my $func_cur = $ord[$ord_cur]; # Match the ordinal to the function RVA
49 6 100       16 next if $idx > ( $fields[6] - 1 );
50 4         10 my $name_cur = unpack 'V', substr $raw, $ptr_name + ( 4 * $idx ), 4;
51 4         12 my $name_str = unpack 'Z*', substr $raw, rva2offset($name_cur), 512;
52 4         9 $ord_cur += $fields[4]; # Add the ordinal base value
53 4         15 $retval{exports}{$name_str} = [ $func_cur + $imageBase, $ord_cur ];
54             }
55 2         15 %retval;
56             }
57              
58             sub rva2offset {
59 14     14 0 63 my ($virtual) = @_;
60 14         33 for my $section ( values %sections ) {
61 63 100 100     165 if ( ( $virtual >= $section->[1] ) and ( $virtual < $section->[1] + $section->[0] ) ) {
62 14         52 return $virtual - ( $section->[1] - $section->[3] );
63             }
64             }
65             }
66             };
67             1;
68             __END__