- 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或更高版本的文件。