Como saber se funcionou?
Com qualquer outra ferramenta desse tipo, você não sabe. O aplicativo inicia, mostra uma data qualquer e você presume que está certo. Mas ele pode ler o relógio por um caminho que nada interceptou - e aí “o teste passou” não significa nada.
A falha que não tem sintoma
Uma ferramenta de deslocamento de tempo que perde um canal não trava e não avisa. O aplicativo simplesmente lê a data real pela única função que ninguém interceptou, e se comporta exatamente como se comportaria em qualquer tarde comum. O teste fica verde. O bug vai para produção.
O RunAsDate é honesto sobre isso na própria documentação - ele diz que “não afetará aplicativos que obtêm a data e a hora atuais de outra fonte” - e depois deixa você descobrir se o seu é um deles. Esta ferramenta trata essa mesma frase como algo a medir.
O que uma sessão realmente relata
Saída real, de executar a ferramenta contra uma das sondas de teste deste projeto - uma que abre deliberadamente uma conexão de rede:
$ 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)
Os dois últimos blocos são o ponto. Nada falhou aqui - o veredito é funciona - e o relatório ainda assim diz, sem ser perguntado, que este aplicativo conversou com a rede antes de terminar, de modo que um hook local pode não ser a história toda. Essa frase é a diferença entre um teste em que você pode confiar e um em que não pode.
Quatro vereditos, e um código de saída para cada
O veredito é alcançado de dentro do processo em execução, antes de você começar a trabalhar, e é o próprio código de saída da ferramenta, de modo que um pipeline pode ramificar a partir dele.
| Veredito | Saída | Significado |
|---|---|---|
| funciona | 0 | todo processo que leu a hora viu o relógio da sessão |
| funciona parcialmente | 10 | alguns canais cobertos, outros não - os não cobertos são listados |
| não funciona | 11 | a substituição não chegou aos canais principais |
| indeterminado | 4 | não foi possível estabelecer a cobertura - um “não sei” honesto |
Quando o veredito é “não funciona”, o processo é parado imediatamente, antes de você poder interagir com ele. O veredito não pode preceder a inicialização, porque a única fonte da verdade é uma leitura feita de dentro do processo em execução. O que é garantido é que você descobre a verdade antes de começar a trabalhar - não que uma inicialização fadada ao fracasso nunca aconteça. Você pode forçar e seguir em frente, e a sessão fica então marcada como não confiável em todo lugar em que aparece, inclusive em qualquer evidência que você exportar.
Outros códigos dizem que falhou a própria execução, e não a substituição - um erro de uso, um alvo que não pôde ser iniciado, uma execução interrompida, outra sessão já em andamento - e nenhum deles é um veredito. A tabela completa está na referência da linha de comando.
Cobertos, observados, não cobertos
Os canais se dividem em três grupos, e o do meio é o interessante.
- Cobertos - interceptados e substituídos. O aplicativo perguntou e recebeu o relógio da sessão. A contagem de chamadas diz quantas vezes.
- Observados - interceptados, contados e deixados de propósito dizendo a verdade. Aqui ficam as esperas por objetos, porque encurtar o tempo limite poderia simular um esgotamento num handle real, e o temporizador multimídia, porque escalá-lo deslocaria o áudio e o MIDI - ambos observados quando a sessão escala o eixo do tempo decorrido. As conexões de rede e um caminho de criação de processos ficam aqui em toda sessão, porque são indícios, não relógios. O contador de alta resolução não está neste grupo: fica intocado a menos que você o peça com
--scale-qpc, e então passa a ser coberto. - Não cobertos - um canal que o aplicativo leu e que a substituição não alcançou. É o que transforma um veredito em funciona parcialmente, e ele é listado pelo nome em vez de resumido.
Uma contagem zero também é informação: o canal estava interceptado e o aplicativo nunca perguntou. Por isso o relatório os lista em vez de escondê-los.
O que a auditoria não consegue ver
O relatório é honesto sobre a cobertura, o que significa ser honesto sobre os próprios limites. Estas leituras acontecem sem nenhuma chamada de função a interceptar, ou totalmente fora do processo:
- leituras diretas da página de dados compartilhada em modo usuário - o valor está na memória, então não há nada a interceptar. Raro em aplicativos comuns, comum em código de licenciamento e antiadulteração
- chamadas diretas ao sistema que contornam as exportações padrão - o mesmo padrão
- hora obtida fora do processo, por WMI, por um servidor COM fora do processo ou por RPC
- hora vinda da rede - um cabeçalho HTTP
Date, NTP, a resposta de uma API ou o próprio relógio de um servidor de banco de dados - hora vinda de drivers de kernel
O caso da rede é o que a auditoria pelo menos consegue perceber, e é por isso que o aviso do relatório acima existe. Para o resto, um veredito funciona significa que os canais que esta ferramenta consegue ver foram cobertos - não que nenhum outro caminho exista.
Em um pipeline
--json emite os mesmos fatos em formato para máquinas, --report grava a
versão para pessoas em um arquivo, e uma sessão cujo veredito seja qualquer coisa diferente de
funciona abre esse arquivo com um aviso de evidência não confiável, em vez de enterrá-lo
no fim.
chrono run "C:\apps\test.exe" --at 2027-12-31T23:59:59 --json --report evidence.txt