- Python数字取证
- Python数字取证 - 首页
- 介绍
- Python入门
- 工件报告
- 移动设备取证
- 嵌入元数据调查
- 网络取证-I
- 网络取证-II
- 使用电子邮件进行调查
- Windows重要工件-I
- Windows重要工件-II
- Windows重要工件-III
- 基于日志工件的调查
- Python数字取证资源
- 快速指南
- Python数字取证 - 资源
- Python数字取证 - 讨论
嵌入元数据调查
在本章中,我们将详细了解如何使用Python数字取证技术调查嵌入式元数据。
介绍
嵌入式元数据是指存储在同一文件中的有关数据的信息,该文件包含由该数据描述的对象。换句话说,它是存储在数字文件本身中的有关数字资产的信息。它始终与文件关联,并且永远无法分离。
在数字取证的情况下,我们无法提取有关特定文件的所有信息。另一方面,嵌入式元数据可以为我们提供对调查至关重要的信息。例如,文本文件的元数据可能包含有关作者、长度、编写日期甚至该文档简要摘要的信息。数字图像可能包含诸如图像长度、快门速度等元数据。
包含元数据属性的工件及其提取
在本节中,我们将学习有关包含元数据属性的各种工件以及使用Python提取它们的过程。
音频和视频
这是两种非常常见的包含嵌入式元数据的工件。可以提取此元数据以用于调查目的。
您可以使用以下Python脚本从音频或MP3文件以及视频或MP4文件中提取常见属性或元数据。
请注意,对于此脚本,我们需要安装一个名为mutagen的第三方python库,该库允许我们从音频和视频文件中提取元数据。可以使用以下命令安装它:
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信息。我们可以使用第三方Python库提取此GPS信息。您可以使用以下Python脚本执行此操作:
首先,下载名为**Python Imaging Library (PIL)**的第三方python库,如下所示:
pip install pillow
这将帮助我们从图像中提取元数据。
我们还可以将嵌入在图像中的GPS详细信息写入KML文件,但为此我们需要下载名为**simplekml**的第三方Python库,如下所示:
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**。我们还需要一个函数来将PIL库提供的度分秒(DMS)元组坐标转换为十进制。可以按如下方式完成:
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
现在,从**simplekml**库中初始化**kml**对象,如下所示:
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代码提取元数据:
首先,安装名为**PyPDF2**的第三方Python库以读取存储在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)
如果PDF是由多个软件创建的,我们也可以定义**custom_print()**方法,如下所示:
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。这些文件拥有大量的元数据,可以揭示有关其作者信息和历史的有趣信息。
请注意,Word(.docx)、Excel(.xlsx)和PowerPoint(.pptx)的2007格式的元数据存储在XML文件中。我们可以使用下面显示的Python脚本在Python中处理这些XML文件:
首先,导入所需的库,如下所示:
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或更高版本的文档。