В этой главе мы подробно узнаем об исследовании встроенных метаданных с использованием цифровой криминалистики 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, мы можем получить результирующие данные в формате, называемом платформой расширяемых метаданных (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 () для извлечения и печати соответствующих значений, таких как title, creator, contributor и т. Д. Следующим образом:
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()
Теперь напечатайте экспорт , имена и адреса, используя код, как показано ниже —
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.
Метаданные офисного документа
Большая часть работы на компьютере выполняется в трех приложениях MS Office — Word, PowerPoint и Excel. Эти файлы обладают огромными метаданными, которые могут предоставить интересную информацию об их авторстве и истории.
Обратите внимание, что метаданные из формата word (.docx), excel (.xlsx) и powerpoint (.pptx) 2007 года хранятся в файле 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 или более поздней версии.