FAT32 Analysis Script

Not completed, but gives some useful information before entering an infinite loop following FAT Chains. :-P

Download the code

   1 #!/usr/bin/perl
   2 #
   3 #
   4 # FAT32 Analysis Script by Mark Duller (May 9, 2008)
   5 #
   6 # References:
   7 #
   8 # http://perldoc.perl.org/
   9 # http://en.wikipedia.org/wiki/Master_boot_record
  10 # http://en.wikipedia.org/wiki/File_Allocation_Table
  11 use strict;
  12 use warnings;
  13 
  14 
  15 print "FAT32 Analysis Script by Mark Duller (May 9, 2008)\n\n";
  16 
  17 
  18 # Turn of output buffer
  19 $|++;
  20 
  21 #$infile = $ARGV[0];
  22 #$infile = "/usr/local/ad6_10_8.5gb";
  23 #$infile = "ad6_4_0gb";
  24 my $infile = "/dev/da0s2";  
  25 
  26 print "Reading from \"$infile\"\n\n";
  27 my $insize = -s $infile;
  28 # Open disk
  29 open(IN, "<", $infile) or die $!;
  30 binmode IN;
  31 
  32 
  33 ###################################################
  34 # check first byte of partition record
  35 seek(IN, 0x1BE, 0);
  36 read(IN, my $buffer, 1);
  37 my $bytebuff = unpack("C",$buffer);
  38 if($bytebuff != 0x00 and $bytebuff != 0x80){
  39         print "First partition does not appear valid! (", unpack("H*", $buffer), ") \n";
  40         print " Continue?";
  41         if(!(<> =~ /[Yy]/)){
  42                 print "Script terminated.\n";
  43                 exit(0);
  44         }
  45 }elsif($bytebuff == 0x00 ){
  46         print "First partition is not bootable.\n";
  47 }elsif($bytebuff == 0x80 ){
  48         print "First partition is bootable.\n";
  49 }
  50 
  51 
  52 ###################################################
  53 # check partition type (must be FAT32)
  54 seek(IN, 0x1C2, 0);
  55 read(IN, $buffer, 1);
  56 my $bytebuff = unpack("C",$buffer);
  57 if($bytebuff != 0x0C){
  58         print " File System does not appear to be FAT32! (", unpack("H*", $buffer), ") \n";
  59         print " Continue?";
  60         if(!(<> =~ /[Yy]/)){
  61                 print "Script terminated.\n";
  62                 exit(0);
  63         }
  64 }elsif($bytebuff == 0x0C ){
  65         print " File System Type: FAT32\n";
  66 }
  67 
  68 
  69 ###################################################
  70 # get location of 1st partition
  71 seek(IN, 0x1C6, 0);
  72 read(IN, $buffer, 4);
  73 my $Part1LocLBA = unpack("L",$buffer);
  74 my $Part1BSLoc = $Part1LocLBA * 512;
  75 my $buffer = scalar reverse pack("L",$Part1LocLBA);
  76 my $buffer = uc unpack("H*",$buffer);
  77 if($Part1LocLBA >= 0){
  78         print " Located at: 0x", $buffer, " LBA (offset = 0x", uc unpack("H*", scalar reverse pack("L",$Part1BSLoc)), ")\n";
  79 }else{
  80         print " Partition location is INVALID!\n";
  81         print " Continue?";
  82         if(!(<> =~ /[Yy]/)){
  83                 print "Script terminated.\n";
  84                 exit(0);
  85         }
  86 }
  87 
  88 
  89 ###################################################
  90 # get size of 1st partition
  91 seek(IN, 0x1CA, 0);
  92 read(IN, $buffer, 4);
  93 my $Part1Size = unpack("L",$buffer);
  94 if($Part1Size >= 0){
  95         print " Size: ", $Part1Size * 512, " Bytes \n";
  96 }else{
  97         print " Partition size is INVALID!\n";
  98         print " Continue?";
  99         if(!(<> =~ /[Yy]/)){
 100                 print "Script terminated.\n";
 101                 exit(0);
 102         }
 103 }
 104 
 105 ###################################################
 106 # Another simple test, confirm MBR sig
 107 seek(IN, 0x1FE, 0);
 108 read(IN, $buffer, 2);
 109 my $buffer = unpack("S",$buffer);
 110 if($buffer != 0xAA55){
 111         print " MBR signature incorrect! (", uc unpack("H*", scalar reverse pack("S",$buffer)), ")\nContinue?";
 112         if(!(<> =~ /[Yy]/)){
 113                 print "Script terminated.\n";
 114                 exit(0);
 115         }
 116 }
 117 
 118 
 119 ###################################################
 120 # examine boot sector of first partition
 121 seek(IN, $Part1BSLoc + 0x03, 0);
 122 read(IN, $buffer, 8);
 123 print "\nExamining 1st Partition Boot Sector:\n OEM Name: ", $buffer, "\n";
 124 # get bytes per sector
 125 seek(IN, $Part1BSLoc + 0x0B, 0);
 126 read(IN, $buffer, 2);
 127 my $Part1BpS = unpack("S",$buffer);
 128 if($Part1BpS > 0){
 129         print " Bytes per sector: ", $Part1BpS, "\n";
 130 }else{
 131         print " Bytes per sector value is INVALID!\nContinue?";
 132         if(!(<> =~ /[Yy]/)){
 133                 print "Script terminated.\n";
 134                 exit(0);
 135         }
 136 }
 137 
 138 
 139 ###################################################
 140 # get sectors per cluster
 141 seek(IN, $Part1BSLoc + 0x0d, 0);
 142 read(IN, $buffer, 1);
 143 my $Part1SpC = unpack("C",$buffer);
 144 if($Part1SpC > 0){
 145         print " Sectors per cluster: ", $Part1SpC, " (", $Part1SpC * $Part1BpS, " Bytes/Cluster)\n";
 146 }else{
 147         print " Sectors per cluster value is INVALID!\nContinue?";
 148         if(!(<> =~ /[Yy]/)){
 149                 print "Script terminated.\n";
 150                 exit(0);
 151         }
 152 }
 153 
 154 
 155 ###################################################
 156 # get first FAT offset
 157 seek(IN, $Part1BSLoc + 0x0e, 0);
 158 read(IN, $buffer, 2);
 159 my $Part1FAT1Loc = unpack("S",$buffer);
 160 my $Part1FAT1Loc = ($Part1FAT1Loc * $Part1BpS) + $Part1BSLoc;
 161 my $buffer = scalar reverse pack("L",$Part1FAT1Loc);
 162 my $buffer = uc scalar unpack("H*",$buffer);
 163 if($Part1FAT1Loc > 0){
 164         print " FAT #1 located at: 0x", $buffer, "\n";
 165 }else{
 166         print " FAT #1 location is INVALID!\nContinue?";
 167         if(!(<> =~ /[Yy]/)){
 168                 print "Script terminated.\n";
 169                 exit(0);
 170         }
 171 }
 172 
 173 
 174 ###################################################
 175 # get number of FAT's
 176 seek(IN, $Part1BSLoc + 0x10, 0);
 177 read(IN, $buffer, 1);
 178 my $Part1NumOfFATs = unpack("C",$buffer);
 179 my $buffer = scalar reverse pack("S",$Part1NumOfFATs);
 180 my $buffer = uc scalar unpack("H*",$buffer);
 181 if($Part1NumOfFATs > 0){
 182         print " Number of FAT's: ", $Part1NumOfFATs, "\n";
 183 }else{
 184         print " Number of FAT's is INVALID!\nContinue?";
 185         if(!(<> =~ /[Yy]/)){
 186                 print "Script terminated.\n";
 187                 exit(0);
 188         }
 189 }
 190 
 191 
 192 ###################################################
 193 # Another simple test for FAT32, value should be 0!
 194 seek(IN, $Part1BSLoc + 0x11, 0);
 195 read(IN, $buffer, 2);
 196 my $buffer = unpack("S",$buffer);
 197 if($buffer != 0){
 198         print " Invalid value detected in Boot Sector!\nContinue?";
 199         if(!(<> =~ /[Yy]/)){
 200                 print "Script terminated.\n";
 201                 exit(0);
 202         }
 203 }
 204 
 205 
 206 ###################################################
 207 # Get total sectors
 208 seek(IN, $Part1BSLoc + 0x13, 0);
 209 read(IN, $buffer, 2);
 210 my $Part1TotalSects = unpack("S",$buffer);
 211 # if value is zero, size is at 0x20
 212 if($Part1TotalSects == 0){
 213         seek(IN, $Part1BSLoc + 0x20, 0);
 214         read(IN, $buffer, 4);
 215         $Part1TotalSects = unpack("L",$buffer);
 216         if($Part1TotalSects > 0){
 217                 print " Total sectors: ", $Part1TotalSects, " (", $Part1TotalSects * $Part1BpS, " Bytes)\n";
 218                 if($Part1TotalSects == $Part1Size){
 219                         print "  Partition size confirmed!\n";
 220                 }else{
 221                         print "  Warning! Partition size mismatch! (MBR vs. BootSect)\n";
 222                 }
 223         }else{
 224                 print " Total sectors value is INVALID!\nContinue?";
 225                 if(!(<> =~ /[Yy]/)){
 226                         print "Script terminated.\n";
 227                         exit(0);
 228                 }
 229         }
 230 }
 231 
 232 
 233 ###################################################
 234 # get sectors per FAT
 235 seek(IN, $Part1BSLoc + 0x24, 0);
 236 read(IN, $buffer, 4);
 237 my $Part1SectpFAT = unpack("L",$buffer);
 238 if($Part1SectpFAT > 0){
 239         print " Sectors per FAT: ", $Part1SectpFAT, "\n";
 240 }else{
 241         print " Sectors per FAT's is INVALID!\nContinue?";
 242         if(!(<> =~ /[Yy]/)){
 243                 print "Script terminated.\n";
 244                 exit(0);
 245         }
 246 }
 247 
 248 
 249 ###################################################
 250 # calculate FAT #2 location
 251 if($Part1NumOfFATs > 1){
 252         my $Part1FAT2Loc = $Part1FAT1Loc + $Part1SectpFAT * $Part1BpS;
 253         my $buffer = scalar reverse pack("L",$Part1FAT2Loc);
 254         my $buffer = uc scalar unpack("H*",$buffer);
 255         print " FAT #2 located at: 0x", $buffer, "\n";
 256 }
 257 
 258 
 259 ###################################################
 260 # get location of root directory
 261 seek(IN, $Part1BSLoc + 0x2C, 0);
 262 read(IN, $buffer, 4);
 263 my $Part1RDLoc = unpack("L",$buffer); # normally this number seems to be 2 (ie: 3rd cluster)
 264 # note, following cluster start location is calculation based on FAT sizes & number, 
 265 #  which seems to be used as 3rd cluster location!
 266 my $Part1CSLocCalcd =  $Part1FAT1Loc + ($Part1NumOfFATs * $Part1SectpFAT * $Part1BpS);
 267 # cluster start offset appears to be 2 clusters before DATA area: 
 268 my $Part1CSLoc = $Part1CSLocCalcd - (2 * $Part1SpC * $Part1BpS);
 269 my $Part1RDLoc = $Part1CSLoc + $Part1RDLoc * $Part1SpC * $Part1BpS;
 270 if($Part1RDLoc == $Part1CSLocCalcd){
 271         print " Location of Root Directory: 0x",  uc unpack("H*", scalar reverse pack("L",$Part1RDLoc)), "\n";
 272         print "  This is also the start of the partition DATA\n  (cluster #2, realistically cluster #0)\n";
 273 }else{
 274         print " Cluster start location doesn't match script programming!\nContinue?";
 275         if(!(<> =~ /[Yy]/)){
 276                 print "Script terminated.\n";
 277                 exit(0);
 278         }
 279 }
 280 
 281 ###################################################
 282 # get FS information location
 283 seek(IN, $Part1BSLoc + 0x30, 0);
 284 read(IN, $buffer, 2);
 285 my $Part1FSLoc = unpack("S",$buffer) * $Part1BpS + $Part1BSLoc;
 286 if($Part1FSLoc > 0){
 287         print " FS Info located at: 0x", uc unpack("H*", scalar reverse pack("L",$Part1FSLoc)), "\n";
 288 }else{
 289         print " FS Info location is INVALID!\nContinue?";
 290         if(!(<> =~ /[Yy]/)){
 291                 print "Script terminated.\n";
 292                 exit(0);
 293         }
 294 }
 295 
 296 
 297 ###################################################
 298 # get boot sector copy location
 299 seek(IN, $Part1BSLoc + 0x32, 0);
 300 read(IN, $buffer, 2);
 301 my $Part1BSCopyLoc = unpack("S",$buffer) * $Part1BpS + $Part1BSLoc;
 302 if($Part1FSLoc > 0 and $Part1FSLoc < $Part1CSLocCalcd){
 303         print " Boot Sector copy located at: 0x", uc unpack("H*", scalar reverse pack("L",$Part1BSCopyLoc)), "\n";
 304 }else{
 305         print " Boot Sector copy location is INVALID!\nContinue?";
 306         if(!(<> =~ /[Yy]/)){
 307                 print "Script terminated.\n";
 308                 exit(0);
 309         }
 310 }
 311 
 312 
 313 ###################################################
 314 # get partition volume label
 315 seek(IN, $Part1BSLoc + 0x47, 0);
 316 read(IN, $buffer, 11);
 317 my $Part1Label = $buffer;
 318 print " Partition Volume Label: ", $Part1Label, "\n";
 319 
 320 
 321 ###################################################
 322 # Another simple test for FAT32
 323 seek(IN, $Part1BSLoc + 0x52, 0);
 324 read(IN, $buffer, 8);
 325 if(!($buffer =~ /FAT32/)){
 326         print " Indicated FS Type is not FAT32! (", $buffer, ")\nContinue?";
 327         if(!(<> =~ /[Yy]/)){
 328                 print "Script terminated.\n";
 329                 exit(0);
 330         }
 331 }
 332 
 333 ###################################################
 334 # Another simple test, confirm BS sig
 335 seek(IN, $Part1BSLoc + 0x1FE, 0);
 336 read(IN, $buffer, 2);
 337 my $buffer = unpack("S",$buffer);
 338 if($buffer != 0xAA55){
 339         print " Boot Sector signature incorrect! (", uc unpack("H*", scalar reverse pack("S",$buffer)), ")\nContinue?";
 340         if(!(<> =~ /[Yy]/)){
 341                 print "Script terminated.\n";
 342                 exit(0);
 343         }
 344 }
 345 
 346 
 347 ###################################################
 348 # compare both Boot Sectors
 349 my $BSmismatch = 0;
 350 for(my $i = 0; $i<512; $i++){
 351 
 352         seek(IN, $Part1BSLoc + $i, 0);
 353         read(IN, $buffer, 1);
 354         my $buffer = unpack("C",$buffer);
 355 
 356         seek(IN, $Part1BSCopyLoc + $i, 0);
 357         read(IN, $bytebuff, 1);
 358         my $bytebuff = unpack("C",$bytebuff);
 359         
 360         if($buffer != $bytebuff){
 361                 $BSmismatch++;
 362                 print " BS mismatch detected! 0x";
 363                 print uc unpack("H*", scalar reverse pack("S",$Part1BSLoc + $i)), ": ";
 364                 print uc unpack("H*", scalar reverse pack("C",$buffer)), " vs ";
 365                 print uc unpack("H*", scalar reverse pack("C",$bytebuff)), " (0x";
 366                 print uc unpack("H*", scalar reverse pack("S",$Part1BSCopyLoc + $i)), ")\n";
 367         }
 368 }
 369 if($BSmismatch != 0){
 370         print " Boot Sector's do not match! ", $BSmismatch, " mismatches detected\nContinue?";
 371         if(!(<> =~ /[Yy]/)){
 372                 print "Script terminated.\n";
 373                 exit(0);
 374         }
 375 }else{
 376         print " Boot Sector matches backup Boot Sector!\n";
 377 }
 378 
 379 
 380 ###################################################
 381 # Start analysing FAT
 382 seek(IN, $Part1FAT1Loc + 0x04, 0);
 383 read(IN, $buffer, 4);
 384 my $Part1EOCval = unpack("L",$buffer) & 0x0FFFFFFF;
 385 print "\nExamining FAT1:\n EOC Value: 0x", uc unpack("H*", scalar reverse pack("L",$Part1EOCval)), "\n";
 386 
 387 # count used clusters
 388 my $Part1EOCcount = 0;
 389 print " Counting used clusters... ";
 390 my $Part1FAT1BSize = $Part1SectpFAT * $Part1BpS;
 391 for(my $i=8;$i < $Part1FAT1BSize; $i = $i + 4){
 392         seek(IN, $Part1FAT1Loc + $i , 0);
 393         read(IN, $buffer, 4);
 394         my $buffer = unpack("L",$buffer);
 395         if(($buffer & 0x0FFFFFFF) == 0x0FFFFFFF){
 396                         $Part1EOCcount++;
 397         }
 398         if($buffer == 0){
 399                 # confirm!
 400                 seek(IN, $Part1FAT1Loc + $i + 4 , 0);
 401                 read(IN, $buffer, 4);
 402                 $buffer = unpack("L",$buffer);
 403                 my $Part1FAT1CU = ($i/4) - 1;
 404                 print $Part1FAT1CU, " (", $Part1FAT1CU * $Part1SpC * $Part1BpS / 1024 / 1024," MBytes)\n";
 405                 if($buffer != 0){
 406                         print "  Detected unfree cluster after free cluster!\n";
 407                 }
 408                 last;
 409         }
 410 }
 411 # display FAT chains count
 412 print " Number of FAT chains: ", $Part1EOCcount, " (non-deleted & deleted)\n";
 413 
 414 ###################################################
 415 # Follow chains
 416 #@Part1Chains 
 417 
 418 my $CIndex = 0;
 419 my $i = 8;
 420 my $PrvChainLoc = $i;
 421 my @Part1Chains = (0,0);
 422 my @Part1ClustMap = (1,1);
 423 for(my $itmp = 2;$itmp < $Part1FAT1BSize/4; $itmp++){
 424   $Part1ClustMap[$itmp] = 0;
 425 }
 426 for($CIndex=0;$CIndex < $Part1EOCcount; $PrvChainLoc = $PrvChainLoc + 4){
 427  if($Part1ClustMap[$i/4] != 1){
 428 #       print "warning! duplicate cluster in use!\n";
 429   for($i=$PrvChainLoc;$i < $Part1FAT1BSize and $Part1ClustMap[$i/4] != 1; ){
 430         seek(IN, $Part1FAT1Loc + $i , 0);
 431         read(IN, $buffer, 4);
 432         my $buffer = unpack("L",$buffer);
 433         if(($buffer & 0x0FFFFFFF) == 0x0FFFFFFF){ 
 434                 $Part1ClustMap[$i/4] = 1;
 435                 $Part1Chains[$CIndex] = $Part1Chains[$CIndex] + 1;
 436                 print $Part1Chains[$CIndex], " =Chain size (", $Part1Chains[$CIndex] * $Part1SpC * $Part1BpS / 1024, "KBytes)\n";
 437                 $CIndex++;
 438                 $Part1Chains[$CIndex] = 0;
 439                 last;
 440         }
 441         if($buffer == 0){
 442                 last;
 443         }
 444         $Part1ClustMap[$i/4] = 1;
 445         $Part1Chains[$CIndex]++;
 446         my $i = $buffer * 4;
 447         
 448   }
 449  }
 450 }
 451 #print $Part1Chains[$CIndex-1], "\n";
 452 
 453 
 454 
 455 # Useful Values:
 456 #
 457 # $Part1FAT1BSize
 458 # $Part1EOCval
 459 # $Part1BSCopyLoc
 460 # $Part1FSLoc
 461 # $Part1CSLocCalcd - calculated location of start of clusters (clusters 0 & 1 seem missing! Intentional?)
 462 # $Part1CSLoc - location of start of clusters (adjusted based on physically observed location of Root Dir)
 463 #       (confirm this with known good FAT32 disks)
 464 # $Part1RDLoc - location of root directory
 465 # $Part1NumOfFATs
 466 # $Part1Size  &  $Part1TotalSects
 467 # $Part1BpS
 468 # $Part1SpC
 469 # $Part1BSLoc
 470 # $Part1FAT1Loc
 471 # $Part1SectpFAT
 472 # $Part1RDLoc
 473 
 474 
 475 
 476 
 477 
 478 close IN;
 479 print "\n\nDONE!\n";
 480 
 481 exit(0);


back to main page

perl/FAT32 (last edited 2009-09-14 19:20:01 by MarkDuller)

Site Meter