#!/usr/bin/perl -w # (C) 2003-2007 Willem Jan Hengeveld # Web: http://www.xs4all.nl/~itsme/ # http://wiki.xda-developers.com/ # # $Id: chainedit.pl 1502 2007-04-15 07:54:20Z itsme $ # # use strict; use IO::File; use Dumpvalue; my $d= Dumpvalue->new(); $|=1; my %editcmds= ( load=>\&ReadXipChain, dump=>\&DumpChain, add=>\&AddToChain, save=>\&WriteXipChain, saveini=>\&WriteXipCfg, setpk=>\&AddPublicKey, ); if (!@ARGV) { die "Usage: chain2cfg [params] ...\n", " add name:start,length,order\n", " load filename\n", " save filename\n", " setpk range:pkfile\n", " dump\n"; } my $chain; while (@ARGV) { my $cmd= shift; if (!exists $editcmds{$cmd}) { die "invalid chain edit command: $cmd\n"; } $chain= $editcmds{$cmd}($chain, \@ARGV); } exit(0); ################################################################### ## reading a xip chain sub ReadXipChain { my ($chain, $args)= @_; if (! @$args) { die "Usage: read \n"; } my $xipchainname= shift @$args; my $fh= IO::File->new($xipchainname, "r") or die "$xipchainname: $!\n"; binmode $fh; my $n= ReadDword($fh); my @chain; for (1..$n) { push @chain, ReadChainEntry($fh); } $fh->close(); return \@chain; } sub ReadChainEntry { my ($fh)= @_; my $data; my $nread= $fh->read($data, 0x290); if ($nread!=0x290) { die "invalid chain\n"; } my @fields= unpack("V3v2VA32VVa*", $data); return { pvAddr=>$fields[0], dwLength=>$fields[1], dwMaxLength=>$fields[2], usOrder=>$fields[3], usFlags=>$fields[4], dwVersion=>$fields[5], szName=>$fields[6], dwAlgoFlags=>$fields[7], dwKeyLen=>$fields[8], byPublicKey=>$fields[9], }; } sub ReadDword { my ($fh)= @_; my $data; $fh->read($data, 4); return unpack("V", $data); } ################################################################### ## writing a xip chain sub WriteXipChain { my ($chain, $args)= @_; if (! @$args) { die "Usage: save \n"; } my $xipchainname= shift @$args; my $fh= IO::File->new($xipchainname, "w+") or die "$xipchainname: $!\n"; binmode $fh; PrintDword($fh, $#$chain+1); for (@$chain) { WriteChainEntry($fh, $_); } $fh->close(); return $chain; } sub WriteChainEntry { my ($fh, $entry)= @_; my $data= pack("V3v2Va32VVa*", $entry->{pvAddr}, $entry->{dwLength} || 0, $entry->{dwMaxLength} || 0, $entry->{usOrder} || 0, $entry->{usFlags} || 1, $entry->{dwVersion} || 0, $entry->{szName}, $entry->{dwAlgoFlags} || 0x8003, $entry->{dwKeyLen} || 0, $entry->{byPublicKey} || ""); $fh->print($data, "\x00" x (0x290-length($data))); } sub PrintDword { my ($fh, $n)= @_; my $data= pack("V", $n); $fh->print($data); } ################################################################### ## dumping a xip chaing sub DumpChain { my ($chain, $args)= @_; #print "------------\n"; #$d->dumpValue($chain); #print "------------\n"; for (sort { $a->{usOrder} <=> $b->{usOrder} } @$chain) { print DumpChainEntry($_), "\n"; } return $chain; } sub DumpChainEntry { my ($xip)= @_; # convert all fields to string representation my $publickey= substr($xip->{byPublicKey}, 0, $xip->{dwKeyLen}); my $rest= substr($xip->{byPublicKey}, $xip->{dwKeyLen}); my $dump= sprintf("%2d %-32s %08lx l=%08lx", $xip->{usOrder}, $xip->{szName}, $xip->{pvAddr}, $xip->{dwLength}); if ($xip->{dwMaxLength}) { $dump .= sprintf(" max=%08lx", $xip->{dwMaxLength}); } if ($xip->{usFlags}!=1) { $dump .= " " . FlagsToString($xip->{usFlags}); } if ($xip->{dwVersion}!=0) { $dump .= " v=$xip->{dwVersion}"; } if ($xip->{dwAlgoFlags}!=0x8003) { $dump .= sprintf(" a=%08lx", $xip->{dwAlgoFlags}); } if ($publickey) { $dump .= sprintf(" keylen=%d", length($publickey)); } if ($rest !~ /^\x00*$/) { $dump .= sprintf(" restlen=%d", length($rest)); } return $dump; } sub FlagsToString { my ($flags)= @_; my @str; push @str, "OK_TO_LOAD" if ($flags&1); push @str, "IS_SIGNED" if ($flags&2); push @str, sprintf("%08lx", $flags&~3) if ($flags&~3); return join ",", @str; } ############################################################# ## writing config file sub WriteXipCfg { my ($chain, $args)= @_; if (! @$args) { die "Usage: saveini \n"; } my $xipcfgname= shift @$args; my $fh= IO::File->new($xipcfgname, "w+") or die "$xipcfgname: $!\n"; for (sort {$a->{usOrder} <=> $b->{usOrder}} @$chain) { WriteXipCfgEntry($fh, $_); } $fh->close(); } sub WriteXipCfgEntry { my ($fh, $xip)= @_; # convert all fields to string representation my $pvAddr= sprintf("%08lx", $xip->{pvAddr}); my $dwLength= sprintf("%08lx", $xip->{dwLength}); my $dwMaxLength= sprintf("%08lx", $xip->{dwMaxLength}); my $usOrder= $xip->{usOrder}; my $usFlags= FlagsToString($xip->{usFlags}); my $dwVersion= $xip->{dwVersion}; my $szName= $xip->{szName}; my $dwAlgoFlags= sprintf("%08lx", $xip->{dwAlgoFlags}); my $publickey= unpack("H*", substr($xip->{byPublicKey}, 0, $xip->{dwKeyLen})); my $rest= unpack("H*", substr($xip->{byPublicKey}, $xip->{dwKeyLen})); $fh->print(<<"__EOF__"); [$szName] start=$pvAddr length=$dwLength maxlength=$dwMaxLength order=$usOrder flags=$usFlags version=$dwVersion algorithm=$dwAlgoFlags key=$publickey rest=$rest __EOF__ } ############################################################# ## adding to the chain sub AddToChain { my ($chain, $args)= @_; if (! @$args) { die "Usage: add \n"; } my $spec= shift @$args; if ($spec !~ /^(\w+):(\w+),(\w+)(?:,(\w+))?$/) { die "must specify xip spec without spaces\n"; } my ($name, $start, $length, $order)= ($1, hex($2), hex($3), $4); if (!defined $order) { $order= max(map {$_->{usOrder}} @$chain)+1; } # make room in chain. $_->{usOrder}++ for grep { $_->{usOrder}>=$order } @$chain; push @$chain, { pvAddr=>$start, dwLength=>$length, usOrder=>$order, szName=>$name, byPublicKey=>"", dwKeyLen=>0, dwMaxLength=>0, usFlags=>1, dwVersion=>0, dwAlgoFlags=>0, }; return $chain; } sub max { return undef unless (@_); my $max= shift; for (@_) { $max= $_ if ($_>$max); } return $max; } ################################################################### ## setting publickeys in chain sub AddPublicKey { my ($chain, $args)= @_; if (! @$args) { die "Usage: setpk \n"; } my $spec= shift @$args; if ($spec !~ /^(\*|\w+)(?:-(\d+))?:(.+)$/) { die "invalid pk spec ($spec), range must be one of : nr nr-nr *\n"; } my ($from, $to, $pkfile)= ($1, $2, $3); if ($from eq "*") { $from= 0; $to= scalar @$chain-1; } elsif (!defined $to) { $to= $from; } if ($to < 0 || $from < 0 || $to >= scalar @$chain || $from >= scalar @$chain ) { die "entry number ($from .. $to) out of range (0..$#$chain)\n"; } my $fh= IO::File->new($pkfile, "r") or die "$pkfile $!\n"; my $data; binmode $fh; my $nread= $fh->read($data, -s $fh); $fh->close(); for ($from .. $to) { $chain->[$_]{byPublicKey}= $data; $chain->[$_]{dwKeyLen}= length($data); } return $chain; }