谁希望成为脚本专家?

每周脚本测验题

*

脚本测验题答案:别担心:任何人,甚至您本人都不能获得这些数据

您知道人们将樱桃柄放进嘴里然后用舌头打个结的技巧吗?据说,这种技巧无法传授:您要么能将樱桃柄打个结,要么不能。我们的答案同这种事情大致相似:您要么知道答案,要么不知道。有人解出了此测验题是因为他们已经知道答案。而有人无法解出此题,是因为他们无法想出答案,尽管他们使出了浑身解数。为什么不能?对大多数人来说,看起来是因为他们还不知道答案。

您说对了:这是有些不公平。我们只希望解此测验题有困难的人们至少能够用他们的舌头将樱桃柄打成一个结。如果您不能做到这点,也不能解出这道测验题,我们只能说:唉,生活真的是很苦,对吧?

嘿,我们只是在开玩笑。不过是在提醒您,生活中有可能(确有可能)比解出脚本测验题和用舌头将樱桃柄打成个结更重要的事情。如果我们想出来会告诉您的。

如果您没有做过上周的测验,可以在这里试一下;或者您也可以继续阅读,让我们告诉您答案。切记:本周我们又给出了一道新测验题。请试一试,看看您的答案会是什么。

返回页首返回页首

2005 年 11 月 18 日答案

确实,这个问题有些棘手。如果您尝试访问“系统”或“应用程序”事件日志,此脚本实际上并没有什么错误。不过,好坏暂且不论,访问“安全”事件日志则有些不同:只要您试图访问“安全”事件日志,就需要在 WMI 别名中指定“安全”权限。换句话说,您的脚本看起来应该象这样:

On Error Resume Next

strComputer = "."
Set objWMIService = GetObject("winmgmts:{(Security)}\\" & strComputer & "\root\cimv2")

Set colEvents = objWMIService.ExecQuery _
        ("Select * From Win32_NTLogEvent Where LogFile = 'Security' AND EventCode = '529'")

For Each objEvent in colEvents
    Wscript.Echo "Category: " & objEvent.Category
    Wscript.Echo "Computer Name: " & objEvent.ComputerName
    Wscript.Echo "Event Code: " & objEvent.EventCode
    Wscript.Echo "Message: " & objEvent.Message
    Wscript.Echo "Record Number: " & objEvent.RecordNumber
    Wscript.Echo "Source Name: " & objEvent.SourceName
    Wscript.Echo "Time Written: " & objEvent.TimeWritten
    Wscript.Echo "Event Type: " & objEvent.Type
    Wscript.Echo "User: " & objEvent.User
    Wscript.Echo objEvent.LogFile
Next

正如您所见,我们在 GetObject 调用中嵌入了“安全”权限:GetObject("winmgmts:{(Security)}\\" & strComputer & "\root\cimv2")。我们知道您是本地“管理员”组的一名成员,这意味着您有权利读取“安全”事件日志。但是那也没有关系:如果您在进行该 WMI 服务调用时没有显式包含“安全”权限,则脚本既不会连接到“安全”事件日志也不会返回任何数据。仅此而已。

当然,一旦您添加了此权限,脚本就应完全按照指定方式运行:

Record Number: 399523
Source Name: Security
Time Written: 20051115223658.000000-480
Event Type: audit failure
User: NT AUTHORITY\SYSTEM
Security
Category: 2
Computer Name: atl-dc-01
Event Code: 529
Message: Logon Failure:

        Reason:         Unknown user name or bad password
        User Name:      Administrator
        Domain:         fabrikam
        Logon Type:     3
        Logon Process:  NtLmSsp
        Authentication Package: MICROSOFT_AUTHENTICATION_PACKAGE_V1_0
        Workstation Name:       red-ws-87

正如我们所说,此问题稍有些棘手,但还是用一种非常简单的办法解决了。

返回页首返回页首

我们应该如何知道这些?

嗯;我们也料定您不会接受“难住了我们”作为答案,是吧?这正是我们所担心的。

不妨坦白跟您讲,这个问题调试起来很费周折。首先,您不会收到错误消息;另外,也不是经常需要这些权限。因此,在脚本无法返回数据时,您不可能马上就在脑海里蹦出“咦,绝对是我忽略了一项权限”这个想法。

注意:但是这些权限(在需要它们的时候)难道不会在某处汇总并予以说明吗?不会的,别妄想了。对脚本专家来讲,这难道不是一件值得关注并且很有意义的事情吗?会是的,并且我们要将其添加到我们的任务列表中。我们只是无法承诺何时能做到这一点。

那么您应如何知道是否需要一项权限呢?您可以去查看 WMI SDK 中的正式类文档。例如,如果您查看 Win32_NTLogEvent class(英文)的文档,将会发现下面的提示:

An application must have SeSecurityPrivilege to receive events from the security event log, 
otherwise "Access Denied" is returned to the application.

注意:对,这不是一个完美的例子,因为实际上您不会收到“访问被拒绝”的错误消息。但至少您知道是需要权限的。

当然,并非每个 WMI 类在 SDK 中均有介绍,并且即使在已经介绍的类中,我们也不能保证会枚举出所有必需的权限。因此,另外一种方法就是运行下面的脚本,该脚本可以找出某类或类方法所需的任何权限:

On Error Resume Next

strComputer = "."
strNameSpace = "root\cimv2"
strClass = "Win32_NTLogEvent"
 
Set objClass = GetObject("winmgmts:\\" & strComputer & "\" _
    & strNameSpace & ":" & strClass)
 
For Each objClassQualifier In objClass.Qualifiers_
    If objClassQualifier.Name = "EnumPrivileges" Then
        strPrivileges = Join(objClassQualifier.Value, ",")
        Wscript.Echo "Class Privileges: " & strPrivileges
    End If
Next

For Each objClassMethod In objClass.Methods_
    For Each objMethodQualifier In objClassMethod.Qualifiers_
        If objMethodQualifier.Name = "Privileges" Then
             strPrivileges = Join(objMethodQualifier.Value, ",")
            Wscript.Echo objClassMethod.Name & ": " & strPrivileges
        End If
    Next
Next

如果您对 Win32_NTLogEvent 类运行此脚本,将会收到如下信息:

Class Privileges: SeSecurityPrivilege

这仍然不是标准答案:毕竟,此权限名称 (SeSecurityPrivilege) 同在 WMI 别名中所指定的权限名称(此例中为 Security)未必是一样的。但至少现在您可以搜索权限名称和 WMI 类的名称,运气好的话还可以找到教您如何实际去使用此权限的其他文档和示例脚本。


返回页首返回页首