Examen des métadonnées intégrées

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 les 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 d'accès aux 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, y compris 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 les 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, les données utiles 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 informatique 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.