Python Digital Forensics - Krótki przewodnik
W tym rozdziale dowiesz się, czym jest kryminalistyka cyfrowa, oraz omówimy jej historię. Zrozumiesz również, gdzie w prawdziwym życiu możesz zastosować kryminalistykę cyfrową i jakie są jej ograniczenia.
Co to jest Digital Forensics?
Kryminalistykę cyfrową można zdefiniować jako gałąź kryminalistyki, która analizuje, bada, identyfikuje i odzyskuje dowody cyfrowe znajdujące się na urządzeniach elektronicznych. Jest powszechnie używany w prawie karnym i dochodzeniach prywatnych.
Na przykład możesz polegać na cyfrowych materiałach śledczych na wypadek kradzieży danych z urządzenia elektronicznego.
Krótki historyczny przegląd cyfrowej medycyny sądowej
Historia przestępstw komputerowych i historyczny przegląd kryminalistyki cyfrowej jest wyjaśniona w tej sekcji, jak podano poniżej -
1970-1980: Pierwsza przestępczość komputerowa
Przed tą dekadą nie stwierdzono przestępstw komputerowych. Gdyby jednak miało się to wydarzyć, zajmowały się nimi obowiązujące wówczas przepisy. Później, w 1978 roku, pierwsze przestępstwo komputerowe zostało uznane w ustawie Florida Computer Crime Act, która zawierała przepisy zabraniające nieuprawnionej modyfikacji lub usuwania danych w systemie komputerowym. Jednak z biegiem czasu, ze względu na postęp technologiczny, zwiększył się również zakres popełnianych przestępstw komputerowych. Aby zajmować się przestępstwami związanymi z prawami autorskimi, prywatnością i pornografią dziecięcą, przyjęto różne inne ustawy.
Lata 80.-90 .: Dekada rozwoju
Ta dekada była dekadą rozwoju cyfrowej medycyny sądowej, a wszystko to za sprawą pierwszego w historii dochodzenia (1986), w którym Cliff Stoll wyśledził hakera o imieniu Markus Hess. W tym okresie rozwinęły się dwa rodzaje cyfrowych dyscyplin kryminalistycznych - pierwsza przy pomocy narzędzi i technik ad hoc opracowanych przez praktyków, którzy potraktowali to jako hobby, a druga przez społeczność naukową. W 1992 roku termin“Computer Forensics”był używany w literaturze naukowej.
2000-2010: Dekada standaryzacji
Po rozwinięciu się kryminalistyki cyfrowej do pewnego poziomu zaistniała potrzeba ustanowienia określonych standardów, którymi można się kierować podczas prowadzenia dochodzeń. W związku z tym różne agencje i organy naukowe opublikowały wytyczne dotyczące kryminalistyki cyfrowej. W 2002 r. Naukowa grupa robocza ds. Dowodów cyfrowych (SWGDE) opublikowała artykuł zatytułowany „Najlepsze praktyki w dziedzinie informatyki śledczej”. Innym piórem w czapce był europejski traktat międzynarodowy mianowicie“The Convention on Cybercrime”został podpisany przez 43 kraje i ratyfikowany przez 16 krajów. Nawet po takich standardach nadal istnieje potrzeba rozwiązania niektórych problemów, które zostały zidentyfikowane przez badaczy.
Proces Digital Forensics
Od czasu pierwszego przestępstwa komputerowego w 1978 r. Nastąpił ogromny wzrost cyfrowej działalności przestępczej. W związku z tym wzrostem istnieje potrzeba zorganizowanego sposobu radzenia sobie z nimi. W 1984 r. Wprowadzono sformalizowany proces, a następnie opracowano wiele nowych i ulepszonych procesów dochodzeń w dziedzinie informatyki śledczej.
Proces dochodzenia w dziedzinie informatyki śledczej obejmuje trzy główne fazy, jak wyjaśniono poniżej:
Faza 1: Nabycie lub zobrazowanie eksponatów
Pierwsza faza kryminalistyki cyfrowej polega na zapisaniu stanu systemu cyfrowego w celu późniejszej analizy. Jest to bardzo podobne do robienia zdjęć, próbek krwi itp. Z miejsca zbrodni. Na przykład obejmuje przechwytywanie obrazu przydzielonych i nieprzydzielonych obszarów dysku twardego lub pamięci RAM.
Faza 2: Analiza
Dane wejściowe tej fazy to dane zebrane w fazie akwizycji. Tutaj dane te zostały zbadane w celu zidentyfikowania dowodów. Ta faza daje trzy rodzaje dowodów w następujący sposób -
Inculpatory evidences - Te dowody potwierdzają daną historię.
Exculpatory evidences - Te dowody są sprzeczne z daną historią.
Evidence of tampering- Te dowody wskazują, że system został zahartowany, aby uniknąć identyfikacji. Obejmuje badanie plików i zawartości katalogu w celu odzyskania usuniętych plików.
Faza 3: Prezentacja lub raportowanie
Jak sama nazwa wskazuje, faza ta przedstawia wnioski i odpowiednie dowody z dochodzenia.
Zastosowania Digital Forensics
Kryminalistyka cyfrowa zajmuje się gromadzeniem, analizowaniem i zabezpieczaniem dowodów zawartych w dowolnym urządzeniu cyfrowym. Korzystanie z kryminalistyki cyfrowej zależy od aplikacji. Jak wspomniano wcześniej, jest używany głównie w następujących dwóch aplikacjach -
Prawo karne
W prawie karnym dowody są gromadzone na poparcie lub przeciwstawienie się hipotezie w sądzie. Procedury kryminalistyczne są bardzo podobne do tych stosowanych w dochodzeniach karnych, ale mają inne wymogi prawne i ograniczenia.
Prywatne dochodzenie
Świat głównie korporacji wykorzystuje kryminalistykę cyfrową do prywatnych dochodzeń. Jest używany, gdy firmy podejrzewają, że pracownicy mogą wykonywać na swoich komputerach nielegalną działalność, która jest sprzeczna z polityką firmy. Kryminalistyka cyfrowa zapewnia firmie lub osobie jedną z najlepszych ścieżek, którymi może kierować się podczas dochodzenia w sprawie naruszenia przepisów cyfrowych.
Działy Digital Forensics
Przestępczość cyfrowa nie ogranicza się do samych komputerów, jednak hakerzy i przestępcy używają na bardzo dużą skalę małych urządzeń cyfrowych, takich jak tablety, smartfony itp. Niektóre urządzenia mają pamięć ulotną, podczas gdy inne mają pamięć nieulotną. Stąd w zależności od rodzaju urządzeń, cyfrowa kryminalistyka ma następujące gałęzie -
Informatyka śledcza
Ta gałąź kryminalistyki cyfrowej zajmuje się komputerami, systemami wbudowanymi i pamięciami statycznymi, takimi jak dyski USB. Szeroki zakres informacji, od dzienników po rzeczywiste pliki na dysku, można zbadać w informatyce śledczej.
Mobile Forensics
Dotyczy to badania danych z urządzeń mobilnych. Branża ta różni się od informatyki śledczej w tym sensie, że urządzenia mobilne mają wbudowany system komunikacji, który jest przydatny do dostarczania przydatnych informacji związanych z lokalizacją.
Network Forensics
Dotyczy to monitorowania i analizy ruchu w sieci komputerowej, zarówno lokalnej, jak i WAN (sieci rozległej) w celu gromadzenia informacji, gromadzenia dowodów lub wykrywania włamań.
Baza danych Forensics
Ta gałąź kryminalistyki cyfrowej zajmuje się badaniami kryminalistycznymi baz danych i ich metadanych.
Umiejętności wymagane do dochodzenia w dziedzinie kryminalistyki cyfrowej
Cyfrowi eksperci kryminalistyczni pomagają w śledzeniu hakerów, odzyskiwaniu skradzionych danych, śledzeniu ataków komputerowych do ich źródła oraz w innych rodzajach dochodzeń dotyczących komputerów. Niektóre z kluczowych umiejętności wymaganych do zostania egzaminatorem kryminalistyki cyfrowej, jak omówiono poniżej:
Wyjątkowe zdolności myślowe
Badacz zajmujący się kryminalistyką cyfrową musi być wybitnym myślicielem i powinien umieć zastosować różne narzędzia i metodologie do konkretnego zadania w celu uzyskania wyników. Musi umieć znaleźć różne wzorce i dokonać między nimi korelacji.
Umiejętności techniczne
Egzaminator kryminalistyki cyfrowej musi mieć dobre umiejętności technologiczne, ponieważ ta dziedzina wymaga znajomości sieci i interakcji systemów cyfrowych.
Pasjonat cyberbezpieczeństwa
Ponieważ dziedzina kryminalistyki cyfrowej polega na rozwiązywaniu cyberprzestępczości i jest to żmudne zadanie, ktoś musi mieć dużo pasji, aby zostać asem śledczym w dziedzinie kryminalistyki cyfrowej.
Zdolności do porozumiewania się
Dobre umiejętności komunikacyjne są niezbędne, aby współpracować z różnymi zespołami i wydobyć wszelkie brakujące dane lub informacje.
Umiejętność tworzenia raportów
Po pomyślnym wdrożeniu akwizycji i analizy, cyfrowy egzaminator sądowy musi wspomnieć o wszystkich ustaleniach w raporcie końcowym i prezentacji. Dlatego musi mieć dobre umiejętności tworzenia raportów i dbałość o szczegóły.
Ograniczenia
Cyfrowe dochodzenie kryminalistyczne oferuje pewne ograniczenia, o których mowa tutaj -
Trzeba przedstawić przekonujące dowody
Jedną z głównych przeszkód w dochodzeniach w zakresie kryminalistyki cyfrowej jest to, że egzaminator musi przestrzegać standardów wymaganych dla dowodów w sądzie, ponieważ dane można łatwo sfałszować. Z drugiej strony informatyk śledczy musi mieć pełną wiedzę na temat wymagań prawnych, postępowania z dowodami i procedur dokumentacyjnych, aby przedstawić przekonujące dowody w sądzie.
Narzędzia badawcze
Skuteczność dochodzenia cyfrowego zależy wyłącznie od wiedzy eksperta z zakresu medycyny sądowej i doboru odpowiedniego narzędzia śledczego. Jeśli zastosowane narzędzie nie jest zgodne z określonymi standardami, to w sądzie dowody mogą zostać odrzucone przez sędziego.
Brak wiedzy technicznej wśród publiczności
Innym ograniczeniem jest to, że niektóre osoby nie są w pełni zaznajomione z informatyką śledczą; dlatego wiele osób nie rozumie tej dziedziny. Śledczy muszą mieć pewność, że przekażą swoje ustalenia sądom w taki sposób, aby pomóc wszystkim zrozumieć wyniki.
Koszt
Wytwarzanie dowodów cyfrowych i ich konserwacja jest bardzo kosztowne. Stąd ten proces może nie być wybrany przez wiele osób, których nie stać na koszty.
W poprzednim rozdziale poznaliśmy podstawy kryminalistyki cyfrowej, jej zalety i ograniczenia. W tym rozdziale zaznajomisz się z Pythonem, podstawowym narzędziem, którego używamy w tym cyfrowym dochodzeniu kryminalistycznym.
Dlaczego Python dla Digital Forensics?
Python jest popularnym językiem programowania i jest używany jako narzędzie do cyberbezpieczeństwa, testów penetracyjnych, a także cyfrowych dochodzeń kryminalistycznych. Wybierając Python jako narzędzie do kryminalistyki cyfrowej, nie potrzebujesz żadnego oprogramowania innych firm do wykonania zadania.
Niektóre z unikalnych cech języka programowania Python, które sprawiają, że dobrze pasuje do projektów z zakresu kryminalistyki cyfrowej, podano poniżej:
Simplicity of Syntax - Składnia Pythona jest prosta w porównaniu z innymi językami, co ułatwia naukę i stosowanie w kryminalistyce cyfrowej.
Comprehensive inbuilt modules - Wszechstronne wbudowane moduły Pythona są doskonałą pomocą przy przeprowadzaniu pełnego cyfrowego dochodzenia kryminalistycznego.
Help and Support - Będąc językiem programowania open source, Python cieszy się doskonałym wsparciem ze strony społeczności programistów i użytkowników.
Funkcje Pythona
Python, będący interpretowanym, interaktywnym i zorientowanym obiektowo językiem skryptowym wysokiego poziomu, zapewnia następujące funkcje -
Easy to Learn - Python jest językiem przyjaznym dla programistów i łatwym do nauczenia, ponieważ ma mniej słów kluczowych i najprostszą strukturę.
Expressive and Easy to read- Język Python ma charakter ekspresyjny; stąd jego kod jest bardziej zrozumiały i czytelny.
Cross-platform Compatible - Python jest językiem kompatybilnym z wieloma platformami, co oznacza, że może działać wydajnie na różnych platformach, takich jak UNIX, Windows i Macintosh.
Interactive Mode Programming - Możemy przeprowadzać interaktywne testowanie i debugowanie kodu, ponieważ Python obsługuje tryb interaktywny do programowania.
Provides Various Modules and Functions - Python ma dużą bibliotekę standardową, która pozwala nam korzystać z bogatego zestawu modułów i funkcji naszego skryptu.
Supports Dynamic Type Checking - Python obsługuje dynamiczne sprawdzanie typów i zapewnia dynamiczne typy danych bardzo wysokiego poziomu.
GUI Programming - Python obsługuje programowanie GUI w celu tworzenia graficznych interfejsów użytkownika.
Integration with other programming languages - Python można łatwo zintegrować z innymi językami programowania, takimi jak C, C ++, JAVA itp.
Instalowanie Pythona
Dystrybucja Pythona jest dostępna na różne platformy, takie jak Windows, UNIX, Linux i Mac. Musimy tylko pobrać kod binarny zgodnie z naszą platformą. W przypadku, gdy kod binarny dla dowolnej platformy nie jest dostępny, musimy mieć kompilator C, aby kod źródłowy mógł zostać skompilowany ręcznie.
Ta sekcja zapozna Cię z instalacją Pythona na różnych platformach
Instalacja Pythona w systemach Unix i Linux
Możesz wykonać poniższe kroki, aby zainstalować Pythona na komputerze z systemem Unix / Linux.
Step 1- Otwórz przeglądarkę internetową. Wpisz i wprowadź www.python.org/downloads/
Step 2 - Pobierz spakowany kod źródłowy dostępny dla systemów Unix / Linux.
Step 3 - Rozpakuj pobrane spakowane pliki.
Step 4 - Jeśli chcesz dostosować niektóre opcje, możesz edytować plik Modules/Setup file.
Step 5 - Użyj następujących poleceń, aby zakończyć instalację -
run ./configure script
make
make install
Po pomyślnym wykonaniu powyższych kroków Python zostanie zainstalowany w swojej standardowej lokalizacji /usr/local/bin i jego biblioteki pod adresem /usr/local/lib/pythonXX gdzie XX to wersja Pythona.
Instalacja Pythona w systemie Windows
Możemy wykonać następujące proste kroki, aby zainstalować Pythona na komputerze z systemem Windows.
Step 1- Otwórz przeglądarkę internetową. Wpisz i wprowadź www.python.org/downloads/
Step 2 - Pobierz instalator Windows python-XYZ.msi plik, gdzie XYZ to wersja, którą musimy zainstalować.
Step 3 - Teraz uruchom ten plik MSI po zapisaniu pliku instalatora na komputerze lokalnym.
Step 4 - Uruchom pobrany plik, co spowoduje wyświetlenie kreatora instalacji języka Python.
Instalacja języka Python na komputerze Macintosh
Aby zainstalować Python 3 na Mac OS X, musimy użyć instalatora pakietów o nazwie Homebrew.
Możesz użyć następującego polecenia, aby zainstalować Homebrew, jeśli nie masz go w swoim systemie -
$ ruby -e "$(curl -fsSL
https://raw.githubusercontent.com/Homebrew/install/master/install)"
Jeśli chcesz zaktualizować menedżera pakietów, możesz to zrobić za pomocą następującego polecenia -
$ brew update
Teraz użyj następującego polecenia, aby zainstalować Python3 w swoim systemie -
$ brew install python3
Ustawianie PATH
Musimy ustawić ścieżkę do instalacji Pythona i różni się to w przypadku platform takich jak UNIX, WINDOWS lub MAC.
Ustawienie ścieżki w systemie Unix / Linux
Możesz użyć następujących opcji, aby ustawić ścieżkę w systemie Unix / Linux -
If using csh shell - Rodzaj setenv PATH "$PATH:/usr/local/bin/python" a następnie naciśnij klawisz Enter.
If using bash shell (Linux) - Typ export ATH="$PATH:/usr/local/bin/python" a następnie naciśnij klawisz Enter.
If using sh or ksh shell - Rodzaj PATH="$PATH:/usr/local/bin/python" a następnie naciśnij klawisz Enter.
Ustawienie ścieżki w systemie Windows
Rodzaj path %path%;C:\Python w wierszu polecenia, a następnie naciśnij klawisz Enter.
Uruchamianie Pythona
Możesz wybrać dowolną z następujących trzech metod, aby uruchomić interpreter Pythona -
Metoda 1: użycie interaktywnego interpretera
System, który zapewnia interpreter wiersza poleceń lub powłokę, może być łatwo użyty do uruchomienia Pythona. Na przykład Unix, DOS itp. Możesz wykonać poniższe kroki, aby rozpocząć kodowanie w interaktywnym interprecie -
Step 1 - Wejdź python w wierszu poleceń.
Step 2 - Rozpocznij kodowanie od razu w interaktywnym tłumaczu, używając poleceń pokazanych poniżej -
$python # Unix/Linux
or
python% # Unix/Linux
or
C:> python # Windows/DOS
Metoda 2: użycie skryptu z wiersza polecenia
Możemy również wykonać skrypt Pythona z linii poleceń, wywołując interpreter w naszej aplikacji. Możesz użyć poleceń pokazanych poniżej -
$python script.py # Unix/Linux
or
python% script.py # Unix/Linux
or
C: >python script.py # Windows/DOS
Metoda 3: Zintegrowane środowisko programistyczne
Jeśli system ma aplikację GUI obsługującą Python, wówczas Python można uruchomić z tego środowiska GUI. Poniżej przedstawiono niektóre środowiska IDE dla różnych platform -
Unix IDE - UNIX ma IDLE IDE dla Pythona.
Windows IDE - Windows ma PythonWin, pierwszy interfejs Windows dla Pythona wraz z GUI.
Macintosh IDE - Macintosh ma IDLE IDE, które jest dostępne z głównej strony internetowej, do pobrania jako pliki MacBinary lub BinHex.
Teraz, gdy już wiesz, jak instalować i uruchamiać polecenia Pythona w systemie lokalnym, przejdźmy szczegółowo do koncepcji kryminalistyki. W tym rozdziale zostaną wyjaśnione różne koncepcje związane z obsługą artefaktów w kryminalistyce cyfrowej w języku Python.
Potrzeba tworzenia raportów
Proces kryminalistyki cyfrowej obejmuje raportowanie jako trzeci etap. Jest to jedna z najważniejszych części procesu kryminalistyki cyfrowej. Utworzenie raportu jest konieczne z następujących powodów -
Jest to dokument, w którym ekspert kryminalistyki cyfrowej przedstawia proces dochodzenia i jego ustalenia.
Inny egzaminator może odwołać się do dobrego cyfrowego raportu kryminalistycznego, aby osiągnąć ten sam wynik w tych samych repozytoriach.
Jest to dokument techniczny i naukowy, który zawiera fakty znalezione w wartościach 1 i 0 dowodów cyfrowych.
Ogólne wytyczne dotyczące tworzenia raportów
Raporty mają na celu dostarczenie czytelnikowi informacji i muszą zaczynać się od solidnych podstaw. śledczy mogą napotkać trudności w skutecznym przedstawieniu swoich ustaleń, jeśli raport zostanie przygotowany bez pewnych ogólnych wytycznych lub standardów. Poniżej przedstawiono kilka ogólnych wskazówek, których należy przestrzegać podczas tworzenia cyfrowych raportów kryminalistycznych -
Summary - Raport musi zawierać krótkie podsumowanie informacji, tak aby czytelnik mógł ustalić cel raportu.
Tools used - Należy wspomnieć o narzędziach, które posłużyły do prowadzenia procesu kryminalistyki cyfrowej, w tym o ich przeznaczeniu.
Repository - Załóżmy, że zbadaliśmy czyjś komputer, a następnie podsumowanie dowodów i analizę odpowiednich materiałów, takich jak e-mail, wewnętrzna historia wyszukiwania itp., A następnie musimy je uwzględnić w raporcie, aby sprawa mogła zostać jasno przedstawiona.
Recommendations for counsel - Raport musi zawierać zalecenia dla prawnika, aby kontynuował lub zaprzestał dochodzenia w oparciu o ustalenia w raporcie.
Tworzenie różnych typów raportów
W powyższej sekcji dowiedzieliśmy się o znaczeniu raportu w kryminalistyce cyfrowej wraz z wytycznymi dotyczącymi jego tworzenia. Niektóre formaty w Pythonie do tworzenia różnego rodzaju raportów zostały omówione poniżej -
Raporty CSV
Jednym z najpopularniejszych formatów wyjściowych raportów jest arkusz kalkulacyjny CSV. Możesz utworzyć plik CSV, aby utworzyć raport z przetworzonych danych za pomocą kodu Python, jak pokazano poniżej -
Najpierw zaimportuj przydatne biblioteki do pisania arkusza kalkulacyjnego -
from __future__ import print_function
import csv
import os
import sys
Teraz wywołaj następującą metodę -
Write_csv(TEST_DATA_LIST, ["Name", "Age", "City", "Job description"], os.getcwd())
Używamy następującej zmiennej globalnej do reprezentowania przykładowych typów danych -
TEST_DATA_LIST = [["Ram", 32, Bhopal, Manager],
["Raman", 42, Indore, Engg.],
["Mohan", 25, Chandigarh, HR],
["Parkash", 45, Delhi, IT]]
Następnie określmy sposób, w jaki należy przejść do dalszych operacji. Otwieramy plik w trybie „w” i ustawiamy argument słowa kluczowego nowej linii na pusty ciąg.
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)
Jeśli uruchomisz powyższy skrypt, otrzymasz następujące dane przechowywane w pliku report1.csv.
Nazwa | Wiek | Miasto | Przeznaczenie |
---|---|---|---|
Baran | 32 | Bhopal | Managerh |
Ramana | 42 | Indore | Engg |
Mohan | 25 | Chandigarh | HR |
Parkash | 45 | Delhi | TO |
Raporty programu Excel
Innym popularnym formatem wyjściowym raportów jest arkusz kalkulacyjny Excel (.xlsx). Możemy stworzyć tabelę, a także wykreślić wykres za pomocą programu Excel. Możemy utworzyć raport z przetworzonych danych w formacie Excel przy użyciu kodu Pythona, jak pokazano poniżej
Najpierw zaimportuj moduł XlsxWriter do tworzenia arkusza kalkulacyjnego -
import xlsxwriter
Teraz utwórz obiekt skoroszytu. W tym celu musimy użyć konstruktora Workbook ().
workbook = xlsxwriter.Workbook('report2.xlsx')
Teraz utwórz nowy arkusz za pomocą modułu add_worksheet ().
worksheet = workbook.add_worksheet()
Następnie zapisz następujące dane w arkuszu -
report2 = (['Ram', 32, ‘Bhopal’],['Mohan',25, ‘Chandigarh’] ,['Parkash',45, ‘Delhi’])
row = 0
col = 0
Możesz iterować te dane i zapisać je w następujący sposób -
for item, cost in (a):
worksheet.write(row, col, item)
worksheet.write(row, col+1, cost)
row + = 1
Teraz zamknijmy ten plik Excel za pomocą metody close ().
workbook.close()
Powyższy skrypt utworzy plik Excel o nazwie report2.xlsx zawierający następujące dane -
Baran | 32 | Bhopal |
Mohan | 25 | Chandigarh |
Parkash | 45 | Delhi |
Investigation Acquisition Media
Dla badacza ważne jest posiadanie szczegółowych notatek dochodzeniowych, aby dokładnie przypomnieć wyniki lub zebrać wszystkie części dochodzenia. Zrzut ekranu jest bardzo przydatny do śledzenia kroków podjętych w ramach konkretnego dochodzenia. Za pomocą następującego kodu Pythona możemy zrobić zrzut ekranu i zapisać go na dysku twardym do wykorzystania w przyszłości.
Najpierw zainstaluj moduł Pythona o nazwie pyscreenshot za pomocą następującego polecenia -
Pip install pyscreenshot
Teraz zaimportuj niezbędne moduły, jak pokazano -
import pyscreenshot as ImageGrab
Użyj poniższego wiersza kodu, aby uzyskać zrzut ekranu -
image = ImageGrab.grab()
Użyj poniższego wiersza kodu, aby zapisać zrzut ekranu w podanej lokalizacji -
image.save('d:/image123.png')
Teraz, jeśli chcesz wyświetlić zrzut ekranu jako wykres, możesz użyć następującego kodu Pythona -
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()
W tym rozdziale wyjaśniono kryminalistykę cyfrową Pythona na urządzeniach mobilnych i związane z nią pojęcia.
Wprowadzenie
Kryminalistyka urządzeń mobilnych jest tą gałęzią kryminalistyki cyfrowej, która zajmuje się pozyskiwaniem i analizą urządzeń mobilnych w celu odzyskania cyfrowych dowodów zainteresowania śledczego. Branża ta różni się od informatyki śledczej, ponieważ urządzenia mobilne mają wbudowany system komunikacji, który jest przydatny do dostarczania przydatnych informacji związanych z lokalizacją.
Chociaż korzystanie ze smartfonów rośnie z dnia na dzień w kryminalistyce cyfrowej, nadal uważa się je za niestandardowe ze względu na swoją niejednorodność. Z drugiej strony sprzęt komputerowy, taki jak dysk twardy, jest uważany za standardowy i rozwijany jako dyscyplina stabilna. W branży kryminalistyki cyfrowej toczy się wiele dyskusji na temat technik stosowanych w przypadku niestandardowych urządzeń, które mają przejściowe dowody, takie jak smartfony.
Artefakty, które można wydobyć z urządzeń mobilnych
Nowoczesne urządzenia mobilne zawierają dużo informacji cyfrowych w porównaniu ze starszymi telefonami posiadającymi jedynie dziennik połączeń lub wiadomości SMS. W ten sposób urządzenia mobilne mogą dostarczyć badaczom wielu informacji na temat ich użytkowników. Niektóre artefakty, które można wyodrębnić z urządzeń mobilnych, są wymienione poniżej -
Messages - Są to przydatne artefakty, które mogą ujawnić stan umysłu właściciela, a nawet mogą przekazać badaczowi nieznane wcześniej informacje.
Location History- Dane historii lokalizacji są użytecznym artefaktem, który może zostać wykorzystany przez śledczych do potwierdzenia konkretnej lokalizacji osoby.
Applications Installed - Uzyskując dostęp do rodzaju zainstalowanych aplikacji, badacz uzyskuje wgląd w nawyki i sposób myślenia użytkowników mobilnych.
Źródła dowodów i przetwarzanie w Pythonie
Smartfony mają bazy danych SQLite i pliki PLIST jako główne źródła dowodów. W tej sekcji będziemy przetwarzać źródła dowodów w Pythonie.
Analizowanie plików PLIST
PLISTA (lista właściwości) to elastyczny i wygodny format do przechowywania danych aplikacji, zwłaszcza na urządzeniach iPhone. Używa rozszerzenia.plist. Tego rodzaju pliki służą do przechowywania informacji o pakietach i aplikacjach. Może mieć dwa formaty:XML i binary. Poniższy kod Pythona otworzy i odczyta plik PLIST. Zauważ, że zanim przejdziemy do tego, musimy stworzyć własnyInfo.plist plik.
Najpierw zainstaluj bibliotekę innej firmy o nazwie biplist następującą komendą -
Pip install biplist
Teraz zaimportuj kilka przydatnych bibliotek do przetwarzania plików plist -
import biplist
import os
import sys
Teraz użyj następującego polecenia w głównej metodzie, aby wczytać plik plist do zmiennej -
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)
Teraz możemy odczytać dane z konsoli lub bezpośrednio wydrukować je z tej zmiennej.
Bazy danych SQLite
SQLite służy jako podstawowe repozytorium danych na urządzeniach mobilnych. SQLite - biblioteka procesowa, która implementuje niezależny, pozbawiony serwera, bezkonfiguracyjny, transakcyjny silnik bazy danych SQL. Jest to baza danych, która jest skonfigurowana na zero, nie musisz jej konfigurować w swoim systemie, w przeciwieństwie do innych baz danych.
Jeśli jesteś nowicjuszem lub nie znasz baz danych SQLite, możesz skorzystać z łącza www.tutorialspoint.com/sqlite/index.htm Dodatkowo możesz skorzystać z łącza www.tutorialspoint.com/sqlite/sqlite_python.htm na wypadek, gdybyś chciał poznaj szczegóły SQLite w Pythonie.
Podczas kryminalistyki mobilnej możemy wchodzić w interakcje z sms.db plik urządzenia mobilnego i może wyodrębnić cenne informacje z messagestół. Python ma wbudowaną bibliotekę o nazwiesqlite3do połączenia z bazą danych SQLite. Możesz zaimportować to samo za pomocą następującego polecenia -
import sqlite3
Teraz za pomocą następującego polecenia możemy połączyć się z bazą danych, powiedzmy sms.db w przypadku urządzeń mobilnych -
Conn = sqlite3.connect(‘sms.db’)
C = conn.cursor()
Tutaj C jest obiektem kursora, za pomocą którego możemy wchodzić w interakcje z bazą danych.
Teraz załóżmy, że jeśli chcemy wykonać określone polecenie, powiedzmy, aby uzyskać szczegóły z pliku abc table, można to zrobić za pomocą następującego polecenia -
c.execute(“Select * from abc”)
c.close()
Wynik powyższego polecenia zostanie zapisany w pliku cursorobiekt. Podobnie możemy użyćfetchall() metody, aby zrzucić wynik do zmiennej, którą możemy manipulować.
Możemy użyć następującego polecenia, aby pobrać dane nazw kolumn tabeli wiadomości w formacie sms.db -
c.execute(“pragma table_info(message)”)
table_data = c.fetchall()
columns = [x[1] for x in table_data
Zauważ, że używamy tutaj polecenia SQLite PRAGMA, które jest specjalnym poleceniem używanym do sterowania różnymi zmiennymi środowiskowymi i flagami stanu w środowisku SQLite. W powyższym poleceniufetchall()metoda zwraca krotkę wyników. Nazwa każdej kolumny jest przechowywana w pierwszym indeksie każdej krotki.
Teraz za pomocą następującego polecenia możemy zapytać tabelę o wszystkie jej dane i zapisać je w zmiennej o nazwie data_msg -
c.execute(“Select * from message”)
data_msg = c.fetchall()
Powyższe polecenie zapisze dane w zmiennej, a dalej możemy również zapisać powyższe dane w pliku CSV za pomocą csv.writer() metoda.
Kopie zapasowe iTunes
Kryminalistyka mobilna iPhone'a może być wykonywana na kopiach zapasowych utworzonych przez iTunes. Eksperci kryminalistyczni polegają na analizie logicznych kopii zapasowych iPhone'a uzyskanych za pośrednictwem iTunes. Protokół AFC (połączenie plików Apple) jest używany przez iTunes do wykonywania kopii zapasowej. Poza tym proces tworzenia kopii zapasowej nie modyfikuje niczego na iPhonie z wyjątkiem rekordów klucza Escrow.
Teraz pojawia się pytanie, dlaczego ważne jest, aby ekspert kryminalistyki cyfrowej rozumiał techniki wykonywania kopii zapasowych iTunes? Jest to ważne na wypadek, gdybyśmy uzyskali dostęp do komputera podejrzanego zamiast do iPhone'a bezpośrednio, ponieważ gdy komputer jest używany do synchronizacji z iPhonem, wówczas kopia zapasowa większości informacji z iPhone'a prawdopodobnie zostanie zapisana na komputerze.
Proces tworzenia kopii zapasowej i jej lokalizacja
Za każdym razem, gdy kopia zapasowa produktu Apple jest zapisywana na komputerze, jest zsynchronizowana z iTunes i będzie istniał określony folder z unikalnym identyfikatorem urządzenia. W najnowszym formacie kopii zapasowej pliki są przechowywane w podfolderach zawierających pierwsze dwa znaki szesnastkowe nazwy pliku. Z tych plików kopii zapasowych znajdują się pliki, takie jak info.plist, które są przydatne wraz z bazą danych o nazwie Manifest.db. W poniższej tabeli przedstawiono lokalizacje kopii zapasowych, które różnią się w zależności od systemów operacyjnych kopii zapasowych iTunes -
OS | Lokalizacja kopii zapasowej |
---|---|
Win7 | C: \ Users \ [nazwa użytkownika] \ AppData \ Roaming \ AppleComputer \ MobileSync \ Backup \ |
MAC OS X | ~ / Library / Application Support / MobileSync / Backup / |
Aby przetworzyć kopię zapasową iTunes za pomocą Pythona, musimy najpierw zidentyfikować wszystkie kopie zapasowe w lokalizacji kopii zapasowej zgodnie z naszym systemem operacyjnym. Następnie będziemy iterować przez każdą kopię zapasową i odczytać bazę danych Manifest.db.
Teraz, z pomocą następującego kodu Pythona, możemy zrobić to samo -
Najpierw zaimportuj niezbędne biblioteki w następujący sposób -
from __future__ import print_function
import argparse
import logging
import os
from shutil import copyfile
import sqlite3
import sys
logger = logging.getLogger(__name__)
Teraz podaj dwa argumenty pozycyjne, a mianowicie INPUT_DIR i OUTPUT_DIR, które reprezentują kopię zapasową iTunes i żądany folder wyjściowy -
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()
Teraz skonfiguruj dziennik w następujący sposób -
if args.v:
logger.setLevel(logging.DEBUG)
else:
logger.setLevel(logging.INFO)
Teraz skonfiguruj format wiadomości dla tego dziennika w następujący sposób -
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)
Poniższy wiersz kodu utworzy niezbędne foldery dla żądanego katalogu wyjściowego przy użyciu os.makedirs() funkcja -
if not os.path.exists(args.OUTPUT_DIR):
os.makedirs(args.OUTPUT_DIR)
Teraz przekaż podane katalogi wejściowe i wyjściowe do funkcji main () w następujący sposób -
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)
Teraz pisz main() funkcja, która będzie dalej wywoływać backup_summary() funkcja identyfikacji wszystkich kopii zapasowych obecnych w folderze wejściowym -
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
Teraz wydrukuj podsumowanie każdej kopii zapasowej na konsoli w następujący sposób -
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]))
Teraz zrzuć zawartość pliku Manifest.db do zmiennej o nazwie 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
Teraz zdefiniujmy funkcję, która przejmie ścieżkę katalogu kopii zapasowej -
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
Teraz używając SQLite3 połączymy się z bazą danych za pomocą kursora o nazwie 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)
Teraz zdefiniuj create_files() metoda w następujący sposób -
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)
Teraz iteruj przez każdy klucz w db_items słownik -
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)
Teraz użyj shutil.copyfile() metoda kopiowania pliku kopii zapasowej w następujący sposób -
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"))
Za pomocą powyższego skryptu Python możemy pobrać zaktualizowaną strukturę plików kopii zapasowej w naszym folderze wyjściowym. Możemy użyćpycrypto Biblioteka pythona do odszyfrowania kopii zapasowych.
Wi - Fi
Urządzenia mobilne mogą być używane do łączenia się ze światem zewnętrznym, łącząc się przez sieci Wi-Fi, które są dostępne wszędzie. Czasami urządzenie łączy się automatycznie z otwartymi sieciami.
W przypadku iPhone'a lista otwartych połączeń Wi-Fi, z którymi urządzenie się połączyło, znajduje się w pliku PLIST o nazwie com.apple.wifi.plist. Ten plik będzie zawierał identyfikator SSID Wi-Fi, identyfikator BSSID i czas połączenia.
Musimy wyodrębnić szczegóły Wi-Fi ze standardowego raportu Cellebrite XML za pomocą Pythona. W tym celu musimy skorzystać z API od Wireless Geographic Logging Engine (WIGLE), popularnej platformy, która może służyć do wyszukiwania lokalizacji urządzenia za pomocą nazw sieci Wi-Fi.
Możemy skorzystać z biblioteki Pythona o nazwie requestsdostęp do API z WIGLE. Można go zainstalować w następujący sposób -
pip install requests
API firmy WIGLE
Musimy zarejestrować się na stronie WIGLE https://wigle.net/accountaby otrzymać darmowe API od WIGLE. Skrypt w języku Python służący do uzyskiwania informacji o urządzeniu użytkownika i jego połączeniu przez API WIGEL został omówiony poniżej -
Najpierw zaimportuj następujące biblioteki do obsługi różnych rzeczy -
from __future__ import print_function
import argparse
import csv
import os
import sys
import xml.etree.ElementTree as ET
import requests
Teraz podaj dwa argumenty pozycyjne, a mianowicie INPUT_FILE i OUTPUT_CSV który będzie reprezentował odpowiednio plik wejściowy z adresem MAC Wi-Fi i żądanym wyjściowym plikiem 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()
Teraz kolejne wiersze kodu sprawdzą, czy plik wejściowy istnieje i czy jest plikiem. Jeśli nie, zamyka skrypt -
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(":")
Teraz przekaż argument do main w następujący sposób -
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)
Teraz przeanalizujemy plik XML w następujący sposób -
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()
Teraz iteruj po elemencie potomnym katalogu głównego w następujący sposób -
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
Teraz sprawdzimy, czy ciąg „ssid” jest obecny w tekście wartości, czy nie -
if "SSID" in value.text:
bssid, ssid = value.text.split("\t")
bssid = bssid[7:]
ssid = ssid[6:]
Teraz musimy dodać BSSID, SSID i znacznik czasu do słownika Wi-Fi w następujący sposób -
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
Parser tekstu, który jest znacznie prostszy niż parser XML, jest pokazany poniżej -
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
Teraz użyjmy modułu requestów WIGLE APIdzwoni i musisz przejść do query_wigle() metoda -
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()
W rzeczywistości istnieje dzienny limit wywołań API WIGLE, jeśli ten limit przekracza, musi pokazywać następujący błąd -
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)
Teraz użyjemy prep_output() metoda spłaszczania słownika w łatwe do zapisania fragmenty -
def prep_output(output, data):
csv_data = {}
google_map = https://www.google.com/maps/search/
Teraz uzyskaj dostęp do wszystkich zgromadzonych przez nas danych w następujący sposób -
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"])
Teraz możemy zapisać wynik w pliku CSV, tak jak zrobiliśmy to we wcześniejszych skryptach w tym rozdziale, używając write_csv() funkcjonować.
W tym rozdziale dowiemy się szczegółowo o badaniu osadzonych metadanych za pomocą cyfrowej analizy śledczej w języku Python.
Wprowadzenie
Osadzone metadane to informacje o danych przechowywanych w tym samym pliku, w którym znajduje się obiekt opisany przez te dane. Innymi słowy, jest to informacja o zasobie cyfrowym przechowywanym w samym pliku cyfrowym. Jest zawsze powiązany z plikiem i nigdy nie może zostać rozdzielony.
W przypadku kryminalistyki cyfrowej nie możemy wyodrębnić wszystkich informacji o konkretnym pliku. Z drugiej strony osadzone metadane mogą dostarczyć nam informacji krytycznych dla dochodzenia. Na przykład metadane pliku tekstowego mogą zawierać informacje o autorze, jego długości, dacie powstania, a nawet krótkie podsumowanie tego dokumentu. Obraz cyfrowy może zawierać metadane, takie jak długość obrazu, czas otwarcia migawki itp.
Artefakty zawierające atrybuty metadanych i ich wyodrębnianie
W tej sekcji dowiemy się o różnych artefaktach zawierających atrybuty metadanych i procesie ich wyodrębniania za pomocą Pythona.
Dźwięk i wideo
Są to dwa bardzo popularne artefakty, które mają osadzone metadane. Te metadane można pobrać w celu zbadania.
Możesz użyć następującego skryptu Python, aby wyodrębnić wspólne atrybuty lub metadane z pliku audio lub MP3 i pliku wideo lub MP4.
Zwróć uwagę, że w przypadku tego skryptu musimy zainstalować bibliotekę Pythona innej firmy o nazwie mutagen, która pozwala nam wyodrębniać metadane z plików audio i wideo. Można go zainstalować za pomocą następującego polecenia -
pip install mutagen
Niektóre z przydatnych bibliotek, które musimy zaimportować dla tego skryptu Pythona, są następujące -
from __future__ import print_function
import argparse
import json
import mutagen
Program obsługi wiersza poleceń przyjmuje jeden argument, który reprezentuje ścieżkę do plików MP3 lub MP4. Następnie użyjemymutagen.file() metoda otwierania dojścia do pliku w następujący sposób -
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)
Teraz musimy użyć dwóch uchwytów, jednego do wyodrębnienia danych z MP3, a drugiego do wyodrębnienia danych z pliku MP4. Możemy zdefiniować te uchwyty w następujący sposób -
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'))
Teraz musimy powtórzyć ten plik MP4 w następujący sposób -
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))
Powyższy skrypt dostarczy nam dodatkowych informacji o plikach MP3 oraz MP4.
Obrazy
Obrazy mogą zawierać różnego rodzaju metadane w zależności od formatu pliku. Jednak większość obrazów zawiera informacje GPS. Możemy wyodrębnić te informacje GPS za pomocą bibliotek Pythona innych firm. Możesz użyć następującego skryptu Python, aby zrobić to samo -
Najpierw pobierz bibliotekę Pythona innej firmy o nazwie Python Imaging Library (PIL) w następujący sposób -
pip install pillow
Pomoże nam to wyodrębnić metadane z obrazów.
Możemy również zapisać szczegóły GPS osadzone w obrazach do pliku KML, ale w tym celu musimy pobrać bibliotekę Pythona innej firmy o nazwie simplekml w następujący sposób -
pip install simplekml
W tym skrypcie najpierw musimy zaimportować następujące biblioteki -
from __future__ import print_function
import argparse
from PIL import Image
from PIL.ExifTags import TAGS
import simplekml
import sys
Teraz program obsługi wiersza poleceń zaakceptuje jeden argument pozycyjny, który zasadniczo reprezentuje ścieżkę pliku zdjęć.
parser = argparse.ArgumentParser('Metadata from images')
parser.add_argument('PICTURE_FILE', help = "Path to picture")
args = parser.parse_args()
Teraz musimy określić adresy URL, które będą wypełniać informacje o współrzędnych. Adresy URL togmaps i open_maps. Potrzebujemy również funkcji konwertującej współrzędną krotki stopniominut sekundy (DMS) dostarczoną przez bibliotekę PIL na dziesiętną. Można to zrobić w następujący sposób -
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
Teraz użyjemy image.open() funkcji, aby otworzyć plik jako obiekt 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
Po znalezieniu GPSInfo tag, zapiszemy odniesienie GPS i przetworzymy współrzędne z process_coords() metoda.
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
Teraz zainicjuj kml obiekt z simplekml biblioteka w następujący sposób -
kml = simplekml.Kml()
kml.newpoint(name = args.PICTURE_FILE, coords = [(lon, lat)])
kml.save(args.PICTURE_FILE + ".kml")
Możemy teraz wydrukować współrzędne z przetworzonych informacji w następujący sposób -
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"))
Dokumenty PDF
Dokumenty PDF zawierają wiele różnych mediów, w tym obrazy, tekst, formularze itp. Kiedy wyodrębniamy osadzone metadane w dokumentach PDF, możemy uzyskać dane wynikowe w formacie zwanym Extensible Metadata Platform (XMP). Możemy wyodrębnić metadane za pomocą następującego kodu Pythona -
Najpierw zainstaluj bibliotekę Pythona innej firmy o nazwie PyPDF2czytać metadane zapisane w formacie XMP. Można go zainstalować w następujący sposób -
pip install PyPDF2
Teraz zaimportuj następujące biblioteki, aby wyodrębnić metadane z plików PDF -
from __future__ import print_function
from argparse import ArgumentParser, FileType
import datetime
from PyPDF2 import PdfFileReader
import sys
Teraz program obsługi wiersza poleceń zaakceptuje jeden argument pozycyjny, który zasadniczo reprezentuje ścieżkę pliku PDF.
parser = argparse.ArgumentParser('Metadata from PDF')
parser.add_argument('PDF_FILE', help='Path to PDF file',type=FileType('rb'))
args = parser.parse_args()
Teraz możemy użyć getXmpMetadata() metoda dostarczania obiektu zawierającego dostępne metadane w następujący sposób -
pdf_file = PdfFileReader(args.PDF_FILE)
xmpm = pdf_file.getXmpMetadata()
if xmpm is None:
print("No XMP metadata found in document.")
sys.exit()
Możemy użyć custom_print() metoda wyodrębniania i drukowania odpowiednich wartości, takich jak tytuł, twórca, współpracownik itp. w następujący sposób -
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)
Możemy też zdefiniować custom_print() w przypadku, gdy plik PDF jest tworzony przy użyciu wielu programów w następujący sposób -
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)))
Możemy również wyodrębnić dowolną inną niestandardową właściwość zapisaną przez oprogramowanie w następujący sposób -
if xmpm.custom_properties:
print("Custom Properties:")
for k, v in xmpm.custom_properties.items():
print("\t{}: {}".format(k, v))
Powyższy skrypt odczyta dokument PDF i wydrukuje metadane zapisane w formacie XMP, w tym niektóre niestandardowe właściwości przechowywane przez oprogramowanie, za pomocą którego utworzono ten plik PDF.
Pliki wykonywalne systemu Windows
Czasami możemy napotkać podejrzany lub nieautoryzowany plik wykonywalny. Ale dla celów badania może być przydatne ze względu na osadzone metadane. Możemy uzyskać takie informacje, jak jego lokalizacja, przeznaczenie i inne atrybuty, takie jak producent, data kompilacji itp. Za pomocą następującego skryptu w Pythonie możemy uzyskać datę kompilacji, przydatne dane z nagłówków oraz importowane i eksportowane symbole.
W tym celu najpierw zainstaluj bibliotekę Pythona innej firmy pefile. Można to zrobić w następujący sposób -
pip install pefile
Po pomyślnym zainstalowaniu zaimportuj następujące biblioteki w następujący sposób -
from __future__ import print_function
import argparse
from datetime import datetime
from pefile import PE
Teraz program obsługi wiersza poleceń zaakceptuje jeden argument pozycyjny, który zasadniczo reprezentuje ścieżkę do pliku wykonywalnego. Możesz także wybrać styl wyjścia, niezależnie od tego, czy potrzebujesz go w sposób szczegółowy i rozwlekły, czy w sposób uproszczony. W tym celu musisz podać opcjonalny argument, jak pokazano poniżej -
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()
Teraz załadujemy wejściowy plik wykonywalny za pomocą klasy PE. Zrzucimy również dane wykonywalne do obiektu słownika przy użyciudump_dict() metoda.
pe = PE(args.EXE_FILE)
ped = pe.dump_dict()
Możemy wyodrębnić podstawowe metadane pliku, takie jak osadzone autorstwo, wersja i czas kompilacji, korzystając z kodu pokazanego poniżej -
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()))
Możemy wyodrębnić przydatne dane z nagłówków w następujący sposób -
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'])
)
Teraz wypakuj listę importu i eksportu z plików wykonywalnych, jak pokazano poniżej -
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()
Teraz drukuj exports, names i addresses używając kodu, jak pokazano poniżej -
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)))
Powyższy skrypt wyodrębni podstawowe metadane, informacje z nagłówków z plików wykonywalnych systemu Windows.
Metadane dokumentów pakietu Office
Większość pracy na komputerze wykonywana jest w trzech aplikacjach MS Office - Word, PowerPoint i Excel. Pliki te posiadają ogromne metadane, które mogą ujawniać interesujące informacje o ich autorstwie i historii.
Zwróć uwagę, że metadane z formatu Word (.docx), Excel (.xlsx) i PowerPoint (.pptx) z 2007 roku są przechowywane w pliku XML. Możemy przetwarzać te pliki XML w Pythonie za pomocą następującego skryptu Pythona pokazanego poniżej -
Najpierw zaimportuj wymagane biblioteki, jak pokazano poniżej -
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()
Teraz sprawdź, czy plik jest plikiem ZIP. W przeciwnym razie zgłoś błąd. Teraz otwórz plik i wyodrębnij kluczowe elementy do przetworzenia, używając następującego kodu -
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'))
Teraz utwórz słownik, aby zainicjować wyodrębnianie metadanych -
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'
}
Posługiwać się iterchildren() metoda dostępu do każdego z tagów w pliku 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))
Podobnie zrób to dla pliku app.xml, który zawiera informacje statystyczne o zawartości dokumentu -
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))
Teraz po uruchomieniu powyższego skryptu możemy uzyskać różne szczegóły dotyczące konkretnego dokumentu. Należy pamiętać, że możemy zastosować ten skrypt tylko w dokumentach pakietu Office 2007 lub nowszych wersji.
W tym rozdziale wyjaśniono podstawy związane z przeprowadzaniem analizy kryminalistycznej sieci przy użyciu języka Python.
Zrozumienie Network Forensics
Kryminalistyka sieci to gałąź kryminalistyki cyfrowej, która zajmuje się monitorowaniem i analizą ruchu w sieci komputerowej, zarówno lokalnej, jak i WAN (sieci rozległej), w celu gromadzenia informacji, gromadzenia dowodów lub wykrywania włamań. Kryminalistyka sieci odgrywa kluczową rolę w badaniu przestępstw cyfrowych, takich jak kradzież własności intelektualnej lub wyciek informacji. Obraz komunikacji sieciowej pomaga badaczowi rozwiązać kilka kluczowych pytań w następujący sposób:
Jakie strony internetowe odwiedzono?
Jakie treści zostały przesłane do naszej sieci?
Jakie treści zostały pobrane z naszej sieci?
Do jakich serwerów uzyskuje się dostęp?
Czy ktoś wysyła poufne informacje poza zapory firmowe?
Internetowa wyszukiwarka dowodów (IEF)
IEF to cyfrowe narzędzie kryminalistyczne do znajdowania, analizowania i przedstawiania dowodów cyfrowych znajdujących się na różnych mediach cyfrowych, takich jak komputer, smartfony, tablety itp. Jest bardzo popularne i używane przez tysiące specjalistów medycyny sądowej.
Korzystanie z IEF
Ze względu na swoją popularność IEF jest w dużym stopniu używany przez specjalistów medycyny sądowej. Niektóre zastosowania IEF są następujące:
Ze względu na potężne możliwości wyszukiwania służy do jednoczesnego przeszukiwania wielu plików lub nośników danych.
Służy również do odzyskiwania usuniętych danych z nieprzydzielonej przestrzeni pamięci RAM za pomocą nowych technik rzeźbienia.
Jeśli badacze chcą odbudować strony internetowe w ich oryginalnym formacie w dniu ich otwarcia, mogą użyć IEF.
Służy również do wyszukiwania woluminów dysków logicznych lub fizycznych.
Zrzuty raportów z IEF do CSV przy użyciu Pythona
IEF przechowuje dane w bazie danych SQLite, a następujący skrypt Python dynamicznie identyfikuje tabele wyników w bazie danych IEF i zrzuca je do odpowiednich plików CSV.
Ten proces odbywa się w krokach pokazanych poniżej
Najpierw wygeneruj bazę wyników IEF, która będzie plikiem bazy danych SQLite z rozszerzeniem .db.
Następnie przeprowadź zapytanie do tej bazy danych, aby zidentyfikować wszystkie tabele.
Na koniec zapisz te tabele wyników w oddzielnym pliku CSV.
Kod w Pythonie
Zobaczmy, jak w tym celu wykorzystać kod Pythona -
W przypadku skryptu Python zaimportuj niezbędne biblioteki w następujący sposób -
from __future__ import print_function
import argparse
import csv
import os
import sqlite3
import sys
Teraz musimy podać ścieżkę do pliku bazy danych 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()
Teraz potwierdzimy istnienie bazy danych IEF w następujący sposób -
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)
Teraz, tak jak to zrobiliśmy we wcześniejszych skryptach, połącz się z bazą danych SQLite w następujący sposób, aby wykonać zapytania za pomocą kursora -
def main(database, out_directory):
print("[+] Connecting to SQLite database")
conn = sqlite3.connect(database)
c = conn.cursor()
Następujące wiersze kodu pobiorą nazwy tabel z bazy danych -
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')]
Teraz wybierzemy wszystkie dane z tabeli i za pomocą fetchall() metoda na obiekcie kursora zapiszemy listę krotek zawierających dane tabeli w całości w zmiennej -
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()
Teraz, używając CSV_Writer() metodę zapiszemy zawartość w pliku 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)
Powyższy skrypt pobierze wszystkie dane z tabel bazy danych IEF i zapisze zawartość do wybranego przez nas pliku CSV.
Praca z danymi w pamięci podręcznej
Z bazy danych wyników IEF możemy pobrać więcej informacji, które niekoniecznie są obsługiwane przez sam IEF. Możemy pobrać dane z pamięci podręcznej, produkt bi do celów informacyjnych, od dostawców usług poczty elektronicznej, takich jak Yahoo, Google itp., Korzystając z bazy danych wyników IEF.
Poniżej znajduje się skrypt Pythona do uzyskiwania dostępu do informacji z pamięci podręcznej z poczty Yahoo, dostępnej w przeglądarce Google Chrome, przy użyciu bazy danych IEF. Zwróć uwagę, że kroki byłyby mniej więcej takie same, jak w ostatnim skrypcie Pythona.
Najpierw zaimportuj niezbędne biblioteki dla Pythona w następujący sposób -
from __future__ import print_function
import argparse
import csv
import os
import sqlite3
import sys
import json
Teraz podaj ścieżkę do pliku bazy danych IEF wraz z dwoma argumentami pozycyjnymi akceptowanymi przez program obsługi wiersza poleceń, tak jak to zrobiono w ostatnim skrypcie -
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()
Teraz potwierdź istnienie bazy danych IEF w następujący sposób -
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)
Teraz nawiąż połączenie z bazą danych SQLite w następujący sposób, aby wykonać zapytania za pomocą kursora -
def main(database, out_csv):
print("[+] Connecting to SQLite database")
conn = sqlite3.connect(database)
c = conn.cursor()
Możesz użyć następujących wierszy kodu, aby pobrać wystąpienia rekordu pamięci podręcznej kontaktów Yahoo Mail -
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)
Teraz lista krotek zwróconych z powyższego zapytania do zapisania w zmiennej w następujący sposób -
contact_cache = c.fetchall()
contact_data = process_contacts(contact_cache)
write_csv(contact_data, out_csv)
Zauważ, że tutaj użyjemy dwóch metod, a mianowicie process_contacts() do konfigurowania listy wyników, a także iteracji przez każdy rekord pamięci podręcznej kontaktów i json.loads() do przechowywania danych JSON wyodrębnionych z tabeli w zmiennej w celu dalszej manipulacji -
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"]])
Teraz w przypadku firmy, tytułu i notatek używana jest metoda get, jak pokazano poniżej -
company = c.get("company", "")
title = c.get("jobTitle", "")
notes = c.get("notes", "")
Teraz dodajmy listę metadanych i wyodrębnionych elementów danych do listy wyników w następujący sposób -
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
Teraz, używając CSV_Writer() metodę, zapiszemy zawartość w pliku 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)
Za pomocą powyższego skryptu możemy przetwarzać buforowane dane z poczty Yahoo za pomocą bazy danych IEF.
Poprzedni rozdział zajmował się niektórymi koncepcjami kryminalistyki sieci przy użyciu Pythona. W tym rozdziale przyjrzyjmy się dokładniej kryminalistyce sieci przy użyciu Pythona.
Zachowanie strony internetowej z piękną zupą
World Wide Web (WWW) to wyjątkowe źródło informacji. Jednak jego dziedzictwo jest bardzo zagrożone ze względu na utratę treści w alarmującym tempie. Szereg instytucji zajmujących się dziedzictwem kulturowym i akademickich, organizacji non-profit i prywatnych przedsiębiorstw zbadało związane z tym kwestie i przyczyniło się do opracowania technicznych rozwiązań archiwizacji internetowej.
Konserwacja stron internetowych lub archiwizacja w sieci to proces gromadzenia danych z sieci WWW, zapewniający przechowywanie danych w archiwum i udostępnianie ich przyszłym badaczom, historykom i opinii publicznej. Zanim przejdziemy dalej do konserwacji strony internetowej, omówmy kilka ważnych kwestii związanych z zachowaniem strony internetowej, jak podano poniżej -
Change in Web Resources - Zasoby internetowe zmieniają się codziennie, co jest wyzwaniem dla zachowania stron internetowych.
Large Quantity of Resources - Kolejną kwestią związaną z konserwacją stron internetowych jest duża ilość zasobów, które należy zachować.
Integrity - Strony internetowe muszą być chronione przed nieautoryzowanymi zmianami, kasowaniem lub usuwaniem w celu ochrony ich integralności.
Dealing with multimedia data - Zachowując strony internetowe, musimy również zajmować się danymi multimedialnymi, a te mogą powodować problemy.
Providing access - Oprócz ochrony, należy również rozwiązać kwestię zapewnienia dostępu do zasobów sieciowych i rozwiązywania problemów własności.
W tym rozdziale będziemy używać biblioteki Pythona o nazwie Beautiful Soup do zachowania strony internetowej.
Czym jest piękna zupa?
Beautiful Soup to biblioteka Pythona do pobierania danych z plików HTML i XML. Może być używany zurlibponieważ potrzebuje danych wejściowych (dokumentu lub adresu URL), aby utworzyć obiekt zupy, ponieważ nie może pobrać samej strony internetowej. Możesz dowiedzieć się o tym szczegółowo na stronie www.crummy.com/software/BeautifulSoup/bs4/doc/
Pamiętaj, że przed użyciem musimy zainstalować bibliotekę innej firmy za pomocą następującego polecenia -
pip install bs4
Następnie za pomocą menedżera pakietów Anaconda możemy zainstalować Beautiful Soup w następujący sposób -
conda install -c anaconda beautifulsoup4
Skrypt Pythona do ochrony stron internetowych
Skrypt w Pythonie do zachowywania stron internetowych przy użyciu biblioteki innej firmy o nazwie Beautiful Soup jest omówiony tutaj -
Najpierw zaimportuj wymagane biblioteki w następujący sposób -
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__)
Zwróć uwagę, że ten skrypt pobierze dwa argumenty pozycyjne, jeden to adres URL, który ma zostać zachowany, a drugi to żądany katalog wyjściowy, jak pokazano poniżej -
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()
Teraz skonfiguruj rejestrowanie dla skryptu, określając procedurę obsługi pliku i strumienia do zapętlenia i udokumentuj proces pozyskiwania, jak pokazano -
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)
Teraz wykonajmy walidację danych wejściowych w wybranym katalogu wyjściowym w następujący sposób -
if not os.path.exists(args.OUTPUT_DIR):
os.makedirs(args.OUTPUT_DIR)
main(args.DOMAIN, args.OUTPUT_DIR)
Teraz zdefiniujemy main() funkcja, która wyodrębni podstawową nazwę witryny poprzez usunięcie niepotrzebnych elementów przed rzeczywistą nazwą wraz z dodatkową walidacją na wejściowym adresie URL w następujący sposób -
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()
Teraz musimy otworzyć połączenie z adresem URL przy pomocy metody urlopen (). Użyjmy bloku try-except w następujący sposób -
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))
Kolejne wiersze kodu zawierają trzy funkcje, jak wyjaśniono poniżej -
write_output() aby zapisać pierwszą stronę internetową w katalogu wyjściowym
find_links() funkcja identyfikacji linków na tej stronie internetowej
recurse_pages() funkcję iteracji i odkrywania wszystkich linków na stronie internetowej.
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))
Teraz zdefiniujmy write_output() metoda w następujący sposób -
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)
Musimy zarejestrować pewne szczegóły dotyczące strony internetowej, a następnie rejestrujemy skrót danych za pomocą hash_data() metoda w następujący sposób -
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)))
Teraz zdefiniuj hash_data() metoda, za pomocą której czytamy UTF-8 zakodowane dane, a następnie wygeneruj plik SHA-256 hash tego w następujący sposób -
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()
Teraz stwórzmy plik Beautifulsoup obiektu z danych strony internetowej pod find_links() metoda w następujący sposób -
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
Teraz musimy zdefiniować recurse_pages() metoda poprzez dostarczenie danych wejściowych adresu URL witryny, bieżącej kolejki łączy, niezweryfikowanego kontekstu SSL i katalogu wyjściowego w następujący sposób -
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
Teraz zapisz dane wyjściowe każdej dostępnej strony internetowej w pliku, przekazując nazwę łącza, dane strony, katalog wyjściowy i licznik w następujący sposób:
write_output(link, page, output_dir, counter)
queue = find_links(website, page, queue)
logger.info("Identified {} links throughout website".format(
len(queue)))
Teraz, kiedy uruchomimy ten skrypt, podając adres URL witryny internetowej, katalog wyjściowy i ścieżkę do pliku dziennika, uzyskamy szczegółowe informacje o tej stronie internetowej, które można wykorzystać w przyszłości.
Polowanie na wirusy
Czy zastanawiałeś się kiedyś, w jaki sposób analitycy sądowi, badacze bezpieczeństwa i respondenci incydentów mogą zrozumieć różnicę między użytecznym oprogramowaniem a złośliwym oprogramowaniem? Odpowiedź tkwi w samym pytaniu, ponieważ bez studiowania złośliwego oprogramowania, szybko generowanego przez hakerów, badacze i specjaliści nie są w stanie odróżnić użytecznego oprogramowania od złośliwego oprogramowania. W tej sekcji omówimyVirusSharenarzędzie do wykonania tego zadania.
Zrozumieć VirusShare
VirusShare to największa prywatna kolekcja próbek złośliwego oprogramowania, która dostarcza badaczom bezpieczeństwa, reagującym na incydenty i analitykom sądowym próbki żywego złośliwego kodu. Zawiera ponad 30 milionów próbek.
Zaletą VirusShare jest lista ogólnie dostępnych skrótów złośliwego oprogramowania. Każdy może użyć tych skrótów do utworzenia bardzo wszechstronnego zestawu skrótów i użyć go do zidentyfikowania potencjalnie złośliwych plików. Ale przed użyciem VirusShare zalecamy odwiedzić witrynęhttps://virusshare.com po więcej szczegółów.
Tworzenie listy skrótów rozdzielanych znakami nowej linii z VirusShare przy użyciu Pythona
Lista skrótów z VirusShare może być używana przez różne narzędzia kryminalistyczne, takie jak X-way i EnCase. W skrypcie omówionym poniżej zamierzamy zautomatyzować pobieranie list skrótów z VirusShare, aby utworzyć listę skrótów rozdzielaną znakami nowej linii.
Do tego skryptu potrzebujemy biblioteki Python innej firmy tqdm które można pobrać w następujący sposób -
pip install tqdm
Zauważ, że w tym skrypcie najpierw przeczytamy stronę z hasłami VirusShare i dynamicznie zidentyfikujemy najnowszą listę skrótów. Następnie zainicjujemy pasek postępu i pobierzemy listę skrótów w żądanym zakresie.
Najpierw zaimportuj następujące biblioteki -
from __future__ import print_function
import argparse
import os
import ssl
import sys
import tqdm
from urllib.request import urlopen
import urllib.error
Ten skrypt pobierze jeden argument pozycyjny, który byłby pożądaną ścieżką dla zestawu skrótów -
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()
Teraz przeprowadzimy walidację standardowego wejścia w następujący sposób -
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)
Teraz musimy zdefiniować main() funkcja z **kwargs jako argument, ponieważ spowoduje to utworzenie słownika, do którego możemy odwołać się z obsługą dostarczonych argumentów kluczowych, jak pokazano poniżej -
def main(hashset, **kwargs):
url = "https://virusshare.com/hashes.4n6"
print("[+] Identifying hash set range from {}".format(url))
context = ssl._create_unverified_context()
Teraz musimy otworzyć stronę skrótów VirusShare przy użyciu urlib.request.urlopen()metoda. Będziemy używać bloku try-except w następujący sposób -
try:
index = urlopen(url, context = context).read().decode("utf-8")
except urllib.error.HTTPError as e:
print("[-] Error accessing webpage - exiting..")
sys.exit(1)
Teraz zidentyfikuj najnowszą listę skrótów z pobranych stron. Możesz to zrobić, znajdując ostatnią instancję kodu HTMLhrefdo listy skrótów VirusShare. Można to zrobić za pomocą następujących linii kodu -
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
Teraz użyjemy tqdm.trange() metoda tworzenia pętli i paska postępu w następujący sposób -
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
Po pomyślnym wykonaniu powyższych kroków otworzymy plik tekstowy zestawu skrótu w trybie +, aby dołączyć go na dole pliku tekstowego.
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))
Po uruchomieniu powyższego skryptu otrzymasz najnowszą listę skrótów zawierającą wartości skrótu MD5 w formacie tekstowym.
W poprzednich rozdziałach omówiono znaczenie i proces kryminalistyki sieci oraz związane z nią koncepcje. W tym rozdziale dowiemy się o roli wiadomości e-mail w kryminalistyce cyfrowej i ich dochodzeniach za pomocą Pythona.
Rola poczty elektronicznej w dochodzeniach
Wiadomości e-mail odgrywają bardzo ważną rolę w komunikacji biznesowej i stały się jedną z najważniejszych aplikacji w Internecie. Są wygodnym trybem przesyłania wiadomości i dokumentów nie tylko z komputerów, ale także z innych gadżetów elektronicznych, takich jak telefony komórkowe i tablety.
Wadą e-maili jest to, że przestępcy mogą ujawniać ważne informacje o swojej firmie. W związku z tym rola wiadomości e-mail w kryminalistyce cyfrowej wzrosła w ostatnich latach. W kryminalistyce cyfrowej wiadomości e-mail są uważane za kluczowe dowody, a analiza nagłówków wiadomości e-mail stała się ważna przy zbieraniu dowodów podczas procesu kryminalistycznego.
Badacz ma następujące cele podczas przeprowadzania analizy śledczej poczty e-mail -
- Aby zidentyfikować głównego przestępcę
- Aby zebrać niezbędne dowody
- Przedstawienie wyników
- Aby zbudować obudowę
Wyzwania w e-mailach śledczych
Kryminalistyka e-mailowa odgrywa bardzo ważną rolę w dochodzeniach, ponieważ większość komunikacji w dzisiejszych czasach opiera się na e-mailach. Jednak e-mailowy śledczy może napotkać następujące wyzwania podczas dochodzenia -
Fałszywe e-maile
Największym wyzwaniem w kryminalistyce e-maili jest wykorzystywanie fałszywych wiadomości e-mail, które są tworzone przez manipulowanie i skrypty nagłówków itp. W tej kategorii przestępcy używają również tymczasowej poczty e-mail, która jest usługą umożliwiającą zarejestrowanemu użytkownikowi otrzymywanie wiadomości e-mail na tymczasowy adres, który wygasa. po pewnym czasie.
Podszywanie się
Innym wyzwaniem w kryminalistyce e-maili jest spoofing, w ramach którego przestępcy przedstawiali e-maile jako cudze. W takim przypadku urządzenie otrzyma zarówno fałszywy, jak i oryginalny adres IP.
Anonimowe ponowne wysyłanie wiadomości e-mail
W tym przypadku serwer poczty e-mail usuwa informacje identyfikacyjne z wiadomości e-mail przed jej przekazaniem dalej. Prowadzi to do kolejnego dużego wyzwania w dochodzeniach e-mailowych.
Techniki stosowane w dochodzeniach kryminalistycznych e-mail
Email forensics to badanie źródła i treści wiadomości e-mail jako dowodów identyfikujących faktycznego nadawcę i odbiorcę wiadomości wraz z innymi informacjami, takimi jak data / godzina transmisji i zamiar nadawcy. Obejmuje badanie metadanych, skanowanie portów, a także wyszukiwanie słów kluczowych.
Niektóre z typowych technik, które mogą być używane w dochodzeniach kryminalistycznych, to:
- Analiza nagłówka
- Badanie serwera
- Badanie urządzeń sieciowych
- Odciski palców nadawcy poczty
- Wbudowane identyfikatory oprogramowania
W kolejnych sekcjach dowiemy się, jak pobierać informacje za pomocą Pythona na potrzeby badania wiadomości e-mail.
Wydobywanie informacji z plików EML
Pliki EML to w zasadzie wiadomości e-mail w formacie plików, które są szeroko stosowane do przechowywania wiadomości e-mail. Są to strukturalne pliki tekstowe, które są zgodne z wieloma klientami poczty e-mail, takimi jak Microsoft Outlook, Outlook Express i Windows Live Mail.
Plik EML przechowuje nagłówki wiadomości e-mail, treść i dane załączników jako zwykły tekst. Używa base64 do kodowania danych binarnych i kodowania Quoted-Printable (QP) do przechowywania informacji o zawartości. Skrypt Pythona, którego można użyć do wyodrębnienia informacji z pliku EML, znajduje się poniżej -
Najpierw zaimportuj następujące biblioteki Pythona, jak pokazano poniżej -
from __future__ import print_function
from argparse import ArgumentParser, FileType
from email import message_from_file
import os
import quopri
import base64
W powyższych bibliotekach quoprisłuży do dekodowania wartości zakodowanych przez QP z plików EML. Wszelkie dane zakodowane w formacie base64 można zdekodować za pomocąbase64 biblioteka.
Następnie podajmy argument do obsługi wiersza poleceń. Zauważ, że tutaj przyjmie tylko jeden argument, który byłby ścieżką do pliku EML, jak pokazano poniżej -
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)
Teraz musimy zdefiniować main() funkcja, w której użyjemy metody o nazwie message_from_file()z biblioteki e-mail, aby odczytać plik, taki jak obiekt. Tutaj uzyskamy dostęp do nagłówków, treści, załączników i innych informacji o ładunku za pomocą wynikowej zmiennej o nazwieemlfile jak pokazano w kodzie podanym poniżej -
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])
Teraz musimy zdefiniować process_payload() metoda, w której wyodrębnimy treść wiadomości za pomocą get_payload()metoda. Będziemy dekodować dane zakodowane przez QP przy użyciuquopri.decodestring()funkcjonować. Sprawdzimy również typ MIME treści, aby mógł poprawnie obsłużyć przechowywanie wiadomości e-mail. Przestrzegaj kodu podanego poniżej -
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)
Po wykonaniu powyższego skryptu otrzymamy informacje nagłówka wraz z różnymi ładunkami na konsoli.
Analiza plików MSG za pomocą Pythona
Wiadomości e-mail są dostępne w wielu różnych formatach. MSG jest jednym z takich formatów używanych przez Microsoft Outlook i Exchange. Pliki z rozszerzeniem MSG mogą zawierać zwykły tekst ASCII w nagłówkach i głównej treści wiadomości, a także hiperłącza i załączniki.
W tej sekcji dowiemy się, jak wyodrębnić informacje z pliku MSG za pomocą interfejsu API programu Outlook. Zwróć uwagę, że następujący skrypt Pythona będzie działał tylko w systemie Windows. W tym celu musimy zainstalować bibliotekę Pythona o nazwiepywin32 w następujący sposób -
pip install pywin32
Teraz zaimportuj następujące biblioteki, używając pokazanych poleceń -
from __future__ import print_function
from argparse import ArgumentParser
import os
import win32com.client
import pywintypes
Teraz podajmy argument do obsługi wiersza poleceń. Tutaj przyjmie dwa argumenty, jeden będzie ścieżką do pliku MSG, a drugi będzie żądanym folderem wyjściowym w następujący sposób -
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)
Teraz musimy zdefiniować main() funkcja, w której zadzwonimy win32com biblioteka do zakładania Outlook API co dodatkowo umożliwia dostęp do MAPI przestrzeń nazw.
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)
Teraz zdefiniuj różne funkcje, których używamy w tym skrypcie. Poniższy kod przedstawia definicję plikudisplay_msg_attribs() funkcja, która pozwala nam wyświetlać różne atrybuty wiadomości, takie jak temat, do, UDW, DW, Rozmiar, SenderName, wysłane itp.
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')))
Teraz zdefiniuj display_msg_recipeints() funkcja, która iteruje wiadomości i wyświetla szczegółowe informacje o adresacie.
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
Następnie definiujemy extract_msg_body() funkcja, która wyodrębnia treść wiadomości, zarówno HTML, jak i zwykły tekst.
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"))
Następnie zdefiniujemy plik extract_attachments() funkcja eksportująca dane załącznika do żądanego katalogu wyjściowego.
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
Po zdefiniowaniu wszystkich funkcji wydrukujemy wszystkie atrybuty na konsoli z następującą linią kodów -
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
Po uruchomieniu powyższego skryptu otrzymamy atrybuty wiadomości i jej załączników w oknie konsoli wraz z kilkoma plikami w katalogu wyjściowym.
Strukturyzacja plików MBOX z Google Takeout przy użyciu Pythona
Pliki MBOX to pliki tekstowe o specjalnym formatowaniu, które dzielą wiadomości przechowywane w. Często występują w połączeniu z systemami UNIX, Thunderbolt i Google Takeouts.
W tej sekcji zobaczysz skrypt Pythona, w którym będziemy porządkować pliki MBOX pobrane z Google Takeouts. Ale wcześniej musimy wiedzieć, jak możemy wygenerować te pliki MBOX za pomocą naszego konta Google lub konta Gmail.
Pozyskanie skrzynki pocztowej konta Google w formacie MBX
Uzyskanie skrzynki pocztowej konta Google oznacza wykonanie kopii zapasowej naszego konta Gmail. Kopię zapasową można wykonać z różnych powodów osobistych lub zawodowych. Pamiętaj, że Google zapewnia tworzenie kopii zapasowych danych Gmaila. Aby uzyskać skrzynkę pocztową konta Google w formacie MBOX, należy wykonać następujące czynności -
otwarty My account deska rozdzielcza.
Przejdź do sekcji Informacje osobiste i prywatność i wybierz łącze Kontroluj zawartość.
Możesz utworzyć nowe archiwum lub zarządzać istniejącym. Jeśli klikniemy,CREATE ARCHIVE link, otrzymamy kilka pól wyboru dla każdego produktu Google, który chcemy uwzględnić.
Po wybraniu produktów uzyskamy swobodę wyboru typu pliku i maksymalnego rozmiaru naszego archiwum wraz z metodą dostawy do wyboru z listy.
Wreszcie otrzymamy kopię zapasową w formacie MBOX.
Kod w Pythonie
Teraz omawiany powyżej plik MBOX można ustrukturyzować za pomocą Pythona, jak pokazano poniżej -
Najpierw musisz zaimportować biblioteki Pythona w następujący sposób -
from __future__ import print_function
from argparse import ArgumentParser
import mailbox
import os
import time
import csv
from tqdm import tqdm
import base64
Wszystkie biblioteki zostały użyte i wyjaśnione we wcześniejszych skryptach, z wyjątkiem mailbox biblioteka używana do parsowania plików MBOX.
Teraz podaj argument do obsługi wiersza poleceń. Tutaj przyjmie dwa argumenty - jeden będzie ścieżką do pliku MBOX, a drugi będzie żądanym folderem wyjściowym.
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)
Teraz zdefiniuję main() funkcja i zadzwoń mbox klasa biblioteki skrzynek pocztowych za pomocą której możemy sparsować plik MBOX podając jego ścieżkę -
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)))
Teraz zdefiniuj metodę czytnika dla mailbox biblioteka w następujący sposób -
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)
Teraz utwórz kilka zmiennych do dalszego przetwarzania w następujący sposób -
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"]
Następnie użyj tqdm wygenerować pasek postępu i śledzić proces iteracji w następujący sposób -
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")
Teraz sprawdź, czy komunikat o pogodzie ma ładunki, czy nie. Jeśli tak, to zdefiniujemywrite_payload() metoda w następujący sposób -
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)
Teraz należy dołączyć dane. Wtedy zadzwonimycreate_report() metoda w następujący sposób -
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
Zwróć uwagę, że powyższe instrukcje if-else są łatwe do zrozumienia. Teraz musimy zdefiniować metodę, która wyodrębni nazwę pliku zmsg obiekt w następujący sposób -
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)
Teraz, za pomocą następujących linii kodu, możesz faktycznie wyeksportować plik -
if isinstance(content_data, str):
open(file_name, 'w').write(content_data)
else:
open(file_name, 'wb').write(content_data)
return file_name
Teraz zdefiniujmy funkcję wyodrębniającą nazwy plików z message aby dokładnie przedstawić nazwy tych plików w następujący sposób -
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)
Teraz możemy napisać plik CSV, definiując rozszerzenie create_report() działają w następujący sposób -
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)
Po uruchomieniu powyższego skryptu otrzymamy raport CSV i katalog pełen załączników.
W tym rozdziale zostaną wyjaśnione różne koncepcje 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 Windowsa 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 ze 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 dla śledczych, a także 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 wyodrębnić z usuniętych plików cenne informacje, takie jak oryginalna ścieżka do pliku, a także czas wysłania go do Kosza.
Pamiętaj, ż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 firm 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ć powiązanego 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ń. Zwróć uwagę, ż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 w sformatowanej dacie i godzinie 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 Python zbadamy ten plik OLE, aby wyodrębnić metadane z Sticky Notes.
W przypadku tego skryptu w Pythonie 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 w tym przypadku przyjmie trzy argumenty - pierwszy to ścieżka do pliku dowodowego, drugi to typ pliku dowodowego, a trzeci to żą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 w Pythonie 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 czynności, 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.
W tym rozdziale omówiono niektóre ważniejsze artefakty w systemie Windows i metodę ich wyodrębniania za pomocą Pythona.
Działania użytkownika
Windows posiadający NTUSER.DATplik do przechowywania różnych działań użytkownika. Każdy profil użytkownika jest podobny do ulaNTUSER.DAT, który przechowuje informacje i konfiguracje dotyczące konkretnego użytkownika. W związku z tym jest bardzo przydatny do celów dochodzeń przeprowadzanych przez analityków sądowych.
Poniższy skrypt w języku Python przeanalizuje niektóre klucze NTUSER.DATdo badania działań użytkownika w systemie. Zanim przejdziemy dalej, w przypadku skryptu Python musimy zainstalować moduły firm trzecich, a mianowicieRegistry, pytsk3, pyewf i Jinja2. Do ich instalacji możemy użyć pip.
Możemy wykonać następujące kroki, aby wyodrębnić informacje z NTUSER.DAT plik -
Najpierw wyszukaj wszystko NTUSER.DAT pliki w systemie.
Następnie przeanalizuj plik WordWheelQuery, TypePath and RunMRU klucz dla każdego NTUSER.DAT plik.
W końcu zapiszemy te artefakty, już przetworzone, do raportu HTML przy użyciu Jinja2 fmodule.
Kod w Pythonie
Zobaczmy, jak w tym celu wykorzystać kod Pythona -
Przede wszystkim musimy zaimportować następujące moduły Pythona -
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
Teraz podaj argument dla obsługi wiersza poleceń. 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 do raportu HTML, jak pokazano poniżej -
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)
Teraz zdefiniujmy main() funkcja wyszukiwania wszystkich NTUSER.DAT pliki, jak pokazano -
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'}
}
Teraz spróbujemy znaleźć klucz w NTUSER.DAT i gdy go znajdziesz, zdefiniuj funkcje przetwarzania użytkownika, jak pokazano poniżej -
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()
Teraz przekaż obiekt słownika i jego ścieżkę do write_html() metoda w następujący sposób -
write_html(report, nt_rec)
Teraz zdefiniuj metodę, która wymaga pytsk uchwyt pliku i wczytaj go do klasy Registry za pośrednictwem rozszerzenia StringIO klasa.
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 zdefiniujemy funkcję, która będzie analizować i obsługiwać WordWheelQuery klucz od NTUSER.DAT plik w następujący sposób -
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
Teraz zdefiniujemy funkcję, która będzie analizować i obsługiwać TypedPaths klucz od NTUSER.DAT plik w następujący sposób -
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
Teraz zdefiniujemy funkcję, która będzie analizować i obsługiwać RunMRU klucz od NTUSER.DAT plik w następujący sposób -
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
Teraz następująca funkcja zajmie się tworzeniem raportu 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)
Nareszcie możemy napisać dokument HTML do raportu. Po uruchomieniu powyższego skryptu uzyskamy informacje z pliku NTUSER.DAT w formacie dokumentu HTML.
LINK pliki
Pliki skrótów są tworzone, gdy użytkownik lub system operacyjny tworzy pliki skrótów do plików, które są często używane, dwukrotnie klikane lub dostępne z dysków systemowych, takich jak dołączona pamięć masowa. Takie rodzaje plików skrótów nazywane są plikami łączy. Uzyskując dostęp do tych plików odsyłaczy, badacz może znaleźć aktywność okna, taką jak czas i miejsce, z którego uzyskano dostęp do tych plików.
Omówmy skrypt języka Python, którego możemy użyć do uzyskania informacji z tych plików LINK systemu Windows.
W przypadku skryptu Python zainstaluj moduły innych firm, mianowicie pylnk, pytsk3, pyewf. Możemy wykonać następujące kroki, aby wyodrębnić informacje zlnk akta
Najpierw wyszukaj lnk pliki w systemie.
Następnie wyodrębnij informacje z tego pliku, przechodząc przez nie.
Teraz w końcu potrzebujemy tych informacji do raportu CSV.
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 csv
import StringIO
from utility.pytskutil import TSKUtil
import pylnk
Teraz podaj argument obsługi wiersza poleceń. 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 do raportu CSV, jak pokazano poniżej -
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)
Teraz zinterpretuj plik dowodowy, tworząc obiekt TSKUtil i iteruj w systemie plików, aby znaleźć pliki kończące się na lnk. Można to zrobić poprzez zdefiniowaniemain() działają w następujący sposób -
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'
]
Teraz, z pomocą następującego kodu, będziemy iterować lnk pliki, tworząc funkcję w następujący sposób -
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)
Teraz musimy zdefiniować dwie funkcje, jedna otworzy plik pytsk obiekt pliku i inne zostaną użyte do napisania raportu CSV, jak pokazano poniżej -
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)
Po uruchomieniu powyższego skryptu uzyskamy informacje z wykrytego lnk pliki w raporcie CSV -
Pobierz pliki z wyprzedzeniem
Za każdym razem, gdy aplikacja jest uruchamiana po raz pierwszy z określonej lokalizacji, system Windows tworzy prefetch files. Służą one do przyspieszenia procesu uruchamiania aplikacji. Rozszerzenie tych plików to.PF a te są przechowywane w ”\Root\Windows\Prefetch” teczka.
Cyfrowi eksperci kryminalistyczni mogą ujawnić dowody wykonywania programu z określonej lokalizacji wraz z danymi użytkownika. Pliki pobierania wstępnego są użytecznymi artefaktami dla eksperta, ponieważ ich wpis pozostaje nawet po usunięciu lub odinstalowaniu programu.
Omówmy skrypt Pythona, który pobierze informacje z plików wstępnego pobierania systemu Windows, jak podano poniżej -
W przypadku skryptu Python zainstaluj moduły innych firm, mianowicie pylnk, pytsk3 i unicodecsv. Przypomnij sobie, że pracowaliśmy już z tymi bibliotekami w skryptach Pythona, które omówiliśmy w poprzednich rozdziałach.
Musimy wykonać poniższe kroki, aby wyodrębnić informacje z prefetch pliki -
Najpierw wyszukaj .pf pliki rozszerzeń lub pliki pobierania wstępnego.
Teraz przeprowadź weryfikację podpisu, aby wyeliminować fałszywe alarmy.
Następnie przeanalizuj format pliku pobierania wstępnego systemu Windows. Różni się to od wersji systemu Windows. Na przykład dla Windows XP jest to 17, dla Windows Vista i Windows 7 to 23, 26 dla Windows 8.1 i 30 dla Windows 10.
Na koniec zapiszemy przeanalizowany wynik w pliku CSV.
Kod w Pythonie
Zobaczmy, jak w tym celu wykorzystać kod Pythona -
Najpierw zaimportuj następujące biblioteki Pythona -
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
Teraz podaj argument do obsługi wiersza poleceń. Tutaj przyjmie dwa argumenty, pierwszy będzie ścieżką do pliku dowodowego, a drugi będzie typem pliku dowodowego. Akceptuje również opcjonalny argument do określenia ścieżki do skanowania w poszukiwaniu plików pobierania wstępnego -
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)
Teraz zinterpretuj plik dowodowy, tworząc obiekt TSKUtil i iteruj w systemie plików, aby znaleźć pliki kończące się na .pf. Można to zrobić poprzez zdefiniowaniemain() działają w następujący sposób -
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)
Teraz zdefiniuj metodę, która dokona weryfikacji podpisów, jak pokazano poniżej -
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)
Teraz zacznij przetwarzać pliki pobierania wstępnego systemu Windows. Tutaj bierzemy przykład plików pobierania wstępnego systemu Windows XP -
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)
Teraz wyodrębnij dane osadzone w wstępnie pobranych plikach za pomocą następującej struktury -
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 ]
Ponieważ udostępniliśmy wersję pobierania wstępnego dla systemu Windows XP, ale co, jeśli napotka wersje pobierania wstępnego dla innych systemów Windows. Następnie musi wyświetlić komunikat o błędzie w następujący sposób -
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)
Teraz zdefiniuj metodę zapisu wyniku do raportu CSV w następujący sposób -
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)
Po uruchomieniu powyższego skryptu otrzymamy informacje z plików wstępnego pobierania wersji systemu Windows XP do arkusza kalkulacyjnego.
W tym rozdziale wyjaśniono dalsze artefakty, które badacz może uzyskać podczas analizy kryminalistycznej w systemie Windows.
Dzienniki zdarzeń
Pliki dziennika zdarzeń systemu Windows, jak sugeruje nazwa, są specjalnymi plikami, które przechowują ważne zdarzenia, takie jak logowanie użytkownika na komputerze, wystąpienie błędu programu, zmiany w systemie, dostęp RDP, zdarzenia specyficzne dla aplikacji itp. Cyberprzestępcy są zawsze zainteresowani zdarzeniem. informacje dziennika, ponieważ zawiera wiele przydatnych informacji historycznych o dostępie do systemu. W poniższym skrypcie w języku Python będziemy przetwarzać zarówno starsze, jak i obecne formaty dziennika zdarzeń systemu Windows.
W przypadku skryptu Python musimy zainstalować moduły innych firm, a mianowicie pytsk3, pyewf, unicodecsv, pyevt and pyevtx. Możemy wykonać poniższe kroki, aby wyodrębnić informacje z dzienników zdarzeń -
Najpierw wyszukaj wszystkie dzienniki zdarzeń, które pasują do argumentu wejściowego.
Następnie przeprowadź weryfikację podpisu pliku.
Teraz przetwórz każdy znaleziony dziennik zdarzeń za pomocą odpowiedniej biblioteki.
Na koniec zapisz dane wyjściowe do arkusza kalkulacyjnego.
Kod w Pythonie
Zobaczmy, jak w tym celu wykorzystać kod Pythona -
Najpierw zaimportuj następujące biblioteki Pythona -
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
Teraz podaj argumenty 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 nazwa dziennika zdarzeń do przetworzenia.
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)
Teraz wejdź w interakcję z dziennikami zdarzeń, aby zapytać o istnienie ścieżki podanej przez użytkownika, tworząc nasz plik TSKUtilobiekt. Można to zrobić za pomocąmain() metoda w następujący sposób -
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)
Teraz musimy przeprowadzić weryfikację podpisu, a następnie zdefiniować metodę, która zapisze całą zawartość do bieżącego katalogu -
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
Na koniec zdefiniuj metodę zapisywania danych wyjściowych do arkusza kalkulacyjnego w następujący sposób -
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)
Po pomyślnym uruchomieniu powyższego skryptu uzyskamy informacje o dzienniku zdarzeń w arkuszu kalkulacyjnym.
Historia Internetu
Historia Internetu jest bardzo przydatna dla analityków kryminalistycznych; ponieważ większość cyberprzestępstw ma miejsce wyłącznie w internecie. Zobaczmy, jak wyodrębnić historię internetową z Internet Explorera, omawiając kryminalistykę systemu Windows, a Internet Explorer jest domyślnie dostępny w systemie Windows.
W przeglądarce Internet Explorer historia internetowa jest zapisywana w formacie index.datplik. Przyjrzyjmy się skryptowi w języku Python, który wyodrębni informacje zindex.dat plik.
Możemy wykonać poniższe kroki, aby wyodrębnić informacje z index.dat pliki -
Najpierw wyszukaj index.dat pliki w systemie.
Następnie wyodrębnij informacje z tego pliku, przechodząc przez nie.
Teraz zapisz wszystkie te informacje w raporcie CSV.
Kod w Pythonie
Zobaczmy, jak w tym celu wykorzystać kod Pythona -
Najpierw zaimportuj następujące biblioteki Pythona -
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
Teraz podaj argumenty dla obsługi wiersza poleceń. Zwróć uwagę, że w tym przypadku przyjmie dwa argumenty - pierwszy będzie ścieżką do pliku dowodowego, a drugi będzie typem pliku dowodowego -
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)
Teraz zinterpretuj plik dowodowy, tworząc obiekt TSKUtili wykonaj iterację w systemie plików, aby znaleźć pliki index.dat. Można to zrobić, definiując plikmain() działają w następujący sposób -
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)
Teraz zdefiniujmy funkcję, za pomocą której możemy skopiować informacje z pliku index.dat do bieżącego katalogu roboczego, a później mogą być przetwarzane przez zewnętrzny moduł -
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
Teraz użyj następującego kodu, aby przeprowadzić walidację podpisu za pomocą wbudowanej funkcji, a mianowicie 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)
Teraz zdefiniuj metodę, która wydrukuje wynik w pliku CSV, jak pokazano poniżej -
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)
Po uruchomieniu powyższego skryptu uzyskamy informacje z pliku index.dat w pliku CSV.
Kopie woluminów w tle
Kopia w tle to technologia zawarta w systemie Windows do ręcznego lub automatycznego wykonywania kopii zapasowych lub migawek plików komputerowych. Nazywa się to również usługą migawki woluminu lub usługą woluminu w tle (VSS).
Z pomocą tych plików VSS eksperci medycyny sądowej mogą uzyskać pewne historyczne informacje o tym, jak system zmieniał się w czasie i jakie pliki istniały na komputerze. Technologia kopiowania w tle wymaga systemu plików NTFS do tworzenia i przechowywania kopii w tle.
W tej sekcji zobaczymy skrypt w Pythonie, który pomaga uzyskać dostęp do dowolnej ilości kopii w tle obecnych w obrazie śledczym.
W przypadku skryptu Python musimy zainstalować moduły firm trzecich, a mianowicie pytsk3, pyewf, unicodecsv, pyvshadow i vss. Możemy wykonać kroki podane poniżej, aby wyodrębnić informacje z plików VSS
Najpierw uzyskaj dostęp do woluminu surowego obrazu i zidentyfikuj wszystkie partycje NTFS.
Następnie wyodrębnij informacje z kopii w tle, przechodząc przez nie.
Teraz w końcu musimy utworzyć listę plików danych w migawkach.
Kod w Pythonie
Zobaczmy, jak w tym celu wykorzystać kod Pythona -
Najpierw zaimportuj następujące biblioteki Pythona -
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
Teraz podaj argumenty dla obsługi wiersza poleceń. Tutaj przyjmie dwa argumenty - pierwszy to ścieżka do pliku dowodowego, a drugi to plik wyjściowy.
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()
Teraz sprawdź istnienie ścieżki pliku wejściowego, a także oddziel katalog od pliku wyjściowego.
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)
Teraz wejdź w interakcję z woluminem pliku dowodowego, tworząc plik TSKUtilobiekt. Można to zrobić za pomocąmain() metoda w następujący sposób -
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)
Teraz zdefiniuj metodę eksploracji przeanalizowanego pliku cienia woluminu w następujący sposób -
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)
Na koniec zdefiniuj metodę zapisu wyniku w arkuszu kalkulacyjnym w następujący sposób -
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)
Po pomyślnym uruchomieniu tego skryptu w Pythonie otrzymamy informacje znajdujące się w VSS do arkusza kalkulacyjnego.
Do tej pory widzieliśmy, jak uzyskać artefakty w systemie Windows za pomocą Pythona. W tym rozdziale dowiemy się o badaniu artefaktów opartych na dziennikach za pomocą języka Python.
Wprowadzenie
Artefakty oparte na dziennikach to skarbnica informacji, która może być bardzo przydatna dla eksperta kryminalistyki cyfrowej. Chociaż mamy różne oprogramowanie monitorujące do zbierania informacji, głównym problemem związanym z analizowaniem przydatnych informacji z nich jest to, że potrzebujemy dużej ilości danych.
Różne artefakty oparte na dziennikach i badanie w języku Python
W tej sekcji omówimy różne artefakty oparte na dziennikach i ich badanie w Pythonie -
Znaczniki czasu
Sygnatura czasowa zawiera dane i czas aktywności w dzienniku. Jest to jeden z ważnych elementów każdego pliku dziennika. Należy pamiętać, że te wartości danych i czasu mogą mieć różne formaty.
Pokazany poniżej skrypt Pythona weźmie nieprzetworzoną datę i godzinę jako dane wejściowe i zapewni sformatowany znacznik czasu jako dane wyjściowe.
W przypadku tego skryptu musimy wykonać następujące kroki -
Najpierw ustaw argumenty, które przyjmą surową wartość danych wraz ze źródłem danych i typem danych.
Teraz zapewnij klasę zapewniającą wspólny interfejs dla danych w różnych formatach dat.
Kod w Pythonie
Zobaczmy, jak w tym celu wykorzystać kod Pythona -
Najpierw zaimportuj następujące moduły Pythona -
from __future__ import print_function
from argparse import ArgumentParser, ArgumentDefaultsHelpFormatter
from datetime import datetime as dt
from datetime import timedelta
Teraz jak zwykle musimy podać argument dla obsługi wiersza poleceń. Tutaj przyjmie trzy argumenty, pierwszy będzie wartością daty do przetworzenia, drugi będzie źródłem tej wartości daty, a trzeci będzie jego typem -
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)
Teraz musimy zdefiniować klasę, która będzie akceptować argumenty wartości daty, źródła daty i typu wartości -
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
Teraz zdefiniujemy metodę, która będzie działać jak kontroler, tak jak metoda 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']
Teraz musimy zdefiniować dwie metody, które będą przetwarzać odpowiednio czas epoki UNIX i CZAS PLIKU -
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')
Po uruchomieniu powyższego skryptu, podając znacznik czasu możemy uzyskać przekonwertowaną wartość w łatwym do odczytania formacie.
Dzienniki serwera sieci Web
Z punktu widzenia eksperta kryminalistyki cyfrowej, dzienniki serwera WWW są kolejnym ważnym artefaktem, ponieważ mogą uzyskać przydatne statystyki użytkownika wraz z informacjami o użytkowniku i lokalizacji geograficznej. Poniżej znajduje się skrypt w Pythonie, który po przetworzeniu dzienników serwera WWW utworzy arkusz kalkulacyjny w celu łatwej analizy informacji.
Przede wszystkim musimy zaimportować następujące moduły Pythona -
from __future__ import print_function
from argparse import ArgumentParser, FileType
import re
import shlex
import logging
import sys
import csv
logger = logging.getLogger(__file__)
Teraz musimy zdefiniować wzorce, które będą analizowane z dzienników -
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*"))]
Teraz podaj argument do obsługi wiersza poleceń. Tutaj przyjmie dwa argumenty, pierwszy będzie dziennikiem IIS do przetworzenia, a drugi będzie wymaganą ścieżką pliku 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")
Teraz musimy zdefiniować metodę main (), która będzie obsługiwać skrypt zbiorczych informacji o dzienniku -
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")
Na koniec musimy zdefiniować metodę, która zapisze dane wyjściowe do arkusza kalkulacyjnego -
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)
Po uruchomieniu powyższego skryptu otrzymamy dzienniki oparte na serwerze WWW w arkuszu kalkulacyjnym.
Skanowanie ważnych plików za pomocą YARA
YARA (Yet Another Recursive Algorithm) to narzędzie do dopasowywania wzorców przeznaczone do identyfikacji złośliwego oprogramowania i reagowania na incydenty. Do skanowania plików użyjemy YARA. W poniższym skrypcie Pythona użyjemy YARA.
YARA możemy zainstalować za pomocą następującego polecenia -
pip install YARA
Możemy postępować zgodnie z instrukcjami podanymi poniżej, aby używać reguł YARA do skanowania plików -
Najpierw skonfiguruj i skompiluj reguły YARA
Następnie przeskanuj pojedynczy plik, a następnie iteruj po katalogach, aby przetworzyć poszczególne pliki.
Na koniec wyeksportujemy wynik do CSV.
Kod w Pythonie
Zobaczmy, jak w tym celu wykorzystać kod Pythona -
Najpierw musimy zaimportować następujące moduły Pythona -
from __future__ import print_function
from argparse import ArgumentParser, ArgumentDefaultsHelpFormatter
import os
import csv
import yara
Następnie podaj argument do obsługi wiersza poleceń. Zauważ, że w tym przypadku przyjmie dwa argumenty - pierwszy to ścieżka do reguł YARA, drugi to plik do przeskanowania.
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)
Teraz zdefiniujemy funkcję main (), która zaakceptuje ścieżkę do reguł yara i pliku do przeskanowania -
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)
Teraz zdefiniuj metodę, która będzie iterować po katalogu i przekaże wynik do innej metody w celu dalszego przetwarzania -
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
Następnie zdefiniuj dwie funkcje. Zauważ, że najpierw użyjemymatch() metoda do yrulesobiekt, a inny zgłosi pasujące informacje do konsoli, jeśli użytkownik nie określi żadnego pliku wyjściowego. Przestrzegaj kodu pokazanego poniżej -
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)
Na koniec zdefiniujemy metodę, która zapisze dane wyjściowe do pliku CSV, jak pokazano poniżej -
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)
Po pomyślnym uruchomieniu powyższego skryptu możemy podać odpowiednie argumenty w wierszu poleceń i wygenerować raport w formacie CSV.