Click here to Skip to main content
15,867,308 members
Articles / Programming Languages / C#

How to make keyloggers life difficult

Rate me:
Please Sign up or sign in to vote.
4.84/5 (21 votes)
20 Jan 2013CPOL4 min read 41.7K   919   60   17
This secure textbox deceptive keyloggers

Introduction  

If you usually typing a password in a desktop application, it may be that a keylogger spies out your secrets. This is obviously not good. Screen keyboards might be a good solution, but again, there could be a screen capture program which watches effortlessly your passwords. Furthermore, screen keyboards are relative unhandily.

The following article presents a relatively simple principle, which prevents the keylogger to write down passwords entered. Basically, the used system is surprisingly easy and can therefore be transferred to other programming / operating systems / platforms, although under a small limitation.

Background 

The first question is: How can a program hide keystrokes? Perhaps there are some difficult ways to do this, but most probable this is not possible. We create an assumption: Entered characters necessarily mean the keylogger sees them. And that's what we use against the keylogger.

The second question is: How can a program generate keystrokes? This is normally possible. For example, we use in C# the class SendKeys, which provides methods for sending keystrokes. By the way, there can be the mentioned limitation because a website has not the authorization to produce keystrokes.

The third question is: How can we combine these two statements? At every time when the user types a character, the program generates some keytrokes more. The keylogger write down all characters both from user and program, but only the user and the program know the entire password. Unauthorized third parties see only letter salad and they can not decrypt the main password.

The fourth question is: Is this main system 100 % secure? Surprisingly and unfortunately no. The prinziple has many weak points, but there are also many solutions to close these gaps. I advise every developer to think about it before they add this concept to their code. Let me explain you the vulnerabilities and the solutions:

  1. Creating random keystrokes after every character allows attackers to reproduce the typed password. Therefore the program must create identical keystrokes. Then again the produced keystrokes should not be identical for all passwords. In summary, we need a algorithm, which produces for every character always the same keystrokes. In addition to this, the length of the generated keystrokes should vary.

  2. For all that, an attacker can create a table with all characters and their hash result either by reverse engineering or by testing. Using this table, he can decrypt the password relatively easy. To prevent this, the generated keystrokes should depend on a password identity, such as account name, account number, e-mail or computer specification. Unfortunately, this is only an obstacle, but no blockage for attackers.

Using the code

At first, the create a new component SecureTextBox with some properties:

C#
public class SecureTextBox : TextBox
{
     /// <summary>
     /// Gets the typed password.
     /// </summary>
     public string Password
     {
          get;
          private set;
     }
 
     /// <summary>
     /// Sets or gets the password ID.
     /// </summary>
     public string ID
     {
          get;
          set;
     }
}

The next step is to implement a constructor for initializing important events:

C#
public SecureTextBox()
{
     this.TextChanged += new EventHandler(SecureTextBox_TextChanged);
     this.KeyDown += new KeyEventHandler(SecureTextBox_KeyDown);
     this.KeyUp += new KeyEventHandler(SecureTextBox_KeyUp);
}    

The methods SecureTextBox_KeyDown and SecureTextBox_KeyUp should ensure that no key is pressed, otherwise characters are inserted incorrectly or even not.  The boolean variable IsTriggering declares if the user entered a character while he is holding another key.

C#
private int KeysPressed = 0;
private bool IsTriggering = false;
 
void SecureTextBox_KeyDown(object sender, KeyEventArgs e)
{
     KeysPressed++;
}
 
void SecureTextBox_KeyUp(object sender, KeyEventArgs e)
{
     KeysPressed--;
     if (KeysPressed == 0 & IsTriggering)
          this.SecureTextBox_TextChanged(null, null);
}

Now consider the random functions. For this example, I used Random combined with a given seed. The seed is created from the ID and the last entered character from user.

C#
Random _NextSaltLength, _NextSaltChar;
// TODO: Extend the CharContent with all important characters!
string CharContent = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
 
private int NextSaltLength(bool CreateNew)
{
	// TODO: Replace this code with your own function!
	if (CreateNew)
		_NextSaltLength = new Random(ID.GetHashCode() - Password[Password.Length - 1].GetHashCode());
	return _NextSaltLength.Next(1, 4);
}
 
private string NextSaltChar(bool CreateNew)
{
	// TODO: Replace this code with your own function!
	if (CreateNew)
		_NextSaltChar = new Random(ID.GetHashCode() + Password[Password.Length - 1].GetHashCode());
	return CharContent[_NextSaltChar.Next(CharContent.Length)].ToString();
}

Finally, we can create the main method which manage the generation of the keystrokes:

C#
private int RemainingSaltChars = 0;
private int LastTextLength = 0;
 
void SecureTextBox_TextChanged(object sender, EventArgs e)
{
     if (KeysPressed > 0)
     {
          IsTriggering = true;
          return;
     }
     IsTriggering = false;
     if (LastTextLength < this.TextLength)
     {
          LastTextLength = this.TextLength;
          if (RemainingSaltChars > 0)
          {
               if (RemainingSaltChars > 1)
                    SendKeys.Send(this.NextSaltChar(false));
               RemainingSaltChars--;
          }
          else
          {
               this.Password += this.Text[this.TextLength - 1];
               RemainingSaltChars = this.NextSaltLength(true);
               SendKeys.Send(this.NextSaltChar(true));
          }
     }
     else
     {
          this.ResetText();
          LastTextLength = 0;
     }
}

Here is a short description of the implementation: If no keys are pressed and the user entered a character in the TextBox, the char is saved, the quantity of the next generated characters are randomly calculated and the first character is send through SendKeys.Send(). Now the program is in a complicated loop and generates the specified keystrokes. This state is interrupted, if RemainingSaltChars is zero. Just as a footnote, if the user presses Delete or Backspace, the text will be reset because otherwise this would mix up the algorithm.

C#
public override void ResetText()
{
     base.ResetText();
     this.Password = String.Empty;
}

Points of Interest 

Originally I have this prinziple from a software, which polls for a password at startup. At first I wondered why more masked characters apperared in the textbox than typed until I figured it out. I implemented my thoughts and experimented with my code using a keylogger. I was very surprised about the effectivity. Then I wanted to analyse how good the other application works while using a keylogger. But the program produced no keystrokes. Unbelievable! I found this very amusing.

In the introduction I said that this prinziple could not be implemented by a website. A solution for this would be a extra Add-On for the browser, which could take on this task. 

History 

Published on 19 January 2013.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Student
Germany Germany
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionWouldn't be simpler... Pin
Cristian Amarie26-Dec-13 9:13
Cristian Amarie26-Dec-13 9:13 
... to have the control memorize in an internal buffer the typed character, and set in the edit control buffer just a dumb char, L'*' or something?
I mean, the idea is cool, but why bother with encryption, when is simpler to just replace everything with dumb public data and keep real data internally.

Or plain emulate an edit control, but just override/implement WM_PAINT/WM_DRAWITEM (sorry, I don't do NET) for a static control, blink a cursor and make borders to emulate an edit (of course is not so simple, but you get the idea) and WM_GETTEXT or Text property will be just null?
My 0.02.
Nuclear launch detected

GeneralMy vote of 5 Pin
Monjurul Habib2-Feb-13 7:04
professionalMonjurul Habib2-Feb-13 7:04 
GeneralMy vote of 5 Pin
Sergio Andrés Gutiérrez Rojas29-Jan-13 16:14
Sergio Andrés Gutiérrez Rojas29-Jan-13 16:14 
GeneralMy vote of 5 Pin
Joezer BH29-Jan-13 3:23
professionalJoezer BH29-Jan-13 3:23 
GeneralMy vote of 5 Pin
Michael Haephrati24-Jan-13 7:42
professionalMichael Haephrati24-Jan-13 7:42 
AnswerRe: My vote of 5 Pin
M. Hudak24-Jan-13 11:43
M. Hudak24-Jan-13 11:43 
GeneralRe: My vote of 5 Pin
Michael Haephrati24-Jan-13 23:41
professionalMichael Haephrati24-Jan-13 23:41 
AnswerRe: My vote of 5 Pin
M. Hudak25-Jan-13 8:28
M. Hudak25-Jan-13 8:28 
Questionclose...i think Pin
Member 322138523-Jan-13 5:00
Member 322138523-Jan-13 5:00 
AnswerRe: close...i think Pin
M. Hudak24-Jan-13 11:33
M. Hudak24-Jan-13 11:33 
GeneralMy vote of 5 Pin
JH6421-Jan-13 11:33
JH6421-Jan-13 11:33 
QuestionDon't think it will work Pin
HaBiX20-Jan-13 21:42
HaBiX20-Jan-13 21:42 
AnswerRe: Don't think it will work Pin
M. Hudak21-Jan-13 23:54
M. Hudak21-Jan-13 23:54 
GeneralRe: Don't think it will work Pin
HaBiX22-Jan-13 0:06
HaBiX22-Jan-13 0:06 
GeneralMy vote of 5 Pin
ojanacek20-Jan-13 21:39
ojanacek20-Jan-13 21:39 
QuestionInteresting: need a keylogger now Pin
Bruno Tabbia20-Jan-13 20:48
Bruno Tabbia20-Jan-13 20:48 
AnswerRe: Interesting: need a keylogger now Pin
Eddy Vluggen9-Feb-13 1:38
professionalEddy Vluggen9-Feb-13 1:38 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.