การตรวจสอบข้อมูลเมตาที่ฝังไว้
ในบทนี้เราจะเรียนรู้รายละเอียดเกี่ยวกับการตรวจสอบข้อมูลเมตาที่ฝังไว้โดยใช้ Python digital forensics
บทนำ
ข้อมูลเมตาแบบฝังคือข้อมูลเกี่ยวกับข้อมูลที่จัดเก็บในไฟล์เดียวกันซึ่งมีออบเจ็กต์ที่อธิบายโดยข้อมูลนั้น กล่าวอีกนัยหนึ่งก็คือข้อมูลเกี่ยวกับสินทรัพย์ดิจิทัลที่เก็บไว้ในไฟล์ดิจิทัลนั้นเอง มันเชื่อมโยงกับไฟล์เสมอและไม่สามารถแยกออกจากกันได้
ในกรณีของนิติดิจิทัลเราไม่สามารถดึงข้อมูลทั้งหมดเกี่ยวกับไฟล์ใดไฟล์หนึ่งได้ ในอีกด้านหนึ่งข้อมูลเมตาที่ฝังไว้สามารถให้ข้อมูลที่สำคัญต่อการตรวจสอบแก่เรา ตัวอย่างเช่นข้อมูลเมตาของไฟล์ข้อความอาจมีข้อมูลเกี่ยวกับผู้แต่งความยาววันที่เขียนและแม้แต่สรุปสั้น ๆ เกี่ยวกับเอกสารนั้น ภาพดิจิทัลอาจมีข้อมูลเมตาเช่นความยาวของภาพความเร็วชัตเตอร์เป็นต้น
อาร์ติแฟกต์ที่มีแอตทริบิวต์ข้อมูลเมตาและการแยก
ในส่วนนี้เราจะเรียนรู้เกี่ยวกับสิ่งประดิษฐ์ต่างๆที่มีแอตทริบิวต์ข้อมูลเมตาและกระบวนการแยกโดยใช้ 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 หรือเวอร์ชันที่ใหม่กว่าเท่านั้น