Contents

不改系統時間測試時間神器:faketime

faketime 是一個強大的工具,它可以讓你在測試中操控時間。這篇文章將教你如何在 Ubuntu 上安裝和使用 faketime。

Ubuntu 安裝

首先,我們需要更新我們的套件列表,然後安裝 faketime:

1
2
3
4
# 更新套件列表
sudo apt update
# 安裝 faketime
sudo apt install faketime

接著,我們需要設定一個環境變數,讓系統知道 faketime 的位置:

1
export LD_PRELOAD=/usr/lib/x86_64-linux-gnu/faketime/libfaketime.so.1

Mac 安裝

Warning
請注意,我沒有 Mac ,所以無法親自測試這段。如果你在使用過程中遇到問題,請參考 faketime 的官方文件或者在網路上尋找解答。

在 Mac 上,我們可以使用 Homebrew 來安裝 faketime:

1
brew install libfaketime

然後,我們需要設定兩個環境變數。這兩個環境變數會讓系統在執行程式時,強制使用我們指定的動態連結庫:

1
2
export DYLD_FORCE_FLAT_NAMESPACE=1
export DYLD_INSERT_LIBRARIES=/path/to/libfaketime.1.dylib

但我沒有 Mac ,所以無法測試這段

指令相關操作

在這部分,我們將介紹如何使用 faketime 來操控時間。

faketime 指令包起來

我們可以使用 faketime 來改變指令的執行時間。例如,我們可以將時間設定為上週五下午五點,或者設定為 2018 年 12 月 24 日的 08:15:42:

1
2
3
4
5
faketime 'last Friday 5 pm'  date
# Fri Aug 11 17:00:00 CST 2023

faketime '2018-12-24 08:15:42' /bin/date
# Mon Dec 24 08:15:42 CST 2018

在這裡, date 可以替換為你想要的指令。

設定相對時間

我們也可以使用 faketime 來設定相對時間。例如,我們可以將時間設定為一小時前:

1
2
3
4
5
6
7
date
# Wed Aug 16 09:23:00 CST 2023
LD_PRELOAD=/usr/lib/x86_64-linux-gnu/faketime/libfaketime.so.1 FAKETIME="-1h" /bin/bash -c 'while [ $SECONDS -lt 5 ]; do date; sleep 1; done'
# Wed Aug 16 08:23:12 CST 2023
# Wed Aug 16 08:23:13 CST 2023
# Wed Aug 16 08:23:14 CST 2023
# Wed Aug 16 08:23:15 CST 2023

設定開始時間

我們也可以使用 faketime 來設定開始時間。例如,我們可以將時間設定為 2023 年 1 月 2 日的 03:04:05:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
date
# Wed Aug 16 09:23:00 CST 2023
LD_PRELOAD=/usr/lib/x86_64-linux-gnu/faketime/libfaketime.so.1 FAKETIME="@2023-01-02 03:04:05" /bin/bash -c 'while [ $SECONDS -lt 5 ]; do date; sleep 1; done'
# Mon Jan  2 03:04:05 CST 2023
# Mon Jan  2 03:04:05 CST 2023
# Mon Jan  2 03:04:05 CST 2023
# Mon Jan  2 03:04:05 CST 2023
# Mon Jan  2 03:04:05 CST 2023
LD_PRELOAD=/usr/lib/x86_64-linux-gnu/faketime/libfaketime.so.1 FAKETIME="@2023-01-02 03:04:05" FAKETIME_DONT_RESET=1 /bin/bash -c 'while [ $SECONDS -lt 5 ]; do date; sleep 1; done'
# Mon Jan  2 03:04:05 CST 2023
# Mon Jan  2 03:04:06 CST 2023
# Mon Jan  2 03:04:07 CST 2023
# Mon Jan  2 03:04:08 CST 2023
# Mon Jan  2 03:04:09 CST 2023

在這裡,時間前面加上@,需要持續時間要加上FAKETIME_DONT_RESET=1環境變數。格式也可以是(+|-)(0-9)+[d|h|m|s|...]

容器執行

如果你想在 Docker 容器中使用 faketime,你可以使用以下的 Docker 映像檔:

以下是一些使用範例:

1
docker run --rm -it --name some-faketime-machine -e "FAKETIME=-15d"    andy23512/faketime-machine bash
1
docker run --rm -it --name some-faketime-machine -e "FAKETIME=1989-01-01 00:00:00"    andy23512/faketime-machine bash
1
docker run --rm -it --name some-faketime-machine -e "FAKETIME=@'1989-01-01 00:00:00'" -e FAKETIME_DONT_RESET=1 andy23512/faketime-machine bash
Tip
事後想想可以手動開 VM 改 Date,把 ntpdate 關掉更新時間,這樣應該也能達到一樣效果?但 Docker 時間好像跟系統綁在一起的,所以沒辦法做更動。

相關指令

相關文章

FAKETIME_STOP_AFTER_SECONDS=3裡面提到幾秒後會還原時間。

1
2
3
4
5
6
LD_PRELOAD=/usr/lib/x86_64-linux-gnu/faketime/libfaketime.so.1 FAKETIME="@2023-01-02 03:04:05" FAKETIME_DONT_RESET=1 FAKETIME_STOP_AFTER_SECONDS=3 /bin/bash -c 'while [ $SECONDS -lt 5 ]; do date; sleep 1; done'
# Mon Jan  2 03:04:05 CST 2023
# Mon Jan  2 03:04:06 CST 2023
# Mon Jan  2 03:04:07 CST 2023
# Wed Aug 16 09:55:02 CST 2023
# Wed Aug 16 09:55:03 CST 2023

問題是faketime使用LD_PRELOAD環境變數來指示程序的動態載入器libfaketime在啟動時載入。libfaketime將執行所謂的“插入” - 用這些例程的自己的副本替換正常的動態庫例程 - 這樣當將來進行動態庫呼叫時,libfaketime可以影響發生的事情。特別是,libfaketime插入與時間相關的呼叫,因此它能夠向程序返回假值。

這適用於大多數程序的原因是它們用於libc進行系統呼叫。libc提供與系統呼叫互動的高級函數,使系統程式設計更容易。在大多數使用 的語言中libc,二進制檔案是動態連結的,這意味著它libc實際上並不包含在二進制檔案中,而是預期libc在運行二進制檔案時系統上將存在(稱為“目標檔案”)的編譯版本,然後就可以載入動態庫了。這種動態載入是faketime通過LD_PRELOAD指令實現的,它改變了載入器的行為。

然而,Go 有兩個不同之處。首先,它是靜態連結的,因此沒有載入器可以關注LD_PRELOAD. 其次,它不使用libc,因此即使它是動態連結的,並且該LD_PRELOAD技巧有效,libc也永遠不會被呼叫,因此它實際上仍然無法實現欺騙程序使用假時間函數的預期目標。

看來 libfaketime 不是萬靈丹。

彩蛋