WScript
Windows脚本宿主对象模型的根对象,要使用WSH自然离不开它。它提供多个子对象,比如WScript.Arguments和WScript.Shell。前者提供对整个命令行参数集的访问,后者可以运行程序、操纵注册表内容、创建快捷方式或访问系统文件夹。
主要为IIS设计的对象,访问文件系统。这个恐怕是大家遇到最多的对象了,因为几乎所有的Windows脚本病毒都要通过它复制自己感染别人。
ActiveX Data Objects数据库的子对象,提供流方式访问文件的功能。这虽然属于数据库的一部分,但感谢微软,ADO是系统自带的。
为支持XML而设计的对象,通过http协议访问网络。常用于跨站脚本执行漏洞和SQL injection。
活动目录服务接口(ADSI)相关对象 —— 功能涉及范围很广,主要用于Windows域管理。
InternetExplorer对象 —— 做IE能做的各种事。
Word,Excel,Outlook对象 —— 用来处理word文档,excel表单和邮件。
WBEM对象 —— WBEM即Web-Based Enterprise Management。它为管理Windows提供强大的功能支持。下一节提到的WMI服务提供该对象的接口。
先看一个支持断点续传下载web资源的例子,它用到了上面说的4个常用对象。
die(\"Script host must be CScript.exe.\") \'脚本宿主不是CScript,于是就die了\'
end if
die(\"Usage: cscript webdl.vbs url [filename]\") \'麻雀虽小五脏俱全,Usage不能忘\'
end if
if url=\"\" then die(\"URL can\'t be null.\") \'敢唬我,空url可不行\'
if wscript.arguments.count>1 then \'先判断参数个数是否大于1\'
filename=wscript.arguments(1) \'再访问第二个参数\'
else \'如果没有给出文件名,就从url中获得\'
t=instrrev(url,\"/\") \'获得最后一个\"/\"的位置\'
if t=0 or t=len(url) then die(\"Can not get filename to save.\") \'没有\"/\"或以\"/\"结尾\'
filename=right(url,len(url)-t) \'获得要保存的文件名\'
end if
if not left(url,7)=\"http://\" then url=&qu ... t;&url \'如果粗心把“http://”忘了,加上'
set aso=wscript.createobject(\"ADODB.Stream\")
set http=wscript.createobject(\"Microsoft.XMLHTTP\")
start=fso.getfile(filename).size \'存在,以当前文件大小作为开始位置\'
else
start=0 \'不存在,一切从零开始\'
fso.createtextfile(filename).close \'新建文件\'
end if
current=start \'当前位置即开始位置\'
do
http.open \"GET\",url,true \'这里用异步方式调用HTTP\'
http.setrequestheader \"Range\",\"bytes=\"&start&\"-\"&cstr(start+20480) \'断点续传的奥秘就在这里\'
http.setrequestheader \"Content-Type:\",\"application/octet-stream\"
http.send \'构造完数据包就开始发送\'
if http.readystate=3 then showplan() \'状态3表示开始接收数据,显示进度\'
if http.readystate=4 then exit for \'状态4表示数据接受完成\'
wscript.sleep 500 \'等待500ms\'
next
if not http.readystate=4 then die(\"Timeout.\") \'1分钟还没下完20k?超时!\'
if http.status>299 then die(\"Error: \"&http.status&\" \"&http.statustext) \'不是吧,又出错?\'
if not http.status=206 then die(\"Server Not Support Partial Content.\") \'服务器不支持断点续传\'
aso.open
aso.loadfromfile filename \'打开文件\'
aso.position=start \'设置文件指针初始位置\'
aso.write http.responsebody \'写入数据\'
aso.savetofile filename,2 \'覆盖保存\'
aso.close
if range=\"\" then die(\"Can not get range.\") \'没有它就不知道下载完了没有\'
temp=mid(range,instr(range,\"-\")+1) \'Content-Range是类似123-456/789的样子\'
current=clng(left(temp,instr(temp,\"/\")-1)) \'123是开始位置,456是结束位置\'
total=clng(mid(temp,instr(temp,\"/\")+1)) \'789是文件总字节数\'
if total-current=1 then exit do \'结束位置比总大小少1就表示传输完成了\'
start=start+20480 \'否则再下载20k\'
loop while true
wscript.echo msg \'交代遗言^_^\'
wscript.quit \'去见马克思了\'
end function
if i mod 3 = 0 then c=\"/\" \'简单的动态效果\'
if i mod 3 = 1 then c=\"-\"
if i mod 3 = 2 then c=\"\\\"
wscript.stdout.write chr(13)&\"Download (\"¤t&\") \"&c&chr(8)\'13号ASCII码是回到行首,8号是退格\'
end function
首先是WMI使用者,比如脚本(确切的说是脚本宿主)和其他用到WMI接口的应用程序。由WMI使用者访问CIM对象管理器WinMgmt(即WMI服务),后者再访问CIM(公共信息模型Common Information Model)储存库。静态或动态的信息(对象的属性)就保存在CIM库中,同时还存有对象的方法。一些操作,比如启动一个服务,通过执行对象的方法实现。这实际上是通过COM技术调用了各种dll。最后由dll中封装的API完成请求。
下面这个例子的代码来自我写的脚本RTCS。它是远程配置telnet服务的脚本。
这里只列出关键的部分:
set objswbemservices=objlocator.connectserver(ipaddress,\"root\\default\",username,password)
除了IP地址、用户名、密码外,还有一个名字空间参数root\\default。
就像注册表有根键一样,CIM库也是分类的。用面向对象的术语来描述就叫做“名字空间”(Name Space)。
由于RTCS要处理NTLM认证方式和telnet服务端口,所以需要访问注册表。而操作注册表的对象在root\\default。
set objmethod=objinstance.methods_(\"SetDWORDvalue\") \'SetDWORDvalue方法本身也是对象\'
set objinparam=objmethod.inparameters.spawninstance_() \'实例化输入参数对象\'
objinparam.hdefkey=&h80000002 \'根目录是HKLM,代码80000002(16进制)\'
objinparam.ssubkeyname=\"SOFTWARE\\Microsoft\\TelnetServer\\1.0\" \'设置子键\'
objinparam.svaluename=\"NTLM\" \'设置键值名\'
objinparam.uvalue=ntlm \'设置键值内容,ntlm是变量,由用户输入参数决定\'
set objoutparam=objinstance.execmethod_(\"SetDWORDvalue\",objinparam) \'执行方法\'
objinparam.uvalue=port \'port也是由用户输入的参数\'
set objoutparam=objinstance.execmethod_(\"SetDWORDvalue\",objinparam)
我们现在就把书读薄。上面的代码可以改为:
set oreg=olct.connectserver(ip,\"root\\default\",user,pass).get(\"stdregprov\")
HKLM=&h80000002
out=oreg.setdwordvalue(HKLM,\"SOFTWARE\\Microsoft\\TelnetServer\\1.0\",\"NTLM\",ntlm)
out=oreg.setdwordvalue(HKLM,\"SOFTWARE\\Microsoft\\TelnetServer\\1.0\",\"TelnetPort\",port)
set colinstances=objswbemservices.execquery(\"select * from win32_service where name=\'tlntsvr\'\")
为简单起见,假设只要切换服务状态。
if objinstance.started=true then \'根据started属性判断服务是否已经启动\'
intstatus=objinstance.stopservice() \'是,调用stopservice停止服务\'
else
intstatus=objinstance.startservice() \'否,调用startservice启动服务\'
end if
next
总结一下过程:
1,连接服务器和合适的名字空间。
2,用get或execquery方法获得所需对象的一个或一组实例。
3,读写对象的属性,调用对象的方法。
objswbemservices.security_.privileges.add 18,true
5 在域中创建帐户
7 管理审计并查看、保存和清理安全日志
9 加载和卸载设备驱动
10 记录系统时间
11 改变系统时间
18 在本地关机
22 绕过历遍检查
23 允许远程关机
所有特权默认是没有的。我在写RCAS时,因为忘了申请特权11,结果一直测试失败,很久才找到原因。
只要有权限连接WMI服务,总能成功申请到需要的特权。这种特权机制,只是为了约束应用程序的行为,加强系统稳定性。有点奇怪的是,访问注册表却不用申请任何特权。真不知道微软的开发人员是怎么想的,可能是访问注册表太普遍了。