mirror of https://github.com/x64dbg/btparser
310 lines
9.0 KiB
Plaintext
310 lines
9.0 KiB
Plaintext
1:
|
|
2:
|
|
3:
|
|
4:
|
|
5:
|
|
6:
|
|
7:
|
|
8:
|
|
9:
|
|
10: string getMFTNameFromRecurdNumber ( UINT32 record_number ) {
|
|
11:
|
|
12: switch ( record_number ) {
|
|
13: case 0 :
|
|
14: return "$MFT" ;
|
|
15: case 1 :
|
|
16: return "$MFTMirr" ;
|
|
17: case 2 :
|
|
18: return "$LogFile" ;
|
|
19: case 3 :
|
|
20: return "$Volume" ;
|
|
21: case 4 :
|
|
22: return "$AttrDef" ;
|
|
23: case 5 :
|
|
24: return "$. (root dir)" ;
|
|
25: case 6 :
|
|
26: return "$Bitmap" ;
|
|
27: case 7 :
|
|
28: return "$Boot" ;
|
|
29: case 8 :
|
|
30: return "$BadClus" ;
|
|
31: case 9 :
|
|
32: return "$Secure" ;
|
|
33: case 10 :
|
|
34: return "$UpCase" ;
|
|
35: case 11 :
|
|
36: return "$Extend" ;
|
|
37: default :
|
|
38: return "" ;
|
|
39: }
|
|
40: }
|
|
41:
|
|
42: string GetMFTRecordComment ( struct MFTRecord & record ) {
|
|
43: string ret ;
|
|
44:
|
|
45: SPrintf ( ret , "Record # %u %s (%s)" ,
|
|
46: record . mftheader . mft_rec_number ,
|
|
47: getMFTNameFromRecurdNumber ( record . mftheader . mft_rec_number ) ,
|
|
48: HeaderFlagsToString ( record . mftheader . flags )
|
|
49: ) ;
|
|
50: return ret ;
|
|
51: }
|
|
52:
|
|
53:
|
|
54: wstring GetMFTNameFromAttribute ( struct Attributes & attrs ) {
|
|
55:
|
|
56: switch ( attrs . attribute . fixed_header . type ) {
|
|
57: case STANDARD_INFORMATION :
|
|
58: return "$STANDARD_INFORMATION" ;
|
|
59: case ATTRIBUTE_LIST :
|
|
60: return "$ATTRIBUTE_LIST" ;
|
|
61: case FILE_NAME :
|
|
62: string ret ;
|
|
63: SPrintf ( ret , "$FILE_NAME (%s)" ,
|
|
64: attrs . attribute . resident_attribute_content . file_name ) ;
|
|
65: return ret ;
|
|
66: case OBJECT_ID :
|
|
67: return "$OBJECT_ID" ;
|
|
68: case SECURITY_DESCRIPTOR :
|
|
69: return "$SECURITY_DESCRIPTOR" ;
|
|
70: case VOLUME_NAME :
|
|
71: return "$VOLUME_NAME" ;
|
|
72: case VOLUME_INFORMATION :
|
|
73: return "$VOLUME_INFORMATION" ;
|
|
74: case DATA :
|
|
75: string ret ;
|
|
76: if ( attrs . attribute . fixed_header . non_res_flag )
|
|
77: return "$DATA (Non resident)" ;
|
|
78: else
|
|
79: return "$DATA (Resident)" ;
|
|
80: case INDEX_ROOT :
|
|
81: return "$INDEX_ROOT" ;
|
|
82: case INDEX_ALLOCATION :
|
|
83: return "$INDEX_ALLOCATION" ;
|
|
84: case BITMAP :
|
|
85: string ret ;
|
|
86: SPrintf ( ret ,
|
|
87: "$BITMAP (map of bits telling for each record if they are in use or not)" ) ;
|
|
88: return ret ;
|
|
89: case REPARSE_POINT :
|
|
90: return "$REPARSE_POINT" ;
|
|
91: case EA_INFORMATION :
|
|
92: return "$EA_INFORMATION" ;
|
|
93: case EA :
|
|
94: return "$EA" ;
|
|
95: case LOGGED_UTILITY_STREAM :
|
|
96: return "$LOGGED_UTILITY_STREAM" ;
|
|
97: default :
|
|
98: return "" ;
|
|
99: }
|
|
100: }
|
|
101:
|
|
102:
|
|
103: string HeaderFlagsToString ( UINT16 flags ) {
|
|
104: string ret ;
|
|
105:
|
|
106: if ( flags & 0x2 )
|
|
107: Strcat ( ret , "Directory, " ) ;
|
|
108: else
|
|
109: Strcat ( ret , "File, " ) ;
|
|
110: if ( flags & 0x1 )
|
|
111: Strcat ( ret , "In use" ) ;
|
|
112: else
|
|
113: Strcat ( ret , "Not in use" ) ;
|
|
114: return ret ;
|
|
115: }
|
|
116:
|
|
117:
|
|
118: string AttributeFlagsToString ( UINT16 flags ) {
|
|
119: string ret ;
|
|
120:
|
|
121: if ( flags & 0x1 )
|
|
122: Strcat ( ret , "Compressed, " ) ;
|
|
123: else
|
|
124: Strcat ( ret , "Not Compressed, " ) ;
|
|
125: if ( flags & 0x4000 )
|
|
126: Strcat ( ret , "Encrypted, " ) ;
|
|
127: else
|
|
128: Strcat ( ret , "Not Encrypted, " ) ;
|
|
129: if ( flags & 0x8000 )
|
|
130: Strcat ( ret , "Sparse" ) ;
|
|
131: else
|
|
132: Strcat ( ret , "Not Sparse" ) ;
|
|
133: return ret ;
|
|
134: }
|
|
135:
|
|
136:
|
|
137: string GetParentDirComment ( uint64 ref_to_parent_dir ) {
|
|
138: string ret ,
|
|
139: details ;
|
|
140:
|
|
141:
|
|
142:
|
|
143: UINT16 seq_num = ref_to_parent_dir >> 24 ;
|
|
144: UQUAD mft_entry = ref_to_parent_dir & 0xFFFFFFFFFFFF ;
|
|
145: if ( mft_entry == 0x5 ) {
|
|
146:
|
|
147: Strcat ( ret , "Root dir, " ) ;
|
|
148: }
|
|
149: SPrintf ( details , "Seq num = %04xh [!!] , MFT entry = %012xh" , seq_num , mft_entry ) ;
|
|
150: Strcat ( ret , details ) ;
|
|
151: return ret ;
|
|
152: }
|
|
153:
|
|
154:
|
|
155: string GetAllocSize ( UQUAD alloc_size ) {
|
|
156:
|
|
157: string ret ;
|
|
158: UQUAD num_clusters = alloc_size / 4096 + ( alloc_size % 4096 ? 1 : 0 ) ;
|
|
159: SPrintf ( ret , "%lu clusters (each cluster 4096 bytes)" , num_clusters ) ;
|
|
160: return ret ;
|
|
161: }
|
|
162:
|
|
163:
|
|
164: string GetDataRunStart ( UINT16 data_run_info ) {
|
|
165: string ret ;
|
|
166: SPrintf ( ret , "Starting at 0x%x (assuming cluster size = 4096)" , data_run_info * 4096 ) ;
|
|
167: return ret ;
|
|
168: }
|
|
169:
|
|
170: typedef struct MFTAttribute {
|
|
171: local int64 mftattribute_start = FTell ( ) ;
|
|
172: struct FixedHeader {
|
|
173: enum < UINT32 > {
|
|
174: STANDARD_INFORMATION = 0x10 ,
|
|
175: ATTRIBUTE_LIST = 0x20 ,
|
|
176: FILE_NAME = 0x30 ,
|
|
177: OBJECT_ID = 0x40 ,
|
|
178: SECURITY_DESCRIPTOR = 0x50 ,
|
|
179: VOLUME_NAME = 0x60 ,
|
|
180: VOLUME_INFORMATION = 0x70 ,
|
|
181: DATA = 0x80 ,
|
|
182: INDEX_ROOT = 0x90 ,
|
|
183: INDEX_ALLOCATION = 0xA0 ,
|
|
184: BITMAP = 0xB0 ,
|
|
185: REPARSE_POINT = 0xC0 ,
|
|
186: EA_INFORMATION = 0xD0 ,
|
|
187: EA = 0xE0 ,
|
|
188: LOGGED_UTILITY_STREAM = 0x100
|
|
189: } type ;
|
|
190: UINT32 attr_length ;
|
|
191: enum < UBYTE > {
|
|
192: RESIDENT = 0 ,
|
|
193: NON_RESIDENT = 1
|
|
194: } non_res_flag < comment = "Resident: stored in the record. Non-resident: stored in the data runs" > ;
|
|
195: UBYTE name_length ;
|
|
196: UINT16 name_offset ;
|
|
197: UINT16 flags < comment = AttributeFlagsToString > ;
|
|
198: UINT16 attribute_id ;
|
|
199: } fixed_header ;
|
|
200: if ( fixed_header . non_res_flag ) {
|
|
201: struct NonResidentAttributeHeader {
|
|
202: UQUAD start_vcn ;
|
|
203: UQUAD end_vcn ;
|
|
204: UINT16 datarun_offset ;
|
|
205: UINT16 compression_size < comment = "If 0, not compressed" > ;
|
|
206: UINT32 padding ;
|
|
207: UQUAD alloc_size < comment = GetAllocSize > ;
|
|
208: UQUAD real_size < comment = "File size in bytes" > ;
|
|
209: UQUAD stream_size < comment = "Equal to real_size unless resized" > ;
|
|
210: } non_resident_attribute_header ;
|
|
211: if ( fixed_header . type == DATA ) {
|
|
212: struct AttributeContentNonResidentData {
|
|
213: UBYTE unknown ;
|
|
214: UBYTE num_of_clusters ;
|
|
215: UINT16 data_run_start < format = hex , comment = GetDataRunStart > ;
|
|
216: char padding [ 3 ] ;
|
|
217: } attribute_content_non_resident_data ;
|
|
218: } else if ( fixed_header . type == BITMAP ) {
|
|
219: struct AttributeContentNonResidentBitmap {
|
|
220: char bitmap [ fixed_header . attr_length - non_resident_attribute_header . datarun_offset ] ;
|
|
221: } attribute_content_non_resident_bitmap ;
|
|
222: } else {
|
|
223:
|
|
224: }
|
|
225: } else {
|
|
226: struct ResidentAttributeHeader {
|
|
227: UINT32 content_size ;
|
|
228: UINT16 content_offset ;
|
|
229: UINT16 indexed_tag ;
|
|
230: } resident_attribute_header ;
|
|
231: struct ResidentAttributeContent {
|
|
232: if ( fixed_header . type == STANDARD_INFORMATION ) {
|
|
233: struct AttributeContentStandardInformation {
|
|
234: FILETIME creation_time ;
|
|
235: FILETIME alteration_time ;
|
|
236: FILETIME mft_changed_time ;
|
|
237: FILETIME read_time ;
|
|
238: UINT32 dos_permissions ;
|
|
239: UINT32 max_num_of_versions ;
|
|
240: UINT32 version_number ;
|
|
241: UINT32 class_id ;
|
|
242: } attribute_content_standard_information ;
|
|
243: if ( resident_attribute_header . content_size != 48 ) {
|
|
244: UINT32 owner_id ;
|
|
245: UINT32 security_id ;
|
|
246: UQUAD quota_charged ;
|
|
247: UQUAD update_sequence_number ;
|
|
248: }
|
|
249: } else if ( fixed_header . type == FILE_NAME ) {
|
|
250: struct AttributeContentFileName {
|
|
251: UQUAD file_ref_to_parent_dir < format = hex , comment = GetParentDirComment > ;
|
|
252: FILETIME file_creation_time ;
|
|
253: FILETIME file_modification_time ;
|
|
254: FILETIME mft_modification_time ;
|
|
255: FILETIME file_access_time ;
|
|
256: UQUAD allocated_size ;
|
|
257: UQUAD real_size ;
|
|
258: UINT32 flags ;
|
|
259: UINT32 used_by_eas_and_reparse ;
|
|
260: UBYTE filename_length_unicode ;
|
|
261: UBYTE filename_namespace ;
|
|
262: } attribute_content_file_name ;
|
|
263:
|
|
264: wchar_t file_name [ attribute_content_file_name . filename_length_unicode ] ;
|
|
265: } else {
|
|
266: UCHAR data [ resident_attribute_header . content_size ] ;
|
|
267:
|
|
268: }
|
|
269: } resident_attribute_content ;
|
|
270: }
|
|
271: UCHAR padding [ fixed_header . attr_length - ( FTell ( ) - mftattribute_start ) ] ;
|
|
272: } mftattribute ;
|
|
273:
|
|
274: struct MFTRecord {
|
|
275: struct MFTHeader {
|
|
276: char file_signature [ 4 ] < comment = "Must be FILE" > ;
|
|
277: UINT16 fixup_offset ;
|
|
278: UINT16 fixup_size ;
|
|
279: UQUAD log_seq_number ;
|
|
280: UINT16 sequence ;
|
|
281: UINT16 hard_links < comment = "Number of hard links" > ;
|
|
282: UINT16 attrib_offset ;
|
|
283: UINT16 flags < comment = HeaderFlagsToString > ;
|
|
284: UINT32 rec_length < comment = "Length of the bytes used for this record. Must be <= 1024" > ;
|
|
285: UINT32 all_length < comment = "Length of the whole record. Must be 1024" > ;
|
|
286: UQUAD base_mft_rec ;
|
|
287: UINT16 next_attr_id ;
|
|
288: UINT16 fixup_pattern ;
|
|
289: UINT32 mft_rec_number < comment = getMFTNameFromRecurdNumber > ;
|
|
290: } mftheader ;
|
|
291: UQUAD fixup ;
|
|
292: local int i ;
|
|
293: local int64 pos ;
|
|
294: local UINT32 terminator ;
|
|
295: for ( i = 0 ; i == i ; i ++ ) {
|
|
296: pos = FTell ( ) ;
|
|
297: terminator = ReadUInt ( ) ;
|
|
298: if ( terminator == 0xFFFFFFFF ) {
|
|
299: struct Terminator {
|
|
300: UINT32 terminator < format = hex , comment = "Must be FF FF FF FF" > ;
|
|
301: } terminator < comment = "Attribute list terminator" > ;
|
|
302: break ;
|
|
303: } else {
|
|
304: struct Attributes {
|
|
305: mftattribute attribute ;
|
|
306: } attributes < comment = GetMFTNameFromAttribute > ;
|
|
307: }
|
|
308: }
|
|
309: } mftrecord < comment = GetMFTRecordComment > ;
|
|
310: tok_eof |