+Implemented TraceFS W32 Cache Manager debug tracer
[captive.git] / src / TraceFS / hookfs.pl
diff --git a/src/TraceFS/hookfs.pl b/src/TraceFS/hookfs.pl
new file mode 100755 (executable)
index 0000000..43279f3
--- /dev/null
@@ -0,0 +1,381 @@
+#! /usr/bin/perl
+# 
+# $Id$
+# Redirect system calls of the specified file system driver to TraceFS.sys
+# Copyright (C) 2003 Jan Kratochvil <project-captive@jankratochvil.net>
+# 
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; exactly version 2 of June 1991 is required
+# 
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+# 
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+use strict;
+use warnings;
+use Carp qw(cluck confess);
+
+
+my $D=0;
+
+my $IMAGE_FILE_IMPORT_DIRECTORY=1;
+my $IMAGE_FILE_EXPORT_DIRECTORY=0;
+my $IMAGE_DATA_DIRECTORY_sizeof=8;
+my $VirtualAddress_rel_to_DATA_DIRECTORY_offs=0;
+my $Size_rel_to_DATA_DIRECTORY_offs=4;
+my $IMAGE_EXPORT_DIRECTORY_sizeof=0x28;
+my $IMAGE_IMPORT_DESCRIPTOR_sizeof=0x14;
+my $Name_rel_to_IMAGE_EXPORT_DIRECTORY_offs=0xC;
+my $NumberOfNames_rel_to_IMAGE_EXPORT_DIRECTORY_offs=0x18;
+my $AddressOfNames_rel_to_IMAGE_EXPORT_DIRECTORY_offs=0x20;
+my $OriginalFirstThunk_rel_to_IMPORT_DIRECTORY_offs=0x0;
+my $FirstThunk_rel_to_IMPORT_DIRECTORY_offs=0x10;
+my $Name_rel_to_IMAGE_IMPORT_BY_NAME_offs=0x2;
+my $IMAGE_SECTION_HEADER_sizeof=0x28;
+my $Name_rel_to_IMAGE_SECTION_HEADER_offs=0x00;
+my $VirtualSize_rel_to_IMAGE_SECTION_HEADER_offs=0x08;
+my $VirtualAddress_rel_to_IMAGE_SECTION_HEADER_offs=0x0C;
+my $SizeOfRawData_rel_to_IMAGE_SECTION_HEADER_offs=0x10;
+my $PointerToRawData_rel_to_IMAGE_SECTION_HEADER_offs=0x14;
+my $Characteristics_rel_to_IMAGE_SECTION_HEADER_offs=0x24;
+
+
+sub uintX_get($$$)
+{
+my($file,$offset,$bits)=@_;
+
+       my $r=0;
+       for (my $byte=0;$byte<$bits/8;$byte++) {
+               confess if !defined $file->[$offset+$byte];
+               $r|=($file->[$offset+$byte])<<($byte*8);
+               }
+       return $r;
+}
+sub uint32_get($$) { return uintX_get($_[0],$_[1],32); }
+sub uint16_get($$) { return uintX_get($_[0],$_[1],16); }
+sub  uint8_get($$) { return uintX_get($_[0],$_[1], 8); }
+
+sub uintX_put($$$$)
+{
+my($file,$offset,$num,$bits)=@_;
+
+       for (my $byte=0;$byte<$bits/8;$byte++) {
+               confess if !defined $offset;
+               $file->[$offset+$byte]=($num>>($byte*8))&0xFF;
+               }
+}
+sub uint32_put($$$) { return uintX_put($_[0],$_[1],$_[2],32); }
+sub uint16_put($$$) { return uintX_put($_[0],$_[1],$_[2],16); }
+sub  uint8_put($$$) { return uintX_put($_[0],$_[1],$_[2], 8); }
+sub uint32_push($$) { return uintX_put($_[0],@{$_[0]},$_[1],32); }
+sub uint16_push($$) { return uintX_put($_[0],@{$_[0]},$_[1],16); }
+sub  uint8_push($$) { return uintX_put($_[0],@{$_[0]},$_[1], 8); }
+
+
+sub sum_offs($)
+{
+my($file)=@_;
+
+       return uint32_get($file,0x3C)   # 3c: Offset to extended header
+                       +0x18   # OptionalHeader
+                       +0x40;  # CheckSum
+}
+
+sub sum_get($)
+{
+my($file)=@_;
+
+       return uint32_get($file,sum_offs($file));
+}
+
+sub sum_put($$)
+{
+my($file,$sum)=@_;
+
+       return uint32_put($file,sum_offs($file),$sum);
+}
+
+sub sum_calc($)
+{
+my($file)=@_;
+
+       $file=[ @$file ];
+       sum_put($file,0);
+       my $length=@$file;
+       my $sum=0;
+       while (@$file) {
+               $sum+=(shift @$file || 0) | (shift @$file || 0)<<8;
+               $sum=($sum&0xFFFF)+($sum>>16);
+               }
+       $sum+=$length;
+       return $sum;
+}
+
+sub align($$)
+{
+my($file,$align)=@_;
+
+       push @$file,0 while @$file%$align;
+}
+
+sub stringz_put($$$)
+{
+my($file,$offset,$stringz)=@_;
+
+       $stringz=[ map({ ord(); } split //,$stringz),0 ];
+       while (@$stringz) {
+               uint8_put($file,$offset,shift @$stringz);
+               $offset++;
+               }
+}
+
+sub stringz_push($$)
+{
+my($file,$stringz)=@_;
+
+       stringz_put($file,@$file,$stringz);
+       align $file,4;
+}
+
+sub stringz_get($$)
+{
+my($file,$offset)=@_;
+
+       my $r="";
+       while ((my $ord=uint8_get($file,$offset))) {
+               $r.=chr $ord;
+               $offset++;
+               }
+       return $r;
+}
+
+sub zeroes_push($$)
+{
+my($file,$count)=@_;
+
+       push @$file,0 while $count-->0;
+}
+
+sub SizeOfImage_offs($)
+{
+my($file)=@_;
+
+       return uint32_get($file,0x3C)   # 3c: Offset to extended header
+                               +0x18   # OptionalHeader
+                               +0x38;  # SizeOfImage
+}
+
+my $align=0x80;
+
+sub sanity($$)
+{
+my($file,$file_id)=@_;
+
+       my $calced=sum_calc($file);
+       if ($calced!=sum_get($file)) {
+               warn sprintf "$file_id: Original checksum wrong: found=0x%08X, calced=0x%08X",sum_get($file),$calced;
+               }
+
+       die sprintf "$file_id: Length 0x%X not aligned",scalar(@$file) if @$file%$align;
+       my $SizeOfImage=uint32_get($file,SizeOfImage_offs($file));
+       die sprintf "$file_id: SizeOfImage==0x%X but file size==0x%X",$SizeOfImage,scalar(@$file) if $SizeOfImage!=@$file;
+}
+
+sub export_names_get($)
+{
+my($tracefs)=@_;
+
+       my $EXPORT_DIRECTORY_offs=uint32_get($tracefs,0x3C)     # 3c: Offset to extended header
+                               +0x18   # OptionalHeader
+                               +0x60   # DataDirectory
+                               +$IMAGE_FILE_EXPORT_DIRECTORY*$IMAGE_DATA_DIRECTORY_sizeof;
+       my $EXPORT_DIRECTORY_VirtualAddress=uint32_get($tracefs,$EXPORT_DIRECTORY_offs+$VirtualAddress_rel_to_DATA_DIRECTORY_offs);
+       my $EXPORT_DIRECTORY_Size=uint32_get($tracefs,$EXPORT_DIRECTORY_offs+$Size_rel_to_DATA_DIRECTORY_offs);
+       die sprintf "EXPORT_DIRECTORY_Size 0x%X less than IMAGE_IMPORT_DESCRIPTOR_sizeof 0x%X",
+                       $EXPORT_DIRECTORY_Size,$IMAGE_IMPORT_DESCRIPTOR_sizeof if $EXPORT_DIRECTORY_Size<$IMAGE_IMPORT_DESCRIPTOR_sizeof;
+       my $IMAGE_EXPORT_DIRECTORY_Name=uint32_get($tracefs,$EXPORT_DIRECTORY_VirtualAddress
+                       +$Name_rel_to_IMAGE_EXPORT_DIRECTORY_offs);
+       my $tracefs_export_name=stringz_get($tracefs,$IMAGE_EXPORT_DIRECTORY_Name);
+       my $tracefs_export_names_num=uint32_get($tracefs,$EXPORT_DIRECTORY_VirtualAddress
+                       +$NumberOfNames_rel_to_IMAGE_EXPORT_DIRECTORY_offs);
+       my $tracefs_export_AddressOfNames=uint32_get($tracefs,$EXPORT_DIRECTORY_VirtualAddress
+                       +$AddressOfNames_rel_to_IMAGE_EXPORT_DIRECTORY_offs);
+       my @tracefs_export_names;
+       print STDERR "$tracefs_export_name exports:\n" if $D;
+       for (my $namei=0;$namei<$tracefs_export_names_num;$namei++) {
+               my $name_address=uint32_get($tracefs,$tracefs_export_AddressOfNames+4*$namei);
+               my $name=stringz_get($tracefs,$name_address);
+               push @tracefs_export_names,$name;
+               print STDERR "\t$name\n" if $D;
+               }
+       
+       return ($tracefs_export_name,@tracefs_export_names);
+}
+
+
+undef $/;
+my $file=[ map({ ord(); } split //,<>) ];
+my $tracefs=[ map({ ord(); } split //,<>) ];
+
+sanity($tracefs,"tracefs");
+my($tracefs_export_name,@tracefs_export_names)=export_names_get($tracefs);
+for my $tname (@tracefs_export_names) {
+       die "tracefs exported tname is not /^T/ compliant: $tname" if $tname!~/^T/;
+       }
+# FIXME: compiled to .sys tracefs contains internal export name .dll but we need import .sys !
+$tracefs_export_name=~s/[.]dll$/.sys/i;
+
+sanity($file,"file");
+
+
+# import directory load
+
+my $IMPORT_DIRECTORY_offs=uint32_get($file,0x3C)       # 3c: Offset to extended header
+                       +0x18   # OptionalHeader
+                       +0x60   # DataDirectory
+                       +$IMAGE_FILE_IMPORT_DIRECTORY*$IMAGE_DATA_DIRECTORY_sizeof;
+my $IMPORT_DIRECTORY_VirtualAddress=uint32_get($file,$IMPORT_DIRECTORY_offs+$VirtualAddress_rel_to_DATA_DIRECTORY_offs);
+my $IMPORT_DIRECTORY_Size=uint32_get($file,$IMPORT_DIRECTORY_offs+$Size_rel_to_DATA_DIRECTORY_offs);
+die sprintf "IMPORT_DIRECTORY_Size 0x%X not aligned to IMAGE_IMPORT_DESCRIPTOR_sizeof 0x%X",
+               $IMPORT_DIRECTORY_Size,$IMAGE_IMPORT_DESCRIPTOR_sizeof if $IMPORT_DIRECTORY_Size%$IMAGE_IMPORT_DESCRIPTOR_sizeof;
+
+my $IMPORT_DIRECTORY=[ @{$file}[$IMPORT_DIRECTORY_VirtualAddress
+               ..($IMPORT_DIRECTORY_VirtualAddress+$IMPORT_DIRECTORY_Size-1)] ];
+uintX_put($file,$IMPORT_DIRECTORY_VirtualAddress,0,8*$IMPORT_DIRECTORY_Size);  # zero the original space
+for (my $zerotail=0;$zerotail<$IMAGE_IMPORT_DESCRIPTOR_sizeof;$zerotail++) {
+       die "IMPORT_DIRECTORY tail not zeroed" if pop @$IMPORT_DIRECTORY;
+       }
+
+
+# import directory entries processing
+
+my %name_to_FirstThunk;        # name->FirstThunk
+my %tname_to_name;     # string->string{^.->T}
+for (
+               my $IMPORT_DIRECTORY_offset=0;
+               $IMPORT_DIRECTORY_offset<@$IMPORT_DIRECTORY;
+               $IMPORT_DIRECTORY_offset+=$IMAGE_IMPORT_DESCRIPTOR_sizeof) {
+       my $OriginalFirstThunk_base=uint32_get($IMPORT_DIRECTORY,$IMPORT_DIRECTORY_offset
+                       +$OriginalFirstThunk_rel_to_IMPORT_DIRECTORY_offs);
+       my $FirstThunk_base=uint32_get($IMPORT_DIRECTORY,$IMPORT_DIRECTORY_offset
+                       +$FirstThunk_rel_to_IMPORT_DIRECTORY_offs);
+       for (my $OriginalFirstThunk=$OriginalFirstThunk_base;;$OriginalFirstThunk+=4) {
+               my $AddressOfData=uint32_get($file,$OriginalFirstThunk);
+               last if !$AddressOfData;
+               my $name=stringz_get($file,$AddressOfData+$Name_rel_to_IMAGE_IMPORT_BY_NAME_offs);
+               print STDERR "import $name\n" if $D;
+               die "Invalid name import as it has leading 'T': $name" if $name=~/^T/;
+               (my $tname=$name)=~s/^./T/;
+               die "Name conflict in tname map: $name->$tname" if exists $tname_to_name{$tname};
+               $tname_to_name{$tname}=$name;
+               die if exists $name_to_FirstThunk{$name};
+               $name_to_FirstThunk{$name}=$FirstThunk_base+($OriginalFirstThunk-$OriginalFirstThunk_base);
+               }
+       }
+
+
+# add-on import directories generation
+my $addon_section_base=@$file;
+
+my $tracefs_export_name_offs=@$file;
+stringz_push($file,$tracefs_export_name);
+
+my $ordinal=1;
+for my $tname (@tracefs_export_names) {
+       die "tracefs exported tname $tname not found in imports" if !exists $tname_to_name{$tname};
+       my $name=$tname_to_name{$tname};
+       my $FirstThunk=$name_to_FirstThunk{$name};
+
+       my $IMAGE_IMPORT_BY_NAME_offs=@$file;
+       uint16_push($file,$ordinal++);  # Hint
+       stringz_push($file,$tname);     # Name
+
+       my $OriginalFirstThunk_offs=@$file;
+       uint32_push($file,$IMAGE_IMPORT_BY_NAME_offs);
+       uint32_push($file,0);   # zero terminator
+
+       uint32_push($IMPORT_DIRECTORY,$OriginalFirstThunk_offs);
+       uint32_push($IMPORT_DIRECTORY,0);       # TimeDateStamp
+       uint32_push($IMPORT_DIRECTORY,0);       # ForwarderChain
+       uint32_push($IMPORT_DIRECTORY,$tracefs_export_name_offs);       # Name
+       uint32_push($IMPORT_DIRECTORY,$FirstThunk);
+       }
+
+zeroes_push $IMPORT_DIRECTORY,$IMAGE_IMPORT_DESCRIPTOR_sizeof;
+uint32_put($file,$IMPORT_DIRECTORY_offs+$VirtualAddress_rel_to_DATA_DIRECTORY_offs,scalar(@$file));
+uint32_put($file,$IMPORT_DIRECTORY_offs+$Size_rel_to_DATA_DIRECTORY_offs,scalar(@$IMPORT_DIRECTORY));
+push @$file,@$IMPORT_DIRECTORY;
+
+
+# rebuild the sections for add-on data
+align $file,$align;
+# concatenate .rdata to .data
+my $SizeOfOptionalHeader_offs=uint32_get($file,0x3C)   # 3c: Offset to extended header
+                       +0x04   # FileHeader
+                       +0x10;  # SizeOfOptionalHeader
+my $SizeOfOptionalHeader=uint16_get($file,$SizeOfOptionalHeader_offs);
+my $NumberOfSections_offs=uint32_get($file,0x3C)       # 3c: Offset to extended header
+                       +0x04   # FileHeader
+                       +0x2;   # NumberOfSections
+my $NumberOfSections=uint16_get($file,$NumberOfSections_offs);
+my $IMAGE_SECTION_HEADER_offs=uint32_get($file,0x3C)   # 3c: Offset to extended header
+                       +0x18   # OptionalHeader
+                       +$SizeOfOptionalHeader;
+
+my($offset_data,$offset_rdata);
+for (
+               my $IMAGE_SECTION_HEADER_offset=$IMAGE_SECTION_HEADER_offs;
+               $IMAGE_SECTION_HEADER_offset<$IMAGE_SECTION_HEADER_offs+$NumberOfSections*$IMAGE_SECTION_HEADER_sizeof;
+               $IMAGE_SECTION_HEADER_offset+=$IMAGE_SECTION_HEADER_sizeof) {
+       my $VirtualAddress=uint32_get($file,$IMAGE_SECTION_HEADER_offset+$VirtualAddress_rel_to_IMAGE_SECTION_HEADER_offs);
+       my $PointerToRawData=uint32_get($file,$IMAGE_SECTION_HEADER_offset+$PointerToRawData_rel_to_IMAGE_SECTION_HEADER_offs);
+       die sprintf "VirtualAddress 0x%X != PointerToRawData 0x%X in IMAGE_SECTION_HEADER at 0x%X",
+                       $VirtualAddress,$PointerToRawData,$IMAGE_SECTION_HEADER_offset if $VirtualAddress!=$PointerToRawData;
+       my $SizeOfRawData=uint32_get($file,$IMAGE_SECTION_HEADER_offset+$SizeOfRawData_rel_to_IMAGE_SECTION_HEADER_offs);
+       my $VirtualSize=uint32_get($file,$IMAGE_SECTION_HEADER_offset+$VirtualSize_rel_to_IMAGE_SECTION_HEADER_offs);
+       $VirtualSize+=$align-1;
+       $VirtualSize-=$VirtualSize%$align;
+       die sprintf "up_align(VirtualSize,0x%X) 0x%X != SizeOfRawData 0x%X in IMAGE_SECTION_HEADER at 0x%X",
+                       $align,$VirtualSize.$SizeOfRawData,$IMAGE_SECTION_HEADER_offset if $VirtualSize!=$SizeOfRawData;
+       my $Characteristics=uint32_get($file,$IMAGE_SECTION_HEADER_offset+$Characteristics_rel_to_IMAGE_SECTION_HEADER_offs);
+       my $is_data =($Characteristics==0xC8000040);
+       my $is_rdata=($Characteristics==0x48000040);
+       die sprintf "Duplicate .data in IMAGE_SECTION_HEADER at 0x%X",$IMAGE_SECTION_HEADER_offset if $is_data && $offset_data;
+       die sprintf "Duplicate .rdata in IMAGE_SECTION_HEADER at 0x%X",$IMAGE_SECTION_HEADER_offset if $is_rdata && $offset_rdata;
+       $offset_data=$IMAGE_SECTION_HEADER_offset if $is_data;
+       $offset_rdata=$IMAGE_SECTION_HEADER_offset if $is_rdata;
+       }
+die ".data section not found" if !$offset_data;
+die ".rdata section not found" if !$offset_rdata;
+my $data_PointerToRawData=uint32_get($file,$offset_data+$PointerToRawData_rel_to_IMAGE_SECTION_HEADER_offs);
+my $data_SizeOfRawData=uint32_get($file,$offset_data+$SizeOfRawData_rel_to_IMAGE_SECTION_HEADER_offs);
+my $data_VirtualSize=uint32_get($file,$offset_data+$VirtualSize_rel_to_IMAGE_SECTION_HEADER_offs);
+my $rdata_PointerToRawData=uint32_get($file,$offset_rdata+$PointerToRawData_rel_to_IMAGE_SECTION_HEADER_offs);
+my $rdata_SizeOfRawData=uint32_get($file,$offset_rdata+$SizeOfRawData_rel_to_IMAGE_SECTION_HEADER_offs);
+my $rdata_VirtualSize=uint32_get($file,$offset_rdata+$VirtualSize_rel_to_IMAGE_SECTION_HEADER_offs);
+die ".data is not right after .rdata" if $rdata_PointerToRawData+$rdata_SizeOfRawData!=$data_PointerToRawData;
+uint32_put($file,$offset_data+$PointerToRawData_rel_to_IMAGE_SECTION_HEADER_offs,$rdata_PointerToRawData);
+uint32_put($file,$offset_data+$VirtualAddress_rel_to_IMAGE_SECTION_HEADER_offs,$rdata_PointerToRawData);
+uint32_put($file,$offset_data+$SizeOfRawData_rel_to_IMAGE_SECTION_HEADER_offs,$rdata_SizeOfRawData+$data_SizeOfRawData);
+uint32_put($file,$offset_data+$VirtualSize_rel_to_IMAGE_SECTION_HEADER_offs,$rdata_SizeOfRawData+$data_VirtualSize);
+# .rdata coalesced to .data, .rdata to be rebuilt now:
+uintX_put($file,$offset_rdata,0,8*$IMAGE_SECTION_HEADER_sizeof);       # zero the original space
+stringz_put($file,$offset_rdata+$Name_rel_to_IMAGE_SECTION_HEADER_offs,"INIT");
+uint32_put($file,$offset_rdata+$VirtualSize_rel_to_IMAGE_SECTION_HEADER_offs,@$file-$addon_section_base);
+uint32_put($file,$offset_rdata+$VirtualAddress_rel_to_IMAGE_SECTION_HEADER_offs,$addon_section_base);
+uint32_put($file,$offset_rdata+$SizeOfRawData_rel_to_IMAGE_SECTION_HEADER_offs,@$file-$addon_section_base);
+uint32_put($file,$offset_rdata+$PointerToRawData_rel_to_IMAGE_SECTION_HEADER_offs,$addon_section_base);
+uint32_put($file,$offset_rdata+$Characteristics_rel_to_IMAGE_SECTION_HEADER_offs,0xE2000020);
+
+
+# file output finalization
+uint32_put($file,SizeOfImage_offs($file),scalar(@$file));
+sum_put($file,sum_calc($file));
+print join("",map({ chr; } @$file));