Class: NTFS::BootSect
- Inherits:
-
Object
- Object
- NTFS::BootSect
- Defined in:
- lib/fs/ntfs/boot_sect.rb
Overview
BootSect represents a volume boot sector.
Instance Attribute Summary collapse
-
#bytesPerCluster ⇒ Object
readonly
Returns the value of attribute bytesPerCluster.
-
#bytesPerFileRec ⇒ Object
readonly
Returns the value of attribute bytesPerFileRec.
-
#bytesPerIndexRec ⇒ Object
readonly
Returns the value of attribute bytesPerIndexRec.
-
#bytesPerSector ⇒ Object
readonly
Returns the value of attribute bytesPerSector.
-
#mediaDescriptor ⇒ Object
readonly
Returns the value of attribute mediaDescriptor.
-
#sectorsPerCluster ⇒ Object
readonly
Returns the value of attribute sectorsPerCluster.
-
#serialNumber ⇒ Object
readonly
Returns the value of attribute serialNumber.
-
#signature ⇒ Object
readonly
Returns the value of attribute signature.
-
#stream ⇒ Object
readonly
Returns the value of attribute stream.
-
#totalCapacity ⇒ Object
readonly
Returns the value of attribute totalCapacity.
-
#version ⇒ Object
Returns the value of attribute version.
-
#volumeInfo ⇒ Object
Returns the value of attribute volumeInfo.
Instance Method Summary collapse
-
#bytesPerRec(size) ⇒ Object
NTFS has an interesting shorthand…
-
#clusterInfo ⇒ Object
From “File System Forensic Analysis” by Brian Carrier.
- #fragTable ⇒ Object
-
#freeBytes ⇒ Object
Returns free space on file system in bytes.
-
#getMaxMft ⇒ Object
Iterate all run lengths & return how many entries fit.
- #getVolumeInfo ⇒ Object
-
#initialize(stream) ⇒ BootSect
constructor
A new instance of BootSect.
-
#isMountable? ⇒ Boolean
Quick check to see if volume is mountable.
-
#lcn2abs(lcn) ⇒ Object
Convert a logical cluster number to an absolute byte position.
- #maxMft ⇒ Object
- #mftEntry(recordNumber) ⇒ Object
-
#mftLoc ⇒ Object
Return the absolute byte position of the MFT.
-
#mftRecToBytePos(recno) ⇒ Object
Use data run to convert mft record number to byte pos.
- #numFrags ⇒ Object
- #rootDir ⇒ Object
- #setup ⇒ Object
-
#to_s ⇒ Object
Convert to string (just return OEM name).
-
#vcn2abs(vcn) ⇒ Object
Convert a virtual cluster number to an absolute byte position.
Constructor Details
#initialize(stream) ⇒ BootSect
Returns a new instance of BootSect.
62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 |
# File 'lib/fs/ntfs/boot_sect.rb', line 62 def initialize(stream) raise "MIQ(NTFS::BootSect.initialize) Nil stream" if stream.nil? # Buffer stream & get enough data to fill BPB. @stream = stream buf = stream.read(SIZEOF_BOOT_PARAMETERS_BLOCK) @bpb = BOOT_PARAMETERS_BLOCK.decode(buf) # Always check magic number first. @signature = @bpb['signature'] raise "MIQ(NTFS::BootSect.initialize) Boot sector is not NTFS: 0x#{'%04x' % signature}" if signature != NTFS_MAGIC # Get accessor values. @bytesPerSector = @bpb['bytes_per_sector'] @bytesPerCluster = @bpb['sectors_per_cluster'] * @bytesPerSector @sectorsPerCluster = @bpb['sectors_per_cluster'] @mediaDescriptor = @bpb['media_descriptor'] @totalCapacity = @bpb['sectors_per_volume'] * @bytesPerSector @bytesPerFileRec = bytesPerRec(@bpb['clusters_per_mft_record']) @bytesPerIndexRec = bytesPerRec(@bpb['clusters_per_index_record']) @serialNumber = @bpb['volume_serial_number'] # MFTs in-memory @sys_mfts = {} @mfts = LruHash.new(NTFS::DEF_CACHE_SIZE) end |
Instance Attribute Details
#bytesPerCluster ⇒ Object (readonly)
Returns the value of attribute bytesPerCluster.
58 59 60 |
# File 'lib/fs/ntfs/boot_sect.rb', line 58 def bytesPerCluster @bytesPerCluster end |
#bytesPerFileRec ⇒ Object (readonly)
Returns the value of attribute bytesPerFileRec.
57 58 59 |
# File 'lib/fs/ntfs/boot_sect.rb', line 57 def bytesPerFileRec @bytesPerFileRec end |
#bytesPerIndexRec ⇒ Object (readonly)
Returns the value of attribute bytesPerIndexRec.
57 58 59 |
# File 'lib/fs/ntfs/boot_sect.rb', line 57 def bytesPerIndexRec @bytesPerIndexRec end |
#bytesPerSector ⇒ Object (readonly)
Returns the value of attribute bytesPerSector.
56 57 58 |
# File 'lib/fs/ntfs/boot_sect.rb', line 56 def bytesPerSector @bytesPerSector end |
#mediaDescriptor ⇒ Object (readonly)
Returns the value of attribute mediaDescriptor.
56 57 58 |
# File 'lib/fs/ntfs/boot_sect.rb', line 56 def mediaDescriptor @mediaDescriptor end |
#sectorsPerCluster ⇒ Object (readonly)
Returns the value of attribute sectorsPerCluster.
56 57 58 |
# File 'lib/fs/ntfs/boot_sect.rb', line 56 def sectorsPerCluster @sectorsPerCluster end |
#serialNumber ⇒ Object (readonly)
Returns the value of attribute serialNumber.
57 58 59 |
# File 'lib/fs/ntfs/boot_sect.rb', line 57 def serialNumber @serialNumber end |
#signature ⇒ Object (readonly)
Returns the value of attribute signature.
58 59 60 |
# File 'lib/fs/ntfs/boot_sect.rb', line 58 def signature @signature end |
#stream ⇒ Object (readonly)
Returns the value of attribute stream.
56 57 58 |
# File 'lib/fs/ntfs/boot_sect.rb', line 56 def stream @stream end |
#totalCapacity ⇒ Object (readonly)
Returns the value of attribute totalCapacity.
57 58 59 |
# File 'lib/fs/ntfs/boot_sect.rb', line 57 def totalCapacity @totalCapacity end |
#version ⇒ Object
Returns the value of attribute version.
60 61 62 |
# File 'lib/fs/ntfs/boot_sect.rb', line 60 def version @version end |
#volumeInfo ⇒ Object
Returns the value of attribute volumeInfo.
60 61 62 |
# File 'lib/fs/ntfs/boot_sect.rb', line 60 def volumeInfo @volumeInfo end |
Instance Method Details
#bytesPerRec(size) ⇒ Object
NTFS has an interesting shorthand…
95 96 97 |
# File 'lib/fs/ntfs/boot_sect.rb', line 95 def bytesPerRec(size) (size < 0) ? 2**size.abs : size * bytesPerCluster end |
#clusterInfo ⇒ Object
From “File System Forensic Analysis” by Brian Carrier
The $Bitmap file, which is located in MFT entry 6, has a $DATA attribute that is used to manage the allocation status of clusters. The bitmap data are organized into 1-byte values, and the least significant bit of each byte corresponds to the cluster that follows the cluster that the most significant bit of the previous byte corresponds to.
135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 |
# File 'lib/fs/ntfs/boot_sect.rb', line 135 def clusterInfo return @clusterInfo unless @clusterInfo.nil? # MFT Entry 6 ==> BITMAP Information ad = mftEntry(6).attributeData data = ad.read(ad.length) ad.rewind c = data.unpack("b#{data.length * 8}")[0] nclusters = c.length on = c.count("1") uclusters = on fclusters = c.length - on @clusterInfo = {"total" => nclusters, "free" => fclusters, "used" => uclusters} end |
#fragTable ⇒ Object
104 105 106 |
# File 'lib/fs/ntfs/boot_sect.rb', line 104 def fragTable @fragTable || @rootFragTable end |
#freeBytes ⇒ Object
Returns free space on file system in bytes.
153 154 155 |
# File 'lib/fs/ntfs/boot_sect.rb', line 153 def freeBytes clusterInfo["free"] * @bytesPerCluster end |
#getMaxMft ⇒ Object
Iterate all run lengths & return how many entries fit.
185 186 187 188 189 |
# File 'lib/fs/ntfs/boot_sect.rb', line 185 def getMaxMft total_clusters = 0 fragTable.each_slice(2) { |_vcn, len| total_clusters += len } total_clusters * @bytesPerCluster / @bytesPerFileRec end |
#getVolumeInfo ⇒ Object
157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 |
# File 'lib/fs/ntfs/boot_sect.rb', line 157 def getVolumeInfo mft = mftEntry(3) vi = {} if nameAttrib = mft.getFirstAttribute(AT_VOLUME_NAME) vi["name"] = nameAttrib.name end if objectidAttrib = mft.getFirstAttribute(AT_OBJECT_ID) vi["objectId"] = objectidAttrib.objectId.to_s vi["birthVolumeId"] = objectidAttrib.birthVolumeId.to_s vi["birthObjectId"] = objectidAttrib.birthObjectId.to_s vi["domainId"] = objectidAttrib.domainId.to_s end if infoAttrib = mft.getFirstAttribute(AT_VOLUME_INFORMATION) vi["version"] = infoAttrib.version vi["flags"] = infoAttrib.flags end vi end |
#isMountable? ⇒ Boolean
Quick check to see if volume is mountable.
210 211 212 213 214 215 216 217 |
# File 'lib/fs/ntfs/boot_sect.rb', line 210 def isMountable? return false if @bpb.nil? b = @bpb['reserved_sectors'] == 0 b &= @bpb['unused1'] == "\0" * 5 b &= @bpb['unused2'] == 0 b &= @bpb['unused4'] == 0 b &= @bpb['signature'] == 0xaa55 end |
#lcn2abs(lcn) ⇒ Object
Convert a logical cluster number to an absolute byte position.
220 221 222 |
# File 'lib/fs/ntfs/boot_sect.rb', line 220 def lcn2abs(lcn) lcn * bytesPerCluster end |
#maxMft ⇒ Object
108 109 110 111 |
# File 'lib/fs/ntfs/boot_sect.rb', line 108 def maxMft return getMaxMft if @fragTable.nil? @maxMft ||= getMaxMft end |
#mftEntry(recordNumber) ⇒ Object
195 196 197 198 199 200 201 202 203 204 205 206 207 |
# File 'lib/fs/ntfs/boot_sect.rb', line 195 def mftEntry(recordNumber) if recordNumber < 12 @sys_mfts[recordNumber] = MftEntry.new(self, recordNumber) unless @sys_mfts.key?(recordNumber) return @sys_mfts[recordNumber] end if @mfts.key?(recordNumber) mft = @mfts[recordNumber] mft.attributeData.rewind unless mft.attributeData.nil? return mft end @mfts[recordNumber] = MftEntry.new(self, recordNumber) end |
#mftLoc ⇒ Object
Return the absolute byte position of the MFT.
100 101 102 |
# File 'lib/fs/ntfs/boot_sect.rb', line 100 def mftLoc @bpb.nil? ? 0 : lcn2abs(@bpb['mft_lcn']) end |
#mftRecToBytePos(recno) ⇒ Object
Use data run to convert mft record number to byte pos.
230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 |
# File 'lib/fs/ntfs/boot_sect.rb', line 230 def mftRecToBytePos(recno) # Return start of mft if rec 0 (no point in the rest of this). return mftLoc if recno == 0 # Find which fragment contains the target mft record. start = fragTable[0]; last_clusters = 0; target_cluster = recno * @bytesPerFileRec / @bytesPerCluster if (recno > @bytesPerCluster / @bytesPerFileRec) && (fragTable.size > 2) total_clusters = 0 fragTable.each_slice(2) do |vcn, len| start = vcn # These are now absolute clusters, not offsets. total_clusters += len break if total_clusters > target_cluster last_clusters += len end # Toss if we haven't found the fragment. raise "MIQ(NTFS::BootSect.mftRecToBytePos) Can't find MFT record #{recno} in data run.\ntarget = #{target_cluster}\ntbl = #{fragTable.inspect}" if total_clusters < target_cluster end # Calculate offset in target cluster & final byte position. offset = (recno - (last_clusters * @bytesPerCluster / @bytesPerFileRec)) * @bytesPerFileRec start * @bytesPerCluster + offset end |
#numFrags ⇒ Object
180 181 182 |
# File 'lib/fs/ntfs/boot_sect.rb', line 180 def numFrags fragTable.size / 2 end |
#rootDir ⇒ Object
191 192 193 |
# File 'lib/fs/ntfs/boot_sect.rb', line 191 def rootDir @rootDir ||= mftEntry(5).indexRoot end |
#setup ⇒ Object
113 114 115 116 117 118 119 120 121 122 123 124 125 |
# File 'lib/fs/ntfs/boot_sect.rb', line 113 def setup @rootFragTable = mftEntry(0).rootAttributeData.data.runSpec @sys_mfts.clear @mfts.clear # MFT Entry 0 ==> Prepare a fragment table. @fragTable = mftEntry(0).attributeData.data.runSpec # Get the data runs for the MFT itself. # MFT Entry 3 ==> Volume Information @volumeInfo = getVolumeInfo @version = @volumeInfo["version"].to_i end |
#to_s ⇒ Object
Convert to string (just return OEM name).
90 91 92 |
# File 'lib/fs/ntfs/boot_sect.rb', line 90 def to_s @bpb['oem_name'].strip end |
#vcn2abs(vcn) ⇒ Object
Convert a virtual cluster number to an absolute byte position.
225 226 227 |
# File 'lib/fs/ntfs/boot_sect.rb', line 225 def vcn2abs(vcn) lcn2abs(vcn) end |