剑走偏锋-使用WMI获取远程计算机进程程序
集中查毒病毒打造内网安全环境
作者:高玉涵
时间:2019.04.18 15:45
博客:blog.csdn.net/cg_i
作者背景环境参见:
《由永恒之蓝病毒引发的运维安全思考——网络边界防御篇 》
《非常运维 一体化终端安全管理系统自动安装脚本详解》
TMOTWTDI -- Perl名言
真正见功夫的地方不在于代码本身,而在于对平台的理解和驾驭能力,可能这就是俗称的“内功”。-- 高玉涵
我希望不曾写过这个程序。--高玉涵
引言
信息安全是运维的根本,直接关系到企业的安危,稍有不慎会造成灾难性后果。比如近段时间,世界范围内爆发的“勒索病毒”事件,另外,国内知名门户网站因系统安全漏洞、病毒引发的数据外泄事件等。因此,信息安全体系建设已经被提到了前所未有的高度。如何提升企业的安全防范水准是目前普遍面临的问题。
很高兴在我写这篇文章时,公司内网环境,正全省统一部署“一体化”终端安全管理系统(简称:管理系统)。这套系统是一整套的内网安全管理解决方案,部署后将彻底解决,我一直苦恼的内网安全问题,提高内网抵御各种风险的能力,我想同样管理着上百台“裸奔”系统机器,并深受其害的同行,应该倍感鼓舞吧!
迫于硬件配置太低,我所在单位内网终端设备(硬件配置:Atom D2560 CPU2.0GHz、DDR2 2G内存、STAT2 160G硬盘、Windows XP Embedded (XPe) 32位)安装管理系统后,业务办理过程中,卡顿感明显(欣慰的是,总部已经意识到这些问题,正差手解决中),身为系统管理人员,在系统安全可控的前提下(《由永恒之蓝病毒引发的运维安全思考——网络边界防御篇 》文中简单介绍了,笔者在系统“祼奔”状态下,做的一些系统控制,防范系统安全的一些尝试,方法还是较为原始),确保各项业务正常办理是首要职责,无奈之下只好“壮士断腕”将防病毒模块卸载(防病毒模块资源占用较大、策略较为严格,会拦截业务系统调用的一些插件,导致某些业务无法理)。
显而易见“自己感冒了都会害怕,传染给电脑呢?”没有防病毒模块的保护,系统安全根本无从谈起。在等待总部给出最终解决方案之前,现有系统控制措施依然有效,安全依然可控。能否在这段过度期间,提高内网系统抵御病毒能力,做到全网范围内,某台终端设备中毒,早发现、早查杀,将风险与损失降到最低?
本文主要讲述,通过Windows管理规范(Windows Management Instrumentation,WMI)技术,实现集中式的病毒扫描机制的方法。显然,WMI是一个庞大的话题,用几本书的篇幅也讲不完,因此,我只能在本文中给出最简短的概览、设计思路、示例代码,同时与大家分享。希望,对你的工作有所帮助,起到举一反三的效果。
集中查毒设计思路图
(图 1 客户端程序与计算机的WMI服务通信)
图1一个WMI客户端程序,定期轮询内网所有计算机,获取远程计算机当前系统运行的进程信息,并将进程程序文件,上转至集中查毒服务器。服务器还提供FTP文件服务功能,用于存放待“抽检”的文件,以远程计算机IP地址命名的文件夹内,标识各自所属内网的那台计算机,一但杀毒程序发现病毒,通过文件夹名称,即可定位内网已中毒的计算机,及时了解内网健康情况,达到早发现、早查杀,将风险与损失降到最低的目的。
你可能会问的一些问题
1.SpotCheck.vbs是什么?
我称之为“抽样检查”程序。抽样检查,是从一批产品中随机抽取少量产品(样本) 进行检验,据以判断该批产品是否合格的统计方法和理论。就像我们人需要定期做体验一样,通过设置“抽检”程序定期执行,可及时了解内网健康情况,做出相对应的诊察手段。
2.为什么是进程程序文件?
远程计算机动辄数十GB至数百GB字节的数据,都传到服务器进行查毒是不现实的(也太傻了)。病毒或恶意程序,要保持传染、窃取、破坏等能力,就必须保持“活性”(驻留在系统进程中)。我们只需“抽检”这部份数据即可达到目地。
用Windows Script Host进行脚本编程
在我不长的从业生涯中,学习过数十种计算机语言,学习它们的背景都是因为系统间某些限制或更善长完成特定任务(有些纯粹是因为好奇)。这带来一个好处,让我避免了系统间的限制和兼容问题,根据要执行任务的平台,以它最自然的语言来完成任务。当然这也会带来一些困扰,如,编写代码时各种“方言”会混淆的写在一起。
显然我这个程序主要应用在Windows平台上,程序生命周期定位在“过渡”期内。所以,程序即要充分利用Windows平台提供的强大功能、编写过程也不能太复杂。
用VBScript脚本语言来完成此类任务,可以说是再合适不过了。VBScript是Microsoft自已的脚本语言很容易上手,其实编写之初,我几乎已经忘记这门语言,我从网上下了一份简明教程,通过几个小时的学习,就可以上手编写程序了。
另一方面,它直接可以同Windows对话,例如DLL对象、COM对象、Windows API类库等等,掌握后可以用它完成Windows下所有的工作,而不必通过复杂的编程环境。
Windows管理规范(WMI)
如果你有一台Windows计算机,维护可能是一项令人沮丧的、耗费时间的工作。将这放大数百倍或数千倍。想象一下公司IT经理每天所要面对的事情(我就是这个苦逼的IT经理)。仅仅是记录计算机库存就是很大的一项任务,然后,有频繁的网络设置更改、网络打印机的添加删除、更新应用程序和移动目标共享文件夹,更别提系统崩溃、硬件失效和用户导致的灾难了,将组织的计算机聚集在一起,就像是试图在水中用手握住50个乒乓球。IT工作是进入隔离病房接受一些正式药物治疗的章程票。
摆脱这种错乱的一种方法是,尽可能地避免从一台计算机走到另一台计算机去做更改。在较大的网络中需求更加迫切。WMI则可以帮助减轻维护负担。WMI提供了一种方式来深入查看Windows的内部工作,监视设置并做出修改。WMI伸手触及到Windows操作系统的每个方面,包括设备驱动、系统服务和应用程序。WMI通过网络来工作,因此,运行一个WMI监视程序(或脚本)的计算机可以进入到组织的网上的任何计算机。
别急“压轴好戏”就要上场了
我知道你们急着想看代码,前面的内容如让你感到无趣或啰嗦,先在此表示歉意。为了保证程序能够正常运行,在这之前,我们还是先看看程序运行后的样子和一些注意事项。
1.程序文件
SpotCheck.vbs主程序文件,log.txt是程序运行后生成的日志文件,内容如下面的样子:
......
GetCurrentProcess():C:\WINDOWS\System32\smss.exe,C:\WINDOWS\system32\csrss.exe,C:\WINDOWS\system32\winlogon.exe,C:\WINDOWS\system32\services.exe,C:\WINDOWS\system32\lsass.exe,C:\WINDOWS\system32\cmd.exe,C:\WINDOWS\system32\ftp.exe,
2019/4/8 15:20:22 remote_assembly_upload_file:Method returned result = 0 Id of new process is 3628
2019/4/8 15:20:23 package_team:Method returned result = 0 Id of new process is 1860
2019/4/8 15:20:23 package_team:Method returned result = 0 Id of new process is 6568
2019/4/8 15:20:24 package_team:Method returned result = 0 Id of new process is 6232
......
可以看出日志中记录了GetCurrentProcess()函数,获取的当前进程运行的所有程序,并给出它们的绝对路径,文件间以逗号分隔,这些就是送去“抽检”的文件。
日志文件中还有一些其它信息,它们是程序运行过程中,函数运行状态,有性趣的同学,可通过研究源来代码来了解它们。
1.远程计算机需要做的一些工作
如果远程计算机启用了防火墙,使用netsh firewall set service RemoteAdmin命令,使防火墙允许远程登录。
在工作组网络上WindowsXP系统,要禁用“简单共享”操作如下:1.单击“开始->运行”,输入“gpedit.msc”回车,打开“组策略编辑器”,计算机配置→Windows设置→安全设置→本地策略→安全选项”;2.右边双击“网络访问:本地帐户的共享和安全模式”打开其属性对话框,更改成“经典_本地用户以自己的身份验证”选项。
SpotCheck程序示例源码
'***********************************************************************************
'*
'* File: SpotCheck.vbs
'* Author: 高玉涵
'* Blog: blog.csdn.net/cg_i
'* Created: 2019/4/6
'* Last Modified:2019/4/9
'* Version: 0.1
'* Declaration:
'* 抽样检查程序,通过WMI技术获取远程计算机进程文件,传至指定集中杀毒服务器
'* 根据查杀结果了解内网计算机健康情况,做出相对应的诊察手段。
'***********************************************************************************
Option Explicit
'BUG = 1,显示错误信息;
public const BUG = 1
public const output_log_file = "log.txt"
public const ForReading = 1, ForWriting = 2, ForAppending = 8
public locator,svcs
dim computer
dim username
dim password
computer = "192.168.0.x"
username = "你的用户名"
password = "你的密码"
'----------------------------------------------------------------------
' Author: 高玉涵
' Created: 2016/8/16
' Version: 0.1
' Name: output_log
' Declare: 写日志
' parameter:
' strFileName 文件名
' strMsg 信息
' mode:
' ForReading = 1, ForWriting = 2, ForAppending = 8
'----------------------------------------------------------------------
Sub output_log(strFileName,mode,strMsg)
Dim fso,file,stream
Set fso = CreateObject("Scripting.FileSystemObject")
Set stream = fso.OpenTextFile(strFileName,mode,True)
stream.WriteLine(Now & vbTab & strMsg & vbcrlf)
stream.close
End Sub
'----------------------------------------------------------------------
' Author: 高玉涵
' Created: 2019/4/6
' Version: 0.1
' Name: manage
' Declare: 管理计算机
' parameter:
' computername 计算机名或IP
' username 用户名
' password 密码
' return: True,False
'----------------------------------------------------------------------
function manage(computername, username, password)
set locator = CreateObject("WbemScripting.SWbemLocator")
on error resume next
set svcs = locator.ConnectServer(computername, "root\CIMV2", username, password)
if err <> 0 then
if BUG then WScript.Echo "manage():" & Err.Description, "0x" & Hex(Err.Number)
output_log output_log_file, ForAppending, "manage():" & Err.Description, "0x" & Hex(Err.Number)
manage = False
exit function
end if
manage = True
end function
'----------------------------------------------------------------------
' Author: 高玉涵
' Created: 2019/4/6
' Version: 0.1
' Name: GetCurrentProcess
' Declare: 获取当前系统进程
' parameter:
' svcs 计算机SWbemServices对象
' return: 成功,返回每个进程执行路径字符串,以逗号分隔。失败,为空
'----------------------------------------------------------------------
function GetCurrentProcess(svcs)
Const wbemFlagReturnImmediately = &h10
Const wbemFlagForwardOnly = &h20
dim processes,process,strTmp
on error resume next
set processes = svcs.ExecQuery("SELECT * FROM Win32_Process", "WQL", _
wbemFlagReturnImmediately + wbemFlagForwardOnly)
for each process in processes
if process.ExecutablePath <> vbEmpty and process.ExecutablePath <> "" then strTmp = strTmp & process.ExecutablePath & ","
next
if err <> 0 then
if BUG then WScript.Echo "GetCurrentProcess():" & Err.Description, "0x" & Hex(Err.Number)
output_log output_log_file, ForAppending, "GetCurrentProcess():" & Err.Description, "0x" & Hex(Err.Number)
GetCurrentProcess = Nothing
exit function
end if
GetCurrentProcess = strTmp
end function
'----------------------------------------------------------------------
' Author: 高玉涵
' Created: 2019/4/7
' Version: 0.1
' Name: remote_assembly_upload_file
' Declare: 远程组装上转文件
' parameter:
' svcs 计算机SWbemServices对象
' ftp_command_template FTP命令模板
' ftp_command_file 组装到的远程目标文件(全路径)
' return: 成功,TRUE 失败,FALSE
'----------------------------------------------------------------------
function remote_assembly_upload_file(svcs,ftp_command_template,ftp_command_file)
dim result,process,processid,packages,pack
on error resume next
set process = svcs.Get("Win32_Process")
result = process.Create("cmd /c echo.>" & ftp_command_file, null, null, processid)
output_log output_log_file, ForAppending, "remote_assembly_upload_file:Method returned result = " & result & " " & "Id of new process is " & processid
packages = split(ftp_command_template, vbcrlf)
for each pack in packages
if not package_team(svcs, pack, ftp_command_file) then
exit for
end if
next
if err <> 0 then
if BUG then WScript.Echo "remote_assembly_upload_file():" & Err.Description, "0x" & Hex(Err.Number)
output_log output_log_file, ForAppending, "remote_assembly_upload_file():" & Err.Description, "0x" & Hex(Err.Number)
remote_assembly_upload_file = False
exit function
end if
remote_assembly_upload_file = True
end function
'----------------------------------------------------------------------
' Author: 高玉涵
' Created: 2019/4/7
' Version: 0.1
' Name: package_team
' Declare: 包装小队 - remote_assembly_upload_file函数的调用
' parameter:
' svcs 计算机SWbemServices对象
' ftp_command_file 组装到的远程目标文件(全路径)
' return: 成功,True 失败,False
'----------------------------------------------------------------------
function package_team(svcs,pack,ftp_command_file)
dim result,process,processid
on error resume next
set process = svcs.Get("Win32_Process")
result = process.Create("cmd /c echo " & pack & ">>" & ftp_command_file,null,null,processid)
output_log output_log_file, ForAppending, "package_team:Method returned result = " & result & " " & "Id of new process is " & processid
if err <> 0 then
if BUG then WScript.Echo "package_team():" & Err.Description, "0x" & Hex(Err.Number)
output_log output_log_file, ForAppending, "package_team():" & Err.Description, "0x" & Hex(Err.Number)
package_team = False
exit function
end if
package_team = True
end function
'----------------------------------------------------------------------
' Author: 高玉涵
' Created: 2019/4/8
' Version: 0.1
' Name: ftp_upload
' Declare: FTP上转
' parameter:
' svcs 计算机SWbemServices对象
' ftp_command_file FTP配置文件(全路径)
' return: 成功,True 失败,False
'----------------------------------------------------------------------
function ftp_upload(svcs,ftp_command_file)
dim result,process,processid
on error resume next
set process = svcs.Get("Win32_Process")
result = process.Create("cmd /c ftp -i -s:" & ftp_command_file,null,null,processid)
output_log output_log_file, ForAppending, "ftp_upload:Method returned result = " & result & " " & "Id of new process is " & processid
if err <> 0 then
if BUG then WScript.Echo "ftp_upload():" & Err.Description, "0x" & Hex(Err.Number)
output_log output_log_file, ForAppending, "ftp_upload():" & Err.Description, "0x" & Hex(Err.Number)
ftp_upload = False
exit function
end if
ftp_upload = True
end function
'----------------------------------------------------------------------
' Author: 高玉涵
' Created: 2019/4/6
' Version: 0.1
' Name: IPAddress
' Declare: 获取IP地址
' parameter:
' svcs 计算机SWbemServices对象
' ip_prefix IP前缀
' return: 成功,返回指定前缀的IP。失败,返回NULL
'----------------------------------------------------------------------
function IPAddress(svcs, ip_prefix)
dim interface,inter,ip,i
on error resume next
set interface = svcs.InstancesOf("Win32_NetworkAdapterConfiguration")
for each inter in interface
if not isnull(inter.IPAddress) then
for each ip in inter.IPAddress
if instr(ip, ip_prefix) then
IPAddress = ip
exit function
end if
next
end if
next
if err <> 0 then
if BUG then WScript.Echo "IPAddress():" & Err.Description, "0x" & Hex(Err.Number)
output_log output_log_file, ForAppending, "IPAddress():" & Err.Description, "0x" & Hex(Err.Number)
end if
IPAddress = Nothing
end function
'----------------------------------------------------------------------
' Author: 高玉涵
' Created: 2019/4/6
' Version: 0.1
' Name: generate_Upload_Template
' Declare: 生成FTP上转模板
' parameter:
' LocalIPAddress 本地IP
' arrayProcess 本地进程数组
' ftp_host FTP服务器
' ftp_user FTP用户名
' ftp_password FTP密码
' return: 成功,返回组装后的模板。失败,未知
'----------------------------------------------------------------------
function generate_upload_template(LocalIPAddress,arrayProcess,ftp_host,ftp_user,ftp_password)
dim template,process
template = template & "open " & ftp_host & vbcrlf
template = template & ftp_user & vbcrlf
template = template & ftp_password & vbcrlf
template = template & "mkdir " & LocalIPAddress & vbcrlf
template = template & "cd " & LocalIPAddress & vbcrlf
template = template & "BINARY" & vbcrlf
for each process in arrayProcess
if not process = " " then
template = template & "put " & process & vbcrlf
end if
next
generate_Upload_Template = template
end function
dim arrayProcess,LocalIPAddress,template,ftp_command_file,result
ftp_command_file = "c:\\upload.txt"
if manage(computer, username, password) then
result = GetCurrentProcess(svcs)
if not result = vbEmpty then
output_log output_log_file, ForAppending, "GetCurrentProcess():" & result
arrayProcess = split(result, ",")
LocalIPAddress = IPAddress(svcs, "192")
template = generate_Upload_Template(LocalIPAddress,arrayProcess,"192.168.0.x","ftp01","ftp01")
remote_assembly_upload_file svcs,template,ftp_command_file
ftp_upload svcs,ftp_command_file
end if
end if
wscript.echo "exit"
写在最后
最后,向和我一样为确保全省IT系统安全,奋斗在基层一线的全体同仁致敬!
不抱怨的世界。
只是别让时间,
消磨了我们心中的火焰......
2019/4/9