Skip to content

Latest commit

 

History

History
271 lines (165 loc) · 51.1 KB

process-emulation.md

File metadata and controls

271 lines (165 loc) · 51.1 KB

Lingue: English | 简体中文 | 繁體中文 | 日本語 | 한국어 | Français | Deutsch | Español | Italiano | Русский | العربية

Aggiornamento: per poll/epoll e stdio con buffer, vedere il contratto attuale. Le note di questa pagina che limitano poll a timeout zero e GKI descrivono il sottoinsieme precedente.

← Indice della documentazione

Emulazione dei processi guest

neverd emulate esegue un’immagine con un profilo esplicito del sistema operativo guest. Trasporto CPU, parsing dell’immagine, ingresso del processo e servizi OS hanno responsabilità separate. Attivare NEVERD_ENABLE_CPU_EMULATION=ON; è incluso anche dall’emulazione dei driver.

Il primo profilo, linux-elf64-v1, esegue ELF ET_EXEC x64/AArch64 e PIE statici ET_DYN autorilocanti a CPL3 o EL0. Carica veri segmenti ELF, crea lo stack iniziale, riprende a quanti e gestisce richieste esplicite di system call Linux. È un modello di processo autonomo, non una distribuzione Linux completa né una promessa di eseguire binari libc arbitrari. Linking dinamico, segnali, thread, filesystem e servizi non supportati falliscono esplicitamente.

CLI e SDK

neverd emulate guest.elf --profile=linux-elf64-v1 \
  --options='{"backend":"auto","arguments":["guest","argument"],"environment":["MODE=test"],"instruction_limit":100000}'

Host Linux compatibili selezionano KVM e host Windows compatibili WHP; le altre combinazioni ISA host/guest usano Unicorn. Un backend selezionato ma indisponibile è un errore, senza fallback silenzioso. L’immagine ELF usa il modello Linux anche se eseguita su Windows. Vedi esecuzione CPU per inventario istruzioni e limiti.

Il CLI emette un singolo report JSON. Exit code 0 per status guest zero, 2 per un altro status, 3 per esecuzione incompleta (inclusi fault e limiti), 1 per setup/API non valido. Lo status guest effettivo è in exit_status. L’entry point C aggiuntivo neverd_emulate_process_json accetta sessione, path non vuoto, profilo esplicito e JSON opzioni facoltativo. Liberare il risultato con neverd_free_string; NULL segnala errore di setup descritto da neverd_last_error. Fault guest o stop per risorse restituiscono un report. L’immagine d’analisi caricata nella sessione non serve e non viene modificata.

report = session.emulate_process(
    "guest.elf", "linux-elf64-v1",
    '{"backend":"unicorn","arguments":["guest"],"environment":[]}',
)
output = bytes.fromhex(report["stdout_hex"])

Opzioni e risultati

Le opzioni sono un oggetto JSON massimo 64 KiB. Campi sconosciuti/null, tipi errati, NUL incorporati e limiti non positivi vengono rifiutati.

Opzione Predefinito Contratto
backend auto auto, unicorn, kvm o whp
arguments Nome input argv completo incluso argv[0]; vuoto usa il default
environment [] Stringhe guest esplicite; non eredita l’ambiente host
instruction_limit 100000 Tentativi d’istruzione ammessi condivisi
event_limit 10000 Eventi syscall, addebitati prima del servizio OS
timeout_microseconds 5000000 Deadline monotona avviata dopo il setup del processo
memory_limit 67108864 Budget di memoria fisica/mappata
stack_size 1048576 Stack allineato alla pagina entro il budget
output_limit 1048576 Byte complessivi catturati da stdout/stderr
instruction_quantum 1024 Intervallo d’ammissione prima di cedere alla runtime
linux_kernel Assente Ramo GKI esplicito, catalogo fisso di attività guest o interfacce del kernel osservate assenti
linux_priority Assente Valori nice espliciti per task e autorità del chiamante per i servizi Linux di priorità grezzi

schema_version è 1. Il report contiene profilo, architettura, backend e motivo della scelta, stop_reason, exit_status nullable, diagnostica, PC di ingresso/corrente, contatori, record dei servizi e ultimo esito CPU tipizzato. Indirizzi, numeri syscall, registri argomento e bit di ritorno sono stringhe esadecimali senza 0x; stdout_hex/stderr_hex preservano NUL e UTF-8 non valido. Un risultato syscall null significa nessun ritorno modellato (per esempio exit o richiesta non supportata), non zero riuscito.

Semantica del profilo Linux

writev condivide queste uscite su x64/ARM64 e tramite Android Bionic. Importa fino a 1024 voci iovec prima dell’output, rifiuta lunghezze negative con EINVAL, verifica gli intervalli utente e applica il limite Linux allineato alle pagine. Un descrittore errato restituisce EBADF prima dell’accesso al vettore; metadati illeggibili restituiscono EFAULT senza output. Un errore successivo nei dati conserva il prefisso copiato. Il budget copre l’intero vettore ed entrambi i flussi prima della pubblicazione. write e writev usano i 32 bit bassi del descrittore; anche il conteggio segue l’importazione Linux a 32 bit. Solo Bionic converte gli errori negativi in -1 ed errno. LinuxOutputNativeTests esegue dieci casi originali su Linux host con reindirizzamento a file ordinari; i casi x64/ARM64 modellati verificano anche i budget. Vedi il contratto di importazione Linux.

La policy OS riusa gli header di programma già decodificati dal loader ELF. Verifica tag ABI, allineamento segmenti, tabelle degli header mappate e limiti degli indirizzi user. Un piano generico controlla estensioni, permessi, sovrapposizioni e budget prima dell’allocazione e pubblica solo uno spazio privato completo. Conserva prefissi/code delle pagine file, azzera BSS, rispetta i permessi e riserva guard gap dello stack. Layout con pagine sovrapposte e header contraddittori sono rifiutati, non indovinati.

Il PIE statico usa un load bias deterministico di almeno 0x40000000, aumentato per rispettare l’allineamento PT_LOAD maggiore. Segmenti, PC d’ingresso e AT_PHDR/AT_ENTRY usano lo stesso bias; i program header originali restano invariati e AT_BASE vale zero senza interpreter. Il mapping usa i byte originali del file, non i fixup dell’analisi; il guest esegue autonomamente relocation e inizializzazione. Il loader decodifica PT_DYNAMIC da record limitati del file originale, senza dipendere dalle section. Se presente, la tabella deve essere leggibile, terminata e contenere al massimo 4096 entry. PT_INTERP e tag esterni di dipendenze/filter/audit sono rifiutati; non c’è dynamic linker, risoluzione simboli o esecuzione costruttori.

Lo stack iniziale contiene argc/argv/envp/auxv allineati, PHDR/PHENT/PHNUM, entry, dimensione pagina e identità. PID/TID/UID/GID modellati valgono 1000. AT_RANDOM contiene i primi 16 byte dello SHA-256 dell’input per riproducibilità; è una policy deterministica del modello, non entropia crittografica. HWCAP/HWCAP2 sono zero; non esiste vDSO.

Sono implementate write, writev, exit, exit_group, getpid e gettid, mmap, mprotect, munmap, brk, con numeri distinti per x64 e ARM64. Il ritorno di SYSCALL x64 applica i clobber RCX/R11, RAX e il PC successivo. ARM64 usa x8 per il numero e x0 per il risultato. Le altre chiamate si fermano come unsupported_service; non vengono eseguite syscall host.

I template TLS statici PT_TLS sono validati come dati del loader: un solo template, estensioni file/memoria limitate, allineamento congruente e byte iniziali leggibili. L’avvio guest alloca e inizializza i blocchi TLS e installa il thread pointer; il modello Linux non inventa un TCB/DTV specifico di libc. Questo permette TLS local-exec generato dal compilatore nei programmi freestanding. TLS dinamico e thread OS restano fuori ambito.

Su x64, arch_prctl supporta ARCH_SET_FS, ARCH_GET_FS, ARCH_SET_GS e ARCH_GET_GS. Set accetta una base user anche non mappata; le dereference successive controllano comunque i permessi. Basi kernel restituiscono EPERM guest; destinazioni Get non valide restituiscono EFAULT senza fault CPU. Le altre operazioni falliscono esplicitamente. ARM64 installa TPIDR_EL0 con MSR; MRS, accessi FS/GS e restore del contesto preservano il thread pointer fra quanti e ingressi backend. Non implementa uno scheduler.

I descrittori 1 e 2 sono sink virtuali di byte. write convalida pagine user leggibili, restituisce il prefisso leggibile se una pagina successiva non è accessibile ed EFAULT se nessun byte è leggibile. Un descrittore errato restituisce EBADF; una write di zero byte verifica comunque l’intervallo utente, senza richiedere pagine mappate né leggere dati. Atomicità delle pipe Linux e file non sono modellati. Il limite output ferma prima di pubblicare una scrittura eccedente.

I servizi di memoria anonima condividono spazio del processo e budget fisico con immagine e stack. mmap accetta esattamente MAP_PRIVATE | MAP_ANONYMOUS, con PROT_NONE, PROT_READ, PROT_READ | PROT_WRITE, PROT_READ | PROT_EXEC o RWX leggibile. Rispetta suggerimenti liberi e allineati a pagina; altrimenti cerca spazi da 0x100000000, poi dall'indirizzo utente minimo, riservando le guardie dello stack. Questa disposizione deterministica non emula ASLR Linux. Le pagine nuove sono indipendenti e azzerate; la rimozione parziale può recuperare pagine non vincolate. Una proiezione CPU o vista del backing ancora conservata può mantenere viva un'allocazione ritirata fino al termine della propria durata.

Le lunghezze sono arrotondate a pagine. munmap tollera buchi e rimozioni ripetute; mprotect modifica il prefisso mappato prima di restituire ENOMEM al primo buco. PROT_NONE conserva allocazione e byte, negando l'accesso guest. Il brk grezzo restituisce il limite richiesto in caso di successo e quello precedente in caso di errore, non lo zero/meno uno del wrapper libc. Il limite iniziale è la fine immagine allineata a pagina. La crescita rispetta altre mappature e budget; la riduzione conserva i byte della pagina parziale restante. Regole e priorità degli errori seguono i servizi Linux di mappatura e protezione.

Mappature di file, condivise o fisse, crescita verso il basso, pagine enormi, blocco memoria, chiavi di protezione, permessi di sola esecuzione/scrittura e altri flag restano esplicitamente non supportati: arresto prima di pubblicare effetti o inventare un ritorno. Errori ordinari di intervallo, lunghezza e allineamento nel sottoinsieme ammesso restituiscono errori guest e consentono di proseguire. Nessun puntatore o richiesta di mappatura guest viene inoltrato all'OS host.

L’input facoltativo linux_kernel registra l’assenza esplicitamente osservata di interfacce del kernel. Per esempio, un fixture senza implementazione di pidfd_open usa:

{"linux_kernel":{"unavailable_syscalls":["pidfd_open"]}}

La chiamata raw selezionata restituisce -ENOSYS prima della validazione degli argomenti, come un ingresso kernel assente, senza creare descrittori o modificare memoria guest. Il wrapper syscall di Bionic conserva la normale conversione -1/errno. Input omesso e lista vuota mantengono il precedente limite non supportato dell’interfaccia; altre chiamate sconosciute non diventano ENOSYS. Attualmente è ammesso solo pidfd_open; nomi sconosciuti, duplicati e tipi errati sono rifiutati. L’input non deduce versione del kernel, disponibilità sull’host o un’implementazione pidfd funzionante. Vedere l’implementazione kernel delle chiamate mancanti.

Un linux_kernel.gki esplicito sceglie un ramo Android common pubblicato da 5.10 a 6.18. Il sottoinsieme implementa pidfd_open per il processo vivo del modello e i task guest dichiarati, oltre all’importazione vettoriale per versione. Condivide la tabella di linux_files; Bionic e trap grezzi condividono proprietà e ordine degli errori. GKI non può coesistere con un’osservazione di pidfd_open assente. L’array facoltativo linux_kernel.tasks dichiara un catalogo fisso e chiuso di altri task vivi, con voci come { "id": 2000, "group_leader": true }. L’omissione lascia non supportata la ricerca esterna; un array vuoto conosce solo il leader corrente. Un PID fuori dal catalogo dichiarato restituisce ESRCH. Catalogo e osservazioni di priorità devono essere coerenti; i thread guest Android cooperativi sono incompatibili. Il livello API Android non sceglie il kernel. Otto sorgenti fissati, descrittori, test e limiti sono nei contratti GKI pubblicati.

L’input facoltativo linux_priority dichiara lo stato nice delle attività di prova con l’UID del chiamante. Le chiamate grezze setpriority e getpriority condividono questo stato tra Linux ELF64 e Android senza cambiare le priorità dell’host.

{"linux_priority":{"tasks":[{"id":1000,"nice":0}],
                   "cap_sys_nice":false,"rlimit_nice":0}}

Gli ID sono valori positivi distinti con segno a 32 bit; nice iniziale va da -20 a 19 e rlimit_nice da 0 a 40. cap_sys_nice e rlimit_nice valgono false e 0 per default; lo stato è sempre esplicito. Input assente, attività non elencate —inclusi nuovi thread senza stato dichiarato— e PRIO_PGRP/PRIO_USER interrompono il servizio come non supportato senza dedurre ereditarietà o proprietà. PRIO_PROCESS con who=0 seleziona l’attività guest corrente, altrimenti quella indicata. Un selettore invalido restituisce -EINVAL grezzo. Le richieste di modifica limitano nice con segno a 32 bit a -20..19. Ridurre nice richiede CAP_SYS_NICE o un RLIMIT_NICE sufficiente; il rifiuto restituisce -EACCES grezzo senza modificare lo stato. La lettura grezza restituisce 20 - nice, la codifica del kernel 40..1, non il risultato tradotto di libc.

Linux setpriority/getpriority.

linux_signals fornisce le azioni iniziali dei segnali per l’intero processo. Una voce assente è sconosciuta e non implica SIG_DFL; un elenco esplicitamente vuoto consente di installare senza leggere l’azione precedente. Tutti e cinque i campi sono obbligatori; i valori senza segno a 64 bit fuori dall’intervallo esatto di JSON usano stringhe decimali.

{"linux_signals":{"actions":[
  {"signal":11,"handler":0,"flags":0,"restorer":0,"mask":0}
]}}

I numeri dei segnali vanno da 1 a 64. rt_sigaction e Bionic condividono lo stato, con layout e ordine degli errori propri di ogni interfaccia. Non sono implementati segnali in attesa, consegna, chiamate ai gestori o maschere per thread; non si usano gestori dell’host. Vedere il contratto completo.

linux_kernel.gki seleziona 5.10–6.18 per pidfd_open e import vettoriale; API Android non sceglie kernel. linux_kernel.tasks dichiara attività vive fisse come { "id": 2000, "group_leader": true }. Omissione: target esterni non supportati; array vuoto: solo leader corrente; fuori catalogo: ESRCH. Priorità coerenti e assenza di thread Android cooperativi sono richieste. La tabella si condivide con linux_files; GKI e pidfd assente sono incompatibili. Vedere il contratto completo.

Il sottoinsieme GKI comprende anche orologi CPU di processo osservati e pidfd ppoll con timeout zero. Quest’ultimo richiede timespec zero esplicita, maschera temporanea nulla e limite linux_files. Scrive solo revents ordinati senza riscrivere il timeout o avanzare l’orologio reale. Un descrittore chiuso produce POLLNVAL; un pidfd vivo non è pronto. Attese bloccanti e disponibilità di altri tipi restano non supportate.

Orologi guest espliciti

L’opzione linux_time fornisce valori fissi alle chiamate Linux e ad Android Bionic. Il modello non legge l’orologio host, non fa avanzare il tempo con le istruzioni e non presume un’epoca predefinita.

{"linux_time":{"clocks":[
  {"id":0,"seconds":"4294967297","nanoseconds":987654321},
  {"id":1,"seconds":123,"nanoseconds":456789}],
  "timezone":{"minutes_west":-60,"dst_time":0}}}

Sono ammessi ID statici 0–9 e 11 e ID CPU negativi del GKI scelto. Identità duplicate, ID sconosciuti e oltre 16384 campioni sono rifiutati. I secondi hanno 64 bit con segno, non negativi per CPU; i nanosecondi sono in [0, 1000000000). Gli interi JSON restano in ±9007199254740991, le stringhe decimali preservano tutta la gamma; il fuso ha 32 bit con segno. C++ usa ProcessOptions::LinuxTime; altri profili OS rifiutano l’opzione.

-16006 è SCHED del PID 2000. PROF e VIRT sono indipendenti. Gli ID SCHED correnti 2, -6 (PID zero) e -8006 (PID 1000) condividono un campione; gli alias PROF sono -8/-8008 e VIRT -7/-8007. Alias duplicati vengono rifiutati anche a valori uguali. I secondi CPU sono non negativi e i nanosecondi normalizzati. L’avanzamento inattivo modifica solo gli orologi di parete 0, 1 e 7; i campioni CPU restano fissi. L’esecuzione non deduce consumo CPU.

advance_on_idle: true attiva nanosleep relativo x64/ARM64 e wrapper Android nominati/variadic. Gli orologi di parete 0, 1 e 7 possono coesistere con campioni CPU fissi; altri tipi sono esclusi da questa politica. I thread eseguibili procedono per primi; quando tutti sono bloccati, il tempo raggiunge la prima scadenza. Un unico thread avanza direttamente. Gli orologi dinamici/FD/di thread codificati restano non supportati.

Il TID della stessa attività corrente identifica anche il suo gruppo, inclusi i thread Android cooperativi senza catalogo esterno. Un PID esterno assente dal catalogo chiuso o vivo ma non leader restituisce EINVAL prima dell’accesso alla destinazione. Catalogo omesso o campione mancante per un gruppo noto: arresto non supportato prima della copia. Tipi invalidi restituiscono EINVAL; campioni validi possono produrre EFAULT nella copia utente. I trap mantengono errori negativi; solo Bionic aggiorna errno e restituisce -1. GKI.

File espliciti in memoria

linux_files fornisce a Linux ELF64 e Android un catalogo chiuso di file immutabili. L’array obbligatorio files può essere vuoto; ogni voce richiede il percorso assoluto canonico path e i byte bytes_hex. Non vengono consultati file host o contenuti /proc impliciti. Senza l’opzione i servizi non sono modellati; i percorsi assenti restituiscono ENOENT.

{"linux_files":{"files":[
  {"path":"/fixture/data","bytes_hex":"00ff410a805a"}],
  "descriptor_limit":256}}

Ogni apertura ha un cursore indipendente; Bionic, syscall, trap e thread guest condividono i descrittori. La chiusura rende riutilizzabile il numero libero più basso. Sono ammessi open/openat in sola lettura, read/close, lseek ordinario, O_CLOEXEC e l’O_LARGEFILE dell’architettura. Solo Bionic converte errno. I fault di lettura conservano il prefisso copiato. stdin non ha contenuto predefinito; percorsi relativi, directory, scritture e link restano esclusi.

C++: ProcessOptions::LinuxFiles. descriptor_limit: 3–4096 (256); files ≤ 256; path < 4096 bytes; component ≤ 255 bytes; data + paths + NUL ≤ 16 MiB; JSON ≤ 64 KiB. Contract.

Una lettura di lunghezza zero può iniziare al limite dello spazio utente. Dopo la verifica dell’intervallo di indirizzi originale, se la posizione del file più la lunghezza richiesta supera INT64_MAX, viene restituito EINVAL anche a EOF, senza modificare il cursore.

Ogni voce può fornire metadata completi; C++ usa LinuxFileOptions::Metadata con percorsi già presenti. fstat/fstat64 e syscall condividono osservazioni fisse senza consultare il sistema host, derivare size dai byte o spostare il cursore. Sono ammessi solo file regolari e campi completi; gli interi a larghezza piena usano stringhe decimali. x64/AArch64 scrivono 144/128 byte con rdev e padding a zero. Un descrittore non valido restituisce EBADF, un output interamente inaccessibile EFAULT. Metadati mancanti, flussi standard sconosciuti e permessi di scrittura misti arrestano l’esecuzione senza modificare byte. Campi e limiti sono nel contratto collegato.

{"linux_files":{"files":[{"path":"/fixture/virtual","bytes_hex":"616263",
  "metadata":{"device":1,"inode":"18446744073709551615","mode":33060,
    "link_count":1,"uid":1000,"gid":1000,"size":0,"block_size":4096,"blocks":0,
    "access_time":{"seconds":0,"nanoseconds":0},
    "modification_time":{"seconds":0,"nanoseconds":0},
    "change_time":{"seconds":0,"nanoseconds":0}}}]}}

Profilo Windows PE64

windows-pe64-v1 supporta processi console Windows x64/ARM64 limitati con PEB/TEB, TLS statico e dinamico, DllMain, API Win32 nominate e grafi DLL espliciti aciclici. I moduli supportano import di codice/dati per nome o ordinale, DIR64, export inoltrati e identità reali del loader. LoadLibraryA / LoadLibraryW, FreeLibrary e GetProcAddress usano il catalogo configurato. CRT/GUI, SEH utente ARM64 basato sui frame, thread e compatibilità Windows generale restano incompleti; mancano prove native ARM64 KVM/WHP.

WindowsProcessTime.cpp gestisce GetSystemTimeAsFileTime, GetTickCount, QueryPerformanceCounter, QueryPerformanceFrequency e ZwDelayExecution tramite gli orologi host. FILETIME conta unità di 100 ns dal 1601; il contatore delle prestazioni usa un orologio monotono e la frequenza dichiarata di 10 MHz, mentre i tick in millisecondi si avvolgono a 32 bit. Sono completati i ritardi relativi non alertable fino a 500 ms e l’intervallo zero; attese alertable, date assolute positive e ritardi maggiori interrompono esplicitamente l’esecuzione, senza abbreviare l’attesa o dichiarare successo. I servizi preservano LastError e condividono lo stesso modello fra i backend CPU. ZwDelayExecution legge solo gli 8 bit bassi di BOOLEAN; i bit inutilizzati del registro argomento non cambiano la politica di attesa.

Windows x64 ABI, QueryPerformanceFrequency, FILETIME.

La memoria virtuale Windows aggiunge VirtualAlloc, VirtualFree, VirtualProtect, VirtualQuery e FlushInstructionCache per il processo corrente. Il livello OS gestisce le prenotazioni; AddressSpace resta responsabile delle pagine impegnate, dei permessi e della memoria sottostante. I test verificano modifiche al codice, errori di accesso e riutilizzo del budget di memoria.

WriteProcessMemory segue il comportamento delle pagine impegnate osservato su x64/ARM64 per scritture fino a 4 KiB nel processo corrente. Conserva la protezione di ogni regione, i prefissi copiati, i conteggi dei byte e LastError, inclusi ERROR_NOACCESS, ERROR_PARTIAL_COPY e il successo dopo un prefisso RX. WindowsMemoryWriteTests.cpp controlla tutte le 25 coppie di protezioni; check_windows_memory_write.py verifica lo stesso eseguibile originale nella CI Windows nativa. Le destinazioni non impegnate restano esplicitamente non supportate.

Le allocazioni private supportano MEM_RESERVE, MEM_COMMIT, MEM_DECOMMIT, MEM_RELEASE e MEM_TOP_DOWN, con allineamento delle prenotazioni a 64 KiB e pagine da 4 KiB. La sola prenotazione non consuma RAM guest. Un nuovo commit conserva i dati e aggiorna i permessi; il decommit restituisce le singole pagine. La verifica dell’intero intervallo e la preparazione delle allocazioni evitano modifiche parziali in caso di errori ordinari. La query restituisce la struttura x64/ARM64 da 48 byte e raggruppa pagine successive della stessa allocazione. Immagine, ambiente, heap, ingressi API e margini dello stack partecipano al posizionamento; l’identità dello stack coincide con il TEB. Se un VirtualProtect riuscito rende di sola lettura la posizione di output dei vecchi permessi, i nuovi permessi restano applicati, i byte di output non cambiano e la chiamata restituisce successo. Una modifica della protezione su pagine non confermate restituisce ERROR_INVALID_ADDRESS, scrive PAGE_NOACCESS nell’output dei vecchi permessi e lascia invariati i permessi delle pagine.

Le protezioni ammesse sono PAGE_NOACCESS, PAGE_READONLY, PAGE_READWRITE, PAGE_EXECUTE_READ e PAGE_EXECUTE_READWRITE. Pagine di guardia, sola esecuzione, copia su scrittura, modificatori di cache, pagine grandi, reset/write-watch/segnaposto e modifiche alle mappature interne del modello restano esplicitamente non supportati. Solo le allocazioni virtuali private possono essere decommesse o rilasciate.

VirtualAlloc, VirtualFree, VirtualProtect, VirtualQuery, MEMORY_BASIC_INFORMATION.

neverd emulate guest.exe --profile=windows-pe64-v1 \
  --options='{"backend":"auto","arguments":["guest.exe","argument"],"environment":["MODE=test"]}'

L’EXE PE32+ a thread singolo mantiene la base preferita e ammette DLL esplicite con ingresso facoltativo e TLS statico. WindowsProcessOptions::Modules o JSON windows.modules fornisce fino a 64 nomi base guest e percorsi di input host tramite name e path, senza cercare o eseguire DLL host. I nomi ASCII ignorano maiuscole/minuscole; duplicati e sostituzioni dei provider di sistema sono rifiutati. Si leggono solo file raggiungibili. Import per nome/ordinale collegano export reali; buchi, simboli mancanti, cicli, import associati/ritardati e load configuration/CFG non supportata falliscono. DIR64 sposta DLL rilocabili in conflitto; collisioni fisse e scritture nei metadati di collegamento falliscono prima di pubblicare l’immagine interessata.

readPEProgramExports possiede export originali e intervalli letti; WindowsProcessModules possiede grafo e gate API per provider/nome comuni al processo. VirtualMemory riserva tutte le immagini prima del mapping; AddressSpace gestisce pagine e permessi. PEB/LDR contiene immagini reali e la lista di inizializzazione conserva l’ordine di registrazione del loader, separato dall’ordine delle chiamate attach basato sulle dipendenze. GetModuleHandleW accetta NULL o nomi base ASCII, ignora maiuscole/minuscole e aggiunge .dll senza estensione. Percorsi, nomi non ASCII e punto finale restano non supportati. Un nome assente restituisce 126; il successo conserva LastError. I modelli API non sono DLL installate.

Byte di input ed estensioni aggregate delle immagini condividono ciascuno memory_limit; anche i mapping del runtime consumano il budget immagine. La preparazione condivide 65,536 record, 64 MiB di letture, nomi limitati e la scadenza del lavoro, senza limite temporale rigido per I/O host. Il fixture originale EXE→DLL→DLL verifica rebasing, ordinali, dati condivisi, identità API, MEM_IMAGE, liste e attach/detach TLS EXE. NeverDWindowsProcessTests include l’oracolo Windows nativo, NeverDPEProgramExportsTests metadati invalidi e budget, NeverDProcessPublicTests parità C ABI/CLI. I backend indisponibili sono saltati esplicitamente.

WindowsProcessLifetime esegue TLS e poi DllMain delle DLL in ordine di dipendenza, seguiti da TLS e ingresso EXE, con CPU e budget comuni. Ogni modulo riceve indice TLS e blocco allineato indipendenti, copiati dall’immagine rilocata e collegata in un’area comune di 64 KiB. L’argomento riservato TLS è zero; DllMain riceve un valore opaco non nullo all’avvio/uscita del processo. L’uscita esplicita separa le DLL inizializzate nell’ordine inverso della lista del loader e poi TLS EXE, anche prima dell’inizializzazione EXE. DllMain(FALSE) iniziale termina con 0xc0000142 senza detach. Errori e budget esauriti non inventano pulizia. Il ritorno dall’ingresso PE con DLL guest richiede terminazione del thread non supportata e si arresta esplicitamente. SizeOfZeroFill non nullo resta escluso; i byte inizializzati a zero nel modello TLS effettivo sono supportati. Le DLL senza ingresso ricevono TLS attach, ma nessuna notifica di detach del processo.

WindowsProcessExports condivide la risoluzione per nome/ordinale tra import statici e GetProcAddress, inclusi codice, dati, alias e catene di inoltro. Solo gli inoltri iniziali usati aggiungono moduli del catalogo e dipendenze di inizializzazione; quelli inutilizzati non caricano file. I nomi distinguono maiuscole; nomi assenti restituiscono NULL/errore 127, ordinali assenti cercati direttamente (inclusi i buchi) NULL/errore 182 e un argomento di query NULL errore 87, il successo conserva LastError. Gli handle sconosciuti restano non supportati. Gli ingressi API esatti fornitore/nome sono riservati una volta dal registro limitato. La risoluzione verifica header PE e metadati export correnti di ogni immagine, rifiuta modifiche o byte illeggibili, limita le catene a 64 elementi e condivide i crediti residui di metadati e la scadenza del processo. Un inoltro a un buco restituisce la base dell’immagine di destinazione e conserva LastError; all’ordinale zero restituisce errore 87. La base è un indirizzo dati e non autorizza l’esecuzione degli header. Gli inoltri a runtime possono caricare moduli configurati e completarli prima di restituire il risultato. La modifica delle tabelle export attive resta non supportata.

WindowsProcessLoader carica nomi base DLL ASCII da windows.modules e gestisce riferimenti espliciti, dipendenze condivise e mantenimento dei moduli iniziali. Ripetere una query inoltrata non aggiunge riferimenti. Ogni ricaricamento assegna una nuova generazione residente allo stesso slot del catalogo. TLS e DllMain usano la stessa CPU sotto i frame API sospesi; il ripristino dei registri conserva le scritture guest e usa il ritorno corrente. I puntatori riservati di attach/detach dinamico sono zero. Un attach fallito durante un caricamento esplicito restituisce 1114 dopo la pulizia, preservando i caricamenti annidati indipendenti riusciti. Lo scaricamento libera immagine e TLS; il ricaricamento ripristina i byte originali. Modifiche esterne a liste loader o puntatori TLS sono rifiutate. I budget di file, immagini e metadati restano cumulativi anche dopo gli errori. I fornitori di sistema usano la base PE mappata come handle di modulo. Ricerca di file, percorsi non ASCII, flag LoadLibraryEx, cicli e transizioni rientranti dello stesso modulo in inizializzazione/scaricamento restano non supportati.

Una DLL come input usa WindowsLibraryHost.cpp: un EXE PE modellato esegue LoadLibraryA → FreeLibrary → ExitProcess sulla stessa CPU guest. L’input occupa uno dei 64 slot del catalogo, conserva il nome base e condivide i budget di preparazione ed esecuzione con host e dipendenze. GetModuleHandle(NULL) identifica l’host; GetModuleFileNameA accetta handle di moduli residenti. L’apertura del proprio file resta limitata all’input esplicito. Il fallimento di collegamento segue l’errore 1114 e la pulizia ordinari; nessuna firma di export è indovinata.

GetEnvironmentVariableW, SetEnvironmentVariableW, GetEnvironmentStringsW, FreeEnvironmentStringsW, ExpandEnvironmentStringsW condividono il blocco ambiente corrente del guest nei parametri di processo del PEB. I nomi ASCII ignorano maiuscole e minuscole; i valori sono UTF-16. Le modifiche verificano input, capacità e permessi di scrittura prima della pubblicazione. Le istantanee restano indipendenti dalle modifiche successive e rilasciano la memoria guest. Il modello limita il blocco a 64 KiB; stringhe ed espansioni hanno limiti e controllano la scadenza. Puntatori di proprietà sconosciuta, blocchi malformati, pagine di codice ANSI e buffer di espansione sovrapposti restano non supportati. WindowsEnvironmentTests.cpp confronta fixture originali x64/ARM64 sui backend disponibili; la CI richiede un oracolo Windows nativo indipendente.

WindowsProcessHeap unifica allocazione, HeapReAlloc, rilascio e interrogazione delle dimensioni dello heap del processo. Il ridimensionamento conserva i byte mantenuti; HEAP_ZERO_MEMORY azzera quelli aggiunti e HEAP_REALLOC_IN_PLACE_ONLY impedisce lo spostamento. Il ridimensionamento fallito conserva il vecchio blocco e restituisce NULL con ERROR_NOT_ENOUGH_MEMORY (8), come nelle osservazioni native. Le pagine indipendenti restituiscono capacità durante riduzione e rilascio; crescita preparata e copie limitate verificano la scadenza. Heap personalizzati, flag di eccezione, proprietà sconosciuta e intervalli inaccessibili arrestano esplicitamente l’esecuzione. WindowsHeapTests.cpp copre entrambe le ISA, spostamento forzato, riuso del budget e atomicità degli errori; CI esegue lo stesso EXE originale su Windows nativo.

windows.peb_version specifica i campi PEB major, minor, build e platform. Sono obbligatori tutti e quattro gli interi senza segno: build ha 16 bit, gli altri 32. Se omessa, l’opzione mantiene a zero i campi di versione del profilo. WindowsProcessEnvironment ne gestisce l’inizializzazione su x64 e ARM64. Questo input non sceglie numeri di servizi nativi e non certifica la compatibilità con quella versione di Windows. I byte dell’ambiente da cui dipende il codice conservato devono coincidere tra acquisizione ed esecuzione nativa.

WindowsSystemModules costruisce immagini modello PE64 limitate per ntdll.dll, kernelbase.dll e kernel32.dll su entrambe le ISA. Le ricerche ASCII GetModuleHandleA / GetModuleHandleW, LoadLibraryA / LoadLibraryW e GetProcAddress condividono le basi mappate; PEB/LDR e MEM_IMAGE descrivono le stesse immagini. Importazioni statiche, ricerche per nome e inoltri guest usano gli stessi ingressi API e risolutore degli export. I fornitori restano residenti, senza callback guest di inizializzazione, e non impediscono il ritorno dall’ingresso dopo lo scaricamento delle normali DLL guest. Modifiche a intestazioni o metadati di export interrompono la ricerca. Nomi di sistema non modellati e ordinali non nulli arrestano esplicitamente l’esecuzione; differenze di maiuscole nei nomi modellati e nomi vuoti restituiscono 127, una ricerca NULL restituisce 87. Byte e indirizzi generati sono regole del modello; layout delle versioni Windows, ordinali nativi e alias tra fornitori non sono ricostruiti. WindowsSystemTests.cpp confronta EXE originali x64/ARM64 con Windows nativo, incluse otto osservazioni indipendenti del ritorno del thread iniziale.

WindowsProcessExceptions implementa AddVectoredExceptionHandler, RemoveVectoredExceptionHandler e RaiseException sulla stessa CPU e con il budget del processo. I gestori ordinati possono modificare le registrazioni, sollevare eccezioni annidate, chiamare API modellate, caricare DLL e terminare il processo. Le violazioni di accesso ai dati x64/ARM64 e le divisioni intere x64 riprendono dopo la convalida delle modifiche guest a CONTEXT, preservando registri generali, SIMD e stato FP supportato. Le eccezioni software riprendono da una vera istruzione di ritorno nel fornitore modellato. Limiti: 128 registrazioni conservate e 16 frame annidati. Risultati non validi, puntatori modificati, campi non supportati e superamenti falliscono esplicitamente. SEH/unwinding ARM64 basato sullo stack, debugger ed errori di esecuzione/guardia restano esclusi. WindowsExceptionTests.cpp confronta EXE/DLL originali con Windows nativo; mancano ancora prove native ARM64 KVM/WHP. AddVectoredExceptionHandler, RemoveVectoredExceptionHandler, RaiseException, CONTEXT x64, ARM64_NT_CONTEXT. I record delle eccezioni software includono EXCEPTION_SOFTWARE_ORIGINATE (0x80), indipendentemente dal flag di non continuabilità del chiamante; l’eseguibile Windows originale verifica i valori esatti dei flag delle eccezioni software e hardware. EXCEPTION_RECORD.

WindowsProcessContext conserva l’origine di ogni frame di dispatch. I fault x64 supportati di accesso ai dati e divisione espongono RF (0x10000) in CONTEXT.EFlags; RaiseException, anche con codici software di violazione di accesso, mantiene il contesto corrente. L’origine resta valida durante VEH/VCH e ricerca/unwind SEH. Una continuazione valida ripristina i flag logici della CPU senza RF; le modifiche guest a RF vengono respinte prima di pubblicare lo stato. Questo profilo limitato non modella breakpoint di istruzione o RF controllato dal guest. WindowsExceptionTests.cpp verifica record salvati, ripristino e CPU/RAM inalterate in caso di rifiuto.

In base a osservazioni native indipendenti, Windows ring3 traduce i fault checked x64 operand_alignment in STATUS_ACCESS_VIOLATION con parametri [read, UINT64_MAX], anche per le scritture. Il livello CPU fornisce la causa; Windows non la deduce dal vettore 13 e non decodifica nuovamente l’istruzione. WindowsAlignmentProcessTests.cpp esegue istruzioni PE originali in 72 scenari di fault e 9 tentativi dopo la correzione dell’indirizzo (72 + 9), verificando PC, RF, XMM e RAM. I fault non classificati o incoerenti restano rifiutati. I report di processi e driver conservano cause e error_code esadecimale, entrambi nullable, distinguendo assenza e zero. Questa consegna riguarda il profilo utente checked x64.

AddVectoredContinueHandler e RemoveVectoredContinueHandler gestiscono un elenco ordinato separato e condividono con i gestori delle eccezioni il limite di 128 registrazioni conservate. Quando un gestore vettorizzato accetta la ripresa, i callback di continuazione ricevono lo stesso record modificabile e CONTEXT. La convalida finale avviene dopo questi callback, comprese le eccezioni annidate e le notifiche DLL. Gli handle non possono essere rimossi tramite l’altra famiglia di gestori. WindowsContinuationTests.cpp confronta EXE originali con Windows nativo per ordine, arresto anticipato, modifiche alle registrazioni, riparazione del contesto, annidamento, callback del caricatore e uscita del processo. Il percorso vettorizzato verificato su Windows x64 consente la ripresa con EXCEPTION_NONCONTINUABLE; ciò non dimostra il comportamento SEH basato sullo stack. L’esecuzione ARM64 nativa resta non verificata. AddVectoredContinueHandler, RemoveVectoredContinueHandler.

RtlCaptureContext è disponibile tramite kernel32.dll e ntdll.dll per x64 e ARM64. I componenti condivisi WindowsProcessContext e IntegerABI salvano PC/SP del chiamante senza modificare lo stato CPU o LastError. Le osservazioni native di Windows confermano i flag x64 0x10000f, la conservazione delle aree home/debug/vettori non scritte e i campi storici degli indirizzi x87 a 32 bit; ARM64 copia LR in PC e azzera X0/LR nel record. Registri, SIMD e controlli floating point provengono dal guest; selettori x64 e maschera delle capacità MXCSR seguono la CPU guest configurata. Destinazioni non valide, non allineate o parzialmente inaccessibili falliscono prima della scrittura. WindowsContextTests.cpp verifica import diretti, ricerche dei provider, callback VEH, output tra pagine e atomicità degli errori. scripts/check_windows_context.py esegue il programma originale su Windows x64/ARM64, con un oracolo nativo separato per lo stato x87 non vuoto. Queste osservazioni ARM64 non provano l’esecuzione nativa KVM/WHP. Ripristino del contesto, analisi dello stack e tabelle di funzioni dinamiche restano attività distinte. WindowsProcessServices.def dichiara vincoli precisi per modulo: la ricerca in kernelbase.dll restituisce ERROR_PROC_NOT_FOUND (127), come nelle osservazioni native, senza inventare un export. RtlCaptureContext.

WindowsProcessSEH usa lo X64SEH condiviso in os/windows/exception/ (NeverDEmulationWindowsException, disponibile senza driver) per x64 __C_specific_handler e UNWIND_INFO V1. Dopo la ricerca VEH supporta filtri, finally, trasferimenti non locali, eccezioni annidate/unwinding in collisione e frame EXE/DLL rilocati, preservando GPR/XMM non volatili. La continuazione tramite filtro esegue VCH sullo stesso CONTEXT. WindowsSEHTests.cpp confronta 23 scenari originali con Windows nativo; KVM/WHP/Unicorn condividono la semantica. Il budget del processo comprende la riconvalida di generazioni, header, byte di unwinding/ambiti, regioni del gestore di linguaggio e associazioni IAT. Metadati modificati o immagini conservate scaricate falliscono esplicitamente. SEH ARM64 basato sui frame, C++ EH, tabelle dinamiche, RtlUnwind/NtContinue generali e unwinding oltre callback del loader/VEH/VCH restano esclusi.

Per EXCEPTION_NONCONTINUABLE, un filtro x64 che restituisce EXCEPTION_CONTINUE_EXECUTION genera STATUS_NONCONTINUABLE_EXCEPTION (0xc0000025, flag 0x81, record collegato nullo) con un nuovo contesto. VEH viene eseguito nuovamente prima della ricerca nello stack logico conservato, mantenendo l’ordine finally, l’identità dei frame EXE/DLL e gli stessi budget di profondità ed esecuzione. I 23 scenari nativi comprendono 21 esecuzioni riuscite e due terminazioni: accettare in VEH/VCH la continuazione di questa eccezione secondaria la lascia non gestita anche ripristinando il CONTEXT originale. Il modello segnala un errore di esecuzione. Gli indirizzi delle eccezioni software coincidono con il PC salvato; indirizzi del dispatcher interno e disposizione dei registri sono scelte del modello. Windows x64 CI.

Prima dei callback di scaricamento dinamico, il modulo esce dall’elenco di inizializzazione; mappatura, ricerca per nome e appartenenza agli elenchi di caricamento/memoria restano disponibili durante i callback. Gli oracoli di ritorno dall’ingresso osservano il thread iniziale indipendentemente dai thread di lavoro del sistema.

WindowsDynamicTests.cpp confronta DLL/EXE originali x64/ARM64 con osservazioni Windows native indipendenti: riferimenti, dipendenze condivise, caricamenti annidati, pulizia dopo errori, inoltri, uscita, DLL senza ingresso e TLS nuovo al ricaricamento. Le regressioni rifiutano metadati modificati e puntatori di codice scaduti, mantengono budget cumulativi e risultati API interrotti incompleti. La CI Windows impone oracolo nativo e casi WHP; cross-compilazione e Unicorn ARM64 non provano l’esecuzione ARM64 nativa.

Una libreria assente nella catena di inoltro di GetProcAddress restituisce 127; un LoadLibrary esplicito per un modulo assente dal catalogo restituisce 126. L’oracolo nativo e ogni backend disponibile verificano tutti i 41 scenari dichiarati. Su Windows, il ritorno dopo lo scaricamento di tutte le DLL viene osservato 16 volte per variante di DLL. Anche un’inizializzazione fallita tramite inoltro di GetProcAddress restituisce 127 dopo la pulizia. I callback di distacco del processo preservano il contenuto dello stack del chiamante che termina.

WindowsExportTests.cpp usa DLL ed EXE originali x64/ARM64 per verificare chiamate inoltrate a codice/dati/ordinali, alias, query di inizializzazione, rebasing, maiuscole, assenze, LastError, cicli, destinazioni non residenti, puntatori non validi e modifiche dopo query riuscite. Lo stesso EXE ha un oracolo Windows nativo indipendente; i casi WHP sono obbligatori nella CI nativa. I test C ABI/CLI confrontano report completi. Le prove hardware ARM64 native restano da acquisire. Varianti EXE con e senza tabella export coprono entrambi i grafi, l’ordine PEB e detach e gli errori per nome/ordinale/NULL.

WindowsLifetimeTests.cpp confronta tracce fisse con processi Windows nativi indipendenti e KVM/WHP/Unicorn: uscita normale, ritorno dall’ingresso, entrambi gli errori DLL, quattro uscite precoci e DLL senza ingresso. Verifica anche errori dei callback, budget comuni, campi TLS rilocati e capacità totale. La prova nativa del ritorno conserva l’handle del thread iniziale e verifica 64 volte il codice di uscita e l’esatta sequenza di notifiche thread/processo. I thread figli restanti vengono terminati dopo l’osservazione; l’uscita del processo non viene interpretata come ritorno dall’ingresso.

{"windows":{"modules":[{"name":"middle.dll","path":"inputs/middle.dll"},{"name":"leaf.dll","path":"inputs/leaf.dll"}]}}

GS su x64 e x18 su ARM64 puntano a TEB con limiti dello stack, puntatore a sé, PID/TID, PEB, parametri, LastError e TLS. UTF-8 rigoroso diventa UTF-16; argv segue le regole di quoting Microsoft CRT. I nomi ambiente sono ASCII, i duplicati senza distinzione tra maiuscole sono rifiutati, i valori possono essere Unicode e il blocco ordinato termina con due NUL. Non eredita ambiente o filesystem host. TLS statico copia template, azzera BSS e scrive un indice a 32 bit; TLS dinamico usa slot TEB separati. Attach/detach legge l’array corrente in ordine con budget e scadenza condivisi. L’uscita normale del processo esegue detach. Il ritorno dall’ingresso è supportato solo senza DLL guest residenti; un secondo ExitProcess durante la pulizia di uscita resta non supportato.

WindowsProcessServices.def definisce ExitProcess, RtlExitUserProcess, handle di output e WriteFile sincrono, LastError, ID e pseudo-handle processo/thread, GetCommandLineW / HeapReAlloc, allocazione/liberazione/dimensione heap, TLS dinamico e LoadLibraryA / LoadLibraryW / FreeLibrary / GetModuleHandleA / GetModuleHandleW / GetProcAddress. Risolve nomi esatti in kernel32.dll, kernelbase.dll, ntdll.dll. Syscall dirette e gate falsi non selezionano modelli. L’heap appartiene al processo e viene recuperato; l’output mantiene i byte binari. Gli errori Win32 sono distinti da I/O asincrono ed eccezioni utente non implementati. Gli alias rispettano l’azzeramento iniziale del contatore e il vero slot di ritorno.

windows.native_calls conserva modulo/funzione, argomenti scalari dichiarati e risultati nullable senza inventare numeri NT. NeverDWindowsProcessTests verifica PE reali, TLS del compilatore, modifiche dei callback, heap, alias, metadati non validi, privilegi e budget; NeverDProcessPublicTests verifica CLI/C ABI. La CI Windows esegue direttamente lo stesso EXE come riferimento indipendente e richiede i test WHP. La prova runtime ARM64 nativa richiede ancora una macchina adatta.

Se il buffer di ingresso non è vuoto ma non è leggibile, WriteFile restituisce ERROR_INVALID_USER_BUFFER (1784), azzera il conteggio dei byte scritti e non produce alcun byte.

windows.defer_unmodeled carica un'immagine i cui fatti di caricamento non sono implementati dal modello e si ferma solo se l'esecuzione dipende da uno di essi. Le esportazioni fuori dall'inventario delle API e i moduli fuori dal catalogo sono collegati a ingressi opachi: ogni identità si risolve in un solo indirizzo, ed eseguirlo ferma l'esecuzione come unsupported_service nominando module!export. Le directory che il modello non interpreta restano non interpretate, i metadati che il file non fornisce non vengono letti al caricamento, e la gestione delle eccezioni basata sui frame attraverso un'immagine simile si ferma. observeProcess aggiunge un ProcessObserver che legge il processo fermo al suo avvio e a ogni sorveglianza di esecuzione; non può modificare lo stato dell'ospite, e quando termina l'esecuzione viene riportato observer. Lo spacchettamento si basa su entrambi.

PE/COFF, ARM64 ABI, WriteFile, TLS, Wine 10.0 loader. GetProcAddress.

Verifica

cmake --build build-cpu --target NeverDLinuxProcessTests NeverDExecutionSessionTests NeverDX64MemoryUpdateTests NeverDThreadPointerTests --parallel 4
ctest --test-dir build-cpu -L '^NeverD(LinuxProcess|ExecutionSession|X64MemoryUpdate|ThreadPointer)Tests$' --output-on-failure
# In una build shared-library/CLI:
cmake --build build-cpu --target NeverDProcessPublicTests --parallel 4
ctest --test-dir build-cpu -L '^NeverDProcessPublicTests$' --output-on-failure

I test compilano ingressi ELF autonomi in assembly e C per entrambe le ISA: data/BSS, metadati di avvio reali, errori syscall, output binario, permessi, scritture parziali, servizi non supportati e budget tra quanti. TLS inizializza blocchi allineati indipendenti, BSS e puntatori thread e ne verifica la conservazione; x64 controlla gli errori arch_prctl senza perdere la base precedente. I backend non disponibili sono esplicitamente saltati. La suite pubblica confronta report e codici di uscita attraverso ABI C condivisa e CLI. PIE verifica auxv e slot RELA inizialmente nulli prima delle proprie rilocazioni di dati e funzioni. I test di mappatura conservano i fixup scegliendo la fonte di analisi; le tabelle dinamiche coprono sezioni assenti e input malformati o dipendenti. Entrambe le ISA provano allocazione, protezione, buchi, rimappatura, crescita/riduzione dell'heap ed errori gestiti. Vere scritture guest verificano fault dopo protezioni ordinarie e parziali. x64 riscrive codice allo stesso indirizzo tra RW e RX e chiama entrambe le versioni; lo stesso ELF gira nativamente su Linux come oracolo indipendente. I test di memoria coprono esaurimento, recupero e snapshot autorevoli senza trattenere RAM. Cross-compilazione e Unicorn ARM64 non sono evidenza di ARM64 KVM/WHP nativo.