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

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) – Сервер через которой осуществляется подключение

    Пост чисто для ознакомления, т.к. не рекомендуется использовать подобный метод. Предполагаем, что были выполнены все процедуры из статьи:

    Enable antispam functionality on Mailbox servers

    Которая собственно сводится к :

    & $env:ExchangeInstallPath\Scripts\Install-AntiSpamAgents.ps1
    Restart-Service MSExchangeTransport

    Сконфигурируем настройка для Windows Update :

    • Give me updates for other Microsoft Products when I update Windows
    • Check for Updates but let me choose whether to download and install them

    wu

    1.  Check for Updates but let me choose whether to download and install them

    AutomaticUpdatesNotificationLevel enumeration — https://msdn.microsoft.com/en-us/library/windows/desktop/aa385806(v=vs.85).aspx

    1 = «Never check for updates»

    2 = «Check for updates but let me choose whether to download and install them»

    3 = «Download updates but let me choose whether to install them»

    4 = «Install updates automatically»

    #Automatic Updates prompts users to approve updates before it downloads or installs the updates
    $aunlNotifyBeforeDownload = 2
    $AutoUpdate = New-Object -ComObject Microsoft.Update.AutoUpdate
    $Settings = $AutoUpdate.Settings
    $Settings.NotificationLevel = $aunlNotifyBeforeDownload
    $Settings.FeaturedUpdatesEnabled = $true
    $Settings.Save()

    2. Give me updates for other Microsoft Products when I update Windows

    AddServiceFlag enumeration — https://msdn.microsoft.com/en-us/library/windows/desktop/bb394819(v=vs.85).aspx

    asfAllowPendingRegistration  = 0x1, asfAllowOnlineRegistration   = 0x2, asfRegisterServiceWithAU     = 0x4

    PS >  1 -bor 2 -bor 4

    7

    #Get Updates for Other Microsoft Products
    $ServiceManager = New-Object -ComObject Microsoft.Update.ServiceManager
    $ServiceManager.AddService2("7971f918-a847-4430-9279-4a52d1efe18d",7,"")
    

    3. Какое последнее доступное обновление на портале Microsoft

    $url = "http://catalog.update.microsoft.com/v7/site/Rss.aspx?q=exchange+server+2007+anti-spam&lang=en"
    $rss = Invoke-WebRequest $url
    $xml = [xml]$rss.Content
    $updates = $xml.rss.channel.item.where({$_.Title -match "Standard"})

     

    
    
    PS > $updates[0]
    title       : Microsoft Exchange Server Standard Anti-spam Filter Updates v3.3.15608.880
    link        : http://go.microsoft.com/fwlink/?LinkID=116494&updateid=0909e460-45b1-4991-80d0-f0e19b103e34
    category    : {Software, Exchange, Definition Updates}
    description : Microsoft Exchange Server Standard Anti-spam Filter Updates keep the heuristics for the anti-spam
                  content filter current.
    pubDate     : 2016-01-15 10:02:17Z
    guid        : 0909e460-45b1-4991-80d0-f0e19b103e34#2016-01-15 10:02:17Z
    
    PS > $updates | Select @{n="Version";e={($_.Title -Split "Updates ")[1]}},pubDate
    
    Version        pubDate
    -------        -------
    v3.3.15608.880 2016-01-15 10:02:17Z
    v3.3.15604.885 2015-12-31 09:14:36Z
    v3.3.15601.886 2015-12-16 08:35:28Z
    v3.3.15517.887 2015-11-26 10:01:25Z
    v3.3.15426.898 2015-10-27 08:55:15Z
    
    PS > $updates | Select @{n="Version";e={($_.Title -Split "Updates ")[1]}},pubDate -First 1
    
    Version        pubDate
    -------        -------
    v3.3.15608.880 2016-01-15 10:02:17Z
    
    
    Последнее обновление было выпущено - 2016-01-15 10:02:17Z
    
    

    up

     

    4. Последнее установленное обновление Microsoft Exchange Server Standard Anti-spam Filter Updates на сервере

    $pattern = "Microsoft Exchange Server Standard Anti-spam Filter Updates"
    $Session= New-Object -ComObject Microsoft.Update.Session
    $Search= $Session.CreateUpdateSearcher()
    $HistoryCount = $Search.GetTotalHistoryCount()
    $Updates = $Search.QueryHistory(0, $HistoryCount) | Where Title -match $pattern
    $Updates | Select @{n="Version";e={($_.Title -Split "Updates ")[1]}},Date
    
    PS > $Updates | Select @{n="Version";e={($_.Title -Split "Updates ")[1]}},Date
    
    Version         Date
    -------         ----
    v3.3.15608.880  09.02.2016 6:29:11
    v3.3.15604.885  13.01.2016 6:26:42
    v3.3.15601.886  24.12.2015 10:14:50
    v3.3.15517.887  03.12.2015 6:10:37
    v3.3.15426.898  03.11.2015 13:17:34
    
    PS > $Updates | Select @{n="Version";e={($_.Title -Split "Updates ")[1]}},Date -First 1
    
    Version        Date
    -------        ----
    v3.3.15608.880 09.02.2016 6:29:11

     

    Можно посмотреть  процесс , который выполнил установку обновления ClientApplicationID:

     

    Operation           : 1
    ResultCode          : 2
    HResult             : 0
    Title               : Microsoft Exchange Server Standard Anti-spam Filter Updates v3.3.15608.880
    Description         : Microsoft Exchange Server Standard Anti-spam Filter Updates keep the heuristics for the
                          anti-spam content filter current.
    UnmappedResultCode  : 0
    ClientApplicationID : <<PROCESS>>: powershell.exe
    ServerSelection     : 3
    ServiceID           : 7971f918-a847-4430-9279-4a52d1efe18d
    SupportUrl          : http://go.microsoft.com/fwlink/?LinkID=76087&clcid=0x409
    
    
    
    
    5. Получим список обновлений с серверов Microsoft и установим
        Microsoft Exchange Server Standard Anti-spam Filter Updates
    
    

    Есть пример Vbscript’a — Searching, Downloading, and Installing Updates , где показан пример работы.

    $pattern = "Microsoft Exchange Server Standard Anti-spam Filter Updates"
    $Session = New-Object -ComObject "Microsoft.Update.Session"
    $Searcher = $Session.CreateupdateSearcher()
    $result = $Searcher.Search("IsInstalled=0 and Type='Software'")
    $updateToInstall = New-Object -ComObject "Microsoft.Update.UpdateColl"
    $updates = $result.Updates
    if($updates) {
        $updates | Where Title -match $pattern | Foreach {
            $updateToInstall.Add($_) | Out-Null
        }
        if($updateToInstall) {
            $downloader = $Session.CreateUpdateDownloader()
            "Downloading..."
            $downloader.Updates = $updateToInstall
            $downloadResult = $downloader.Download()
            Write-OutPut "Download Result:"
            Write-OutPut $downloadResult
                    
            $installer = $Session.CreateUpdateInstaller()
            "Installing..."
            $installer.Updates = $updateToInstall
            $installationResult = $installer.Install()
            Write-OutPut "Installation Result:"
            Write-OutPut $installationResult
        }
    }

     

    Q&A:

    1. Можно ли установить Windows Management Framework 5.0 вместе с Exchange Server 2013 CU11?

    Согласно статье Exchange Server Supportability Matrix — WMF 5.0 не поддерживается. В случае если вы установите данное

    обновление, это приведет к невозможности работы с EMS. Поэтому следует удалить данное обновление для корректной работы.

    2. Можно ли удалить обновление Microsoft Exchange Server Standard Anti-spam Filter Updates?

    Данное обновление не поддерживает удаление. Т.к. свойство IsUninstallable=false.

    VARIANT_TRUE if a user can uninstall the update from a computer; otherwise, VARIANT_FALSE.

    3. Как обновить обновления ядра защиты от вредоносных программ и определения ?

    Воспользоваться инструкцией из статьи — Download engine and definition updates

    4. Есть ли модуль для работы с Windows Update?

    MVP по PowerShell Michal Gajda , за что ему Спасибо, написал модуль — Windows Update PowerShell Module.

    Для серверов у которых установлен WMF 5.0 для установки модуля лучше использовать OneGet:

    https://www.powershellgallery.com/packages/PSWindowsUpdate/1.5.2.2

    Рассмотрим 2 метода:

    1. Используя Com объект  InternetExplorer.Application
    2. Используя командлет Invoke-WebRequest (появился с версии 3.0)

     

    Учетом успешного выполнения скрипта, будет вывод сообщения – “Ваш последний визит был: время”

    1.  InternetExplorer.Application

    Определим, какие поля нам требуется заполнить, достаточно просто.

    В поля  “Имя Пользователя” и “Пароль” подставим значения  ,и посмотрим, какие  поля требуют заполнения.

    ie

    PS >  $ie.Document.forms.Item(1) | Format-Table Name,Value -Auto
    
    name                     value
    ----                     -----
    vb_login_username        abc
    cookieuser               1
    vb_login_password        abc
                             Войти
    s
    do                       login
    vb_login_md5password
    vb_login_md5password_utf

     

    Как видно выше для заполнения нужны поля vb_login_username и vb_login_password.

    
    
    # Имя пользователя
    $login = "MyUser"
    # Пароль
    $pass = "Password"
    # Адрес страницы
    $url = "http://forum.oszone.net"
    # Создание Com-объекта
    $ie = New-Object -ComObject InternetExplorer.Application
    # Переход к заданному адресу
    $ie.Navigate($url)
    # Ждем загрузки страницы
    while($ie.Busy) { Start-Sleep -Milliseconds 100 }
    
    # Объект страницы
    $doc = $ie.Document
    # Заполняем поле - Имя пользователя
    $doc.getElementsByName("vb_login_username") | % {$_.Value = $login}
    # Заполняем поле - Пароль
    $doc.getElementsByName("vb_login_password") | % {$_.Value  = $pass}
    # Снимаем галочку - Сохранить
    $doc.getElementsByName("cookieuser") | % {$_.Value  = 0}
    
    # Находим кнопку войти - нажимаем ее
    $doc.getElementsByTagName("input") | ? {$_.Value -eq "Войти"} | % {$_.Click()}
    while($ie.Busy) { Start-Sleep -Milliseconds 100 }
    # После авторизации, получаем данные
    $doc.Body.OuterText.Split("`r`n") -match "Ваш последний визит"

     

    2. Invoke-WebRequest

    Определим, какие поля нам требуется заполнить у формы.

    PS >  $r.Forms[1].Fields | Format-Table -Auto
    
    Key                      Value
    ---                      -----
    navbar_username          Имя пользователя
    cb_cookieuser_navbar     1
    vb_login_password
    s                        7f2a40e2749a7bf18b1b371
    do                       login
    vb_login_md5password
    vb_login_md5password_utf

     

    Требуется нам заполнить все поля или достаточно определенных. В этом нам поможет Fiddler.

    • Запускаем Fiddler
    • Запускаем любой браузер ( логирование командлета Invoke-WebRequest тоже поддерживается)
    • Переходим на сайт
    • Заполняем поля , нажимаем “Войти”
    • Смотрим Post запрос

    iw

     

    Поле vb_login_password – пустое, но вычисляется md5 hash пароля.

    $md5 = [Security.Cryptography.MD5]::Create()
    $bstr = [Text.Encoding]::ASCII.GetBytes("abc")
    $hash = (-join ($md5.ComputeHash($bstr) | %{ "{0:X2}" -f $_})).ToLower()
    $hash
    900150983cd24fb0d6963f7d28e17f72

    Для ASCII символов, если пароль в Unicode, данный метод не подойдет. Какой алгоритм преобразования

    пароля содержащего Unicode символы — http://forum.oszone.net/clientscript/vbulletin_md5.js?v=364

    Вычислим поле vb_login_md5password для Unicode , на примере – Привет:

    $str = -join([char[]]"Привет" | % {"&#"+ ([int][char]$_) +";"})
    (-join ($md5.ComputeHash([Text.Encoding]::ASCII.GetBytes($str))| %{ "{0:X2}" -f $_})).ToLower()

    «&#»+ [int][char]П –> 1055 + “;” = “П”

    Вычислим поле vb_login_md5password_utf для Unicode , на примере – Привет:

    $str = [int[]][char[]]"Привет" | % {$_ -band 255} 
    (-join ($md5.ComputeHash($str) | %{ "{0:X2}" -f $_})).ToLower()

    При использовании полей vb_login_md5password и vb_login_md5password_utf, поле vb_login_password – не заполняем.

    Но можно не утруждать себя данными алгоритмами, а просто заполнить поля vb_login_username и vb_login_password.

    $login = "MyUser"
    $pass = "Password"
    $url = "http://forum.oszone.net"
    $r = Invoke-WebRequest $url -SessionVariable oz
    $form = $r.Forms[1]
    $form.Fields["vb_login_username"] = $login
    $form.Fields["vb_login_password"] = $pass
    $r = Invoke-WebRequest -Uri ("$url/" + $form.Action)`
        -WebSession $oz -Method POST -Body $form.Fields
    $r = Invoke-WebRequest -Uri $url -WebSession $oz
    $r.ParsedHtml.Body.OuterText.Split("`r`n") -match "Ваш последний визит"

     

    В конечном итоге получим вывод:

    output

     

    Для примера рассмотрим два домена contoso.com и tailspintoys.com. В домене Contoso.com добавим suffix ex.com и выполним обновление в tailspintoys.com.

    Добавление UPN Suffix через ADSI:

    $domain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()            
    $dn = "cn=Partitions,cn=Configuration," + ($domain.GetDirectoryEntry()).distinguishedName 
    
    $upn = "ex.com"            
    $p = [adsi]"LDAP://$dn"            
    $p.PutEX(3, "uPNSuffixes", @($upn))            
    $p.SetInfo()

    Где  3 – APPEND. Другие значения можно посмотреть ADS_PROPERTY_OPERATION_ENUM.

    ADS_PROPERTY_APPEND

    Instructs the directory service to append the specified value(s) to the existing values(s).

    When the ADS_PROPERTY_APPEND operation is specified, the new attribute value(s) are automatically committed to the directory service and removed from the local cache.

    This forces the local cache to be updated from the directory service the next time the attribute value(s) are retrieved.

    Добавление  UPN Suffix через Set-ADObject:

    Set-ADObject -Identity $dn -Add @{upnsuffixes = $upn }  
    Добавление UPN Suffix через Set-ADForest:

    Set-ADForest -Identity (Get-ADForest) -UPNSuffixes @{add = $upn}

    Проверим, что суффикс добавился:

    PS > (Get-ADForest).UPNSuffixes test.contoso.com

     

    Проверим, обновился ли суффикс в другом лесе – нет, не обновился.

    su

     

    При нажатии кнопки Refresh, происходит обновление и мы видим, что суффикс добавился.

    su1

     

    Что происходит при нажатии на кнопку Refersh?

    Для это установим и запустим WireShark. Удалим все фильтры , выберем адаптер и нажмем – Start Capturing packet.

    В оснастке domain.msc – нажмем кнопку Refresh. Происходит вызов функции NetrGetForestTrustInformation.

    В спецификации MS-NRPC есть ее описание и метод DsrGetForestTrustInformation , который реализован в

    функциях I_NetLogonControl2 и DsGetForestTrustInformationW.

    wr

    Через I_NetLogonControl2:

    $target = "contoso.com" 
    Add-Type @"
        [DllImport("netapi32.dll", CharSet=CharSet.Unicode)] 
        public static extern int I_NetLogonControl2(
            string lpServerName, 
            uint lpFunctionCode, 
            uint lpQueryLevel, 
            ref IntPtr lpInputData, 
            out IntPtr queryInformation
        ); 
    "@ -Name "NetLogon" -Namespace Win32Functions
    
    $out = [IntPtr]::Zero
    $domain = [Runtime.InteropServices.Marshal]::StringToCoTaskMemAuto($target)
    [Win32Functions.NetLogon]::I_NetLogonControl2($null,5,2,[ref]$domain,[ref]$out)

     

    5 — NETLOGON_CONTROL_REDISCOVER

    Forces a domain controller (DC) to rediscover the specified trusted domain DC.

    Можно обойтись и без Add-Type, т.к. при запуске PowerShell загружается непубличный класс System.DirectoryServices.ActiveDirectory.UnsafeNativeMethods в котором уже определена функция I_NetLogonControl2.

    $target = "contoso.com" 
    $zero = [Runtime.InteropServices.Marshal]::StringToHGlobalUni($target)
    $ptr = [Runtime.InteropServices.Marshal]::AllocHGlobal(
        [Runtime.InteropServices.Marshal]::SizeOf([Intptr]::Zero)
    )
    [Runtime.InteropServices.Marshal]::WriteIntPtr($ptr, $zero)
    $Assembly = [AppDomain]::CurrentDomain.GetAssemblies() |
        ? {$_.GlobalAssemblyCache -And 
            $_.Location.Split('\\')[-1].Equals('System.DirectoryServices.dll') }
    $UnsafeNativeMethods = $Assembly.GetType(
        'System.DirectoryServices.ActiveDirectory.UnsafeNativeMethods'
    )
    $I_NetLogonControl2 = $UnsafeNativeMethods.GetMethod('I_NetLogonControl2')
    $I_NetLogonControl2.Invoke($null,@($null,5,2,$ptr,[IntPtr]::Zero))

    Через DsrGetForestTrustInformation:

    Add-Type @"
        [DllImport("netapi32.dll", CharSet=CharSet.Unicode)] 
        public static extern int DsGetForestTrustInformationW(
            string ServerName, 
            string TrustedDomainName, 
            uint Flags, 
            out IntPtr ForestTrustInfo
        ); 
    "@ -Name "NetLogon" -Namespace Win32Functions
    $out = [IntPtr]::Zero
    $domain = "contoso.com"
    [Win32Functions.NetLogon]::DsGetForestTrustInformationW(
        $null,$domain,1,[ref]$out
    )

    Есть еще один способ, использования без Add-Type, данная методика хорошо описана у Boe Prox — http://learn-powershell.net/2014/12/18/retrieving-a-registry-key-lastwritetime-using-powershell/

    $Domain = [AppDomain]::CurrentDomain
    $DynAssembly = New-Object System.Reflection.AssemblyName('AD_TEMP')
    $AssemblyBuilder = $Domain.DefineDynamicAssembly($DynAssembly, [System.Reflection.Emit.AssemblyBuilderAccess]::Run) 
    $ModuleBuilder = $AssemblyBuilder.DefineDynamicModule('ADModule', $False)
    
    $TypeBuilder = $ModuleBuilder.DefineType('netapi32', 'Public, Class')
      
    $PInvokeMethod = $TypeBuilder.DefineMethod(
            'DsGetForestTrustInformationW', #Method Name
            [Reflection.MethodAttributes] 'PrivateScope, Public, Static, HideBySig, PinvokeImpl', #Method Attributes
            [Int], #Method Return Type
            [Type[]] @(
                [String], #ServerName
                [String], #TrustedDomainName
                [UInt32], #Flags
                [IntPtr].MakeByRefType() #ForestTrustInfo
            ) #Method Parameters
        )
      
    $DllImportConstructor = [Runtime.InteropServices.DllImportAttribute].GetConstructor(@([String]))
    
    $FieldArray = [Reflection.FieldInfo[]] @(       
            [Runtime.InteropServices.DllImportAttribute].GetField('EntryPoint'),
            [Runtime.InteropServices.DllImportAttribute].GetField('SetLastError'),
            [Runtime.InteropServices.DllImportAttribute].GetField('CharSet')
        )
      
    $FieldValueArray = [Object[]] @(
        'DsGetForestTrustInformationW', 
        $True,
        [System.Runtime.InteropServices.CharSet]::Unicode
    )
      
    $SetLastErrorCustomAttribute = New-Object Reflection.Emit.CustomAttributeBuilder(
            $DllImportConstructor,
            @('netapi32.dll'),
            $FieldArray,
            $FieldValueArray
        )
      
    $PInvokeMethod.SetCustomAttribute($SetLastErrorCustomAttribute)
    [void]$TypeBuilder.CreateType()
    
    $domain = "contoso.com"
    [netapi32]::DsGetForestTrustInformationW($null,$domain,1,[ref][IntPtr]::Zero)
    Отслеживать

    Настройте получение новых записей по электронной почте.