Ważne artefakty w systemie Windows-I
W tym rozdziale zostaną wyjaśnione różne pojęcia związane z kryminalistyką Microsoft Windows oraz ważne artefakty, które badacz może uzyskać w trakcie dochodzenia.
Wprowadzenie
Artefakty to obiekty lub obszary w systemie komputerowym, które zawierają ważne informacje związane z czynnościami wykonywanymi przez użytkownika komputera. Rodzaj i lokalizacja tych informacji zależy od systemu operacyjnego. Podczas analizy kryminalistycznej artefakty te odgrywają bardzo ważną rolę w zatwierdzaniu lub odrzucaniu obserwacji badacza.
Znaczenie artefaktów systemu Windows dla kryminalistyki
Artefakty systemu Windows nabierają znaczenia z następujących powodów -
Około 90% ruchu na świecie pochodzi z komputerów używających systemu Windows jako systemu operacyjnego. Dlatego też artefakty systemu Windows są bardzo istotne dla badaczy kryminalistyki cyfrowej.
System operacyjny Windows przechowuje różnego rodzaju dowody związane z aktywnością użytkownika w systemie komputerowym. Jest to kolejny powód, który pokazuje znaczenie artefaktów systemu Windows dla kryminalistyki cyfrowej.
Wielokrotnie badacz obraca dochodzenie wokół starych i tradycyjnych obszarów, takich jak dane skrzynek użytkowników. Artefakty systemu Windows mogą prowadzić badanie w kierunku nietradycyjnych obszarów, takich jak dane utworzone przez system lub artefakty.
System Windows zapewnia ogromną ilość artefaktów, które są pomocne zarówno dla śledczych, jak i dla firm i osób prowadzących nieformalne dochodzenia.
Wzrost cyberprzestępczości w ostatnich latach to kolejny powód, dla którego artefakty systemu Windows są ważne.
Artefakty systemu Windows i ich skrypty w języku Python
W tej sekcji omówimy niektóre artefakty systemu Windows i skrypty Pythona do pobierania z nich informacji.
Kosz
Jest to jeden z ważnych artefaktów systemu Windows do badań kryminalistycznych. Kosz systemu Windows zawiera pliki, które zostały usunięte przez użytkownika, ale nie zostały jeszcze fizycznie usunięte przez system. Nawet jeśli użytkownik całkowicie usunie plik z systemu, jest to ważne źródło dochodzeń. Dzieje się tak, ponieważ egzaminator może wydobyć z usuniętych plików cenne informacje, takie jak oryginalna ścieżka do pliku, a także czas wysłania go do Kosza.
Należy pamiętać, że przechowywanie dowodów z Kosza zależy od wersji systemu Windows. W poniższym skrypcie w Pythonie zajmiemy się systemem Windows 7, w którym tworzy dwa pliki:$R plik zawierający rzeczywistą zawartość przetworzonego pliku i $I plik zawierający oryginalną nazwę pliku, ścieżkę, rozmiar pliku w momencie usunięcia pliku.
W przypadku skryptu Python musimy zainstalować moduły stron trzecich, a mianowicie pytsk3, pyewf i unicodecsv. Możemy użyćpipaby je zainstalować. Możemy wykonać następujące kroki, aby wyodrębnić informacje z Kosza -
Najpierw musimy użyć metody rekurencyjnej, aby przeskanować plik $Recycle.bin folder i zaznacz wszystkie pliki zaczynające się od $I.
Następnie przeczytamy zawartość plików i przeanalizujemy dostępne struktury metadanych.
Teraz będziemy szukać skojarzonego pliku $ R.
Na koniec zapiszemy wyniki do pliku CSV do przeglądu.
Zobaczmy, jak w tym celu wykorzystać kod Pythona -
Najpierw musimy zaimportować następujące biblioteki Pythona -
from __future__ import print_function
from argparse import ArgumentParser
import datetime
import os
import struct
from utility.pytskutil import TSKUtil
import unicodecsv as csv
Następnie musimy podać argument do obsługi wiersza poleceń. Zauważ, że w tym przypadku przyjmie trzy argumenty - pierwszy to ścieżka do pliku dowodowego, drugi to typ pliku dowodowego, a trzeci to pożądana ścieżka wyjściowa do raportu CSV, jak pokazano poniżej -
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)
Teraz zdefiniuj main()funkcja, która będzie obsługiwać całe przetwarzanie. Będzie szukać$I plik w następujący sposób -
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")
Teraz, gdybyśmy znaleźli $I plik, to należy go wysłać do process_dollar_i() funkcja, która zaakceptuje tsk_util obiektu, a także listę $I pliki, jak pokazano poniżej -
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:])
Teraz wyszukaj pliki $ R w następujący sposób -
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
Teraz zdefiniuj read_dollar_i() metoda odczytu $Ipliki, innymi słowy, analizują metadane. Użyjemyread_random()metoda odczytywania pierwszych ośmiu bajtów podpisu. To nie zwróci żadnego, jeśli podpis nie jest zgodny. Następnie będziemy musieli odczytać i rozpakować wartości z$I plik, jeśli jest to prawidłowy plik.
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)
Teraz po wyodrębnieniu tych plików musimy zinterpretować liczby całkowite na wartości czytelne dla człowieka przy użyciu sizeof_fmt() funkcja, jak pokazano poniżej -
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}
Teraz musimy zdefiniować sizeof_fmt() działają w następujący sposób -
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)
Teraz zdefiniuj funkcję dla interpretowanych liczb całkowitych na sformatowaną datę i godzinę w następujący sposób -
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')
Teraz zdefiniujemy write_csv() metoda zapisywania przetworzonych wyników do pliku CSV w następujący sposób -
def write_csv(outfile, fieldnames, data):
with open(outfile, 'wb') as open_outfile:
csvfile = csv.DictWriter(open_outfile, fieldnames)
csvfile.writeheader()
csvfile.writerows(data)
Po uruchomieniu powyższego skryptu otrzymamy dane z pliku $ I i $ R.
Kartki samoprzylepne
Windows Sticky Notes zastępuje prawdziwy zwyczaj pisania piórem i papierem. Te notatki były używane do unoszenia się na pulpicie z różnymi opcjami kolorów, czcionek itp. W systemie Windows 7 plik Sticky Notes jest przechowywany jako plik OLE, dlatego w poniższym skrypcie Pythona zbadamy ten plik OLE, aby wyodrębnić metadane z Sticky Notes.
W przypadku tego skryptu Python musimy zainstalować moduły stron trzecich, a mianowicie olefile, pytsk3, pyewfi unicodecsv. Możemy użyć poleceniapip aby je zainstalować.
Możemy wykonać kroki omówione poniżej, aby wyodrębnić informacje z pliku karteczek samoprzylepnych, a mianowicie StickyNote.sn -
Najpierw otwórz plik dowodowy i znajdź wszystkie pliki StickyNote.snt.
Następnie przeanalizuj metadane i zawartość ze strumienia OLE i zapisz zawartość RTF do plików.
Na koniec utwórz raport w formacie CSV zawierający te metadane.
Kod w Pythonie
Zobaczmy, jak w tym celu wykorzystać kod Pythona -
Najpierw zaimportuj następujące biblioteki Pythona -
from __future__ import print_function
from argparse import ArgumentParser
import unicodecsv as csv
import os
import StringIO
from utility.pytskutil import TSKUtil
import olefile
Następnie zdefiniuj zmienną globalną, która będzie używana w tym skrypcie -
REPORT_COLS = ['note_id', 'created', 'modified', 'note_text', 'note_file']
Następnie musimy podać argument do obsługi wiersza poleceń. Zauważ, że tutaj przyjmie trzy argumenty - pierwszy to ścieżka do pliku dowodowego, drugi to typ pliku dowodowego, a trzeci to pożądana ścieżka wyjściowa w następujący sposób -
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)
Teraz zdefiniujemy main() funkcja, która będzie podobna do poprzedniego skryptu, jak pokazano poniżej -
def main(evidence, image_type, report_folder):
tsk_util = TSKUtil(evidence, image_type)
note_files = tsk_util.recurse_files('StickyNotes.snt', '/Users','equals')
Teraz przejrzyjmy pliki wynikowe. Wtedy zadzwonimyparse_snt_file() do przetworzenia pliku, a następnie zapiszemy plik RTF z rozszerzeniem write_note_rtf() metoda w następujący sposób -
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)
Następnie musimy zdefiniować różne funkcje używane w tym skrypcie.
Przede wszystkim zdefiniujemy create_file_like_obj() funkcja do odczytywania rozmiaru pliku, biorąc pytskobiekt pliku. Wtedy zdefiniujemyparse_snt_file() funkcja, która przyjmie obiekt podobny do pliku jako dane wejściowe i jest używana do odczytu i interpretacji pliku karteczek samoprzylepnych.
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
Teraz utwórz plik RTF, definiując write_note_rtf() działają w następujący sposób
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'])
Teraz przetłumaczymy zagnieżdżony słownik na płaską listę słowników, które są bardziej odpowiednie dla arkusza kalkulacyjnego CSV. Zostanie to zrobione przez zdefiniowanieprep_note_report()funkcjonować. Na koniec zdefiniujemywrite_csv() funkcjonować.
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)
Po uruchomieniu powyższego skryptu uzyskamy metadane z pliku Sticky Notes.
Pliki rejestru
Pliki rejestru systemu Windows zawierają wiele ważnych szczegółów, które są jak skarbnica informacji dla analityka sądowego. Jest to hierarchiczna baza danych, która zawiera szczegóły związane z konfiguracją systemu operacyjnego, aktywnością użytkowników, instalacją oprogramowania itp. W poniższym skrypcie Pythona uzyskamy dostęp do wspólnych podstawowych informacji zSYSTEM i SOFTWARE pokrzywka.
W przypadku tego skryptu Python musimy zainstalować moduły stron trzecich, a mianowicie pytsk3, pyewf i registry. Możemy użyćpip aby je zainstalować.
Możemy wykonać poniższe kroki, aby wyodrębnić informacje z rejestru systemu Windows -
Najpierw znajdź gałęzie rejestru do przetworzenia według ich nazwy, a także ścieżki.
Następnie otwieramy te pliki za pomocą modułów StringIO i Registry.
W końcu musimy przetworzyć każdą gałąź i wydrukować przeanalizowane wartości na konsoli w celu interpretacji.
Kod w Pythonie
Zobaczmy, jak w tym celu wykorzystać kod Pythona -
Najpierw zaimportuj następujące biblioteki Pythona -
from __future__ import print_function
from argparse import ArgumentParser
import datetime
import StringIO
import struct
from utility.pytskutil import TSKUtil
from Registry import Registry
Teraz podaj argument dla programu obsługi wiersza poleceń. Tutaj przyjmie dwa argumenty - pierwszy to ścieżka do pliku dowodowego, drugi to typ pliku dowodowego, jak pokazano poniżej -
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)
Teraz zdefiniujemy main() funkcja wyszukiwania SYSTEM i SOFTWARE pokrzywka wewnątrz /Windows/System32/config folder w następujący sposób -
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)
Teraz zdefiniuj funkcję otwierania pliku rejestru. W tym celu musimy pobrać rozmiar pliku zpytsk metadane w następujący sposób -
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)
Teraz, za pomocą następującej metody, możemy przetworzyć SYSTEM> ul -
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))
Teraz musimy zdefiniować funkcję dla interpretowanych liczb całkowitych na sformatowaną datę i godzinę w następujący sposób -
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')
Teraz za pomocą następującej metody możemy przetworzyć SOFTWARE ul -
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))
Po uruchomieniu powyższego skryptu otrzymamy metadane zapisane w plikach rejestru systemu Windows.