Chrono Mock
IdiomaPortuguês (Brasil)

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.

A tela de resultado do Chrono Mock com What the application read aberto: uma tabela com Function, Read from e Reads. NtCreateUserProcess aparece como lida do relógio real por projeto e connect como lida do relógio real por projeto e conectada tarde, enquanto as demais linhas - GetSystemTimeAsFileTime, NtQuerySystemTime, GetTimeZoneInformation e o resto - leem do relógio falso, cada uma com a sua contagem. Dois avisos ficam sob a tabela.
A mesma resposta na janela. Cada função que o aplicativo poderia ter consultado, de onde ele realmente leu e quantas vezes - inclusive as deixadas de propósito no relógio real, que são nomeadas em vez de contadas em silêncio como um sucesso.

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.

VereditoSaídaSignificado
funciona0todo processo que leu a hora viu o relógio da sessão
funciona parcialmente10alguns canais cobertos, outros não - os não cobertos são listados
não funciona11a substituição não chegou aos canais principais
indeterminado4nã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

Experimente