สิ่งประดิษฐ์ที่สำคัญใน Windows-II

บทนี้พูดถึงสิ่งประดิษฐ์ที่สำคัญกว่าใน Windows และวิธีการแยกโดยใช้ Python

กิจกรรมของผู้ใช้

Windows มี NTUSER.DATไฟล์สำหรับจัดเก็บกิจกรรมต่างๆของผู้ใช้ โปรไฟล์ผู้ใช้ทุกคนมีกลุ่มเหมือนNTUSER.DATซึ่งเก็บข้อมูลและการกำหนดค่าที่เกี่ยวข้องกับผู้ใช้นั้นโดยเฉพาะ ดังนั้นจึงมีประโยชน์อย่างมากสำหรับการตรวจสอบโดยนักวิเคราะห์ทางนิติวิทยาศาสตร์

สคริปต์ Python ต่อไปนี้จะแยกวิเคราะห์คีย์บางส่วนของ NTUSER.DATสำหรับการสำรวจการกระทำของผู้ใช้ในระบบ ก่อนที่จะดำเนินการต่อไปสำหรับสคริปต์ Python เราจำเป็นต้องติดตั้งโมดูลของบุคคลที่สามคือRegistry, pytsk3, pyewf และ Jinja2. เราสามารถใช้ pip เพื่อติดตั้งได้

เราสามารถทำตามขั้นตอนต่อไปนี้เพื่อดึงข้อมูลจาก NTUSER.DAT ไฟล์ -

  • ก่อนอื่นให้ค้นหาทั้งหมด NTUSER.DAT ไฟล์ในระบบ

  • จากนั้นแยกวิเคราะห์ไฟล์ WordWheelQuery, TypePath and RunMRU ที่สำคัญสำหรับแต่ละ NTUSER.DAT ไฟล์.

  • ในที่สุดเราจะเขียนสิ่งประดิษฐ์เหล่านี้ซึ่งประมวลผลแล้วลงในรายงาน HTML โดยใช้ Jinja2 fmodule

รหัส 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

รหัส 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 จะสร้างขึ้น prefetch files. สิ่งเหล่านี้ใช้เพื่อเร่งกระบวนการเริ่มต้นแอปพลิเคชัน นามสกุลของไฟล์เหล่านี้คือ.PF และสิ่งเหล่านี้จะถูกเก็บไว้ในไฟล์ ”\Root\Windows\Prefetch” โฟลเดอร์

ผู้เชี่ยวชาญด้านนิติวิทยาศาสตร์ดิจิทัลสามารถเปิดเผยหลักฐานการทำงานของโปรแกรมจากตำแหน่งที่ระบุพร้อมกับรายละเอียดของผู้ใช้ ไฟล์ Prefetch เป็นสิ่งประดิษฐ์ที่มีประโยชน์สำหรับผู้ตรวจสอบเนื่องจากรายการจะยังคงอยู่แม้ว่าโปรแกรมจะถูกลบหรือไม่ได้ติดตั้งก็ตาม

ให้เราพูดคุยเกี่ยวกับสคริปต์ Python ที่จะดึงข้อมูลจากไฟล์ดึงข้อมูลล่วงหน้าของ Windows ตามที่ระบุด้านล่าง -

สำหรับสคริปต์ Python ให้ติดตั้งโมดูลของบุคคลที่สามคือ pylnk, pytsk3 และ unicodecsv. โปรดจำไว้ว่าเราได้ทำงานกับไลบรารีเหล่านี้แล้วในสคริปต์ Python ที่เราได้กล่าวถึงในบทก่อนหน้านี้

เราต้องทำตามขั้นตอนด้านล่างเพื่อดึงข้อมูลออกมา prefetch ไฟล์ -

  • ขั้นแรกให้สแกนหา .pf ไฟล์นามสกุลหรือไฟล์ prefetch

  • ตอนนี้ทำการตรวจสอบลายเซ็นเพื่อกำจัดผลบวกปลอม

  • จากนั้นแยกวิเคราะห์รูปแบบไฟล์การดึงข้อมูลล่วงหน้าของ 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

ตอนนี้ให้อาร์กิวเมนต์สำหรับตัวจัดการบรรทัดคำสั่ง ที่นี่จะยอมรับสองข้อโต้แย้งอันดับแรกคือพา ธ ไปยังไฟล์หลักฐานและอันดับที่สองจะเป็นประเภทของไฟล์หลักฐาน นอกจากนี้ยังยอมรับอาร์กิวเมนต์ที่เป็นทางเลือกสำหรับการระบุเส้นทางในการสแกนหาไฟล์ prefetch -

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 ลงในสเปรดชีต