Chrono Mock
SpracheDeutsch

Woher wissen Sie, dass es gewirkt hat?

Bei jedem anderen Werkzeug dieser Art wissen Sie es nicht. Die Anwendung startet, zeigt irgendein Datum, und Sie nehmen an, dass es stimmt. Sie kann die Uhr aber über einen Weg lesen, den nichts abgefangen hat - und dann bedeutet „der Test ist bestanden“ gar nichts.

Der Fehler, der kein Symptom hat

Ein Zeitverschiebungs-Werkzeug, das einen Kanal verpasst, stürzt nicht ab und warnt nicht. Die Anwendung liest das echte Datum einfach über die eine Funktion, die niemand abgefangen hat, und verhält sich genau so, wie sie sich an jedem gewöhnlichen Nachmittag verhalten hätte. Der Test wird grün. Der Fehler wird ausgeliefert.

RunAsDate ist dazu in seiner eigenen Dokumentation ehrlich - es sagt, dass es „Anwendungen nicht beeinflusst, die das aktuelle Datum bzw. die aktuelle Uhrzeit aus einer anderen Quelle beziehen“ - und überlässt Ihnen dann herauszufinden, ob Ihre eine davon ist. Dieses Werkzeug behandelt genau diesen Satz als etwas, das man misst.

Was eine Sitzung tatsächlich meldet

Echte Ausgabe, aus einem Lauf des Werkzeugs gegen einen der Test-Prozesse dieses Projekts - einen, der absichtlich eine Netzwerkverbindung öffnet:

$ 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)

Die letzten beiden Blöcke sind der Punkt. Nichts ist hier fehlgeschlagen - das Urteil ist funktioniert - und der Bericht sagt trotzdem ungefragt, dass diese Anwendung mit dem Netzwerk gesprochen hat, bevor sie fertig war, sodass ein lokaler Hook womöglich nicht die ganze Geschichte ist. Dieser Satz ist der Unterschied zwischen einem Test, dem Sie vertrauen können, und einem, dem nicht.

Der Ergebnisbildschirm von Chrono Mock mit geöffnetem Abschnitt What the application read: eine Tabelle mit Function, Read from und Reads. NtCreateUserProcess ist als bewusst von der echten Uhr gelesen markiert und connect als bewusst von der echten Uhr gelesen und spät eingehakt, während die übrigen Zeilen - GetSystemTimeAsFileTime, NtQuerySystemTime, GetTimeZoneInformation und die anderen - von der gefälschten Uhr lesen, jede mit eigener Zahl. Unter der Tabelle stehen zwei Warnungen.
Dieselbe Antwort im Fenster. Jede Funktion, die die Anwendung hätte abfragen können, woraus sie tatsächlich gelesen hat und wie oft - auch die, die absichtlich auf der echten Uhr bleiben, die genannt statt stillschweigend als Bestehen gezählt werden.

Vier Urteile, und zu jedem ein Exit-Code

Das Urteil wird im laufenden Prozess gefällt, bevor Sie mit der Arbeit beginnen, und es ist der Exit-Code des Werkzeugs selbst, sodass eine Pipeline danach verzweigen kann.

UrteilExitBedeutung
funktioniert0jeder Prozess, der die Zeit las, sah die Sitzungsuhr
funktioniert teilweise10einige Kanäle abgedeckt, andere nicht - die nicht abgedeckten werden aufgelistet
funktioniert nicht11die Ersetzung hat die wichtigen Kanäle nicht erreicht
unbestimmt4die Abdeckung ließ sich nicht feststellen - ein ehrliches „Ich weiß es nicht“

Lautet das Urteil „funktioniert nicht“, wird der Prozess sofort angehalten, bevor Sie mit ihm arbeiten können. Das Urteil kann dem Start nicht vorausgehen, denn die einzige Quelle der Wahrheit ist eine Messung aus dem laufenden Prozess heraus. Garantiert ist, dass Sie die Wahrheit erfahren, bevor Sie mit der Arbeit beginnen - nicht, dass ein aussichtsloser Start nie stattfindet. Sie können das übergehen und weitermachen, und die Sitzung wird dann überall, wo sie erscheint, als unzuverlässig gekennzeichnet, auch in jedem Nachweis, den Sie exportieren.

Andere Codes sagen, dass der Lauf selbst scheiterte und nicht die Ersetzung - ein Bedienfehler, ein Ziel, das nicht starten konnte, ein abgebrochener Lauf, eine bereits laufende andere Sitzung - und keiner davon ist ein Urteil. Die vollständige Tabelle steht in der Kommandozeilen-Referenz.

Abgedeckt, beobachtet, nicht abgedeckt

Kanäle fallen in drei Behälter, und der mittlere ist der interessante.

  • Abgedeckt - abgefangen und ersetzt. Die Anwendung hat gefragt und die Sitzungsuhr bekommen. Die Aufrufzahl sagt, wie oft.
  • Beobachtet - abgefangen, gezählt und bewusst bei der Wahrheit gelassen. Hier stehen Wartevorgänge auf Objekte, weil ein verkürztes Zeitlimit eine Zeitüberschreitung an einem echten Handle vortäuschen könnte, und der Multimedia-Timer, weil seine Skalierung Audio- und MIDI-Timing verschieben würde - beide beobachtet, wenn die Sitzung die Achse der verstrichenen Zeit skaliert. Netzwerkverbindungen und ein Weg der Prozesserzeugung stehen in jeder Sitzung hier, weil sie Indizien sind, keine Uhren. Der hochauflösende Zähler gehört nicht in diese Gruppe: Er bleibt unberührt, bis Sie ihn mit --scale-qpc anfordern, und ist dann abgedeckt.
  • Nicht abgedeckt - ein Kanal, den die Anwendung gelesen hat und den die Ersetzung nicht erreichte. Das macht aus einem Urteil funktioniert teilweise, und er wird namentlich aufgelistet statt weggefasst.

Auch eine Null ist eine Information: Der Kanal war abgefangen, und die Anwendung hat nie gefragt. Deshalb listet der Bericht sie auf, statt sie zu verbergen.

Was die Prüfung nicht sehen kann

Der Bericht ist ehrlich zur Abdeckung, und das heißt, ehrlich zu seinen eigenen Grenzen zu sein. Dies wird ohne jeden Funktionsaufruf gelesen, den man abfangen könnte, oder ganz außerhalb des Prozesses:

  • direkte Lesezugriffe auf die gemeinsame Datenseite im Benutzermodus - der Wert steht im Speicher, es gibt also nichts abzufangen. Selten in gewöhnlichen Anwendungen, häufig in Lizenz- und Manipulationsschutz-Code
  • direkte Systemaufrufe, die die Standard-Exporte umgehen - dasselbe Muster
  • Zeit, die außerhalb des Prozesses bezogen wird, über WMI, einen COM-Server außerhalb des Prozesses oder RPC
  • Zeit aus dem Netzwerk - ein HTTP-Date-Header, NTP, eine API-Antwort oder die eigene Uhr eines Datenbankservers
  • Zeit von Kerneltreibern

Den Netzwerkfall kann die Prüfung wenigstens bemerken, weshalb die Warnung im Bericht oben existiert. Für den Rest bedeutet ein Urteil funktioniert, dass die Kanäle, die dieses Werkzeug sehen kann, abgedeckt waren - nicht, dass es keinen anderen Weg gibt.

In einer Pipeline

--json gibt dieselben Fakten in Maschinenform aus, --report schreibt die Fassung für Menschen in eine Datei, und eine Sitzung, deren Urteil etwas anderes als funktioniert ist, stellt dieser Datei einen Hinweis auf unzuverlässige Nachweise voran, statt ihn unten zu vergraben.

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

Ausprobieren