Skip to content

Commit

Permalink
Added new features
Browse files Browse the repository at this point in the history
- Added support for Windows XP artifacts
- Added support for selection of specific artifacts
  • Loading branch information
Silv3rHorn committed Dec 22, 2017
1 parent 245bbb1 commit 460cb63
Show file tree
Hide file tree
Showing 3 changed files with 197 additions and 70 deletions.
67 changes: 34 additions & 33 deletions artifact_extractor.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@
"""Script to extract common Windows artifacts from source image and its shadow copies."""

from __future__ import print_function
import argparse
import hashlib
import itertools
import logging
import os
import sys

import artifacts
import artifact_selector
import vsm

from datetime import datetime as dt
Expand Down Expand Up @@ -135,7 +136,7 @@ def _get_output_path(output_part_dir, artifact_output_path):
def _get_vsc_ctime(base_path_spec):
return vsm.VSS_CREATION_TIMESTAMPS[base_path_spec.parent.store_index + 1]

def extract_artifacts(self, base_path_specs, output_base_dir):
def extract_artifacts(self, base_path_specs, output_base_dir, selection):
# Move non-vsc to front of list to be processed first
for base_path_spec in base_path_specs:
if base_path_spec.parent.type_indicator != 'VSHADOW':
Expand Down Expand Up @@ -174,80 +175,80 @@ def extract_artifacts(self, base_path_specs, output_base_dir):

output_part_dir = os.path.join(output_base_dir, partition)
for artifact in itertools.chain(artifacts.SYSTEM_FILE, artifacts.SYSTEM_DIR, artifacts.FILE_ADS):
if len(artifact) == 3 and artifact in artifacts.FILE_ADS: # extract ADS (artifacts.FILE_ADS)
file_entry = self._get_file_entry(base_path_spec, artifact[0], artifact[2])
if artifact[0] not in selection:
continue
if len(artifact) == 4 and artifact in artifacts.FILE_ADS: # extract ADS (artifacts.FILE_ADS)
file_entry = self._get_file_entry(base_path_spec, artifact[1], artifact[3])
else:
file_entry = self._get_file_entry(base_path_spec, artifact[0], None)
file_entry = self._get_file_entry(base_path_spec, artifact[1], None)
if file_entry is None:
continue

output_path = self._get_output_path(output_part_dir, artifact[1])
output_path = self._get_output_path(output_part_dir, artifact[2])
if base_path_spec.parent.type_indicator == 'VSHADOW':
if file_entry.IsFile(): # artifacts.SYSTEM_FILE
self.export_file(file_entry, os.path.join(output_path, vsc_dir, artifact[0].split('/')[-1]))
self.export_file(file_entry, os.path.join(output_path, vsc_dir, artifact[1].split('/')[-1]))
elif file_entry.IsDirectory(): # artifacts.SYSTEM_DIR
self.export_file(file_entry, os.path.join(output_path, vsc_dir), artifact[2])
self.export_file(file_entry, os.path.join(output_path, vsc_dir), artifact[3])
else:
if file_entry.IsFile(): # artifacts.SYSTEM_FILE
output_path = os.path.join(output_path, artifact[0].split('/')[-1])
output_path = os.path.join(output_path, artifact[1].split('/')[-1])
self.export_file(file_entry, output_path)
elif file_entry.IsDirectory(): # artifacts.SYSTEM_DIR
self.export_file(file_entry, output_path, artifact[2])
self.export_file(file_entry, output_path, artifact[3])

users_file_entry = self._get_file_entry(base_path_spec, '/Users', None)
if any(x in ['lnk_xp', 'iehist_xp', 'usrclass_xp'] for x in selection):
users_file_entry = self._get_file_entry(base_path_spec, '/Documents and Settings', None)
else:
users_file_entry = self._get_file_entry(base_path_spec, '/Users', None)
if users_file_entry is None:
continue
for user_file_entry in users_file_entry.sub_file_entries:
stat_object = user_file_entry.GetStat()
if stat_object.type == definitions.FILE_ENTRY_TYPE_DIRECTORY:
dir_name = user_file_entry.path_spec.location.split('/')[-1]
if dir_name not in ['All Users', 'Default', 'Default User', 'Default.migrated', 'Public']:
if dir_name not in ['All Users', 'Default', 'Default User', 'Default.migrated', 'Public',
'LocalService', 'NetworkService']:

for artifact in artifacts.USER_FILE:
artifact_location = user_file_entry.path_spec.location + artifact[0]
if artifact[0] not in selection:
continue
artifact_location = user_file_entry.path_spec.location + artifact[1]
file_entry = self._get_file_entry(base_path_spec, artifact_location, None)
if file_entry is None:
continue
output_path = self._get_output_path(output_part_dir, artifact[1])
output_path = self._get_output_path(output_part_dir, artifact[2])

if base_path_spec.parent.type_indicator == 'VSHADOW':
output_path = os.path.join(output_path, vsc_dir, 'Users', dir_name,
artifact[0].split('/')[-1])
artifact[1].split('/')[-1])
self.export_file(file_entry, output_path)
else:
output_path = os.path.join(output_path, 'Users', dir_name, artifact[0].split('/')[-1])
output_path = os.path.join(output_path, 'Users', dir_name, artifact[1].split('/')[-1])
self.export_file(file_entry, output_path)

for artifact in artifacts.USER_DIR:
artifact_location = user_file_entry.path_spec.location + artifact[0]
if artifact[0] not in selection:
continue
artifact_location = user_file_entry.path_spec.location + artifact[1]
file_entry = self._get_file_entry(base_path_spec, artifact_location, None)
if file_entry is None:
continue
output_path = os.path.join(self._get_output_path(output_part_dir, artifact[1]), dir_name)
output_path = os.path.join(self._get_output_path(output_part_dir, artifact[2]), dir_name)

if base_path_spec.parent.type_indicator == 'VSHADOW':
self.export_file(file_entry, os.path.join(output_path, vsc_dir), artifact[2])
self.export_file(file_entry, os.path.join(output_path, vsc_dir), artifact[3])
else:
self.export_file(file_entry, output_path, artifact[2])
self.export_file(file_entry, output_path, artifact[3])


def main():
""" The main program function.
Returns:
A boolean containing True if successful or False if not.
"""
argument_parser = argparse.ArgumentParser(description=(
'Extracts common Windows artifacts from source (including Volume Shadow Copies).'))
argument_parser.add_argument('source', nargs='?', metavar='image.raw', default=None, help=(
'path of the directory or filename of a storage media image containing the file.'))
argument_parser.add_argument('dest', nargs='?', metavar='destination', default=None, help=(
'destination directory where the output will be stored.'))
options = argument_parser.parse_args()

if not options.source or not options.dest:
print('One or more arguments is missing.\n')
argument_parser.print_help()
print('')
options = artifact_selector.get_selection()
if not options:
return False

date_timestamp = dt.now()
Expand All @@ -266,7 +267,7 @@ def main():

if os.path.exists(options.dest):
print('')
artifact_extractor.extract_artifacts(base_path_specs, options.dest)
artifact_extractor.extract_artifacts(base_path_specs, options.dest, options.artifact)
else:
print('Cannot find destination directory.\n')
return False
Expand Down
108 changes: 108 additions & 0 deletions artifact_selector.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import argparse

from argparse import RawTextHelpFormatter


def parse_selection(artifacts):
std_7 = {'reg', 'regb', 'ntuser', 'usrclass', 'evtl', 'setupapi', 'prefetch', 'amcache', 'srum', 'sccm', 'lnk',
'jmp', 'iehist'}
std_xp = {'reg', 'regb_xp', 'ntuser', 'usrclass_xp', 'evtl_xp', 'setupapi_xp', 'prefetch', 'lnk_xp', 'iehist_xp'}
all_7 = std_7 | {'recycle', 'mft', 'usnjrnl', 'logfile'}
all_xp = std_xp | {'recycle_xp', 'mft', 'usnjrnl', 'logfile'}
supported = all_7 | all_xp | {'std', 'std_xp', 'all', 'all_xp'}

selection = artifacts.split(',')
selection = set(selection)

# remove unsupported artifacts
unsupported = set()
for selected in selection:
if selected not in supported:
print("{} artifact is not supported.\n".format(selected))
unsupported.add(selected)
selection = selection - unsupported

# expand std, std_xp, all, all_xp
if 'std' in selection:
selection = selection | std_7
if 'std_xp' in selection:
selection = selection | std_xp
if 'all' in selection:
selection = selection | all_7
if 'all_xp' in selection:
selection = selection | all_xp

# remove std, std_xp, all_7, all_xp
selection = selection - {'std', 'std_xp', 'all', 'all_xp'}

if len(selection) == 0:
selection = None

return selection


def get_selection():
argument_parser = argparse.ArgumentParser(description=(
'ArtifactExtractor extracts selected Windows artifacts from forensic images and VSCs.\n'
'It utilises various libraries and example code written by Joachim Metz.\n\n'

'Supported Artifacts: \n'
'\t std \t\t reg, regb, ntuser, usrclass, evtl, setupapi, prefetch, amcache, srum, sccm, lnk, jmp, iehist\n'
'\t std_xp \t reg, regb_xp, ntuser, usrclass_xp, evtl_xp, setupapi_xp, prefetch, lnk_xp, iehist_xp\n'
'\t all \t\t all (Windows 7+) - WARNING: SLOW!\n'
'\t all_xp \t all (Windows XP) - WARNING: SLOW!\n'
'\t reg \t\t registry hives\n'
'\t regb \t\t backup registry hives (Windows 7+)\n'
'\t regb_xp \t backup registry hives (Windows XP)\n'
'\t ntuser \t users\' ntuser hive\n'
'\t usrclass \t users\' usrclass hive (Windows 7+)\n'
'\t usrclass_xp \t users\' usrclass (Windows XP)\n'
'\t evtl \t\t event logs (Windows 7+)\n'
'\t evtl_xp \t event logs (Windows XP)\n'
'\t setupapi \t setupapi log (Windows 7+)\n'
'\t setupapi_xp \t setupapi log (Windows XP)\n'
'\t prefetch \t prefetch\n'
'\t amcache \t amcache and/or recentfilecache.bcf\n'
'\t srum \t\t system resource usage monitor\n'
'\t sccm \t\t system center configuration manager software metering\n'
'\t lnk \t\t users\' lnk files (Windows 7+)\n'
'\t lnk_xp \t users\' lnk files (Windows XP)\n'
'\t jmp \t\t users\' jmp lists\n'
'\t iehist \t users\' ie history (Windows 7+)\n'
'\t iehist_xp \t users\' ie history (Windows XP)\n'
'\t recycle \t users\' recycle bin files (Windows 7+) - does not provide owner or original file name\n'
'\t recycle_xp \t users\' recycle bin files (Windows XP) - does not provide owner\n'
'\t mft \t\t ntfs mft\n'
'\t usnjrnl \t ntfs usnjurnl - WARNING: SLOW!\n'
'\t logfile \t ntfs logfile\n\n'

'Usage: \n'
'\t Extract essential (in developer\'s opinion) artifacts\n'
'\t artifact_extractor <forensic image> <dest> -a std\n\n'

'\t Extract essential (in developer\'s opinion) artifacts + $MFT\n'
'\t artifact_extractor <forensic image> <dest> -a std,mft\n\n'), formatter_class=RawTextHelpFormatter)

argument_parser.add_argument('source', nargs='?', metavar='image.raw', default=None, help=(
'path of the directory or filename of a storage media image containing the file.'))
argument_parser.add_argument('dest', nargs='?', metavar='destination', default=None, help=(
'destination directory where the output will be stored.'))
argument_parser.add_argument('-a', '--artifact', default=None, help=(
'artifacts to extract. See above for list of supported artifacts.'))

options = argument_parser.parse_args()

if not options.source or not options.dest:
print('One or more arguments is missing.\n')
argument_parser.print_help()
print('')
return False

if not options.artifact:
options.artifact = 'std'
options.artifact = parse_selection(options.artifact)

if options.artifact is None:
return False
else:
return options
92 changes: 55 additions & 37 deletions artifacts.py
Original file line number Diff line number Diff line change
@@ -1,55 +1,73 @@
LOC_REG = u'/Windows/System32/config/'
LOC_WINEVT = u'/Windows/System32/winevt/logs/'
LOC_WINEVT = LOC_REG
LOC_WINEVTX = u'/Windows/System32/winevt/logs/'
LOC_AMCACHE = u'/Windows/AppCompat/Programs/'
LOC_RECENT = u'/AppData/Roaming/Microsoft/Windows/Recent/'

SYSTEM_FILE = [ # [artifact_path, destination_directory]
[LOC_REG + u'SAM', u'/Registry/'],
[LOC_REG + u'SECURITY', u'/Registry/'],
[LOC_REG + u'SOFTWARE', u'/Registry/'],
[LOC_REG + u'SYSTEM', u'/Registry/'],

[LOC_REG + u'RegBack/SAM', u'/Registry/RegBack/'],
[LOC_REG + u'RegBack/SECURITY', u'/Registry/RegBack/'],
[LOC_REG + u'RegBack/SOFTWARE', u'/Registry/RegBack/'],
[LOC_REG + u'RegBack/SYSTEM', u'/Registry/RegBack/'],

[LOC_WINEVT + u'Application.evtx', u'/OSLogs/'],
[LOC_WINEVT + u'Security.evtx', u'/OSLogs/'],
[LOC_WINEVT + u'Setup.evtx', u'/OSLogs/'],
[LOC_WINEVT + u'System.evtx', u'/OSLogs/'],
[LOC_WINEVT + u'Microsoft-Windows-DriverFrameworks-UserMode%4Operational.evtx', u'/OSLogs/'],
[LOC_WINEVT + u'Microsoft-Windows-PowerShell%4Operational.evtx', u'/OSLogs/'],
[LOC_WINEVT + u'Microsoft-Windows-TaskScheduler%4Operational.evtx', u'/OSLogs/'],
[LOC_WINEVT + u'Microsoft-Windows-TerminalServices-RemoteConnectionManager%4Operational.evtx', u'/OSLogs/'],
[LOC_WINEVT + u'Microsoft-Windows-TerminalServices-LocalSessionManager%4Operational.evtx', u'/OSLogs/'],
[LOC_WINEVT + u'Microsoft-Windows-Windows Firewall With Advanced Security%4Firewall.evtx', u'/OSLogs/'],

[LOC_AMCACHE + u'Amcache.hve', u'/MRU/Prog/'],
[LOC_AMCACHE + u'RecentFileCache.bcf', u'/MRU/Prog/'],

[u'/Windows/Inf/setupapi.dev.log', u'/Registry/']
['reg', LOC_REG + u'SAM', u'/Registry/'],
['reg', LOC_REG + u'SECURITY', u'/Registry/'],
['reg', LOC_REG + u'SOFTWARE', u'/Registry/'],
['reg', LOC_REG + u'SYSTEM', u'/Registry/'],

['regb', LOC_REG + u'RegBack/SAM', u'/Registry/RegBack/'],
['regb', LOC_REG + u'RegBack/SECURITY', u'/Registry/RegBack/'],
['regb', LOC_REG + u'RegBack/SOFTWARE', u'/Registry/RegBack/'],
['regb', LOC_REG + u'RegBack/SYSTEM', u'/Registry/RegBack/'],

['regb_xp', LOC_REG + u'Repair/SAM', u'/Registry/Repair/'],
['regb_xp', LOC_REG + u'Repair/SECURITY', u'/Registry/Repair/'],
['regb_xp', LOC_REG + u'Repair/SOFTWARE', u'/Registry/Repair/'],
['regb_xp', LOC_REG + u'Repair/SYSTEM', u'/Registry/Repair/'],

['evtl', LOC_WINEVTX + u'Application.evtx', u'/OSLogs/'],
['evtl', LOC_WINEVTX + u'Security.evtx', u'/OSLogs/'],
['evtl', LOC_WINEVTX + u'Setup.evtx', u'/OSLogs/'],
['evtl', LOC_WINEVTX + u'System.evtx', u'/OSLogs/'],
['evtl', LOC_WINEVTX + u'Microsoft-Windows-DriverFrameworks-UserMode%4Operational.evtx', u'/OSLogs/'],
['evtl', LOC_WINEVTX + u'Microsoft-Windows-PowerShell%4Operational.evtx', u'/OSLogs/'],
['evtl', LOC_WINEVTX + u'Microsoft-Windows-TaskScheduler%4Operational.evtx', u'/OSLogs/'],
['evtl', LOC_WINEVTX + u'Microsoft-Windows-TerminalServices-RemoteConnectionManager%4Operational.evtx', u'/OSLogs/'],
['evtl', LOC_WINEVTX + u'Microsoft-Windows-TerminalServices-LocalSessionManager%4Operational.evtx', u'/OSLogs/'],
['evtl', LOC_WINEVTX + u'Microsoft-Windows-Windows Firewall With Advanced Security%4Firewall.evtx', u'/OSLogs/'],

['evtl_xp', LOC_WINEVT + u'AppEvent.evt', u'/OSLogs/'],
['evtl_xp', LOC_WINEVT + u'SecEvent.evt', u'/OSLogs/'],
['evtl_xp', LOC_WINEVT + u'SysEvent.evt', u'/OSLogs/'],

['amcache', LOC_AMCACHE + u'Amcache.hve', u'/MRU/Prog/'],
['amcache', LOC_AMCACHE + u'RecentFileCache.bcf', u'/MRU/Prog/'],

['setupapi', u'/Windows/Inf/setupapi.dev.log', u'/Registry/'],
['setupapi_xp', u'/Windows/setupapi.log', u'/Registry/'],

['mft', u'/$MFT', u'/Filesystem/'],
['logfile', u'/$LogFile', u'/Filesystem/']
]

SYSTEM_DIR = [ # [artifact_path, destination_directory, isRecursive]
# [u'/$Recycle.Bin', u'/Recycle/', True],
[u'/Windows/Prefetch', u'/MRU/Prog/prefetch/', False],
[u'/Windows/System32/sru', u'/MRU/Prog/srum/', False],
[u'/Windows/System32/wbem/Repository', u'/MRU/Prog/sccm/', False]
['recycle', u'/$Recycle.Bin', u'/Recycle/', True],
['recycle_xp', u'/RECYCLER', u'/Recycle/', True],
['prefetch', u'/Windows/Prefetch', u'/MRU/Prog/prefetch/', False],
['srum', u'/Windows/System32/sru', u'/MRU/Prog/srum/', False],
['sccm', u'/Windows/System32/wbem/Repository', u'/MRU/Prog/sccm/', False]
]

USER_FILE = [ # [artifact_path, destination_directory]
[u'/NTUSER.DAT', u'/Registry/'],
[u'/AppData/Local/Microsoft/Windows/UsrClass.dat', u'/Registry/']
['ntuser', u'/NTUSER.DAT', u'/Registry/'],
['usrclass', u'/AppData/Local/Microsoft/Windows/UsrClass.dat', u'/Registry/'],
['usrclass_xp', u'/Local Settings/Application Data/Microsoft/Windows/UsrClass.dat', u'/Registry/']
]

USER_DIR = [ # [artifact_path, destination_directory, isRecursive]
[LOC_RECENT, u'MRU/Files/lnk/', False],
[LOC_RECENT + u'AutomaticDestinations', u'/MRU/Files/jmp/', False],
[LOC_RECENT + u'CustomDestinations', u'/MRU/Files/jmp/', False],
[u'/AppData/Local/Microsoft/Windows/WebCache', u'/MRU/Files/webcache/', False]
['lnk', LOC_RECENT, u'MRU/Files/lnk/', False],
['lnk_xp', u'/Recent/', u'MRU/Files/lnk/', False],
['jmp', LOC_RECENT + u'AutomaticDestinations', u'/MRU/Files/jmp/', False],
['jmp', LOC_RECENT + u'CustomDestinations', u'/MRU/Files/jmp/', False],
['iehist', u'/AppData/Local/Microsoft/Windows/WebCache', u'/MRU/Files/iehist/', False],
['iehist_xp', u'/Local Settings/History/History.IE5', u'/MRU/Files/iehist/', True]
]

FILE_ADS = [ # [artifact_path, destination_directory, ADS Name]
# [u'/$Extend/$UsnJrnl', u'/filesystem/', u'$J']
['usnjrnl', u'/$Extend/$UsnJrnl', u'/Filesystem/', u'$J']
]

0 comments on commit 460cb63

Please sign in to comment.