#!perl -w use strict; use IO::File; use Digest::SHA qw(sha1); use Getopt::Long; # this script verifies that the sha1 hashes stored in the first elf section # match the hashes of the other sections in qualcomm gsm binaries my $fixhashes; # the input file can be either a complete gsmv2.nb image, or an extracted .elf file GetOptions( "-f"=>\$fixhashes, ) or die usage(); my $fn=shift or die usage(); sub usage { return "Usage: gsmv2hash [-f] gsmv2.nb\n" ." -f fix incorrect hashes\n" ." gsmv2.nb specifies the file containing the gsmv2 or elf binary\n"; } my $fh= IO::File->new($fn, "r+") or die "$fn: $!\n"; binmode $fh; my $elfbase= find_elfbase($fh); $fh->seek($elfbase, SEEK_SET); my $elf= readelfheader($fh); warn sprintf("hdrsize=0x%x pgmofs=0x%x\n", $elf->{hdrsize}, $elf->{pgmofs}) if $elf->{hdrsize}!=0x34 || $elf->{pgmofs}!=0x34; $fh->seek($elfbase+$elf->{pgmofs}, SEEK_SET); my @pgm; push @pgm, readpgmheader($fh, $elf->{pgmsize}) for 1..$elf->{pgmcount}; my @sect; push @sect, readsectheader($fh, $elf->{sectsize}) for 1..$elf->{sectcount}; $fh->seek($elfbase+$pgm[0]{offset}, SEEK_SET); printf("%08lx elf+%08lx : sha1list\n", $elfbase+$pgm[0]{offset}, $pgm[0]{offset}); my $shalist= readshalist($fh); my @storedsha; push @storedsha, substr($shalist, $_*20, 20) for 0..length($shalist)/20-1; #printf("stor_%02x %s\n", $_, unpack('H*',$storedsha[$_])) for 0..$#storedsha; my @calcedsha; for my $i (1..$#pgm) { my $pgm= $pgm[$i]; my $section; $fh->seek($elfbase+$pgm->{offset}, SEEK_SET); $fh->read($section, $pgm->{filesize}); push @calcedsha, sha1($section); #printf("calc_%02x %s\n", $i-1, unpack("H*", $calcedsha[-1])); } my $allok= 1; for my $i (0..$#calcedsha) { my $pgm= $pgm[$i+1]; if ($calcedsha[$i] ne $storedsha[$i] && $storedsha[$i]ne "\x00" x 20) { $allok= 0; if ($fixhashes) { $fh->seek($elfbase+$pgm[0]{offset}+0x28+$i*20, SEEK_SET); $fh->print($calcedsha[$i]); printf("patching hash at %08lx (%08lx-%08lx)\n", $elfbase+$pgm[0]{offset}+0x28+$i*20, $elfbase+$pgm->{offset}, $elfbase+$pgm->{offset}+$pgm->{filesize}); } else { printf("%08lx (%08lx-%08lx): %2d mismatch: %s != %s\n", $elfbase+$pgm[0]{offset}+0x28+$i*20, $elfbase+$pgm->{offset}, $elfbase+$pgm->{offset}+$pgm->{filesize}, $i, unpack("H*",$calcedsha[$i]), unpack("H*",$storedsha[$i])); } } } if ($allok) { printf("all (%d) sections ok\n", scalar @calcedsha); } sub readshalist { my ($fh)= @_; my $hdr; $fh->read($hdr, 0x28); my @hdr=unpack("V*", $hdr); my $shalist; $fh->read($shalist, $hdr[4]); return $shalist; } sub readelfheader { my ($fh)= @_; my $data; $fh->read($data, 0x34); my (undef, $pgmofs, undef, $hdrsize, $pgmsize, $pgmcount, $sectsize, $sectcount)= unpack("a28Va8v5", $data); return { pgmofs=>$pgmofs, hdrsize=>$hdrsize, pgmsize=>$pgmsize, pgmcount=>$pgmcount, sectsize=>$sectsize, sectcount=>$sectcount, }; } sub readpgmheader { my ($fh, $size)= @_; my $data; $fh->read($data, $size); my %hdr; ( $hdr{type}, $hdr{offset}, $hdr{vaddr}, $hdr{paddr}, $hdr{filesize}, $hdr{memsize}, $hdr{flags}, $hdr{align}, )= unpack("V*", $data); return \%hdr; } sub readsectheader { my ($fh, $size)= @_; my $data; $fh->read($data, $size); my %hdr; ( $hdr{name}, $hdr{type}, $hdr{flags}, $hdr{addr}, $hdr{offset}, $hdr{size}, $hdr{link}, $hdr{info}, $hdr{addralign}, $hdr{entsize}, )= unpack("V*", $data); return \%hdr; } sub find_elfbase { my ($fh)=@_; my $data; $fh->read($data, 0x20); return 0 if substr($data,0,4) eq "\x7f"."ELF"; die sprintf("%08lx: unknown file format: %s\n", $fh->tell()-0x20, unpack 'H*', $data) unless substr($data,0,8) eq "\x07\x4e\x91\x8a\x4b\x36\x55\xec"; $fh->seek(0x40020, SEEK_SET); $fh->read($data, 0x10); die "unexpected block hdr at 0x40000\n" unless substr($data, 0, 8) eq "\xac\x9f\x56\xfe\x7a\x12\x7f\xcd"; $fh->seek(0x41020, SEEK_SET); $fh->read($data, 0x10); die "unexpected page hdr at 0x41000\n" unless substr($data, 0, 8) eq "\x9c\x12\x0f\xfa\xc9\xb6\x8f\x5a"; $fh->seek(0x280020, SEEK_SET); $fh->read($data, 0x10); return 0x280020 if substr($data,0,4) eq "\x7f"."ELF"; die sprintf("%08lx: unknown file format: %s\n", $fh->tell()-0x10, unpack 'H*', $data); }