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 FakeUtcTicks; // DateTime ticks (UTC)
public int Enabled; // 1 = mock active, 0 = passthrough
}
// -------------------------------------------------------------------------
// 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)
{
try
{
// Open the shared memory created by the UI process
_mmf = System.IO.MemoryMappedFiles.MemoryMappedFile.OpenExisting(mmfName);
_view = _mmf.CreateViewAccessor(0, Marshal.SizeOf());
InstallHooks();
RemoteHooking.WakeUpProcess();
// Keep alive until process exits
while (true) Thread.Sleep(500);
}
catch (Exception ex)
{
File.AppendAllText(
Path.Combine(Path.GetTempPath(), "TimeMocker.Hook.log"),
$"[{DateTime.Now}] ERROR: {ex}\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 info.Enabled == 1
? new DateTime(info.FakeUtcTicks, DateTimeKind.Utc)
: DateTime.UtcNow;
}
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)]
delegate void GetSystemTimeDelegate(out SYSTEMTIME lpSystemTime);
void GetSystemTime_Hook(out SYSTEMTIME lpSystemTime)
{
lpSystemTime = SYSTEMTIME.FromDateTime(GetFakeUtc());
}
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
delegate void GetLocalTimeDelegate(out SYSTEMTIME lpLocalTime);
void GetLocalTime_Hook(out SYSTEMTIME lpLocalTime)
{
lpLocalTime = SYSTEMTIME.FromDateTime(GetFakeUtc().ToLocalTime());
}
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
delegate int NtQuerySystemTimeDelegate(out long systemTime);
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)]
delegate void GetSystemTimeAsFileTimeDelegate(out long lpFileTime);
void GetSystemTimeAsFileTime_Hook(out long lpFileTime)
{
var epoch = new DateTime(1601, 1, 1, 0, 0, 0, DateTimeKind.Utc);
lpFileTime = (GetFakeUtc() - epoch).Ticks;
}
void GetSystemTimePreciseAsFileTime_Hook(out long lpFileTime)
{
var epoch = new DateTime(1601, 1, 1, 0, 0, 0, DateTimeKind.Utc);
lpFileTime = (GetFakeUtc() - epoch).Ticks;
}
}
}