При просмотре событий журнала в Event Viewer, вкладка General содержит тело события в удобном для понимая виде и в зависимости от локализации ОС,по возможности, отображает строки в заданной локализации.
В нашем примере событие с номером 4663 содержит поле Accesses , которое имеет значение в удобном для нас виде — DELETE.
При переключении на вкладку Details и выбором формата XML, получаем значение формата %%N( N – число). Что является неудобным для понимания.
В нашем примере 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.
Получим значения ключа реестра 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
Примерный вывод:
С помощью редактора ресурсов, например Resource Hacker, посмотрим, что содержит C:\Windows\System32\MsObjs.dll ( System\DS используется для события 4663).
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
$size = [System.Runtime.InteropServices.Marshal]::SizeOf([System.Type] $type)$obj = [System.Runtime.InteropServices.Marshal]::PtrToStructure($ptr, [System.Type] $type)
Пример выполнения:
Касаемо события 4663, можно легко обойтись без получение данных из ресурса. Поле Message содержит уже преобразованное значение, Access Mask легко привести к типу Security.AccessControl.FileSystemRights.
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)