Исследование встроенных метаданных

В этой главе мы подробно узнаем об исследовании встроенных метаданных с помощью цифровой криминалистики Python.

Введение

Встроенные метаданные - это информация о данных, хранящихся в том же файле, в котором есть объект, описанный этими данными. Другими словами, это информация о цифровом активе, хранящаяся в самом цифровом файле. Он всегда связан с файлом и никогда не может быть разделен.

В случае цифровой криминалистики мы не можем извлечь всю информацию о конкретном файле. С другой стороны, встроенные метаданные могут предоставить нам важную информацию для расследования. Например, метаданные текстового файла могут содержать информацию об авторе, его длине, дате написания и даже краткое изложение этого документа. Цифровое изображение может включать в себя метаданные, такие как длина изображения, выдержка и т. Д.

Артефакты, содержащие атрибуты метаданных, и их извлечение

В этом разделе мы узнаем о различных артефактах, содержащих атрибуты метаданных, и о процессе их извлечения с помощью Python.

Аудио и видео

Это два очень распространенных артефакта со встроенными метаданными. Эти метаданные могут быть извлечены с целью расследования.

Вы можете использовать следующий скрипт Python для извлечения общих атрибутов или метаданных из аудио- или MP3-файла и видео или MP4-файла.

Обратите внимание, что для этого скрипта нам необходимо установить стороннюю библиотеку python с именем mutagen, которая позволяет нам извлекать метаданные из аудио- и видеофайлов. Его можно установить с помощью следующей команды -

pip install mutagen

Некоторые из полезных библиотек, которые нам нужно импортировать для этого скрипта Python, следующие:

from __future__ import print_function

import argparse
import json
import mutagen

Обработчик командной строки примет один аргумент, представляющий путь к файлам MP3 или MP4. Затем мы будем использоватьmutagen.file() метод, чтобы открыть дескриптор файла следующим образом -

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)

Теперь нам нужно использовать два дескриптора: один для извлечения данных из MP3 и один для извлечения данных из файла MP4. Мы можем определить эти ручки следующим образом:

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

Теперь нам нужно перебрать этот файл MP4 следующим образом:

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

Приведенный выше сценарий предоставит нам дополнительную информацию о файлах MP3 и MP4.

Картинки

Изображения могут содержать разные типы метаданных в зависимости от формата файла. Однако большинство изображений содержат информацию GPS. Мы можем извлечь эту информацию GPS с помощью сторонних библиотек Python. Вы можете использовать следующий скрипт Python, который можно использовать для того же -

Сначала загрузите стороннюю библиотеку Python с именем Python Imaging Library (PIL) следующим образом -

pip install pillow

Это поможет нам извлекать метаданные из изображений.

Мы также можем записать данные GPS, встроенные в изображения, в файл KML, но для этого нам нужно загрузить стороннюю библиотеку Python с именем simplekml следующим образом -

pip install simplekml

В этом скрипте сначала нам нужно импортировать следующие библиотеки -

from __future__ import print_function
import argparse

from PIL import Image
from PIL.ExifTags import TAGS

import simplekml
import sys

Теперь обработчик командной строки примет один позиционный аргумент, который в основном представляет путь к файлу с фотографиями.

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

Теперь нам нужно указать URL-адреса, которые будут заполнять информацию о координатах. URL-адресаgmaps и open_maps. Нам также нужна функция для преобразования координат кортежа в градусах-минутах (DMS), предоставляемых библиотекой PIL, в десятичные числа. Это можно сделать следующим образом -

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

Теперь мы будем использовать image.open() функция, чтобы открыть файл как объект 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

Найдя GPSInfo тег, мы сохраним ссылку GPS и обработаем координаты с process_coords() метод.

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

Теперь инициируйте kml объект из simplekml библиотека следующим образом -

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

Теперь мы можем распечатать координаты из обработанной информации следующим образом:

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

PDF-документы

Документы PDF имеют широкий спектр носителей, включая изображения, текст, формы и т. Д. Когда мы извлекаем встроенные метаданные в документы PDF, мы можем получить результирующие данные в формате, называемом Extensible Metadata Platform (XMP). Мы можем извлечь метаданные с помощью следующего кода Python -

Сначала установите стороннюю библиотеку Python с именем PyPDF2для чтения метаданных, хранящихся в формате XMP. Его можно установить следующим образом -

pip install PyPDF2

Теперь импортируйте следующие библиотеки для извлечения метаданных из файлов PDF:

from __future__ import print_function
from argparse import ArgumentParser, FileType

import datetime
from PyPDF2 import PdfFileReader
import sys

Теперь обработчик командной строки примет один позиционный аргумент, который в основном представляет путь к файлу PDF-файла.

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

Теперь мы можем использовать getXmpMetadata() для предоставления объекта, содержащего доступные метаданные, следующим образом:

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

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

Мы можем использовать custom_print() метод для извлечения и печати соответствующих значений, таких как заголовок, создатель, участник и т. д., следующим образом:

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)

Мы также можем определить custom_print() метод в случае, если PDF создается с использованием нескольких программ, а именно:

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

Мы также можем извлечь любое другое настраиваемое свойство, сохраненное программным обеспечением, следующим образом:

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

Приведенный выше сценарий прочитает документ PDF и распечатает метаданные, хранящиеся в формате XMP, включая некоторые настраиваемые свойства, хранящиеся в программном обеспечении, с помощью которого был создан этот PDF-файл.

Исполняемые файлы Windows

Иногда мы можем столкнуться с подозрительным или несанкционированным исполняемым файлом. Но для целей исследования это может быть полезно из-за встроенных метаданных. Мы можем получить информацию, такую ​​как его местонахождение, его назначение и другие атрибуты, такие как производитель, дата компиляции и т. Д. С помощью следующего скрипта Python мы можем получить дату компиляции, полезные данные из заголовков и импортированных, а также экспортированных символов.

Для этого сначала установите стороннюю библиотеку Python. pefile. Это можно сделать следующим образом -

pip install pefile

После успешной установки импортируйте следующие библиотеки следующим образом:

from __future__ import print_function

import argparse
from datetime import datetime
from pefile import PE

Теперь обработчик командной строки примет один позиционный аргумент, который в основном представляет путь к исполняемому файлу. Вы также можете выбрать стиль вывода, будь то подробный и подробный или упрощенный. Для этого вам нужно указать необязательный аргумент, как показано ниже -

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

Теперь мы загрузим входной исполняемый файл с помощью класса PE. Мы также сбрасываем исполняемые данные в объект словаря, используяdump_dict() метод.

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

Мы можем извлечь основные метаданные файла, такие как встроенное авторство, версия и время компиляции, используя код, показанный ниже -

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

Мы можем извлечь полезные данные из заголовков следующим образом:

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

Теперь извлеките список импорта и экспорта из исполняемых файлов, как показано ниже -

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

Теперь распечатайте exports, names и addresses используя код, как показано ниже -

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

Приведенный выше сценарий извлечет основные метаданные, информацию из заголовков из исполняемых файлов Windows.

Метаданные документа Office

Большая часть работы на компьютере выполняется в трех приложениях MS Office - Word, PowerPoint и Excel. Эти файлы содержат огромные метаданные, которые могут предоставить интересную информацию об их авторстве и истории.

Обратите внимание, что метаданные из формата 2007 Word (.docx), excel (.xlsx) и powerpoint (.pptx) хранятся в файле XML. Мы можем обработать эти XML-файлы в Python с помощью следующего скрипта Python, показанного ниже:

Сначала импортируйте необходимые библиотеки, как показано ниже -

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

Теперь проверьте, является ли файл ZIP-файлом. В противном случае возникает ошибка. Теперь откройте файл и извлеките ключевые элементы для обработки, используя следующий код -

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

Теперь создайте словарь для начала извлечения метаданных -

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

Использовать iterchildren() метод доступа к каждому из тегов в файле 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))

Точно так же сделайте это для файла app.xml, который содержит статистическую информацию о содержимом документа -

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

Теперь, после запуска приведенного выше сценария, мы можем получить различные сведения о конкретном документе. Обратите внимание, что мы можем применить этот сценарий только к документам Office 2007 или более поздней версии.