Artefactos importantes en Windows-I
Este capítulo explicará varios conceptos relacionados con la ciencia forense de Microsoft Windows y los artefactos importantes que un investigador puede obtener del proceso de investigación.
Introducción
Los artefactos son los objetos o áreas dentro de un sistema informático que tienen información importante relacionada con las actividades realizadas por el usuario de la computadora. El tipo y la ubicación de esta información dependen del sistema operativo. Durante el análisis forense, estos artefactos juegan un papel muy importante al aprobar o desaprobar la observación del investigador.
Importancia de los artefactos de Windows para la ciencia forense
Los artefactos de Windows adquieren importancia debido a las siguientes razones:
Alrededor del 90% del tráfico mundial proviene de las computadoras que utilizan Windows como sistema operativo. Es por eso que para los examinadores forenses digitales, los artefactos de Windows son muy esenciales.
El sistema operativo Windows almacena diferentes tipos de evidencias relacionadas con la actividad del usuario en el sistema informático. Esta es otra razón que muestra la importancia de los artefactos de Windows para la ciencia forense digital.
Muchas veces, el investigador gira la investigación en torno a áreas antiguas y tradicionales, como los datos almacenados por el usuario. Los artefactos de Windows pueden llevar la investigación hacia áreas no tradicionales como los datos creados por el sistema o los artefactos.
Windows proporciona una gran cantidad de artefactos que son útiles para los investigadores, así como para las empresas y las personas que realizan investigaciones informales.
El aumento de los delitos cibernéticos en los últimos años es otra razón por la que los artefactos de Windows son importantes.
Artefactos de Windows y sus scripts de Python
En esta sección, vamos a discutir sobre algunos artefactos de Windows y scripts de Python para obtener información de ellos.
Papelera de reciclaje
Es uno de los artefactos importantes de Windows para la investigación forense. La papelera de reciclaje de Windows contiene los archivos que el usuario ha eliminado, pero que el sistema aún no ha eliminado físicamente. Incluso si el usuario elimina completamente el archivo del sistema, sirve como una fuente importante de investigación. Esto se debe a que el examinador puede extraer información valiosa, como la ruta del archivo original, así como la hora en que se envió a la Papelera de reciclaje, de los archivos eliminados.
Tenga en cuenta que el almacenamiento de la evidencia de la Papelera de reciclaje depende de la versión de Windows. En el siguiente script de Python, vamos a tratar con Windows 7 donde crea dos archivos:$R archivo que contiene el contenido real del archivo reciclado y $I archivo que contiene el nombre del archivo original, la ruta y el tamaño del archivo cuando se eliminó el archivo.
Para el script de Python, necesitamos instalar módulos de terceros, a saber pytsk3, pyewf y unicodecsv. Nosotros podemos usarpippara instalarlos. Podemos seguir los siguientes pasos para extraer información de la Papelera de reciclaje:
Primero, necesitamos usar un método recursivo para escanear a través del $Recycle.bin carpeta y seleccione todos los archivos que comienzan con $I.
A continuación, leeremos el contenido de los archivos y analizaremos las estructuras de metadatos disponibles.
Ahora, buscaremos el archivo $ R asociado.
Por último, escribiremos los resultados en un archivo CSV para su revisión.
Veamos cómo usar el código Python para este propósito:
Primero, necesitamos importar las siguientes bibliotecas de 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
A continuación, necesitamos proporcionar un argumento para el controlador de línea de comandos. Tenga en cuenta que aquí aceptará tres argumentos: primero es la ruta al archivo de evidencia, segundo es el tipo de archivo de evidencia y tercero es la ruta de salida deseada para el informe CSV, como se muestra a continuación:
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)
Ahora, defina el main()función que se encargará de todo el procesamiento. Buscará$I archivo de la siguiente manera:
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")
Ahora, si encontramos $I archivo, luego debe enviarse a process_dollar_i() función que aceptará el tsk_util objeto, así como la lista de $I archivos, como se muestra a continuación -
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:])
Ahora, busque archivos $ R de la siguiente manera:
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
Ahora, define read_dollar_i() método para leer el $Iarchivos, en otras palabras, analizar los metadatos. Usaremosread_random()método para leer los primeros ocho bytes de la firma. Esto devolverá ninguno si la firma no coincide. Después de eso, tendremos que leer y descomprimir los valores de$I archivo si es un archivo válido.
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)
Ahora, después de extraer estos archivos, necesitamos interpretar los números enteros en valores legibles por humanos usando sizeof_fmt() funciona como se muestra a continuación -
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}
Ahora, necesitamos definir sizeof_fmt() funciona de la siguiente manera:
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)
Ahora, defina una función para enteros interpretados en fecha y hora formateadas de la siguiente manera:
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')
Ahora, definiremos write_csv() método para escribir los resultados procesados en un archivo CSV de la siguiente manera:
def write_csv(outfile, fieldnames, data):
with open(outfile, 'wb') as open_outfile:
csvfile = csv.DictWriter(open_outfile, fieldnames)
csvfile.writeheader()
csvfile.writerows(data)
Cuando ejecute el script anterior, obtendremos los datos del archivo $ I y $ R.
Notas adhesivas
Windows Sticky Notes reemplaza el hábito real de escribir con lápiz y papel. Estas notas solían flotar en el escritorio con diferentes opciones de colores, fuentes, etc. En Windows 7, el archivo Sticky Notes se almacena como un archivo OLE, por lo que en el siguiente script de Python investigaremos este archivo OLE para extraer metadatos de Sticky Notes.
Para este script de Python, necesitamos instalar módulos de terceros, a saber olefile, pytsk3, pyewfy unicodecsv. Podemos usar el comandopip para instalarlos.
Podemos seguir los pasos que se describen a continuación para extraer la información del archivo de notas adhesivas, a saber StickyNote.sn -
En primer lugar, abra el archivo de pruebas y busque todos los archivos StickyNote.snt.
Luego, analice los metadatos y el contenido de la secuencia OLE y escriba el contenido RTF en los archivos.
Por último, cree un informe CSV de estos metadatos.
Código Python
Veamos cómo usar el código Python para este propósito:
Primero, importe las siguientes bibliotecas de 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
A continuación, defina una variable global que se utilizará en este script:
REPORT_COLS = ['note_id', 'created', 'modified', 'note_text', 'note_file']
A continuación, necesitamos proporcionar un argumento para el controlador de línea de comandos. Tenga en cuenta que aquí aceptará tres argumentos: el primero es la ruta al archivo de evidencia, el segundo es el tipo de archivo de evidencia y el tercero es la ruta de salida deseada de la siguiente manera:
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)
Ahora, definiremos main() función que será similar a la secuencia de comandos anterior como se muestra a continuación:
def main(evidence, image_type, report_folder):
tsk_util = TSKUtil(evidence, image_type)
note_files = tsk_util.recurse_files('StickyNotes.snt', '/Users','equals')
Ahora, iteremos a través de los archivos resultantes. Entonces llamaremosparse_snt_file() función para procesar el archivo y luego escribiremos el archivo RTF con el write_note_rtf() método de la siguiente manera -
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)
A continuación, necesitamos definir varias funciones utilizadas en este script.
Primero que nada definiremos create_file_like_obj() función para leer el tamaño del archivo tomando pytskobjeto de archivo. Entonces definiremosparse_snt_file() función que aceptará el objeto similar a un archivo como entrada y se utiliza para leer e interpretar el archivo de notas adhesivas.
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
Ahora, cree un archivo RTF definiendo write_note_rtf() funciona de la siguiente manera
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'])
Ahora, traduciremos el diccionario anidado en una lista plana de diccionarios que son más apropiados para una hoja de cálculo CSV. Se hará definiendoprep_note_report()función. Por último, definiremoswrite_csv() función.
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)
Después de ejecutar el script anterior, obtendremos los metadatos del archivo Sticky Notes.
Archivos de registro
Los archivos de registro de Windows contienen muchos detalles importantes que son como un tesoro de información para un analista forense. Es una base de datos jerárquica que contiene detalles relacionados con la configuración del sistema operativo, la actividad del usuario, la instalación de software, etc. En el siguiente script de Python vamos a acceder a información de línea base común desdeSYSTEM y SOFTWARE urticaria.
Para este script de Python, necesitamos instalar módulos de terceros, a saber pytsk3, pyewf y registry. Nosotros podemos usarpip para instalarlos.
Podemos seguir los pasos que se indican a continuación para extraer la información del registro de Windows:
Primero, busque las secciones del registro para procesarlas por su nombre y por su ruta.
Luego, abrimos estos archivos usando los módulos StringIO y Registry.
Por último, necesitamos procesar todas y cada una de las colmenas e imprimir los valores analizados en la consola para su interpretación.
Código Python
Veamos cómo usar el código Python para este propósito:
Primero, importe las siguientes bibliotecas de 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
Ahora, proporcione un argumento para el controlador de la línea de comandos. Aquí aceptará dos argumentos - primero es la ruta al archivo de evidencia, segundo es el tipo de archivo de evidencia, como se muestra a continuación -
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)
Ahora definiremos main() función para buscar SYSTEM y SOFTWARE urticaria dentro /Windows/System32/config carpeta de la siguiente manera:
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)
Ahora, defina la función para abrir el archivo de registro. Para este propósito, necesitamos recopilar el tamaño del archivo depytsk metadatos de la siguiente manera:
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)
Ahora, con la ayuda del siguiente método, podemos procesar SYSTEM> colmena -
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))
Ahora, necesitamos definir una función para enteros interpretados en fecha y hora formateadas de la siguiente manera:
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')
Ahora, con la ayuda del siguiente método, podemos procesar SOFTWARE colmena -
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))
Después de ejecutar el script anterior, obtendremos los metadatos almacenados en los archivos del Registro de Windows.