Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# exfat_bootsector_checksum.py
# 2018-05-19
# Copyright (c) 2018 Tet Woo Lee
#
# 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, either version 3 of the License, or
# (at your option) any later version.
#
# 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, see <http://www.gnu.org/licenses/>.
# Changelog:
# v1a: fixed typo in instructions
# v1: Initial version
# Tested working with:
# Python 3.6.4 (v3.6.4:d48eceb, Dec 19 2017, 06:04:45) [MSC v.1900 32 bit (Intel)] on win32
"""
Simple script to calculate checksum of an exFAT boot sector. To change
volume serial number of an exFAT volume, first alter the 4-byte id at
offset 0x64 of the exFAT volume. Then export boot sector [sectors 0 to
10] of the volume to a file. Generate a new checksum sector with this
file as 'infile' using the 'outfile' option. Replace the volume checksum
sector (sector 11) with the generated file. FOR SAFETY, DO NOT REPLACE
BACKUP BOOT SECTOR (sectors 12-23).
Run chkdsk /f to synchronise the boot sector with the backup boot
sector. Re-run chkdsk /f to ensure new volume serial number has been
maintained. If it has reverted to the old serial number, this is because
the backup boot sector has been used by chkdsk to replace the main one,
perhaps because there was an error when the main boot sector was edited.
"""
import argparse
import sys
import struct
import math
# Example C function to calculate exFAT volume checksum:
# Adapted from: https://www.sans.org/reading-room/whitepapers/forensics/reverse-engineering-microsoft-exfat-file-system-33274
#
# UINT32 CalcChecksum(const unsigned char data[], long n)
# {
# UINT32 checksum = 0;
#
# for (long i = 0; i < n; i++) {
# if (i == 106 || i == 107 || i == 112) continue;
# checksum = ((checksum<<31) | (checksum>>1)) + (UINT32) data[i];
# }
# return checksum;
# }
#
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('infile', help="Input file containing boot sector",
type=argparse.FileType('rb'))
parser.add_argument('outfile', nargs='?', help="Output checksum sector file (optional, will not replace)",
type=argparse.FileType('xb'))
parser.add_argument('-s','--size', help="Output file size in bytes (sector size, default: 512)",
default='512', type=int)
parsed_args = parser.parse_args()
i = 0
checksum = 0
# Read byte-by-byte and calculate checksum
with parsed_args.infile as f:
byte = f.read(1)
while len(byte) != 0:
if i == 106 or i == 107 or i == 112:
pass
else:
checksum = ((checksum << 31) | (checksum >> 1)) + ord(byte);
checksum &= 0xFFFFFFFF # keep as 32-bits
byte = f.read(1)
i +=1
print("exFAT checksum of input data is: 0x{:X}".format(checksum))
# Write checksum file
if parsed_args.outfile is not None:
print("Generating {}-byte output file containing repeats of checksum.".format(parsed_args.size))
checksum_repeats = math.ceil(parsed_args.size/4)
packed = (struct.pack("<L",checksum)*checksum_repeats)[:parsed_args.size]
# generate file containing rounded up # of repeats than truncate
# for correctness only, output size should always be multiple of 4 if it is an actual sector size
with parsed_args.outfile as f:
f.write(packed)
if __name__ == '__main__':
exit(main())
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment