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

Archive for the ‘PowerShell’ Category

В наборе  Windows Server 2003 Resource Kit Tools , есть утилита tsctst.exe — Terminal Server Client License Dump , для просмотра клиентских лицензий сервера терминалов. Основное ограничение на системах x64, что tsctst.exe –  читает раздел реестра — HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\MSLicensing\Store , где отсутствует данная информация.

Если хочется воспользоваться данной утилитой, то можно скопировать/импортировать данные из HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\MSLicensing\Store и перенести в раздел :  HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSLicensing\Store и утилита вновь будет работать.

Нас будет интересовать ключ реестра LicenseXXX\ClientLicense(REG_BINARY), где содержится информация о сертификатах.

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

1) System.Security.Cryptography.Pkcs.SignedCms

Add-Type -AssemblyName System.Security            
$license = Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\MSLicensing\Store\LICENSE000" -Name "ClientLicense"            
$cms = New-Object System.Security.Cryptography.Pkcs.SignedCms            
$cms.Decode($license.ClientLicense)            
$cms.Certificates             
            
PS > $cms.Certificates

Thumbprint                                Subject
----------                                -------
785F491C579B6421FE155EFB6E6482F1E44FC266  L=CON + CN=SBTR
B50C54A586ADC8FA249639B7A06E3CE4B2C24070  L=SYSTEM + CN=CL-01 + SERIALNUMBER="1BcKee1dW/BhjacbI9tTm0hPCQ

2) System.Security.Cryptography.X509Certificates.X509Certificate2Collection

$certs = [System.Security.Cryptography.X509Certificates.X509Certificate2Collection]::new()            
$certs.Import($license.ClientLicense)            
$certs            
            
PS > $cms.Certificates

Thumbprint                                Subject
----------                                -------
785F491C579B6421FE155EFB6E6482F1E44FC266  L=CON + CN=SBTR
B50C54A586ADC8FA249639B7A06E3CE4B2C24070  L=SYSTEM + CN=CL-01 + SERIALNUMBER="1BcKee1dW/BhjacbI9tTm0hPCQ

Можно сохранить данные в файл и посмотреть, через оснастку — certmgr.msc:

$license.ClientLicense | Set-Content certs.p7b -Encoding Byte

Утилита tsctst.exe читает следующие OID:

Microsoft Hydra……………………………1.3.6.1.4.1.311.18

     License Info root

        szOID_PKIX_LICENSE_INFO                 1.3.6.1.4.1.311.18.1

     Manufacturer value

        szOID_PKIX_MANUFACTURER                 1.3.6.1.4.1.311.18.2

     Manufacturer Specfic Data

        szOID_PKIX_MANUFACTURER_MS_SPECIFIC     1.3.6.1.4.1.311.18.3

     OID for Certificate Version Stamp

        szOID_PKIX_HYDRA_CERT_VERSION           1.3.6.1.4.1.311.18.4

     OID for License Server to identify licensed product.

        szOID_PKIX_LICENSED_PRODUCT_INFO        1.3.6.1.4.1.311.18.5

     OID for License Server specific info.

        szOID_PKIX_MS_LICENSE_SERVER_INFO       1.3.6.1.4.1.311.18.6

     Extension OID reserved for product policy module — only one is allowed.

        szOID_PKIS_PRODUCT_SPECIFIC_OID         1.3.6.1.4.1.311.18.7

        szOID_PKIS_TLSERVER_SPK_OID             1.3.6.1.4.1.311.18.8

Вывод утилиты:

PS > tsctst.exe /A

*** License # 1 ***

TS Certificate Version — 0x00050001

Licensed Product

        HWID — 0x00000002, 0x7d4eb2c0, 0x37ffa910, 0x75eb542b, 0x87707af6

        Client Platform ID — 0x000000ff

        Company Name — Microsoft Corporation

Issuer — SBTR

Scope — COM

Issued to machine — CL-01

Issued to user — SYSTEM

        TS Locale ID — 0x00000419

        License ID — A02-5.02-S

Licensed Product Version 0005.0002, Flag 0x80d48000

Temporary       RTM

Valid from — 1d30f66 8ff2a100 Mon Aug 07 13:18:50 2017

Expires on — 1d3561f 79202100 Sun Nov 05 13:18:50 2017

Поля:

Issuer,Scope,Issued to machine,Issued to user -  Subject,Issuer

TS Certificate Version — 1.3.6.1.4.1.311.18.4(szOID_PKIX_HYDRA_CERT_VERSION)

Licensed Product — 1.3.6.1.4.1.311.18.2(szOID_PKIX_MANUFACTURER),

                 1.3.6.1.4.1.311.18.6 (szOID_PKIX_MS_LICENSE_SERVER_INFO)

TS Locale ID  -   1.3.6.1.4.1.311.18.5( szOID_PKIX_LICENSED_PRODUCT_INFO)

License ID — 1.3.6.1.4.1.311.18.5( szOID_PKIX_LICENSED_PRODUCT_INFO)

Licensed Product Version — 1.3.6.1.4.1.311.18.5( szOID_PKIX_LICENSED_PRODUCT_INFO)

Valid from – NotBefore

Expires on – NotAfter

Function Get-TSCtst {            
 Param(            
  $Path = "HKLM:\SOFTWARE\Microsoft\MSLicensing\Store"            
 )            
             
 $Certs = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2Collection            
 $License = Get-ChildItem $Path | Get-ItemProperty -Name ClientLicense             
             
 $LicenseType = @{            
  "A02-5.00-EX" = 'Windows 2000 TS CAL from the "Built-in" pool.'             
  "A02-5.00-S"  = "Windows 2000 TS temporary or permanent CAL."             
  "A02-5.02-S"  = "Windows server 2003 TS temporary or permanent CAL."             
  "A02-6.00-S"  = "Windows server 2008 TS/2008 R2 RDS temporary or permanent CAL."             
 }            
             
 if(-not $License)             
 {            
  "Can't decode license"            
  break            
 }            
             
 foreach($l in $License)             
 {            
  $Certs.Import($l.ClientLicense)            
 }            
             
            
            
 $Certs = $Certs.Where{$_.Subject -match "SERIALNUMBER"}            
 $Number = 1            
             
 foreach($Cert in $Certs)            
 {            
  #Info            
  [byte[]]$info = $Cert.Extensions | Where{$_.Oid.Value -eq '1.3.6.1.4.1.311.18.5'} | Foreach {$_.RawData}             
  #TSVer            
  [byte[]]$btsver = $Cert.Extensions | Where{$_.Oid.Value -eq '1.3.6.1.4.1.311.18.4'} | Foreach {$_.RawData}            
  [Array]::Reverse($btsver)            
  $TSVer = "0x$([BitConverter]::ToString($btsver).replace('-',''))"            
  #Compnay            
  $bcompany = $Cert.Extensions | Where {$_.Oid.Value -eq '1.3.6.1.4.1.311.18.2'} | Foreach {$_.RawData}            
  $Company = [Text.Encoding]::Unicode.GetString($bcompany)            
  #Product            
  $CId = "0x$([BitConverter]::ToString($info[5..8]).Replace('-','').ToLower())"            
  #Issuer,Scope            
  $Scope,$Issuer = $Cert.Issuer -split "\s\+\s" | Foreach {$_.split("=")[1]}            
  #User,Machine Issuer            
  $UIssuer,$MIssuer = $Cert.Subject -split "\s\+\s" -match "^(L|CN)" | Foreach {$_.split("=")[1]}            
  #Locale            
  $bid = $info[12..13]            
  [Array]::Reverse($bid)            
  $TSId = "0x$([BitConverter]::ToString($bid).Replace('-','').PadLeft(8,'0'))"            
  #License            
  [string]$LicStr = [Text.Encoding]::Unicode.GetString($info) -split "\0" -match "A02-"            
  #License Type            
  $LicType = $LicenseType[$LicStr]            
  #Licensed Product Version            
  $LPV = "{0}.{1}, Flag 0x{2}" -f [BitConverter]::ToString($info,58,1).PadLeft(4,'0'),            
   [BitConverter]::ToString($info,60,1).PadLeft(4,'0').ToLower(),            
   [BitConverter]::ToString($info,63,4).Replace('-','').ToLower()            
  #HWID            
  $RegHWID    = "HKLM:\SOFTWARE\Microsoft\MSLicensing\HardwareID"            
  $ClientHWID = (Get-ItemProperty $RegHWID).ClientHWID            
  $HexClientHWID = for($i=0;$i -le $ClientHWID.Count -1 ; $i+=4) {            
   $temp = $ClientHWID[$i..($i+3)]            
   [Array]::Reverse($temp)            
   "0x$([BitConverter]::ToString($temp).Replace('-','').ToLower())"            
  }            
  $HexClientHWID = $HexClientHWID -join ', '            
              
  [PSCustomObject]@{            
   "ID"        = $Number            
   "TS Certificate Version"   = $TSVer            
   "HWID"         = $HexClientHWID            
   "Client Platform ID"       = $CId            
   "Company Name"             = $Company            
   "Issuer"        = $Issuer            
   "Scope"         = $Scope            
   "Issued to machine"     = $MIssuer            
   "Issued to user"      = $UIssuer            
   "TS Locale ID"       = $TSId            
   "License ID"       = $LicStr            
   "License Type"      = $LicType            
   "Licensed Product Version" = $LPV            
   "Valid from"       = $Cert.NotBefore            
   "Expires on"       = $Cert.NotAfter            
  }            
              
  $Number++            
 }            
}            
            

ID                       : 1
TS Certificate Version   : 0x00050001
HWID                     : 0x00000002, 0x7d4eb2c0, 0x37ffa910, 0x75eb542b, 0x87707af6
Client Platform ID       : 0x000000ff
Company Name             : Microsoft Corporation
Issuer                   : SBTR
Scope                    : CON
Issued to machine        : CL-01
Issued to user           : SYSTEM
TS Locale ID             : 0x00000419
License ID               : A02-5.02-S
License Type             : Windows server 2003 TS temporary or permanent CAL.
Licensed Product Version : 0005.0002, Flag 0x80d48000
Valid from               : 8/7/2017 1:18:50 PM
Expires on               : 11/5/2017 1:18:50 PM

PS. Скрипт Get-TSCtst.ps1 .

Реклама

Read Full Post »

Для симуляции различных ошибок  проверки сертификатов, существует прекрасный сайт — https://badssl.com/ , который предоставляет отличный набор тестов. В PowerShell 6.0 у командлетов Invoke-WebRequest/Invoke-RestMethod появился параметр –SkipCertificateCheck . Версия PowerShell v2.0  — Windows PowerShell 2.0 Deprecation. В PowerShell v3-5.1 — отсутствуют параметры для игнорирования ошибок связанных с сертификатами, поэтому воспользуемся сторонними методами.

Быстрый поиск в Google по теме игнорирование ошибок связанный с сертификатами дает большое количество различный способов.

Все примеры для PowerShell v5.1 и https://expired.badssl.com/

1 – 2 – не работают

3 – работает частично

4 – 5 – работают

 

Первоначальная ошибка:

er_06092017

 

 

1. [Net.ServicePointManager]::ServerCertificateValidationCallback = { $true }

[System.Net.ServicePointManager]::ServerCertificateValidationCallback = { $true }            
Invoke-RestMethod https://expired.badssl.com/            
       
Invoke-RestMethod : The underlying connection was closed: An unexpected error occurred on a send.
At line:1 char:1
+ Invoke-RestMethod https://expired.badssl.com/
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-RestMethod], WebException
    + FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeRestMethodCommand
            
2. https://blogs.technet.microsoft.com/bshukla/2010/04/12/ignoring-ssl-trust-in-powershell-system-net-webclient/

$netAssembly = [Reflection.Assembly]::GetAssembly([System.Net.Configuration.SettingsSection])            
            
if($netAssembly)            
{            
    $bindingFlags = [Reflection.BindingFlags] "Static,GetProperty,NonPublic"            
    $settingsType = $netAssembly.GetType("System.Net.Configuration.SettingsSectionInternal")            
            
    $instance = $settingsType.InvokeMember("Section", $bindingFlags, $null, $null, @())            
            
 if($instance)            
    {            
        $bindingFlags = "NonPublic","Instance"            
        $useUnsafeHeaderParsingField = $settingsType.GetField("useUnsafeHeaderParsing", $bindingFlags)            
            
  if($useUnsafeHeaderParsingField)            
        {            
          $useUnsafeHeaderParsingField.SetValue($instance, $true)            
        }            
    }            
}            
            
Invoke-RestMethod : The underlying connection was closed: An unexpected error occurred on a send.
At line:1 char:1
+ Invoke-RestMethod https://expired.badssl.com/
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-RestMethod], WebException
    + FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeRestMethodCommand
            

3. [Net.ServicePointManager]::CertificatePolicy = New-Object TrustAllCertsPolicy

Тут странности у меня – в 90% случаев отрабатывает, 10% – после перезапуска сессии, уже не отрабатывало. Проверяйте.

add-type @"
     using System.Net;
     using System.Security.Cryptography.X509Certificates;
     public class TrustAllCertsPolicy : ICertificatePolicy {
         public bool CheckValidationResult(
             ServicePoint srvPoint, X509Certificate certificate,
             WebRequest request, int certificateProblem) {
             return true;
         }
     }
"@            
[System.Net.ServicePointManager]::CertificatePolicy = New-Object TrustAllCertsPolicy            
Invoke-RestMethod https://expired.badssl.com/

 

С ошибкой:

witherr_06092017

 

Без ошибки:

withouterr_06092017

 

4. https://stackoverflow.com/questions/36456104/invoke-restmethod-ignore-self-signed-certs

if (-not("dummy" -as [type])) {            
    add-type -TypeDefinition @"
using System;
using System.Net;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;

public static class Dummy {
    public static bool ReturnTrue(object sender,
        X509Certificate certificate,
        X509Chain chain,
        SslPolicyErrors sslPolicyErrors) { return true; }

    public static RemoteCertificateValidationCallback GetDelegate() {
        return new RemoteCertificateValidationCallback(Dummy.ReturnTrue);
    }
}
"@            
}            
            
[System.Net.ServicePointManager]::ServerCertificateValidationCallback = [dummy]::GetDelegate()            
            
Invoke-RestMethod https://expired.badssl.com/

 

При использовании Add-Type создается dll с случайным именем, а после удаляется. У тех, кто использует SRP/Aplocker — потребуются дополнительные действия для администратора. Утилита ProcMon – покажет, какие действия с файловой системой производит Add-Type. Сохраним вывод ProcMon в csv формате.


PS > Import-Csv AddType.CSV | Where Path -match "Temp" | Format-Table -Auto Operation,Path,Detail

Operation                     Path                                              Detail
---------                     ----                                              ------
CreateFile                    C:\Users\user\AppData\Local\Temp\ucp2kjeg.tmp     Desired Access: Generic Write, Read Attri
CloseFile                     C:\Users\user\AppData\Local\Temp\ucp2kjeg.tmp
CreateFile                    C:\Users\user\AppData\Local\Temp\ucp2kjeg.0.cs    Desired Access: Generic Write, Read Attri
WriteFile                     C:\Users\user\AppData\Local\Temp\ucp2kjeg.0.cs    Offset: 0, Length: 465, Priority: Normal
CloseFile                     C:\Users\user\AppData\Local\Temp\ucp2kjeg.0.cs
CreateFile                    C:\Users\user\AppData\Local\Temp\ucp2kjeg.dll     Desired Access: Generic Read/Write, Dispo
CloseFile                     C:\Users\user\AppData\Local\Temp\ucp2kjeg.dll
CreateFile                    C:\Users\user\AppData\Local\Temp\ucp2kjeg.cmdline Desired Access: Generic Write, Read Attri
WriteFile                     C:\Users\user\AppData\Local\Temp\ucp2kjeg.cmdline Offset: 0, Length: 357, Priority: Normal
CloseFile                     C:\Users\user\AppData\Local\Temp\ucp2kjeg.cmdline
CreateFile                    C:\Users\user\AppData\Local\Temp\ucp2kjeg.out     Desired Access: Generic Write, Read Attri
CreateFile                    C:\Users\user\AppData\Local\Temp\ucp2kjeg.err     Desired Access: Generic Write, Read Attri
WriteFile                     C:\Users\user\AppData\Local\Temp\ucp2kjeg.out     Offset: 0, Length: 440, Priority: Normal
CreateFile                    C:\Users\user\AppData\Local\Temp\ucp2kjeg.dll     Desired Access: Generic Read, Disposition
QueryStandardInformationFile  C:\Users\user\AppData\Local\Temp\ucp2kjeg.dll     AllocationSize: 4 096, EndOfFile: 3 584,
ReadFile                      C:\Users\user\AppData\Local\Temp\ucp2kjeg.dll     Offset: 0, Length: 3 584, Priority: Norma
CloseFile                     C:\Users\user\AppData\Local\Temp\ucp2kjeg.dll
CreateFile                    C:\Users\user\AppData\Local\Temp\ucp2kjeg.pdb     Desired Access: Read Attributes, Disposit
CreateFile                    C:\Users\user\AppData\Local\Temp\ucp2kjeg.out     Desired Access: Read Attributes, Delete,
QueryAttributeTagFile         C:\Users\user\AppData\Local\Temp\ucp2kjeg.out     Attributes: A, ReparseTag: 0x0
SetDispositionInformationFile C:\Users\user\AppData\Local\Temp\ucp2kjeg.out     Delete: True
CloseFile                     C:\Users\user\AppData\Local\Temp\ucp2kjeg.out
CreateFile                    C:\Users\user\AppData\Local\Temp\ucp2kjeg.0.cs    Desired Access: Read Attributes, Delete,
QueryAttributeTagFile         C:\Users\user\AppData\Local\Temp\ucp2kjeg.0.cs    Attributes: A, ReparseTag: 0x0
SetDispositionInformationFile C:\Users\user\AppData\Local\Temp\ucp2kjeg.0.cs    Delete: True
CloseFile                     C:\Users\user\AppData\Local\Temp\ucp2kjeg.0.cs
CreateFile                    C:\Users\user\AppData\Local\Temp\ucp2kjeg.tmp     Desired Access: Read Attributes, Delete,
QueryAttributeTagFile         C:\Users\user\AppData\Local\Temp\ucp2kjeg.tmp     Attributes: A, ReparseTag: 0x0
SetDispositionInformationFile C:\Users\user\AppData\Local\Temp\ucp2kjeg.tmp     Delete: True
CloseFile                     C:\Users\user\AppData\Local\Temp\ucp2kjeg.tmp
CreateFile                    C:\Users\user\AppData\Local\Temp\ucp2kjeg.pdb     Desired Access: Read Attributes, Delete,
CreateFile                    C:\Users\user\AppData\Local\Temp\ucp2kjeg.err     Desired Access: Read Attributes, Delete,
QueryAttributeTagFile         C:\Users\user\AppData\Local\Temp\ucp2kjeg.err     Attributes: A, ReparseTag: 0x0
SetDispositionInformationFile C:\Users\user\AppData\Local\Temp\ucp2kjeg.err     Delete: True
CloseFile                     C:\Users\user\AppData\Local\Temp\ucp2kjeg.err
CreateFile                    C:\Users\user\AppData\Local\Temp\ucp2kjeg.cmdline Desired Access: Read Attributes, Delete,
QueryAttributeTagFile         C:\Users\user\AppData\Local\Temp\ucp2kjeg.cmdline Attributes: A, ReparseTag: 0x0
SetDispositionInformationFile C:\Users\user\AppData\Local\Temp\ucp2kjeg.cmdline Delete: True
CloseFile                     C:\Users\user\AppData\Local\Temp\ucp2kjeg.cmdline
CreateFile                    C:\Users\user\AppData\Local\Temp\ucp2kjeg.dll     Desired Access: Read Attributes, Delete,
QueryAttributeTagFile         C:\Users\user\AppData\Local\Temp\ucp2kjeg.dll     Attributes: A, ReparseTag: 0x0
SetDispositionInformationFile C:\Users\user\AppData\Local\Temp\ucp2kjeg.dll     Delete: True
CloseFile                     C:\Users\user\AppData\Local\Temp\ucp2kjeg.dll

 

Попробуем сгенерировать динамический код в памяти не используя промежуточный файл. Для этого сначала создадим dll и получим IL код.

Add-Type $code -OutputAssembly cert.dll

Для получение IL-кода — воспользуемся бесплатным продуктом dotPeek (существует большого количество декомпиляторов на любой вкус – ildasm, .Net Reflector, ILSpy и т.д.)

dotPeek – Load — Windows  — IL Viewer

 

dp_06092017

 

Получим код:

il_06092017



Создадим динамический модуль:

https://blogs.technet.microsoft.com/heyscriptingguy/2013/06/25/use-powershell-to-interact-with-the-windows-api-part-1/
https://blogs.technet.microsoft.com/heyscriptingguy/2013/06/26/use-powershell-to-interact-with-the-windows-api-part-2/
https://blogs.technet.microsoft.com/heyscriptingguy/2013/06/27/use-powershell-to-interact-with-the-windows-api-part-3/


# Module Builder            
$Domain = [AppDomain]::CurrentDomain            
$DynAssembly = New-Object System.Reflection.AssemblyName('Win32IC')            
            
# Запускать в памяти [System.Reflection.Emit.AssemblyBuilderAccess]::Run            
$AssemblyBuilder = $Domain.DefineDynamicAssembly(            
    $DynAssembly,             
    [System.Reflection.Emit.AssemblyBuilderAccess]::Run            
)             
$ModuleBuilder = $AssemblyBuilder.DefineDynamicModule('Win32IC', $False)            
            
# [Dummy]::GetDelegate()            
$TypeBuilder = $ModuleBuilder.DefineType('Dummy', 'Public, Class')            
            
# Определим ReturnTrue метод            
$ReturnTrue = $TypeBuilder.DefineMethod(            
 # Название метода            
 'ReturnTrue',            
 # Атрибуты класса            
 [Reflection.MethodAttributes] 'Public, Static',            
 # Тип возврата            
 [bool],            
 # Тип параметров             
 [Type[]] @(            
  [object],            
  [System.Security.Cryptography.X509Certificates.X509Certificate],            
  [System.Security.Cryptography.X509Certificates.X509Chain],            
  [System.Net.Security.SslPolicyErrors]            
 )            
)             
            
$ilReturnTrue = $ReturnTrue.GetILGenerator()            
$ilReturnTrue.Emit([Reflection.Emit.OpCodes]::Ldc_I4_1)            
$ilReturnTrue.Emit([Reflection.Emit.OpCodes]::Ret)            
            
# Определим GetDelegate метод            
$GetDelegate = $TypeBuilder.DefineMethod(            
 'GetDelegate',            
 [Reflection.MethodAttributes] 'Public, Static',            
 [System.Net.Security.RemoteCertificateValidationCallback],            
 $null            
)            
            
$ctor = [System.Net.Security.RemoteCertificateValidationCallback].GetConstructor(            
 [type[]]@([object],[intptr])            
)            
            
$ilGetDelegate = $GetDelegate.GetILGenerator()            
$ilGetDelegate.Emit([Reflection.Emit.OpCodes]::Ldnull)            
$ilGetDelegate.Emit([Reflection.Emit.OpCodes]::Ldftn,$ReturnTrue)            
$ilGetDelegate.Emit([Reflection.Emit.OpCodes]::Newobj,$ctor)            
$ilGetDelegate.Emit([Reflection.Emit.OpCodes]::Ret)            
            
# Создание типа и вызов метода            
$Dummy = $TypeBuilder.CreateType()            
[System.Net.ServicePointManager]::ServerCertificateValidationCallback = $Dummy::GetDelegate()            
            
#  Выполнияем запрос к сайту         
Invoke-RestMethod https://expired.badssl.com/

 

g4_06092017

 

5. Использование модуля Tunable-SSL-Validator

Описание проблемы — http://huddledmasses.org/blog/validating-self-signed-certificates-properly-from-powershell/

Скачать модуль — https://github.com/Jaykul/Tunable-SSL-Validator/archive/master.zip

В данной модуле Invoke-WebRequest/Invoke-RestMethod – переопределены в виде proxy function и добавляют дополнительную диагностическую информацию.


Get-Command -Module TunableSSLValidator -Verb Invoke            
            

CommandType     Name               Version    Source
-----------     ----               -------    ------
Function        Invoke-RestMethod  0.0        TunableSSLValidator
Function        Invoke-WebRequest  0.0        TunableSSLValidator

Дополнительная диагностическая информация:
wir_06092017

 

Добавим сертификат в исключение:

Add-SessionTrustedCertificate -LastFailed

e5_06092017


# Удалим сертификат            
Get-SessionTrustedCertificate | Remove-SessionTrustedCertificate            
            
# Отключим проверку            
Disable-SSLChainValidation            
            
Invoke-RestMethod https://expired.badssl.com/

 

l5_06092017

 

PS. Пример динамического кода из 4 пункта –RemoteCertificateValidationCallback .

Read Full Post »

Цель нашей лабораторной получить сертификат от доверенного центра сертификации для Skype For Business 2015:
    1. Выбор доверенного центра сертификации
        . Let’s Encrypt
    2. Выбрать способ проверки для получения сертификата
        . Windows DNS Server / Yandex PDD
    3. Определить список доменов в сертификате
        . sfb.contoso.ru,lyncdiscoverinternal.contoso.ru,web.contoso.ru,contoso.ru
    4. Получить сертификат
        . ACMESharp
        . DNSServer module
        . Yandex API
    5. Установить сертификат на сервер Skype For Business 2015
        . Import-CsCertificate
        . Set-CsCertificate
        . Get-CsCertificate

* В сертификате от Let’s Encrypt отсутствует CRL , поэтому может возникнуть проблема с запуском сервиса Fabric и требуется исправление ключа — <Parameter Name=”CrlCheckingFlag” Value=”0”

  https://itbasedtelco.wordpress.com/2016/06/14/s4b-front-end-servers-event-4097-flooding/

 

1. Выбор доверенного центра сертификации

Центры сертификации WoSign и StartCom, которые ранее выдавали сертификаты с большим сроком действия от 1 до 3 лет, перестали быть доверенными компанией Microsoft (https://blogs.technet.microsoft.com/mmpc/2017/08/08/microsoft-to-remove-wosign-and-startcom-certificates-in-windows-10/) и многими другими, хотя возможность получения и покупка сертификатов возможна.
К счастью, есть доверенный центр сертификации
Let’s Encrypt. Его функционала будет достаточно, для создания федерации с другими партнерами и взаимодействия с сервисом Skype. Для взаимодействия с облачными сервисами, требуется дополнительное тестирование.

Let’s Encrypt:
    Стоимость                            — бесплатно
    Сертификат действителен  — 90 дней
    SAN                                  — 100 доменов
    CRL                                   — не поддерживается
    OCSP                                — поддерживается
    IDN(Internationalized Domain Names) — поддерживается
    OV,EV                               — не поддерживается
    Способ проверки             — ftp,http,dns
    RSA размер ключа            — по умолчанию 2048 (2048, 3072, 4096)
    WildCard                            —
https://letsencrypt.org/2017/07/06/wildcard-certificates-coming-jan-2018.html
    Список ограничений       — https://letsencrypt.org/docs/rate-limits/

Список совместимости:
   
https://community.letsencrypt.org/t/which-browsers-and-operating-systems-support-lets-encrypt/4394
    https://letsencrypt.org/docs/certificate-compatibility/

Сроки реализации новых функций:
   
https://letsencrypt.org/upcoming-features/

# IDN (Internationalized Domain Names)             
[System.Globalization.IdnMapping]::new().GetAscii("президент.рф")            
xn--d1abbgf6aiiy.xn--p1ai                
            
[System.Globalization.IdnMapping]::new().GetUnicode("xn--d1abbgf6aiiy.xn--p1ai")            
президент.рф            

2. Выбрать способ проверки для получения сертификата

В данной лабораторной работе, для простоты был выбран способ проверки через записи DNS.

Рассмотрим вариант создания записей в бесплатном сервисе Яндекс.Почта(предоставляет функционал DNS сервера) и Windows DNS Server(должен быть опубликован в интернет).

Кому лень использовать скрипты, может с легкостью получить сертификаты через онлайн сервис — https://www.sslforfree.com/

Windows DnsServer Module — https://technet.microsoft.com/en-us/itpro/powershell/windows/dnsserver/dnsserver

    Get-DnsServerResourceRecord — https://technet.microsoft.com/en-us/itpro/powershell/windows/dnsserver/get-dnsserverresourcerecord

    Add-DnsServerResourceRecord — https://technet.microsoft.com/en-us/itpro/powershell/windows/dnsserver/add-dnsserverresourcerecord

    Set-DnsServerResourceRecord — https://technet.microsoft.com/en-us/itpro/powershell/windows/dnsserver/set-dnsserverresourcerecord

Yandex API Управление DNS — https://tech.yandex.ru/pdd/doc/concepts/api-dns-docpage/

    GET  /api2/admin/dns/list? — Получить DNS-записи домена —
https://tech.yandex.ru/pdd/doc/reference/dns-list-docpage/

    POST /api2/admin/dns/add   — Добавить DNS-запись        — https://tech.yandex.ru/pdd/doc/reference/dns-add-docpage/

    POST /api2/admin/dns/edit  — Редактировать DNS-запись   — https://tech.yandex.ru/pdd/doc/reference/dns-edit-docpage/

Как подключить себе сервис Яндекса:

   
https://blog.it-kb.ru/2016/02/03/delegating-dns-domain-to-yandex-ns-servers-and-connect-to-free-services-yandex-mail-for-domain-with-1000-unlimited-mailboxes-and-yandex-disk/

Все DNS записи будут типа TXT, формата:

name                       class  rr    text

_acme-challenge.domain   IN     TXT   "123drNmQL5vX0bu4YZlgy5wKNBlCny4yrjF1lSaUndc"

 

3. Определить список доменов в сертификате

Требования к сертифакату для Skype For Business 2015 —
https://technet.microsoft.com/en-us/library/dn933910.aspx

Воспользуемся мастером и скопируем список из вкладки Subject Alternative Name — https://blogs.technet.microsoft.com/uclobby/2015/05/15/renewing-skype-for-business-server-2015-certificates/

4. Получить сертификат

К этому пункту у нас должна быть собрана информация:

    + $DomainName    = "contoso.ru"

    + $SAN           = "sfb.contoso.ru","lyncdiscoverinternal.contoso.ru","web.contoso.ru","contoso.ru"

    + $email         = "pki@contoso.ru"

    * Yandex API — учетные данные, зарегистрированный и настроенный домен

+ — Обязательные параметры

* — Необязательные параметры

! — Все действия выполняем под учетной записью с правами Администратора

Для работы с Let’s Encrypt с помощью PowerShell , есть замечательный модуль ACMESharp.

Документация по модулю — https://pkisharp.github.io/ACMESharp-docs/

User Guide: ACMESharp PowerShell Client  — https://pkisharp.github.io/ACMESharp-docs/User-Guide.html

Quick Start: ACMESharp PowerShell Client — https://pkisharp.github.io/ACMESharp-docs/Quick-Start.html

ACMESharp создает Vault , которое содержит очень чувствительные данные.

* https://github.com/ebekker/ACMESharp/wiki/Vaults,-Vault-Providers-and-Vault-Profiles

:sys — the default system-wide Vault if you are running as an elevated user (admin)

    %ALLUSERSPROFILE%\ACMESharp\sysVault

:user — the default user-specific Vault if you are running as a non-elevated user

    %LOCALAPPDATA%\ACMESharp\userVault

Папки:

VAULT  /**/ = "00-VAULT"; // Vault Info

MTADT  /**/ = "01-MTADT"; // Asset Meta Data

PRVDR  /**/ = "10-PRVDR"; // Challenge Handler Provider Profile

INSTP  /**/ = "18-INSTP"; // Installer Provider Profile

CSRDT  /**/ = "30-CSRDT"; // CSR Generation Details

KEYGN  /**/ = "40-KEYGN"; // Private Key Generation Details

KEYPM  /**/ = "45-KEYPM"; // Private Key PEM Export

CSRGN  /**/ = "50-CSRGN"; // CSR Export

CSRPM  /**/ = "55-CSRPM"; // CSR PEM Export

CSRDR  /**/ = "56-CSRDR"; // CSR DER Export

CRTPM  /**/ = "65-CRTPM"; // Certificate PEM Export

CRTDR  /**/ = "66-CRTDR"; // Certificate DER Export

ISUPM  /**/ = "75-ISUPM"; // Issuer Certificate PEM Export

ISUDR  /**/ = "76-ISUDR"; // Issuer Certificate DER Export

ASSET  /**/ = "99-ASSET"; // Generic Asset

vault

Т.к. по умолчанию используется  EFS для защиты Vault ,а у тех где данный функционал отключен , надо будет явно отключить в файле конфигурации EFS — https://pkisharp.github.io/ACMESharp-docs/Local-Vault-EFS.html .

public string RootPath

        { get; set; }

public bool CreatePath

        { get; set; }

public bool BypassEFS

        { get; set; }

 

$param = (Get-ACMEVaultProfile).VaultParameters            
$param.Add("BypassEFS",$true)            
Set-ACMEVaultProfile -ProfileName ":user" -Provider local -VaultParameters $param  -Force

 

{

  "$type": "ACMESharp.Vault.Profile.VaultProfile, ACMESharp.Vault",

  "Name": "user",

  "ProviderName": "local",

  "ProviderParameters": null,

  "VaultParameters": {

    "$type": "System.Collections.Generic.Dictionary`2[[System.String, mscorlib],[System.Object, mscorlib]], mscorlib",

    "BypassEFS": true,

    "CreatePath": true,

    "RootPath": "C:\\Users\\Administrator\\AppData\\Local\\ACMESharp\\userVault"

  }

}

 

Шаг 1: Установка модуля ACMESharp

Install-Module -Name ACMESharp -AllowClobber            
            

Данный модуль располагается в двух репозитариях PSGallery и Nuget. Версия в PSGallery новее и на момент написания 0.9.0.321:

Install-Module -Name ACMESharp -AllowClobber -Repository PSGallery -Force

Шаг 2: Инициализация хранилища

Initialize-ACMEVault
iv_25082017_1

Шаг 3: Регистрация аккаунта

* Let’s Encrypt CA поддерживает только тип email contact, URI формата mailto:<email-address>

New-ACMERegistration -Contacts mailto:$emailAcceptTos

Шаг 4: Подтверждение владением записей домена

В ACMESharp реализовано два типа ChallengeType: dns-01 , http-01 .

https-01 — описан https://pkisharp.github.io/ACMESharp-docs/Quick-Start.html

 

PS > Get-ACMEChallengeHandlerProfile -ListChallengeTypes

dns-01

http-01

PS > Get-ACMEChallengeHandlerProfile -ListChallengeHandlers

manual

Длинная запись:

 

New-ACMEIdentifier -Dns lyncdiscoverinternal.contoso.ru -Alias sfb1            
New-ACMEIdentifier -Dns sfb.contoso.ru -Alias sfb2            
New-ACMEIdentifier -Dns web.contoso.ru -Alias sfb3            
New-ACMEIdentifier -Dns contoso.ru -Alias sfb4            
            
Complete-ACMEChallenge -IdentifierRef sfb1 -ChallengeType dns-01 -Handler manual            
Complete-ACMEChallenge -IdentifierRef sfb2 -ChallengeType dns-01 -Handler manual            
Complete-ACMEChallenge -IdentifierRef sfb3 -ChallengeType dns-01 -Handler manual            
Complete-ACMEChallenge -IdentifierRef sfb4 -ChallengeType dns-01 -Handler manual             
            
Submit-ACMEChallenge -IdentifierRef sfb1 -ChallengeType dns-01            
Submit-ACMEChallenge -IdentifierRef sfb2 -ChallengeType dns-01            
Submit-ACMEChallenge -IdentifierRef sfb3 -ChallengeType dns-01            
Submit-ACMEChallenge -IdentifierRef sfb4 -ChallengeType dns-01            
            
## Give it a minute, just in case -- or go get more coffee            
 sleep -s 60            
            
Update-ACMEIdentifier -IdentifierRef sfb1            
Update-ACMEIdentifier -IdentifierRef sfb2            
Update-ACMEIdentifier -IdentifierRef sfb3            
Update-ACMEIdentifier -IdentifierRef sfb4

 

Короткая запись:

$SAN | Foreach-Object {$i=1} {            
 $id = "sfb$i"            
 New-ACMEIdentifier -Dns $_ -Alias $id | Out-Null            
 Complete-ACMEChallenge -IdentifierRef $id -ChallengeType dns-01 -Handler manual            
 $i++            
}
# Содержит ресурсные записи, которые потребуется создать на DNS сервере            
# Требуются для подтверждения владением доменом            
$DNS = (Get-ACMEVault).Identifiers.Where{$_.Alias -like "sfb*"}.Authorization.Challenges.Where{$_.Type -eq "dns-01"}.Challenge            
chdns_25082017_1            
# Создаем ресурсные записи             
# Windows DNS Server            
$AllRecords = (Get-DnsServerResourceRecord -ZoneName $DomainName -RRType Txt).Where{$_.Name -match "_acme-challenge"}            
            
$DNS.Foreach{            
 $rdns = $_            
 $name = $_.RecordName.TrimEnd($DomainName)            
 $record = $AllRecords.Where{$_.HostName -eq $name}            
 if($record)            
 {            
  $newrecord = $record.Clone()            
  $newrecord.RecordData.DescriptiveText = $_.RecordValue            
  Set-DnsServerResourceRecord -ZoneName $DomainName -NewInputObject $newrecord -OldInputObject $record            
 }            
 else            
 {            
  ADD-DnsServerResourceRecord -ZoneName $DomainName -Txt -Name $name -DescriptiveText $_.RecordValue            
 }            
}
# Yandex API            
Function Input-Captcha            
{            
 Param($src)            
             
 Add-Type -AssemblyName System.Windows.Forms,System.Drawing            
            
 $bimg  = [Net.WebClient]::new().DownloadData($src)            
 $ms    = [IO.MemoryStream]::new($bimg)            
 $img   = [Drawing.Image]::FromStream($ms)            
            
 [Windows.Forms.Application]::EnableVisualStyles();            
             
 $form = New-Object Windows.Forms.Form -Property @{            
  Text            = "Captcha"            
  Width           = 300            
  Height          = 227            
  FormBorderStyle = "FixedDialog"            
  Icon            = [Drawing.Icon]::ExtractAssociatedIcon(            
   "$env:ProgramFiles\Internet Explorer\iexplore.exe"            
  )            
 }            
             
 $pictureBox = New-Object Windows.Forms.PictureBox -Property @{            
  Width    = $img.Size.Width            
  Height   = $img.Size.Height            
  Location = New-Object Drawing.Size(60,30)            
  Image    = $img            
 }            
             
 $button = New-Object Windows.Forms.Button -Property @{            
  Location = New-Object Drawing.Size(12,153)            
  Size     = New-Object Drawing.Size(260,23)            
  Text     = "Set"            
 }            
             
 $button.Add_Click({            
  $script:rep = $textbox.Text.Trim()            
  $form.Close()            
 })            
            
 $textbox = New-Object Windows.Forms.TextBox -Property @{            
  Location = New-Object Drawing.Size(12,117)             
  Size     = New-Object Drawing.Size(260,20)             
 }            
             
 $form.Controls.Add($pictureBox)            
 $form.Controls.Add($button)            
 $form.Controls.Add($textbox)             
 $form.Add_Shown( { $form.Activate() } )            
 $form.ShowDialog() | Out-Null            
}            
            
Function Get-Token            
{            
 [CmdLetBinding()]            
 Param(            
  [String]$Url    = "https://pddimp.yandex.ru",            
  [String]$TUrl   = "https://pddimp.yandex.ru/api2/admin/get_token",            
  [Parameter(Mandatory)]            
   [PSCredential]$Credential,            
  [Parameter(Mandatory)]            
   [String]$DomainName            
 )            
             
 try {            
  # Логинемся в Yandex Passport            
  $wr = Invoke-WebRequest $TUrl -SessionVariable ya            
              
  # В форме заполняем login&passwd            
  $Form = $wr.Forms[0]            
  $Form.Fields["login"]  = $Credential.UserName            
  $Form.Fields["passwd"] = $Credential.GetNetworkCredential().Password            
              
  if($Form)            
  {            
   # Отправляем Post запрос            
   $wr = Invoke-WebRequest -Uri $Form.Action -WebSession $ya -Method POST -Body $Form            
               
   # Если успешно, то переходим к странице получения токена            
   $wr = Invoke-WebRequest $TUrl -WebSession $ya            
   if($wr.ParsedHtml.Title -eq "Admin's token management")            
   {            
    $Form = $wr.Forms[0]            
                
    # Удаляем из post запроса token_del             
    $Form.Fields.Remove("token_del") | Out-Null            
                
    # Вводим капчу            
    Input-Captcha $wr.Images.src            
                
    # Заполняем форму rep - Captcha            
    $Form.Fields["rep"]         = $rep             
    $Form.Fields["domain_name"] = $DomainName            
                
    # Отправляем Post запрос с методом token_get            
    $wr=Invoke-WebRequest -Uri "$url$($Form.Action)" -WebSession $ya -Method POST -Body $Form            
                
    # Возвращаем полученный токен            
    $wr.ParsedHtml.getElementsByTagName("Strong").Item(0).outerText            
   }            
  }            
 }            
 catch {            
  $_            
 }            
}            
            
$LUrl = 'https://pddimp.yandex.ru/api2/admin/dns/list?domain={0}' -f $DomainName            
$EUrl = 'https://pddimp.yandex.ru/api2/admin/dns/edit'            
$AUrl = 'https://pddimp.yandex.ru/api2/admin/dns/add'            
            
$PddToken = Get-Token -Credential "" -DomainName $DomainName            
            
if($PddToken)            
{            
 # Получим все записи для дальнейшего сравнения            
 $AllRecords = Invoke-WebRequest $LUrl -Headers @{            
  "accept" = "application/json"            
  PddToken = $PddToken            
 } | Foreach Content | ConvertFrom-Json | Foreach {$_.Records}            
             
 $DNS | Foreach {            
  $rdns = $_            
  $record = $AllRecords.Where{$_.FQDN -eq $rdns.RecordName}             
  if($record) {            
   # Обновляем данные            
   Invoke-WebRequest $EUrl -Headers @{"accept"="application/json" ; PddToken = $PddToken} -Method POST -Body @{            
    'domain'=$DomainName            
    'record_id'=$record.record_id            
    'content'=$rdns.RecordValue            
   }            
  }            
  else {            
   # Создаем записи            
   Invoke-WebRequest $AUrl -Headers @{"accept"="application/json" ; PddToken = $PddToken} -Method POST -Body @{            
    'domain'=$DomainName            
    'type'='TXT'            
    'subdomain' = $rdns.RecordName.TrimEnd($DomainName)            
    'content'=$rdns.RecordValue            
   }            
  }            
 }            
} 
 
Т.к. для подтверждения требуется вводить captcha вручную.
captcha_25082017_2           
 
# Отправить запрос на проверку            
(Get-ACMEIdentifier).Where{$_.Alias -match "sfb"}.Foreach{Submit-ACMEChallenge -IdentifierRef $_.Alias -ChallengeType dns-01}            
sleep -s 60            
(Get-ACMEIdentifier).Where{$_.Alias -match "sfb"}.Foreach{Update-ACMEIdentifier -IdentifierRef $_.Alias}

uacme_25082017_1

 

Шаг 5. Генерация сертификата

$idref = (Get-ACMEIdentifier).Where{$_.Alias -match "sfb"} | Select -First 1 | Foreach {$_.Alias}            
$aref =  (Get-ACMEIdentifier).Where{$_.Alias -match "sfb"} | Select -Skip 1 | Foreach {$_.Alias}            
New-ACMECertificate -Generate -IdentifierRef $idref -AlternativeIdentifierRefs $aref -Alias SfB2015FECert

new_25082017_1

# Отправить запрос            
Submit-ACMECertificate -CertificateRef SfB2015FECert

submit_25082017_1

Update-ACMECertificate -CertificateRef SfB2015FECert

update_25082017_1

 

Шаг 6. Экспорт в PFX

Get-ACMECertificate SfB2015FECert -ExportPkcs12 "SfB2015FECert.pfx" -CertificatePassword '12345678'
 
5. Установить сертификат на сервер Skype For Business 2015
 
Пример можно посмотреть - https://www.tbs-certificates.co.uk/FAQ/en/install-skype-business-server.html
 
Import-CsCertificate -Path "c:\SfB2015FECert.pfx" -PrivateKeyExportable $True -Password '12345678'            
Set-CsCertificate -Type Default, WebServicesInternal, WebServicesExternal,OAuthTokenIssuer -Thumbprint "439637470DF2D0E9DF933EADEA1F0610B3BA841C"            
            
Get-CsCertificate | Ft Thumbprint,Use            

Thumbprint                               Use
----------                               ---
439637470DF2D0E9DF933EADEA1F0610B3BA841C Default
439637470DF2D0E9DF933EADEA1F0610B3BA841C WebServicesInternal
439637470DF2D0E9DF933EADEA1F0610B3BA841C WebServicesExternal
439637470DF2D0E9DF933EADEA1F0610B3BA841C OAuthTokenIssuer

PS. Скрипты к статье:

le_windows_dns.txt -  Пример для Windows DNS Server

le_yandex_dns.txt    — Пример использования Yandex API PDD

Read Full Post »

В Windows 10 Version 1607 / Windows Server 2016 Microsoft добавили новый WMI провайдер WUAProvider для работы с обновлениями, который значительно облегчает  автоматизацию установки обновлений в Nano Server и при удаленном использовании интерфейса Windows Update Agent (ранее приходилось использовать PsExec/Task Scheduler). В предыдущих версиях ОС можно воспользоваться отличным модулем PSWindowsUpdate, для установки:

Install-Module PSWindowsUpdate -Scope AllUsers -Force

Список кодов возврата:

https://gist.github.com/AACJ/48a019ba0708a92d4ba60cc202d55e47

UpdateID:

https://www.catalog.update.microsoft.com

wu_22082017

В версиях Windows до Windows Server 2016, изменить значение Update можно:

# 0 - "Not configured"            
# 1 - "Disabled"            
# 2 - "Notify before download"            
# 3 - "Notify before installation"            
# 4 - "Scheduled installation"            
                     
$WUSettings = (New-Object -ComObject Microsoft.Update.AutoUpdate).Settings.NotificationLevel            
$WUSettings.NotificationLevel=2            
$WUSettings.Save()

Данный метод(SET) не работает в Windows Server 2016 (только GET) и можно воспользоваться изменением настроек через реестр/GPO/sconfig и т.д:

# HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU            
            
# AUOptions	Reg_DWORD             
# 2 = извещать перед загрузкой;            
# 3 = автоматически загружать и оповещать об установке;            
# 4 = автоматически загружать и начинать установку по расписанию (только при задании параметров scheduledDay и SheduledInstallTime);            
# 5 = требуется Automatic Updates, но пользователь может выполнить настройку самостоятельно            
            
# NoAutoUpdate Reg_DWORD            
# 0 = включить Automatic Updates;             
# 1 = отключить Automatic Updates            
            
Set-ItemProperty -Path HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU -Name AUOptions -Value 3

WUAProvider на данный момент документирован не полностью. Основной класс для работы MSFT_WUOperationsSession в котором реализовано 9 методов:

$wu = Get-CimClass -Namespace root/Microsoft/Windows/WindowsUpdate -ClassName MSFT_WUOperationsSession            
$wu.CimClassMethods            
            

Name                     ReturnType Parameters
----                     ---------- ----------
SetCallerID                  UInt32 {CallerID}
ApplyApplicableUpdates       UInt32 {HResult}
ScanForUpdates               UInt32 {OnlineScan, SearchCriteria, HResult, Updates}
ScanForUpdatesWithProxy      UInt32 {BypassList, BypassLocal, ProxyAddress, SearchCriteria...}
DownloadUpdates              UInt32 {Updates, HResult}
DownloadUpdatesWithProxy     UInt32 {BypassList, BypassLocal, ProxyAddress, Updates...}
InstallUpdates               UInt32 {Updates, HResult}
UninstallUpdates             UInt32 {Updates, HResult}
CheckWUOperationState        UInt32 {operationState}
 
# Поиск доступных обновлений - ScanForUpdates            
$ci = New-CimInstance -Namespace root/Microsoft/Windows/WindowsUpdate -ClassName MSFT_WUOperationsSession              
$result = $ci | Invoke-CimMethod -MethodName ScanForUpdates -Arguments @{SearchCriteria="IsInstalled=0";OnlineScan=$true}            

PS > $result

HResult ReturnValue Updates
------- ----------- -------
      0           0 {MSFT_WUUpdate (UpdateID = "ac3ad46a-f129-4ee7-bd4d-360385e87a1a")
	  
PS > $result.Updates

Description    : The Microsoft .NET Framework 4.7 is a highly compatible, in-place update for all the previous versions
                  of .NET Framework 4.X. After you install this update, you may have to restart your computer.
KBArticleID    :
MsrcSeverity   :
RevisionNumber : 202
Title          : Microsoft .NET Framework 4.7 for Windows 10 Version 1607 and Windows Server 2016 for x64 (KB3186568)
UpdateID       : ac3ad46a-f129-4ee7-bd4d-360385e87a1a
PSComputerName :

Description    : A security issue has been identified in a Microsoft software product that could affect your system. Yo
                 u can help protect your system by installing this update from Microsoft. For a complete listing of the
                  issues that are included in this update, see the associated Microsoft Knowledge Base article. After y
                 ou install this update, you may have to restart your system.
KBArticleID    :
MsrcSeverity   : Moderate
RevisionNumber : 201
Title          : 2017-08 Cumulative Update for Windows Server 2016 for x64-based Systems (KB4034658)
UpdateID       : fbf6de89-bf23-48b9-aef0-aae76f268708
PSComputerName :

Description    : Install this update to revise the definition files that are used to detect viruses, spyware, and other
                  potentially unwanted software. Once you have installed this item, it cannot be removed.
KBArticleID    :
MsrcSeverity   :
RevisionNumber : 200
Title          : Definition Update for Windows Defender - KB2267602 (Definition 1.249.1352.0)
UpdateID       : c9219ede-5e09-4639-a278-9cb27136e751
PSComputerName :
 
# Скачать обновление под индексом 2 - DownloadUpdates            
Invoke-CimMethod -InputObject $ci -MethodName DownloadUpdates -Arguments @{            
 Updates = [CimInstance[]]$result.Updates.Item(2)            
}            
HResult ReturnValue PSComputerName
------- ----------- --------------
      0           0
               
# Установить обновление под индексом 2 - InstallUpdates            
Invoke-CimMethod -InputObject $ci -MethodName InstallUpdates -Arguments @{            
 Updates = [CimInstance[]]$result.Updates.Item(2)            
}            

HResult ReturnValue PSComputerName
------- ----------- --------------
      0           0
# Выборочно скачать обновление (исключим для примера Microsoft .NET Framework 4.7)            
Invoke-CimMethod -InputObject $ci -MethodName DownloadUpdates -Arguments @{            
 Updates = [CimInstance[]]($result.Updates.Where{$_.UpdateID -ne 'ac3ad46a-f129-4ee7-bd4d-360385e87a1a'})            
}            

HResult ReturnValue PSComputerName
------- ----------- --------------
      0           0
               
# Выборочно установить обновление (исключим для примера Microsoft .NET Framework 4.7)            
Invoke-CimMethod -InputObject $ci -MethodName InstallUpdates -Arguments @{            
 Updates = [CimInstance[]]($result.Updates.Where{$_.UpdateID -ne 'ac3ad46a-f129-4ee7-bd4d-360385e87a1a'})            
}            

HResult ReturnValue PSComputerName
------- ----------- --------------
      0           0
 
# Установить все обновления - ApplyApplicableUpdates            
Invoke-CimMethod -InputObject $ci -MethodName ApplyApplicableUpdates            

HResult ReturnValue PSComputerName
------- ----------- --------------
      0           0
 
# Текущий статус операции            
Invoke-CimMethod -InputObject $ci -MethodName CheckWUOperationState            

operationState ReturnValue PSComputerName
-------------- ----------- --------------
Idle                     0

operationState ReturnValue PSComputerName
-------------- ----------- --------------
Downloading              0

operationState ReturnValue PSComputerName
-------------- ----------- --------------
Installing               0
 
# Список установленных обновлений            
$ci = New-CimInstance -Namespace root/Microsoft/Windows/WindowsUpdate -ClassName MSFT_WUOperationsSession            
$result = $ci | Invoke-CimMethod -MethodName ScanForUpdates -Arguments @{SearchCriteria="IsInstalled=1";OnlineScan=$true}            
$result.Updates

Отдельно стоит уточнить про метод UninstallUpdates при вызове данного метода для обновлений, которые устанавливались не через WSUS , данный метод вернет — 2149842984|WU_E_UNINSTALL_NOT_ALLOWED|uninstall is not allowed due to non managed environment .  В модуле PSWindowsUpdate для удаления используется утилита wusa.exe .

# Удалить обновление под индексом 2 - UninstallUpdates            
Invoke-CimMethod -InputObject $ci -MethodName UninstallUpdates -Arguments @{            
 Updates = [CimInstance[]]$result.Updates.Item(2)            
}

Полезные ссылки:

https://docs.microsoft.com/en-us/windows-server/get-started/manage-nano-server

https://blogs.technet.microsoft.com/nanoserver/2016/01/16/updating-nano-server-using-windows-update-or-windows-server-update-service/

https://blogs.technet.microsoft.com/nanoserver/2016/10/07/updating-nano-server/

Read Full Post »

Существует большое количество утилит для получения handle Name. Наиболее известные – handle.exe , Process Explorer, Process Hacker. Для PowerShell доступен модуль PoshInternals за авторством Adam Driscoll.

fm

После установки модуля, будет доступен командлет Get-Handle:

gh

Но на Windows 10 x64, командлет выдавал странные результаты. Утилита handle.exe  — отрабатывает штатно.

Ruben Boonen предоставил прекрасный скрипт для получения открытых handle-ов для указанного процесса — Get-Handles.ps1 . Его скрипт был немного изменен, чтобы в вывод не попадала лишняя информация и не было группировки. Для получения имени, используем функцию NtQueryObject .

Add-Type -TypeDefinition @'
    using System;
	using System.Runtime.InteropServices;

    public enum OBJECT_INFORMATION_CLASS
    {
        ObjectBasicInformation,
        ObjectNameInformation,
        ObjectTypeInformation,
        ObjectAllInformation,
        ObjectDataInformation
    }

    [Flags]
    public enum ProcessAccessFlags : uint
    {
        All                     = 0x001F0FFF,
        Terminate               = 0x00000001,
        CreateThread            = 0x00000002,
        VirtualMemoryOperation  = 0x00000008,
        VirtualMemoryRead       = 0x00000010,
        VirtualMemoryWrite      = 0x00000020,
        DuplicateHandle         = 0x00000040,
        CreateProcess           = 0x000000080,
        SetQuota                = 0x00000100,
        SetInformation          = 0x00000200,
        QueryInformation        = 0x00000400,
        QueryLimitedInformation = 0x00001000,
        Synchronize             = 0x00100000
    }

    [Flags]
    public enum DuplicateOptions : uint
    {
        DUPLICATE_CLOSE_SOURCE = 0x00000001,
        DUPLICATE_SAME_ACCESS = 0x00000002
    }

    public struct OBJECT_NAME_INFORMATION
    {
        public UNICODE_STRING Name;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct UNICODE_STRING : IDisposable
    {
        public ushort Length;
        public ushort MaximumLength;
        private IntPtr buffer;

        public UNICODE_STRING(string s)
        {
           Length = (ushort)(s.Length * 2);
           MaximumLength = (ushort)(Length + 2);
           buffer = Marshal.StringToHGlobalUni(s);
        }

        public void Dispose()
        {
            Marshal.FreeHGlobal(buffer);
            buffer = IntPtr.Zero;
        }

        public override string ToString()
        {
            return Marshal.PtrToStringUni(buffer);
        }
    }
    
    public enum NtStatus : uint
    {
        Success = 0x00000000,
        InvalidHandle = 0xc0000008
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct SYSTEM_HANDLE_INFORMATION
    {
        public UInt32 ProcessId;
        public Byte ObjectTypeNumber;
        public Byte Flags;
        public UInt16 HandleValue;
        public IntPtr Object_Pointer;
        public UInt32 GrantedAccess;
    }

    public static class Ntdll
	{
        [DllImport("ntdll.dll", SetLastError = true)]
        public static extern NtStatus NtQueryObject(
            [In]  IntPtr Handle,
            [In]  OBJECT_INFORMATION_CLASS ObjectInformationClass,
            [Out] IntPtr ObjectInformation,
            [In]  int ObjectInformationLength,
            [Out] out int ReturnLength);
    
        [DllImport("ntdll.dll")] 
        public static extern int NtQuerySystemInformation( 
            int SystemInformationClass, 
            IntPtr SystemInformation, 
            int SystemInformationLength, 
            ref int ReturnLength);
    }

    public static class Kernel32
    {
        [DllImport("kernel32.dll", SetLastError = true)]
            public static extern bool CloseHandle(IntPtr hObject);
        [DllImport("kernel32.dll", SetLastError = true)]
            public static extern IntPtr OpenProcess(ProcessAccessFlags processAccess, bool bInheritHandle, uint processId);
        [DllImport("kernel32.dll", SetLastError = true)]
            public static extern IntPtr GetCurrentProcess();
        [DllImport("kernel32.dll", SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool DuplicateHandle(IntPtr hSourceProcessHandle,
            IntPtr hSourceHandle, IntPtr hTargetProcessHandle, out IntPtr lpTargetHandle,
            uint dwDesiredAccess, [MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, DuplicateOptions options);
        [DllImport("kernel32.dll")]
            public static extern uint QueryDosDevice(string lpDeviceName, System.Text.StringBuilder lpTargetPath, uint ucchMax);
    }
'@            
            
function  ConvertTo-RegularFileName             
{            
    param($RawFileName)            
            
    foreach($logicalDrive in [Environment]::GetLogicalDrives())             
    {            
       $targetPath = New-Object System.Text.StringBuilder 256            
       if([Kernel32]::QueryDosDevice($logicalDrive.Substring(0, 2), $targetPath, 256) -eq 0)             
       {            
          return $targetPath              
       }            
       $targetPathString = $targetPath.ToString()            
       if($RawFileName.StartsWith($targetPathString))            
       {            
          $RawFileName = $RawFileName.Replace($targetPathString,$logicalDrive.Substring(0, 2))            
          break            
       }            
    }            
    return $RawFileName            
}            
            
function Get-Handles {            
<#
.SYNOPSIS
	Use NtQuerySystemInformation::SystemHandleInformation to get a list of
	open handles in the specified process, works on x32/x64.

	Notes:
	* For more robust coding I would recomend using @mattifestation's
	  Get-NtSystemInformation.ps1 part of PowerShellArsenal.

.DESCRIPTION
	Author: Ruben Boonen (@FuzzySec)
	License: BSD 3-Clause
	Required Dependencies: None
	Optional Dependencies: None

.EXAMPLE
	C:\PS> Get-Handles -ProcID 1234
#>            
            
 [CmdletBinding()]            
 param (            
  [Parameter(Mandatory = $True)]            
  [int]$ProcID            
 )            
            
 # Make sure the PID exists            
 if (!$(get-process -Id $ProcID -ErrorAction SilentlyContinue)) {            
  Write-Verbose "[!] The specified PID doesn't exist, exiting..`n"            
  Return            
 } else {            
  Write-Verbose "[>] PID $ProcID --> $((Get-Process -Id $ProcID).ProcessName)"            
 }            
            
 # Flag switches (0 = NONE?)            
 $FlagSwitches = @{            
  0 = 'NONE'            
  1 = 'PROTECT_FROM_CLOSE'            
  2 = 'INHERIT'            
 }            
             
 # Taken from @mattifestation --> Get-NtSystemInformation.ps1            
 # https://github.com/mattifestation/PowerShellArsenal/blob/master/WindowsInternals/Get-NtSystemInformation.ps1            
 $OSVersion = [Version](Get-WmiObject Win32_OperatingSystem).Version            
 $OSMajorMinor = "$($OSVersion.Major).$($OSVersion.Minor)"            
 switch ($OSMajorMinor)            
 {            
  '10.0' # Windows 10 - Incomplete still, but 99% of the what you will see in any given process (work in progress, need to pull up KD)            
  {            
   $TypeSwitches = @{            
    0x03 = 'Directory'; 0x04 = 'SymbolicLink'; 0x05 = 'Token'; 0x07 = 'Process'; 0x08 = 'Thread';            
    0x0D = 'Event'; 0x0E = 'Mutant'; 0x10 = 'Semaphore'; 0x11 = 'Timer'; 0x12 = 'IRTimer';            
    0x15 = 'WindowStation'; 0x16 = 'Desktop'; 0x17 = 'Composition'; 0x18 = 'RawInputManager';            
    0x19 = 'TpWorkerFactory'; 0x1E = 'IoCompletion'; 0x1F = 'WaitCompletionPacket'; 0x20 = 'File';            
    0x21 = 'TmTm'; 0x22 = 'TmTx'; 0x23 = 'TmRm'; 0x24 = 'TmEn'; 0x25 = 'Section'; 0x26 = 'Session';            
    0x27 = 'Partition'; 0x28 = 'Key'; 0x29 = 'ALPC Port'; 0x2C = 'EtwRegistration'; 0x2F = 'DmaDomain';            
    0x31 = 'FilterConnectionPort';            
   }            
  }            
              
  '6.2' # Windows 8 and Windows Server 2012            
  {            
   $TypeSwitches = @{            
    0x02 = 'Type'; 0x03 = 'Directory'; 0x04 = 'SymbolicLink'; 0x05 = 'Token'; 0x06 = 'Job';            
    0x07 = 'Process'; 0x08 = 'Thread'; 0x09 = 'UserApcReserve'; 0x0A = 'IoCompletionReserve';            
    0x0B = 'DebugObject'; 0x0C = 'Event'; 0x0D = 'EventPair'; 0x0E = 'Mutant'; 0x0F = 'Callback';            
    0x10 = 'Semaphore'; 0x11 = 'Timer'; 0x12 = 'IRTimer'; 0x13 = 'Profile'; 0x14 = 'KeyedEvent';            
    0x15 = 'WindowStation'; 0x16 = 'Desktop'; 0x17 = 'CompositionSurface'; 0x18 = 'TpWorkerFactory';            
    0x19 = 'Adapter'; 0x1A = 'Controller'; 0x1B = 'Device'; 0x1C = 'Driver'; 0x1D = 'IoCompletion';            
    0x1E = 'WaitCompletionPacket'; 0x1F = 'File'; 0x20 = 'TmTm'; 0x21 = 'TmTx'; 0x22 = 'TmRm';            
    0x23 = 'TmEn'; 0x24 = 'Section'; 0x25 = 'Session'; 0x26 = 'Key'; 0x27 = 'ALPC Port';            
    0x28 = 'PowerRequest'; 0x29 = 'WmiGuid'; 0x2A = 'EtwRegistration'; 0x2B = 'EtwConsumer';            
    0x2C = 'FilterConnectionPort'; 0x2D = 'FilterCommunicationPort'; 0x2E = 'PcwObject';            
    0x2F = 'DxgkSharedResource'; 0x30 = 'DxgkSharedSyncObject';            
   }            
  }            
             
  '6.1' # Windows 7 and Window Server 2008 R2            
  {            
   $TypeSwitches = @{            
    0x02 = 'Type'; 0x03 = 'Directory'; 0x04 = 'SymbolicLink'; 0x05 = 'Token'; 0x06 = 'Job';            
    0x07 = 'Process'; 0x08 = 'Thread'; 0x09 = 'UserApcReserve'; 0x0a = 'IoCompletionReserve';            
    0x0b = 'DebugObject'; 0x0c = 'Event'; 0x0d = 'EventPair'; 0x0e = 'Mutant'; 0x0f = 'Callback';            
    0x10 = 'Semaphore'; 0x11 = 'Timer'; 0x12 = 'Profile'; 0x13 = 'KeyedEvent'; 0x14 = 'WindowStation';            
    0x15 = 'Desktop'; 0x16 = 'TpWorkerFactory'; 0x17 = 'Adapter'; 0x18 = 'Controller';            
    0x19 = 'Device'; 0x1a = 'Driver'; 0x1b = 'IoCompletion'; 0x1c = 'File'; 0x1d = 'TmTm';            
    0x1e = 'TmTx'; 0x1f = 'TmRm'; 0x20 = 'TmEn'; 0x21 = 'Section'; 0x22 = 'Session'; 0x23 = 'Key';            
    0x24 = 'ALPC Port'; 0x25 = 'PowerRequest'; 0x26 = 'WmiGuid'; 0x27 = 'EtwRegistration';            
    0x28 = 'EtwConsumer'; 0x29 = 'FilterConnectionPort'; 0x2a = 'FilterCommunicationPort';            
    0x2b = 'PcwObject';            
   }            
  }            
             
  '6.0' # Windows Vista and Windows Server 2008            
  {            
   $TypeSwitches = @{            
    0x01 = 'Type'; 0x02 = 'Directory'; 0x03 = 'SymbolicLink'; 0x04 = 'Token'; 0x05 = 'Job';            
    0x06 = 'Process'; 0x07 = 'Thread'; 0x08 = 'DebugObject'; 0x09 = 'Event'; 0x0a = 'EventPair';            
    0x0b = 'Mutant'; 0x0c = 'Callback'; 0x0d = 'Semaphore'; 0x0e = 'Timer'; 0x0f = 'Profile';            
    0x10 = 'KeyedEvent'; 0x11 = 'WindowStation'; 0x12 = 'Desktop'; 0x13 = 'TpWorkerFactory';            
    0x14 = 'Adapter'; 0x15 = 'Controller'; 0x16 = 'Device'; 0x17 = 'Driver'; 0x18 = 'IoCompletion';            
    0x19 = 'File'; 0x1a = 'TmTm'; 0x1b = 'TmTx'; 0x1c = 'TmRm'; 0x1d = 'TmEn'; 0x1e = 'Section';            
    0x1f = 'Session'; 0x20 = 'Key'; 0x21 = 'ALPC Port'; 0x22 = 'WmiGuid'; 0x23 = 'EtwRegistration';            
    0x24 = 'FilterConnectionPort'; 0x25 = 'FilterCommunicationPort';            
   }            
  }            
 }            
             
 Write-Verbose "[+] Calling NtQuerySystemInformation::SystemHandleInformation"            
 [int]$BuffPtr_Size = 0            
 while ($true) {            
  [IntPtr]$BuffPtr = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($BuffPtr_Size)            
  $SystemInformationLength = New-Object Int            
             
  $CallResult = [Ntdll]::NtQuerySystemInformation(16, $BuffPtr, $BuffPtr_Size, [ref]$SystemInformationLength)            
              
  # STATUS_INFO_LENGTH_MISMATCH            
  if ($CallResult -eq 0xC0000004) {            
   [System.Runtime.InteropServices.Marshal]::FreeHGlobal($BuffPtr)            
   [int]$BuffPtr_Size = [System.Math]::Max($BuffPtr_Size,$SystemInformationLength)            
  }            
  # STATUS_SUCCESS            
  elseif ($CallResult -eq 0x00000000) {            
   Write-Verbose "[?] Success, allocated $BuffPtr_Size byte result buffer`n"            
   break            
  }            
  # Probably: 0xC0000005 -> STATUS_ACCESS_VIOLATION            
  else {            
   [System.Runtime.InteropServices.Marshal]::FreeHGlobal($BuffPtr)            
   Write-Verbose "[!] Error, NTSTATUS Value: $('{0:X}' -f ($CallResult))`n"            
   return            
  }            
 }            
             
 $SYSTEM_HANDLE_INFORMATION = New-Object SYSTEM_HANDLE_INFORMATION            
 $SYSTEM_HANDLE_INFORMATION = $SYSTEM_HANDLE_INFORMATION.GetType()            
 if ([System.IntPtr]::Size -eq 4) {            
  $SYSTEM_HANDLE_INFORMATION_Size = 16 # This makes sense!            
 } else {            
  $SYSTEM_HANDLE_INFORMATION_Size = 24 # This doesn't make sense, should be 20 on x64 but that doesn't work.            
                                             # Ask no questions, hear no lies!            
 }            
             
 $BuffOffset = $BuffPtr.ToInt64()            
 $HandleCount = [System.Runtime.InteropServices.Marshal]::ReadInt32($BuffOffset)            
 $BuffOffset = $BuffOffset + [System.IntPtr]::Size            
 Write-Verbose "[>] Result buffer contains $HandleCount SystemHandleInformation objects"            
             
 $SystemHandleArray = @()            
 for ($i=0; $i -lt $HandleCount; $i++){            
  # PtrToStructure only objects we are targeting, this is expensive computation            
  if ([System.Runtime.InteropServices.Marshal]::ReadInt32($BuffOffset) -eq $ProcID) {            
   $SystemPointer = New-Object System.Intptr -ArgumentList $BuffOffset            
   $Cast = [system.runtime.interopservices.marshal]::PtrToStructure($SystemPointer,[type]$SYSTEM_HANDLE_INFORMATION)            
               
   $HashTable = @{            
    PID = $Cast.ProcessID            
    ObjectType = if (!$($TypeSwitches[[int]$Cast.ObjectTypeNumber])) { "0x$('{0:X2}' -f [int]$Cast.ObjectTypeNumber)" } else { $TypeSwitches[[int]$Cast.ObjectTypeNumber] }            
    HandleFlags = $FlagSwitches[[int]$Cast.Flags]            
    Handle = "0x$('{0:X4}' -f [int]$Cast.HandleValue)"            
    KernelPointer = if ([System.IntPtr]::Size -eq 4) { "0x$('{0:X}' -f $Cast.Object_Pointer.ToInt32())" } else { "0x$('{0:X}' -f $Cast.Object_Pointer.ToInt64())" }            
    AccessMask = "0x$('{0:X8}' -f $($Cast.GrantedAccess -band 0xFFFF0000))"            
   }            
               
   $Object = New-Object PSObject -Property $HashTable            
   $SystemHandleArray += $Object            
               
  }            
            
  $BuffOffset = $BuffOffset + $SYSTEM_HANDLE_INFORMATION_Size            
 }            
             
 Write-Verbose "[>] PID $ProcID has $($SystemHandleArray.count) handle objects"            
 if ($($SystemHandleArray.count) -eq 0) {            
  [System.Runtime.InteropServices.Marshal]::FreeHGlobal($BuffPtr)            
  Write-Verbose "[!] No process handles found, exiting..`n"            
  Return            
 }            
             
 # Set column order and auto size            
 $SystemHandleArray | Select-Object PID,ObjectType,HandleFlags,Handle,KernelPointer,AccessMask            
             
 # Free SYSTEM_HANDLE_INFORMATION array            
 [System.Runtime.InteropServices.Marshal]::FreeHGlobal($BuffPtr)            
}            
            
Function Get-HandleName {            
    [CmdLetBinding()]            
    param(            
        [Parameter(Mandatory=$true,            
            ValueFromPipelineByPropertyName=$true)]            
            [IntPtr][Int]$Handle,            
        [Parameter(Mandatory=$true,            
            ValueFromPipelineByPropertyName=$true)]            
        [Alias("PID")]            
            [UInt32]$ID,            
        [Parameter(Mandatory=$false,            
            ValueFromPipelineByPropertyName=$true)]            
        [Alias("ObjectType")]            
            [String]$Type            
    )            
            
    Process            
    {            
        $duplicatedHandle = [IntPtr]::Zero            
            
        $processHandle = [Kernel32]::OpenProcess([ProcessAccessFlags]::DuplicateHandle, $true, $ID)            
        $process = [Kernel32]::GetCurrentProcess()            
        $options = [DuplicateOptions]::DUPLICATE_SAME_ACCESS            
        [bool]$success = [Kernel32]::DuplicateHandle($processHandle, $handle, $process, [ref]$duplicatedHandle, 0, $false, $options)            
        [Kernel32]::CloseHandle($processHandle) | Out-Null            
                   
        if(!$success)             
        {            
            return            
        }            
            
        $dummy = 0            
        $length = [Runtime.InteropServices.Marshal]::SizeOf([type][OBJECT_NAME_INFORMATION]) + 256            
        [IntPtr]$buffer = [Runtime.InteropServices.Marshal]::AllocHGlobal($length)            
                
        $status = [NTDLL]::NtQueryObject(            
            $duplicatedHandle,            
            [OBJECT_INFORMATION_CLASS]::ObjectNameInformation,             
            $buffer,             
            $length,             
            [ref]$dummy            
        )            
            
        if ($status -eq [NtStatus]::Success)            
        {            
            $temp = [Runtime.InteropServices.Marshal]::PtrToStructure($buffer, [type][OBJECT_NAME_INFORMATION])            
            $rName = $temp.Name.ToString()            
            
            if($rName)            
            {            
                $Name = ConvertTo-RegularFileName $rName            
                [PSCustomObject]@{            
                    Handle = "0x{0:X}" -f [long]$Handle            
                    ID     = $ID            
                    Type   = $Type            
                    Name   = $Name            
                }                        
            }            
        }            
                
        [Runtime.InteropServices.Marshal]::FreeHGlobal($buffer) | Out-Null            
        [Kernel32]::CloseHandle($duplicatedHandle) | Out-Null            
     }            
}

Пример вывода:

Get-Handles -ProcID (Get-Process FoxitReader).ID -Verbose | Get-HandleName

gh1


$h = Get-Handles -ProcID (Get-Process FoxitReader).ID | Get-HandleName            
$h | Where Name -match "pdf"

gh2

 

Get-HandleName.ps1https://github.com/PoshKazun/Garbage/blob/master/Get-HandleName.ps1

Read Full Post »

В серверных операционных системах Microsoft по умолчанию отсутствует служба WebClient. Данный функционал включается установкой дополнительного компонента, который зависит от версии OS.

Windows Server 2008 R2*,2012,2012 R2 (компонент Desktop-Experience)**:

  Install-WindowsFeature Desktop-Experience

* Для Windows Server 2008 R2 командлет   Install-WindowsFeature отсутствует, вместо него используйте Add-WindowsFeature .

de211116

Windows Server 2016(компонент WebDAV-Redirector)**:

Install-WindowsFeature WebDAV-Redirector –Restart
wd211116

** Требуется обязательная перезагрузка

В систему добавляется две новые службы(WebClient&MRxDAV):

s211116 

Настройка службы WebClient производится через реестр. Подробнее про параметры, можно прочитать —  WebDAV Redirector Registry Settings

Основной параметр BasicAuthLevel имеет значение:

  • 0 — Basic authentication disabled
  • 1 — Basic authentication enabled for SSL shares only
  • 2 or greater — Basic authentication enabled for SSL shares and for non-SSL shares

В более младших версиях, параметр по умолчанию: 2

reg211116

 

Для проверки работы:

nd211116

 

Read Full Post »

Рассмотрим, как изменить расширенное свойство файлов на примере Comments.

ep10112016

Для получения свойств, можно воспользоваться Com-классом Shell.Application. Подробнее — Retrieving Extended File Properties

# Отобразить расширенные свойства            
$folder = "С:\files"            
$file = "Document"            
$shell = New-Object -ComObject Shell.Application            
$ns = $shell.NameSpace($folder)            
$fn = $ns.Items()| Where {$_.Name -eq $file}            
            
0..60 | Select-Object @{n="Name";e={$ns.GetDetailsOf($ns,$_)}},            
    @{n="Value";e={$ns.GetDetailsOf($fn,$_)}}

Выведем отдельное свойство Comments:

sp101116

Для изменения расширенных свойств Shell.Application не подходит, воспользуемся другими методами.

1.  Office.Application

Разберем на примере Word.Application для других классов отличия лишь в имени класса.

Param(            
 $Path  = "D:\mydoc.docx",            
 $PropertyName  = "Comments",            
 $Value = "$(Get-Date): $env:COMPUTERNAME - $env:USERNAME",            
 [switch]$Show            
)            
            
# Создаем экземпляр класса            
$App = New-Object -ComObject word.application            
$App.Visible = $false            
$Document = $App.Documents.Open($Path)            
$BuiltIn = $Document.BuiltInDocumentProperties             
            
# Список свойств            
$Property = @(             
 @{n="Name";e={$_.GetType().InvokeMember("Name", "GetProperty",$null,$_,$null)}},             
 @{n="Value";e={$_.GetType().InvokeMember("Value", "GetProperty",$null,$_,$null)}}            
)            
            
if($Show)            
{            
 $BuiltIn | Select-Object -Property $Property | Out-Default            
}            
else             
{            
 # Свойство            
 $PP = $BuiltIn.GetType().InvokeMember("Item", "GetProperty",$null,$BuiltIn,$PropertyName)              
 $PP | Select-Object -Property $Property | Out-Default            
            
 # Изменим свойство            
 $PP.GetType().InvokeMember("Value", "SetProperty",$null,$PP,$Value)            
            
 $PP = $BuiltIn.GetType().InvokeMember("Item", "GetProperty",$null,$BuiltIn,$PropertyName)              
 $PP | Select-Object -Property $Property | Out-Default            
             
    # Сохраняем            
 $Document.Saved = $false            
 $Document.Save()            
}            
            
# Освобождаем ресурсы            
$App.Quit()            
[Runtime.InteropServices.Marshal]::ReleaseComObject($App) | Out-Null            
$App = $null            
[GC]::Collect()            
[GC]::WaitForPendingFinalizers()

Для получения свойств, параметр Show:

wep101116

Для изменения Comments:

wcc101116

2. Библиотека Dsofile.dll

Скачаем и установим библиотеку – https://www.microsoft.com/en-us/download/confirmation.aspx?id=8422.

Зарегистрируем библиотеку:

regsvr32 "C:\Program Files (x86)\DsoFile\dsofile.dll"

reg101116

Если система у Вас x-64, то запустите x-86 версию PowerShell.

Add-Type -Path "C:\Program Files (x86)\DsoFile\Demo\Interop.Dsofile.dll"            
$file = "D:\mydoc.docx"            
$op = New-Object DsoFile.OleDocumentPropertiesClass            
# Режим редактирования            
$op.Open($file,$false)            
            
# Получение свойств            
$op.SummaryProperties            
            
# Комментарий            
$op.SummaryProperties.Comments            
            
# Изменим комментарий            
$op.SummaryProperties.Comments = Get-Date            
            
# Сохраняем            
$op.Save()            

3.  OpenXmlSDK

Скачиваем и устанавливаем — https://www.microsoft.com/en-us/download/details.aspx?id=30425

Add-Type -Path "C:\Program Files (x86)\Open XML SDK\V2.5\lib\DocumentFormat.OpenXml.dll"            
$file = "D:\mydoc.docx"            
$file = [DocumentFormat.OpenXml.Packaging.WordprocessingDocument]::Open($file,$true)            
$file.PackageProperties.Description = Get-Date            
$file.Close()            

4. WindowsAPICodePack

Для работы с PackageManagement есть хорошая документация с подробными примерами — https://msdn.microsoft.com/ru-ru/powershell/wmf/5.0/oneget_cmdlets

# Найдем пакеты для установки            
$package = "WindowsAPICodePack-Shell","WindowsAPICodePack-Core"            
Find-Package -Name $package -Provider NuGet -Source http://www.nuget.org/api/v2            
            
pk101116
# Установим            
$package = "WindowsAPICodePack-Shell","WindowsAPICodePack-Core"            
Find-Package -Name $package -Provider NuGet -Source http://www.nuget.org/api/v2 | Install-Package            
            
# Проверим установку            
Get-Package $package | Format-List Name,Source            

gp101116

Внесем изменения в Comments:
Add-Type -Path "C:\Program Files\NuGet\Packages\WindowsAPICodePack-Core.1.1.2\lib\Microsoft.WindowsAPICodePack.dll"            
Add-Type -Path "C:\Program Files\NuGet\Packages\WindowsAPICodePack-Shell.1.1.1\lib\Microsoft.WindowsAPICodePack.Shell.dll"            
            
$file = "D:\mydoc.docx"            
$sh = [Microsoft.WindowsAPICodePack.Shell.ShellFile]::FromFilePath($file)                       
$sh.Properties.System.Comment.Value = Get-Date

Read Full Post »

Older Posts »