Menyelidiki Metadata Tersemat

Dalam bab ini, kita akan mempelajari secara detail tentang menyelidiki metadata tersemat menggunakan forensik digital Python.

pengantar

Metadata tertanam adalah informasi tentang data yang disimpan dalam file yang sama yang objeknya dijelaskan oleh data tersebut. Dengan kata lain, ini adalah informasi tentang aset digital yang disimpan dalam file digital itu sendiri. Itu selalu dikaitkan dengan file dan tidak pernah dapat dipisahkan.

Dalam kasus forensik digital, kami tidak dapat mengekstrak semua informasi tentang file tertentu. Di sisi lain, metadata yang disematkan dapat memberi kami informasi yang penting untuk penyelidikan. Misalnya, metadata file teks mungkin berisi informasi tentang penulis, panjangnya, tanggal tertulis, dan bahkan ringkasan singkat tentang dokumen itu. Gambar digital mungkin menyertakan metadata seperti panjang gambar, kecepatan rana, dll.

Artefak yang Mengandung Atribut Metadata dan Ekstraksi mereka

Di bagian ini, kita akan belajar tentang berbagai artefak yang mengandung atribut metadata dan proses ekstraksinya menggunakan Python.

Audio dan Video

Ini adalah dua artefak yang sangat umum yang memiliki metadata yang disematkan. Metadata ini dapat diekstraksi untuk tujuan penyelidikan.

Anda dapat menggunakan skrip Python berikut untuk mengekstrak atribut atau metadata umum dari file audio atau MP3 dan video atau file MP4.

Perhatikan bahwa untuk skrip ini, kita perlu menginstal pustaka python pihak ketiga bernama mutagen yang memungkinkan kita mengekstrak metadata dari file audio dan video. Itu dapat diinstal dengan bantuan perintah berikut -

pip install mutagen

Beberapa pustaka berguna yang perlu kita impor untuk skrip Python ini adalah sebagai berikut -

from __future__ import print_function

import argparse
import json
import mutagen

Penangan baris perintah akan mengambil satu argumen yang mewakili jalur ke file MP3 atau MP4. Kemudian, kami akan menggunakanmutagen.file() metode untuk membuka pegangan ke file sebagai berikut -

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)

Sekarang, kita perlu menggunakan dua pegangan, satu untuk mengekstrak data dari MP3 dan satu lagi untuk mengekstrak data dari file MP4. Kita dapat mendefinisikan pegangan ini sebagai berikut -

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

Sekarang, kita perlu mengulang melalui file MP4 ini sebagai berikut -

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

Skrip di atas akan memberi kita informasi tambahan tentang file MP3 dan juga MP4.

Gambar-gambar

Gambar mungkin berisi jenis metadata yang berbeda tergantung pada format filenya. Namun, sebagian besar gambar menyematkan informasi GPS. Kami dapat mengekstrak informasi GPS ini dengan menggunakan pustaka Python pihak ketiga. Anda dapat menggunakan skrip Python berikut dapat digunakan untuk melakukan hal yang sama -

Pertama, unduh pustaka python pihak ketiga bernama Python Imaging Library (PIL) sebagai berikut -

pip install pillow

Ini akan membantu kami mengekstrak metadata dari gambar.

Kami juga dapat menulis detail GPS yang disematkan dalam gambar ke file KML, tetapi untuk ini kami perlu mengunduh pustaka Python pihak ketiga bernama simplekml sebagai berikut -

pip install simplekml

Dalam skrip ini, pertama-tama kita perlu mengimpor pustaka berikut -

from __future__ import print_function
import argparse

from PIL import Image
from PIL.ExifTags import TAGS

import simplekml
import sys

Sekarang, penangan baris perintah akan menerima satu argumen posisi yang pada dasarnya mewakili jalur file foto.

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

Sekarang, kita perlu menentukan URL yang akan mengisi informasi koordinat. URL-nya adalahgmaps dan open_maps. Kita juga membutuhkan fungsi untuk mengubah koordinat tupel derajat menit detik (DMS), yang disediakan oleh perpustakaan PIL, menjadi desimal. Itu dapat dilakukan sebagai berikut -

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

Sekarang, kami akan menggunakan image.open() berfungsi untuk membuka file sebagai objek 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

Setelah menemukan GPSInfo tag, kami akan menyimpan referensi GPS dan memproses koordinat dengan process_coords() metode.

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

Sekarang, mulai kml objek dari simplekml perpustakaan sebagai berikut -

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

Kami sekarang dapat mencetak koordinat dari informasi yang diproses sebagai berikut -

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

Dokumen PDF

Dokumen PDF memiliki berbagai macam media termasuk gambar, teks, formulir, dll. Saat kami mengekstrak metadata yang disematkan dalam dokumen PDF, kami mungkin mendapatkan data yang dihasilkan dalam format yang disebut Platform Metadata yang Dapat Diperluas (XMP). Kita dapat mengekstrak metadata dengan bantuan kode Python berikut -

Pertama, instal pustaka Python pihak ketiga bernama PyPDF2untuk membaca metadata yang disimpan dalam format XMP. Itu dapat diinstal sebagai berikut -

pip install PyPDF2

Sekarang, impor pustaka berikut untuk mengekstrak metadata dari file PDF -

from __future__ import print_function
from argparse import ArgumentParser, FileType

import datetime
from PyPDF2 import PdfFileReader
import sys

Sekarang, penangan baris perintah akan menerima satu argumen posisi yang pada dasarnya mewakili jalur file dari file PDF.

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

Sekarang kita bisa menggunakan getXmpMetadata() metode untuk menyediakan objek yang berisi metadata yang tersedia sebagai berikut -

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

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

Kita bisa gunakan custom_print() metode untuk mengekstrak dan mencetak nilai yang relevan seperti judul, pencipta, kontributor dll sebagai berikut -

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)

Kami juga bisa mendefinisikan custom_print() metode jika PDF dibuat menggunakan beberapa perangkat lunak sebagai berikut -

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

Kami juga dapat mengekstrak properti khusus lainnya yang disimpan oleh perangkat lunak sebagai berikut -

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

Skrip di atas akan membaca dokumen PDF dan akan mencetak metadata yang disimpan dalam format XMP termasuk beberapa properti khusus yang disimpan oleh perangkat lunak dengan bantuan PDF yang telah dibuat.

File Windows Executables

Terkadang kami mungkin menemukan file eksekusi yang mencurigakan atau tidak sah. Tetapi untuk tujuan penyelidikan, ini mungkin berguna karena metadata yang disematkan. Kita bisa mendapatkan informasi seperti lokasinya, tujuannya dan atribut lain seperti pabrikan, tanggal kompilasi, dll. Dengan bantuan mengikuti skrip Python kita bisa mendapatkan tanggal kompilasi, data yang berguna dari header dan simbol yang diimpor serta diekspor.

Untuk tujuan ini, pertama instal pustaka Python pihak ketiga pefile. Itu dapat dilakukan sebagai berikut -

pip install pefile

Setelah Anda berhasil menginstal ini, impor pustaka berikut sebagai berikut -

from __future__ import print_function

import argparse
from datetime import datetime
from pefile import PE

Sekarang, penangan baris perintah akan menerima satu argumen posisi yang pada dasarnya mewakili jalur file dari file yang dapat dieksekusi. Anda juga dapat memilih gaya keluaran, apakah Anda membutuhkannya secara mendetail dan bertele-tele atau dengan cara yang disederhanakan. Untuk ini, Anda perlu memberikan argumen opsional seperti yang ditunjukkan di bawah ini -

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

Sekarang, kita akan memuat file yang dapat dieksekusi dengan menggunakan kelas PE. Kami juga akan membuang data yang dapat dieksekusi ke objek kamus dengan menggunakandump_dict() metode.

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

Kami dapat mengekstrak metadata file dasar seperti kepengarangan yang disematkan, versi dan waktu kompilasi menggunakan kode yang ditunjukkan di bawah ini -

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

Kami dapat mengekstrak data yang berguna dari header sebagai berikut -

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

Sekarang, ekstrak daftar impor dan ekspor dari file yang dapat dieksekusi seperti yang ditunjukkan di bawah ini -

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

Sekarang, cetak exports, names dan addresses menggunakan kode seperti yang ditunjukkan di bawah ini -

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

Skrip di atas akan mengekstrak metadata dasar, informasi dari header dari file windows yang dapat dieksekusi.

Metadata Dokumen Office

Sebagian besar pekerjaan di komputer dilakukan dalam tiga aplikasi MS Office - Word, PowerPoint dan Excel. File-file ini memiliki metadata yang sangat besar, yang dapat mengungkapkan informasi menarik tentang penulis dan sejarahnya.

Perhatikan bahwa metadata dari format 2007 kata (.docx), excel (.xlsx) dan powerpoint (.pptx) disimpan dalam file XML. Kami dapat memproses file XML ini dengan Python dengan bantuan skrip Python berikut yang ditunjukkan di bawah ini -

Pertama, impor pustaka yang diperlukan seperti yang ditunjukkan di bawah ini -

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

Sekarang, periksa apakah file tersebut adalah file ZIP. Jika tidak, ajukan kesalahan. Sekarang, buka file dan ekstrak elemen kunci untuk diproses menggunakan kode berikut -

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

Sekarang, buat kamus untuk memulai ekstraksi metadata -

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

Menggunakan iterchildren() metode untuk mengakses setiap tag dalam file 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))

Demikian pula, lakukan ini untuk file app.xml yang berisi informasi statistik tentang konten dokumen -

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

Sekarang setelah menjalankan skrip di atas, kita bisa mendapatkan detail berbeda tentang dokumen tertentu. Perhatikan bahwa kami hanya dapat menerapkan skrip ini pada dokumen Office 2007 atau versi yang lebih baru.