Artefatti importanti in Windows-I

Questo capitolo illustrerà i vari concetti coinvolti nell'analisi forense di Microsoft Windows e gli importanti artefatti che un investigatore può ottenere dal processo di indagine.

introduzione

Gli artefatti sono gli oggetti o le aree all'interno di un sistema informatico che contengono informazioni importanti relative alle attività svolte dall'utente del computer. Il tipo e la posizione di queste informazioni dipendono dal sistema operativo. Durante l'analisi forense, questi artefatti svolgono un ruolo molto importante nell'approvare o disapprovare l'osservazione dell'investigatore.

Importanza degli artefatti di Windows per le analisi forensi

Gli artefatti di Windows assumono importanza per i seguenti motivi:

  • Circa il 90% del traffico nel mondo proviene dai computer che utilizzano Windows come sistema operativo. Ecco perché per gli esaminatori di digital forensics gli artefatti di Windows sono molto essenziali.

  • Il sistema operativo Windows memorizza diversi tipi di evidenze relative all'attività dell'utente sul sistema informatico. Questo è un altro motivo che mostra l'importanza degli artefatti di Windows per la digital forensics.

  • Molte volte l'investigatore ruota l'indagine attorno ad aree vecchie e tradizionali come i dati raccolti dagli utenti. Gli artefatti di Windows possono condurre l'indagine verso aree non tradizionali come i dati creati dal sistema o gli artefatti.

  • Windows fornisce una grande abbondanza di artefatti che sono utili per gli investigatori, nonché per le aziende e gli individui che eseguono indagini informali.

  • L'aumento della criminalità informatica negli ultimi anni è un altro motivo per cui gli artefatti di Windows sono importanti.

Artefatti di Windows e relativi script Python

In questa sezione, discuteremo di alcuni artefatti di Windows e script Python per recuperare informazioni da essi.

Cestino

È uno dei più importanti artefatti di Windows per le indagini forensi. Il cestino di Windows contiene i file che sono stati eliminati dall'utente, ma non ancora rimossi fisicamente dal sistema. Anche se l'utente rimuove completamente il file dal sistema, funge da importante fonte di indagine. Questo perché l'esaminatore può estrarre informazioni preziose, come il percorso del file originale e l'ora in cui è stato inviato al Cestino, dai file eliminati.

Si noti che l'archiviazione delle prove del Cestino dipende dalla versione di Windows. Nel seguente script Python, ci occuperemo di Windows 7 dove crea due file:$R file che contiene il contenuto effettivo del file riciclato e $I file che contiene il nome del file originale, il percorso e la dimensione del file quando il file è stato eliminato.

Per lo script Python dobbiamo installare moduli di terze parti, vale a dire pytsk3, pyewf e unicodecsv. Possiamo usarepipper installarli. Possiamo seguire i seguenti passaggi per estrarre le informazioni dal Cestino:

  • Innanzitutto, dobbiamo utilizzare il metodo ricorsivo per eseguire la scansione del file $Recycle.bin cartella e seleziona tutti i file che iniziano con $I.

  • Successivamente, leggeremo il contenuto dei file e analizzeremo le strutture di metadati disponibili.

  • Ora cercheremo il file $ R associato.

  • Alla fine, scriveremo i risultati nel file CSV per la revisione.

Vediamo come utilizzare il codice Python per questo scopo -

Innanzitutto, dobbiamo importare le seguenti librerie 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

Successivamente, dobbiamo fornire un argomento per il gestore della riga di comando. Nota che qui accetterà tre argomenti: il primo è il percorso del file delle prove, il secondo è il tipo di file delle prove e il terzo è il percorso di output desiderato per il report CSV, come mostrato di seguito -

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)

Ora, definisci il file main()funzione che gestirà tutta l'elaborazione. Cercherà$I file come segue -

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")

Ora, se abbiamo trovato $I file, quindi deve essere inviato a process_dollar_i() funzione che accetterà il file tsk_util oggetto così come l'elenco di $I file, come mostrato di seguito -

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:])

Ora, cerca i file $ R come segue:

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

Adesso definisci read_dollar_i() metodo per leggere il file $Ifile, in altre parole, analizzano i metadati. Noi useremoread_random()metodo per leggere i primi otto byte della firma. Questo non restituirà nessuno se la firma non corrisponde. Dopodiché, dovremo leggere e decomprimere i valori da$I file se questo è un file valido.

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)

Ora, dopo aver estratto questi file, dobbiamo interpretare gli interi in valori leggibili dall'uomo utilizzando sizeof_fmt() funziona come mostrato di seguito -

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}

Ora, dobbiamo definire sizeof_fmt() funzionare come segue:

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)

Ora, definisci una funzione per interi interpretati in data e ora formattate come segue:

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')

Ora definiremo write_csv() metodo per scrivere i risultati elaborati in un file CSV come segue:

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

Quando esegui lo script precedente, otterremo i dati dal file $ I e $ R.

Note adesive

Windows Sticky Notes sostituisce l'abitudine del mondo reale di scrivere con carta e penna. Queste note erano solite galleggiare sul desktop con diverse opzioni per colori, caratteri ecc. In Windows 7 il file Sticky Notes è memorizzato come file OLE, quindi nel seguente script Python esamineremo questo file OLE per estrarre i metadati da Sticky Notes.

Per questo script Python, dobbiamo installare moduli di terze parti, vale a dire olefile, pytsk3, pyewfe unicodecsv. Possiamo usare il comandopip per installarli.

Possiamo seguire i passaggi discussi di seguito per estrarre le informazioni dal file della nota adesiva StickyNote.sn -

  • Innanzitutto, apri il file delle prove e trova tutti i file StickyNote.snt.

  • Quindi, analizzare i metadati e il contenuto dal flusso OLE e scrivere il contenuto RTF nei file.

  • Infine, crea un rapporto CSV di questi metadati.

Codice Python

Vediamo come utilizzare il codice Python per questo scopo -

Innanzitutto, importa le seguenti librerie 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

Quindi, definisci una variabile globale che verrà utilizzata in questo script:

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

Successivamente, dobbiamo fornire un argomento per il gestore della riga di comando. Nota che qui accetterà tre argomenti: il primo è il percorso del file di prova, il secondo è il tipo di file di prova e il terzo è il percorso di output desiderato come segue -

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)

Ora definiremo main() funzione che sarà simile allo script precedente come mostrato di seguito -

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

Ora, iteriamo attraverso i file risultanti. Allora chiameremoparse_snt_file() per elaborare il file e quindi scriveremo il file RTF con l'estensione write_note_rtf() metodo come segue -

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)

Successivamente, dobbiamo definire varie funzioni utilizzate in questo script.

Prima di tutto definiremo create_file_like_obj() funzione per leggere la dimensione del file prendendo pytskoggetto file. Quindi definiremoparse_snt_file() funzione che accetterà l'oggetto simile a file come input e viene utilizzata per leggere e interpretare il file di note adesive.

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

Ora, crea un file RTF definendo write_note_rtf() funzionare come segue

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'])

Ora, tradurremo il dizionario annidato in un elenco semplice di dizionari più appropriati per un foglio di calcolo CSV. Sarà fatto definendoprep_note_report()funzione. Infine, definiremowrite_csv() funzione.

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)

Dopo aver eseguito lo script precedente, otterremo i metadati dal file Sticky Notes.

File di registro

I file di registro di Windows contengono molti dettagli importanti che sono come un tesoro di informazioni per un analista forense. È un database gerarchico che contiene dettagli relativi alla configurazione del sistema operativo, all'attività dell'utente, all'installazione del software, ecc. Nel seguente script Python accediamo alle informazioni di base comuni dalSYSTEM e SOFTWARE orticaria.

Per questo script Python, dobbiamo installare moduli di terze parti, vale a dire pytsk3, pyewf e registry. Possiamo usarepip per installarli.

Possiamo seguire i passaggi indicati di seguito per estrarre le informazioni dal registro di Windows -

  • Innanzitutto, trova gli hive del registro da elaborare in base al nome e al percorso.

  • Quindi dobbiamo aprire questi file utilizzando StringIO e moduli di registro.

  • Alla fine dobbiamo elaborare ogni singolo hive e stampare i valori analizzati sulla console per l'interpretazione.

Codice Python

Vediamo come utilizzare il codice Python per questo scopo -

Innanzitutto, importa le seguenti librerie 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

Fornisci ora un argomento per il gestore della riga di comando. Qui accetterà due argomenti: il primo è il percorso del file delle prove, il secondo è il tipo di file delle prove, come mostrato di seguito -

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)

Adesso definiremo main() funzione per la ricerca SYSTEM e SOFTWARE orticaria all'interno /Windows/System32/config cartella come segue -

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)

Ora, definisci la funzione per aprire il file di registro. A tal fine, dobbiamo raccogliere la dimensione del file dapytsk metadati come segue -

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)

Ora, con l'aiuto del seguente metodo, possiamo elaborare SYSTEM> alveare -

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))

Ora, dobbiamo definire una funzione per interi interpretati in data e ora formattate come segue:

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')

Ora con l'aiuto del seguente metodo possiamo elaborare SOFTWARE alveare -

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))

Dopo aver eseguito lo script precedente, otterremo i metadati memorizzati nei file del registro di Windows.