Python-digital-forensics-important-artifacts-in-windows

提供:Dev Guides
移動先:案内検索

Windows-Iの重要なアーティファクト

この章では、Microsoft Windowsのフォレンジックに関連するさまざまな概念と、調査プロセスで調査者が取得できる重要なアーティファクトについて説明します。

前書き

アーティファクトとは、コンピューターユーザーが実行したアクティビティに関連する重要な情報を持つコンピューターシステム内のオブジェクトまたは領域です。 この情報の種類と場所は、オペレーティングシステムによって異なります。 フォレンジック分析中、これらのアーティファクトは、調査員の観察の承認または不承認において非常に重要な役割を果たします。

フォレンジックのためのWindowsアーティファクトの重要性

Windowsのアーティファクトは、次の理由により重要であると想定しています-

  • 世界のトラフィックの約90%は、Windowsをオペレーティングシステムとして使用しているコンピューターからのものです。 そのため、デジタルフォレンジック検査官にとって、Windowsアーティファクトは非常に重要です。
  • Windowsオペレーティングシステムには、コンピューターシステムでのユーザーアクティビティに関連するさまざまな種類の証拠が保存されます。 これは、デジタルフォレンジックにとってWindowsアーティファクトの重要性を示すもう1つの理由です。
  • 多くの場合、調査員は、ユーザーが作成したデータのような古い領域と従来の領域を中心に調査を展開します。 Windowsのアーティファクトは、システムが作成したデータやアーティファクトのような非伝統的な領域に調査を導くことができます。
  • 調査担当者だけでなく、非公式の調査を行う企業や個人にも役立つ、大量のアーティファクトがWindowsによって提供されます。
  • 近年のサイバー犯罪の増加は、Windowsアーティファクトが重要であるもう1つの理由です。

WindowsアーティファクトとPythonスクリプト

このセクションでは、Windowsアーティファクトとそれらから情報を取得するPythonスクリプトについて説明します。

ごみ箱

フォレンジック調査のための重要なWindowsアーティファクトの1つです。 Windowsのごみ箱には、ユーザーによって削除されたが、まだシステムによって物理的に削除されていないファイルが含まれています。 ユーザーがファイルをシステムから完全に削除したとしても、それは調査の重要なソースとなります。 これは、削除されたファイルから、審査官が元のファイルパスやごみ箱に送信された時刻などの貴重な情報を抽出できるためです。

ごみ箱の証拠の保存は、Windowsのバージョンに依存することに注意してください。 次のPythonスクリプトでは、2つのファイルを作成するWindows 7を扱います。リサイクルファイルの実際のコンテンツを含む $ R ファイルと、元のファイル名、パス、ファイルサイズを含む $ I ファイルファイルが削除されたとき。

Pythonスクリプトの場合、サードパーティのモジュール、つまり pytsk3、pyewf および unicodecsv をインストールする必要があります。 pip を使用してそれらをインストールできます。 次の手順に従って、ごみ箱から情報を抽出できます-

  • まず、再帰メソッドを使用して $ Recycle.bin フォルダーをスキャンし、 $ I で始まるすべてのファイルを選択する必要があります。
  • 次に、ファイルの内容を読み取り、使用可能なメタデータ構造を解析します。
  • 次に、関連する$ Rファイルを検索します。
  • 最後に、レビューのために結果をCSVファイルに書き込みます。

この目的のためにPythonコードを使用する方法を見てみましょう-

まず、次のPythonライブラリをインポートする必要があります-

from __future__ import print_function
from argparse import ArgumentParser

import datetime
import os
import struct

from utility.pytskutil import TSKUtil
import unicodecsv as csv

次に、コマンドラインハンドラーの引数を指定する必要があります。 以下に示すように、ここでは3つの引数を受け入れることに注意してください。最初は証拠ファイルへのパス、2番目は証拠ファイルのタイプ、3番目はCSVレポートへの望ましい出力パスです-

if __name__ == '__main__':
   parser = argparse.ArgumentParser('Recycle Bin evidences')
   parser.add_argument('EVIDENCE_FILE', help = "Path to evidence file")
   parser.add_argument('IMAGE_TYPE', help = "Evidence file format",
   choices = ('ewf', 'raw'))
   parser.add_argument('CSV_REPORT', help = "Path to CSV report")
   args = parser.parse_args()
   main(args.EVIDENCE_FILE, args.IMAGE_TYPE, args.CSV_REPORT)

ここで、すべての処理を処理する* main()関数を定義します。 次のように *$ I ファイルを検索します-

def main(evidence, image_type, report_file):
   tsk_util = TSKUtil(evidence, image_type)
   dollar_i_files = tsk_util.recurse_files("$I", path = '/$Recycle.bin',logic = "startswith")

   if dollar_i_files is not None:
      processed_files = process_dollar_i(tsk_util, dollar_i_files)
      write_csv(report_file,['file_path', 'file_size', 'deleted_time','dollar_i_file', 'dollar_r_file', 'is_directory'],processed_files)
   else:
      print("No $I files found")

さて、 $ I ファイルが見つかった場合は、* process_dollar_i()関数に送信する必要があります。この関数は、以下に示すように、 *$ I ファイルのリストと tsk_util オブジェクトを受け入れます。

def process_dollar_i(tsk_util, dollar_i_files):
   processed_files = []

   for dollar_i in dollar_i_files:
      file_attribs = read_dollar_i(dollar_i[2])
      if file_attribs is None:
         continue
      file_attribs['dollar_i_file'] = os.path.join('/$Recycle.bin', dollar_i[1][1:])

今、次のように$ Rファイルを検索します-

recycle_file_path = os.path.join('/$Recycle.bin',dollar_i[1].rsplit("/", 1)[0][1:])
dollar_r_files = tsk_util.recurse_files(
   "$R" + dollar_i[0][2:],path = recycle_file_path, logic = "startswith")

   if dollar_r_files is None:
      dollar_r_dir = os.path.join(recycle_file_path,"$R" + dollar_i[0][2:])
      dollar_r_dirs = tsk_util.query_directory(dollar_r_dir)

   if dollar_r_dirs is None:
      file_attribs['dollar_r_file'] = "Not Found"
      file_attribs['is_directory'] = 'Unknown'

   else:
      file_attribs['dollar_r_file'] = dollar_r_dir
      file_attribs['is_directory'] = True

   else:
      dollar_r = [os.path.join(recycle_file_path, r[1][1:])for r in dollar_r_files]
      file_attribs['dollar_r_file'] = ";".join(dollar_r)
      file_attribs['is_directory'] = False
      processed_files.append(file_attribs)
   return processed_files

次に、* read_dollar_i()メソッドを定義して、 *$ I ファイルを読み取ります。つまり、メタデータを解析します。 * read_random()メソッドを使用して、署名の最初の8バイトを読み取ります。 署名が一致しない場合、これはnoneを返します。 その後、有効なファイルである場合は、 *$ I ファイルから値を読み取って展開する必要があります。

def read_dollar_i(file_obj):
   if file_obj.read_random(0, 8) != '\x01\x00\x00\x00\x00\x00\x00\x00':
      return None
   raw_file_size = struct.unpack('<q', file_obj.read_random(8, 8))
   raw_deleted_time = struct.unpack('<q',   file_obj.read_random(16, 8))
   raw_file_path = file_obj.read_random(24, 520)

今、これらのファイルを抽出した後、以下に示すように* sizeof_fmt()*関数を使用して整数を人間が読める値に解釈する必要があります-

file_size = sizeof_fmt(raw_file_size[0])
deleted_time = parse_windows_filetime(raw_deleted_time[0])

file_path = raw_file_path.decode("utf16").strip("\x00")
return {'file_size': file_size, 'file_path': file_path,'deleted_time': deleted_time}

今、私たちは次のように* sizeof_fmt()*関数を定義する必要があります-

def sizeof_fmt(num, suffix = 'B'):
   for unit in ['', 'Ki', 'Mi', 'Gi', 'Ti', 'Pi', 'Ei', 'Zi']:
      if abs(num) < 1024.0:
         return "%3.1f%s%s" % (num, unit, suffix)
      num/= 1024.0
   return "%.1f%s%s" % (num, 'Yi', suffix)

今、次のようにフォーマットされた日付と時刻に解釈された整数の関数を定義します-

def parse_windows_filetime(date_value):
   microseconds = float(date_value)/10
   ts = datetime.datetime(1601, 1, 1) + datetime.timedelta(
      microseconds = microseconds)
   return ts.strftime('%Y-%m-%d %H:%M:%S.%f')

今、私たちは次のように処理された結果をCSVファイルに書き込むために* write_csv()*メソッドを定義します-

def write_csv(outfile, fieldnames, data):
   with open(outfile, 'wb') as open_outfile:
      csvfile = csv.DictWriter(open_outfile, fieldnames)
      csvfile.writeheader()
      csvfile.writerows(data)

上記のスクリプトを実行すると、$ Iおよび$ Rファイルからデータが取得されます。

ポストイット

Windows付箋は、ペンと紙で書くという現実の習慣を置き換えます。 これらのノートは、色、フォントなどのさまざまなオプションを使用してデスクトップに浮かびました。 Windows 7では、付箋ファイルはOLEファイルとして保存されるため、次のPythonスクリプトでは、このOLEファイルを調査して付箋からメタデータを抽出します。

このPythonスクリプトでは、サードパーティのモジュール、つまり olefile、pytsk3、pyewf 、unicodecsvをインストールする必要があります。 コマンド pip を使用してそれらをインストールできます。

付箋ファイルから情報を抽出するために、以下で説明する手順、つまり StickyNote.sn を実行できます-

  • まず、証拠ファイルを開き、すべてのStickyNote.sntファイルを見つけます。
  • 次に、OLEストリームからメタデータとコンテンツを解析し、RTFコンテンツをファイルに書き込みます。
  • 最後に、このメタデータのCSVレポートを作成します。

Pythonコード

この目的のためにPythonコードを使用する方法を見てみましょう-

まず、次のPythonライブラリをインポートします-

from __future__ import print_function
from argparse import ArgumentParser

import unicodecsv as csv
import os
import StringIO

from utility.pytskutil import TSKUtil
import olefile

次に、このスクリプト全体で使用されるグローバル変数を定義します-

REPORT_COLS = ['note_id', 'created', 'modified', 'note_text', 'note_file']

次に、コマンドラインハンドラーの引数を指定する必要があります。 ここでは、3つの引数を受け入れることに注意してください。1つ目は証拠ファイルへのパス、2つ目は証拠ファイルのタイプ、3つ目は次のような望ましい出力パスです-

if __name__ == '__main__':
   parser = argparse.ArgumentParser('Evidence from Sticky Notes')
   parser.add_argument('EVIDENCE_FILE', help="Path to evidence file")
   parser.add_argument('IMAGE_TYPE', help="Evidence file format",choices=('ewf', 'raw'))
   parser.add_argument('REPORT_FOLDER', help="Path to report folder")
   args = parser.parse_args()
   main(args.EVIDENCE_FILE, args.IMAGE_TYPE, args.REPORT_FOLDER)

さて、以下に示すように、前のスクリプトに似た* main()*関数を定義します-

def main(evidence, image_type, report_folder):
   tsk_util = TSKUtil(evidence, image_type)
   note_files = tsk_util.recurse_files('StickyNotes.snt', '/Users','equals')

次に、結果のファイルを反復処理します。 次に、* parse_snt_file()関数を呼び出してファイルを処理し、次に write_note_rtf()*メソッドを使用してRTFファイルを書き込みます。

report_details = []
for note_file in note_files:
   user_dir = note_file[1].split("/")[1]
   file_like_obj = create_file_like_obj(note_file[2])
   note_data = parse_snt_file(file_like_obj)

   if note_data is None:
      continue
   write_note_rtf(note_data, os.path.join(report_folder, user_dir))
   report_details += prep_note_report(note_data, REPORT_COLS,"/Users" + note_file[1])
   write_csv(os.path.join(report_folder, 'sticky_notes.csv'), REPORT_COLS,report_details)

次に、このスクリプトで使用されるさまざまな関数を定義する必要があります。

まず、 pytsk ファイルオブジェクトを取得して、ファイルのサイズを読み取る* create_file_like_obj()関数を定義します。 次に、入力としてファイルのようなオブジェクトを受け入れ、付箋ファイルの読み取りと解釈に使用される parse_snt_file()*関数を定義します。

def parse_snt_file(snt_file):

   if not olefile.isOleFile(snt_file):
      print("This is not an OLE file")
      return None
   ole = olefile.OleFileIO(snt_file)
   note = {}

   for stream in ole.listdir():
      if stream[0].count("-") == 3:
         if stream[0] not in note:
            note[stream[0]] = {"created": ole.getctime(stream[0]),"modified": ole.getmtime(stream[0])}
         content = None
         if stream[1] == '0':
            content = ole.openstream(stream).read()
         elif stream[1] == '3':
            content = ole.openstream(stream).read().decode("utf-16")
         if content:
            note[stream[0]][stream[1]] = content
    return note

次に、次のように* write_note_rtf()*関数を定義してRTFファイルを作成します

def write_note_rtf(note_data, report_folder):
   if not os.path.exists(report_folder):
      os.makedirs(report_folder)

   for note_id, stream_data in note_data.items():
      fname = os.path.join(report_folder, note_id + ".rtf")
      with open(fname, 'w') as open_file:
         open_file.write(stream_data['0'])

次に、ネストされた辞書を、CSVスプレッドシートにより適した辞書のフラットリストに変換します。 * prep_note_report()関数を定義することにより実行されます。 最後に、 write_csv()*関数を定義します。

def prep_note_report(note_data, report_cols, note_file):
   report_details = []

   for note_id, stream_data in note_data.items():
      report_details.append({
         "note_id": note_id,
         "created": stream_data['created'],
         "modified": stream_data['modified'],
         "note_text": stream_data['3'].strip("\x00"),
         "note_file": note_file
      })
   return report_details
def write_csv(outfile, fieldnames, data):
   with open(outfile, 'wb') as open_outfile:
      csvfile = csv.DictWriter(open_outfile, fieldnames)
      csvfile.writeheader()
      csvfile.writerows(data)

上記のスクリプトを実行した後、Sticky Notesファイルからメタデータを取得します。

レジストリファイル

Windowsレジストリファイルには、法医学アナリストにとって貴重な情報の宝庫のような多くの重要な詳細が含まれています。 これは、オペレーティングシステムの構成、ユーザーアクティビティ、ソフトウェアのインストールなどに関する詳細を含む階層型データベースです。 次のPythonスクリプトでは、 SYSTEM および SOFTWARE ハイブから共通のベースライン情報にアクセスします。

このPythonスクリプトでは、サードパーティのモジュール、つまり pytsk3、pyewf および registry をインストールする必要があります。 pip を使用してそれらをインストールできます。

私たちは、Windowsレジストリから情報を抽出するための以下の手順に従うことができます-

  • 最初に、名前とパスで処理するレジストリハイブを見つけます。
  • 次に、StringIOおよびレジストリモジュールを使用してこれらのファイルを開きます。
  • 最後に、すべてのハイブを処理し、解析した値を解釈のためにコンソールに出力する必要があります。

Pythonコード

この目的のためにPythonコードを使用する方法を見てみましょう-

まず、次のPythonライブラリをインポートします-

from __future__ import print_function
from argparse import ArgumentParser

import datetime
import StringIO
import struct

from utility.pytskutil import TSKUtil
from Registry import Registry

次に、コマンドラインハンドラーの引数を指定します。 ここでは2つの引数を受け入れます-最初に証拠ファイルへのパス、2番目に証拠ファイルのタイプ、以下に示すように-

if __name__ == '__main__':
   parser = argparse.ArgumentParser('Evidence from Windows Registry')
   parser.add_argument('EVIDENCE_FILE', help = "Path to evidence file")
   parser.add_argument('IMAGE_TYPE', help = "Evidence file format",
   choices = ('ewf', 'raw'))
   args = parser.parse_args()
   main(args.EVIDENCE_FILE, args.IMAGE_TYPE)

次のように /Windows/System32/config フォルダー内の SYSTEM および SOFTWARE ハイブを検索するための* main()*関数を定義します-

def main(evidence, image_type):
   tsk_util = TSKUtil(evidence, image_type)
   tsk_system_hive = tsk_util.recurse_files('system', '/Windows/system32/config', 'equals')
   tsk_software_hive = tsk_util.recurse_files('software', '/Windows/system32/config', 'equals')
   system_hive = open_file_as_reg(tsk_system_hive[0][2])
   software_hive = open_file_as_reg(tsk_software_hive[0][2])
   process_system_hive(system_hive)
   process_software_hive(software_hive)

次に、レジストリファイルを開くための関数を定義します。 この目的のために、次のように pytsk メタデータからファイルのサイズを収集する必要があります-

def open_file_as_reg(reg_file):
   file_size = reg_file.info.meta.size
   file_content = reg_file.read_random(0, file_size)
   file_like_obj = StringIO.StringIO(file_content)
   return Registry.Registry(file_like_obj)

今、次の方法の助けを借りて、私たちは SYSTEM> ハイブを処理できます-

def process_system_hive(hive):
   root = hive.root()
   current_control_set = root.find_key("Select").value("Current").value()
   control_set = root.find_key("ControlSet{:03d}".format(current_control_set))
   raw_shutdown_time = struct.unpack(
      '<Q', control_set.find_key("Control").find_key("Windows").value("ShutdownTime").value())

   shutdown_time = parse_windows_filetime(raw_shutdown_time[0])
   print("Last Shutdown Time: {}".format(shutdown_time))

   time_zone = control_set.find_key("Control").find_key("TimeZoneInformation")
      .value("TimeZoneKeyName").value()

   print("Machine Time Zone: {}".format(time_zone))
   computer_name = control_set.find_key("Control").find_key("ComputerName").find_key("ComputerName")
      .value("ComputerName").value()

   print("Machine Name: {}".format(computer_name))
   last_access = control_set.find_key("Control").find_key("FileSystem")
      .value("NtfsDisableLastAccessUpdate").value()
   last_access = "Disabled" if last_access == 1 else "enabled"
   print("Last Access Updates: {}".format(last_access))

今、私たちは次のようにフォーマットされた日付と時刻に整数を解釈するための関数を定義する必要があります-

def parse_windows_filetime(date_value):
   microseconds = float(date_value)/10
   ts = datetime.datetime(1601, 1, 1) + datetime.timedelta(microseconds = microseconds)
   return ts.strftime('%Y-%m-%d %H:%M:%S.%f')

def parse_unix_epoch(date_value):
   ts = datetime.datetime.fromtimestamp(date_value)
   return ts.strftime('%Y-%m-%d %H:%M:%S.%f')

今、次の方法の助けを借りて、*ソフトウェア*ハイブを処理できます-

def process_software_hive(hive):
   root = hive.root()
   nt_curr_ver = root.find_key("Microsoft").find_key("Windows NT")
      .find_key("CurrentVersion")

   print("Product name: {}".format(nt_curr_ver.value("ProductName").value()))
   print("CSD Version: {}".format(nt_curr_ver.value("CSDVersion").value()))
   print("Current Build: {}".format(nt_curr_ver.value("CurrentBuild").value()))
   print("Registered Owner: {}".format(nt_curr_ver.value("RegisteredOwner").value()))
   print("Registered Org:
      {}".format(nt_curr_ver.value("RegisteredOrganization").value()))

   raw_install_date = nt_curr_ver.value("InstallDate").value()
   install_date = parse_unix_epoch(raw_install_date)
   print("Installation Date: {}".format(install_date))

上記のスクリプトを実行した後、メタデータをWindowsレジストリファイルに保存します。