В этой главе рассказывается о некоторых более важных артефактах в Windows и их методе извлечения с использованием Python.
Действия пользователя
Windows, имеющая файл NTUSER.DAT для хранения различных действий пользователя. Каждый профиль пользователя имеет куст, как NTUSER.DAT , который хранит информацию и конфигурации, относящиеся конкретно к этому пользователю. Следовательно, это очень полезно для целей расследования судебно-медицинскими экспертами.
Следующий скрипт Python проанализирует некоторые ключи NTUSER.DAT для изучения действий пользователя в системе. Прежде чем продолжить, для скрипта Python нам нужно установить сторонние модули, а именно Registry, pytsk3 , pyewf и Jinja2 . Мы можем использовать pip для их установки.
Мы можем выполнить следующие шаги для извлечения информации из файла NTUSER.DAT —
-
Сначала выполните поиск всех файлов NTUSER.DAT в системе.
-
Затем проанализируйте ключи WordWheelQuery, TypePath и RunMRU для каждого файла NTUSER.DAT .
-
Наконец, мы запишем эти уже обработанные артефакты в отчет HTML с помощью модуля jinja2.
Сначала выполните поиск всех файлов NTUSER.DAT в системе.
Затем проанализируйте ключи WordWheelQuery, TypePath и RunMRU для каждого файла NTUSER.DAT .
Наконец, мы запишем эти уже обработанные артефакты в отчет HTML с помощью модуля jinja2.
Код Python
Давайте посмотрим, как использовать код Python для этой цели —
Прежде всего нам нужно импортировать следующие модули Python —
from __future__ import print_function from argparse import ArgumentParser import os import StringIO import struct from utility.pytskutil import TSKUtil from Registry import Registry import jinja2
Теперь предоставьте аргумент для обработчика командной строки. Здесь он будет принимать три аргумента — первый — это путь к файлу улик, второй — тип файла улик, а третий — желаемый путь вывода к отчету HTML, как показано ниже —
if __name__ == '__main__': parser = argparse.ArgumentParser('Information from user activities') parser.add_argument('EVIDENCE_FILE',help = "Path to evidence file") parser.add_argument('IMAGE_TYPE',help = "Evidence file format",choices = ('ewf', 'raw')) parser.add_argument('REPORT',help = "Path to report file") args = parser.parse_args() main(args.EVIDENCE_FILE, args.IMAGE_TYPE, args.REPORT)
Теперь давайте определим функцию main () для поиска всех файлов NTUSER.DAT , как показано ниже:
def main(evidence, image_type, report): tsk_util = TSKUtil(evidence, image_type) tsk_ntuser_hives = tsk_util.recurse_files('ntuser.dat','/Users', 'equals') nt_rec = { 'wordwheel': {'data': [], 'title': 'WordWheel Query'}, 'typed_path': {'data': [], 'title': 'Typed Paths'}, 'run_mru': {'data': [], 'title': 'Run MRU'} }
Теперь мы попытаемся найти ключ в файле NTUSER.DAT и, как только вы его найдете, определите функции пользовательской обработки, как показано ниже:
for ntuser in tsk_ntuser_hives: uname = ntuser[1].split("/") open_ntuser = open_file_as_reg(ntuser[2]) try: explorer_key = open_ntuser.root().find_key("Software").find_key("Microsoft") .find_key("Windows").find_key("CurrentVersion").find_key("Explorer") except Registry.RegistryKeyNotFoundException: continue nt_rec['wordwheel']['data'] += parse_wordwheel(explorer_key, uname) nt_rec['typed_path']['data'] += parse_typed_paths(explorer_key, uname) nt_rec['run_mru']['data'] += parse_run_mru(explorer_key, uname) nt_rec['wordwheel']['headers'] = \ nt_rec['wordwheel']['data'][0].keys() nt_rec['typed_path']['headers'] = \ nt_rec['typed_path']['data'][0].keys() nt_rec['run_mru']['headers'] = \ nt_rec['run_mru']['data'][0].keys()
Теперь передайте объект словаря и его путь методу write_html () следующим образом:
write_html(report, nt_rec)
Теперь определите метод, который берет дескриптор файла pytsk и считывает его в класс Registry через класс StringIO .
def open_file_as_reg(reg_file): file_size = reg_file.info.meta.size file_content = reg_file.read_random(0, file_size) file_like_obj = StringIO.StringIO(file_content) return Registry.Registry(file_like_obj)
Теперь мы определим функцию, которая будет анализировать и обрабатывать ключ WordWheelQuery из файла NTUSER.DAT следующим образом:
def parse_wordwheel(explorer_key, username): try: wwq = explorer_key.find_key("WordWheelQuery") except Registry.RegistryKeyNotFoundException: return [] mru_list = wwq.value("MRUListEx").value() mru_order = [] for i in xrange(0, len(mru_list), 2): order_val = struct.unpack('h', mru_list[i:i + 2])[0] if order_val in mru_order and order_val in (0, -1): break else: mru_order.append(order_val) search_list = [] for count, val in enumerate(mru_order): ts = "N/A" if count == 0: ts = wwq.timestamp() search_list.append({ 'timestamp': ts, 'username': username, 'order': count, 'value_name': str(val), 'search': wwq.value(str(val)).value().decode("UTF-16").strip("\x00") }) return search_list
Теперь мы определим функцию, которая будет анализировать и обрабатывать ключ TypedPaths из файла NTUSER.DAT следующим образом:
def parse_typed_paths(explorer_key, username): try: typed_paths = explorer_key.find_key("TypedPaths") except Registry.RegistryKeyNotFoundException: return [] typed_path_details = [] for val in typed_paths.values(): typed_path_details.append({ "username": username, "value_name": val.name(), "path": val.value() }) return typed_path_details
Теперь мы определим функцию, которая будет анализировать и обрабатывать ключ RunMRU из файла NTUSER.DAT следующим образом:
def parse_run_mru(explorer_key, username): try: run_mru = explorer_key.find_key("RunMRU") except Registry.RegistryKeyNotFoundException: return [] if len(run_mru.values()) == 0: return [] mru_list = run_mru.value("MRUList").value() mru_order = [] for i in mru_list: mru_order.append(i) mru_details = [] for count, val in enumerate(mru_order): ts = "N/A" if count == 0: ts = run_mru.timestamp() mru_details.append({ "username": username, "timestamp": ts, "order": count, "value_name": val, "run_statement": run_mru.value(val).value() }) return mru_details
Теперь следующая функция будет обрабатывать создание отчета HTML —
def write_html(outfile, data_dict): cwd = os.path.dirname(os.path.abspath(__file__)) env = jinja2.Environment(loader=jinja2.FileSystemLoader(cwd)) template = env.get_template("user_activity.html") rendering = template.render(nt_data=data_dict) with open(outfile, 'w') as open_outfile: open_outfile.write(rendering)
Наконец мы можем написать HTML-документ для отчета. После запуска вышеуказанного скрипта мы получим информацию из файла NTUSER.DAT в формате документа HTML.
Файлы LINK
Файлы ярлыков создаются, когда пользователь или операционная система создает файлы ярлыков для файлов, которые часто используются, дважды щелкаются или доступны с системных дисков, таких как подключенное хранилище. Такие виды файлов ярлыков называются файлами ссылок. Получив доступ к этим файлам ссылок, исследователь может определить активность окна, например время и место, откуда были получены эти файлы.
Давайте обсудим скрипт Python, который мы можем использовать для получения информации из этих файлов Windows LINK.
Для скрипта Python установите сторонние модули, а именно pylnk, pytsk3, pyewf . Мы можем выполнить следующие шаги, чтобы извлечь информацию из файлов LNK
-
Сначала найдите файлы lnk в системе.
-
Затем извлеките информацию из этого файла, просматривая их.
-
Теперь, наконец, нам нужно эту информацию в отчете CSV.
Сначала найдите файлы lnk в системе.
Затем извлеките информацию из этого файла, просматривая их.
Теперь, наконец, нам нужно эту информацию в отчете CSV.
Код Python
Давайте посмотрим, как использовать код Python для этой цели —
Сначала импортируйте следующие библиотеки Python —
from __future__ import print_function from argparse import ArgumentParser import csv import StringIO from utility.pytskutil import TSKUtil import pylnk
Теперь предоставьте аргумент для обработчика командной строки. Здесь он будет принимать три аргумента: первый — это путь к файлу улик, второй — тип файла улик, а третий — желаемый выходной путь к отчету CSV, как показано ниже —
if __name__ == '__main__': parser = argparse.ArgumentParser('Parsing LNK files') parser.add_argument('EVIDENCE_FILE', help = "Path to evidence file") parser.add_argument('IMAGE_TYPE', help = "Evidence file format",choices = ('ewf', 'raw')) parser.add_argument('CSV_REPORT', help = "Path to CSV report") args = parser.parse_args() main(args.EVIDENCE_FILE, args.IMAGE_TYPE, args.CSV_REPORT)
Теперь интерпретируйте файл улик, создав объект TSKUtil, и переберите файловую систему, чтобы найти файлы, оканчивающиеся на lnk . Это можно сделать, определив функцию main () следующим образом:
def main(evidence, image_type, report): tsk_util = TSKUtil(evidence, image_type) lnk_files = tsk_util.recurse_files("lnk", path="/", logic="endswith") if lnk_files is None: print("No lnk files found") exit(0) columns = [ 'command_line_arguments', 'description', 'drive_serial_number', 'drive_type', 'file_access_time', 'file_attribute_flags', 'file_creation_time', 'file_modification_time', 'file_size', 'environmental_variables_location', 'volume_label', 'machine_identifier', 'local_path', 'network_path', 'relative_path', 'working_directory' ]
Теперь с помощью следующего кода мы будем перебирать файлы lnk , создавая функцию следующим образом:
parsed_lnks = [] for entry in lnk_files: lnk = open_file_as_lnk(entry[2]) lnk_data = {'lnk_path': entry[1], 'lnk_name': entry[0]} for col in columns: lnk_data[col] = getattr(lnk, col, "N/A") lnk.close() parsed_lnks.append(lnk_data) write_csv(report, columns + ['lnk_path', 'lnk_name'], parsed_lnks)
Теперь нам нужно определить две функции: одна откроет объект файла pytsk, а другая будет использована для написания отчета CSV, как показано ниже:
def open_file_as_lnk(lnk_file): file_size = lnk_file.info.meta.size file_content = lnk_file.read_random(0, file_size) file_like_obj = StringIO.StringIO(file_content) lnk = pylnk.file() lnk.open_file_object(file_like_obj) return lnk def write_csv(outfile, fieldnames, data): with open(outfile, 'wb') as open_outfile: csvfile = csv.DictWriter(open_outfile, fieldnames) csvfile.writeheader() csvfile.writerows(data)
После запуска вышеуказанного скрипта мы получим информацию из обнаруженных файлов lnk в отчете CSV —
Файлы предварительной загрузки
Каждый раз, когда приложение запускается впервые из определенного места, Windows создает файлы предварительной выборки . Они используются для ускорения процесса запуска приложения. Расширение этих файлов — .PF, и они хранятся в папке «\ Root \ Windows \ Prefetch» .
Цифровые судебные эксперты могут раскрыть доказательства выполнения программы из указанного места вместе с данными пользователя. Файлы предварительной выборки являются полезными артефактами для экзаменатора, поскольку их запись сохраняется даже после удаления или удаления программы.
Давайте обсудим скрипт Python, который будет извлекать информацию из файлов предварительной загрузки Windows, как показано ниже —
Для сценария Python установите сторонние модули, а именно pylnk, pytsk3 и unicodecsv . Напомним, что мы уже работали с этими библиотеками в скриптах Python, которые мы обсуждали в предыдущих главах.
Мы должны следовать приведенным ниже шагам, чтобы извлечь информацию из файлов предварительной выборки —
-
Во-первых, сканируйте файлы расширения .pf или файлы предварительной выборки.
-
Теперь выполните проверку подписи, чтобы устранить ложные срабатывания.
-
Затем проанализируйте формат файла предварительной загрузки Windows. Это отличается от версии для Windows. Например, для Windows XP это 17, для Windows Vista и Windows 7 это 23, 26 для Windows 8.1 и 30 для Windows 10.
-
Наконец, мы запишем проанализированный результат в файл CSV.
Во-первых, сканируйте файлы расширения .pf или файлы предварительной выборки.
Теперь выполните проверку подписи, чтобы устранить ложные срабатывания.
Затем проанализируйте формат файла предварительной загрузки Windows. Это отличается от версии для Windows. Например, для Windows XP это 17, для Windows Vista и Windows 7 это 23, 26 для Windows 8.1 и 30 для Windows 10.
Наконец, мы запишем проанализированный результат в файл CSV.
Код Python
Давайте посмотрим, как использовать код Python для этой цели —
Сначала импортируйте следующие библиотеки Python —
from __future__ import print_function import argparse from datetime import datetime, timedelta import os import pytsk3 import pyewf import struct import sys import unicodecsv as csv from utility.pytskutil import TSKUtil
Теперь предоставьте аргумент для обработчика командной строки. Здесь он будет принимать два аргумента: первый будет путь к файлу доказательств, а второй будет тип файла доказательств. Он также принимает необязательный аргумент для указания пути для сканирования файлов предварительной выборки —
if __name__ == "__main__": parser = argparse.ArgumentParser('Parsing Prefetch files') parser.add_argument("EVIDENCE_FILE", help = "Evidence file path") parser.add_argument("TYPE", help = "Type of Evidence",choices = ("raw", "ewf")) parser.add_argument("OUTPUT_CSV", help = "Path to write output csv") parser.add_argument("-d", help = "Prefetch directory to scan",default = "/WINDOWS/PREFETCH") args = parser.parse_args() if os.path.exists(args.EVIDENCE_FILE) and \ os.path.isfile(args.EVIDENCE_FILE): main(args.EVIDENCE_FILE, args.TYPE, args.OUTPUT_CSV, args.d) else: print("[-] Supplied input file {} does not exist or is not a ""file".format(args.EVIDENCE_FILE)) sys.exit(1)
Теперь интерпретируйте файл улик, создав объект TSKUtil, и переберите файловую систему, чтобы найти файлы, заканчивающиеся на .pf . Это можно сделать, определив функцию main () следующим образом:
def main(evidence, image_type, output_csv, path): tsk_util = TSKUtil(evidence, image_type) prefetch_dir = tsk_util.query_directory(path) prefetch_files = None if prefetch_dir is not None: prefetch_files = tsk_util.recurse_files(".pf", path=path, logic="endswith") if prefetch_files is None: print("[-] No .pf files found") sys.exit(2) print("[+] Identified {} potential prefetch files".format(len(prefetch_files))) prefetch_data = [] for hit in prefetch_files: prefetch_file = hit[2] pf_version = check_signature(prefetch_file)
Теперь определите метод, который будет проверять подписи, как показано ниже:
def check_signature(prefetch_file): version, signature = struct.unpack("^<2i", prefetch_file.read_random(0, 8)) if signature == 1094927187: return version else: return None if pf_version is None: continue pf_name = hit[0] if pf_version == 17: parsed_data = parse_pf_17(prefetch_file, pf_name) parsed_data.append(os.path.join(path, hit[1].lstrip("//"))) prefetch_data.append(parsed_data)
Теперь начните обработку файлов предварительной загрузки Windows. Здесь мы берем пример файлов предварительной загрузки Windows XP —
def parse_pf_17(prefetch_file, pf_name): create = convert_unix(prefetch_file.info.meta.crtime) modify = convert_unix(prefetch_file.info.meta.mtime) def convert_unix(ts): if int(ts) == 0: return "" return datetime.utcfromtimestamp(ts) def convert_filetime(ts): if int(ts) == 0: return "" return datetime(1601, 1, 1) + timedelta(microseconds=ts / 10)
Теперь извлеките данные, встроенные в предварительно выбранные файлы, используя структуру следующим образом:
pf_size, name, vol_info, vol_entries, vol_size, filetime, \ count = struct.unpack("<i60s32x3iq16xi",prefetch_file.read_random(12, 136)) name = name.decode("utf-16", "ignore").strip("/x00").split("/x00")[0] vol_name_offset, vol_name_length, vol_create, \ vol_serial = struct.unpack("<2iqi",prefetch_file.read_random(vol_info, 20)) vol_serial = hex(vol_serial).lstrip("0x") vol_serial = vol_serial[:4] + "-" + vol_serial[4:] vol_name = struct.unpack( "<{}s".format(2 * vol_name_length), prefetch_file.read_random(vol_info + vol_name_offset,vol_name_length * 2))[0] vol_name = vol_name.decode("utf-16", "ignore").strip("/x00").split("/x00")[0] return [ pf_name, name, pf_size, create, modify, convert_filetime(filetime), count, vol_name, convert_filetime(vol_create), vol_serial ]
Поскольку мы предоставили версию предварительной выборки для Windows XP, но что, если она встретит версии предварительной выборки для других Windows. Затем он должен отобразить сообщение об ошибке следующим образом:
elif pf_version == 23: print("[-] Windows Vista / 7 PF file {} -- unsupported".format(pf_name)) continue elif pf_version == 26: print("[-] Windows 8 PF file {} -- unsupported".format(pf_name)) continue elif pf_version == 30: print("[-] Windows 10 PF file {} -- unsupported".format(pf_name)) continue else: print("[-] Signature mismatch - Name: {}\nPath: {}".format(hit[0], hit[1])) continue write_output(prefetch_data, output_csv)
Теперь определите метод записи результата в отчет CSV следующим образом:
def write_output(data, output_csv): print("[+] Writing csv report") with open(output_csv, "wb") as outfile: writer = csv.writer(outfile) writer.writerow([ "File Name", "Prefetch Name", "File Size (bytes)", "File Create Date (UTC)", "File Modify Date (UTC)", "Prefetch Last Execution Date (UTC)", "Prefetch Execution Count", "Volume", "Volume Create Date", "Volume Serial", "File Path" ]) writer.writerows(data)
После запуска приведенного выше сценария мы получим информацию из файлов предварительной загрузки версии для Windows XP в электронную таблицу.