Questo argomento è stato trattato in systemsAndNetworksSecurity
Windows API (incl. COM)
L’API di Windows è l’API per la user mode del sistema per gli OS della famiglia Windows. Prima dell’introduzione delle versioni a 64 bit di Windows, era chiamata Win32API per distinguerla dall’API originale a 16 bit.
In origine, consisteva di sole funzioni con lo stile del linguaggio C, linguaggio tipicamente utilizzato alla nascita di Windows che era sufficientemente a basso livello per esporre servizi di sistema; d’altro canto, questo comporta un grande numero di funzioni unito alla mancanza di consistenza e raggruppamento logico.
Per risolvere questo problema, con le nuove API si utilizza un nuovo meccanismo, il Component Object Module (COM), che viene originariamente pensato per permettere alle applicazioni della suite Office di comunicare e scambiarsi documenti (abilità chiamata Object Linking and Embedding, OLE), implementata all’inizio usando un meccanismo di comunicazione chiamato Dynamic Data Exchange (DDE).
Il principio di base di COM prevede che i client comunichino con gli oggetti (COM server objects) mediante l’uso di interfacce, cioè dei metodi logicamente collegati, raggruppati in una tabella virtuale di dispatch (virtual table dispatch mechanism), un metodo che utilizzando anche i compilatori C++ per implementare il dispatch di funzioni virtuali. Questo comporta la compatibilità binaria e la rimozione di problemi legati al compilatore (compiler name mangling issue), ed è possibile richiamare questi metodi da più linguaggi di programmazione.
Il secondo principio è che l’implementazione dei componenti è caricata dinamicamente anziché essere linkata in modo statico al client. Da qui, il termine COM server tipicamente si riferisce a una Dynamic Link Library (DLL) o un eseguibile (exe) in cui le classi COM sono implementate.
Windows Runtime
Una nuova API viene introdotta a partire da Windows 8, insieme a un runtime di supporto: Windows Runtime (WinRT)1. Consiste di servizi di piattaforma (platform services) per lo sviluppo delle Windows App, applicativi in grado di avere come target diversi dispositivi con diversi form factor, dai PC alle Xbox.
WinRT è implementato con COM come base, aggiungendo varie estensioni all’infrastruttura di base COM. Le nuove app sono soggette a nuove regole, al contrario delle applicazioni “normali” (Windows Desktop Applications o Classic Windows Applications).
.NET framework
A comporre Windows c’è anche il framework .NET, a sua volta scomponibile in:
- Common Language Runtime (CLR): l’engine a runtime per .NET, che include un compilatore Just in Time, che traduce le istruzioni in Common Intermediate Language (CIL) nelle istruzioni dell’hardware sottostante, un garbage collector, verifica dei tipi, sicurezza per l’accesso al codice (code access security) e altro. L’engine è implementato come una DLL (nello specifico: COM in-process server) e usa varie funzioni offerte dalla Windows API;
- .NET Framework Class Library (FCL): una vasta collezioni di tipi che implementano funzionalità necessarie alle applicazioni client e server, come servizi per la UI, networking, accesso a database etc.
Applicazioni scritte in C++, C# e JavaScript2 possono utilizzare API WinRT con il framework .NET.

Services, functions and routines
Services
Service: definizione
Un service è una routine chiamabile all’interno dell’OS, un driver per periferiche o un processo server.
Alcuni esempi:
- Windows API functions: subroutine della Windows API che sono documentate e chiamabili (
CreateProcess,CreateFile,GetMessage); - sistemi nativi del sistema (system calls): non documentate, sono servizi dell’OS richiamabili in user mode (
NtCreateUserProcess, all’interno diCreateProcess); - kernel support functions (o routines): sono delle subroutine all’interno di Windows che possono essere chiamate solo in kernel mode (
ExAllocatePollWithTag, usata dei driver di periferiche per allocare memoria nell’heap di sistema, cioè nei pools); - windows services: processi avviati dal service control manager di Windows (ad esempio il servizio task scheduler, eseguito in user mode, per i comandi
schtasks- simili acronin UNIX); - Dynamic Link Libraries (DLLs): subroutines chiamabili linkate come file binario che possono essere dinamicamente caricate da applicazioni che usano le subroutines all’interno (
Kernel32.dll. Sono ampiamente usate dai componenti user-mode e hanno il vantaggio, rispetto alle librerie statiche, di poter essere condivise da più applicazioni, con l’OS che si assicura che vi sia una sola copia in memoria quando almeno un’applicazione referenzia quella DLL).
Processo
Un processo è, ad alto livello, composto da:
- uno spazio degli indirizzi privato e virtuale;
- un programma eseguibile, che definisce il codice e dati iniziali e viene mappato all’interno dello spazio degli indirizzi del processo;
- una lista di open handles, cioè la mappatura di risorse di sistema come semafori, oggetti di sincronizzazione e file che sono accessibili a tutti i threads nel processo;
- security context, cioè un access token che identifica l’utente, i gruppi di sicurezza, attributi, claims, capabilities, User Account Control (UAC) virtualization state, sessione e limited user account state associato al processo, insieme all’identificativo di AppContainer e le relative informazioni di sandboxing;
- process ID, identificativo univoco che è parte di un identificativo detto client ID;
- almeno un thread in esecuzione (è permessa la creazione di un processo vuoto, ma è prettamente inutile).
Stato di un processo in task manager
- Processi senza UI:
- running: caso normale. Questo è lo stato che si verifica quando tutti i threads sono in attesa di qualcosa (ad esempio un’operazione di I/O);
- suspended: quando tutti i threads del processo sono in uno stato di sospensione. È improbabile che accada normalmente, ma si può provocare chiamando l’API nativa
NtSuspendProcessattraverso dei tool;
- Windows Apps (hosting Windows Runtime):
- suspended: normalmente accade quando l’app perde lo stato di primo piano (l’utente minimizza tutte le finestre dell’applicazione), dopo 5 secondi in modo da non consumare CPU o risorse e permettendo alle nuove app in primo piano di ottenere tutte le risorse della macchina;
- not responding: può avvenire se un thread nel processo che è creato dall’interfaccia utente non ha controllato la coda dei messaggi per l’attività legata alla UI per almeno 5 secondi.
Processo genitore
Ogni processo punta anche al processo genitore o, in generale, al processo che l’ha creato. Se il genitore non esiste più, l’informazione non viene aggiornata ed è possibile che un processo faccia riferimento a un genitore non più esistente; in questa eventualità, non verrebbe a crearsi nessun problema perché nessun aspetto dipende dal fatto che quella informazione venga tenuta aggiornata.
Threads
Un thread è un’entità, parte di un processo, di cui Windows ne programma l’esecuzione. Ciascun thread ha le seguenti componenti essenziali:
- il contenuto dell’insieme dei registri della CPU, che rappresentano lo stato del processore;
- due stack, uno per il tread mentre viene eseguito in kernel mode e uno per l’esecuzione in user mode;
- area di storage privata, detta thread local storage (TLS), usata dai sottosistemi, librerie di runtime e DLL;
- identificatore univoco, detto thread ID, che è parte della struttura interna chiamata client ID e che viene generata nello stesso namespace del process ID, quindi non c’è sovrapposizione.
Alcune volte, i thread hanno il proprio security context, o token, che è spesso usato dal server di applicazioni multithreaded che “impersonificano” il contesto di sicurezza che i client servono. I registri volatili, gli stack e le aree private di storage compongono il contesto del thread (thread’s context); mediante la funzione GetThreadContext di Windows è possibile avere accesso a questa informazione architecture-specific.
Threads: fibers vs user-mode scheduling
Il cambiamento di esecuzione da un thread all’altro coinvolge il kernel scheduler e può essere un’operazione costosa, soprattutto se due threads si alternano spesso. Windows implementa due meccanismi per ridurre questo costo: fibers e user-mode scheduling (UMS).
Le fibers sono chiamate spesso “thread leggeri”, in quanto permettono a una applicazione di programmare l’esecuzione dei suoi thread anziché affidarsi al meccanismo basato su priorità integrato in WIndows. Sono invisibili al kernel e sono implementati in user mode, nella libreria Kernel32.dll.
I thread possono essere convertiti in fibers in esecuzione con una chiamata alla funzione di Windows ConvertThreadToFiber. Le fibers possono creare altre fibers con la funzione CreateFiber e ciascuna fiber può avere il suo insieme di fibers. Diversamente da un thread, l’esecuzione di una fiber non inizia finché non viene manualmente selezionata con la funzione SwtichToFiber; a quel punto, la fiber viene eseguita fino all’uscita o finché, a sua volta, seleziona una nuova fibra (cooperative multitasking).
Usare le fiber generalmente non è una buona idea, poiché sono invisibili al kernel. Condividono inoltre lo stesso thread local storage, poiché più fibers possono essere eseguite nello stesso thread (nonostante esista il fiber local storage - FLS, non risolve tutti i problemi, poiché le fibers I/O bound comunque avranno scarse prestazioni). Più fibers non possono essere eseguite in modo concorrente su più di un processore3 e vengono limitate solo al cooperative multitasking. Dunque, nella maggioranza degli scenari, è più conveniente lasciare che il kernel Windows gestisca lo scheduling usando i thread appropriati di volta in volta per il compito.
L’user-mode scheduling è disponibile solo per le versioni a 64 bit di Windows. I thread con UMS hanno il proprio stato dei thread e sono visibili al kernel. Il kernel permette più thread UMS di emettere system calls bloccanti e di condividere risorse. Quando due o più thread UMS hanno bisogno di eseguire del lavoro in user mode, possono periodicamente cambiare contesto d’esecuzione in un user-mode senza coinvolgere lo scheduler: dalla prospettiva del kernel, lo stesso kernel thread è in esecuzione e dunque nulla è cambiato. Ogni thread UMS ha il proprio thread context invece di condividere il contesto del singolo thread.
Quando un thread UMS esegue un’operazione che richiede l’entrata nel kernel (come una system call), passa al suo thread dedicato kernel mode (directed context switch); i thread UMS concorrenti comunque non possono essere eseguiti su più processori, ma seguono un modello pre-emptible che non è più solamente cooperativo.
Nonostante il contesto di esecuzione sia unico per ogni thread, ogni thread di un processo condivide lo spazio degli indirizzi virtuali e il resto delle risorse che appartengono al processo; tutti i thread hanno accesso completo per lettura e scrittura allo spazio di indirizzamento virtuale del processo. Non è permesso ai threads di fare riferimento, per errore, allo spazio di indirizzamento di un altro processo, a meno che:
- l’altro processo non renda disponibile parti del suo spazio degli indirizzi privati come una selezione di memoria condivisa (shared memory selection);
- il processo che possiede il thread ha il permesso di aprire un altro processo per usare funzioni di memoria cross-processo (cross-process memory functions).
I thread UMS hanno il loro scheduler, rappresentato un normale thread che si è convertito a UMS chiamando la funzione EnterUmsSchedulingMode ed è responsabile per la creazione, gestione ed eliminazione dei thread UMS. Lo scheduler di sistema determina quando il thread UMS scheduler viene eseguito in base alla sua priorità rispetto agli altri threads in stato di ready. Il processore su cui lo scheduler è eseguito è influenzato anche dall’affinità4 del thread, in modo uguale ai thread non UMS. Un’applicazione può creare uno scheduler UMS per ogni processore che viene utilizzato per eseguire i thread UMS.
Jobs
Con il concetto di job si estende il modello dei processi. Un job object permette la gestione e la manipolazione di gruppi di processi come un'unità e viene permesso il controllo di certi attributi, così come la creazione di limiti per il processo (o i processi) associati al job. Registra anche informazioni di accounting di base associate con il job e per tutti i processi associati con il job ma che sono terminati. Il job object compensa la mancanza di un albero di processi strutturati in Windows, presente in UNIX.
Memoria Virtuale
Windows implementa un sistema di memoria virtuale basato su uno spazio degli indirizzi lineare che fornisce ad ogni processo l’illusione di avere il proprio grande spazio degli indirizzi privato. A runtime, il gestore della memoria, con l’assistenza dall’hardware (cioè l’MMU) mappa gli indirizzi virtuali in indirizzi fisici in cui i dati sono effettivamente conservati. La dimensione dello spazio degli indirizzi virtuali cambia in base alla piattaforma hardware: sui sistemi a 32 bit, lo spazio degli indirizzi virtuali ha un massimo teorico di 4 GB, mentre sui sistemi a 64 bit viene fornito uno spazio degli indirizzi molto più grande (128 TB).
Di default, Windows alloca la metà più bassa dello spazio degli indirizzi ai processi per il loro storage privato (da 0x00000000 a 0x7FFFFFFF) e la metà superiore per la memoria protetta dell’OS (da 0x80000000 a 0xFFFFFFFF). Windows supporta le operazioni a boot time, come il qualifierIncreaseUserVa nel Boot Configuration Database, per dare ai processi che eseguono programmi segnati in modo apposito la possibilità di usare fino a 3 GB di spazio degli indirizzi privato, lasciando 1 GB all’OS[ ^notaSpazio].

Kernel Mode vs User Mode
Per evitare che le applicazioni utente accedano o modifichino dati cruciali per l’OS, Windows impiega due modalità di accesso: user mode e kernel mode.
Le applicazioni utente sono eseguite in user mode, mentre il codice dell’OS (eg. servizi di sistema e driver per i dispositivi) sono eseguiti in kernel mode, una modalità di esecuzione in un processore che permette l’acceso alla memoria del sistema e tutte le istruzioni della CPU stessa.
Hypervisor
Per fornire un servizio di virtualizzazione, quasi tutte le soluzioni moderne utilizzano un hypervisor, che è un componente specializzato e altamente specializzato che permette la virtualizzazione e l’isolamento di tutte le risorse sulla macchina. Per Windows, l’hypervisor di default è Hyper-V, usato anche per eseguire WSL2.
A causa della sua natura altamente privilegiata e perché ha accesso persino più grande del kernel stesso, può proteggere e monitorare una singola istanza dell’host per offrire garanzie aggiuntive rispetto a quello che il kernel può fornire.
Servizi di terminale
Windows supporta più sessioni utente interattive su un singolo sistema (i terminali). Con Windows Terminal Services un utente remoto può stabilire una connessione con un’altra macchina, accedere ed eseguire applicazioni sul server; il server trasmette l’interfaccia grafica al client che, a sua volta, trasmette l’input dell’utente al server. Le edizioni per client di Windows permettono un singolo utente remoto di essere connesso alla macchina, mentre le edizioni per server alzano il limite a due utenti in contemporanea.
Oggetti e handles
Kernel Object: definizione
In Windows, un kernel object è una singola istanza a runtime di un tipo oggetto staticamente definito.
Un kernel object comprende:
- tipo di dato definito a livello di sistema;
- funzioni che operano su istanze del tipo di dato;
- un insieme di attributi dell’oggetto/
Questi oggetti sono basati su oggetti di più basso livello che Windows crea e gestisce. Un processo, ad esempio, è un’istanza del tipo process object e un file è un’istanza del tipo file object. Gli handles sono le istanze degli oggetti.
Un attributo di un oggetto è un campo di dati in un oggetto che definisce in modo parziale lo stato dell’oggetto. Per esempio, un oggetto di tipo processo può avere attributi che includono il process ID, una priorità di base per lo scheduling e un puntatore a un oggetto di tipo access token.
Un metodo di oggetto permette la manipolazione di un oggetto, generalmente lettura e cambiamento di attributi dell’oggetto. Per esempio, il metodo per l’apertura di un processo può accettare un identificativo di un processo come input e ritornare un puntatore all’oggetto come output.
La struttura interna di un oggetto non è nota e non è possibile vedere l’intera struttura dati; è necessario chiamare un object service per ottenere o inserire dati in un oggetto, ma non è possibile direttamente leggere o cambiare dati in un processo.
Gli oggetti, mediante l’aiuto dell’object manager (cioè gli handlers), forniscono un mezzo pratico per:
- fornire nomi leggibili da umani per le risorse di sistema;
- condividere risorse e dati tra processi;
- proteggere risorse da accessi non autorizzati;
- reference tracking, che permette al sistema di riconoscere quando un oggetto non è più in uso e può essere dunque deallocato.
Non tutte le strutture dati sono oggetti: solo i dati che devono essere condivisi, protetti, nominati o resi visibili a programmi in user-mode sono messi in oggetti. Le strutture sono usate solo da un componente dell’OS per implementare funzioni interne non sono oggetti.
Registro
I registri sono il database del sistema che contiene tutte le configurazioni del sistema stesso:
- le informazioni necessarie al boot e la configurazione del sistema;
- impostazioni per software-system wide che controllano le operazioni di Windows;
- il database di sicurezza;
- configurazione delle impostazioni di ciascun utente, come lo screensaver da usare.
Il registro fornisce una finestra per i dati volatili in memoria, come lo stato corrente dell’hardware del sistema e i performance counters di Windows.
Sicurezza
Windows è progettato per essere sicuro ed essere conforme con i requisiti di vari governi formali e ratings sulla sicurezza come il Common Criteria for Information Technology Security Evaluation (CCITSE). Le capabilities core per la sicurezza di Windows includono:
- protezione discretionary e mandatory per tutte gli oggetti di sistema condivisibili, come file, directory, processi, threads etc;
- security auditing per l’accountability dei soggetti - o utenti - e le azioni che intraprendono (user authentication at logon?);
- evitare che un utente acceda a risorse non inizializzate, come memoria libera o spazio su disco, che un utente ha deallocato.
I requisiti che hanno portato alla specifica di Windows NT sono:
- fornire un OS con una memoria virtuale realmente a 32 bit, preempitive, reentrant5;
- essere eseguito su più architetture hardware e piattaforme;
- esere eseguito e scalare bene su sistemi multiprocessing simmetrici6;
- essere una grande piattaforma di computing distribuita, sia come rete di client che come server;
- eseguire la maggioranze delle app a 16 bit per MS-DOS e Microsoft Windows 3.1;
- rispettare i requisiti dei governi per la compliance POSIX 1003.1;
- rispettare i requisiti dei governi e dell’industria per la sicurezza degli OS;
- essere facilmente adottabile dal mercato globale per il supporto a Unicode.
I goal che si volevano ottenere:
- estensibilità (extensibility): il codice deve essere scritto per crescere in modo comodo e cambiare in base ai cambiamenti dei requisiti del mercato;
- portabilità (portability): il sistema deve essere in grado di essere eseguito su più architetture hardware e deve essere in grado di spostarsi con relativa facilità su nuove architetture in base alla domanda del mercato;
- affidabilità e robustezza (reliability and robustness): il sistema deve proteggersi da malfunzionamenti interni e tampering esterno. Le applicazioni non devono essere in grado di rappresentare danno all’OS o ad altre applicazioni;
- compatibilità (compatibility): nonostante Windows NT debba estendere tecnologia esistente, la sua interfaccia utente e le sue APIs devono essere compatibili con le versioni più vecchie di Windows e con MS-DOS. Deve essere anche interoperabile bene con altri sistemi, come UNIX, OS/2 e NetWare;
- prestazioni (performance): nei limiti di altri obiettivi di progettazione, il sistema deve essere quanto più veloce possibile e responsive su ogni piattaforma hardware.
Architettura del sistema

Architecture: Kernel Mode
La kernel mode è scomponibile in componenti chiave:
- executive: contiene i servizi base dell’OS, come la gestione della memoria, la gestione dei thread e dei processi, I/O, networking e IPC;
- Windows kernel: consiste di funzioni di basso livello dell’OS, come la schedulazione dei threads, l’invio di interruzioni ed eccezioni e la sincronizzazione multiprocessore. Fornisce anche un insieme di routine e di oggetti di base che il resto del componente executive usa per implementare costrutti di alto livello;
- device drivers: include sia i driver per dispositivi hardware, che traducono le chiamate di funzioni I/O dell’utente in richieste specifiche del dispositivo hardware di I/O, e dispositivi non hardware come filesystem e driver di rete;
- Hardware Abstraction Layer (HAL): uno strato di codice che isola il kernel, i driver dei dispositivi e il resto dell’executive dalle differenze hardware relative alle specifiche piattaforme (come diverse schede madri);
- windowing and graphics systems: implementa le funzioni della GUI, come le finestre, i controlli dell’interfaccia e il drawing.
Ad un livello inferiore c’è l’hypervisor layer, che non include driver o altri moduli all’interno, ma è composto a sua volta da più strati e servizi interni, come un gestore della memoria, uno scheduler per il processore virtuale, gestione di interruzioni e timer, routine per la sincronizzazione, partizionamento (cioè le istanze delle virtual machine), inter-partition communication e altro.
Virtualization-based Security (VBS)
L’hypervisor fornisce un nuovo insieme di servizi, noti come Virtualization-based Security (VBS):
- device guard: fornisce l’Hypervisor Code Integrity (HVCI) per garanzie maggiori relativamente alla firma del codice e permette la personalizzazione della politica di firma di Window, sia per la user mode che per la kernel mode;
- hyper guard: protegge i dati chiave relativi al kernel e all’hypervisor, le strutture e il codice;
- credential guard: impedisce accesso non autorizzati alle credenziali e ai segreti dell’account di dominio, insieme alla biometria sicura;
- application guard: fornisce un sandboxing ancora più forte per il browser Edge;
- host guardian e shielded fabric: sfrutta un TPM virtuale (v-TPM) per proteggere una macchina virtuale dall’infrastruttura su cui viene eseguita. Questi componenti non sono vulnerabili in caso di driver mal scritti o malevoli, a prescindere da se siano firmati o meno.
La separazione tra la user mode e la kernel mode fornisce protezione per l’OS dal codice in user mode, a prescindere dal fatto che sia malevolo o meno. Se una parte di codice kernel mode non desiderata entra nel sistema, questo è fondamentalmente compromesso perché tutto il codice kernel mode ha accesso completo al sistema (l’esempio è il codice malevolo inserito nei driver). L’hypervisor fornisce garanzie addizionali contro gli attacchi, creando un insieme di capabilities VBS che estendono la normale separazione basata su privilegi (privilege based separation) attraverso l’introduzione dei Virtual Trust Levels (VTLs).
Virtual Secure Mode
Virtual Secure Mode (VSM) è un insieme di capabilites e ottimizzazioni dell’hypervisor che offrono alle partizioni host e guest la creazione e la gestione di nuovi confini di sicurezza all’interno del software del sistema operativo. Sono state introdotte con Windows 10 e Windows Server 2016 e sono attive di default se l’hardware lo supporta, mentre in versioni più vecchie di Windows 10 si possono attivare usando una policy o con il menù delle funzionalità di Windows (voce: Isolated User Mode).
Con VSM viene reso possibile per le partizioni host e guest di creare regioni di memoria isolata per lo storage e per l’utilizzo di risorse di sicurezza del sistema. L’accesso a queste regioni isolate è controllato e permesso solamente attraverso l’hypervisor, che è una parte ad alti privilegi e alta fiducia del Trusted Compute Base (TCB) del sistema e, dunque, l’hypervisor è eseguito a un livello di privilegi più alto del software del sistema operativo. Questo gli consente di avere il controllo esclusivo delle risorse hardware chiave del sistema operativo e può proteggere le regioni isolate da un accesso non autorizzato, anche da parte del software del sistema operativo.
Virtual Trust Levels
VSM ottiene e mantiene l’isolamento mediante i Virtual Trust Levels (VTLs), abilitati e gestiti sia per partizione che per processore virtuale. I VTL sono gerarchici, con i livelli più alti a privilegi maggiori rispetto a quelli a basso livello (VTL0 è il livello meno privilegiato, VTL1 lo è menlo di VTL2, etc), con un massimo di 16 livelli supportati.
Ogni VTL ha il proprio insieme di protezioni per l’accesso alla memoria, che sono gestite dall’hypervisor in uno spazio degli indirizzi fisico della partizione (partition’s physical address space). Non possono essere modificati dal livelli software del sistema che sono in esecuzione nella partizione.
I VTL con privilegi più alti possono applicare la protezione della propria memoria; un VTL più basso può proteggere le sue regioni di memoria mettendole al sicuro in un VTL più alto (VTL0 può salvare un secreto in VTL1, così il segreto è al sicuro anche se VTL0 è compromesso, ma il segreto può avere accesso solo da VTL1).
Concetti generali
- Memory access protection: ogni VTL mantiene un insieme di protezioni dell’accesso alla memoria fisica guest. Software in esecuzione a un certo VTL può accedere solo a memoria con rispetto a queste protezioni;
- virtual processor state: i processori virtuali mantengono uno stato separato per VTL. Ogni VTL definisce un insieme di registri privati e software in esecuzione a VTL più basso non può accedere allo stato dei registri del processore virtuale di VTL più alto;
- interrupts: insieme a uno stato del processore separato, ogni VTL ha anche il proprio sottosistema delle interruzioni. Questo permette VTL più alti di elaborare interruzioni senza rischiare interferenze da un VTL più basso;
- overlay pages: sono delle pagine di memoria salvate separatamente per ogni VTL, in modo che VTL più alti abbiano accesso affidabile e isolato ad alcuni dati e strutture.
Nella pratica, il codice utente e kernel viene eseguito sull’hypervisor Hyper-V. Con VBS abilitato, un VTL1 è presente e contiene il proprio kernel sicuro, in esecuzione nella modalità privilegiata del processore. Una modalità di ambiente per l’utente runtime, detta Isolated User Mode (IUM) è eseguita in maniera non privilegiata. Il kernel sicuro possiede il proprio binario separato (securekernel.exe).
La IUM è sia (a) un ambiente che restringe le chiamate di sistema permesse che le DLL nella user mode standard possono effettuare, sia (b) un framework che aggiunge un sistema sicuro speciale che può essere eseguito solo in VTL1. Le syscall aggiuntive sono esposte in maniera simile alle normali chiamate di sistema. Un meccanismo di copy-on-write impedisce che le applicazioni a VTL0 possano effetuare modifiche ai binari usati in VTL1.
Il codice della kernel mode in esecuzione a VTL0 non può alterare la user mode in esecuzione a VTL1, poiché VTL1 ha più privilegi. Il codice della user mode a VTL1 non può alterare la kernel mode in esecuzione a VTL0 poiché la user mode non può comunque alterare il kernel. Le applicazioni in user mode a VTL1 devono comunque passare per le syscall di Windows e i rispettivi controllo accessi se desiderano accedere alle risorse.

VBS is isolation
I livelli di privilegi (user mode e kernel mode) applicano il potere, mentre i VTL applicano l’isolamento. Le applicazioni in user mode a VTL1 sono meno potenti di applicazioni o driver a VTL0 e sono isolati. Poiché il kernel sicuro non implementa tutte le capability di sistema, si sceglie a mano quali chiamate di sistema saranno inoltrate al kernel a VTL0, ricordando che (a) ogni forma di I/O (inclusi file, reti e registri) è completamente proibita e (b) la grafica è fuori discussione poiché non è consentita la comunicazione con nessun driver.
Second Level Address Translation (SLAT)
Il kernel sicuro non ha accesso completo alla memoria e alle risorse di VTL0. Può usare l’hypervisor per limitare l’accesso all’OS in VTL0 a determinate posizioni di memoria sfruttando il supporto hardware della CPU noto come Second Level Address Translation (SLAT), che è anche alla base di Credential Guard che può conservare segreti in queste locazioni. Il kernel sicuro può usare la tecnologia SLAT per interdire e controllare l’esecuzione di locazioni di memoria, presupposto fondamentale per Device Guard.

Impedire il bypass di SLAT
Per impedire che driver di periferiche sfruttino dispositivi hardware per accedere direttamente alla memoria, il sistema usa l’I/O memory management unit (MMU); questo si usa anche per impedire che i driver di periferiche usino direttamente il directory memory access (DMA) per accedere direttamente all’hypervisor o a regioni di fisiche memoria del secure kernel: questo bypasserebbe SLAT poiché non è coinvolta la memoria virtuale.
Boot del sistema
In fase di boot, l’hypervisor è il primo componente del sistema che viene lanciato dal boot loader. Può programmare SLAT e l’I/O MMU definendo gli ambienti di esecuzione VTL0 e VTL1. Mentre in VTL1, il bootloader è nuovamente eseguito, caricando il secure kernel che può configurare il sistema ulteriormente sulla base delle necessità. Solo in seguito, quando il VTL viene abbassato, si esegue il kernel normale in VTL0 (VTL0 jail) e non può fare escape.
Trustlets
Classi speciali di binari a cui è permessa l’esecuzione in VTL1. Ogni trustlet ha un identificativo univoco e una firma. Il secure kernel ha una conoscenza hardcoded di quali trustlet sono state create a un certo punto ed è impossibile crearne di nuove senza accedere al kernel sicuro (che solo Microsoft può alterare) e le trustlet esistenti non possono essere patchate in nessun modo.
Key system components

Environment subsystems
L’executive di Windows è organizzato in sottosistemi (subsystems). Ogni subsystem può fornire accesso a diversi sottoinsiemi dei servizi nativi in Windows.
Il compito di un subsystem è l’esposizione di servizi di base del sistema executive di Windows alle applicazioni. I subsystems determinano cosa può fare un’applicazione; ciascuna immagine eseguibile (.exe) è legata a un unico subsystem.
Quando un’immagine è eseguita, il codice di creazione del processo esamina il codice del tipo di subsystem nell’intestazione dell’immagine, in modo che possa notificare il nuovo processo al sottosistema appropriato. Le DLL del sottosistema Windows (Kernel32.dll, User32.dll, Advapi32.dll, Gdi32.dll) implementano le funzioni API di Windows. Le DLL del sottosistema SUA7 (Psxdll.dll) sono usate per implementare le funzioni API SUA, sulle versioni di Windows che supportano POSIX.
Quando un’applicazione chiama una funzione in un sottosistema DLL, può accadere una tra le seguenti tre cose:
- la funzione è implementata direttamente in user mode all’interno del sottosistema DLL. Nessun messaggio è inviato al processo del sottosistema e non vengono richiamati servizi dell’executive di Windows, la funzione è eseguita in user mode e il risultato è restituito al chiamante;
- la funzione richiede una o più chiamate all’executive di Windows;
- la funzione richiede del lavoro che deve essere svolto nel processo dell’environment subsystem che, eseguito in user mode, è responsabile di mantenere lo stato delle applicazioni del client in esecuzione sotto il loro controllo.
Subsystem startup
I subsystem sono avviati dal processo Session Manager (Smss.exe) e le informazioni sullo startup del sottosistema sono conservate nella chiave di registro HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\SubSystems.
Il valore Required nel registro elenca i sottosistemi che sono caricati all’avvio del sistema, e può avere due stati Windowse Debug. Il valore Windows contiene le specifiche del file del sottosistema Windows, Csrss.exe (Client-Server Runtime Subsystem).
Windows subsystem
Windows è il sottosistema primario in cui sono conservate tutte le funzioni di base. Gli altri sottosistemi chiamano il sottosistema Windows per svolgere l’I/O a display. Il sottosistema Windows è un componente richiesto per ogni sistema Windows, anche server o senza utenti interattivi loggati.
Windows viene progettato per supportare più environment subsystem indipendenti. Con ogni sottosistema che implementa tutto il codice per gestire le finestre e l’I/O Grafico, si incorrerebbe in un grande quantitativo di duplicazione delle funzioni di sistema, che impatterebbe negativamente sia la dimensione del sistema che le prestazioni.
Windows subsystem components
Footnotes
-
Il runtime non deve essere confuso con Windows RT, un flavour di Windows per dispositivi ARM. ↩
-
Usando l’estensione WinJS. ↩
-
Si intende “processore logico”. Quindi, nelle CPU multicore, un singolo core; nelle CPU multithreaded (SMT - Simultaneous MultiThreading - come Hyper-Threading di Intel) si intende un singolo core logico (quindi, se la CPU è 4C8T, si prende un “thread” di uno dei core). ↩
-
Da Gemini: Affinity is essentially a mask or a set of allowed processors. When the Windows kernel scheduler decides which thread to run next, it only considers the logical processors included in that thread’s affinity mask. Di default è impostata uguale per tutti, ma può essere modificata per evitare/favorire alcuni fenomeni (eg. cache misses). ↩
-
Reentrant (or Reentrancy) describes the quality of code, particularly kernel code or shared library code, that can be safely executed by multiple threads concurrently. ↩
-
TL;DL: stessa mobo, stessa memoria, due processori (quindi due socket fisiche con due CPU e non solo una singola CPU con più core/Hyperthreading). ↩
-
SUA sta per * Subsystem for UNIX-based Applications*. ↩