#!perl -w use strict; use IO::File; use Getopt::Long; # -- shared memory buffer # ~/phones/htc_diamond/devices/dutch/about-smem.txt # ~/phones/htc_kaiser/devices/shmem-dump.txt # ~/phones/htc_diamond/devices/dutch/hex-phys01f.txt # ~/phones/htc_kaiser/devices/dpram-dump.txt # # -- wince section of flash # ~/phones/htc_diamond/devices/dutch/readme.txt # ~/phones/htc_kaiser/devices/readme-allflash.txt # # -- gsm section of flash # ~/phones/htc_kaiser/devices/readme-gsmflash.txt # ~/phones/htc_kaiser/gsm/1.27.12.11-22.45.88.07/readme-oemsbl.txt # ~/phones/htc_kaiser/gsm/1.27.12.11-22.45.88.07/readme-radio.txt # # -- elf binary in gsm section # ~/phones/htc_kaiser/gsm/1.27.12.11-22.45.88.07/elf-header.dump # ~/phones/htc_kaiser/gsm/1.27.12.11-22.45.88.07/elf-segsummary.txt # ~/phones/htc_kaiser/gsm/gsm-1.58.25.17/elf-headers.txt # ~/phones/htc_kaiser/gsm/gsm-1.58.25.17/readme-elf.txt # # -- efs in gsm section # ~/phones/htc_diamond/devices/dutch/flash/nand_efs-hexdump.txt # ~/phones/htc_diamond/devices/dutch/flash/nand_htc-hexdump.txt # ~/phones/htc_kaiser/devices/efs-allpages.txt # ~/phones/htc_kaiser/devices/efs-dump.txt # # -- flashdrv driver # ~/phones/htc_kaiser/devices/filesys/readme.txt # ~/phones/htc_kaiser/devices/nandctrl-values.txt my $do_extract=0; my $elf_savename= "elf.nb"; my $qcsbl_savename= "qcsbl.nb"; my $oemsbl_savename= "oemsbl.nb"; GetOptions( "e"=>sub { $do_extract=15; }, "elf=s"=>sub { $do_extract|=4; $elf_savename=$_[1]; }, "qcsbl=s"=>sub { $do_extract|=2; $qcsbl_savename=$_[1]; }, "oemsbl=s"=>sub { $do_extract|=1; $oemsbl_savename=$_[1]; }, ) or die usage(); sub usage { return <<__EOF__ Usage: parsegsmv2 [options] gsmv2.nb -e : extract all -elf=NM : extract only elf binary -qcsbl=NM : extract only qcsbl -oemsbl=NM: extract only oemsbl __EOF__ } processfile($_) for @ARGV; # file consists of blocks of size 0x20000, containing pages of size 0x800, # 00000000: 83838383 73d71034 00000003 page.qcsbl info # 00020000: aa01de33 dbfe99af 00000001 page.unknown # 00040000: fe569fac cd7f127a 00000003 page.dir blocks # # 00040800: 55ee73aa e35ebddb 00000002 dir#1, contains blocknums # 00041000: fa0f129c 5a8fb6c9 00000001 .. # 00041800: oemsbl + elf offsets # 00044800: aa7d1b9a 1f7d48bc 00000002 dir#2 # 00045000: 9d41bea1 f1ded2ea 00000001 checksum? # # 00060000: fe569fac cd7f127a 00000003 page.dir blocks # # 00061000: fa0f129c 5a8fb6c9 00000001 .. # 00061800: oemsbl + elf offsets # 00064800: aa7d1b9a 1f7d48bc 00000002 dir#2 # 00065000: 9d41bea1 f1ded2ea 00000001 checksum? # # --- qcsbl # 00140000: 5fe85ddf 5264cebc 00000000 page.qcsbldata # --- oemsbl1 # 001c0000: fa0f129c 5a8fb6c9 00000000 page.oemsbldata # 001e0000: fa0f129c 5a8fb6c9 00000000 page.oemsbldata # 00200000: fa0f129c 5a8fb6c9 00000000 page.oemsbldata # # --- oemsbl2 # 00280000: fa0f129c 5a8fb6c9 00000000 page.oemsbldata # 002a0000: fa0f129c 5a8fb6c9 00000000 page.oemsbldata # 002c0000: fa0f129c 5a8fb6c9 00000000 page.oemsbldata # --- amss # 00540000: 464c457f 00010101 00000000 # # # magic number summary: # -- fileheader # 8a914e07 ec55364b : file header # -- block headers # 83838383 73d71034 : qcsbl info # aa01de33 dbfe99af : 1, 0 # fe569fac cd7f127a : 3, {1, 2} mibib # 5fe85ddf 5264cebc : qcsbl data # page headers # 55ee73aa e35ebddb : dir #1 # fa0f129c 5a8fb6c9 : name + oemsbl + elf ofs # aa7d1b9a 1f7d48bc : dir #2 # 9d41bea1 f1ded2ea : checksum ? # # 464c457f : elf header # ffffffff ffffffff : empty # todo: process file as blocks containing pages. sub processfile { my ($fn)=@_; my $fh=IO::File->new($fn, "r") || die "$fn: $!\n"; binmode $fh; my %hdr; my $hdrdata; $fh->read($hdrdata, 32) || die "read hdr: $!\n"; my @hfields= unpack('V*',$hdrdata); if ($hfields[0]!=0x8a914e07 || $hfields[1]!=0xec55364b) { # try again later $fh->seek(0, 0); $hdr{bodysize}= -s $fh; } else { $hdr{timestamp}= $hfields[2]; # 3 = ? $hdr{bodysize}= $hfields[4]; $hdr{sigsize}= $hfields[5]; # 6 = ? signature keyid # 7 = ? printf("fileheader: %08lx %08lx %08lx\n", @hfields[3,6,7]); } my $pagehandler; my $ofs= 0; while (!$fh->eof() && $ofs<$hdr{bodysize}) { my $page; $fh->read($page, 0x800); $ofs += 0x800; my @magic= unpack("VV", $page); if ($pagehandler) { $pagehandler->($page); } elsif ($magic[0]==0x8a914e07 && $magic[1]==0xec55364b) { warn sprintf("unexpected fileheader at %08lx\n", $ofs+0x20); } elsif (($magic[0]==0x83838383 && $magic[1]==0x73d71034) || ($magic[0]==0x73d71034)) { # qcsbl info my $bl= $hdr{qcsbl}= {}; my @f= unpack("V12", $page); # 2 = ? $bl->{headersize}= $f[3]; # 4 = ? $bl->{baseaddr}= $f[5]; $bl->{imagesize}= $f[6]; if ($f[6]!=$f[7] || $f[6]+$f[5]!=$f[8] || $f[8]!=$f[10]) { warn sprintf("unexpected qcsbl header values\n"); } # 9 = ? #10 == field 8 #11 = ? #12 .. headersize : unknown data # followed by FF my $qcsbldata= substr($page, 0x30); # read rest of qcsbl info $pagehandler= sub { $qcsbldata .= $page; if (length($qcsbldata)>=$hdr{qcsbl}{headersize}) { undef $pagehandler; $hdr{qcsbl}{data}= substr($qcsbldata, 0, $hdr{qcsbl}{headersize}); } }; } elsif ($magic[0]==0xaa01de33 && $magic[1]==0xdbfe99af) { # ? 1,0 printf("unknown1: %8x %8x\n", unpack("VV", substr($page, 8, 8))); # page padded with 0x00, block padded with 0xff pages. } elsif ($magic[0]==0xfe569fac && $magic[1]==0xcd7f127a) { # ? 3,{1,2} printf("unknown2: %8x %8x 'mibib' ?\n", unpack("VV", substr($page, 8, 8))); # page padded with 0x00 # followed by pages containing: dir, oemsbl info, checksum } elsif ($magic[0]==0x55ee73aa && $magic[1]==0xe35ebddb) { # dir#1 # page padded with 0xff my ($type, $n)=unpack("VV", substr($page, 8, 8)); die sprintf("@%08lx - n too large: %08lx\n", $fh->tell()-0x800, $n) if ($n>0x100); if (defined $hdr{dir}{type} && $hdr{dir}{type}!=$type) { printf("WARN@%08lx: dir2.type!=dir1.type: %d != %d\n", $fh->tell()-0x800, $hdr{dir}{type}, $type); } if (defined $hdr{dir}{type} && @{$hdr{dir}{entries}}!=$n) { printf("WARN@%08lx: dir2.n!=dir1.n: %d != %d\n", $fh->tell()-0x800, scalar @{$hdr{dir}{entries}}, $n); } $hdr{dir}{type} ||= $type; for (my $i=0 ; $i<$n ; $i++) { my $ent= $hdr{dir}{entries}[$i] ||= {}; my ($name, $ofs, $size, $attr)= unpack("A16VVV", substr($page, 16+0x1c*$i, 0x1c)); if (exists $ent->{name} && $ent->{name} ne $name) { printf("WARN@%08lx: dirX.n!=dir2.n: %s != %s\n", $fh->tell()-0x800, $ent->{name}, $name); } $ent->{name} ||= $name; $ent->{ofs} ||= $ofs; $ent->{size} ||= $size; $ent->{attr} ||= $attr; } } elsif ($magic[0]==0xfa0f129c && $magic[1]==0x5a8fb6c9) { # dword 2 : type # page padded with 0x00 # block padded with 0xff # # 1-> followed by pages containing oemsbl + elf offsets # 0-> followed by pages containing oemsbl data # # if (unpack("V", substr($page, 8,4))) { # 1 -> name + oemsbl+elf ofs # page $pagehandler= sub { my $bl= $hdr{oemsbl}= {}; my @f= unpack("V10", $_[0]); $bl->{baseaddr}= $f[3]; $bl->{imagesize}= $f[4]; if ($f[4]!=$f[5] || $f[4]+$f[3]!=$f[6] || $f[6]!=$f[8]) { warn sprintf("unexpected oemsbl header values\n"); } $pagehandler= sub { my $bl= $hdr{elf}= {}; my @f= unpack("V10", $_[0]); $bl->{baseaddr}= $f[3]; $bl->{imagesize}= $f[4]; if ($f[4]!=$f[5] || $f[4]+$f[3]!=$f[6] || $f[6]!=$f[8]) { warn sprintf("unexpected elf header values\n"); } undef $pagehandler; }; }; } else { # 0 -> data # read data in 0x1f800 sized chunks. if (!$pagehandler) { my $save= IO::File->new($oemsbl_savename, "w") or die "$oemsbl_savename $!\n" if ($do_extract&1); binmode $save if ($save); my $savesize= $hdr{oemsbl}{imagesize}; printf("%08lx oemsbl %08lx %08lx\n", $fh->tell()-0x800, $hdr{oemsbl}{baseaddr}, $hdr{oemsbl}{imagesize}); $pagehandler= sub { my $page= shift; my @magic= unpack("VV", $page); if (length($page)>$savesize) { $page= substr($page, 0, $savesize); } if ($save && ($save->tell()%0x1f800)==0 && $magic[0]==0xfa0f129c && $magic[1]==0x5a8fb6c9 ) { # skip markers printf("marker @%08lx\n", $fh->tell()-0x800); return; } $save->print($page) if ($save); $savesize -= length($page); if ($savesize==0) { $save->close() if ($save); printf("%08lx: end of oemsbl\n", $fh->tell()-0x800+length($page)); undef $save; undef $pagehandler; } # todo: write left over data }; } } } elsif ($magic[0]==0xaa7d1b9a && $magic[1]==0x1f7d48bc) { # dir#2 # page padded with 0xff my ($type, $n)=unpack("VV", substr($page, 8, 8)); die sprintf("@%08lx - n too large: %08lx\n", $fh->tell()-0x800, $n) if ($n>0x100); if (defined $hdr{dir}{type} && $hdr{dir}{type}!=$type) { printf("WARN@%08lx: dir2.type!=dir1.type: %d != %d\n", $fh->tell()-0x800, $hdr{dir}{type}, $type); } if (defined $hdr{dir}{type} && @{$hdr{dir}{entries}}!=$n) { printf("WARN@%08lx: dir2.n!=dir1.n: %d != %d\n", $fh->tell()-0x800, scalar @{$hdr{dir}{entries}}, $n); } for (my $i=0 ; $i<$n ; $i++) { my $ent= $hdr{dir}{entries}[$i] ||= {}; my ($name, $unk2, $unk3, $unk4)= unpack("A16VVV", substr($page, 16+0x1c*$i, 0x1c)); if (exists $ent->{name} && $ent->{name} ne $name) { printf("WARN: dirX.n!=dir2.n: %s != %s\n", $ent->{name}, $name); } if (exists $ent->{unk2} && $ent->{unk2} ne $unk2) { printf("WARN: dirX.unk2!=dir2.unk2: %x != %x\n", $ent->{unk2}, $unk2); } if (exists $ent->{unk3} && $ent->{unk3} ne $unk3) { printf("WARN: dirX.unk3!=dir2.unk3: %x != %x\n", $ent->{unk3}, $unk3); } if (exists $ent->{unk4} && $ent->{unk4} ne $unk4) { printf("WARN: dirX.unk4!=dir2.unk4: %x != %x\n", $ent->{unk4}, $unk4); } $ent->{name} ||= $name; $ent->{unk2} ||= $unk2; $ent->{unk3} ||= $unk3; $ent->{unk4} ||= $unk4; } } elsif ($magic[0]==0x9d41bea1 && $magic[1]==0xf1ded2ea) { # ?? checksum ? # page/block padded with 0xff printf("unknown3: %8x %8x - checksum?\n", unpack("VV", substr($page, 8, 8))); } elsif ($magic[0]==0x5fe85ddf && $magic[1]==0x5264cebc) { # 2,3 : 0 # 4 : file name # page padded with 0x00 # rest of block: qcsbl data my @f=unpack("VVVVA*", $page); my $save= IO::File->new($qcsbl_savename, "w") or die "$qcsbl_savename: $!\n" if ($do_extract&2); binmode $save if ($save); my $savesize= $hdr{qcsbl}{imagesize}; printf("%08lx qcsbl %08lx %08lx %s\n", $fh->tell()-0x800, $hdr{qcsbl}{baseaddr}, $hdr{qcsbl}{imagesize}, $f[4] || "qcsbl.nb"); $pagehandler= sub { my $page= shift; if (length($page)>$savesize) { $page= substr($page, 0, $savesize); } $save->print($page) if ($save); $savesize -= length($page); if ($savesize==0) { $save->close() if ($save); printf("%08lx: end of qcsbl\n", $fh->tell()-0x800+length($page)); undef $save; undef $pagehandler; } }; } elsif ($magic[0]==0x464c457f) { # dump dir when elf found. printf("flash directory: (t=%d)\n", $hdr{dir}{type}); for my $ent (@{$hdr{dir}{entries}}) { printf("%-16s %6x%6x [%6x0000] %4x %8x%8x %8x\n", map { $_ eq '64kofs' ? $ent->{ofs}*2 : $ent->{$_} } qw(name ofs size 64kofs attr unk2 unk3 unk4)); } my $save= IO::File->new($elf_savename, "w") or die "$elf_savename: $!\n" if ($do_extract&4); binmode $save if ($save); my $savesize= $hdr{elf}{imagesize}; $save->print($page) if ($save); $savesize -= length($page); printf("%08lx elf %08lx %08lx\n", $fh->tell()-0x800, $hdr{elf}{baseaddr}, $hdr{elf}{imagesize}); $pagehandler= sub { my $page= shift; if (length($page)>$savesize) { $page= substr($page, 0, $savesize); } $save->print($page) if ($save); $savesize -= length($page); if ($savesize==0) { $save->close() if ($save); printf("%08lx: end of elf\n", $fh->tell()-0x800+length($page)); undef $save; undef $pagehandler; } }; } elsif ($page =~ /^\x00+$/ || $page =~ /^\xFF+$/) { # ignore empty ( 00 / ff ) pages } else { warn sprintf("unrecognized page @%08lx: %08lx %08lx\n", $fh->tell()-0x800, @magic); } } }