boot.go 2.83 KB
Newer Older
Michael Cohen's avatar
Michael Cohen committed
1
2
3
4
package ntfs

import (
	"errors"
5
6
	"fmt"
	"io"
Michael Cohen's avatar
Michael Cohen committed
7
8
9
10
11
12

	"www.velocidex.com/golang/vtypes"
)

type NTFS_BOOT_SECTOR struct {
	vtypes.Object
Michael Cohen's avatar
Michael Cohen committed
13
14

	cluster_size int64
Michael Cohen's avatar
Michael Cohen committed
15
16
17
}

func (self *NTFS_BOOT_SECTOR) ClusterSize() int64 {
Michael Cohen's avatar
Michael Cohen committed
18
19
20
21
22
23
24
	// This is frequently used so we can cache it locally.
	if self.cluster_size == 0 {
		self.cluster_size = self.Get("_cluster_size").AsInteger() *
			self.Get("sector_size").AsInteger()
	}

	return self.cluster_size
Michael Cohen's avatar
Michael Cohen committed
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
}

func (self *NTFS_BOOT_SECTOR) BlockCount() int64 {
	return self.Get("_volume_size").AsInteger() / self.ClusterSize()
}

func (self *NTFS_BOOT_SECTOR) RecordSize() int64 {
	_record_size := self.Get("_mft_record_size").AsInteger()
	if _record_size > 0 {
		return _record_size * self.ClusterSize()
	}
	return 1 << uint32(-_record_size)
}

func (self *NTFS_BOOT_SECTOR) MFT() (*MFT_ENTRY, error) {
	// The MFT is a table of MFT_ENTRY records read from an
	// abstracted reader which is itself a $DATA attribute of the
	// first MFT record:

	// MFT[0] -> Attr $DATA contains the entire $MFT stream.

	// We therefore need to bootstrap the MFT:
	// 1. Read the first entry in the first cluster using the disk reader.
	// 2. Search for the $DATA attribute.
	// 3. Reconstruct the runlist and RunReader from this attribute.
	// 4. Instantiate the MFT over this new reader.
	offset := self.Get("_mft_cluster").AsInteger() * self.ClusterSize()
	root_entry, err := _NewMFTEntry(self, self.Reader(), offset)
	if err != nil {
		return nil, err
	}

	for _, attr := range root_entry.Attributes() {
		// $DATA attribute = 128.
		if attr.Get("type").AsInteger() == 128 {
			result, err := _NewMFTEntry(self, attr.Data(), 0)
			return result, err
		}
	}

	return nil, errors.New("$DATA attribute not found for $MFT")
}

68
69
70
71
// NTFS Parsing starts with the boot record.
func NewBootRecord(profile *vtypes.Profile, reader io.ReaderAt, offset int64) (
	*NTFS_BOOT_SECTOR, error) {

72
	record_obj, err := profile.Create("NTFS_BOOT_SECTOR", offset, reader, nil)
73
74
75
76
	if err != nil {
		return nil, err
	}

Michael Cohen's avatar
Michael Cohen committed
77
	self := &NTFS_BOOT_SECTOR{Object: record_obj}
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
	if self.Get("magic").AsInteger() != 0xaa55 {
		return self, errors.New("Invalid magic")
	}

	switch self.ClusterSize() {
	case 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40,
		0x80, 0x100, 0x200, 0x400, 0x800, 0x1000:
		break
	default:
		return self, errors.New(
			fmt.Sprintf("Invalid cluster size %x",
				self.ClusterSize()))
	}

	sector_size := self.Get("sector_size").AsInteger()
	if sector_size == 0 || (sector_size%512 != 0) {
		return self, errors.New("Invalid sector_size")
	}

	if self.BlockCount() == 0 {
		return self, errors.New("Volume size is 0")
	}

	return self, nil
}

Michael Cohen's avatar
Michael Cohen committed
104
105
106
107
108
109
110
111
112
113
114
115
116
func GetProfile() (*vtypes.Profile, error) {
	profile := vtypes.NewProfile()
	err := profile.ParseStructDefinitions(NTFS_PROFILE)
	if err != nil {
		return nil, err
	}
	vtypes.AddModel(profile)

	// Add our local parsers.
	AddParsers(profile)

	return profile, nil
}