dissect.target 3.20.dev23__py3-none-any.whl → 3.20.dev26__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|