mirror of
https://github.com/pbatard/Fido.git
synced 2025-09-16 22:28:02 +02:00
Compare commits
1 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
bd9475773c |
161
Fido.ps1
161
Fido.ps1
@@ -1,5 +1,5 @@
|
|||||||
#
|
#
|
||||||
# Fido v1.02 - Retail Windows ISO Downloader
|
# Fido v1.03 - Retail Windows ISO Downloader
|
||||||
# Copyright © 2019 Pete Batard <pete@akeo.ie>
|
# Copyright © 2019 Pete Batard <pete@akeo.ie>
|
||||||
# ConvertTo-ImageSource: Copyright © 2016 Chris Carter
|
# ConvertTo-ImageSource: Copyright © 2016 Chris Carter
|
||||||
#
|
#
|
||||||
@@ -31,6 +31,10 @@ param(
|
|||||||
# (Optional) Name of a pipe the download URL should be sent to.
|
# (Optional) Name of a pipe the download URL should be sent to.
|
||||||
# If not provided, a browser window is opened instead.
|
# If not provided, a browser window is opened instead.
|
||||||
[string]$PipeName,
|
[string]$PipeName,
|
||||||
|
# (Optional) Disable IE First Run Customize so that Invoke-WebRequest
|
||||||
|
# doesn't throw an exception if the user has never launched IE.
|
||||||
|
# Note that this requires the script to run elevated.
|
||||||
|
[switch]$DisableFirstRunCustomize,
|
||||||
# (Optional) Toggle expert mode (additional ISOs to choose).
|
# (Optional) Toggle expert mode (additional ISOs to choose).
|
||||||
[switch]$Expert = $False
|
[switch]$Expert = $False
|
||||||
)
|
)
|
||||||
@@ -81,6 +85,13 @@ Add-Type -AssemblyName PresentationFramework
|
|||||||
[Gui.Utils]::ShowWindow(([System.Diagnostics.Process]::GetCurrentProcess() | Get-Process).MainWindowHandle, 0) | Out-Null
|
[Gui.Utils]::ShowWindow(([System.Diagnostics.Process]::GetCurrentProcess() | Get-Process).MainWindowHandle, 0) | Out-Null
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
# Make sure PowerShell 3.0 or later is used (for Invoke-WebRequest)
|
||||||
|
if ($PSVersionTable.PSVersion.Major -lt 3) {
|
||||||
|
Write-Host Error: PowerShell 3.0 or later is required to run this script.
|
||||||
|
[System.Windows.MessageBox]::Show("PowerShell 3.0 or later is required to run this script.`nYou can download it from: https://www.microsoft.com/en-us/download/details.aspx?id=34595", "Error", "OK", "Error") | Out-Null
|
||||||
|
exit -1
|
||||||
|
}
|
||||||
|
|
||||||
#region Data
|
#region Data
|
||||||
$WindowsVersions = @(
|
$WindowsVersions = @(
|
||||||
@(
|
@(
|
||||||
@@ -366,6 +377,17 @@ function Get-Translation([string]$Text)
|
|||||||
return $Text
|
return $Text
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Some PowerShells don't have Microsoft.mshtml assembly (comes with MS Office?)
|
||||||
|
# so we can't use ParsedHtml or IHTMLDocument[2|3] features there...
|
||||||
|
function GetElementById([object]$Request, [string]$Id)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
return $Request.ParsedHtml.IHTMLDocument3_GetElementByID($Id)
|
||||||
|
} catch {
|
||||||
|
return $Request.AllElements | ? {$_.id -eq $Id}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function Error([string]$ErrorMessage)
|
function Error([string]$ErrorMessage)
|
||||||
{
|
{
|
||||||
Write-Host $ErrorMessage
|
Write-Host $ErrorMessage
|
||||||
@@ -378,6 +400,17 @@ function Error([string]$ErrorMessage)
|
|||||||
$script:Stage = -1
|
$script:Stage = -1
|
||||||
$Continue.IsEnabled = $True
|
$Continue.IsEnabled = $True
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function Get-RandomDate()
|
||||||
|
{
|
||||||
|
[DateTime]$Min = "1/1/2008"
|
||||||
|
[DateTime]$Max = [DateTime]::Now
|
||||||
|
|
||||||
|
$RandomGen = new-object random
|
||||||
|
$RandomTicks = [Convert]::ToInt64( ($Max.ticks * 1.0 - $Min.Ticks * 1.0 ) * $RandomGen.NextDouble() + $Min.Ticks * 1.0 )
|
||||||
|
$Date = new-object DateTime($RandomTicks)
|
||||||
|
return $Date.ToString("yyyyMMdd")
|
||||||
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Form
|
#region Form
|
||||||
@@ -401,14 +434,20 @@ $MaxStage = 4
|
|||||||
$SessionId = ""
|
$SessionId = ""
|
||||||
$ExitCode = -1
|
$ExitCode = -1
|
||||||
$Locale = "en-US"
|
$Locale = "en-US"
|
||||||
|
$DFRCKey = "HKLM:\Software\Policies\Microsoft\Internet Explorer\Main\"
|
||||||
|
$DFRCName = "DisableFirstRunCustomize"
|
||||||
|
$DFRCAdded = $False
|
||||||
$RequestData = @{}
|
$RequestData = @{}
|
||||||
$RequestData["GetLangs"] = @("a8f8f489-4c7f-463a-9ca6-5cff94d8d041", "GetSkuInformationByProductEdition" )
|
$RequestData["GetLangs"] = @("a8f8f489-4c7f-463a-9ca6-5cff94d8d041", "getskuinformationbyproductedition" )
|
||||||
$RequestData["GetLinks"] = @("cfa9e580-a81e-4a4b-a846-7b21bf4e2e5b", "GetProductDownloadLinksBySku" )
|
$RequestData["GetLinks"] = @("cfa9e580-a81e-4a4b-a846-7b21bf4e2e5b", "GetProductDownloadLinksBySku" )
|
||||||
|
# Create a semi-random Linux User-Agent string
|
||||||
|
$FirefoxVersion = Get-Random -Minimum 30 -Maximum 60
|
||||||
|
$FirefoxDate = Get-RandomDate
|
||||||
|
$UserAgent = "Mozilla/5.0 (X11; Linux i586; rv:$FirefoxVersion.0) Gecko/$FirefoxDate Firefox/$FirefoxVersion.0"
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
# Localization
|
# Localization
|
||||||
$EnglishMessages = "en-US|Version|Release|Edition|Language|Architecture|Download|Continue|Back|Close|Cancel|Error|Please wait...|Download using a browser"
|
$EnglishMessages = "en-US|Version|Release|Edition|Language|Architecture|Download|Continue|Back|Close|Cancel|Error|Please wait...|Download using a browser|Temporarily banned by Microsoft for requesting too many downloads - Please try again later..."
|
||||||
[string[]]$English = $EnglishMessages.Split('|')
|
[string[]]$English = $EnglishMessages.Split('|')
|
||||||
[string[]]$Localized = $null
|
[string[]]$Localized = $null
|
||||||
if ($LocData -and (-not $LocData.StartsWith("en-US"))) {
|
if ($LocData -and (-not $LocData.StartsWith("en-US"))) {
|
||||||
@@ -420,6 +459,20 @@ if ($LocData -and (-not $LocData.StartsWith("en-US"))) {
|
|||||||
$Locale = $Localized[0]
|
$Locale = $Localized[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# If asked, disable IE first run customize as it interferes with Invoke-WebRequest
|
||||||
|
if ($DisableFirstRunCustomize) {
|
||||||
|
try {
|
||||||
|
# Only create the key if it doesn't already exist
|
||||||
|
Get-ItemProperty -Path $DFRCKey -Name $DFRCName -ErrorActionPreference "Stop"
|
||||||
|
} catch {
|
||||||
|
if (-not (Test-Path $DFRCKey)) {
|
||||||
|
New-Item -Path $DFRCKey -Force | Out-Null
|
||||||
|
}
|
||||||
|
Set-ItemProperty -Path $DFRCKey -Name $DFRCName -Value 1
|
||||||
|
$DFRCAdded = $True
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
# Form creation
|
# Form creation
|
||||||
$XMLForm = [Windows.Markup.XamlReader]::Load((New-Object System.Xml.XmlNodeReader $XAML))
|
$XMLForm = [Windows.Markup.XamlReader]::Load((New-Object System.Xml.XmlNodeReader $XAML))
|
||||||
$XAML.SelectNodes("//*[@Name]") | ForEach-Object { Set-Variable -Name ($_.Name) -Value $XMLForm.FindName($_.Name) -Scope Script }
|
$XAML.SelectNodes("//*[@Name]") | ForEach-Object { Set-Variable -Name ($_.Name) -Value $XMLForm.FindName($_.Name) -Scope Script }
|
||||||
@@ -470,10 +523,12 @@ $Continue.add_click({
|
|||||||
Write-Host Querying $url
|
Write-Host Querying $url
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$r = Invoke-WebRequest -SessionVariable "Session" $url
|
# Note: We can't use -UseBasicParsing since we need JS to create the session-id
|
||||||
$script:SessionId = $r.ParsedHtml.IHTMLDocument3_GetElementById("session-id").Value
|
# TODO: Use -Headers @{"Cache-Control"="no-cache"}?
|
||||||
|
$r = Invoke-WebRequest -UserAgent $UserAgent -SessionVariable "Session" $url
|
||||||
|
$script:SessionId = $(GetElementById -Request $r -Id "session-id").Value
|
||||||
if (-not $SessionId) {
|
if (-not $SessionId) {
|
||||||
$ErrorMessage = $r.ParsedHtml.IHTMLDocument3_GetElementByID("errorModalMessage").innerHtml
|
$ErrorMessage = $(GetElementById -Request $r -Id "errorModalMessage").InnerText
|
||||||
if ($ErrorMessage) {
|
if ($ErrorMessage) {
|
||||||
Write-Host "$(Get-Translation("Error")): ""$ErrorMessage"""
|
Write-Host "$(Get-Translation("Error")): ""$ErrorMessage"""
|
||||||
}
|
}
|
||||||
@@ -526,11 +581,18 @@ $Continue.add_click({
|
|||||||
$i = 0
|
$i = 0
|
||||||
$SelectedIndex = 0
|
$SelectedIndex = 0
|
||||||
try {
|
try {
|
||||||
$r = Invoke-WebRequest -WebSession $Session $url
|
$r = Invoke-WebRequest -UserAgent $UserAgent -WebSession $Session $url
|
||||||
foreach ($var in $r.ParsedHtml.IHTMLDocument3_GetElementByID("product-languages")) {
|
# Go through an XML conversion to keep all PowerShells happy...
|
||||||
|
if (-not $($r.AllElements | ? {$_.id -eq "product-languages"})) {
|
||||||
|
throw "Unexpected server response"
|
||||||
|
}
|
||||||
|
$html = $($r.AllElements | ? {$_.id -eq "product-languages"}).InnerHTML
|
||||||
|
$html = "<options>" + $html.Replace("selected value", "value") + "</options>"
|
||||||
|
$xml = [xml]$html
|
||||||
|
foreach ($var in $xml.options.option) {
|
||||||
$json = $var.value | ConvertFrom-Json;
|
$json = $var.value | ConvertFrom-Json;
|
||||||
if ($json) {
|
if ($json) {
|
||||||
$array += @(New-Object PsObject -Property @{ DisplayLanguage = $var.text; Language = $json.language; Id = $json.id })
|
$array += @(New-Object PsObject -Property @{ DisplayLanguage = $var.InnerText; Language = $json.language; Id = $json.id })
|
||||||
if (Select-Language($json.language)) {
|
if (Select-Language($json.language)) {
|
||||||
$SelectedIndex = $i
|
$SelectedIndex = $i
|
||||||
}
|
}
|
||||||
@@ -538,7 +600,7 @@ $Continue.add_click({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ($array.Length -eq 0) {
|
if ($array.Length -eq 0) {
|
||||||
$ErrorMessage = $r.ParsedHtml.IHTMLDocument3_GetElementByID("errorModalMessage").innerHtml
|
$ErrorMessage = $(GetElementById -Request $r -Id "errorModalMessage").innerText
|
||||||
if ($ErrorMessage) {
|
if ($ErrorMessage) {
|
||||||
Write-Host "$(Get-Translation("Error")): ""$ErrorMessage"""
|
Write-Host "$(Get-Translation("Error")): ""$ErrorMessage"""
|
||||||
}
|
}
|
||||||
@@ -568,37 +630,55 @@ $Continue.add_click({
|
|||||||
$SelectedIndex = 0
|
$SelectedIndex = 0
|
||||||
$array = @()
|
$array = @()
|
||||||
try {
|
try {
|
||||||
$r = Invoke-WebRequest -WebSession $Session $url
|
$r = Invoke-WebRequest -UserAgent $UserAgent -WebSession $Session $url
|
||||||
foreach ($var in $r.ParsedHtml.IHTMLDocument3_GetElementsByTagName("span") | Where-Object { $_.className -eq "product-download-type" }) {
|
if (-not $($r.AllElements | ? {$_.id -eq "expiration-time"})) {
|
||||||
$Link = $var.ParentNode | Select -Expand href
|
$ErrorMessage = $(GetElementById -Request $r -Id "errorModalMessage").innerText
|
||||||
$Type = $var.innerText
|
if ($ErrorMessage) {
|
||||||
# Maybe Microsoft will provide public ARM/ARM64 retail ISOs one day...
|
Write-Host "$(Get-Translation("Error")): ""$ErrorMessage"""
|
||||||
if ($Type -like "*arm64*") {
|
}
|
||||||
$Type = "Arm64"
|
throw Get-Translation($English[14])
|
||||||
if ($ENV:PROCESSOR_ARCHITECTURE -eq "ARM64") {
|
}
|
||||||
$SelectedIndex = $i
|
$html = $($r.AllElements | ? {$_.tagname -eq "input"}).outerHTML
|
||||||
}
|
# Need to fix the HTML and JSON data so that it is well-formed
|
||||||
} elseif ($Type -like "*arm*") {
|
$html = $html.Replace("class=product-download-hidden", "")
|
||||||
$Type = "Arm"
|
$html = $html.Replace("type=hidden", "")
|
||||||
if ($ENV:PROCESSOR_ARCHITECTURE -eq "ARM") {
|
$html = $html.Replace(">", "/>")
|
||||||
$SelectedIndex = $i
|
$html = $html.Replace(": I", ": ""I")
|
||||||
}
|
$html = $html.Replace(" }", """ }")
|
||||||
} elseif ($Type -like "*x64*") {
|
$html = "<inputs>" + $html + "</inputs>"
|
||||||
$Type = "x64"
|
$xml = [xml]$html
|
||||||
if ($ENV:PROCESSOR_ARCHITECTURE -eq "AMD64") {
|
foreach ($var in $xml.inputs.input) {
|
||||||
$SelectedIndex = $i
|
$json = $var.value | ConvertFrom-Json;
|
||||||
}
|
if ($json) {
|
||||||
} elseif ($Type -like "*x86*") {
|
$Type = $json.DownloadType
|
||||||
$Type = "x86"
|
# Maybe Microsoft will provide public ARM/ARM64 retail ISOs one day...
|
||||||
if ($ENV:PROCESSOR_ARCHITECTURE -eq "X86") {
|
if ($Type -like "*arm64*") {
|
||||||
$SelectedIndex = $i
|
$Type = "Arm64"
|
||||||
}
|
if ($ENV:PROCESSOR_ARCHITECTURE -eq "ARM64") {
|
||||||
|
$SelectedIndex = $i
|
||||||
|
}
|
||||||
|
} elseif ($Type -like "*arm*") {
|
||||||
|
$Type = "Arm"
|
||||||
|
if ($ENV:PROCESSOR_ARCHITECTURE -eq "ARM") {
|
||||||
|
$SelectedIndex = $i
|
||||||
|
}
|
||||||
|
} elseif ($Type -like "*x64*") {
|
||||||
|
$Type = "x64"
|
||||||
|
if ($ENV:PROCESSOR_ARCHITECTURE -eq "AMD64") {
|
||||||
|
$SelectedIndex = $i
|
||||||
|
}
|
||||||
|
} elseif ($Type -like "*x86*") {
|
||||||
|
$Type = "x86"
|
||||||
|
if ($ENV:PROCESSOR_ARCHITECTURE -eq "X86") {
|
||||||
|
$SelectedIndex = $i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$array += @(New-Object PsObject -Property @{ Type = $Type; Link = $json.Uri })
|
||||||
|
$i++
|
||||||
}
|
}
|
||||||
$array += @(New-Object PsObject -Property @{ Type = $Type; Link = $Link })
|
|
||||||
$i++
|
|
||||||
}
|
}
|
||||||
if ($array.Length -eq 0) {
|
if ($array.Length -eq 0) {
|
||||||
$ErrorMessage = $r.ParsedHtml.IHTMLDocument3_GetElementByID("errorModalMessage").innerHtml
|
$ErrorMessage = $(GetElementById -Request $r -Id "errorModalMessage").innerText
|
||||||
if ($ErrorMessage) {
|
if ($ErrorMessage) {
|
||||||
Write-Host "$(Get-Translation("Error")): ""$ErrorMessage"""
|
Write-Host "$(Get-Translation("Error")): ""$ErrorMessage"""
|
||||||
}
|
}
|
||||||
@@ -699,4 +779,7 @@ $XMLForm.ShowDialog() | Out-Null
|
|||||||
if (-not $PipeName) {
|
if (-not $PipeName) {
|
||||||
Stop-Job -Job $Job
|
Stop-Job -Job $Job
|
||||||
}
|
}
|
||||||
|
if ($DFRCAdded) {
|
||||||
|
Remove-ItemProperty -Path $DFRCKey -Name $DFRCName
|
||||||
|
}
|
||||||
exit $ExitCode
|
exit $ExitCode
|
||||||
|
Reference in New Issue
Block a user