#sample partial of powerview powershell script (note: must be run on 32bit/x86 powershell)
#https://www.blackhillsinfosec.com/finding-buried-treasure-in-server-message-block-smb/
iex (get-content .\filefinds.ps1 |out-string)
PS C:\> Get-NetComputer –OperatingSystem *2003* | Out-File –Encoding ASCII Windows2003Hosts.txt
PS C:\> Get-NetComputer –OperatingSystem *2008* | Out-File –Encoding ASCII Windows2008Hosts.txt
PS C:\> Get-NetComputer –OperatingSystem *2012* | Out-File –Encoding ASCII Windows2012Hosts.txt
PS C:\> Get-NetComputer –OperatingSystem *2016* | Out-File –Encoding ASCII Windows2016Hosts.txt
PS C:\> Get-NetComputer –OperatingSystem *2019* | Out-File –Encoding ASCII Windows2019Hosts.txt
PS C:\> Get-NetComputer –OperatingSystem *XP* | Out-File –Encoding ASCII WindowsXPHosts.txt
PS C:\> Get-NetComputer –OperatingSystem *7* | Out-File –Encoding ASCII Windows7Hosts.txt
PS C:\> Get-NetComputer –OperatingSystem * 8* | Out-File –Encoding ASCII Windows8Hosts.txt
PS C:\> Get-NetComputer –OperatingSystem *10* | Out-File –Encoding ASCII Windows10Hosts.txt
PS C:\> Invoke-ShareFinder –ComputerFile Windows2003Hosts.txt -NoPing –ExcludeIPC –ExcludePrint –CheckShareAccess | Out-File –Encoding ASCII Windows2003Shares.txt
PS C:\> Invoke-ShareFinder –ComputerFile Windows2008Hosts.txt -NoPing –ExcludeIPC –ExcludePrint –CheckShareAccess | Out-File –Encoding ASCII Windows2008Shares.txt
PS C:\> Invoke-ShareFinder –ComputerFile Windows2012Hosts.txt -NoPing –ExcludeIPC –ExcludePrint –CheckShareAccess | Out-File –Encoding ASCII Windows2012Shares.txt
PS C:\> Invoke-ShareFinder –ComputerFile Windows2016Hosts.txt -NoPing –ExcludeIPC –ExcludePrint –CheckShareAccess | Out-File –Encoding ASCII Windows2016Shares.txt
PS C:\> Invoke-ShareFinder –ComputerFile Windows2019Hosts.txt -NoPing –ExcludeIPC –ExcludePrint –CheckShareAccess | Out-File –Encoding ASCII Windows2019Shares.txt
PS C:\> Invoke-ShareFinder –ComputerFile WindowsXPHosts.txt -NoPing –ExcludeIPC –ExcludePrint –CheckShareAccess | Out-File –Encoding ASCII WindowsXPShares.txt
PS C:\> Invoke-ShareFinder –ComputerFile Windows7Hosts.txt -NoPing –ExcludeIPC –ExcludePrint –CheckShareAccess | Out-File –Encoding ASCII Windows7Shares.txt
PS C:\> Invoke-ShareFinder –ComputerFile Windows8Hosts.txt -NoPing –ExcludeIPC –ExcludePrint –CheckShareAccess | Out-File –Encoding ASCII Windows8Shares.txt
PS C:\> Invoke-ShareFinder –ComputerFile Windows10Hosts.txt -NoPing –ExcludeIPC –ExcludePrint –CheckShareAccess | Out-File –Encoding ASCII Windows10Shares.txt
---------------------
findfiles.ps1
----------------------------
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 Convert-LDAPProperty {
# helper to convert specific LDAP property result fields
param(
[Parameter(Mandatory=$True,ValueFromPipeline=$True)]
[ValidateNotNullOrEmpty()]
$Properties
)
$ObjectProperties = @{}
$Properties.PropertyNames | ForEach-Object {
if (($_ -eq "objectsid") -or ($_ -eq "sidhistory")) {
# convert the SID to a string
$ObjectProperties[$_] = (New-Object System.Security.Principal.SecurityIdentifier($Properties[$_][0],0)).Value
}
elseif($_ -eq "objectguid") {
# convert the GUID to a string
$ObjectProperties[$_] = (New-Object Guid (,$Properties[$_][0])).Guid
}
elseif( ($_ -eq "lastlogon") -or ($_ -eq "lastlogontimestamp") -or ($_ -eq "pwdlastset") -or ($_ -eq "lastlogoff") -or ($_ -eq "badPasswordTime") ) {
# convert timestamps
if ($Properties[$_][0] -is [System.MarshalByRefObject]) {
# if we have a System.__ComObject
$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]) {
# convert misc com objects
$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 Get-NetForest {
[CmdletBinding()]
param(
[Parameter(ValueFromPipeline=$True)]
[String]
$Forest
)
process {
if($Forest) {
$ForestContext = New-Object System.DirectoryServices.ActiveDirectory.DirectoryContext('Forest', $Forest)
try {
$ForestObject = [System.DirectoryServices.ActiveDirectory.Forest]::GetForest($ForestContext)
}
catch {
Write-Debug "The specified forest $Forest does not exist, could not be contacted, or there isn't an existing trust."
$Null
}
}
else {
# otherwise use the current forest
$ForestObject = [System.DirectoryServices.ActiveDirectory.Forest]::GetCurrentForest()
}
if($ForestObject) {
# get the SID of the forest root
$ForestSid = (New-Object System.Security.Principal.NTAccount($ForestObject.RootDomain,"krbtgt")).Translate([System.Security.Principal.SecurityIdentifier]).Value
$Parts = $ForestSid -Split "-"
$ForestSid = $Parts[0..$($Parts.length-2)] -join "-"
$ForestObject | Add-Member NoteProperty 'RootDomainSid' $ForestSid
$ForestObject
}
}
}
function Get-NetForestDomain {
[CmdletBinding()]
param(
[Parameter(ValueFromPipeline=$True)]
[String]
$Forest,
[String]
$Domain
)
process {
if($Domain) {
# try to detect a wild card so we use -like
if($Domain.Contains('*')) {
(Get-NetForest -Forest $Forest).Domains | Where-Object {$_.Name -like $Domain}
}
else {
# match the exact domain name if there's not a wildcard
(Get-NetForest -Forest $Forest).Domains | Where-Object {$_.Name.ToLower() -eq $Domain.ToLower()}
}
}
else {
# return all domains
$ForestObject = Get-NetForest -Forest $Forest
if($ForestObject) {
$ForestObject.Domains
}
}
}
}
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 {
# if there's no -DomainController specified, try to pull the primary DC
# to reflect queries through
$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://*") {
# if we're searching the global catalog
$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-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 {
# so this isn't repeated if users are passed on the pipeline
$CompSearcher = Get-DomainSearcher -Domain $Domain -DomainController $DomainController -ADSpath $ADSpath -PageSize $PageSize
}
process {
if ($CompSearcher) {
# if we're checking for unconstrained delegation
if($Unconstrained) {
Write-Verbose "Searching for computers with for unconstrained delegation"
$Filter += "(userAccountControl:1.2.840.113556.1.4.803:=524288)"
}
# set the filters for the seracher if it exists
if($Printers) {
Write-Verbose "Searching for printers"
# $CompSearcher.filter="(&(objectCategory=printQueue)$Filter)"
$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) {
# TODO: how can these results be piped to ping for a speedup?
$Up = Test-Connection -Count 1 -Quiet -ComputerName $_.properties.dnshostname
}
if($Up) {
# return full data objects
if ($FullData) {
# convert/process the LDAP fields for each result
Convert-LDAPProperty -Properties $_.Properties
}
else {
# otherwise we're just returning the DNS host name
$_.properties.dnshostname
}
}
}
}
catch {
Write-Warning "Error: $_"
}
}
}
}
function Get-NameField {
[CmdletBinding()]
param(
[Parameter(Mandatory=$True,ValueFromPipeline=$True)]
$Object
)
process {
if($Object) {
if ( [bool]($Object.PSobject.Properties.name -match "dnshostname") ) {
# objects from Get-NetComputer
$Object.dnshostname
}
elseif ( [bool]($Object.PSobject.Properties.name -match "name") ) {
# objects from Get-NetDomainController
$Object.name
}
else {
# strings and catch alls
$Object
}
}
else {
return $Null
}
}
}
function Get-NetShare {
[CmdletBinding()]
param(
[Parameter(ValueFromPipeline=$True)]
[Alias('HostName')]
[String]
$ComputerName = 'localhost'
)
begin {
if ($PSBoundParameters['Debug']) {
$DebugPreference = 'Continue'
}
}
process {
# process multiple host object types from the pipeline
$ComputerName = Get-NameField -Object $ComputerName
# arguments for NetShareEnum
$QueryLevel = 1
$PtrInfo = [IntPtr]::Zero
$EntriesRead = 0
$TotalRead = 0
$ResumeHandle = 0
# get the share information
$Result = $Netapi32::NetShareEnum($ComputerName, $QueryLevel, [ref]$PtrInfo, -1, [ref]$EntriesRead, [ref]$TotalRead, [ref]$ResumeHandle)
# Locate the offset of the initial intPtr
$Offset = $PtrInfo.ToInt64()
Write-Debug "Get-NetShare result: $Result"
# 0 = success
if (($Result -eq 0) -and ($Offset -gt 0)) {
# Work out how mutch to increment the pointer by finding out the size of the structure
$Increment = $SHARE_INFO_1::GetSize()
# parse all the result structures
for ($i = 0; ($i -lt $EntriesRead); $i++) {
# create a new int ptr at the given offset and cast
# the pointer as our result structure
$NewIntPtr = New-Object System.Intptr -ArgumentList $Offset
$Info = $NewIntPtr -as $SHARE_INFO_1
# return all the sections of the structure
$Info | Select-Object *
$Offset = $NewIntPtr.ToInt64()
$Offset += $Increment
}
# free up the result buffer
$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 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'
}
# random object for delay
$RandNo = New-Object System.Random
Write-Verbose "[*] Running Invoke-ShareFinder with delay of $Delay"
# figure out the shares we want to ignore
[String[]] $ExcludedShares = @('')
if ($ExcludePrint) {
$ExcludedShares = $ExcludedShares + "PRINT$"
}
if ($ExcludeIPC) {
$ExcludedShares = $ExcludedShares + "IPC$"
}
if ($ExcludeStandard) {
$ExcludedShares = @('', "ADMIN$", "IPC$", "C$", "PRINT$")
}
if(!$ComputerName) {
if($Domain) {
$TargetDomains = @($Domain)
}
elseif($SearchForest) {
# get ALL the domains in the forest to search
$TargetDomains = Get-NetForestDomain | ForEach-Object { $_.Name }
}
else {
# use the local domain
$TargetDomains = @( (Get-NetDomain).name )
}
# if we're using a host file list, read the targets in and add them to the target list
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
}
}
# remove any null target hosts, uniquify the list and shuffle it
$ComputerName = $ComputerName | Where-Object { $_ } | Sort-Object -Unique | Sort-Object { Get-Random }
if($($ComputerName.count) -eq 0) {
throw "No hosts found!"
}
}
# script block that enumerates a server
$HostEnumBlock = {
param($ComputerName, $Ping, $CheckShareAccess, $ExcludedShares, $CheckAdmin)
# optionally check if the server is up first
$Up = $True
if($Ping) {
$Up = Test-Connection -Count 1 -Quiet -ComputerName $ComputerName
}
if($Up) {
# get the shares for this host and check what we find
$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
# make sure we get a real share name back
if (($NetName) -and ($NetName.trim() -ne '')) {
# if we're just checking for access to ADMIN$
if($CheckAdmin) {
if($NetName.ToUpper() -eq "ADMIN$") {
try {
$Null = [IO.Directory]::GetFiles($Path)
"\\$ComputerName\$NetName `t- $Remark"
}
catch {
Write-Debug "Error accessing path $Path : $_"
}
}
}
# skip this share if it's in the exclude list
elseif ($ExcludedShares -NotContains $NetName.ToUpper()) {
# see if we want to check access to this share
if($CheckShareAccess) {
# check if the user has access to this path
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"
# if we're using threading, kick off the script block with Invoke-ThreadedFunction
$ScriptParams = @{
'Ping' = $(-not $NoPing)
'CheckShareAccess' = $CheckShareAccess
'ExcludedShares' = $ExcludedShares
'CheckAdmin' = $CheckAdmin
}
# kick off the threaded script block + arguments
Invoke-ThreadedFunction -ComputerName $ComputerName -ScriptBlock $HostEnumBlock -ScriptParameters $ScriptParams
}
else {
if(-not $NoPing -and ($ComputerName.count -ne 1)) {
# ping all hosts in parallel
$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 "[*] Total number of active hosts: $($ComputerName.count)"
$Counter = 0
ForEach ($Computer in $ComputerName) {
$Counter = $Counter + 1
# sleep for our semi-randomized interval
Start-Sleep -Seconds $RandNo.Next((1-$Jitter)*$Delay, (1+$Jitter)*$Delay)
Write-Verbose "[*] Enumerating server $Computer ($Counter of $($ComputerName.count))"
Invoke-Command -ScriptBlock $HostEnumBlock -ArgumentList $Computer, $False, $CheckShareAccess, $ExcludedShares, $CheckAdmin
}
}
}
}
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 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])
{
$k = ($Module.GetType($FullName))
return $k
}
$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)
{
# Apply the specified enum type to each element
$Null = $EnumBuilder.DefineLiteral($Key, $EnumElements[$Key] -as $EnumType)
}
$EnumBuilder.CreateType()
}
# A helper function used to reduce typing while defining struct
# fields.
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 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)
# Sort each field according to the orders specified
# Unfortunately, PSv2 doesn't have the luxury of the
# hashtable [Ordered] accelerator.
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) }
}
# Make the struct aware of its own size.
# No more having to call [Runtime.InteropServices.Marshal]::SizeOf!
$SizeMethod = $StructBuilder.DefineMethod('GetSize',
'Public, Static',
[Int],
[Type[]] @())
$ILGenerator = $SizeMethod.GetILGenerator()
# Thanks for the help, Jason Shirk!
$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)
# Allow for explicit casting from an IntPtr
# No more having to call [Runtime.InteropServices.Marshal]::PtrToStructure!
$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 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
{
# Define one type for each DLL
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)
# Make each ByRef parameter an Out parameter
$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 }
# Equivalent to C# version of [DllImport(DllName)]
$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 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
}
$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 NetWkstaUserEnum ([Int]) @([String], [Int], [IntPtr].MakeByRefType(), [Int], [Int32].MakeByRefType(), [Int32].MakeByRefType(), [Int32].MakeByRefType())),
(func netapi32 NetSessionEnum ([Int]) @([String], [String], [String], [Int], [IntPtr].MakeByRefType(), [Int], [Int32].MakeByRefType(), [Int32].MakeByRefType(), [Int32].MakeByRefType())),
(func netapi32 NetApiBufferFree ([Int]) @([IntPtr])),
(func advapi32 OpenSCManagerW ([IntPtr]) @([String], [String], [Int])),
(func advapi32 CloseServiceHandle ([Int]) @([IntPtr])),
(func wtsapi32 WTSOpenServerEx ([IntPtr]) @([String])),
(func wtsapi32 WTSEnumerateSessionsEx ([Int]) @([IntPtr], [Int32].MakeByRefType(), [Int], [IntPtr].MakeByRefType(), [Int32].MakeByRefType())),
(func wtsapi32 WTSQuerySessionInformation ([Int]) @([IntPtr], [Int], [Int], [IntPtr].MakeByRefType(), [Int32].MakeByRefType())),
(func wtsapi32 WTSFreeMemoryEx ([Int]) @([Int32], [IntPtr], [Int32])),
(func wtsapi32 WTSFreeMemory ([Int]) @([IntPtr])),
(func wtsapi32 WTSCloseServer ([Int]) @([IntPtr])),
(func kernel32 GetLastError ([Int]) @())
)
# enum used by $WTS_SESSION_INFO_1 below
$WTSConnectState = psenum $Mod WTS_CONNECTSTATE_CLASS UInt16 @{
Active = 0
Connected = 1
ConnectQuery = 2
Shadow = 3
Disconnected = 4
Idle = 5
Listen = 6
Reset = 7
Down = 8
Init = 9
}
# the WTSEnumerateSessionsEx result structure
$WTS_SESSION_INFO_1 = struct $Mod WTS_SESSION_INFO_1 @{
ExecEnvId = field 0 UInt32
State = field 1 $WTSConnectState
SessionId = field 2 UInt32
pSessionName = field 3 String -MarshalAs @('LPWStr')
pHostName = field 4 String -MarshalAs @('LPWStr')
pUserName = field 5 String -MarshalAs @('LPWStr')
pDomainName = field 6 String -MarshalAs @('LPWStr')
pFarmName = field 7 String -MarshalAs @('LPWStr')
}
# the particular WTSQuerySessionInformation result structure
$WTS_CLIENT_ADDRESS = struct $mod WTS_CLIENT_ADDRESS @{
AddressFamily = field 0 UInt32
Address = field 1 Byte[] -MarshalAs @('ByValArray', 20)
}
# the NetShareEnum result structure
$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')
}
# the NetWkstaUserEnum result structure
$WKSTA_USER_INFO_1 = struct $Mod WKSTA_USER_INFO_1 @{
wkui1_username = field 0 String -MarshalAs @('LPWStr')
wkui1_logon_domain = field 1 String -MarshalAs @('LPWStr')
wkui1_oth_domains = field 2 String -MarshalAs @('LPWStr')
wkui1_logon_server = field 3 String -MarshalAs @('LPWStr')
}
# the NetSessionEnum result structure
$SESSION_INFO_10 = struct $Mod SESSION_INFO_10 @{
sesi10_cname = field 0 String -MarshalAs @('LPWStr')
sesi10_username = field 1 String -MarshalAs @('LPWStr')
sesi10_time = field 2 UInt32
sesi10_idle_time = field 3 UInt32
}
$Types = $FunctionDefinitions | Add-Win32Type -Module $Mod -Namespace 'Win32'
$Netapi32 = $Types['netapi32']
$Advapi32 = $Types['advapi32']
$Kernel32 = $Types['kernel32']
$Wtsapi32 = $Types['wtsapi32']