Pythonデジタルフォレンジック-クイックガイド

この章では、デジタルフォレンジックとは何か、およびその歴史的レビューについて紹介します。また、デジタルフォレンジックを実際の生活のどこに適用できるかとその限界についても理解できます。

デジタルフォレンジックとは何ですか?

デジタルフォレンジックは、電子デバイスに存在するデジタル証拠を分析、調査、識別、および回復するフォレンジックサイエンスの分野として定義できます。これは一般的に刑法や私的調査に使用されます。

たとえば、誰かが電子デバイス上のデータを盗んだ場合に備えて、デジタルフォレンジックが証拠を抽出することに頼ることができます。

デジタルフォレンジックの簡単な歴史的レビュー

このセクションでは、コンピューター犯罪の歴史とデジタルフォレンジックの歴史的レビューについて、以下のように説明します。

1970年代から1980年代:最初のコンピューター犯罪

この10年以前は、コンピューター犯罪は認識されていません。しかし、それが起こることになっている場合、当時の既存の法律はそれらを扱いました。その後、1978年に、最初のコンピューター犯罪がフロリダコンピューター犯罪法で認められました。これには、コンピューターシステム上のデータの不正な変更または削除に対する法律が含まれていました。しかし、時間の経過とともに、技術の進歩により、犯されるコンピューター犯罪の範囲も増加しました。著作権、プライバシー、児童ポルノに関連する犯罪に対処するために、他のさまざまな法律が可決されました。

1980〜1990年代:開発の10年

この10年は、デジタルフォレンジックの開発の10年でした。これはすべて、CliffStollがMarkusHessという名前のハッカーを追跡した最初の調査(1986年)によるものです。この期間中に、2種類のデジタルフォレンジック分野が開発されました。1つは趣味としてそれを取り入れた実践者によって開発されたアドホックツールと技術の助けを借りて、2つ目は科学界によって開発されました。1992年には、“Computer Forensics”学術文献で使用されました。

2000年代から2010年代:標準化の10年

デジタルフォレンジックがある程度発展した後、調査を行う際に従うことができるいくつかの特定の基準を作成する必要がありました。したがって、さまざまな科学機関や団体がデジタルフォレンジックのガイドラインを公開しています。2002年、デジタルエビデンスに関する科学ワーキンググループ(SWGDE)は、「コンピュータフォレンジックのベストプラクティス」という名前の論文を発表しました。キャップのもう一つの羽は、ヨーロッパ主導の国際条約でした。“The Convention on Cybercrime”43カ国が署名し、16カ国が批准しました。そのような基準の後でさえ、研究者によって特定されたいくつかの問題を解決する必要があります。

デジタルフォレンジックのプロセス

1978年の最初のコンピューター犯罪以来、デジタル犯罪活動は大幅に増加しています。この増加のために、それらに対処するための構造化された方法が必要です。1984年に、正式なプロセスが導入され、その後、多数の新しく改善されたコンピュータフォレンジック調査プロセスが開発されました。

コンピュータフォレンジックの調査プロセスには、以下で説明する3つの主要なフェーズが含まれます。

フェーズ1:展示物の取得またはイメージング

デジタルフォレンジックの最初のフェーズでは、後で分析できるようにデジタルシステムの状態を保存します。犯罪現場から写真や血液サンプルなどを撮るのと非常によく似ています。たとえば、ハードディスクまたはRAMの割り当て済み領域と未割り当て領域のイメージをキャプチャする必要があります。

フェーズ2:分析

このフェーズの入力は、取得フェーズで取得されたデータです。ここでは、証拠を特定するためにこのデータを調べました。このフェーズでは、次の3種類の証拠が得られます。

  • Inculpatory evidences −これらの証拠は、特定の履歴をサポートします。

  • Exculpatory evidences −これらの証拠は特定の歴史と矛盾します。

  • Evidence of tampering−これらの証拠は、システムが識別を回避するために調整されたことを示しています。これには、削除されたファイルを回復するためのファイルとディレクトリの内容の調査が含まれます。

フェーズ3:プレゼンテーションまたはレポート

名前が示すように、このフェーズは、調査からの結論と対応する証拠を提示します。

デジタルフォレンジックのアプリケーション

デジタルフォレンジックは、デジタルデバイスに含まれる証拠の収集、分析、および保存を扱います。デジタルフォレンジックの使用は、アプリケーションによって異なります。前述のように、主に次の2つのアプリケーションで使用されます-

刑法

刑法では、法廷での仮説を支持または反対するために証拠が収集されます。フォレンジック手順は、犯罪捜査で使用される手順と非常によく似ていますが、法的要件と制限が異なります。

私立探偵

主に企業の世界では、私立探偵にデジタルフォレンジックを使用しています。これは、従業員が会社のポリシーに反する違法な活動をコンピューター上で実行している可能性があると企業が疑う場合に使用されます。デジタルフォレンジックは、企業または個人がデジタル不正行為について誰かを調査する際に取る最良のルートの1つを提供します。

デジタルフォレンジックのブランチ

デジタル犯罪はコンピューターだけにとどまりませんが、ハッカーや犯罪者はタブレットやスマートフォンなどの小さなデジタルデバイスを非常に大規模に使用しています。一部のデバイスには揮発性メモリがあり、その他のデバイスには不揮発性メモリがあります。したがって、デバイスのタイプに応じて、デジタルフォレンジックには次のブランチがあります-

コンピュータフォレンジック

このデジタルフォレンジックの分野では、コンピューター、組み込みシステム、およびUSBドライブなどの静的メモリを扱います。ログからドライブ上の実際のファイルまで、幅広い情報をコンピュータフォレンジックで調査できます。

モバイルフォレンジック

これは、モバイルデバイスからのデータの調査を扱います。このブランチは、モバイルデバイスに位置に関連する有用な情報を提供するのに役立つ通信システムが組み込まれているという点で、コンピュータフォレンジックとは異なります。

ネットワークフォレンジクス

これは、情報収集、証拠収集、または侵入検知を目的とした、ローカルとWAN(ワイドエリアネットワーク)の両方のコンピュータネットワークトラフィックの監視と分析を扱います。

データベースフォレンジクス

デジタルフォレンジックのこのブランチは、データベースとそのメタデータのフォレンジック研究を扱います。

デジタルフォレンジック調査に必要なスキル

デジタルフォレンジックの審査官は、ハッカーの追跡、盗まれたデータの回復、コンピューター攻撃の追跡、およびコンピューターに関する他の種類の調査を支援します。以下で説明するように、デジタルフォレンジック審査官になるために必要な主要なスキルのいくつか-

優れた思考能力

デジタルフォレンジック調査員は、優れた思想家である必要があり、出力を取得するための特定の割り当てにさまざまなツールと方法論を適用できる必要があります。彼/彼女は異なるパターンを見つけ、それらの間で相関関係を作ることができなければなりません。

技術的なスキル

この分野ではネットワーク、デジタルシステムの相互作用に関する知識が必要であるため、デジタルフォレンジックの審査官は優れた技術スキルを持っている必要があります。

サイバーセキュリティに情熱を注ぐ

デジタルフォレンジックの分野はすべてサイバー犯罪の解決であり、これは退屈な作業であるため、誰かがエースのデジタルフォレンジック調査員になるには多くの情熱が必要です。

コミュニケーションスキル

さまざまなチームと調整し、不足しているデータや情報を抽出するには、優れたコミュニケーションスキルが必要です。

レポート作成に長けている

取得と分析の実装が成功した後、デジタルフォレンジック審査官はすべての調査結果を最終レポートとプレゼンテーションに記載する必要があります。したがって、彼/彼女はレポート作成の優れたスキルと細部への注意を持っている必要があります。

制限事項

ここで説明するように、デジタルフォレンジック調査には特定の制限があります-

説得力のある証拠を作成する必要がある

デジタルフォレンジック調査の主な欠点の1つは、データが簡単に改ざんされる可能性があるため、審査官は法廷での証拠に必要な基準に準拠する必要があることです。一方、コンピュータフォレンジックの調査員は、法廷で説得力のある証拠を提示するために、法的要件、証拠の取り扱い、および文書化の手順に関する完全な知識を持っている必要があります。

調査ツール

デジタル調査の有効性は、デジタルフォレンジック審査官の専門知識と適切な調査ツールの選択に完全に依存しています。使用されたツールが指定された基準に準拠していない場合、法廷で、裁判官は証拠を否定することができます。

聴衆の間の技術的知識の欠如

もう1つの制限は、一部の個人がコンピュータフォレンジックに完全に精通していないことです。したがって、多くの人がこの分野を理解していません。捜査官は、誰もが結果を理解できるように、調査結果を裁判所に必ず伝える必要があります。

費用

デジタル証拠を作成して保存するには、非常にコストがかかります。したがって、このプロセスは、コストを支払う余裕のない多くの人々によって選択されない可能性があります。

前の章では、デジタルフォレンジックの基本、その利点と制限について学びました。この章では、このデジタルフォレンジック調査で使用している重要なツールであるPythonに慣れることができます。

なぜPythonfor Digital Forensicsなのか?

Pythonは人気のあるプログラミング言語であり、サイバーセキュリティ、侵入テスト、デジタルフォレンジック調査のツールとして使用されています。デジタルフォレンジックのツールとしてPythonを選択した場合、タスクを完了するために他のサードパーティソフトウェアは必要ありません。

デジタルフォレンジックプロジェクトに最適なPythonプログラミング言語のユニークな機能のいくつかを以下に示します-

  • Simplicity of Syntax − Pythonの構文は他の言語に比べて単純であるため、デジタルフォレンジックの学習と使用が容易になります。

  • Comprehensive inbuilt modules − Pythonの包括的な組み込みモジュールは、完全なデジタルフォレンジック調査を実行するための優れた支援手段です。

  • Help and Support −オープンソースプログラミング言語であるPythonは、開発者とユーザーのコミュニティから優れたサポートを受けています。

Pythonの機能

Pythonは、高水準のインタープリター型のインタラクティブなオブジェクト指向スクリプト言語であり、次の機能を提供します。

  • Easy to Learn − Pythonは、キーワードが少なく、構造が最も単純なため、開発者にとって使いやすく、習得しやすい言語です。

  • Expressive and Easy to read−Python言語は本質的に表現力豊かです。したがって、そのコードはより理解しやすく、読みやすくなります。

  • Cross-platform Compatible − Pythonはクロスプラットフォーム互換の言語です。つまり、UNIX、Windows、Macintoshなどのさまざまなプラットフォームで効率的に実行できます。

  • Interactive Mode Programming − Pythonはプログラミングのインタラクティブモードをサポートしているため、コードのインタラクティブなテストとデバッグを行うことができます。

  • Provides Various Modules and Functions − Pythonには大規模な標準ライブラリがあり、スクリプトに豊富なモジュールと関数のセットを使用できます。

  • Supports Dynamic Type Checking − Pythonは動的型チェックをサポートし、非常に高レベルの動的データ型を提供します。

  • GUI Programming − Pythonは、グラフィカルユーザーインターフェイスを開発するためのGUIプログラミングをサポートしています。

  • Integration with other programming languages − Pythonは、C、C ++、JAVAなどの他のプログラミング言語と簡単に統合できます。

Pythonのインストール

Pythonディストリビューションは、Windows、UNIX、Linux、Macなどのさまざまなプラットフォームで利用できます。プラットフォームに従ってバイナリコードをダウンロードするだけで済みます。プラットフォームのバイナリコードが利用できない場合は、ソースコードを手動でコンパイルできるようにCコンパイラが必要です。

このセクションでは、さまざまなプラットフォームへのPythonのインストールについて説明します。

UnixおよびLinuxへのPythonのインストール

以下に示す手順に従って、Unix / LinuxマシンにPythonをインストールできます。

Step 1−Webブラウザを開きます。www.python.org/downloads/と入力して入力します

Step 2 − Unix / Linuxで利用可能なzip形式のソースコードをダウンロードします。

Step 3 −ダウンロードしたzipファイルを解凍します。

Step 4 −いくつかのオプションをカスタマイズしたい場合は、 Modules/Setup file

Step 5 −インストールを完了するには、次のコマンドを使用します−

run ./configure script
make
make install

上記の手順を正常に完了すると、Pythonが標準の場所にインストールされます /usr/local/bin とそのライブラリ /usr/local/lib/pythonXX ここで、XXはPythonのバージョンです。

WindowsへのPythonのインストール

次の簡単な手順に従って、WindowsマシンにPythonをインストールできます。

Step 1−Webブラウザを開きます。www.python.org/downloads/と入力して入力します

Step 2 −Windowsインストーラーをダウンロードします python-XYZ.msi ファイル。ここで、XYZはインストールする必要のあるバージョンです。

Step 3 −インストーラファイルをローカルマシンに保存した後、そのMSIファイルを実行します。

Step 4 −ダウンロードしたファイルを実行すると、Pythonインストールウィザードが表示されます。

MacintoshへのPythonのインストール

Mac OSXにPython3をインストールするには、という名前のパッケージインストーラーを使用する必要があります Homebrew

システムにHomebrewがない場合は、次のコマンドを使用してHomebrewをインストールできます。

$ ruby -e "$(curl -fsSL
https://raw.githubusercontent.com/Homebrew/install/master/install)"

パッケージマネージャーを更新する必要がある場合は、次のコマンドを使用して更新できます-

$ brew update

次に、次のコマンドを使用して、Python3をシステムにインストールします-

$ brew install python3

パスの設定

Pythonインストールのパスを設定する必要があります。これは、UNIX、WINDOWS、MACなどのプラットフォームによって異なります。

Unix / Linuxでのパス設定

次のオプションを使用して、Unix / Linuxでパスを設定できます-

  • If using csh shell -タイプ setenv PATH "$PATH:/usr/local/bin/python" 次に、Enterキーを押します。

  • If using bash shell (Linux) −タイプ export ATH="$PATH:/usr/local/bin/python" 次に、Enterキーを押します。

  • If using sh or ksh shell -タイプ PATH="$PATH:/usr/local/bin/python" 次に、Enterキーを押します。

Windowsでのパス設定

タイプ path %path%;C:\Python コマンドプロンプトでEnterキーを押します。

Pythonの実行

次の3つの方法のいずれかを選択して、Pythonインタープリターを起動できます-

方法1:インタラクティブインタプリタを使用する

コマンドラインインタープリターまたはシェルを提供するシステムは、Pythonの起動に簡単に使用できます。たとえば、Unix、DOSなどです。以下の手順に従って、インタラクティブインタプリタでコーディングを開始できます。

Step 1 −入力 python コマンドラインで。

Step 2 −以下に示すコマンドを使用して、インタラクティブインタプリタですぐにコーディングを開始します−

$python # Unix/Linux
or
python% # Unix/Linux
or
C:> python # Windows/DOS

方法2:コマンドラインからスクリプトを使用する

アプリケーションでインタープリターを呼び出すことにより、コマンドラインでPythonスクリプトを実行することもできます。以下に示すコマンドを使用できます-

$python script.py # Unix/Linux
or
python% script.py # Unix/Linux
or
C: >python script.py # Windows/DOS

方法3:統合開発環境

システムにPythonをサポートするGUIアプリケーションがある場合、PythonはそのGUI環境から実行できます。さまざまなプラットフォーム向けのIDEの一部を以下に示します-

  • Unix IDE −UNIXにはPython用のIDLEIDEがあります。

  • Windows IDE − WindowsにはPythonWinがあります。これは、GUIを備えたPython用の最初のWindowsインターフェイスです。

  • Macintosh IDE −MacintoshにはIDLEIDEがあり、メインのWebサイトから入手でき、MacBinaryファイルまたはBinHexファイルとしてダウンロードできます。

ローカルシステムでのPythonコマンドのインストールと実行に慣れてきたので、フォレンジックの概念について詳しく見ていきましょう。この章では、Pythonデジタルフォレンジックでアーティファクトを処理する際のさまざまな概念について説明します。

レポート作成の必要性

デジタルフォレンジックのプロセスには、第3フェーズとしてのレポートが含まれます。これは、デジタルフォレンジックプロセスの最も重要な部分の1つです。以下の理由により、レポートの作成が必要です。

  • これは、デジタルフォレンジック審査官が調査プロセスとその調査結果を概説した文書です。

  • 優れたデジタルフォレンジックレポートは、別の審査官が参照して、同じリポジトリを指定して同じ結果を得ることができます。

  • これは、デジタル証拠の1と0の中に見つかった事実を含む技術的および科学的文書です。

レポート作成の一般的なガイドライン

レポートは、読者に情報を提供するために作成されており、強固な基盤から始める必要があります。レポートが一般的なガイドラインや基準なしで作成された場合、調査員は調査結果を効率的に提示するのが困難になる可能性があります。デジタルフォレンジックレポートを作成する際に従う必要のある一般的なガイドラインを以下に示します。

  • Summary −読者がレポートの目的を確認できるように、レポートには情報の簡単な要約が含まれている必要があります。

  • Tools used −デジタルフォレンジックのプロセスを実行するために使用されてきたツールについて、その目的を含めて言及する必要があります。

  • Repository −誰かのコンピューターを調査し、証拠の要約と、電子メール、内部検索履歴などの関連資料の分析を調査した場合、ケースを明確に提示できるように、それらをレポートに含める必要があります。

  • Recommendations for counsel −報告書には、報告書の調査結果に基づいて調査を継続または中止するための弁護士への推奨事項が含まれている必要があります。

さまざまなタイプのレポートの作成

上記のセクションでは、デジタルフォレンジックにおけるレポートの重要性と、レポートを作成するためのガイドラインについて説明しました。さまざまな種類のレポートを作成するためのPythonの形式のいくつかを以下で説明します-

CSVレポート

レポートの最も一般的な出力形式の1つは、CSVスプレッドシートレポートです。以下に示すように、Pythonコードを使用してCSVを作成し、処理されたデータのレポートを作成できます。

まず、スプレッドシートを作成するための便利なライブラリをインポートします-

from __future__ import print_function
import csv
import os
import sys

ここで、次のメソッドを呼び出します-

Write_csv(TEST_DATA_LIST, ["Name", "Age", "City", "Job description"], os.getcwd())

サンプルデータ型を表すために、次のグローバル変数を使用しています-

TEST_DATA_LIST = [["Ram", 32, Bhopal, Manager], 
   ["Raman", 42, Indore, Engg.],
   ["Mohan", 25, Chandigarh, HR], 
   ["Parkash", 45, Delhi, IT]]

次に、次の操作に進む方法を定義しましょう。ファイルを「w」モードで開き、改行キーワード引数を空の文字列に設定します。

def Write_csv(data, header, output_directory, name = None):
   if name is None:
      name = "report1.csv"
   print("[+] Writing {} to {}".format(name, output_directory))
   
   with open(os.path.join(output_directory, name), "w", newline = "") as \ csvfile:
      writer = csv.writer(csvfile)
      writer.writerow(header)
      writer.writerow(data)

上記のスクリプトを実行すると、report1.csvファイルに次の詳細が保存されます。

名前 年齢 指定
32 ボパール マネージャー
ラマン 42 インドール Engg
モハン 25 チャンディーガル 人事
パーカッシュ 45 デリー それ

Excelレポート

レポートのもう1つの一般的な出力形式は、Excel(.xlsx)スプレッドシートレポートです。Excelを使用して、テーブルを作成し、グラフをプロットすることもできます。以下に示すように、Pythonコードを使用してExcel形式で処理されたデータのレポートを作成できます-

まず、スプレッドシートを作成するためのXlsxWriterモジュールをインポートします-

import xlsxwriter

次に、ブックオブジェクトを作成します。このためには、Workbook()コンストラクターを使用する必要があります。

workbook = xlsxwriter.Workbook('report2.xlsx')

次に、add_worksheet()モジュールを使用して新しいワークシートを作成します。

worksheet = workbook.add_worksheet()

次に、次のデータをワークシートに書き込みます-

report2 = (['Ram', 32, ‘Bhopal’],['Mohan',25, ‘Chandigarh’] ,['Parkash',45, ‘Delhi’])

row = 0
col = 0

このデータを繰り返し処理して、次のように書き込むことができます-

for item, cost in (a):
   worksheet.write(row, col, item)
   worksheet.write(row, col+1, cost)
   row + = 1

それでは、close()メソッドを使用してこのExcelファイルを閉じましょう。

workbook.close()

上記のスクリプトは、次のデータを持つreport2.xlsxという名前のExcelファイルを作成します-

32 ボパール
モハン 25 チャンディーガル
パーカッシュ 45 デリー

調査取得メディア

調査員は、調査結果を正確に思い出したり、すべての調査をまとめたりするために、詳細な調査メモを用意することが重要です。スクリーンショットは、特定の調査のために実行された手順を追跡するのに非常に役立ちます。次のPythonコードを使用して、スクリーンショットを撮り、将来使用するためにハードディスクに保存できます。

まず、次のコマンドを使用してpyscreenshotという名前のPythonモジュールをインストールします-

Pip install pyscreenshot

次に、図のように必要なモジュールをインポートします-

import pyscreenshot as ImageGrab

次のコード行を使用してスクリーンショットを取得します-

image = ImageGrab.grab()

次のコード行を使用して、スクリーンショットを指定された場所に保存します-

image.save('d:/image123.png')

ここで、スクリーンショットをグラフとしてポップアップする場合は、次のPythonコードを使用できます-

import numpy as np
import matplotlib.pyplot as plt
import pyscreenshot as ImageGrab
imageg = ImageGrab.grab()
plt.imshow(image, cmap='gray', interpolation='bilinear')
plt.show()

この章では、モバイルデバイスでのPythonデジタルフォレンジックと関連する概念について説明します。

前書き

モバイルデバイスフォレンジックは、調査対象のデジタル証拠を回復するためにモバイルデバイスの取得と分析を扱うデジタルフォレンジックのブランチです。モバイルデバイスには、位置に関連する有用な情報を提供するのに役立つ通信システムが組み込まれているため、このブランチはコンピュータフォレンジックとは異なります。

スマートフォンの使用はデジタルフォレンジックで日々増加していますが、それでもその異質性のために非標準と見なされています。一方、ハードディスクなどのコンピュータハードウェアは標準と見なされており、安定した分野としても開発されています。デジタルフォレンジック業界では、スマートフォンなどの一時的な証拠がある非標準デバイスに使用される手法について多くの議論があります。

モバイルデバイスから抽出可能なアーティファクト

最新のモバイルデバイスは、通話記録またはSMSメッセージしかない古い電話と比較して多くのデジタル情報を持っています。したがって、モバイルデバイスは、調査員にそのユーザーに関する多くの洞察を提供できます。モバイルデバイスから抽出できるいくつかのアーティファクトは次のとおりです-

  • Messages −これらは、所有者の心の状態を明らかにすることができ、調査員に以前の未知の情報を与えることさえできる有用なアーティファクトです。

  • Location History−ロケーション履歴データは、調査員が人の特定のロケーションについて検証するために使用できる便利なアーティファクトです。

  • Applications Installed −インストールされているアプリケーションの種類にアクセスすることで、調査員はモバイルユーザーの習慣や考え方についての洞察を得ることができます。

Pythonでの証拠ソースと処理

スマートフォンには、証拠の主要なソースとしてSQLiteデータベースとPLISTファイルがあります。このセクションでは、Pythonで証拠のソースを処理します。

PLISTファイルの分析

PLIST(プロパティリスト)は、特にiPhoneデバイスにアプリケーションデータを保存するための柔軟で便利な形式です。拡張子を使用します.plist。バンドルやアプリケーションに関する情報を保存するために使用されるこの種のファイル。次の2つの形式にすることができます。XML そして binary。次のPythonコードは、PLISTファイルを開いて読み取ります。これに進む前に、独自に作成する必要があることに注意してくださいInfo.plist ファイル。

まず、という名前のサードパーティライブラリをインストールします biplist 次のコマンドで-

Pip install biplist

ここで、plistファイルを処理するためにいくつかの便利なライブラリをインポートします-

import biplist
import os
import sys

ここで、mainメソッドの下で次のコマンドを使用して、plistファイルを変数に読み込むことができます-

def main(plist):
   try:
      data = biplist.readPlist(plist)
   except (biplist.InvalidPlistException,biplist.NotBinaryPlistException) as e:
print("[-] Invalid PLIST file - unable to be opened by biplist")
sys.exit(1)

これで、コンソールでデータを読み取るか、この変数から直接印刷することができます。

SQLiteデータベース

SQLiteは、モバイルデバイスのプライマリデータリポジトリとして機能します。SQLiteは、自己完結型のサーバーレスのゼロ構成のトランザクションSQLデータベースエンジンを実装するインプロセスライブラリです。これはゼロ構成のデータベースであり、他のデータベースとは異なり、システムで構成する必要はありません。

あなたが初心者やSQLiteのデータベースに慣れていない場合は、リンクに従うことができwww.tutorialspoint.com/sqlite/index.htmをさらに、あなたがリンクに従うことができwww.tutorialspoint.com/sqlite/sqlite_python.htmをあなたがしたい場合は、 PythonでSQLiteの詳細を説明します。

モバイルフォレンジック中に、私たちは sms.db モバイルデバイスのファイルとから貴重な情報を抽出することができます messageテーブル。Pythonには、という名前の組み込みライブラリがありますsqlite3SQLiteデータベースに接続するため。次のコマンドで同じものをインポートできます-

import sqlite3

これで、次のコマンドを使用して、データベースに接続できます。 sms.db モバイルデバイスの場合-

Conn = sqlite3.connect(‘sms.db’)
C = conn.cursor()

ここで、Cは、データベースと対話できるカーソルオブジェクトです。

ここで、特定のコマンドを実行する場合、たとえば、から詳細を取得するとします。 abc table、次のコマンドを使用して実行できます-

c.execute(“Select * from abc”)
c.close()

上記のコマンドの結果は、 cursorオブジェクト。同様に使用できますfetchall() 結果を操作可能な変数にダンプするメソッド。

次のコマンドを使用して、メッセージテーブルの列名データを取得できます。 sms.db

c.execute(“pragma table_info(message)”)
table_data = c.fetchall()
columns = [x[1] for x in table_data

ここでは、SQLite環境内のさまざまな環境変数と状態フラグを制御するために使用される特別なコマンドであるSQLitePRAGMAコマンドを使用していることに注意してください。上記のコマンドでは、fetchall()メソッドは結果のタプルを返します。各列の名前は、各タプルの最初のインデックスに格納されます。

これで、次のコマンドを使用して、テーブルにすべてのデータをクエリし、名前の付いた変数に格納できます。 data_msg

c.execute(“Select * from message”)
data_msg = c.fetchall()

上記のコマンドはデータを変数に保存し、さらに上記のデータを使用してCSVファイルに書き込むこともできます csv.writer() 方法。

iTunesのバックアップ

iPhoneモバイルフォレンジックは、iTunesによって作成されたバックアップで実行できます。フォレンジック審査官は、iTunesを通じて取得したiPhoneの論理バックアップの分析に依存しています。AFC(Appleファイル接続)プロトコルは、バックアップを取るためにiTunesによって使用されます。さらに、バックアッププロセスは、エスクローキーレコードを除いてiPhone上の何も変更しません。

さて、デジタルフォレンジックの専門家がiTunesバックアップのテクニックを理解することがなぜ重要なのかという疑問が生じます。コンピューターをiPhoneとの同期に使用すると、iPhone上のほとんどの情報がコンピューターにバックアップされる可能性が高いため、iPhoneではなく容疑者のコンピューターに直接アクセスする場合に重要です。

バックアップのプロセスとその場所

Apple製品がコンピュータにバックアップされるときはいつでも、それはiTunesと同期しており、デバイスの一意のIDを持つ特定のフォルダがあります。最新のバックアップ形式では、ファイルはファイル名の最初の2つの16進文字を含むサブフォルダーに格納されます。これらのバックアップファイルから、Manifest.dbという名前のデータベースと一緒に役立つinfo.plistのようないくつかのファイルがあります。次の表は、iTunesバックアップのオペレーティングシステムによって異なるバックアップ場所を示しています-

OS バックアップ場所
Win7 C:\ Users \ [username] \ AppData \ Roaming \ AppleComputer \ MobileSync \ Backup \
MAC OS X 〜/ Library / Application Suport / MobileSync / Backup /

PythonでiTunesバックアップを処理するには、最初にオペレーティングシステムに従ってバックアップ場所にあるすべてのバックアップを識別する必要があります。次に、各バックアップを繰り返し処理し、データベースManifest.dbを読み取ります。

さて、次のPythonコードの助けを借りて、同じことができます-

まず、必要なライブラリを次のようにインポートします-

from __future__ import print_function
import argparse
import logging
import os

from shutil import copyfile
import sqlite3
import sys
logger = logging.getLogger(__name__)

ここで、iTunesのバックアップと目的の出力フォルダを表すINPUT_DIRとOUTPUT_DIRという2つの位置引数を指定します。

if __name__ == "__main__":
   parser.add_argument("INPUT_DIR",help = "Location of folder containing iOS backups, ""e.g. ~\Library\Application Support\MobileSync\Backup folder")
   parser.add_argument("OUTPUT_DIR", help = "Output Directory")
   parser.add_argument("-l", help = "Log file path",default = __file__[:-2] + "log")
   parser.add_argument("-v", help = "Increase verbosity",action = "store_true") args = parser.parse_args()

ここで、ログを次のように設定します-

if args.v:
   logger.setLevel(logging.DEBUG)
else:
   logger.setLevel(logging.INFO)

ここで、このログのメッセージ形式を次のように設定します-

msg_fmt = logging.Formatter("%(asctime)-15s %(funcName)-13s""%(levelname)-8s %(message)s")
strhndl = logging.StreamHandler(sys.stderr)
strhndl.setFormatter(fmt = msg_fmt)

fhndl = logging.FileHandler(args.l, mode = 'a')
fhndl.setFormatter(fmt = msg_fmt)

logger.addHandler(strhndl)
logger.addHandler(fhndl)
logger.info("Starting iBackup Visualizer")
logger.debug("Supplied arguments: {}".format(" ".join(sys.argv[1:])))
logger.debug("System: " + sys.platform)
logger.debug("Python Version: " + sys.version)

次のコード行は、を使用して目的の出力ディレクトリに必要なフォルダを作成します os.makedirs() 関数-

if not os.path.exists(args.OUTPUT_DIR):
   os.makedirs(args.OUTPUT_DIR)

ここで、提供された入力ディレクトリと出力ディレクトリを次のようにmain()関数に渡します。

if os.path.exists(args.INPUT_DIR) and os.path.isdir(args.INPUT_DIR):
   main(args.INPUT_DIR, args.OUTPUT_DIR)
else:
   logger.error("Supplied input directory does not exist or is not ""a directory")
   sys.exit(1)

書いて main() さらに呼び出す関数 backup_summary() 入力フォルダに存在するすべてのバックアップを識別する機能-

def main(in_dir, out_dir):
   backups = backup_summary(in_dir)
def backup_summary(in_dir):
   logger.info("Identifying all iOS backups in {}".format(in_dir))
   root = os.listdir(in_dir)
   backups = {}
   
   for x in root:
      temp_dir = os.path.join(in_dir, x)
      if os.path.isdir(temp_dir) and len(x) == 40:
         num_files = 0
         size = 0
         
         for root, subdir, files in os.walk(temp_dir):
            num_files += len(files)
            size += sum(os.path.getsize(os.path.join(root, name))
               for name in files)
         backups[x] = [temp_dir, num_files, size]
   return backups

次に、各バックアップの概要を次のようにコンソールに出力します。

print("Backup Summary")
print("=" * 20)

if len(backups) > 0:
   for i, b in enumerate(backups):
      print("Backup No.: {} \n""Backup Dev. Name: {} \n""# Files: {} \n""Backup Size (Bytes): {}\n".format(i, b, backups[b][1], backups[b][2]))

ここで、Manifest.dbファイルの内容をdb_itemsという名前の変数にダンプします。

try:
   db_items = process_manifest(backups[b][0])
   except IOError:
      logger.warn("Non-iOS 10 backup encountered or " "invalid backup. Continuing to next backup.")
continue

ここで、バックアップのディレクトリパスを取得する関数を定義しましょう-

def process_manifest(backup):
   manifest = os.path.join(backup, "Manifest.db")
   
   if not os.path.exists(manifest):
      logger.error("Manifest DB not found in {}".format(manifest))
      raise IOError

ここで、SQLite3を使用して、c −という名前のカーソルでデータベースに接続します。

c = conn.cursor()
items = {}

for row in c.execute("SELECT * from Files;"):
   items[row[0]] = [row[2], row[1], row[3]]
return items

create_files(in_dir, out_dir, b, db_items)
   print("=" * 20)
else:
   logger.warning("No valid backups found. The input directory should be
      " "the parent-directory immediately above the SHA-1 hash " "iOS device backups")
      sys.exit(2)

ここで、 create_files() 次のような方法-

def create_files(in_dir, out_dir, b, db_items):
   msg = "Copying Files for backup {} to {}".format(b, os.path.join(out_dir, b))
   logger.info(msg)

ここで、の各キーを繰り返し処理します。 db_items 辞書−

for x, key in enumerate(db_items):
   if db_items[key][0] is None or db_items[key][0] == "":
      continue
   else:
      dirpath = os.path.join(out_dir, b,
os.path.dirname(db_items[key][0]))
   filepath = os.path.join(out_dir, b, db_items[key][0])
   
   if not os.path.exists(dirpath):
      os.makedirs(dirpath)
      original_dir = b + "/" + key[0:2] + "/" + key
   path = os.path.join(in_dir, original_dir)
   
   if os.path.exists(filepath):
      filepath = filepath + "_{}".format(x)

今、使用します shutil.copyfile() バックアップしたファイルを次のようにコピーする方法−

try:
   copyfile(path, filepath)
   except IOError:
      logger.debug("File not found in backup: {}".format(path))
         files_not_found += 1
   if files_not_found > 0:
      logger.warning("{} files listed in the Manifest.db not" "found in
backup".format(files_not_found))
   copyfile(os.path.join(in_dir, b, "Info.plist"), os.path.join(out_dir, b,
"Info.plist"))
   copyfile(os.path.join(in_dir, b, "Manifest.db"), os.path.join(out_dir, b,
"Manifest.db"))
   copyfile(os.path.join(in_dir, b, "Manifest.plist"), os.path.join(out_dir, b,
"Manifest.plist"))
   copyfile(os.path.join(in_dir, b, "Status.plist"),os.path.join(out_dir, b,
"Status.plist"))

上記のPythonスクリプトを使用すると、出力フォルダー内の更新されたバックアップファイル構造を取得できます。使用できますpycrypto バックアップを復号化するためのPythonライブラリ。

Wi-Fi

モバイルデバイスは、どこでも利用できるWi-Fiネットワークを介して接続することにより、外の世界に接続するために使用できます。デバイスがこれらのオープンネットワークに自動的に接続される場合があります。

iPhoneの場合、デバイスが接続されているオープンWi-Fi接続のリストは、という名前のPLISTファイルに保存されます。 com.apple.wifi.plist。このファイルには、Wi-Fi SSID、BSSID、および接続時間が含まれます。

Pythonを使用して、標準のCellebriteXMLレポートからWi-Fiの詳細を抽出する必要があります。このためには、Wi-Fiネットワークの名前を使用してデバイスの場所を見つけるために使用できる人気のあるプラットフォームであるWireless Geographic Logging Engine(WIGLE)のAPIを使用する必要があります。

名前の付いたPythonライブラリを使用できます requestsWIGLEからAPIにアクセスします。次のようにインストールできます-

pip install requests

WIGLEのAPI

WIGLEのウェブサイトで登録する必要があります https://wigle.net/accountWIGLEから無料のAPIを入手します。WIGELのAPIを介してユーザーデバイスとその接続に関する情報を取得するためのPythonスクリプトについて以下で説明します-

まず、さまざまなことを処理するために次のライブラリをインポートします-

from __future__ import print_function

import argparse
import csv
import os
import sys
import xml.etree.ElementTree as ET
import requests

ここで、2つの位置引数を提供します。 INPUT_FILE そして OUTPUT_CSV これは、Wi-FiMACアドレスを持つ入力ファイルと目的の出力CSVファイルをそれぞれ表します-

if __name__ == "__main__":
   parser.add_argument("INPUT_FILE", help = "INPUT FILE with MAC Addresses")
   parser.add_argument("OUTPUT_CSV", help = "Output CSV File")
   parser.add_argument("-t", help = "Input type: Cellebrite XML report or TXT
file",choices = ('xml', 'txt'), default = "xml")
   parser.add_argument('--api', help = "Path to API key
   file",default = os.path.expanduser("~/.wigle_api"),
   type = argparse.FileType('r'))
   args = parser.parse_args()

次のコード行は、入力ファイルが存在し、ファイルであるかどうかを確認します。そうでない場合は、スクリプトを終了します-

if not os.path.exists(args.INPUT_FILE) or \ not os.path.isfile(args.INPUT_FILE):
   print("[-] {} does not exist or is not a
file".format(args.INPUT_FILE))
   sys.exit(1)
directory = os.path.dirname(args.OUTPUT_CSV)
if directory != '' and not os.path.exists(directory):
   os.makedirs(directory)
api_key = args.api.readline().strip().split(":")

ここで、次のように引数をmainに渡します-

main(args.INPUT_FILE, args.OUTPUT_CSV, args.t, api_key)
def main(in_file, out_csv, type, api_key):
   if type == 'xml':
      wifi = parse_xml(in_file)
   else:
      wifi = parse_txt(in_file)
query_wigle(wifi, out_csv, api_key)

ここで、XMLファイルを次のように解析します-

def parse_xml(xml_file):
   wifi = {}
   xmlns = "{http://pa.cellebrite.com/report/2.0}"
   print("[+] Opening {} report".format(xml_file))
   
   xml_tree = ET.parse(xml_file)
   print("[+] Parsing report for all connected WiFi addresses")
   
   root = xml_tree.getroot()

ここで、ルートの子要素を次のように繰り返します。

for child in root.iter():
   if child.tag == xmlns + "model":
      if child.get("type") == "Location":
         for field in child.findall(xmlns + "field"):
            if field.get("name") == "TimeStamp":
               ts_value = field.find(xmlns + "value")
               try:
               ts = ts_value.text
               except AttributeError:
continue

ここで、「ssid」文字列が値のテキストに存在するかどうかを確認します-

if "SSID" in value.text:
   bssid, ssid = value.text.split("\t")
   bssid = bssid[7:]
   ssid = ssid[6:]

次に、BSSID、SSID、タイムスタンプを次のようにWi-Fi辞書に追加する必要があります-

if bssid in wifi.keys():

wifi[bssid]["Timestamps"].append(ts)
   wifi[bssid]["SSID"].append(ssid)
else:
   wifi[bssid] = {"Timestamps": [ts], "SSID":
[ssid],"Wigle": {}}
return wifi

XMLパーサーよりもはるかに単純なテキストパーサーを以下に示します-

def parse_txt(txt_file):
   wifi = {}
   print("[+] Extracting MAC addresses from {}".format(txt_file))
   
   with open(txt_file) as mac_file:
      for line in mac_file:
         wifi[line.strip()] = {"Timestamps": ["N/A"], "SSID":
["N/A"],"Wigle": {}}
return wifi

それでは、requestsモジュールを使用して作成しましょう WIGLE API呼び出して、に進む必要があります query_wigle() 方法−

def query_wigle(wifi_dictionary, out_csv, api_key):
   print("[+] Querying Wigle.net through Python API for {} "
"APs".format(len(wifi_dictionary)))
   for mac in wifi_dictionary:

   wigle_results = query_mac_addr(mac, api_key)
def query_mac_addr(mac_addr, api_key):

   query_url = "https://api.wigle.net/api/v2/network/search?" \
"onlymine = false&freenet = false&paynet = false" \ "&netid = {}".format(mac_addr)
   req = requests.get(query_url, auth = (api_key[0], api_key[1]))
   return req.json()

実際には、WIGLE API呼び出しには1日あたりの制限があり、その制限を超えると、次のようなエラーが表示される必要があります。

try:
   if wigle_results["resultCount"] == 0:
      wifi_dictionary[mac]["Wigle"]["results"] = []
         continue
   else:
      wifi_dictionary[mac]["Wigle"] = wigle_results
except KeyError:
   if wigle_results["error"] == "too many queries today":
      print("[-] Wigle daily query limit exceeded")
      wifi_dictionary[mac]["Wigle"]["results"] = []
      continue
   else:
      print("[-] Other error encountered for " "address {}: {}".format(mac,
wigle_results['error']))
   wifi_dictionary[mac]["Wigle"]["results"] = []
   continue
prep_output(out_csv, wifi_dictionary)

今、私たちは使用します prep_output() 辞書を簡単に書き込み可能なチャンクにフラット化する方法-

def prep_output(output, data):
   csv_data = {}
   google_map = https://www.google.com/maps/search/

これまでに収集したすべてのデータに次のようにアクセスします-

for x, mac in enumerate(data):
   for y, ts in enumerate(data[mac]["Timestamps"]):
      for z, result in enumerate(data[mac]["Wigle"]["results"]):
         shortres = data[mac]["Wigle"]["results"][z]
         g_map_url = "{}{},{}".format(google_map, shortres["trilat"],shortres["trilong"])

これで、この章の前のスクリプトで行ったように、を使用して出力をCSVファイルに書き込むことができます。 write_csv() 関数。

この章では、Pythonデジタルフォレンジックを使用した埋め込みメタデータの調査について詳しく学習します。

前書き

埋め込まれたメタデータは、同じファイルに格納されているデータに関する情報であり、そのデータによってオブジェクトが記述されています。言い換えれば、それはデジタルファイル自体に保存されているデジタル資産に関する情報です。常にファイルに関連付けられており、分離することはできません。

デジタルフォレンジックの場合、特定のファイルに関するすべての情報を抽出することはできません。一方、埋め込まれたメタデータは、調査に不可欠な情報を提供してくれます。たとえば、テキストファイルのメタデータには、作成者、その長さ、作成日、さらにはそのドキュメントに関する短い要約に関する情報が含まれている場合があります。デジタル画像には、画像の長さ、シャッター速度などのメタデータが含まれる場合があります。

メタデータ属性を含むアーティファクトとその抽出

このセクションでは、メタデータ属性を含むさまざまなアーティファクトと、Pythonを使用したそれらの抽出プロセスについて学習します。

オーディオとビデオ

これらは、メタデータが埋め込まれている2つの非常に一般的なアーティファクトです。このメタデータは、調査の目的で抽出できます。

次のPythonスクリプトを使用して、オーディオまたはMP3ファイルとビデオまたはMP4ファイルから共通の属性またはメタデータを抽出できます。

このスクリプトでは、オーディオファイルとビデオファイルからメタデータを抽出できるようにする、mutagenという名前のサードパーティのPythonライブラリをインストールする必要があることに注意してください。次のコマンドを使用してインストールできます-

pip install mutagen

このPythonスクリプト用にインポートする必要のある便利なライブラリのいくつかは次のとおりです。

from __future__ import print_function

import argparse
import json
import mutagen

コマンドラインハンドラーは、MP3またはMP4ファイルへのパスを表す1つの引数を取ります。次に、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)

ここで、2つのハンドルを使用する必要があります。1つはMP3からデータを抽出するためのもので、もう1つは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ライブラリをダウンロードします 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

これで、コマンドラインハンドラーは、基本的に写真のファイルパスを表す1つの位置引数を受け入れます。

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)タプル座標を10進数に変換する関数も必要です。それは次のように行うことができます-

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ライブラリをインストールします PyPDF2XMP形式で保存されたメタデータを読み取ります。次のようにインストールできます-

pip install PyPDF2

ここで、PDFファイルからメタデータを抽出するために次のライブラリをインポートします-

from __future__ import print_function
from argparse import ArgumentParser, FileType

import datetime
from PyPDF2 import PdfFileReader
import sys

これで、コマンドラインハンドラーは、基本的にPDFファイルのファイルパスを表す1つの位置引数を受け入れます。

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ドキュメントを読み取り、そのPDFが作成されたソフトウェアによって保存されたいくつかのカスタムプロパティを含むXMP形式で保存されたメタデータを印刷します。

Windows実行可能ファイル

疑わしい、または許可されていない実行可能ファイルに遭遇することがあります。ただし、メタデータが埋め込まれているため、調査の目的で役立つ場合があります。場所、目的、製造元、コンパイル日などの他の属性などの情報を取得できます。次のPythonスクリプトを使用して、コンパイル日、ヘッダーからの有用なデータ、インポートおよびエクスポートされたシンボルを取得できます。

この目的のために、最初にサードパーティのPythonライブラリをインストールします pefile。それは次のように行うことができます-

pip install pefile

これを正常にインストールしたら、次のライブラリを次のようにインポートします-

from __future__ import print_function

import argparse
from datetime import datetime
from pefile import PE

これで、コマンドラインハンドラーは、基本的に実行可能ファイルのファイルパスを表す1つの位置引数を受け入れます。また、詳細で詳細な方法で必要な場合でも、単純化された方法で必要な場合でも、出力のスタイルを選択できます。このためには、以下に示すようにオプションの引数を指定する必要があります-

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

今、印刷 exportsnames そして 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の3つのアプリケーション(Word、PowerPoint、Excel)で行われます。これらのファイルには巨大なメタデータがあり、作成者や履歴に関する興味深い情報を公開できます。

2007形式のWord(.docx)、Excel(.xlsx)、およびPowerPoint(.pptx)のメタデータがXMLファイルに保存されていることに注意してください。以下に示す次のPythonスクリプトを使用して、これらのXMLファイルを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))

上記のスクリプトを実行した後、特定のドキュメントに関するさまざまな詳細を取得できます。このスクリプトは、Office2007以降のバージョンのドキュメントにのみ適用できることに注意してください。

この章では、Pythonを使用してネットワークフォレンジックを実行する際の基本事項について説明します。

ネットワークフォレンジックを理解する

ネットワークフォレンジックは、情報収集、証拠収集、または侵入検知を目的として、ローカルとWAN(ワイドエリアネットワーク)の両方のコンピュータネットワークトラフィックの監視と分析を扱うデジタルフォレンジックのブランチです。ネットワークフォレンジックは、知的財産の盗難や情報漏えいなどのデジタル犯罪の調査において重要な役割を果たします。ネットワーク通信の写真は、調査員が次のようにいくつかの重要な質問を解決するのに役立ちます-

  • どのWebサイトにアクセスしましたか?

  • 私たちのネットワークにはどのようなコンテンツがアップロードされていますか?

  • 私たちのネットワークからどのようなコンテンツがダウンロードされましたか?

  • どのサーバーにアクセスしていますか?

  • 誰かが会社のファイアウォールの外に機密情報を送信していますか?

インターネットエビデンスファインダー(IEF)

IEFは、コンピューター、スマートフォン、タブレットなどのさまざまなデジタルメディアで見つかったデジタル証拠を検索、分析、提示するためのデジタルフォレンジックツールです。非常に人気があり、何千人ものフォレンジック専門家によって使用されています。

IEFの使用

その人気のために、IEFは法医学の専門家によって大いに使用されています。IEFの用途のいくつかは次のとおりです-

  • その強力な検索機能により、複数のファイルまたはデータメディアを同時に検索するために使用されます。

  • また、新しいカービング技術を使用して、RAMの未割り当て領域から削除されたデータを回復するためにも使用されます。

  • 調査員がWebページを開いた日に元の形式で再構築したい場合は、IEFを使用できます。

  • また、論理ディスクボリュームまたは物理ディスクボリュームの検索にも使用されます。

Pythonを使用してIEFからCSVにレポートをダンプする

IEFはデータをSQLiteデータベースに保存し、次のPythonスクリプトはIEFデータベース内の結果テーブルを動的に識別し、それぞれのCSVファイルにダンプします。

このプロセスは、以下に示す手順で実行されます。

  • まず、拡張子が.dbのSQLiteデータベースファイルとなるIEF結果データベースを生成します。

  • 次に、そのデータベースにクエリを実行して、すべてのテーブルを識別します。

  • 最後に、この結果テーブルを個々のCSVファイルに書き込みます。

Pythonコード

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

Pythonスクリプトの場合、必要なライブラリを次のようにインポートします-

from __future__ import print_function

import argparse
import csv
import os
import sqlite3
import sys

ここで、IEFデータベースファイルへのパスを指定する必要があります-

if __name__ == '__main__':
   parser = argparse.ArgumentParser('IEF to CSV')
   parser.add_argument("IEF_DATABASE", help="Input IEF database")
   parser.add_argument("OUTPUT_DIR", help="Output DIR")
   args = parser.parse_args()

ここで、IEFデータベースの存在を次のように確認します。

if not os.path.exists(args.OUTPUT_DIR):
   os.makedirs(args.OUTPUT_DIR)
if os.path.exists(args.IEF_DATABASE) and \ os.path.isfile(args.IEF_DATABASE):
   main(args.IEF_DATABASE, args.OUTPUT_DIR)
else:
   print("[-] Supplied input file {} does not exist or is not a " "file".format(args.IEF_DATABASE))
   sys.exit(1)

ここで、以前のスクリプトで行ったように、次のようにSQLiteデータベースと接続して、カーソルを介してクエリを実行します-

def main(database, out_directory):
   print("[+] Connecting to SQLite database")
   conn = sqlite3.connect(database)
   c = conn.cursor()

次のコード行は、データベースからテーブルの名前をフェッチします-

print("List of all tables to extract")
c.execute("select * from sqlite_master where type = 'table'")
tables = [x[2] for x in c.fetchall() if not x[2].startswith('_') and not x[2].endswith('_DATA')]

次に、テーブルからすべてのデータを選択します。 fetchall() カーソルオブジェクトのメソッドは、テーブルのデータ全体を含むタプルのリストを変数に格納します-

print("Dumping {} tables to CSV files in {}".format(len(tables), out_directory))

for table in tables:
c.execute("pragma table_info('{}')".format(table))
table_columns = [x[1] for x in c.fetchall()]

c.execute("select * from '{}'".format(table))
table_data = c.fetchall()

さて、 CSV_Writer() CSVファイルにコンテンツを書き込む方法-

csv_name = table + '.csv'
csv_path = os.path.join(out_directory, csv_name)
print('[+] Writing {} table to {} CSV file'.format(table,csv_name))

with open(csv_path, "w", newline = "") as csvfile:
   csv_writer = csv.writer(csvfile)
   csv_writer.writerow(table_columns)
   csv_writer.writerows(table_data)

上記のスクリプトは、IEFデータベースのテーブルからすべてのデータをフェッチし、選択したCSVファイルにコンテンツを書き込みます。

キャッシュされたデータの操作

IEF結果データベースから、IEF自体で必ずしもサポートされていないより多くの情報を取得できます。IEF結果データベースを使用して、Yahoo、Googleなどの電子メールサービスプロバイダーから、情報の副産物であるキャッシュデータを取得できます。

以下は、IEFデータベースを使用して、GoogleChromeでアクセスされるYahooメールからキャッシュされたデータ情報にアクセスするためのPythonスクリプトです。手順は、最後のPythonスクリプトで実行した手順とほぼ同じであることに注意してください。

まず、Pythonに必要なライブラリを次のようにインポートします-

from __future__ import print_function
import argparse
import csv
import os
import sqlite3
import sys
import json

ここで、IEFデータベースファイルへのパスと、最後のスクリプトで行ったようにコマンドラインハンドラーによって受け入れられる2つの位置引数を指定します。

if __name__ == '__main__':
   parser = argparse.ArgumentParser('IEF to CSV')
   parser.add_argument("IEF_DATABASE", help="Input IEF database")
   parser.add_argument("OUTPUT_DIR", help="Output DIR")
   args = parser.parse_args()

ここで、IEFデータベースの存在を次のように確認します-

directory = os.path.dirname(args.OUTPUT_CSV)

if not os.path.exists(directory):os.makedirs(directory)
if os.path.exists(args.IEF_DATABASE) and \ os.path.isfile(args.IEF_DATABASE):
   main(args.IEF_DATABASE, args.OUTPUT_CSV)
   else: print("Supplied input file {} does not exist or is not a " "file".format(args.IEF_DATABASE))
sys.exit(1)

ここで、次のようにSQLiteデータベースに接続して、カーソルを介してクエリを実行します-

def main(database, out_csv):
   print("[+] Connecting to SQLite database")
   conn = sqlite3.connect(database)
   c = conn.cursor()

次のコード行を使用して、Yahooメールの連絡先キャッシュレコードのインスタンスをフェッチできます-

print("Querying IEF database for Yahoo Contact Fragments from " "the Chrome Cache Records Table")
   try:
      c.execute("select * from 'Chrome Cache Records' where URL like " "'https://data.mail.yahoo.com" "/classicab/v2/contacts/?format=json%'")
   except sqlite3.OperationalError:
      print("Received an error querying the database --    database may be" "corrupt or not have a Chrome Cache Records table")
      sys.exit(2)

ここで、上記のクエリから返されたタプルのリストは、次のように変数に保存されます。

contact_cache = c.fetchall()
contact_data = process_contacts(contact_cache)
write_csv(contact_data, out_csv)

ここでは、2つの方法を使用することに注意してください。 process_contacts() 結果リストを設定し、各連絡先キャッシュレコードを反復処理します。 json.loads() テーブルから抽出されたJSONデータを変数に保存してさらに操作する-

def process_contacts(contact_cache):
   print("[+] Processing {} cache files matching Yahoo contact cache " " data".format(len(contact_cache)))
   results = []
   
   for contact in contact_cache:
      url = contact[0]
      first_visit = contact[1]
      last_visit = contact[2]
      last_sync = contact[3]
      loc = contact[8]
	   contact_json = json.loads(contact[7].decode())
      total_contacts = contact_json["total"]
      total_count = contact_json["count"]
      
      if "contacts" not in contact_json:
         continue
      for c in contact_json["contacts"]:
         name, anni, bday, emails, phones, links = ("", "", "", "", "", "")
            if "name" in c:
            name = c["name"]["givenName"] + " " + \ c["name"]["middleName"] + " " + c["name"]["familyName"]
            
            if "anniversary" in c:
            anni = c["anniversary"]["month"] + \"/" + c["anniversary"]["day"] + "/" + \c["anniversary"]["year"]
            
            if "birthday" in c:
            bday = c["birthday"]["month"] + "/" + \c["birthday"]["day"] + "/" + c["birthday"]["year"]
            
            if "emails" in c:
               emails = ', '.join([x["ep"] for x in c["emails"]])
            
            if "phones" in c:
               phones = ', '.join([x["ep"] for x in c["phones"]])
            
            if "links" in c:
              links = ', '.join([x["ep"] for x in c["links"]])

会社、タイトル、メモの場合、getメソッドは次のように使用されます-

company = c.get("company", "")
title = c.get("jobTitle", "")
notes = c.get("notes", "")

ここで、メタデータと抽出されたデータ要素のリストを次のように結果リストに追加しましょう。

results.append([url, first_visit, last_visit, last_sync, loc, name, bday,anni, emails, phones, links, company, title, notes,total_contacts, total_count])
return results

さて、 CSV_Writer() メソッド、CSVファイルにコンテンツを書き込みます-

def write_csv(data, output):
   print("[+] Writing {} contacts to {}".format(len(data), output))
   with open(output, "w", newline="") as csvfile:
      csv_writer = csv.writer(csvfile)
      csv_writer.writerow([
         "URL", "First Visit (UTC)", "Last Visit (UTC)",
         "Last Sync (UTC)", "Location", "Contact Name", "Bday",
         "Anniversary", "Emails", "Phones", "Links", "Company", "Title",
         "Notes", "Total Contacts", "Count of Contacts in Cache"])
      csv_writer.writerows(data)

上記のスクリプトの助けを借りて、IEFデータベースを使用してYahooメールからキャッシュされたデータを処理できます。

前の章では、Pythonを使用したネットワークフォレンジックの概念のいくつかを扱いました。この章では、Pythonを使用したネットワークフォレンジクスをより深いレベルで理解しましょう。

美しいスープによるWebページの保存

ワールドワイドウェブ(WWW)は、独自の情報リソースです。ただし、そのレガシーは、驚くべき速度でコンテンツが失われるため、リスクが高くなります。多くの文化遺産や学術機関、非営利団体、民間企業が関連する問題を調査し、Webアーカイブの技術的ソリューションの開発に貢献してきました。

Webページの保存またはWebアーカイブは、World Wide Webからデータを収集し、データがアーカイブに保存され、将来の研究者、歴史家、および一般の人々が利用できるようにするプロセスです。Webページの保存に進む前に、以下に示すように、Webページの保存に関連するいくつかの重要な問題について説明しましょう。

  • Change in Web Resources − Webリソースは日々変化し続けており、これはWebページの保存における課題です。

  • Large Quantity of Resources − Webページの保存に関連するもう1つの問題は、保存する必要のある大量のリソースです。

  • Integrity − Webページは、その整合性を保護するために、許可されていない修正、削除、または削除から保護する必要があります。

  • Dealing with multimedia data − Webページを保存する一方で、マルチメディアデータも処理する必要があります。そうすると、問題が発生する可能性があります。

  • Providing access −保存に加えて、Webリソースへのアクセスの提供と所有権の問題への対処の問題も解決する必要があります。

この章では、次の名前のPythonライブラリを使用します。 Beautiful Soup Webページの保存用。

美しいスープとは何ですか?

Beautiful Soupは、HTMLおよびXMLファイルからデータを引き出すためのPythonライブラリです。で使用できますurlibWebページ自体をフェッチできないため、スープオブジェクトを作成するために入力(ドキュメントまたはURL)が必要なためです。これについて詳しくは、www.crummy.com / software / BeautifulSoup / bs4 / doc /をご覧ください。

使用する前に、次のコマンドを使用してサードパーティのライブラリをインストールする必要があることに注意してください-

pip install bs4

次に、Anacondaパッケージマネージャーを使用して、次のようにBeautifulSoupをインストールできます-

conda install -c anaconda beautifulsoup4

Webページを保存するためのPythonスクリプト

Beautiful Soupと呼ばれるサードパーティライブラリを使用してWebページを保存するためのPythonスクリプトについて、ここで説明します-

まず、必要なライブラリを次のようにインポートします-

from __future__ import print_function
import argparse

from bs4 import BeautifulSoup, SoupStrainer
from datetime import datetime

import hashlib
import logging
import os
import ssl
import sys
from urllib.request import urlopen

import urllib.error
logger = logging.getLogger(__name__)

このスクリプトは2つの位置引数を取ることに注意してください。1つは保持されるURLであり、もう1つは以下に示すように目的の出力ディレクトリです。

if __name__ == "__main__":
   parser = argparse.ArgumentParser('Web Page preservation')
   parser.add_argument("DOMAIN", help="Website Domain")
   parser.add_argument("OUTPUT_DIR", help="Preservation Output Directory")
   parser.add_argument("-l", help="Log file path",
   default=__file__[:-3] + ".log")
   args = parser.parse_args()

次に、ループ内にあるファイルとストリームハンドラーを指定してスクリプトのログを設定し、次のように取得プロセスを文書化します。

logger.setLevel(logging.DEBUG)
msg_fmt = logging.Formatter("%(asctime)-15s %(funcName)-10s""%(levelname)-8s %(message)s")
strhndl = logging.StreamHandler(sys.stderr)
strhndl.setFormatter(fmt=msg_fmt)
fhndl = logging.FileHandler(args.l, mode='a')
fhndl.setFormatter(fmt=msg_fmt)

logger.addHandler(strhndl)
logger.addHandler(fhndl)
logger.info("Starting BS Preservation")
logger.debug("Supplied arguments: {}".format(sys.argv[1:]))
logger.debug("System " + sys.platform)
logger.debug("Version " + sys.version)

ここで、次のように、目的の出力ディレクトリで入力検証を実行しましょう。

if not os.path.exists(args.OUTPUT_DIR):
   os.makedirs(args.OUTPUT_DIR)
main(args.DOMAIN, args.OUTPUT_DIR)

ここで、を定義します main() 次のように、入力URLの追加の検証とともに、実際の名前の前に不要な要素を削除することにより、Webサイトのベース名を抽出する関数-

def main(website, output_dir):
   base_name = website.replace("https://", "").replace("http://", "").replace("www.", "")
   link_queue = set()
   
   if "http://" not in website and "https://" not in website:
      logger.error("Exiting preservation - invalid user input: {}".format(website))
      sys.exit(1)
   logger.info("Accessing {} webpage".format(website))
   context = ssl._create_unverified_context()

次に、urlopen()メソッドを使用してURLとの接続を開く必要があります。次のようにtry-exceptブロックを使用しましょう-

try:
   index = urlopen(website, context=context).read().decode("utf-8")
except urllib.error.HTTPError as e:
   logger.error("Exiting preservation - unable to access page: {}".format(website))
   sys.exit(2)
logger.debug("Successfully accessed {}".format(website))

コードの次の行には、以下で説明する3つの関数が含まれています-

  • write_output() 最初のWebページを出力ディレクトリに書き込む

  • find_links() このウェブページ上のリンクを識別する機能

  • recurse_pages() Webページ上のすべてのリンクを繰り返して検出する機能。

write_output(website, index, output_dir)
link_queue = find_links(base_name, index, link_queue)
logger.info("Found {} initial links on webpage".format(len(link_queue)))
recurse_pages(website, link_queue, context, output_dir)
logger.info("Completed preservation of {}".format(website))

さて、定義しましょう write_output() 次のような方法-

def write_output(name, data, output_dir, counter=0):
   name = name.replace("http://", "").replace("https://", "").rstrip("//")
   directory = os.path.join(output_dir, os.path.dirname(name))
   
   if not os.path.exists(directory) and os.path.dirname(name) != "":
      os.makedirs(directory)

Webページに関する詳細をログに記録する必要があります。次に、を使用してデータのハッシュをログに記録します。 hash_data() 次のような方法-

logger.debug("Writing {} to {}".format(name, output_dir)) logger.debug("Data Hash: {}".format(hash_data(data)))
path = os.path.join(output_dir, name)
path = path + "_" + str(counter)
with open(path, "w") as outfile:
   outfile.write(data)
logger.debug("Output File Hash: {}".format(hash_file(path)))

ここで、定義します hash_data() 私たちが読んだ助けを借りた方法 UTF-8 エンコードされたデータを生成し、 SHA-256 次のようにそれのハッシュ-

def hash_data(data):
   sha256 = hashlib.sha256()
   sha256.update(data.encode("utf-8"))
   return sha256.hexdigest()
def hash_file(file):
   sha256 = hashlib.sha256()
   with open(file, "rb") as in_file:
      sha256.update(in_file.read())
return sha256.hexdigest()

さて、作成しましょう Beautifulsoup 下のWebページデータからのオブジェクト find_links() 次のような方法-

def find_links(website, page, queue):
   for link in BeautifulSoup(page, "html.parser",parse_only = SoupStrainer("a", href = True)):
      if website in link.get("href"):
         if not os.path.basename(link.get("href")).startswith("#"):
            queue.add(link.get("href"))
   return queue

今、私たちは定義する必要があります recurse_pages() 次のように、WebサイトのURL、現在のリンクキュー、未確認のSSLコンテキスト、および出力ディレクトリの入力を提供する方法。

def recurse_pages(website, queue, context, output_dir):
   processed = []
   counter = 0
   
   while True:
      counter += 1
      if len(processed) == len(queue):
         break
      for link in queue.copy(): if link in processed:
         continue
	   processed.append(link)
      try:
      page = urlopen(link,      context=context).read().decode("utf-8")
      except urllib.error.HTTPError as e:
         msg = "Error accessing webpage: {}".format(link)
         logger.error(msg)
         continue

次に、リンク名、ページデータ、出力ディレクトリ、およびカウンターを次のように渡して、アクセスした各Webページの出力をファイルに書き込みます。

write_output(link, page, output_dir, counter)
queue = find_links(website, page, queue)
logger.info("Identified {} links throughout website".format(
   len(queue)))

ここで、WebサイトのURL、出力ディレクトリ、およびログファイルへのパスを指定してこのスクリプトを実行すると、将来の使用に使用できるそのWebページの詳細が取得されます。

ウイルスハンティング

フォレンジックアナリスト、セキュリティ研究者、インシデント回答者が、有用なソフトウェアとマルウェアの違いをどのように理解できるのか疑問に思ったことはありませんか?答えは質問自体にあります。なぜなら、ハッカーによって急速に生成されるマルウェアについて研究しなければ、研究者や専門家が有用なソフトウェアとマルウェアの違いを区別することはまったく不可能だからです。このセクションでは、VirusShare、このタスクを実行するためのツール。

VirusShareを理解する

VirusShareは、セキュリティ研究者、インシデントレスポンダー、およびフォレンジックアナリストにライブの悪意のあるコードのサンプルを提供するマルウェアサンプルの最大の個人所有コレクションです。3,000万を超えるサンプルが含まれています。

VirusShareの利点は、無料で入手できるマルウェアハッシュのリストです。誰でもこれらのハッシュを使用して非常に包括的なハッシュセットを作成し、それを使用して潜在的に悪意のあるファイルを識別することができます。ただし、VirusShareを使用する前に、次のサイトにアクセスすることをお勧めします。https://virusshare.com 詳細については。

Pythonを使用してVirusShareから改行区切りのハッシュリストを作成する

VirusShareのハッシュリストは、X-waysやEnCaseなどのさまざまなフォレンジックツールで使用できます。以下で説明するスクリプトでは、VirusShareからのハッシュのリストのダウンロードを自動化して、改行で区切られたハッシュリストを作成します。

このスクリプトには、サードパーティのPythonライブラリが必要です tqdm 次のようにダウンロードできます-

pip install tqdm

このスクリプトでは、最初にVirusShareハッシュページを読み取り、最新のハッシュリストを動的に識別することに注意してください。次に、プログレスバーを初期化し、目的の範囲でハッシュリストをダウンロードします。

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

from __future__ import print_function

import argparse
import os
import ssl
import sys
import tqdm

from urllib.request import urlopen
import urllib.error

このスクリプトは、ハッシュセットの目的のパスとなる1つの位置引数を取ります-

if __name__ == '__main__':
   parser = argparse.ArgumentParser('Hash set from VirusShare')
   parser.add_argument("OUTPUT_HASH", help = "Output Hashset")
   parser.add_argument("--start", type = int, help = "Optional starting location")
   args = parser.parse_args()

ここで、次のように標準入力検証を実行します-

directory = os.path.dirname(args.OUTPUT_HASH)
if not os.path.exists(directory):
   os.makedirs(directory)
if args.start:
   main(args.OUTPUT_HASH, start=args.start)
else:
   main(args.OUTPUT_HASH)

今、私たちは定義する必要があります main() と機能する **kwargs これにより辞書が作成されるため、引数として、以下に示すように、提供されたキー引数をサポートするために参照できます。

def main(hashset, **kwargs):
   url = "https://virusshare.com/hashes.4n6"
   print("[+] Identifying hash set range from {}".format(url))
   context = ssl._create_unverified_context()

次に、を使用してVirusShareハッシュページを開く必要があります。 urlib.request.urlopen()方法。次のようにtry-exceptブロックを使用します-

try:
   index = urlopen(url, context = context).read().decode("utf-8")
except urllib.error.HTTPError as e:
   print("[-] Error accessing webpage - exiting..")
   sys.exit(1)

次に、ダウンロードしたページから最新のハッシュリストを特定します。これを行うには、HTMLの最後のインスタンスを見つけますhrefVirusShareハッシュリストへのタグ付け。次のコード行で実行できます-

tag = index.rfind(r'a href = "hashes/VirusShare_')
stop = int(index[tag + 27: tag + 27 + 5].lstrip("0"))

if "start" not in kwa<rgs:
   start = 0
else:
   start = kwargs["start"]

if start < 0 or start > stop:
   print("[-] Supplied start argument must be greater than or equal ""to zero but less than the latest hash list, ""currently: {}".format(stop))
sys.exit(2)
print("[+] Creating a hashset from hash lists {} to {}".format(start, stop))
hashes_downloaded = 0

今、私たちは使用します tqdm.trange() 次のようにループとプログレスバーを作成する方法-

for x in tqdm.trange(start, stop + 1, unit_scale=True,desc="Progress"):
   url_hash = "https://virusshare.com/hashes/VirusShare_"\"{}.md5".format(str(x).zfill(5))
   try:
      hashes = urlopen(url_hash, context=context).read().decode("utf-8")
      hashes_list = hashes.split("\n")
   except urllib.error.HTTPError as e:
      print("[-] Error accessing webpage for hash list {}"" - continuing..".format(x))
   continue

上記の手順を正常に実行した後、ハッシュセットテキストファイルを+モードで開き、テキストファイルの下部に追加します。

with open(hashset, "a+") as hashfile:
   for line in hashes_list:
   if not line.startswith("#") and line != "":
      hashes_downloaded += 1
      hashfile.write(line + '\n')
   print("[+] Finished downloading {} hashes into {}".format(
      hashes_downloaded, hashset))

上記のスクリプトを実行すると、MD5ハッシュ値をテキスト形式で含む最新のハッシュリストが表示されます。

前の章では、ネットワークフォレンジックの重要性とプロセス、および関連する概念について説明しました。この章では、デジタルフォレンジックにおける電子メールの役割とPythonを使用した電子メールの調査について学びましょう。

調査における電子メールの役割

電子メールはビジネスコミュニケーションにおいて非常に重要な役割を果たし、インターネット上で最も重要なアプリケーションの1つとして浮上しています。コンピュータだけでなく、携帯電話やタブレットなどの他の電子機器からもメッセージやドキュメントを送信するのに便利なモードです。

電子メールのマイナス面は、犯罪者が会社に関する重要な情報を漏らす可能性があることです。したがって、デジタルフォレンジックにおける電子メールの役割は近年増加しています。デジタルフォレンジックでは、電子メールは重要な証拠と見なされており、電子メールヘッダー分析はフォレンジックプロセス中に証拠を収集するために重要になっています。

調査員は、電子メールフォレンジックを実行する際に次の目標を持っています-

  • 主な犯罪者を特定する
  • 必要な証拠を収集する
  • 調査結果を提示するには
  • ケースを作成するには

電子メールフォレンジックにおける課題

現代のコミュニケーションのほとんどは電子メールに依存しているため、電子メールフォレンジックは調査において非常に重要な役割を果たします。ただし、電子メールのフォレンジック調査担当者は、調査中に次の課題に直面する可能性があります-

偽のメール

電子メールフォレンジックの最大の課題は、ヘッダーなどを操作およびスクリプト化することによって作成された偽の電子メールの使用です。このカテゴリでは、犯罪者は、登録ユーザーが期限切れの一時アドレスで電子メールを受信できるようにするサービスである一時電子メールも使用します。一定期間後。

なりすまし

電子メールフォレンジックにおけるもう1つの課題は、犯罪者が他人の電子メールを提示していたなりすましです。この場合、マシンは偽のIPアドレスと元のIPアドレスの両方を受け取ります。

匿名の再メール

ここで、電子メールサーバーは、さらに転送する前に、電子メールメッセージから識別情報を取り除きます。これは、電子メール調査のもう1つの大きな課題につながります。

電子メールフォレンジック調査で使用される手法

電子メールフォレンジックは、メッセージの実際の送信者と受信者を特定するための証拠としての電子メールのソースとコンテンツの調査であり、送信の日付/時刻や送信者の意図などの他の情報も含まれます。これには、メタデータの調査、ポートスキャン、およびキーワード検索が含まれます。

電子メールのフォレンジック調査に使用できる一般的な手法のいくつかは次のとおりです。

  • ヘッダー分析
  • サーバー調査
  • ネットワークデバイスの調査
  • 送信者メーラーの指紋
  • ソフトウェア組み込み識別子

次のセクションでは、電子メール調査の目的でPythonを使用して情報を取得する方法を学習します。

EMLファイルからの情報の抽出

EMLファイルは基本的にファイル形式の電子メールであり、電子メールメッセージの保存に広く使用されています。これらは、Microsoft Outlook、Outlook Express、WindowsLiveメールなどの複数の電子メールクライアント間で互換性のある構造化テキストファイルです。

EMLファイルには、電子メールヘッダー、本文コンテンツ、添付ファイルデータがプレーンテキストとして保存されます。base64を使用してバイナリデータをエンコードし、Quoted-Printable(QP)エンコードを使用してコンテンツ情報を格納します。EMLファイルから情報を抽出するために使用できるPythonスクリプトを以下に示します-

まず、以下に示すように、次のPythonライブラリをインポートします-

from __future__ import print_function
from argparse import ArgumentParser, FileType
from email import message_from_file

import os
import quopri
import base64

上記のライブラリでは、 quopriEMLファイルからQPエンコードされた値をデコードするために使用されます。base64でエンコードされたデータは、base64 図書館。

次に、コマンドラインハンドラーの引数を指定しましょう。ここでは、以下に示すように、EMLファイルへのパスとなる引数を1つだけ受け入れることに注意してください。

if __name__ == '__main__':
   parser = ArgumentParser('Extracting information from EML file')
   parser.add_argument("EML_FILE",help="Path to EML File", type=FileType('r'))
   args = parser.parse_args()
   main(args.EML_FILE)

今、私たちは定義する必要があります main() 名前の付いたメソッドを使用する関数 message_from_file()電子メールライブラリからオブジェクトのようなファイルを読み取ります。ここでは、次の名前の結果変数を使用して、ヘッダー、本文のコンテンツ、添付ファイル、およびその他のペイロード情報にアクセスします。emlfile 以下のコードに示すように-

def main(input_file):
   emlfile = message_from_file(input_file)
   for key, value in emlfile._headers:
      print("{}: {}".format(key, value))
print("\nBody\n")

if emlfile.is_multipart():
   for part in emlfile.get_payload():
      process_payload(part)
else:
   process_payload(emlfile[1])

今、私たちは定義する必要があります process_payload() を使用してメッセージ本文のコンテンツを抽出する方法 get_payload()方法。QPでエンコードされたデータを使用してデコードしますquopri.decodestring()関数。また、コンテンツのMIMEタイプをチェックして、電子メールの保存を適切に処理できるようにします。以下のコードを確認してください-

def process_payload(payload):
   print(payload.get_content_type() + "\n" + "=" * len(payload.get_content_type()))
   body = quopri.decodestring(payload.get_payload())
   
   if payload.get_charset():
      body = body.decode(payload.get_charset())
else:
   try:
      body = body.decode()
   except UnicodeDecodeError:
      body = body.decode('cp1252')

if payload.get_content_type() == "text/html":
   outfile = os.path.basename(args.EML_FILE.name) + ".html"
   open(outfile, 'w').write(body)
elif payload.get_content_type().startswith('application'):
   outfile = open(payload.get_filename(), 'wb')
   body = base64.b64decode(payload.get_payload())
   outfile.write(body)
   outfile.close()
   print("Exported: {}\n".format(outfile.name))
else:
   print(body)

上記のスクリプトを実行した後、コンソールでさまざまなペイロードとともにヘッダー情報を取得します。

Pythonを使用したMSGファイルの分析

電子メールメッセージにはさまざまな形式があります。MSGは、MicrosoftOutlookおよびExchangeで使用されるそのような形式の1つです。MSG拡張子の付いたファイルには、ヘッダーとメインメッセージ本文のプレーンASCIIテキスト、およびハイパーリンクと添付ファイルが含まれる場合があります。

このセクションでは、OutlookAPIを使用してMSGファイルから情報を抽出する方法を学習します。次のPythonスクリプトはWindowsでのみ機能することに注意してください。このために、という名前のサードパーティのPythonライブラリをインストールする必要がありますpywin32 次のように-

pip install pywin32

次に、示されているコマンドを使用して次のライブラリをインポートします-

from __future__ import print_function
from argparse import ArgumentParser

import os
import win32com.client
import pywintypes

ここで、コマンドラインハンドラーの引数を指定しましょう。ここでは、次のように2つの引数を受け入れます。1つはMSGファイルへのパスで、もう1つは目的の出力フォルダーです。

if __name__ == '__main__':
   parser = ArgumentParser(‘Extracting information from MSG file’)
   parser.add_argument("MSG_FILE", help="Path to MSG file")
   parser.add_argument("OUTPUT_DIR", help="Path to output folder")
   args = parser.parse_args()
   out_dir = args.OUTPUT_DIR
   
   if not os.path.exists(out_dir):
      os.makedirs(out_dir)
   main(args.MSG_FILE, args.OUTPUT_DIR)

今、私たちは定義する必要があります main() 呼び出す関数 win32com 設定用のライブラリ Outlook API これにより、 MAPI 名前空間。

def main(msg_file, output_dir):
   mapi = win32com.client.Dispatch("Outlook.Application").GetNamespace("MAPI")
   msg = mapi.OpenSharedItem(os.path.abspath(args.MSG_FILE))
   
   display_msg_attribs(msg)
   display_msg_recipients(msg)
   
   extract_msg_body(msg, output_dir)
   extract_attachments(msg, output_dir)

ここで、このスクリプトで使用しているさまざまな関数を定義します。以下のコードは、display_msg_attribs() 件名、宛先、BCC、CC、サイズ、送信者名、送信済みなど、メッセージのさまざまな属性を表示できるようにする関数。

def display_msg_attribs(msg):
   attribs = [
      'Application', 'AutoForwarded', 'BCC', 'CC', 'Class',
      'ConversationID', 'ConversationTopic', 'CreationTime',
      'ExpiryTime', 'Importance', 'InternetCodePage', 'IsMarkedAsTask',
      'LastModificationTime', 'Links','ReceivedTime', 'ReminderSet',
      'ReminderTime', 'ReplyRecipientNames', 'Saved', 'Sender',
      'SenderEmailAddress', 'SenderEmailType', 'SenderName', 'Sent',
      'SentOn', 'SentOnBehalfOfName', 'Size', 'Subject',
      'TaskCompletedDate', 'TaskDueDate', 'To', 'UnRead'
   ]
   print("\nMessage Attributes")
   for entry in attribs:
      print("{}: {}".format(entry, getattr(msg, entry, 'N/A')))

ここで、 display_msg_recipeints() メッセージを繰り返し処理し、受信者の詳細を表示する関数。

def display_msg_recipients(msg):
   recipient_attrib = ['Address', 'AutoResponse', 'Name', 'Resolved', 'Sendable']
   i = 1
   
   while True:
   try:
      recipient = msg.Recipients(i)
   except pywintypes.com_error:
      break
   print("\nRecipient {}".format(i))
   print("=" * 15)
   
   for entry in recipient_attrib:
      print("{}: {}".format(entry, getattr(recipient, entry, 'N/A')))
   i += 1

次に、定義します extract_msg_body() メッセージから本文コンテンツ、HTML、およびプレーンテキストを抽出する関数。

def extract_msg_body(msg, out_dir):
   html_data = msg.HTMLBody.encode('cp1252')
   outfile = os.path.join(out_dir, os.path.basename(args.MSG_FILE))
   
   open(outfile + ".body.html", 'wb').write(html_data)
   print("Exported: {}".format(outfile + ".body.html"))
   body_data = msg.Body.encode('cp1252')
   
   open(outfile + ".body.txt", 'wb').write(body_data)
   print("Exported: {}".format(outfile + ".body.txt"))

次に、を定義します extract_attachments() 添付ファイルデータを目的の出力ディレクトリにエクスポートする関数。

def extract_attachments(msg, out_dir):
   attachment_attribs = ['DisplayName', 'FileName', 'PathName', 'Position', 'Size']
   i = 1 # Attachments start at 1
   
   while True:
      try:
         attachment = msg.Attachments(i)
   except pywintypes.com_error:
      break

すべての関数が定義されたら、次のコード行を使用してすべての属性をコンソールに出力します-

print("\nAttachment {}".format(i))
print("=" * 15)
   
for entry in attachment_attribs:
   print('{}: {}'.format(entry, getattr(attachment, entry,"N/A")))
outfile = os.path.join(os.path.abspath(out_dir),os.path.split(args.MSG_FILE)[-1])
   
if not os.path.exists(outfile):
os.makedirs(outfile)
outfile = os.path.join(outfile, attachment.FileName)
attachment.SaveAsFile(outfile)
   
print("Exported: {}".format(outfile))
i += 1

上記のスクリプトを実行した後、出力ディレクトリ内のいくつかのファイルとともに、コンソールウィンドウにメッセージとその添付ファイルの属性を取得します。

Pythonを使用してGoogleデータエクスポートからMBOXファイルを構造化する

MBOXファイルは、メッセージを分割する特別なフォーマットのテキストファイルです。これらは、UNIXシステム、Thunderbolt、およびGoogleデータエクスポートに関連して見つかることがよくあります。

このセクションでは、Pythonスクリプトを確認します。このスクリプトでは、Googleデータエクスポートから取得したMBOXファイルを構造化します。ただし、その前に、GoogleアカウントまたはGmailアカウントを使用してこれらのMBOXファイルを生成する方法を知っておく必要があります。

GoogleアカウントメールボックスをMBX形式で取得する

Googleアカウントのメールボックスを取得することは、Gmailアカウントのバックアップを取ることを意味します。バックアップは、さまざまな個人的または職業上の理由で取得できます。GoogleはGmailデータのバックアップを提供していることに注意してください。GoogleアカウントのメールボックスをMBOX形式で取得するには、以下の手順に従う必要があります-

  • 開いた My account ダッシュボード。

  • [個人情報とプライバシー]セクションに移動し、[コンテンツの管理]リンクを選択します。

  • 新しいアーカイブを作成することも、既存のアーカイブを管理することもできます。クリックすると、CREATE ARCHIVE リンクをクリックすると、含めたいGoogle製品ごとにいくつかのチェックボックスが表示されます。

  • 製品を選択した後、リストから選択する配信方法とともに、アーカイブのファイルタイプと最大サイズを自由に選択できます。

  • 最後に、このバックアップをMBOX形式で取得します。

Pythonコード

これで、上記のMBOXファイルは、以下に示すようにPythonを使用して構造化できます。

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

from __future__ import print_function
from argparse import ArgumentParser

import mailbox
import os
import time
import csv
from tqdm import tqdm

import base64

すべてのライブラリは、以前のスクリプトで使用および説明されていますが、 mailbox MBOXファイルの解析に使用されるライブラリ。

ここで、コマンドラインハンドラーの引数を指定します。ここでは、2つの引数を受け入れます。1つはMBOXファイルへのパスで、もう1つは目的の出力フォルダーです。

if __name__ == '__main__':
   parser = ArgumentParser('Parsing MBOX files')
   parser.add_argument("MBOX", help="Path to mbox file")
   parser.add_argument(
      "OUTPUT_DIR",help = "Path to output directory to write report ""and exported content")
   args = parser.parse_args()
   main(args.MBOX, args.OUTPUT_DIR)

今、定義します main() 関数と呼び出し mbox パスを指定することでMBOXファイルを解析できるメールボックスライブラリのクラス-

def main(mbox_file, output_dir):
   print("Reading mbox file")
   mbox = mailbox.mbox(mbox_file, factory=custom_reader)
   print("{} messages to parse".format(len(mbox)))

次に、のリーダーメソッドを定義します mailbox 次のようなライブラリ-

def custom_reader(data_stream):
   data = data_stream.read()
   try:
      content = data.decode("ascii")
   except (UnicodeDecodeError, UnicodeEncodeError) as e:
      content = data.decode("cp1252", errors="replace")
   return mailbox.mboxMessage(content)

次に、次のようにさらに処理するための変数をいくつか作成します。

parsed_data = []
attachments_dir = os.path.join(output_dir, "attachments")

if not os.path.exists(attachments_dir):
   os.makedirs(attachments_dir)
columns = [
   "Date", "From", "To", "Subject", "X-Gmail-Labels", "Return-Path", "Received", 
   "Content-Type", "Message-ID","X-GM-THRID", "num_attachments_exported", "export_path"]

次に、 tqdm プログレスバーを生成し、次のように反復プロセスを追跡します-

for message in tqdm(mbox):
   msg_data = dict()
   header_data = dict(message._headers)
for hdr in columns:
   msg_data[hdr] = header_data.get(hdr, "N/A")

ここで、天気メッセージにペイロードがあるかどうかを確認します。持っている場合は、定義しますwrite_payload() 次のような方法-

if len(message.get_payload()):
   export_path = write_payload(message, attachments_dir)
   msg_data['num_attachments_exported'] = len(export_path)
   msg_data['export_path'] = ", ".join(export_path)

ここで、データを追加する必要があります。次に、create_report() 次のような方法-

parsed_data.append(msg_data)
create_report(
   parsed_data, os.path.join(output_dir, "mbox_report.csv"), columns)
def write_payload(msg, out_dir):
   pyld = msg.get_payload()
   export_path = []
   
if msg.is_multipart():
   for entry in pyld:
      export_path += write_payload(entry, out_dir)
else:
   content_type = msg.get_content_type()
   if "application/" in content_type.lower():
      content = base64.b64decode(msg.get_payload())
      export_path.append(export_content(msg, out_dir, content))
   elif "image/" in content_type.lower():
      content = base64.b64decode(msg.get_payload())
      export_path.append(export_content(msg, out_dir, content))

   elif "video/" in content_type.lower():
      content = base64.b64decode(msg.get_payload())
      export_path.append(export_content(msg, out_dir, content))
   elif "audio/" in content_type.lower():
      content = base64.b64decode(msg.get_payload())
      export_path.append(export_content(msg, out_dir, content))
   elif "text/csv" in content_type.lower():
      content = base64.b64decode(msg.get_payload())
      export_path.append(export_content(msg, out_dir, content))
   elif "info/" in content_type.lower():
      export_path.append(export_content(msg, out_dir,
      msg.get_payload()))
   elif "text/calendar" in content_type.lower():
      export_path.append(export_content(msg, out_dir,
      msg.get_payload()))
   elif "text/rtf" in content_type.lower():
      export_path.append(export_content(msg, out_dir,
      msg.get_payload()))
   else:
      if "name=" in msg.get('Content-Disposition', "N/A"):
         content = base64.b64decode(msg.get_payload())
      export_path.append(export_content(msg, out_dir, content))
   elif "name=" in msg.get('Content-Type', "N/A"):
      content = base64.b64decode(msg.get_payload())
      export_path.append(export_content(msg, out_dir, content))
return export_path

上記のif-elseステートメントは理解しやすいことに注意してください。次に、ファイル名をから抽出するメソッドを定義する必要があります。msg 次のようなオブジェクト-

def export_content(msg, out_dir, content_data):
   file_name = get_filename(msg)
   file_ext = "FILE"
   
   if "." in file_name: file_ext = file_name.rsplit(".", 1)[-1]
   file_name = "{}_{:.4f}.{}".format(file_name.rsplit(".", 1)[0], time.time(), file_ext)
   file_name = os.path.join(out_dir, file_name)

これで、次のコード行を使用して、実際にファイルをエクスポートできます-

if isinstance(content_data, str):
   open(file_name, 'w').write(content_data)
else:
   open(file_name, 'wb').write(content_data)
return file_name

それでは、ファイル名を抽出する関数を定義しましょう。 message これらのファイルの名前を次のように正確に表すには-

def get_filename(msg):
   if 'name=' in msg.get("Content-Disposition", "N/A"):
      fname_data = msg["Content-Disposition"].replace("\r\n", " ")
      fname = [x for x in fname_data.split("; ") if 'name=' in x]
      file_name = fname[0].split("=", 1)[-1]
   elif 'name=' in msg.get("Content-Type", "N/A"):
      fname_data = msg["Content-Type"].replace("\r\n", " ")
      fname = [x for x in fname_data.split("; ") if 'name=' in x]
      file_name = fname[0].split("=", 1)[-1]
   else:
      file_name = "NO_FILENAME"
   fchars = [x for x in file_name if x.isalnum() or x.isspace() or x == "."]
   return "".join(fchars)

これで、を定義することでCSVファイルを作成できます。 create_report() 次のように機能します-

def create_report(output_data, output_file, columns):
   with open(output_file, 'w', newline="") as outfile:
      csvfile = csv.DictWriter(outfile, columns)
      csvfile.writeheader()
      csvfile.writerows(output_data)

上記のスクリプトを実行すると、CSVレポートと添付ファイルでいっぱいのディレクトリが表示されます。

この章では、Microsoft Windowsフォレンジックに関連するさまざまな概念と、調査員が調査プロセスから取得できる重要な成果物について説明します。

前書き

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

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

Windowsアーティファクトは、次の理由により重要性を帯びています-

  • 世界のトラフィックの約90%は、オペレーティングシステムとしてWindowsを使用しているコンピューターから来ています。そのため、デジタルフォレンジックの審査官にとってWindowsアーティファクトは非常に重要です。

  • Windowsオペレーティングシステムは、コンピュータシステムでのユーザーアクティビティに関連するさまざまな種類の証拠を保存します。これは、デジタルフォレンジックにおけるWindowsアーティファクトの重要性を示すもう1つの理由です。

  • 多くの場合、調査員は、ユーザーが作成したデータなど、古くて伝統的な領域を中心に調査を展開します。Windowsアーティファクトは、システムで作成されたデータやアーティファクトなどの非伝統的な領域に向けて調査を導くことができます。

  • 非常に豊富なアーティファクトがWindowsによって提供されており、調査者だけでなく、非公式の調査を行う企業や個人にも役立ちます。

  • 近年のサイバー犯罪の増加は、Windowsアーティファクトが重要であるもう1つの理由です。

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

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

ごみ箱

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

ごみ箱の証拠の保存は、Windowsのバージョンによって異なることに注意してください。次のPythonスクリプトでは、2つのファイルを作成するWindows7を扱います。$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つの引数を受け入れることに注意してください。1つ目は証拠ファイルへのパス、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() を受け入れる関数 tsk_util オブジェクトとのリスト $I 以下に示すように、ファイル-

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

今、私たちは定義します write_csv() 次のように、処理された結果を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付箋は、書くという現実の習慣をペンと紙に置き換えます。これらのメモは、色やフォントなどのさまざまなオプションを使用してデスクトップに表示されていました。Windows7では、付箋ファイルは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() ファイルを処理する関数を実行してから、RTFファイルを write_note_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)

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

まず最初に定義します create_file_like_obj() ファイルのサイズを読み取るための関数 pytskファイルオブジェクト。次に、定義します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

次に、定義してRTFファイルを作成します write_note_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)

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

レジストリファイル

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

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

Windowsレジストリから情報を抽出するには、以下の手順に従います。

  • まず、名前とパスで処理するレジストリハイブを見つけます。

  • 次に、StringIOモジュールとRegistryモジュールを使用してこれらのファイルを開きます。

  • 最後に、すべてのハイブを処理し、解析された値をコンソールに出力して解釈する必要があります。

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つの引数を受け入れます。1つ目は証拠ファイルへのパス、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)

今、私たちは定義します main() 検索機能 SYSTEM そして SOFTWARE 内のじんましん /Windows/System32/config 次のようにフォルダ-

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

次の方法で処理できます SOFTWARE ハイブ−

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レジストリファイルに保存されているメタデータを取得します。

この章では、Windowsのいくつかのより重要なアーティファクトと、Pythonを使用したそれらの抽出方法について説明します。

ユーザーアクティビティ

持っているウィンドウ NTUSER.DATさまざまなユーザーアクティビティを保存するためのファイル。すべてのユーザープロファイルは次のようなハイブを持っていますNTUSER.DAT、そのユーザーに特に関連する情報と構成を格納します。したがって、フォレンジックアナリストによる調査の目的に非常に役立ちます。

次のPythonスクリプトは、のキーのいくつかを解析します NTUSER.DATシステム上のユーザーのアクションを調査するため。先に進む前に、Pythonスクリプトの場合、サードパーティのモジュールをインストールする必要があります。Registry, pytsk3、pyewfおよび Jinja2。pipを使用してインストールできます。

次の手順に従って、から情報を抽出できます。 NTUSER.DAT ファイル-

  • まず、すべてを検索します NTUSER.DAT システム内のファイル。

  • 次に、 WordWheelQuery, TypePath and RunMRU それぞれのキー NTUSER.DAT ファイル。

  • 最後に、すでに処理されたこれらのアーティファクトを、を使用してHTMLレポートに書き込みます。 Jinja2 fmodule。

Pythonコード

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

まず、次のPythonモジュールをインポートする必要があります-

from __future__ import print_function
from argparse import ArgumentParser

import os
import StringIO
import struct

from utility.pytskutil import TSKUtil
from Registry import Registry
import jinja2

ここで、コマンドラインハンドラーの引数を指定します。ここでは、3つの引数を受け入れます。1つ目は証拠ファイルへのパス、2つ目は証拠ファイルのタイプ、3つ目は以下に示すようにHTMLレポートへの目的の出力パスです。

if __name__ == '__main__':
   parser = argparse.ArgumentParser('Information from user activities')
   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',help = "Path to report file")
   args = parser.parse_args()
   main(args.EVIDENCE_FILE, args.IMAGE_TYPE, args.REPORT)

さて、定義しましょう main() すべてを検索するための機能 NTUSER.DAT 示されているように、ファイル-

def main(evidence, image_type, report):
   tsk_util = TSKUtil(evidence, image_type)
   tsk_ntuser_hives = tsk_util.recurse_files('ntuser.dat','/Users', 'equals')
   
   nt_rec = {
      'wordwheel': {'data': [], 'title': 'WordWheel Query'},
      'typed_path': {'data': [], 'title': 'Typed Paths'},
      'run_mru': {'data': [], 'title': 'Run MRU'}
   }

今、私たちはで鍵を見つけようとします NTUSER.DAT ファイルを見つけたら、以下に示すようにユーザー処理関数を定義します-

for ntuser in tsk_ntuser_hives:
   uname = ntuser[1].split("/")

open_ntuser = open_file_as_reg(ntuser[2])
try:
   explorer_key = open_ntuser.root().find_key("Software").find_key("Microsoft")
      .find_key("Windows").find_key("CurrentVersion").find_key("Explorer")
   except Registry.RegistryKeyNotFoundException:
      continue
   nt_rec['wordwheel']['data'] += parse_wordwheel(explorer_key, uname)
   nt_rec['typed_path']['data'] += parse_typed_paths(explorer_key, uname)
   nt_rec['run_mru']['data'] += parse_run_mru(explorer_key, uname)
   nt_rec['wordwheel']['headers'] = \ nt_rec['wordwheel']['data'][0].keys()
   nt_rec['typed_path']['headers'] = \ nt_rec['typed_path']['data'][0].keys()
   nt_rec['run_mru']['headers'] = \ nt_rec['run_mru']['data'][0].keys()

次に、辞書オブジェクトとそのパスをに渡します write_html() 次のような方法-

write_html(report, nt_rec)

次に、メソッドを定義します。 pytsk ファイルハンドルを作成し、を介してレジストリクラスに読み込みます。 StringIO クラス。

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)

次に、解析して処理する関数を定義します WordWheelQuery からのキー NTUSER.DAT 次のようにファイル-

def parse_wordwheel(explorer_key, username):
   try:
      wwq = explorer_key.find_key("WordWheelQuery")
   except Registry.RegistryKeyNotFoundException:
      return []
   mru_list = wwq.value("MRUListEx").value()
   mru_order = []
   
   for i in xrange(0, len(mru_list), 2):
      order_val = struct.unpack('h', mru_list[i:i + 2])[0]
   if order_val in mru_order and order_val in (0, -1):
      break
   else:
      mru_order.append(order_val)
   search_list = []
   
   for count, val in enumerate(mru_order):
      ts = "N/A"
      if count == 0:
         ts = wwq.timestamp()
      search_list.append({
         'timestamp': ts,
         'username': username,
         'order': count,
         'value_name': str(val),
         'search': wwq.value(str(val)).value().decode("UTF-16").strip("\x00")
})
   return search_list

次に、解析して処理する関数を定義します TypedPaths からのキー NTUSER.DAT 次のようにファイル-

def parse_typed_paths(explorer_key, username):
   try:
      typed_paths = explorer_key.find_key("TypedPaths")
   except Registry.RegistryKeyNotFoundException:
      return []
   typed_path_details = []
   
   for val in typed_paths.values():
      typed_path_details.append({
         "username": username,
         "value_name": val.name(),
         "path": val.value()
      })
   return typed_path_details

次に、解析して処理する関数を定義します RunMRU からのキー NTUSER.DAT 次のようにファイル-

def parse_run_mru(explorer_key, username):
   try:
      run_mru = explorer_key.find_key("RunMRU")
   except Registry.RegistryKeyNotFoundException:
      return []
   
   if len(run_mru.values()) == 0:
      return []
   mru_list = run_mru.value("MRUList").value()
   mru_order = []
   
   for i in mru_list:
      mru_order.append(i)
   mru_details = []
   
   for count, val in enumerate(mru_order):
      ts = "N/A"
      if count == 0:
         ts = run_mru.timestamp()
      mru_details.append({
         "username": username,
         "timestamp": ts,
         "order": count,
         "value_name": val,
         "run_statement": run_mru.value(val).value()
      })
   return mru_details

これで、次の関数がHTMLレポートの作成を処理します-

def write_html(outfile, data_dict):
   cwd = os.path.dirname(os.path.abspath(__file__))
   env = jinja2.Environment(loader=jinja2.FileSystemLoader(cwd))
   template = env.get_template("user_activity.html")
   rendering = template.render(nt_data=data_dict)
   
   with open(outfile, 'w') as open_outfile:
      open_outfile.write(rendering)

ついにレポート用のHTMLドキュメントを書くことができます。上記のスクリプトを実行した後、HTMLドキュメント形式でNTUSER.DATファイルから情報を取得します。

LINKファイル

ショートカットファイルは、ユーザーまたはオペレーティングシステムが、接続されたストレージなどのシステムドライブから頻繁に使用、ダブルクリック、またはアクセスされるファイルのショートカットファイルを作成するときに作成されます。このような種類のショートカットファイルは、リンクファイルと呼ばれます。これらのリンクファイルにアクセスすることにより、調査員は、これらのファイルにアクセスした時間や場所などのウィンドウのアクティビティを見つけることができます。

これらのWindowsLINKファイルから情報を取得するために使用できるPythonスクリプトについて説明します。

Pythonスクリプトの場合、サードパーティのモジュールをインストールします。 pylnk, pytsk3, pyewf。次の手順に従って、から情報を抽出できます。lnk ファイル

  • まず、 lnk システム内のファイル。

  • 次に、それらを反復処理して、そのファイルから情報を抽出します。

  • さて、ついにこの情報をCSVレポートに追加する必要があります。

Pythonコード

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

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

from __future__ import print_function
from argparse import ArgumentParser

import csv
import StringIO

from utility.pytskutil import TSKUtil
import pylnk

ここで、コマンドラインハンドラーの引数を指定します。ここでは、3つの引数を受け入れます。1つ目は証拠ファイルへのパス、2つ目は証拠ファイルのタイプ、3つ目はCSVレポートへの目的の出力パスです。

if __name__ == '__main__':
   parser = argparse.ArgumentParser('Parsing LNK files')
   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)

次に、のオブジェクトを作成して証拠ファイルを解釈します。 TSKUtil ファイルシステムを反復処理して、で終わるファイルを見つけます lnk。それは定義することによって行うことができますmain() 次のように機能します-

def main(evidence, image_type, report):
   tsk_util = TSKUtil(evidence, image_type)
   lnk_files = tsk_util.recurse_files("lnk", path="/", logic="endswith")
   
   if lnk_files is None:
      print("No lnk files found")
      exit(0)
   columns = [
      'command_line_arguments', 'description', 'drive_serial_number',
      'drive_type', 'file_access_time', 'file_attribute_flags',
      'file_creation_time', 'file_modification_time', 'file_size',
      'environmental_variables_location', 'volume_label',
      'machine_identifier', 'local_path', 'network_path',
      'relative_path', 'working_directory'
   ]

次のコードの助けを借りて、繰り返します lnk 次のように関数を作成してファイルを作成します-

parsed_lnks = []

for entry in lnk_files:
   lnk = open_file_as_lnk(entry[2])
   lnk_data = {'lnk_path': entry[1], 'lnk_name': entry[0]}
   
   for col in columns:
      lnk_data[col] = getattr(lnk, col, "N/A")
   lnk.close()
   parsed_lnks.append(lnk_data)
write_csv(report, columns + ['lnk_path', 'lnk_name'], parsed_lnks)

次に、2つの関数を定義する必要があります。1つは pytsk 以下に示すように、ファイルオブジェクトなどがCSVレポートの作成に使用されます。

def open_file_as_lnk(lnk_file):
   file_size = lnk_file.info.meta.size
   file_content = lnk_file.read_random(0, file_size)
   file_like_obj = StringIO.StringIO(file_content)
   lnk = pylnk.file()
   lnk.open_file_object(file_like_obj)
   return lnk
def write_csv(outfile, fieldnames, data):
   with open(outfile, 'wb') as open_outfile:
      csvfile = csv.DictWriter(open_outfile, fieldnames)
      csvfile.writeheader()
      csvfile.writerows(data)

上記のスクリプトを実行した後、検出された情報から情報を取得します lnk CSVレポートのファイル-

プリフェッチファイル

アプリケーションが特定の場所から初めて実行されるときはいつでも、Windowsは prefetch files。これらは、アプリケーションの起動プロセスを高速化するために使用されます。これらのファイルの拡張子は.PF これらはに保存されます ”\Root\Windows\Prefetch” フォルダ。

デジタルフォレンジックの専門家は、ユーザーの詳細とともに、指定された場所からのプログラム実行の証拠を明らかにすることができます。プリフェッチファイルは、プログラムが削除またはアンインストールされた後もエントリが残るため、審査官にとって有用なアーティファクトです。

以下に示すように、Windowsプリフェッチファイルから情報をフェッチするPythonスクリプトについて説明します。

Pythonスクリプトの場合、サードパーティのモジュールをインストールします。 pylnk, pytsk3 そして unicodecsv。前の章で説明したPythonスクリプトで、これらのライブラリをすでに使用していることを思い出してください。

から情報を抽出するには、以下の手順に従う必要があります prefetch ファイル-

  • まず、スキャンします .pf 拡張ファイルまたはプリフェッチファイル。

  • 次に、署名の検証を実行して、誤検知を排除します。

  • 次に、Windowsプリフェッチファイル形式を解析します。これはWindowsのバージョンによって異なります。たとえば、Windows XPの場合は17、WindowsVistaおよびWindows7の場合は23、Windows 8.1の場合は26、Windows10の場合は30です。

  • 最後に、解析結果をCSVファイルに書き込みます。

Pythonコード

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

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

from __future__ import print_function
import argparse
from datetime import datetime, timedelta

import os
import pytsk3
import pyewf
import struct
import sys
import unicodecsv as csv
from utility.pytskutil import TSKUtil

ここで、コマンドラインハンドラーの引数を指定します。ここでは、2つの引数を受け入れます。1つは証拠ファイルへのパスで、2つ目は証拠ファイルのタイプです。また、プリフェッチファイルをスキャンするパスを指定するためのオプションの引数も受け入れます-

if __name__ == "__main__":
   parser = argparse.ArgumentParser('Parsing Prefetch files')
   parser.add_argument("EVIDENCE_FILE", help = "Evidence file path")
   parser.add_argument("TYPE", help = "Type of Evidence",choices = ("raw", "ewf"))
   parser.add_argument("OUTPUT_CSV", help = "Path to write output csv")
   parser.add_argument("-d", help = "Prefetch directory to scan",default = "/WINDOWS/PREFETCH")
   args = parser.parse_args()
   
   if os.path.exists(args.EVIDENCE_FILE) and \
      os.path.isfile(args.EVIDENCE_FILE):
   main(args.EVIDENCE_FILE, args.TYPE, args.OUTPUT_CSV, args.d)
else:
   print("[-] Supplied input file {} does not exist or is not a ""file".format(args.EVIDENCE_FILE))
   sys.exit(1)

次に、のオブジェクトを作成して証拠ファイルを解釈します。 TSKUtil ファイルシステムを反復処理して、で終わるファイルを見つけます .pf。それは定義することによって行うことができますmain() 次のように機能します-

def main(evidence, image_type, output_csv, path):
   tsk_util = TSKUtil(evidence, image_type)
   prefetch_dir = tsk_util.query_directory(path)
   prefetch_files = None
   
   if prefetch_dir is not None:
      prefetch_files = tsk_util.recurse_files(".pf", path=path, logic="endswith")
   
   if prefetch_files is None:
      print("[-] No .pf files found")
      sys.exit(2)
   print("[+] Identified {} potential prefetch files".format(len(prefetch_files)))
   prefetch_data = []
   
   for hit in prefetch_files:
      prefetch_file = hit[2]
      pf_version = check_signature(prefetch_file)

次に、以下に示すように、署名の検証を行うメソッドを定義します。

def check_signature(prefetch_file):
   version, signature = struct.unpack("^<2i", prefetch_file.read_random(0, 8))
   
   if signature == 1094927187:
      return version
   else:
      return None
   
   if pf_version is None:
      continue
   pf_name = hit[0]
   
   if pf_version == 17:
      parsed_data = parse_pf_17(prefetch_file, pf_name)
      parsed_data.append(os.path.join(path, hit[1].lstrip("//")))
      prefetch_data.append(parsed_data)

ここで、Windowsプリフェッチファイルの処理を開始します。ここでは、WindowsXPのプリフェッチファイルの例を取り上げています-

def parse_pf_17(prefetch_file, pf_name):
   create = convert_unix(prefetch_file.info.meta.crtime)
   modify = convert_unix(prefetch_file.info.meta.mtime)
def convert_unix(ts):
   if int(ts) == 0:
      return ""
   return datetime.utcfromtimestamp(ts)
def convert_filetime(ts):
   if int(ts) == 0:
      return ""
   return datetime(1601, 1, 1) + timedelta(microseconds=ts / 10)

ここで、次のようにstructを使用して、プリフェッチされたファイルに埋め込まれたデータを抽出します。

pf_size, name, vol_info, vol_entries, vol_size, filetime, \
   count = struct.unpack("<i60s32x3iq16xi",prefetch_file.read_random(12, 136))
name = name.decode("utf-16", "ignore").strip("/x00").split("/x00")[0]

vol_name_offset, vol_name_length, vol_create, \
   vol_serial = struct.unpack("<2iqi",prefetch_file.read_random(vol_info, 20))
   vol_serial = hex(vol_serial).lstrip("0x")
   vol_serial = vol_serial[:4] + "-" + vol_serial[4:]
   vol_name = struct.unpack(
      "<{}s".format(2 * vol_name_length),
      prefetch_file.read_random(vol_info + vol_name_offset,vol_name_length * 2))[0]

vol_name = vol_name.decode("utf-16", "ignore").strip("/x00").split("/x00")[0]
return [
   pf_name, name, pf_size, create,
   modify, convert_filetime(filetime), count, vol_name,
   convert_filetime(vol_create), vol_serial ]

Windows XPのプリフェッチバージョンを提供したので、他のWindowsのプリフェッチバージョンが発生した場合はどうなりますか。次に、次のようなエラーメッセージを表示する必要があります-

elif pf_version == 23:
   print("[-] Windows Vista / 7 PF file {} -- unsupported".format(pf_name))
   continue
elif pf_version == 26:
   print("[-] Windows 8 PF file {} -- unsupported".format(pf_name))
   continue
elif pf_version == 30:
   print("[-] Windows 10 PF file {} -- unsupported".format(pf_name))
continue

else:
   print("[-] Signature mismatch - Name: {}\nPath: {}".format(hit[0], hit[1]))
continue
write_output(prefetch_data, output_csv)

ここで、結果をCSVレポートに書き込む方法を次のように定義します。

def write_output(data, output_csv):
   print("[+] Writing csv report")
   with open(output_csv, "wb") as outfile:
      writer = csv.writer(outfile)
      writer.writerow([
         "File Name", "Prefetch Name", "File Size (bytes)",
         "File Create Date (UTC)", "File Modify Date (UTC)",
         "Prefetch Last Execution Date (UTC)",
         "Prefetch Execution Count", "Volume", "Volume Create Date",
         "Volume Serial", "File Path" ])
      writer.writerows(data)

上記のスクリプトを実行した後、WindowsXPバージョンのプリフェッチファイルからスプレッドシートに情報を取得します。

この章では、調査員がWindowsでのフォレンジック分析中に取得できるその他のアーティファクトについて説明します。

イベントログ

Windowsイベントログファイルは、名前が示すように、ユーザーがコンピューターにログオンしたとき、プログラムでエラーが発生したとき、システムの変更、RDPアクセス、アプリケーション固有のイベントなどの重要なイベントを保存する特別なファイルです。サイバー調査員は常にイベントに関心があります。システムへのアクセスに関する多くの有用な履歴情報を提供するため、ログ情報。次のPythonスクリプトでは、従来のWindowsイベントログ形式と現在のWindowsイベントログ形式の両方を処理します。

Pythonスクリプトの場合、サードパーティのモジュールをインストールする必要があります。 pytsk3, pyewf, unicodecsv, pyevt and pyevtバツ。以下の手順に従って、イベントログから情報を抽出できます-

  • まず、入力引数に一致するすべてのイベントログを検索します。

  • 次に、ファイル署名の検証を実行します。

  • 次に、見つかった各イベントログを適切なライブラリで処理します。

  • 最後に、出力をスプレッドシートに書き込みます。

Pythonコード

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

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

from __future__ import print_function
import argparse
import unicodecsv as csv
import os
import pytsk3
import pyewf
import pyevt
import pyevtx
import sys
from utility.pytskutil import TSKUtil

ここで、コマンドラインハンドラーの引数を指定します。ここでは、3つの引数を受け入れることに注意してください。1つ目は証拠ファイルへのパス、2つ目は証拠ファイルのタイプ、3つ目は処理するイベントログの名前です。

if __name__ == "__main__":
   parser = argparse.ArgumentParser('Information from Event Logs')
   parser.add_argument("EVIDENCE_FILE", help = "Evidence file path")
   parser.add_argument("TYPE", help = "Type of Evidence",choices = ("raw", "ewf"))
   parser.add_argument(
      "LOG_NAME",help = "Event Log Name (SecEvent.Evt, SysEvent.Evt, ""etc.)")
   
   parser.add_argument(
      "-d", help = "Event log directory to scan",default = "/WINDOWS/SYSTEM32/WINEVT")
   
   parser.add_argument(
      "-f", help = "Enable fuzzy search for either evt or"" evtx extension", action = "store_true")
   args = parser.parse_args()
   
   if os.path.exists(args.EVIDENCE_FILE) and \ os.path.isfile(args.EVIDENCE_FILE):
      main(args.EVIDENCE_FILE, args.TYPE, args.LOG_NAME, args.d, args.f)
   else:
      print("[-] Supplied input file {} does not exist or is not a ""file".format(args.EVIDENCE_FILE))
   sys.exit(1)

次に、イベントログを操作して、ユーザーが指定したパスの存在を照会します。 TSKUtilオブジェクト。それはの助けを借りて行うことができますmain() 次のような方法-

def main(evidence, image_type, log, win_event, fuzzy):
   tsk_util = TSKUtil(evidence, image_type)
   event_dir = tsk_util.query_directory(win_event)
   
   if event_dir is not None:
      if fuzzy is True:
         event_log = tsk_util.recurse_files(log, path=win_event)
   else:
      event_log = tsk_util.recurse_files(log, path=win_event, logic="equal")
   
   if event_log is not None:
      event_data = []
      for hit in event_log:
         event_file = hit[2]
         temp_evt = write_file(event_file)

次に、署名の検証を実行してから、コンテンツ全体を現在のディレクトリに書き込むメソッドを定義する必要があります。

def write_file(event_file):
   with open(event_file.info.name.name, "w") as outfile:
      outfile.write(event_file.read_random(0, event_file.info.meta.size))
   return event_file.info.name.name
      if pyevt.check_file_signature(temp_evt):
         evt_log = pyevt.open(temp_evt)
         print("[+] Identified {} records in {}".format(
            evt_log.number_of_records, temp_evt))
         
         for i, record in enumerate(evt_log.records):
            strings = ""
            for s in record.strings:
               if s is not None:
                  strings += s + "\n"
            event_data.append([
               i, hit[0], record.computer_name,
               record.user_security_identifier,
               record.creation_time, record.written_time,
               record.event_category, record.source_name,
               record.event_identifier, record.event_type,
               strings, "",
               os.path.join(win_event, hit[1].lstrip("//"))
            ])
      elif pyevtx.check_file_signature(temp_evt):
         evtx_log = pyevtx.open(temp_evt)
         print("[+] Identified {} records in {}".format(
            evtx_log.number_of_records, temp_evt))
         for i, record in enumerate(evtx_log.records):
            strings = ""
            for s in record.strings:
			   if s is not None:
               strings += s + "\n"
         event_data.append([
            i, hit[0], record.computer_name,
            record.user_security_identifier, "",
            record.written_time, record.event_level,
            record.source_name, record.event_identifier,
            "", strings, record.xml_string,
            os.path.join(win_event, hit[1].lstrip("//"))
      ])
      else:
         print("[-] {} not a valid event log. Removing temp" file...".format(temp_evt))
         os.remove(temp_evt)
      continue
      write_output(event_data)
   else:
      print("[-] {} Event log not found in {} directory".format(log, win_event))
      sys.exit(3)
else:
   print("[-] Win XP Event Log Directory {} not found".format(win_event))
   sys.exit(2

最後に、出力をスプレッドシートに書き込む方法を次のように定義します-

def write_output(data):
   output_name = "parsed_event_logs.csv"
   print("[+] Writing {} to current working directory: {}".format(
      output_name, os.getcwd()))
   
   with open(output_name, "wb") as outfile:
      writer = csv.writer(outfile)
      writer.writerow([
         "Index", "File name", "Computer Name", "SID",
         "Event Create Date", "Event Written Date",
         "Event Category/Level", "Event Source", "Event ID",
         "Event Type", "Data", "XML Data", "File Path"
      ])
      writer.writerows(data)

上記のスクリプトを正常に実行すると、イベントログの情報がスプレッドシートに記録されます。

インターネットの歴史

インターネットの歴史は、フォレンジックアナリストにとって非常に役立ちます。ほとんどのサイバー犯罪はインターネット上でのみ発生するためです。Windowsのフォレンジックについて説明しているので、InternetExplorerからインターネット履歴を抽出する方法を見てみましょう。InternetExplorerはデフォルトでWindowsに付属しています。

Internet Explorerでは、インターネット履歴はに保存されます index.datファイル。から情報を抽出するPythonスクリプトを調べてみましょう。index.dat ファイル。

以下の手順に従って、から情報を抽出できます。 index.dat ファイル-

  • まず、 index.dat システム内のファイル。

  • 次に、それらを反復処理して、そのファイルから情報を抽出します。

  • 次に、このすべての情報をCSVレポートに書き込みます。

Pythonコード

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

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

from __future__ import print_function
import argparse

from datetime import datetime, timedelta
import os
import pytsk3
import pyewf
import pymsiecf
import sys
import unicodecsv as csv

from utility.pytskutil import TSKUtil

ここで、コマンドラインハンドラーの引数を指定します。ここでは、2つの引数を受け入れることに注意してください。1つ目は証拠ファイルへのパスで、2つ目は証拠ファイルのタイプです。

if __name__ == "__main__":
parser = argparse.ArgumentParser('getting information from internet history')
   parser.add_argument("EVIDENCE_FILE", help = "Evidence file path")
   parser.add_argument("TYPE", help = "Type of Evidence",choices = ("raw", "ewf"))
   parser.add_argument("-d", help = "Index.dat directory to scan",default = "/USERS")
   args = parser.parse_args()
   
   if os.path.exists(args.EVIDENCE_FILE) and os.path.isfile(args.EVIDENCE_FILE):
      main(args.EVIDENCE_FILE, args.TYPE, args.d)
   else:
      print("[-] Supplied input file {} does not exist or is not a ""file".format(args.EVIDENCE_FILE))
      sys.exit(1)

次に、のオブジェクトを作成して証拠ファイルを解釈します。 TSKUtilファイルシステムを反復処理して、index.datファイルを見つけます。それは定義することによって行うことができますmain() 次のように機能します-

def main(evidence, image_type, path):
   tsk_util = TSKUtil(evidence, image_type)
   index_dir = tsk_util.query_directory(path)
   
   if index_dir is not None:
      index_files = tsk_util.recurse_files("index.dat", path = path,logic = "equal")
      
      if index_files is not None:
         print("[+] Identified {} potential index.dat files".format(len(index_files)))
         index_data = []
         
         for hit in index_files:
            index_file = hit[2]
            temp_index = write_file(index_file)

ここで、index.datファイルの情報を現在の作業ディレクトリにコピーし、後でサードパーティのモジュールで処理できる関数を定義します。

def write_file(index_file):
   with open(index_file.info.name.name, "w") as outfile:
   outfile.write(index_file.read_random(0, index_file.info.meta.size))
return index_file.info.name.name

次に、次のコードを使用して、組み込み関数を使用して署名の検証を実行します。 check_file_signature()

if pymsiecf.check_file_signature(temp_index):
   index_dat = pymsiecf.open(temp_index)
   print("[+] Identified {} records in {}".format(
   index_dat.number_of_items, temp_index))

   for i, record in enumerate(index_dat.items):
   try:
      data = record.data
   if data is not None:
      data = data.rstrip("\x00")
   except AttributeError:
   
   if isinstance(record, pymsiecf.redirected):
      index_data.append([
         i, temp_index, "", "", "", "", "",record.location, "", "", record.offset,os.path.join(path, hit[1].lstrip("//"))])
   
   elif isinstance(record, pymsiecf.leak):
      index_data.append([
         i, temp_index, record.filename, "","", "", "", "", "", "", record.offset,os.path.join(path, hit[1].lstrip("//"))])
   continue
   
   index_data.append([
      i, temp_index, record.filename,
      record.type, record.primary_time,
      record.secondary_time,
      record.last_checked_time, record.location,
      record.number_of_hits, data, record.offset,
      os.path.join(path, hit[1].lstrip("//"))
   ])
   else:
      print("[-] {} not a valid index.dat file. Removing "
      "temp file..".format(temp_index))
      os.remove("index.dat")
      continue
      os.remove("index.dat")
      write_output(index_data)
   else:
      print("[-] Index.dat files not found in {} directory".format(path))
   sys.exit(3)
   else:
      print("[-] Directory {} not found".format(win_event))
   sys.exit(2)

次に、以下に示すように、CSVファイルで出力を出力するメソッドを定義します-

def write_output(data):
   output_name = "Internet_Indexdat_Summary_Report.csv"
   print("[+] Writing {} with {} parsed index.dat files to current "
   "working directory: {}".format(output_name, len(data),os.getcwd()))
   
   with open(output_name, "wb") as outfile:
      writer = csv.writer(outfile)
      writer.writerow(["Index", "File Name", "Record Name",
      "Record Type", "Primary Date", "Secondary Date",
      "Last Checked Date", "Location", "No. of Hits",
      "Record Data", "Record Offset", "File Path"])
      writer.writerows(data)

上記のスクリプトを実行した後、CSVファイルのindex.datファイルから情報を取得します。

ボリュームシャドウコピー

シャドウコピーは、コンピューターファイルのバックアップコピーまたはスナップショットを手動または自動で取得するためのWindowsに含まれているテクノロジです。ボリュームスナップショットサービスまたはボリュームシャドウサービス(VSS)とも呼ばれます。

これらのVSSファイルの助けを借りて、フォレンジックの専門家は、システムが時間の経過とともにどのように変化したか、およびどのファイルがコンピューターに存在したかについての履歴情報を得ることができます。シャドウコピーテクノロジでは、シャドウコピーを作成および保存するために、ファイルシステムがNTFSである必要があります。

このセクションでは、フォレンジックイメージに存在する任意のボリュームのシャドウコピーにアクセスするのに役立つPythonスクリプトを紹介します。

Pythonスクリプトの場合、サードパーティのモジュールをインストールする必要があります。 pytsk3, pyewf, unicodecsv, pyvshadow そして vss。以下の手順に従って、VSSファイルから情報を抽出できます。

  • まず、RAWイメージのボリュームにアクセスし、すべてのNTFSパーティションを特定します。

  • 次に、シャドウコピーを反復処理して、そのシャドウコピーから情報を抽出します。

  • 最後に、スナップショット内のデータのファイルリストを作成する必要があります。

Pythonコード

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

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

from __future__ import print_function
import argparse
from datetime import datetime, timedelta

import os
import pytsk3
import pyewf
import pyvshadow
import sys
import unicodecsv as csv

from utility import vss
from utility.pytskutil import TSKUtil
from utility import pytskutil

ここで、コマンドラインハンドラーの引数を指定します。ここでは、2つの引数を受け入れます。1つは証拠ファイルへのパスで、もう1つは出力ファイルです。

if __name__ == "__main__":
   parser = argparse.ArgumentParser('Parsing Shadow Copies')
   parser.add_argument("EVIDENCE_FILE", help = "Evidence file path")
   parser.add_argument("OUTPUT_CSV", help = "Output CSV with VSS file listing")
   args = parser.parse_args()

次に、入力ファイルパスの存在を検証し、ディレクトリを出力ファイルから分離します。

directory = os.path.dirname(args.OUTPUT_CSV)
if not os.path.exists(directory) and directory != "":
   os.makedirs(directory)
if os.path.exists(args.EVIDENCE_FILE) and \ os.path.isfile(args.EVIDENCE_FILE):
   main(args.EVIDENCE_FILE, args.OUTPUT_CSV)
else:
   print("[-] Supplied input file {} does not exist or is not a "
   "file".format(args.EVIDENCE_FILE))
   
   sys.exit(1)

次に、を作成して証拠ファイルのボリュームを操作します TSKUtilオブジェクト。それはの助けを借りて行うことができますmain() 次のような方法-

def main(evidence, output):
   tsk_util = TSKUtil(evidence, "raw")
   img_vol = tsk_util.return_vol()

if img_vol is not None:
   for part in img_vol:
      if tsk_util.detect_ntfs(img_vol, part):
         print("Exploring NTFS Partition for VSS")
         explore_vss(evidence, part.start * img_vol.info.block_size,output)
      else:
         print("[-] Must be a physical preservation to be compatible ""with this script")
         sys.exit(2)

ここで、解析されたボリュームシャドウファイルを探索する方法を次のように定義します-

def explore_vss(evidence, part_offset, output):
   vss_volume = pyvshadow.volume()
   vss_handle = vss.VShadowVolume(evidence, part_offset)
   vss_count = vss.GetVssStoreCount(evidence, part_offset)
   
   if vss_count > 0:
      vss_volume.open_file_object(vss_handle)
      vss_data = []
      
      for x in range(vss_count):
         print("Gathering data for VSC {} of {}".format(x, vss_count))
         vss_store = vss_volume.get_store(x)
         image = vss.VShadowImgInfo(vss_store)
         vss_data.append(pytskutil.openVSSFS(image, x))
write_csv(vss_data, output)

最後に、スプレッドシートに結果を書き込む方法を次のように定義します-

def write_csv(data, output):
   if data == []:
      print("[-] No output results to write")
      sys.exit(3)
   print("[+] Writing output to {}".format(output))
   if os.path.exists(output):
      append = True
with open(output, "ab") as csvfile:
      csv_writer = csv.writer(csvfile)
      headers = ["VSS", "File", "File Ext", "File Type", "Create Date",
         "Modify Date", "Change Date", "Size", "File Path"]
      if not append:
         csv_writer.writerow(headers)
      for result_list in data:
         csv_writer.writerows(result_list)

このPythonスクリプトを正常に実行すると、VSSにある情報がスプレッドシートに取り込まれます。

これまで、Pythonを使用してWindowsでアーティファクトを取得する方法を見てきました。この章では、Pythonを使用したログベースのアーティファクトの調査について学習しましょう。

前書き

ログベースのアーティファクトは、デジタルフォレンジックの専門家にとって非常に役立つ情報の宝庫です。情報を収集するためのさまざまな監視ソフトウェアがありますが、それらから有用な情報を解析するための主な問題は、大量のデータが必要なことです。

さまざまなログベースのアーティファクトとPythonでの調査

このセクションでは、さまざまなログベースのアーティファクトとPythonでのそれらの調査について説明します-

タイムスタンプ

タイムスタンプは、アクティビティのデータと時刻をログに伝達します。これは、ログファイルの重要な要素の1つです。これらのデータと時間の値はさまざまな形式で提供される可能性があることに注意してください。

以下に示すPythonスクリプトは、生の日時を入力として受け取り、フォーマットされたタイムスタンプを出力として提供します。

このスクリプトでは、次の手順に従う必要があります-

  • まず、データのソースとデータ型とともに生データ値を取得する引数を設定します。

  • 次に、さまざまな日付形式のデータに共通のインターフェイスを提供するためのクラスを提供します。

Pythonコード

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

まず、次のPythonモジュールをインポートします-

from __future__ import print_function
from argparse import ArgumentParser, ArgumentDefaultsHelpFormatter
from datetime import datetime as dt
from datetime import timedelta

いつものように、コマンドラインハンドラーの引数を指定する必要があります。ここでは、3つの引数を受け入れます。最初は処理される日付値、2番目はその日付値のソース、3番目はそのタイプです-

if __name__ == '__main__':
   parser = ArgumentParser('Timestamp Log-based artifact')
   parser.add_argument("date_value", help="Raw date value to parse")
   parser.add_argument(
      "source", help = "Source format of date",choices = ParseDate.get_supported_formats())
   parser.add_argument(
      "type", help = "Data type of input value",choices = ('number', 'hex'), default = 'int')
   
   args = parser.parse_args()
   date_parser = ParseDate(args.date_value, args.source, args.type)
   date_parser.run()
   print(date_parser.timestamp)

ここで、日付値、日付ソース、および値型の引数を受け入れるクラスを定義する必要があります-

class ParseDate(object):
   def __init__(self, date_value, source, data_type):
      self.date_value = date_value
      self.source = source
      self.data_type = data_type
      self.timestamp = None

次に、main()メソッドと同じようにコントローラーのように機能するメソッドを定義します。

def run(self):
   if self.source == 'unix-epoch':
      self.parse_unix_epoch()
   elif self.source == 'unix-epoch-ms':
      self.parse_unix_epoch(True)
   elif self.source == 'windows-filetime':
      self.parse_windows_filetime()
@classmethod
def get_supported_formats(cls):
   return ['unix-epoch', 'unix-epoch-ms', 'windows-filetime']

ここで、UnixエポックタイムとFILETIMEをそれぞれ処理する2つのメソッドを定義する必要があります-

def parse_unix_epoch(self, milliseconds=False):
   if self.data_type == 'hex':
      conv_value = int(self.date_value)
      if milliseconds:
         conv_value = conv_value / 1000.0
   elif self.data_type == 'number':
      conv_value = float(self.date_value)
      if milliseconds:
         conv_value = conv_value / 1000.0
   else:
      print("Unsupported data type '{}' provided".format(self.data_type))
      sys.exit('1')
   ts = dt.fromtimestamp(conv_value)
   self.timestamp = ts.strftime('%Y-%m-%d %H:%M:%S.%f')
def parse_windows_filetime(self):
   if self.data_type == 'hex':
      microseconds = int(self.date_value, 16) / 10.0
   elif self.data_type == 'number':
      microseconds = float(self.date_value) / 10
   else:
      print("Unsupported data type '{}'   provided".format(self.data_type))
      sys.exit('1')
   ts = dt(1601, 1, 1) + timedelta(microseconds=microseconds)
   self.timestamp = ts.strftime('%Y-%m-%d %H:%M:%S.%f')

上記のスクリプトを実行した後、タイムスタンプを指定することで、変換された値を読みやすい形式で取得できます。

Webサーバーログ

デジタルフォレンジックの専門家の観点からは、Webサーバーログは、ユーザーと地理的な場所に関する情報とともに有用なユーザー統計を取得できるため、もう1つの重要な成果物です。以下は、情報の分析を容易にするために、Webサーバーログを処理した後にスプレッドシートを作成するPythonスクリプトです。

まず、次のPythonモジュールをインポートする必要があります-

from __future__ import print_function
from argparse import ArgumentParser, FileType

import re
import shlex
import logging
import sys
import csv

logger = logging.getLogger(__file__)

ここで、ログから解析されるパターンを定義する必要があります-

iis_log_format = [
   ("date", re.compile(r"\d{4}-\d{2}-\d{2}")),
   ("time", re.compile(r"\d\d:\d\d:\d\d")),
   ("s-ip", re.compile(
      r"((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.|$)){4}")),
   ("cs-method", re.compile(
      r"(GET)|(POST)|(PUT)|(DELETE)|(OPTIONS)|(HEAD)|(CONNECT)")),
   ("cs-uri-stem", re.compile(r"([A-Za-z0-1/\.-]*)")),
   ("cs-uri-query", re.compile(r"([A-Za-z0-1/\.-]*)")),
   ("s-port", re.compile(r"\d*")),
   ("cs-username", re.compile(r"([A-Za-z0-1/\.-]*)")),
   ("c-ip", re.compile(
      r"((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.|$)){4}")),
   ("cs(User-Agent)", re.compile(r".*")),
   ("sc-status", re.compile(r"\d*")),
   ("sc-substatus", re.compile(r"\d*")),
   ("sc-win32-status", re.compile(r"\d*")),
   ("time-taken", re.compile(r"\d*"))]

ここで、コマンドラインハンドラーの引数を指定します。ここでは、2つの引数を受け入れます。1つは処理するIISログ、2つ目は目的のCSVファイルパスです。

if __name__ == '__main__':
   parser = ArgumentParser('Parsing Server Based Logs')
   parser.add_argument('iis_log', help = "Path to IIS Log",type = FileType('r'))
   parser.add_argument('csv_report', help = "Path to CSV report")
   parser.add_argument('-l', help = "Path to processing log",default=__name__ + '.log')
   args = parser.parse_args()
   logger.setLevel(logging.DEBUG)
   msg_fmt = logging.Formatter(
      "%(asctime)-15s %(funcName)-10s ""%(levelname)-8s %(message)s")
   
   strhndl = logging.StreamHandler(sys.stdout)
   strhndl.setFormatter(fmt = msg_fmt)
   fhndl = logging.FileHandler(args.log, mode = 'a')
   fhndl.setFormatter(fmt = msg_fmt)
   
   logger.addHandler(strhndl)
   logger.addHandler(fhndl)
   logger.info("Starting IIS Parsing ")
   logger.debug("Supplied arguments: {}".format(", ".join(sys.argv[1:])))
   logger.debug("System " + sys.platform)
   logger.debug("Version " + sys.version)
   main(args.iis_log, args.csv_report, logger)
   iologger.info("IIS Parsing Complete")

次に、バルクログ情報のスクリプトを処理するmain()メソッドを定義する必要があります-

def main(iis_log, report_file, logger):
   parsed_logs = []

for raw_line in iis_log:
   line = raw_line.strip()
   log_entry = {}

if line.startswith("#") or len(line) == 0:
   continue

if '\"' in line:
   line_iter = shlex.shlex(line_iter)
else:
   line_iter = line.split(" ")
   for count, split_entry in enumerate(line_iter):
      col_name, col_pattern = iis_log_format[count]

      if col_pattern.match(split_entry):
         log_entry[col_name] = split_entry
else:
   logger.error("Unknown column pattern discovered. "
      "Line preserved in full below")
      logger.error("Unparsed Line: {}".format(line))
      parsed_logs.append(log_entry)
      
      logger.info("Parsed {} lines".format(len(parsed_logs)))
      cols = [x[0] for x in iis_log_format]
      
      logger.info("Creating report file: {}".format(report_file))
      write_csv(report_file, cols, parsed_logs)
      logger.info("Report created")

最後に、出力をスプレッドシートに書き込むメソッドを定義する必要があります-

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

上記のスクリプトを実行した後、スプレッドシートでWebサーバーベースのログを取得します。

YARAを使用して重要なファイルをスキャンする

YARA(Yet Another Recursive Algorithm)は、マルウェアの識別とインシデント対応のために設計されたパターンマッチングユーティリティです。ファイルのスキャンにはYARAを使用します。次のPythonスクリプトでは、YARAを使用します。

次のコマンドを使用してYARAをインストールできます-

pip install YARA

YARAルールを使用してファイルをスキャンするための以下の手順に従うことができます-

  • まず、YARAルールを設定してコンパイルします

  • 次に、1つのファイルをスキャンし、ディレクトリを反復処理して個々のファイルを処理します。

  • 最後に、結果をCSVにエクスポートします。

Pythonコード

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

まず、次のPythonモジュールをインポートする必要があります-

from __future__ import print_function
from argparse import ArgumentParser, ArgumentDefaultsHelpFormatter

import os
import csv
import yara

次に、コマンドラインハンドラーの引数を指定します。ここでは2つの引数を受け入れることに注意してください。1つはYARAルールへのパス、2つ目はスキャンするファイルです。

if __name__ == '__main__':
   parser = ArgumentParser('Scanning files by YARA')
   parser.add_argument(
      'yara_rules',help = "Path to Yara rule to scan with. May be file or folder path.")
   parser.add_argument('path_to_scan',help = "Path to file or folder to scan")
   parser.add_argument('--output',help = "Path to output a CSV report of scan results")
   args = parser.parse_args()
   main(args.yara_rules, args.path_to_scan, args.output)

次に、スキャンするyaraルールとファイルへのパスを受け入れるmain()関数を定義します-

def main(yara_rules, path_to_scan, output):
   if os.path.isdir(yara_rules):
      yrules = yara.compile(yara_rules)
   else:
      yrules = yara.compile(filepath=yara_rules)
   if os.path.isdir(path_to_scan):
      match_info = process_directory(yrules, path_to_scan)
   else:
      match_info = process_file(yrules, path_to_scan)
   columns = ['rule_name', 'hit_value', 'hit_offset', 'file_name',
   'rule_string', 'rule_tag']
   
   if output is None:
      write_stdout(columns, match_info)
   else:
      write_csv(output, columns, match_info)

ここで、ディレクトリを反復処理し、結果を別のメソッドに渡してさらに処理するメソッドを定義します-

def process_directory(yrules, folder_path):
   match_info = []
   for root, _, files in os.walk(folder_path):
      for entry in files:
         file_entry = os.path.join(root, entry)
         match_info += process_file(yrules, file_entry)
   return match_info

次に、2つの関数を定義します。最初に使用することに注意してくださいmatch() 方法 yrulesユーザーが出力ファイルを指定しない場合、オブジェクトと別のオブジェクトは、一致する情報をコンソールに報告します。以下に示すコードを確認してください-

def process_file(yrules, file_path):
   match = yrules.match(file_path)
   match_info = []
   
   for rule_set in match:
      for hit in rule_set.strings:
         match_info.append({
            'file_name': file_path,
            'rule_name': rule_set.rule,
            'rule_tag': ",".join(rule_set.tags),
            'hit_offset': hit[0],
            'rule_string': hit[1],
            'hit_value': hit[2]
         })
   return match_info
def write_stdout(columns, match_info):
   for entry in match_info:
      for col in columns:
         print("{}: {}".format(col, entry[col]))
   print("=" * 30)

最後に、以下に示すように、CSVファイルに出力を書き込むメソッドを定義します。

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

上記のスクリプトを正常に実行すると、コマンドラインで適切な引数を指定し、CSVレポートを生成できます。