You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
73 lines
2.7 KiB
Go
73 lines
2.7 KiB
Go
3 years ago
|
/*
|
||
|
Package bootsect provides functions to parse the boot sector (also sometimes called Volume Boot Record, VBR, or
|
||
|
$Boot file) of an NTFS volume.
|
||
|
*/
|
||
|
package bootsect
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
|
||
|
"b612.me/wincmd/ntfs/binutil"
|
||
|
)
|
||
|
|
||
|
// BootSector represents the parsed data of an NTFS boot sector. The OemId should typically be "NTFS " ("NTFS"
|
||
|
// followed by 4 trailing spaces) for a valid NTFS boot sector.
|
||
|
type BootSector struct {
|
||
|
OemId string
|
||
|
BytesPerSector int
|
||
|
SectorsPerCluster int
|
||
|
MediaDescriptor byte
|
||
|
SectorsPerTrack int
|
||
|
NumberofHeads int
|
||
|
HiddenSectors int
|
||
|
TotalSectors uint64
|
||
|
MftClusterNumber uint64
|
||
|
MftMirrorClusterNumber uint64
|
||
|
FileRecordSegmentSizeInBytes int
|
||
|
IndexBufferSizeInBytes int
|
||
|
VolumeSerialNumber []byte
|
||
|
}
|
||
|
|
||
|
// Parse parses the data of an NTFS boot sector into a BootSector structure.
|
||
|
func Parse(data []byte) (BootSector, error) {
|
||
|
if len(data) < 80 {
|
||
|
return BootSector{}, fmt.Errorf("boot sector data should be at least 80 bytes but is %d", len(data))
|
||
|
}
|
||
|
r := binutil.NewLittleEndianReader(data)
|
||
|
bytesPerSector := int(r.Uint16(0x0B))
|
||
|
sectorsPerCluster := int(int8(r.Byte(0x0D)))
|
||
|
if sectorsPerCluster < 0 {
|
||
|
// Quoth Wikipedia: The number of sectors in a cluster. If the value is negative, the amount of sectors is 2
|
||
|
// to the power of the absolute value of this field.
|
||
|
sectorsPerCluster = 1 << -sectorsPerCluster
|
||
|
}
|
||
|
bytesPerCluster := bytesPerSector * sectorsPerCluster
|
||
|
return BootSector{
|
||
|
OemId: string(r.Read(0x03, 8)),
|
||
|
BytesPerSector: bytesPerSector,
|
||
|
SectorsPerCluster: sectorsPerCluster,
|
||
|
MediaDescriptor: r.Byte(0x15),
|
||
|
SectorsPerTrack: int(r.Uint16(0x18)),
|
||
|
NumberofHeads: int(r.Uint16(0x1A)),
|
||
|
HiddenSectors: int(r.Uint16(0x1C)),
|
||
|
TotalSectors: r.Uint64(0x28),
|
||
|
MftClusterNumber: r.Uint64(0x30),
|
||
|
MftMirrorClusterNumber: r.Uint64(0x38),
|
||
|
FileRecordSegmentSizeInBytes: bytesOrClustersToBytes(r.Byte(0x40), bytesPerCluster),
|
||
|
IndexBufferSizeInBytes: bytesOrClustersToBytes(r.Byte(0x44), bytesPerCluster),
|
||
|
VolumeSerialNumber: binutil.Duplicate(r.Read(0x48, 8)),
|
||
|
}, nil
|
||
|
}
|
||
|
|
||
|
func bytesOrClustersToBytes(b byte, bytesPerCluster int) int {
|
||
|
// From Wikipedia:
|
||
|
// A positive value denotes the number of clusters in a File Record Segment. A negative value denotes the amount of
|
||
|
// bytes in a File Record Segment, in which case the size is 2 to the power of the absolute value.
|
||
|
// (0xF6 = -10 → 210 = 1024).
|
||
|
i := int(int8(b))
|
||
|
if i < 0 {
|
||
|
return 1 << -i
|
||
|
}
|
||
|
return i * bytesPerCluster
|
||
|
}
|