Artefacts importants dans Windows-I

Ce chapitre expliquera divers concepts impliqués dans la criminalistique de Microsoft Windows et les artefacts importants qu'un enquêteur peut obtenir à partir du processus d'enquête.

introduction

Les artefacts sont les objets ou les zones d'un système informatique qui contiennent des informations importantes liées aux activités effectuées par l'utilisateur de l'ordinateur. Le type et l'emplacement de ces informations dépendent du système d'exploitation. Au cours de l'analyse médico-légale, ces artefacts jouent un rôle très important dans l'approbation ou la désapprobation de l'observation de l'enquêteur.

Importance des artefacts Windows pour la criminalistique

Les artefacts Windows prennent de l'importance pour les raisons suivantes:

  • Environ 90% du trafic mondial provient des ordinateurs utilisant Windows comme système d'exploitation. C'est pourquoi, pour les examinateurs en criminalistique numérique, les artefacts Windows sont essentiels.

  • Le système d'exploitation Windows stocke différents types de preuves liées à l'activité de l'utilisateur sur le système informatique. C'est une autre raison qui montre l'importance des artefacts Windows pour la criminalistique numérique.

  • Plusieurs fois, l'enquêteur tourne l'enquête autour de domaines anciens et traditionnels tels que les données créées par l'utilisateur. Les artefacts Windows peuvent mener l'enquête vers des domaines non traditionnels tels que les données créées par le système ou les artefacts.

  • Une grande abondance d'artefacts est fournie par Windows, ce qui est utile pour les enquêteurs ainsi que pour les entreprises et les particuliers effectuant des enquêtes informelles.

  • L'augmentation de la cybercriminalité ces dernières années est une autre raison pour laquelle les artefacts Windows sont importants.

Artefacts Windows et leurs scripts Python

Dans cette section, nous allons discuter de certains artefacts Windows et des scripts Python pour en extraire des informations.

Corbeille

C'est l'un des artefacts Windows importants pour les enquêtes médico-légales. La corbeille de Windows contient les fichiers qui ont été supprimés par l'utilisateur, mais pas encore physiquement supprimés par le système. Même si l'utilisateur supprime complètement le fichier du système, il sert de source d'enquête importante. En effet, l'examinateur peut extraire des informations précieuses, comme le chemin du fichier d'origine ainsi que l'heure à laquelle il a été envoyé à la corbeille, à partir des fichiers supprimés.

Notez que le stockage des preuves de la Corbeille dépend de la version de Windows. Dans le script Python suivant, nous allons traiter de Windows 7 où il crée deux fichiers:$R fichier contenant le contenu réel du fichier recyclé et $I fichier qui contient le nom du fichier d'origine, le chemin, la taille du fichier lorsque le fichier a été supprimé.

Pour le script Python, nous devons installer des modules tiers, à savoir pytsk3, pyewf et unicodecsv. On peut utiliserpippour les installer. Nous pouvons suivre les étapes suivantes pour extraire les informations de la corbeille -

  • Tout d'abord, nous devons utiliser une méthode récursive pour parcourir le $Recycle.bin dossier et sélectionnez tous les fichiers commençant par $I.

  • Ensuite, nous lirons le contenu des fichiers et analyserons les structures de métadonnées disponibles.

  • Maintenant, nous allons rechercher le fichier $ R associé.

  • Enfin, nous écrirons les résultats dans un fichier CSV pour examen.

Voyons comment utiliser le code Python à cette fin -

Tout d'abord, nous devons importer les bibliothèques Python suivantes -

from __future__ import print_function
from argparse import ArgumentParser

import datetime
import os
import struct

from utility.pytskutil import TSKUtil
import unicodecsv as csv

Ensuite, nous devons fournir un argument pour le gestionnaire de ligne de commande. Notez qu'ici, il acceptera trois arguments - le premier est le chemin d'accès au fichier de preuves, le second est le type de fichier de preuves et le troisième est le chemin de sortie souhaité vers le rapport CSV, comme indiqué ci-dessous -

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)

Maintenant, définissez le main()fonction qui gérera tout le traitement. Il recherchera$I fichier comme suit -

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

Maintenant, si nous avons trouvé $I fichier, puis il doit être envoyé à process_dollar_i() fonction qui acceptera le tsk_util objet ainsi que la liste des $I fichiers, comme indiqué ci-dessous -

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

Maintenant, recherchez les fichiers $ R comme suit -

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

Maintenant, définissez read_dollar_i() méthode pour lire le $Ifichiers, en d'autres termes, analysent les métadonnées. Nous utiliseronsread_random()méthode pour lire les huit premiers octets de la signature. Cela ne renverra aucun si la signature ne correspond pas. Après cela, nous devrons lire et décompresser les valeurs de$I fichier s'il s'agit d'un fichier valide.

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)

Maintenant, après avoir extrait ces fichiers, nous devons interpréter les entiers en valeurs lisibles par l'homme en utilisant sizeof_fmt() fonction comme indiqué ci-dessous -

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}

Maintenant, nous devons définir sizeof_fmt() fonction comme suit -

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)

Maintenant, définissez une fonction pour les entiers interprétés en date et heure formatées comme suit -

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

Maintenant, nous allons définir write_csv() méthode pour écrire les résultats traités dans un fichier CSV comme suit -

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

Lorsque vous exécutez le script ci-dessus, nous obtenons les données des fichiers $ I et $ R.

Notes autocollantes

Windows Sticky Notes remplace l'habitude du monde réel d'écrire avec un stylo et du papier. Ces notes flottaient sur le bureau avec différentes options pour les couleurs, les polices, etc. Sous Windows 7, le fichier Sticky Notes est stocké sous forme de fichier OLE, donc dans le script Python suivant, nous étudierons ce fichier OLE pour extraire les métadonnées de Sticky Notes.

Pour ce script Python, nous devons installer des modules tiers à savoir olefile, pytsk3, pyewfet unicodecsv. Nous pouvons utiliser la commandepip pour les installer.

Nous pouvons suivre les étapes décrites ci-dessous pour extraire les informations du fichier Sticky note à savoir StickyNote.sn -

  • Tout d'abord, ouvrez le fichier de preuves et recherchez tous les fichiers StickyNote.snt.

  • Ensuite, analysez les métadonnées et le contenu du flux OLE et écrivez le contenu RTF dans des fichiers.

  • Enfin, créez un rapport CSV de ces métadonnées.

Code Python

Voyons comment utiliser le code Python à cette fin -

Tout d'abord, importez les bibliothèques Python suivantes -

from __future__ import print_function
from argparse import ArgumentParser

import unicodecsv as csv
import os
import StringIO

from utility.pytskutil import TSKUtil
import olefile

Ensuite, définissez une variable globale qui sera utilisée dans ce script -

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

Ensuite, nous devons fournir un argument pour le gestionnaire de ligne de commande. Notez qu'ici, il acceptera trois arguments - le premier est le chemin d'accès au fichier de preuves, le second est le type de fichier de preuves et le troisième est le chemin de sortie souhaité comme suit -

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)

Maintenant, nous allons définir main() fonction qui sera similaire au script précédent comme indiqué ci-dessous -

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

Maintenant, parcourons les fichiers résultants. Ensuite, nous appelleronsparse_snt_file() pour traiter le fichier, puis nous écrirons le fichier RTF avec le write_note_rtf() méthode comme suit -

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)

Ensuite, nous devons définir diverses fonctions utilisées dans ce script.

Tout d'abord nous définirons create_file_like_obj() fonction de lecture de la taille du fichier en prenant pytskobjet de fichier. Ensuite, nous définironsparse_snt_file() fonction qui acceptera l'objet de type fichier comme entrée et est utilisée pour lire et interpréter le fichier pense-bête.

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

Maintenant, créez un fichier RTF en définissant write_note_rtf() fonction comme suit

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

Maintenant, nous allons traduire le dictionnaire imbriqué en une liste plate de dictionnaires plus appropriés pour une feuille de calcul CSV. Cela se fera en définissantprep_note_report()fonction. Enfin, nous définironswrite_csv() fonction.

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)

Après avoir exécuté le script ci-dessus, nous obtiendrons les métadonnées du fichier Sticky Notes.

Fichiers de registre

Les fichiers de registre Windows contiennent de nombreux détails importants qui sont comme une mine d'informations pour un analyste médico-légal. Il s'agit d'une base de données hiérarchique qui contient des détails relatifs à la configuration du système d'exploitation, à l'activité des utilisateurs, à l'installation de logiciels, etc. Dans le script Python suivant, nous allons accéder aux informations de base communesSYSTEM et SOFTWARE urticaire.

Pour ce script Python, nous devons installer des modules tiers à savoir pytsk3, pyewf et registry. On peut utiliserpip pour les installer.

Nous pouvons suivre les étapes ci-dessous pour extraire les informations du registre Windows -

  • Tout d'abord, recherchez les ruches de registre à traiter par son nom et par son chemin.

  • Ensuite, nous ouvrons ces fichiers en utilisant les modules StringIO et Registry.

  • Enfin, nous devons traiter chaque ruche et imprimer les valeurs analysées sur la console pour interprétation.

Code Python

Voyons comment utiliser le code Python à cette fin -

Tout d'abord, importez les bibliothèques Python suivantes -

from __future__ import print_function
from argparse import ArgumentParser

import datetime
import StringIO
import struct

from utility.pytskutil import TSKUtil
from Registry import Registry

Maintenant, fournissez un argument pour le gestionnaire de ligne de commande. Ici, il acceptera deux arguments - le premier est le chemin d'accès au fichier de preuves, le second est le type de fichier de preuves, comme indiqué ci-dessous -

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)

Maintenant nous allons définir main() fonction de recherche SYSTEM et SOFTWARE ruches à l'intérieur /Windows/System32/config dossier comme suit -

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)

Maintenant, définissez la fonction d'ouverture du fichier de registre. Pour cela, nous devons collecter la taille du fichier à partir depytsk métadonnées comme suit -

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)

Maintenant, avec l'aide de la méthode suivante, nous pouvons traiter SYSTEM> ruche -

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

Maintenant, nous devons définir une fonction pour les entiers interprétés en date et heure formatées comme suit -

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

Maintenant, avec l'aide de la méthode suivante, nous pouvons traiter SOFTWARE ruche -

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

Après avoir exécuté le script ci-dessus, nous obtiendrons les métadonnées stockées dans les fichiers du registre Windows.