This post provides an example PowerShell script to create a global system low-level keyboard hook, detecting and discarding the first Alt+Tab combination after the hook is installed. This was developed and tested on Windows Vista.
This is run out of a PowerShell script using runtime compiled VB.Net code, with a form-less Application.Run() call to wait for the message. You could also hook WH_MOUSE_LL using similar code.
I was originally trying to hook WH_GETMESSAGE, but it seems this is not possible without a DLL which is loaded and injected into running processes to provide the shared address space, something which negated what I was trying to achieve (a simple demo with PowerShell).
Note that the MSDN SetWindowsHookEx Function description (http://msdn.microsoft.com/en-us/library/ms644990.aspx) defines WH_KEYBOARD_LL as a global only hook, which to my understanding means it needs to be referenced in a DLL, which is most definitely not the case below as hMod is intptr.zero and it's all running out of script/dynamic code. I also tested using the hinstance of the compiled module, which didn't work, and using loadlibrary user32 which did work (I don't understand why I just copied from some other example).
## KeyboardHookExample.ps1 ##
#
# Description:
# Example global WH_KEYBOARD_LL hook in PowerShell using VB.Net runtime compiled code.
# Adds a hook in the LIFO chain for all keyboard activity to CallbackFunction(), which checks for and discards the first Alt+Tab combination
#
# Author:
# Wayne Martin, 15/06/2008, http://waynes-world-it.blogspot.com/
#
# References:
# CLRInsideOut\WinSigGen.exe
# http://msdn.microsoft.com/en-us/library/ms644959(VS.85).aspx
# http://msdn.microsoft.com/en-us/library/ms644960(VS.85).aspx
# http://support.microsoft.com/kb/319524
# http://blogs.msdn.com/toub/archive/2006/05/03/589468.aspx
$provider = new-object Microsoft.VisualBasic.VBCodeProvider
$params = new-object System.CodeDom.Compiler.CompilerParameters
$params.GenerateInMemory = $True
$refs = "System.dll","Microsoft.VisualBasic.dll","System.Windows.Forms.dll"
$params.ReferencedAssemblies.AddRange($refs)
# VB.NET EXAMPLE
$txtCode = @'
Imports Microsoft.VisualBasic
Imports System
Imports System.Runtime.InteropServices
Imports System.Windows.Forms
Public Class hookExample
Public hookHandle As Integer
Public hWndExplorer as Integer
Private CallbackDelegate As HookProc
_
Public Structure KBDLLHOOKSTRUCT
'''DWORD->unsigned int
Public vkCode As UInteger
'''DWORD->unsigned int
Public scanCode As UInteger
'''DWORD->unsigned int
Public flags As UInteger
'''DWORD->unsigned int
Public time As UInteger
'''ULONG_PTR->unsigned int
Public dwExtraInfo As UInteger
End Structure
'''Return Type: LRESULT->LONG_PTR->int
'''code: int
'''wParam: WPARAM->UINT_PTR->unsigned int
'''lParam: LPARAM->LONG_PTR->int
_
Public Delegate Function HOOKPROC(ByVal code As Integer, ByVal wParam As System.IntPtr, ByVal lParam As System.IntPtr) As Integer
_
Public Structure HHOOK__
'''int
Public unused As Integer
End Structure
_
Public Structure HINSTANCE__
'''int
Public unused As Integer
End Structure
'''Return Type: HHOOK->HHOOK__*
'''idHook: int
'''lpfn: HOOKPROC
'''hmod: HINSTANCE->HINSTANCE__*
'''dwThreadId: DWORD->unsigned int
_
Public Shared Function SetWindowsHookExW(ByVal idHook As Integer, ByVal lpfn As HOOKPROC, ByVal hmod As System.IntPtr, ByVal dwThreadId As UInteger) As System.IntPtr
End Function
'''Return Type: LRESULT->LONG_PTR->int
'''hhk: HHOOK->HHOOK__*
'''nCode: int
'''wParam: WPARAM->UINT_PTR->unsigned int
'''lParam: LPARAM->LONG_PTR->int
_
Public Shared Function CallNextHookEx( ByVal hhk As System.IntPtr, ByVal nCode As Integer, ByVal wParam As System.IntPtr, ByVal lParam As System.IntPtr) As Integer
End Function
'''Return Type: BOOL->int
'''hhk: HHOOK->HHOOK__*
_
Public Shared Function UnhookWindowsHookEx( ByVal hhk As System.IntPtr) As Boolean
End Function
'''HC_ACTION -> 0
Public Const HC_ACTION As Integer = 0
'''LLKHF_ALTDOWN -> (KF_ALTDOWN >> 8)
Public Const LLKHF_ALTDOWN As Integer = (KF_ALTDOWN) >> (8)
'''KF_ALTDOWN -> 0x2000
Public Const KF_ALTDOWN As Integer = 8192
'''VK_TAB -> 0x09
Public Const VK_TAB As Integer = 9
'''WH_KEYBOARD_LL -> 13
Public Const WH_KEYBOARD_LL As Integer = 13
'''Return Type: HMODULE->HINSTANCE->HINSTANCE__*
'''lpLibFileName: LPCWSTR->WCHAR*
_
Public Shared Function LoadLibraryW( ByVal lpLibFileName As String) As System.IntPtr
End Function
Public Sub New()
CallbackDelegate = New HookProc(AddressOf CallbackFunction)
'Dim hInstance As IntPtr
' This doesn't work:
'hInstance = Marshal.GetHINSTANCE(Reflection.Assembly.GetExecutingAssembly().GetModules()(0))
'hookHandle = SetWindowsHookExW(WH_KEYBOARD_LL, CallbackDelegate, hInstance, 0)
' This does work, but is unnecessary and intptr.zero also works:
'hInstance = LoadLibraryW("User32")
'hookHandle = SetWindowsHookExW(WH_KEYBOARD_LL, CallbackDelegate, hInstance, 0)
hookHandle = SetWindowsHookExW(WH_KEYBOARD_LL, CallbackDelegate, IntPtr.Zero, 0)
If (hookHandle > 0) Then
console.writeline("Keyboard hooked, press Alt+Tab when any window has focus to test")
End If
Application.Run()
End Sub
Public Function CallbackFunction(ByVal code As Integer, ByVal wParam As IntPtr, ByVal lParam As IntPtr) As Integer
Dim HookStruct As KBDLLHOOKSTRUCT
If (code < 0) Then
Return CallNextHookEx(hookHandle, code, wParam, lParam)
End If
hookStruct = CType(Marshal.PtrToStructure(lParam, GetType(KBDLLHOOKSTRUCT)),KBDLLHOOKSTRUCT)
If (Hookstruct.vkCode = VK_TAB) And (Hookstruct.flags And LLKHF_ALTDOWN) Then
console.writeline("Alt+Tab detected, discarding and removing hook")
Call UnhookWindowsHookEx(hookHandle)
Application.Exit()
Return 1
Else
Return CallNextHookEx(hookHandle, code, wParam, lParam)
End If
End Function
End Class
'@
$results = $provider.CompileAssemblyFromSource($params, $txtCode)
if ($results.Errors.Count) {
$codeLines = $txtCode.Split("`n");
foreach ($ce in $results.Errors)
{
write-host "Error: $($codeLines[$($ce.Line - 1)])"
write-host $ce
#$ce out-default
}
Throw "INVALID DATA: Errors encountered while compiling code"
}
$mAssembly = $results.CompiledAssembly
$i = $mAssembly.CreateInstance("hookExample")
#$r = $i.New()
Wayne's World of IT (WWoIT), Copyright 2008 Wayne Martin.
No comments:
Post a Comment