Несколько месяцев тому назад я приступил к решению одной задачи. Я решил доказать, что PowerShell может использоваться как инструмент контроля безопасности. Я закончил работу над своей публикацией, в которой описывается код PowerShell, позволяющий собирать события файловой системы, выполнять некоторые базовые функции анализа, а затем выводить результаты в графическом формате. Возможно, моя платформа безопасности с использованием скриптов (Security Scripting Platform; SSP) не является минимально жизнеспособным продуктом, но она, как мне кажется, полезна в качестве простого инструмента контроля для одиночного каталога файлов.
По завершении проекта я понимал, что определенный функционал можно усовершенствовать. Обработка событий выполнялась неэф-фективно. Передача информации между различными частями платформы SSP требовала оптимизации, а информация, отображаемая с помощью примитивного кода Out-GridView, была представлена в виде прославленной таблицы.
Я решил повысить жизнеспособность платформы SSP. В первую очередь нужно было оптимизировать процедуру обработки событий. Изначально она была разработана таким образом, что обработчик в моем блоке скрипта Register-EngineEvent (см. информацию о блоках скриптов) извлекал сообщения о событиях файла и отправлял их во внутреннюю очередь, а затем — в основной фрагмент кода, ПО для классификации.
Подумав немного, я понял, что можно отправлять сообщения напрямую с помощью функции Register-EngineEvent -forward из блока скрипта обработки событий и просто удалить ненужный уровень этих жутких очередей.
Ниже представлена упрощенная и оптимизированная версия.
Затем я взял основной фрагмент кода, где выполняется классификация файлов по наличию номеров социального страхования и прочих важных ключевых слов. При поступлении событий из обработчика запускается реклассификация файлов. Затем этот код периодически отображает некоторые результаты классификации.
В последней версии я удалил классификацию в «реальном времени» и сосредоточился на очистке кода PowerShell и улучшении графики — об этом речь пойдет ниже.
В первоначальной версии я выбрал неправильный путь и доверил модулю блокировки данных PowerShell синхронизацию доступа к данным от параллельных задач, которые я применял для выполнения рутинной работы. Последующее тестирование показало, что бесплатный модуль, который реализует командлет Lock-Object, не работает.
Даже начинающие системные программисты знают, что синхронизацию проще выполнять с помощью сообщений, а не низкоуровневых блокировок. Я изменил код, и теперь, как упоминалось выше, сообщения направляются из обработчика событий непосредственно в основной цикл обработки сообщений. Иными словами: мне удалось обеспечивать обработку асинхронных событий в синхронном режиме.
Самое заметное мое достижение за последний месяц — я разобрался, как встраивать диаграммы в стиле Майкрософт в PowerShell. Другими словами, гистограммами, линейными, точечными и иными диаграммами, доступными в Excel и Word, можно управлять на программном уровне в PowerShell. Я сравнительно недавно занимаюсь программированием в PowerShell, поэтому меня это сильно порадовало. Дополнительную информацию об элементах управления .Net Framework см. в этой публикации.
Это классно! Поначалу я даже подумал, что смогу также заменить запутанный код Out-GridView.
Но вскоре стало ясно, что проблема состоит в том, что помимо этого приходится иметь дело с элементами интерактивного программи-рования, связанными с формами Майкрософт. Я же просто хотел отображать свои диаграммы .Net и не писать код для низкоуровневых функций. Существует ли упрощенный способ решения этой задачи?
После активных размышлений я пришел к выводу, что простейшим способом в данной ситуации будет запуск каждой диаграммы в ее собственной функции runspace как отдельной задачи. (Примечание для фанатов: таким образом я избавился от необходимости создания функции обработки сообщений для всех диаграмм, так как каждая из них выполняется отдельно в виде модальных диалогов.)
В этом мне также помог бесплатный модуль PowerShell, который сворачивает запутанные элементы управления диаграмм .Net. Спасибо тебе, Мариус!
Ранее я уже настроил систему управления задачами, чтобы сканировать и классифицировать каждый контролируемый файл в каталоге. И теперь для запуска диаграмм мне нужно было просто обеспечить повторное использование этого кода управления задачами.
Я создал круговую диаграмму для представления относительной концентрации конфиденциальных данных, гистограмму для разбивки файлов по типу конфиденциальных данных. Но больше всего я горжусь классической ступенчатой диаграммой событий для условий всплеска доступа к файлам, что может быть признаком атаки.

Моя отличная информационная панель. Неплохо для PowerShell
с диаграммами .Net.
Если кому-то из вас интересен основной фрагмент кода, выполняющего всю работу в моей платформе SSP, то он приведен здесь ниже:
Конечно, в рамках любой задачи главным является не сама цель, а процесс поиска решения. Согласны? В данном случае я узнал главную для себя вещь — PowerShell можно использовать для контроля безопасности. Для одиночного каталога, в небольшой системе. Причем только в случае эпизодического использования.
Я планирую внести определенные улучшения в реализацию, которую я только что представил (добавить графику в режиме реального времени), однако я вовсе не ожидаю, что мое финальное программное обеспече-ние станет чем-то большим, чем просто модельным проектом.
Контроль событий файлов, анализ и графическое отображение информации для всей системы — это крайне сложная задача для одного человека. В принципе, мое решение можно перекодировать, используя C++, но в рамках приложения по-прежнему придется решать проблему задержек и отклонений при обработке низкоуровневых событий. Чтобы сделать это правильно, необходимо иметь некие механизмы глубоко в самой ОС (для начала), а затем выполнять гораздо более серьезный анализ событий файлов, чем тот, что выполняется моим примитивным кодом. Это далеко не просто!
Обычно я заканчиваю подобные публикации категории «сделай сам» словами «вы знаете, о чем речь». Не буду вас разочаровывать.
Вы знаете, о чем речь. Наше собственное решение корпоративного класса является настоящейплатформой безопасности данных (Data Security Platform; DSP), так как оно выполняет функции классификации, анализа, обнаружения угроз и многое другое на уровне целых ИТ-систем.
Не отступайте, пробуйте свои варианты. Может быть, даже на основе этого проекта. Разбирайтесь с реальными возможностями платформы DSP, изучайте ее слабые и сильные стороны.
Есть вопросы? Не стесняйтесь и задавайте их нам!
По завершении проекта я понимал, что определенный функционал можно усовершенствовать. Обработка событий выполнялась неэф-фективно. Передача информации между различными частями платформы SSP требовала оптимизации, а информация, отображаемая с помощью примитивного кода Out-GridView, была представлена в виде прославленной таблицы.
Новые и улучшенные возможности
Я решил повысить жизнеспособность платформы SSP. В первую очередь нужно было оптимизировать процедуру обработки событий. Изначально она была разработана таким образом, что обработчик в моем блоке скрипта Register-EngineEvent (см. информацию о блоках скриптов) извлекал сообщения о событиях файла и отправлял их во внутреннюю очередь, а затем — в основной фрагмент кода, ПО для классификации.
Подумав немного, я понял, что можно отправлять сообщения напрямую с помощью функции Register-EngineEvent -forward из блока скрипта обработки событий и просто удалить ненужный уровень этих жутких очередей.
Ниже представлена упрощенная и оптимизированная версия.
1. #Count events, detect bursts, forward to main interface 2. 3. $cur = Get-Date 4. $Global:Count=0 5. $Global:baseline = @{"Monday" = @(1,1,1); "Tuesday" = @(1,.5,1);"Wednesday" = @(4,4,4);"Thursday" = @(7,12,4); "Friday" = @(5,4,6); "Saturday"=@(2,1,1); "Sunday"= @(2,4,2)} 6. $Global:cnts = @(0,0,0) 7. $Global:burst = $false 8. $Global:evarray = New-Object System.Collections.ArrayList 9. 10. $action = { 11. $Global:Count++ 12. $d=(Get-Date).DayofWeek 13. $i= [math]::floor((Get-Date).Hour/8) 14. 15. $Global:cnts[$i]++ 16. 17. 18. #event auditing! 19. 20. $rawtime = $EventArgs.NewEvent.TargetInstance.LastAccessed.Substring(8,6) 21. $filename = $EventArgs.NewEvent.TargetInstance.Name 22. $etime= [datetime]::ParseExact($rawtime,"HHmmss",$null) 23. 24. 25. $msg="$($etime)): Access of file $($filename)" 26. $msg|Out-File C:\Users\Administrator\Documents\events.log -Append 27. 28. New-Event -SourceIdentifier Delta -MessageData "Access" -EventArguments $filename #notify 29. 30. $Global:evarray.Add(@($filename,$etime)) 31. if(!$Global:burst) { 32. $Global:start=$etime 33. $Global:burst=$true 34. } 35. else { 36. if($Global:start.AddMinutes(15) -gt $etime ) { 37. $Global:Count++ 38. #File behavior analytics 39. $sfactor=2*[math]::sqrt( $Global:baseline["$($d)"][$i]) 40. 41. if ($Global:Count -gt $Global:baseline["$($d)"][$i] + 2*$sfactor) { #at 95% level of poisson 42. 43. 44. "$($etime): Burst of $($Global:Count) accesses"| Out-File C:\Users\Administrator\Documents\events.log -Append 45. $Global:Count=0 46. $Global:burst =$false 47. New-Event -SourceIdentifier Delta -MessageData "Burst" -EventArguments $Global:evarray #notify on burst 48. 49. $Global:evarray= [System.Collections.ArrayList] @() 50. } 51. } 52. else { $Global:burst =$false; $Global:Count=0; $Global:evarray= [System.Collections.ArrayList] @()} 53. } 54. } 55. 56. Register-EngineEvent -SourceIdentifier Delta -Forward 57. Register-WmiEvent -Query "SELECT * FROM __InstanceModificationEvent WITHIN 5 WHERE TargetInstance ISA 'CIM_DataFile' and TargetInstance.Path = '\\Users\\Administrator\\' and targetInstance.Drive = 'C:' and (targetInstance.Extension = 'txt' or targetInstance.Extension = 'doc' or targetInstance.Extension = 'rtf') and targetInstance.LastAccessed > '$($cur)' " -sourceIdentifier "Accessor" -Action $action 58. Write-Host "starting engine ..." 59. 60. while ($true) { 61. 62. Wait-Event -SourceIdentifier Access # just hang on this so I don't exit 63. 64. }
Затем я взял основной фрагмент кода, где выполняется классификация файлов по наличию номеров социального страхования и прочих важных ключевых слов. При поступлении событий из обработчика запускается реклассификация файлов. Затем этот код периодически отображает некоторые результаты классификации.
В последней версии я удалил классификацию в «реальном времени» и сосредоточился на очистке кода PowerShell и улучшении графики — об этом речь пойдет ниже.
В первоначальной версии я выбрал неправильный путь и доверил модулю блокировки данных PowerShell синхронизацию доступа к данным от параллельных задач, которые я применял для выполнения рутинной работы. Последующее тестирование показало, что бесплатный модуль, который реализует командлет Lock-Object, не работает.
Даже начинающие системные программисты знают, что синхронизацию проще выполнять с помощью сообщений, а не низкоуровневых блокировок. Я изменил код, и теперь, как упоминалось выше, сообщения направляются из обработчика событий непосредственно в основной цикл обработки сообщений. Иными словами: мне удалось обеспечивать обработку асинхронных событий в синхронном режиме.
PowerShell и диаграммы .Net Framework
Самое заметное мое достижение за последний месяц — я разобрался, как встраивать диаграммы в стиле Майкрософт в PowerShell. Другими словами, гистограммами, линейными, точечными и иными диаграммами, доступными в Excel и Word, можно управлять на программном уровне в PowerShell. Я сравнительно недавно занимаюсь программированием в PowerShell, поэтому меня это сильно порадовало. Дополнительную информацию об элементах управления .Net Framework см. в этой публикации.
Это классно! Поначалу я даже подумал, что смогу также заменить запутанный код Out-GridView.
Но вскоре стало ясно, что проблема состоит в том, что помимо этого приходится иметь дело с элементами интерактивного программи-рования, связанными с формами Майкрософт. Я же просто хотел отображать свои диаграммы .Net и не писать код для низкоуровневых функций. Существует ли упрощенный способ решения этой задачи?
После активных размышлений я пришел к выводу, что простейшим способом в данной ситуации будет запуск каждой диаграммы в ее собственной функции runspace как отдельной задачи. (Примечание для фанатов: таким образом я избавился от необходимости создания функции обработки сообщений для всех диаграмм, так как каждая из них выполняется отдельно в виде модальных диалогов.)
В этом мне также помог бесплатный модуль PowerShell, который сворачивает запутанные элементы управления диаграмм .Net. Спасибо тебе, Мариус!
Ранее я уже настроил систему управления задачами, чтобы сканировать и классифицировать каждый контролируемый файл в каталоге. И теперь для запуска диаграмм мне нужно было просто обеспечить повторное использование этого кода управления задачами.
Я создал круговую диаграмму для представления относительной концентрации конфиденциальных данных, гистограмму для разбивки файлов по типу конфиденциальных данных. Но больше всего я горжусь классической ступенчатой диаграммой событий для условий всплеска доступа к файлам, что может быть признаком атаки.

Моя отличная информационная панель. Неплохо для PowerShell
с диаграммами .Net.
Если кому-то из вас интересен основной фрагмент кода, выполняющего всю работу в моей платформе SSP, то он приведен здесь ниже:
1. $scan = { #file content scanner 2. $name=$args[0] 3. function scan { 4. Param ( 5. [parameter(position=1)] 6. [string] $Name 7. ) 8. $classify =@{"Top Secret"=[regex]'[tT]op [sS]ecret'; "Sensitive"=[regex]'([Cc]onfidential)|([sS]nowflake)'; "Numbers"=[regex]'[0-9]{3}-[0-9]{2}-[0-9]{3}' } 9. 10. $data = Get-Content $Name 11. 12. $cnts= @() 13. 14. if($data.Length -eq 0) { return $cnts} 15. 16. foreach ($key in $classify.Keys) { 17. 18. $m=$classify[$key].matches($data) 19. 20. if($m.Count -gt 0) { 21. $cnts+= @($key,$m.Count) 22. } 23. } 24. $cnts 25. } 26. scan $name 27. } 28. 29. 30. 31. 32. #launch a .net chart 33. function nchart ($r, $d, $t,$g,$a) { 34. 35. $task= { 36. Param($d,$t,$g,$a) 37. 38. Import-Module C:\Users\Administrator\Documents\charts.psm1 39. $chart = New-Chart -Dataset $d -Title $t -Type $g -Axis $a 40. Show-Chart $chart 41. 42. } 43. $Task = [powershell]::Create().AddScript($task).AddArgument($d).AddArgument($t).AddArgument($g).AddArgument($a) 44. $Task.RunspacePool = $r 45. $Task.BeginInvoke() 46. 47. } 48. 49. Register-EngineEvent -SourceIdentifier Delta -Action { 50. 51. if($event.MessageData -eq "Burst") { #just look at bursts 52. New-Event -SourceIdentifier File -MessageData $event.MessageData -EventArguments $event.SourceArgs 53. } 54. 55. 56. Remove-Event -SourceIdentifier Delta 57. } 58. 59. 60. 61. 62. $list=Get-WmiObject -Query "SELECT * From CIM_DataFile where Path = '\\Users\\Administrator\\' and Drive = 'C:' and (Extension = 'txt' or Extension = 'doc' or Extension = 'rtf')" 63. 64. 65. #long list --let's multithread 66. 67. #runspace 68. $RunspacePool = [RunspaceFactory]::CreateRunspacePool(1,5) 69. $RunspacePool.Open() 70. $Tasks = @() 71. 72. 73. 74. 75. foreach ($item in $list) { 76. 77. $Task = [powershell]::Create().AddScript($scan).AddArgument($item.Name) 78. $Task.RunspacePool = $RunspacePool 79. 80. $status= $Task.BeginInvoke() 81. $Tasks += @($status,$Task,$item.Name) 82. } 83. 84. 85. 86. 87. #wait 88. while ($Tasks.isCompleted -contains $false){ 89. 90. } 91. 92. 93. #Analytics, count number of sensitive content for each file 94. $obj = @{} 95. $tdcnt=0 96. $sfcnt=0 97. $nfcnt=0 98. 99. 100. for ($i=0; $i -lt $Tasks.Count; $i=$i+3) { 101. $match=$Tasks[$i+1].EndInvoke($Tasks[$i]) 102. 103. if ($match.Count -gt 0) { 104. $s = ([string]$Tasks[$i+2]).LastIndexOf("\")+1 105. 106. $obj.Add($Tasks[$i+2].Substring($s),$match) 107. for( $j=0; $j -lt $match.Count; $j=$j+2) { 108. switch -wildcard ($match[$j]) { 109. 'Top*' { $tdcnt+= 1 } 110. 111. 'Sens*' { $sfcnt+= 1} 112. 113. 'Numb*' { $nfcnt+=1} 114. 115. } 116. 117. } 118. } 119. $Tasks[$i+1].Dispose() 120. 121. } 122. 123. 124. #Display Initial Dashboard 125. #Pie chart of sensitive files based on total counts of senstive dat 126. $piedata= @{} 127. foreach ( $key in $obj.Keys) { 128. $senscnt =0 129. for($k=1; $k -lt $obj[$key].Count;$k=$k+2) { 130. $senscnt+= $obj[$key][$k] 131. 132. } 133. $piedata.Add($key, $senscnt) 134. 135. } 136. 137. 138. nchart $RunspacePool $piedata "Files with Sensitive Content" "Pie" $false 139. 140. #Bar Chart of Total Files, Sensitive vs Total 141. $bardata = @{"Total Files" = $Tasks.Count} 142. $bardata.Add("Files w. Top Secret",$tdcnt) 143. $bardata.Add("Files w. Sensitive", $sfcnt) 144. $bardata.Add("Files w. SS Numbers",$nfcnt) 145. 146. 147. nchart $RunspacePool $bardata "Sensitive Files" "Bar" $false 148. 149. 150. #run event handler as a seperate job 151. Start-Job -Name EventHandler -ScriptBlock({C:\Users\Administrator\Documents\evhandler.ps1}) 152. 153. 154. while ($true) { #main message handling loop 155. 156. [System.Management.Automation.PSEventArgs] $args = Wait-Event -SourceIdentifier File # wait on event 157. Remove-Event -SourceIdentifier File 158. #Write-Host $args.SourceArgs 159. if ($args.MessageData -eq "Burst") { 160. #Display Bursty event 161. $dt=$args.SourceArgs 162. #time in seconds 163. [datetime]$sevent =$dt[0][1] 164. 165. $xyarray = [ordered]@{} 166. $xyarray.Add(0,1) 167. for($j=1;$j -lt $dt.Count;$j=$j+1) { 168. [timespan]$diff = $dt[$j][1] - $sevent 169. $xyarray.Add($diff.Seconds,$j+1) 170. } 171. nchart $RunspacePool $xyarray "Burst Event" "StepLine" $true 172. } 173. 174. 175. }#while 176. 177. Write-Host "Done!"
Извлеченные уроки
Конечно, в рамках любой задачи главным является не сама цель, а процесс поиска решения. Согласны? В данном случае я узнал главную для себя вещь — PowerShell можно использовать для контроля безопасности. Для одиночного каталога, в небольшой системе. Причем только в случае эпизодического использования.
Я планирую внести определенные улучшения в реализацию, которую я только что представил (добавить графику в режиме реального времени), однако я вовсе не ожидаю, что мое финальное программное обеспече-ние станет чем-то большим, чем просто модельным проектом.
Контроль событий файлов, анализ и графическое отображение информации для всей системы — это крайне сложная задача для одного человека. В принципе, мое решение можно перекодировать, используя C++, но в рамках приложения по-прежнему придется решать проблему задержек и отклонений при обработке низкоуровневых событий. Чтобы сделать это правильно, необходимо иметь некие механизмы глубоко в самой ОС (для начала), а затем выполнять гораздо более серьезный анализ событий файлов, чем тот, что выполняется моим примитивным кодом. Это далеко не просто!
Обычно я заканчиваю подобные публикации категории «сделай сам» словами «вы знаете, о чем речь». Не буду вас разочаровывать.
Вы знаете, о чем речь. Наше собственное решение корпоративного класса является настоящейплатформой безопасности данных (Data Security Platform; DSP), так как оно выполняет функции классификации, анализа, обнаружения угроз и многое другое на уровне целых ИТ-систем.
Не отступайте, пробуйте свои варианты. Может быть, даже на основе этого проекта. Разбирайтесь с реальными возможностями платформы DSP, изучайте ее слабые и сильные стороны.
Есть вопросы? Не стесняйтесь и задавайте их нам!

