dissect.target 3.20.dev23__py3-none-any.whl → 3.20.dev26__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- dissect/target/filesystems/btrfs.py +4 -1
- dissect/target/filesystems/ntfs.py +18 -2
- dissect/target/plugins/os/windows/regf/shellbags.py +351 -345
- {dissect.target-3.20.dev23.dist-info → dissect.target-3.20.dev26.dist-info}/METADATA +1 -1
- {dissect.target-3.20.dev23.dist-info → dissect.target-3.20.dev26.dist-info}/RECORD +10 -10
- {dissect.target-3.20.dev23.dist-info → dissect.target-3.20.dev26.dist-info}/COPYRIGHT +0 -0
- {dissect.target-3.20.dev23.dist-info → dissect.target-3.20.dev26.dist-info}/LICENSE +0 -0
- {dissect.target-3.20.dev23.dist-info → dissect.target-3.20.dev26.dist-info}/WHEEL +0 -0
- {dissect.target-3.20.dev23.dist-info → dissect.target-3.20.dev26.dist-info}/entry_points.txt +0 -0
- {dissect.target-3.20.dev23.dist-info → dissect.target-3.20.dev26.dist-info}/top_level.txt +0 -0
@@ -181,6 +181,9 @@ class BtrfsFilesystemEntry(FilesystemEntry):
|
|
181
181
|
|
182
182
|
# Add block information of the filesystem
|
183
183
|
st_info.st_blksize = entry.btrfs.sector_size
|
184
|
-
|
184
|
+
|
185
|
+
st_info.st_blocks = 0
|
186
|
+
if not self.is_dir():
|
187
|
+
st_info.st_blocks = (st_info.st_blksize // 512) * math.ceil(st_info.st_size / st_info.st_blksize)
|
185
188
|
|
186
189
|
return st_info
|
@@ -1,5 +1,6 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
|
+
import math
|
3
4
|
import stat
|
4
5
|
from typing import BinaryIO, Iterator, Optional
|
5
6
|
|
@@ -151,12 +152,14 @@ class NtfsFilesystemEntry(FilesystemEntry):
|
|
151
152
|
record = self.dereference()
|
152
153
|
|
153
154
|
size = 0
|
155
|
+
real_size = 0
|
154
156
|
if self.is_symlink():
|
155
157
|
mode = stat.S_IFLNK
|
156
158
|
elif self.is_file():
|
157
159
|
mode = stat.S_IFREG
|
158
160
|
try:
|
159
161
|
size = record.size(self.ads)
|
162
|
+
real_size = record.size(self.ads, allocated=True)
|
160
163
|
except NtfsFileNotFoundError as e:
|
161
164
|
# Occurs when it cannot find the the specific ads inside its attributes
|
162
165
|
raise FileNotFoundError from e
|
@@ -176,16 +179,29 @@ class NtfsFilesystemEntry(FilesystemEntry):
|
|
176
179
|
0,
|
177
180
|
size,
|
178
181
|
stdinfo.last_access_time.timestamp(),
|
179
|
-
stdinfo.
|
182
|
+
stdinfo.last_modification_time.timestamp(),
|
183
|
+
# ctime gets set to creation time for python <3.12 purposes
|
180
184
|
stdinfo.creation_time.timestamp(),
|
181
185
|
]
|
182
186
|
)
|
183
187
|
|
184
188
|
# Set the nanosecond resolution separately
|
185
189
|
st_info.st_atime_ns = stdinfo.last_access_time_ns
|
186
|
-
st_info.st_mtime_ns = stdinfo.
|
190
|
+
st_info.st_mtime_ns = stdinfo.last_modification_time_ns
|
191
|
+
|
187
192
|
st_info.st_ctime_ns = stdinfo.creation_time_ns
|
188
193
|
|
194
|
+
st_info.st_birthtime = stdinfo.creation_time.timestamp()
|
195
|
+
st_info.st_birthtime_ns = stdinfo.creation_time_ns
|
196
|
+
|
197
|
+
# real_size is none if the size is resident
|
198
|
+
st_info.st_blksize = record.ntfs.cluster_size
|
199
|
+
blocks = 0
|
200
|
+
if not record.resident:
|
201
|
+
blocks = math.ceil(real_size / 512)
|
202
|
+
|
203
|
+
st_info.st_blocks = blocks
|
204
|
+
|
189
205
|
return st_info
|
190
206
|
|
191
207
|
def attr(self) -> AttributeMap:
|
@@ -1,6 +1,10 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
1
3
|
import io
|
2
4
|
import logging
|
3
5
|
import uuid
|
6
|
+
from datetime import datetime
|
7
|
+
from typing import Any, Iterator
|
4
8
|
|
5
9
|
from dissect.cstruct import cstruct
|
6
10
|
from dissect.util.ts import dostimestamp
|
@@ -13,7 +17,9 @@ from dissect.target.helpers.descriptor_extensions import (
|
|
13
17
|
UserRecordDescriptorExtension,
|
14
18
|
)
|
15
19
|
from dissect.target.helpers.record import create_extended_descriptor
|
20
|
+
from dissect.target.helpers.regutil import RegistryKey
|
16
21
|
from dissect.target.plugin import Plugin, export
|
22
|
+
from dissect.target.target import Target
|
17
23
|
|
18
24
|
log = logging.getLogger(__name__)
|
19
25
|
|
@@ -170,30 +176,30 @@ struct SHITEM_MTP_VOLUME_GUID {
|
|
170
176
|
};
|
171
177
|
|
172
178
|
struct SHITEM_MTP_VOLUME {
|
173
|
-
uint16
|
174
|
-
uint8
|
175
|
-
uint8
|
176
|
-
uint16
|
177
|
-
uint32
|
178
|
-
uint32
|
179
|
-
uint16
|
180
|
-
uint16
|
181
|
-
uint16
|
182
|
-
uint16
|
183
|
-
uint32
|
184
|
-
uint64
|
185
|
-
uint32
|
186
|
-
uint32
|
187
|
-
uint32
|
188
|
-
uint32
|
189
|
-
uint32
|
190
|
-
wchar
|
191
|
-
wchar
|
192
|
-
wchar
|
193
|
-
SHITEM_MTP_VOLUME_GUID
|
194
|
-
uint32
|
195
|
-
char
|
196
|
-
uint32
|
179
|
+
uint16 size;
|
180
|
+
uint8 type;
|
181
|
+
uint8 unk0;
|
182
|
+
uint16 data_size;
|
183
|
+
uint32 data_signature;
|
184
|
+
uint32 unk1;
|
185
|
+
uint16 unk2;
|
186
|
+
uint16 unk3;
|
187
|
+
uint16 unk4;
|
188
|
+
uint16 unk5;
|
189
|
+
uint32 unk6;
|
190
|
+
uint64 unk7;
|
191
|
+
uint32 unk8;
|
192
|
+
uint32 name_size;
|
193
|
+
uint32 identifier_size;
|
194
|
+
uint32 filesystem_size;
|
195
|
+
uint32 num_guid;
|
196
|
+
wchar name[name_size];
|
197
|
+
wchar identifier[identifier_size];
|
198
|
+
wchar filesystem[filesystem_size];
|
199
|
+
SHITEM_MTP_VOLUME_GUID guids[num_guid];
|
200
|
+
uint32 unk9;
|
201
|
+
char class_identifier[16];
|
202
|
+
uint32 num_properties;
|
197
203
|
};
|
198
204
|
|
199
205
|
struct SHITEM_USERS_PROPERTY_VIEW {
|
@@ -248,267 +254,6 @@ c_bag = cstruct().load(bag_def)
|
|
248
254
|
DELEGATE_ITEM_IDENTIFIER = b"\x74\x1a\x59\x5e\x96\xdf\xd3\x48\x8d\x67\x17\x33\xbc\xee\x28\xba"
|
249
255
|
|
250
256
|
|
251
|
-
ShellBagRecord = create_extended_descriptor([RegistryRecordDescriptorExtension, UserRecordDescriptorExtension])(
|
252
|
-
"windows/shellbag",
|
253
|
-
[
|
254
|
-
("path", "path"),
|
255
|
-
("datetime", "creation_time"),
|
256
|
-
("datetime", "modification_time"),
|
257
|
-
("datetime", "access_time"),
|
258
|
-
("datetime", "regf_modification_time"),
|
259
|
-
],
|
260
|
-
)
|
261
|
-
|
262
|
-
|
263
|
-
class ShellBagsPlugin(Plugin):
|
264
|
-
"""Windows Shellbags plugin.
|
265
|
-
|
266
|
-
References:
|
267
|
-
- https://github.com/libyal/libfwsi
|
268
|
-
"""
|
269
|
-
|
270
|
-
KEYS = [
|
271
|
-
"HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\Shell",
|
272
|
-
"HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\ShellNoRoam",
|
273
|
-
"HKEY_CURRENT_USER\\Software\\Classes\\Local Settings\\Software\\Microsoft\\Windows\\Shell",
|
274
|
-
"HKEY_CURRENT_USER\\Software\\Classes\\Local Settings\\Software\\Microsoft\\Windows\\ShellNoRoam",
|
275
|
-
"HKEY_CURRENT_USER\\Software\\Classes\\Wow6432Node\\Local Settings\\Software\\Microsoft\\Windows\\Shell",
|
276
|
-
"HKEY_CURRENT_USER\\Software\\Classes\\Wow6432Node\\Local Settings\\Software\\Microsoft\\Windows\\ShellNoRoam",
|
277
|
-
"HKEY_CURRENT_USER\\Local Settings\\Software\\Microsoft\\Windows\\Shell\\BagMRU",
|
278
|
-
]
|
279
|
-
|
280
|
-
def __init__(self, target):
|
281
|
-
super().__init__(target)
|
282
|
-
self.bagkeys = list(self.target.registry.keys(self.KEYS))
|
283
|
-
|
284
|
-
def check_compatible(self) -> None:
|
285
|
-
if not len(self.bagkeys) > 0:
|
286
|
-
raise UnsupportedPluginError("No shellbags found")
|
287
|
-
|
288
|
-
@export(record=ShellBagRecord)
|
289
|
-
def shellbags(self):
|
290
|
-
"""Return Windows Shellbags.
|
291
|
-
|
292
|
-
Shellbags are registry keys to improve user experience when using Windows Explorer. It stores information about
|
293
|
-
for example file/folder creation time and access time.
|
294
|
-
|
295
|
-
References:
|
296
|
-
- https://www.hackingarticles.in/forensic-investigation-shellbags/
|
297
|
-
"""
|
298
|
-
for regkey in self.bagkeys:
|
299
|
-
try:
|
300
|
-
bagsmru = regkey.subkey("BagMRU")
|
301
|
-
|
302
|
-
for r in self._walk_bags(bagsmru, None):
|
303
|
-
yield r
|
304
|
-
except RegistryKeyNotFoundError:
|
305
|
-
continue
|
306
|
-
except Exception: # noqa
|
307
|
-
self.target.log.exception("Exception while parsing shellbags")
|
308
|
-
continue
|
309
|
-
|
310
|
-
def _walk_bags(self, key, path_prefix):
|
311
|
-
path_prefix = [] if path_prefix is None else [path_prefix]
|
312
|
-
|
313
|
-
user = self.target.registry.get_user(key)
|
314
|
-
|
315
|
-
for reg_val in key.values():
|
316
|
-
name, value = reg_val.name, reg_val.value
|
317
|
-
if not name.isdigit():
|
318
|
-
continue
|
319
|
-
path = None
|
320
|
-
|
321
|
-
for item in parse_shell_item_list(value):
|
322
|
-
path = "\\".join(path_prefix + [item.name])
|
323
|
-
yield ShellBagRecord(
|
324
|
-
path=windows_path(path),
|
325
|
-
creation_time=item.creation_time,
|
326
|
-
modification_time=item.modification_time,
|
327
|
-
access_time=item.access_time,
|
328
|
-
regf_modification_time=key.ts,
|
329
|
-
_target=self.target,
|
330
|
-
_user=user,
|
331
|
-
_key=key,
|
332
|
-
)
|
333
|
-
|
334
|
-
for r in self._walk_bags(key.subkey(name), path):
|
335
|
-
yield r
|
336
|
-
|
337
|
-
|
338
|
-
def parse_shell_item_list(buf):
|
339
|
-
offset = 0
|
340
|
-
end = len(buf)
|
341
|
-
list_buf = memoryview(buf)
|
342
|
-
|
343
|
-
parent = None
|
344
|
-
while offset < end:
|
345
|
-
size = c_bag.uint16(list_buf[offset : offset + 2])
|
346
|
-
|
347
|
-
if size == 0:
|
348
|
-
break
|
349
|
-
|
350
|
-
item_buf = list_buf[offset : offset + size]
|
351
|
-
|
352
|
-
entry = None
|
353
|
-
if size >= 8:
|
354
|
-
signature = c_bag.uint32(item_buf[4:8])
|
355
|
-
if signature == 0x39DE2184:
|
356
|
-
entry = CONTROL_PANEL_CATEGORY
|
357
|
-
elif signature == 0x4D677541:
|
358
|
-
entry = CDBURN
|
359
|
-
elif signature == 0x49534647:
|
360
|
-
entry = GAME_FOLDER
|
361
|
-
elif signature == 0xFFFFFF38:
|
362
|
-
entry = CONTROL_PANEL_CPL_FILE
|
363
|
-
|
364
|
-
if size >= 10 and not entry:
|
365
|
-
signature = c_bag.uint32(item_buf[6:10])
|
366
|
-
if signature == 0x07192006:
|
367
|
-
entry = MTP_FILE_ENTRY
|
368
|
-
elif signature == 0x10312005:
|
369
|
-
entry = MTP_VOLUME
|
370
|
-
elif signature in (0x10141981, 0x23A3DFD5, 0x23FEBBEE, 0x3B93AFBB, 0xBEEBEE00):
|
371
|
-
entry = USERS_PROPERTY_VIEW
|
372
|
-
elif signature == 0x46534643:
|
373
|
-
entry = UNKNOWN_0x74
|
374
|
-
|
375
|
-
if size >= 38 and not entry:
|
376
|
-
if item_buf[size - 32 : size] == DELEGATE_ITEM_IDENTIFIER:
|
377
|
-
entry = DELEGATE
|
378
|
-
|
379
|
-
if size >= 3 and not entry:
|
380
|
-
class_type = item_buf[2]
|
381
|
-
mask_type = class_type & 0x70
|
382
|
-
|
383
|
-
if mask_type == 0x00:
|
384
|
-
if class_type == 0x00:
|
385
|
-
entry = UNKNOWN0
|
386
|
-
elif class_type == 0x01:
|
387
|
-
entry = UNKNOWN1
|
388
|
-
|
389
|
-
elif mask_type == 0x10:
|
390
|
-
if class_type == 0x1F:
|
391
|
-
entry = ROOT_FOLDER
|
392
|
-
|
393
|
-
elif mask_type == 0x20:
|
394
|
-
if class_type in (0x23, 0x25, 0x29, 0x2A, 0x2E, 0x2F):
|
395
|
-
entry = VOLUME
|
396
|
-
|
397
|
-
elif mask_type == 0x30:
|
398
|
-
if class_type in (0x30, 0x31, 0x32, 0x35, 0x36, 0xB1):
|
399
|
-
entry = FILE_ENTRY
|
400
|
-
|
401
|
-
elif mask_type == 0x40:
|
402
|
-
if class_type in (0x41, 0x42, 0x46, 0x47, 0x4C, 0xC3):
|
403
|
-
entry = NETWORK
|
404
|
-
|
405
|
-
elif mask_type == 0x50:
|
406
|
-
if class_type == 0x52:
|
407
|
-
entry = COMPRESSED_FOLDER
|
408
|
-
|
409
|
-
elif mask_type == 0x60:
|
410
|
-
if class_type == 0x61:
|
411
|
-
entry = URI
|
412
|
-
|
413
|
-
elif mask_type == 0x70:
|
414
|
-
if class_type == 0x71:
|
415
|
-
entry = CONTROL_PANEL
|
416
|
-
else:
|
417
|
-
if not entry:
|
418
|
-
log.debug("No supported shell item found for size 0x%04x and type 0x%02x", size, class_type)
|
419
|
-
entry = UNKNOWN
|
420
|
-
|
421
|
-
if not entry:
|
422
|
-
log.debug("No supported shell item found for size 0x%04x", size)
|
423
|
-
entry = UNKNOWN
|
424
|
-
|
425
|
-
entry = entry(item_buf)
|
426
|
-
entry.parent = parent
|
427
|
-
|
428
|
-
first_extension_block_offset = c_bag.uint16(item_buf[-2:])
|
429
|
-
if 4 <= first_extension_block_offset < size - 2:
|
430
|
-
extension_offset = first_extension_block_offset
|
431
|
-
while extension_offset < size - 2:
|
432
|
-
extension_size = c_bag.uint16(item_buf[extension_offset : extension_offset + 2])
|
433
|
-
|
434
|
-
if extension_size == 0:
|
435
|
-
break
|
436
|
-
|
437
|
-
if extension_size > size - extension_offset:
|
438
|
-
log.debug(
|
439
|
-
"Extension size exceeds item size: 0x%04x > 0x%04x - 0x%04x",
|
440
|
-
extension_size,
|
441
|
-
size,
|
442
|
-
extension_offset,
|
443
|
-
)
|
444
|
-
break # Extension size too large
|
445
|
-
|
446
|
-
extension_buf = item_buf[extension_offset : extension_offset + extension_size]
|
447
|
-
extension_signature = c_bag.uint32(extension_buf[4:8])
|
448
|
-
|
449
|
-
ext = None
|
450
|
-
|
451
|
-
if extension_signature >> 16 != 0xBEEF:
|
452
|
-
log.debug("Got unsupported extension signature 0x%08x from item %r", extension_signature, entry)
|
453
|
-
pass # Unsupported
|
454
|
-
|
455
|
-
elif extension_signature == 0xBEEF0000:
|
456
|
-
pass
|
457
|
-
|
458
|
-
elif extension_signature == 0xBEEF0001:
|
459
|
-
pass
|
460
|
-
|
461
|
-
elif extension_signature == 0xBEEF0003:
|
462
|
-
ext = EXTENSION_BLOCK_BEEF0004
|
463
|
-
|
464
|
-
elif extension_signature == 0xBEEF0004:
|
465
|
-
ext = EXTENSION_BLOCK_BEEF0004
|
466
|
-
|
467
|
-
elif extension_signature == 0xBEEF0005:
|
468
|
-
ext = EXTENSION_BLOCK_BEEF0005
|
469
|
-
|
470
|
-
elif extension_signature == 0xBEEF0006:
|
471
|
-
pass
|
472
|
-
|
473
|
-
elif extension_signature == 0xBEEF000A:
|
474
|
-
pass
|
475
|
-
|
476
|
-
elif extension_signature == 0xBEEF0013:
|
477
|
-
pass
|
478
|
-
|
479
|
-
elif extension_signature == 0xBEEF0014:
|
480
|
-
pass
|
481
|
-
|
482
|
-
elif extension_signature == 0xBEEF0019:
|
483
|
-
pass
|
484
|
-
|
485
|
-
elif extension_signature == 0xBEEF0025:
|
486
|
-
pass
|
487
|
-
|
488
|
-
elif extension_signature == 0xBEEF0026:
|
489
|
-
pass
|
490
|
-
|
491
|
-
else:
|
492
|
-
log.debug(
|
493
|
-
"Got unsupported beef extension signature 0x%08x from item %r", extension_signature, entry
|
494
|
-
)
|
495
|
-
pass
|
496
|
-
|
497
|
-
if ext is None:
|
498
|
-
ext = EXTENSION_BLOCK
|
499
|
-
log.debug("Unimplemented extension signature 0x%08x from item %r", extension_signature, entry)
|
500
|
-
|
501
|
-
ext = ext(extension_buf)
|
502
|
-
|
503
|
-
entry.extensions.append(ext)
|
504
|
-
extension_offset += extension_size
|
505
|
-
|
506
|
-
parent = entry
|
507
|
-
yield entry
|
508
|
-
|
509
|
-
offset += size
|
510
|
-
|
511
|
-
|
512
257
|
class SHITEM:
|
513
258
|
STRUCT = None
|
514
259
|
|
@@ -521,43 +266,43 @@ class SHITEM:
|
|
521
266
|
self.parent = None
|
522
267
|
self.extensions = []
|
523
268
|
|
269
|
+
def __repr__(self) -> str:
|
270
|
+
return f"<{self.__class__.__name__}>"
|
271
|
+
|
524
272
|
@property
|
525
|
-
def name(self):
|
273
|
+
def name(self) -> str:
|
526
274
|
return f"<SHITEM 0x{self.size:x}>"
|
527
275
|
|
528
276
|
@property
|
529
|
-
def creation_time(self):
|
277
|
+
def creation_time(self) -> None:
|
530
278
|
return None
|
531
279
|
|
532
280
|
@property
|
533
|
-
def modification_time(self):
|
281
|
+
def modification_time(self) -> None:
|
534
282
|
return None
|
535
283
|
|
536
284
|
@property
|
537
|
-
def access_time(self):
|
285
|
+
def access_time(self) -> None:
|
538
286
|
return None
|
539
287
|
|
540
288
|
@property
|
541
|
-
def file_size(self):
|
289
|
+
def file_size(self) -> None:
|
542
290
|
return None
|
543
291
|
|
544
292
|
@property
|
545
|
-
def file_reference(self):
|
293
|
+
def file_reference(self) -> None:
|
546
294
|
return None
|
547
295
|
|
548
|
-
def extension(self, cls):
|
296
|
+
def extension(self, cls: Any) -> Any | None:
|
549
297
|
for ext in self.extensions:
|
550
298
|
if isinstance(ext, cls):
|
551
299
|
return ext
|
552
300
|
return None
|
553
301
|
|
554
|
-
def __repr__(self):
|
555
|
-
return f"<{self.__class__.__name__}>"
|
556
|
-
|
557
302
|
|
558
303
|
class UNKNOWN(SHITEM):
|
559
304
|
@property
|
560
|
-
def name(self):
|
305
|
+
def name(self) -> str:
|
561
306
|
type_number = hex(self.type) if self.type else self.type
|
562
307
|
return f"<UNKNOWN size=0x{self.size:04x} type={type_number}>"
|
563
308
|
|
@@ -573,7 +318,7 @@ class UNKNOWN0(SHITEM):
|
|
573
318
|
self.guid = uuid.UUID(bytes_le=fh.read(16))
|
574
319
|
|
575
320
|
@property
|
576
|
-
def name(self):
|
321
|
+
def name(self) -> str:
|
577
322
|
if self.guid:
|
578
323
|
GUID_name = shell_folder_ids.DESCRIPTIONS.get(str(self.guid))
|
579
324
|
return GUID_name or f"<UNKNOWN0: {{{self.guid}}}>"
|
@@ -585,11 +330,11 @@ class UNKNOWN1(SHITEM):
|
|
585
330
|
STRUCT = c_bag.SHITEM_UNKNOWN1
|
586
331
|
|
587
332
|
@property
|
588
|
-
def name(self):
|
333
|
+
def name(self) -> str:
|
589
334
|
return f"<UNKNOWN1 0x{self.size:x}>"
|
590
335
|
|
591
336
|
|
592
|
-
class ROOT_FOLDER(SHITEM):
|
337
|
+
class ROOT_FOLDER(SHITEM):
|
593
338
|
STRUCT = c_bag.SHITEM_ROOT_FOLDER
|
594
339
|
|
595
340
|
def __init__(self, fh):
|
@@ -601,7 +346,7 @@ class ROOT_FOLDER(SHITEM): # noqa
|
|
601
346
|
self.extension = None
|
602
347
|
|
603
348
|
@property
|
604
|
-
def name(self):
|
349
|
+
def name(self) -> str:
|
605
350
|
GUID_name = shell_folder_ids.DESCRIPTIONS.get(str(self.guid))
|
606
351
|
return GUID_name or f"{{{self.item.folder_id.name}: {self.guid}}}"
|
607
352
|
|
@@ -616,12 +361,12 @@ class VOLUME(SHITEM):
|
|
616
361
|
if self.type == 0x2E:
|
617
362
|
self.identifier = uuid.UUID(bytes_le=buf[4:20].tobytes())
|
618
363
|
else:
|
619
|
-
self.volume_name = self.fh.read(20).rstrip(b"\x00").decode()
|
364
|
+
self.volume_name = self.fh.read(20).rstrip(b"\x00").decode(errors="surrogateescape")
|
620
365
|
if self.size >= 41:
|
621
366
|
self.identifier = uuid.UUID(bytes_le=buf[25:41].tobytes())
|
622
367
|
|
623
368
|
@property
|
624
|
-
def name(self):
|
369
|
+
def name(self) -> str:
|
625
370
|
if self.volume_name:
|
626
371
|
return self.volume_name
|
627
372
|
if self.identifier:
|
@@ -630,7 +375,7 @@ class VOLUME(SHITEM):
|
|
630
375
|
return f"<VOLUME 0x{self.type:02x}>"
|
631
376
|
|
632
377
|
|
633
|
-
class FILE_ENTRY(SHITEM):
|
378
|
+
class FILE_ENTRY(SHITEM):
|
634
379
|
STRUCT = c_bag.SHITEM_FILE_ENTRY
|
635
380
|
|
636
381
|
def __init__(self, buf):
|
@@ -644,7 +389,7 @@ class FILE_ENTRY(SHITEM): # noqa
|
|
644
389
|
self.primary_name = c_bag.wchar[None](self.fh)
|
645
390
|
self.is_unicode = True
|
646
391
|
else:
|
647
|
-
self.primary_name = c_bag.char[None](self.fh).decode()
|
392
|
+
self.primary_name = c_bag.char[None](self.fh).decode(errors="surrogateescape")
|
648
393
|
self.is_unicode = False
|
649
394
|
|
650
395
|
if self.fh.tell() % 2:
|
@@ -659,17 +404,17 @@ class FILE_ENTRY(SHITEM): # noqa
|
|
659
404
|
if self.is_unicode:
|
660
405
|
self.secondary_name = c_bag.wchar[None](self.fh)
|
661
406
|
else:
|
662
|
-
self.secondary_name = c_bag.char[None](self.fh).decode()
|
407
|
+
self.secondary_name = c_bag.char[None](self.fh).decode(errors="surrogateescape")
|
663
408
|
|
664
409
|
@property
|
665
|
-
def name(self):
|
410
|
+
def name(self) -> str:
|
666
411
|
ext = self.extension(EXTENSION_BLOCK_BEEF0004)
|
667
412
|
if ext and ext.long_name:
|
668
413
|
return ext.long_name
|
669
414
|
return self.primary_name
|
670
415
|
|
671
416
|
@property
|
672
|
-
def modification_time(self):
|
417
|
+
def modification_time(self) -> datetime | None:
|
673
418
|
ts = self.item.modification_time
|
674
419
|
if ts > 0:
|
675
420
|
return dostimestamp(ts, swap=True)
|
@@ -691,15 +436,15 @@ class NETWORK(SHITEM):
|
|
691
436
|
self.comments = c_bag.char[None](self.fh)
|
692
437
|
|
693
438
|
@property
|
694
|
-
def name(self):
|
695
|
-
return self.item.location.decode()
|
439
|
+
def name(self) -> str:
|
440
|
+
return self.item.location.decode(errors="surrogateescape")
|
696
441
|
|
697
442
|
|
698
|
-
class COMPRESSED_FOLDER(SHITEM):
|
443
|
+
class COMPRESSED_FOLDER(SHITEM):
|
699
444
|
STRUCT = c_bag.SHITEM_COMPRESSED_FOLDER
|
700
445
|
|
701
446
|
@property
|
702
|
-
def name(self):
|
447
|
+
def name(self) -> str:
|
703
448
|
return "<COMPRESSED_FOLDER>"
|
704
449
|
|
705
450
|
|
@@ -714,14 +459,14 @@ class URI(SHITEM):
|
|
714
459
|
if self.item.flags & 0x80:
|
715
460
|
self.uri = c_bag.wchar[None](self.fh)
|
716
461
|
else:
|
717
|
-
self.uri = c_bag.char[None](self.fh).decode()
|
462
|
+
self.uri = c_bag.char[None](self.fh).decode(errors="surrogateescape")
|
718
463
|
|
719
464
|
@property
|
720
|
-
def name(self):
|
465
|
+
def name(self) -> str:
|
721
466
|
return self.uri or "<URI>"
|
722
467
|
|
723
468
|
|
724
|
-
class CONTROL_PANEL(SHITEM):
|
469
|
+
class CONTROL_PANEL(SHITEM):
|
725
470
|
STRUCT = c_bag.SHITEM_CONTROL_PANEL
|
726
471
|
|
727
472
|
def __init__(self, buf):
|
@@ -729,12 +474,12 @@ class CONTROL_PANEL(SHITEM): # noqa
|
|
729
474
|
self.guid = uuid.UUID(bytes_le=self.item.guid)
|
730
475
|
|
731
476
|
@property
|
732
|
-
def name(self):
|
477
|
+
def name(self) -> str:
|
733
478
|
GUID_name = shell_folder_ids.DESCRIPTIONS.get(str(self.guid))
|
734
479
|
return GUID_name or f"<CONTROL_PANEL {self.guid}>"
|
735
480
|
|
736
481
|
|
737
|
-
class CONTROL_PANEL_CATEGORY(SHITEM):
|
482
|
+
class CONTROL_PANEL_CATEGORY(SHITEM):
|
738
483
|
STRUCT = c_bag.SHITEM_CONTROL_PANEL_CATEGORY
|
739
484
|
CATEGORIES = {
|
740
485
|
0: "All Control Panel Items",
|
@@ -752,7 +497,7 @@ class CONTROL_PANEL_CATEGORY(SHITEM): # noqa
|
|
752
497
|
}
|
753
498
|
|
754
499
|
@property
|
755
|
-
def name(self):
|
500
|
+
def name(self) -> str:
|
756
501
|
categ_str = self.CATEGORIES.get(self.item.category)
|
757
502
|
if categ_str:
|
758
503
|
return categ_str
|
@@ -763,11 +508,11 @@ class CDBURN(SHITEM):
|
|
763
508
|
STRUCT = c_bag.SHITEM_CDBURN
|
764
509
|
|
765
510
|
@property
|
766
|
-
def name(self):
|
511
|
+
def name(self) -> str:
|
767
512
|
return "<CDBURN>"
|
768
513
|
|
769
514
|
|
770
|
-
class GAME_FOLDER(SHITEM):
|
515
|
+
class GAME_FOLDER(SHITEM):
|
771
516
|
STRUCT = c_bag.SHITEM_GAME_FOLDER
|
772
517
|
|
773
518
|
def __init__(self, buf):
|
@@ -775,43 +520,43 @@ class GAME_FOLDER(SHITEM): # noqa
|
|
775
520
|
self.guid = uuid.UUID(bytes_le=self.item.identifier)
|
776
521
|
|
777
522
|
@property
|
778
|
-
def name(self):
|
523
|
+
def name(self) -> str:
|
779
524
|
return f"<GAME_FOLDER {{{self.guid}}}>"
|
780
525
|
|
781
526
|
|
782
|
-
class CONTROL_PANEL_CPL_FILE(SHITEM):
|
527
|
+
class CONTROL_PANEL_CPL_FILE(SHITEM):
|
783
528
|
STRUCT = c_bag.SHITEM_CONTROL_PANEL_CPL_FILE
|
784
529
|
|
785
530
|
@property
|
786
|
-
def name(self):
|
531
|
+
def name(self) -> str:
|
787
532
|
return f"<CONTROL_PANEL_CPL_FILE path={self.item.cpl_path} name={self.item.name} comments={self.item.comments}>"
|
788
533
|
|
789
534
|
|
790
|
-
class MTP_FILE_ENTRY(SHITEM):
|
535
|
+
class MTP_FILE_ENTRY(SHITEM):
|
791
536
|
STRUCT = c_bag.SHITEM_MTP_FILE_ENTRY
|
792
537
|
|
793
538
|
@property
|
794
|
-
def name(self):
|
539
|
+
def name(self) -> str:
|
795
540
|
return "<MTP_FILE_ENTRY>"
|
796
541
|
|
797
542
|
@property
|
798
|
-
def creation_time(self):
|
543
|
+
def creation_time(self) -> datetime:
|
799
544
|
return self.item.creation_time
|
800
545
|
|
801
546
|
@property
|
802
|
-
def modification_time(self):
|
547
|
+
def modification_time(self) -> datetime:
|
803
548
|
return self.item.modification_time
|
804
549
|
|
805
550
|
|
806
|
-
class MTP_VOLUME(SHITEM):
|
551
|
+
class MTP_VOLUME(SHITEM):
|
807
552
|
STRUCT = c_bag.SHITEM_MTP_FILE_ENTRY
|
808
553
|
|
809
554
|
@property
|
810
|
-
def name(self):
|
555
|
+
def name(self) -> str:
|
811
556
|
return "<MTP_VOLUME>"
|
812
557
|
|
813
558
|
|
814
|
-
class USERS_PROPERTY_VIEW(SHITEM):
|
559
|
+
class USERS_PROPERTY_VIEW(SHITEM):
|
815
560
|
STRUCT = c_bag.SHITEM_USERS_PROPERTY_VIEW
|
816
561
|
|
817
562
|
def __init__(self, buf):
|
@@ -823,13 +568,13 @@ class USERS_PROPERTY_VIEW(SHITEM): # noqa
|
|
823
568
|
self.guid = uuid.UUID(bytes_le=self.item.identifier)
|
824
569
|
|
825
570
|
@property
|
826
|
-
def name(self):
|
571
|
+
def name(self) -> str:
|
827
572
|
# As we don't know how to handle identifier_size other than 16 bytes, we fall back to data_signature
|
828
573
|
property_view = self.guid or self.identifier
|
829
574
|
return f"<USERS_PROPERTY_VIEW {{{property_view}}}>"
|
830
575
|
|
831
576
|
|
832
|
-
class UNKNOWN_0x74(SHITEM):
|
577
|
+
class UNKNOWN_0x74(SHITEM):
|
833
578
|
STRUCT = c_bag.SHITEM_UNKNOWN_0x74
|
834
579
|
|
835
580
|
def __init__(self, buf):
|
@@ -839,11 +584,11 @@ class UNKNOWN_0x74(SHITEM): # noqa
|
|
839
584
|
self.subitem = c_bag.SHITEM_UNKNOWN_0x74_SUBITEM(self.fh)
|
840
585
|
|
841
586
|
@property
|
842
|
-
def name(self):
|
843
|
-
return self.subitem.primary_name.decode() if self.subitem else "<UNKNOWN_0x74>"
|
587
|
+
def name(self) -> str:
|
588
|
+
return self.subitem.primary_name.decode(errors="surrogateescape") if self.subitem else "<UNKNOWN_0x74>"
|
844
589
|
|
845
590
|
@property
|
846
|
-
def modification_time(self):
|
591
|
+
def modification_time(self) -> datetime | None:
|
847
592
|
if self.subitem.modification_time > 0:
|
848
593
|
return dostimestamp(self.subitem.modification_time, swap=True) if self.subitem else None
|
849
594
|
return None
|
@@ -858,38 +603,38 @@ class DELEGATE(SHITEM):
|
|
858
603
|
self.shell_identifier = uuid.UUID(bytes_le=self.item.shell_identifier)
|
859
604
|
|
860
605
|
@property
|
861
|
-
def name(self):
|
606
|
+
def name(self) -> str:
|
862
607
|
GUID_name = shell_folder_ids.DESCRIPTIONS.get(str(self.shell_identifier))
|
863
608
|
return GUID_name if GUID_name else f"{{{self.shell_identifier}}}"
|
864
609
|
|
865
610
|
|
866
|
-
class EXTENSION_BLOCK:
|
611
|
+
class EXTENSION_BLOCK:
|
867
612
|
def __init__(self, buf):
|
868
613
|
self.buf = buf
|
869
614
|
self.fh = io.BytesIO(buf)
|
870
615
|
self.header = c_bag.EXTENSION_BLOCK_HEADER(self.fh)
|
871
616
|
|
617
|
+
def __repr__(self) -> str:
|
618
|
+
return f"<EXTENSION_BLOCK size=0x{self.size:04x} version=0x{self.version:04x} signature=0x{self.signature:08x}>"
|
619
|
+
|
872
620
|
@property
|
873
|
-
def size(self):
|
621
|
+
def size(self) -> int:
|
874
622
|
return self.header.size
|
875
623
|
|
876
624
|
@property
|
877
|
-
def data_size(self):
|
625
|
+
def data_size(self) -> int:
|
878
626
|
return self.size - 8 # minus header
|
879
627
|
|
880
628
|
@property
|
881
|
-
def version(self):
|
629
|
+
def version(self) -> int:
|
882
630
|
return self.header.version
|
883
631
|
|
884
632
|
@property
|
885
|
-
def signature(self):
|
633
|
+
def signature(self) -> int:
|
886
634
|
return self.header.signature
|
887
635
|
|
888
|
-
def __repr__(self):
|
889
|
-
return f"<EXTENSION_BLOCK size=0x{self.size:04x} version=0x{self.version:04x} signature=0x{self.signature:08x}>"
|
890
|
-
|
891
636
|
|
892
|
-
class EXTENSION_BLOCK_BEEF0004(EXTENSION_BLOCK):
|
637
|
+
class EXTENSION_BLOCK_BEEF0004(EXTENSION_BLOCK):
|
893
638
|
def __init__(self, buf):
|
894
639
|
super().__init__(buf)
|
895
640
|
fh = self.fh
|
@@ -923,8 +668,269 @@ class EXTENSION_BLOCK_BEEF0004(EXTENSION_BLOCK): # noqa
|
|
923
668
|
self.localized_name = c_bag.wchar[None](fh)
|
924
669
|
|
925
670
|
|
926
|
-
class EXTENSION_BLOCK_BEEF0005(EXTENSION_BLOCK):
|
671
|
+
class EXTENSION_BLOCK_BEEF0005(EXTENSION_BLOCK):
|
927
672
|
def __init__(self, buf):
|
928
673
|
super().__init__(buf)
|
929
674
|
c_bag.char[16](self.fh) # GUID?
|
930
675
|
self.shell_items = self.fh.read(self.data_size - 18)
|
676
|
+
|
677
|
+
|
678
|
+
ShellBagRecord = create_extended_descriptor([RegistryRecordDescriptorExtension, UserRecordDescriptorExtension])(
|
679
|
+
"windows/shellbag",
|
680
|
+
[
|
681
|
+
("datetime", "ts_mtime"),
|
682
|
+
("datetime", "ts_atime"),
|
683
|
+
("datetime", "ts_btime"),
|
684
|
+
("string", "type"),
|
685
|
+
("path", "path"),
|
686
|
+
("datetime", "regf_mtime"),
|
687
|
+
],
|
688
|
+
)
|
689
|
+
|
690
|
+
|
691
|
+
class ShellBagsPlugin(Plugin):
|
692
|
+
"""Windows Shellbags plugin."""
|
693
|
+
|
694
|
+
KEYS = [
|
695
|
+
"HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\Shell",
|
696
|
+
"HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\ShellNoRoam",
|
697
|
+
"HKEY_CURRENT_USER\\Software\\Classes\\Local Settings\\Software\\Microsoft\\Windows\\Shell",
|
698
|
+
"HKEY_CURRENT_USER\\Software\\Classes\\Local Settings\\Software\\Microsoft\\Windows\\ShellNoRoam",
|
699
|
+
"HKEY_CURRENT_USER\\Software\\Classes\\Wow6432Node\\Local Settings\\Software\\Microsoft\\Windows\\Shell",
|
700
|
+
"HKEY_CURRENT_USER\\Software\\Classes\\Wow6432Node\\Local Settings\\Software\\Microsoft\\Windows\\ShellNoRoam",
|
701
|
+
"HKEY_CURRENT_USER\\Local Settings\\Software\\Microsoft\\Windows\\Shell\\BagMRU",
|
702
|
+
]
|
703
|
+
|
704
|
+
def __init__(self, target: Target):
|
705
|
+
super().__init__(target)
|
706
|
+
self.bagkeys: list[RegistryKey] = list(self.target.registry.keys(self.KEYS))
|
707
|
+
|
708
|
+
def check_compatible(self) -> None:
|
709
|
+
if not self.bagkeys:
|
710
|
+
raise UnsupportedPluginError("No shellbags found")
|
711
|
+
|
712
|
+
@export(record=ShellBagRecord)
|
713
|
+
def shellbags(self) -> Iterator[ShellBagRecord]:
|
714
|
+
"""Yields Windows Shellbags.
|
715
|
+
|
716
|
+
Shellbags are registry keys to improve user experience when using Windows Explorer.
|
717
|
+
They contain information such as file and folder creation time and access time.
|
718
|
+
|
719
|
+
References:
|
720
|
+
- https://github.com/libyal/libfwsi
|
721
|
+
- https://www.giac.org/paper/gcfa/9576/windows-shellbag-forensics-in-depth/128522
|
722
|
+
- https://www.hackingarticles.in/forensic-investigation-shellbags/
|
723
|
+
"""
|
724
|
+
for regkey in self.bagkeys:
|
725
|
+
try:
|
726
|
+
yield from self._walk_bags(regkey.subkey("BagMRU"), None)
|
727
|
+
|
728
|
+
except RegistryKeyNotFoundError:
|
729
|
+
continue
|
730
|
+
|
731
|
+
except Exception as e:
|
732
|
+
self.target.log.error("Exception while parsing shellbags")
|
733
|
+
self.target.log.debug("", exc_info=e)
|
734
|
+
continue
|
735
|
+
|
736
|
+
def _walk_bags(self, key: RegistryKey, path_prefix: str | None) -> Iterator[ShellBagRecord]:
|
737
|
+
"""Recursively walk shellbags from the given RegistryKey location."""
|
738
|
+
path_prefix = [] if path_prefix is None else [path_prefix]
|
739
|
+
user = self.target.registry.get_user(key)
|
740
|
+
|
741
|
+
for reg_val in key.values():
|
742
|
+
name, value = reg_val.name, reg_val.value
|
743
|
+
if not name.isdigit():
|
744
|
+
continue
|
745
|
+
path = None
|
746
|
+
|
747
|
+
for item in parse_shell_item_list(value):
|
748
|
+
path = "\\".join(path_prefix + [item.name])
|
749
|
+
yield ShellBagRecord(
|
750
|
+
ts_mtime=item.modification_time,
|
751
|
+
ts_atime=item.access_time,
|
752
|
+
ts_btime=item.creation_time,
|
753
|
+
type=item.__class__.__name__,
|
754
|
+
path=windows_path(path),
|
755
|
+
regf_mtime=key.ts,
|
756
|
+
_key=key,
|
757
|
+
_user=user,
|
758
|
+
_target=self.target,
|
759
|
+
)
|
760
|
+
|
761
|
+
yield from self._walk_bags(key.subkey(name), path)
|
762
|
+
|
763
|
+
|
764
|
+
def parse_shell_item_list(buf: bytes) -> Iterator[SHITEM]:
|
765
|
+
"""Parse a shellbag item from the given bytes."""
|
766
|
+
|
767
|
+
offset = 0
|
768
|
+
end = len(buf)
|
769
|
+
list_buf = memoryview(buf)
|
770
|
+
parent = None
|
771
|
+
|
772
|
+
while offset < end:
|
773
|
+
size = c_bag.uint16(list_buf[offset : offset + 2])
|
774
|
+
|
775
|
+
if size == 0:
|
776
|
+
break
|
777
|
+
|
778
|
+
item_buf = list_buf[offset : offset + size]
|
779
|
+
|
780
|
+
entry = None
|
781
|
+
if size >= 8:
|
782
|
+
signature = c_bag.uint32(item_buf[4:8])
|
783
|
+
if signature == 0x39DE2184:
|
784
|
+
entry = CONTROL_PANEL_CATEGORY
|
785
|
+
elif signature == 0x4D677541:
|
786
|
+
entry = CDBURN
|
787
|
+
elif signature == 0x49534647:
|
788
|
+
entry = GAME_FOLDER
|
789
|
+
elif signature == 0xFFFFFF38:
|
790
|
+
entry = CONTROL_PANEL_CPL_FILE
|
791
|
+
|
792
|
+
if size >= 10 and not entry:
|
793
|
+
signature = c_bag.uint32(item_buf[6:10])
|
794
|
+
if signature == 0x07192006:
|
795
|
+
entry = MTP_FILE_ENTRY
|
796
|
+
elif signature == 0x10312005:
|
797
|
+
entry = MTP_VOLUME
|
798
|
+
elif signature in (0x10141981, 0x23A3DFD5, 0x23FEBBEE, 0x3B93AFBB, 0xBEEBEE00):
|
799
|
+
entry = USERS_PROPERTY_VIEW
|
800
|
+
elif signature == 0x46534643:
|
801
|
+
entry = UNKNOWN_0x74
|
802
|
+
|
803
|
+
if size >= 38 and not entry:
|
804
|
+
if item_buf[size - 32 : size] == DELEGATE_ITEM_IDENTIFIER:
|
805
|
+
entry = DELEGATE
|
806
|
+
|
807
|
+
if size >= 3 and not entry:
|
808
|
+
class_type = item_buf[2]
|
809
|
+
mask_type = class_type & 0x70
|
810
|
+
|
811
|
+
if mask_type == 0x00:
|
812
|
+
if class_type == 0x00:
|
813
|
+
entry = UNKNOWN0
|
814
|
+
elif class_type == 0x01:
|
815
|
+
entry = UNKNOWN1
|
816
|
+
|
817
|
+
elif mask_type == 0x10:
|
818
|
+
if class_type == 0x1F:
|
819
|
+
entry = ROOT_FOLDER
|
820
|
+
|
821
|
+
elif mask_type == 0x20:
|
822
|
+
if class_type in (0x23, 0x25, 0x29, 0x2A, 0x2E, 0x2F):
|
823
|
+
entry = VOLUME
|
824
|
+
|
825
|
+
elif mask_type == 0x30:
|
826
|
+
if class_type in (0x30, 0x31, 0x32, 0x35, 0x36, 0xB1):
|
827
|
+
entry = FILE_ENTRY
|
828
|
+
|
829
|
+
elif mask_type == 0x40:
|
830
|
+
if class_type in (0x41, 0x42, 0x46, 0x47, 0x4C, 0xC3):
|
831
|
+
entry = NETWORK
|
832
|
+
|
833
|
+
elif mask_type == 0x50:
|
834
|
+
if class_type == 0x52:
|
835
|
+
entry = COMPRESSED_FOLDER
|
836
|
+
|
837
|
+
elif mask_type == 0x60:
|
838
|
+
if class_type == 0x61:
|
839
|
+
entry = URI
|
840
|
+
|
841
|
+
elif mask_type == 0x70:
|
842
|
+
if class_type == 0x71:
|
843
|
+
entry = CONTROL_PANEL
|
844
|
+
else:
|
845
|
+
if not entry:
|
846
|
+
log.debug("No supported shell item found for size 0x%04x and type 0x%02x", size, class_type)
|
847
|
+
entry = UNKNOWN
|
848
|
+
|
849
|
+
if not entry:
|
850
|
+
log.debug("No supported shell item found for size 0x%04x", size)
|
851
|
+
entry = UNKNOWN
|
852
|
+
|
853
|
+
entry = entry(item_buf)
|
854
|
+
entry.parent = parent
|
855
|
+
|
856
|
+
first_extension_block_offset = c_bag.uint16(item_buf[-2:])
|
857
|
+
if 4 <= first_extension_block_offset < size - 2:
|
858
|
+
extension_offset = first_extension_block_offset
|
859
|
+
while extension_offset < size - 2:
|
860
|
+
extension_size = c_bag.uint16(item_buf[extension_offset : extension_offset + 2])
|
861
|
+
|
862
|
+
if extension_size == 0:
|
863
|
+
break
|
864
|
+
|
865
|
+
if extension_size > size - extension_offset:
|
866
|
+
log.debug(
|
867
|
+
"Extension size exceeds item size: 0x%04x > 0x%04x - 0x%04x",
|
868
|
+
extension_size,
|
869
|
+
size,
|
870
|
+
extension_offset,
|
871
|
+
)
|
872
|
+
break # Extension size too large
|
873
|
+
|
874
|
+
extension_buf = item_buf[extension_offset : extension_offset + extension_size]
|
875
|
+
extension_signature = c_bag.uint32(extension_buf[4:8])
|
876
|
+
|
877
|
+
ext = None
|
878
|
+
|
879
|
+
if extension_signature >> 16 != 0xBEEF:
|
880
|
+
log.debug("Got unsupported extension signature 0x%08x from item %r", extension_signature, entry)
|
881
|
+
pass # Unsupported
|
882
|
+
|
883
|
+
elif extension_signature == 0xBEEF0000:
|
884
|
+
pass
|
885
|
+
|
886
|
+
elif extension_signature == 0xBEEF0001:
|
887
|
+
pass
|
888
|
+
|
889
|
+
elif extension_signature == 0xBEEF0003:
|
890
|
+
ext = EXTENSION_BLOCK_BEEF0004
|
891
|
+
|
892
|
+
elif extension_signature == 0xBEEF0004:
|
893
|
+
ext = EXTENSION_BLOCK_BEEF0004
|
894
|
+
|
895
|
+
elif extension_signature == 0xBEEF0005:
|
896
|
+
ext = EXTENSION_BLOCK_BEEF0005
|
897
|
+
|
898
|
+
elif extension_signature == 0xBEEF0006:
|
899
|
+
pass
|
900
|
+
|
901
|
+
elif extension_signature == 0xBEEF000A:
|
902
|
+
pass
|
903
|
+
|
904
|
+
elif extension_signature == 0xBEEF0013:
|
905
|
+
pass
|
906
|
+
|
907
|
+
elif extension_signature == 0xBEEF0014:
|
908
|
+
pass
|
909
|
+
|
910
|
+
elif extension_signature == 0xBEEF0019:
|
911
|
+
pass
|
912
|
+
|
913
|
+
elif extension_signature == 0xBEEF0025:
|
914
|
+
pass
|
915
|
+
|
916
|
+
elif extension_signature == 0xBEEF0026:
|
917
|
+
pass
|
918
|
+
|
919
|
+
else:
|
920
|
+
log.debug(
|
921
|
+
"Got unsupported beef extension signature 0x%08x from item %r", extension_signature, entry
|
922
|
+
)
|
923
|
+
pass
|
924
|
+
|
925
|
+
if ext is None:
|
926
|
+
ext = EXTENSION_BLOCK
|
927
|
+
log.debug("Unimplemented extension signature 0x%08x from item %r", extension_signature, entry)
|
928
|
+
|
929
|
+
ext = ext(extension_buf)
|
930
|
+
|
931
|
+
entry.extensions.append(ext)
|
932
|
+
extension_offset += extension_size
|
933
|
+
|
934
|
+
parent = entry
|
935
|
+
offset += size
|
936
|
+
yield entry
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: dissect.target
|
3
|
-
Version: 3.20.
|
3
|
+
Version: 3.20.dev26
|
4
4
|
Summary: This module ties all other Dissect modules together, it provides a programming API and command line tools which allow easy access to various data sources inside disk images or file collections (a.k.a. targets)
|
5
5
|
Author-email: Dissect Team <dissect@fox-it.com>
|
6
6
|
License: Affero General Public License v3
|
@@ -23,7 +23,7 @@ dissect/target/containers/vmdk.py,sha256=5fQGkJy4esXONXrKLbhpkQDt8Fwx19YENK2mOm7
|
|
23
23
|
dissect/target/data/autocompletion/target_bash_completion.sh,sha256=wrOQ_ED-h8WFcjCmY6n4qKl84tWJv9l8ShFHDfJqJyA,3592
|
24
24
|
dissect/target/filesystems/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
25
25
|
dissect/target/filesystems/ad1.py,sha256=nEPzaaRsb6bL4ItFo0uLdmdLvrmK9BjqHeD3FOp3WQI,2413
|
26
|
-
dissect/target/filesystems/btrfs.py,sha256=
|
26
|
+
dissect/target/filesystems/btrfs.py,sha256=TotOs0-VOmgSijERZb1pOrIH_E7B1J_DRKqws8ttQTk,6569
|
27
27
|
dissect/target/filesystems/cb.py,sha256=6LcoJiwsYu1Han31IUzVpZVDTifhTLTx_gLfNpB_p6k,5329
|
28
28
|
dissect/target/filesystems/config.py,sha256=GQOtixIIt-Jjtpl3IVqUTujcBFfWaAZeAtvxgNUNetU,11963
|
29
29
|
dissect/target/filesystems/cpio.py,sha256=ssVCjkAtLn2FqmNxeo6U5boyUdSYFxLWfXpytHYGPqs,641
|
@@ -34,7 +34,7 @@ dissect/target/filesystems/fat.py,sha256=ZSw-wS57vo5eIXJndfI1rZkGu_qh-vyioMzCZFZ
|
|
34
34
|
dissect/target/filesystems/ffs.py,sha256=Wu8sS1jjmD0QXXcAaD2h_zzfvinjco8qvj0hErufZ-4,4555
|
35
35
|
dissect/target/filesystems/itunes.py,sha256=w2lcWv6jlBPm84tsGZehxKBMXXyuW3KlmwVTF4ssQec,6395
|
36
36
|
dissect/target/filesystems/jffs.py,sha256=Ceqa5Em2pepnXMH_XZFmSNjQyWPo1uWTthBFSMWfKRo,3926
|
37
|
-
dissect/target/filesystems/ntfs.py,sha256=
|
37
|
+
dissect/target/filesystems/ntfs.py,sha256=Losf35q9aLm-YdwVllT5so99s-GqTF1ZXMbLX0PUNC0,7624
|
38
38
|
dissect/target/filesystems/overlay.py,sha256=d0BNZcVd3SzBcM1SZO5nX2FrEYcdtVH34BPJQ6Oh4x8,4753
|
39
39
|
dissect/target/filesystems/smb.py,sha256=uxfcOWwEoDCw8Qpsa94T5Pn-SKd4WXs4OOrzVVI55d8,6406
|
40
40
|
dissect/target/filesystems/squashfs.py,sha256=ehzlThXB7n96XUvQnsK5tWLsA9HIxYN-Zxl7aO9D7ts,3921
|
@@ -334,7 +334,7 @@ dissect/target/plugins/os/windows/regf/nethist.py,sha256=QHbG9fmZNmjSVhrgqMvMo12
|
|
334
334
|
dissect/target/plugins/os/windows/regf/recentfilecache.py,sha256=goS6ajLIh6ZU-Gq4tupoxBoQCfMDp2qJgg-Nn5qFIsY,1850
|
335
335
|
dissect/target/plugins/os/windows/regf/regf.py,sha256=D1GrljF-sV8cWIjWJ3zH7k52i1OWD8poEC_PIeZMEis,3419
|
336
336
|
dissect/target/plugins/os/windows/regf/runkeys.py,sha256=-2HcdnVytzCt1xwgAI8rHDnwk8kwLPWURumvhrGnIHU,4278
|
337
|
-
dissect/target/plugins/os/windows/regf/shellbags.py,sha256
|
337
|
+
dissect/target/plugins/os/windows/regf/shellbags.py,sha256=-8WkdplG0FR37XgpCTd4iDdQvvrgtOk9kZY5qLsW5J8,26984
|
338
338
|
dissect/target/plugins/os/windows/regf/shimcache.py,sha256=TY7GEFnxb8h99q12CzM0SwVlUymi4hFPae3uuM0M6kY,9998
|
339
339
|
dissect/target/plugins/os/windows/regf/trusteddocs.py,sha256=3yvpBDM-Asg0rvGN2TwALGRm9DYogG6TxRau9D6FBbw,3700
|
340
340
|
dissect/target/plugins/os/windows/regf/usb.py,sha256=nSAHB4Cdd0wF2C1EK_XYOfWCyqOgTZCLfDhuSmr7rdM,9709
|
@@ -368,10 +368,10 @@ dissect/target/volumes/luks.py,sha256=OmCMsw6rCUXG1_plnLVLTpsvE1n_6WtoRUGQbpmu1z
|
|
368
368
|
dissect/target/volumes/lvm.py,sha256=wwQVR9I3G9YzmY6UxFsH2Y4MXGBcKL9aayWGCDTiWMU,2269
|
369
369
|
dissect/target/volumes/md.py,sha256=7ShPtusuLGaIv27SvEETtgsuoQyAa4iAAeOR1NEaajI,1689
|
370
370
|
dissect/target/volumes/vmfs.py,sha256=-LoUbn9WNwTtLi_4K34uV_-wDw2W5hgaqxZNj4UmqAQ,1730
|
371
|
-
dissect.target-3.20.
|
372
|
-
dissect.target-3.20.
|
373
|
-
dissect.target-3.20.
|
374
|
-
dissect.target-3.20.
|
375
|
-
dissect.target-3.20.
|
376
|
-
dissect.target-3.20.
|
377
|
-
dissect.target-3.20.
|
371
|
+
dissect.target-3.20.dev26.dist-info/COPYRIGHT,sha256=m-9ih2RVhMiXHI2bf_oNSSgHgkeIvaYRVfKTwFbnJPA,301
|
372
|
+
dissect.target-3.20.dev26.dist-info/LICENSE,sha256=DZak_2itbUtvHzD3E7GNUYSRK6jdOJ-GqncQ2weavLA,34523
|
373
|
+
dissect.target-3.20.dev26.dist-info/METADATA,sha256=9ta9bS0OqAMhf6vc8zDqGxpXSkY-YRJMAHWRucXo3f8,12897
|
374
|
+
dissect.target-3.20.dev26.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
|
375
|
+
dissect.target-3.20.dev26.dist-info/entry_points.txt,sha256=BWuxAb_6AvUAQpIQOQU0IMTlaF6TDht2AIZK8bHd-zE,492
|
376
|
+
dissect.target-3.20.dev26.dist-info/top_level.txt,sha256=Mn-CQzEYsAbkxrUI0TnplHuXnGVKzxpDw_po_sXpvv4,8
|
377
|
+
dissect.target-3.20.dev26.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
{dissect.target-3.20.dev23.dist-info → dissect.target-3.20.dev26.dist-info}/entry_points.txt
RENAMED
File without changes
|
File without changes
|