Учебники

Важные артефакты в Windows-II

В этой главе рассказывается о некоторых более важных артефактах в 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 в электронную таблицу.