【问题标题】:How to capture global keystrokes with PowerShell?如何使用 PowerShell 捕获全局击键?
【发布时间】:2019-06-11 16:45:57
【问题描述】:

Powershell 可以监听和捕获按键吗?

是否可以编写一个 PowerShell 脚本,例如 AutoHotkey,它位于托盘中并等待您按下预定义的键盘键开始执行?并且每次按下所述键时可能不会返回但会触发?

我想要实现的是 - 仅在启动脚本后按按钮执行预定义的脚本操作,因此将其放在桌面上并定义快捷键不起作用。

例如:
我希望每次按“x”键时输入 3 次文本“TEST”,但我希望只有在执行此操作的脚本正在运行时才会发生这种情况。因此,当脚本未运行时 - 按“x”将无济于事。

基本上,AutoHotkey 可以做到这一点,但如果可能的话,我想在 PowerShell 中做到这一点,而无需编写大量 C# 代码,因为那样我只需为此编写一个小型 C# 托盘应用程序。

【问题讨论】:

标签: powershell hotkeys keystroke keylogger


【解决方案1】:

也许不能直接在 PowerShell 中运行,但您几乎可以运行任何 C# 代码,这里有一个基于 excellent solution by Peter Hinchley 的基本工作示例:

Add-Type -TypeDefinition '
using System;
using System.IO;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace KeyLogger {
  public static class Program {
    private const int WH_KEYBOARD_LL = 13;
    private const int WM_KEYDOWN = 0x0100;

    private static HookProc hookProc = HookCallback;
    private static IntPtr hookId = IntPtr.Zero;
    private static int keyCode = 0;

    [DllImport("user32.dll")]
    private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);

    [DllImport("user32.dll")]
    private static extern bool UnhookWindowsHookEx(IntPtr hhk);

    [DllImport("user32.dll")]
    private static extern IntPtr SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hMod, uint dwThreadId);

    [DllImport("kernel32.dll")]
    private static extern IntPtr GetModuleHandle(string lpModuleName);

    public static int WaitForKey() {
      hookId = SetHook(hookProc);
      Application.Run();
      UnhookWindowsHookEx(hookId);
      return keyCode;
    }

    private static IntPtr SetHook(HookProc hookProc) {
      IntPtr moduleHandle = GetModuleHandle(Process.GetCurrentProcess().MainModule.ModuleName);
      return SetWindowsHookEx(WH_KEYBOARD_LL, hookProc, moduleHandle, 0);
    }

    private delegate IntPtr HookProc(int nCode, IntPtr wParam, IntPtr lParam);

    private static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam) {
      if (nCode >= 0 && wParam == (IntPtr)WM_KEYDOWN) {
        keyCode = Marshal.ReadInt32(lParam);
        Application.Exit();
      }
      return CallNextHookEx(hookId, nCode, wParam, lParam);
    }
  }
}
' -ReferencedAssemblies System.Windows.Forms

while ($true) {
    $key = [System.Windows.Forms.Keys][KeyLogger.Program]::WaitForKey()
    if ($key -eq "X") {
        Write-Host "Do something now."
    }
}

第 2 版

(使用回调):

Add-Type -TypeDefinition '
  using System;
  using System.IO;
  using System.Diagnostics;
  using System.Runtime.InteropServices;
  using System.Windows.Forms;

  namespace PowerShell {
    public static class KeyLogger {
      private const int WH_KEYBOARD_LL = 13;
      private const int WM_KEYDOWN = 0x0100;

      private delegate IntPtr HookProc(int nCode, IntPtr wParam, IntPtr lParam);

      private static Action<Keys> keyCallback;
      private static IntPtr hookId = IntPtr.Zero;

      [DllImport("user32.dll")]
      private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);

      [DllImport("user32.dll")]
      private static extern bool UnhookWindowsHookEx(IntPtr hhk);

      [DllImport("user32.dll")]
      private static extern IntPtr SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hMod, uint dwThreadId);

      [DllImport("kernel32.dll")]
      private static extern IntPtr GetModuleHandle(string lpModuleName);

      public static void Run(Action<Keys> callback) {
        keyCallback = callback;
        hookId = SetHook();
        Application.Run();
        UnhookWindowsHookEx(hookId);
      }

      private static IntPtr SetHook() {
        IntPtr moduleHandle = GetModuleHandle(Process.GetCurrentProcess().MainModule.ModuleName);
        return SetWindowsHookEx(WH_KEYBOARD_LL, HookCallback, moduleHandle, 0);
      }

      private static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam) {
        if (nCode >= 0 && wParam == (IntPtr)WM_KEYDOWN) {
          var key = (Keys)Marshal.ReadInt32(lParam);
          keyCallback(key);
        }
        return CallNextHookEx(hookId, nCode, wParam, lParam);
      }
    }
  }
' -ReferencedAssemblies System.Windows.Forms

[PowerShell.KeyLogger]::Run({
  param($key)
  if ($key -eq "X") {
    Write-Host "Do something now."
  }
})

【讨论】:

  • 谢谢,马泽。 J. Doe,您可以将此代码与 script I wrote that puts a bunch of code snippets in your system tray 结合使用,以便轻松启用或禁用它。
  • 实际上可以在 Powershell 中完全做到这一点,而无需一行 C#。由于 PInvoke 调用,您必须使用 ModuleBuilder(与 C# 相比,它涉及大量额外代码)
  • @bluuf 可能。很想看看。但最终它只是 .NET,所以脚本中的 C#、VB 或 PS 代码并不重要。对吗?
  • @marsze 仅适用于恶意软件构建者,它产生了真正的不同:嵌入在 Powershell 中的 C# 代码被编译(它将文件写入磁盘并随后将其删除)。使用模块构建器在内存中完成所有操作(这就是许多无文件恶意软件避免检测的方式)
  • V2 在尝试退出时似乎崩溃了,无论我在 if 语句中输入 exit 还是仅使用键盘发送 ctrl+c
【解决方案2】:

您可以在第三方编码脚本(Autohotkey)的帮助下使用 Powershell 捕获击键

您只需要在 Powershell 中读取此 Windows 注册表项。

$val = (Get-ItemProperty -path 'HKCU:\Software\GetKeypressValue').KeypressValue

如果你再运行这两个 AHk 脚本 (KeypressValueToREG + ShowKeypressValue) 那么它是间接可能的。

注意 - ShowKeypressValue.ahk 仅用于直观地显示您的所有鼠标按钮点击和所有键盘击键(无需使用此脚本)

您只能在后台运行 KeypressValueToREG.Ahk,然后您就可以开始了。 (您可以将所有击键值捕获到一个变量中$val

KeypressValueToREG.ahk

;KeypressValueToREG.ahk comes from KeypressOSD.ahk that was Created by Author RaptorX
; Open this Script in Wordpad and For Changelog look to the Bottom of the script. 
;This code works with a getkeyname from a Dllcall (See Bottom Script- by Lexikos)
;you can press the esc key to exit.

#SingleInstance force
#NoEnv
SetBatchLines, -1
ListLines, Off

; Settings
    global TransN                := 200      ; 0~255
    global ShowSingleKey         := True
    global ShowMouseButton       := True
    global ShowSingleModifierKey := True
    global ShowModifierKeyCount  := true
    global ShowStickyModKeyCount := false
    global DisplayTime           := 2000     ; In milliseconds
    global GuiPosition           := "Bottom" ; Top or Bottom
    global FontSize              := 50
    global GuiHeight             := 115

CreateGUI()
CreateHotkey()
return

OnKeyPressed:
    try {
        key := GetKeyStr()
        ShowHotkey(key)
        SetTimer, HideGUI, % -1 * DisplayTime
    }
return

OnKeyUp:
return

_OnKeyUp:
    tickcount_start := A_TickCount
return


CreateGUI() {
    global

    Gui, +AlwaysOnTop -Caption +Owner +LastFound +E0x20
    Gui, Margin, 0, 0
    Gui, Color, Black
    Gui, Font, cWhite s%FontSize% bold, Arial
    Gui, Add, Text, vHotkeyText Center y20

    WinSet, Transparent, %TransN%
}

CreateHotkey() {
    Loop, 95
    {
        k := Chr(A_Index + 31)
        k := (k = " ") ? "Space" : k

        Hotkey, % "~*" k, OnKeyPressed
        Hotkey, % "~*" k " Up", _OnKeyUp
    }

    Loop, 24 ; F1-F24
    {
        Hotkey, % "~*F" A_Index, OnKeyPressed
        Hotkey, % "~*F" A_Index " Up", _OnKeyUp
    }

    Loop, 10 ; Numpad0 - Numpad9
    {
        Hotkey, % "~*Numpad" A_Index - 1, OnKeyPressed
        Hotkey, % "~*Numpad" A_Index - 1 " Up", _OnKeyUp
    }

    Otherkeys := "WheelDown|WheelUp|WheelLeft|WheelRight|XButton1|XButton2|Browser_Forward|Browser_Back|Browser_Refresh|Browser_Stop|Browser_Search|Browser_Favorites|Browser_Home|Volume_Mute|Volume_Down|Volume_Up|Media_Next|Media_Prev|Media_Stop|Media_Play_Pause|Launch_Mail|Launch_Media|Launch_App1|Launch_App2|Help|Sleep|PrintScreen|CtrlBreak|Break|AppsKey|NumpadDot|NumpadDiv|NumpadMult|NumpadAdd|NumpadSub|NumpadEnter|Tab|Enter|Esc|BackSpace"
               . "|Del|Insert|Home|End|PgUp|PgDn|Up|Down|Left|Right|ScrollLock|CapsLock|NumLock|Pause|sc145|sc146|sc046|sc123"
    Loop, parse, Otherkeys, |
    {
        Hotkey, % "~*" A_LoopField, OnKeyPressed
        Hotkey, % "~*" A_LoopField " Up", _OnKeyUp
    }

    If ShowMouseButton {
        Loop, Parse, % "LButton|MButton|RButton", |
            Hotkey, % "~*" A_LoopField, OnKeyPressed
    }

    for i, mod in ["Ctrl", "Shift", "Alt"] {
        Hotkey, % "~*" mod, OnKeyPressed
        Hotkey, % "~*" mod " Up", OnKeyUp
    }
    for i, mod in ["LWin", "RWin"]
        Hotkey, % "~*" mod, OnKeyPressed
}

ShowHotkey(HotkeyStr) {
    WinGetPos, ActWin_X, ActWin_Y, ActWin_W, ActWin_H, A
    if !ActWin_W
        throw

    text_w := (ActWin_W > A_ScreenWidth) ? A_ScreenWidth : ActWin_W

    ;remove this gui codeline if you want only to Write the Value to Windows registry
    ;GuiControl,     , HotkeyText, %HotkeyStr%
    ;GuiControl,     , HotkeyText, %HotkeyStr%

    RegWrite, REG_SZ, HKEY_CURRENT_USER,software\GetKeypressValue,KeypressValue,%HotkeyStr%

    ;remove this gui codeline if you want only to Write the Value to Windows registry
    ;GuiControl, Move, HotkeyText, w%text_w% Center
    ;GuiControl, Move, HotkeyText, w%text_w% Center

    if (GuiPosition = "Top")
        gui_y := ActWin_Y
    else
        gui_y := (ActWin_Y+ActWin_H) - 115 - 50

    ;remove this gui codeline if you want only to Write the Value to Windows registry
    ;Gui, Show, NoActivate x%ActWin_X% y%gui_y% h%GuiHeight% w%text_w%
    ;Gui, Show, NoActivate x%ActWin_X% y%gui_y% h%GuiHeight% w%text_w%
}

GetKeyStr() {
    static modifiers := ["Ctrl", "Shift", "Alt", "LWin", "RWin"]
    static repeatCount := 1

    for i, mod in modifiers {
        if GetKeyState(mod)
            prefix .= mod " + "
    }

    if (!prefix && !ShowSingleKey)
        throw

    key := SubStr(A_ThisHotkey, 3)

    if (key ~= "i)^(Ctrl|Shift|Alt|LWin|RWin)$") {
        if !ShowSingleModifierKey {
            throw
        }
        key := ""
        prefix := RTrim(prefix, "+ ")

        if ShowModifierKeyCount {
            if !InStr(prefix, "+") && IsDoubleClickEx() {
                if (A_ThisHotKey != A_PriorHotKey) || ShowStickyModKeyCount {
                    if (++repeatCount > 1) {
                        prefix .= " ( * " repeatCount " )"
                    }
                } else {
                    repeatCount := 0
                }
            } else {
                repeatCount := 1
            }
        }
    } else {
        if ( StrLen(key) = 1 ) {
            key := GetKeyChar(key, "A")
        } else if ( SubStr(key, 1, 2) = "sc" ) {
            key := SpecialSC(key)
        } else if (key = "LButton") && IsDoubleClick() {
            key := "Double-Click"
        }
        _key := (key = "Double-Click") ? "LButton" : key

        static pre_prefix, pre_key, keyCount := 1
        global tickcount_start
        if (prefix && pre_prefix) && (A_TickCount-tickcount_start < 300) {
            if (prefix != pre_prefix) {
                result := pre_prefix pre_key ", " prefix key
            } else {
                keyCount := (key=pre_key) ? (keyCount+1) : 1
                key := (keyCount>2) ? (key " (" keyCount ")") : (pre_key ", " key)
            }
        } else {
            keyCount := 1
        }

        pre_prefix := prefix
        pre_key := _key

        repeatCount := 1
    }
    return result ? result : prefix . key
}

SpecialSC(sc) {
    static k := {sc046: "ScrollLock", sc145: "NumLock", sc146: "Pause", sc123: "Genius LuxeMate Scroll"}
    return k[sc]
}

; by Lexikos - https://autohotkey.com/board/topic/110808-getkeyname-for-other-languages/#entry682236
GetKeyChar(Key, WinTitle:=0) {
    thread := WinTitle=0 ? 0
        : DllCall("GetWindowThreadProcessId", "ptr", WinExist(WinTitle), "ptr", 0)
    hkl := DllCall("GetKeyboardLayout", "uint", thread, "ptr")
    vk := GetKeyVK(Key), sc := GetKeySC(Key)
    VarSetCapacity(state, 256, 0)
    VarSetCapacity(char, 4, 0)
    n := DllCall("ToUnicodeEx", "uint", vk, "uint", sc
        , "ptr", &state, "ptr", &char, "int", 2, "uint", 0, "ptr", hkl)
    return StrGet(&char, n, "utf-16")
}

IsDoubleClick(MSec = 300) {
    Return (A_ThisHotKey = A_PriorHotKey) && (A_TimeSincePriorHotkey < MSec)
}

IsDoubleClickEx(MSec = 300) {
    preHotkey := RegExReplace(A_PriorHotkey, "i) Up$")
    Return (A_ThisHotKey = preHotkey) && (A_TimeSincePriorHotkey < MSec)
}

HideGUI() {
    Gui, Hide
}

~esc::exitapp    
;---------------------------------------------
; ChangeLog : v2.22 (2017-02-25) - Now pressing the same combination keys continuously more than 2 times,
;                                  for example press Ctrl+V 3 times, will displayed as "Ctrl + v (3)"
;             v2.21 (2017-02-24) - Fixed LWin/RWin not poping up start menu
;             v2.20 (2017-02-24) - Added displaying continuous-pressed combination keys.
;                                  e.g.: With CTRL key held down, pressing K and U continuously will shown as "Ctrl + k, u"
;             v2.10 (2017-01-22) - Added ShowStickyModKeyCount option
;             v2.09 (2017-01-22) - Added ShowModifierKeyCount option
;             v2.08 (2017-01-19) - Fixed a bug
;             v2.07 (2017-01-19) - Added ShowSingleModifierKey option (default is True)
;             v2.06 (2016-11-23) - Added more keys. Thanks to SashaChernykh.
;             v2.05 (2016-10-01) - Fixed not detecting "Ctrl + ScrollLock/NumLock/Pause". Thanks to lexikos.
;             v2.04 (2016-10-01) - Added NumpadDot and AppsKey
;             v2.03 (2016-09-17) - Added displaying "Double-Click" of the left mouse button.
;             v2.02 (2016-09-16) - Added displaying mouse button, and 3 settings (ShowMouseButton, FontSize, GuiHeight)
;             v2.01 (2016-09-11) - Display non english keyboard layout characters when combine with modifer keys.
;             v2.00 (2016-09-01) - Removed the "Fade out" effect because of its buggy.
;                                - Added support for non english keyboard layout.
;                                - Added GuiPosition setting.
;             v1.00 (2013-10-11) - First release.
;--------------------------------------------

ShowKeypressValue.ahk

#SingleInstance force
Gui, +AlwaysOnTop -MaximizeBox ; -Caption +Resize -MinimizeBox +Disabled -SysMenu -Owner +OwnDialogs
Gui, Add, Text, center y10 h50 w300 vVar,  %KeypressValue%
Gui, Color, White
Gui, show
size=20
Gui, Font, s%size%
GuiControl, Font, var

;run KeypressValueToREG.ahk - together with ShowKeypressValue.ahk
;The Features Are:
; - It will Show On your Screen, [All your Mouse Movements] and [All Keyboard Shortcuts Movement]
; - You can Make Scripts, that can do actions with MultiClicks on All Keyboard Shortcuts Clicks, How Cool Is that. 

loop
{
RegRead, KeypressValue, HKEY_CURRENT_USER,software\GetKeypressValue,KeypressValue ; read KeypressValue
sleep 50
GuiControl,, var, %KeypressValue%



if (KeypressValue="Alt ( * 2 )") ;use this for [1x=Alt][2x=Alt ( * 2 )][3x=Alt ( * 3 )] [and many more]
{
;Here you can put any AHK CODE 
msgbox you did click Alt 2x Times
}

if (KeypressValue="Alt ( * 3 )") ;use this for [1x=Alt][2x=Alt ( * 2 )][3x=Alt ( * 3 )] [and many more]
{
;Here you can put any AHK CODE 
msgbox you did click Alt 3x Times
}


} ;End Loop

~esc::exitapp

【讨论】:

  • 最初的问题不是只在 PowerShell 中执行,没有 AutoHotKey?
  • 谢谢,但正如@marsze 指出的那样,如果我必须使用两个 AHK 脚本和 PowerShell,我只需使用一个 AHK 脚本来做我想做的事。 :) 问题的本质是 PowerShell 是否可以自己侦听和捕获击键以制作在按键时触发的脚本。
  • 嗨@mar​​sze - 你不能直接在PowerShell中使用脚本捕获所有击键动作,在我的例子中你也可以(间接)做,只有一个AHK脚本(在后台运行) - 之后,您只需读取一个注册表项,然后将其放入一个变量中,然后使用该变量,您可以执行任何真正的 PowerShell 语言。
  • @stevecody 你错了,这可以直接在 Powershell 中完成。甚至可以在 Powershell 脚本中不使用一行 C# 代码(通过使用反射来构建 Pinvoke 方法)
  • @stevecody 优雅和单行代码在您必须求助于 3rd 方工具时并不真正相关,尤其是在不使用 3rd 方工具的情况下(Marsze 提供的解决方案是我称之为优雅的解决方案)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-04-10
  • 2014-05-06
  • 1970-01-01
  • 2013-04-17
  • 1970-01-01
  • 2019-07-11
相关资源
最近更新 更多