1
+ # -*- coding: binary -*-
1
2
module Rex
2
3
module Parser
3
4
@@ -11,11 +12,12 @@ class NTFS
11
12
#
12
13
# Initialize the NTFS class with an already open file handler
13
14
#
15
+ data_attribute = 128
14
16
def initialize ( file_handler )
15
17
@file_handler = file_handler
16
18
data = @file_handler . read ( 4096 )
17
19
# Boot sector reading
18
- @bytes_per_sector = data [ 11 , 2 ] . unpack ( "S " ) [ 0 ]
20
+ @bytes_per_sector = data [ 11 , 2 ] . unpack ( "v " ) [ 0 ]
19
21
@sector_per_cluster = data [ 13 ] . unpack ( "C" ) [ 0 ]
20
22
@cluster_per_mft_record = data [ 64 ] . unpack ( "c" ) [ 0 ]
21
23
if @cluster_per_mft_record < 0
@@ -25,7 +27,7 @@ def initialize(file_handler)
25
27
@bytes_per_mft_record = @bytes_per_sector * @sector_per_cluster * @cluster_per_mft_record
26
28
end
27
29
@bytes_per_cluster = @sector_per_cluster * @bytes_per_sector
28
- @mft_logical_cluster_number = data [ 48 , 8 ] . unpack ( "Q" ) [ 0 ]
30
+ @mft_logical_cluster_number = data [ 48 , 8 ] . unpack ( "Q< " ) [ 0 ]
29
31
@mft_offset = @mft_logical_cluster_number * @sector_per_cluster * @bytes_per_sector
30
32
@file_handler . seek ( @mft_offset )
31
33
@mft = @file_handler . read ( @bytes_per_mft_record )
@@ -35,15 +37,15 @@ def initialize(file_handler)
35
37
# Gather the MFT entry corresponding to his number
36
38
#
37
39
def mft_record_from_mft_num ( mft_num )
38
- cluster_from_attribute_non_resident ( mft_record_attribute ( @mft ) [ 128 ] [ "data" ] , mft_num * @cluster_per_mft_record , @bytes_per_mft_record )
40
+ cluster_from_attribute_non_resident ( mft_record_attribute ( @mft ) [ data_attribute ] [ "data" ] , mft_num * @cluster_per_mft_record , @bytes_per_mft_record )
39
41
end
40
42
41
43
#
42
44
# Get the size of the file in the $FILENAME (64) attribute
43
45
#
44
46
def real_size_from_filenameattribute ( attribute )
45
47
filename_attribute = attribute
46
- filename_attribute [ 48 , 8 ] . unpack ( "Q" ) [ 0 ]
48
+ filename_attribute [ 48 , 8 ] . unpack ( "Q< " ) [ 0 ]
47
49
end
48
50
49
51
#
@@ -66,10 +68,10 @@ def filename_from_filenameattribute(attribute)
66
68
def file_content_from_mft_num ( mft_num , size )
67
69
mft_record = mft_record_from_mft_num ( mft_num )
68
70
attribute_list = mft_record_attribute ( mft_record )
69
- if attribute_list [ 128 ] [ "resident" ]
70
- return attribute_list [ 128 ] [ "data" ]
71
+ if attribute_list [ data_attribute ] [ "resident" ]
72
+ return attribute_list [ data_attribute ] [ "data" ]
71
73
else
72
- return cluster_from_attribute_non_resident ( attribute_list [ 128 ] [ "data" ] ) [ 0 , size ]
74
+ return cluster_from_attribute_non_resident ( attribute_list [ data_attribute ] [ "data" ] ) [ 0 , size ]
73
75
end
74
76
end
75
77
@@ -78,12 +80,12 @@ def file_content_from_mft_num(mft_num, size)
78
80
#
79
81
def parse_index ( index_entry )
80
82
res = { }
81
- filename_size = index_entry [ 10 , 2 ] . unpack ( "S " ) [ 0 ]
83
+ filename_size = index_entry [ 10 , 2 ] . unpack ( "v " ) [ 0 ]
82
84
filename_attribute = index_entry [ 16 , filename_size ]
83
85
# Should be 8 bytes but it doesn't work
84
- # mft_offset = index_entry[0.unpack("Q",:8])[0]
86
+ # mft_offset = index_entry[0.unpack("Q< ",:8])[0]
85
87
# work with 4 bytes
86
- mft_offset = index_entry [ 0 , 4 ] . unpack ( "<I " ) [ 0 ]
88
+ mft_offset = index_entry [ 0 , 4 ] . unpack ( "V " ) [ 0 ]
87
89
res [ filename_from_filenameattribute ( filename_attribute ) ] = { "mft_offset" => mft_offset , "file_size" => real_size_from_filenameattribute ( filename_attribute ) }
88
90
res
89
91
end
@@ -93,27 +95,27 @@ def parse_index(index_entry)
93
95
# INDEX_ALLOCATION
94
96
#
95
97
def parse_index_list ( index_record , index_allocation_attribute )
96
- offset_index_entry_list = index_record [ 0 , 4 ] . unpack ( "<I " ) [ 0 ]
97
- index_size = index_record [ offset_index_entry_list + 8 , 2 ] . unpack ( "S " ) [ 0 ]
98
+ offset_index_entry_list = index_record [ 0 , 4 ] . unpack ( "V " ) [ 0 ]
99
+ index_size = index_record [ offset_index_entry_list + 8 , 2 ] . unpack ( "v " ) [ 0 ]
98
100
index_entry = index_record [ offset_index_entry_list , index_size ]
99
101
res = { }
100
- while index_entry [ 12 , 4 ] . unpack ( "<I " ) [ 0 ] & 2 != 2
102
+ while index_entry [ 12 , 4 ] . unpack ( "V " ) [ 0 ] & 2 != 2
101
103
res . update ( parse_index ( index_entry ) )
102
104
# if son
103
- if index_entry [ 12 , 4 ] . unpack ( "<I " ) [ 0 ] & 1 == 1
105
+ if index_entry [ 12 , 4 ] . unpack ( "V " ) [ 0 ] & 1 == 1
104
106
# should be 8 bytes length
105
- vcn = index_entry [ -8 , 4 ] . unpack ( "I " ) [ 0 ]
107
+ vcn = index_entry [ -8 , 4 ] . unpack ( "V " ) [ 0 ]
106
108
res_son = parse_index_list ( index_allocation_attribute [ vcn * @bytes_per_cluster + 24 , index_size * @bytes_per_cluster ] , index_allocation_attribute )
107
109
res . update ( res_son )
108
110
end
109
111
offset_index_entry_list += index_size
110
- index_size = index_record [ offset_index_entry_list + 8 , 2 ] . unpack ( "S " ) [ 0 ]
112
+ index_size = index_record [ offset_index_entry_list + 8 , 2 ] . unpack ( "v " ) [ 0 ]
111
113
index_entry = index_record [ offset_index_entry_list , index_size ]
112
114
end
113
115
# if son on the last
114
- if index_entry [ 12 , 4 ] . unpack ( "<I " ) [ 0 ] & 1 == 1
116
+ if index_entry [ 12 , 4 ] . unpack ( "V " ) [ 0 ] & 1 == 1
115
117
# should be 8 bytes length
116
- vcn = index_entry [ -8 , 4 ] . unpack ( "I " ) [ 0 ]
118
+ vcn = index_entry [ -8 , 4 ] . unpack ( "V " ) [ 0 ]
117
119
res_son = parse_index_list ( index_allocation_attribute [ vcn * @bytes_per_cluster + 24 , index_size * @bytes_per_cluster ] , index_allocation_attribute )
118
120
res . update ( res_son )
119
121
end
@@ -134,10 +136,10 @@ def index_list_from_attributes(attributes)
134
136
end
135
137
136
138
def cluster_from_attribute_non_resident ( attribute , cluster_num = 0 , size_max = ( ( 2 **31 ) - 1 ) )
137
- lowvcn = attribute [ 16 , 8 ] . unpack ( "<Q " ) [ 0 ]
138
- highvcn = attribute [ 24 , 8 ] . unpack ( "<Q " ) [ 0 ]
139
- offset = attribute [ 32 , 2 ] . unpack ( "<S " ) [ 0 ]
140
- real_size = attribute [ 48 , 8 ] . unpack ( "<Q " ) [ 0 ]
139
+ lowvcn = attribute [ 16 , 8 ] . unpack ( "Q< " ) [ 0 ]
140
+ highvcn = attribute [ 24 , 8 ] . unpack ( "Q< " ) [ 0 ]
141
+ offset = attribute [ 32 , 2 ] . unpack ( "v " ) [ 0 ]
142
+ real_size = attribute [ 48 , 8 ] . unpack ( "Q< " ) [ 0 ]
141
143
attribut = ""
142
144
run_list_num = lowvcn
143
145
old_offset = 0
@@ -148,16 +150,16 @@ def cluster_from_attribute_non_resident(attribute, cluster_num = 0, size_max = (
148
150
run_length_size = first_runlist_byte & 15
149
151
run_length = attribute [ offset + 1 , run_length_size ]
150
152
run_length += "\x00 " * ( 8 - run_length_size )
151
- run_length = run_length . unpack ( "<Q " ) [ 0 ]
153
+ run_length = run_length . unpack ( "Q< " ) [ 0 ]
152
154
153
155
offset_run_offset = offset + 1 + run_length_size
154
156
run_offset = attribute [ offset_run_offset , run_offset_size ]
155
157
if run_offset [ -1 ] . ord & 128 == 128
156
- run_offset += "\xFF " . force_encoding ( "BINARY" ) * ( 8 - run_offset_size )
158
+ run_offset += "\xFF " * ( 8 - run_offset_size )
157
159
else
158
160
run_offset += "\x00 " * ( 8 - run_offset_size )
159
161
end
160
- run_offset = run_offset . unpack ( "<q " ) [ 0 ]
162
+ run_offset = run_offset . unpack ( "q< " ) [ 0 ]
161
163
162
164
size_wanted = [ run_length * @bytes_per_cluster , size_max - attribut . length ] . min
163
165
@@ -187,31 +189,31 @@ def cluster_from_attribute_non_resident(attribute, cluster_num = 0, size_max = (
187
189
def mft_record_attribute ( mft_record )
188
190
attribute_list_offset = mft_record [ 20 , 2 ] . unpack ( "C" ) [ 0 ]
189
191
curs = attribute_list_offset
190
- attribute_identifier = mft_record [ curs , 4 ] . unpack ( "I " ) [ 0 ]
192
+ attribute_identifier = mft_record [ curs , 4 ] . unpack ( "V " ) [ 0 ]
191
193
res = { }
192
194
while attribute_identifier != 0xFFFFFFFF
193
- # attribute_size=mft_record[curs + 4, 4].unpack("I ")[0]
195
+ # attribute_size=mft_record[curs + 4, 4].unpack("V ")[0]
194
196
# should be on 4 bytes but doesnt work
195
- attribute_size = mft_record [ curs + 4 , 2 ] . unpack ( "S " ) [ 0 ]
197
+ attribute_size = mft_record [ curs + 4 , 2 ] . unpack ( "v " ) [ 0 ]
196
198
#print_debug("attribute_size: #{attribute_size}, attribute_identifier: #{attribute_identifier}")
197
199
# resident
198
200
if mft_record [ curs + 8 ] == "\x00 "
199
- content_size = mft_record [ curs + 16 , 4 ] . unpack ( "<I " ) [ 0 ]
200
- content_offset = mft_record [ curs + 20 , 2 ] . unpack ( "S " ) [ 0 ]
201
+ content_size = mft_record [ curs + 16 , 4 ] . unpack ( "V " ) [ 0 ]
202
+ content_offset = mft_record [ curs + 20 , 2 ] . unpack ( "v " ) [ 0 ]
201
203
res [ attribute_identifier ] = mft_record [ curs + content_offset , content_size ]
202
204
else
203
205
# non resident
204
- if attribute_identifier == 128
206
+ if attribute_identifier == data_attribute
205
207
res [ attribute_identifier ] = mft_record [ curs , attribute_size ]
206
208
else
207
209
res [ attribute_identifier ] = cluster_from_attribute_non_resident ( mft_record [ curs , attribute_size ] )
208
210
end
209
211
end
210
- if attribute_identifier == 128
212
+ if attribute_identifier == data_attribute
211
213
res [ attribute_identifier ] = { "data" => res [ attribute_identifier ] , "resident" => mft_record [ curs + 8 ] == "\x00 " }
212
214
end
213
215
curs += attribute_size
214
- attribute_identifier = mft_record [ curs , 4 ] . unpack ( "I " ) [ 0 ]
216
+ attribute_identifier = mft_record [ curs , 4 ] . unpack ( "V " ) [ 0 ]
215
217
end
216
218
res
217
219
end
0 commit comments