Windows 中的重要工件 - I



本章將解釋 Microsoft Windows 取證中涉及的各種概念以及調查人員可以從調查過程中獲得的重要工件。

簡介

工件是計算機系統中包含與計算機使用者執行的活動相關的重要資訊的物體或區域。此資訊型別和位置取決於作業系統。在取證分析過程中,這些工件在批准或不批准調查人員的觀察結果方面發揮著非常重要的作用。

Windows 工件對取證的重要性

由於以下原因,Windows 工件具有重要意義:

  • 全世界約 90% 的流量來自使用 Windows 作為作業系統的計算機。這就是為什麼對於數字取證檢查員來說,Windows 工件非常重要。

  • Windows 作業系統儲存與計算機系統上使用者活動相關的不同型別的證據。這是另一個表明 Windows 工件對數字取證的重要性。

  • 許多時候,調查人員會圍繞使用者建立的資料等舊的和傳統的領域進行調查。Windows 工件可以將調查引向非傳統領域,例如系統建立的資料或工件。

  • Windows 提供了大量工件,這對調查人員以及執行非正式調查的公司和個人都有幫助。

  • 近年來網路犯罪的增加是 Windows 工件很重要的另一個原因。

Windows 工件及其 Python 指令碼

在本節中,我們將討論一些 Windows 工件和 Python 指令碼,以從中獲取資訊。

回收站

它是取證調查中重要的 Windows 工件之一。Windows 回收站包含使用者已刪除但系統尚未物理刪除的檔案。即使使用者從系統中完全刪除了檔案,它也仍然是重要的調查來源。這是因為檢查員可以從已刪除的檔案中提取有價值的資訊,例如原始檔案路徑以及將其傳送到回收站的時間。

請注意,回收站證據的儲存取決於 Windows 的版本。在以下 Python 指令碼中,我們將處理 Windows 7,它建立兩個檔案:包含已回收檔案實際內容的$R檔案和包含原始檔名、路徑、檔案大小以及檔案刪除時間的$I檔案。

對於 Python 指令碼,我們需要安裝第三方模組,即pytsk3、pyewfunicodecsv。我們可以使用pip來安裝它們。我們可以按照以下步驟從回收站中提取資訊:

  • 首先,我們需要使用遞迴方法掃描$Recycle.bin資料夾,並選擇所有以$I開頭的檔案。

  • 接下來,我們將讀取檔案內容並解析可用的元資料結構。

  • 現在,我們將搜尋關聯的 $R 檔案。

  • 最後,我們將結果寫入 CSV 檔案以供審查。

讓我們看看如何為此目的使用 Python 程式碼:

首先,我們需要匯入以下 Python 庫:

from __future__ import print_function
from argparse import ArgumentParser

import datetime
import os
import struct

from utility.pytskutil import TSKUtil
import unicodecsv as csv

接下來,我們需要為命令列處理程式提供引數。請注意,這裡它將接受三個引數 - 第一個是證據檔案路徑,第二個是證據檔案型別,第三個是所需的 CSV 報告輸出路徑,如下所示:

if __name__ == '__main__':
   parser = argparse.ArgumentParser('Recycle Bin evidences')
   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)

現在,定義main()函式,該函式將處理所有處理過程。它將如下搜尋$I檔案:

def main(evidence, image_type, report_file):
   tsk_util = TSKUtil(evidence, image_type)
   dollar_i_files = tsk_util.recurse_files("$I", path = '/$Recycle.bin',logic = "startswith")
   
   if dollar_i_files is not None:
      processed_files = process_dollar_i(tsk_util, dollar_i_files)
      write_csv(report_file,['file_path', 'file_size', 'deleted_time','dollar_i_file', 'dollar_r_file', 'is_directory'],processed_files)
   else:
      print("No $I files found")

現在,如果我們找到了$I檔案,則必須將其傳送到process_dollar_i()函式,該函式將接受tsk_util物件以及$I檔案列表,如下所示:

def process_dollar_i(tsk_util, dollar_i_files):
   processed_files = []
   
   for dollar_i in dollar_i_files:
      file_attribs = read_dollar_i(dollar_i[2])
      if file_attribs is None:
         continue
      file_attribs['dollar_i_file'] = os.path.join('/$Recycle.bin', dollar_i[1][1:])

現在,搜尋 $R 檔案,如下所示:

recycle_file_path = os.path.join('/$Recycle.bin',dollar_i[1].rsplit("/", 1)[0][1:])
dollar_r_files = tsk_util.recurse_files(
   "$R" + dollar_i[0][2:],path = recycle_file_path, logic = "startswith")
   
   if dollar_r_files is None:
      dollar_r_dir = os.path.join(recycle_file_path,"$R" + dollar_i[0][2:])
      dollar_r_dirs = tsk_util.query_directory(dollar_r_dir)
   
   if dollar_r_dirs is None:
      file_attribs['dollar_r_file'] = "Not Found"
      file_attribs['is_directory'] = 'Unknown'
   
   else:
      file_attribs['dollar_r_file'] = dollar_r_dir
      file_attribs['is_directory'] = True
   
   else:
      dollar_r = [os.path.join(recycle_file_path, r[1][1:])for r in dollar_r_files]
      file_attribs['dollar_r_file'] = ";".join(dollar_r)
      file_attribs['is_directory'] = False
      processed_files.append(file_attribs)
   return processed_files  

現在,定義read_dollar_i()方法來讀取$I檔案,換句話說,解析元資料。我們將使用read_random()方法讀取簽名的前八個位元組。如果簽名不匹配,這將返回 None。之後,如果這是一個有效的檔案,我們將不得不從$I檔案中讀取和解包值。

def read_dollar_i(file_obj):
   if file_obj.read_random(0, 8) != '\x01\x00\x00\x00\x00\x00\x00\x00':
      return None
   raw_file_size = struct.unpack('<q', file_obj.read_random(8, 8))
   raw_deleted_time = struct.unpack('<q',   file_obj.read_random(16, 8))
   raw_file_path = file_obj.read_random(24, 520)

現在,在提取這些檔案後,我們需要使用sizeof_fmt()函式將整數解釋為人類可讀的值,如下所示:

file_size = sizeof_fmt(raw_file_size[0])
deleted_time = parse_windows_filetime(raw_deleted_time[0])

file_path = raw_file_path.decode("utf16").strip("\x00")
return {'file_size': file_size, 'file_path': file_path,'deleted_time': deleted_time}

現在,我們需要定義sizeof_fmt()函式,如下所示:

def sizeof_fmt(num, suffix = 'B'):
   for unit in ['', 'Ki', 'Mi', 'Gi', 'Ti', 'Pi', 'Ei', 'Zi']:
      if abs(num) < 1024.0:
         return "%3.1f%s%s" % (num, unit, suffix)
      num /= 1024.0
   return "%.1f%s%s" % (num, 'Yi', suffix)

現在,定義一個函式將解釋的整數轉換為格式化的日期和時間,如下所示:

def parse_windows_filetime(date_value):
   microseconds = float(date_value) / 10
   ts = datetime.datetime(1601, 1, 1) + datetime.timedelta(
      microseconds = microseconds)
   return ts.strftime('%Y-%m-%d %H:%M:%S.%f')

現在,我們將定義write_csv()方法將處理後的結果寫入 CSV 檔案,如下所示:

def write_csv(outfile, fieldnames, data):
   with open(outfile, 'wb') as open_outfile:
      csvfile = csv.DictWriter(open_outfile, fieldnames)
      csvfile.writeheader()
      csvfile.writerows(data)

執行上述指令碼後,我們將從 $I 和 $R 檔案中獲取資料。

便籤

Windows 便籤取代了用筆和紙書寫的現實世界習慣。這些便籤用於在桌面上浮動,並提供不同的顏色、字型等選項。在 Windows 7 中,便籤檔案儲存為 OLE 檔案,因此在以下 Python 指令碼中,我們將調查此 OLE 檔案以從便籤中提取元資料。

對於此 Python 指令碼,我們需要安裝第三方模組,即olefile、pytsk3、pyewf和 unicodecsv。我們可以使用命令pip來安裝它們。

我們可以按照以下討論的步驟從名為StickyNote.sn的便籤檔案中提取資訊:

  • 首先,開啟證據檔案並找到所有 StickyNote.snt 檔案。

  • 然後,從 OLE 流中解析元資料和內容,並將 RTF 內容寫入檔案。

  • 最後,建立此元資料的 CSV 報告。

Python 程式碼

讓我們看看如何為此目的使用 Python 程式碼:

首先,匯入以下 Python 庫:

from __future__ import print_function
from argparse import ArgumentParser

import unicodecsv as csv
import os
import StringIO

from utility.pytskutil import TSKUtil
import olefile

接下來,定義一個將在整個指令碼中使用的全域性變數:

REPORT_COLS = ['note_id', 'created', 'modified', 'note_text', 'note_file']

接下來,我們需要為命令列處理程式提供引數。請注意,這裡它將接受三個引數 - 第一個是證據檔案路徑,第二個是證據檔案型別,第三個是所需的輸出路徑,如下所示:

if __name__ == '__main__':
   parser = argparse.ArgumentParser('Evidence from Sticky Notes')
   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_FOLDER', help="Path to report folder")
   args = parser.parse_args()
   main(args.EVIDENCE_FILE, args.IMAGE_TYPE, args.REPORT_FOLDER)

現在,我們將定義main()函式,它將類似於前面的指令碼,如下所示:

def main(evidence, image_type, report_folder):
   tsk_util = TSKUtil(evidence, image_type)
   note_files = tsk_util.recurse_files('StickyNotes.snt', '/Users','equals')

現在,讓我們遍歷結果檔案。然後,我們將呼叫parse_snt_file()函式來處理檔案,然後我們將使用write_note_rtf()方法寫入 RTF 檔案,如下所示:

report_details = []
for note_file in note_files:
   user_dir = note_file[1].split("/")[1]
   file_like_obj = create_file_like_obj(note_file[2])
   note_data = parse_snt_file(file_like_obj)
   
   if note_data is None:
      continue
   write_note_rtf(note_data, os.path.join(report_folder, user_dir))
   report_details += prep_note_report(note_data, REPORT_COLS,"/Users" + note_file[1])
   write_csv(os.path.join(report_folder, 'sticky_notes.csv'), REPORT_COLS,report_details)

接下來,我們需要定義此指令碼中使用的各種函式。

首先,我們將為讀取檔案大小定義create_file_like_obj()函式,方法是獲取pytsk檔案物件。然後,我們將定義parse_snt_file()函式,該函式將檔案類物件作為其輸入,並用於讀取和解釋便籤檔案。

def parse_snt_file(snt_file):
   
   if not olefile.isOleFile(snt_file):
      print("This is not an OLE file")
      return None
   ole = olefile.OleFileIO(snt_file)
   note = {}
   
   for stream in ole.listdir():
      if stream[0].count("-") == 3:
         if stream[0] not in note:
            note[stream[0]] = {"created": ole.getctime(stream[0]),"modified": ole.getmtime(stream[0])}
         content = None
         if stream[1] == '0':
            content = ole.openstream(stream).read()
         elif stream[1] == '3':
            content = ole.openstream(stream).read().decode("utf-16")
         if content:
            note[stream[0]][stream[1]] = content
	return note

現在,透過定義write_note_rtf()函式建立一個 RTF 檔案,如下所示

def write_note_rtf(note_data, report_folder):
   if not os.path.exists(report_folder):
      os.makedirs(report_folder)
   
   for note_id, stream_data in note_data.items():
      fname = os.path.join(report_folder, note_id + ".rtf")
      with open(fname, 'w') as open_file:
         open_file.write(stream_data['0'])

現在,我們將巢狀字典轉換為字典的扁平列表,這些字典更適合 CSV 電子表格。這將透過定義prep_note_report()函式來完成。最後,我們將定義write_csv()函式。

def prep_note_report(note_data, report_cols, note_file):
   report_details = []
   
   for note_id, stream_data in note_data.items():
      report_details.append({
         "note_id": note_id,
         "created": stream_data['created'],
         "modified": stream_data['modified'],
         "note_text": stream_data['3'].strip("\x00"),
         "note_file": note_file
      })
   return report_details
def write_csv(outfile, fieldnames, data):
   with open(outfile, 'wb') as open_outfile:
      csvfile = csv.DictWriter(open_outfile, fieldnames)
      csvfile.writeheader()
      csvfile.writerows(data)

執行上述指令碼後,我們將從便籤檔案中獲取元資料。

登錄檔檔案

Windows 登錄檔檔案包含許多重要的詳細資訊,對於取證分析師來說,這些詳細資訊就像一個資訊寶庫。它是一個分層資料庫,包含與作業系統配置、使用者活動、軟體安裝等相關的資訊。在以下 Python 指令碼中,我們將從SYSTEMSOFTWARE配置單元訪問常見的基線資訊。

對於此 Python 指令碼,我們需要安裝第三方模組,即pytsk3、pyewfregistry。我們可以使用pip來安裝它們。

我們可以按照以下給出的步驟從 Windows 登錄檔中提取資訊:

  • 首先,根據其名稱和路徑查詢要處理的登錄檔配置單元。

  • 然後,我們使用 StringIO 和 Registry 模組開啟這些檔案。

  • 最後,我們需要處理每個配置單元並將解析後的值列印到控制檯以供解釋。

Python 程式碼

讓我們看看如何為此目的使用 Python 程式碼:

首先,匯入以下 Python 庫:

from __future__ import print_function
from argparse import ArgumentParser

import datetime
import StringIO
import struct

from utility.pytskutil import TSKUtil
from Registry import Registry

現在,為命令列處理程式提供引數。這裡它將接受兩個引數 - 第一個是證據檔案路徑,第二個是證據檔案型別,如下所示:

if __name__ == '__main__':
   parser = argparse.ArgumentParser('Evidence from Windows Registry')
   parser.add_argument('EVIDENCE_FILE', help = "Path to evidence file")
   parser.add_argument('IMAGE_TYPE', help = "Evidence file format",
   choices = ('ewf', 'raw'))
   args = parser.parse_args()
   main(args.EVIDENCE_FILE, args.IMAGE_TYPE)

現在,我們將定義main()函式,用於在/Windows/System32/config資料夾中搜索SYSTEMSOFTWARE配置單元,如下所示:

def main(evidence, image_type):
   tsk_util = TSKUtil(evidence, image_type)
   tsk_system_hive = tsk_util.recurse_files('system', '/Windows/system32/config', 'equals')
   tsk_software_hive = tsk_util.recurse_files('software', '/Windows/system32/config', 'equals')
   system_hive = open_file_as_reg(tsk_system_hive[0][2])
   software_hive = open_file_as_reg(tsk_software_hive[0][2])
   process_system_hive(system_hive)
   process_software_hive(software_hive)

現在,定義開啟登錄檔檔案的函式。為此,我們需要從pytsk元資料中收集檔案大小,如下所示:

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)

現在,藉助以下方法,我們可以處理SYSTEM>配置單元:

def process_system_hive(hive):
   root = hive.root()
   current_control_set = root.find_key("Select").value("Current").value()
   control_set = root.find_key("ControlSet{:03d}".format(current_control_set))
   raw_shutdown_time = struct.unpack(
      '<Q', control_set.find_key("Control").find_key("Windows").value("ShutdownTime").value())
   
   shutdown_time = parse_windows_filetime(raw_shutdown_time[0])
   print("Last Shutdown Time: {}".format(shutdown_time))
   
   time_zone = control_set.find_key("Control").find_key("TimeZoneInformation")
      .value("TimeZoneKeyName").value()
   
   print("Machine Time Zone: {}".format(time_zone))
   computer_name = control_set.find_key("Control").find_key("ComputerName").find_key("ComputerName")
      .value("ComputerName").value()
   
   print("Machine Name: {}".format(computer_name))
   last_access = control_set.find_key("Control").find_key("FileSystem")
      .value("NtfsDisableLastAccessUpdate").value()
   last_access = "Disabled" if last_access == 1 else "enabled"
   print("Last Access Updates: {}".format(last_access))

現在,我們需要定義一個函式將解釋的整數轉換為格式化的日期和時間,如下所示:

def parse_windows_filetime(date_value):
   microseconds = float(date_value) / 10
   ts = datetime.datetime(1601, 1, 1) + datetime.timedelta(microseconds = microseconds)
   return ts.strftime('%Y-%m-%d %H:%M:%S.%f')

def parse_unix_epoch(date_value):
   ts = datetime.datetime.fromtimestamp(date_value)
   return ts.strftime('%Y-%m-%d %H:%M:%S.%f')

現在,藉助以下方法,我們可以處理SOFTWARE配置單元:

def process_software_hive(hive):
   root = hive.root()
   nt_curr_ver = root.find_key("Microsoft").find_key("Windows NT")
      .find_key("CurrentVersion")
   
   print("Product name: {}".format(nt_curr_ver.value("ProductName").value()))
   print("CSD Version: {}".format(nt_curr_ver.value("CSDVersion").value()))
   print("Current Build: {}".format(nt_curr_ver.value("CurrentBuild").value()))
   print("Registered Owner: {}".format(nt_curr_ver.value("RegisteredOwner").value()))
   print("Registered Org: 
      {}".format(nt_curr_ver.value("RegisteredOrganization").value()))
   
   raw_install_date = nt_curr_ver.value("InstallDate").value()
   install_date = parse_unix_epoch(raw_install_date)
   print("Installation Date: {}".format(install_date))

執行上述指令碼後,我們將獲得儲存在 Windows 登錄檔檔案中的元資料。

廣告

© . All rights reserved.