Chrono Mock
語言繁體中文

你怎麼知道它生效了?

用其他任何同類工具,你都無從得知。應用程式啟動了,顯示出某個日期,而你只能假定一切正常。可它也許透過一條沒有被攔截的途徑讀取時鐘——到那時,「測試通過」就毫無意義。

沒有症狀的失敗

漏掉一個通道的時間偏移工具既不會當機,也不會警告。應用程式只是透過那個沒人掛鉤的函式讀到了真實日期,然後像任何一個普通的下午那樣照常執行。測試變綠了,錯誤就這樣上線了。

RunAsDate 在自己的文件裡對此很坦誠——它說自己「對從其他來源取得目前日期/時間的應用程式不起作用」——然後讓你自己去弄清楚你的應用程式是不是其中之一。這個工具把同一句話當作需要量測的東西。

工作階段實際回報了什麼

真實的輸出,來自對本專案自帶的一個測試探針執行該工具——這個探針會故意開啟一個網路連線:

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

最後兩塊才是重點。這裡沒有任何東西失敗——判定是生效——可報告仍然主動告訴你,這個應用程式在結束之前與網路通訊過,所以本機掛鉤可能不是事情的全部。這句話,就是一個你能信任的測試和一個你不能信任的測試之間的區別。

Chrono Mock 的結果畫面,What the application read 已展開:一張包含 Function、Read from 與 Reads 欄的表。NtCreateUserProcess 被標為依設計讀取真實時鐘,connect 被標為依設計讀取真實時鐘且較晚才掛鉤,其餘各列——GetSystemTimeAsFileTime、NtQuerySystemTime、GetTimeZoneInformation 等——讀取的是偽造的時鐘,每一列都有各自的次數。表格下方有兩則警告。
視窗裡的同一個答案。應用程式本來可能詢問的每一個函式、它實際讀取的來源,以及次數——包括那些刻意留在真實時鐘上的函式,它們會被點名,而不是被悄悄算作通過。

四種判定,各有一個結束代碼

判定是在執行中的處理序內部得出的,早於你開始工作,並且它就是工具自己的結束代碼,因此管線可以據此分支。

判定結束代碼意義
生效0所有讀取過時間的處理序看到的都是工作階段時鐘
部分生效10部分通道已涵蓋,部分未涵蓋——未涵蓋的會逐一列出
未生效11替換沒有到達關鍵通道
無法確定4無法確認涵蓋情況——一句誠實的「我不知道」

當判定為「未生效」時,處理序會被立即停止,在你與它互動之前。判定不可能先於啟動,因為唯一的事實依據,是從執行中的處理序內部取得的讀數。能保證的是你在開始工作之前就會得知真相——而不是注定失敗的啟動永遠不會發生。你可以選擇覆寫並繼續,此後這個工作階段會在它出現的所有地方——包括你匯出的任何證據——被標記為不可靠。

其他代碼表示出問題的是執行本身,而不是替換 - 用法錯誤、無法啟動的目標、被中途截斷的執行、已在執行的另一個工作階段等,它們都不是判定。完整的表格在命令列參考中。

已涵蓋、已觀察、未涵蓋

通道分為三類,而中間那一類最值得注意。

  • 已涵蓋——已掛鉤並被替換。應用程式詢問了,得到的是工作階段時鐘。呼叫次數說明了詢問的次數。
  • 已觀察 - 被攔截、被計數,並刻意讓它們如實回傳。物件等待在這裡,因為縮短逾時可能在真實控制代碼上偽造逾時,多媒體計時器也在這裡,因為縮放它會讓音訊和 MIDI 的時序錯位 - 這兩者在工作階段縮放經過時間軸時才會被監看。網路連線和一條處理序建立路徑在每個工作階段中都在這裡,因為它們是證據,而不是時鐘。高解析度計數器不屬於這一組:除非你用 --scale-qpc 要求,否則它保持原樣,要求之後它就被涵蓋。
  • 未涵蓋——應用程式讀取過、而替換沒有觸及的通道。正是它讓判定變成部分生效,並且會逐個點名列出,而不是被概括掉。

次數為零同樣是資訊:通道已被掛鉤,而應用程式從未詢問過。這就是報告把它們列出來而不是隱藏的原因。

稽核看不到的東西

報告對涵蓋情況是誠實的,這意味著對它自身的邊界也是誠實的。下面這些,讀取時沒有可供攔截的函式呼叫,或是完全發生在處理序之外:

  • 對共用使用者模式資料頁的直接讀取——數值就在記憶體裡,所以沒有什麼可掛鉤的。在一般應用程式中很少見,在授權與防竄改程式碼中很常見
  • 繞過標準匯出函式的直接系統呼叫——同樣的模式
  • 在處理序外透過 WMI、處理序外 COM 伺服器或 RPC 取得的時間
  • 來自網路的時間——HTTP 的 Date 標頭、NTP、API 回應,或是資料庫伺服器自己的時鐘
  • 來自核心驅動程式的時間

網路這一種,是稽核至少能察覺的,這就是上面的報告裡為什麼有那則警告。對於其餘的情形,生效的判定意味著這個工具能看到的通道已被涵蓋——而不是不存在其他途徑。

在管線中

--json 以機器可讀的形式輸出同樣的事實,--report 把給人讀的版本寫入檔案,判定不是生效的工作階段,會在該檔案開頭放一條證據不可靠的橫幅,而不是把它埋在結尾。

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

親自試試