Python 数字取证 - 快速指南
Python 数字取证 - 简介
本章将向您介绍数字取证的全部内容及其历史回顾。您还将了解在现实生活中可以在哪里应用数字取证及其局限性。
什么是数字取证?
数字取证可以定义为分析、检查、识别和恢复电子设备上的数字证据的取证科学的一个分支。它通常用于刑法和私人调查。
例如,您可以依靠数字取证提取证据,以防有人窃取电子设备上的某些数据。
数字取证的简要历史回顾
本节解释了计算机犯罪的历史和数字取证的历史回顾,如下所示 -
1970 年代至 1980 年代:第一次计算机犯罪
在这十年之前,还没有发现任何计算机犯罪。然而,如果这种情况发生,当时的现行法律会处理它们。后来,1978 年,佛罗里达州《计算机犯罪法》承认了第一起计算机犯罪,其中包括禁止未经授权修改或删除计算机系统上的数据的立法。但随着时间的推移,由于技术的进步,计算机犯罪的范围也随之增加。为了处理与版权、隐私和儿童色情制品有关的犯罪,还通过了各种其他法律。
1980年代-90年代:发展十年
这十年是数字取证的发展十年,这一切都是因为克里夫·斯托尔 (Cliff Stoll) 首次进行调查(1986 年),追踪了名为马库斯·赫斯 (Markus Hess) 的黑客。在此期间,发展了两种数字取证学科——第一种是借助由以数字取证为爱好的从业者开发的临时工具和技术,第二种是由科学界开发的。1992年, “计算机取证”一词出现在学术文献中。
2000 年代至 2010 年代:标准化十年
数字取证发展到一定程度后,需要制定一些具体的标准来进行调查时可以遵循。因此,各种科学机构和团体发布了数字取证指南。2002年,数字证据科学工作组(SWGDE)发表了一篇名为“计算机取证的最佳实践”的论文。另一个亮点是由欧洲主导的国际条约,即《网络犯罪公约》,由 43 个国家签署并由 16 个国家批准。即使制定了这样的标准,仍然需要解决研究人员发现的一些问题。
数字取证过程
自 1978 年首次出现计算机犯罪以来,数字犯罪活动大幅增加。由于这种增量,需要以结构化的方式来处理它们。1984 年,引入了正式流程,此后开发了大量新的和改进的计算机取证调查流程。
计算机取证调查过程涉及三个主要阶段,如下所述 -
第一阶段:展品采集或成像
数字取证的第一阶段涉及保存数字系统的状态,以便以后进行分析。这与从犯罪现场拍摄照片、血样等非常相似。例如,它涉及捕获硬盘或 RAM 的已分配和未分配区域的图像。
第二阶段:分析
该阶段的输入是采集阶段获取的数据。在这里,我们对这些数据进行了检查以找出证据。此阶段提供三种证据如下 -
有罪证据- 这些证据支持特定的历史。
无罪证据- 这些证据与特定历史相矛盾。
篡改证据- 这些证据表明系统经过了修改以避免识别。它包括检查文件和目录内容以恢复已删除的文件。
第三阶段:演示或报告
顾名思义,此阶段呈现调查的结论和相应的证据。
数字取证的应用
数字取证涉及收集、分析和保存任何数字设备中包含的证据。数字取证的使用取决于应用程序。如前所述,它主要用于以下两个应用程序 -
刑法
在刑法中,收集证据是为了支持或反对法庭上的假设。取证程序与刑事调查中使用的程序非常相似,但具有不同的法律要求和限制。
私人调查
企业界主要使用数字取证进行私人调查。当公司怀疑员工可能在其计算机上执行违反公司政策的非法活动时,就会使用它。数字取证为公司或个人调查某人的数字不当Behave提供了最佳途径之一。
数字取证的分支
数字犯罪不仅限于计算机,但黑客和犯罪分子也大规模使用平板电脑、智能手机等小型数字设备。一些设备具有易失性存储器,而另一些设备具有非易失性存储器。因此,根据设备的类型,数字取证有以下分支 -
计算机取证
数字取证的这一分支涉及计算机、嵌入式系统和静态存储器(例如 USB 驱动器)。计算机取证可以调查从日志到驱动器上的实际文件的各种信息。
移动取证
这涉及对移动设备数据的调查。该分支与计算机取证不同,因为移动设备具有内置通信系统,可用于提供与位置相关的有用信息。
网络取证
它涉及对本地和 WAN(广域网)计算机网络流量的监控和分析,以实现信息收集、证据收集或入侵检测。
数据库取证
数字取证的这个分支涉及数据库及其元数据的取证研究。
数字取证调查所需的技能
数字取证检查员帮助跟踪黑客、恢复被盗数据、跟踪计算机攻击追溯到其源头,并协助涉及计算机的其他类型的调查。成为数字取证检查员所需的一些关键技能如下所述 -
杰出的思维能力
数字取证调查员必须是一位杰出的思考者,并且应该能够在特定任务上应用不同的工具和方法来获取输出。他/她必须能够找到不同的模式并在它们之间建立关联。
技术能力
数字取证检查员必须具备良好的技术技能,因为该领域需要网络知识、数字系统如何交互。
热衷于网络安全
由于数字取证领域主要是解决网络犯罪,这是一项繁琐的任务,因此需要有很大的热情才能成为王牌数字取证调查员。
沟通技巧
良好的沟通技巧是与各个团队协调并提取任何缺失的数据或信息所必需的。
擅长报告制作
成功实施采集和分析后,数字取证检查员必须在最终报告和演示中提及所有发现结果。因此,他/她必须具备良好的报告制作技巧和对细节的关注。
局限性
数字取证调查存在一定的局限性,如下所述 -
需要拿出令人信服的证据
数字取证调查的主要挫折之一是审查员必须遵守法庭证据所需的标准,因为数据很容易被篡改。另一方面,计算机取证调查员必须完全了解法律要求、证据处理和文件程序,以便在法庭上提供令人信服的证据。
调查工具
数字调查的有效性完全取决于数字取证检查员的专业知识和正确调查工具的选择。如果所使用的工具不符合规定的标准,那么在法庭上,法官可能会否认证据。
观众缺乏技术知识
另一个限制是有些人并不完全熟悉计算机取证;因此,很多人不了解这个领域。调查人员必须确保以帮助每个人理解结果的方式与法院沟通他们的调查结果。
成本
生成数字证据并保存它们的成本非常高。因此,很多无力承担费用的人可能不会选择这种工艺。
Python 数字取证 - 入门
在上一章中,我们了解了数字取证的基础知识、其优点和局限性。本章将让您熟悉 Python,这是我们在数字取证调查中使用的基本工具。
为什么使用 Python 进行数字取证?
Python 是一种流行的编程语言,被用作网络安全、渗透测试以及数字取证调查的工具。当您选择Python作为数字取证工具时,您不需要任何其他第三方软件来完成任务。
Python 编程语言的一些独特功能使其非常适合数字取证项目,如下所示:
语法简单- 与其他语言相比,Python 的语法很简单,这使得人们更容易学习和使用数字取证。
全面的内置模块- Python 全面的内置模块是执行完整的数字取证调查的极好帮助。
帮助和支持- 作为一种开源编程语言,Python 享有开发人员和用户社区的出色支持。
Python的特点
Python 是一种高级、解释性、交互式和面向对象的脚本语言,提供以下功能 -
易于学习- Python 是一种开发人员友好且易于学习的语言,因为它具有较少的关键字和最简单的结构。
富有表现力且易于阅读- Python 语言本质上具有表现力;因此它的代码更容易理解和可读。
跨平台兼容- Python 是一种跨平台兼容语言,这意味着它可以在各种平台上高效运行,例如 UNIX、Windows 和 Macintosh。
交互模式编程- 我们可以对代码进行交互测试和调试,因为Python支持交互模式编程。
提供各种模块和函数- Python 拥有大型标准库,允许我们为脚本使用丰富的模块和函数集。
支持动态类型检查- Python 支持动态类型检查并提供非常高级的动态数据类型。
GUI 编程- Python 支持 GUI 编程来开发图形用户界面。
与其他编程语言集成- Python 可以轻松地与其他编程语言(如 C、C++、JAVA 等)集成。
安装Python
Python 发行版可用于各种平台,例如 Windows、UNIX、Linux 和 Mac。我们只需要根据我们的平台下载二进制代码。如果没有任何平台的二进制代码,我们必须有一个 C 编译器,以便可以手动编译源代码。
本节将让您熟悉 Python 在各种平台上的安装 -
Unix 和 Linux 上的 Python 安装
您可以按照下面所示的步骤在 Unix/Linux 计算机上安装 Python。
步骤 1 - 打开 Web 浏览器。输入并输入www.python.org/downloads/
步骤 2 - 下载适用于 Unix/Linux 的压缩源代码。
步骤 3 - 解压下载的压缩文件。
步骤 4 - 如果您想自定义某些选项,您可以编辑模块/设置文件。
步骤 5 - 使用以下命令完成安装 -
run ./configure script make make install
成功完成上述步骤后,Python 将安装在其标准位置/usr/local/bin及其库/usr/local/lib/pythonXX中,其中 XX 是 Python 的版本。
Windows 上的 Python 安装
我们可以按照以下简单步骤在 Windows 计算机上安装 Python。
步骤 1 - 打开网络浏览器。输入并输入www.python.org/downloads/
步骤 2 - 下载 Windows 安装程序python-XYZ.msi文件,其中 XYZ 是我们需要安装的版本。
步骤 3 - 现在将安装程序文件保存到本地计算机后运行该 MSI 文件。
步骤 4 - 运行下载的文件,这将打开 Python 安装向导。
Macintosh 上的 Python 安装
为了在 Mac OS X 上安装 Python 3,我们必须使用名为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 - Type setenv PATH "$PATH:/usr/local/bin/python" and then press Enter.
If using bash shell (Linux) − Type export ATH="$PATH:/usr/local/bin/python" and then press Enter.
If using sh or ksh shell - Type PATH="$PATH:/usr/local/bin/python" and then press Enter.
Windows 下的路径设置
在命令提示符处键入路径 %path%;C:\Python ,然后按 Enter。
运行Python
您可以选择以下三种方法中的任何一种来启动Python解释器 -
方法一:使用交互式解释器
提供命令行解释器或 shell 的系统可以轻松地用于启动 Python。例如,Unix、DOS 等。您可以按照下面给出的步骤在交互式解释器中开始编码 -
步骤 1 -在命令行输入python 。
步骤 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
方法三:集成开发环境
如果系统具有支持 Python 的 GUI 应用程序,则可以从该 GUI 环境运行 Python。下面给出了一些适用于各种平台的 IDE -
Unix IDE - UNIX 有用于 Python 的 IDLE IDE。
Windows IDE - Windows 有 PythonWin,这是第一个用于 Python 和 GUI 的 Windows 界面。
Macintosh IDE - Macintosh 有 IDLE IDE,可从主网站获取,可作为 MacBinary 或 BinHex 文件下载。
工件报告
现在您已经熟悉了在本地系统上安装和运行 Python 命令,让我们详细介绍取证的概念。本章将解释处理 Python 数字取证中的工件所涉及的各种概念。
需要创建报告
数字取证过程包括第三阶段的报告。这是数字取证过程中最重要的部分之一。由于以下原因,有必要创建报告 -
数字取证检查员在该文件中概述了调查过程及其调查结果。
一份好的数字取证报告可以被另一位审查员引用,以通过给定的相同存储库获得相同的结果。
它是一份技术和科学文档,包含在数字证据的 1 和 0 中发现的事实。
报告创建的一般准则
编写报告的目的是向读者提供信息,并且必须从坚实的基础开始。如果报告的编写没有一些一般性的指导方针或标准,调查人员可能会在有效地提出调查结果时遇到困难。下面给出了创建数字取证报告时必须遵循的一些一般准则 -
摘要- 报告必须包含信息的简短摘要,以便读者可以确定报告的目的。
使用的工具- 我们必须提到用于进行数字取证过程的工具,包括它们的目的。
存储库- 假设我们调查了某人的计算机,然后对电子邮件、内部搜索历史记录等相关材料进行证据总结和分析,然后它们必须包含在报告中,以便清楚地呈现案件。
对律师的建议- 报告必须根据报告中的调查结果向律师提出继续或停止调查的建议。
创建不同类型的报告
在上一节中,我们了解了数字取证中报告的重要性以及创建报告的指南。下面讨论了 Python 中用于创建不同类型报告的一些格式 -
CSV 报告
最常见的报告输出格式之一是 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 | 印多尔 | 恩格 |
莫罕 | 25 | 昌迪加尔 | 人力资源 |
帕卡什 | 45 | 德里 | 它 |
Excel 报告
另一种常见的报告输出格式是 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 数字移动设备取证
本章将解释移动设备上的Python数字取证以及所涉及的概念。
介绍
移动设备取证是数字取证的一个分支,它处理移动设备的获取和分析以恢复调查兴趣的数字证据。该分支与计算机取证不同,因为移动设备具有内置通信系统,可用于提供与位置相关的有用信息。
尽管智能手机在数字取证中的使用日益增加,但由于其异构性,它仍然被认为是非标准的。另一方面,计算机硬件,例如硬盘,也被认为是标准的,并且也作为一门稳定的学科而发展。在数字取证行业中,对于具有短暂证据的非标准设备(例如智能手机)所使用的技术存在很多争论。
可从移动设备中提取的工件
与仅具有通话记录或短信的旧手机相比,现代移动设备拥有大量数字信息。因此,移动设备可以为调查人员提供有关其用户的大量见解。可以从移动设备中提取的一些工件如下所述 -
消息- 这些是有用的文物,可以揭示所有者的精神状态,甚至可以向调查员提供一些以前未知的信息。
位置历史记录- 位置历史记录数据是一个有用的工件,调查人员可以使用它来验证一个人的特定位置。
安装的应用程序- 通过访问安装的应用程序类型,调查人员可以深入了解移动用户的习惯和思维。
Python 中的证据来源和处理
智能手机以 SQLite 数据库和 PLIST 文件作为主要证据来源。在本节中,我们将使用 python 处理证据来源。
分析 PLIST 文件
PLIST(属性列表)是一种灵活且方便的格式,用于存储应用程序数据,尤其是在 iPhone 设备上。它使用扩展名.plist。此类文件用于存储有关捆绑包和应用程序的信息。它可以采用两种格式:XML和二进制。以下 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详细了解 SQLite 和 Python。
在移动取证过程中,我们可以与移动设备的sms.db文件进行交互,并可以从消息表中提取有价值的信息。Python 有一个名为sqlite3的内置库,用于连接 SQLite 数据库。您可以使用以下命令导入相同的内容 -
import sqlite3
现在,借助以下命令,我们可以连接数据库,例如移动设备的sms.db -
Conn = sqlite3.connect(‘sms.db’) C = conn.cursor()
这里,C是游标对象,借助它我们可以与数据库进行交互。
现在,假设如果我们想执行一个特定的命令,比如从abc 表中获取详细信息,可以借助以下命令来完成 -
c.execute(“Select * from abc”) c.close()
上述命令的结果将存储在游标对象中。类似地,我们可以使用fetchall()方法将结果转储到我们可以操作的变量中。
我们可以使用以下命令获取sms.db中消息表的列名数据-
c.execute(“pragma table_info(message)”) table_data = c.fetchall() columns = [x[1] for x in table_data
请注意,这里我们使用 SQLite PRAGMA 命令,这是一个特殊命令,用于控制 SQLite 环境中的各种环境变量和状态标志。在上面的命令中,fetchall()方法返回一个结果元组。每列的名称存储在每个元组的第一个索引中。
现在,借助以下命令,我们可以查询表中的所有数据并将其存储在名为data_msg的变量中-
c.execute(“Select * from message”) data_msg = c.fetchall()
上面的命令会将数据存储在变量中,此外我们还可以使用csv.writer()方法将上述数据写入 CSV 文件中。
iTunes 备份
iPhone 移动取证可以对 iTunes 所做的备份执行。取证检查人员依赖于分析通过 iTunes 获取的 iPhone 逻辑备份。iTunes 使用 AFC(Apple 文件连接)协议进行备份。此外,除了托管密钥记录之外,备份过程不会修改 iPhone 上的任何内容。
现在,问题来了:为什么数字取证专家了解 iTunes 备份技术很重要?这一点很重要,以防我们直接访问嫌疑人的电脑而不是 iPhone,因为当使用电脑与 iPhone 同步时,iPhone 上的大部分信息很可能会备份到电脑上。
备份过程及其位置
每当 Apple 产品备份到计算机时,它都会与 iTunes 同步,并且会出现一个带有设备唯一 ID 的特定文件夹。在最新的备份格式中,文件存储在包含文件名前两个十六进制字符的子文件夹中。在这些备份文件中,有一些文件(例如 info.plist)与名为 Manifest.db 的数据库一起很有用。下表显示了备份位置,随 iTunes 备份操作系统的不同而变化 -
操作系统 | 备份位置 |
---|---|
WIN7 | C:\Users\[用户名]\AppData\Roaming\AppleComputer\MobileSync\Backup\ |
操作系统 | 〜/库/应用程序支持/MobileSync/备份/ |
为了使用 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__)
现在,提供两个位置参数,即 INPUT_DIR 和 OUTPUT_DIR,它们代表 iTunes 备份和所需的输出文件夹 -
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 网络连接到外部世界。有时设备会自动连接到这些开放网络。
对于 iPhone,设备已连接的开放 Wi-Fi 连接列表存储在名为com.apple.wifi.plist的 PLIST 文件中。该文件将包含 Wi-Fi SSID、BSSID 和连接时间。
我们需要使用 Python 从标准 Cellebrite XML 报告中提取 Wi-Fi 详细信息。为此,我们需要使用无线地理记录引擎 (WIGLE) 的 API,这是一个流行的平台,可用于使用 Wi-Fi 网络名称查找设备的位置。
我们可以使用名为requests的 Python 库来访问 WIGLE 的 API。它可以按如下方式安装 -
pip install requests
来自 WIGLE 的 API
我们需要在 WIGLE 网站https://wigle.net/account上注册才能获得 WIGLE 的免费 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
现在,提供两个位置参数,即INPUT_FILE和OUTPUT_CSV,它们分别代表具有 Wi-Fi MAC 地址的输入文件和所需的输出 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 和时间戳添加到 wifi 字典中,如下所示 -
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 调用有一个限制,如果超过该限制,则必须显示错误,如下所示 -
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"])
现在,我们可以使用write_csv()函数将输出写入 CSV 文件,就像我们在本章前面的脚本中所做的那样。
研究嵌入式元数据
在本章中,我们将详细了解如何使用 Python 数字取证调查嵌入式元数据。
介绍
嵌入式元数据是有关存储在同一文件中的数据的信息,该文件具有该数据所描述的对象。换句话说,它是存储在数字文件本身中的有关数字资产的信息。它始终与文件相关联,永远无法分离。
在数字取证中,我们无法提取有关特定文件的所有信息。另一方面,嵌入式元数据可以为我们提供对调查至关重要的信息。例如,文本文件的元数据可能包含有关作者、其长度、编写日期甚至有关该文档的简短摘要的信息。数字图像可以包括元数据,例如图像的长度、快门速度等。
包含元数据属性及其提取的工件
在本节中,我们将了解包含元数据属性的各种工件及其使用 Python 的提取过程。
音频和视频
这是两个非常常见的具有嵌入式元数据的工件。可以出于调查目的提取此元数据。
您可以使用以下 Python 脚本从音频或 MP3 文件以及视频或 MP4 文件中提取公共属性或元数据。
请注意,对于此脚本,我们需要安装一个名为 mutagen 的第三方 python 库,它允许我们从音频和视频文件中提取元数据。可以借助以下命令进行安装 -
pip install mutagen
我们需要为此 Python 脚本导入的一些有用的库如下 -
from __future__ import print_function import argparse import json import mutagen
命令行处理程序将采用一个参数来表示 MP3 或 MP4 文件的路径。然后,我们将使用mutagen.file()方法打开文件的句柄,如下所示 -
if __name__ == '__main__': parser = argparse.ArgumentParser('Python Metadata Extractor') parser.add_argument("AV_FILE", help="File to extract metadata from") args = parser.parse_args() av_file = mutagen.File(args.AV_FILE) file_ext = args.AV_FILE.rsplit('.', 1)[-1] if file_ext.lower() == 'mp3': handle_id3(av_file) elif file_ext.lower() == 'mp4': handle_mp4(av_file)
现在,我们需要使用两个句柄,一个用于从 MP3 文件中提取数据,另一个用于从 MP4 文件中提取数据。我们可以如下定义这些句柄 -
def handle_id3(id3_file): id3_frames = {'TIT2': 'Title', 'TPE1': 'Artist', 'TALB': 'Album','TXXX': 'Custom', 'TCON': 'Content Type', 'TDRL': 'Date released','COMM': 'Comments', 'TDRC': 'Recording Date'} print("{:15} | {:15} | {:38} | {}".format("Frame", "Description","Text","Value")) print("-" * 85) for frames in id3_file.tags.values(): frame_name = id3_frames.get(frames.FrameID, frames.FrameID) desc = getattr(frames, 'desc', "N/A") text = getattr(frames, 'text', ["N/A"])[0] value = getattr(frames, 'value', "N/A") if "date" in frame_name.lower(): text = str(text) print("{:15} | {:15} | {:38} | {}".format( frame_name, desc, text, value)) def handle_mp4(mp4_file): cp_sym = u"\u00A9" qt_tag = { cp_sym + 'nam': 'Title', cp_sym + 'art': 'Artist', cp_sym + 'alb': 'Album', cp_sym + 'gen': 'Genre', 'cpil': 'Compilation', cp_sym + 'day': 'Creation Date', 'cnID': 'Apple Store Content ID', 'atID': 'Album Title ID', 'plID': 'Playlist ID', 'geID': 'Genre ID', 'pcst': 'Podcast', 'purl': 'Podcast URL', 'egid': 'Episode Global ID', 'cmID': 'Camera ID', 'sfID': 'Apple Store Country', 'desc': 'Description', 'ldes': 'Long Description'} genre_ids = json.load(open('apple_genres.json'))
现在,我们需要迭代这个 MP4 文件,如下所示 -
print("{:22} | {}".format('Name', 'Value')) print("-" * 40) for name, value in mp4_file.tags.items(): tag_name = qt_tag.get(name, name) if isinstance(value, list): value = "; ".join([str(x) for x in value]) if name == 'geID': value = "{}: {}".format( value, genre_ids[str(value)].replace("|", " - ")) print("{:22} | {}".format(tag_name, value))
上面的脚本将为我们提供有关 MP3 和 MP4 文件的附加信息。
图片
图像可能包含不同类型的元数据,具体取决于其文件格式。然而,大多数图像都嵌入了 GPS 信息。我们可以使用第三方 Python 库提取 GPS 信息。您可以使用以下 Python 脚本来执行相同的操作 -
首先,下载名为Python Imaging Library (PIL)的第三方 python 库,如下所示 -
pip install pillow
这将帮助我们从图像中提取元数据。
我们还可以将图像中嵌入的 GPS 详细信息写入 KML 文件,但为此我们需要下载名为simplekml的第三方 Python 库,如下所示 -
pip install simplekml
在此脚本中,首先我们需要导入以下库 -
from __future__ import print_function import argparse from PIL import Image from PIL.ExifTags import TAGS import simplekml import sys
现在,命令行处理程序将接受一个位置参数,该参数基本上代表照片的文件路径。
parser = argparse.ArgumentParser('Metadata from images') parser.add_argument('PICTURE_FILE', help = "Path to picture") args = parser.parse_args()
现在,我们需要指定将填充坐标信息的 URL。URL 是gmaps和open_maps。我们还需要一个函数将 PIL 库提供的度分秒 (DMS) 元组坐标转换为十进制。可以按如下方式完成 -
gmaps = "https://www.google.com/maps?q={},{}" open_maps = "http://www.openstreetmap.org/?mlat={}&mlon={}" def process_coords(coord): coord_deg = 0 for count, values in enumerate(coord): coord_deg += (float(values[0]) / values[1]) / 60**count return coord_deg
现在,我们将使用image.open()函数将文件作为 PIL 对象打开。
img_file = Image.open(args.PICTURE_FILE) exif_data = img_file._getexif() if exif_data is None: print("No EXIF data found") sys.exit() for name, value in exif_data.items(): gps_tag = TAGS.get(name, name) if gps_tag is not 'GPSInfo': continue
找到GPSInfo标签后,我们将存储 GPS 参考并使用process_coords()方法处理坐标。
lat_ref = value[1] == u'N' lat = process_coords(value[2]) if not lat_ref: lat = lat * -1 lon_ref = value[3] == u'E' lon = process_coords(value[4]) if not lon_ref: lon = lon * -1
现在,从simplekml库启动kml对象,如下所示 -
kml = simplekml.Kml() kml.newpoint(name = args.PICTURE_FILE, coords = [(lon, lat)]) kml.save(args.PICTURE_FILE + ".kml")
我们现在可以从处理后的信息中打印坐标,如下所示 -
print("GPS Coordinates: {}, {}".format(lat, lon)) print("Google Maps URL: {}".format(gmaps.format(lat, lon))) print("OpenStreetMap URL: {}".format(open_maps.format(lat, lon))) print("KML File {} created".format(args.PICTURE_FILE + ".kml"))
PDF文档
PDF 文档具有多种媒体,包括图像、文本、表单等。当我们提取 PDF 文档中嵌入的元数据时,我们可能会得到可扩展元数据平台 (XMP) 格式的结果数据。我们可以借助以下 Python 代码提取元数据 -
首先,安装名为PyPDF2的第三方 Python 库来读取以 XMP 格式存储的元数据。它可以按如下方式安装 -
pip install PyPDF2
现在,导入以下库以从 PDF 文件中提取元数据 -
from __future__ import print_function from argparse import ArgumentParser, FileType import datetime from PyPDF2 import PdfFileReader import sys
现在,命令行处理程序将接受一个位置参数,该参数基本上表示 PDF 文件的文件路径。
parser = argparse.ArgumentParser('Metadata from PDF') parser.add_argument('PDF_FILE', help='Path to PDF file',type=FileType('rb')) args = parser.parse_args()
现在我们可以使用getXmpMetadata()方法提供一个包含可用元数据的对象,如下所示 -
pdf_file = PdfFileReader(args.PDF_FILE) xmpm = pdf_file.getXmpMetadata() if xmpm is None: print("No XMP metadata found in document.") sys.exit()
我们可以使用custom_print()方法来提取并打印相关值,如标题、创建者、贡献者等,如下所示 -
custom_print("Title: {}", xmpm.dc_title) custom_print("Creator(s): {}", xmpm.dc_creator) custom_print("Contributors: {}", xmpm.dc_contributor) custom_print("Subject: {}", xmpm.dc_subject) custom_print("Description: {}", xmpm.dc_description) custom_print("Created: {}", xmpm.xmp_createDate) custom_print("Modified: {}", xmpm.xmp_modifyDate) custom_print("Event Dates: {}", xmpm.dc_date)
如果使用多个软件创建 PDF,我们还可以定义custom_print()方法,如下所示 -
def custom_print(fmt_str, value): if isinstance(value, list): print(fmt_str.format(", ".join(value))) elif isinstance(value, dict): fmt_value = [":".join((k, v)) for k, v in value.items()] print(fmt_str.format(", ".join(value))) elif isinstance(value, str) or isinstance(value, bool): print(fmt_str.format(value)) elif isinstance(value, bytes): print(fmt_str.format(value.decode())) elif isinstance(value, datetime.datetime): print(fmt_str.format(value.isoformat())) elif value is None: print(fmt_str.format("N/A")) else: print("warn: unhandled type {} found".format(type(value)))
我们还可以提取软件保存的任何其他自定义属性,如下所示 -
if xmpm.custom_properties: print("Custom Properties:") for k, v in xmpm.custom_properties.items(): print("\t{}: {}".format(k, v))
上述脚本将读取 PDF 文档,并打印以 XMP 格式存储的元数据,包括制作 PDF 的软件存储的一些自定义属性。
Windows 可执行文件
有时我们可能会遇到可疑或未经授权的可执行文件。但出于调查目的,由于嵌入的元数据,它可能很有用。我们可以获得诸如其位置、用途和其他属性(例如制造商、编译日期等)的信息。借助以下 Python 脚本,我们可以获得编译日期、标题中的有用数据以及导入和导出的符号。
为此,首先安装第三方 Python 库pefile。可以按如下方式完成 -
pip install pefile
成功安装后,导入以下库,如下所示 -
from __future__ import print_function import argparse from datetime import datetime from pefile import PE
现在,命令行处理程序将接受一个位置参数,该参数基本上表示可执行文件的文件路径。您还可以选择输出的样式,无论您需要详细、详细的方式还是简化的方式。为此,您需要给出一个可选参数,如下所示 -
parser = argparse.ArgumentParser('Metadata from executable file') parser.add_argument("EXE_FILE", help = "Path to exe file") parser.add_argument("-v", "--verbose", help = "Increase verbosity of output", action = 'store_true', default = False) args = parser.parse_args()
现在,我们将使用 PE 类加载输入可执行文件。我们还将使用dump_dict()方法将可执行数据转储到字典对象。
pe = PE(args.EXE_FILE) ped = pe.dump_dict()
我们可以使用下面所示的代码提取基本的文件元数据,例如嵌入的作者、版本和编译时间 -
file_info = {} for structure in pe.FileInfo: if structure.Key == b'StringFileInfo': for s_table in structure.StringTable: for key, value in s_table.entries.items(): if value is None or len(value) == 0: value = "Unknown" file_info[key] = value print("File Information: ") print("==================") for k, v in file_info.items(): if isinstance(k, bytes): k = k.decode() if isinstance(v, bytes): v = v.decode() print("{}: {}".format(k, v)) comp_time = ped['FILE_HEADER']['TimeDateStamp']['Value'] comp_time = comp_time.split("[")[-1].strip("]") time_stamp, timezone = comp_time.rsplit(" ", 1) comp_time = datetime.strptime(time_stamp, "%a %b %d %H:%M:%S %Y") print("Compiled on {} {}".format(comp_time, timezone.strip()))
我们可以从标头中提取有用的数据,如下所示 -
for section in ped['PE Sections']: print("Section '{}' at {}: {}/{} {}".format( section['Name']['Value'], hex(section['VirtualAddress']['Value']), section['Misc_VirtualSize']['Value'], section['SizeOfRawData']['Value'], section['MD5']) )
现在,从可执行文件中提取导入和导出的列表,如下所示 -
if hasattr(pe, 'DIRECTORY_ENTRY_IMPORT'): print("\nImports: ") print("=========") for dir_entry in pe.DIRECTORY_ENTRY_IMPORT: dll = dir_entry.dll if not args.verbose: print(dll.decode(), end=", ") continue name_list = [] for impts in dir_entry.imports: if getattr(impts, "name", b"Unknown") is None: name = b"Unknown" else: name = getattr(impts, "name", b"Unknown") name_list.append([name.decode(), hex(impts.address)]) name_fmt = ["{} ({})".format(x[0], x[1]) for x in name_list] print('- {}: {}'.format(dll.decode(), ", ".join(name_fmt))) if not args.verbose: print()
现在,使用如下所示的代码打印出口、名称和地址-
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 可执行文件的标头中提取基本元数据、信息。
办公文档元数据
计算机上的大部分工作都是通过 MS Office 的三个应用程序完成的——Word、PowerPoint 和 Excel。这些文件拥有巨大的元数据,可以公开有关其作者和历史的有趣信息。
请注意,2007 年格式的元数据(word (.docx)、excel (.xlsx) 和 powerpoint (.pptx))存储在 XML 文件中。我们可以借助如下所示的 Python 脚本在 Python 中处理这些 XML 文件 -
首先,导入所需的库,如下所示 -
from __future__ import print_function from argparse import ArgumentParser from datetime import datetime as dt from xml.etree import ElementTree as etree import zipfile parser = argparse.ArgumentParser('Office Document Metadata’) parser.add_argument("Office_File", help="Path to office file to read") args = parser.parse_args()
现在,检查该文件是否是 ZIP 文件。否则,提出错误。现在,打开文件并使用以下代码提取要处理的关键元素 -
zipfile.is_zipfile(args.Office_File) zfile = zipfile.ZipFile(args.Office_File) core_xml = etree.fromstring(zfile.read('docProps/core.xml')) app_xml = etree.fromstring(zfile.read('docProps/app.xml'))
现在,创建一个字典来启动元数据的提取 -
core_mapping = { 'title': 'Title', 'subject': 'Subject', 'creator': 'Author(s)', 'keywords': 'Keywords', 'description': 'Description', 'lastModifiedBy': 'Last Modified By', 'modified': 'Modified Date', 'created': 'Creat