using System; using System.IO; using System.IO.Pipes; using System.Runtime.InteropServices; using System.Threading; using EasyHook; namespace TimeMocker.Hook { /// /// Shared memory layout written by the UI and read by the hook. /// Stored in a named memory-mapped file so no pipe latency on hot path. /// [StructLayout(LayoutKind.Sequential)] public struct MockTimeInfo { public long DeltaTicks; // Offset to add to DateTime.UtcNow.Ticks (can be negative) } // ------------------------------------------------------------------------- // Win32 structs // ------------------------------------------------------------------------- [StructLayout(LayoutKind.Sequential)] public struct SYSTEMTIME { public ushort wYear, wMonth, wDayOfWeek, wDay; public ushort wHour, wMinute, wSecond, wMilliseconds; public static SYSTEMTIME FromDateTime(DateTime dt) { return new SYSTEMTIME { wYear = (ushort)dt.Year, wMonth = (ushort)dt.Month, wDayOfWeek = (ushort)dt.DayOfWeek, wDay = (ushort)dt.Day, wHour = (ushort)dt.Hour, wMinute = (ushort)dt.Minute, wSecond = (ushort)dt.Second, wMilliseconds = (ushort)dt.Millisecond }; } } // ------------------------------------------------------------------------- // EasyHook entry point – called after DLL is injected // ------------------------------------------------------------------------- public class InjectionEntryPoint : IEntryPoint { private readonly string _mmfName; private System.IO.MemoryMappedFiles.MemoryMappedFile _mmf; private System.IO.MemoryMappedFiles.MemoryMappedViewAccessor _view; // Hook handles private LocalHook _getSystemTimeHook; private LocalHook _getLocalTimeHook; private LocalHook _ntQuerySystemTimeHook; private LocalHook _getSystemTimeAsFileTimeHook; private LocalHook _getSystemTimePreciseAsFileTimeHook; public InjectionEntryPoint(RemoteHooking.IContext context, string mmfName) { _mmfName = mmfName; } public void Run(RemoteHooking.IContext context, string mmfName) { string logPath = Path.Combine(Path.GetTempPath(), "TimeMocker.Hook.log"); try { File.AppendAllText(logPath, $"[{DateTime.Now}] Starting hook, MMF: {mmfName}\r\n"); // Open the shared memory created by the UI process _mmf = System.IO.MemoryMappedFiles.MemoryMappedFile.OpenExisting(mmfName); _view = _mmf.CreateViewAccessor(0, Marshal.SizeOf()); File.AppendAllText(logPath, $"[{DateTime.Now}] MMF opened successfully\r\n"); InstallHooks(); File.AppendAllText(logPath, $"[{DateTime.Now}] Hooks installed successfully\r\n"); RemoteHooking.WakeUpProcess(); File.AppendAllText(logPath, $"[{DateTime.Now}] Process woken up\r\n"); // Keep alive until process exits while (true) Thread.Sleep(500); } catch (Exception ex) { File.AppendAllText(logPath, $"[{DateTime.Now}] ERROR: {ex.GetType().Name}: {ex.Message}\r\n{ex.StackTrace}\r\n"); } finally { _getSystemTimeHook?.Dispose(); _getLocalTimeHook?.Dispose(); _ntQuerySystemTimeHook?.Dispose(); _getSystemTimeAsFileTimeHook?.Dispose(); _getSystemTimePreciseAsFileTimeHook?.Dispose(); _view?.Dispose(); _mmf?.Dispose(); } } // ------------------------------------------------------------------------- // Helpers // ------------------------------------------------------------------------- private MockTimeInfo ReadMockInfo() { _view.Read(0, out MockTimeInfo info); return info; } private DateTime GetFakeUtc() { var info = ReadMockInfo(); // Return real UTC time plus the stored delta offset return new DateTime(DateTime.UtcNow.Ticks + info.DeltaTicks, DateTimeKind.Utc); } private void InstallHooks() { _getSystemTimeHook = LocalHook.Create( LocalHook.GetProcAddress("kernel32.dll", "GetSystemTime"), new GetSystemTimeDelegate(GetSystemTime_Hook), this); _getSystemTimeHook.ThreadACL.SetExclusiveACL(new[] { 0 }); _getLocalTimeHook = LocalHook.Create( LocalHook.GetProcAddress("kernel32.dll", "GetLocalTime"), new GetLocalTimeDelegate(GetLocalTime_Hook), this); _getLocalTimeHook.ThreadACL.SetExclusiveACL(new[] { 0 }); _ntQuerySystemTimeHook = LocalHook.Create( LocalHook.GetProcAddress("ntdll.dll", "NtQuerySystemTime"), new NtQuerySystemTimeDelegate(NtQuerySystemTime_Hook), this); _ntQuerySystemTimeHook.ThreadACL.SetExclusiveACL(new[] { 0 }); _getSystemTimeAsFileTimeHook = LocalHook.Create( LocalHook.GetProcAddress("kernel32.dll", "GetSystemTimeAsFileTime"), new GetSystemTimeAsFileTimeDelegate(GetSystemTimeAsFileTime_Hook), this); _getSystemTimeAsFileTimeHook.ThreadACL.SetExclusiveACL(new[] { 0 }); _getSystemTimePreciseAsFileTimeHook = LocalHook.Create( LocalHook.GetProcAddress("kernel32.dll", "GetSystemTimePreciseAsFileTime"), new GetSystemTimeAsFileTimeDelegate(GetSystemTimePreciseAsFileTime_Hook), this); _getSystemTimePreciseAsFileTimeHook.ThreadACL.SetExclusiveACL(new[] { 0 }); } // ------------------------------------------------------------------------- // Hook implementations // ------------------------------------------------------------------------- [UnmanagedFunctionPointer(CallingConvention.StdCall)] private delegate void GetSystemTimeDelegate(out SYSTEMTIME lpSystemTime); private void GetSystemTime_Hook(out SYSTEMTIME lpSystemTime) { lpSystemTime = SYSTEMTIME.FromDateTime(GetFakeUtc()); } [UnmanagedFunctionPointer(CallingConvention.StdCall)] private delegate void GetLocalTimeDelegate(out SYSTEMTIME lpLocalTime); private void GetLocalTime_Hook(out SYSTEMTIME lpLocalTime) { lpLocalTime = SYSTEMTIME.FromDateTime(GetFakeUtc().ToLocalTime()); } [UnmanagedFunctionPointer(CallingConvention.StdCall)] private delegate int NtQuerySystemTimeDelegate(out long systemTime); private int NtQuerySystemTime_Hook(out long systemTime) { // FILETIME epoch: Jan 1, 1601 var epoch = new DateTime(1601, 1, 1, 0, 0, 0, DateTimeKind.Utc); systemTime = (GetFakeUtc() - epoch).Ticks; return 0; // STATUS_SUCCESS } [UnmanagedFunctionPointer(CallingConvention.StdCall)] private delegate void GetSystemTimeAsFileTimeDelegate(out long lpFileTime); private void GetSystemTimeAsFileTime_Hook(out long lpFileTime) { var epoch = new DateTime(1601, 1, 1, 0, 0, 0, DateTimeKind.Utc); lpFileTime = (GetFakeUtc() - epoch).Ticks; } private void GetSystemTimePreciseAsFileTime_Hook(out long lpFileTime) { var epoch = new DateTime(1601, 1, 1, 0, 0, 0, DateTimeKind.Utc); lpFileTime = (GetFakeUtc() - epoch).Ticks; } } }