Fixed message typo.
[captive.git] / src / TraceFS / hookfs.pl
1 #! /usr/bin/perl
2
3 # $Id$
4 # Redirect system calls of the specified file system driver to TraceFS.sys
5 # Copyright (C) 2003 Jan Kratochvil <project-captive@jankratochvil.net>
6
7 # This program is free software; you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; exactly version 2 of June 1991 is required
10
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 # GNU General Public License for more details.
15
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19
20
21 use strict;
22 use warnings;
23 use Carp qw(cluck confess);
24
25
26 my $D=0;
27
28 my $IMAGE_FILE_IMPORT_DIRECTORY=1;
29 my $IMAGE_FILE_EXPORT_DIRECTORY=0;
30 my $IMAGE_DATA_DIRECTORY_sizeof=8;
31 my $VirtualAddress_rel_to_DATA_DIRECTORY_offs=0;
32 my $Size_rel_to_DATA_DIRECTORY_offs=4;
33 my $IMAGE_EXPORT_DIRECTORY_sizeof=0x28;
34 my $IMAGE_IMPORT_DESCRIPTOR_sizeof=0x14;
35 my $Name_rel_to_IMAGE_EXPORT_DIRECTORY_offs=0xC;
36 my $NumberOfNames_rel_to_IMAGE_EXPORT_DIRECTORY_offs=0x18;
37 my $AddressOfNames_rel_to_IMAGE_EXPORT_DIRECTORY_offs=0x20;
38 my $OriginalFirstThunk_rel_to_IMPORT_DIRECTORY_offs=0x0;
39 my $FirstThunk_rel_to_IMPORT_DIRECTORY_offs=0x10;
40 my $Name_rel_to_IMAGE_IMPORT_BY_NAME_offs=0x2;
41 my $IMAGE_SECTION_HEADER_sizeof=0x28;
42 my $Name_rel_to_IMAGE_SECTION_HEADER_offs=0x00;
43 my $VirtualSize_rel_to_IMAGE_SECTION_HEADER_offs=0x08;
44 my $VirtualAddress_rel_to_IMAGE_SECTION_HEADER_offs=0x0C;
45 my $SizeOfRawData_rel_to_IMAGE_SECTION_HEADER_offs=0x10;
46 my $PointerToRawData_rel_to_IMAGE_SECTION_HEADER_offs=0x14;
47 my $Characteristics_rel_to_IMAGE_SECTION_HEADER_offs=0x24;
48
49
50 sub uintX_get($$$)
51 {
52 my($file,$offset,$bits)=@_;
53
54         my $r=0;
55         for (my $byte=0;$byte<$bits/8;$byte++) {
56                 confess if !defined $file->[$offset+$byte];
57                 $r|=($file->[$offset+$byte])<<($byte*8);
58                 }
59         return $r;
60 }
61 sub uint32_get($$) { return uintX_get($_[0],$_[1],32); }
62 sub uint16_get($$) { return uintX_get($_[0],$_[1],16); }
63 sub  uint8_get($$) { return uintX_get($_[0],$_[1], 8); }
64
65 sub uintX_put($$$$)
66 {
67 my($file,$offset,$num,$bits)=@_;
68
69         for (my $byte=0;$byte<$bits/8;$byte++) {
70                 confess if !defined $offset;
71                 $file->[$offset+$byte]=($num>>($byte*8))&0xFF;
72                 }
73 }
74 sub uint32_put($$$) { return uintX_put($_[0],$_[1],$_[2],32); }
75 sub uint16_put($$$) { return uintX_put($_[0],$_[1],$_[2],16); }
76 sub  uint8_put($$$) { return uintX_put($_[0],$_[1],$_[2], 8); }
77 sub uint32_push($$) { return uintX_put($_[0],@{$_[0]},$_[1],32); }
78 sub uint16_push($$) { return uintX_put($_[0],@{$_[0]},$_[1],16); }
79 sub  uint8_push($$) { return uintX_put($_[0],@{$_[0]},$_[1], 8); }
80
81
82 sub sum_offs($)
83 {
84 my($file)=@_;
85
86         return uint32_get($file,0x3C)   # 3c: Offset to extended header
87                         +0x18   # OptionalHeader
88                         +0x40;  # CheckSum
89 }
90
91 sub sum_get($)
92 {
93 my($file)=@_;
94
95         return uint32_get($file,sum_offs($file));
96 }
97
98 sub sum_put($$)
99 {
100 my($file,$sum)=@_;
101
102         return uint32_put($file,sum_offs($file),$sum);
103 }
104
105 sub sum_calc($)
106 {
107 my($file)=@_;
108
109         $file=[ @$file ];
110         sum_put($file,0);
111         my $length=@$file;
112         my $sum=0;
113         while (@$file) {
114                 $sum+=(shift @$file || 0) | (shift @$file || 0)<<8;
115                 $sum=($sum&0xFFFF)+($sum>>16);
116                 }
117         $sum+=$length;
118         return $sum;
119 }
120
121 sub align($$)
122 {
123 my($file,$align)=@_;
124
125         push @$file,0 while @$file%$align;
126 }
127
128 sub stringz_put($$$)
129 {
130 my($file,$offset,$stringz)=@_;
131
132         $stringz=[ map({ ord(); } split //,$stringz),0 ];
133         while (@$stringz) {
134                 uint8_put($file,$offset,shift @$stringz);
135                 $offset++;
136                 }
137 }
138
139 sub stringz_push($$)
140 {
141 my($file,$stringz)=@_;
142
143         stringz_put($file,@$file,$stringz);
144         align $file,4;
145 }
146
147 sub stringz_get($$)
148 {
149 my($file,$offset)=@_;
150
151         my $r="";
152         while ((my $ord=uint8_get($file,$offset))) {
153                 $r.=chr $ord;
154                 $offset++;
155                 }
156         return $r;
157 }
158
159 sub zeroes_push($$)
160 {
161 my($file,$count)=@_;
162
163         push @$file,0 while $count-->0;
164 }
165
166 sub SizeOfImage_offs($)
167 {
168 my($file)=@_;
169
170         return uint32_get($file,0x3C)   # 3c: Offset to extended header
171                                 +0x18   # OptionalHeader
172                                 +0x38;  # SizeOfImage
173 }
174
175 my $align=0x80;
176
177 sub sanity($$)
178 {
179 my($file,$file_id)=@_;
180
181         my $calced=sum_calc($file);
182         if ($calced!=sum_get($file)) {
183                 warn sprintf "$file_id: Original checksum wrong: found=0x%08X, calced=0x%08X",sum_get($file),$calced;
184                 }
185
186         die sprintf "$file_id: Length 0x%X not aligned",scalar(@$file) if @$file%$align;
187         my $SizeOfImage=uint32_get($file,SizeOfImage_offs($file));
188         die sprintf "$file_id: SizeOfImage==0x%X but file size==0x%X",$SizeOfImage,scalar(@$file) if $SizeOfImage!=@$file;
189 }
190
191 sub export_names_get($)
192 {
193 my($tracefs)=@_;
194
195         my $EXPORT_DIRECTORY_offs=uint32_get($tracefs,0x3C)     # 3c: Offset to extended header
196                                 +0x18   # OptionalHeader
197                                 +0x60   # DataDirectory
198                                 +$IMAGE_FILE_EXPORT_DIRECTORY*$IMAGE_DATA_DIRECTORY_sizeof;
199         my $EXPORT_DIRECTORY_VirtualAddress=uint32_get($tracefs,$EXPORT_DIRECTORY_offs+$VirtualAddress_rel_to_DATA_DIRECTORY_offs);
200         my $EXPORT_DIRECTORY_Size=uint32_get($tracefs,$EXPORT_DIRECTORY_offs+$Size_rel_to_DATA_DIRECTORY_offs);
201         die sprintf "EXPORT_DIRECTORY_Size 0x%X less than IMAGE_IMPORT_DESCRIPTOR_sizeof 0x%X",
202                         $EXPORT_DIRECTORY_Size,$IMAGE_IMPORT_DESCRIPTOR_sizeof if $EXPORT_DIRECTORY_Size<$IMAGE_IMPORT_DESCRIPTOR_sizeof;
203         my $IMAGE_EXPORT_DIRECTORY_Name=uint32_get($tracefs,$EXPORT_DIRECTORY_VirtualAddress
204                         +$Name_rel_to_IMAGE_EXPORT_DIRECTORY_offs);
205         my $tracefs_export_name=stringz_get($tracefs,$IMAGE_EXPORT_DIRECTORY_Name);
206         my $tracefs_export_names_num=uint32_get($tracefs,$EXPORT_DIRECTORY_VirtualAddress
207                         +$NumberOfNames_rel_to_IMAGE_EXPORT_DIRECTORY_offs);
208         my $tracefs_export_AddressOfNames=uint32_get($tracefs,$EXPORT_DIRECTORY_VirtualAddress
209                         +$AddressOfNames_rel_to_IMAGE_EXPORT_DIRECTORY_offs);
210         my @tracefs_export_names;
211         print STDERR "$tracefs_export_name exports:\n" if $D;
212         for (my $namei=0;$namei<$tracefs_export_names_num;$namei++) {
213                 my $name_address=uint32_get($tracefs,$tracefs_export_AddressOfNames+4*$namei);
214                 my $name=stringz_get($tracefs,$name_address);
215                 push @tracefs_export_names,$name;
216                 print STDERR "\t$name\n" if $D;
217                 }
218         
219         return ($tracefs_export_name,@tracefs_export_names);
220 }
221
222
223 undef $/;
224 my $file=[ map({ ord(); } split //,<>) ];
225 my $tracefs=[ map({ ord(); } split //,<>) ];
226
227 sanity($tracefs,"tracefs");
228 my($tracefs_export_name,@tracefs_export_names)=export_names_get($tracefs);
229 for my $tname (@tracefs_export_names) {
230         die "tracefs exported tname is not /^T/ compliant: $tname" if $tname!~/^T/;
231         }
232 # FIXME: compiled to .sys tracefs contains internal export name .dll but we need import .sys !
233 $tracefs_export_name=~s/[.]dll$/.sys/i;
234
235 sanity($file,"file");
236
237
238 # import directory load
239
240 my $IMPORT_DIRECTORY_offs=uint32_get($file,0x3C)        # 3c: Offset to extended header
241                         +0x18   # OptionalHeader
242                         +0x60   # DataDirectory
243                         +$IMAGE_FILE_IMPORT_DIRECTORY*$IMAGE_DATA_DIRECTORY_sizeof;
244 my $IMPORT_DIRECTORY_VirtualAddress=uint32_get($file,$IMPORT_DIRECTORY_offs+$VirtualAddress_rel_to_DATA_DIRECTORY_offs);
245 my $IMPORT_DIRECTORY_Size=uint32_get($file,$IMPORT_DIRECTORY_offs+$Size_rel_to_DATA_DIRECTORY_offs);
246 die sprintf "IMPORT_DIRECTORY_Size 0x%X not aligned to IMAGE_IMPORT_DESCRIPTOR_sizeof 0x%X",
247                 $IMPORT_DIRECTORY_Size,$IMAGE_IMPORT_DESCRIPTOR_sizeof if $IMPORT_DIRECTORY_Size%$IMAGE_IMPORT_DESCRIPTOR_sizeof;
248
249 my $IMPORT_DIRECTORY=[ @{$file}[$IMPORT_DIRECTORY_VirtualAddress
250                 ..($IMPORT_DIRECTORY_VirtualAddress+$IMPORT_DIRECTORY_Size-1)] ];
251 uintX_put($file,$IMPORT_DIRECTORY_VirtualAddress,0,8*$IMPORT_DIRECTORY_Size);   # zero the original space
252 for (my $zerotail=0;$zerotail<$IMAGE_IMPORT_DESCRIPTOR_sizeof;$zerotail++) {
253         die "IMPORT_DIRECTORY tail not zeroed" if pop @$IMPORT_DIRECTORY;
254         }
255
256
257 # import directory entries processing
258
259 my %name_to_FirstThunk; # name->FirstThunk
260 my %tname_to_name;      # string->string{^.->T}
261 for (
262                 my $IMPORT_DIRECTORY_offset=0;
263                 $IMPORT_DIRECTORY_offset<@$IMPORT_DIRECTORY;
264                 $IMPORT_DIRECTORY_offset+=$IMAGE_IMPORT_DESCRIPTOR_sizeof) {
265         my $OriginalFirstThunk_base=uint32_get($IMPORT_DIRECTORY,$IMPORT_DIRECTORY_offset
266                         +$OriginalFirstThunk_rel_to_IMPORT_DIRECTORY_offs);
267         my $FirstThunk_base=uint32_get($IMPORT_DIRECTORY,$IMPORT_DIRECTORY_offset
268                         +$FirstThunk_rel_to_IMPORT_DIRECTORY_offs);
269         for (my $OriginalFirstThunk=$OriginalFirstThunk_base;;$OriginalFirstThunk+=4) {
270                 my $AddressOfData=uint32_get($file,$OriginalFirstThunk);
271                 last if !$AddressOfData;
272                 my $name=stringz_get($file,$AddressOfData+$Name_rel_to_IMAGE_IMPORT_BY_NAME_offs);
273                 print STDERR "import $name\n" if $D;
274                 die "Invalid name import as it has leading 'T': $name" if $name=~/^T/;
275                 (my $tname=$name)=~s/^./T/;
276                 die "Name conflict in tname map: $name->$tname" if exists $tname_to_name{$tname};
277                 $tname_to_name{$tname}=$name;
278                 die if exists $name_to_FirstThunk{$name};
279                 $name_to_FirstThunk{$name}=$FirstThunk_base+($OriginalFirstThunk-$OriginalFirstThunk_base);
280                 }
281         }
282
283
284 # add-on import directories generation
285 my $addon_section_base=@$file;
286
287 my $tracefs_export_name_offs=@$file;
288 stringz_push($file,$tracefs_export_name);
289
290 my $ordinal=1;
291 for my $tname (@tracefs_export_names) {
292         do { warn "tracefs exported tname $tname not found in imports"; next; } if !exists $tname_to_name{$tname};
293         my $name=$tname_to_name{$tname};
294         my $FirstThunk=$name_to_FirstThunk{$name};
295
296         my $IMAGE_IMPORT_BY_NAME_offs=@$file;
297         uint16_push($file,$ordinal++);  # Hint
298         stringz_push($file,$tname);     # Name
299
300         my $OriginalFirstThunk_offs=@$file;
301         uint32_push($file,$IMAGE_IMPORT_BY_NAME_offs);
302         uint32_push($file,0);   # zero terminator
303
304         uint32_push($IMPORT_DIRECTORY,$OriginalFirstThunk_offs);
305         uint32_push($IMPORT_DIRECTORY,0);       # TimeDateStamp
306         uint32_push($IMPORT_DIRECTORY,0);       # ForwarderChain
307         uint32_push($IMPORT_DIRECTORY,$tracefs_export_name_offs);       # Name
308         uint32_push($IMPORT_DIRECTORY,$FirstThunk);
309         }
310
311 zeroes_push $IMPORT_DIRECTORY,$IMAGE_IMPORT_DESCRIPTOR_sizeof;
312 uint32_put($file,$IMPORT_DIRECTORY_offs+$VirtualAddress_rel_to_DATA_DIRECTORY_offs,scalar(@$file));
313 uint32_put($file,$IMPORT_DIRECTORY_offs+$Size_rel_to_DATA_DIRECTORY_offs,scalar(@$IMPORT_DIRECTORY));
314 push @$file,@$IMPORT_DIRECTORY;
315
316
317 # rebuild the sections for add-on data
318 align $file,$align;
319 # concatenate .rdata to .data
320 my $SizeOfOptionalHeader_offs=uint32_get($file,0x3C)    # 3c: Offset to extended header
321                         +0x04   # FileHeader
322                         +0x10;  # SizeOfOptionalHeader
323 my $SizeOfOptionalHeader=uint16_get($file,$SizeOfOptionalHeader_offs);
324 my $NumberOfSections_offs=uint32_get($file,0x3C)        # 3c: Offset to extended header
325                         +0x04   # FileHeader
326                         +0x2;   # NumberOfSections
327 my $NumberOfSections=uint16_get($file,$NumberOfSections_offs);
328 my $IMAGE_SECTION_HEADER_offs=uint32_get($file,0x3C)    # 3c: Offset to extended header
329                         +0x18   # OptionalHeader
330                         +$SizeOfOptionalHeader;
331
332 my($offset_data,$offset_rdata);
333 for (
334                 my $IMAGE_SECTION_HEADER_offset=$IMAGE_SECTION_HEADER_offs;
335                 $IMAGE_SECTION_HEADER_offset<$IMAGE_SECTION_HEADER_offs+$NumberOfSections*$IMAGE_SECTION_HEADER_sizeof;
336                 $IMAGE_SECTION_HEADER_offset+=$IMAGE_SECTION_HEADER_sizeof) {
337         my $VirtualAddress=uint32_get($file,$IMAGE_SECTION_HEADER_offset+$VirtualAddress_rel_to_IMAGE_SECTION_HEADER_offs);
338         my $PointerToRawData=uint32_get($file,$IMAGE_SECTION_HEADER_offset+$PointerToRawData_rel_to_IMAGE_SECTION_HEADER_offs);
339         die sprintf "VirtualAddress 0x%X != PointerToRawData 0x%X in IMAGE_SECTION_HEADER at 0x%X",
340                         $VirtualAddress,$PointerToRawData,$IMAGE_SECTION_HEADER_offset if $VirtualAddress!=$PointerToRawData;
341         my $SizeOfRawData=uint32_get($file,$IMAGE_SECTION_HEADER_offset+$SizeOfRawData_rel_to_IMAGE_SECTION_HEADER_offs);
342         my $VirtualSize=uint32_get($file,$IMAGE_SECTION_HEADER_offset+$VirtualSize_rel_to_IMAGE_SECTION_HEADER_offs);
343         $VirtualSize+=$align-1;
344         $VirtualSize-=$VirtualSize%$align;
345         die sprintf "up_align(VirtualSize,0x%X) 0x%X != SizeOfRawData 0x%X in IMAGE_SECTION_HEADER at 0x%X",
346                         $align,$VirtualSize.$SizeOfRawData,$IMAGE_SECTION_HEADER_offset if $VirtualSize!=$SizeOfRawData;
347         my $Characteristics=uint32_get($file,$IMAGE_SECTION_HEADER_offset+$Characteristics_rel_to_IMAGE_SECTION_HEADER_offs);
348         my $is_data =($Characteristics==0xC8000040);
349         my $is_rdata=($Characteristics==0x48000040);
350         die sprintf "Duplicate .data in IMAGE_SECTION_HEADER at 0x%X",$IMAGE_SECTION_HEADER_offset if $is_data && $offset_data;
351         die sprintf "Duplicate .rdata in IMAGE_SECTION_HEADER at 0x%X",$IMAGE_SECTION_HEADER_offset if $is_rdata && $offset_rdata;
352         $offset_data=$IMAGE_SECTION_HEADER_offset if $is_data;
353         $offset_rdata=$IMAGE_SECTION_HEADER_offset if $is_rdata;
354         }
355 die ".data section not found" if !$offset_data;
356 die ".rdata section not found" if !$offset_rdata;
357 my $data_PointerToRawData=uint32_get($file,$offset_data+$PointerToRawData_rel_to_IMAGE_SECTION_HEADER_offs);
358 my $data_SizeOfRawData=uint32_get($file,$offset_data+$SizeOfRawData_rel_to_IMAGE_SECTION_HEADER_offs);
359 my $data_VirtualSize=uint32_get($file,$offset_data+$VirtualSize_rel_to_IMAGE_SECTION_HEADER_offs);
360 my $rdata_PointerToRawData=uint32_get($file,$offset_rdata+$PointerToRawData_rel_to_IMAGE_SECTION_HEADER_offs);
361 my $rdata_SizeOfRawData=uint32_get($file,$offset_rdata+$SizeOfRawData_rel_to_IMAGE_SECTION_HEADER_offs);
362 my $rdata_VirtualSize=uint32_get($file,$offset_rdata+$VirtualSize_rel_to_IMAGE_SECTION_HEADER_offs);
363 die ".data is not right after .rdata" if $rdata_PointerToRawData+$rdata_SizeOfRawData!=$data_PointerToRawData;
364 uint32_put($file,$offset_data+$PointerToRawData_rel_to_IMAGE_SECTION_HEADER_offs,$rdata_PointerToRawData);
365 uint32_put($file,$offset_data+$VirtualAddress_rel_to_IMAGE_SECTION_HEADER_offs,$rdata_PointerToRawData);
366 uint32_put($file,$offset_data+$SizeOfRawData_rel_to_IMAGE_SECTION_HEADER_offs,$rdata_SizeOfRawData+$data_SizeOfRawData);
367 uint32_put($file,$offset_data+$VirtualSize_rel_to_IMAGE_SECTION_HEADER_offs,$rdata_SizeOfRawData+$data_VirtualSize);
368 # .rdata coalesced to .data, .rdata to be rebuilt now:
369 uintX_put($file,$offset_rdata,0,8*$IMAGE_SECTION_HEADER_sizeof);        # zero the original space
370 stringz_put($file,$offset_rdata+$Name_rel_to_IMAGE_SECTION_HEADER_offs,"INIT");
371 uint32_put($file,$offset_rdata+$VirtualSize_rel_to_IMAGE_SECTION_HEADER_offs,@$file-$addon_section_base);
372 uint32_put($file,$offset_rdata+$VirtualAddress_rel_to_IMAGE_SECTION_HEADER_offs,$addon_section_base);
373 uint32_put($file,$offset_rdata+$SizeOfRawData_rel_to_IMAGE_SECTION_HEADER_offs,@$file-$addon_section_base);
374 uint32_put($file,$offset_rdata+$PointerToRawData_rel_to_IMAGE_SECTION_HEADER_offs,$addon_section_base);
375 uint32_put($file,$offset_rdata+$Characteristics_rel_to_IMAGE_SECTION_HEADER_offs,0xE2000020);
376
377
378 # file output finalization
379 uint32_put($file,SizeOfImage_offs($file),scalar(@$file));
380 sum_put($file,sum_calc($file));
381 print join("",map({ chr; } @$file));