Python Digital Forensics - Guide rapide

Ce chapitre vous donnera une introduction à ce qu'est la criminalistique numérique et à son examen historique. Vous comprendrez également où vous pouvez appliquer la criminalistique numérique dans la vie réelle et ses limites.

Qu'est-ce que la criminalistique numérique?

La criminalistique numérique peut être définie comme la branche de la science médico-légale qui analyse, examine, identifie et récupère les preuves numériques résidant sur les appareils électroniques. Il est couramment utilisé pour le droit pénal et les enquêtes privées.

Par exemple, vous pouvez compter sur des preuves d'extrait par la criminalistique numérique au cas où quelqu'un vole des données sur un appareil électronique.

Bref examen historique de la criminalistique numérique

L'histoire des crimes informatiques et la revue historique de la criminalistique numérique sont expliquées dans cette section comme indiqué ci-dessous -

1970-1980: premier crime informatique

Avant cette décennie, aucun crime informatique n'avait été reconnu. Cependant, si cela est censé se produire, les lois alors en vigueur les traitent. Plus tard, en 1978, le premier crime informatique a été reconnu dans le Florida Computer Crime Act, qui comprenait une législation contre la modification non autorisée ou la suppression de données sur un système informatique. Mais au fil du temps, en raison des progrès de la technologie, l'éventail des délits informatiques commis s'est également accru. Pour faire face aux crimes liés au droit d'auteur, à la vie privée et à la pornographie juvénile, diverses autres lois ont été adoptées.

Années 1980-1990: Décennie du développement

Cette décennie a été la décennie du développement de la criminalistique numérique, tout cela à cause de la toute première enquête (1986) dans laquelle Cliff Stoll a suivi le pirate informatique nommé Markus Hess. Au cours de cette période, deux types de disciplines de criminalistique numérique se sont développés - le premier à l'aide d'outils et de techniques ad hoc développés par des praticiens qui l'ont pris comme un passe-temps, tandis que le second a été développé par la communauté scientifique. En 1992, le terme“Computer Forensics”a été utilisé dans la littérature académique.

Années 2000-2010: Décennie de la normalisation

Après le développement de la criminalistique numérique à un certain niveau, il était nécessaire d'établir des normes spécifiques qui peuvent être suivies lors de la réalisation d'enquêtes. En conséquence, divers organismes et organismes scientifiques ont publié des lignes directrices pour la criminalistique numérique. En 2002, le Groupe de travail scientifique sur les preuves numériques (SWGDE) a publié un article intitulé «Meilleures pratiques pour l'informatique légale». Une autre plume dans la casquette était un traité international dirigé par l'Europe, à savoir“The Convention on Cybercrime”a été signé par 43 nations et ratifié par 16 nations. Même après de telles normes, il est toujours nécessaire de résoudre certains problèmes qui ont été identifiés par les chercheurs.

Processus de criminalistique numérique

Depuis le premier crime informatique de l'histoire en 1978, les activités criminelles numériques ont considérablement augmenté. En raison de cet incrément, il est nécessaire de les traiter de manière structurée. En 1984, un processus formalisé a été mis en place et, par la suite, un grand nombre de processus d'enquête judiciaire informatique nouveaux et améliorés ont été développés.

Un processus d'enquête informatique judiciaire comprend trois phases principales, comme expliqué ci-dessous -

Phase 1: Acquisition ou imagerie des expositions

La première phase de la criminalistique numérique consiste à enregistrer l'état du système numérique afin qu'il puisse être analysé plus tard. C'est très similaire à la prise de photographies, d'échantillons de sang, etc. sur une scène de crime. Par exemple, il s'agit de capturer une image de zones allouées et non allouées d'un disque dur ou d'une RAM.

Phase 2: Analyse

L'entrée de cette phase est la donnée acquise lors de la phase d'acquisition. Ici, ces données ont été examinées pour identifier les preuves. Cette phase donne trois types de preuves comme suit -

  • Inculpatory evidences - Ces preuves soutiennent une histoire donnée.

  • Exculpatory evidences - Ces preuves contredisent une histoire donnée.

  • Evidence of tampering- Ces preuves montrent que le système a été tempéré pour éviter l'identification. Il comprend l'examen des fichiers et du contenu du répertoire pour récupérer les fichiers supprimés.

Phase 3: Présentation ou rapport

Comme son nom l'indique, cette phase présente la conclusion et les preuves correspondantes de l'enquête.

Applications de la criminalistique numérique

La criminalistique numérique traite de la collecte, de l'analyse et de la préservation des preuves contenues dans tout appareil numérique. L'utilisation de la criminalistique numérique dépend de l'application. Comme mentionné précédemment, il est principalement utilisé dans les deux applications suivantes -

Loi criminelle

En droit pénal, les preuves sont collectées pour étayer ou opposer une hypothèse devant le tribunal. Les procédures médico-légales sont très similaires à celles utilisées dans les enquêtes criminelles, mais avec des exigences et des limites juridiques différentes.

Enquête privée

Le monde de l'entreprise utilise principalement la criminalistique numérique pour les enquêtes privées. Il est utilisé lorsque les entreprises soupçonnent que les employés peuvent effectuer une activité illégale sur leurs ordinateurs qui est contraire à la politique de l'entreprise. La criminalistique numérique constitue l'un des meilleurs itinéraires à emprunter pour une entreprise ou une personne lorsqu'elle enquête sur une personne pour inconduite numérique.

Branches de la criminalistique numérique

Le crime numérique ne se limite pas aux seuls ordinateurs, mais les pirates et les criminels utilisent également de petits appareils numériques tels que des tablettes, des téléphones intelligents, etc. à très grande échelle. Certains appareils ont une mémoire volatile, tandis que d'autres ont une mémoire non volatile. Par conséquent, selon le type d'appareils, la criminalistique numérique a les branches suivantes -

Informatique légale

Cette branche de la criminalistique numérique traite des ordinateurs, des systèmes embarqués et des mémoires statiques telles que les clés USB. Une large gamme d'informations, des journaux aux fichiers réels sur le lecteur, peut être étudiée par la criminalistique informatique.

Forensics mobiles

Cela concerne l'enquête sur les données des appareils mobiles. Cette branche est différente de la criminalistique informatique en ce sens que les appareils mobiles ont un système de communication intégré qui est utile pour fournir des informations utiles liées à l'emplacement.

Forensics réseau

Il s'agit de la surveillance et de l'analyse du trafic du réseau informatique, à la fois local et WAN (réseau étendu) à des fins de collecte d'informations, de collecte de preuves ou de détection d'intrusions.

Forensics de base de données

Cette branche de la criminalistique numérique traite de l'étude médico-légale des bases de données et de leurs métadonnées.

Compétences requises pour les enquêtes de criminalistique numérique

Les examinateurs de criminalistique numérique aident à suivre les pirates informatiques, à récupérer les données volées, à suivre les attaques informatiques jusqu'à leur source et à participer à d'autres types d'enquêtes impliquant des ordinateurs. Certaines des compétences clés requises pour devenir examinateur médico-légal numérique, comme indiqué ci-dessous -

Capacités de réflexion exceptionnelles

Un enquêteur en criminalistique numérique doit être un penseur exceptionnel et doit être capable d'appliquer différents outils et méthodologies sur une mission particulière pour obtenir le résultat. Il doit être capable de trouver différents modèles et d'établir des corrélations entre eux.

Compétences techniques

Un examinateur médico-légal numérique doit avoir de bonnes compétences technologiques car ce domaine nécessite la connaissance du réseau, comment le système numérique interagit.

Passionné de cybersécurité

Parce que le domaine de la criminalistique numérique consiste à résoudre les cyber-crimes et qu'il s'agit d'une tâche fastidieuse, il faut beaucoup de passion pour que quelqu'un devienne un enquêteur médico-légal numérique.

Compétences en communication

De bonnes compétences en communication sont indispensables pour se coordonner avec diverses équipes et pour extraire les données ou informations manquantes.

Habile dans la rédaction de rapports

Après la mise en œuvre réussie de l'acquisition et de l'analyse, un examinateur médico-légal numérique doit mentionner toutes les conclusions du rapport final et de la présentation. Par conséquent, il / elle doit avoir de bonnes compétences en rédaction de rapports et une attention aux détails.

Limites

L'enquête médico-légale numérique offre certaines limites, comme indiqué ici -

Besoin de produire des preuves convaincantes

L'un des principaux inconvénients de l'enquête médico-légale numérique est que l'examinateur doit se conformer aux normes requises pour la preuve devant le tribunal, car les données peuvent être facilement falsifiées. D'autre part, l'investigateur médico-légal doit avoir une connaissance complète des exigences légales, du traitement des preuves et des procédures de documentation pour présenter des preuves convaincantes devant le tribunal.

Outils d'enquête

L'efficacité de l'enquête numérique repose entièrement sur l'expertise de l'examinateur médico-légal numérique et la sélection de l'outil d'enquête approprié. Si l'outil utilisé n'est pas conforme aux normes spécifiées, alors devant le tribunal, les preuves peuvent être rejetées par le juge.

Manque de connaissances techniques du public

Une autre limitation est que certaines personnes ne sont pas complètement familiarisées avec la criminalistique informatique; par conséquent, de nombreuses personnes ne comprennent pas ce domaine. Les enquêteurs doivent s'assurer de communiquer leurs conclusions aux tribunaux de manière à aider chacun à comprendre les résultats.

Coût

Produire des preuves numériques et les préserver est très coûteux. Par conséquent, ce processus peut ne pas être choisi par de nombreuses personnes qui ne peuvent pas se permettre le coût.

Dans le chapitre précédent, nous avons appris les bases de la criminalistique numérique, ses avantages et ses limites. Ce chapitre vous mettra à l'aise avec Python, l'outil essentiel que nous utilisons dans cette enquête d'investigation numérique.

Pourquoi Python pour la criminalistique numérique?

Python est un langage de programmation populaire et est utilisé comme outil pour la cybersécurité, les tests de pénétration ainsi que les enquêtes médico-légales numériques. Lorsque vous choisissez Python comme outil de criminalistique numérique, vous n'avez besoin d'aucun autre logiciel tiers pour accomplir la tâche.

Certaines des caractéristiques uniques du langage de programmation Python qui en font un bon choix pour les projets de criminalistique numérique sont indiquées ci-dessous -

  • Simplicity of Syntax - La syntaxe de Python est simple par rapport à d'autres langages, ce qui facilite l'apprentissage et la mise en œuvre pour la criminalistique numérique.

  • Comprehensive inbuilt modules - Les modules intégrés complets de Python sont une excellente aide pour effectuer une enquête médico-légale numérique complète.

  • Help and Support - En tant que langage de programmation open source, Python bénéficie d'un excellent support de la communauté des développeurs et des utilisateurs.

Caractéristiques de Python

Python, étant un langage de script de haut niveau, interprété, interactif et orienté objet, offre les fonctionnalités suivantes -

  • Easy to Learn - Python est un langage convivial et facile à apprendre pour les développeurs, car il contient moins de mots-clés et la structure la plus simple.

  • Expressive and Easy to read- Le langage Python est de nature expressive; par conséquent, son code est plus compréhensible et lisible.

  • Cross-platform Compatible - Python est un langage compatible multiplateforme, ce qui signifie qu'il peut fonctionner efficacement sur diverses plates-formes telles que UNIX, Windows et Macintosh.

  • Interactive Mode Programming - Nous pouvons faire des tests interactifs et déboguer du code car Python prend en charge un mode interactif pour la programmation.

  • Provides Various Modules and Functions - Python a une grande bibliothèque standard qui nous permet d'utiliser un riche ensemble de modules et de fonctions pour notre script.

  • Supports Dynamic Type Checking - Python prend en charge la vérification de type dynamique et fournit des types de données dynamiques de très haut niveau.

  • GUI Programming - Python prend en charge la programmation GUI pour développer des interfaces utilisateur graphiques.

  • Integration with other programming languages - Python peut être facilement intégré à d'autres langages de programmation comme C, C ++, JAVA, etc.

Installer Python

La distribution Python est disponible pour diverses plates-formes telles que Windows, UNIX, Linux et Mac. Nous devons seulement télécharger le code binaire selon notre plate-forme. Dans le cas où le code binaire pour une plate-forme n'est pas disponible, nous devons avoir un compilateur C afin que le code source puisse être compilé manuellement.

Cette section vous familiarisera avec l'installation de Python sur différentes plates-formes -

Installation de Python sous Unix et Linux

Vous pouvez suivre les étapes ci-dessous pour installer Python sur une machine Unix / Linux.

Step 1- Ouvrez un navigateur Web. Tapez et entrez www.python.org/downloads/

Step 2 - Téléchargez le code source zippé disponible pour Unix / Linux.

Step 3 - Extrayez les fichiers compressés téléchargés.

Step 4 - Si vous souhaitez personnaliser certaines options, vous pouvez modifier le Modules/Setup file.

Step 5 - Utilisez les commandes suivantes pour terminer l'installation -

run ./configure script
make
make install

Une fois que vous avez terminé avec succès les étapes ci-dessus, Python sera installé à son emplacement standard /usr/local/bin et ses bibliothèques à /usr/local/lib/pythonXX où XX est la version de Python.

Installation de Python sous Windows

Nous pouvons suivre les étapes simples suivantes pour installer Python sur une machine Windows.

Step 1- Ouvrez un navigateur Web. Tapez et entrez www.python.org/downloads/

Step 2 - Téléchargez le programme d'installation de Windows python-XYZ.msi fichier, où XYZ est la version que nous devons installer.

Step 3 - Exécutez maintenant ce fichier MSI après avoir enregistré le fichier d'installation sur votre machine locale.

Step 4 - Exécutez le fichier téléchargé qui fera apparaître l'assistant d'installation de Python.

Installation de Python sur Macintosh

Pour installer Python 3 sur Mac OS X, nous devons utiliser un programme d'installation de package nommé Homebrew.

Vous pouvez utiliser la commande suivante pour installer Homebrew, dans le cas où vous ne l'avez pas sur votre système -

$ ruby -e "$(curl -fsSL
https://raw.githubusercontent.com/Homebrew/install/master/install)"

Si vous devez mettre à jour le gestionnaire de packages, vous pouvez le faire à l'aide de la commande suivante -

$ brew update

Maintenant, utilisez la commande suivante pour installer Python3 sur votre système -

$ brew install python3

Définition du CHEMIN

Nous devons définir le chemin pour l'installation de Python et cela diffère avec les plates-formes telles que UNIX, WINDOWS ou MAC.

Paramétrage du chemin sous Unix / Linux

Vous pouvez utiliser les options suivantes pour définir le chemin sous Unix / Linux -

  • If using csh shell - Type setenv PATH "$PATH:/usr/local/bin/python" puis appuyez sur Entrée.

  • If using bash shell (Linux) - Type export ATH="$PATH:/usr/local/bin/python" puis appuyez sur Entrée.

  • If using sh or ksh shell - Type PATH="$PATH:/usr/local/bin/python" puis appuyez sur Entrée.

Paramètre de chemin sous Windows

Type path %path%;C:\Python à l'invite de commande, puis appuyez sur Entrée.

Exécuter Python

Vous pouvez choisir l'une des trois méthodes suivantes pour démarrer l'interpréteur Python -

Méthode 1: Utilisation de l'interpréteur interactif

Un système qui fournit un interpréteur de ligne de commande ou un shell peut facilement être utilisé pour démarrer Python. Par exemple, Unix, DOS etc. Vous pouvez suivre les étapes ci-dessous pour commencer à coder dans l'interpréteur interactif -

Step 1 - Entrez python sur la ligne de commande.

Step 2 - Commencez immédiatement le codage dans l'interpréteur interactif en utilisant les commandes ci-dessous -

$python # Unix/Linux
or
python% # Unix/Linux
or
C:> python # Windows/DOS

Méthode 2: utilisation du script à partir de la ligne de commande

Nous pouvons également exécuter un script Python en ligne de commande en appelant l'interpréteur sur notre application. Vous pouvez utiliser les commandes ci-dessous -

$python script.py # Unix/Linux
or
python% script.py # Unix/Linux
or
C: >python script.py # Windows/DOS

Méthode 3: Environnement de développement intégré

Si un système a une application GUI qui prend en charge Python, Python peut être exécuté à partir de cet environnement GUI. Certains des IDE pour diverses plates-formes sont donnés ci-dessous -

  • Unix IDE - UNIX a IDLE IDE pour Python.

  • Windows IDE - Windows a PythonWin, la première interface Windows pour Python avec GUI.

  • Macintosh IDE - Macintosh a IDLE IDE qui est disponible sur le site Web principal, téléchargeable sous forme de fichiers MacBinary ou BinHex'd.

Maintenant que vous êtes à l'aise avec l'installation et l'exécution des commandes Python sur votre système local, passons aux concepts d'investigation en détail. Ce chapitre expliquera divers concepts impliqués dans le traitement des artefacts dans la criminalistique numérique Python.

Besoin de création de rapport

Le processus de criminalistique numérique comprend la production de rapports comme troisième phase. C'est l'une des parties les plus importantes du processus médico-légal numérique. La création de rapport est nécessaire pour les raisons suivantes -

  • C'est le document dans lequel l'examinateur médico-légal numérique décrit le processus d'enquête et ses conclusions.

  • Un bon rapport médico-légal numérique peut être référencé par un autre examinateur pour obtenir le même résultat avec les mêmes référentiels.

  • Il s'agit d'un document technique et scientifique qui contient des faits trouvés dans les 1 et 0 des preuves numériques.

Directives générales pour la création de rapports

Les rapports sont rédigés pour fournir des informations au lecteur et doivent commencer par une base solide. les enquêteurs peuvent avoir des difficultés à présenter efficacement leurs conclusions si le rapport est préparé sans certaines lignes directrices ou normes générales. Certaines directives générales qui doivent être suivies lors de la création de rapports médico-légaux numériques sont données ci-dessous -

  • Summary - Le rapport doit contenir un bref résumé des informations afin que le lecteur puisse déterminer l'objectif du rapport.

  • Tools used - Nous devons mentionner les outils qui ont été utilisés pour mener à bien le processus de criminalistique numérique, y compris leur finalité.

  • Repository - Supposons que nous ayons enquêté sur l'ordinateur de quelqu'un, puis le résumé des preuves et l'analyse du matériel pertinent comme le courrier électronique, l'historique de recherche interne, etc., puis ils doivent être inclus dans le rapport afin que le cas puisse être clairement présenté.

  • Recommendations for counsel - Le rapport doit contenir les recommandations aux avocats de poursuivre ou de cesser l'enquête sur la base des conclusions du rapport.

Création de différents types de rapports

Dans la section ci-dessus, nous avons appris l'importance du rapport dans la criminalistique numérique ainsi que les lignes directrices pour le créer. Certains des formats en Python pour créer différents types de rapports sont décrits ci-dessous -

Rapports CSV

L'un des formats de sortie les plus courants des rapports est un rapport de feuille de calcul CSV. Vous pouvez créer un CSV pour créer un rapport des données traitées à l'aide du code Python comme indiqué ci-dessous -

Tout d'abord, importez des bibliothèques utiles pour écrire la feuille de calcul -

from __future__ import print_function
import csv
import os
import sys

Maintenant, appelez la méthode suivante -

Write_csv(TEST_DATA_LIST, ["Name", "Age", "City", "Job description"], os.getcwd())

Nous utilisons la variable globale suivante pour représenter des exemples de types de données -

TEST_DATA_LIST = [["Ram", 32, Bhopal, Manager], 
   ["Raman", 42, Indore, Engg.],
   ["Mohan", 25, Chandigarh, HR], 
   ["Parkash", 45, Delhi, IT]]

Ensuite, définissons la méthode pour poursuivre les opérations. Nous ouvrons le fichier en mode «w» et définissons l'argument du mot-clé de nouvelle ligne sur une chaîne vide.

def Write_csv(data, header, output_directory, name = None):
   if name is None:
      name = "report1.csv"
   print("[+] Writing {} to {}".format(name, output_directory))
   
   with open(os.path.join(output_directory, name), "w", newline = "") as \ csvfile:
      writer = csv.writer(csvfile)
      writer.writerow(header)
      writer.writerow(data)

Si vous exécutez le script ci-dessus, vous obtiendrez les détails suivants stockés dans le fichier report1.csv.

Nom Âge Ville La désignation
RAM 32 Bhopal Managerh
Raman 42 Indore Engg
Mohan 25 Chandigarh HEURE
Parkash 45 Delhi IL

Rapports Excel

Un autre format de sortie courant des rapports est le rapport de feuille de calcul Excel (.xlsx). Nous pouvons créer un tableau et également tracer le graphique en utilisant Excel. Nous pouvons créer un rapport des données traitées au format Excel en utilisant le code Python comme indiqué ci-dessous -

Tout d'abord, importez le module XlsxWriter pour créer une feuille de calcul -

import xlsxwriter

Maintenant, créez un objet classeur. Pour cela, nous devons utiliser le constructeur Workbook ().

workbook = xlsxwriter.Workbook('report2.xlsx')

Maintenant, créez une nouvelle feuille de calcul en utilisant le module add_worksheet ().

worksheet = workbook.add_worksheet()

Ensuite, écrivez les données suivantes dans la feuille de calcul -

report2 = (['Ram', 32, ‘Bhopal’],['Mohan',25, ‘Chandigarh’] ,['Parkash',45, ‘Delhi’])

row = 0
col = 0

Vous pouvez parcourir ces données et les écrire comme suit -

for item, cost in (a):
   worksheet.write(row, col, item)
   worksheet.write(row, col+1, cost)
   row + = 1

Maintenant, fermons ce fichier Excel en utilisant la méthode close ().

workbook.close()

Le script ci-dessus créera un fichier Excel nommé report2.xlsx contenant les données suivantes -

RAM 32 Bhopal
Mohan 25 Chandigarh
Parkash 45 Delhi

Médias d'acquisition d'enquête

Il est important pour un enquêteur de disposer des notes d'enquête détaillées pour rappeler avec précision les conclusions ou rassembler tous les éléments d'enquête. Une capture d'écran est très utile pour suivre les étapes d'une enquête particulière. Avec l'aide du code Python suivant, nous pouvons prendre la capture d'écran et la sauvegarder sur le disque dur pour une utilisation future.

Tout d'abord, installez le module Python nommé pyscreenshot en utilisant la commande suivante -

Pip install pyscreenshot

Maintenant, importez les modules nécessaires comme indiqué -

import pyscreenshot as ImageGrab

Utilisez la ligne de code suivante pour obtenir la capture d'écran -

image = ImageGrab.grab()

Utilisez la ligne de code suivante pour enregistrer la capture d'écran à l'emplacement donné -

image.save('d:/image123.png')

Maintenant, si vous souhaitez afficher la capture d'écran sous forme de graphique, vous pouvez utiliser le code Python suivant -

import numpy as np
import matplotlib.pyplot as plt
import pyscreenshot as ImageGrab
imageg = ImageGrab.grab()
plt.imshow(image, cmap='gray', interpolation='bilinear')
plt.show()

Ce chapitre explique la criminalistique numérique Python sur les appareils mobiles et les concepts impliqués.

introduction

La criminalistique des appareils mobiles est la branche de la criminalistique numérique qui traite de l'acquisition et de l'analyse d'appareils mobiles pour récupérer des preuves numériques d'intérêt pour les enquêtes. Cette branche est différente de la criminalistique informatique car les appareils mobiles ont un système de communication intégré qui est utile pour fournir des informations utiles liées à l'emplacement.

Bien que l'utilisation des smartphones augmente de jour en jour dans la criminalistique numérique, elle est toujours considérée comme non standard en raison de son hétérogénéité. D'autre part, le matériel informatique, tel que le disque dur, est considéré comme standard et développé comme une discipline stable également. Dans l'industrie de la criminalistique numérique, il y a beaucoup de débats sur les techniques utilisées pour les appareils non standard, ayant des preuves transitoires, comme les smartphones.

Artefacts extractibles à partir d'appareils mobiles

Les appareils mobiles modernes possèdent beaucoup d'informations numériques par rapport aux téléphones plus anciens n'ayant qu'un journal des appels ou des messages SMS. Ainsi, les appareils mobiles peuvent fournir aux enquêteurs de nombreuses informations sur leur utilisateur. Certains artefacts qui peuvent être extraits d'appareils mobiles sont mentionnés ci-dessous -

  • Messages - Ce sont les artefacts utiles qui peuvent révéler l'état d'esprit du propriétaire et peuvent même donner des informations inconnues à l'enquêteur.

  • Location History- Les données d'historique de localisation sont un artefact utile qui peut être utilisé par les enquêteurs pour valider l'emplacement particulier d'une personne.

  • Applications Installed - En accédant au type d'applications installées, l'enquêteur obtient un aperçu des habitudes et de la pensée de l'utilisateur mobile.

Sources et traitement des preuves en Python

Les smartphones ont des bases de données SQLite et des fichiers PLIST comme principales sources de preuves. Dans cette section, nous allons traiter les sources des preuves en python.

Analyse des fichiers PLIST

Un PLIST (Property List) est un format flexible et pratique pour stocker des données d'application, en particulier sur les appareils iPhone. Il utilise l'extension.plist. Ce type de fichiers est utilisé pour stocker des informations sur les bundles et les applications. Il peut se présenter sous deux formats:XML et binary. Le code Python suivant ouvrira et lira le fichier PLIST. Notez qu'avant de procéder à cela, nous devons créer notre propreInfo.plist fichier.

Tout d'abord, installez une bibliothèque tierce nommée biplist par la commande suivante -

Pip install biplist

Maintenant, importez quelques bibliothèques utiles pour traiter les fichiers plist -

import biplist
import os
import sys

Maintenant, utilisez la commande suivante sous la méthode main peut être utilisée pour lire le fichier plist dans une variable -

def main(plist):
   try:
      data = biplist.readPlist(plist)
   except (biplist.InvalidPlistException,biplist.NotBinaryPlistException) as e:
print("[-] Invalid PLIST file - unable to be opened by biplist")
sys.exit(1)

Maintenant, nous pouvons soit lire les données sur la console, soit les imprimer directement, à partir de cette variable.

Bases de données SQLite

SQLite sert de référentiel de données principal sur les appareils mobiles. SQLite est une bibliothèque en processus qui implémente un moteur de base de données SQL transactionnel autonome, sans serveur et sans configuration. C'est une base de données, qui est configurée à zéro, vous n'avez pas besoin de la configurer dans votre système, contrairement à d'autres bases de données.

Si vous êtes novice ou que vous ne connaissez pas les bases de données SQLite, vous pouvez suivre le lien www.tutorialspoint.com/sqlite/index.htm De plus, vous pouvez suivre le lien www.tutorialspoint.com/sqlite/sqlite_python.htm au cas où vous le souhaiteriez entrer dans les détails de SQLite avec Python.

Au cours de la criminalistique mobile, nous pouvons interagir avec le sms.db fichier d'un appareil mobile et peut extraire des informations précieuses de messagetable. Python a une bibliothèque intégrée nomméesqlite3pour se connecter à la base de données SQLite. Vous pouvez importer le même avec la commande suivante -

import sqlite3

Maintenant, avec l'aide de la commande suivante, nous pouvons nous connecter à la base de données, disons sms.db en cas d'appareils mobiles -

Conn = sqlite3.connect(‘sms.db’)
C = conn.cursor()

Ici, C est l'objet curseur à l'aide duquel nous pouvons interagir avec la base de données.

Maintenant, supposons que si nous voulons exécuter une commande particulière, disons pour obtenir les détails du abc table, cela peut être fait à l'aide de la commande suivante -

c.execute(“Select * from abc”)
c.close()

Le résultat de la commande ci-dessus serait stocké dans le cursorobjet. De même, nous pouvons utiliserfetchall() méthode pour vider le résultat dans une variable que nous pouvons manipuler.

Nous pouvons utiliser la commande suivante pour obtenir les données des noms de colonne de la table de messages dans sms.db -

c.execute(“pragma table_info(message)”)
table_data = c.fetchall()
columns = [x[1] for x in table_data

Notez qu'ici, nous utilisons la commande SQLite PRAGMA qui est une commande spéciale à utiliser pour contrôler diverses variables d'environnement et indicateurs d'état dans l'environnement SQLite. Dans la commande ci-dessus, lefetchall()La méthode renvoie un tuple de résultats. Le nom de chaque colonne est stocké dans le premier index de chaque tuple.

Maintenant, à l'aide de la commande suivante, nous pouvons interroger la table pour toutes ses données et les stocker dans la variable nommée data_msg -

c.execute(“Select * from message”)
data_msg = c.fetchall()

La commande ci-dessus stockera les données dans la variable et nous pouvons également écrire les données ci-dessus dans un fichier CSV en utilisant csv.writer() méthode.

Sauvegardes iTunes

La criminalistique mobile iPhone peut être effectuée sur les sauvegardes effectuées par iTunes. Les examinateurs légistes s'appuient sur l'analyse des sauvegardes logiques iPhone acquises via iTunes. Le protocole AFC (Apple File Connection) est utilisé par iTunes pour effectuer la sauvegarde. En outre, le processus de sauvegarde ne modifie rien sur l'iPhone à l'exception des enregistrements de clé de dépôt.

Maintenant, la question se pose de savoir pourquoi il est important pour un expert en criminalistique numérique de comprendre les techniques sur les sauvegardes iTunes? C'est important au cas où nous aurions accès directement à l'ordinateur du suspect au lieu de l'iPhone, car lorsqu'un ordinateur est utilisé pour se synchroniser avec l'iPhone, la plupart des informations sur l'iPhone sont susceptibles d'être sauvegardées sur l'ordinateur.

Processus de sauvegarde et son emplacement

Chaque fois qu'un produit Apple est sauvegardé sur l'ordinateur, il est synchronisé avec iTunes et il y aura un dossier spécifique avec l'identifiant unique de l'appareil. Dans le dernier format de sauvegarde, les fichiers sont stockés dans des sous-dossiers contenant les deux premiers caractères hexadécimaux du nom de fichier. À partir de ces fichiers de sauvegarde, il existe des fichiers tels que info.plist qui sont utiles avec la base de données nommée Manifest.db. Le tableau suivant montre les emplacements de sauvegarde, qui varient selon les systèmes d'exploitation des sauvegardes iTunes -

OS Emplacement de sauvegarde
Win7 C: \ Users \ [nom d'utilisateur] \ AppData \ Roaming \ AppleComputer \ MobileSync \ Backup \
MAC OS X ~ / Bibliothèque / Application Suport / MobileSync / Backup /

Pour traiter la sauvegarde iTunes avec Python, nous devons d'abord identifier toutes les sauvegardes dans l'emplacement de sauvegarde selon notre système d'exploitation. Ensuite, nous allons parcourir chaque sauvegarde et lire la base de données Manifest.db.

Maintenant, avec l'aide du code Python suivant, nous pouvons faire de même -

Tout d'abord, importez les bibliothèques nécessaires comme suit -

from __future__ import print_function
import argparse
import logging
import os

from shutil import copyfile
import sqlite3
import sys
logger = logging.getLogger(__name__)

Maintenant, fournissez deux arguments de position à savoir INPUT_DIR et OUTPUT_DIR qui représente la sauvegarde iTunes et le dossier de sortie souhaité -

if __name__ == "__main__":
   parser.add_argument("INPUT_DIR",help = "Location of folder containing iOS backups, ""e.g. ~\Library\Application Support\MobileSync\Backup folder")
   parser.add_argument("OUTPUT_DIR", help = "Output Directory")
   parser.add_argument("-l", help = "Log file path",default = __file__[:-2] + "log")
   parser.add_argument("-v", help = "Increase verbosity",action = "store_true") args = parser.parse_args()

Maintenant, configurez le journal comme suit -

if args.v:
   logger.setLevel(logging.DEBUG)
else:
   logger.setLevel(logging.INFO)

Maintenant, configurez le format de message pour ce journal comme suit -

msg_fmt = logging.Formatter("%(asctime)-15s %(funcName)-13s""%(levelname)-8s %(message)s")
strhndl = logging.StreamHandler(sys.stderr)
strhndl.setFormatter(fmt = msg_fmt)

fhndl = logging.FileHandler(args.l, mode = 'a')
fhndl.setFormatter(fmt = msg_fmt)

logger.addHandler(strhndl)
logger.addHandler(fhndl)
logger.info("Starting iBackup Visualizer")
logger.debug("Supplied arguments: {}".format(" ".join(sys.argv[1:])))
logger.debug("System: " + sys.platform)
logger.debug("Python Version: " + sys.version)

La ligne de code suivante créera les dossiers nécessaires pour le répertoire de sortie souhaité en utilisant os.makedirs() fonction -

if not os.path.exists(args.OUTPUT_DIR):
   os.makedirs(args.OUTPUT_DIR)

Maintenant, passez les répertoires d'entrée et de sortie fournis à la fonction main () comme suit -

if os.path.exists(args.INPUT_DIR) and os.path.isdir(args.INPUT_DIR):
   main(args.INPUT_DIR, args.OUTPUT_DIR)
else:
   logger.error("Supplied input directory does not exist or is not ""a directory")
   sys.exit(1)

Maintenant écris main() fonction qui appellera en outre backup_summary() fonction pour identifier toutes les sauvegardes présentes dans le dossier d'entrée -

def main(in_dir, out_dir):
   backups = backup_summary(in_dir)
def backup_summary(in_dir):
   logger.info("Identifying all iOS backups in {}".format(in_dir))
   root = os.listdir(in_dir)
   backups = {}
   
   for x in root:
      temp_dir = os.path.join(in_dir, x)
      if os.path.isdir(temp_dir) and len(x) == 40:
         num_files = 0
         size = 0
         
         for root, subdir, files in os.walk(temp_dir):
            num_files += len(files)
            size += sum(os.path.getsize(os.path.join(root, name))
               for name in files)
         backups[x] = [temp_dir, num_files, size]
   return backups

Maintenant, imprimez le résumé de chaque sauvegarde sur la console comme suit -

print("Backup Summary")
print("=" * 20)

if len(backups) > 0:
   for i, b in enumerate(backups):
      print("Backup No.: {} \n""Backup Dev. Name: {} \n""# Files: {} \n""Backup Size (Bytes): {}\n".format(i, b, backups[b][1], backups[b][2]))

Maintenant, videz le contenu du fichier Manifest.db dans la variable nommée db_items.

try:
   db_items = process_manifest(backups[b][0])
   except IOError:
      logger.warn("Non-iOS 10 backup encountered or " "invalid backup. Continuing to next backup.")
continue

Maintenant, définissons une fonction qui prendra le chemin du répertoire de la sauvegarde -

def process_manifest(backup):
   manifest = os.path.join(backup, "Manifest.db")
   
   if not os.path.exists(manifest):
      logger.error("Manifest DB not found in {}".format(manifest))
      raise IOError

Maintenant, en utilisant SQLite3, nous allons nous connecter à la base de données par le curseur nommé c -

c = conn.cursor()
items = {}

for row in c.execute("SELECT * from Files;"):
   items[row[0]] = [row[2], row[1], row[3]]
return items

create_files(in_dir, out_dir, b, db_items)
   print("=" * 20)
else:
   logger.warning("No valid backups found. The input directory should be
      " "the parent-directory immediately above the SHA-1 hash " "iOS device backups")
      sys.exit(2)

Maintenant, définissez le create_files() méthode comme suit -

def create_files(in_dir, out_dir, b, db_items):
   msg = "Copying Files for backup {} to {}".format(b, os.path.join(out_dir, b))
   logger.info(msg)

Maintenant, parcourez chaque clé dans le db_items dictionnaire -

for x, key in enumerate(db_items):
   if db_items[key][0] is None or db_items[key][0] == "":
      continue
   else:
      dirpath = os.path.join(out_dir, b,
os.path.dirname(db_items[key][0]))
   filepath = os.path.join(out_dir, b, db_items[key][0])
   
   if not os.path.exists(dirpath):
      os.makedirs(dirpath)
      original_dir = b + "/" + key[0:2] + "/" + key
   path = os.path.join(in_dir, original_dir)
   
   if os.path.exists(filepath):
      filepath = filepath + "_{}".format(x)

Maintenant, utilisez shutil.copyfile() méthode pour copier le fichier sauvegardé comme suit -

try:
   copyfile(path, filepath)
   except IOError:
      logger.debug("File not found in backup: {}".format(path))
         files_not_found += 1
   if files_not_found > 0:
      logger.warning("{} files listed in the Manifest.db not" "found in
backup".format(files_not_found))
   copyfile(os.path.join(in_dir, b, "Info.plist"), os.path.join(out_dir, b,
"Info.plist"))
   copyfile(os.path.join(in_dir, b, "Manifest.db"), os.path.join(out_dir, b,
"Manifest.db"))
   copyfile(os.path.join(in_dir, b, "Manifest.plist"), os.path.join(out_dir, b,
"Manifest.plist"))
   copyfile(os.path.join(in_dir, b, "Status.plist"),os.path.join(out_dir, b,
"Status.plist"))

Avec le script Python ci-dessus, nous pouvons obtenir la structure de fichiers de sauvegarde mise à jour dans notre dossier de sortie. On peut utiliserpycrypto bibliothèque python pour décrypter les sauvegardes.

Wifi

Les appareils mobiles peuvent être utilisés pour se connecter au monde extérieur en se connectant via des réseaux Wi-Fi disponibles partout. Parfois, l'appareil se connecte automatiquement à ces réseaux ouverts.

Dans le cas de l'iPhone, la liste des connexions Wi-Fi ouvertes avec lesquelles l'appareil s'est connecté est stockée dans un fichier PLIST nommé com.apple.wifi.plist. Ce fichier contiendra le SSID Wi-Fi, le BSSID et l'heure de connexion.

Nous devons extraire les détails Wi-Fi du rapport XML standard Cellebrite à l'aide de Python. Pour cela, nous devons utiliser l'API du Wireless Geographic Logging Engine (WIGLE), une plate-forme populaire qui peut être utilisée pour trouver l'emplacement d'un appareil à l'aide des noms de réseaux Wi-Fi.

Nous pouvons utiliser la bibliothèque Python nommée requestspour accéder à l'API depuis WIGLE. Il peut être installé comme suit -

pip install requests

API de WIGLE

Nous devons nous inscrire sur le site Web de WIGLE https://wigle.net/accountpour obtenir une API gratuite de WIGLE. Le script Python pour obtenir les informations sur la machine utilisateur et sa connexion via l'API de WIGEL est décrit ci-dessous -

Tout d'abord, importez les bibliothèques suivantes pour gérer différentes choses -

from __future__ import print_function

import argparse
import csv
import os
import sys
import xml.etree.ElementTree as ET
import requests

Maintenant, fournissez deux arguments de position à savoir INPUT_FILE et OUTPUT_CSV qui représentera respectivement le fichier d'entrée avec l'adresse MAC Wi-Fi et le fichier CSV de sortie souhaité -

if __name__ == "__main__":
   parser.add_argument("INPUT_FILE", help = "INPUT FILE with MAC Addresses")
   parser.add_argument("OUTPUT_CSV", help = "Output CSV File")
   parser.add_argument("-t", help = "Input type: Cellebrite XML report or TXT
file",choices = ('xml', 'txt'), default = "xml")
   parser.add_argument('--api', help = "Path to API key
   file",default = os.path.expanduser("~/.wigle_api"),
   type = argparse.FileType('r'))
   args = parser.parse_args()

Les lignes de code suivantes vérifieront maintenant si le fichier d'entrée existe et est un fichier. Sinon, il quitte le script -

if not os.path.exists(args.INPUT_FILE) or \ not os.path.isfile(args.INPUT_FILE):
   print("[-] {} does not exist or is not a
file".format(args.INPUT_FILE))
   sys.exit(1)
directory = os.path.dirname(args.OUTPUT_CSV)
if directory != '' and not os.path.exists(directory):
   os.makedirs(directory)
api_key = args.api.readline().strip().split(":")

Maintenant, passez l'argument à main comme suit -

main(args.INPUT_FILE, args.OUTPUT_CSV, args.t, api_key)
def main(in_file, out_csv, type, api_key):
   if type == 'xml':
      wifi = parse_xml(in_file)
   else:
      wifi = parse_txt(in_file)
query_wigle(wifi, out_csv, api_key)

Maintenant, nous allons analyser le fichier XML comme suit -

def parse_xml(xml_file):
   wifi = {}
   xmlns = "{http://pa.cellebrite.com/report/2.0}"
   print("[+] Opening {} report".format(xml_file))
   
   xml_tree = ET.parse(xml_file)
   print("[+] Parsing report for all connected WiFi addresses")
   
   root = xml_tree.getroot()

Maintenant, parcourez l'élément enfant de la racine comme suit -

for child in root.iter():
   if child.tag == xmlns + "model":
      if child.get("type") == "Location":
         for field in child.findall(xmlns + "field"):
            if field.get("name") == "TimeStamp":
               ts_value = field.find(xmlns + "value")
               try:
               ts = ts_value.text
               except AttributeError:
continue

Maintenant, nous allons vérifier que la chaîne 'ssid' est présente ou non dans le texte de la valeur -

if "SSID" in value.text:
   bssid, ssid = value.text.split("\t")
   bssid = bssid[7:]
   ssid = ssid[6:]

Maintenant, nous devons ajouter BSSID, SSID et horodatage au dictionnaire wifi comme suit -

if bssid in wifi.keys():

wifi[bssid]["Timestamps"].append(ts)
   wifi[bssid]["SSID"].append(ssid)
else:
   wifi[bssid] = {"Timestamps": [ts], "SSID":
[ssid],"Wigle": {}}
return wifi

L'analyseur de texte qui est beaucoup plus simple que l'analyseur XML est montré ci-dessous -

def parse_txt(txt_file):
   wifi = {}
   print("[+] Extracting MAC addresses from {}".format(txt_file))
   
   with open(txt_file) as mac_file:
      for line in mac_file:
         wifi[line.strip()] = {"Timestamps": ["N/A"], "SSID":
["N/A"],"Wigle": {}}
return wifi

Maintenant, utilisons le module de requêtes pour faire WIGLE APIappels et doivent passer au query_wigle() méthode -

def query_wigle(wifi_dictionary, out_csv, api_key):
   print("[+] Querying Wigle.net through Python API for {} "
"APs".format(len(wifi_dictionary)))
   for mac in wifi_dictionary:

   wigle_results = query_mac_addr(mac, api_key)
def query_mac_addr(mac_addr, api_key):

   query_url = "https://api.wigle.net/api/v2/network/search?" \
"onlymine = false&freenet = false&paynet = false" \ "&netid = {}".format(mac_addr)
   req = requests.get(query_url, auth = (api_key[0], api_key[1]))
   return req.json()

En fait, il y a une limite par jour pour les appels d'API WIGLE, si cette limite dépasse, il doit afficher une erreur comme suit -

try:
   if wigle_results["resultCount"] == 0:
      wifi_dictionary[mac]["Wigle"]["results"] = []
         continue
   else:
      wifi_dictionary[mac]["Wigle"] = wigle_results
except KeyError:
   if wigle_results["error"] == "too many queries today":
      print("[-] Wigle daily query limit exceeded")
      wifi_dictionary[mac]["Wigle"]["results"] = []
      continue
   else:
      print("[-] Other error encountered for " "address {}: {}".format(mac,
wigle_results['error']))
   wifi_dictionary[mac]["Wigle"]["results"] = []
   continue
prep_output(out_csv, wifi_dictionary)

Maintenant, nous allons utiliser prep_output() méthode pour aplatir le dictionnaire en morceaux facilement inscriptibles -

def prep_output(output, data):
   csv_data = {}
   google_map = https://www.google.com/maps/search/

Maintenant, accédez à toutes les données que nous avons collectées jusqu'à présent comme suit -

for x, mac in enumerate(data):
   for y, ts in enumerate(data[mac]["Timestamps"]):
      for z, result in enumerate(data[mac]["Wigle"]["results"]):
         shortres = data[mac]["Wigle"]["results"][z]
         g_map_url = "{}{},{}".format(google_map, shortres["trilat"],shortres["trilong"])

Maintenant, nous pouvons écrire la sortie dans un fichier CSV comme nous l'avons fait dans les scripts précédents de ce chapitre en utilisant write_csv() fonction.

Dans ce chapitre, nous apprendrons en détail comment étudier les métadonnées intégrées à l'aide de la criminalistique numérique Python.

introduction

Les métadonnées incorporées sont les informations sur les données stockées dans le même fichier contenant l'objet décrit par ces données. En d'autres termes, ce sont les informations sur un actif numérique stockées dans le fichier numérique lui-même. Il est toujours associé au fichier et ne peut jamais être séparé.

En cas de criminalistique numérique, nous ne pouvons pas extraire toutes les informations sur un fichier particulier. D'un autre côté, les métadonnées intégrées peuvent nous fournir des informations essentielles à l'enquête. Par exemple, les métadonnées d'un fichier texte peuvent contenir des informations sur l'auteur, sa longueur, la date de rédaction et même un bref résumé de ce document. Une image numérique peut inclure des métadonnées telles que la longueur de l'image, la vitesse d'obturation, etc.

Artefacts contenant des attributs de métadonnées et leur extraction

Dans cette section, nous découvrirons divers artefacts contenant des attributs de métadonnées et leur processus d'extraction à l'aide de Python.

Audio et vidéo

Ce sont les deux artefacts très courants qui ont les métadonnées incorporées. Ces métadonnées peuvent être extraites à des fins d'enquête.

Vous pouvez utiliser le script Python suivant pour extraire des attributs communs ou des métadonnées d'un fichier audio ou MP3 et d'une vidéo ou d'un fichier MP4.

Notez que pour ce script, nous devons installer une bibliothèque python tierce nommée mutagen qui nous permet d'extraire des métadonnées à partir de fichiers audio et vidéo. Il peut être installé à l'aide de la commande suivante -

pip install mutagen

Certaines des bibliothèques utiles que nous devons importer pour ce script Python sont les suivantes:

from __future__ import print_function

import argparse
import json
import mutagen

Le gestionnaire de ligne de commande prendra un argument qui représente le chemin vers les fichiers MP3 ou MP4. Ensuite, nous utiliseronsmutagen.file() méthode pour ouvrir un handle vers le fichier comme suit -

if __name__ == '__main__':
   parser = argparse.ArgumentParser('Python Metadata Extractor')
   parser.add_argument("AV_FILE", help="File to extract metadata from")
   args = parser.parse_args()
   av_file = mutagen.File(args.AV_FILE)
   file_ext = args.AV_FILE.rsplit('.', 1)[-1]
   
   if file_ext.lower() == 'mp3':
      handle_id3(av_file)
   elif file_ext.lower() == 'mp4':
      handle_mp4(av_file)

Maintenant, nous devons utiliser deux poignées, l'une pour extraire les données du MP3 et l'autre pour extraire les données du fichier MP4. Nous pouvons définir ces poignées comme suit -

def handle_id3(id3_file):
   id3_frames = {'TIT2': 'Title', 'TPE1': 'Artist', 'TALB': 'Album','TXXX':
      'Custom', 'TCON': 'Content Type', 'TDRL': 'Date released','COMM': 'Comments',
         'TDRC': 'Recording Date'}
   print("{:15} | {:15} | {:38} | {}".format("Frame", "Description","Text","Value"))
   print("-" * 85)
   
   for frames in id3_file.tags.values():
      frame_name = id3_frames.get(frames.FrameID, frames.FrameID)
      desc = getattr(frames, 'desc', "N/A")
      text = getattr(frames, 'text', ["N/A"])[0]
      value = getattr(frames, 'value', "N/A")
      
      if "date" in frame_name.lower():
         text = str(text)
      print("{:15} | {:15} | {:38} | {}".format(
         frame_name, desc, text, value))
def handle_mp4(mp4_file):
   cp_sym = u"\u00A9"
   qt_tag = {
      cp_sym + 'nam': 'Title', cp_sym + 'art': 'Artist',
      cp_sym + 'alb': 'Album', cp_sym + 'gen': 'Genre',
      'cpil': 'Compilation', cp_sym + 'day': 'Creation Date',
      'cnID': 'Apple Store Content ID', 'atID': 'Album Title ID',
      'plID': 'Playlist ID', 'geID': 'Genre ID', 'pcst': 'Podcast',
      'purl': 'Podcast URL', 'egid': 'Episode Global ID',
      'cmID': 'Camera ID', 'sfID': 'Apple Store Country',
      'desc': 'Description', 'ldes': 'Long Description'}
genre_ids = json.load(open('apple_genres.json'))

Maintenant, nous devons parcourir ce fichier MP4 comme suit -

print("{:22} | {}".format('Name', 'Value'))
print("-" * 40)

for name, value in mp4_file.tags.items():
   tag_name = qt_tag.get(name, name)
   
   if isinstance(value, list):
      value = "; ".join([str(x) for x in value])
   if name == 'geID':
      value = "{}: {}".format(
      value, genre_ids[str(value)].replace("|", " - "))
   print("{:22} | {}".format(tag_name, value))

Le script ci-dessus nous donnera des informations supplémentaires sur les fichiers MP3 et MP4.

Images

Les images peuvent contenir différents types de métadonnées en fonction de leur format de fichier. Cependant, la plupart des images intègrent des informations GPS. Nous pouvons extraire ces informations GPS en utilisant des bibliothèques Python tierces. Vous pouvez utiliser le script Python suivant pour faire de même -

Tout d'abord, téléchargez la bibliothèque Python tierce nommée Python Imaging Library (PIL) comme suit -

pip install pillow

Cela nous aidera à extraire les métadonnées des images.

Nous pouvons également écrire les détails GPS intégrés dans les images dans un fichier KML, mais pour cela, nous devons télécharger une bibliothèque Python tierce nommée simplekml comme suit -

pip install simplekml

Dans ce script, nous devons d'abord importer les bibliothèques suivantes -

from __future__ import print_function
import argparse

from PIL import Image
from PIL.ExifTags import TAGS

import simplekml
import sys

Maintenant, le gestionnaire de ligne de commande acceptera un argument de position qui représente essentiellement le chemin du fichier des photos.

parser = argparse.ArgumentParser('Metadata from images')
parser.add_argument('PICTURE_FILE', help = "Path to picture")
args = parser.parse_args()

Maintenant, nous devons spécifier les URL qui rempliront les informations de coordonnées. Les URL sontgmaps et open_maps. Nous avons également besoin d'une fonction pour convertir la coordonnée du tuple degré minute seconde (DMS), fournie par la bibliothèque PIL, en décimale. Cela peut être fait comme suit -

gmaps = "https://www.google.com/maps?q={},{}"
open_maps = "http://www.openstreetmap.org/?mlat={}&mlon={}"

def process_coords(coord):
   coord_deg = 0
   
   for count, values in enumerate(coord):
      coord_deg += (float(values[0]) / values[1]) / 60**count
   return coord_deg

Maintenant, nous allons utiliser image.open() pour ouvrir le fichier en tant qu'objet PIL.

img_file = Image.open(args.PICTURE_FILE)
exif_data = img_file._getexif()

if exif_data is None:
   print("No EXIF data found")
   sys.exit()
for name, value in exif_data.items():
   gps_tag = TAGS.get(name, name)
   if gps_tag is not 'GPSInfo':
      continue

Après avoir trouvé le GPSInfo tag, nous stockerons la référence GPS et traiterons les coordonnées avec le process_coords() méthode.

lat_ref = value[1] == u'N'
lat = process_coords(value[2])

if not lat_ref:
   lat = lat * -1
lon_ref = value[3] == u'E'
lon = process_coords(value[4])

if not lon_ref:
   lon = lon * -1

Maintenant, lancez kml objet de simplekml bibliothèque comme suit -

kml = simplekml.Kml()
kml.newpoint(name = args.PICTURE_FILE, coords = [(lon, lat)])
kml.save(args.PICTURE_FILE + ".kml")

Nous pouvons maintenant imprimer les coordonnées à partir des informations traitées comme suit -

print("GPS Coordinates: {}, {}".format(lat, lon))
print("Google Maps URL: {}".format(gmaps.format(lat, lon)))
print("OpenStreetMap URL: {}".format(open_maps.format(lat, lon)))
print("KML File {} created".format(args.PICTURE_FILE + ".kml"))

Documents PDF

Les documents PDF ont une grande variété de supports, notamment des images, du texte, des formulaires, etc. Lorsque nous extrayons des métadonnées intégrées dans des documents PDF, nous pouvons obtenir les données résultantes dans le format appelé Plateforme de métadonnées extensible (XMP). Nous pouvons extraire les métadonnées à l'aide du code Python suivant -

Tout d'abord, installez une bibliothèque Python tierce nommée PyPDF2pour lire les métadonnées stockées au format XMP. Il peut être installé comme suit -

pip install PyPDF2

Maintenant, importez les bibliothèques suivantes pour extraire les métadonnées des fichiers PDF -

from __future__ import print_function
from argparse import ArgumentParser, FileType

import datetime
from PyPDF2 import PdfFileReader
import sys

Désormais, le gestionnaire de ligne de commande acceptera un argument de position qui représente essentiellement le chemin du fichier PDF.

parser = argparse.ArgumentParser('Metadata from PDF')
parser.add_argument('PDF_FILE', help='Path to PDF file',type=FileType('rb'))
args = parser.parse_args()

Maintenant, nous pouvons utiliser getXmpMetadata() méthode pour fournir un objet contenant les métadonnées disponibles comme suit -

pdf_file = PdfFileReader(args.PDF_FILE)
xmpm = pdf_file.getXmpMetadata()

if xmpm is None:
   print("No XMP metadata found in document.")
   sys.exit()

On peut utiliser custom_print() méthode pour extraire et imprimer les valeurs pertinentes comme le titre, le créateur, le contributeur, etc. comme suit -

custom_print("Title: {}", xmpm.dc_title)
custom_print("Creator(s): {}", xmpm.dc_creator)
custom_print("Contributors: {}", xmpm.dc_contributor)
custom_print("Subject: {}", xmpm.dc_subject)
custom_print("Description: {}", xmpm.dc_description)
custom_print("Created: {}", xmpm.xmp_createDate)
custom_print("Modified: {}", xmpm.xmp_modifyDate)
custom_print("Event Dates: {}", xmpm.dc_date)

On peut aussi définir custom_print() méthode dans le cas où le PDF est créé à l'aide de plusieurs logiciels comme suit -

def custom_print(fmt_str, value):
   if isinstance(value, list):
      print(fmt_str.format(", ".join(value)))
   elif isinstance(value, dict):
      fmt_value = [":".join((k, v)) for k, v in value.items()]
      print(fmt_str.format(", ".join(value)))
   elif isinstance(value, str) or isinstance(value, bool):
      print(fmt_str.format(value))
   elif isinstance(value, bytes):
      print(fmt_str.format(value.decode()))
   elif isinstance(value, datetime.datetime):
      print(fmt_str.format(value.isoformat()))
   elif value is None:
      print(fmt_str.format("N/A"))
   else:
      print("warn: unhandled type {} found".format(type(value)))

Nous pouvons également extraire toute autre propriété personnalisée enregistrée par le logiciel comme suit -

if xmpm.custom_properties:
   print("Custom Properties:")
   
   for k, v in xmpm.custom_properties.items():
      print("\t{}: {}".format(k, v))

Le script ci-dessus lira le document PDF et imprimera les métadonnées stockées au format XMP, y compris certaines propriétés personnalisées stockées par le logiciel à l'aide duquel ce PDF a été créé.

Fichiers exécutables Windows

Parfois, nous pouvons rencontrer un fichier exécutable suspect ou non autorisé. Mais à des fins d'enquête, cela peut être utile en raison des métadonnées intégrées. Nous pouvons obtenir des informations telles que son emplacement, son objectif et d'autres attributs tels que le fabricant, la date de compilation, etc. Avec l'aide du script Python suivant, nous pouvons obtenir la date de compilation, des données utiles à partir des en-têtes et des symboles importés et exportés.

Pour cela, installez d'abord la bibliothèque Python tierce pefile. Cela peut être fait comme suit -

pip install pefile

Une fois que vous avez installé cela avec succès, importez les bibliothèques suivantes comme suit -

from __future__ import print_function

import argparse
from datetime import datetime
from pefile import PE

Désormais, le gestionnaire de ligne de commande acceptera un argument de position qui représente essentiellement le chemin du fichier exécutable. Vous pouvez également choisir le style de sortie, que vous en ayez besoin de manière détaillée et détaillée ou de manière simplifiée. Pour cela, vous devez donner un argument optionnel comme indiqué ci-dessous -

parser = argparse.ArgumentParser('Metadata from executable file')
parser.add_argument("EXE_FILE", help = "Path to exe file")
parser.add_argument("-v", "--verbose", help = "Increase verbosity of output",
action = 'store_true', default = False)
args = parser.parse_args()

Maintenant, nous allons charger le fichier exécutable d'entrée en utilisant la classe PE. Nous allons également vider les données exécutables dans un objet dictionnaire en utilisantdump_dict() méthode.

pe = PE(args.EXE_FILE)
ped = pe.dump_dict()

Nous pouvons extraire les métadonnées de fichier de base telles que la paternité intégrée, la version et l'heure de compilation en utilisant le code ci-dessous -

file_info = {}
for structure in pe.FileInfo:
   if structure.Key == b'StringFileInfo':
      for s_table in structure.StringTable:
         for key, value in s_table.entries.items():
            if value is None or len(value) == 0:
               value = "Unknown"
            file_info[key] = value
print("File Information: ")
print("==================")

for k, v in file_info.items():
   if isinstance(k, bytes):
      k = k.decode()
   if isinstance(v, bytes):
      v = v.decode()
   print("{}: {}".format(k, v))
comp_time = ped['FILE_HEADER']['TimeDateStamp']['Value']
comp_time = comp_time.split("[")[-1].strip("]")
time_stamp, timezone = comp_time.rsplit(" ", 1)
comp_time = datetime.strptime(time_stamp, "%a %b %d %H:%M:%S %Y")
print("Compiled on {} {}".format(comp_time, timezone.strip()))

Nous pouvons extraire les données utiles des en-têtes comme suit -

for section in ped['PE Sections']:
   print("Section '{}' at {}: {}/{} {}".format(
      section['Name']['Value'], hex(section['VirtualAddress']['Value']),
      section['Misc_VirtualSize']['Value'],
      section['SizeOfRawData']['Value'], section['MD5'])
   )

Maintenant, extrayez la liste des importations et des exportations à partir de fichiers exécutables comme indiqué ci-dessous -

if hasattr(pe, 'DIRECTORY_ENTRY_IMPORT'):
   print("\nImports: ")
   print("=========")
   
   for dir_entry in pe.DIRECTORY_ENTRY_IMPORT:
      dll = dir_entry.dll
      
      if not args.verbose:
         print(dll.decode(), end=", ")
         continue
      name_list = []
      
      for impts in dir_entry.imports:
         if getattr(impts, "name", b"Unknown") is None:
            name = b"Unknown"
         else:
            name = getattr(impts, "name", b"Unknown")
			name_list.append([name.decode(), hex(impts.address)])
      name_fmt = ["{} ({})".format(x[0], x[1]) for x in name_list]
      print('- {}: {}'.format(dll.decode(), ", ".join(name_fmt)))
   if not args.verbose:
      print()

Maintenant, imprimez exports, names et addresses en utilisant le code comme indiqué ci-dessous -

if hasattr(pe, 'DIRECTORY_ENTRY_EXPORT'):
   print("\nExports: ")
   print("=========")
   
   for sym in pe.DIRECTORY_ENTRY_EXPORT.symbols:
      print('- {}: {}'.format(sym.name.decode(), hex(sym.address)))

Le script ci-dessus extraira les métadonnées de base, les informations des en-têtes des fichiers exécutables de Windows.

Métadonnées des documents Office

La plupart du travail sur ordinateur est effectué dans trois applications de MS Office - Word, PowerPoint et Excel. Ces fichiers possèdent d'énormes métadonnées, qui peuvent exposer des informations intéressantes sur leur paternité et leur histoire.

Notez que les métadonnées du format 2007 de word (.docx), excel (.xlsx) et powerpoint (.pptx) sont stockées dans un fichier XML. Nous pouvons traiter ces fichiers XML en Python à l'aide du script Python ci-dessous -

Tout d'abord, importez les bibliothèques requises comme indiqué ci-dessous -

from __future__ import print_function
from argparse import ArgumentParser
from datetime import datetime as dt
from xml.etree import ElementTree as etree

import zipfile
parser = argparse.ArgumentParser('Office Document Metadata’)
parser.add_argument("Office_File", help="Path to office file to read")
args = parser.parse_args()

Maintenant, vérifiez si le fichier est un fichier ZIP. Sinon, signalez une erreur. Maintenant, ouvrez le fichier et extrayez les éléments clés pour le traitement en utilisant le code suivant -

zipfile.is_zipfile(args.Office_File)
zfile = zipfile.ZipFile(args.Office_File)
core_xml = etree.fromstring(zfile.read('docProps/core.xml'))
app_xml = etree.fromstring(zfile.read('docProps/app.xml'))

Maintenant, créez un dictionnaire pour lancer l'extraction des métadonnées -

core_mapping = {
   'title': 'Title',
   'subject': 'Subject',
   'creator': 'Author(s)',
   'keywords': 'Keywords',
   'description': 'Description',
   'lastModifiedBy': 'Last Modified By',
   'modified': 'Modified Date',
   'created': 'Created Date',
   'category': 'Category',
   'contentStatus': 'Status',
   'revision': 'Revision'
}

Utilisation iterchildren() méthode pour accéder à chacune des balises dans le fichier XML -

for element in core_xml.getchildren():
   for key, title in core_mapping.items():
      if key in element.tag:
         if 'date' in title.lower():
            text = dt.strptime(element.text, "%Y-%m-%dT%H:%M:%SZ")
         else:
            text = element.text
         print("{}: {}".format(title, text))

De même, faites cela pour le fichier app.xml qui contient des informations statistiques sur le contenu du document -

app_mapping = {
   'TotalTime': 'Edit Time (minutes)',
   'Pages': 'Page Count',
   'Words': 'Word Count',
   'Characters': 'Character Count',
   'Lines': 'Line Count',
   'Paragraphs': 'Paragraph Count',
   'Company': 'Company',
   'HyperlinkBase': 'Hyperlink Base',
   'Slides': 'Slide count',
   'Notes': 'Note Count',
   'HiddenSlides': 'Hidden Slide Count',
}
for element in app_xml.getchildren():
   for key, title in app_mapping.items():
      if key in element.tag:
         if 'date' in title.lower():
            text = dt.strptime(element.text, "%Y-%m-%dT%H:%M:%SZ")
         else:
            text = element.text
         print("{}: {}".format(title, text))

Maintenant, après avoir exécuté le script ci-dessus, nous pouvons obtenir les différents détails sur le document particulier. Notez que nous pouvons appliquer ce script sur les documents Office 2007 ou version ultérieure uniquement.

Ce chapitre expliquera les principes de base impliqués dans l'exécution de l'investigation de réseau à l'aide de Python.

Comprendre la criminalistique réseau

La criminalistique réseau est une branche de la criminalistique numérique qui traite de la surveillance et de l'analyse du trafic du réseau informatique, à la fois local et WAN (réseau étendu), à des fins de collecte d'informations, de collecte de preuves ou de détection d'intrusions. La criminalistique de réseau joue un rôle essentiel dans les enquêtes sur les crimes numériques tels que le vol de propriété intellectuelle ou la fuite d'informations. Une image des communications réseau aide un enquêteur à résoudre certaines questions cruciales comme suit -

  • Quels sites Web ont été consultés?

  • Quel type de contenu a été téléchargé sur notre réseau?

  • Quel type de contenu a été téléchargé depuis notre réseau?

  • À quels serveurs accède-t-on?

  • Quelqu'un envoie-t-il des informations sensibles en dehors des pare-feu de l'entreprise?

Internet Evidence Finder (IEF)

IEF est un outil médico-légal numérique pour trouver, analyser et présenter des preuves numériques trouvées sur différents supports numériques comme l'ordinateur, les smartphones, les tablettes, etc. Il est très populaire et utilisé par des milliers de professionnels de la médecine légale.

Utilisation d'IEF

En raison de sa popularité, IEF est utilisé dans une large mesure par les professionnels de la médecine légale. Certaines des utilisations de l'IEF sont les suivantes -

  • En raison de ses puissantes capacités de recherche, il est utilisé pour rechercher simultanément plusieurs fichiers ou supports de données.

  • Il est également utilisé pour récupérer des données supprimées de l'espace non alloué de la RAM grâce à de nouvelles techniques de sculpture.

  • Si les enquêteurs souhaitent reconstruire les pages Web dans leur format d'origine à la date à laquelle elles ont été ouvertes, ils peuvent utiliser IEF.

  • Il est également utilisé pour rechercher des volumes de disque logiques ou physiques.

Dumping des rapports d'IEF vers CSV à l'aide de Python

IEF stocke les données dans une base de données SQLite et le script Python suivant identifiera dynamiquement les tables de résultats dans la base de données IEF et les videra dans les fichiers CSV respectifs.

Ce processus se fait selon les étapes ci-dessous

  • Tout d'abord, générez la base de données de résultats IEF qui sera un fichier de base de données SQLite se terminant par l'extension .db.

  • Ensuite, interrogez cette base de données pour identifier toutes les tables.

  • Enfin, écrivez ces tableaux de résultats dans un fichier CSV individuel.

Code Python

Voyons comment utiliser le code Python à cette fin -

Pour le script Python, importez les bibliothèques nécessaires comme suit -

from __future__ import print_function

import argparse
import csv
import os
import sqlite3
import sys

Maintenant, nous devons fournir le chemin d'accès au fichier de base de données IEF -

if __name__ == '__main__':
   parser = argparse.ArgumentParser('IEF to CSV')
   parser.add_argument("IEF_DATABASE", help="Input IEF database")
   parser.add_argument("OUTPUT_DIR", help="Output DIR")
   args = parser.parse_args()

Maintenant, nous allons confirmer l'existence de la base de données IEF comme suit -

if not os.path.exists(args.OUTPUT_DIR):
   os.makedirs(args.OUTPUT_DIR)
if os.path.exists(args.IEF_DATABASE) and \ os.path.isfile(args.IEF_DATABASE):
   main(args.IEF_DATABASE, args.OUTPUT_DIR)
else:
   print("[-] Supplied input file {} does not exist or is not a " "file".format(args.IEF_DATABASE))
   sys.exit(1)

Maintenant, comme nous l'avons fait dans les scripts précédents, établissez la connexion avec la base de données SQLite comme suit pour exécuter les requêtes via le curseur -

def main(database, out_directory):
   print("[+] Connecting to SQLite database")
   conn = sqlite3.connect(database)
   c = conn.cursor()

Les lignes de code suivantes récupèrent les noms des tables de la base de données -

print("List of all tables to extract")
c.execute("select * from sqlite_master where type = 'table'")
tables = [x[2] for x in c.fetchall() if not x[2].startswith('_') and not x[2].endswith('_DATA')]

Maintenant, nous allons sélectionner toutes les données du tableau et en utilisant fetchall() méthode sur l'objet curseur, nous allons stocker la liste des tuples contenant les données de la table dans son intégralité dans une variable -

print("Dumping {} tables to CSV files in {}".format(len(tables), out_directory))

for table in tables:
c.execute("pragma table_info('{}')".format(table))
table_columns = [x[1] for x in c.fetchall()]

c.execute("select * from '{}'".format(table))
table_data = c.fetchall()

Maintenant, en utilisant CSV_Writer() méthode nous allons écrire le contenu dans un fichier CSV -

csv_name = table + '.csv'
csv_path = os.path.join(out_directory, csv_name)
print('[+] Writing {} table to {} CSV file'.format(table,csv_name))

with open(csv_path, "w", newline = "") as csvfile:
   csv_writer = csv.writer(csvfile)
   csv_writer.writerow(table_columns)
   csv_writer.writerows(table_data)

Le script ci-dessus récupérera toutes les données des tables de la base de données IEF et écrira le contenu dans le fichier CSV de notre choix.

Travailler avec des données mises en cache

À partir de la base de données de résultats IEF, nous pouvons extraire plus d'informations qui ne sont pas nécessairement prises en charge par IEF lui-même. Nous pouvons récupérer les données en cache, un produit bi pour information, auprès d'un fournisseur de services de messagerie comme Yahoo, Google, etc. en utilisant la base de données de résultats IEF.

Ce qui suit est le script Python pour accéder aux informations de données mises en cache à partir de Yahoo mail, accessible sur Google Chrome, à l'aide de la base de données IEF. Notez que les étapes seraient plus ou moins les mêmes que celles suivies dans le dernier script Python.

Tout d'abord, importez les bibliothèques nécessaires pour Python comme suit -

from __future__ import print_function
import argparse
import csv
import os
import sqlite3
import sys
import json

Maintenant, fournissez le chemin d'accès au fichier de base de données IEF avec deux arguments de position acceptés par le gestionnaire de ligne de commande comme cela a été fait dans le dernier script -

if __name__ == '__main__':
   parser = argparse.ArgumentParser('IEF to CSV')
   parser.add_argument("IEF_DATABASE", help="Input IEF database")
   parser.add_argument("OUTPUT_DIR", help="Output DIR")
   args = parser.parse_args()

Maintenant, confirmez l'existence de la base de données IEF comme suit -

directory = os.path.dirname(args.OUTPUT_CSV)

if not os.path.exists(directory):os.makedirs(directory)
if os.path.exists(args.IEF_DATABASE) and \ os.path.isfile(args.IEF_DATABASE):
   main(args.IEF_DATABASE, args.OUTPUT_CSV)
   else: print("Supplied input file {} does not exist or is not a " "file".format(args.IEF_DATABASE))
sys.exit(1)

Maintenant, établissez la connexion avec la base de données SQLite comme suit pour exécuter les requêtes via le curseur -

def main(database, out_csv):
   print("[+] Connecting to SQLite database")
   conn = sqlite3.connect(database)
   c = conn.cursor()

Vous pouvez utiliser les lignes de code suivantes pour récupérer les instances de l'enregistrement de cache de contacts Yahoo Mail -

print("Querying IEF database for Yahoo Contact Fragments from " "the Chrome Cache Records Table")
   try:
      c.execute("select * from 'Chrome Cache Records' where URL like " "'https://data.mail.yahoo.com" "/classicab/v2/contacts/?format=json%'")
   except sqlite3.OperationalError:
      print("Received an error querying the database --    database may be" "corrupt or not have a Chrome Cache Records table")
      sys.exit(2)

Maintenant, la liste des tuples renvoyés par la requête ci-dessus à enregistrer dans une variable comme suit -

contact_cache = c.fetchall()
contact_data = process_contacts(contact_cache)
write_csv(contact_data, out_csv)

Notez qu'ici nous utiliserons deux méthodes à savoir process_contacts() pour configurer la liste de résultats ainsi que pour parcourir chaque enregistrement de cache de contact et json.loads() pour stocker les données JSON extraites de la table dans une variable pour une manipulation ultérieure -

def process_contacts(contact_cache):
   print("[+] Processing {} cache files matching Yahoo contact cache " " data".format(len(contact_cache)))
   results = []
   
   for contact in contact_cache:
      url = contact[0]
      first_visit = contact[1]
      last_visit = contact[2]
      last_sync = contact[3]
      loc = contact[8]
	   contact_json = json.loads(contact[7].decode())
      total_contacts = contact_json["total"]
      total_count = contact_json["count"]
      
      if "contacts" not in contact_json:
         continue
      for c in contact_json["contacts"]:
         name, anni, bday, emails, phones, links = ("", "", "", "", "", "")
            if "name" in c:
            name = c["name"]["givenName"] + " " + \ c["name"]["middleName"] + " " + c["name"]["familyName"]
            
            if "anniversary" in c:
            anni = c["anniversary"]["month"] + \"/" + c["anniversary"]["day"] + "/" + \c["anniversary"]["year"]
            
            if "birthday" in c:
            bday = c["birthday"]["month"] + "/" + \c["birthday"]["day"] + "/" + c["birthday"]["year"]
            
            if "emails" in c:
               emails = ', '.join([x["ep"] for x in c["emails"]])
            
            if "phones" in c:
               phones = ', '.join([x["ep"] for x in c["phones"]])
            
            if "links" in c:
              links = ', '.join([x["ep"] for x in c["links"]])

Maintenant, pour la société, le titre et les notes, la méthode get est utilisée comme indiqué ci-dessous -

company = c.get("company", "")
title = c.get("jobTitle", "")
notes = c.get("notes", "")

Maintenant, ajoutons la liste des métadonnées et des éléments de données extraits à la liste de résultats comme suit -

results.append([url, first_visit, last_visit, last_sync, loc, name, bday,anni, emails, phones, links, company, title, notes,total_contacts, total_count])
return results

Maintenant, en utilisant CSV_Writer() méthode, nous écrirons le contenu dans un fichier CSV -

def write_csv(data, output):
   print("[+] Writing {} contacts to {}".format(len(data), output))
   with open(output, "w", newline="") as csvfile:
      csv_writer = csv.writer(csvfile)
      csv_writer.writerow([
         "URL", "First Visit (UTC)", "Last Visit (UTC)",
         "Last Sync (UTC)", "Location", "Contact Name", "Bday",
         "Anniversary", "Emails", "Phones", "Links", "Company", "Title",
         "Notes", "Total Contacts", "Count of Contacts in Cache"])
      csv_writer.writerows(data)

Avec l'aide du script ci-dessus, nous pouvons traiter les données en cache de Yahoo mail en utilisant la base de données IEF.

Le chapitre précédent a traité de certains des concepts de la criminalistique de réseau utilisant Python. Dans ce chapitre, apprenons à un niveau plus approfondi la criminalistique réseau utilisant Python.

Conservation de la page Web avec une belle soupe

Le World Wide Web (WWW) est une source d'information unique. Cependant, son héritage est à haut risque en raison de la perte de contenu à un rythme alarmant. Un certain nombre d'institutions patrimoniales culturelles et universitaires, d'organisations à but non lucratif et d'entreprises privées ont exploré les problèmes en jeu et contribué au développement de solutions techniques pour l'archivage Web.

La préservation des pages Web ou l'archivage Web est le processus de collecte des données du World Wide Web, en s'assurant que les données sont conservées dans une archive et en les rendant disponibles pour les futurs chercheurs, historiens et le public. Avant d'aller plus loin dans la préservation de la page Web, laissez-nous discuter de certaines questions importantes liées à la préservation de la page Web, comme indiqué ci-dessous -

  • Change in Web Resources - Les ressources Web changent chaque jour, ce qui est un défi pour la préservation des pages Web.

  • Large Quantity of Resources - Un autre problème lié à la préservation des pages Web est la grande quantité de ressources à préserver.

  • Integrity - Les pages Web doivent être protégées contre les modifications, la suppression ou la suppression non autorisées pour protéger leur intégrité.

  • Dealing with multimedia data - Tout en préservant les pages Web, nous devons également traiter les données multimédias, ce qui peut entraîner des problèmes.

  • Providing access - Outre la préservation, la question de l'accès aux ressources Web et du traitement des problèmes de propriété doit également être résolue.

Dans ce chapitre, nous allons utiliser la bibliothèque Python nommée Beautiful Soup pour la préservation des pages Web.

Qu'est-ce que la belle soupe?

Beautiful Soup est une bibliothèque Python permettant d'extraire des données de fichiers HTML et XML. Il peut être utilisé avecurlibcar il a besoin d'une entrée (document ou url) pour créer un objet soupe, car il ne peut pas récupérer la page Web elle-même. Vous pouvez en savoir plus à ce sujet sur www.crummy.com/software/BeautifulSoup/bs4/doc/

Notez qu'avant de l'utiliser, nous devons installer une bibliothèque tierce à l'aide de la commande suivante -

pip install bs4

Ensuite, en utilisant le gestionnaire de paquets Anaconda, nous pouvons installer Beautiful Soup comme suit -

conda install -c anaconda beautifulsoup4

Script Python pour préserver les pages Web

Le script Python pour la préservation des pages Web à l'aide d'une bibliothèque tierce appelée Beautiful Soup est discuté ici -

Tout d'abord, importez les bibliothèques requises comme suit -

from __future__ import print_function
import argparse

from bs4 import BeautifulSoup, SoupStrainer
from datetime import datetime

import hashlib
import logging
import os
import ssl
import sys
from urllib.request import urlopen

import urllib.error
logger = logging.getLogger(__name__)

Notez que ce script prendra deux arguments de position, l'un est l'URL qui doit être préservée et l'autre est le répertoire de sortie souhaité comme indiqué ci-dessous -

if __name__ == "__main__":
   parser = argparse.ArgumentParser('Web Page preservation')
   parser.add_argument("DOMAIN", help="Website Domain")
   parser.add_argument("OUTPUT_DIR", help="Preservation Output Directory")
   parser.add_argument("-l", help="Log file path",
   default=__file__[:-3] + ".log")
   args = parser.parse_args()

Maintenant, configurez la journalisation du script en spécifiant un fichier et un gestionnaire de flux pour être en boucle et documentez le processus d'acquisition comme indiqué -

logger.setLevel(logging.DEBUG)
msg_fmt = logging.Formatter("%(asctime)-15s %(funcName)-10s""%(levelname)-8s %(message)s")
strhndl = logging.StreamHandler(sys.stderr)
strhndl.setFormatter(fmt=msg_fmt)
fhndl = logging.FileHandler(args.l, mode='a')
fhndl.setFormatter(fmt=msg_fmt)

logger.addHandler(strhndl)
logger.addHandler(fhndl)
logger.info("Starting BS Preservation")
logger.debug("Supplied arguments: {}".format(sys.argv[1:]))
logger.debug("System " + sys.platform)
logger.debug("Version " + sys.version)

Maintenant, faisons la validation d'entrée sur le répertoire de sortie souhaité comme suit -

if not os.path.exists(args.OUTPUT_DIR):
   os.makedirs(args.OUTPUT_DIR)
main(args.DOMAIN, args.OUTPUT_DIR)

Maintenant, nous allons définir le main() fonction qui extraira le nom de base du site Web en supprimant les éléments inutiles avant le nom réel avec une validation supplémentaire sur l'URL d'entrée comme suit -

def main(website, output_dir):
   base_name = website.replace("https://", "").replace("http://", "").replace("www.", "")
   link_queue = set()
   
   if "http://" not in website and "https://" not in website:
      logger.error("Exiting preservation - invalid user input: {}".format(website))
      sys.exit(1)
   logger.info("Accessing {} webpage".format(website))
   context = ssl._create_unverified_context()

Maintenant, nous devons ouvrir une connexion avec l'URL en utilisant la méthode urlopen (). Utilisons le bloc try-except comme suit -

try:
   index = urlopen(website, context=context).read().decode("utf-8")
except urllib.error.HTTPError as e:
   logger.error("Exiting preservation - unable to access page: {}".format(website))
   sys.exit(2)
logger.debug("Successfully accessed {}".format(website))

Les lignes de code suivantes incluent trois fonctions comme expliqué ci-dessous -

  • write_output() pour écrire la première page Web dans le répertoire de sortie

  • find_links() fonction pour identifier les liens sur cette page Web

  • recurse_pages() fonction pour parcourir et découvrir tous les liens sur la page Web.

write_output(website, index, output_dir)
link_queue = find_links(base_name, index, link_queue)
logger.info("Found {} initial links on webpage".format(len(link_queue)))
recurse_pages(website, link_queue, context, output_dir)
logger.info("Completed preservation of {}".format(website))

Maintenant, définissons write_output() méthode comme suit -

def write_output(name, data, output_dir, counter=0):
   name = name.replace("http://", "").replace("https://", "").rstrip("//")
   directory = os.path.join(output_dir, os.path.dirname(name))
   
   if not os.path.exists(directory) and os.path.dirname(name) != "":
      os.makedirs(directory)

Nous devons enregistrer certains détails sur la page Web, puis nous enregistrons le hachage des données en utilisant hash_data() méthode comme suit -

logger.debug("Writing {} to {}".format(name, output_dir)) logger.debug("Data Hash: {}".format(hash_data(data)))
path = os.path.join(output_dir, name)
path = path + "_" + str(counter)
with open(path, "w") as outfile:
   outfile.write(data)
logger.debug("Output File Hash: {}".format(hash_file(path)))

Maintenant, définissez hash_data() méthode à l'aide de laquelle nous lisons le UTF-8 données encodées, puis générez le SHA-256 hachage de celui-ci comme suit -

def hash_data(data):
   sha256 = hashlib.sha256()
   sha256.update(data.encode("utf-8"))
   return sha256.hexdigest()
def hash_file(file):
   sha256 = hashlib.sha256()
   with open(file, "rb") as in_file:
      sha256.update(in_file.read())
return sha256.hexdigest()

Maintenant, créons un Beautifulsoup objet hors des données de la page Web sous find_links() méthode comme suit -

def find_links(website, page, queue):
   for link in BeautifulSoup(page, "html.parser",parse_only = SoupStrainer("a", href = True)):
      if website in link.get("href"):
         if not os.path.basename(link.get("href")).startswith("#"):
            queue.add(link.get("href"))
   return queue

Maintenant, nous devons définir recurse_pages() méthode en lui fournissant les entrées de l'URL du site Web, la file d'attente de liens actuelle, le contexte SSL non vérifié et le répertoire de sortie comme suit -

def recurse_pages(website, queue, context, output_dir):
   processed = []
   counter = 0
   
   while True:
      counter += 1
      if len(processed) == len(queue):
         break
      for link in queue.copy(): if link in processed:
         continue
	   processed.append(link)
      try:
      page = urlopen(link,      context=context).read().decode("utf-8")
      except urllib.error.HTTPError as e:
         msg = "Error accessing webpage: {}".format(link)
         logger.error(msg)
         continue

Maintenant, écrivez la sortie de chaque page Web accédée dans un fichier en passant le nom du lien, les données de la page, le répertoire de sortie et le compteur comme suit -

write_output(link, page, output_dir, counter)
queue = find_links(website, page, queue)
logger.info("Identified {} links throughout website".format(
   len(queue)))

Maintenant, lorsque nous exécutons ce script en fournissant l'URL du site Web, le répertoire de sortie et un chemin vers le fichier journal, nous obtiendrons les détails de cette page Web qui pourront être utilisés pour une utilisation future.

Chasse aux virus

Vous êtes-vous déjà demandé comment les analystes légistes, les chercheurs en sécurité et les répondants aux incidents pouvaient comprendre la différence entre les logiciels utiles et les logiciels malveillants? La réponse réside dans la question elle-même, car sans étudier les logiciels malveillants, générés rapidement par les pirates, il est tout à fait impossible pour les chercheurs et les spécialistes de faire la différence entre les logiciels utiles et les logiciels malveillants. Dans cette section, parlons deVirusShare, un outil pour accomplir cette tâche.

Comprendre VirusShare

VirusShare est la plus grande collection privée d'échantillons de logiciels malveillants destinée à fournir aux chercheurs en sécurité, aux intervenants en cas d'incident et aux analystes judiciaires des échantillons de code malveillant en direct. Il contient plus de 30 millions d'échantillons.

L'avantage de VirusShare est la liste des hachages de logiciels malveillants disponibles gratuitement. N'importe qui peut utiliser ces hachages pour créer un ensemble de hachages très complet et l'utiliser pour identifier les fichiers potentiellement malveillants. Mais avant d'utiliser VirusShare, nous vous suggérons de visiterhttps://virusshare.com pour plus de détails.

Création d'une liste de hachage délimitée par une nouvelle ligne à partir de VirusShare à l'aide de Python

Une liste de hachage de VirusShare peut être utilisée par divers outils médico-légaux tels que X-Ways et EnCase. Dans le script décrit ci-dessous, nous allons automatiser le téléchargement des listes de hachages depuis VirusShare pour créer une liste de hachage délimitée par une nouvelle ligne.

Pour ce script, nous avons besoin d'une bibliothèque Python tierce tqdm qui peut être téléchargé comme suit -

pip install tqdm

Notez que dans ce script, nous allons d'abord lire la page de hachage de VirusShare et identifier dynamiquement la liste de hachage la plus récente. Ensuite, nous initialiserons la barre de progression et téléchargerons la liste de hachage dans la plage souhaitée.

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

from __future__ import print_function

import argparse
import os
import ssl
import sys
import tqdm

from urllib.request import urlopen
import urllib.error

Ce script prendra un argument de position, qui serait le chemin souhaité pour le jeu de hachage -

if __name__ == '__main__':
   parser = argparse.ArgumentParser('Hash set from VirusShare')
   parser.add_argument("OUTPUT_HASH", help = "Output Hashset")
   parser.add_argument("--start", type = int, help = "Optional starting location")
   args = parser.parse_args()

Maintenant, nous allons effectuer la validation d'entrée standard comme suit -

directory = os.path.dirname(args.OUTPUT_HASH)
if not os.path.exists(directory):
   os.makedirs(directory)
if args.start:
   main(args.OUTPUT_HASH, start=args.start)
else:
   main(args.OUTPUT_HASH)

Maintenant, nous devons définir main() fonction avec **kwargs comme argument car cela créera un dictionnaire auquel nous pouvons nous référer pour prendre en charge les arguments clés fournis comme indiqué ci-dessous -

def main(hashset, **kwargs):
   url = "https://virusshare.com/hashes.4n6"
   print("[+] Identifying hash set range from {}".format(url))
   context = ssl._create_unverified_context()

Maintenant, nous devons ouvrir la page de hachage de VirusShare en utilisant urlib.request.urlopen()méthode. Nous utiliserons le bloc try-except comme suit -

try:
   index = urlopen(url, context = context).read().decode("utf-8")
except urllib.error.HTTPError as e:
   print("[-] Error accessing webpage - exiting..")
   sys.exit(1)

Maintenant, identifiez la dernière liste de hachage des pages téléchargées. Vous pouvez le faire en recherchant la dernière instance du code HTMLhrefbalise à la liste de hachage VirusShare. Cela peut être fait avec les lignes de code suivantes -

tag = index.rfind(r'a href = "hashes/VirusShare_')
stop = int(index[tag + 27: tag + 27 + 5].lstrip("0"))

if "start" not in kwa<rgs:
   start = 0
else:
   start = kwargs["start"]

if start < 0 or start > stop:
   print("[-] Supplied start argument must be greater than or equal ""to zero but less than the latest hash list, ""currently: {}".format(stop))
sys.exit(2)
print("[+] Creating a hashset from hash lists {} to {}".format(start, stop))
hashes_downloaded = 0

Maintenant, nous allons utiliser tqdm.trange() méthode pour créer une boucle et une barre de progression comme suit -

for x in tqdm.trange(start, stop + 1, unit_scale=True,desc="Progress"):
   url_hash = "https://virusshare.com/hashes/VirusShare_"\"{}.md5".format(str(x).zfill(5))
   try:
      hashes = urlopen(url_hash, context=context).read().decode("utf-8")
      hashes_list = hashes.split("\n")
   except urllib.error.HTTPError as e:
      print("[-] Error accessing webpage for hash list {}"" - continuing..".format(x))
   continue

Après avoir exécuté les étapes ci-dessus avec succès, nous ouvrirons le fichier texte du jeu de hachage en mode + pour l'ajouter au bas du fichier texte.

with open(hashset, "a+") as hashfile:
   for line in hashes_list:
   if not line.startswith("#") and line != "":
      hashes_downloaded += 1
      hashfile.write(line + '\n')
   print("[+] Finished downloading {} hashes into {}".format(
      hashes_downloaded, hashset))

Après avoir exécuté le script ci-dessus, vous obtiendrez la dernière liste de hachage contenant les valeurs de hachage MD5 au format texte.

Les chapitres précédents ont discuté de l'importance et du processus de la criminalistique de réseau et des concepts impliqués. Dans ce chapitre, apprenons le rôle des e-mails dans la criminalistique numérique et leur investigation à l'aide de Python.

Rôle du courrier électronique dans les enquêtes

Les e-mails jouent un rôle très important dans les communications d'entreprise et sont devenus l'une des applications les plus importantes sur Internet. Ils constituent un mode pratique pour envoyer des messages ainsi que des documents, non seulement à partir d'ordinateurs, mais également à partir d'autres gadgets électroniques tels que les téléphones portables et les tablettes.

Le côté négatif des e-mails est que les criminels peuvent divulguer des informations importantes sur leur entreprise. Par conséquent, le rôle des e-mails dans la criminalistique numérique a augmenté ces dernières années. En criminalistique numérique, les e-mails sont considérés comme des preuves cruciales et l'analyse des en-têtes d'e-mails est devenue importante pour collecter des preuves pendant le processus médico-légal.

Un enquêteur a les objectifs suivants tout en effectuant des analyses de courrier électronique -

  • Identifier le principal criminel
  • Pour recueillir les preuves nécessaires
  • Présenter les résultats
  • Pour construire le boîtier

Défis en criminalistique des e-mails

La criminalistique des e-mails joue un rôle très important dans les enquêtes car la plupart des communications à l'ère actuelle reposent sur les e-mails. Cependant, un enquêteur judiciaire par courrier électronique peut être confronté aux défis suivants au cours de l'enquête:

Faux e-mails

Le plus grand défi de l'investigation des e-mails est l'utilisation de faux e-mails créés en manipulant et en scriptant des en-têtes, etc. après une certaine période.

Usurpation d'identité

Un autre défi de la criminalistique des e-mails est l'usurpation d'identité dans laquelle les criminels présentaient un e-mail comme celui de quelqu'un d'autre. Dans ce cas, la machine recevra à la fois une fausse adresse IP et une adresse IP d'origine.

Re-emailing anonyme

Ici, le serveur de messagerie supprime les informations d'identification du message électronique avant de le transférer davantage. Cela conduit à un autre grand défi pour les enquêtes sur les e-mails.

Techniques utilisées dans les enquêtes judiciaires par courrier électronique

La criminalistique des e-mails est l'étude de la source et du contenu d'un e-mail en tant que preuve permettant d'identifier l'expéditeur et le destinataire réels d'un message ainsi que d'autres informations telles que la date / heure de transmission et l'intention de l'expéditeur. Il s'agit d'étudier les métadonnées, l'analyse des ports ainsi que la recherche par mot-clé.

Certaines des techniques courantes qui peuvent être utilisées pour les enquêtes judiciaires sur les e-mails sont

  • Analyse des en-têtes
  • Investigation du serveur
  • Enquête sur les périphériques réseau
  • Empreintes digitales de l'expéditeur
  • Identifiants intégrés au logiciel

Dans les sections suivantes, nous allons apprendre à récupérer des informations à l'aide de Python à des fins d'enquête sur les e-mails.

Extraction d'informations à partir de fichiers EML

Les fichiers EML sont essentiellement des e-mails au format de fichier largement utilisés pour stocker les e-mails. Il s'agit de fichiers texte structurés compatibles avec plusieurs clients de messagerie tels que Microsoft Outlook, Outlook Express et Windows Live Mail.

Un fichier EML stocke les en-têtes d'e-mail, le contenu du corps et les données des pièces jointes sous forme de texte brut. Il utilise base64 pour coder les données binaires et le codage Quoted-Printable (QP) pour stocker les informations de contenu. Le script Python qui peut être utilisé pour extraire des informations du fichier EML est donné ci-dessous -

Tout d'abord, importez les bibliothèques Python suivantes comme indiqué ci-dessous -

from __future__ import print_function
from argparse import ArgumentParser, FileType
from email import message_from_file

import os
import quopri
import base64

Dans les bibliothèques ci-dessus, quopriest utilisé pour décoder les valeurs encodées QP à partir de fichiers EML. Toutes les données encodées en base64 peuvent être décodées à l'aide debase64 bibliothèque.

Ensuite, fournissons un argument pour le gestionnaire de ligne de commande. Notez qu'ici, il n'acceptera qu'un seul argument qui serait le chemin du fichier EML comme indiqué ci-dessous -

if __name__ == '__main__':
   parser = ArgumentParser('Extracting information from EML file')
   parser.add_argument("EML_FILE",help="Path to EML File", type=FileType('r'))
   args = parser.parse_args()
   main(args.EML_FILE)

Maintenant, nous devons définir main() fonction dans laquelle nous utiliserons la méthode nommée message_from_file()à partir de la bibliothèque de courrier électronique pour lire le fichier comme un objet. Ici, nous allons accéder aux en-têtes, au contenu du corps, aux pièces jointes et à d'autres informations sur la charge utile en utilisant la variable résultante nomméeemlfile comme indiqué dans le code ci-dessous -

def main(input_file):
   emlfile = message_from_file(input_file)
   for key, value in emlfile._headers:
      print("{}: {}".format(key, value))
print("\nBody\n")

if emlfile.is_multipart():
   for part in emlfile.get_payload():
      process_payload(part)
else:
   process_payload(emlfile[1])

Maintenant, nous devons définir process_payload() méthode dans laquelle nous allons extraire le contenu du corps du message en utilisant get_payload()méthode. Nous décoderons les données encodées QP en utilisantquopri.decodestring()fonction. Nous vérifierons également le type de contenu MIME afin qu'il puisse gérer correctement le stockage du courrier électronique. Observez le code ci-dessous -

def process_payload(payload):
   print(payload.get_content_type() + "\n" + "=" * len(payload.get_content_type()))
   body = quopri.decodestring(payload.get_payload())
   
   if payload.get_charset():
      body = body.decode(payload.get_charset())
else:
   try:
      body = body.decode()
   except UnicodeDecodeError:
      body = body.decode('cp1252')

if payload.get_content_type() == "text/html":
   outfile = os.path.basename(args.EML_FILE.name) + ".html"
   open(outfile, 'w').write(body)
elif payload.get_content_type().startswith('application'):
   outfile = open(payload.get_filename(), 'wb')
   body = base64.b64decode(payload.get_payload())
   outfile.write(body)
   outfile.close()
   print("Exported: {}\n".format(outfile.name))
else:
   print(body)

Après avoir exécuté le script ci-dessus, nous obtiendrons les informations d'en-tête ainsi que diverses charges utiles sur la console.

Analyse des fichiers MSG à l'aide de Python

Les e-mails sont disponibles dans de nombreux formats différents. MSG est l'un de ces types de format utilisé par Microsoft Outlook et Exchange. Les fichiers avec l'extension MSG peuvent contenir du texte ASCII brut pour les en-têtes et le corps du message principal ainsi que des liens hypertexte et des pièces jointes.

Dans cette section, nous allons apprendre à extraire des informations d'un fichier MSG à l'aide de l'API Outlook. Notez que le script Python suivant ne fonctionnera que sous Windows. Pour cela, nous devons installer une bibliothèque Python tierce nomméepywin32 comme suit -

pip install pywin32

Maintenant, importez les bibliothèques suivantes à l'aide des commandes affichées -

from __future__ import print_function
from argparse import ArgumentParser

import os
import win32com.client
import pywintypes

Maintenant, fournissons un argument pour le gestionnaire de ligne de commande. Ici, il acceptera deux arguments l'un serait le chemin du fichier MSG et l'autre serait le dossier de sortie souhaité comme suit -

if __name__ == '__main__':
   parser = ArgumentParser(‘Extracting information from MSG file’)
   parser.add_argument("MSG_FILE", help="Path to MSG file")
   parser.add_argument("OUTPUT_DIR", help="Path to output folder")
   args = parser.parse_args()
   out_dir = args.OUTPUT_DIR
   
   if not os.path.exists(out_dir):
      os.makedirs(out_dir)
   main(args.MSG_FILE, args.OUTPUT_DIR)

Maintenant, nous devons définir main() fonction dans laquelle nous appellerons win32com bibliothèque pour la mise en place Outlook API qui permet en outre d'accéder à la MAPI espace de noms.

def main(msg_file, output_dir):
   mapi = win32com.client.Dispatch("Outlook.Application").GetNamespace("MAPI")
   msg = mapi.OpenSharedItem(os.path.abspath(args.MSG_FILE))
   
   display_msg_attribs(msg)
   display_msg_recipients(msg)
   
   extract_msg_body(msg, output_dir)
   extract_attachments(msg, output_dir)

Maintenant, définissez différentes fonctions que nous utilisons dans ce script. Le code ci-dessous montre la définition dudisplay_msg_attribs() fonction qui nous permet d'afficher divers attributs d'un message comme sujet, à, BCC, CC, taille, SenderName, envoyé, etc.

def display_msg_attribs(msg):
   attribs = [
      'Application', 'AutoForwarded', 'BCC', 'CC', 'Class',
      'ConversationID', 'ConversationTopic', 'CreationTime',
      'ExpiryTime', 'Importance', 'InternetCodePage', 'IsMarkedAsTask',
      'LastModificationTime', 'Links','ReceivedTime', 'ReminderSet',
      'ReminderTime', 'ReplyRecipientNames', 'Saved', 'Sender',
      'SenderEmailAddress', 'SenderEmailType', 'SenderName', 'Sent',
      'SentOn', 'SentOnBehalfOfName', 'Size', 'Subject',
      'TaskCompletedDate', 'TaskDueDate', 'To', 'UnRead'
   ]
   print("\nMessage Attributes")
   for entry in attribs:
      print("{}: {}".format(entry, getattr(msg, entry, 'N/A')))

Maintenant, définissez le display_msg_recipeints() fonction qui parcourt les messages et affiche les détails du destinataire.

def display_msg_recipients(msg):
   recipient_attrib = ['Address', 'AutoResponse', 'Name', 'Resolved', 'Sendable']
   i = 1
   
   while True:
   try:
      recipient = msg.Recipients(i)
   except pywintypes.com_error:
      break
   print("\nRecipient {}".format(i))
   print("=" * 15)
   
   for entry in recipient_attrib:
      print("{}: {}".format(entry, getattr(recipient, entry, 'N/A')))
   i += 1

Ensuite, nous définissons extract_msg_body() fonction qui extrait le contenu du corps, HTML et texte brut, du message.

def extract_msg_body(msg, out_dir):
   html_data = msg.HTMLBody.encode('cp1252')
   outfile = os.path.join(out_dir, os.path.basename(args.MSG_FILE))
   
   open(outfile + ".body.html", 'wb').write(html_data)
   print("Exported: {}".format(outfile + ".body.html"))
   body_data = msg.Body.encode('cp1252')
   
   open(outfile + ".body.txt", 'wb').write(body_data)
   print("Exported: {}".format(outfile + ".body.txt"))

Ensuite, nous définirons le extract_attachments() fonction qui exporte les données de pièce jointe dans le répertoire de sortie souhaité.

def extract_attachments(msg, out_dir):
   attachment_attribs = ['DisplayName', 'FileName', 'PathName', 'Position', 'Size']
   i = 1 # Attachments start at 1
   
   while True:
      try:
         attachment = msg.Attachments(i)
   except pywintypes.com_error:
      break

Une fois toutes les fonctions définies, nous imprimerons tous les attributs sur la console avec la ligne de codes suivante -

print("\nAttachment {}".format(i))
print("=" * 15)
   
for entry in attachment_attribs:
   print('{}: {}'.format(entry, getattr(attachment, entry,"N/A")))
outfile = os.path.join(os.path.abspath(out_dir),os.path.split(args.MSG_FILE)[-1])
   
if not os.path.exists(outfile):
os.makedirs(outfile)
outfile = os.path.join(outfile, attachment.FileName)
attachment.SaveAsFile(outfile)
   
print("Exported: {}".format(outfile))
i += 1

Après avoir exécuté le script ci-dessus, nous obtiendrons les attributs du message et ses pièces jointes dans la fenêtre de la console ainsi que plusieurs fichiers dans le répertoire de sortie.

Structurer des fichiers MBOX à partir de Google Takeout à l'aide de Python

Les fichiers MBOX sont des fichiers texte avec un formatage spécial qui divise les messages stockés à l'intérieur. Ils sont souvent associés aux systèmes UNIX, Thunderbolt et Google Takeouts.

Dans cette section, vous verrez un script Python, où nous allons structurer les fichiers MBOX obtenus à partir de Google Takeouts. Mais avant cela, nous devons savoir comment générer ces fichiers MBOX en utilisant notre compte Google ou notre compte Gmail.

Acquérir la boîte aux lettres du compte Google au format MBX

L'acquisition de la boîte aux lettres du compte Google implique la sauvegarde de notre compte Gmail. La sauvegarde peut être effectuée pour diverses raisons personnelles ou professionnelles. Notez que Google assure la sauvegarde des données Gmail. Pour acquérir notre boîte aux lettres de compte Google au format MBOX, vous devez suivre les étapes ci-dessous -

  • Ouvert My account tableau de bord.

  • Accédez à la section Informations personnelles et confidentialité et sélectionnez Contrôler votre lien de contenu.

  • Vous pouvez créer une nouvelle archive ou gérer une archive existante. Si nous cliquons,CREATE ARCHIVE lien, puis nous obtiendrons des cases à cocher pour chaque produit Google que nous souhaitons inclure.

  • Après avoir sélectionné les produits, nous aurons la liberté de choisir le type de fichier et la taille maximale de nos archives, ainsi que la méthode de livraison à sélectionner dans la liste.

  • Enfin, nous obtiendrons cette sauvegarde au format MBOX.

Code Python

Maintenant, le fichier MBOX discuté ci-dessus peut être structuré à l'aide de Python comme indiqué ci-dessous -

Tout d'abord, vous devez importer les bibliothèques Python comme suit -

from __future__ import print_function
from argparse import ArgumentParser

import mailbox
import os
import time
import csv
from tqdm import tqdm

import base64

Toutes les bibliothèques ont été utilisées et expliquées dans les scripts précédents, à l'exception du mailbox bibliothèque qui est utilisée pour analyser les fichiers MBOX.

Maintenant, fournissez un argument pour le gestionnaire de ligne de commande. Ici, il acceptera deux arguments - l'un serait le chemin du fichier MBOX, et l'autre serait le dossier de sortie souhaité.

if __name__ == '__main__':
   parser = ArgumentParser('Parsing MBOX files')
   parser.add_argument("MBOX", help="Path to mbox file")
   parser.add_argument(
      "OUTPUT_DIR",help = "Path to output directory to write report ""and exported content")
   args = parser.parse_args()
   main(args.MBOX, args.OUTPUT_DIR)

Maintenant, va définir main() fonction et appel mbox classe de bibliothèque de boîtes aux lettres à l'aide de laquelle nous pouvons analyser un fichier MBOX en fournissant son chemin -

def main(mbox_file, output_dir):
   print("Reading mbox file")
   mbox = mailbox.mbox(mbox_file, factory=custom_reader)
   print("{} messages to parse".format(len(mbox)))

Maintenant, définissez une méthode de lecture pour mailbox bibliothèque comme suit -

def custom_reader(data_stream):
   data = data_stream.read()
   try:
      content = data.decode("ascii")
   except (UnicodeDecodeError, UnicodeEncodeError) as e:
      content = data.decode("cp1252", errors="replace")
   return mailbox.mboxMessage(content)

Maintenant, créez des variables pour un traitement ultérieur comme suit -

parsed_data = []
attachments_dir = os.path.join(output_dir, "attachments")

if not os.path.exists(attachments_dir):
   os.makedirs(attachments_dir)
columns = [
   "Date", "From", "To", "Subject", "X-Gmail-Labels", "Return-Path", "Received", 
   "Content-Type", "Message-ID","X-GM-THRID", "num_attachments_exported", "export_path"]

Ensuite, utilisez tqdm pour générer une barre de progression et suivre le processus d'itération comme suit -

for message in tqdm(mbox):
   msg_data = dict()
   header_data = dict(message._headers)
for hdr in columns:
   msg_data[hdr] = header_data.get(hdr, "N/A")

Maintenant, vérifiez que le message météo a des charges utiles ou non. Si c'est le cas, nous définironswrite_payload() méthode comme suit -

if len(message.get_payload()):
   export_path = write_payload(message, attachments_dir)
   msg_data['num_attachments_exported'] = len(export_path)
   msg_data['export_path'] = ", ".join(export_path)

Maintenant, les données doivent être ajoutées. Ensuite, nous appelleronscreate_report() méthode comme suit -

parsed_data.append(msg_data)
create_report(
   parsed_data, os.path.join(output_dir, "mbox_report.csv"), columns)
def write_payload(msg, out_dir):
   pyld = msg.get_payload()
   export_path = []
   
if msg.is_multipart():
   for entry in pyld:
      export_path += write_payload(entry, out_dir)
else:
   content_type = msg.get_content_type()
   if "application/" in content_type.lower():
      content = base64.b64decode(msg.get_payload())
      export_path.append(export_content(msg, out_dir, content))
   elif "image/" in content_type.lower():
      content = base64.b64decode(msg.get_payload())
      export_path.append(export_content(msg, out_dir, content))

   elif "video/" in content_type.lower():
      content = base64.b64decode(msg.get_payload())
      export_path.append(export_content(msg, out_dir, content))
   elif "audio/" in content_type.lower():
      content = base64.b64decode(msg.get_payload())
      export_path.append(export_content(msg, out_dir, content))
   elif "text/csv" in content_type.lower():
      content = base64.b64decode(msg.get_payload())
      export_path.append(export_content(msg, out_dir, content))
   elif "info/" in content_type.lower():
      export_path.append(export_content(msg, out_dir,
      msg.get_payload()))
   elif "text/calendar" in content_type.lower():
      export_path.append(export_content(msg, out_dir,
      msg.get_payload()))
   elif "text/rtf" in content_type.lower():
      export_path.append(export_content(msg, out_dir,
      msg.get_payload()))
   else:
      if "name=" in msg.get('Content-Disposition', "N/A"):
         content = base64.b64decode(msg.get_payload())
      export_path.append(export_content(msg, out_dir, content))
   elif "name=" in msg.get('Content-Type', "N/A"):
      content = base64.b64decode(msg.get_payload())
      export_path.append(export_content(msg, out_dir, content))
return export_path

Observez que les instructions if-else ci-dessus sont faciles à comprendre. Maintenant, nous devons définir une méthode qui extraira le nom de fichier dumsg objet comme suit -

def export_content(msg, out_dir, content_data):
   file_name = get_filename(msg)
   file_ext = "FILE"
   
   if "." in file_name: file_ext = file_name.rsplit(".", 1)[-1]
   file_name = "{}_{:.4f}.{}".format(file_name.rsplit(".", 1)[0], time.time(), file_ext)
   file_name = os.path.join(out_dir, file_name)

Maintenant, à l'aide des lignes de code suivantes, vous pouvez réellement exporter le fichier -

if isinstance(content_data, str):
   open(file_name, 'w').write(content_data)
else:
   open(file_name, 'wb').write(content_data)
return file_name

Maintenant, définissons une fonction pour extraire les noms de fichiers du message pour représenter avec précision les noms de ces fichiers comme suit -

def get_filename(msg):
   if 'name=' in msg.get("Content-Disposition", "N/A"):
      fname_data = msg["Content-Disposition"].replace("\r\n", " ")
      fname = [x for x in fname_data.split("; ") if 'name=' in x]
      file_name = fname[0].split("=", 1)[-1]
   elif 'name=' in msg.get("Content-Type", "N/A"):
      fname_data = msg["Content-Type"].replace("\r\n", " ")
      fname = [x for x in fname_data.split("; ") if 'name=' in x]
      file_name = fname[0].split("=", 1)[-1]
   else:
      file_name = "NO_FILENAME"
   fchars = [x for x in file_name if x.isalnum() or x.isspace() or x == "."]
   return "".join(fchars)

Maintenant, nous pouvons écrire un fichier CSV en définissant le create_report() fonction comme suit -

def create_report(output_data, output_file, columns):
   with open(output_file, 'w', newline="") as outfile:
      csvfile = csv.DictWriter(outfile, columns)
      csvfile.writeheader()
      csvfile.writerows(output_data)

Une fois que vous exécutez le script ci-dessus, nous obtiendrons le rapport CSV et le répertoire plein de pièces jointes.

Ce chapitre expliquera divers concepts impliqués dans la criminalistique de Microsoft Windows et les artefacts importants qu'un enquêteur peut obtenir 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 de criminalistique numérique, les artefacts Windows sont très 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 de 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 par un stylo et du papier. Ces notes flottaient sur le bureau avec différentes options pour les couleurs, les polices, etc. Dans Windows 7, le fichier Sticky Notes est stocké en tant que 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 communes à partir duSYSTEM 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.

Ce chapitre décrit certains artefacts plus importants dans Windows et leur méthode d'extraction à l'aide de Python.

Activités des utilisateurs

Windows ayant NTUSER.DATfichier pour stocker diverses activités de l'utilisateur. Chaque profil utilisateur a une ruche commeNTUSER.DAT, qui stocke spécifiquement les informations et les configurations liées à cet utilisateur. Par conséquent, il est très utile aux fins d'enquête par les analystes légistes.

Le script Python suivant analysera certaines des clés de NTUSER.DATpour explorer les actions d'un utilisateur sur le système. Avant de continuer, pour le script Python, nous devons installer des modules tiers à savoirRegistry, pytsk3, pyewf et Jinja2. Nous pouvons utiliser pip pour les installer.

Nous pouvons suivre les étapes suivantes pour extraire des informations de NTUSER.DAT fichier -

  • Tout d'abord, recherchez tout NTUSER.DAT fichiers dans le système.

  • Puis analysez le WordWheelQuery, TypePath and RunMRU clé pour chacun NTUSER.DAT fichier.

  • Enfin, nous écrirons ces artefacts, déjà traités, dans un rapport HTML en utilisant Jinja2 fmodule.

Code Python

Voyons comment utiliser le code Python à cette fin -

Tout d'abord, nous devons importer les modules Python suivants -

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

Maintenant, fournissez un argument pour le gestionnaire de ligne de commande. 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 HTML, comme indiqué ci-dessous -

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)

Maintenant, définissons main() fonction de recherche de tout NTUSER.DAT fichiers, comme indiqué -

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

Maintenant, nous allons essayer de trouver la clé dans NTUSER.DAT et une fois que vous l'avez trouvé, définissez les fonctions de traitement de l'utilisateur comme indiqué ci-dessous -

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

Maintenant, passez l'objet dictionnaire et son chemin à write_html() méthode comme suit -

write_html(report, nt_rec)

Maintenant, définissez une méthode, qui prend pytsk handle de fichier et lisez-le dans la classe Registry via le StringIO classe.

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, nous allons définir la fonction qui analysera et gère WordWheelQuery clé de NTUSER.DAT fichier comme suit -

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

Maintenant, nous allons définir la fonction qui analysera et gère TypedPaths clé de NTUSER.DAT fichier comme suit -

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

Maintenant, nous allons définir la fonction qui analysera et gère RunMRU clé de NTUSER.DAT fichier comme suit -

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

Maintenant, la fonction suivante gérera la création du rapport 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)

Enfin, nous pouvons écrire un document HTML pour le rapport. Après avoir exécuté le script ci-dessus, nous obtiendrons les informations du fichier NTUSER.DAT au format de document HTML.

Fichiers LINK

Les fichiers de raccourcis sont créés lorsqu'un utilisateur ou le système d'exploitation crée des fichiers de raccourcis pour les fichiers qui sont fréquemment utilisés, double-cliqués ou accessibles à partir de lecteurs système tels que le stockage attaché. Ces types de fichiers de raccourcis sont appelés fichiers de lien. En accédant à ces fichiers de lien, un enquêteur peut trouver l'activité de la fenêtre telle que l'heure et l'emplacement à partir desquels ces fichiers ont été consultés.

Laissez-nous discuter du script Python que nous pouvons utiliser pour obtenir les informations de ces fichiers Windows LINK.

Pour le script Python, installez des modules tiers à savoir pylnk, pytsk3, pyewf. Nous pouvons suivre les étapes suivantes pour extraire des informations delnk des dossiers

  • Tout d'abord, recherchez lnk fichiers dans le système.

  • Ensuite, extrayez les informations de ce fichier en les parcourant.

  • Maintenant, nous avons enfin besoin de ces informations dans un rapport CSV.

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 csv
import StringIO

from utility.pytskutil import TSKUtil
import pylnk

Maintenant, fournissez l'argument du gestionnaire de ligne de commande. 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('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)

Maintenant, interprétez le fichier de preuves en créant un objet de TSKUtil et parcourez le système de fichiers pour trouver les fichiers se terminant par lnk. Cela peut être fait en définissantmain() fonction comme suit -

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

Maintenant, avec l'aide du code suivant, nous allons parcourir lnk fichiers en créant une fonction comme suit -

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)

Nous devons maintenant définir deux fonctions, l'une ouvrira le pytsk objet de fichier et autre seront utilisés pour écrire le rapport CSV comme indiqué ci-dessous -

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)

Après avoir exécuté le script ci-dessus, nous obtiendrons les informations de la découverte lnk fichiers dans un rapport CSV -

Pré-extraire les fichiers

Chaque fois qu'une application s'exécute pour la première fois à partir d'un emplacement spécifique, Windows crée prefetch files. Ceux-ci sont utilisés pour accélérer le processus de démarrage de l'application. L'extension de ces fichiers est.PF et ceux-ci sont stockés dans le ”\Root\Windows\Prefetch” dossier.

Les experts en criminalistique numérique peuvent révéler la preuve de l'exécution du programme à partir d'un emplacement spécifié ainsi que les détails de l'utilisateur. Les fichiers de prélecture sont des artefacts utiles pour l'examinateur car leur entrée est conservée même après la suppression ou la désinstallation du programme.

Laissez-nous discuter du script Python qui récupérera les informations des fichiers de prélecture Windows comme indiqué ci-dessous -

Pour le script Python, installez des modules tiers à savoir pylnk, pytsk3 et unicodecsv. Rappelons que nous avons déjà travaillé avec ces bibliothèques dans les scripts Python dont nous avons parlé dans les chapitres précédents.

Nous devons suivre les étapes ci-dessous pour extraire des informations de prefetch fichiers -

  • Tout d'abord, recherchez .pf fichiers d'extension ou les fichiers de prélecture.

  • Maintenant, effectuez la vérification de la signature pour éliminer les faux positifs.

  • Ensuite, analysez le format de fichier de prélecture Windows. Cela diffère avec la version Windows. Par exemple, pour Windows XP, il est de 17, pour Windows Vista et Windows 7, il est de 23, 26 pour Windows 8.1 et 30 pour Windows 10.

  • Enfin, nous écrirons le résultat analysé dans un fichier CSV.

Code Python

Voyons comment utiliser le code Python à cette fin -

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

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

Maintenant, fournissez un argument pour le gestionnaire de ligne de commande. Ici, il acceptera deux arguments, le premier serait le chemin d'accès au fichier de preuve et le second serait le type de fichier de preuve. Il accepte également un argument facultatif pour spécifier le chemin pour rechercher les fichiers de prélecture -

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)

Maintenant, interprétez le fichier de preuves en créant un objet de TSKUtil et parcourez le système de fichiers pour trouver les fichiers se terminant par .pf. Cela peut être fait en définissantmain() fonction comme suit -

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)

Maintenant, définissez une méthode qui fera la validation des signatures comme indiqué ci-dessous -

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)

Maintenant, commencez à traiter les fichiers de prélecture Windows. Ici, nous prenons l'exemple des fichiers de prélecture de 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)

Maintenant, extrayez les données incorporées dans les fichiers pré-extraits en utilisant struct comme suit -

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 ]

Comme nous avons fourni la version de prélecture pour Windows XP, mais que se passe-t-il si elle rencontre des versions de prélecture pour d'autres Windows. Ensuite, il doit afficher un message d'erreur comme suit -

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)

Maintenant, définissez la méthode d'écriture du résultat dans le rapport CSV comme suit -

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)

Après avoir exécuté le script ci-dessus, nous obtiendrons les informations des fichiers de prélecture de la version de Windows XP dans une feuille de calcul.

Ce chapitre explique les autres artefacts qu'un enquêteur peut obtenir lors d'une analyse médico-légale sous Windows.

Journaux d'événements

Les fichiers journaux d'événements Windows, en tant que nom –suggests, sont des fichiers spéciaux qui stockent des événements importants comme lorsque l'utilisateur se connecte à l'ordinateur, lorsque le programme rencontre une erreur, à propos des modifications du système, de l'accès RDP, des événements spécifiques à l'application, etc. Les cyber-enquêteurs sont toujours intéressés par l'événement informations de journal car il fournit de nombreuses informations historiques utiles sur l'accès au système. Dans le script Python suivant, nous allons traiter les formats de journaux d'événements Windows hérités et actuels.

Pour le script Python, nous devons installer des modules tiers, à savoir pytsk3, pyewf, unicodecsv, pyevt and pyevtX. Nous pouvons suivre les étapes ci-dessous pour extraire les informations des journaux d'événements -

  • Tout d'abord, recherchez tous les journaux d'événements qui correspondent à l'argument d'entrée.

  • Ensuite, effectuez la vérification de la signature du fichier.

  • Maintenant, traitez chaque journal des événements trouvé avec la bibliothèque appropriée.

  • Enfin, écrivez la sortie dans une feuille de calcul.

Code Python

Voyons comment utiliser le code Python à cette fin -

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

from __future__ import print_function
import argparse
import unicodecsv as csv
import os
import pytsk3
import pyewf
import pyevt
import pyevtx
import sys
from utility.pytskutil import TSKUtil

Maintenant, fournissez les arguments 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 nom du journal des événements à traiter.

if __name__ == "__main__":
   parser = argparse.ArgumentParser('Information from Event Logs')
   parser.add_argument("EVIDENCE_FILE", help = "Evidence file path")
   parser.add_argument("TYPE", help = "Type of Evidence",choices = ("raw", "ewf"))
   parser.add_argument(
      "LOG_NAME",help = "Event Log Name (SecEvent.Evt, SysEvent.Evt, ""etc.)")
   
   parser.add_argument(
      "-d", help = "Event log directory to scan",default = "/WINDOWS/SYSTEM32/WINEVT")
   
   parser.add_argument(
      "-f", help = "Enable fuzzy search for either evt or"" evtx extension", action = "store_true")
   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.LOG_NAME, args.d, args.f)
   else:
      print("[-] Supplied input file {} does not exist or is not a ""file".format(args.EVIDENCE_FILE))
   sys.exit(1)

Maintenant, interagissez avec les journaux d'événements pour interroger l'existence du chemin fourni par l'utilisateur en créant notre TSKUtilobjet. Cela peut être fait avec l'aide demain() méthode comme suit -

def main(evidence, image_type, log, win_event, fuzzy):
   tsk_util = TSKUtil(evidence, image_type)
   event_dir = tsk_util.query_directory(win_event)
   
   if event_dir is not None:
      if fuzzy is True:
         event_log = tsk_util.recurse_files(log, path=win_event)
   else:
      event_log = tsk_util.recurse_files(log, path=win_event, logic="equal")
   
   if event_log is not None:
      event_data = []
      for hit in event_log:
         event_file = hit[2]
         temp_evt = write_file(event_file)

Maintenant, nous devons effectuer une vérification de signature suivie de la définition d'une méthode qui écrira tout le contenu dans le répertoire courant -

def write_file(event_file):
   with open(event_file.info.name.name, "w") as outfile:
      outfile.write(event_file.read_random(0, event_file.info.meta.size))
   return event_file.info.name.name
      if pyevt.check_file_signature(temp_evt):
         evt_log = pyevt.open(temp_evt)
         print("[+] Identified {} records in {}".format(
            evt_log.number_of_records, temp_evt))
         
         for i, record in enumerate(evt_log.records):
            strings = ""
            for s in record.strings:
               if s is not None:
                  strings += s + "\n"
            event_data.append([
               i, hit[0], record.computer_name,
               record.user_security_identifier,
               record.creation_time, record.written_time,
               record.event_category, record.source_name,
               record.event_identifier, record.event_type,
               strings, "",
               os.path.join(win_event, hit[1].lstrip("//"))
            ])
      elif pyevtx.check_file_signature(temp_evt):
         evtx_log = pyevtx.open(temp_evt)
         print("[+] Identified {} records in {}".format(
            evtx_log.number_of_records, temp_evt))
         for i, record in enumerate(evtx_log.records):
            strings = ""
            for s in record.strings:
			   if s is not None:
               strings += s + "\n"
         event_data.append([
            i, hit[0], record.computer_name,
            record.user_security_identifier, "",
            record.written_time, record.event_level,
            record.source_name, record.event_identifier,
            "", strings, record.xml_string,
            os.path.join(win_event, hit[1].lstrip("//"))
      ])
      else:
         print("[-] {} not a valid event log. Removing temp" file...".format(temp_evt))
         os.remove(temp_evt)
      continue
      write_output(event_data)
   else:
      print("[-] {} Event log not found in {} directory".format(log, win_event))
      sys.exit(3)
else:
   print("[-] Win XP Event Log Directory {} not found".format(win_event))
   sys.exit(2

Enfin, définissez une méthode pour écrire la sortie dans une feuille de calcul comme suit -

def write_output(data):
   output_name = "parsed_event_logs.csv"
   print("[+] Writing {} to current working directory: {}".format(
      output_name, os.getcwd()))
   
   with open(output_name, "wb") as outfile:
      writer = csv.writer(outfile)
      writer.writerow([
         "Index", "File name", "Computer Name", "SID",
         "Event Create Date", "Event Written Date",
         "Event Category/Level", "Event Source", "Event ID",
         "Event Type", "Data", "XML Data", "File Path"
      ])
      writer.writerows(data)

Une fois que vous avez exécuté avec succès le script ci-dessus, nous obtiendrons les informations du journal des événements dans une feuille de calcul.

Histoire d'Internet

L'histoire d'Internet est très utile pour les analystes médico-légaux; car la plupart des cybercrimes se produisent uniquement sur Internet. Voyons comment extraire l'historique Internet d'Internet Explorer, alors que nous discutons de la criminalistique Windows, et Internet Explorer est fourni par défaut avec Windows.

Sur Internet Explorer, l'historique Internet est enregistré dans index.datfichier. Examinons un script Python, qui extraira les informations deindex.dat fichier.

Nous pouvons suivre les étapes ci-dessous pour extraire des informations de index.dat fichiers -

  • Tout d'abord, recherchez index.dat fichiers dans le système.

  • Ensuite, extrayez les informations de ce fichier en les parcourant.

  • Maintenant, écrivez toutes ces informations dans un rapport CSV.

Code Python

Voyons comment utiliser le code Python à cette fin -

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

from __future__ import print_function
import argparse

from datetime import datetime, timedelta
import os
import pytsk3
import pyewf
import pymsiecf
import sys
import unicodecsv as csv

from utility.pytskutil import TSKUtil

Maintenant, fournissez des arguments pour le gestionnaire de ligne de commande. Notez qu'ici, il acceptera deux arguments - le premier serait le chemin d'accès au fichier de preuve et le second serait le type de fichier de preuve -

if __name__ == "__main__":
parser = argparse.ArgumentParser('getting information from internet history')
   parser.add_argument("EVIDENCE_FILE", help = "Evidence file path")
   parser.add_argument("TYPE", help = "Type of Evidence",choices = ("raw", "ewf"))
   parser.add_argument("-d", help = "Index.dat directory to scan",default = "/USERS")
   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.d)
   else:
      print("[-] Supplied input file {} does not exist or is not a ""file".format(args.EVIDENCE_FILE))
      sys.exit(1)

Maintenant, interprétez le fichier de preuves en créant un objet de TSKUtilet parcourez le système de fichiers pour trouver les fichiers index.dat. Cela peut être fait en définissant lemain() fonction comme suit -

def main(evidence, image_type, path):
   tsk_util = TSKUtil(evidence, image_type)
   index_dir = tsk_util.query_directory(path)
   
   if index_dir is not None:
      index_files = tsk_util.recurse_files("index.dat", path = path,logic = "equal")
      
      if index_files is not None:
         print("[+] Identified {} potential index.dat files".format(len(index_files)))
         index_data = []
         
         for hit in index_files:
            index_file = hit[2]
            temp_index = write_file(index_file)

Maintenant, définissez une fonction à l'aide de laquelle nous pouvons copier les informations du fichier index.dat dans le répertoire de travail actuel et plus tard, elles peuvent être traitées par un module tiers -

def write_file(index_file):
   with open(index_file.info.name.name, "w") as outfile:
   outfile.write(index_file.read_random(0, index_file.info.meta.size))
return index_file.info.name.name

Maintenant, utilisez le code suivant pour effectuer la validation de signature à l'aide de la fonction intégrée à savoir check_file_signature() -

if pymsiecf.check_file_signature(temp_index):
   index_dat = pymsiecf.open(temp_index)
   print("[+] Identified {} records in {}".format(
   index_dat.number_of_items, temp_index))

   for i, record in enumerate(index_dat.items):
   try:
      data = record.data
   if data is not None:
      data = data.rstrip("\x00")
   except AttributeError:
   
   if isinstance(record, pymsiecf.redirected):
      index_data.append([
         i, temp_index, "", "", "", "", "",record.location, "", "", record.offset,os.path.join(path, hit[1].lstrip("//"))])
   
   elif isinstance(record, pymsiecf.leak):
      index_data.append([
         i, temp_index, record.filename, "","", "", "", "", "", "", record.offset,os.path.join(path, hit[1].lstrip("//"))])
   continue
   
   index_data.append([
      i, temp_index, record.filename,
      record.type, record.primary_time,
      record.secondary_time,
      record.last_checked_time, record.location,
      record.number_of_hits, data, record.offset,
      os.path.join(path, hit[1].lstrip("//"))
   ])
   else:
      print("[-] {} not a valid index.dat file. Removing "
      "temp file..".format(temp_index))
      os.remove("index.dat")
      continue
      os.remove("index.dat")
      write_output(index_data)
   else:
      print("[-] Index.dat files not found in {} directory".format(path))
   sys.exit(3)
   else:
      print("[-] Directory {} not found".format(win_event))
   sys.exit(2)

Maintenant, définissez une méthode qui imprimera la sortie dans un fichier CSV, comme indiqué ci-dessous -

def write_output(data):
   output_name = "Internet_Indexdat_Summary_Report.csv"
   print("[+] Writing {} with {} parsed index.dat files to current "
   "working directory: {}".format(output_name, len(data),os.getcwd()))
   
   with open(output_name, "wb") as outfile:
      writer = csv.writer(outfile)
      writer.writerow(["Index", "File Name", "Record Name",
      "Record Type", "Primary Date", "Secondary Date",
      "Last Checked Date", "Location", "No. of Hits",
      "Record Data", "Record Offset", "File Path"])
      writer.writerows(data)

Après avoir exécuté le script ci-dessus, nous obtiendrons les informations du fichier index.dat dans le fichier CSV.

Clichés instantanés de volume

Un cliché instantané est la technologie incluse dans Windows pour effectuer des copies de sauvegarde ou des instantanés de fichiers informatiques manuellement ou automatiquement. Il est également appelé service de cliché de volume ou service de cliché de volume (VSS).

Avec l'aide de ces fichiers VSS, les experts légistes peuvent avoir des informations historiques sur la façon dont le système a changé au fil du temps et quels fichiers existaient sur l'ordinateur. La technologie de cliché instantané nécessite que le système de fichiers soit NTFS pour créer et stocker des clichés instantanés.

Dans cette section, nous allons voir un script Python, qui aide à accéder à n'importe quel volume de clichés instantanés présents dans l'image médico-légale.

Pour le script Python, nous devons installer des modules tiers, à savoir pytsk3, pyewf, unicodecsv, pyvshadow et vss. Nous pouvons suivre les étapes ci-dessous pour extraire les informations des fichiers VSS

  • Tout d'abord, accédez au volume de l'image brute et identifiez toutes les partitions NTFS.

  • Ensuite, extrayez les informations de ces clichés instantanés en les parcourant.

  • Maintenant, nous devons enfin créer une liste de fichiers de données dans les instantanés.

Code Python

Voyons comment utiliser le code Python à cette fin -

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

from __future__ import print_function
import argparse
from datetime import datetime, timedelta

import os
import pytsk3
import pyewf
import pyvshadow
import sys
import unicodecsv as csv

from utility import vss
from utility.pytskutil import TSKUtil
from utility import pytskutil

Maintenant, fournissez des arguments pour le gestionnaire de ligne de commande. Ici, il acceptera deux arguments - le premier est le chemin d'accès au fichier de preuve et le second est le fichier de sortie.

if __name__ == "__main__":
   parser = argparse.ArgumentParser('Parsing Shadow Copies')
   parser.add_argument("EVIDENCE_FILE", help = "Evidence file path")
   parser.add_argument("OUTPUT_CSV", help = "Output CSV with VSS file listing")
   args = parser.parse_args()

Maintenant, validez l'existence du chemin du fichier d'entrée et séparez également le répertoire du fichier de sortie.

directory = os.path.dirname(args.OUTPUT_CSV)
if not os.path.exists(directory) and directory != "":
   os.makedirs(directory)
if os.path.exists(args.EVIDENCE_FILE) and \ os.path.isfile(args.EVIDENCE_FILE):
   main(args.EVIDENCE_FILE, args.OUTPUT_CSV)
else:
   print("[-] Supplied input file {} does not exist or is not a "
   "file".format(args.EVIDENCE_FILE))
   
   sys.exit(1)

Maintenant, interagissez avec le volume du fichier de preuves en créant le TSKUtilobjet. Cela peut être fait avec l'aide demain() méthode comme suit -

def main(evidence, output):
   tsk_util = TSKUtil(evidence, "raw")
   img_vol = tsk_util.return_vol()

if img_vol is not None:
   for part in img_vol:
      if tsk_util.detect_ntfs(img_vol, part):
         print("Exploring NTFS Partition for VSS")
         explore_vss(evidence, part.start * img_vol.info.block_size,output)
      else:
         print("[-] Must be a physical preservation to be compatible ""with this script")
         sys.exit(2)

Maintenant, définissez une méthode pour explorer le fichier d'ombre de volume analysé comme suit -

def explore_vss(evidence, part_offset, output):
   vss_volume = pyvshadow.volume()
   vss_handle = vss.VShadowVolume(evidence, part_offset)
   vss_count = vss.GetVssStoreCount(evidence, part_offset)
   
   if vss_count > 0:
      vss_volume.open_file_object(vss_handle)
      vss_data = []
      
      for x in range(vss_count):
         print("Gathering data for VSC {} of {}".format(x, vss_count))
         vss_store = vss_volume.get_store(x)
         image = vss.VShadowImgInfo(vss_store)
         vss_data.append(pytskutil.openVSSFS(image, x))
write_csv(vss_data, output)

Enfin, définissez la méthode d'écriture du résultat dans une feuille de calcul comme suit -

def write_csv(data, output):
   if data == []:
      print("[-] No output results to write")
      sys.exit(3)
   print("[+] Writing output to {}".format(output))
   if os.path.exists(output):
      append = True
with open(output, "ab") as csvfile:
      csv_writer = csv.writer(csvfile)
      headers = ["VSS", "File", "File Ext", "File Type", "Create Date",
         "Modify Date", "Change Date", "Size", "File Path"]
      if not append:
         csv_writer.writerow(headers)
      for result_list in data:
         csv_writer.writerows(result_list)

Une fois que vous avez exécuté avec succès ce script Python, nous obtiendrons les informations résidant dans VSS dans une feuille de calcul.

Jusqu'à présent, nous avons vu comment obtenir des artefacts dans Windows en utilisant Python. Dans ce chapitre, apprenons à étudier les artefacts basés sur les journaux à l'aide de Python.

introduction

Les artefacts basés sur des journaux sont le trésor d'informations qui peuvent être très utiles pour un expert en criminalistique numérique. Bien que nous ayons divers logiciels de surveillance pour collecter les informations, le principal problème pour analyser les informations utiles est que nous avons besoin de beaucoup de données.

Divers artefacts basés sur les journaux et enquête en Python

Dans cette section, discutons de divers artefacts basés sur les journaux et de leur investigation en Python -

Horodatages

L'horodatage transmet les données et l'heure de l'activité dans le journal. C'est l'un des éléments importants de tout fichier journal. Notez que ces données et valeurs d'heure peuvent être de différents formats.

Le script Python présenté ci-dessous prendra la date-heure brute comme entrée et fournit un horodatage formaté en sortie.

Pour ce script, nous devons suivre les étapes suivantes -

  • Tout d'abord, configurez les arguments qui prendront la valeur des données brutes avec la source des données et le type de données.

  • Maintenant, fournissez une classe pour fournir une interface commune pour les données dans différents formats de date.

Code Python

Voyons comment utiliser le code Python à cette fin -

Tout d'abord, importez les modules Python suivants -

from __future__ import print_function
from argparse import ArgumentParser, ArgumentDefaultsHelpFormatter
from datetime import datetime as dt
from datetime import timedelta

Maintenant, comme d'habitude, nous devons fournir un argument pour le gestionnaire de ligne de commande. Ici, il acceptera trois arguments, le premier serait la valeur de date à traiter, le second serait la source de cette valeur de date et le troisième serait son type -

if __name__ == '__main__':
   parser = ArgumentParser('Timestamp Log-based artifact')
   parser.add_argument("date_value", help="Raw date value to parse")
   parser.add_argument(
      "source", help = "Source format of date",choices = ParseDate.get_supported_formats())
   parser.add_argument(
      "type", help = "Data type of input value",choices = ('number', 'hex'), default = 'int')
   
   args = parser.parse_args()
   date_parser = ParseDate(args.date_value, args.source, args.type)
   date_parser.run()
   print(date_parser.timestamp)

Maintenant, nous devons définir une classe qui acceptera les arguments pour la valeur de la date, la source de la date et le type de valeur -

class ParseDate(object):
   def __init__(self, date_value, source, data_type):
      self.date_value = date_value
      self.source = source
      self.data_type = data_type
      self.timestamp = None

Nous allons maintenant définir une méthode qui agira comme un contrôleur tout comme la méthode main () -

def run(self):
   if self.source == 'unix-epoch':
      self.parse_unix_epoch()
   elif self.source == 'unix-epoch-ms':
      self.parse_unix_epoch(True)
   elif self.source == 'windows-filetime':
      self.parse_windows_filetime()
@classmethod
def get_supported_formats(cls):
   return ['unix-epoch', 'unix-epoch-ms', 'windows-filetime']

Maintenant, nous devons définir deux méthodes qui traiteront respectivement l'heure de l'époque Unix et FILETIME -

def parse_unix_epoch(self, milliseconds=False):
   if self.data_type == 'hex':
      conv_value = int(self.date_value)
      if milliseconds:
         conv_value = conv_value / 1000.0
   elif self.data_type == 'number':
      conv_value = float(self.date_value)
      if milliseconds:
         conv_value = conv_value / 1000.0
   else:
      print("Unsupported data type '{}' provided".format(self.data_type))
      sys.exit('1')
   ts = dt.fromtimestamp(conv_value)
   self.timestamp = ts.strftime('%Y-%m-%d %H:%M:%S.%f')
def parse_windows_filetime(self):
   if self.data_type == 'hex':
      microseconds = int(self.date_value, 16) / 10.0
   elif self.data_type == 'number':
      microseconds = float(self.date_value) / 10
   else:
      print("Unsupported data type '{}'   provided".format(self.data_type))
      sys.exit('1')
   ts = dt(1601, 1, 1) + timedelta(microseconds=microseconds)
   self.timestamp = ts.strftime('%Y-%m-%d %H:%M:%S.%f')

Après avoir exécuté le script ci-dessus, en fournissant un horodatage, nous pouvons obtenir la valeur convertie dans un format facile à lire.

Journaux du serveur Web

Du point de vue de l'expert en criminalistique numérique, les journaux du serveur Web sont un autre artefact important car ils peuvent obtenir des statistiques utiles sur l'utilisateur ainsi que des informations sur l'utilisateur et les emplacements géographiques. Voici le script Python qui créera une feuille de calcul, après le traitement des journaux du serveur Web, pour une analyse facile des informations.

Tout d'abord, nous devons importer les modules Python suivants -

from __future__ import print_function
from argparse import ArgumentParser, FileType

import re
import shlex
import logging
import sys
import csv

logger = logging.getLogger(__file__)

Maintenant, nous devons définir les modèles qui seront analysés à partir des journaux -

iis_log_format = [
   ("date", re.compile(r"\d{4}-\d{2}-\d{2}")),
   ("time", re.compile(r"\d\d:\d\d:\d\d")),
   ("s-ip", re.compile(
      r"((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.|$)){4}")),
   ("cs-method", re.compile(
      r"(GET)|(POST)|(PUT)|(DELETE)|(OPTIONS)|(HEAD)|(CONNECT)")),
   ("cs-uri-stem", re.compile(r"([A-Za-z0-1/\.-]*)")),
   ("cs-uri-query", re.compile(r"([A-Za-z0-1/\.-]*)")),
   ("s-port", re.compile(r"\d*")),
   ("cs-username", re.compile(r"([A-Za-z0-1/\.-]*)")),
   ("c-ip", re.compile(
      r"((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.|$)){4}")),
   ("cs(User-Agent)", re.compile(r".*")),
   ("sc-status", re.compile(r"\d*")),
   ("sc-substatus", re.compile(r"\d*")),
   ("sc-win32-status", re.compile(r"\d*")),
   ("time-taken", re.compile(r"\d*"))]

Maintenant, fournissez un argument pour le gestionnaire de ligne de commande. Ici, il acceptera deux arguments, le premier serait le journal IIS à traiter, le second serait le chemin du fichier CSV souhaité.

if __name__ == '__main__':
   parser = ArgumentParser('Parsing Server Based Logs')
   parser.add_argument('iis_log', help = "Path to IIS Log",type = FileType('r'))
   parser.add_argument('csv_report', help = "Path to CSV report")
   parser.add_argument('-l', help = "Path to processing log",default=__name__ + '.log')
   args = parser.parse_args()
   logger.setLevel(logging.DEBUG)
   msg_fmt = logging.Formatter(
      "%(asctime)-15s %(funcName)-10s ""%(levelname)-8s %(message)s")
   
   strhndl = logging.StreamHandler(sys.stdout)
   strhndl.setFormatter(fmt = msg_fmt)
   fhndl = logging.FileHandler(args.log, mode = 'a')
   fhndl.setFormatter(fmt = msg_fmt)
   
   logger.addHandler(strhndl)
   logger.addHandler(fhndl)
   logger.info("Starting IIS Parsing ")
   logger.debug("Supplied arguments: {}".format(", ".join(sys.argv[1:])))
   logger.debug("System " + sys.platform)
   logger.debug("Version " + sys.version)
   main(args.iis_log, args.csv_report, logger)
   iologger.info("IIS Parsing Complete")

Nous devons maintenant définir la méthode main () qui gérera le script pour les informations du journal en bloc -

def main(iis_log, report_file, logger):
   parsed_logs = []

for raw_line in iis_log:
   line = raw_line.strip()
   log_entry = {}

if line.startswith("#") or len(line) == 0:
   continue

if '\"' in line:
   line_iter = shlex.shlex(line_iter)
else:
   line_iter = line.split(" ")
   for count, split_entry in enumerate(line_iter):
      col_name, col_pattern = iis_log_format[count]

      if col_pattern.match(split_entry):
         log_entry[col_name] = split_entry
else:
   logger.error("Unknown column pattern discovered. "
      "Line preserved in full below")
      logger.error("Unparsed Line: {}".format(line))
      parsed_logs.append(log_entry)
      
      logger.info("Parsed {} lines".format(len(parsed_logs)))
      cols = [x[0] for x in iis_log_format]
      
      logger.info("Creating report file: {}".format(report_file))
      write_csv(report_file, cols, parsed_logs)
      logger.info("Report created")

Enfin, nous devons définir une méthode qui écrira la sortie dans une feuille de calcul -

def write_csv(outfile, fieldnames, data):
   with open(outfile, 'w', newline="") 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 journaux basés sur le serveur Web dans une feuille de calcul.

Analyse des fichiers importants à l'aide de YARA

YARA (Yet Another Recursive Algorithm) est un utilitaire de correspondance de modèles conçu pour l'identification des logiciels malveillants et la réponse aux incidents. Nous utiliserons YARA pour scanner les fichiers. Dans le script Python suivant, nous utiliserons YARA.

Nous pouvons installer YARA à l'aide de la commande suivante -

pip install YARA

Nous pouvons suivre les étapes ci-dessous pour utiliser les règles YARA pour analyser les fichiers -

  • Tout d'abord, configurez et compilez les règles YARA

  • Ensuite, scannez un seul fichier, puis parcourez les répertoires pour traiter les fichiers individuels.

  • Enfin, nous exporterons le résultat au format CSV.

Code Python

Voyons comment utiliser le code Python à cette fin -

Tout d'abord, nous devons importer les modules Python suivants -

from __future__ import print_function
from argparse import ArgumentParser, ArgumentDefaultsHelpFormatter

import os
import csv
import yara

Ensuite, fournissez un argument pour le gestionnaire de ligne de commande. Notez qu'ici, il acceptera deux arguments - le premier est le chemin d'accès aux règles YARA, le second est le fichier à analyser.

if __name__ == '__main__':
   parser = ArgumentParser('Scanning files by YARA')
   parser.add_argument(
      'yara_rules',help = "Path to Yara rule to scan with. May be file or folder path.")
   parser.add_argument('path_to_scan',help = "Path to file or folder to scan")
   parser.add_argument('--output',help = "Path to output a CSV report of scan results")
   args = parser.parse_args()
   main(args.yara_rules, args.path_to_scan, args.output)

Nous allons maintenant définir la fonction main () qui acceptera le chemin d'accès aux règles yara et au fichier à analyser -

def main(yara_rules, path_to_scan, output):
   if os.path.isdir(yara_rules):
      yrules = yara.compile(yara_rules)
   else:
      yrules = yara.compile(filepath=yara_rules)
   if os.path.isdir(path_to_scan):
      match_info = process_directory(yrules, path_to_scan)
   else:
      match_info = process_file(yrules, path_to_scan)
   columns = ['rule_name', 'hit_value', 'hit_offset', 'file_name',
   'rule_string', 'rule_tag']
   
   if output is None:
      write_stdout(columns, match_info)
   else:
      write_csv(output, columns, match_info)

Maintenant, définissez une méthode qui itérera dans le répertoire et transmettra le résultat à une autre méthode pour un traitement ultérieur -

def process_directory(yrules, folder_path):
   match_info = []
   for root, _, files in os.walk(folder_path):
      for entry in files:
         file_entry = os.path.join(root, entry)
         match_info += process_file(yrules, file_entry)
   return match_info

Ensuite, définissez deux fonctions. Notez que nous allons d'abord utilisermatch() méthode pour yrulesobjet et un autre rapportera les informations correspondantes à la console si l'utilisateur ne spécifie aucun fichier de sortie. Observez le code ci-dessous -

def process_file(yrules, file_path):
   match = yrules.match(file_path)
   match_info = []
   
   for rule_set in match:
      for hit in rule_set.strings:
         match_info.append({
            'file_name': file_path,
            'rule_name': rule_set.rule,
            'rule_tag': ",".join(rule_set.tags),
            'hit_offset': hit[0],
            'rule_string': hit[1],
            'hit_value': hit[2]
         })
   return match_info
def write_stdout(columns, match_info):
   for entry in match_info:
      for col in columns:
         print("{}: {}".format(col, entry[col]))
   print("=" * 30)

Enfin, nous définirons une méthode qui écrira la sortie dans un fichier CSV, comme indiqué ci-dessous -

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

Une fois que vous avez exécuté le script ci-dessus avec succès, nous pouvons fournir des arguments appropriés sur la ligne de commande et générer un rapport CSV.