├── Analyze-ProcmonLog.ps1 ├── Malware Analysis.PMF └── README.md /Analyze-ProcmonLog.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .Synopsis 3 | Script to parse Process Monitor XML log file, and give you a summary report. 4 | The report conaints sections dedicated to Processes Created, File Activity, Registry Activity, Network Traffic, and Unique Hosts 5 | Autor: Moti Bani, contact: Moti.Bani@Microsoft.com 6 | .DESCRIPTION 7 | Instructions to prepare the Process Monitor trace this script requires: 8 | Start Procmon. 9 | Stop the Procmon trace. 10 | Add an Include filter for "Result is SUCCESS". 11 | Save the trace: 12 | * Events displayed using current filter 13 | * DO NOT SELECT Also include profiling events 14 | * Format XML - do not check the stack traces or stack symbols options 15 | .EXAMPLE 16 | .\Analyze-ProcmonLog.ps1 .\Logfile.XML 17 | 18 | LEGAL DISCLAIMER 19 | This Sample Code is provided for the purpose of illustration only and is not 20 | intended to be used in a production environment. THIS SAMPLE CODE AND ANY 21 | RELATED INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER 22 | EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF 23 | MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE. We grant You a 24 | nonexclusive, royalty-free right to use and modify the Sample Code and to 25 | reproduce and distribute the object code form of the Sample Code, provided 26 | that You agree: (i) to not use Our name, logo, or trademarks to market Your 27 | software product in which the Sample Code is embedded; (ii) to include a valid 28 | copyright notice on Your software product in which the Sample Code is embedded; 29 | and (iii) to indemnify, hold harmless, and defend Us and Our suppliers from and 30 | against any claims or lawsuits, including attorneys’ fees, that arise or result 31 | from the use or distribution of the Sample Code. 32 | 33 | This posting is provided "AS IS" with no warranties, and confers no rights. Use 34 | of included script samples are subject to the terms specified 35 | at http://www.microsoft.com/info/cpyright.htm. 36 | #> 37 | 38 | 39 | # ---------------------------------------------------------------------- 40 | # Helper functions 41 | # ---------------------------------------------------------------------- 42 | 43 | # Remove invalid characters and normalize the path 44 | Function NormalizeFileName ([string]$rawPath) 45 | { 46 | $rawPath = $rawPath.Replace("\??\",'') 47 | 48 | $invalidChars = [IO.Path]::GetInvalidPathChars() -join '' 49 | $EscapeChars = "[{0}]" -f [RegEx]::Escape($invalidChars) 50 | return ($rawPath -replace $EscapeChars) 51 | } 52 | 53 | # Split the executable name from the arguments 54 | Function SplitCommandLine([string]$rawCommandLine) 55 | { 56 | if($rawCommandLine -match [RegEx]'(?i)([a-z]):.(?:[^\\/:*?"<>|\r\n]+\\)*[^\\/:*?"<>|\r\n]*\.\w{3}') 57 | { 58 | $Matches[0] 59 | } 60 | # non-standard command line, try to split 61 | else 62 | { 63 | $rawCommandLine.Split()[0] 64 | } 65 | } 66 | 67 | Function isNumeric ($x) { 68 | $x2 = 0 69 | $isNum = [System.Int32]::TryParse($x, [ref]$x2) 70 | return $isNum 71 | } 72 | 73 | #simple output of the report to console 74 | Function ConsoleWrite() 75 | { 76 | if($ProcessesActivity.Count -gt 0) 77 | { 78 | Write-Host "Processes Created:" 79 | Write-Host "==================" -NoNewline 80 | $ProcessesActivity | Format-Table @{n='Time';e={'{0}' -f $_.Time.ToShortTimeString()}},@{n='Parent Process';e={'{0} ({1})' -f $_.ParentProcessName, $_.ParentProcessPID}},@{n='Process';e={'{0} ({1})' -f $_.ChildProcessName, $_.ChildProcessPID}} -AutoSize 81 | } 82 | 83 | if ($FilesCreatedList.count -gt 0) 84 | { 85 | Write-Host "Files Created:" 86 | Write-Host "==================" -NoNewline 87 | $FilesCreatedList | Format-Table @{n='Time';e={'{0}' -f $_.Time.ToShortTimeString()}},@{n='Process';e={'{0}' -f $_.ProcessName}},@{n='Path';e={'{0}' -f $_.Path }} 88 | } 89 | 90 | if ($FilesRenamedList.count -gt 0) 91 | { 92 | Write-Host "Files Renamed:" 93 | Write-Host "==================" -NoNewline 94 | $FilesRenamedList | Format-Table @{n='Time';e={'{0}' -f $_.Time.ToShortTimeString()}},@{n='Process';e={'{0}' -f $_.ProcessName}},@{n='Path';e={'{0}' -f $_.Path }} 95 | } 96 | 97 | if ($FileDeletedList.count -gt 0) 98 | { 99 | Write-Host "Files Deleted:" 100 | Write-Host "==================" -NoNewline 101 | $FileDeletedList | Format-Table @{n='Time';e={'{0}' -f $_.Time.ToShortTimeString()}},@{n='Process';e={'{0}' -f $_.ProcessName}},@{n='Path';e={'{0}' -f $_.Path }} 102 | } 103 | 104 | 105 | if ($RegCreateKeysList.count -gt 0) 106 | { 107 | Write-Host "Registry Created:" 108 | Write-Host "==================" -NoNewline 109 | $RegCreateKeysList | Format-Table @{n='Time';e={'{0}' -f $_.Time.ToShortTimeString()}},@{n='Process';e={'{0}' -f $_.ProcessName}},@{n='Path';e={'{0}' -f $_.Path }} 110 | } 111 | 112 | if ($RegSetValuesList.count -gt 0) 113 | { 114 | Write-Host "Registry Writes:" 115 | Write-Host "==================" -NoNewline 116 | $RegSetValuesList | Sort-Object -Property Path, Value -Unique | Format-Table @{n='Time';e={'{0}' -f $_.Time.ToShortTimeString()}},@{n='Process';e={'{0}' -f $_.ProcessName}},@{n='Path';e={'{0}={1}' -f $_.Path, $_.Value}} 117 | } 118 | 119 | if ($RegDeleteValuesList.count -gt 0) 120 | { 121 | Write-Host "Registry Deletes:" 122 | Write-Host "==================" -NoNewline 123 | $RegDeleteValuesList | Format-Table @{n='Time';e={'{0}' -f $_.Time.ToShortTimeString()}},@{n='Process';e={'{0}' -f $_.ProcessName}},@{n='Path';e={'{0}' -f $_.Path}} 124 | } 125 | 126 | if ($NetworkTraffic.count -gt 0) 127 | { 128 | Write-Host "Network Traffic:" 129 | Write-Host "==================" -NoNewline 130 | $NetworkTraffic | Format-Table @{n='Time';e={'{0}' -f $_.Time.ToShortTimeString()}},@{n='Protocol';e={'{0}' -f $_.Protocol}},@{n='Process';e={'{0}' -f $_.ProcessName}},@{n='Path';e={'{0}' -f $_.Path}} 131 | } 132 | 133 | if ($RemoteServers.count -gt 0) 134 | { 135 | Write-Host "Unique Hosts:" 136 | Write-Host "==================" 137 | $RemoteServers | Select-Object -Unique 138 | } 139 | } 140 | 141 | function Analyze-ProcmonLog 142 | { 143 | [CmdletBinding()] 144 | [Alias()] 145 | [OutputType([int])] 146 | Param 147 | ( 148 | # Specifies the name of the input Procmon XML file. 149 | [parameter(Mandatory=$true)] 150 | [String] 151 | $ProcmonXmlFile 152 | ) 153 | 154 | # ---------------------------------------------------------------------- 155 | # Collections 156 | # ---------------------------------------------------------------------- 157 | 158 | $ProcessesActivity = @() 159 | $FilesCreatedList = @() 160 | $FilesRenamedList = @() 161 | $FileDeletedList = @() 162 | $RegCreateKeysList = @() 163 | $RegDeleteValuesList = @() 164 | $RegSetValuesList = @() 165 | $NetworkTraffic = @() 166 | $RemoteServers = @() 167 | $ProcessesArray = @() 168 | 169 | # ---------------------------------------------------------------------- 170 | # Filters 171 | # ---------------------------------------------------------------------- 172 | 173 | # remove noisy Windows' processes, set UseFilteredProcesses to $false to include all process names 174 | $FilteredProcesses = @('SearchProtocolHost.exe', 'ngen.exe','SearchFilterHost.exe','wmiadap.exe','DllHost.exe','SearchIndexer.exe') 175 | $UseFilteredProcesses = $true 176 | 177 | 178 | # Build the list of events 179 | $inputFile = [xml](Get-Content $ProcmonXmlFile) 180 | $Events = $inputFile.procmon.eventlist.event 181 | 182 | # Main loop 183 | Foreach($Event in $Events) { 184 | switch ($Event.Operation) 185 | { 186 | "Process Create" { 187 | $currentPID = $Event.Detail.Substring(5,$Event.Detail.IndexOf(",")-5) 188 | $currentCommandLine = $Event.Detail.Substring($Event.Detail.IndexOf(",")+16) 189 | $ParentPID = $Event.PID 190 | $ParentCommandLine = NormalizeFileName(SplitCommandLine($Event.Process_Name)) 191 | $EXE = NormalizeFileName(SplitCommandLine $currentCommandLine) 192 | $CreateTime = [DateTime]$Event.Time_of_Day 193 | if($UseFilteredProcesses) 194 | { 195 | if($FilteredProcesses -notcontains [System.IO.Path]::GetFileName($EXE)) 196 | { 197 | $obj = new-object psobject -Property @{ 198 | Time = $CreateTime 199 | ChildProcessName = $currentCommandLine#[System.IO.Path]::GetFileName($EXE) 200 | ChildProcessPID = $currentPID 201 | ParentProcessName = $ParentCommandLine 202 | ParentProcessPID = $ParentPID 203 | } 204 | $ProcessesActivity += $obj 205 | } 206 | } 207 | } 208 | 209 | "CreateFile" { 210 | if ($Event.Result -eq 'SUCCESS') 211 | { 212 | $currentProcess = $Event.Process_Name 213 | $currentPath = $Event.Path 214 | $CreateTime = [DateTime]$Event.Time_of_Day 215 | 216 | if($FilteredProcesses -notcontains $currentProcess) 217 | { 218 | $obj = new-object psobject -Property @{ 219 | Time = $CreateTime 220 | ProcessName = $currentProcess 221 | Path = $currentPath 222 | } 223 | $FilesCreatedList += $obj 224 | } 225 | } 226 | 227 | } 228 | 229 | "SetRenameInformationFile" { 230 | if ($Event.Result -eq 'SUCCESS') 231 | { 232 | $currentProcess = $Event.Process_Name 233 | $currentPath = $Event.Path 234 | $CreateTime = [DateTime]$Event.Time_of_Day 235 | 236 | if($FilteredProcesses -notcontains $currentProcess) 237 | { 238 | $obj = new-object psobject -Property @{ 239 | Time = $CreateTime 240 | ProcessName = $currentProcess 241 | Path = $currentPath 242 | } 243 | $FilesRenamedList += $obj 244 | } 245 | } 246 | 247 | } 248 | 249 | "SetDispositionInformationFile" { 250 | if ($Event.Result -eq 'SUCCESS') 251 | { 252 | $currentProcess = $Event.Process_Name 253 | $currentPath = $Event.Path 254 | $CreateTime = [DateTime]$Event.Time_of_Day 255 | 256 | if($FilteredProcesses -notcontains $currentProcess) 257 | { 258 | $obj = new-object psobject -Property @{ 259 | Time = $CreateTime 260 | ProcessName = $currentProcess 261 | Path = $currentPath 262 | } 263 | $FileDeletedList += $obj 264 | } 265 | } 266 | 267 | } 268 | 269 | "RegCreateKeysListKey" { 270 | if ($Event.Result -eq 'SUCCESS') 271 | { 272 | $currentProcess = $Event.Process_Name 273 | $currentPath = $Event.Path 274 | $CreateTime = [DateTime]$Event.Time_of_Day 275 | 276 | if($FilteredProcesses -notcontains $currentProcess) 277 | { 278 | $obj = new-object psobject -Property @{ 279 | Time = $CreateTime 280 | ProcessName = $currentProcess 281 | Path = $currentPath 282 | } 283 | $RegCreateKeysList += $obj 284 | } 285 | } 286 | 287 | } 288 | 289 | "RegSetValuesListValue" { 290 | if ($Event.Result -eq 'SUCCESS') 291 | { 292 | $currentProcess = $Event.Process_Name 293 | $currentPath = $Event.Path 294 | $CreateTime = [DateTime]$Event.Time_of_Day 295 | 296 | if($FilteredProcesses -notcontains $currentProcess) 297 | { 298 | $LengthLocation = $Event.Detail.IndexOf("Length:")+8 299 | $CommaLocation = $Event.Detail.IndexOf(",",$LengthLocation) 300 | if ($CommaLocation -gt 0) 301 | { 302 | $DataLength = $Event.Detail.Substring($LengthLocation, $Event.Detail.IndexOf(",",$LengthLocation)-$LengthLocation) 303 | } 304 | else 305 | { 306 | $DataLength = '' 307 | } 308 | 309 | 310 | if (isNumeric($DataLength)) 311 | { 312 | $strValue = $Event.Detail.Substring($Event.Detail.IndexOf("Data: ")+6) 313 | $obj = new-object psobject -Property @{ 314 | Time = $CreateTime 315 | ProcessName = $currentProcess 316 | Value = $strValue 317 | Path = $currentPath 318 | } 319 | 320 | } 321 | else 322 | { 323 | $obj = new-object psobject -Property @{ 324 | Time = $CreateTime 325 | ProcessName = $currentProcess 326 | Value = '' 327 | Path = $currentPath 328 | } 329 | } 330 | $RegSetValuesList += $obj 331 | } 332 | } 333 | 334 | } 335 | 336 | "RegDeleteValue" { 337 | 338 | $currentProcess = $Event.Process_Name 339 | $currentPath = $Event.Path 340 | $CreateTime = [DateTime]$Event.Time_of_Day 341 | 342 | if($FilteredProcesses -notcontains $currentProcess) 343 | { 344 | 345 | 346 | $obj = new-object psobject -Property @{ 347 | Time = $CreateTime 348 | ProcessName = $currentProcess 349 | Path = $currentPath 350 | } 351 | 352 | $RegDeleteValuesList += $obj 353 | } 354 | } 355 | 356 | "RegDeleteKey" { 357 | 358 | $currentProcess = $Event.Process_Name 359 | $currentPath = $Event.Path 360 | $CreateTime = [DateTime]$Event.Time_of_Day 361 | 362 | if($FilteredProcesses -notcontains $currentProcess) 363 | { 364 | 365 | 366 | $obj = new-object psobject -Property @{ 367 | Time = $CreateTime 368 | ProcessName = $currentProcess 369 | Path = $currentPath 370 | } 371 | 372 | $RegDeleteValuesList += $obj 373 | } 374 | } 375 | 376 | "UDP Send" { 377 | 378 | $currentProcess = $Event.Process_Name 379 | $currentPath = $Event.Path 380 | $CreateTime = [DateTime]$Event.Time_of_Day 381 | $RemoteServers += $currentPath.Split()[2].split(":")[0] 382 | if($FilteredProcesses -notcontains $currentProcess) 383 | { 384 | 385 | 386 | $obj = new-object psobject -Property @{ 387 | Time = $CreateTime 388 | ProcessName = $currentProcess 389 | Protocol = "UDP" 390 | Path = $currentPath 391 | } 392 | 393 | $NetworkTraffic += $obj 394 | } 395 | } 396 | 397 | "UDP Receive" { 398 | 399 | $currentProcess = $Event.Process_Name 400 | $currentPath = $Event.Path 401 | $CreateTime = [DateTime]$Event.Time_of_Day 402 | $RemoteServers += $currentPath.Split()[2].split(":")[0] 403 | 404 | if($FilteredProcesses -notcontains $currentProcess) 405 | { 406 | 407 | 408 | $obj = new-object psobject -Property @{ 409 | Time = $CreateTime 410 | ProcessName = $currentProcess 411 | Protocol = "UDP" 412 | Path = $currentPath 413 | } 414 | 415 | $NetworkTraffic += $obj 416 | } 417 | } 418 | 419 | "TCP Send" { 420 | 421 | $currentProcess = $Event.Process_Name 422 | $currentPath = $Event.Path 423 | $CreateTime = [DateTime]$Event.Time_of_Day 424 | $RemoteServers += $currentPath.Split()[2].split(":")[0] 425 | 426 | if($FilteredProcesses -notcontains $currentProcess) 427 | { 428 | 429 | 430 | $obj = new-object psobject -Property @{ 431 | Time = $CreateTime 432 | ProcessName = $currentProcess 433 | Protocol = "TCP" 434 | Path = $currentPath 435 | } 436 | 437 | $NetworkTraffic += $obj 438 | } 439 | } 440 | 441 | "TCP Receive" { 442 | 443 | $currentProcess = $Event.Process_Name 444 | $currentPath = $Event.Path 445 | $CreateTime = [DateTime]$Event.Time_of_Day 446 | $RemoteServers += $currentPath.Split()[2].split(":")[0] 447 | 448 | if($FilteredProcesses -notcontains $currentProcess) 449 | { 450 | 451 | 452 | $obj = new-object psobject -Property @{ 453 | Time = $CreateTime 454 | ProcessName = $currentProcess 455 | Protocol = "TCP" 456 | Path = $currentPath 457 | } 458 | 459 | $NetworkTraffic += $obj 460 | } 461 | } 462 | } 463 | } # end ForEach 464 | ConsoleWrite 465 | } -------------------------------------------------------------------------------- /Malware Analysis.PMF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MotiBa/ProcessMonitorAnalyzeMalware/2b4a6e7eac7750f0afd911ca02aebca219c08225/Malware Analysis.PMF -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ProcessMonitorAnalyzeMalware 2 | Script to parse Process Monitor XML log file, and give you a summary report. 3 | The report conaints sections dedicated to Processes Created, File Activity, Registry Activity, Network Traffic, and Unique Hosts 4 | 5 | Instructions to prepare the Process Monitor trace this script requires: 6 | Start Procmon. 7 | Stop the Procmon trace. 8 | Add an Include filter for "Result is SUCCESS". 9 | Save the trace: 10 | * Events displayed using current filter 11 | * DO NOT SELECT Also include profiling events 12 | * Format XML - do not check the stack traces or stack symbols options 13 | 14 | EXAMPLE: 15 | .\Analyze-ProcmonLog.ps1 .\Logfile.XML 16 | --------------------------------------------------------------------------------