Feeds:
Записи
Комментарии

В проекте PSCX есть замечательная функция Show-Tree , которая послужит  основой в решении задачи.

function Show-ADTreeOU            
{            
    [CmdletBinding()]            
    param(            
        [Parameter(Position=0,             
                   ValueFromPipeline=$true,             
                   ValueFromPipelineByPropertyName=$true)]            
        [ValidateNotNullOrEmpty()]            
        [alias("DistinguishedName")]            
        [string]            
        $Path = (Get-ADDomain).DistinguishedName,            
                                
        [ValidateRange(0, 256)]            
        [int]            
        $Depth = [int]::MaxValue,             
                    
        [Parameter()]            
        [ValidateRange(1, 100)]            
        [int]            
        $IndentSize = 3,             
                    
        [Parameter()]            
        [ValidateRange(0, 2147483647)]            
        [int]            
        $Width,            
                    
        [Parameter()]            
        [switch]            
        $ShowLeafObject,            
            
                    
        [Parameter()]            
        [switch]            
        $UseAsciiLineArt            
    )            
            
    Begin            
    {            
        Set-StrictMode -Version Latest            
                    
                    
        if ($Width -eq 0)            
        {            
            $Width = $host.UI.RawUI.BufferSize.Width            
        }            
                    
        $asciiChars = @{            
            EndCap        = '\'            
            Junction      = '|'            
            HorizontalBar = '-'            
            VerticalBar   = '|'            
        }            
                    
        $cp437Chars = @{            
            EndCap        = '└'            
            Junction      = '├'            
            HorizontalBar = '─'            
            VerticalBar   = '│'            
        }            
                    
        if (($Host.CurrentCulture.TextInfo.OEMCodePage -eq 437) -and !$UseAsciiLineArt)            
        {            
            $lineChars = $cp437Chars            
        }            
        else            
        {            
            $lineChars = $asciiChars            
        }            
                           
        function GetIndentString([bool[]]$IsLast)            
        {            
            $str = ''            
            for ($i=0; $i -lt $IsLast.Count - 1; $i++)            
            {            
                $str += if ($IsLast[$i]) {' '} else {$lineChars.VerticalBar}            
                $str += " " * ($IndentSize - 1)            
            }            
            $str += if ($IsLast[-1]) {$lineChars.EndCap} else {$lineChars.Junction}            
            $str += $lineChars.HorizontalBar * ($IndentSize - 1)            
            $str            
        }            
                    
        function CompactString([string]$String, [int]$MaxWidth = $Width)            
        {            
            $updatedString = $String            
            if ($String.Length -ge $MaxWidth)            
            {            
                $ellipsis = '...'            
                $updatedString = $String.Substring(0, $MaxWidth - $ellipsis.Length - 1) + $ellipsis            
            }            
            $updatedString                
        }            
            
        function ShowItemText([string]$ItemPath, [string]$ItemName, [bool[]]$IsLast)            
        {            
            if ($IsLast.Count -eq 0)             
            {            
                $String = CompactString $ItemPath             
            }            
            else            
            {                     
                $String = CompactString "$(GetIndentString $IsLast)$ItemName "            
            }            
                        
            if($ShowLeafObject)             
            {            
                if($ItemPath)             
                {            
                    $Users = $Script:ADO[$ItemPath].Where({$_.ObjectClass -eq "user"}).Count            
     $Computers = $Script:ADO[$ItemPath].Where({$_.ObjectClass -eq "computer"}).Count            
                    $GR = $Script:ADO[$ItemPath].Where({$_.ObjectClass -eq "group"})            
                    $DL_GR_SEC = $GR.Where({$_.GroupScope -eq "DomainLocal" -and $_.GroupCategory -eq "Security"}).Count            
                    $GL_GR_SEC = $GR.Where({$_.GroupScope -eq "Global" -and $_.GroupCategory -eq "Security"}).Count            
                    $UN_GR_SEC = $GR.Where({$_.GroupScope -eq "Universal" -and $_.GroupCategory -eq "Security"}).Count            
                    $DL_GR_DIS = $GR.Where({$_.GroupScope -eq "DomainLocal" -and $_.GroupCategory -eq "Distribution"}).Count            
                    $GL_GR_DIS = $GR.Where({$_.GroupScope -eq "Global" -and $_.GroupCategory -eq "Distribution"}).Count            
                    $UN_GR_DIS = $GR.Where({$_.GroupScope -eq "Universal" -and $_.GroupCategory -eq "Distribution"}).Count            
            
                    [PSCustomObject]@{            
                        Name      = $String              
                        Users     = $Users            
      Computers = $Computers            
                        DL_GR_SEC = $DL_GR_SEC            
                        GL_GR_SEC = $GL_GR_SEC            
                        UN_GR_SEC = $UN_GR_SEC            
                        DL_GR_DIS = $DL_GR_DIS            
                        GL_GR_DIS = $GL_GR_DIS            
                        UN_GR_DIS = $UN_GR_DIS            
                    }            
                }            
            }             
            else             
            {            
               $String              
            }             
        }            
                    
                    
        function ShowItem([string]$ItemPath, [string]$ItemName='', [bool[]]$IsLast=@())            
        {                    
            # Show current item            
            ShowItemText $ItemPath $ItemName $IsLast            
                        
            # grab its children.  This let's us know if there            
            $childItems = @()            
            if ($IsLast.Count -lt $Depth)            
            {            
                $childItems = @(Get-ADOrganizationalUnit -Filter * -SearchBase $ItemPath -SearchScope OneLevel | Select DistinguishedName, Name)                   
            }            
            
            # Track parent's "last item" status to determine which level gets a vertical bar            
            $IsLast += @($false)            
                                    
            # Recurse through child items            
            for ($i=0; $i -lt $childItems.Count; $i++)            
            {            
                $childItemPath = $childItems[$i].DistinguishedName            
                $childItemName = $childItems[$i].Name            
                $IsLast[-1] = ($i -eq $childItems.Count - 1)            
                ShowItem $childItemPath $childItemName $IsLast            
            }            
        }            
    }            
            
    Process            
    {               
  if($ShowLeafObject)            
  {            
   $ADObjects = @()            
   $ADObjects += Get-ADObject -Filter "ObjectClass -eq 'user' -or ObjectClass -eq 'computers'" -SearchBase $Path            
   $ADObjects += Get-ADGroup -Filter * -SearchBase $Path            
               
   if($ADObjects)            
   {            
    $Script:ADO  = @{}            
    $ADO[$Path] = $ADObjects            
    $ADObjects | Group-Object {$_.DistinguishedName.Split(",",2)[1]} | Foreach-Object {            
     if($_.Name.StartsWith("OU="))            
     {            
      $ADO[$_.Name] = $_.Group            
     }                 
    }            
   }            
  }            
                    
  if($Path)             
  {                  
            ShowItem $Path             
        }             
    }                    
}

shtree

 

Файл: show-adtreeou-ps1.doc

Рассмотрим отправку Post запроса, на примере сайта http://pr.nca.by/. Для этого можно воспользоваться двумя командлетами Invoke-WebRequest/Invoke-RestMethod, оба которые поддерживают параметр –Method.

Форма запроса на сайте выглядит:

form

 

Для получение параметров, которые требуется передать в теле post запроса , существует большое количество различных инструментов:

1. Встроенные средства браузеров, на примере IE/Google Chrome

Для запуска  Developer Tools в IE нажмите F12 или перейдите в меню

Tools –> F12 Developer Tools

IE_DV12

Переходим в раздел Network – нажимаем F5 и выполняем запрос:

IE_NET

 

Выбираем запрос и смотрим результаты:

IE_HR

IE_BODY

 

В Google Chrome для запуска инструментов разработчика нажмите F12 или перейдите в меню:

CH_DV12

CH_REQ

 

В Google Chrome удобно сделано копирование и можно сразу получить результат для утилиты curl.

CH_SV


curl "http://pr.nca.by/edc63526a304744e5976afdac72281a6.a_request.show_prices_count_test.xml" -H "Origin: http://pr.nca.by" -H "Accept-Encoding: gzip, deflate" -H "Accept-Language: ru-RU,ru;q=0.8,en-US;q=0.6,en;q=0.4" -H "User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36" -H "Content-type: application/x-www-form-urlencoded" -H "Accept: */*" -H "Referer: http://pr.nca.by/" -H "Cookie: atereq=edc63526a304744e5976afdac72281a6; __utmt=1; __utma=97320926.45800996.1472894768.1472894768.1472916142.2; __utmb=97320926.1.10.1472916142; __utmc=97320926; __utmz=97320926.1472894768.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none)" -H "Connection: keep-alive" --data "v_param=3&v_objnum=17030&v_date_begin=1.7.2016&v_date_end=3.9.2016&v_sq_begin=20&v_sq_end=200&v_purpose=40101&v_soato=5000000000&v_typ=3&v_name=&v_pr_b=25000&v_pr_e=250000&v_cur=4&v_url=http//pr.nca.by/&v_room=&v_floor_beg=&v_floor_end=&v_floor_number_beg=&v_floor_number_end=&v_input_date_beg=&v_input_date_end=&v_purpose_KSforIP=-1&v_wallmat=" --compressed

 

2. Программа-анализатор трафика WireShark

Фильтр: http && ip.dst_host == 93.125.111.85 (по HTTP и IP адресу назначения)

http && http.host contains «nca.by» (по HTTP и содержанию в хосте шаблона)

wr

 

3. Прокси программа для работы с веб – Fiddler

f1

 

Тело запроса:

f2

Ответ, как в текстовой форме, так и в XML:

f3

 

Теперь когда у нас есть все данные, мы можем сформировать Post запрос в PowerShell.

Чтобы отправить запрос на сайт, нам надо получить url-адрес, для этого мы должны перейти на сайт http://pr.nca.by/ , сохранить сессию в переменную. В cookies мы получаем значение atereq, которое участвует в формировании запроса.  И url запроса, должен иметь вид:

http://pr.nca.by/{atereq}.a_request.show_prices_count_test.xml

 

f4

 

$url = "http://pr.nca.by/"
$ct  = "application/x-www-form-urlencoded"
$s = Invoke-WebRequest $url -SessionVariable sb
$val = $sb.Cookies.GetCookies($url).Value
$purl = "${url}$val.a_request.show_prices_count_test.xml"
$r = Invoke-WebRequest $purl -ContentType $ct -WebSession $sb -Method Post -Body @{
	"v_param"="3"
	"v_objnum"="17030"
	"v_date_begin"="1.7.2016"
	"v_date_end"="31.7.2016"
	"v_sq_begin"="20"
	"v_sq_end"="200"
	"v_purpose"="40101"
	"v_soato"="5000000000"
	"v_typ"="3"
	"v_name"=""
	"v_pr_b"="25000"
	"v_pr_e"="2500000"
	"v_cur"="4"
	"v_url"="http//pr.nca.by/"
	"v_room"=""
	"v_floor_beg"=""
	"v_floor_end"=""
	"v_floor_number_beg"=""
	"v_floor_number_end"=""
	"v_input_date_beg"=""
	"v_input_date_end"=""
	"v_purpose_KSforIP"="-1"
	"v_wallmat"=""
}
([xml]$r.Content).response.rowcount_all

Примерный вывод:
PS > $r.Content
375

PS > ([xml]$r.Content).response.rowcount_all
375

При использовании командлета Invoke-RestMethod мы получим готовый ответ  в xml:

$r = Invoke-RestMethod $purl -ContentType $ct -WebSession $sb -Method Post .....

PS > $r
xml                            response
---                            --------
version="1.0" encoding="utf-8" response


PS > $r.response
type            rowcount_all
----            ------------
show_info_count 375


PS > $r.response.rowcount_all
375

В данной статье, рассмотрим 3 способа:

  1. Предоставление права SeRemoteShutdownPrivilege
  2. Делегирование прав WMI
  3. Использование Windows Powershell Remoting / JEA

Наш стенд:

  • 2 клиентских ОС – Windows 10
  • 1 DC – Windows Server 2012 R2
  • Администратор: Contoso\Administrator
  • Пользователь: Contoso\User1

I. Первый способ – это  предоставление права SeRemoteShutdownPrivilege

Force shutdown from a remote system — Этот параметр безопасности определяет, каким пользователям разрешено завершать работу компьютера из удаленного расположения в сети. Это позволяет членам группы администраторов или конкретных пользователей для управления компьютерами (для задач, таких как перезапуск) из удаленного расположения.

Для предоставления SeRemoteShutdownPrivilege мы можем использовать: GPO, ntrights, secedit и т.д.

Утилита ntrights входит в комплект Windows Server 2003 Resource Kit Tools . После установки комплекта , выполняем:

ntrights -r SeRemoteShutdownPrivilege -u Contoso\User1

Другая утилита, которая поставляется по умолчанию является —  secedit.

При использовании secedit, потребуется подготовить шаблон.

SECEDIT /configure /db secedit.sdb /cfg "c:\SEC_TEMPLATE.inf"

[Unicode]
Unicode=yes

[Registry Values]

[Privilege Rights]

SeRemoteShutdownPrivilege = "Administrators","Contoso\User1"

[Version]

signature="$CHICAGO$"
Revision=1

[Profile Description]
Description=Local Security Template

SECEDIT /configure /db secedit.sdb /cfg "c:\SEC_TEMPLATE.inf"

 

Для предоставления права вручную:

lgpo_30082016

 

Предоставим право SeRemoteShutdownPrivilege через PowerShell. Для этого нам потребуется отличный модуль — UserRights PowerShell Module .

1. Устанавливаем UserRights PowerShell Module

$fname = "UserRights.zip"            
$dpath = "$env:UserProfile\Documents\WindowsPowerShell\Modules\UserRights"            
$url = "https://gallery.technet.microsoft.com/scriptcenter/UserRights-PowerShell-1ff45589/file/124612/1/UserRights.zip"            
[Net.WebClient]::new().DownloadFile($url, $fname)            
Unblock-File $fname            
Expand-Archive -LiteralPath $fname -DestinationPath $dpath

 

2. Проверим у кого есть право SeRemoteShutdownPrivilege по умолчанию

PS > Get-UserRight -Privileges SeRemoteShutdownPrivilege

Right                     ComputerName Accounts
-----                     ------------ --------
SeRemoteShutdownPrivilege W10-CL2      BUILTIN\Administrators

3. Добавим право SeRemoteShutdownPrivilege для пользователя Contoso\User1

 
PS > Grant-UserRight -Account Contoso\User1 -Privileges SeRemoteShutdownPrivilege -PassThru 

Right                     ComputerName Accounts 
-----                     ------------ -------- 
SeRemoteShutdownPrivilege W10-CL2      {BUILTIN\Administrators, CONTOSO\User1}

 

4. Добавим правило исключения в firewall

Get-NetFirewallRule -DisplayName "File and Printer Sharing (SMB-In)" | Enable-NetFirewallRule

 

5. Выключаем удаленную машину

shutdown /m \\W10-CL2.Contoso.Com /s /t 0

 

 

Какие WinApi использует утилита shutdown:

Определим сигнатуру и выполним функцию. 
 
$Signature = @"
[DllImport("advapi32.dll")]
	public static extern bool InitiateSystemShutdown(
        [MarshalAs(UnmanagedType.LPStr)] string lpMachinename,
        [MarshalAs(UnmanagedType.LPStr)] string lpMessage,
        Int32 dwTimeout,
        bool bForceAppsClosed,
        bool bRebootAfterShutdown);
"@

$ShutDown = Add-Type -MemberDefinition $Signature -Name "Win32" -Namespace Win32Functions -PassThru
$ShutDown::InitiateSystemShutdown("W10-CL2.Contoso.Com","",0,$true,$false)

 

Можно использовать другой метод, который описан в статье — Weekend Scripter: Give Your PowerShell Console a Glassy Theme .

 


#region Module Builder
$Domain = [AppDomain]::CurrentDomain
$DynAssembly = New-Object System.Reflection.AssemblyName('PInvokeAssembly')
# Only run in memory
$AssemblyBuilder = $Domain.DefineDynamicAssembly($DynAssembly, [System.Reflection.Emit.AssemblyBuilderAccess]::Run)
$ModuleBuilder = $AssemblyBuilder.DefineDynamicModule('PInvokeModule', $False)
#endregion Module Builder
$TypeBuilder = $ModuleBuilder.DefineType('PInvoke', 'Public, Class')

$PInvokeMethod = $TypeBuilder.DefineMethod(
    'InitiateSystemShutdown', #Method Name
    [Reflection.MethodAttributes] 'PrivateScope, Public, Static, HideBySig, PinvokeImpl', #Method Attributes
    [bool], #Method Return Type
    [Type[]] @([string],[string],[Int32],[bool],[bool]) #Method Parameters
)

$DllImportConstructor = [Runtime.InteropServices.DllImportAttribute].GetConstructor(@([String]))
$FieldArray = [Reflection.FieldInfo[]] @(
	[Runtime.InteropServices.DllImportAttribute].GetField('EntryPoint'),
	[Runtime.InteropServices.DllImportAttribute].GetField('PreserveSig')
)

$FieldValueArray = [Object[]] @(
    'InitiateSystemShutdown', #CASE SENSITIVE!!
    $False
)

$CustomAttributeBuilder = New-Object Reflection.Emit.CustomAttributeBuilder(
    $DllImportConstructor,
	@('Advapi32.dll'),
	$FieldArray,
	$FieldValueArray
)

$PInvokeMethod.SetCustomAttribute($CustomAttributeBuilder)

[void]$TypeBuilder.CreateType()
[PInvoke]::InitiateSystemShutdown("W10-CL2.Contoso.Com","",0,$true,$false)

 

Для удаления права:

PS > Revoke-UserRight -Account Contoso\User1 -Privileges SeRemoteShutdownPrivilege -PassThru

Right                     ComputerName Accounts
-----                     ------------ --------
SeRemoteShutdownPrivilege W10-CL2      BUILTIN\Administrators

 

II. Второй способ — Делегирование прав WMI

Удаленный доступ к WMI по умолчанию разрешен только группе Администраторы. Т.к. право Access this computer from network по умолчанию назначено Domain Users для клиентских ос, то мы его трогать не будем.

Т.к. группе Distributed COM Users выданны нужные права, то нам будет достаточно только добавить туда пользователя или группу. Если Вам потребуется ограничить права более детально:

  1. Создайте группу и добавьте заданных пользователей
  2. Проверить, что группа имеет право — Access this computer from network user right
  3. Component Services (dcomcnfg.exe) -> Computers -> My Computer –> Propertiesdcom_30082016
  4. Проверить галочку Default Properties -> Enable Distributed COMdcom1_30082016
  5. COM Security tab -> Edit Limits секция Launch and Activation Permissions и добавить группу, указав Allow для Local Launch, Remote Launch, Local Activation, Remote Activationdcom2_30082016dcom3_30082016
  6. Открываем My Computer -> переходим к разделу DCOM Config находим Windows Management and Instrumentation -> Properties –> Security. В разделе Launch and Activation Permissions назначаем Allow для Local Launch, Remote Launch, Local Activation, Remote Activationdcom4_30082016
  7. Закрываем
  8. WMI Control(WmiMgmt.msc) –> Properties –> Security –> Root\Cimv2 –> Security. Добавляем права Allow для Remote Enablewmi_30082016

Как в начале было сказано, мы пойдет более простым путем.

1. Добавим пользователя «Contoso\User1» в группу «Remote Management Users»

Add-LocalGroupMember "Distributed COM Users" -Member "Contoso\User1"

2. Добавим правила в FireWall

Get-NetFirewallRule -DisplayName "Windows Management Instrumentation (WMI-IN)" | Enable-NetFirewallRule

 

3. Добавим право SeRemoteShutdownPrivilege для пользователя Contoso\User1 ,т.к. выключение происходит удаленно то требуется данное право. Для локального выключения – SeShutdownPrivilege.

PS > Grant-UserRight -Account Contoso\User1 -Privileges SeRemoteShutdownPrivilege -PassThru

Right                     ComputerName Accounts
-----                     ------------ --------
SeRemoteShutdownPrivilege W10-CL2      {BUILTIN\Administrators, CONTOSO\User1}

 

4. Добавим разрешения для namespace = root\cimv2, где расположен класс Win32_OperatingSystem


$Namespace = "root\cimv2"
$systemSecurity = Get-CimInstance -Namespace $Namespace -ClassName __SystemSecurity   
$oDacl = Invoke-CimMethod -InputObject $systemSecurity -MethodName GetSecurityDescriptor 

$sd = $oDacl.Descriptor

$domain = "Contoso"
$user = "User1"

$ntuser = New-Object System.Security.Principal.NTAccount($domain,$user) 

$trustee = New-CimInstance -Namespace root/cimv2 -ClassName Win32_Trustee -ClientOnly -Property @{
	Domain = $domain
	Name = $user
	SidString = $ntuser.Translate([Security.Principal.SecurityIdentifier]).Value
} 

# AceType Allow = 0 Deny = 1
# AccessMask 35 = Execute Methods,Enable Account,Remote Enable

$ace = New-CimInstance -Namespace root/cimv2 -ClassName Win32_Ace -ClientOnly -Property @{
	AceType=[uint32]0
	Trustee=$trustee
	AccessMask=[uint32]35
	AceFlags=[uint32]0
} 

[CIMInstance[]] $nDacl = $null 
foreach ($iAce in $sd.DACL) { 
	$nDacl += $iAce
}
 
$newDacl += $ace 
$sd.DACL = $newDacl 
                
Invoke-CimMethod -InputObject $systemSecurity -MethodName SetSecurityDescriptor -Arguments @{ 
	Descriptor = $sd 
}

 

5. Выключаем

Stop-Computer -ComputerName w10-cl2.contoso.com -Force

 

С переводом проекта PowerShell — https://github.com/PowerShell/PowerShell в разряд OpenSource, у нас появилась возможность посмотреть работу командлета Stop-Computer.

 


PS > Get-Command Stop-Computer | Format-List Module,ImplementingType

Module           : Microsoft.PowerShell.Management
ImplementingType : Microsoft.PowerShell.Commands.StopComputerCommand

Зная тип переходим на сайте github и в поиске указываем StopComputerCommad.

 

github_posh

 

 

Данный командлет  начинается в строке 3185 — #region Stop-Computer  и заканчивается 3606 — #endregion .

 

Командлет работает,как с DCOM, так и WSMAN.

PS > Get-Help Stop-Computer -Parameter Authentication

-Authentication <AuthenticationLevel>
    Указывает уровень проверки подлинности, используемый для подключения к WMI. (Stop-Computer использует WMI.) 
	Значение по умолчанию  Packet.

    Packet:          проверка подлинности COM на уровне пакетов.
	
    Значение по умолчанию                Packet (4)

	/// Packet = 4,
    [Alias("Authentication")]
    public AuthenticationLevel DcomAuthentication { get; set; } = AuthenticationLevel.Packet;

 

На платформе Windows Desktop , используется по умолчанию  DcomProtocol. Для поддержки других платформ(например Linux,MacOs) — WsmanProtocol.

 

Подробнее :

https://blogs.msdn.microsoft.com/dotnet/2014/12/04/introducing-net-core/

https://blogs.msdn.microsoft.com/dotnet/2015/02/03/coreclr-is-now-open-source/

[ValidateSet(ComputerWMIHelper.DcomProtocol, ComputerWMIHelper.WsmanProtocol)]
        public string Protocol { get; set; } = 
#if CORECLR
            //CoreClr does not support DCOM protocol
            // This change makes sure that the the command works seamlessly if user did not explicitly entered the protocol
            ComputerWMIHelper.WsmanProtocol;
#else
            ComputerWMIHelper.DcomProtocol;
#endif
 
PS > Get-Help Stop-Computer -Parameter Impersonation

-Impersonation <ImpersonationLevel>
    Задает уровень олицетворения, используемый при вызове WMI. (Stop-Computer использует WMI.) Значение по умолчанию 
    "Impersonate".

    Impersonate:  позволяет объектам использовать учетные данные вызывающей стороны.

	// Impersonate = 3,
    public ImpersonationLevel Impersonation { get; set; } = ImpersonationLevel.Impersonate;

Рассматривать работу в Job не будем. Метод Win32Shutdown определен:

uint32 Win32Shutdown (

[in] sint32 Flags,

[in] sint32 Reserved =

);

Если параметр -Force не указан, то используется флаг — 0x1,   иначе 0x5.

1 (0x1)

Shutdown — Shuts down the computer to a point where it is safe to turn off the power.

 

5 (0x5)

Forced Shutdown (1 + 4) — Shuts down the computer to a point where it is safe to turn off the power.

 




# Определяется флаг
	object[] flags = new object[] { 1, 0 };
            if (Force.IsPresent)
                flags[0] = 5;


# Вызов метода Win32Shutdown класса Win32_OperatingSystem с заданными параметрами
		
ConnectionOptions options = ComputerWMIHelper.GetConnectionOptions(DcomAuthentication, this.Impersonation, this.Credential);
    ManagementScope scope = new ManagementScope(ComputerWMIHelper.GetScopeString(computer, ComputerWMIHelper.WMI_Path_CIM), options);
    ObjectQuery query = new ObjectQuery("select * from " + ComputerWMIHelper.WMI_Class_OperatingSystem);
    using (_searcher = new ManagementObjectSearcher(scope, query, enumOptions))
    {
        foreach (ManagementObject obj in _searcher.Get())
         {
            using (obj)
             {
                object result = obj.InvokeMethod("Win32shutdown", flags);

 

Для просмотра internal properties класса ComputerWMIHelper:

PS > $class = [Reflection.Assembly]::Load(
		"Microsoft.PowerShell.Commands.Management").GetType(
			"Microsoft.PowerShell.Commands.ComputerWMIHelper")

PS > $class.GetFields([Reflection.BindingFlags]"NonPublic,Static").Name
NetBIOSNameMaxLength
WMI_Class_SystemRestore
WMI_Class_OperatingSystem
WMI_Class_Service
WMI_Class_ComputerSystem
WMI_Class_PingStatus
WMI_Path_CIM
WMI_Path_Default
ErrorCode_Interface
ErrorCode_Service
SE_SHUTDOWN_NAME
SE_REMOTE_SHUTDOWN_NAME
DcomProtocol
WsmanProtocol
CimUriPrefix
CimOperatingSystemNamespace
CimOperatingSystemShutdownMethod
CimQueryDialect
localhostStr

PS > $class.GetField(
	"WMI_Class_OperatingSystem",[Reflection.BindingFlags]"NonPublic,Static").GetValue($class)
Win32_OperatingSystem

PS > $class.GetFields([Reflection.BindingFlags]"NonPublic,Static").Foreach({
	[pscustomobject]@{
		Name = $_.Name
		Value = $class.GetField($_.Name,[Reflection.BindingFlags]"NonPublic,Static").GetValue($class)
	}
})

Name                                                                                Value
----                                                                                -----
NetBIOSNameMaxLength                                                                   15
WMI_Class_SystemRestore                                                     SystemRestore
WMI_Class_OperatingSystem                                           Win32_OperatingSystem
WMI_Class_Service                                                           Win32_Service
WMI_Class_ComputerSystem                                             Win32_ComputerSystem
WMI_Class_PingStatus                                                     Win32_PingStatus
WMI_Path_CIM                                                                  \root\cimv2
WMI_Path_Default                                                            \root\default
ErrorCode_Interface                                                                  1717
ErrorCode_Service                                                                    1056
SE_SHUTDOWN_NAME                                                      SeShutdownPrivilege
SE_REMOTE_SHUTDOWN_NAME                                         SeRemoteShutdownPrivilege
DcomProtocol                                                                         DCOM
WsmanProtocol                                                                       WSMan
CimUriPrefix                     http://schemas.microsoft.com/wbem/wsman/1/wmi/root/cimv2
CimOperatingSystemNamespace                                                    root/cimv2
CimOperatingSystemShutdownMethod                                            Win32shutdown
CimQueryDialect                                                                       WQL
localhostStr                                                                    localhost

 

III. Третий способ —  Windows Powershell Remoting / JEA

Отличное описание Windows PowerShell endpoints в серии статей — https://blogs.technet.microsoft.com/heyscriptingguy/2014/03/31/introduction-to-powershell-endpoints/

Перейдем сразу к реализации, т.к. вариантов много, рассмотрим один из них.

1. Включаем PowerShell Remoting

PS > Enable-PSSessionConfiguration -Force
WinRM has been updated to receive requests.
WinRM service type changed successfully.
WinRM service started.

WinRM has been updated for remote management.
WinRM firewall exception enabled.

2. Зарегистрируем endpoint с учетными данными администратора

New-PSSessionConfigurationFile –Path ShutDownEnd.pssc –SessionType RestrictedRemoteServer -LanguageMode NoLanguage -VisibleCmdlets Stop-Computer 
Register-PSSessionConfiguration -Name ShutDown -Path ShutDownEnd.pssc –ShowSecurityDescriptorUI -RunAsCredential Contoso\Administrator

3. Выключаем

$session = New-PSSession -ConfigurationName ShutDown -ComputerName W10-CL2.contoso.com
Invoke-Command -Session $session -ScriptBlock {Stop-Computer -Force}

 

 

Параметр ShowSecurityDescriptorUI вызывает окно где мы указываем права доступа, достаточно Execute(Invoke).

psc-30082016

Для автоматизации процесса, скачайте скрипт Add-PoShEndpointAccess.

Add-PoShEndpointAccess -SamAccountName "contoso\User1" -EndpointName ShutDown

 

И в качестве примера, приведу использование JEA (Just Enough Administration).

Just Enough Administration (JEA) — это технология безопасности, позволяющая делегировать администрирование в отношении всего, чем можно управлять через PowerShell. JEA позволяет сделать следующее:

  • Уменьшить число администраторов на компьютерах, используя виртуальные учетные записи, которые допускают выполнение привилегированных действий от имени обычных пользователей.
  • Ограничить доступные пользователям действия, указав, какие командлеты, функции и внешние команды могут выполнять пользователи.
  • Лучше понять, что делают ваши пользователи, используя детализированные записи с «подсматриванием» для команд, выполняемых пользователем за время сеанса.

Подробнее:

https://msdn.microsoft.com/en-us/powershell/jea/readme

https://github.com/PowerShell/JEA

# Fields in the role capability
$MaintenanceRoleCapabilityCreationParams = @{
    Author = 'Contoso Admin'
    CompanyName = 'Contoso'
    VisibleCmdlets = @{ Name = 'Stop-Computer' ; Parameters =  @{ Name = 'Force' } }
}

# Create the demo module, which will contain the maintenance Role Capability File
New-Item -Path "$env:ProgramFiles\WindowsPowerShell\Modules\Demo_Module" -ItemType Directory
New-ModuleManifest -Path "$env:ProgramFiles\WindowsPowerShell\Modules\Demo_Module\Demo_Module.psd1"
New-Item -Path "$env:ProgramFiles\WindowsPowerShell\Modules\Demo_Module\RoleCapabilities" -ItemType Directory

# Create the Role Capability file
New-PSRoleCapabilityFile -Path "$env:ProgramFiles\WindowsPowerShell\Modules\Demo_Module\RoleCapabilities\Maintenance.psrc" @MaintenanceRoleCapabilityCreationParams

# Determine domain
$domain = (Get-CimInstance -ClassName Win32_ComputerSystem).Domain

# Replace with your non-admin group name
$NonAdministrator = "$domain\User1"

# Specify the settings for this JEA endpoint
# Note: You will not be able to use a virtual account if you are using WMF 5.0 on Windows 7 or Windows Server 2008 R2
$JEAConfigParams = @{
    SessionType = 'RestrictedRemoteServer'
    RoleDefinitions = @{
        $NonAdministrator = @{ RoleCapabilities = 'Maintenance' }
    }
    TranscriptDirectory = "$env:ProgramData\JEAConfiguration\Transcripts"
}

# Set up a folder for the Session Configuration files
if (-not (Test-Path "$env:ProgramData\JEAConfiguration"))
{
    New-Item -Path "$env:ProgramData\JEAConfiguration" -ItemType Directory
}

# Specify the name of the JEA endpoint
$sessionName = 'JEA_ShutDown'

if (Get-PSSessionConfiguration -Name $sessionName -ErrorAction SilentlyContinue)
{
    Unregister-PSSessionConfiguration -Name $sessionName -ErrorAction Stop
}

New-PSSessionConfigurationFile -Path "$env:ProgramData\JEAConfiguration\JEADemo.pssc" @JEAConfigParams

# Register the session configuration
Register-PSSessionConfiguration -Name $sessionName -Path "$env:ProgramData\JEAConfiguration\JEADemo.pssc" -RunAsCredential Contoso\Administrator
}

$session = New-PSSession -ConfigurationName ShutDown -ComputerName W10-CL2.contoso.com
Invoke-Command -Session $session -ScriptBlock {Stop-Computer -Force}

Дополнительная информация:

1) Setting up a Remote WMI Connection  — https://msdn.microsoft.com/en-us/library/aa822854(v=vs.85).aspx

 

2) DSC Resource to manage WMI Namespace Security — https://github.com/PowerShell/WmiNamespaceSecurity/blob/master/WmiNamespaceSecurity.psm1

 

3) Privilege Constants — https://msdn.microsoft.com/en-us/library/windows/desktop/bb530716(v=vs.85).aspx

 

4) Force shutdown from a remote system — https://technet.microsoft.com/en-us/library/dn221951(v=ws.11).aspx

 

5) Access to WMI Namespaces — https://msdn.microsoft.com/en-us/library/aa822575(v=vs.85).aspx

 

6) Windows PowerShell endpoints — https://blogs.technet.microsoft.com/heyscriptingguy/2014/03/31/introduction-to-powershell-endpoints/

 

7) Just Enough Administration — https://msdn.microsoft.com/en-us/powershell/jea/readme

 

8) Constrained PowerShell endpoints – Visible cmdlets, session types, and language modes — https://4sysops.com/archives/constrained-powershell-endpoints-visible-cmdlets-session-types-and-language-modes/

 

9) Using Powershell and Reflection API to invoke methods from .NET Assemblies — https://blog.netspi.com/using-powershell-and-reflection-api-to-invoke-methods-from-net-assemblies/

FSRM предоставляет инструменты для базового активного мониторинга  и контроля имен файлов по заданному шаблону. Когда большую популярность набирают вирусы шифраторы, то FSRM будет хорошим добавлением к антивирусу.

Ресурс https://fsrm.experiant.ca/ предоставляет шаблоны для противодействия ransomware. Скрипты для автоматического добавления политик — https://github.com/m-dwyer/CryptoBlocker

Начиная с Windows Server 2012 и выше доступен модуль для работы с FSRM — https://technet.microsoft.com/en-us/library/jj900651(v=wps.630).aspx и новые WMI классы — https://msdn.microsoft.com/en-us/library/hh706679(v=vs.85).aspx. Поэтому простой перенос модуля с Windows Server 2012 на 2008 R2 не получится.

И задача сводится к простым действиям:

$url = "https://fsrm.experiant.ca/api/v1/combined"            
$pat = @((Invoke-WebRequest -Uri $url).content | convertfrom-json | % {$_.filters})            
Set-FsrmFileGroup -Name "RMS" -IncludePattern $list -IncludePattern $pat

В Windows Server 2008 R2 доступна утилита filescrn.exe для обновления file group, попробуем ей  воспользоваться.

PS > $tmpl = @((Invoke-WebRequest -Uri "https://fsrm.experiant.ca/api/v1/combined").Content | ConvertFrom-JSON | Foreach {$_.filters})            
PS > filescrn Filegroup modify /FileGroup:"RSM" /Members:"$($tmpl -join '|')"            
Параметр задан неверно.

Получаем ошибку. Опытным путем установим, сколько значений мы можем передать параметру Members. У меня получилось = 216

PS > ($tmpl.Count-1)..1 | Foreach {            
>>     $temp = $tmpl[0..$_] -join "|"            
>>     filescrn Filegroup Add /FileGroup:"RSM" /Members:"$temp" | Out-Null            
>>     if($?) {            
>>         $_            
>>         break            
>>     }            
>> }            
>>            
216

Какие у нас есть варианты обновления больше 216 элементов?

  • 1) Экспорт и последующий импорт xml-файла c более поздних систем начиная с Windows Server 2012 и выше

    2) Добавление данных в xml-файла с последующим импортом3) Создание нескольких групп в которых меньше 216 элементов

    4) Использовать FSRM API

  • 5) Вручную

Рассмотрим 4 вариант для обновления данных. В этом нам поможет Com класс FSRM.FsrmFileGroupManager — https://msdn.microsoft.com/en-us/library/bb625542(v=vs.85).aspx

Для преобразования \uFFFF воспользуемся простыми методами:

PS > $tmpl[120]            
Comment d\u00e9bloquer mes fichiers.txt            
            
PS > [char]0x00e9            
é            
            
PS > [regex]::Unescape("Comment d\u00e9bloquer mes fichiers.txt")             
Comment débloquer mes fichiers.txt


Function Update-FsrmFileGroup {            
             
 [CmdletBinding()]             
 param (            
  [Parameter(Mandatory=$True)]            
  # Название группы            
  [String]$GroupName             
 )            
             
 # Исключения для метода GetFileGroup            
 # https://msdn.microsoft.com/en-us/library/cc422727.aspx            
 $rvalue = @{            
  "0x80045301" = "`nThe specified file group could not be found."            
  "0x80045308" = "`nThe specified name is not valid."            
  "0x8004530D" = "`nThe content of the name parameter exceeds the maximum length of 4,000 characters."            
  "0x80070057" = "`nThe fileGroup parameter is NULL."            
 }            
 # Скачиваем обновленный шаблон            
 # Для замены \uFFFF в "Comment d\u00e9bloquer mes fichiers.txt"            
 # Используем метод [regex]::Unescape            
 # [regex]::Unescape("Comment d\u00e9bloquer mes fichiers.txt")            
 # Comment débloquer mes fichiers.txt	            
 $url = "https://fsrm.experiant.ca/api/v1/combined"            
 $tmpl = (New-Object Net.WebClient).DownloadString($url).split(",[") -notmatch ":" | Foreach {            
  [regex]::Unescape(($_.Trim("}").Trim("]").Trim('"')))            
 }            
             
 # Создаем ComObject - 8f1363f6-656f-4496-9226-13aecbd7718f            
 #	$t = [Type]::GetTypeFromCLSID("8f1363f6-656f-4496-9226-13aecbd7718f")            
 #	$obj = [Activator]::CreateInstance($t)            
 #            
 $fsrm = New-Object -ComObject FSRM.FsrmFileGroupManager            
             
 try {            
  $fgroup = $fsrm.GetFileGroup($GroupName)            
 }            
 catch {            
  $ex = $_.Exception.InnerException.InnerException.Message.Split(":")[1].Trim()            
  Write-Error -Message "$($rvalue[$ex])" -Category InvalidData            
 }            
             
 if($fgroup) {            
  # Получаем список текущих элементов в группе            
  $members = $fgroup.Members            
  Write-Verbose "Количество элментов в группе: $($members.count)"            
              
  if($tmpl) {            
   Write-Verbose "Удаляем все элементы из группы"            
   $count = $members.count            
   for($i = 1 ; $i -le $count; $i++) {            
    $members.Remove(1)            
   }            
               
   Write-Verbose "Добавляем элементы $($tmpl.Count) в группу"            
   for($i = 0 ; $i -lt $tmpl.count; $i++) {            
    $members.Add($tmpl[$i])            
   }            
               
   Write-Verbose "Сохраняем изменения"            
   $fgroup.Members = $members            
   $fgroup.Commit()            
  }            
 }            
}

Проверим, правильность обновления.

PS > Update-FsrmFileGroup -GroupName RSM -Verbose            
ПОДРОБНО: Количество элментов в группе: 120            
ПОДРОБНО: Удаляем все элементы из группы            
ПОДРОБНО: Добавляем элементы 441 в группу            
ПОДРОБНО: Сохраняем изменения            
            
PS > (New-Object -ComObject FSRM.FsrmFileGroupManager).GetFileGroup($GroupName).Members.Count            
441

Обновление прошло успешно.

Для мониторинга подозрительной активности и определения проблемных пользователей, и проверки правильно ли работают настройки, нам поможет событие 8215 .

Description:

The system detected that user <user name> attempted to save <file> on <directory>. This file matches the «<file group name>» file group which is not permitted on the system.

PS > Get-WinEvent -FilterHashtable @{LogName="Application";ID=8215} -MaxEvents 1 -ComputerName srvfile            
            
TimeCreated  : 16.08.2016 10:23:11            
ProviderName : SRMSVC            
Id           : 8215            
Message      : Пользователь Contoso\sidorov попытался сохранить файл C:\Share\1123.777 в папке C:\ на сервере srvfile.             
    Этот файл принадлежит к группе файлов "RSM", которая не разрешена на сервере.

При просмотре событий журнала в Event Viewer, вкладка General содержит тело события в удобном для понимая виде и в зависимости от локализации ОС,по  возможности, отображает строки в заданной локализации.

 

evntvwr

В нашем примере событие  с номером 4663 содержит поле Accesses , которое имеет значение в удобном для нас виде — DELETE.

При переключении на вкладку Details и выбором формата XML, получаем значение формата %%N( N – число). Что является неудобным для понимания.

evtxml

В нашем примере AccessList имеет значение %%1537.

 

Теперь остается понять, откуда берется значение %%1537. Для этого обратимся к документации на портале Technet.

В разделе Log-name описан параметр ParameterMessageFile , который и указываем где хранятся описание.

HKLM\SYSTEM\CurrentControlSet\Services\EventLog\Log-name\Source-name

 

 

Data type

Range

Default value

REG_EXPAND_SZ

Path and file name [; Path and file name]

(None)

 

Description

Stores the location of the file that contains values for variables in the event description. This file is optional.

The descriptive text in the event message file can contain variables. The variables indicated by %n are resolved by substituting data from the event record. The variables indicated by %%n are resolved by substituting text from a parameter message file (stored in the ParameterMessageFile entry). The Event Log service locates the correct values and replaces the variables before displaying the text in the Description field in Event Viewer.

 

reg

 

Получим значения ключа реестра ParameterMessageFile :

$key = "HKLM:\SYSTEM\CurrentControlSet\Services\EventLog"            
$property = "ParameterMessageFile"            
$hp = @{n="Path";e={            
 $_.PsPath.Substring($_.PsPath.IndexOf("EventLog\") + 9)}            
},$property            
Get-ChildItem -Path $key -Recurse |             
 Get-ItemProperty -Name $property -ErrorAction SilentlyContinue |             
  Select-Object -Property $hp

 

Примерный вывод:

messagefile

 

С помощью редактора ресурсов, например Resource Hacker, посмотрим, что содержит C:\Windows\System32\MsObjs.dll ( System\DS используется для события 4663).

 

rh

 

0x601, «DELETE\r\n» (где HEX –> DEC 0x601 –> 1537)

Выгрузим значения из MESSAGETABLE.  Спасибо  Hans Passant за пример кода  Marshaling a message table resource.

 

param(            
    # Путь к библиотеки из ключа ParameterMessageFile            
 $DLLPath = (Join-Path -Path $env:SystemRoot -ChildPath \System32\msobjs.dll)            
)            
            
            
# Определяем сигнатуры            
#            
# 1. LoadLibrary             
# https://msdn.microsoft.com/en-us/library/windows/desktop/ms684175.aspx            
# 2. https://msdn.microsoft.com/en-us/library/windows/desktop/ms683152.aspx            
#            
# Процессы вызывают функцию LoadLibrary  для явного связывания             
# с библиотекой DLL.При успешном завершении функция сопоставляет указанную библиотеку DLL            
# с адресным пространством вызывающего процесса и возвращает дескриптор библиотеки DLL            
#            
# Если у библиотеки DLL есть функция точки входа, операционная система вызывает эту            
# функцию в контексте потока, в котором вызвана функция LoadLibrary.Функция точки входа             
# не вызывается, если библиотека DLL уже присоединена к процессу в результате             
# предыдущего вызова функции LoadLibrary, за которым не последовал вызов функции FreeLibrary.            
#            
# 3. FindResource            
# https://msdn.microsoft.com/en-us/library/windows/desktop/ms648042.aspx            
#            
# Функция FindResource выясняет место ресурса с заданным типом и именем в указанном модуле.             
#            
#            
# 4. LockResource            
# https://msdn.microsoft.com/en-us/library/windows/desktop/ms648047.aspx            
#            
# Функция LockResource блокирует указанный ресурс в памяти.            
#            
# 5. LoadResource            
# https://msdn.microsoft.com/en-us/library/windows/desktop/ms648046.aspx            
#             
# Функция читает ресурс из модуля.            
#            
$signature = @"
namespace Win32 {
	using System;
	using System.Runtime.InteropServices;
	
	[StructLayout(LayoutKind.Sequential)]
	public struct MESSAGE_RESOURCE_BLOCK {
		public int LowId;
		public int HighId;
		public int OffsetToEntries;
	}
	
	public class API {
		[DllImport("kernel32.dll", SetLastError = true)]
			public static extern IntPtr LoadLibrary(string fileName);
		[DllImport("kernel32.dll", SetLastError = true)]
			public static extern IntPtr FindResource(IntPtr hModule, int lpID, int lpType);
		[DllImport("kernel32.dll")]
			public static extern IntPtr LockResource(IntPtr hResData);
		[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
			public static extern IntPtr LoadResource(IntPtr hModule,IntPtr hResInfo);
		[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
			public static extern bool FreeLibrary(IntPtr hModule);
	}
}
"@            
            
# Загружаем сигнатуры             
Add-Type $signature            
            
# https://msdn.microsoft.com/en-us/library/windows/desktop/ms648009.aspx            
# RT_MESSAGETABLE MAKEINTRESOURCE(11) - Message-table entry.            
$RT_MESSAGETABLE = 11            
$msg = New-Object -TypeName 'System.Collections.Generic.Dictionary[int,string]'            
            
# Получаем указатель на библиотеку            
$hModule = [Win32.API]::LoadLibrary($DLLPath)            
# Получаем расположения ресурса            
$hTable = [Win32.API]::FindResource($hModule, 1, $RT_MESSAGETABLE)            
            
if($hTable) {            
    # Указатель на ресурс            
 $rTable = [Win32.API]::LoadResource($hModule, $hTable)            
 $mTable = [Win32.API]::LockResource($rTable)            
             
    # Количество полей в ресурсе            
 $numberOfBlocks = [System.Runtime.InteropServices.Marshal]::ReadInt32($mTable)            
 $blockPtr = [IntPtr]::Add($mTable, 4)            
    # Определяем размер структуры Win32.MESSAGE_RESOURCE_BLOCK            
 $strMRB = New-Object -TypeName Win32.MESSAGE_RESOURCE_BLOCK            
 $blockSize = [System.Runtime.InteropServices.Marshal]::SizeOf($strMRB)            
             
    # Получаем значения полей из ресурса            
 for($i=0 ; $i -lt $numberOfBlocks ; $i++) {            
  $block = [System.Runtime.InteropServices.Marshal]::PtrToStructure($blockPtr,
[System.Type][Win32.MESSAGE_RESOURCE_BLOCK])            
  $entryPtr = [IntPtr]::Add($mTable, $block.OffsetToEntries)            
            
        for ($id = $block.LowId; $id -le $block.HighId; $id++) {            
            $length = [System.Runtime.InteropServices.Marshal]::ReadInt16($entryPtr)            
            $flags = [System.Runtime.InteropServices.Marshal]::ReadInt16($entryPtr, 2)            
            $textPtr = [IntPtr]::Add($entryPtr, 4)            
            $text = "Bad flags??";            
            if ($flags -eq 0) {            
                $text = [System.Runtime.InteropServices.Marshal]::PtrToStringAnsi($textPtr)            
            }            
            elseif ($flags -eq 1) {            
                $text = [System.Runtime.InteropServices.Marshal]::PtrToStringUni($textPtr)            
            }            
            $text = $text.Replace("`r`n", "").Trim()            
   $msg.Add($id,$text)            
            
            $entryPtr = [IntPtr]::Add($entryPtr, $length)            
        }            
        $blockPtr = [IntPtr]::Add($blockPtr, $blockSize)            
 }            
}            
            
# Освобождаем ресурсы            
[Win32.API]::FreeLibrary($hModule) | Out-Null

 

При вызове метода [Runtime.InteropServices.Marshal]::PtrToStructure с установленным Microsoft .NET Framework 4.5.1 или the Microsoft .NET Framework 4.5.2 получаем исключение:

The specified structure must be blittable or have layout information.

В статье указан вариант решения, надо явно привести к типу System.Type

Exceptions in Windows PowerShell, other dynamic languages, and dynamically executed C# code when code that targets the .NET Framework calls some methods

$size = [System.Runtime.InteropServices.Marshal]::SizeOf([System.Type] $type)$obj = [System.Runtime.InteropServices.Marshal]::PtrToStructure($ptr, [System.Type] $type)

 

Пример выполнения:

 

image

 

Касаемо события 4663, можно легко обойтись без получение данных из ресурса. Поле Message содержит уже преобразованное значение, Access Mask легко привести к типу Security.AccessControl.FileSystemRights.

evtposh

PS >

$evt.Message.Split(«`n») -match «Access(es|\sMask)» -replace «:\s+»,»=» |

Out-String | ConvertFrom-StringData

Name                           Value

—-                           ——

Access Mask                    0x4

Accesses                       AppendData (or AddSubdirectory or CreatePipeInstance)

PS >

[System.Security.AccessControl.FileSystemRights]$evt.Properties[9].Value

AppendData

 

В первом скрипте выгружали все значения ресурса. С помощью функции  FormatMessage можно получить  значения по коду.

Function Get-FormatMessage {            
 [OutputType('System.String')]            
 [CmdletBinding()]            
  param(            
   [Parameter(Mandatory = $true)]            
   [int]$MessageCode,            
   [string]$DllPath = (Join-Path -Path $env:SystemRoot -ChildPath \System32\msobjs.dll)            
  )            
            
$signature = @"
	namespace Win32 {
		using System;
		using System.Runtime.InteropServices;
		
		
		public class API {
		
			[DllImport("kernel32.dll", SetLastError=true)]
				public static extern uint FormatMessage(
					uint dwFlags,
					IntPtr lpSource,
					int dwMessageId,
					uint dwLanguageId,
					ref IntPtr lpBuffer,
					uint nSize,
					IntPtr Arguments
				);
			[DllImport("kernel32.dll", SetLastError = true)]
				public static extern IntPtr LoadLibrary(string fileName);
			[DllImport("kernel32.dll", SetLastError = true)]
				public static extern IntPtr LoadResource(IntPtr hModule,IntPtr hResInfo);
			[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
				public static extern bool FreeLibrary(IntPtr hModule);
			[DllImport("kernel32.dll", SetLastError=true, CharSet=CharSet.Auto)]
				public static extern IntPtr LocalFree(IntPtr hMem);
		}
	}
"@            
             
 try {Add-Type $signature}            
    catch {Write-Warning $_.Exception.Message; return}            
            
 $FORMAT_MESSAGE_ALLOCATE_BUFFER = 0x00000100            
 $FORMAT_MESSAGE_FROM_HMODULE  = 0x00000800            
 $FORMAT_MESSAGE_IGNORE_INSERTS = 0x00000200            
            
 $hModule = [Win32.API]::LoadLibrary($DllPath)            
             
 if($hModule) {            
  $dwFlags = $FORMAT_MESSAGE_ALLOCATE_BUFFER -bor $FORMAT_MESSAGE_FROM_HMODULE -bor $FORMAT_MESSAGE_IGNORE_INSERTS            
  $lpBuffer = [IntPtr]::Zero            
            
  if ([Win32.API]::FormatMessage($dwFlags,$hModule,$MessageCode,0,[ref]$lpBuffer,0,[IntPtr]::Zero)) {            
   [Runtime.InteropServices.Marshal]::PtrToStringAnsi($lpBuffer).Trim()            
  }            
  else {            
    Write-Output "No messages are assoicated with code : $MessageCode ."            
  }            
 }            
            
 [Win32.API]::FreeLibrary($hModule) | Out-Null            
 [Win32.API]::LocalFree($lpBuffer) | Out-Null            
}

Пример выполнения:

PS > Get-FormatMessage -MessageCode 1537

DELETE

PS > Get-FormatMessage -MessageCode 1539

WRITE_DAC

PS > Get-FormatMessage -MessageCode 4418

AppendData (or AddSubdirectory or CreatePipeInstance)

Задача: Сформировать запрос в котором больше 25 условий

Для формирования запросов мы можем использовать 3 параметра:

  • -FilterXPath <String>
  • -FilterXml <XmlDocument>
  • -FilterHashtable <Hashtable[]>

    Подробную справку по параметрам у Get-WinEventhttps://technet.microsoft.com/en-us/library/hh849682.aspx .

    Предположим, что нам надо получить EventID с 4620 по 4660.

    1.  -FilterHashtable <Hashtable[]>

    Ограничимся двумя параметрами — LogName=<String[]> и — ID=<Int32[]>.

    image

    Получаем ошибку. Опытным путем проверим, сколько максимально возможно  = 23 условия.

    image

    Видим, что если больше 23 условий, то получаем ошибку.

    2. -FilterXPath <String>

    image

    Получаем ошибку. Опытным путем проверим, сколько максимально возможно  = 23 условия.

    image

     

    !!! Название полей регистрозависим EventId (правильный вариант EventID) – выдаст ошибку.

    FilterHashtable и FilterXpath — не поддерживают выше 23 выражений в одном запросе, как и несколько запросов разом.

     

    3. -FilterXml <XmlDocument>

    image

    Аналогичное ограничение для одной записи в 23 условия.

     

    Теперь обратимся к официальной документации и поищем ответ там.

    С этим нам поможет полезная статья Consuming Eventshttps://msdn.microsoft.com/en-us/library/windows/desktop/dd996910%28v=vs.85%29.aspx .

    You can use the XPath expressions directly when calling the EvtQuery or EvtSubscribe functions or you can use a structured XML query that contains the XPath expression. For simple queries that query events from a single source, using an XPath expression is fine. If the XPath expression is a compound expression that contains more than 20 expressions or you are querying for events from multiple sources, then you must use a structured XML query. For details on the elements of a structured XML query, see Query Schema.

    Если используется более 20 условий, используйте формат – XML. Так же есть указание на WinApi функцию EvtQuery , которая и используется в конечном итоге для получения событий.

    Обратившись к справке по EvtQuery, обнаружим интересный параметр, который принимает функция:

    Query [in]
        A query that specifies the types of events that you want to retrieve. You can specify an XPath 1.0 query or structured XML query. If your XPath contains more than 20 expressions, use a structured XML query. To receive all events, set this parameter to NULL or "*".

     

    Как устроен командлет Get-WinEvent?

    Данный командлет основан на использовании  .Net класса System.Diagnostics.Eventing.Reader.EventLogReader. Компания Microsoft открыла часть платформы .Net и мы можем посмотреть устройство этого класса.

    Перейдя по ссылке можно посмотреть исходный код, где мы находим метод, который называется, как и WinApi функция – EvtQuery:

    [System.Security.SecurityCritical]
        public static EventLogHandle EvtQuery(
            EventLogHandle session,
            string path,
            string query,
            int flags) {
        if (s_platformNotSupported)
            throw new System.PlatformNotSupportedException();
        EventLogHandle handle = UnsafeNativeMethods.EvtQuery(session, path, query, flags);
        int win32Error = Marshal.GetLastWin32Error();
        if (handle.IsInvalid)
            EventLogException.Throw(win32Error);
        return handle;
    }

    Определение функции происходит в данном классе UnsafeNativeMethods.cs:

    [DllImport(WEVTAPI, CallingConvention = CallingConvention.Winapi, SetLastError = true)]
       [SecurityCritical]
        internal static extern EventLogHandle EvtQuery(
            EventLogHandle session,
            [MarshalAs(UnmanagedType.LPWStr)]string path,
            [MarshalAs(UnmanagedType.LPWStr)]string query,
        int flags);

     

    Теперь осталось дело за малым сформировать XML запрос, который содержит в одном подзапросе меньше 23 условий. Пример такого запроса можно увидеть в EventLog –> Administrative Events –> Properties –> Edit Filter.

    <QueryList>
      <Query Id="0" Path="Application">
        <Select Path="Application">*[System[(Level=1  or Level=2 or Level=3)]]</Select>
        <Select Path="Security">*[System[(Level=1  or Level=2 or Level=3)]]</Select>
        <Select Path="System">*[System[(Level=1  or Level=2 or Level=3)]]</Select>
        <Select Path="HardwareEvents">*[System[(Level=1  or Level=2 or Level=3)]]</Select>
        <Select Path="Internet Explorer">*[System[(Level=1  or Level=2 or Level=3)]]</Select>

    image

    Запрос отработал, как требуется. Отсюда вывод, если запрос содержит много условий, несколько источников – выбор FilterXml.

  • FilterHashTable/FilterXPath

    FilterXML

    Больше 23 условий
    bad

    good
    Несколько источников bad
    good

    Q&A:

    1. Какой инструмент использовать для формирования XPath/XML запросов?

    https://blogs.msdn.microsoft.com/powershell/2011/04/14/using-get-winevent-filterxml-to-process-windows-events/

    Очень удобный инструмент для тестирования и формирования запросов, встроенное средство Event Viewer ( запуск — eventvwr.msc . Show-Eventlog).

    image

     

    Вкладка XML:

    image

     

    2. В Event Viewer можно в фильтре указать диапазон?

    Да, можно. Посмотрим на запрос:

    <QueryList>
      <Query Id="0" Path="Security">
        <Select Path="Security">*[System[(Level=1 ) and ( (EventID &gt;= 4620 and EventID &lt;= 4660) ) and TimeCreated[timediff(@SystemTime) &lt;= 3600000]]]</Select>
      </Query>
    </QueryList>

     

    EventID >= 4620 and EventID <= 4660 – Да, запрос отработает, но работает он гораздо медленнее. Поэтому, если время для Вас не критично, то используйте вместо громоздких выражений.

    3. При использвании Get-WiEvent в ОC отличных от английского языка, поле Message у событий пустое?

    Да, такое поведения иногда присутствует не только у данного командлета. Для этого нам поможет простая функция Using-Culture .

    Using-Culture en-us {
        Get-WinEvent -FilterXml $xml -MaxEvent 1
    }

    4. Ограничения XPath v1?

    XPath 1.0 limitations

    Windows Event Log supports a subset of XPath 1.0. The primary restriction is that only XML elements that represent events can be selected by an event selector. An XPath query that does not select an event is not valid. All valid selector paths start with * or "Event". All location paths operate on the event nodes and are composed of a series of steps. Each step is a structure of three parts: the axis, node test, and predicate. For more information about these parts and about XPath 1.0, see XML Path Language (XPath). Windows Event Log places the following restrictions on the expression:

    • Axis: Only the Child (default) and Attribute (and its shorthand "@") axis are supported.
    • Node Tests: Only node names and NCName tests are supported. The "*" character, which selects any character, is supported.
    • Predicates: Any valid XPath expression is acceptable if the location paths conform to the following restrictions:
      • Standard operators OR, AND, =, !=, <=, <, >=, >, and parentheses are supported.
      • Generating a string value for a node name is not supported.
      • Evaluation in reverse order is not supported.
      • Node sets are not supported.
      • Namespace scoping is not supported.
      • Namespace, processing, and comment nodes are not supported.
      • Context size is not supported.
      • Variable bindings are not supported.
      • The position function, and its shorthand array reference, is supported (on leaf nodes only).
      • The Band function is supported. The function performs a bitwise AND for two integer number arguments. If the result of the bitwise AND is nonzero, the function evaluates to true; otherwise, the function evaluates to false.
      • The timediff function is supported. The function computes the difference between the second argument and the first argument. One of the arguments must be a literal number. The arguments must use FILETIME representation. The result is the number of milliseconds between the two times. The result is positive if the second argument represents a later time; otherwise, it is negative. When the second argument is not provided, the current system time is used.

    5. Можно ли читать напрямую из файла?

    Если пользователь обладает правами на чтение файл из папки %SystemRoot%\System32\Winevt\Logs\*.evtx , но не рекомендуется.

    Get-WinEvent -Path C:\Windows\System32\Winevt\Logs\Application.evtx

    6. В каких единицах указывается время?

    Время в запросах используется в миллисекундах. Для получения можно использовать New-TimeSpan.

    image

    7. Почему в XML запросе требуется экранирование символов в отличие от XPath?

    В XPath запросах используются =, !=, <=, <, >=, >. Но в XML надо следовать некоторым правилам.

    NOTE:  When an XPath expression occurs in an XML document, any < and <= operators must be quoted according to XML 1.0 rules by using, for example, &lt; and &lt;=. In the following example the value of the test attribute is an XPath expression: <xsl:if test="@value &lt; 10">…</xsl:if>

    Для преобразования, можно использовать несколько методов:

    PS > [System.Web.HttpUtility]::HtmlEncode("<=")
    &lt;=

    < -> &lt;
    > -> &gt;
    " -> &quot;
    ‘ -> &apos;
    & -> &amp;

    PS > [System.Security.SecurityElement]::Escape("<=")
    &lt;=

    URL: https://FQDN/mapi/emsmdb/?MailboxId=ID

    ID: Можно узнать используя Test E-mail AutoConfiguration или использовать  UPN

    owa

    $username ="mail@contoso.com"
    $password = "1q2w3e4r5t"
    $url = "https://mail.contoso.com/mapi/emsmdb/?MailboxId=$username"
    $auth=$username+":"+$password
    
    $Encoded = [System.Text.Encoding]::UTF8.GetBytes($auth)
    $EncodedPassword = [System.Convert]::ToBase64String($Encoded)
    
    $headers = @{
        "User-Agent" = "Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0"
        "Accept" = "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
        "Accept-Language" = "ru-RU,ru;q=0.8,en-US;q=0.5,en;q=0.3"
        "Accept-Encoding" = "gzip, deflate"
        "Authorization" = "Basic $EncodedPassword"
    }
    
    $data = Invoke-WebRequest -Uri $url -Headers $headers -SessionVariable o 
    $body = $data.ParsedHtml.body.outerText.split("`r`n")
    $udata = $body -match ":" -replace ": "," = " | Out-String | ConvertFrom-StringData

    Вывод:

    PS >  $udata | Format-Table -Auto
    
    Name           Value
    ----           -----
    SID            S-1-5-21-1055359317-2347530276
    Authentication OrgId
    Mailbox        dbxpr03mb528.eurprd03.prod.outlook.com
    UPN            test@mus.onmicrosoft.com
    Cafe           am2pr09ca0004.eurprd09.prod.outlook.com
    Created        3/23/2016 4:35:42 PM
    Vdir Path      /mapi/emsmdb/
    User           test@mus.onmicrosoft.com
    Organization   EURPR03A002.prod.outlook.com/Microsoft Ex
    Version        15.1.443.14

    Mailbox – Сервер на котором расположен почтовый ящик

    Cafe(Client Access  front end server) – Сервер через которой осуществляется подключение