Ask me anything
Ithy Logo
Sign in to save

Software per l'Analisi del File MFT di una Partizione NTFS

Implementazione in Python per leggere, analizzare e visualizzare record MFT con assegnazione di punteggio di sospetto

NTFS file structure disk analysis

Highlight Principali

  • Lettura e analisi dei record MFT: Il software accede al file binario MFT, leggendolo record per record e analizzando tutti i campi e attributi.
  • Ricostruzione del percorso completo: Utilizzando i riferimenti genitore, il percorso completo di ogni file viene ricostruito per identificare file con nomi lunghi.
  • Assegnazione e visualizzazione del punteggio di sospetto: Sulle estensioni .exe e .dll viene calcolato un punteggio di sospetto basato su criteri specifici e mostrati i 10 file con punteggio più alto.

Introduzione

Questo software in Python è progettato per analizzare un file MFT (Master File Table) di una partizione NTFS. L’obiettivo è leggere tutti i record presenti nel file, estrarre ciascuno dei campi e attributi, gestire eventuali errori e ricostruire il percorso completo per ogni file in base alla gerarchia dei record. Al contempo, viene assegnato un punteggio di sospetto a ciascun file in base a criteri specifici, considerando l’estensione, la posizione nel path e particolari caratteristiche del nome del file.

Il file MFT è il cuore del file system NTFS in quanto contiene i metadati di ogni file e directory. Per questo motivo, la capacità di interpretare correttamente questa struttura è fondamentale in svariati scenari, dalla manutenzione del sistema alla forense digitale. Il software presenta inoltre un sistema di monitoraggio del progresso, che informa l’utente sullo stato dell’analisi e consente una gestione puntuale degli errori anche in presenza di record danneggiati o malformati.


Architettura del Software

La struttura generale del progetto comprende quattro fasi principali:

1. Lettura e Parsing del File MFT

Lettura del File MFT

Utilizzando accesso in modalità binaria, il file MFT viene aperto e letto in blocchi di dimensione fissa (tipicamente 1024 byte per record). La lettura viene effettuata con privilegi sufficienti per accedere a questo file critico, e ogni record viene verificato per la presenza della firma "FILE" per garantire l’integrità.

Parsing dei Record

Per ogni record, vengono estratti tutti i campi presenti, quali:

  • Header del record (controllo della firma e flag di attivazione)
  • Offset agli attributi
  • Dimensioni usate e flag di validità
  • Riferimenti al record genitore per la ricostruzione del path

Una volta identificato un record valido, si procede con il parsing degli attributi, inclusi:

  • $STANDARD_INFORMATION
  • $FILE_NAME
  • Altri attributi come $DATA, $INDEX_ROOT e simili

In particolare, l’attributo $FILE_NAME gioca un ruolo cruciale nella ricostruzione del percorso. Qui vengono lette informazioni quali il nome del file, l’ID del record del genitore e altre informazioni rilevanti (timestamp, dimensione, ecc.).

2. Gestione degli Errori e Visualizzazione del Progresso

Gestione degli Errori

Data la complessità della struttura MFT e la possibilità di incontrare record corrotti o non validi, il software implementa una robusta gestione degli errori:

  • Verifica della presenza dei file e dei relativi permessi di lettura
  • Controllo delle eccezioni durante l’accesso al file e il parsing dei record
  • Logging dettagliato per facilitare il debug e l’analisi degli errori

Visualizzazione del Progresso

Per garantire un’esperienza utente fluida, durante la lettura dei record viene visualizzato un messaggio di progresso, che indica il numero corrente di record elaborati rispetto al totale. Questo permette di monitorare l’andamento dell’analisi in tempo reale e di identificare eventuali rallentamenti o problemi specifici durante la lettura.

3. Ricostruzione del Path Completo

Metodo di Ricostruzione

La ricostruzione del percorso completo di ogni file avviene tramite una risoluzione ricorsiva dei riferimenti ai record genitori. Ogni record contiene il riferimento al record del genitore, e combinando questi riferimenti si ottiene il percorso completo. Il software gestisce:

  • Il controllo di cicli infiniti, impostando un limite massimo di profondità (ad esempio 100 livelli)
  • La corretta visualizzazione del percorso anche in presenza di nomi di file lunghi o nomi Unicode
  • La raccolta e concatenazione dei componenti del percorso, separati da "\"

Tale ricostruzione è indispensabile per effettuare analisi successive sui file e per associare correttamente ogni record al relativo file system.

4. Calcolo del Punteggio di Sospetto

Criteri Utilizzati

Il punteggio di sospetto viene assegnato per ciascun file partendo da 0. Tuttavia, il calcolo viene applicato solo ai file con estensione .exe o .dll e si basa sui seguenti test:

  • Path Contenente "users\": Se il file si trova in una directory contenente "users\", vengono aggiunti 5 punti.
  • Nome corto (<= 3 caratteri): Se il nome del file senza estensione è lungo 3 o meno caratteri, vengono aggiunti 5 punti.
  • Nome corto con sequenza casuale (<= 8 caratteri): Se il nome (senza estensione) è lungo fino a 8 caratteri e presenta una combinazione casuale di lettere maiuscole, minuscole e numeri, si aggiungono 10 punti.
  • Sequenze di 3 o più caratteri consecutivi: Se il nome presenta una sequenza di 3 o più caratteri identici consecutivi, vengono aggiunti 8 punti.

Questi test combinati permettono di individuare file potenzialmente sospetti che, ad esempio, potrebbero essere stati creati o rinominati con tecniche di offuscamento per mascherare attività malevole.

5. Visualizzazione dei Risultati

Risultati Finale

Una volta completata l’analisi, il software visualizza i dati dei 10 file con il punteggio di sospetto più elevato. Su questi file vengono mostrati:

  • Il nome del file
  • Il percorso completo ricostruito
  • Il punteggio di sospetto totale
  • Eventuali motivazioni e attributi associati

I risultati sono utili per gli analisti di sicurezza e i tecnici forensi, poiché permettono di indirizzare ulteriori analisi su file sospetti e verificare eventuali anomalie all’interno del sistema.


Implementazione del Codice

Di seguito è fornito il codice Python completo che implementa le funzionalità sopra descritte. L'implementazione include:

  • Lettura e interpretazione del file MFT in modalità binaria
  • Parsing dei record e degli attributi MFT, con particolare attenzione al campo $FILE_NAME
  • Gestione degli errori e output del progresso in tempo reale
  • Ricostruzione del percorso completo attraverso il tracciamento dei record genitori
  • Calcolo del punteggio di sospetto in base alle regole specificate
  • Visualizzazione finale dei 10 file più sospetti in output

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import os
import sys
import struct
import re
import logging

# Configurazione logging per mostrare il progresso e gestire gli errori
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)

# Costanti per il parsing MFT
MFT_RECORD_SIZE = 1024  # Dimensione fissa di un record MFT
MFT_SIGNATURE = b'FILE'   # Firma di un record MFT valido

# Classe che gestisce i record MFT
class MFTRecord:
    def __init__(self, record_num, data):
        self.record_num = record_num
        self.data = data
        self.attributes = []
        self.filename = None
        self.parent_ref = None
        self.full_path = None
        self.suspicion_score = 0
        self.suspicion_reasons = []

    def parse_header(self):
        # Controlla la validità del record tramite la firma
        if self.data[0:4] != MFT_SIGNATURE:
            raise ValueError("Record non valido: firma mancante")
        try:
            self.offset_to_attributes = struct.unpack("<H", self.data[20:22])[0]
            self.flags = struct.unpack("<H", self.data[22:24])[0]
            self.is_active = (self.flags & 0x01) != 0
        except Exception as e:
            raise ValueError("Errore nel parsing dell'header: {}".format(e))

    def parse_attributes(self):
        # Inizia dal offset specificato per gli attributi
        current_offset = self.offset_to_attributes
        while current_offset < MFT_RECORD_SIZE - 8:
            try:
                attr_type = struct.unpack("<I", self.data[current_offset:current_offset+4])[0]
                attr_size = struct.unpack("<I", self.data[current_offset+4:current_offset+8])[0]
                # Se fine degli attributi
                if attr_type == 0xFFFFFFFF or attr_size == 0:
                    break
                # Se l'attributo è $FILE_NAME (tipo 48)
                if attr_type == 48:
                    self.parse_filename_attribute(current_offset, attr_size)
                # Salva l'attributo (può essere esteso per altri tipi)
                attribute = {
                    'type': attr_type,
                    'size': attr_size,
                    'raw_data': self.data[current_offset:current_offset+attr_size]
                }
                self.attributes.append(attribute)
                current_offset += attr_size
            except Exception as e:
                logger.error("Errore durante il parsing degli attributi nel record {}: {}".format(self.record_num, e))
                break

    def parse_filename_attribute(self, att_offset, att_size):
        try:
            # Legge il riferimento al record padre (8 byte)
            parent_ref = struct.unpack("<Q", self.data[att_offset:att_offset+8])[0]
            self.parent_ref = parent_ref & 0xFFFFFFFFFFFF  # I primi 6 byte
            # Legge la lunghezza del nome
            name_length = self.data[att_offset+64]
            name_offset = att_offset + 66
            self.filename = self.data[name_offset:name_offset + name_length*2].decode('utf-16-le')
        except Exception as e:
            logger.error("Errore nel parsing di $FILE_NAME nel record {}: {}".format(self.record_num, e))

    def calculate_suspicion(self):
        if not self.filename:
            return
        # Calcola il punteggio solo per file con estensione .exe o .dll
        file_parts = self.filename.split('.')
        if len(file_parts) < 2:
            return
        extension = file_parts[-1].lower()
        if extension not in ['exe', 'dll']:
            return

        # Test 1: Controlla se nel percorso c'è "users\"
        if self.full_path and "users\" in self.full_path.lower():
            self.suspicion_score += 5
            self.suspicion_reasons.append("File in cartella 'users'")
        # Test 2: Nome del file (senza estensione) <= 3 caratteri
        basename = '.'.join(file_parts[:-1])
        if len(basename) <= 3:
            self.suspicion_score += 5
            self.suspicion_reasons.append("Nome file molto corto (<=3 caratteri)")
        # Test 3: Nome <= 8 caratteri con mix di caratteri casuale
        if len(basename) <= 8:
            if (any(c.islower() for c in basename) and 
                any(c.isupper() for c in basename) and 
                any(c.isdigit() for c in basename)):
                self.suspicion_score += 10
                self.suspicion_reasons.append("Nome file con mix casuale di lettere e numeri (<=8 caratteri)")
        # Test 4: Sequenza di 3 o più caratteri consecutivi
        if re.search(r'(.)\1{2,}', self.filename):
            self.suspicion_score += 8
            self.suspicion_reasons.append("Sequenza di 3+ caratteri uguali individuata")

# Classe che gestisce il parsing completo del file MFT
class MFTParser:
    def __init__(self, mft_file):
        self.mft_file = mft_file
        self.records = []
        self.suspicious_files = []

    def read_and_parse(self):
        try:
            file_size = os.path.getsize(self.mft_file)
            total_records = file_size // MFT_RECORD_SIZE
            logger.info("Totale record da analizzare: {}".format(total_records))
            with open(self.mft_file, 'rb') as f:
                for i in range(total_records):
                    record_data = f.read(MFT_RECORD_SIZE)
                    record = MFTRecord(i, record_data)
                    try:
                        record.parse_header()
                        if record.is_active:
                            record.parse_attributes()
                            self.records.append(record)
                    except Exception as e:
                        logger.error("Errore nel record {}: {}".format(i, e))
                    # Visualizza progresso ogni 100 record
                    if (i + 1) % 100 == 0:
                        logger.info("Record analizzati: {} / {}".format(i + 1, total_records))
        except Exception as e:
            logger.error("Errore durante la lettura del file MFT: {}".format(e))
            sys.exit(1)

    def reconstruct_paths(self):
        logger.info("Ricostruzione dei percorsi completi...")
        # Costruisce un dizionario di record per riferimento rapido
        record_dict = {r.record_num: r for r in self.records if r.filename}
        for record in self.records:
            if not record.filename:
                continue
            try:
                path_components = []
                current = record
                depth = 0
                # Percorre i record genitori fino a raggiungere la radice (tipicamente record con parent_ref 5 o assente)
                while current and current.parent_ref and depth < 100:
                    path_components.insert(0, current.filename)
                    parent = record_dict.get(current.parent_ref)
                    current = parent
                    depth += 1
                record.full_path = "\".join(path_components)
            except Exception as e:
                logger.error("Errore nella ricostruzione del percorso per il record {}: {}".format(record.record_num, e))
        logger.info("Ricostruzione percorsi completata.")

    def evaluate_suspicion(self):
        for record in self.records:
            record.calculate_suspicion()
            if record.suspicion_score > 0:
                self.suspicious_files.append({
                    'record_num': record.record_num,
                    'filename': record.filename,
                    'full_path': record.full_path,
                    'suspicion_score': record.suspicion_score,
                    'reasons': record.suspicion_reasons
                })
        # Ordina per punteggio in ordine decrescente
        self.suspicious_files.sort(key=lambda x: x['suspicion_score'], reverse=True)

    def display_top_suspicious(self, top_n=10):
        print("\nI 10 file con il punteggio di sospetto maggiore:")
        print("{:<10} {:<30} {:<60} {:<10}".format("Record", "Nome File", "Path", "Punteggio"))
        print("-"*120)
        for file in self.suspicious_files[:top_n]:
            print("{:<10} {:<30} {:<60} {:<10}".format(
                file['record_num'], file['filename'][:30],
                (file['full_path'] if file['full_path'] else "N/A")[:60],
                file['suspicion_score']
            ))
            for reason in file['reasons']:
                print(" " * 12 + "- " + reason)
            print("-"*120)

def main():
    if len(sys.argv) != 2:
        print("Utilizzo: python mft_analyzer.py <percorso_del_file_MFT>")
        sys.exit(1)
    
    mft_file_path = sys.argv[1]
    parser = MFTParser(mft_file_path)
    parser.read_and_parse()
    parser.reconstruct_paths()
    parser.evaluate_suspicion()
    parser.display_top_suspicious()

if __name__ == "__main__":
    main()
  

Il codice sopra rappresenta un'implementazione completa per leggere e analizzare un file MFT. Ogni record valido viene processato e gli attributi vengono estratti con particolare attenzione al campo $FILE_NAME che è fondamentale per ricostruire il percorso completo del file. Inoltre, il codice gestisce i possibili errori durante il parsing e utilizza il modulo logging per una visualizzazione del progresso.

Approfondimenti Tecnici

Per garantire la robustezza dell'applicazione sono state considerate le seguenti tematiche:

Gestione degli Errori

Il sistema di gestione degli errori viene attivato durante la lettura del file, il parsing di ciascun record e l'estrazione degli attributi. La registrazione con il modulo logging assicura che ogni anomalia (come record non validi, errori di decodifica di stringhe Unicode, o iterazioni eccessive nella ricostruzione dei percorsi) sia segnalata in tempo reale. Questo approccio consente di isolare i problemi senza interrompere il processo di analisi globale.

Visualizzazione del Progresso

La visualizzazione del progresso è integrata direttamente durante l'iterazione sui record. Ogni 100 record analizzati, viene stampato un messaggio di progresso con il numero totale di record processati, consentendo agli operatori di monitorare la velocità della procedura e intervenire in caso di anomalie.

Ricostruzione dei Percorsi

La ricostruzione del percorso completo si basa sul tracciamento ricorsivo dei riferimenti ai record genitori. Considerando che la struttura NTFS prevede che le directory siano referenziate tramite record specifici, il software effettua un ciclo che concatena i nomi fino a raggiungere la directory di root o un limite preimpostato, evitando così loop infiniti. Questo permette di ottenere percorsi leggibili e corretti anche per file con nomi lunghi.

Calcolo del Punteggio di Sospetto

L'algoritmo per il calcolo del punteggio di sospetto viene eseguito solo per file aventi estensione .exe o .dll. I criteri implementati considerano:

  • La presenza delle stringhe nel path (ad es. "users\")
  • La lunghezza del nome (senza estensione)
  • Il pattern del nome che deve contenere caratteri casuali miscelati (minuscoli, maiuscoli e numerici) in una lunghezza non superiore a 8 caratteri
  • La presenza di sequenze ripetute di caratteri consecutivi

La combinazione di questi test consente di ottenere un punteggio quantitativo che aiuta a identificare potenzialmente file progettati in modo sospetto, utili per ulteriori analisi in ambito di sicurezza e forense.


Tabella Riassuntiva dei 10 File Sospetti (Esempio)

Di seguito viene mostrata una tabella esemplificativa dei 10 file con il maggior punteggio di sospetto, con le relative informazioni rilevanti:

Record Nome File Percorso Completo Punteggio di Sospetto
1023 AB1.exe C:\Users\Admin\Downloads\AB1.exe 18
874 XyZ.dll C:\Users\Public\XyZ.dll 15
567 QRS.exe C:\Users\User\AppData\Local\QRS.exe 12
432 LMN.dll C:\Users\Default\Documents\LMN.dll 11
320 OPQ.exe C:\Users\Admin\Desktop\OPQ.exe 10
210 RST.dll C:\Users\Guest\Downloads\RST.dll 9
185 UVWX.exe C:\Users\Public\Documents\UVWX.exe 9
150 YZ12.dll C:\Users\User\AppData\Roaming\YZ12.dll 8
98 ABCD.exe C:\Users\Admin\Downloads\ABCD.exe 8
66 EFGH.dll C:\Users\Default\Documents\EFGH.dll 7

Conclusioni e Considerazioni Finali

L'applicazione presentata fornisce un framework completo per la lettura, analisi e valutazione dei record MFT di una partizione NTFS. Dalla gestione degli errori, passando per la ricostruzione accurata dei percorsi fino all'assegnazione di un punteggio di sospetto basato su criteri ben definiti, ogni fase è progettata per facilitare un'approfondita analisi forense e di sicurezza.

Grazie ad un'architettura modulare e all'utilizzo del logging, il software non solo opera con efficacia ma offre anche la trasparenza necessaria per il debugging ed eventuali ottimizzazioni. Infine, la visualizzazione dei file sospetti consente di individuare rapidamente potenziali anomalie, rappresentando un utile strumento per analisti e specialisti in cybersecurity.

Riferimenti

Raccomandazioni e Ricerche Correlate

trustedsec.com
TSCOPY - TrustedSec
virustotal.it.softonic.com
VirusTotal Online

Last updated February 25, 2025

AI-assisted research. Sources, methodology and corrections · Source-checked guides and tools · Explore published answers

Ask Ithy AI