📄 rdmsflsh.pl
字号:
#!perl -w# (C) 2003-2007 Willem Jan Hengeveld <itsme@xs4all.nl># Web: http://www.xs4all.nl/~itsme/# http://wiki.xda-developers.com/## $Id: rdmsflsh.pl 1638 2007-11-01 12:31:13Z itsme $## this perl script decodes the IMGFS rom filesystem format# as used by wm2005 devices## todo: repair import table.# todo: get this from romhdr.# todo: add sequence nrs to identically named sections# # problem: files with RELOC section, the reloc section needs more padding.# package Reader;use strict;use IO::File;sub new { my ($type, $fh, $base)= @_; my $self= bless { fh=>$fh, baseofs=>$base, }, $type; return $self;}package TyphReader;use strict;use IO::File;our @ISA=qw(Reader);sub ReadData { my ($self, $ofs, $len, $desc)= @_; printf("%08lx-%08lx l=%08lx %s\n", $ofs, $ofs+$len, $len, $desc) if ($main::verbose && $desc); $self->{fh}->seek($self->cv2file($ofs), SEEK_SET) or return; my $data=""; $self->{fh}->read($data, $len) or return; return $data;}sub ReadDword { my ($self, $ofs, $desc)= @_; printf("%08lx-%08lx l=%08lx %s\n", $ofs, $ofs+4, 4, $desc) if ($main::verbose && $desc); $self->{fh}->seek($self->cv2file($ofs), SEEK_SET) or return; my $data; $self->{fh}->read($data, 4) or return; return unpack("V", $data);}sub cv2file { my ($self, $ofs)= @_; return $self->{baseofs} + $ofs;}package HimaReader;use strict;use IO::File;our @ISA=qw(Reader);sub ReadData { my ($self, $ofs, $len, $desc)= @_; printf("%08lx-%08lx l=%08lx %s\n", $ofs, $ofs+$len, $len, $desc) if ($main::verbose && $desc); my $data=""; while ($len) { my $want= $len; my $realofs= $self->cv2file($ofs); $self->{fh}->seek($realofs, SEEK_SET) or return; if ($want>0x40000-($realofs&0x3ffff)) { $want= 0x40000-($realofs&0x3ffff); } $self->{fh}->read($data, $want, length($data)) or return; $len -= $want; $ofs += $want; } return $data;}sub ReadDword { my ($self, $ofs, $desc)= @_; printf("%08lx-%08lx l=%08lx %s\n", $ofs, $ofs+4, 4, $desc) if ($main::verbose && $desc); $self->{fh}->seek($self->cv2file($ofs), SEEK_SET) or return; my $data; $self->{fh}->read($data, 4) or return; return unpack("V", $data);}sub cv2file { my ($self, $ofs)= @_; return $self->{baseofs} + int($ofs/0x3f000)*0x40000+($ofs%0x3f000);}package cvtime;use strict;use POSIX;sub convert2unixtime { my $wtime= shift; my @w= unpack("VV", $wtime); return int(($w[1]*(2**32)+$w[0])/10000000.0-11644473600)}package main;use strict;use Getopt::Long;use Carp;use IO::File;use integer;use XdaDevelopers::CompressUtils;$|=1;## i split the wm2005 sp rom apart as follows:## 00000000-00000400 : hdr.nb# 00000400-00200400 : first xip# 00200400-00210000 : empty, filled with 0xff# 00210000-003f0000 : second xip# 003f0000-01819f00 : imgfs filesystem# 01819f00-01819f80 signature?# 01819f80-01b00000 empty 0xff## this script operates on the extracted imgfs part#my $reader;my $outdir;our $verbose;my %stats;sub usage { return <<__EOF__Usage: rdmsflsh [-d savedir] file__EOF__}GetOptions( "d=s"=> \$outdir, "v"=>\$verbose,) or die usage();my $fn= shift or die "usage: need filename\n";my $fh= IO::File->new($fn, "r") or croak "$fn: $!\n";binmode $fh;mkdir $outdir if ($outdir);# himalaya 80500000# 00000000: f8 ac 2c 9d e3 d4 2b 4d bd 30 91 6e d8 4f 31 dc# 00000010: 00000001 00000001 00000001 00000034# 00000020: 00000040 00001000 00001000 "LZX"# 00000030: 0000057b 00000020# # typhoon: 003f0000# 00000000: f8 ac 2c 9d e3 d4 2b 4d bd 30 91 6e d8 4f 31 dc# 00000010: 00000001 00000001 00000001 00000034# 00000020: 00000008 00000200 00001000 "XPR"# 00000030: 00001730 00000100# universal: 706e0000# 00000000: f8 ac 2c 9d e3 d4 2b 4d bd 30 91 6e d8 4f 31 dc# 00000010: 00000001 00000001 00000001 00000034# 00000020: 00000008 00000200 00001000 "LZX"# 00000030: 0000ce0c 00000100my $imgfs_hdr= findimgfs_header($fh) || die "could not find imgfs header\n";# universal tornado0 tornado1 wizard himalaya typhoon# 0000: ** "MSFLSH50" **# 0008: ** 00000000 **# 000c: ** 00000038 ** size of this header# 0010: ** 00000000 **# 0014: ** 00000000 **# 0018: 00000000 00000000 00000000 00000000 00000010 00000000# 001c: 0000005e 00000048 0000004c 00000060 00000000 0000003f start block of imgfs# 0020: 00000080 00000080 00000080 00000080 00000040 00000080 nr of sectors per block# 0024: 00010000 00010000 00010000 00010000 00040000 00010000 imgfs blocksize# 0028: ** 00000000 **# 002c: ** 00000001 **# 0030: 00000000 00000000 00000000 00000000 00000010 00000000# 0034: 00000000 00000000 00000000 00000000 0000006c 00000000# 0038: 00000392 000001e8 00000204 00000330 00000000 00000171 nr of blocks in imgfs# 003c: 00000080 00000080 00000080 00000080 0000003f 00000080 nr of used sectors per block# 0040: 00010000 00010000 00010000 00010000 00040000 00010000 imgfs blocksize# 0044: 00000000 00000000 00000000 00000000 00000002 00000000# :005e0000: :00480000: :004c0000: :00600000: :00500000: :003f0000:# 0000: ** f8ac2c9de3d42b4dbd30916ed84f31dc ** guidBootSignature# 0010: ** 00000001 ** dwFSVersion;# 0014: ** 00000001 ** dwSectorsPerHeaderBlock;# 0018: ** 00000001 ** dwRunsPerFileHeader;# 001c: ** 00000034 ** dwBytesPerHeader;# 0020: 00000008 00000008 00000008 00000008 00000040 00000008 dwChunksPerSector;# 0024: 00000200 00000200 00000200 00000200 00001000 00000200 dwFirstHeaderBlockOffset;# 0028: ** 00001000 ** dwDataBlockSize;# 002c: "LZX" "LZX" "LZX" "LZX" "LZX" "XPR" zCompressionType[4];# 0030: 0000ce0c 000072a5 00001e72 0000cf31 0000057b 00001730 dwFreeSectorCount;# 0034: 00000100 00000100 00000100 00000100 00000020 00000100 dwHiddenSectorCount;# 0038: ** 00000000 ** dwUpdateModeFlag;# 0000: magic == f8ac2c9de3d42b4dbd30916ed84f31dc# 0010:0 dwFSVersion;# 0014:1 dwSectorsPerHeaderBlock;# 0018:2 dwRunsPerFileHeader;# 001c:3 header length == 0x34 dwBytesPerHeader;# 0020:4 .. reader type dwChunksPerSector;# 0024:5 .. dir block size dwFirstHeaderBlockOffset;# 0028:6 dwDataBlockSize;# 002c:7 compression magic == "LZX" or == "XPR" szCompressionType[4];# 0030:8 .. nr of files dwFreeSectorCount;# 0034:9 dwHiddenSectorCount;# 0038:a dwUpdateModeFlag;sub findimgfs_header { my ($fh)= @_; my $signature= pack("H*", "f8ac2c9de3d42b4dbd30916ed84f31dc"); my $ofs= 0; while (1) { my $data; $fh->seek($ofs, SEEK_SET); $fh->read($data, 512) or last; my $i= index($data, $signature); if ($i>512-40) { warn sprintf("ignoring sig at %08lx\n", $ofs+$i); } elsif ($i>=0) { my @hdr= unpack("V7A4V2", substr($data, $i+16)); if ($hdr[3]==0x34 && ($hdr[7] eq "LZX" || $hdr[7] eq "XPR")) { printf("found hdr at %08lx\n", $ofs+$i); return { baseofs=>$ofs+$i, compression=>$hdr[7], readertype=>$hdr[4], dirblocksize=>$hdr[5], firstdirofs=>$hdr[5], nroffiles=>$hdr[8], }; } else { printf("sig at %08lx, but inv hdr: %08lx %s\n", $i+$ofs, $hdr[3], $hdr[7]); } } $ofs += 0x400; } return undef;}my $rd= $imgfs_hdr->{readertype}==8 || $imgfs_hdr->{readertype}==0x20 ? TyphReader->new($fh, $imgfs_hdr->{baseofs}) : $imgfs_hdr->{readertype}==0x40 ? HimaReader->new($fh, $imgfs_hdr->{baseofs}) : undef;if (!$rd) { die sprintf("could not determine filetype: rd=0x%x base=0x%x\n", $imgfs_hdr->{readertype}, $imgfs_hdr->{baseofs});}my $dirdata="";my $ofs= $imgfs_hdr->{firstdirofs};while (length($dirdata)==0 || $ofs) { my $magic= $rd->ReadDword($ofs, "dirblock magic"); if ($magic != 0x2f5314ce) { carp sprintf("%08lx: magic =%08lx != 2f5314ce\n", $ofs, $magic); last; } my $dirchunk = $rd->ReadData($ofs+8, $imgfs_hdr->{dirblocksize}-8, sprintf("dirchunk %08lx", $ofs)); $dirchunk =~ s/(?:\xff\xff\xff\xff)+$//; $dirdata .= $dirchunk; $ofs= $rd->ReadDword($ofs+4, "dirblock nextptr");}my %ref;for (my $i= 0 ; $i<length($dirdata) ; $i+=4*13){ my $type= unpack("V", substr($dirdata, $i, 4)); $stats{sprintf("ent_%x", $type)}++; if ($type == 0xfffff6fe || $type == 0xfffffefe) { my $file= process_fileentry(substr($dirdata, $i, 4*13), sprintf("fileent %d", $i)); save_file($outdir, $file) if ($outdir); printf(" %7d %04x %s %s\n", $file->{size}, $file->{attributes}, POSIX::strftime("%Y-%m-%d %H:%M:%S", localtime $file->{timestamp}), $file->{name}); }}print "statistics:\n";print map { sprintf("%6d [ 0x%08x ] %s\n", $stats{$_}, $stats{$_}, $_) } sort keys %stats;exit(0);sub save_data { my ($filename, $data)= @_; my $fh= IO::File->new($filename, "w") or die "$filename: $!\n"; binmode $fh; $fh->print($data) if (defined $data && length($data)); $fh->close();}sub save_file { my ($dirpath, $file)= @_; my $filepath= sprintf("%s/%s", $dirpath, $file->{name}); if ($file->{sections} && @{$file->{sections}}) { my $exedata= reconstruct_binary($file); save_data($filepath, $exedata); } else { save_data($filepath, $file->{data}); }}sub unpack_e32rom { my ($data)= @_; my %e32rom; ( $e32rom{objcnt}, # 0x00 $e32rom{imageflags}, # 0x02 $e32rom{entryrva}, # 0x04 $e32rom{vbase}, # 0x08 $e32rom{subsysmajor}, # 0x0C $e32rom{subsysminor}, # 0x0E $e32rom{stackmax}, # 0x10 $e32rom{vsize}, # 0x14 $e32rom{sect14rva}, # 0x18 $e32rom{sect14size}, # 0x1C $e32rom{timestamp}, # 0x20 $e32rom{EXP_rva}, $e32rom{EXP_size}, # 0x24 $e32rom{IMP_rva}, $e32rom{IMP_size}, # 0x2c $e32rom{RES_rva}, $e32rom{RES_size}, # 0x34 $e32rom{EXC_rva}, $e32rom{EXC_size}, # 0x3c $e32rom{SEC_rva}, $e32rom{SEC_size}, # 0x44 $e32rom{FIX_rva}, $e32rom{FIX_size}, # 0x4c $e32rom{DEB_rva}, $e32rom{DEB_size}, # 0x54 $e32rom{IMD_rva}, $e32rom{IMD_size}, # 0x5c $e32rom{MSP_rva}, $e32rom{MSP_size}, # 0x64 $e32rom{subsys}, # 0x6c )= unpack("v2V2v2V23v", $data); return \%e32rom;}sub unpack_o32rom { my ($data)= @_; my %o32rom; ( $o32rom{vsize}, $o32rom{rva}, $o32rom{psize}, $o32rom{dataptr}, $o32rom{realaddr}, $o32rom{flags}, ) = unpack("V6", $data); return \%o32rom;}sub parse_pe_data { my ($data)= @_; my %pe; if (length($data) < 0x70) { printf("WARNING: PE data block too small\n"); } $pe{e32rom}= unpack_e32rom(substr($data, 0, 0x70)); if (length($data) < 0x70 + $pe{e32rom}{objcnt}*0x18) { printf("WARNING: PE data block too small\n"); } elsif (length($data) > 0x70 + $pe{e32rom}{objcnt}*0x18) { printf("WARNING: PE data block too large: %s\n", unpack("H*", substr($data, 0x70+0x18*$pe{e32rom}{objcnt}))); } for (my $i=0 ; $i<$pe{e32rom}{objcnt} ; $i++) { push @{$pe{o32rom}}, unpack_o32rom(substr($data, 0x70+0x18*$i, 0x18)); } return \%pe;}sub pack_mz_header { return pack("H*", "4d5a90000300000004000000ffff0000"). pack("H*", "b8000000000000004000000000000000"). pack("H*", "00000000000000000000000000000000"). pack("H*", "00000000000000000000000080000000"). pack("H*", "0e1fba0e00b409cd21b8014ccd215468"). pack("H*", "69732070726f6772616d2063616e6e6f"). pack("H*", "742062652072756e20696e20444f5320"). pack("H*", "6d6f64652e0d0d0a2400000000000000");}sub pack_e32exe { my ($e32exe)= @_; my @info= qw(EXP IMP RES EXC SEC FIX DEB IMD MSP TLS CBK RS1 RS2 RS3 RS4 RS5); return pack("a4vvVVVvvvCCVV8v6V4v2V6", $e32exe->{magic}, $e32exe->{cpu}, $e32exe->{objcnt}, $e32exe->{timestamp}, $e32exe->{symtaboff}, $e32exe->{symcount}, $e32exe->{opthdrsize}, $e32exe->{imageflags}, $e32exe->{coffmagic}, $e32exe->{linkmajor}, $e32exe->{linkminor}, $e32exe->{codesize}, $e32exe->{initdsize}, $e32exe->{uninitdsize}, $e32exe->{entryrva}, $e32exe->{codebase}, $e32exe->{database}, $e32exe->{vbase}, $e32exe->{objalign}, $e32exe->{filealign}, $e32exe->{osmajor}, $e32exe->{osminor}, $e32exe->{usermajor}, $e32exe->{userminor}, $e32exe->{subsysmajor}, $e32exe->{subsysminor}, $e32exe->{res1}, $e32exe->{vsize}, $e32exe->{hdrsize}, $e32exe->{filechksum}, $e32exe->{subsys}, $e32exe->{dllflags}, $e32exe->{stackmax}, $e32exe->{stackinit}, $e32exe->{heapmax}, $e32exe->{heapinit}, $e32exe->{res2}, $e32exe->{hdrextra}, ). join("", map { pack("VV", $e32exe->{"${_}_rva"}||0, $e32exe->{"${_}_size"}||0) } @info);}sub pack_o32obj { my ($o32obj)= @_; return pack("a8V8", $o32obj->{name}, $o32obj->{vsize}, $o32obj->{rva}, $o32obj->{psize},
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -