PDA

Просмотр полной версии : [Статья] KeyLogger [1]


HideFebruary
03.03.2016, 23:25
Прошлая статья: KeyLogger [0] ([Ссылки могут видеть только зарегистрированные и активированные пользователи])

Продолжаем тему написания кейлоггера. Немного обдумав концепцию, я пришел к некоторому решению которое опишу далее по тексту.

Итак, мой дорогой читатель, мы научились узнавать нажата ли клавиша, но в таком виде использовать логгер, лично мне, очень неудобно. Вот не хочу я каждый раз проверять нажата ли клавиша которая мне нужна, я ленивый. Хочу что бы когда ее нажали, я первый об этом узнал и уже, если нужно, что-то предпринял.

Надеюсь ты читал про события ([Ссылки могут видеть только зарегистрированные и активированные пользователи]) в c#, иначе далее придется туго! Хотя всегда можно скопировать исходник в конце статьи и даже не париться. В любом случае советую изучить, не раз пригодится.

Сейчас мы будем переделывать наш прошлый класс, который сам будет проверять нажаты ли нужные нам кнопки и уведомлять нас в случае нажатия, а далее уже по нашему усмотрению. Для этого необходимо иметь в классе список клавиш на проверку, далее, за очень короткий промежуток времени проверять каждую из них. Создадим класс с именем KeyWatcher и добавим метод WinApi с константой из прошлой статьи и методом IsPressing. Далее объявим в классе две новые переменные, это массив Keys и Timer из пространства имен System.Timers, главное не перепутать, таймеров там много!


private readonly Keys[] _chekingKeys;
private readonly System.Timers.Timer _timer;


Также, нам нужен метод, который подпишется на событие таймера и будет проверять по нему какие кнопки нажаты и сам генерировать событие! Я назвал этот метод OnTimer, но к реализации вернемся позже. В конструкторе класса предлагаю инициализировать переменные:


public KeyWatcher ( IEnumerable<Keys> keys )
{
_chekingKeys = keys.ToArray ();

const double interval = 10;
_timer = new System.Timers.Timer ( interval );
_timer.Elapsed += OnTimer;
}


В аргумент конструктора требуем список клавиш о нажатии которых нужно уведомлять.

Класс сконструирован. Теперь остается сделать реализацию метода OnTimer и добавить в наш класс событие на которое можно подписаться, при чем событие должно быть с параметром, что бы явно знать какая из клавиш нам пришла. Когда я первый раз делал набросок класса, запускал таймер сразу в конструкторе с очень коротким интервалом, это не правильно подумал я, потому что не факт что сразу после запуска кто-то подпишется, а таймер будет тратить процессорное время впустую и я выпилил эту идею и придумал новую. Мы будем запускать таймер только в том случае, если кто-то подписался на событие и отключать его сразу после того как все отписались, очень хорошее решение на мой взгляд. Правда это немного усложняет код, но это ничего, какой читер боится сложностей? :)

Для реализации умного таймера, нам нужно добавить приватное событие в классе и инкапсулировать логику подписки с помощью свойства. Кстати, в .net есть замечательный класс KeyEventArgs ([Ссылки могут видеть только зарегистрированные и активированные пользователи] px) который мы будем использовать как параметр в событиях.


public event EventHandler<KeyEventArgs> Pressed
{
add
{
_pressed += value;

if( !_timer.Enabled ) _timer.Start();
}

remove
{
_pressed -= value;

if( _pressed == null ) _timer.Stop();
}
}

private event EventHandler<KeyEventArgs> _pressed;


Вот такая несложная система у нас будет. Теперь нужно определить наш метод OnTimer и добавить еще один метод для удобства, который будет генерировать событие - OnPressed:


private void OnTimer ( Object sender, System.Timers.ElapsedEventArgs e )
{
foreach( var key in _chekingKeys )
{
if ( IsPressing ( key ) ) OnPressed ( key );
}
}

private void OnPressed( Keys key )
{
var buffer = _pressed;
buffer?.Invoke ( this, new KeyEventArgs ( key ) );
}


А также добавим метод который очистит ресурсы таймера и заодно отпишет нас от его события:


public void Close ()
{
_timer.Close ();
_timer.Elapsed -= OnTimer;
}


В целом наш класс готов для использования. Проверим как он работает, добавив такой код в мейне:


var keys = Enum.GetValues(typeof(Keys)).Cast<Keys>();
var keyWatcher = new KeyWatcher(keys);

keyWatcher.Pressed += KeyWatcherPressed;

WriteLine ( $"{nameof ( KeyWatcher )} запущен, нажмите любую клавишу что бы остановить программу" );
ReadLine ();

keyWatcher.Pressed -= KeyWatcherPressed;
keyWatcher.Close ();


И реализация метода который подписался на событие:

private static void KeyWatcherPressed( Object sender, KeyEventArgs e )
{
WriteLine ( $"{e.KeyData.ToString ()} is pressed" );
}


На этом всё. Исходники из данной статьи под спойлером.



using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using static System.Console;

namespace ConsoleApp
{
class Program
{
static void Main ( string [ ] args )
{
var keys = Enum.GetValues(typeof(Keys)).Cast<Keys>();
var keyWatcher = new KeyWatcher(keys);

keyWatcher.Pressed += KeyWatcherPressed;

WriteLine ( $"{nameof ( KeyWatcher )} запущен, нажмите любую клавишу что бы остановить программу" );
ReadLine ();

keyWatcher.Pressed -= KeyWatcherPressed;
keyWatcher.Close ();
}

private static void KeyWatcherPressed ( Object sender, KeyEventArgs e )
{
WriteLine ( $"{e.KeyData.ToString ()} is pressed" );
}
}

public sealed class KeyWatcher
{
public event EventHandler<KeyEventArgs> Pressed
{
add
{
_pressed += value;

if( !_timer.Enabled ) _timer.Start();
}
remove
{
_pressed -= value;

if( _pressed == null ) _timer.Stop();
}
}

public KeyWatcher ( IEnumerable<Keys> keys )
{
_chekingKeys = keys.ToArray ();

const double interval = 10;
_timer = new System.Timers.Timer ( interval );
_timer.Elapsed += OnTimer;
}

public void Close ()
{
_timer.Close ();
_timer.Elapsed -= OnTimer;
}

private void OnTimer ( Object sender, System.Timers.ElapsedEventArgs e )
{
foreach( var key in _chekingKeys )
{
if ( IsPressing ( key ) ) OnPressed ( key );
}
}

private void OnPressed( Keys key )
{
var buffer = _pressed;
buffer?.Invoke ( this, new KeyEventArgs ( key ) );
}

private bool IsPressing ( Keys key )
=> GetAsyncKeyState ( key ) == PRESSING;

private const int PRESSING = -32767;

private readonly Keys[] _chekingKeys;
private readonly System.Timers.Timer _timer;
private event EventHandler<KeyEventArgs> _pressed;

[DllImport ( "User32.dll" )]
private static extern short GetAsyncKeyState ( Keys key );
}
}



Следующая статья: KeyLogger [2] ([Ссылки могут видеть только зарегистрированные и активированные пользователи])