powerview当中的-Get-NetGroupMember(Get-DomainGroupMember)

Get-DomainGroupMember

function Get-DomainGroupMember {
<#
.SYNOPSIS
Return the members of a specific domain group.
Author: Will Schroeder (@harmj0y)  
License: BSD 3-Clause  
Required Dependencies: Get-DomainSearcher, Get-DomainGroup, Get-DomainGroupMember, Convert-ADName, Get-DomainObject, ConvertFrom-SID  
.DESCRIPTION
Builds a directory searcher object using Get-DomainSearcher, builds a custom
LDAP filter based on targeting/filter parameters, and searches for the specified
group matching the criteria. Each result is then rebound and the full user
or group object is returned.
.PARAMETER Identity
A SamAccountName (e.g. Group1), DistinguishedName (e.g. CN=group1,CN=Users,DC=testlab,DC=local),
SID (e.g. S-1-5-21-890171859-3433809279-3366196753-1114), or GUID (e.g. 4c435dd7-dc58-4b14-9a5e-1fdb0e80d202)
specifying the group to query for. Wildcards accepted.
.PARAMETER Domain
Specifies the domain to use for the query, defaults to the current domain.
.PARAMETER Recurse
Switch. If the group member is a group, recursively try to query its members as well.
.PARAMETER RecurseUsingMatchingRule
Switch. Use LDAP_MATCHING_RULE_IN_CHAIN in the LDAP search query to recurse.
Much faster than manual recursion, but doesn't reveal cross-domain groups,
and only returns user accounts (no nested group objects themselves).
.PARAMETER LDAPFilter
Specifies an LDAP query string that is used to filter Active Directory objects.
.PARAMETER SearchBase
The LDAP source to search through, e.g. "LDAP://OU=secret,DC=testlab,DC=local"
Useful for OU queries.
.PARAMETER Server
Specifies an Active Directory server (domain controller) to bind to.
.PARAMETER SearchScope
Specifies the scope to search under, Base/OneLevel/Subtree (default of Subtree).
.PARAMETER ResultPageSize
Specifies the PageSize to set for the LDAP searcher object.
.PARAMETER ServerTimeLimit
Specifies the maximum amount of time the server spends searching. Default of 120 seconds.
.PARAMETER SecurityMasks
Specifies an option for examining security information of a directory object.
One of 'Dacl', 'Group', 'None', 'Owner', 'Sacl'.
.PARAMETER Tombstone
Switch. Specifies that the searcher should also return deleted/tombstoned objects.
.PARAMETER Credential
A [Management.Automation.PSCredential] object of alternate credentials
for connection to the target domain.
.EXAMPLE
Get-DomainGroupMember "Desktop Admins"
GroupDomain             : testlab.local
GroupName               : Desktop Admins
GroupDistinguishedName  : CN=Desktop Admins,CN=Users,DC=testlab,DC=local
MemberDomain            : testlab.local
MemberName              : Testing Group
MemberDistinguishedName : CN=Testing Group,CN=Users,DC=testlab,DC=local
MemberObjectClass       : group
MemberSID               : S-1-5-21-890171859-3433809279-3366196753-1129
GroupDomain             : testlab.local
GroupName               : Desktop Admins
GroupDistinguishedName  : CN=Desktop Admins,CN=Users,DC=testlab,DC=local
MemberDomain            : testlab.local
MemberName              : arobbins.a
MemberDistinguishedName : CN=Andy Robbins (admin),CN=Users,DC=testlab,DC=local
MemberObjectClass       : user
MemberSID               : S-1-5-21-890171859-3433809279-3366196753-1112
.EXAMPLE
'Desktop Admins' | Get-DomainGroupMember -Recurse
GroupDomain             : testlab.local
GroupName               : Desktop Admins
GroupDistinguishedName  : CN=Desktop Admins,CN=Users,DC=testlab,DC=local
MemberDomain            : testlab.local
MemberName              : Testing Group
MemberDistinguishedName : CN=Testing Group,CN=Users,DC=testlab,DC=local
MemberObjectClass       : group
MemberSID               : S-1-5-21-890171859-3433809279-3366196753-1129
GroupDomain             : testlab.local
GroupName               : Testing Group
GroupDistinguishedName  : CN=Testing Group,CN=Users,DC=testlab,DC=local
MemberDomain            : testlab.local
MemberName              : harmj0y
MemberDistinguishedName : CN=harmj0y,CN=Users,DC=testlab,DC=local
MemberObjectClass       : user
MemberSID               : S-1-5-21-890171859-3433809279-3366196753-1108
GroupDomain             : testlab.local
GroupName               : Desktop Admins
GroupDistinguishedName  : CN=Desktop Admins,CN=Users,DC=testlab,DC=local
MemberDomain            : testlab.local
MemberName              : arobbins.a
MemberDistinguishedName : CN=Andy Robbins (admin),CN=Users,DC=testlab,DC=local
MemberObjectClass       : user
MemberSID               : S-1-5-21-890171859-3433809279-3366196753-1112
.EXAMPLE
Get-DomainGroupMember -Domain testlab.local -Identity 'Desktop Admins' -RecurseUingMatchingRule
GroupDomain             : testlab.local
GroupName               : Desktop Admins
GroupDistinguishedName  : CN=Desktop Admins,CN=Users,DC=testlab,DC=local
MemberDomain            : testlab.local
MemberName              : harmj0y
MemberDistinguishedName : CN=harmj0y,CN=Users,DC=testlab,DC=local
MemberObjectClass       : user
MemberSID               : S-1-5-21-890171859-3433809279-3366196753-1108
GroupDomain             : testlab.local
GroupName               : Desktop Admins
GroupDistinguishedName  : CN=Desktop Admins,CN=Users,DC=testlab,DC=local
MemberDomain            : testlab.local
MemberName              : arobbins.a
MemberDistinguishedName : CN=Andy Robbins (admin),CN=Users,DC=testlab,DC=local
MemberObjectClass       : user
MemberSID               : S-1-5-21-890171859-3433809279-3366196753-1112
.EXAMPLE
Get-DomainGroup *admin* -Properties samaccountname | Get-DomainGroupMember
.EXAMPLE
'CN=Enterprise Admins,CN=Users,DC=testlab,DC=local', 'Domain Admins' | Get-DomainGroupMember
.EXAMPLE
$SecPassword = ConvertTo-SecureString 'Password123!' -AsPlainText -Force
$Cred = New-Object System.Management.Automation.PSCredential('TESTLAB\dfm.a', $SecPassword)
Get-DomainGroupMember -Credential $Cred -Identity 'Domain Admins'
.EXAMPLE
Get-Domain | Select-Object -Expand name
testlab.local
'dev\domain admins' | Get-DomainGroupMember -Verbose
VERBOSE: [Get-DomainSearcher] search string: LDAP://PRIMARY.testlab.local/DC=testlab,DC=local
VERBOSE: [Get-DomainGroupMember] Extracted domain 'dev.testlab.local' from 'dev\domain admins'
VERBOSE: [Get-DomainSearcher] search string: LDAP://PRIMARY.testlab.local/DC=dev,DC=testlab,DC=local
VERBOSE: [Get-DomainGroupMember] Get-DomainGroupMember filter string: (&(objectCategory=group)(|(samAccountName=domain admins)))
VERBOSE: [Get-DomainSearcher] search string: LDAP://PRIMARY.testlab.local/DC=dev,DC=testlab,DC=local
VERBOSE: [Get-DomainObject] Get-DomainObject filter string: (&(|(distinguishedname=CN=user1,CN=Users,DC=dev,DC=testlab,DC=local)))
GroupDomain             : dev.testlab.local
GroupName               : Domain Admins
GroupDistinguishedName  : CN=Domain Admins,CN=Users,DC=dev,DC=testlab,DC=local
MemberDomain            : dev.testlab.local
MemberName              : user1
MemberDistinguishedName : CN=user1,CN=Users,DC=dev,DC=testlab,DC=local
MemberObjectClass       : user
MemberSID               : S-1-5-21-339048670-1233568108-4141518690-201108
VERBOSE: [Get-DomainSearcher] search string: LDAP://PRIMARY.testlab.local/DC=dev,DC=testlab,DC=local
VERBOSE: [Get-DomainObject] Get-DomainObject filter string: (&(|(distinguishedname=CN=Administrator,CN=Users,DC=dev,DC=testlab,DC=local)))
GroupDomain             : dev.testlab.local
GroupName               : Domain Admins
GroupDistinguishedName  : CN=Domain Admins,CN=Users,DC=dev,DC=testlab,DC=local
MemberDomain            : dev.testlab.local
MemberName              : Administrator
MemberDistinguishedName : CN=Administrator,CN=Users,DC=dev,DC=testlab,DC=local
MemberObjectClass       : user
MemberSID               : S-1-5-21-339048670-1233568108-4141518690-500
.OUTPUTS
PowerView.GroupMember
Custom PSObject with translated group member property fields.
.LINK
http://www.powershellmagazine.com/2013/05/23/pstip-retrieve-group-membership-of-an-active-directory-group-recursively/
#>

    #初始化 SuppressMessageAttribute 类的新实例,同时指定静态分析工具的类别和分析规则的标识符  
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '')]
    #OutputType,这个属性的主要作用就是用来描述方法所返回的对象类型
    [OutputType('PowerView.GroupMember')]
    [CmdletBinding(DefaultParameterSetName = 'None')]
    Param(
        [Parameter(Position = 0, Mandatory = $True, ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True)]
        [Alias('DistinguishedName', 'SamAccountName', 'Name', 'MemberDistinguishedName', 'MemberName')]
        [String[]]
        $Identity,
        #ValidateNotNullOrEmpty()验证仅检查提供的参数以验证其不为null或为空。如果根本不提供该参数,则无需检查。请注意,这不同于null或为空
        [ValidateNotNullOrEmpty()]
        [String]
        $Domain,


        [Parameter(ParameterSetName = 'ManualRecurse')]
        [Switch]
        $Recurse,


        [Parameter(ParameterSetName = 'RecurseUsingMatchingRule')]
        [Switch]
        $RecurseUsingMatchingRule,


        [ValidateNotNullOrEmpty()]
        [Alias('Filter')]
        [String]
        $LDAPFilter,


        [ValidateNotNullOrEmpty()]
        [Alias('ADSPath')]
        [String]
        $SearchBase,


        [ValidateNotNullOrEmpty()]
        [Alias('DomainController')]
        [String]
        $Server,

        #用户输入的参数要在我们的集合当中
        [ValidateSet('Base', 'OneLevel', 'Subtree')]
        [String]
        $SearchScope = 'Subtree',

        #限制范围 
        [ValidateRange(1, 10000)]
        [Int]
        $ResultPageSize = 200,


        [ValidateRange(1, 10000)]
        [Int]
        $ServerTimeLimit,

        [ValidateSet('Dacl', 'Group', 'None', 'Owner', 'Sacl')]
        [String]
        $SecurityMasks,

        #[switch]不使用-DoSomething参数,则-DoSomething的变量自动设为$False。 如果我运行的脚本带-DoSomething参数,$ DoSomething则被设置为$ True。 没有必要传递值给-DoSomething参数。Windows PowerShell中,如果你使用它,则将其设置为$ True,。 这是switch参数运行的方法,像Get-ChildItem的-Recurse参数。https://www.jb51.net/article/53050.htm
        [Switch]
        $Tombstone,

        [Management.Automation.PSCredential]
        [Management.Automation.CredentialAttribute()]
        $Credential = [Management.Automation.PSCredential]::Empty
    )

    BEGIN {
        $SearcherArguments = @{
            'Properties' = 'member,samaccountname,distinguishedname'
        }
        #有一个全局变量$PSBoundParameters,它是一个Hash表,里面保存了所有的输入参数和输入参数的值
        if ($PSBoundParameters['Domain']) { $SearcherArguments['Domain'] = $Domain }
        if ($PSBoundParameters['LDAPFilter']) { $SearcherArguments['LDAPFilter'] = $LDAPFilter }
        if ($PSBoundParameters['SearchBase']) { $SearcherArguments['SearchBase'] = $SearchBase }
        if ($PSBoundParameters['Server']) { $SearcherArguments['Server'] = $Server }
        if ($PSBoundParameters['SearchScope']) { $SearcherArguments['SearchScope'] = $SearchScope }
        if ($PSBoundParameters['ResultPageSize']) { $SearcherArguments['ResultPageSize'] = $ResultPageSize }
        if ($PSBoundParameters['ServerTimeLimit']) { $SearcherArguments['ServerTimeLimit'] = $ServerTimeLimit }
        if ($PSBoundParameters['Tombstone']) { $SearcherArguments['Tombstone'] = $Tombstone }
        if ($PSBoundParameters['Credential']) { $SearcherArguments['Credential'] = $Credential }

        $ADNameArguments = @{}
        if ($PSBoundParameters['Domain']) { $ADNameArguments['Domain'] = $Domain }
        if ($PSBoundParameters['Server']) { $ADNameArguments['Server'] = $Server }
        if ($PSBoundParameters['Credential']) { $ADNameArguments['Credential'] = $Credential }
    }

    PROCESS {
        $GroupSearcher = Get-DomainSearcher @SearcherArguments
        if ($GroupSearcher) {
            if ($PSBoundParameters['RecurseUsingMatchingRule']) {
                $SearcherArguments['Identity'] = $Identity
                $SearcherArguments['Raw'] = $True
                #调用Get-DomainGroup函数,其中Get-DomainOBject也就是Get-ADObject的别名,获取一个或多个Active Directory对象。
                $Group = Get-DomainGroup @SearcherArguments
                if (-not $Group) {
                    Write-Warning "[Get-DomainGroupMember] Error searching for group with identity: $Identity"
                }
                else {
                    $GroupFoundName = $Group.properties.item('samaccountname')[0]
                    $GroupFoundDN = $Group.properties.item('distinguishedname')[0]

                    if ($PSBoundParameters['Domain']) {
                        $GroupFoundDomain = $Domain
                    }
                    else {
                        # if a domain isn't passed, try to extract it from the found group distinguished name
                        if ($GroupFoundDN) {
                            $GroupFoundDomain = $GroupFoundDN.SubString($GroupFoundDN.IndexOf('DC=')) -replace 'DC=','' -replace ',','.'
                        }
                    }
                    Write-Verbose "[Get-DomainGroupMember] Using LDAP matching rule to recurse on '$GroupFoundDN', only user accounts will be returned."
                    #(sAMAccountType=805306368) All user objects
                    # All members of specified group, including 
due to group nesting--(memberOf:1.2.840.113556.1.4.1941:= 
cn=Test,ou=East,dc=Domain,dc=com)
                    $GroupSearcher.filter = "(&(samAccountType=805306368)(memberof:1.2.840.113556.1.4.1941:=$GroupFoundDN))"
                    $GroupSearcher.PropertiesToLoad.AddRange(('distinguishedName'))
                    $Members = $GroupSearcher.FindAll() | ForEach-Object {$_.Properties.distinguishedname[0]}
                }
                $Null = $SearcherArguments.Remove('Raw')
            }
            else {
                $IdentityFilter = ''
                $Filter = ''
                $Identity | Where-Object {$_} | ForEach-Object {
                    $IdentityInstance = $_
                    if ($IdentityInstance -match '.+\\.+') {
                        # DOMAIN\groupname
                        $ConvertedIdentityInstance = $IdentityInstance | Convert-ADName -OutputType Canonical
                        if ($ConvertedIdentityInstance) {
                            $GroupDomain = $ConvertedIdentityInstance.SubString(0, $ConvertedIdentityInstance.IndexOf('/'))
                            $GroupName = $IdentityInstance.Split('\')[1]
                            $IdentityFilter += "(samAccountName=$GroupName)"
                            $SearcherArguments['Domain'] = $GroupDomain
                            Write-Verbose "[Get-DomainGroupMember] Extracted domain '$GroupDomain' from '$IdentityInstance'"
                            $GroupSearcher = Get-DomainSearcher @SearcherArguments
                        }
                    }
                    else {
                        $IdentityInstance = $IdentityInstance.Replace('(', '\28').Replace(')', '\29')
                        if ($IdentityInstance -match '^S-1-.*') {
                            $IdentityFilter += "(objectsid=$IdentityInstance)"
                        }
                        elseif ($IdentityInstance -match '^CN=.*') {
                            $IdentityFilter += "(distinguishedname=$IdentityInstance)"
                        }
                        else {
                            try {
                                $GuidByteString = (-Join (([Guid]$IdentityInstance).ToByteArray() | ForEach-Object {$_.ToString('X').PadLeft(2,'0')})) -Replace '(..)','\$1'
                                $IdentityFilter += "(objectguid=$GuidByteString)"
                            }
                            catch {
                                $IdentityFilter += "(samAccountName=$IdentityInstance)"
                            }
                        }
                    }
                }
                if ($IdentityFilter -and ($IdentityFilter.Trim() -ne '') ) {
                    $Filter += "(|$IdentityFilter)"
                }


                if ($PSBoundParameters['LDAPFilter']) {
                    Write-Verbose "[Get-DomainGroupMember] Using additional LDAP filter: $LDAPFilter"
                    $Filter += "$LDAPFilter"
                }


                $GroupSearcher.filter = "(&(objectCategory=group)$Filter)"
                Write-Verbose "[Get-DomainGroupMember] Get-DomainGroupMember filter string: $($GroupSearcher.filter)"
                try {
                    $Result = $GroupSearcher.FindOne()
                }
                catch {
                    Write-Warning "[Get-DomainGroupMember] Error searching for group with identity '$Identity': $_"
                    $Members = @()
                }


                $GroupFoundName = ''
                $GroupFoundDN = ''


                if ($Result) {
                    $Members = $Result.properties.item('member')

                    if ($Members.count -eq 0) {
                        # ranged searching, thanks @meatballs__ !
                        $Finished = $False
                        $Bottom = 0
                        $Top = 0

                        while (-not $Finished) {
                            $Top = $Bottom + 1499
                            $MemberRange="member;range=$Bottom-$Top"
                            $Bottom += 1500
                            #PropertiesToLoad--获取一个值,该值指示在搜索过程中要检索的属性列表
                            #要检索特定属性,请在开始搜索之前将它们添加到此集合中。例如,searcher.PropertiesToLoad.Add("phone");将电话属性添加到要在搜索中检索的属性列表。
                            $Null = $GroupSearcher.PropertiesToLoad.Clear()
                            $Null = $GroupSearcher.PropertiesToLoad.Add("$MemberRange")
                            $Null = $GroupSearcher.PropertiesToLoad.Add('samaccountname')
                            $Null = $GroupSearcher.PropertiesToLoad.Add('distinguishedname')

                            try {
                                #执行搜索并仅返回找到的第一个条目。
                                $Result = $GroupSearcher.FindOne()
                                #Properties.PropertyNames此属性列表中所有键的枚举,包括默认属性列表中的键 
                                #搜索查询功能将请求member; range = 1000- *值,该值将返回member; range = 1000- *属性(不带值)和member; range = 1000-1999属性(后1000个值) 
                                $RangedProperty = $Result.Properties.PropertyNames -like "member;range=*"
                                $Members += $Result.Properties.item($RangedProperty)
                                $GroupFoundName = $Result.properties.item('samaccountname')[0]
                                $GroupFoundDN = $Result.properties.item('distinguishedname')[0]

                                if ($Members.count -eq 0) {
                                    $Finished = $True
                                }
                            }
                            catch [System.Management.Automation.MethodInvocationException] {
                                $Finished = $True
                            }
                        }
                    }
                    else {
                        $GroupFoundName = $Result.properties.item('samaccountname')[0]
                        $GroupFoundDN = $Result.properties.item('distinguishedname')[0]
                        $Members += $Result.Properties.item($RangedProperty)
                    }


                    if ($PSBoundParameters['Domain']) {
                        $GroupFoundDomain = $Domain
                    }
                    else {
                        # if a domain isn't passed, try to extract it from the found group distinguished name 如果没有传递某个域,请尝试从发现的组专有名称中提取它
                        if ($GroupFoundDN) {
                            $GroupFoundDomain = $GroupFoundDN.SubString($GroupFoundDN.IndexOf('DC=')) -replace 'DC=','' -replace ',','.'
                        }
                    }
                }
            }


            ForEach ($Member in $Members) {
                if ($Recurse -and $UseMatchingRule) {
                    $Properties = $_.Properties
                }
                else {
                    $ObjectSearcherArguments = $SearcherArguments.Clone()
                    $ObjectSearcherArguments['Identity'] = $Member
                    $ObjectSearcherArguments['Raw'] = $True
                    $ObjectSearcherArguments['Properties'] = 'distinguishedname,cn,samaccountname,objectsid,objectclass'
                    $Object = Get-DomainObject @ObjectSearcherArguments
                    $Properties = $Object.Properties
                }

                if ($Properties) {
                    $GroupMember = New-Object PSObject
                    $GroupMember | Add-Member Noteproperty 'GroupDomain' $GroupFoundDomain
                    $GroupMember | Add-Member Noteproperty 'GroupName' $GroupFoundName
                    $GroupMember | Add-Member Noteproperty 'GroupDistinguishedName' $GroupFoundDN

                    if ($Properties.objectsid) {
                        $MemberSID = ((New-Object System.Security.Principal.SecurityIdentifier $Properties.objectsid[0], 0).Value)
                    }
                    else {
                        $MemberSID = $Null
                    }


                    try {
                        $MemberDN = $Properties.distinguishedname[0]
                        if ($MemberDN -match 'ForeignSecurityPrincipals|S-1-5-21') {
                            try {
                                if (-not $MemberSID) {
                                    $MemberSID = $Properties.cn[0]
                                }
                                $MemberSimpleName = Convert-ADName -Identity $MemberSID -OutputType 'DomainSimple' @ADNameArguments

                                if ($MemberSimpleName) {
                                    $MemberDomain = $MemberSimpleName.Split('@')[1]
                                }
                                else {
                                    Write-Warning "[Get-DomainGroupMember] Error converting $MemberDN"
                                    $MemberDomain = $Null
                                }
                            }
                            catch {
                                Write-Warning "[Get-DomainGroupMember] Error converting $MemberDN"
                                $MemberDomain = $Null
                            }
                        }
                        else {
                            # extract the FQDN from the Distinguished Name
                            $MemberDomain = $MemberDN.SubString($MemberDN.IndexOf('DC=')) -replace 'DC=','' -replace ',','.'
                        }
                    }
                    catch {
                        $MemberDN = $Null
                        $MemberDomain = $Null
                    }


                    if ($Properties.samaccountname) {
                        # forest users have the samAccountName set
                        $MemberName = $Properties.samaccountname[0]
                    }
                    else {
                        # external trust users have a SID, so convert it
                        try {
                            $MemberName = ConvertFrom-SID -ObjectSID $Properties.cn[0] @ADNameArguments
                        }
                        catch {
                            # if there's a problem contacting the domain to resolve the SID
                            $MemberName = $Properties.cn[0]
                        }
                    }

                    if ($Properties.objectclass -match 'computer') {
                        $MemberObjectClass = 'computer'
                    }
                    elseif ($Properties.objectclass -match 'group') {
                        $MemberObjectClass = 'group'
                    }
                    elseif ($Properties.objectclass -match 'user') {
                        $MemberObjectClass = 'user'
                    }
                    else {
                        $MemberObjectClass = $Null
                    }
                    $GroupMember | Add-Member Noteproperty 'MemberDomain' $MemberDomain
                    $GroupMember | Add-Member Noteproperty 'MemberName' $MemberName
                    $GroupMember | Add-Member Noteproperty 'MemberDistinguishedName' $MemberDN
                    $GroupMember | Add-Member Noteproperty 'MemberObjectClass' $MemberObjectClass
                    $GroupMember | Add-Member Noteproperty 'MemberSID' $MemberSID
                    $GroupMember.PSObject.TypeNames.Insert(0, 'PowerView.GroupMember')
                    $GroupMember

                    # if we're doing manual recursion
                    if ($PSBoundParameters['Recurse'] -and $MemberDN -and ($MemberObjectClass -match 'group')) {
                        Write-Verbose "[Get-DomainGroupMember] Manually recursing on group: $MemberDN"
                        $SearcherArguments['Identity'] = $MemberDN
                        $Null = $SearcherArguments.Remove('Properties')
                        Get-DomainGroupMember @SearcherArguments
                    }
                }
            }
            $GroupSearcher.dispose()
        }
    }
}

在这里插入图片描述

对于组的检索成员-代码实现

以下代码示例显示如何使用范围检索获取组的成员。此示例检索 0-500(包含 0 和 500)之间的项。此结果集的最大项数为 501。

DirectoryEntry group = new DirectoryEntry("LDAP://CN=Sales,DC=Fabrikam,DC=COM");
DirectorySearcher groupMember = new DirectorySearcher
    (group,"(objectClass=*)",new string[]{"member;Range=0-500"},SearchScope.Base);
SearchResult result = groupMember.FindOne();
// Each entry contains a property name and the path (ADsPath).
// The following code returns the property name from the PropertyCollection. 
//每个条目包含一个属性名和路径(ADsPath)。下面的代码从PropertyCollection返回属性名
String propName=String.Empty;
foreach(string s in result.Properties.PropertyNames)
{
    if ( s.ToLower() != "adspath")
    {
      propName = s;
      break;
    }
}
foreach(string member in result.Properties[propName])
{
     Console.WriteLine(member);
}

还可以通过在结果集内的指定点开始和结束,使用范围检索来检索一部分的结果集。为此,需修改 {“member;Range=0-500”} 语句。例如,要检索结果集中的第三项和第四项,您应该使用语句 {“member;Range=2-3”}。要检索从 502 开始到结果集末尾的所有项,请使用语句 {“member;Range=501-*”}。

对于Get-DomainGroup方法,用法:Get-DomainGroup -Properties samaccountname -Identity ‘S-1-5-21-890171859-3433809279-3366196753-1117’ | fl

function Get-DomainGroup {
<#
.SYNOPSIS


Return all groups or specific group objects in AD.


Author: Will Schroeder (@harmj0y)  
License: BSD 3-Clause  
Required Dependencies: Get-DomainSearcher, Get-DomainObject, Convert-ADName, Convert-LDAPProperty  


.DESCRIPTION


Builds a directory searcher object using Get-DomainSearcher, builds a custom
LDAP filter based on targeting/filter parameters, and searches for all objects
matching the criteria. To only return specific properties, use
"-Properties samaccountname,usnchanged,...". By default, all group objects for
the current domain are returned. To return the groups a specific user/group is
a part of, use -MemberIdentity X to execute token groups enumeration.


.PARAMETER Identity


A SamAccountName (e.g. Group1), DistinguishedName (e.g. CN=group1,CN=Users,DC=testlab,DC=local),
SID (e.g. S-1-5-21-890171859-3433809279-3366196753-1114), or GUID (e.g. 4c435dd7-dc58-4b14-9a5e-1fdb0e80d202)
specifying the group to query for. Wildcards accepted.


.PARAMETER MemberIdentity


A SamAccountName (e.g. Group1), DistinguishedName (e.g. CN=group1,CN=Users,DC=testlab,DC=local),
SID (e.g. S-1-5-21-890171859-3433809279-3366196753-1114), or GUID (e.g. 4c435dd7-dc58-4b14-9a5e-1fdb0e80d202)
specifying the user/group member to query for group membership.


.PARAMETER AdminCount


Switch. Return users with '(adminCount=1)' (meaning are/were privileged).


.PARAMETER Domain


Specifies the domain to use for the query, defaults to the current domain.


.PARAMETER LDAPFilter


Specifies an LDAP query string that is used to filter Active Directory objects.


.PARAMETER Properties


Specifies the properties of the output object to retrieve from the server.


.PARAMETER SearchBase


The LDAP source to search through, e.g. "LDAP://OU=secret,DC=testlab,DC=local"
Useful for OU queries.


.PARAMETER Server


Specifies an Active Directory server (domain controller) to bind to.


.PARAMETER SearchScope


Specifies the scope to search under, Base/OneLevel/Subtree (default of Subtree).


.PARAMETER ResultPageSize


Specifies the PageSize to set for the LDAP searcher object.


.PARAMETER ServerTimeLimit


Specifies the maximum amount of time the server spends searching. Default of 120 seconds.


.PARAMETER SecurityMasks


Specifies an option for examining security information of a directory object.
One of 'Dacl', 'Group', 'None', 'Owner', 'Sacl'.


.PARAMETER Tombstone


Switch. Specifies that the searcher should also return deleted/tombstoned objects.


.PARAMETER FindOne


Only return one result object.


.PARAMETER Credential


A [Management.Automation.PSCredential] object of alternate credentials
for connection to the target domain.


.PARAMETER Raw


Switch. Return raw results instead of translating the fields into a custom PSObject.


.EXAMPLE


Get-DomainGroup | select samaccountname


samaccountname
--------------
WinRMRemoteWMIUsers__
Administrators
Users
Guests
Print Operators
Backup Operators
...


.EXAMPLE


Get-DomainGroup *admin* | select distinguishedname


distinguishedname
-----------------
CN=Administrators,CN=Builtin,DC=testlab,DC=local
CN=Hyper-V Administrators,CN=Builtin,DC=testlab,DC=local
CN=Schema Admins,CN=Users,DC=testlab,DC=local
CN=Enterprise Admins,CN=Users,DC=testlab,DC=local
CN=Domain Admins,CN=Users,DC=testlab,DC=local
CN=DnsAdmins,CN=Users,DC=testlab,DC=local
CN=Server Admins,CN=Users,DC=testlab,DC=local
CN=Desktop Admins,CN=Users,DC=testlab,DC=local


.EXAMPLE


Get-DomainGroup -Properties samaccountname -Identity 'S-1-5-21-890171859-3433809279-3366196753-1117' | fl


samaccountname
--------------
Server Admins


.EXAMPLE


'CN=Desktop Admins,CN=Users,DC=testlab,DC=local' | Get-DomainGroup -Server primary.testlab.local -Verbose
VERBOSE: Get-DomainSearcher search string: LDAP://DC=testlab,DC=local
VERBOSE: Get-DomainGroup filter string: (&(objectCategory=group)(|(distinguishedname=CN=DesktopAdmins,CN=Users,DC=testlab,DC=local)))


usncreated            : 13245
grouptype             : -2147483646
samaccounttype        : 268435456
samaccountname        : Desktop Admins
whenchanged           : 8/10/2016 12:30:30 AM
objectsid             : S-1-5-21-890171859-3433809279-3366196753-1118
objectclass           : {top, group}
cn                    : Desktop Admins
usnchanged            : 13255
dscorepropagationdata : 1/1/1601 12:00:00 AM
name                  : Desktop Admins
distinguishedname     : CN=Desktop Admins,CN=Users,DC=testlab,DC=local
member                : CN=Andy Robbins (admin),CN=Users,DC=testlab,DC=local
whencreated           : 8/10/2016 12:29:43 AM
instancetype          : 4
objectguid            : f37903ed-b333-49f4-abaa-46c65e9cca71
objectcategory        : CN=Group,CN=Schema,CN=Configuration,DC=testlab,DC=local


.EXAMPLE


$SecPassword = ConvertTo-SecureString 'Password123!' -AsPlainText -Force
$Cred = New-Object System.Management.Automation.PSCredential('TESTLAB\dfm.a', $SecPassword)
Get-DomainGroup -Credential $Cred


.EXAMPLE


Get-Domain | Select-Object -Expand name
testlab.local


'DEV\Domain Admins' | Get-DomainGroup -Verbose -Properties distinguishedname
VERBOSE: [Get-DomainSearcher] search string: LDAP://PRIMARY.testlab.local/DC=testlab,DC=local
VERBOSE: [Get-DomainGroup] Extracted domain 'dev.testlab.local' from 'DEV\Domain Admins'
VERBOSE: [Get-DomainSearcher] search string: LDAP://PRIMARY.testlab.local/DC=dev,DC=testlab,DC=local
VERBOSE: [Get-DomainGroup] filter string: (&(objectCategory=group)(|(samAccountName=Domain Admins)))


distinguishedname
-----------------
CN=Domain Admins,CN=Users,DC=dev,DC=testlab,DC=local


.OUTPUTS


PowerView.Group


Custom PSObject with translated group property fields.
#>


    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '')]
    [OutputType('PowerView.Group')]
    [CmdletBinding(DefaultParameterSetName = 'AllowDelegation')]
    Param(
        [Parameter(Position = 0, ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True)]
        [Alias('DistinguishedName', 'SamAccountName', 'Name', 'MemberDistinguishedName', 'MemberName')]
        [String[]]
        $Identity,


        [ValidateNotNullOrEmpty()]
        [Alias('UserName')]
        [String]
        $MemberIdentity,


        [Switch]
        $AdminCount,


        [ValidateNotNullOrEmpty()]
        [String]
        $Domain,


        [ValidateNotNullOrEmpty()]
        [Alias('Filter')]
        [String]
        $LDAPFilter,


        [ValidateNotNullOrEmpty()]
        [String[]]
        $Properties,


        [ValidateNotNullOrEmpty()]
        [Alias('ADSPath')]
        [String]
        $SearchBase,


        [ValidateNotNullOrEmpty()]
        [Alias('DomainController')]
        [String]
        $Server,


        [ValidateSet('Base', 'OneLevel', 'Subtree')]
        [String]
        $SearchScope = 'Subtree',


        [ValidateRange(1, 10000)]
        [Int]
        $ResultPageSize = 200,


        [ValidateRange(1, 10000)]
        [Int]
        $ServerTimeLimit,


        [ValidateSet('Dacl', 'Group', 'None', 'Owner', 'Sacl')]
        [String]
        $SecurityMasks,


        [Switch]
        $Tombstone,


        [Alias('ReturnOne')]
        [Switch]
        $FindOne,


        [Management.Automation.PSCredential]
        [Management.Automation.CredentialAttribute()]
        $Credential = [Management.Automation.PSCredential]::Empty,


        [Switch]
        $Raw
    )


    BEGIN {
        $SearcherArguments = @{}
        if ($PSBoundParameters['Domain']) { $SearcherArguments['Domain'] = $Domain }
        if ($PSBoundParameters['Properties']) { $SearcherArguments['Properties'] = $Properties }
        if ($PSBoundParameters['SearchBase']) { $SearcherArguments['SearchBase'] = $SearchBase }
        if ($PSBoundParameters['Server']) { $SearcherArguments['Server'] = $Server }
        if ($PSBoundParameters['SearchScope']) { $SearcherArguments['SearchScope'] = $SearchScope }
        if ($PSBoundParameters['ResultPageSize']) { $SearcherArguments['ResultPageSize'] = $ResultPageSize }
        if ($PSBoundParameters['ServerTimeLimit']) { $SearcherArguments['ServerTimeLimit'] = $ServerTimeLimit }
        if ($PSBoundParameters['SecurityMasks']) { $SearcherArguments['SecurityMasks'] = $SecurityMasks }
        if ($PSBoundParameters['Tombstone']) { $SearcherArguments['Tombstone'] = $Tombstone }
        if ($PSBoundParameters['Credential']) { $SearcherArguments['Credential'] = $Credential }
        #先创建DomainSearcher
        $GroupSearcher = Get-DomainSearcher @SearcherArguments
    }


    PROCESS {
        #如果DomainSearcher存在
        if ($GroupSearcher) {
            if ($PSBoundParameters['MemberIdentity']) {


                if ($SearcherArguments['Properties']) {
                    $OldProperties = $SearcherArguments['Properties']
                }
                #Identity参数指定要获取的Active Directory对象  
                #通过提供下列属性值之一来指定活动目录对象。括号中的标识符是属性的LDAP显示名称。
                #例如C:\PS>Get-ADObject -Identity "DC=AppNC" -server "FABRIKAM-SRV1:60000" 其中的DC=AppNC指定的其实
                #就是DistinguishedName  
                $SearcherArguments['Identity'] = $MemberIdentity
                $SearcherArguments['Raw'] = $True
                
                #此处返回搜索的结果,也就是SearchResult类
                Get-DomainObject @SearcherArguments | ForEach-Object {
                    # convert the user/group to a directory entry
                    # 检索的DirectoryEntry对应于信息搜索结果从Active Directory域服务层次。
                    $ObjectDirectoryEntry = $_.GetDirectoryEntry()


                    # cause the cache to calculate the token groups for the user/group
                    #导致缓存计算用户/组的令牌组
                    #将指定属性的值加载到属性缓存中
                    $ObjectDirectoryEntry.RefreshCache('tokenGroups')


                    $ObjectDirectoryEntry.TokenGroups | ForEach-Object {
                        # convert the token group sid
                        #使用安全标识符 (SID) 的指定二进制表示形式初始化 SecurityIdentifier 类的新实例
                        #public SecurityIdentifier (byte[] binaryForm, int offset);
                        #参数1-binaryForm-Byte[]-表示 SID 的字节数组
                        #参数2-offset-Int32-要用作 binaryForm 中的起始索引的字节偏移量
                        $GroupSid = (New-Object System.Security.Principal.SecurityIdentifier($_,0)).Value


                        # ignore the built in groups
                        # https://blog.csdn.net/wqiancangq/article/details/100080445
                        if ($GroupSid -notmatch '^S-1-5-32-.*') {
                            $SearcherArguments['Identity'] = $GroupSid
                            $SearcherArguments['Raw'] = $False
                            if ($OldProperties) { $SearcherArguments['Properties'] = $OldProperties }
                            $Group = Get-DomainObject @SearcherArguments
                            if ($Group) {
                                #public void Insert (int index, T item);添加到我们的定义的属性当中,给对象的自定义类型的名称
                                $Group.PSObject.TypeNames.Insert(0, 'PowerView.Group')
                                $Group
                            }
                        }
                    }
                }
            }
            #如果不存在DomainSearcher,我们就重新配置
            else {
                $IdentityFilter = ''
                $Filter = ''
                $Identity | Where-Object {$_} | ForEach-Object {
                    $IdentityInstance = $_


                    if ($IdentityInstance -match '.+\\.+') {
                        # DOMAIN\groupname
                        $ConvertedIdentityInstance = $IdentityInstance | Convert-ADName -OutputType Canonical
                        if ($ConvertedIdentityInstance) {
                            $GroupDomain = $ConvertedIdentityInstance.SubString(0, $ConvertedIdentityInstance.IndexOf('/'))
                            $GroupName = $IdentityInstance.Split('\')[1]
                            $IdentityFilter += "(samAccountName=$GroupName)"
                            $SearcherArguments['Domain'] = $GroupDomain
                            Write-Verbose "[Get-DomainGroup] Extracted domain '$GroupDomain' from '$IdentityInstance'"
                            $GroupSearcher = Get-DomainSearcher @SearcherArguments
                        }
                    }
                    else {
                        $IdentityInstance = $IdentityInstance.Replace('(', '\28').Replace(')', '\29')
                        #匹配到是以S-1开头的,那么就去设置search string为S-1-开头
                        if ($IdentityInstance -match '^S-1-.*') {
                            $IdentityFilter += "(objectsid=$IdentityInstance)"
                        }
                        #如果是以CN开头的那么就去设置distinguishedname
                        elseif ($IdentityInstance -match '^CN=.*') {
                            $IdentityFilter += "(distinguishedname=$IdentityInstance)"
                        }
                        else {
                            try {
                                 #objectguid的配置
                                 #在C#当中
                                 #ToString("X2") 为C#中的字符串格式控制符
                                 #X为     十六进制
                                 #2为     每次都是两位数
                                 #比如   0x0A ,若没有2,就只会输出0xA  
                                 #PadLeft返回一个新字符串,该字符串通过在此实例中的字符左侧填充指定的 Unicode 字符来达到指定的总长度,从而使这些字符右对齐。   
                                $GuidByteString = (-Join (([Guid]$IdentityInstance).ToByteArray() | ForEach-Object {$_.ToString('X').PadLeft(2,'0')})) -Replace '(..)','\$1'
                                $IdentityFilter += "(objectguid=$GuidByteString)"
                            }
                            catch {
                                $IdentityFilter += "(|(samAccountName=$IdentityInstance)(name=$IdentityInstance))"
                            }
                        }
                    }
                }
                #然后下面就是去设置 $Filter
                if ($IdentityFilter -and ($IdentityFilter.Trim() -ne '') ) {
                    $Filter += "(|$IdentityFilter)"
                }


                if ($PSBoundParameters['AdminCount']) {
                    Write-Verbose '[Get-DomainGroup] Searching for adminCount=1'
                    $Filter += '(admincount=1)'
                }
                if ($PSBoundParameters['LDAPFilter']) {
                    Write-Verbose "[Get-DomainGroup] Using additional LDAP filter: $LDAPFilter"
                    $Filter += "$LDAPFilter"
                }

                $GroupSearcher.filter = "(&(objectCategory=group)$Filter)"
                Write-Verbose "[Get-DomainGroup] filter string: $($GroupSearcher.filter)"

                if ($PSBoundParameters['FindOne']) { $Results = $GroupSearcher.FindOne() }
                else { $Results = $GroupSearcher.FindAll() }
                $Results | Where-Object {$_} | ForEach-Object {
                    if ($PSBoundParameters['Raw']) {
                        # return raw result objects
                        $Group = $_
                    }
                    else {
                        $Group = Convert-LDAPProperty -Properties $_.Properties
                    }
                    $Group.PSObject.TypeNames.Insert(0, 'PowerView.Group')
                    $Group
                }
                if ($Results) {
                    try { $Results.dispose() }
                    catch {
                        Write-Verbose "[Get-DomainGroup] Error disposing of the Results object"
                    }
                }
                $GroupSearcher.dispose()
            }
        }
    }
}

下面是关于上面的一些用法的总结

关于Get-ADObject的用法

在这里插入图片描述

对于DefaultParameterSetName的设置其实也就是我们的参数集的设置

Function Get-Somthing  
{  
    [CmdletBinding()]  
    Param  
    (  
        [Parameter(ParameterSetName='List1')][String]$apple,  
        [Parameter(ParameterSetName='List1')][String]$orange,  
        [Parameter(ParameterSetName='List1')][String]$mango,  
  
        [Parameter(ParameterSetName='List2')][String]$water,  
        [Parameter(ParameterSetName='List2')][String]$milk,  
        [Parameter(ParameterSetName='List2')][String]$juice,  
  
        [Parameter(ParameterSetName='List3')][String]$phone,  
        [Parameter(ParameterSetName='List3')][String]$computer,  
        [Parameter(ParameterSetName='List3')][String]$camera,  
  
        [Parameter(ParameterSetName='List1')]  
        [Parameter(ParameterSetName='List2')]  
        [String]$car,  
  
        [Parameter()][String]$boy,  
        [Parameter()][String]$girl  
    )  
}  

像这样[CmdletBinding(DefaultParameterSetName='List3')],当你再次使用Get-Help Get-Somthing命令时,默认第一条使用的语句方法,正是你设置的默认参数集。

NAME  
    Get-Somthing  
      
SYNTAX  
    Get-Somthing [-phone <string>] [-computer <string>] [-camera <string>] [-boy <string>] [-girl <string>]  [<CommonParameters>]  
      
    Get-Somthing [-apple <string>] [-orange <string>] [-mango <string>] [-car <string>] [-boy <string>] [-girl <string>]  [<CommonParameters>]  
      
    Get-Somthing [-water <string>] [-milk <string>] [-juice <string>] [-car <string>] [-boy <string>] [-girl <string>]  [<CommonParameters>]  

给一个psobject赋值属性的方式可以像如下所示

在这里插入图片描述

PowerShell 参数绑定之ValueFromPipeline和ValueFromPipelineByPropertyName

Function Test-Function

{
  param
  (
    [Parameter(ValueFromPipeline=$true)]
    [Int]
    $Number1,
 

    [Parameter(ValueFromPipelineByPropertyName=$true)]
    [Int]
    $Number2
  )
 Process
{
    " Number1=$Number1 , Number2=$Number2"
 }
}

上面的函数Test-Function接受两个参数。两个参数都可以从管道中获取,第一个接受一个完整的管道结果,第二个期望接受一个包含“Number2”属性名的管道输入。所以会有两种不同的表现。

PS>  1..10 | Test-Function -Number2 6
 Number1=1 , Number2=6
 Number1=2 , Number2=6
 Number1=3 , Number2=6
 Number1=4 , Number2=6
 Number1=5 , Number2=6
 Number1=6 , Number2=6
 Number1=7 , Number2=6
 Number1=8 , Number2=6
 Number1=9 , Number2=6
 Number1=10 , Number2=6

整形数组通过管道将元素传递给参数-Number1,但是另外一个参数-Number2的值却一直是6,这也正如我们所设想。如果将一个脚本块传给参数-Number2,PowerShell 首先不会报错,它会尝试去执行这个脚本块,尽量得到自己需要的类型的结果。

PS> 5..10 | Test-Function -Number2 { $_ * 2 }
 Number1=5 , Number2=10
 Number1=6 , Number2=12
 Number1=7 , Number2=14
 Number1=8 , Number2=16
 Number1=9 , Number2=18
 Number1=10 , Number2=20

上面的表现形式不是期望的,但是有时却很实用:第一,可以将本来不能传递给参数Number2的管道输入传递给了Number2,第二传递的是动态处理后的管道输入。最后再来演示ValueFromPipelineByPropertyName的功能:

PS>1..3 | foreach {

 $temp=New-Object psobject | select Number1,Number2

 $temp.Number1=Get-Random -Maximum 100

 $temp.Number2=Get-Random -Maximum 100

 $temp
} | Test-Function

输出为:

 Number1=0 ,Number2=46
 Number1=0 ,Number2=2
 Number1=0 ,Number2=26

参考文章:https://github.com/PowerShellMafia/PowerSploit/blob/445f7b2510c4553dcd9451bc4daccb20c8e67cbb/Recon/PowerView.ps1#L4769
https://blog.csdn.net/itanders/article/details/51280555
https://www.pstips.net/mutual-exclusive-parameters.html
https://blog.csdn.net/itanders/article/details/51508578
https://www.pstips.net/valuefrompipeline-and-valuefrompipelinebypropertyname.html
https://social.technet.microsoft.com/Forums/windowsserver/en-US/4df9feec-a140-4c51-b530-0a749df0373d/understanding-validatenotnullorempty
https://blog.csdn.net/evergreen79/article/details/8739829
https://blog.csdn.net/zuoshenglo/article/details/78355151
https://docs.microsoft.com/en-us/dotnet/api/system.directoryservices.directorysearcher.propertiestoload?redirectedfrom=MSDN&view=netframework-4.8#System_DirectoryServices_DirectorySearcher_PropertiesToLoad
https://www.cnblogs.com/oneiter/archive/2009/08/13/1545286.html
https://docs.microsoft.com/zh-cn/previous-versions/windows/desktop/ldap/searching-using-range-retrieval?redirectedfrom=MSDN

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值