#!perl -w # (C) 2003-2007 Willem Jan Hengeveld # Web: http://www.xs4all.nl/~itsme/ # http://wiki.xda-developers.com/ # # $Id: $ # use strict; use IO::File; use Getopt::Long; my %hdrtypes= ( 0x100 => "IPL", 0x101 => "G3IPL", 0x102 => "G4IPL", 0x103 => "H3IPL", 0x200 => "SPL", #0x201 => "G3SPL", #0x202 => "G4SPL", 0x300 => "GSM", 0x301 => "GSMV2", 0x700 => "Extrom", # or subsplash 'dopod' 0x800 => "cpldcode", # ... guess 0x900 => "Extrom2", 0x600 => "MainSplash", # 'dopod enjoy mobility' 0x400 => "OS", #0x410 => ? #0x500 => ? ); my %hdrtypes_r= map { ($hdrtypes{$_} => $_ ) } keys %hdrtypes; sub usage { return <<__EOF__ Usage: dbhdecode [-d] image.dbh dbhdecode -e image.dbh -a ipl=ipl.nb -a spl=spl.nb -a 0x700=image.nb -p cid=CDL__Z07 -p version=1.2.301.2 -p devname=StarTrek __EOF__ } my $doencode; my $dodecode; my @sections; my %params; GetOptions( "d" => \$dodecode, "e" => \$doencode, "a=s" => sub { push @sections, parse_section_spec($_[1]); }, "p=s" => sub { my ($k,$v)= parse_keyval($_[1]); if ($k eq "devname" && $v eq "StarTrek") { $hdrtypes{0x700} = "SubSplash"; $hdrtypes_r{SubSplash} = 0x700; } $params{$k}=$v; }, ) or die usage(); sub parse_keyval { return $_[0] =~ /(.*?)=(.*)/; } sub parse_section_spec { my ($k,$v)= parse_keyval(@_) or die "invalid sectionspec: @_"; my $kval; if (exists $hdrtypes_r{$k}) { $kval= $hdrtypes_r{$k}; } else { $kval= eval($k); } if (!defined $kval || $kval==0) { die "invalid section type: $k\n"; } if (!-f $v) { die "file not found: $k ($kval): $v\n"; } return { type=>$kval, filename=>$v, }; } # signature wrapper: # 0x20 byte header, then every 1M +- 0x89 bytes are inserted. # at end of file, 0x80 bytes extra # physofs stripedofs # 00000020 00000000 file header # 00000220 00000200 ipl.0 # 00000a20 00000a00 ipl.1 # 00001220 00001200 spl.0 # 00101220 00101200 ? # 001012a9 00101200 BCL1? # 001512a9 00151200 BCL1? # 001a12a9 001a1200 BCL1? # 001f12a9 001f1200 BCL1? # 00241332 00241200 BCL1? # 00291332 00291200 BCL1? # 002e1332 002e1200 BCL1? # 003313bb 00331200 BCL1? # 003813bb 00381200 BM6 - splash # 0038d5bb 0038d400 This is smartphone signature # 003bd5bb 003bd400 partition table # 003bd7bb 003bd600 MSFLSH50 # 003bd9bb 003bd800 start of xip # 003bd9fb 003bd840 ECEC # 0053de21 romhr # 005cd6cd 005cd400 start of xip # 005cd70d 005cd440 ECEC # 0088d868 0088d400 start of imgfs if ($doencode) { my $hdr = create_nbh_header(\%params, \@sections); my $ofn= shift or die usage(); my $ofh= IO::File->new($ofn, "w") or die "$ofn: $!\n"; binmode $ofh; $ofh->print(pack_nbh_header($hdr)); for my $section (@sections) { my $ifh= IO::File->new($section->{filename}, "r"); binmode $ifh; my $data; while (!$ifh->eof) { $ifh->read($data, 1024*1024) or die "error reading inputfile\n"; $ofh->print($data); } my $pos= $ofh->tell(); $ofh->seek(($pos|0x1ff)+1, SEEK_SET) if ($pos&0x1ff); $ifh->close(); } $ofh->close(); } else { my $ifn= shift or die usage(); my $ifh= IO::File->new($ifn) or die "$ifn: $!\n"; binmode $ifh; my $hdrdata= read_nbh_header($ifh); my $hdr= parse_nbh_header($hdrdata); printf("magic: %s\n", pack("C*", @{$hdr->{magic}})); printf("devname: %s\n", $hdr->{devname}); printf("cid: %s\n", $hdr->{cid}); printf("version: %s\n", $hdr->{version}); printf("language: %s\n", $hdr->{language}); if ($hdr->{devname} eq "StarTrek") { $hdrtypes{0x700} = "SubSplash"; } for my $ent (@{$hdr->{entries}}) { my $ofs= $ifh->tell(); $ifh->read($ent->{data}, $ent->{length}); printf("%08lx: %08lx %08lx %s %s\n", $ofs, $ent->{offset}, $ent->{length}, unpack("H*", substr($ent->{data}, 0, 16)), sectionname($ent->{type})); savefile(sectionname($ent->{type}).($ent->{type} eq "GSM" ? ".cnb" : ".nb"), $ent->{data}) if ($dodecode); } $ifh->close(); } sub savefile { my $ofh= IO::File->new($_[0], "w"); binmode $ofh; $ofh->print($_[1]); $ofh->close(); } sub sectionname { return $hdrtypes{$_[0]} if (exists $hdrtypes{$_[0]}); return sprintf("unknown_%x", $_[0]); } sub read_nbh_header { my ($fh)= @_; my $data; $fh->read($data, 0x200); return $data; } sub parse_nbh_header { my ($data)= @_; my %hdr; my ($typestr, $ofsstr, $lenstr); ( @{$hdr{magic}}[0..7], # V8 0000: 'HTCIMAGE' $hdr{devname}, # A32 0020: "StarTrek" $typestr, #a128 0040: 00000101 00000102 00000200 00000300 00000700 00000600 00000400 $ofsstr, #a128 00c0: 00000200 00000a00 00001200 00101200 00381200 0038d400 003bd400 $lenstr, #a128 0140: 00000800 00000800 00100000 00280000 0000c200 00030000 02610000 $hdr{cid}, # A32 01c0: "CDL__Z07" $hdr{version}, # A16 01e0: "1.2.301.2" $hdr{language}, # A16 01f0: "UK" )= unpack("V8A32a128a128a128A32A16A16", $data); my @types= unpack("V*", $typestr); my @offsets= unpack("V*", $ofsstr); my @lengths= unpack("V*", $lenstr); push @{$hdr{entries}}, { type=>$types[$_], offset=>$offsets[$_], length=>$lengths[$_] } for grep { $types[$_] && $offsets[$_] && $lengths[$_] } (0..$#types); return \%hdr; } sub pack_nbh_header { my ($hdr)= @_; #use Dumpvalue; Dumpvalue->new->dumpValue($hdr); return pack("V8a32a128a128a128a32a16a16", @{$hdr->{magic}}[0..7], # V8 0000: 'HTCIMAGE' $hdr->{devname}, # A32 0020: "StarTrek" pack("V*", map { $_->{type} } @{$hdr->{entries}}), #a128 0040: 00000101 00000102 00000200 00000300 00000700 00000600 00000400 pack("V*", map { $_->{offset} } @{$hdr->{entries}}), #a128 00c0: 00000200 00000a00 00001200 00101200 00381200 0038d400 003bd400 pack("V*", map { $_->{length} } @{$hdr->{entries}}), #a128 0140: 00000800 00000800 00100000 00280000 0000c200 00030000 02610000 $hdr->{cid}, # A32 01c0: "CDL__Z07" $hdr->{version}, # A16 01e0: "1.2.301.2" $hdr->{language}, # A16 01f0: "UK" ); } sub create_nbh_header { my ($params, $sections)= @_; my %hdr; $hdr{magic}= [ unpack("C8", "HTCIMAGE") ]; $hdr{devname}= $params->{devname} || 'StarTrek'; $hdr{cid}= $params->{cid} || '11111111'; $hdr{version}= $params->{version} || '1.0.0.0'; $hdr{language}= $params->{languange} || 'UK'; my $offset= 0x200; for my $section (@$sections) { my $filesize= -s $section->{filename}; $filesize = ($filesize|0x1ff)+1 if ($filesize&0x1ff); push @{$hdr{entries}}, { type=>$section->{type}, offset=>$offset, length=>$filesize, }; $offset += $filesize; } return \%hdr; }