├── ConvertTo-LogonTracer.ps1 ├── Get-DoSvc4n6.ps1 ├── README.md └── bulk_downloader.ps1 /ConvertTo-LogonTracer.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | *********************************** LICENSE *********************************** 3 | This program is free software: you can redistribute it and/or modify 4 | it under the terms of the GNU General Public License as published by 5 | the Free Software Foundation, either version 3 of the License, or 6 | any later version. 7 | This program is distributed in the hope that it will be useful, 8 | but WITHOUT ANY WARRANTY; without even the implied warranty of 9 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 | GNU General Public License for more details. 11 | You can view the GNU General Public License at 12 | WARNING: This program is provided "as-is" 13 | ******************************************************************************* 14 | #> 15 | 16 | <# 17 | .SYNOPSIS 18 | This script converts an XML file created with EvtxECmd to an XML that can be imported into LogonTracer. 19 | .DESCRIPTION 20 | This script converts an XML file created with EvtxECmd to an XML that can be imported into LogonTracer. It can also split the input file into smaller XML files and can search for keywords. Based on tests: ~320MB XML processed in ~16min. 21 | 22 | Test environment: 23 | (OS) Windows 10 Pro ENG 21H2 24 | # EvtxECmd (v1.0.0.0) 25 | # Powershell 7.2.5 / Powershell 5.1 26 | (OS) REMnux v7 Focal (Ubuntu 20.04.1 LTS) 27 | # LogonTracer (Dockerfile: Dec 19, 2021) | https://github.com/JPCERTCC/LogonTracer/tree/master/docker 28 | .PARAMETER InputXML 29 | Path to the XML source file to parse. No other file formats are currently supported by the script. 30 | .PARAMETER OutputPath 31 | Output path 32 | .PARAMETER SplitAt 33 | Maximum number of events per XML ouput file 34 | .PARAMETER Keywords 35 | Comma separated keywords to search. The search is case insensitive and the keywords are searched as substrings. 36 | .EXAMPLE 37 | First parse the EVTX files with EvtxECmd and save the ouput in XML format. Examples: 38 | EvtxECmd.exe -d --xml --xmlf evtxecmd.xml --inc 4624,4625,4768,4769,4776,4672 39 | EvtxECmd.exe -d --xml --xmlf evtxecmd.xml --inc 4624,4625,4768,4769,4776,4672 --sd "2020-12-06 00:00:00" --ed "2020-12-07 00:00:00" 40 | 41 | Then run the script to make the XML file compatible with LogonTracer. Examples: 42 | 43 | ConvertTo-LogonTracer.ps1 -InputXML .\evtxecmd.xml 44 | ConvertTo-LogonTracer.ps1 -InputXML .\evtxecmd.xml -SplitAt 10000 -Keywords keyword1,keyword2,keyword3 45 | 46 | .NOTES 47 | Author : Gabriele Zambelli 48 | Twitter : @gazambelli 49 | 50 | CHANGELOG 51 | 2022-07-16: It's no longer needed to manually edit the script to set it up 52 | 2020-12-07: First release 53 | .LINK 54 | GitHub : https://github.com/forensenellanebbia/powershell-scripts 55 | Blog post : https://forensenellanebbia.blogspot.com/2020/12/lets-combine-evtxecmd-with-logontracer.html 56 | #> 57 | 58 | [CmdletBinding()] 59 | Param ( 60 | [Parameter(Position=0,Mandatory = $True)][string]$InputXML, 61 | [Parameter(Position=1,Mandatory = $False)][string]$OutputPath = (Get-ChildItem $InputXML).DirectoryName, 62 | [Parameter(Position=2,Mandatory = $False)][int]$SplitAt = 5000, 63 | [Parameter(Position=2,Mandatory = $False)][string[]]$Keywords = "" 64 | ) 65 | 66 | $InputXML = (Get-ChildItem $InputXML).FullName 67 | 68 | function New-File ($DstPartNumber){ 69 | $DstFile = $OutputPath + "\" + $DstFileTemplateName + "_" + $Suffix + "_" + $DstPartNumber + ".xml" 70 | return $DstFile 71 | } 72 | 73 | $StartTimeNoFormat = Get-Date 74 | $StartTime = Get-Date -date $StartTimeNoFormat -format "yyyy-MM-dd HH:mm:ss" 75 | $Suffix = Get-Date -date $StartTimeNoFormat -format "yyyyMMdd_HHmmss" 76 | $DstFileTemplateName = "EvtxECmd_LogonTracer" 77 | 78 | Write-Host "`n*** ConvertTo-LogonTracer (v2022-07-16) *** " 79 | Write-Host "Start Time : $StartTime" 80 | Write-Host "`nINPUT" 81 | Write-Host "Source File : $InputXML" 82 | 83 | $SrcNumberLines = [Linq.Enumerable]::Count([System.IO.File]::ReadLines($InputXML)) 84 | $SrcNumberLine = 0 85 | $DstNumberLines = 1 86 | $DstPartNumber = 1 87 | $XmlHeader = '' 88 | $XmlFooter = "" 89 | 90 | Write-Host "Lines to parse : $SrcNumberLines" 91 | Write-Host "`nOUTPUT" 92 | Write-Host "Keywords to search : $(($Keywords -notlike '').Count)" 93 | Write-Host "Max events per file: $SplitAt" 94 | Write-Host "Output path : $OutputPath" 95 | Write-Host "Output file(s) : $($DstFileTemplateName+'_'+$Suffix+'_###.xml')" 96 | 97 | $KeywordMatches = 0 98 | 99 | foreach($SrcLine in [System.IO.File]::ReadLines($InputXML)){ 100 | $SrcNumberLine++ 101 | $StepPercentage = [int]($SrcNumberLines/100) 102 | if($SrcNumberLine -eq 1){ 103 | Write-Host "`nProgress: " -NoNewline 104 | } 105 | if(($SrcNumberLine % $StepPercentage) -eq 0){ 106 | Write-Host "$(($SrcNumberLine/$SrcNumberLines).tostring("P")).." -NoNewline 107 | } 108 | $DstFile = New-File($DstPartNumber) 109 | if($SrcLine -eq ""){ 110 | $MergedElements = $SrcLine 111 | } 112 | else{ 113 | $MergedElements += $SrcLine -join ("") 114 | if($SrcLine -eq ""){ 115 | $MergedElements = $MergedElements -replace "","" -replace ">[ ]*<","><" 116 | if(($Keywords -notlike '').Count -gt 0){ 117 | $KeywordFound = 0 118 | $Keywords | ForEach-Object{ 119 | if($MergedElements -match $_){ 120 | $KeywordFound++ 121 | $KeywordMatches++ 122 | } 123 | } 124 | } 125 | if(($Keywords -notlike '').Count -eq 0 -or $KeywordFound -gt 0){ 126 | if($DstNumberLines -eq 1){ 127 | Add-Content $DstFile -Value $XmlHeader -NoNewline 128 | Add-Content $DstFile -Value $MergedElements -NoNewline 129 | $DstNumberLines++ 130 | } 131 | elseif($DstNumberLines -ne 1 -and $DstNumberLines -lt $SplitAt){ 132 | try { 133 | Add-Content $DstFile -Value $MergedElements -NoNewline 134 | } 135 | catch { 136 | Start-Sleep -Seconds 2 137 | Add-Content $DstFile -Value $MergedElements -NoNewline 138 | } 139 | if($SrcNumberLine -eq $SrcNumberLines){ 140 | Add-Content $DstFile -Value $XmlFooter -NoNewline 141 | } 142 | $DstNumberLines++ 143 | } 144 | elseif($DstNumberLines -eq $SplitAt){ 145 | Add-Content $DstFile -Value $XmlFooter -NoNewline 146 | $DstPartNumber++ 147 | $DstFile = New-File($DstPartNumber) 148 | $DstNumberLines = 2 149 | Add-Content $DstFile -Value $XmlHeader -NoNewline 150 | Add-Content $DstFile -Value $MergedElements -NoNewline 151 | } 152 | } 153 | elseif(($Keywords -notlike '').Count -gt 0 -and $KeywordFound -eq 0){ 154 | if($SrcNumberLine -eq $SrcNumberLines){ 155 | Add-Content $DstFile -Value $XmlFooter -NoNewline 156 | } 157 | } 158 | } 159 | } 160 | } 161 | 162 | Write-Host "Done!" 163 | $EndTime = Get-Date -format "yyyy-MM-dd HH:mm:ss" 164 | 165 | if(($Keywords -notlike '').Count -gt 0){ 166 | Write-Host "`nNumber of matches : $KeywordMatches" 167 | } 168 | 169 | Write-Host "`nEnd Time: $EndTime" 170 | -------------------------------------------------------------------------------- /Get-DoSvc4n6.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | *********************************** LICENSE *********************************** 3 | This program is free software: you can redistribute it and/or modify 4 | it under the terms of the GNU General Public License as published by 5 | the Free Software Foundation, either version 3 of the License, or 6 | any later version. 7 | 8 | This program is distributed in the hope that it will be useful, 9 | but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | GNU General Public License for more details. 12 | 13 | You can view the GNU General Public License at 14 | 15 | WARNING: This program is provided "as-is" 16 | ******************************************************************************* 17 | #> 18 | 19 | <# 20 | .SYNOPSIS 21 | Extracts different categories of events from DoSvc ETL logs 22 | .DESCRIPTION 23 | Extracts different categories of events from DoSvc ETL logs. 24 | Depending on the version of Win10, Delivery Optimization Logs are stored by default in the path: 25 | Win10 1507 | C:\Windows\Logs\dosvc 26 | Win10 1709+ | C:\Windows\ServiceProfiles\NetworkService\AppData\Local\Microsoft\Windows\DeliveryOptimization\Logs 27 | 28 | Test environment: (OS) Windows 10 Pro ENG (version 1803/1809/1903/21H1) + Powershell 5.1 + Powershell 7.1 29 | Script tested against logs collected from: 30 | - Windows 10 Home x86 ENG (version 1507) 31 | - Windows 10 Pro x64 ENG (version 1709/1803/1809/1903/21H1) 32 | - Windows 10 Pro x64 ITA (version 1809/1903) 33 | .PARAMETER DiskInfo 34 | Extracts the events containing information about computer disk space 35 | .PARAMETER CaptivePortal 36 | Extracts all events containing "Captive Portal detected on network" 37 | .PARAMETER ExternalIP 38 | Extracts all the external/public IP addresses assigned by an Internet Service Provider (ISP) 39 | .PARAMETER ExtractAll 40 | If used, the script will extract DiskInfo + CaptivePortal + ExternalIP + InternalIP + LocalPeers + PowerState + ShutdownTime events. It won't do any geolocation. 41 | .PARAMETER GeolocationAPI 42 | Allows to choose among a few APIs. If needed, use -TokenAPI to provide the authorization token. 43 | .PARAMETER TokenAPI 44 | Allows to provide the API token for the geolocation service to be used 45 | .PARAMETER InternalIP 46 | Extracts all the internal/private IP addresses assigned to the computer 47 | .PARAMETER LocalPeers 48 | Extracts the private IP addresses of "PCs on my local network". These events are stored in the ETL logs if the device has the following option turned ON: 49 | WindowsUpdate | Advanced Option | DeliveryOptimization | Allow downloads from other PCs 50 | .PARAMETER LogPath 51 | Path containing the DoSvc .etl files to analyze. LogPath can also be a filename. The script will search recursively by default. 52 | .PARAMETER OutputPath 53 | Path where the output files will be written to. By default it's the current working directory. 54 | .PARAMETER PowerState 55 | Extracts Battery Status events 56 | .PARAMETER ShutdownTime 57 | Extracts Shutdown events (from ETL logs) 58 | .PARAMETER SkipIP2Location 59 | If used, the script won't perform any IP geolocation lookup. 60 | .PARAMETER TimeFormat 61 | Allows to choose between UTC (default) or LocalTime 62 | .EXAMPLE 63 | Get-DoSvc4n6.ps1 dosvc.20181212_133942_927.etl -ExtractAll 64 | 65 | This command parses a single file and extracts events for all categories. 66 | .EXAMPLE 67 | Get-DoSvc4n6.ps1 C:\CustomLogPath -ExternalIP -SkipIP2Location -OutputPath C:\CustomOutputPath 68 | 69 | This command recursively parses a path, extracts external/public IP addresses, doesn't do any IP location lookup and saves the output files to a custom path. 70 | .EXAMPLE 71 | Get-DoSvc4n6.ps1 C:\CustomLogPath -InternalIP -PowerState -ExternalIP -GeolocationAPI ipwhois -TimeFormat LocalTime 72 | 73 | This command recursively parses a custom path, extracts the events related to the selected categories, uses the ipwhois API and shows timestamps in local time. 74 | .NOTES 75 | Author : Gabriele Zambelli 76 | Twitter : @gazambelli 77 | 78 | CHANGELOG 79 | 2021-08-14: [ FIXED ] Minor bug fixes 80 | 2021-08-13: [CHANGED] Removed KeyCDN 81 | [ FIXED ] ipinfo.io requires a token in the URL 82 | [ NEW ] Added the parameter TokenAPI to provide the needed token 83 | 2019-08-23: [ NEW ] Added compatibility for Google Timesketch 84 | [ NEW ] Added new switches to extract more data: 85 | DiskInfo, CaptivePortal, ExternalIP, InternalIP, LocalPeers, PowerState, ShutdownTime, ExtractAll 86 | [ NEW ] New switch GeolocationAPI to select which geolocation API to use among the ones supported by the script 87 | [ NEW ] Timestamps can now be shown in UTC or local time 88 | [ NEW ] Added support for Windows 10 1903 89 | [ FIXED ] Added new condition to avoid extracting "ExternalIP" false positive results 90 | [CHANGED] The script requires PS Version >=5 with DeliveryOptimization module 91 | [CHANGED] The script was renamed to Get-DoSvc4n6 92 | [CHANGED] "NoIP2Location" switch renamed to "SkipIP2Location" 93 | 2019-04-05: Added switch -SkipIP2Location, parameter -OutputPath and "isVpn" column 94 | Minor improvements 95 | 2019-04-03: Added progress bars and a warning message 96 | 2019-01-31: Fixed compatibility issues with Win10 1809 ETL logs which 97 | may contain more than one "ExternalIpAddress" per file 98 | 2018-12-05: First release 99 | .LINK 100 | GitHub : https://github.com/forensenellanebbia 101 | Blog post : https://forensenellanebbia.blogspot.it/2018/12/what-was-my-ip-ask-dosvc-on-windows-10.html 102 | DFIR Review: https://dfir.pubpub.org/pub/httnwst7 103 | #> 104 | 105 | #requires -Version 5 106 | 107 | [CmdletBinding()] 108 | Param ( 109 | [Parameter(Mandatory=$false,Position=0)][string]$LogPath, 110 | [switch]$DiskInfo, 111 | [switch]$CaptivePortal, 112 | [switch]$ExternalIP, 113 | [switch]$InternalIP, 114 | [switch]$LocalPeers, 115 | [switch]$PowerState, 116 | [switch]$ShutdownTime, 117 | [switch]$ExtractAll, 118 | [ValidateSet("ipwhois","ipinfo","ip-api")][string]$GeolocationAPI, 119 | [Parameter(Mandatory=$false)][string]$TokenAPI, 120 | [switch]$SkipIP2Location, 121 | [ValidateSet("UTC","LocalTime")][string]$TimeFormat, 122 | [Parameter(Mandatory=$false)][string]$OutputPath 123 | ) 124 | 125 | <# 126 | Microsoft Get-DeliveryOptimizationLog cmdlet retrieves and parses DeliveryOptimization logs. 127 | Cmdlet available since Windows 10 Insider Preview Build 17074: 128 | https://blogs.windows.com/windowsexperience/2018/01/11/announcing-windows-10-insider-preview-build-17074-pc/ 129 | 130 | Check out these guides on the meaning of the various diagnostic fields: 131 | https://github.com/MicrosoftDocs/windows-itpro-docs/blob/master/windows/privacy/basic-level-windows-diagnostic-events-and-fields-1703.md 132 | https://github.com/MicrosoftDocs/windows-itpro-docs/blob/master/windows/privacy/basic-level-windows-diagnostic-events-and-fields-1709.md 133 | https://github.com/MicrosoftDocs/windows-itpro-docs/blob/master/windows/privacy/basic-level-windows-diagnostic-events-and-fields-1803.md 134 | https://github.com/MicrosoftDocs/windows-itpro-docs/blob/master/windows/privacy/basic-level-windows-diagnostic-events-and-fields-1809.md 135 | https://github.com/MicrosoftDocs/windows-itpro-docs/blob/master/windows/privacy/basic-level-windows-diagnostic-events-and-fields-1903.md 136 | 137 | Windows Update Delivery Optimization and privacy 138 | https://support.microsoft.com/en-us/help/4468254/windows-update-delivery-optimization-faq 139 | #> 140 | 141 | $CheckDeliveryOptimizationModule = Get-Module -ListAvailable -Name DeliveryOptimization 142 | if(-Not ($CheckDeliveryOptimizationModule)) 143 | { 144 | Write-Host "`nERROR: DeliveryOptimization module is NOT installed`n" -ForegroundColor Yellow 145 | break 146 | } 147 | 148 | #region script title 149 | $script_version = "2021-08-14" 150 | $script_name = "Get-DoSvc4n6" 151 | 152 | #http://www.patorjk.com/software/taag/#p=display&f=Big&t=Get-DoSvc4n6 153 | $script_title = @" 154 | _____ _ _____ _____ _ _ __ 155 | / ____| | | | __ \ / ____| | || | / / 156 | | | __ ___| |_ ______| | | | ___| (_____ _____| || |_ _ __ / /_ 157 | | | |_ |/ _ \ __|______| | | |/ _ \\___ \ \ / / __|__ _| '_ \| '_ \ 158 | | |__| | __/ |_ | |__| | (_) |___) \ V / (__ | | | | | | (_) | 159 | \_____|\___|\__| |_____/ \___/_____/ \_/ \___| |_| |_| |_|\___/ 160 | "@ 161 | 162 | Write-Host "`n$script_title`n(v.$script_version)`r`n`nAuthor: Gabriele Zambelli @gazambelli`nhttps://github.com/forensenellanebbia`n" -ForegroundColor Cyan 163 | #endregion script title 164 | 165 | #region functions 166 | function Set-Filenames ($TimeFormatLabel) 167 | { 168 | $OutputPath + "\" + $ScriptStartedDateTime + "_TS-" + $TimeFormatLabel + "_DoSvc_" + $SwitchLabel + ".csv" 169 | $OutputPath + "\" + $ScriptStartedDateTime + "_TS-" + $TimeFormatLabel + "_DoSvc_" + $SwitchLabel + ".json" 170 | } 171 | 172 | function Write-ToFile ($ParameterOutput, $File_csv) 173 | { 174 | if(Test-Path $File_csv) 175 | { 176 | $ParameterOutput = $ParameterOutput | ConvertTo-Csv -NoTypeInformation | Select-Object -Skip 1 177 | Add-Content $File_csv -Value $ParameterOutput 178 | } 179 | else 180 | { 181 | $ParameterOutput | Export-Csv $File_csv -NoTypeInformation 182 | } 183 | } 184 | 185 | function Write-APIResponseToFile ($IpProgressCounter, $IpCount, $ApiResponse, $FileExtIP2Loc_json) 186 | { 187 | if ($IpProgressCounter -lt $IpCount) 188 | { 189 | $ApiResponse = $ApiResponse + "," 190 | } 191 | Add-Content $FileExtIP2Loc_json -Value $ApiResponse 192 | Start-Sleep -Seconds 3 #Waiting time between API calls 193 | } 194 | 195 | function Show-Results ($File_csv, $KeyCategory) 196 | { 197 | if($null -eq $File_csv) 198 | { 199 | $File_csv = "temporary.idontexist" 200 | } 201 | if(Test-Path $File_csv) 202 | { 203 | $rows = (Import-Csv $File_csv | Measure-Object).Count 204 | $text = "[+] $KeyCategory" 205 | } 206 | else 207 | { 208 | $rows = 0 209 | $text = "[-] $KeyCategory" 210 | } 211 | $text = $text + " " * (18 - $text.length) + ": " 212 | Write-Host $text $rows 213 | } 214 | #endregion functions 215 | 216 | if($LogPath -and ($PSBoundParameters.Count -ge 2) -and ($PSBoundParameters.ContainsKey('DiskInfo') -or $PSBoundParameters.ContainsKey('CaptivePortal') -or $PSBoundParameters.ContainsKey('ExternalIP') -or $PSBoundParameters.ContainsKey('InternalIP') -or $PSBoundParameters.ContainsKey('LocalPeers') -or $PSBoundParameters.ContainsKey('PowerState') -or $PSBoundParameters.ContainsKey('ShutdownTime') -or $PSBoundParameters.ContainsKey('ExtractAll') -or $PSBoundParameters.ContainsKey('TokenAPI'))) 217 | { 218 | $InitialDateTime = Get-Date 219 | $ScriptStartedDateTime = $InitialDateTime | Get-Date -Format "yyyyMMdd_HHmmss" 220 | 221 | #region check parameters 222 | if($PSBoundParameters.ContainsKey('ExtractAll')) 223 | { 224 | if(-Not ($PSBoundParameters.ContainsKey('DiskInfo') -or $PSBoundParameters.ContainsKey('CaptivePortal') -or $PSBoundParameters.ContainsKey('ExternalIP') -or $PSBoundParameters.ContainsKey('InternalIP') -or $PSBoundParameters.ContainsKey('LocalPeers') -or $PSBoundParameters.ContainsKey('PowerState') -or $PSBoundParameters.ContainsKey('ShutdownTime'))) 225 | { 226 | $PSBoundParameters.Add('DiskInfo','True') 227 | $PSBoundParameters.Add('CaptivePortal','True') 228 | $PSBoundParameters.Add('ExternalIP','True') 229 | $PSBoundParameters.Add('InternalIP','True') 230 | $PSBoundParameters.Add('LocalPeers','True') 231 | $PSBoundParameters.Add('PowerState','True') 232 | $PSBoundParameters.Add('ShutdownTime','True') 233 | } 234 | else 235 | { 236 | Write-Host "`nWARNING: ExtractAll is not compatible with the selected parameters`n" -ForegroundColor Yellow 237 | Write-Host "Script started: $($InitialDateTime | Get-Date -Format "yyyy-MM-dd HH:mm:ss")" 238 | Write-Host "Script ended : $(Get-Date -Format "yyyy-MM-dd HH:mm:ss")`n" 239 | break 240 | } 241 | } 242 | 243 | if($PSBoundParameters.ContainsKey('OutputPath')) 244 | { 245 | if(Test-Path $OutputPath) 246 | { 247 | $OutputPath = $OutputPath -replace "\\$" #remove trailing backslash 248 | } 249 | else 250 | { 251 | throw "The output path doesn't exist." 252 | } 253 | } 254 | else 255 | { 256 | $OutputPath = Get-Location | Select-Object -ExpandProperty Path 257 | } 258 | #endregion check parameters 259 | 260 | #region ETL parsing 261 | $ETLs = Get-ChildItem $LogPath -File -Include ("domgmt*.etl", "dosvc*.etl") -Recurse | Sort-Object Name 262 | if($ETLs) 263 | { 264 | Write-Host "[+] DoSvc ETL files found: $($ETLs.Count)" -ForegroundColor Green 265 | $EtlProgressCounter = 0 #counter 266 | foreach($ETL in $ETLs) 267 | { 268 | $EtlProgressCounter++ 269 | Write-Progress -Activity "Processing Event Trace Log (.etl) files:" -Status "Processing $($EtlProgressCounter) of $($ETLs.Count)" -CurrentOperation $ETL 270 | #Decode logs + add a column named LogName which contains the name and path of the log file 271 | $GetDeliveryOptmizationLogOutput = Get-DeliveryOptimizationLog -Path $ETL.FullName | ForEach-Object {$_ | Add-Member -MemberType NoteProperty -Name LogName -Value $ETL.Name -PassThru} 272 | $GetDeliveryOptmizationLogOutput = $GetDeliveryOptmizationLogOutput | ForEach-Object {$_ | Add-Member -MemberType NoteProperty -Name LogPath -Value $ETL.DirectoryName -PassThru} 273 | 274 | if($GetDeliveryOptmizationLogOutput) 275 | { 276 | if($PSBoundParameters.ContainsValue("LocalTime")) 277 | { 278 | $GetDeliveryOptmizationLogOutput = $GetDeliveryOptmizationLogOutput | Select-Object LogName,LogPath,@{N="TimeCreated";E={$_.TimeCreated.ToLocalTime()}},ProcessId,ThreadId,Level,LevelName,Message,Function,LineNumber,ErrorCode 279 | #local time - add Datetime ISO8601 format field 280 | $GetDeliveryOptmizationLogOutput = $GetDeliveryOptmizationLogOutput | ForEach-Object {$_ | Add-Member -MemberType NoteProperty -Name datetime -Value (get-date $_.TimeCreated.ToLocalTime() -Format "o") -PassThru} 281 | #local time - add Unix timestamp (nanoseconds) 282 | $GetDeliveryOptmizationLogOutput = $GetDeliveryOptmizationLogOutput | ForEach-Object {$_ | Add-Member -MemberType NoteProperty -Name timestamp -Value ([int64]((get-date $_.TimeCreated)-(get-date "1/1/1970")).Totalmilliseconds) -PassThru} 283 | $TimeFormatLabel = "LT" 284 | } 285 | else 286 | { 287 | #UTC - add Datetime ISO8601 format field 288 | $GetDeliveryOptmizationLogOutput = $GetDeliveryOptmizationLogOutput | ForEach-Object {$_ | Add-Member -MemberType NoteProperty -Name datetime -Value (get-date $_.TimeCreated -Format "o") -PassThru} 289 | #UTC - add Unix timestamp (nanoseconds) 290 | $GetDeliveryOptmizationLogOutput = $GetDeliveryOptmizationLogOutput | ForEach-Object {$_ | Add-Member -MemberType NoteProperty -Name timestamp -Value ([int64]((get-date $_.TimeCreated)-(get-date "1/1/1970")).Totalmilliseconds) -PassThru} 291 | $TimeFormatLabel = "UTC" #timestamps are shown by default in UTC 292 | } 293 | 294 | 295 | if($PSBoundParameters.ContainsKey('CaptivePortal')) 296 | { 297 | $SwitchLabel = "CaptivePortal" 298 | $FileCaptivePortal_csv, $FileCaptivePortal_json = Set-Filenames $TimeFormatLabel 299 | 300 | $CaptivePortalOutput = $GetDeliveryOptmizationLogOutput | Where-Object {($_.Function -Like "*::IsNetworkConnectivityPresent") -and ($_.Message -like "*Captive Portal*")} 301 | $CaptivePortalOutput = $CaptivePortalOutput | ForEach-Object {$_ | Add-Member -MemberType NoteProperty -Name timestamp_desc -Value "CaptivePortal" -PassThru} 302 | $CaptivePortalOutput = $CaptivePortalOutput | ForEach-Object {$_ | Add-Member -MemberType NoteProperty -Name Network -Value "" -PassThru} 303 | if($CaptivePortalOutput) 304 | { 305 | <# 306 | timesketch: example CSV file 307 | message,timestamp,datetime,timestamp_desc,extra_field_1,extra_field_2 308 | A message,1331698658276340,2015-07-24T19:01:01+00:00,Write time,foo,bar 309 | https://github.com/google/timesketch/blob/master/docs/CreateTimelineFromJSONorCSV.md 310 | #> 311 | $CaptivePortalOutput = $CaptivePortalOutput | Select-Object @{N="message";E={"[Captive Portal] " + $_.Message}},timestamp,datetime,timestamp_desc,LogName,LogPath,TimeCreated,@{N="Network";E={($_.Message -replace "Captive Portal detected on network ","" -replace "'","")}},ProcessId,ThreadId,Level,LevelName,Function,LineNumber,ErrorCode 312 | Write-ToFile $CaptivePortalOutput $FileCaptivePortal_csv 313 | } 314 | } 315 | 316 | if($PSBoundParameters.ContainsKey('DiskInfo')) 317 | { 318 | $SwitchLabel = "DiskInfo" 319 | $FileDiskInfo_csv, $FileDiskInfo_json = Set-Filenames $TimeFormatLabel 320 | 321 | $DiskInfoOutput = $GetDeliveryOptmizationLogOutput | Where-Object {($_.Function -Like "*::_IsDownloadEnabledPerDiskTotalSize") -or ($_.Function -like "*::TracePerfSnap")} 322 | $DiskInfoOutput = $DiskInfoOutput | Where-Object {($_.Message -Like "*disk*")} 323 | $DiskInfoOutput = $DiskInfoOutput | ForEach-Object {$_ | Add-Member -MemberType NoteProperty -Name timestamp_desc -Value "DiskInfo" -PassThru} 324 | $DiskInfoOutput = $DiskInfoOutput | ForEach-Object {$_ | Add-Member -MemberType NoteProperty -Name totalDiskSize -Value "" -PassThru} 325 | $DiskInfoOutput = $DiskInfoOutput | ForEach-Object {$_ | Add-Member -MemberType NoteProperty -Name freeDiskSpace -Value "" -PassThru} 326 | $DiskInfoOutput = $DiskInfoOutput | Select-Object @{N="message";E={$_.Message -replace ".*totalDiskSize","totalDiskSize" -replace "PERF.*disk:","disk:" -replace ", peers.*",""}},timestamp,datetime,timestamp_desc,LogName,LogPath,TimeCreated,ProcessId,ThreadId,Level,LevelName,Function,LineNumber,ErrorCode 327 | 328 | if($DiskInfoOutput) 329 | { 330 | foreach($DiskInfoEvent in $DiskInfoOutput) 331 | { 332 | if($DiskInfoEvent.message -like "*totalDiskSize*") 333 | { 334 | $DiskInfoEvent = $DiskInfoEvent | Select-Object Message,timestamp,datetime,timestamp_desc,LogName,LogPath,TimeCreated,@{N="totalDiskSize";E={$DiskInfoEvent.message -replace "totalDiskSize = ",""}},freeDiskSpace,ProcessId,ThreadId,Level,LevelName,Function,LineNumber,ErrorCode 335 | } 336 | if($DiskInfoEvent.message -like "disk:*") 337 | { 338 | $DiskInfoEventMessageJson = $DiskInfoEvent.message -replace "disk:","" -replace "\[","{" -replace "\]","}" | ConvertFrom-Json 339 | $DiskInfoEventtotalDiskSize = [math]::Round($DiskInfoEventMessageJson.total/1024/1024/1024,2) 340 | $DiskInfoEventtotalDiskSize = $DiskInfoEventtotalDiskSize.ToString() + " GB ($($DiskInfoEventMessageJson.total) Bytes)" 341 | $DiskInfoEvent = $DiskInfoEvent | Select-Object Message,timestamp,datetime,timestamp_desc,LogName,LogPath,TimeCreated,@{N="totalDiskSize";E={$DiskInfoEventtotalDiskSize}},freeDiskSpace,ProcessId,ThreadId,Level,LevelName,Function,LineNumber,ErrorCode 342 | $DiskInfoEventfreeDiskSpace = [math]::Round($DiskInfoEventMessageJson.free/1024/1024,0) 343 | $DiskInfoEventfreeDiskSpace = $DiskInfoEventfreeDiskSpace.ToString() + " MB ($($DiskInfoEventMessageJson.free) Bytes)" 344 | $DiskInfoEvent = $DiskInfoEvent | Select-Object Message,timestamp,datetime,timestamp_desc,LogName,LogPath,TimeCreated,totalDiskSize,@{N="freeDiskSpace";E={$DiskInfoEventfreeDiskSpace}},ProcessId,ThreadId,Level,LevelName,Function,LineNumber,ErrorCode 345 | } 346 | 347 | if($null -eq $DiskInfoEvent.freeDiskSpace) 348 | { 349 | $DiskInfoEvent = $DiskInfoEvent | Select-Object @{N="message";E={"[Disk Information] totalDiskSize: " + $_.totalDiskSize}},timestamp,datetime,timestamp_desc,LogName,LogPath,TimeCreated,totalDiskSize,freeDiskSpace,ProcessId,ThreadId,Level,LevelName,Function,LineNumber,ErrorCode 350 | } 351 | else 352 | { 353 | $DiskInfoEvent = $DiskInfoEvent | Select-Object @{N="message";E={"[Disk Information] totalDiskSize: " + $_.totalDiskSize + ", freeDiskSpace: " + $_.freeDiskSpace}},timestamp,datetime,timestamp_desc,LogName,LogPath,TimeCreated,totalDiskSize,freeDiskSpace,ProcessId,ThreadId,Level,LevelName,Function,LineNumber,ErrorCode 354 | } 355 | Write-ToFile $DiskInfoEvent $FileDiskInfo_csv 356 | } 357 | } 358 | } 359 | 360 | if($PSBoundParameters.ContainsKey('InternalIP')) 361 | { 362 | $SwitchLabel = "InternalIP" 363 | $FileInternalIP_csv, $FileInternalIP_json = Set-Filenames $TimeFormatLabel 364 | 365 | $InternalIPOutput = $GetDeliveryOptmizationLogOutput | Where-Object {($_.Function -like "*::_InternalAnnounce") -and ($_.Message -like "*ReportedIp*")} 366 | $InternalIPOutput = $InternalIPOutput | ForEach-Object {$_ | Add-Member -MemberType NoteProperty -Name timestamp_desc -Value "InternalIP" -PassThru} 367 | $InternalIPOutput = $InternalIPOutput | Select-Object @{N="message";E={$_.Message -replace "Swarm.*announce request:",""}},timestamp,datetime,timestamp_desc,LogName,LogPath,TimeCreated,ProcessId,ThreadId,Level,LevelName,Function,LineNumber,ErrorCode 368 | $InternalIPOutput = $InternalIPOutput | ForEach-Object {$_ | Add-Member -MemberType NoteProperty -Name InternalIp -Value (($_.Message | ConvertFrom-Json).ReportedIp) -PassThru} 369 | $InternalIPOutput = $InternalIPOutput | ForEach-Object {$_ | Add-Member -MemberType NoteProperty -Name SubnetMask -Value (($_.Message | ConvertFrom-Json).SubnetMask) -PassThru} 370 | $InternalIPOutput = $InternalIPOutput | ForEach-Object {$_ | Add-Member -MemberType NoteProperty -Name Ipv6 -Value (($_.Message | ConvertFrom-Json).Ipv6) -PassThru} 371 | $InternalIPOutput = $InternalIPOutput | ForEach-Object {$_ | Add-Member -MemberType NoteProperty -Name ClientCompactVersion -Value (($_.Message | ConvertFrom-Json).ClientCompactVersion) -PassThru} 372 | 373 | if($InternalIPOutput) 374 | { 375 | if($null -eq $_.Ipv6) 376 | { 377 | $InternalIPOutput = $InternalIPOutput | Select-Object @{N="message";E={"[Private IP] IP: " + $_.InternalIP + ", Subnet Mask: " + $_.SubnetMask}},timestamp,datetime,timestamp_desc,LogName,LogPath,TimeCreated,InternalIP,SubnetMask,Ipv6,ClientCompactVersion,ProcessId,ThreadId,Level,LevelName,Function,LineNumber,ErrorCode 378 | } 379 | else 380 | { 381 | $InternalIPOutput = $InternalIPOutput | Select-Object @{N="message";E={"[Private IP] IP: " + $_.InternalIP + ", Subnet Mask: " + $_.SubnetMask + ", Ipv6: " + $_.Ipv6}},timestamp,datetime,timestamp_desc,LogName,LogPath,TimeCreated,InternalIP,SubnetMask,Ipv6,ClientCompactVersion,ProcessId,ThreadId,Level,LevelName,Function,LineNumber,ErrorCode 382 | } 383 | Write-ToFile $InternalIPOutput $FileInternalIP_csv 384 | } 385 | } 386 | 387 | if($PSBoundParameters.ContainsKey('LocalPeers')) 388 | { 389 | $SwitchLabel = "LocalPeers" 390 | $FileLocalPeers_csv, $FileLocalPeers_json = Set-Filenames $TimeFormatLabel 391 | 392 | $LocalPeersOutput = $GetDeliveryOptmizationLogOutput | Where-Object {($_.Function -like "*::ConnectionComplete") -and ($_.Message -match "(\[192\.168\.)|(\[10\.)|(\[172\.1[6-9]\.)|(\[172\.2[0-9]\.)|(\[172\.3[0-1]\.)")} 393 | $LocalPeersOutput = $LocalPeersOutput | ForEach-Object {$_ | Add-Member -MemberType NoteProperty -Name timestamp_desc -Value "LocalPeers" -PassThru} 394 | if($LocalPeersOutput) 395 | { 396 | $LocalPeersOutput = $LocalPeersOutput | Select-Object @{N="message";E={"[PC on local network] " + $_.Message}},timestamp,datetime,timestamp_desc,LogName,LogPath,TimeCreated,ProcessId,ThreadId,Level,LevelName,Function,LineNumber,ErrorCode 397 | Write-ToFile $LocalPeersOutput $FileLocalPeers_csv 398 | } 399 | } 400 | 401 | if($PSBoundParameters.ContainsKey('PowerState')) 402 | { 403 | $SwitchLabel = "PowerState" 404 | $FilePowerState_csv, $FilePowerState_json = Set-Filenames $TimeFormatLabel 405 | 406 | $PowerStateOutput = $GetDeliveryOptmizationLogOutput | Where-Object {($_.Message -like "*battery*") -or ($_.Message -like "*power state*") -or ($_.Message -like "*power status*")} 407 | $PowerStateOutput = $PowerStateOutput | ForEach-Object {$_ | Add-Member -MemberType NoteProperty -Name timestamp_desc -Value "PowerState" -PassThru} 408 | if($PowerStateOutput) 409 | { 410 | foreach($PowerStateEvent in $PowerStateOutput) 411 | { 412 | if(($PowerStateEvent.Message -like "*battery? 1*") -or ($PowerStateEvent.Message -like "*power status = 0*") -or ($PowerStateEvent.Message -like "*low battery*")) 413 | { 414 | $PowerStateEvent = $PowerStateEvent | Select-Object @{N="message";E={"[Power mode: unplugged] " + $_.Message}},timestamp,datetime,timestamp_desc,LogName,LogPath,TimeCreated,ProcessId,ThreadId,Level,LevelName,Function,LineNumber,ErrorCode 415 | } 416 | elseif(($PowerStateEvent.Message -like "*battery? 0*") -or ($PowerStateEvent.Message -like "*power status = 1*")) 417 | { 418 | $PowerStateEvent = $PowerStateEvent | Select-Object @{N="message";E={"[Power mode: plugged in] " + $_.Message}},timestamp,datetime,timestamp_desc,LogName,LogPath,TimeCreated,ProcessId,ThreadId,Level,LevelName,Function,LineNumber,ErrorCode 419 | } 420 | elseif($PowerStateEvent.Message -like "*battery level*") 421 | { 422 | $PowerStateEvent = $PowerStateEvent | Select-Object @{N="message";E={"[Battery level] " + $_.Message}},timestamp,datetime,timestamp_desc,LogName,LogPath,TimeCreated,ProcessId,ThreadId,Level,LevelName,Function,LineNumber,ErrorCode 423 | } 424 | else 425 | { 426 | $PowerStateEvent = $PowerStateEvent | Select-Object @{N="message";E={"[Power state] " + $_.Message}},timestamp,datetime,timestamp_desc,LogName,LogPath,TimeCreated,ProcessId,ThreadId,Level,LevelName,Function,LineNumber,ErrorCode 427 | } 428 | Write-ToFile $PowerStateEvent $FilePowerState_csv 429 | } 430 | } 431 | } 432 | 433 | if($PSBoundParameters.ContainsKey('ShutdownTime')) 434 | { 435 | $SwitchLabel = "ShutdownTime" 436 | $FileShutdownTime_csv, $FileShutdownTime_json = Set-Filenames $TimeFormatLabel 437 | 438 | $ShutdownTimeOutput = $GetDeliveryOptmizationLogOutput | Where-Object {($_.Message -match "system shutdown. 1") -or ($_.Message -like "*sleep/hibernation state*")} 439 | $ShutdownTimeOutput = $ShutdownTimeOutput | ForEach-Object {$_ | Add-Member -MemberType NoteProperty -Name timestamp_desc -Value "ShutdownTime" -PassThru} 440 | if($ShutdownTimeOutput) 441 | { 442 | foreach($ShutdownTimeEvent in $ShutdownTimeOutput) 443 | { 444 | if($ShutdownTimeEvent.Message -match "system shutdown. 1") 445 | { 446 | $ShutdownTimeEvent = $ShutdownTimeEvent | Select-Object @{N="message";E={"[System Restart/Shutdown] " + $_.Message}},timestamp,datetime,timestamp_desc,LogName,LogPath,TimeCreated,ProcessId,ThreadId,Level,LevelName,Function,LineNumber,ErrorCode 447 | } 448 | else 449 | { 450 | $ShutdownTimeEvent = $ShutdownTimeEvent | Select-Object @{N="message";E={"[Sleep/Hibernation] " + $_.Message}},timestamp,datetime,timestamp_desc,LogName,LogPath,TimeCreated,ProcessId,ThreadId,Level,LevelName,Function,LineNumber,ErrorCode 451 | } 452 | Write-ToFile $ShutdownTimeEvent $FileShutdownTime_csv 453 | } 454 | } 455 | } 456 | 457 | if($PSBoundParameters.ContainsKey('ExternalIP')) 458 | { 459 | $SwitchLabel = "ExternalIP" 460 | $FileExternalIP_csv, $FileExternalIP_json = Set-Filenames $TimeFormatLabel 461 | 462 | #region isVpn 463 | $search_isVpn = $GetDeliveryOptmizationLogOutput | Select-Object TimeCreated,ProcessId,ThreadId,Message | Where-Object {($_.Message -Like "*Detected connected VPN adapter*")} 464 | if($search_isVpn) 465 | { 466 | $search_isVpn = $search_isVpn | Select-Object @{name="TimeCreated";expression={$_.TimeCreated.ToString("yyyy-MM-dd HH:mm")}},ProcessId,ThreadId,Message 467 | $search_isVpn = $search_isVpn | Select-Object TimeCreated,ProcessId,ThreadId,@{name="isVpn";expression={$_.Message}} | Sort-Object TimeCreated -Unique 468 | } 469 | #endregion isVpn 470 | 471 | #region GeoResponse 472 | $search_ip = $GetDeliveryOptmizationLogOutput | Where-Object {(($_.Function -eq "CGeoInfoProvider::RefreshConfigs") -or ($_.Function -eq "CServiceConfigProvider::_CallService")) -and ($_.Message -match "GEO(:)? response:")} 473 | $search_ip = $search_ip | ForEach-Object {$_ | Add-Member -MemberType NoteProperty -Name timestamp_desc -Value "ExternalIP" -PassThru} 474 | if($search_ip) 475 | { 476 | foreach($IP in $search_ip) #ETL files may contain more than one "GEO: response:" value 477 | { 478 | $messageJSON = "[" + ($IP.Message -replace ".*{","{") + "]" 479 | $messageCSV = ($messageJSON | ConvertFrom-Json) | Select-Object ExternalIpAddress,CountryCode,KeyValue_EndpointFullUri,Version,CompactVersion,isVpn | ConvertTo-Csv -NoTypeInformation 480 | #Add new member "ExternalIpAddress" 481 | $IP = $IP | ForEach-Object {$_ | Add-Member -MemberType NoteProperty -Name ExternalIpAddress -Value ($messageCSV | ConvertFrom-Csv).ExternalIpAddress -PassThru} 482 | #Add new member "CountryCode" 483 | $IP = $IP | ForEach-Object {$_ | Add-Member -MemberType NoteProperty -Name CountryCode -Value ($messageCSV | ConvertFrom-Csv).CountryCode -PassThru} 484 | #Add new member "KeyValue_EndpointFullUri" 485 | $IP = $IP | ForEach-Object {$_ | Add-Member -MemberType NoteProperty -Name KeyValue_EndpointFullUri -Value ($messageCSV | ConvertFrom-Csv).KeyValue_EndpointFullUri -PassThru} 486 | #Add new member "Version" 487 | $IP = $IP | ForEach-Object {$_ | Add-Member -MemberType NoteProperty -Name Version -Value ($messageCSV | ConvertFrom-Csv).Version -PassThru} 488 | #Add new member "CompactVersion" 489 | $IP = $IP | ForEach-Object {$_ | Add-Member -MemberType NoteProperty -Name CompactVersion -Value ($messageCSV | ConvertFrom-Csv).CompactVersion -PassThru} 490 | #Change the order of the objects 491 | $IP = $IP | Select-Object @{N="message";E={"[Public IP] IP: " + $_.ExternalIpAddress + ", CountryCode: " + $_.CountryCode}},timestamp,datetime,timestamp_desc,LogName,LogPath,@{name="TimeCreated";expression={$_.TimeCreated.ToString("yyyy-MM-dd HH:mm:ss")}},ExternalIpAddress,isVpn,CountryCode,KeyValue_EndpointFullUri,Version,CompactVersion,ProcessId,ThreadId,Level,LevelName,Function,LineNumber 492 | 493 | if($search_isVpn) 494 | { 495 | foreach($isVpn in $search_isVpn) 496 | { 497 | <#Compare timestamps of isVpn and ExternalIp events; if the two values are close to each other, and if the two events 498 | belong to the same ProcessId, then that ExternalIP was assigned when using a VPN.#> 499 | if($isVpn.TimeCreated.Substring(0,15) -eq $IP.TimeCreated.Substring(0,15) -and $isVpn.ProcessId -eq $IP.ProcessId) 500 | { 501 | $IP.isVpn = $isVpn.isVpn 502 | } 503 | } 504 | } 505 | 506 | Write-ToFile $IP $FileExternalIP_csv 507 | 508 | } 509 | } 510 | } 511 | #endregion GeoResponse 512 | } 513 | } 514 | 515 | #region Summary_ExceptExternalIP 516 | Write-Host "`nEvents found per category" 517 | if($PSBoundParameters.ContainsKey('CaptivePortal')) { Show-Results $FileCaptivePortal_csv CaptivePortal } 518 | if($PSBoundParameters.ContainsKey('DiskInfo')) { Show-Results $FileDiskInfo_csv DiskInfo } 519 | if($PSBoundParameters.ContainsKey('ExternalIP')) { Show-Results $FileExternalIP_csv ExternalIP } 520 | if($PSBoundParameters.ContainsKey('InternalIP')) { Show-Results $FileInternalIP_csv InternalIP } 521 | if($PSBoundParameters.ContainsKey('LocalPeers')) { Show-Results $FileLocalPeers_csv LocalPeers } 522 | if($PSBoundParameters.ContainsKey('PowerState')) { Show-Results $FilePowerState_csv PowerState } 523 | if($PSBoundParameters.ContainsKey('ShutdownTime')) { Show-Results $FileShutdownTime_csv ShutdownTime } 524 | Write-Host "`n" 525 | #endregion Summary_ExceptExternalIP 526 | 527 | #region Summary_ExternalIP 528 | if($PSBoundParameters.ContainsKey('ExternalIP')) 529 | { 530 | if(($null -ne $FileExternalIP_csv) -and (Test-Path $FileExternalIP_csv)) 531 | { 532 | #ExternalIP JSON OUTPUT 533 | $FileExternalIP_csv_input = Import-Csv $FileExternalIP_csv 534 | $FileExternalIP_csv_input | ConvertTo-Json | Out-File $FileExternalIP_json -Encoding utf8 535 | 536 | $IP_uniq = $FileExternalIP_csv_input | Select-Object ExternalIpAddress -unique #get unique IP addresses 537 | $IP_uniq = ($IP_uniq | Select-Object -ExpandProperty ExternalIpAddress) | Sort-Object { [version]$_ } #sort by IP address 538 | 539 | Write-Host "[+] Unique ExternalIP(s) found: $($IP_uniq.count)" -ForegroundColor Green 540 | 541 | if($PSBoundParameters.ContainsKey('SkipIP2Location')) 542 | { 543 | #SUMMARY: Display number of occurrences per unique IP address 544 | Write-Host "`nNumber of occurrences per unique ExternalIP:" 545 | $FileExternalIP_csv_input | Group-Object ExternalIpAddress -NoElement | Sort-Object { [version]$_.Name } | Select-Object Count, @{N="ExternalIP";E={$_.Name}} | Format-Table 546 | } 547 | else 548 | { 549 | #region GeolocationAPI 550 | if($PSBoundParameters.ContainsValue('ipwhois')) 551 | { 552 | $SwitchLabel = "IP2Location-API_ipwhois" 553 | } 554 | elseif($PSBoundParameters.ContainsValue('ipinfo')) 555 | { 556 | $SwitchLabel = "IP2Location-API_ipinfo" 557 | } 558 | else 559 | { 560 | $SwitchLabel = "IP2Location-API_ip-api" 561 | } 562 | 563 | $FileExtIP2Loc_csv, $FileExtIP2Loc_json = Set-Filenames $TimeFormatLabel 564 | 565 | Add-Content $FileExtIP2Loc_json -Value "[" 566 | $IpProgressCounter = 0 567 | foreach($IP in $IP_uniq) 568 | { 569 | $IpProgressCounter++ 570 | if($PSBoundParameters.ContainsValue('ipwhois')) 571 | { 572 | Write-Progress -Activity "Performing IP location lookup using ipwhois API:" -Status "Processing $($IpProgressCounter) of $($IP_uniq.Count)" -CurrentOperation $IP 573 | $Ip2LocUrl = "http://free.ipwhois.io/json/" + $IP #https://ipwhois.io/documentation 574 | $ApiResponse = (Invoke-WebRequest $Ip2LocUrl).Content 575 | Write-APIResponseToFile $IpProgressCounter $IP_uniq.Count $ApiResponse $FileExtIP2Loc_json 576 | } 577 | elseif($PSBoundParameters.ContainsValue('ipinfo')) 578 | { 579 | Write-Progress -Activity "Performing IP location lookup using ipinfo API:" -Status "Processing $($IpProgressCounter) of $($IP_uniq.Count)" -CurrentOperation $IP 580 | $Ip2LocUrl = "https://ipinfo.io/" + $IP + "?token=" + $TokenAPI #https://ipinfo.io/developers 581 | $ApiResponse = (Invoke-WebRequest $Ip2LocUrl).Content 582 | Write-APIResponseToFile $IpProgressCounter $IP_uniq.Count $ApiResponse $FileExtIP2Loc_json 583 | } 584 | else 585 | { 586 | Write-Progress -Activity "Performing IP location lookup using ip-api API:" -Status "Processing $($IpProgressCounter) of $($IP_uniq.Count)" -CurrentOperation $IP 587 | $Ip2LocUrl = "http://ip-api.com/json/" + $IP #http://ip-api.com/docs/api:json 588 | $ApiResponse = (Invoke-WebRequest $Ip2LocUrl).Content 589 | Write-APIResponseToFile $IpProgressCounter $IP_uniq.Count $ApiResponse $FileExtIP2Loc_json 590 | } 591 | } 592 | Add-Content $FileExtIP2Loc_json -Value "]" 593 | (Get-Content $FileExtIP2Loc_json | ConvertFrom-Json) | Export-Csv $FileExtIP2Loc_csv -NoTypeInformation 594 | #endregion GeolocationAPI 595 | 596 | #SUMMARY: show unique IP addresses found 597 | Write-Host "`nWarning: IP geolocation data could be inaccurate and should be treated with caution" -ForegroundColor Red -BackgroundColor White 598 | $IP2Loc = (Get-Content $FileExtIP2Loc_json | ConvertFrom-Json) 599 | if($PSBoundParameters.ContainsValue('ipwhois')) 600 | { 601 | <#Fields in the response: 602 | ip,success,type,continent,continent_code,country,country_code,country_flag,country_capital,country_phone, 603 | country_neighbours,region,city,latitude,longitude,asn,org,isp,timezone,timezone_name,timezone_dstOffset, 604 | timezone_gmtOffset,timezone_gmt,currency,currency_code,currency_symbol,currency_rates,currency_plural 605 | #> 606 | $IP2Loc | Select-Object @{Name="ExternalIp";Expression={$_.ip}},asn,isp,city,country_code | Format-Table -AutoSize 607 | } 608 | elseif($PSBoundParameters.ContainsValue('ipinfo')) 609 | { 610 | #Fields in the response: ip,hostname,anycast,city,region,country,loc,org,postal,timezone 611 | $IP2Loc | Select-Object @{Name="ExternalIp";Expression={$_.ip}},org,city,country | Format-Table -AutoSize 612 | } 613 | else 614 | { 615 | #Fields in the response: as,city,country,countryCode,isp,lat,lon,org,query,region,regionName,status,timezone,zip 616 | $IP2Loc | Select-Object @{Name="ExternalIp";Expression={$_.query}},isp,city,countrycode | Format-Table -AutoSize 617 | } 618 | } 619 | } 620 | } 621 | #endregion Summary_ExternalIP 622 | 623 | Write-Host "Done!`n" 624 | } 625 | else 626 | { 627 | Write-Host "[-] DoSvc ETL files found: $($ETLs.Count)`n" -ForegroundColor Yellow 628 | } 629 | #endregion ETL parsing 630 | 631 | Write-Host "Script started: $($InitialDateTime | Get-Date -Format "yyyy-MM-dd HH:mm:ss")" 632 | Write-Host "Script ended : $(Get-Date -Format "yyyy-MM-dd HH:mm:ss")`n" 633 | } 634 | else 635 | { 636 | Write-Host "The script extracts different categories of events from DoSvc ETL logs. Use Get-Help for more details." 637 | 638 | Write-Host "`nWARNING: Not enough parameters provided." -ForegroundColor Yellow 639 | Write-Host "`nExamples:" 640 | Write-Host ".\$script_name.ps1 dosvc.20181212_133942_927.etl -ExtractAll" 641 | Write-Host ".\$script_name.ps1 C:\CustomLogPath -ExternalIP -SkipIP2Location -OutputPath C:\CustomOutputPath" 642 | Write-Host ".\$script_name.ps1 C:\Windows\ServiceProfiles\NetworkService\AppData\Local\Microsoft\Windows\DeliveryOptimization\Logs -DiskInfo`n" 643 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # powershell-scripts 2 | 3 | ## [DFIR] 4 | 5 | **Get-DoSvc4n6.ps1** extracts external/public IP addresses and more from DoSvc ETL logs. 6 | Depending on the version of Win10, Delivery Optimization Logs are stored by default in the path: 7 | - [1507] C:\Windows\Logs\dosvc 8 | - [1709+] C:\Windows\ServiceProfiles\NetworkService\AppData\Local\Microsoft\Windows\DeliveryOptimization\Logs 9 | 10 | Blog post: https://forensenellanebbia.blogspot.com/2018/12/what-was-my-ip-ask-dosvc-on-windows-10.html
11 | DFIR Review: https://dfir.pubpub.org/pub/httnwst7 12 | 13 | **ConvertTo-LogonTracer.ps1** converts a filtered XML file created with [EvtxECmd](https://ericzimmerman.github.io) to an XML that can be imported into [LogonTracer](https://github.com/JPCERTCC/LogonTracer). That allows to load into LogonTracer just the events that the tool can interpret, speeding up the whole process. The script can also search for keywords and splits a large XML into smaller chunks. 14 | 15 | Blog post: https://forensenellanebbia.blogspot.com/2020/12/lets-combine-evtxecmd-with-logontracer.html
16 | 17 | ## [MISC] 18 | 19 | **bulk_downloader.ps1** downloads files in bulk from conference websites 20 | -------------------------------------------------------------------------------- /bulk_downloader.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | Author : Gabriele Zambelli 3 | Blog : https://forensenellanebbia.blogspot.it 4 | Twitter: @gazambelli 5 | 6 | I wrote this script to download files in bulk from conference websites 7 | 8 | How to use: 9 | .\bulk_downloader.ps1 10 | 11 | Change History: 12 | 2020-06-06 First release 13 | 14 | Tested against: 15 | - https://www.first.org/resources/papers/2020 16 | - https://dfrws.org/eu-2020-program/ 17 | - https://www.sans.org/cyber-security-summit/archives/dfir 18 | - https://media.defcon.org/DEF%20CON%2025/DEF%20CON%2025%20presentations/ 19 | - https://conference.hitb.org/hitbsecconf2019ams/materials/ 20 | - conference.hitb.org/hitbsecconf2013ams/materials/ 21 | - https://recon.cx/2018/brussels/slides/ 22 | - https://www.bluehatil.com/abstracts 23 | - https://2019.pass-the-salt.org/schedule/ 24 | 25 | #> 26 | 27 | # File extensions to download 28 | $extensions = @("pdf","pptx") 29 | 30 | # Download slides published in the last XX days - this setting works with sans.org only 31 | $last_nn_days = 120 32 | 33 | 34 | $uri = $args[0] 35 | 36 | if ($uri) { 37 | Write-Host "These are the file extensions that have been selected:`n$extensions" 38 | $html = Invoke-WebRequest -Uri $uri 39 | foreach ($extension in $extensions) { 40 | $i=0 41 | #U: https://www.sans.org/cyber-security-summit/archives/dfir 42 | #H: https://www.sans.org/cyber-security-summit/archives/file/summit_archive_<10-digit epoch ts>.pdf 43 | if($uri -like "*sans.org*"){ 44 | $last_nn_days2epoch_ts = [int64](([datetime]::UtcNow).AddDays(-$last_nn_days)-(get-date "1/1/1970")).TotalSeconds 45 | $files_to_download = $html.Links.href | Where-Object { $_ -like ("*." + $extension) -and $_ -notlike "*trademarkuse.pdf" -and $_.substring(($_.length)-14),(($_.length)-4) -ge $last_nn_days2epoch_ts} 46 | } 47 | else { 48 | $files_to_download = $html.Links.href | Where-Object { $_ -like ("*." + $extension)} 49 | } 50 | Write-Host "`n** Downloading now $($files_to_download.Count) .$($extension.ToUpper()) file(s)" 51 | $files_to_download | ForEach-Object { 52 | Start-Sleep -Seconds 1 53 | #URI cleanup 54 | $_ = $_ -replace "&","&" -replace " "," " 55 | $filename = $_ -replace "^.*/","" 56 | #Filename cleanup 57 | $filename = [System.Web.HttpUtility]::UrlDecode($filename) -replace ":","" -replace " "," " 58 | $i++ 59 | Write-Host " $i`t$filename" 60 | 61 | #U: https://www.sans.org/cyber-security-summit/archives/dfir 62 | #H: https://www.sans.org/cyber-security-summit/archives/file/summit_archive_<10-digit epoch ts>.pdf 63 | if($_.StartsWith("http")) { 64 | Invoke-WebRequest -Uri $_ -OutFile $filename 65 | } 66 | 67 | #U: https://www.sstic.org/2020/presentation// 68 | #H: //www.sstic.org/media/SSTIC2020/SSTIC-actes//filename.pdf 69 | elseif($_.StartsWith("//")) { 70 | Invoke-WebRequest -Uri ("https:" + $_) -OutFile $filename 71 | } 72 | 73 | #U: https://www.first.org/resources/papers/2020 74 | #H: /resources/papers/path/filename.pdf 75 | elseif($_.StartsWith("/") -and $_.substring(1,1) -ne "/") { 76 | Invoke-WebRequest -Uri (($uri.Split("/")[0..2] -join "/") + $_) -OutFile $filename 77 | } 78 | 79 | #U: http://domain.com/path/page.htm 80 | #H: Path/filename.pdf 81 | elseif(-not $_.StartsWith("/") -and $_ -like "*/*") { 82 | Invoke-WebRequest -Uri (($uri | Select-String -pattern ".*/").Matches.Value + $_) -OutFile $filename 83 | } 84 | 85 | #U: https://media.defcon.org/DEF%20CON%2025/DEF%20CON%2025%20presentations/ 86 | #H: filename.pdf 87 | else { 88 | Invoke-WebRequest -Uri ($uri + $_) -OutFile $filename 89 | } 90 | } 91 | } 92 | Write-Host "`nDone!`n" 93 | } 94 | else { 95 | Write-Host "Please provide a URI" 96 | } --------------------------------------------------------------------------------