Chrono Mock
LinguaItaliano

Come fai a sapere che ha funzionato?

Con qualsiasi altro strumento di questo tipo, non lo sai. L'applicazione parte, mostra una data qualsiasi e tu dai per scontato che sia giusta. Ma può leggere l'orologio da una via che nulla ha intercettato - e allora «il test è passato» non significa niente.

Il fallimento che non ha sintomi

Uno strumento di spostamento del tempo che manca un canale non si blocca e non avvisa. L'applicazione legge semplicemente la data reale attraverso l'unica funzione che nessuno ha intercettato, e si comporta esattamente come si sarebbe comportata in un qualsiasi pomeriggio normale. Il test diventa verde. Il bug va in produzione.

RunAsDate è onesto su questo nella propria documentazione - dice che «non influirà sulle applicazioni che ricavano la data e l'ora correnti da un'altra fonte» - e poi ti lascia scoprire se la tua è una di quelle. Questo strumento tratta quella stessa frase come qualcosa da misurare.

Che cosa riporta davvero una sessione

Output reale, ottenuto eseguendo lo strumento contro una delle sonde di test di questo progetto - una che apre deliberatamente una connessione di rete:

$ chrono run "...\pnet-x64.exe" --at 2038-01-19T03:14:07 --report session.txt

Chrono Mock - session report
  target:   C:\ChronoMock\tools\probes\bin\pnet-x64.exe
  verdict:  WORKS - time substitution took effect  (processes: 1)
            every process that read time saw the session clock
  session:  fake clock reached 2038-01-19T03:14:10
            real elapsed 3.4s, fake elapsed 3.4s
  exited:   the target closed itself with code 0
  covered channels (substituted, with call counts, 4 calls in all):
            - pid 9152: GetSystemTimeAsFileTime (1 call)
            - pid 9152: NtQuerySystemTime (3 calls)
            ... every other channel, including the ones never called
  observed channels (hooked but left real):
            - pid 9152: NtCreateUserProcess (0 calls)
            - pid 9152: connect (1 call)
  warnings:
            - the target opened a network connection - it may read time from a
              server, which no local hook can cover (source.network_at_start)
  requested:  2038-01-19T03:14:07 (zone +02:00 (host default), mode flow)

Gli ultimi due blocchi sono il punto. Qui nulla è fallito - il verdetto è funziona - e il report dice comunque, senza che glielo si chieda, che questa applicazione ha parlato con la rete prima di finire, quindi un hook locale potrebbe non essere tutta la storia. Quella frase è la differenza tra un test di cui ti puoi fidare e uno di cui no.

La schermata dei risultati di Chrono Mock con What the application read aperto: una tabella con Function, Read from e Reads. NtCreateUserProcess è indicata come letta dall'orologio reale per progetto e connect come letta dall'orologio reale per progetto e agganciata in ritardo, mentre le altre righe - GetSystemTimeAsFileTime, NtQuerySystemTime, GetTimeZoneInformation e le altre - leggono dall'orologio finto, ciascuna con il proprio conteggio. Due avvisi stanno sotto la tabella.
La stessa risposta nella finestra. Ogni funzione che l'applicazione avrebbe potuto interrogare, da che cosa ha davvero letto e quante volte - comprese quelle lasciate di proposito sull'orologio reale, che sono nominate invece di essere contate in silenzio come un successo.

Quattro verdetti, e un codice di uscita per ciascuno

Il verdetto si raggiunge dall'interno del processo in esecuzione, prima che tu inizi a lavorare, ed è il codice di uscita stesso dello strumento, così una pipeline può diramare su di esso.

VerdettoUscitaSignificato
funziona0ogni processo che ha letto l'ora ha visto l'orologio della sessione
funziona in parte10alcuni canali coperti, altri no - quelli non coperti sono elencati
non funziona11la sostituzione non ha raggiunto i canali chiave
indeterminato4non è stato possibile stabilire la copertura - un onesto «non lo so»

Quando il verdetto è «non funziona», il processo viene fermato subito, prima che tu possa interagire con esso. Il verdetto non può precedere l'avvio, perché l'unica fonte di verità è una lettura presa dall'interno del processo in esecuzione. Ciò che è garantito è che scopri la verità prima di cominciare a lavorare - non che un avvio destinato a fallire non avvenga mai. Puoi forzare e proseguire, e la sessione viene allora contrassegnata come inaffidabile ovunque compaia, comprese le prove che esporti.

Altri codici dicono che è fallita l'esecuzione stessa e non la sostituzione - un errore d'uso, un target che non si è potuto avviare, un'esecuzione interrotta, un'altra sessione già in corso - e nessuno di essi è un verdetto. La tabella completa è nel riferimento della riga di comando.

Coperti, osservati, non coperti

I canali si dividono in tre gruppi, e quello di mezzo è il più interessante.

  • Coperti - intercettati e sostituiti. L'applicazione ha chiesto e ha ottenuto l'orologio della sessione. Il conteggio delle chiamate dice quante volte.
  • Osservati - intercettati, contati e lasciati di proposito a dire la verità. Qui ci sono le attese su oggetti, perché accorciarne il timeout potrebbe simulare una scadenza su un handle reale, e il timer multimediale, perché scalarlo sposterebbe i tempi di audio e MIDI - entrambi osservati quando la sessione scala l'asse del tempo trascorso. Le connessioni di rete e una via di creazione dei processi sono qui in ogni sessione, perché sono indizi, non orologi. Il contatore ad alta risoluzione non è in questo gruppo: resta intatto finché non lo chiedi con --scale-qpc, e allora è coperto.
  • Non coperti - un canale che l'applicazione ha letto e che la sostituzione non ha raggiunto. È ciò che trasforma un verdetto in funziona in parte, ed è elencato per nome invece di essere riassunto.

Anche un conteggio pari a zero è un'informazione: il canale era intercettato e l'applicazione non ha mai chiesto. Per questo il report li elenca invece di nasconderli.

Che cosa l'audit non può vedere

Il report è onesto sulla copertura, il che significa essere onesti sui propri limiti. Queste letture avvengono senza alcuna chiamata di funzione da intercettare, oppure del tutto fuori dal processo:

  • letture dirette della pagina dati condivisa in modalità utente - il valore è in memoria, quindi non c'è nulla da agganciare. Rare nelle applicazioni normali, comuni nel codice di licenza e anti-manomissione
  • chiamate di sistema dirette che aggirano le esportazioni standard - stesso schema
  • ora ottenuta fuori dal processo, tramite WMI, un server COM fuori processo o RPC
  • ora dalla rete - un'intestazione HTTP Date, NTP, la risposta di un'API o l'orologio stesso di un server di database
  • ora proveniente da driver del kernel

Il caso della rete è quello che l'audit può almeno notare, ed è per questo che esiste l'avviso nel report qui sopra. Per il resto, un verdetto funziona significa che i canali che questo strumento può vedere sono stati coperti - non che non esista nessun'altra via.

In una pipeline

--json emette gli stessi fatti in forma leggibile da una macchina, --report scrive la versione per le persone in un file, e una sessione il cui verdetto sia qualsiasi cosa diversa da funziona apre quel file con un avviso di prova inaffidabile invece di seppellirlo in fondo.

chrono run "C:\apps\test.exe" --at 2027-12-31T23:59:59 --json --report evidence.txt

Provalo