📄 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 00000100
my $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;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -