Categories:
  🠪  General

Email
  🠪  Servers
  🠪  Testing
  🠪  Tips

Hardware
  🠪  3D Printing
  🠪  Apple
  🠪  Batteries
  🠪  Drives
  🠪  Edgerouter
  🠪  Electronics
  🠪  Laptop
  🠪  Modems
  🠪  Phone
  🠪  Printers
  🠪  Raspberry Pi
  🠪  Tablets
  🠪  Testing
  🠪  Virtualization

hidden
  🠪  General

Links
  🠪  Interesting
  🠪  Media

Network
  🠪  Data
  🠪  Testing
  🠪  VPN

Scripts
  🠪  Batch
  🠪  Linux
  🠪  Powershell

Servers
  🠪  Databases
  🠪  Misc
  🠪  Website

Software
  🠪  Other

Utilities
  🠪  Backup
  🠪  Fix Issues
  🠪  Recovery

Video
  🠪  Editing

Websites
  🠪  HTML
  🠪  Testing

Windows
  🠪  Adjustments
  🠪  Issues
  🠪  Remote Desktop
  🠪  Security
  🠪  Slow
  🠪  Software
  🠪  Startup

Submit Entry
Airin's Notes

Category: Scripts 🠪 Powershell
Hotkeys Test
November 27, 2023

#https://hinchley.net/articles/creating-a-key-logger-via-a-global-system-hoo
k-using-powershell/
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;
public static string strKey = "";
public static bool blnDown = false;

//public delegate void EventHandler(string strKey);
//public event EventHandler KeyWasPressed;
public static event EventHandler KeyWasPressed = delegate {};


public static void Main() {
hookId = SetHook(hookProc);
Application.Run();
UnhookWindowsHookEx(hookId);
}

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) {
int vkCode = Marshal.ReadInt32(lParam);
strKey = Convert.ToString((Keys)vkCode);

if ( wParam == (IntPtr)WM_KEYDOWN ) {
blnDown = true;
}else{
blnDown = false;
}

Application.Exit();
//Console.WriteLine( strKey );
//RaiseKeyPressed( strKey );

//if ( strKey == "Escape"){
// Application.Exit();
//}
}

return CallNextHookEx(hookId, nCode, wParam, lParam);
}

public static void RaiseKeyPressed (string message)
{
if (KeyWasPressed != null){
KeyWasPressed( message, null );
}
}





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

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

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

[DllImport("kernel32.dll")]
private static extern IntPtr GetModuleHandle(string lpModuleName);
}
}
"@ -ReferencedAssemblies System.Windows.Forms

$Debug = $false
#if ( $psISE ) { $Debug = $true }
$Keys = @{}
$Keys.Shift = $false
$Keys.Alt = $false
$Keys.Ctrl = $false
$Keys.HotKeys = @()
$Keys.KeepLooping = $true
$Keys.KeyIsDown = $false
$Keys.LastKey = ""
$Keys.Letter = ""
$Keys.Pressed = @{}

$Keys.Program = [KeyLogger.Program]
$Keys.Queued = [System.Collections.Queue]::Synchronized( (New-Object
System.Collections.Queue) )
#Register-ObjectEvent -InputObject $Keylogger -EventName "KeyWasPressed"
-Action { Write-Host ("KeyWasPressed: @ $(Get-Date) " + $EventArgs.Message +
"`r`n" + ( $EventArgs | fl * | Out-String )) }
[void]
[System.Reflection.Assembly]::LoadWithPartialName("'System.Windows.Forms")



$signature = '[DllImport("user32.dll")]public static extern short
GetKeyState(int nVirtKey);'
$type = Add-Type -MemberDefinition $signature -Name User32 -Namespace
GetKeyState -PassThru

function Add-Hotkey($CTRL = $false, $ALT = $false, $Shift = $false, $Key =
"", $Name = "", $Action = "type", $Variable=""){
$NewHK = @{}
$NewHK.CTRL = $CTRL
$NewHK.ALT = $ALT
$NewHK.Shift = $Shift
$NewHK.Key = $Key
$NewHK.Name = $Name
$NewHK.Action = $Action
$NewHK.Variable = $Variable
$Keys.HotKeys += $NewHK
}


function CheckForHotkeys{
# Interesting thing to note: Alt key can only be detected if it was NOT
the first key pressed!
# CTRL-ALT-T will detect all 3, but ALT-CTRL-T will only detect CTRL-T.



foreach ( $HK in $Keys.HotKeys ){
if ( $Keys.Alt -eq $HK.Alt -and $Keys.CTRL -eq $HK.CTRL -and
$Keys.Shift -eq $HK.Shift -and $Keys.Pressed.Item($HK.Key) ){
Write-Host "Found hot key: '$($HK.Name)' Action:
'$($HK.Action)' Variable: '$($HK.Variable)'"

switch ($HK.Action)
{
'type' {
Add-ToSendKeysQueue -Text $HK.Variable
}
'ps' {
Invoke-Expression $HK.Variable
}
'exit' {
$Keys.KeepLooping = $false
}
Default {
Write-Host "Unknown action: '$($HK.Action)' Variable:
'$HK.Variable'"
}
}

}
}

}




function Get-KeyState([uint16]$keyCode, $KeyName = ""){ #Used to query any
key state
#List of keycodes is here:
https://docs.microsoft.com/en-us/windows/desktop/inputdev/virtual-key-codes

if ($KeyName.length -gt 0 ){
if ( $KeyName.length -gt 1 ){
if ( $KeyName -eq "F1" ) { $keyCode = 0x70 }
if ( $KeyName -eq "F2" ) { $keyCode = 0x71 }
if ( $KeyName -eq "F3" ) { $keyCode = 0x72 }
if ( $KeyName -eq "F4" ) { $keyCode = 0x73 }
if ( $KeyName -eq "F5" ) { $keyCode = 0x74 }
if ( $KeyName -eq "F6" ) { $keyCode = 0x75 }
if ( $KeyName -eq "F7" ) { $keyCode = 0x76 }
if ( $KeyName -eq "F8" ) { $keyCode = 0x77 }
if ( $KeyName -eq "F9" ) { $keyCode = 0x78 }
if ( $KeyName -eq "F10" ) { $keyCode = 0x79 }
if ( $KeyName -eq "F11" ) { $keyCode = 0x7A }
if ( $KeyName -eq "F12" ) { $keyCode = 0x7B }
}elseif ( $KeyName.length -eq 1 ){
$keyCode = [byte][char]($KeyName).ToUpper()
}

if ( $keyCode -eq 0 ){ return $null }
}

return [bool]($type::GetKeyState($keyCode) -band 0x80)
}


function Add-ToSendKeysQueue($Text){
Write-Host "$(Get-Date) - Adding text to send queue once keys are
released: $($Text)"
$Keys.Queued.Clear()
$Keys.Queued.Enqueue($Text)
}

function Send-Keys($Text){
if ( $true ){

#Save current clipboard text so we can revert back to it
$OldClipboard = Get-Clipboard

#Save text to the clipboard
Set-Clipboard $Text

#Send CTRL-V command to paste text
[System.Windows.Forms.SendKeys]::SendWait("^v") | Out-Null
Write-Host "$(Get-Date) - Just typed in text: $($Text)"

#Set clipboard text back to what it was
Set-Clipboard $OldClipboard

}else{
[System.Windows.Forms.SendKeys]::SendWait($Text) | Out-Null
Write-Host "$(Get-Date) - Just typed in text: $($Text)"
}
}





Add-Hotkey -CTRL $true -Shift $true -Key "F12" -Action "Exit" -Name "Exit"
Write-Host "Added exit hotkey of CTRL-SHIFT-F12"

Add-Hotkey -CTRL $true -Shift $true -Key "Q" -Action "type" -Name "Test1"
-Variable "Typing this is a test"
Write-Host "Added test hotkey of CTRL-SHIFT-Q"

Add-Hotkey -CTRL $true -Shift $true -Key "D" -Action "ps" -Name "Test2"
-Variable "Add-ToSendKeysQueue -Text (Get-Date)"
Write-Host "Added test hotkey of CTRL-SHIFT-D"

do{

#Show how long the last process loop took
if ( $Debug ) { if ( $StartTime ) { Write-Host "Time taken:
$((New-Timespan $StartTime).TotalMilliseconds)" } }

$SkipKeyCheck = $false

#Check if key was released while PS was processing, and if so then don't
wait for another key press!
if ( $NewKey.length -gt 0 -and $Keys.KeyIsDown ){

$StillPressed = Get-KeyState -KeyName $NewKey
if ( $StillPressed -eq $false ){
$SkipKeyCheck = $true
$Keys.Pressed.Remove( $NewKey )
}
}

#Check all keys that are marked as being pressed, if any aren't then
skip waiting for keypress
$AllKeys = ( $Keys.Pressed.Keys | Sort )
foreach ( $Key in $AllKeys ){
$StillPressed = Get-KeyState -KeyName $Key
if ( $StillPressed -eq $false ){
$SkipKeyCheck = $true
$Keys.Pressed.Remove( $Key )
}
}


#Start Program
if ( $SkipKeyCheck -eq $false ){
$Keys.Program::Main()
if ( $Debug ) { $StartTime = Get-Date }
$Keys.KeyIsDown = $Keys.Program::blnDown
$NewKey = $Keys.Program::strKey

}else{
#Key was released very quickly, run loop again
$Keys.KeyIsDown = $false
}

#Get Keys



#Check for duplicate
if ( $NewKey -eq $Keys.LastKey -and $Keys.KeyIsDown -and
$Keys.Pressed.Item( $Keys.LastKey ) ){
#Nothing changed, key must be held down, don't do anything

}else{

$Keys.LastKey = $NewKey


if ( $Keys.LastKey.length -gt 0 ){
#Save current state of this key
if ( $Keys.KeyIsDown ){
$Keys.Pressed.Item( $Keys.LastKey ) = $true
}else{
# Key is not pressed, change value to null
$Keys.Pressed.Remove( $Keys.LastKey )
}



#Shift Key
$Keys.Shift = [bool]($type::GetKeyState(0x10) -band 0x80)
if ( ! $Keys.Shift ){
$Keys.Pressed.Remove( "LShiftKey" )
$Keys.Pressed.Remove( "RShiftKey" )
}


#ALT Key
$Keys.ALT = [bool]($type::GetKeyState(0x12) -band 0x80)
if ( ! $Keys.ALT ){
$Keys.Pressed.Remove( "LMenu" )
$Keys.Pressed.Remove( "RMenu" )
}


#CTRL Key
$Keys.CTRL = [bool]($type::GetKeyState(0x11) -band 0x80)
if ( ! $Keys.CTRL ){
$Keys.Pressed.Remove( "LControlKey" )
$Keys.Pressed.Remove( "RControlKey" )
}



# Only check for hotkeys if one of these keys is pressed
if ( $Keys.ALT -or $Keys.CTRL -or $Debug ){
CheckForHotkeys

if ( $Keys.KeyIsDown ){
Write-Host "Pressed: $($Keys.LastKey) Shift:
$($Keys.Shift) ALT: $($Keys.ALT) CTRL: $($Keys.CTRL) Pressed: $(
$Keys.Pressed.Keys -join ',')"
}else{
Write-Host "Released: $($Keys.LastKey) Shift:
$($Keys.Shift) ALT: $($Keys.ALT) CTRL: $($Keys.CTRL) Pressed: $(
$Keys.Pressed.Keys -join ',')"
}
}


if ( $Debug -and $NewKey.Length -eq 1 ){
$StillPressed = Get-KeyState (
[byte][char]($NewKey).ToUpper() )
if ( $StillPressed -eq $false ){
Write-Host "Key $($NewKey) has been released"
#$Keys.Pressed.Remove( $NewKey )
}elseif ( $Debug ){
Write-Host "Key $($NewKey) is still pressed"
}
}


# If something is queued then run it if no keys are still
pressed
if ( $Keys.Queued.count -gt 0 -and ( $Keys.Pressed.Keys.count
-eq 0 -or $false ) ){
do {
#Item is queued and no keys are pressed
$Text = $Keys.Queued.Dequeue()
Send-Keys -Text $Text

}while ( $Keys.Queued.count -gt 0 )
}



#if ( $Keys.LastKey -eq "Escape" ){ $Keys.KeepLooping = $false }

} # End of $Keys.LastKey.length -gt 0

} # End of duplicate check
}
while ( $Keys.KeepLooping )


Get-EventSubscriber | Unregister-Event





This site is meant to be used as a reference for myself, although others may find it useful. I use it to keep track of certain fixes, software, and other solutions which I may need while assisting customers. The page layout is pure HTML/CSS and is kept simple to optimize loading time and fast results.

Return to Airin's Computers