added files
This commit is contained in:
parent
8be86cb1fc
commit
8eb9a58938
1
AUTHORS
Normal file
1
AUTHORS
Normal file
@ -0,0 +1 @@
|
|||||||
|
fatmapper is written and maintained by Patrick Neumann <patrick@neumannsland.de>.
|
21
CHANGES
Normal file
21
CHANGES
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
Release 1.0 (in development)
|
||||||
|
============================
|
||||||
|
|
||||||
|
|
||||||
|
(Incompatible) changes
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
* none
|
||||||
|
|
||||||
|
|
||||||
|
Features added
|
||||||
|
--------------
|
||||||
|
|
||||||
|
* none
|
||||||
|
|
||||||
|
|
||||||
|
Bugs fixed
|
||||||
|
----------
|
||||||
|
|
||||||
|
* none
|
||||||
|
|
BIN
doc/fatmapper_documentation.pdf
Normal file
BIN
doc/fatmapper_documentation.pdf
Normal file
Binary file not shown.
858
fatmapper.py
Executable file
858
fatmapper.py
Executable file
@ -0,0 +1,858 @@
|
|||||||
|
#!/usr/bin/env python2.7
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
"""Display general details of a FAT file system."""
|
||||||
|
|
||||||
|
#===============================================================================
|
||||||
|
#
|
||||||
|
# FILE:
|
||||||
|
# ./fatmapper.py
|
||||||
|
#
|
||||||
|
# BASIC USAGE:
|
||||||
|
# $ ./fatmapper.py [-h|--help] [-o|--offset OFFSET] image
|
||||||
|
# OR
|
||||||
|
# $ python fatmapper.py [-h|--help] [-o|--offset OFFSET] image
|
||||||
|
#
|
||||||
|
# OPTIONS:
|
||||||
|
# -h,
|
||||||
|
# --help show help message and exit
|
||||||
|
# -o OFFSET,
|
||||||
|
# --offset OFFSET offset in sectors (default=0)
|
||||||
|
# image raw image file (esp. dd)
|
||||||
|
#
|
||||||
|
# EXIT STATES:
|
||||||
|
# 0 = success
|
||||||
|
# 1 = Python version not tested/supported
|
||||||
|
# 2 = image file does not exist
|
||||||
|
# 3 = offset is not a positive integer
|
||||||
|
# 4 = wrong or missing data in dictionary
|
||||||
|
# 5 = empty image file
|
||||||
|
# 6 = image file smaller than offset
|
||||||
|
# 7 = invalid vbr signatures
|
||||||
|
#
|
||||||
|
# REQUIREMENTS:
|
||||||
|
# python2.7
|
||||||
|
#
|
||||||
|
# NOTES:
|
||||||
|
# Tested on:
|
||||||
|
# - ArchLinux (64-Bit) + python 2.7.13
|
||||||
|
# - Raspbian GNU/Linux 8.0 (32-Bit) + python 2.7.9
|
||||||
|
# - macOS (10.12.3) + python 2.7.10
|
||||||
|
# - Bash on Ubuntu on Windows 10 (64-Bit) + python 2.7.6
|
||||||
|
# with:
|
||||||
|
# - raw images
|
||||||
|
# - filesystem(s) in partition(s)
|
||||||
|
# - filesystem without a partition
|
||||||
|
# - no partition/no filesystem (empty file)
|
||||||
|
# - invalid filesystem
|
||||||
|
# - FAT12, FAT16 and FAT32
|
||||||
|
#
|
||||||
|
# WARRANTY:
|
||||||
|
# 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 LICENSE file for more details.
|
||||||
|
#
|
||||||
|
# HISTORY:
|
||||||
|
# See the CHANGES file for more details.
|
||||||
|
#
|
||||||
|
#===============================================================================
|
||||||
|
|
||||||
|
#=== MODULES ===================================================================
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import hashlib
|
||||||
|
import os
|
||||||
|
import os.path
|
||||||
|
import re
|
||||||
|
import struct
|
||||||
|
import sys
|
||||||
|
|
||||||
|
#=== INFO ======================================================================
|
||||||
|
|
||||||
|
"""@author: Patrick Neumann
|
||||||
|
@contact: patrick@neumannsland.de
|
||||||
|
@copyright: Copyright (C) 2017, Patrick Neumann
|
||||||
|
@license: GNU General Public License 3.0
|
||||||
|
@date: 2017-03-26
|
||||||
|
@version: 1.0.0
|
||||||
|
@status: Development
|
||||||
|
"""
|
||||||
|
|
||||||
|
#=== CHECKS ====================================================================
|
||||||
|
|
||||||
|
"""Only tested with Python versions 2.7.9, 2.7.10 and 2.7.13!
|
||||||
|
|
||||||
|
Python 3.x is not supported!
|
||||||
|
"""
|
||||||
|
|
||||||
|
if sys.version_info < (2, 7, 0):
|
||||||
|
sys.stderr.write("Sorry, your Python version was not tested but may work?\n")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
if sys.version_info >= (3, 0, 0):
|
||||||
|
sys.stderr.write("Error: Python 3.x is not supported!\n")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
#=== FUNCTIONS =================================================================
|
||||||
|
|
||||||
|
# This is a private module function only for the other module functions.
|
||||||
|
# Unfortunately, the underscore does not prevent other developers from using
|
||||||
|
# it directly from outside this module. :-(
|
||||||
|
# sphinx-apidoc does ignore it. :-)
|
||||||
|
def _checkimage(_file):
|
||||||
|
"""Check image file.
|
||||||
|
|
||||||
|
Check if the image is a existing file.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
_file : str
|
||||||
|
path to the image file.
|
||||||
|
|
||||||
|
Raises
|
||||||
|
------
|
||||||
|
Exits if the image does not exist.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if not os.path.isfile(_file):
|
||||||
|
sys.stderr.write("Error: image file does not exist!\n")
|
||||||
|
sys.exit(2)
|
||||||
|
|
||||||
|
#-------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# This is a private module function only for the other module functions.
|
||||||
|
# Unfortunately, the underscore does not prevent other developers from using
|
||||||
|
# it directly from outside this module. :-(
|
||||||
|
# sphinx-apidoc does ignore it. :-)
|
||||||
|
def _checkoffset(loffset):
|
||||||
|
"""Check offset.
|
||||||
|
|
||||||
|
Check if the offset is an integer and is equal or greater than zero.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
loffset : int
|
||||||
|
offset in bytes in the image file.
|
||||||
|
|
||||||
|
Raises
|
||||||
|
------
|
||||||
|
Exits if the offset is not equal or greater than zero.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if not isinstance(loffset, int):
|
||||||
|
sys.stderr.write("Error: offset is not an integer!\n")
|
||||||
|
sys.exit(3)
|
||||||
|
|
||||||
|
if not loffset >= 0:
|
||||||
|
sys.stderr.write("Error: offset is not equal or greater zero!\n")
|
||||||
|
sys.exit(3)
|
||||||
|
|
||||||
|
#-------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# This is a private module function only for the other module functions.
|
||||||
|
# Unfortunately, the underscore does not prevent other developers from using
|
||||||
|
# it directly from outside this module. :-(
|
||||||
|
# sphinx-apidoc does ignore it. :-)
|
||||||
|
def _checkdict(check, _dict):
|
||||||
|
"""Check dictionary.
|
||||||
|
|
||||||
|
Check if the dictionary contains all needed data and that the data has
|
||||||
|
the right type.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
check : dict)
|
||||||
|
dictionary with necessary keys and types of the data dictionary.
|
||||||
|
|
||||||
|
_dict : dict
|
||||||
|
dictionary with mapping of parts of a fat VBR to a human readable index.
|
||||||
|
|
||||||
|
Raises
|
||||||
|
------
|
||||||
|
Exits if the data dictionary does not contain the necessary keys or the
|
||||||
|
type of data is incorrect.
|
||||||
|
"""
|
||||||
|
|
||||||
|
for key, var in check.items():
|
||||||
|
if not key in _dict:
|
||||||
|
sys.stderr.write("Error: necessary data is missing in dictionary!\n")
|
||||||
|
sys.exit(4)
|
||||||
|
# attention: on 32-Bit systems there is more often used long
|
||||||
|
# where on 64-Bit systems is int big enough:
|
||||||
|
if not isinstance(_dict[key], var):
|
||||||
|
sys.stderr.write("Error: invalid type of data in dictionary!\n")
|
||||||
|
sys.exit(4)
|
||||||
|
|
||||||
|
#-------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# This is a private module function only for the other module functions.
|
||||||
|
# Unfortunately, the underscore does not prevent other developers from using
|
||||||
|
# it directly from outside this module. :-(
|
||||||
|
# sphinx-apidoc does ignore it. :-)
|
||||||
|
def _getargs():
|
||||||
|
"""Get command line arguments.
|
||||||
|
|
||||||
|
Parses the command line arguments, checks and return them.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
tuple
|
||||||
|
A tuple containing the checked path to the image file
|
||||||
|
and the offset in bytes in the image file calculated from sectors.
|
||||||
|
|
||||||
|
The default offset is 0.
|
||||||
|
|
||||||
|
For example:
|
||||||
|
( "fat12_in_partition.dd", 1048576 )
|
||||||
|
"""
|
||||||
|
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
parser.add_argument("image",
|
||||||
|
type=str,
|
||||||
|
help="raw image file (esp.: image.dd)")
|
||||||
|
parser.add_argument("-o",
|
||||||
|
"--offset",
|
||||||
|
type=int,
|
||||||
|
default=0,
|
||||||
|
help="offset in sectors (default=0)")
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
_checkimage(args.image)
|
||||||
|
limage = args.image
|
||||||
|
|
||||||
|
_checkoffset(args.offset)
|
||||||
|
loffset = args.offset * 512
|
||||||
|
|
||||||
|
return (limage, loffset)
|
||||||
|
|
||||||
|
#-------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def getsize(_file, loffset):
|
||||||
|
"""Get image file size.
|
||||||
|
|
||||||
|
Get size of the image file.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
_file : str
|
||||||
|
path to the image file.
|
||||||
|
|
||||||
|
loffset : int
|
||||||
|
offset in bytes in the image file.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
int
|
||||||
|
size of image file in bytes.
|
||||||
|
|
||||||
|
Raises
|
||||||
|
------
|
||||||
|
Exits if image file is empty or smaller than offset.
|
||||||
|
"""
|
||||||
|
|
||||||
|
_checkimage(_file)
|
||||||
|
_checkoffset(loffset)
|
||||||
|
|
||||||
|
lsize = os.stat(_file)[6]
|
||||||
|
|
||||||
|
if lsize == 0:
|
||||||
|
sys.stderr.write("Error: the image file is an empty file!\n")
|
||||||
|
sys.exit(5)
|
||||||
|
|
||||||
|
if lsize < loffset + 512:
|
||||||
|
sys.stderr.write("Error: size of the image file is smaller than offset (+ 512 byte)!\n")
|
||||||
|
sys.exit(6)
|
||||||
|
|
||||||
|
return lsize
|
||||||
|
|
||||||
|
#-------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def getmd5(_file):
|
||||||
|
"""Get MD5 of image file.
|
||||||
|
|
||||||
|
Get MD5 digest in hex for the content of the image file.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
_file : str
|
||||||
|
path to the image file.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
str
|
||||||
|
MD5 digest in uppercase hex.
|
||||||
|
"""
|
||||||
|
|
||||||
|
_checkimage(_file)
|
||||||
|
|
||||||
|
lfile = open(_file, "rb")
|
||||||
|
lmd5 = hashlib.md5(lfile.read()).hexdigest().upper()
|
||||||
|
lfile.close()
|
||||||
|
|
||||||
|
return lmd5
|
||||||
|
|
||||||
|
#-------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def checkvbr(_file, loffset):
|
||||||
|
"""Check for FAT VBR.
|
||||||
|
|
||||||
|
Check for the existence of a valid FAT12/16/32 VBR.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
_file : str
|
||||||
|
path to the image file.
|
||||||
|
|
||||||
|
loffset : int
|
||||||
|
offset in bytes in the image file.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
str
|
||||||
|
file_system_type (FAT1X or FAT32)
|
||||||
|
|
||||||
|
Raises
|
||||||
|
------
|
||||||
|
Exits if the VBR can not be verified as a FAT12/16/32 VBR.
|
||||||
|
"""
|
||||||
|
|
||||||
|
_checkimage(_file)
|
||||||
|
_checkoffset(loffset)
|
||||||
|
|
||||||
|
lfile = open(_file, "rb")
|
||||||
|
lfile.seek(loffset + 38)
|
||||||
|
extended_signature = lfile.read(1)
|
||||||
|
|
||||||
|
if extended_signature == "\x29":
|
||||||
|
lfile.seek(15, 1)
|
||||||
|
file_system_type = lfile.read(8)
|
||||||
|
lfile.seek(448, 1)
|
||||||
|
vbr_signature = lfile.read(2)
|
||||||
|
file_system_type_switch = "FAT1X"
|
||||||
|
else:
|
||||||
|
lfile.seek(loffset + 66)
|
||||||
|
extended_signature = lfile.read(1)
|
||||||
|
lfile.seek(15, 1)
|
||||||
|
file_system_type = lfile.read(8)
|
||||||
|
lfile.seek(420, 1)
|
||||||
|
vbr_signature = lfile.read(2)
|
||||||
|
file_system_type_switch = "FAT32"
|
||||||
|
lfile.close()
|
||||||
|
|
||||||
|
condition1 = extended_signature == "\x29"
|
||||||
|
condition2 = re.search(r'FAT(1(2|6)|32) ', file_system_type)
|
||||||
|
condition3 = vbr_signature == "\x55\xaa"
|
||||||
|
|
||||||
|
if not (condition1 and condition2 and condition3):
|
||||||
|
sys.stderr.write("Error: no valid FAT12/FAT16/FAT32 VBR found!\n")
|
||||||
|
sys.exit(7)
|
||||||
|
|
||||||
|
#print "fileSystemTypeSwitch:", fileSystemTypeSwitch
|
||||||
|
|
||||||
|
return file_system_type_switch
|
||||||
|
|
||||||
|
#-------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def getraw(_file, loffset):
|
||||||
|
"""Get global data.
|
||||||
|
|
||||||
|
Read raw data from the VBR into a python dictionary.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
_file : str
|
||||||
|
path to the image file.
|
||||||
|
|
||||||
|
loffset : int
|
||||||
|
offset in bytes in the image file.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
dict
|
||||||
|
dictionary with mapping of parts of a fat VBR to a human readable index.
|
||||||
|
|
||||||
|
For example:
|
||||||
|
|
||||||
|
{"bytesPerSector": 512,
|
||||||
|
"sectorsPerCluster": 64,
|
||||||
|
"reservedSectors": 1,
|
||||||
|
"fatCopies": 2,
|
||||||
|
"rootDirectoryEntries": 1024,
|
||||||
|
"sectorsInPartition": 0,
|
||||||
|
"rawMediaDescriptor": 248,
|
||||||
|
"sectorsPerFat": 64,
|
||||||
|
"sectorsPerTrack": 32,
|
||||||
|
"numberOfHeads": 64,
|
||||||
|
"hiddenSectors": 0,
|
||||||
|
"largerSectorsInPartition": 260096}
|
||||||
|
"""
|
||||||
|
|
||||||
|
_checkimage(_file)
|
||||||
|
_checkoffset(loffset)
|
||||||
|
|
||||||
|
keys = ["bytesPerSector", "sectorsPerCluster", "reservedSectors",
|
||||||
|
"fatCopies", "rootDirectoryEntries", "sectorsInPartition",
|
||||||
|
"rawMediaDescriptor", "sectorsPerFat", "sectorsPerTrack",
|
||||||
|
"numberOfHeads", "hiddenSectors", "largerSectorsInPartition"]
|
||||||
|
|
||||||
|
lfile = open(_file, "rb")
|
||||||
|
lfile.seek(loffset + 3) # just skip the jumpcode
|
||||||
|
oem_name_version = lfile.read(8)
|
||||||
|
raw = lfile.read(2 + 1 + 2 + 1 + 2 + 2 + 1 + 2 + 2 + 2 + 4 + 4)
|
||||||
|
values = struct.unpack_from("<HBHBHHBHHHII", raw)
|
||||||
|
_dict = dict(zip(keys, values))
|
||||||
|
_dict["oemNameVersion"] = oem_name_version
|
||||||
|
lfile.close()
|
||||||
|
|
||||||
|
#for k, v in _dict.items():
|
||||||
|
# print k, ":", v
|
||||||
|
|
||||||
|
return _dict
|
||||||
|
|
||||||
|
#-------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def getraw1x(_file, loffset):
|
||||||
|
"""Get FAT12/16 spezial data.
|
||||||
|
|
||||||
|
Read raw data from the VBR into a python dictionary.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
_file : str
|
||||||
|
path to the image file.
|
||||||
|
|
||||||
|
loffset : int
|
||||||
|
offset in bytes in the image file.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
dict
|
||||||
|
dictionary with mapping of parts of a fat VBR to a human readable index.
|
||||||
|
|
||||||
|
For example:
|
||||||
|
|
||||||
|
{"logicalDriveNumber": 128,
|
||||||
|
"RESERVED": 0,
|
||||||
|
"extendedSignature": 41,
|
||||||
|
"rawVolumeSerialNumber": 549101972,
|
||||||
|
"oemNameVersion": "mkfs.fat",
|
||||||
|
"volumeLabel": "NO NAME ",
|
||||||
|
"rawFileSystemType": "FAT12 ",
|
||||||
|
"vbrSignature": "\x55\xaa"}
|
||||||
|
"""
|
||||||
|
|
||||||
|
_checkimage(_file)
|
||||||
|
_checkoffset(loffset)
|
||||||
|
|
||||||
|
keys = ["logicalDriveNumber", "RESERVED", "extendedSignature",
|
||||||
|
"rawVolumeSerialNumber"]
|
||||||
|
|
||||||
|
lfile = open(_file, "rb")
|
||||||
|
lfile.seek(loffset + 36) # just skip the global area
|
||||||
|
raw = lfile.read(1 + 1 + 1 + 4)
|
||||||
|
values = struct.unpack_from("<BBBI", raw)
|
||||||
|
_dict = dict(zip(keys, values))
|
||||||
|
_dict["volumeLabel"] = lfile.read(11)
|
||||||
|
_dict["rawFileSystemType"] = lfile.read(8)
|
||||||
|
lfile.seek(448, 1) # absolute offset 510 - actual offset 62 = relative offset 448
|
||||||
|
_dict["vbrSignature"] = lfile.read(2)
|
||||||
|
lfile.close()
|
||||||
|
|
||||||
|
#for k, v in _dict.items():
|
||||||
|
# print k, ":", v
|
||||||
|
|
||||||
|
return _dict
|
||||||
|
|
||||||
|
#-------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def getraw32(_file, loffset):
|
||||||
|
"""Get FAT32 spezial data.
|
||||||
|
|
||||||
|
Read raw data from the VBR into a python dictionary.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
_file :str
|
||||||
|
path to the image file.
|
||||||
|
|
||||||
|
loffset : int
|
||||||
|
offset in bytes in the image file.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
dict
|
||||||
|
dictionary with mapping of parts of a fat VBR to a human readable index.
|
||||||
|
|
||||||
|
For example:
|
||||||
|
|
||||||
|
{"sectorsPerFat": 947,
|
||||||
|
"mirrorFlags": 0,
|
||||||
|
"fileSystemVersion": 0,
|
||||||
|
"firstClusterOfRootDirectory": 2,
|
||||||
|
"fsinfoSector": 1,
|
||||||
|
"backupBootSector": 6,
|
||||||
|
"logicalDriveNumber": 128,
|
||||||
|
"RESERVED": 0,
|
||||||
|
"extendedSignature": 41,
|
||||||
|
"rawVolumeSerialNumber": 774266948,
|
||||||
|
"volumeLabel": "NO NAME ",
|
||||||
|
"rawFileSystemType": "FAT32 ",
|
||||||
|
"vbrSignature": "\x55\xaa"}
|
||||||
|
"""
|
||||||
|
|
||||||
|
_checkimage(_file)
|
||||||
|
_checkoffset(loffset)
|
||||||
|
|
||||||
|
keys1 = ["sectorsPerFat", "mirrorFlags", "fileSystemVersion",
|
||||||
|
"firstClusterOfRootDirectory", "fsinfoSector", "backupBootSector"]
|
||||||
|
keys2 = ["logicalDriveNumber", "RESERVED", "extendedSignature",
|
||||||
|
"rawVolumeSerialNumber"]
|
||||||
|
lfile = open(_file, "rb")
|
||||||
|
lfile.seek(loffset + 36) # just skip the global area
|
||||||
|
raw1 = lfile.read(4 + 2 + 2 + 4 + 2 + 2)
|
||||||
|
values1 = struct.unpack_from("<IHHIHH", raw1)
|
||||||
|
_dict = dict(zip(keys1, values1))
|
||||||
|
lfile.seek(12, 1)
|
||||||
|
raw2 = lfile.read(1 + 1 + 1 + 4)
|
||||||
|
values2 = struct.unpack_from("<BBBI", raw2)
|
||||||
|
_dict.update(dict(zip(keys2, values2)))
|
||||||
|
_dict["volumeLabel"] = lfile.read(11)
|
||||||
|
_dict["rawFileSystemType"] = lfile.read(8)
|
||||||
|
lfile.seek(420, 1) # absolute offset 510 - actual offset 90 = relative offset 420
|
||||||
|
_dict["vbrSignature"] = lfile.read(2)
|
||||||
|
lfile.close()
|
||||||
|
|
||||||
|
#for k, v in _dict.items():
|
||||||
|
# print k, ":", v
|
||||||
|
|
||||||
|
return _dict
|
||||||
|
|
||||||
|
#-------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def transform(_dict):
|
||||||
|
"""Transform data.
|
||||||
|
|
||||||
|
Do some modifications to the raw data.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
_dict : dict
|
||||||
|
dictionary with mapping of parts of a fat VBR to a human readable index.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
dict
|
||||||
|
dictionary with mapping of transformed parts of a fat VBR to a human
|
||||||
|
readable index.
|
||||||
|
"""
|
||||||
|
|
||||||
|
check = {"bytesPerSector": (int, long),
|
||||||
|
"sectorsPerCluster": (int, long),
|
||||||
|
"rawMediaDescriptor": (int, long),
|
||||||
|
"sectorsInPartition": (int, long),
|
||||||
|
"largerSectorsInPartition": (int, long),
|
||||||
|
"rawFileSystemType": str,
|
||||||
|
"rawVolumeSerialNumber": (int, long)}
|
||||||
|
_checkdict(check, _dict)
|
||||||
|
|
||||||
|
_dict["bytesPerCluster"] = _dict["bytesPerSector"] * _dict["sectorsPerCluster"]
|
||||||
|
if _dict["rawMediaDescriptor"] == 240:
|
||||||
|
_dict["mediaDescriptor"] = "floppy disk"
|
||||||
|
elif _dict["rawMediaDescriptor"] == 248:
|
||||||
|
_dict["mediaDescriptor"] = "hard disk"
|
||||||
|
else:
|
||||||
|
sys.stderr.write("Error: no valid media descriptor found!\n")
|
||||||
|
sys.exit(2)
|
||||||
|
|
||||||
|
if _dict["sectorsInPartition"] == 0:
|
||||||
|
_dict["sectorsInPartition"] = _dict["largerSectorsInPartition"]
|
||||||
|
_dict["fileSystemType"] = _dict["rawFileSystemType"][:3] + " " + _dict["rawFileSystemType"][3:]
|
||||||
|
_dict["volumeSerialNumber"] = "0x" + '{:08X}'.format(_dict["rawVolumeSerialNumber"])
|
||||||
|
|
||||||
|
#for k, v in _dict.items():
|
||||||
|
# print k, ":", v
|
||||||
|
|
||||||
|
return _dict
|
||||||
|
|
||||||
|
#-------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def getrootdirsize32(_file, loffset, _dict):
|
||||||
|
"""Get FAT32 root directory informations.
|
||||||
|
|
||||||
|
Read raw data about the root directory from the FAT32 VBR into a python dictionary.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
loffset : int
|
||||||
|
offset in bytes in the image file.
|
||||||
|
|
||||||
|
_dict :dict
|
||||||
|
dictionary with mapping of parts of a fat VBR to a human readable index.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
dict
|
||||||
|
dictionary with start, end and size of the root directory.
|
||||||
|
"""
|
||||||
|
|
||||||
|
_checkoffset(loffset)
|
||||||
|
check = {"reservedSectors": (int, long),
|
||||||
|
"bytesPerSector": (int, long),
|
||||||
|
"firstClusterOfRootDirectory": (int, long),
|
||||||
|
"sectorsPerFat": (int, long),
|
||||||
|
"sectorsPerCluster": (int, long)}
|
||||||
|
_checkdict(check, _dict)
|
||||||
|
|
||||||
|
lfile = open(_file, "rb")
|
||||||
|
lfile.seek(loffset +
|
||||||
|
(_dict["reservedSectors"] * _dict["bytesPerSector"]) +
|
||||||
|
(_dict["firstClusterOfRootDirectory"] * 4))
|
||||||
|
raw = lfile.read(4)
|
||||||
|
cluster = struct.unpack_from("<I", raw)[0]
|
||||||
|
_min = _dict["firstClusterOfRootDirectory"]
|
||||||
|
_max = _min
|
||||||
|
counter = 1
|
||||||
|
while cluster >= 2 and cluster <= 268435446:
|
||||||
|
if cluster > _max:
|
||||||
|
_max = cluster
|
||||||
|
if cluster < _min:
|
||||||
|
_min = cluster
|
||||||
|
counter = counter + 1
|
||||||
|
_next = loffset + (_dict["reservedSectors"] * _dict["bytesPerSector"]) + (cluster * 4)
|
||||||
|
if not lfile.tell() == _next:
|
||||||
|
lfile.seek(_next - lfile.tell(), 1)
|
||||||
|
raw = lfile.read(4)
|
||||||
|
cluster = struct.unpack_from("<I", raw)[0]
|
||||||
|
lfile.close()
|
||||||
|
|
||||||
|
# In the FAT the root directory starts in the entry 2 (0 = label and 1 = reserved)
|
||||||
|
# but in the data area it starts in cluster in the first (= 0)!
|
||||||
|
# To find the root directory in the FAT you have to use the entry in the VBR.
|
||||||
|
# To find the root directory in the data area you have to use the entry in the VBR - 2!
|
||||||
|
root_directory = {"rootDirectoryStartSector": _dict["reservedSectors"] +
|
||||||
|
2 * _dict["sectorsPerFat"] + _min - 2,
|
||||||
|
"rootDirectoryEndSector": _dict["reservedSectors"] +
|
||||||
|
2 * _dict["sectorsPerFat"] +
|
||||||
|
_max -
|
||||||
|
2 +
|
||||||
|
_dict["sectorsPerCluster"] -
|
||||||
|
1,
|
||||||
|
"rootDirectorySize": counter * _dict["sectorsPerCluster"]}
|
||||||
|
|
||||||
|
#for k, v in rootDirectory.items():
|
||||||
|
# print k, ":", v
|
||||||
|
|
||||||
|
return root_directory
|
||||||
|
|
||||||
|
#-------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def calculate(_file, loffset, _dict):
|
||||||
|
"""Calcuate data.
|
||||||
|
|
||||||
|
Do some calculations with the raw/modified data.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
_dict : dict
|
||||||
|
dictionary with mapping of transformed parts of a fat VBR to
|
||||||
|
a human readable index.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
dict
|
||||||
|
dictionary with mapping of transformed parts and added calculations of
|
||||||
|
a fat VBR to a human readable index.
|
||||||
|
"""
|
||||||
|
|
||||||
|
_checkoffset(loffset)
|
||||||
|
check = {"reservedSectors": (int, long),
|
||||||
|
"sectorsPerFat": (int, long),
|
||||||
|
"sectorsInPartition": (int, long),
|
||||||
|
"rootDirectoryEntries": (int, long),
|
||||||
|
"bytesPerSector": (int, long)}
|
||||||
|
_checkdict(check, _dict)
|
||||||
|
|
||||||
|
_dict["FAT0StartSector"] = _dict["reservedSectors"]
|
||||||
|
_dict["FAT0EndSector"] = _dict["reservedSectors"] + _dict["sectorsPerFat"] - 1
|
||||||
|
_dict["FAT1StartSector"] = _dict["FAT0EndSector"] + 1
|
||||||
|
_dict["FAT1EndSector"] = _dict["FAT1StartSector"] + _dict["sectorsPerFat"] - 1
|
||||||
|
_dict["DataAreaStartSector"] = _dict["FAT1EndSector"] + 1
|
||||||
|
_dict["DataAreaEndSector"] = _dict["sectorsInPartition"] - 1
|
||||||
|
_dict["DataAreaSize"] = _dict["sectorsInPartition"] - _dict["DataAreaStartSector"]
|
||||||
|
if _dict["rootDirectoryEntries"] != 0:
|
||||||
|
_dict["rootDirectoryStartSector"] = _dict["DataAreaStartSector"]
|
||||||
|
_dict["rootDirectorySize"] = _dict["rootDirectoryEntries"] * 32 / _dict["bytesPerSector"]
|
||||||
|
_dict["rootDirectoryEndSector"] = (_dict["DataAreaStartSector"] +
|
||||||
|
_dict["rootDirectorySize"] - 1)
|
||||||
|
else:
|
||||||
|
_dict.update(getrootdirsize32(_file, loffset, _dict))
|
||||||
|
|
||||||
|
#for k, v in _dict.items():
|
||||||
|
# print k, ":", v
|
||||||
|
|
||||||
|
return _dict
|
||||||
|
|
||||||
|
#-------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def getlabel2(_file, loffset, _dict):
|
||||||
|
"""Get FAT32 root directory data.
|
||||||
|
|
||||||
|
Read raw data about the label from the FAT32 root directory into a python dictionary.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
loffset : int
|
||||||
|
offset in bytes in the image file.
|
||||||
|
|
||||||
|
_dict : dict
|
||||||
|
dictionary with mapping of parts of a fat VBR to a human readable index.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
str
|
||||||
|
volume label from root directory
|
||||||
|
"""
|
||||||
|
|
||||||
|
_checkoffset(loffset)
|
||||||
|
check = {"rootDirectoryStartSector": (int, long),
|
||||||
|
"bytesPerSector": (int, long)}
|
||||||
|
_checkdict(check, _dict)
|
||||||
|
|
||||||
|
lfile = open(_file, "rb")
|
||||||
|
lfile.seek(loffset + _dict["rootDirectoryStartSector"] * _dict["bytesPerSector"])
|
||||||
|
volume_label2 = lfile.read(11)
|
||||||
|
lfile.close()
|
||||||
|
|
||||||
|
#print "volumeLabel2:", volumeLabel2
|
||||||
|
|
||||||
|
return volume_label2
|
||||||
|
|
||||||
|
#-------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def main(_file, loffset, _dict):
|
||||||
|
"""Print data.
|
||||||
|
|
||||||
|
Insert the data into the template and print it on stdout.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
_file : str
|
||||||
|
path to the image file.
|
||||||
|
|
||||||
|
loffset : int
|
||||||
|
offset in bytes in the image file.
|
||||||
|
|
||||||
|
_dict : dict
|
||||||
|
dictionary with mapping of transformed parts and added
|
||||||
|
calculations of a fat VBR to a human readable index.
|
||||||
|
"""
|
||||||
|
|
||||||
|
_checkimage(_file)
|
||||||
|
_checkoffset(loffset)
|
||||||
|
check = {"size": (int, long),
|
||||||
|
"md5": str,
|
||||||
|
"volumeLabel": str,
|
||||||
|
"oemNameVersion": str,
|
||||||
|
"fileSystemType": str,
|
||||||
|
"volumeSerialNumber": str,
|
||||||
|
"bytesPerSector": (int, long),
|
||||||
|
"bytesPerCluster": (int, long),
|
||||||
|
"sectorsPerCluster": (int, long),
|
||||||
|
"sectorsInPartition": (int, long),
|
||||||
|
"fatCopies": (int, long),
|
||||||
|
"reservedSectors": (int, long),
|
||||||
|
"FAT0StartSector": (int, long),
|
||||||
|
"FAT0EndSector": (int, long),
|
||||||
|
"sectorsPerFat": (int, long),
|
||||||
|
"FAT1StartSector": (int, long),
|
||||||
|
"FAT1EndSector": (int, long),
|
||||||
|
"DataAreaStartSector": (int, long),
|
||||||
|
"DataAreaEndSector": (int, long),
|
||||||
|
"DataAreaSize": (int, long),
|
||||||
|
"rootDirectoryStartSector": (int, long),
|
||||||
|
"rootDirectoryEndSector": (int, long),
|
||||||
|
"rootDirectorySize": (int, long)}
|
||||||
|
_checkdict(check, _dict)
|
||||||
|
|
||||||
|
tpl24_1 = "{:<24}{}"
|
||||||
|
tpl24_1b = "{:<24}{} bytes"
|
||||||
|
tpl24_2 = "{:<24}{} bytes ({} Sectors)"
|
||||||
|
tpl24_3 = "{:<24}{} - {} ({} Sectors)"
|
||||||
|
tpl21 = "{:<21}{} - {} ({} Sectors)"
|
||||||
|
print "File Information"
|
||||||
|
print tpl24_1.format("-- Name:",
|
||||||
|
_file)
|
||||||
|
print tpl24_1.format("-- File Size:",
|
||||||
|
_dict["size"])
|
||||||
|
print tpl24_1.format("-- MD5:",
|
||||||
|
_dict["md5"])
|
||||||
|
print
|
||||||
|
print "FS Information"
|
||||||
|
print tpl24_1.format("-- Volume Label:",
|
||||||
|
_dict["volumeLabel"])
|
||||||
|
if _dict["fileSystemType"] == "FAT 32 ":
|
||||||
|
print tpl24_1.format("-- Volume Label #2:",
|
||||||
|
getlabel2(_file, loffset, _dict))
|
||||||
|
print tpl24_1.format("-- OEM Name:",
|
||||||
|
_dict["oemNameVersion"])
|
||||||
|
print tpl24_1.format("-- File System:",
|
||||||
|
_dict["fileSystemType"])
|
||||||
|
print tpl24_1.format("-- Volume ID:",
|
||||||
|
_dict["volumeSerialNumber"])
|
||||||
|
print
|
||||||
|
print tpl24_1b.format("-- Sector Size:",
|
||||||
|
_dict["bytesPerSector"])
|
||||||
|
print tpl24_2.format("-- Cluster Size:",
|
||||||
|
_dict["bytesPerCluster"],
|
||||||
|
_dict["sectorsPerCluster"])
|
||||||
|
print tpl24_1.format("-- Number of Sectors:",
|
||||||
|
_dict["sectorsInPartition"])
|
||||||
|
print tpl24_1.format("-- Number of FATs:",
|
||||||
|
_dict["fatCopies"])
|
||||||
|
print tpl24_3.format("-- Total Range:",
|
||||||
|
0,
|
||||||
|
int(_dict["sectorsInPartition"]) - 1,
|
||||||
|
_dict["sectorsInPartition"])
|
||||||
|
print
|
||||||
|
print "FS Layout"
|
||||||
|
print tpl21.format("-- Reserved:",
|
||||||
|
0,
|
||||||
|
int(_dict["reservedSectors"]) - 1,
|
||||||
|
_dict["reservedSectors"])
|
||||||
|
print tpl21.format("---- VBR:", 0, 0, 1)
|
||||||
|
print tpl21.format("-- FAT 0:",
|
||||||
|
_dict["FAT0StartSector"],
|
||||||
|
_dict["FAT0EndSector"],
|
||||||
|
_dict["sectorsPerFat"])
|
||||||
|
print tpl21.format("-- FAT 1:",
|
||||||
|
_dict["FAT1StartSector"],
|
||||||
|
_dict["FAT1EndSector"],
|
||||||
|
_dict["sectorsPerFat"])
|
||||||
|
print tpl21.format("-- Data Area:",
|
||||||
|
_dict["DataAreaStartSector"],
|
||||||
|
_dict["DataAreaEndSector"],
|
||||||
|
_dict["DataAreaSize"])
|
||||||
|
print tpl21.format("---- Root Dir:",
|
||||||
|
_dict["rootDirectoryStartSector"],
|
||||||
|
_dict["rootDirectoryEndSector"],
|
||||||
|
_dict["rootDirectorySize"])
|
||||||
|
|
||||||
|
#=== MAIN ======================================================================
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
(IMAGE, OFFSET) = _getargs()
|
||||||
|
SIZE = getsize(IMAGE, OFFSET) # has to be done after _getargs()!
|
||||||
|
SWITCH = checkvbr(IMAGE, OFFSET)
|
||||||
|
DATA = getraw(IMAGE, OFFSET)
|
||||||
|
if SWITCH == "FAT1X":
|
||||||
|
DATA.update(getraw1x(IMAGE, OFFSET))
|
||||||
|
else:
|
||||||
|
DATA.update(getraw32(IMAGE, OFFSET))
|
||||||
|
DATA["size"] = SIZE
|
||||||
|
DATA["md5"] = getmd5(IMAGE)
|
||||||
|
DATA = transform(DATA)
|
||||||
|
DATA = calculate(IMAGE, OFFSET, DATA)
|
||||||
|
main(IMAGE, OFFSET, DATA)
|
||||||
|
sys.exit(0)
|
12
fatmapper_dict_missing_data.py
Executable file
12
fatmapper_dict_missing_data.py
Executable file
@ -0,0 +1,12 @@
|
|||||||
|
#!/usr/bin/env python2.7
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# Only a short python script to provoke exit state 4 of the fatmapper module!
|
||||||
|
|
||||||
|
import fatmapper
|
||||||
|
|
||||||
|
empty = {}
|
||||||
|
|
||||||
|
fatmapper.transform( empty )
|
||||||
|
|
||||||
|
sys.exit( 0 )
|
18
fatmapper_dict_wrong_type.py
Executable file
18
fatmapper_dict_wrong_type.py
Executable file
@ -0,0 +1,18 @@
|
|||||||
|
#!/usr/bin/env python2.7
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# Only a short python script to provoke exit state 4 of the fatmapper module!
|
||||||
|
|
||||||
|
import fatmapper
|
||||||
|
|
||||||
|
wrong = { "bytesPerSector": "string",
|
||||||
|
"sectorsPerCluster": "string",
|
||||||
|
"rawMediaDescriptor": "string",
|
||||||
|
"sectorsInPartition": "string",
|
||||||
|
"largerSectorsInPartition": "string",
|
||||||
|
"rawFileSystemType": 1,
|
||||||
|
"rawVolumeSerialNumber": "string" }
|
||||||
|
|
||||||
|
fatmapper.transform( wrong )
|
||||||
|
|
||||||
|
sys.exit( 0 )
|
Loading…
x
Reference in New Issue
Block a user