我们在写程序时,时常要用到文件操作。文件操作是一种I/O读写,必定要用到系统调用或API。在WinApi的CreateFile函数中

HANDLE CreateFileA(
  LPCSTR                lpFileName,
  DWORD                 dwDesiredAccess,
  DWORD                 dwShareMode,
  LPSECURITY_ATTRIBUTES lpSecurityAttributes,
  DWORD                 dwCreationDisposition,
  DWORD                 dwFlagsAndAttributes,
  HANDLE                hTemplateFile
);

倒数第二个参数设置为 CREATE_ALWAYS时,创建的新文件会覆盖原有的同名文件。《终将成为F》就利用了类似的思想,构造了一个非常巧妙的结构。

假设一个监控程序P,它在每分钟开始的时刻创建监控文件,这个文件记录该分钟内的事件。考虑以下情况:在程序开始运行时,系统时间比标准时间快1分钟(这里的“快”指数值比标准时间大)。在程序运行的过程中,系统通过校准,将时间调整为了标准时间,会发生什么呢?

假设调整为标准时间的时刻是a:b:c(标准时间),那么该系统错误显示的时间就是a:(b+1):c。假设P在a:(b+1):00(系统时间)时刻创建的文件名为a:b+1,那么在系统正确显示后,在新的a:b+1时刻,系统会创建新的a:b+1文件。这样一来,实际上的a:b(标准时间)文件就被覆盖了。《全部成为F》的作者就用这样的构造,做出了一个密室。具体情况可参见小说或动画。

我在WINDOWS环境下编写了以下程序来验证这个构造的正确性:

SYSTEMTIME time;
GetLocalTime(&time);
while (true)
{
    while (true)
    {
        GetLocalTime(&time);
        if (time.wSecond == 0 && time.wMilliseconds == 0)
        {
            break;
        }
    }

    wchar_t* name = (wchar_t*)malloc(0xff);
    memset(name, 0, 0xff);
    wsprintf(name, L"Time is %02d:%02d", time.wHour, time.wMinute);

    HANDLE hFile = CreateFile(name, GENERIC_READ | GENERIC_WRITE, 
        0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);

    if (hFile == INVALID_HANDLE_VALUE)
    {
        GetError(0);
        exit(-1);
    }

    DWORD real = 0;     

    if (WriteFile(hFile, &count, 4, &real, NULL) == false)
    {
        GetError(1);
        exit(-1);
    }

    Sleep(1);
    count++;
    CloseHandle(hFile);
    free(name);
}

在运行前,我将系统时间调快了一分钟,并在运行时开启自动对时。结果成功地验证了该构造的正确性。

2:55的计数为0,2:56的计数为1……系统在2:58调整为了正确的时间,按照前述,2:58的计数应该为4,因为计数为3的2:58文件被覆盖了。用WINHEX查看:

4在前面是因为x86采取小端序,小端在前

这样来就验证了该结构的正确性。

发表评论

电子邮件地址不会被公开。 必填项已用*标注