1
1
# -*- coding: binary -*-
2
2
module Rex
3
3
module Parser
4
-
5
4
###
6
5
#
7
6
# This class parses the contents of an NTFS partition file.
@@ -19,17 +18,17 @@ def initialize(file_handler)
19
18
@file_handler = file_handler
20
19
data = @file_handler . read ( 4096 )
21
20
# Boot sector reading
22
- @bytes_per_sector = data [ 11 , 2 ] . unpack ( "v" ) [ 0 ]
23
- @sector_per_cluster = data [ 13 ] . unpack ( "C" ) [ 0 ]
24
- @cluster_per_mft_record = data [ 64 ] . unpack ( "c" ) [ 0 ]
21
+ @bytes_per_sector = data [ 11 , 2 ] . unpack ( 'v' ) [ 0 ]
22
+ @sector_per_cluster = data [ 13 ] . unpack ( 'C' ) [ 0 ]
23
+ @cluster_per_mft_record = data [ 64 ] . unpack ( 'c' ) [ 0 ]
25
24
if @cluster_per_mft_record < 0
26
25
@bytes_per_mft_record = 2 **( -@cluster_per_mft_record )
27
26
@cluster_per_mft_record = @bytes_per_mft_record . to_f / @bytes_per_sector / @sector_per_cluster
28
27
else
29
28
@bytes_per_mft_record = @bytes_per_sector * @sector_per_cluster * @cluster_per_mft_record
30
29
end
31
30
@bytes_per_cluster = @sector_per_cluster * @bytes_per_sector
32
- @mft_logical_cluster_number = data [ 48 , 8 ] . unpack ( "Q<" ) [ 0 ]
31
+ @mft_logical_cluster_number = data [ 48 , 8 ] . unpack ( 'Q<' ) [ 0 ]
33
32
@mft_offset = @mft_logical_cluster_number * @sector_per_cluster * @bytes_per_sector
34
33
@file_handler . seek ( @mft_offset )
35
34
@mft = @file_handler . read ( @bytes_per_mft_record )
@@ -40,7 +39,7 @@ def initialize(file_handler)
40
39
#
41
40
def mft_record_from_mft_num ( mft_num )
42
41
mft_num_offset = mft_num * @cluster_per_mft_record
43
- mft_data_attribute = mft_record_attribute ( @mft ) [ DATA_ATTRIBUTE_ID ] [ " data" ]
42
+ mft_data_attribute = mft_record_attribute ( @mft ) [ DATA_ATTRIBUTE_ID ] [ ' data' ]
44
43
cluster_from_attribute_non_resident ( mft_data_attribute , mft_num_offset , @bytes_per_mft_record )
45
44
end
46
45
@@ -49,7 +48,7 @@ def mft_record_from_mft_num(mft_num)
49
48
#
50
49
def real_size_from_filenameattribute ( attribute )
51
50
filename_attribute = attribute
52
- filename_attribute [ 48 , 8 ] . unpack ( "Q<" ) [ 0 ]
51
+ filename_attribute [ 48 , 8 ] . unpack ( 'Q<' ) [ 0 ]
53
52
end
54
53
55
54
#
@@ -59,7 +58,7 @@ def filename_from_filenameattribute(attribute)
59
58
filename_attribute = attribute
60
59
length_of_name = filename_attribute [ 64 ] . ord
61
60
# uft16 *2
62
- d = ::Encoding ::Converter . new ( " UTF-16LE" , " UTF-8" )
61
+ d = ::Encoding ::Converter . new ( ' UTF-16LE' , ' UTF-8' )
63
62
d . convert ( filename_attribute [ 66 , ( length_of_name * 2 ) ] )
64
63
end
65
64
@@ -72,10 +71,10 @@ def filename_from_filenameattribute(attribute)
72
71
def file_content_from_mft_num ( mft_num , size )
73
72
mft_record = mft_record_from_mft_num ( mft_num )
74
73
attribute_list = mft_record_attribute ( mft_record )
75
- if attribute_list [ DATA_ATTRIBUTE_ID ] [ " resident" ]
76
- return attribute_list [ DATA_ATTRIBUTE_ID ] [ " data" ]
74
+ if attribute_list [ DATA_ATTRIBUTE_ID ] [ ' resident' ]
75
+ return attribute_list [ DATA_ATTRIBUTE_ID ] [ ' data' ]
77
76
else
78
- data_attribute = attribute_list [ DATA_ATTRIBUTE_ID ] [ " data" ]
77
+ data_attribute = attribute_list [ DATA_ATTRIBUTE_ID ] [ ' data' ]
79
78
return cluster_from_attribute_non_resident ( data_attribute ) [ 0 , size ]
80
79
end
81
80
end
@@ -85,14 +84,15 @@ def file_content_from_mft_num(mft_num, size)
85
84
#
86
85
def parse_index ( index_entry )
87
86
res = { }
88
- filename_size = index_entry [ 10 , 2 ] . unpack ( "v" ) [ 0 ]
87
+ filename_size = index_entry [ 10 , 2 ] . unpack ( 'v' ) [ 0 ]
89
88
filename_attribute = index_entry [ 16 , filename_size ]
90
89
# Should be 8 bytes but it doesn't work
91
- # mft_offset = index_entry[0.unpack("Q<" ,:8])[0]
90
+ # mft_offset = index_entry[0.unpack('Q<' ,:8])[0]
92
91
# work with 4 bytes
93
- mft_offset = index_entry [ 0 , 4 ] . unpack ( "V" ) [ 0 ]
94
- res [ filename_from_filenameattribute ( filename_attribute ) ] = { "mft_offset" => mft_offset ,
95
- "file_size" => real_size_from_filenameattribute ( filename_attribute ) }
92
+ mft_offset = index_entry [ 0 , 4 ] . unpack ( 'V' ) [ 0 ]
93
+ res [ filename_from_filenameattribute ( filename_attribute ) ] = {
94
+ 'mft_offset' => mft_offset ,
95
+ 'file_size' => real_size_from_filenameattribute ( filename_attribute ) }
96
96
res
97
97
end
98
98
@@ -101,29 +101,29 @@ def parse_index(index_entry)
101
101
# INDEX_ALLOCATION
102
102
#
103
103
def parse_index_list ( index_record , index_allocation_attribute )
104
- offset_index_entry_list = index_record [ 0 , 4 ] . unpack ( "V" ) [ 0 ]
105
- index_size = index_record [ offset_index_entry_list + 8 , 2 ] . unpack ( "v" ) [ 0 ]
104
+ offset_index_entry_list = index_record [ 0 , 4 ] . unpack ( 'V' ) [ 0 ]
105
+ index_size = index_record [ offset_index_entry_list + 8 , 2 ] . unpack ( 'v' ) [ 0 ]
106
106
index_entry = index_record [ offset_index_entry_list , index_size ]
107
107
res = { }
108
- while index_entry [ 12 , 4 ] . unpack ( "V" ) [ 0 ] & 2 != 2
108
+ while index_entry [ 12 , 4 ] . unpack ( 'V' ) [ 0 ] & 2 != 2
109
109
res . update ( parse_index ( index_entry ) )
110
110
# if son
111
- if index_entry [ 12 , 4 ] . unpack ( "V" ) [ 0 ] & 1 == 1
111
+ if index_entry [ 12 , 4 ] . unpack ( 'V' ) [ 0 ] & 1 == 1
112
112
# should be 8 bytes length
113
- vcn = index_entry [ -8 , 4 ] . unpack ( "V" ) [ 0 ]
113
+ vcn = index_entry [ -8 , 4 ] . unpack ( 'V' ) [ 0 ]
114
114
vcn_in_bytes = vcn * @bytes_per_cluster
115
115
index_size_in_bytes = index_size * @bytes_per_cluster
116
116
res_son = parse_index_list ( index_allocation_attribute [ vcn_in_bytes + 24 , index_size_in_bytes ] , index_allocation_attribute )
117
117
res . update ( res_son )
118
118
end
119
119
offset_index_entry_list += index_size
120
- index_size = index_record [ offset_index_entry_list + 8 , 2 ] . unpack ( "v" ) [ 0 ]
120
+ index_size = index_record [ offset_index_entry_list + 8 , 2 ] . unpack ( 'v' ) [ 0 ]
121
121
index_entry = index_record [ offset_index_entry_list , index_size ]
122
122
end
123
123
# if son on the last
124
- if index_entry [ 12 , 4 ] . unpack ( "V" ) [ 0 ] & 1 == 1
124
+ if index_entry [ 12 , 4 ] . unpack ( 'V' ) [ 0 ] & 1 == 1
125
125
# should be 8 bytes length
126
- vcn = index_entry [ -8 , 4 ] . unpack ( "V" ) [ 0 ]
126
+ vcn = index_entry [ -8 , 4 ] . unpack ( 'V' ) [ 0 ]
127
127
vcn_in_bytes = vcn * @bytes_per_cluster
128
128
index_size_in_bytes = index_size * @bytes_per_cluster
129
129
res_son = parse_index_list ( index_allocation_attribute [ vcn_in_bytes + 24 , index_size_in_bytes ] , index_allocation_attribute )
@@ -141,16 +141,16 @@ def index_list_from_attributes(attributes)
141
141
if attributes . key? ( INDEX_ALLOCATION_ID )
142
142
return parse_index_list ( index_record , attributes [ INDEX_ALLOCATION_ID ] )
143
143
else
144
- return parse_index_list ( index_record , "" )
144
+ return parse_index_list ( index_record , '' )
145
145
end
146
146
end
147
147
148
148
def cluster_from_attribute_non_resident ( attribute , cluster_num = 0 , size_max = ( ( 2 **31 ) - 1 ) )
149
- lowvcn = attribute [ 16 , 8 ] . unpack ( "Q<" ) [ 0 ]
150
- highvcn = attribute [ 24 , 8 ] . unpack ( "Q<" ) [ 0 ]
151
- offset = attribute [ 32 , 2 ] . unpack ( "v" ) [ 0 ]
152
- real_size = attribute [ 48 , 8 ] . unpack ( "Q<" ) [ 0 ]
153
- attribut = ""
149
+ lowvcn = attribute [ 16 , 8 ] . unpack ( 'Q<' ) [ 0 ]
150
+ highvcn = attribute [ 24 , 8 ] . unpack ( 'Q<' ) [ 0 ]
151
+ offset = attribute [ 32 , 2 ] . unpack ( 'v' ) [ 0 ]
152
+ real_size = attribute [ 48 , 8 ] . unpack ( 'Q<' ) [ 0 ]
153
+ attribut = ''
154
154
run_list_num = lowvcn
155
155
old_offset = 0
156
156
@@ -160,7 +160,7 @@ def cluster_from_attribute_non_resident(attribute, cluster_num = 0, size_max = (
160
160
run_length_size = first_runlist_byte & 15
161
161
run_length = attribute [ offset + 1 , run_length_size ]
162
162
run_length += "\x00 " * ( 8 - run_length_size )
163
- run_length = run_length . unpack ( "Q<" ) [ 0 ]
163
+ run_length = run_length . unpack ( 'Q<' ) [ 0 ]
164
164
165
165
offset_run_offset = offset + 1 + run_length_size
166
166
run_offset = attribute [ offset_run_offset , run_offset_size ]
@@ -169,7 +169,7 @@ def cluster_from_attribute_non_resident(attribute, cluster_num = 0, size_max = (
169
169
else
170
170
run_offset += "\x00 " * ( 8 - run_offset_size )
171
171
end
172
- run_offset = run_offset . unpack ( "q<" ) [ 0 ]
172
+ run_offset = run_offset . unpack ( 'q<' ) [ 0 ]
173
173
174
174
size_wanted = [ run_length * @bytes_per_cluster , size_max - attribut . length ] . min
175
175
@@ -179,7 +179,7 @@ def cluster_from_attribute_non_resident(attribute, cluster_num = 0, size_max = (
179
179
run_list_offset = run_list_offset . to_i
180
180
@file_handler . seek ( run_list_offset )
181
181
182
- data = ""
182
+ data = ''
183
183
while data . length < size_wanted
184
184
data << @file_handler . read ( size_wanted - data . length )
185
185
end
@@ -195,22 +195,21 @@ def cluster_from_attribute_non_resident(attribute, cluster_num = 0, size_max = (
195
195
196
196
#
197
197
# return the attribute list from the MFT record
198
- # deal with resident and non resident attributes (but not $DATA due to perforemence issue)
198
+ # deal with resident and non resident attributes (but not $DATA due to performance issue)
199
199
#
200
200
def mft_record_attribute ( mft_record )
201
- attribute_list_offset = mft_record [ 20 , 2 ] . unpack ( "C" ) [ 0 ]
201
+ attribute_list_offset = mft_record [ 20 , 2 ] . unpack ( 'C' ) [ 0 ]
202
202
curs = attribute_list_offset
203
- attribute_identifier = mft_record [ curs , 4 ] . unpack ( "V" ) [ 0 ]
203
+ attribute_identifier = mft_record [ curs , 4 ] . unpack ( 'V' ) [ 0 ]
204
204
res = { }
205
205
while attribute_identifier != 0xFFFFFFFF
206
- # attribute_size=mft_record[curs + 4, 4].unpack("V" )[0]
206
+ # attribute_size=mft_record[curs + 4, 4].unpack('V' )[0]
207
207
# should be on 4 bytes but doesnt work
208
- attribute_size = mft_record [ curs + 4 , 2 ] . unpack ( "v" ) [ 0 ]
209
- #print_debug("attribute_size: #{attribute_size}, attribute_identifier: #{attribute_identifier}")
208
+ attribute_size = mft_record [ curs + 4 , 2 ] . unpack ( 'v' ) [ 0 ]
210
209
# resident
211
210
if mft_record [ curs + 8 ] == "\x00 "
212
- content_size = mft_record [ curs + 16 , 4 ] . unpack ( "V" ) [ 0 ]
213
- content_offset = mft_record [ curs + 20 , 2 ] . unpack ( "v" ) [ 0 ]
211
+ content_size = mft_record [ curs + 16 , 4 ] . unpack ( 'V' ) [ 0 ]
212
+ content_offset = mft_record [ curs + 20 , 2 ] . unpack ( 'v' ) [ 0 ]
214
213
res [ attribute_identifier ] = mft_record [ curs + content_offset , content_size ]
215
214
else
216
215
# non resident
@@ -221,11 +220,12 @@ def mft_record_attribute(mft_record)
221
220
end
222
221
end
223
222
if attribute_identifier == DATA_ATTRIBUTE_ID
224
- res [ attribute_identifier ] = { "data" => res [ attribute_identifier ] ,
225
- "resident" => mft_record [ curs + 8 ] == "\x00 " }
223
+ res [ attribute_identifier ] = {
224
+ 'data' => res [ attribute_identifier ] ,
225
+ 'resident' => mft_record [ curs + 8 ] == "\x00 " }
226
226
end
227
227
curs += attribute_size
228
- attribute_identifier = mft_record [ curs , 4 ] . unpack ( "V" ) [ 0 ]
228
+ attribute_identifier = mft_record [ curs , 4 ] . unpack ( 'V' ) [ 0 ]
229
229
end
230
230
res
231
231
end
@@ -236,22 +236,17 @@ def mft_record_attribute(mft_record)
236
236
def file ( path )
237
237
repertory = mft_record_from_mft_num ( 5 )
238
238
index_entry = { }
239
- for r in path . split ( " \\ " )
239
+ for r in path . split ( '\\' )
240
240
attributes = mft_record_attribute ( repertory )
241
241
index = index_list_from_attributes ( attributes )
242
- #print_debug("#{index}")
243
242
unless index . key? ( r )
244
- fail ArgumentError , " File path does not exist" , caller
243
+ fail ArgumentError , ' File path does not exist' , caller
245
244
end
246
245
index_entry = index [ r ]
247
- repertory = mft_record_from_mft_num ( index_entry [ " mft_offset" ] )
246
+ repertory = mft_record_from_mft_num ( index_entry [ ' mft_offset' ] )
248
247
end
249
- file_content_from_mft_num ( index_entry [ " mft_offset" ] , index_entry [ " file_size" ] )
248
+ file_content_from_mft_num ( index_entry [ ' mft_offset' ] , index_entry [ ' file_size' ] )
250
249
end
251
250
end
252
251
end
253
252
end
254
- # f = open(ARGV[0],"r")
255
- # ntfs = NTFS.new(f)
256
- # puts ntfs.file(ARGV[1])
257
- # f.close
0 commit comments