functionGet-LoggedCode { # to speed up SID-to-user translation, # we use a hash table with already translated SIDs # it is empty at first $translateTable = @{}
# read all raw events $logInfo = @{ ProviderName="Microsoft-Windows-PowerShell"; Id = 4104 } Get-WinEvent-FilterHashtable$logInfo | # take each raw set of data... ForEach-Object { # store the code in this entry
# if this is the first part, take it if ($_.Properties[0].Value -eq1) { $code = $_.Properties[2].Value } # else, add it to the $code variable else { $code += $_.Properties[2].Value }
# return the object when all parts have been processed if ($_.Properties[0].Value -eq$_.Properties[1].Value) { # turn SID into user $userSID = $_.UserId
# if the cache does not contain the user SID yet... if (!$translateTable.ContainsKey($userSid)) { try { # ...try and turn it into a real name, and add it # to the cache $identifier = New-Object System.Security.Principal.SecurityIdentifier($userSID) $result = $identifier.Translate( [System.Security.Principal.NTAccount]).Value $translateTable[$userSid] = $result } catch { # if this fails, use the SID instead of a real name $translateTable[$userSid] = $userSID } } else {
}
# create a new object and extract the interesting # parts from the raw data to compose a "cooked" # object with useful data [PSCustomObject]@{ # when this was logged Time = $_.TimeCreated # script code that was logged Code = $code # path of script file (this is empty for interactive # commands) Path = $_.Properties[4].Value # log level # by default, only level "Warning" will be logged Level = $_.LevelDisplayName # user who executed the code # take the real user name from the cache of translated # user names User = $translateTable[$userSID] } } } }