You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在C# WPF应用启动前检测系统时间是否被篡改?

检测C# WPF应用启动前系统时间篡改的方案

方案1:读取Windows系统事件日志

Windows系统会自动记录时间变更操作,其中EventID 12代表系统时间被设置为更早的时间,EventID 13代表被设置为更晚的时间。应用启动时,可查询系统日志中对应的事件,结合应用上次运行的时间范围判断是否存在篡改行为。

  • 实现步骤:
    1. 读取System日志源下EventID为12/13的事件
    2. 过滤出事件发生时间在应用上次关闭到本次启动之间的记录
    3. 若存在EventID 12的事件,说明用户将时间改早,触发许可证校验失败

代码示例:

using System.Diagnostics;

public bool CheckTimeTamperingFromEventLog(DateTime lastRunUtc)
{
    var eventLog = new EventLog("System");
    foreach (EventLogEntry entry in eventLog.Entries)
    {
        if (entry.InstanceId == 12 || entry.InstanceId == 13)
        {
            var eventTimeUtc = entry.TimeGenerated.ToUniversalTime();
            if (eventTimeUtc > lastRunUtc && entry.InstanceId == 12)
            {
                return true; // 检测到时间篡改
            }
        }
    }
    return false;
}

方案2:加密存储上次运行的UTC时间

每次应用正常退出时,将当前UTC时间加密存储到本地(注册表或加密文件),下次启动时解密读取该时间并与当前UTC时间对比:

  • 若当前UTC时间 < 存储的上次运行时间,说明用户将系统时间改早
  • 为应对异常退出场景,可定时(比如每10分钟)更新存储的时间,避免崩溃导致记录未更新

代码示例(用DPAPI加密存储到注册表):

using System.Security.Cryptography;
using Microsoft.Win32;

public static class TimeStorage
{
    private const string RegistryPath = @"Software\YourAppName\License";
    private const string TimeKey = "LastRunUtc";

    public static void SaveLastRunTime()
    {
        var utcNow = DateTime.UtcNow.ToString("o");
        byte[] encryptedData = ProtectedData.Protect(
            System.Text.Encoding.UTF8.GetBytes(utcNow),
            null,
            DataProtectionScope.LocalMachine);

        using (var key = Registry.LocalMachine.CreateSubKey(RegistryPath))
        {
            key?.SetValue(TimeKey, encryptedData);
        }
    }

    public static DateTime? GetLastRunTime()
    {
        using (var key = Registry.LocalMachine.OpenSubKey(RegistryPath))
        {
            if (key?.GetValue(TimeKey) is byte[] encryptedData)
            {
                try
                {
                    byte[] decryptedData = ProtectedData.Unprotect(
                        encryptedData,
                        null,
                        DataProtectionScope.LocalMachine);
                    var utcStr = System.Text.Encoding.UTF8.GetString(decryptedData);
                    return DateTime.Parse(utcStr, null, System.Globalization.DateTimeStyles.RoundtripKind);
                }
                catch
                {
                    return null; // 解密失败,说明记录被篡改
                }
            }
        }
        return null;
    }
}

// 启动时检测
var lastRunUtc = TimeStorage.GetLastRunTime();
if (lastRunUtc.HasValue && DateTime.UtcNow < lastRunUtc.Value)
{
    // 触发许可证失效逻辑
}
// 正常运行时定时保存,比如在窗口Closing事件或定时任务中执行
TimeStorage.SaveLastRunTime();

方案3:结合网络时间校验(在线场景)

在线环境下,可从NTP服务器获取标准UTC时间,与本地UTC时间对比,若偏差超过合理范围(比如5分钟),则判断存在篡改。离线场景下可 fallback 到前两种方案。

代码示例:

using System.Net.Sockets;

public DateTime? GetNtpTime(string ntpServer = "pool.ntp.org")
{
    try
    {
        var ntpData = new byte[48];
        ntpData[0] = 0x1B; // LI=0, Version=3, Mode=3(Client)

        using (var socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp))
        {
            socket.Connect(ntpServer, 123);
            socket.Send(ntpData);
            socket.Receive(ntpData);
            socket.Close();
        }

        ulong intPart = BitConverter.ToUInt32(ntpData, 40);
        ulong fracPart = BitConverter.ToUInt32(ntpData, 44);

        intPart = SwapEndianness(intPart);
        fracPart = SwapEndianness(fracPart);

        var milliseconds = (intPart * 1000) + ((fracPart * 1000) / 0x100000000L);
        var ntpTime = new DateTime(1900, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddMilliseconds(milliseconds);
        return ntpTime;
    }
    catch
    {
        return null;
    }
}

private static ulong SwapEndianness(ulong x)
{
    return ((x & 0x000000FF) << 24) +
           ((x & 0x0000FF00) << 8) +
           ((x & 0x00FF0000) >> 8) +
           ((x & 0xFF000000) >> 24);
}

// 启动时校验
var ntpTime = GetNtpTime();
if (ntpTime.HasValue)
{
    var timeDiff = Math.Abs((DateTime.UtcNow - ntpTime.Value).TotalMinutes);
    if (timeDiff > 5) // 偏差超过5分钟判定篡改
    {
        // 处理逻辑
    }
}

注意事项

  • 单一方法存在漏洞风险,建议结合多种方案(比如日志检测+本地加密存储)
  • 加密存储的位置和方式需避免用户轻易篡改,例如使用LocalMachine注册表(需管理员权限)或加密文件
  • 网络时间校验需处理超时和离线情况,不能强制依赖网络

内容的提问来源于stack exchange,提问作者Darth Tyrannosaurus

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 18:15:18