Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#

How to make keyloggers life difficult

4.84/5 (21 votes)
20 Jan 2013CPOL4 min read 43.8K   924  
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)