Python数字取证 Windows中的重要工件-II

Python数字取证 Windows中的重要工件-II

本章讲述了Windows中一些更重要的人工制品,以及使用Python提取它们的方法。

用户活动

Windows有 NTUSER.DAT 文件用于存储各种用户活动。每个用户配置文件都有像 NTUSER.DAT 这样的蜂巢,它专门存储与该用户有关的信息和配置。因此,它对法医分析人员的调查非常有用。

下面的Python脚本将解析 NTUSER.DAT 的一些键,以探索用户在系统中的行为。在进一步进行之前,对于Python脚本,我们需要安装第三方模块,即 Registry、pytsk3 、pyewf和 Jinja2。 我们可以使用pip来安装它们。

我们可以按照以下步骤从 NTUSER.DAT 文件中提取信息—-。

  • 首先,搜索系统中的所有 NTUSER.DAT 文件。

  • 然后解析每个 NTUSER.DAT 文件的 WordWheelQuery、TypePath和RunMRU 键。

  • 最后,我们将通过使用 Jinja2 fmodule将这些已经处理过的工件写到HTML报告中。

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 文件句柄并通过 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)

现在,我们将定义函数来解析和处理 NTUSER.DAT 文件中的 WordWheelQuery 键,如下所示

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  

现在,我们将定义函数来解析和处理 NTUSER.DAT 文件中的 TypedPaths 键,如下所示

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

现在,我们将定义函数来解析和处理 NTUSER.DAT 文件中的 RunMRU 键,如下所示

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报告。

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 “文件夹中。

数字取证专家可以揭示从指定位置执行程序的证据,以及用户的详细信息。预取文件对检查者来说是有用的人工制品,因为即使在程序被删除或取消安装后,它们的条目仍然存在。

让我们讨论一下从Windows预取文件中获取信息的Python脚本,如下所示

对于Python脚本,安装第三方模块即 pylnk、pytsk3unicodecsv。 回顾一下,我们已经在前几章讨论过的Python脚本中使用过这些库了。

我们必须按照下面的步骤从 prefetch 文件中提取信息—-。

  • 首先,扫描 .pf 扩展文件或预取文件。

  • 现在,执行签名验证以消除误报。

  • 接下来,解析Windows预取文件的格式。这随Windows版本的不同而不同。例如,对于Windows XP是17,对于Windows Vista和Windows 7是23,对于Windows 8.1是26,对于Windows 10是30。

  • 最后,我们将把解析后的结果写入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)

现在,通过使用struct提取嵌入在预取文件中的数据,如下所示

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版本的预取文件中获取信息,并输入电子表格。

Python教程

Java教程

Web教程

数据库教程

图形图像教程

大数据教程

开发工具教程

计算机教程