├── 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 | }
--------------------------------------------------------------------------------