From 5dae1da5604404bce335663d252dc6fcaed6933e Mon Sep 17 00:00:00 2001 From: Gus Date: Fri, 26 Jul 2019 17:15:20 +0200 Subject: [PATCH] Code update - WPinternals is now a .NET Core 3.0 application - Implemented new unlock process for Spec A devices - Updated logic for unlocking Spec B devices - Implemented MMOS support for Spec B devices - Implemented battery status in Flash Mode - Implemented Fuse configuration information in Flash Mode - Implemented Reboot from mass storage for Spec A and some Spec B devices - Implemented shutdown from flash mode (preliminary) - Fixed label mode support for Spec B --- .gitignore | 334 + CommandLine.cs | 48 +- FolderSelectDialog.cs | 234 +- Models/LumiaDownloadModel.cs | 355 + Models/MassStorage.cs | 33 + Models/NokiaFlashModel.cs | 106 + Models/NokiaPhoneModel.cs | 13 + Models/PatchEngine.cs | 10 +- PatchDefinitions.xml | 258 + .../PublishProfiles/FolderProfile.pubxml.user | 6 + SBA | Bin 0 -> 960 bytes .../LumiaFlashRomSourceSelectionViewModel.cs | 35 +- ViewModels/LumiaFlashRomViewModel.cs | 82 +- ViewModels/LumiaModeViewModel.cs | 2 +- ViewModels/LumiaUnlockBootViewModel.cs | 1349 +-- ViewModels/LumiaUnlockBootloaderViewModel.cs | 2253 ++++ ViewModels/LumiaUnlockRootViewModel.cs | 24 +- ViewModels/LumiaV2UnlockBootViewModel.cs | 1114 +- ViewModels/NokiaFlashViewModel.cs | 245 + ViewModels/NokiaModeMassStorageViewModel.cs | 49 +- ViewModels/NokiaNormalViewModel.cs | 176 +- ViewModels/PhoneNotifierViewModel.cs | 27 +- ViewModels/SwitchModeViewModel.cs | 192 +- Views/FlashResourcesView.xaml | 20 + Views/FlashRomView.xaml | 36 +- Views/NokiaFlashView.xaml | 123 +- Views/NokiaLabelView.xaml | 2 +- Views/NokiaModeFlashView.xaml | 5 + Views/NokiaModeMassStorageView.xaml | 83 +- Views/NokiaModeMassStorageView.xaml.cs | 2 +- Views/NokiaNormalView.xaml | 40 +- WPinternals.Core.csproj | 53 + ...sproj.user => WPinternals.Core.csproj.user | 320 +- WPinternals.csproj | 1276 --- WPinternals.sln | 174 +- WinUSBNet/API/WinUSBDevice.cs | 8 + WinUSBNet/API/WinUSBDeviceAPI.cs | 3 + WinUSBNet/USBPipe.cs | 16 + app.config | 12 - packages.config | 4 - .../Newtonsoft.Json.9.0.1.nupkg | Bin 1603487 -> 0 bytes .../Newtonsoft.Json.9.0.1.nuspec | 50 - .../lib/net20/Newtonsoft.Json.dll | Bin 483840 -> 0 bytes .../lib/net20/Newtonsoft.Json.xml | 9793 ----------------- .../lib/net35/Newtonsoft.Json.dll | Bin 447488 -> 0 bytes .../lib/net35/Newtonsoft.Json.xml | 8922 --------------- .../lib/net40/Newtonsoft.Json.dll | Bin 489472 -> 0 bytes .../lib/net40/Newtonsoft.Json.xml | 9229 ---------------- .../lib/net45/Newtonsoft.Json.dll | Bin 526336 -> 0 bytes .../lib/net45/Newtonsoft.Json.xml | 9229 ---------------- .../lib/netstandard1.0/Newtonsoft.Json.dll | Bin 468480 -> 0 bytes .../lib/netstandard1.0/Newtonsoft.Json.xml | 8756 --------------- .../Newtonsoft.Json.dll | Bin 419840 -> 0 bytes .../Newtonsoft.Json.xml | 8409 -------------- .../Newtonsoft.Json.dll | Bin 468480 -> 0 bytes .../Newtonsoft.Json.xml | 8756 --------------- .../Newtonsoft.Json.9.0.1/tools/install.ps1 | 116 - packages/repositories.config | 4 - 58 files changed, 5522 insertions(+), 66864 deletions(-) create mode 100644 .gitignore create mode 100644 Properties/PublishProfiles/FolderProfile.pubxml.user create mode 100644 SBA create mode 100644 ViewModels/LumiaUnlockBootloaderViewModel.cs create mode 100644 WPinternals.Core.csproj rename WPinternals.csproj.user => WPinternals.Core.csproj.user (68%) delete mode 100644 WPinternals.csproj delete mode 100644 app.config delete mode 100644 packages.config delete mode 100644 packages/Newtonsoft.Json.9.0.1/Newtonsoft.Json.9.0.1.nupkg delete mode 100644 packages/Newtonsoft.Json.9.0.1/Newtonsoft.Json.9.0.1.nuspec delete mode 100644 packages/Newtonsoft.Json.9.0.1/lib/net20/Newtonsoft.Json.dll delete mode 100644 packages/Newtonsoft.Json.9.0.1/lib/net20/Newtonsoft.Json.xml delete mode 100644 packages/Newtonsoft.Json.9.0.1/lib/net35/Newtonsoft.Json.dll delete mode 100644 packages/Newtonsoft.Json.9.0.1/lib/net35/Newtonsoft.Json.xml delete mode 100644 packages/Newtonsoft.Json.9.0.1/lib/net40/Newtonsoft.Json.dll delete mode 100644 packages/Newtonsoft.Json.9.0.1/lib/net40/Newtonsoft.Json.xml delete mode 100644 packages/Newtonsoft.Json.9.0.1/lib/net45/Newtonsoft.Json.dll delete mode 100644 packages/Newtonsoft.Json.9.0.1/lib/net45/Newtonsoft.Json.xml delete mode 100644 packages/Newtonsoft.Json.9.0.1/lib/netstandard1.0/Newtonsoft.Json.dll delete mode 100644 packages/Newtonsoft.Json.9.0.1/lib/netstandard1.0/Newtonsoft.Json.xml delete mode 100644 packages/Newtonsoft.Json.9.0.1/lib/portable-net40+sl5+wp80+win8+wpa81/Newtonsoft.Json.dll delete mode 100644 packages/Newtonsoft.Json.9.0.1/lib/portable-net40+sl5+wp80+win8+wpa81/Newtonsoft.Json.xml delete mode 100644 packages/Newtonsoft.Json.9.0.1/lib/portable-net45+wp80+win8+wpa81/Newtonsoft.Json.dll delete mode 100644 packages/Newtonsoft.Json.9.0.1/lib/portable-net45+wp80+win8+wpa81/Newtonsoft.Json.xml delete mode 100644 packages/Newtonsoft.Json.9.0.1/tools/install.ps1 delete mode 100644 packages/repositories.config diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9c9348a --- /dev/null +++ b/.gitignore @@ -0,0 +1,334 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +# *.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# JetBrains Rider +.idea/ +*.sln.iml + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ diff --git a/CommandLine.cs b/CommandLine.cs index 7dd5bb8..1061b71 100644 --- a/CommandLine.cs +++ b/CommandLine.cs @@ -217,7 +217,7 @@ namespace WPinternals Notifier = new PhoneNotifierViewModel(); UIContext.Send(s => Notifier.Start(), null); FlashModel = (NokiaFlashModel)(await SwitchModeViewModel.SwitchTo(Notifier, PhoneInterfaces.Lumia_Flash)); - byte[] GptChunk = LumiaV2UnlockBootViewModel.GetGptChunk(FlashModel, 0x20000); + byte[] GptChunk = LumiaUnlockBootloaderViewModel.GetGptChunk(FlashModel, 0x20000); GPT GPT = new GPT(GptChunk); string Xml = File.ReadAllText(args[2]); GPT.MergePartitions(Xml, false); @@ -436,13 +436,47 @@ namespace WPinternals break; case "relockphone": Notifier = new PhoneNotifierViewModel(); - UIContext.Send(s => Notifier.Start(), null); + try + { + UIContext.Send(s => Notifier.Start(), null); + FlashModel = (NokiaFlashModel)(await SwitchModeViewModel.SwitchTo(Notifier, PhoneInterfaces.Lumia_Flash)); + Info = FlashModel.ReadPhoneInfo(); + Info.Log(LogType.ConsoleOnly); - if (args.Length > 2) - await LumiaV2UnlockBootViewModel.LumiaV2RelockPhone(Notifier, args[2]); - else - await LumiaV2UnlockBootViewModel.LumiaV2RelockPhone(Notifier, null); + FFU ProfileFFU = null; + FFU CurrentFFU; + for (int i = 2; i <= 3; i++) + { + if (args.Length > i) + { + CurrentFFU = new FFU(args[i]); + string CurrentVersion = CurrentFFU.GetOSVersion(); + string PlatformID = CurrentFFU.PlatformID; + // Check if the current FFU matches the connected phone, so that the FFU can be used for profiling. + if (Info.PlatformID.StartsWith(PlatformID, StringComparison.OrdinalIgnoreCase)) + ProfileFFU = CurrentFFU; + } + } + + if (ProfileFFU == null) + { + List FFUs = App.Config.FFURepository.Where(e => (Info.PlatformID.StartsWith(e.PlatformID, StringComparison.OrdinalIgnoreCase) && e.Exists())).ToList(); + if (FFUs.Count() > 0) + ProfileFFU = new FFU(FFUs[0].Path); + else + throw new WPinternalsException("Profile FFU missing"); + } + LogFile.Log("Profile FFU: " + ProfileFFU.Path); + + UIContext.Send(s => Notifier.Start(), null); + + await LumiaUnlockBootloaderViewModel.LumiaRelockUEFI(Notifier, ProfileFFU.Path); + } + catch (Exception Ex) + { + LogFile.LogException(Ex); + } Notifier.Stop(); break; case "addffu": @@ -580,7 +614,7 @@ namespace WPinternals throw new WPinternalsException("No donor-FFU found with supported OS version"); } - await LumiaV2UnlockBootViewModel.LumiaV2UnlockBootloader(Notifier, ProfileFFU.Path, null, SupportedFFU.Path); + await LumiaUnlockBootloaderViewModel.LumiaUnlockUEFI(Notifier, ProfileFFU.Path, null, SupportedFFU.Path); Notifier.Stop(); } diff --git a/FolderSelectDialog.cs b/FolderSelectDialog.cs index fa5a651..ab4fa87 100644 --- a/FolderSelectDialog.cs +++ b/FolderSelectDialog.cs @@ -4,7 +4,6 @@ using System; using System.Windows.Forms; -using System.Reflection; namespace WPinternals { @@ -79,48 +78,14 @@ namespace WPinternals /// True if the user presses OK else false public bool ShowDialog(IntPtr hWndOwner) { - bool flag = false; + var fbd = new FolderBrowserDialog(); + fbd.Description = this.Title; + fbd.SelectedPath = this.InitialDirectory; + fbd.ShowNewFolderButton = false; + if (fbd.ShowDialog(new WindowWrapper(hWndOwner)) != DialogResult.OK) return false; + ofd.FileName = fbd.SelectedPath; - if (Environment.OSVersion.Version.Major >= 6) - { - var r = new Reflector("System.Windows.Forms"); - - uint num = 0; - Type typeIFileDialog = r.GetType("FileDialogNative.IFileDialog"); - object dialog = r.Call(ofd, "CreateVistaDialog"); - r.Call(ofd, "OnBeforeVistaDialog", dialog); - - uint options = (uint)r.CallAs(typeof(System.Windows.Forms.FileDialog), ofd, "GetOptions"); - options |= (uint)r.GetEnum("FileDialogNative.FOS", "FOS_PICKFOLDERS"); - r.CallAs(typeIFileDialog, dialog, "SetOptions", options); - - object pfde = r.New("FileDialog.VistaDialogEvents", ofd); - object[] parameters = new object[] { pfde, num }; - r.CallAs2(typeIFileDialog, dialog, "Advise", parameters); - num = (uint)parameters[1]; - try - { - int num2 = (int)r.CallAs(typeIFileDialog, dialog, "Show", hWndOwner); - flag = 0 == num2; - } - finally - { - r.CallAs(typeIFileDialog, dialog, "Unadvise", num); - GC.KeepAlive(pfde); - } - } - else - { - var fbd = new FolderBrowserDialog(); - fbd.Description = this.Title; - fbd.SelectedPath = this.InitialDirectory; - fbd.ShowNewFolderButton = false; - if (fbd.ShowDialog(new WindowWrapper(hWndOwner)) != DialogResult.OK) return false; - ofd.FileName = fbd.SelectedPath; - flag = true; - } - - return flag; + return true; } #endregion @@ -150,189 +115,4 @@ namespace WPinternals private IntPtr _hwnd; } - - /// - /// This class is from the Front-End for Dosbox and is used to present a 'vista' dialog box to select folders. - /// Being able to use a vista style dialog box to select folders is much better then using the shell folder browser. - /// http://code.google.com/p/fed/ - /// - /// Example: - /// var r = new Reflector("System.Windows.Forms"); - /// - public class Reflector - { - #region variables - - string m_ns; - Assembly m_asmb; - - #endregion - - #region Constructors - - /// - /// Constructor - /// - /// The namespace containing types to be used - public Reflector(string ns) - : this(ns, ns) - { } - - /// - /// Constructor - /// - /// A specific assembly name (used if the assembly name does not tie exactly with the namespace) - /// The namespace containing types to be used - public Reflector(string an, string ns) - { - m_ns = ns; - m_asmb = null; - foreach (AssemblyName aN in Assembly.GetExecutingAssembly().GetReferencedAssemblies()) - { - if (aN.FullName.StartsWith(an)) - { - m_asmb = Assembly.Load(aN); - break; - } - } - } - - #endregion - - #region Methods - - /// - /// Return a Type instance for a type 'typeName' - /// - /// The name of the type - /// A type instance - public Type GetType(string typeName) - { - Type type = null; - string[] names = typeName.Split('.'); - - if (names.Length > 0) - type = m_asmb.GetType(m_ns + "." + names[0]); - - for (int i = 1; i < names.Length; ++i) - { - type = type.GetNestedType(names[i], BindingFlags.NonPublic); - } - return type; - } - - /// - /// Create a new object of a named type passing along any params - /// - /// The name of the type to create - /// - /// An instantiated type - public object New(string name, params object[] parameters) - { - Type type = GetType(name); - - ConstructorInfo[] ctorInfos = type.GetConstructors(); - foreach (ConstructorInfo ci in ctorInfos) - { - try - { - return ci.Invoke(parameters); - } - catch { } - } - - return null; - } - - /// - /// Calls method 'func' on object 'obj' passing parameters 'parameters' - /// - /// The object on which to excute function 'func' - /// The function to execute - /// The parameters to pass to function 'func' - /// The result of the function invocation - public object Call(object obj, string func, params object[] parameters) - { - return Call2(obj, func, parameters); - } - - /// - /// Calls method 'func' on object 'obj' passing parameters 'parameters' - /// - /// The object on which to excute function 'func' - /// The function to execute - /// The parameters to pass to function 'func' - /// The result of the function invocation - public object Call2(object obj, string func, object[] parameters) - { - return CallAs2(obj.GetType(), obj, func, parameters); - } - - /// - /// Calls method 'func' on object 'obj' which is of type 'type' passing parameters 'parameters' - /// - /// The type of 'obj' - /// The object on which to excute function 'func' - /// The function to execute - /// The parameters to pass to function 'func' - /// The result of the function invocation - public object CallAs(Type type, object obj, string func, params object[] parameters) - { - return CallAs2(type, obj, func, parameters); - } - - /// - /// Calls method 'func' on object 'obj' which is of type 'type' passing parameters 'parameters' - /// - /// The type of 'obj' - /// The object on which to excute function 'func' - /// The function to execute - /// The parameters to pass to function 'func' - /// The result of the function invocation - public object CallAs2(Type type, object obj, string func, object[] parameters) - { - MethodInfo methInfo = type.GetMethod(func, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); - return methInfo.Invoke(obj, parameters); - } - - /// - /// Returns the value of property 'prop' of object 'obj' - /// - /// The object containing 'prop' - /// The property name - /// The property value - public object Get(object obj, string prop) - { - return GetAs(obj.GetType(), obj, prop); - } - - /// - /// Returns the value of property 'prop' of object 'obj' which has type 'type' - /// - /// The type of 'obj' - /// The object containing 'prop' - /// The property name - /// The property value - public object GetAs(Type type, object obj, string prop) - { - PropertyInfo propInfo = type.GetProperty(prop, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); - return propInfo.GetValue(obj, null); - } - - /// - /// Returns an enum value - /// - /// The name of enum type - /// The name of the value - /// The enum value - public object GetEnum(string typeName, string name) - { - Type type = GetType(typeName); - FieldInfo fieldInfo = type.GetField(name); - return fieldInfo.GetValue(null); - } - - #endregion - - } } diff --git a/Models/LumiaDownloadModel.cs b/Models/LumiaDownloadModel.cs index 179100a..fbc43bb 100644 --- a/Models/LumiaDownloadModel.cs +++ b/Models/LumiaDownloadModel.cs @@ -30,6 +30,7 @@ using System.Net.Http; using System.Net.Http.Headers; using System.IO; using System.Xml; +using System.Xml.Serialization; namespace WPinternals { @@ -144,6 +145,148 @@ namespace WPinternals return FfuUrl; } + internal static string SearchENOSW(string ProductType, string PhoneFirmwareRevision) + { + if (ProductType == "") + ProductType = null; + + if (ProductType != null) + { + ProductType = ProductType.ToUpper(); + if (ProductType.StartsWith("RM") && !ProductType.StartsWith("RM-")) + ProductType = "RM-" + ProductType.Substring(2); + } + + DiscoveryQueryParameters DiscoveryQueryParams = new DiscoveryQueryParameters + { + manufacturerName = "Microsoft", + manufacturerProductLine = "Lumia", + packageType = "Test Mode", + packageClass = "Public", + manufacturerHardwareModel = ProductType + }; + DiscoveryParameters DiscoveryParams = new DiscoveryParameters + { + query = DiscoveryQueryParams + }; + + DataContractJsonSerializer Serializer1 = new DataContractJsonSerializer(typeof(DiscoveryParameters)); + MemoryStream JsonStream1 = new MemoryStream(); + Serializer1.WriteObject(JsonStream1, DiscoveryParams); + JsonStream1.Seek(0L, SeekOrigin.Begin); + string JsonContent = new StreamReader(JsonStream1).ReadToEnd(); + + Uri RequestUri = new Uri("https://api.swrepository.com/rest-api/discovery/1/package"); + + HttpClient HttpClient = new HttpClient(); + HttpClient.DefaultRequestHeaders.UserAgent.TryParseAdd("SoftwareRepository"); + HttpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); + + Task HttpPostTask = HttpClient.PostAsync(RequestUri, new StringContent(JsonContent, Encoding.UTF8, "application/json")); + HttpPostTask.Wait(); + HttpResponseMessage Response = HttpPostTask.Result; + + string JsonResultString = ""; + if (Response.StatusCode == HttpStatusCode.OK) + { + Task ReadResponseTask = Response.Content.ReadAsStringAsync(); + ReadResponseTask.Wait(); + JsonResultString = ReadResponseTask.Result; + } + + SoftwarePackage Package = null; + using (MemoryStream JsonStream2 = new MemoryStream(Encoding.UTF8.GetBytes(JsonResultString))) + { + DataContractJsonSerializer Serializer2 = new DataContractJsonSerializer(typeof(SoftwarePackages)); + SoftwarePackages SoftwarePackages = (SoftwarePackages)Serializer2.ReadObject(JsonStream2); + if (SoftwarePackages != null) + { + foreach (SoftwarePackage pkg in SoftwarePackages.softwarePackages) + Package = SoftwarePackages.softwarePackages.FirstOrDefault(); + } + } + + if (Package == null) + throw new WPinternalsException("ENOSW package not found"); + + SoftwareFile FileInfo = Package.files.Where(f => f.fileName.EndsWith(".secwim", StringComparison.OrdinalIgnoreCase)).First(); + + SoftwareFile DPLF = Package.files.Where(f => f.fileName.EndsWith(".dpl", StringComparison.OrdinalIgnoreCase)).First(); + Uri DPLUri = new Uri("https://api.swrepository.com/rest-api/discovery/fileurl/1/" + Package.id + "/" + DPLF.fileName); + Task GetDPLTask = HttpClient.GetStringAsync(DPLUri); + GetDPLTask.Wait(); + string DPLString = GetDPLTask.Result; + + string DPLUrl = ""; + FileUrlResult FileUrlDPL = null; + using (MemoryStream JsonStream3 = new MemoryStream(Encoding.UTF8.GetBytes(DPLString))) + { + DataContractJsonSerializer Serializer3 = new DataContractJsonSerializer(typeof(FileUrlResult)); + FileUrlDPL = (FileUrlResult)Serializer3.ReadObject(JsonStream3); + if (FileUrlDPL != null) + { + DPLUrl = FileUrlDPL.url; + } + } + + if (DPLUrl == "") + throw new WPinternalsException("DPL not found"); + + Task GetDPLStrTask = HttpClient.GetStringAsync(DPLUrl); + GetDPLStrTask.Wait(); + string DPLStrString = GetDPLStrTask.Result; + + DPL.Package dpl; + XmlSerializer serializer = new XmlSerializer(typeof(DPL.Package)); + using (StringReader reader = new StringReader(DPLStrString.Replace("ft:", "").Replace("dpl:", "").Replace("typedes:", ""))) + { + dpl = (DPL.Package)serializer.Deserialize(reader); + } + + foreach (DPL.File file in dpl.Content.Files.File) + { + string name = file.Name; + + DPL.Range range = file.Extensions.MmosWimFile.UseCaseCompatibilities.Compatibility.FirstOrDefault().Range; + + if (IsFirmwareBetween(PhoneFirmwareRevision, range.From, range.To)) + FileInfo = Package.files.Where(f => f.fileName.EndsWith(name, StringComparison.OrdinalIgnoreCase)).First(); + } + + Uri FileInfoUri = new Uri("https://api.swrepository.com/rest-api/discovery/fileurl/1/" + Package.id + "/" + FileInfo.fileName); + Task GetFileInfoTask = HttpClient.GetStringAsync(FileInfoUri); + GetFileInfoTask.Wait(); + string FileInfoString = GetFileInfoTask.Result; + + string ENOSWUrl = ""; + FileUrlResult FileUrl = null; + using (MemoryStream JsonStream3 = new MemoryStream(Encoding.UTF8.GetBytes(FileInfoString))) + { + DataContractJsonSerializer Serializer3 = new DataContractJsonSerializer(typeof(FileUrlResult)); + FileUrl = (FileUrlResult)Serializer3.ReadObject(JsonStream3); + if (FileUrl != null) + { + ENOSWUrl = FileUrl.url; + } + } + + HttpClient.Dispose(); + + return ENOSWUrl; + } + + private static bool IsFirmwareBetween(string PhoneFirmwareRevision, string Limit1, string Limit2) + { + var version = new Version(PhoneFirmwareRevision); + var version1 = new Version(Limit1); + var version2 = new Version(Limit2); + + var result = version.CompareTo(version1); + var result2 = version.CompareTo(version2); + + return result >= 0 && result2 <= 0; + } + internal static string[] SearchEmergencyFiles(string ProductType) { ProductType = ProductType.ToUpper(); @@ -475,4 +618,216 @@ namespace WPinternals [DataMember] public string fileType; } + + public static class DPL + { + [XmlRoot(ElementName = "BasicProductCodes")] + public class BasicProductCodes + { + [XmlElement(ElementName = "BasicProductCode")] + public List BasicProductCode { get; set; } + } + + [XmlRoot(ElementName = "Identification")] + public class Identification + { + [XmlElement(ElementName = "TypeDesignator")] + public string TypeDesignator { get; set; } + [XmlElement(ElementName = "BasicProductCodes")] + public BasicProductCodes BasicProductCodes { get; set; } + [XmlElement(ElementName = "Purpose")] + public string Purpose { get; set; } + } + + [XmlRoot(ElementName = "Extensions")] + public class Extensions + { + [XmlElement(ElementName = "PackageType")] + public string PackageType { get; set; } + [XmlElement(ElementName = "Identification")] + public Identification Identification { get; set; } + [XmlElement(ElementName = "FileType")] + public string FileType { get; set; } + [XmlElement(ElementName = "MmosWimFile")] + public MmosWimFile MmosWimFile { get; set; } + } + + [XmlRoot(ElementName = "PackageDescription")] + public class PackageDescription + { + [XmlElement(ElementName = "Identifier")] + public string Identifier { get; set; } + [XmlElement(ElementName = "Revision")] + public string Revision { get; set; } + [XmlElement(ElementName = "Extensions")] + public Extensions Extensions { get; set; } + } + + [XmlRoot(ElementName = "Digest")] + public class Digest + { + [XmlAttribute(AttributeName = "method")] + public string Method { get; set; } + [XmlAttribute(AttributeName = "encoding")] + public string Encoding { get; set; } + [XmlText] + public string Text { get; set; } + } + + [XmlRoot(ElementName = "Digests")] + public class Digests + { + [XmlElement(ElementName = "Digest")] + public List Digest { get; set; } + } + + [XmlRoot(ElementName = "Range")] + public class Range + { + [XmlAttribute(AttributeName = "from")] + public string From { get; set; } + [XmlAttribute(AttributeName = "to")] + public string To { get; set; } + } + + [XmlRoot(ElementName = "Compatibility")] + public class Compatibility + { + [XmlElement(ElementName = "Range")] + public Range Range { get; set; } + [XmlAttribute(AttributeName = "useCase")] + public string UseCase { get; set; } + } + + [XmlRoot(ElementName = "UseCaseCompatibilities")] + public class UseCaseCompatibilities + { + [XmlElement(ElementName = "Compatibility")] + public List Compatibility { get; set; } + } + + [XmlRoot(ElementName = "MmosWimFile")] + public class MmosWimFile + { + [XmlElement(ElementName = "UseCaseCompatibilities")] + public UseCaseCompatibilities UseCaseCompatibilities { get; set; } + } + + [XmlRoot(ElementName = "File")] + public class File + { + [XmlElement(ElementName = "Name")] + public string Name { get; set; } + [XmlElement(ElementName = "Digests")] + public Digests Digests { get; set; } + [XmlElement(ElementName = "Revision")] + public string Revision { get; set; } + [XmlElement(ElementName = "Extensions")] + public Extensions Extensions { get; set; } + } + + [XmlRoot(ElementName = "Files")] + public class Files + { + [XmlElement(ElementName = "File")] + public List File { get; set; } + } + + [XmlRoot(ElementName = "Content")] + public class Content + { + [XmlElement(ElementName = "PackageDescription")] + public PackageDescription PackageDescription { get; set; } + [XmlElement(ElementName = "Files")] + public Files Files { get; set; } + } + + [XmlRoot(ElementName = "CanonicalizationMethod", Namespace = "http://www.w3.org/2000/09/xmldsig#")] + public class CanonicalizationMethod + { + [XmlAttribute(AttributeName = "Algorithm")] + public string Algorithm { get; set; } + } + + [XmlRoot(ElementName = "SignatureMethod", Namespace = "http://www.w3.org/2000/09/xmldsig#")] + public class SignatureMethod + { + [XmlAttribute(AttributeName = "Algorithm")] + public string Algorithm { get; set; } + } + + [XmlRoot(ElementName = "Transform", Namespace = "http://www.w3.org/2000/09/xmldsig#")] + public class Transform + { + [XmlAttribute(AttributeName = "Algorithm")] + public string Algorithm { get; set; } + } + + [XmlRoot(ElementName = "Transforms", Namespace = "http://www.w3.org/2000/09/xmldsig#")] + public class Transforms + { + [XmlElement(ElementName = "Transform", Namespace = "http://www.w3.org/2000/09/xmldsig#")] + public Transform Transform { get; set; } + } + + [XmlRoot(ElementName = "DigestMethod", Namespace = "http://www.w3.org/2000/09/xmldsig#")] + public class DigestMethod + { + [XmlAttribute(AttributeName = "Algorithm")] + public string Algorithm { get; set; } + } + + [XmlRoot(ElementName = "Reference", Namespace = "http://www.w3.org/2000/09/xmldsig#")] + public class Reference + { + [XmlElement(ElementName = "Transforms", Namespace = "http://www.w3.org/2000/09/xmldsig#")] + public Transforms Transforms { get; set; } + [XmlElement(ElementName = "DigestMethod", Namespace = "http://www.w3.org/2000/09/xmldsig#")] + public DigestMethod DigestMethod { get; set; } + [XmlElement(ElementName = "DigestValue", Namespace = "http://www.w3.org/2000/09/xmldsig#")] + public string DigestValue { get; set; } + [XmlAttribute(AttributeName = "URI")] + public string URI { get; set; } + } + + [XmlRoot(ElementName = "SignedInfo", Namespace = "http://www.w3.org/2000/09/xmldsig#")] + public class SignedInfo + { + [XmlElement(ElementName = "CanonicalizationMethod", Namespace = "http://www.w3.org/2000/09/xmldsig#")] + public CanonicalizationMethod CanonicalizationMethod { get; set; } + [XmlElement(ElementName = "SignatureMethod", Namespace = "http://www.w3.org/2000/09/xmldsig#")] + public SignatureMethod SignatureMethod { get; set; } + [XmlElement(ElementName = "Reference", Namespace = "http://www.w3.org/2000/09/xmldsig#")] + public Reference Reference { get; set; } + } + + [XmlRoot(ElementName = "KeyInfo", Namespace = "http://www.w3.org/2000/09/xmldsig#")] + public class KeyInfo + { + [XmlElement(ElementName = "KeyName", Namespace = "http://www.w3.org/2000/09/xmldsig#")] + public string KeyName { get; set; } + } + + [XmlRoot(ElementName = "Signature", Namespace = "http://www.w3.org/2000/09/xmldsig#")] + public class Signature + { + [XmlElement(ElementName = "SignedInfo", Namespace = "http://www.w3.org/2000/09/xmldsig#")] + public SignedInfo SignedInfo { get; set; } + [XmlElement(ElementName = "SignatureValue", Namespace = "http://www.w3.org/2000/09/xmldsig#")] + public string SignatureValue { get; set; } + [XmlElement(ElementName = "KeyInfo", Namespace = "http://www.w3.org/2000/09/xmldsig#")] + public KeyInfo KeyInfo { get; set; } + [XmlAttribute(AttributeName = "xmlns")] + public string Xmlns { get; set; } + } + + [XmlRoot(ElementName = "Package")] + public class Package + { + [XmlElement(ElementName = "Content")] + public Content Content { get; set; } + [XmlElement(ElementName = "Signature", Namespace = "http://www.w3.org/2000/09/xmldsig#")] + public Signature Signature { get; set; } + } + } } diff --git a/Models/MassStorage.cs b/Models/MassStorage.cs index 24942fc..78164be 100644 --- a/Models/MassStorage.cs +++ b/Models/MassStorage.cs @@ -35,6 +35,8 @@ namespace WPinternals internal IntPtr hDrive = (IntPtr)(-1); private bool OpenWithWriteAccess; + private QualcommSerial Serial; + internal MassStorage(string DevicePath): base(DevicePath) { try @@ -71,6 +73,37 @@ namespace WPinternals catch { } } + internal void AttachQualcommSerial(string DevicePath) + { + try + { + Serial = new QualcommSerial(DevicePath); + Serial.EncodeCommands = false; + } + catch { } + } + + internal bool DoesDeviceSupportReboot() + { + return Serial != null; + } + + internal void Reboot() + { + if (Serial == null) + return; + + try + { + // This will succeed on new models + Serial.SendData(new byte[] { 0x7, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0 }); + + // This will succeed on old models + Serial.SendData(new byte[] { 0x7E, 0xA, 0x0, 0x0, 0xB6, 0xB5, 0x7E }); + } + catch { } + } + protected override void Dispose(bool disposing) { if (Disposed) diff --git a/Models/NokiaFlashModel.cs b/Models/NokiaFlashModel.cs index 27cb539..ab82678 100644 --- a/Models/NokiaFlashModel.cs +++ b/Models/NokiaFlashModel.cs @@ -75,6 +75,36 @@ namespace WPinternals return System.Text.ASCIIEncoding.ASCII.GetString(Bytes).Trim(new char[] { '\0' }); } + [Flags] + internal enum Fuse + { + SecureBoot = 1, + FfuVerify = 2, + Jtag = 4, + Shk = 8, + Simlock = 16, + ProductionDone = 32, + Rkh = 64, + PublicId = 128, + Dak = 256, + SecGen = 512, + OemId = 1024, + FastBoot = 2048, + SpdmSecMode = 4096, + RpmWdog = 8192, + Ssm = 16384 + } + + public bool? ReadFuseStatus(Fuse fuse) + { + uint? flags = ReadSecurityFlags(); + if (!flags.HasValue) + return null; + + var finalconfig = (Fuse)flags.Value; + return finalconfig.HasFlag(fuse); + } + public uint? ReadSecurityFlags() { byte[] Response = ReadParam("FCS"); @@ -84,6 +114,24 @@ namespace WPinternals return (UInt32)((Response[0] << 24) | (Response[1] << 16) | (Response[2] << 8) | Response[3]); } + public uint? ReadCurrentChargeLevel() + { + byte[] Response = ReadParam("CS"); + if ((Response == null) || (Response.Length != 8)) return null; + + // This value is in big endian + return (UInt32)((Response[0] << 24) | (Response[1] << 16) | (Response[2] << 8) | Response[3]) + 1; + } + + public uint? ReadCurrentChargeCurrent() + { + byte[] Response = ReadParam("CS"); + if ((Response == null) || (Response.Length != 8)) return null; + + // This value is in big endian and needs to be XOR'd with 0xFFFFFFFF + return ((UInt32)((Response[4] << 24) | (Response[5] << 16) | (Response[6] << 8) | Response[7]) ^ 0xFFFFFFFF) + 1; + } + public UefiSecurityStatusResponse ReadSecurityStatus() { if (_SecurityStatus != null) @@ -320,7 +368,18 @@ namespace WPinternals if (ResultCode != 0) ThrowFlashError(ResultCode); } + + internal void SwitchToMmosContext() + { + byte[] Request = new byte[7]; + ByteOperations.WriteAsciiString(Request, 0, "NOKXCBA"); + ExecuteRawVoidMethod(Request); + + ResetDevice(); + Dispose(true); + } + private void ThrowFlashError(int ErrorCode) { string SubMessage; @@ -451,6 +510,53 @@ namespace WPinternals LogFile.EndAction("FlashFFU"); } + public void FlashMMOS(string MMOSPath, ProgressUpdater UpdaterPerChunk) + { + LogFile.BeginAction("FlashMMOS"); + + ProgressUpdater Progress = UpdaterPerChunk; + + PhoneInfo Info = ReadPhoneInfo(); + if (!Info.MmosOverUsbSupported) + throw new WPinternalsException("Flash failed!", "Protocols not supported"); + + FileInfo info = new FileInfo(MMOSPath); + uint length = uint.Parse(info.Length.ToString()); + + int offset = 0; + int maximumbuffersize = 0x00240000; + + uint totalcounts = (uint)Math.Truncate((decimal)length / maximumbuffersize); + + using (System.IO.FileStream MMOSFile = new System.IO.FileStream(MMOSPath, System.IO.FileMode.Open, System.IO.FileAccess.Read)) + { + for (int i = 1; i <= (uint)Math.Truncate((decimal)length / maximumbuffersize); i++) + { + Progress.IncreaseProgress(1); + byte[] data = new byte[maximumbuffersize]; + MMOSFile.Read(data, 0, maximumbuffersize); + + LoadMmosBinary(length, (uint)offset, false, data); + + offset += maximumbuffersize; + } + + if (length - offset != 0) + { + Progress.IncreaseProgress(1); + + byte[] data = new byte[length - offset]; + MMOSFile.Read(data, 0, (int)(length - offset)); + LoadMmosBinary(length, (uint)offset, false, data); + } + + SwitchToMmosContext(); + ResetPhone(); + } + + LogFile.EndAction("FlashMMOS"); + } + public void FlashSectors(UInt32 StartSector, byte[] Data, int Progress = 0) { // Start sector is in UInt32, so max size of eMMC is 2 TB. diff --git a/Models/NokiaPhoneModel.cs b/Models/NokiaPhoneModel.cs index 48435e9..9e71c27 100644 --- a/Models/NokiaPhoneModel.cs +++ b/Models/NokiaPhoneModel.cs @@ -302,6 +302,19 @@ namespace WPinternals } } + public void ResetDevice() + { + try + { + foreach (var pipe in Device.Pipes) + { + pipe.Abort(); + pipe.Reset(); + } + } + catch { } + } + /// /// Disposes the UsbDevice including all unmanaged WinUSB handles. This function /// should be called when the UsbDevice object is no longer in use, otherwise diff --git a/Models/PatchEngine.cs b/Models/PatchEngine.cs index 290f115..376de7a 100644 --- a/Models/PatchEngine.cs +++ b/Models/PatchEngine.cs @@ -427,18 +427,17 @@ namespace WPinternals } // Backup original owner and ACL - OriginalACL = File.GetAccessControl(FilePath); + + OriginalACL = new FileSecurity(FilePath, AccessControlSections.Owner | AccessControlSections.Group | AccessControlSections.Access); // And take the original security to create new security rules. - FileSecurity NewACL = File.GetAccessControl(FilePath); + FileSecurity NewACL = new FileSecurity(FilePath, AccessControlSections.Owner | AccessControlSections.Group | AccessControlSections.Access); // Take ownership NewACL.SetOwner(WindowsIdentity.GetCurrent().User); - File.SetAccessControl(FilePath, NewACL); // And create a new access rule NewACL.SetAccessRule(new FileSystemAccessRule(WindowsIdentity.GetCurrent().User, FileSystemRights.FullControl, AccessControlType.Allow)); - File.SetAccessControl(FilePath, NewACL); // Open the file for patching Stream = new FileStream(FilePath, FileMode.Open, FileAccess.ReadWrite); @@ -460,9 +459,8 @@ namespace WPinternals { // Restore original owner and access rules. // The OriginalACL cannot be reused directly. - FileSecurity NewACL = File.GetAccessControl(FilePath); + FileSecurity NewACL = new FileSecurity(FilePath, AccessControlSections.Owner | AccessControlSections.Group | AccessControlSections.Access); NewACL.SetSecurityDescriptorBinaryForm(OriginalACL.GetSecurityDescriptorBinaryForm()); - File.SetAccessControl(FilePath, NewACL); // Revert to self RestorePrivilege.Revert(); diff --git a/PatchDefinitions.xml b/PatchDefinitions.xml index 4b34000..3edbe46 100644 --- a/PatchDefinitions.xml +++ b/PatchDefinitions.xml @@ -3997,6 +3997,264 @@ DEALINGS IN THE SOFTWARE. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Properties/PublishProfiles/FolderProfile.pubxml.user b/Properties/PublishProfiles/FolderProfile.pubxml.user new file mode 100644 index 0000000..312c6e3 --- /dev/null +++ b/Properties/PublishProfiles/FolderProfile.pubxml.user @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/SBA b/SBA new file mode 100644 index 0000000000000000000000000000000000000000..fd9c7437c9438b09ede535673557622810a8f023 GIT binary patch literal 960 zcmezGoS$1zlv-Szni7y$RFYYenV-kN$iTp$3d9TyEMQW;n*+*wdv|Y^NTAHIhsLu- z%*4!+5|eWk*$yg*<@xY9C8a-|mx^%`17W3cIOiEOa=%|qGns%?DY1^WRNZ02% zN?sl=EN74ZwmCf^wJcrYleM+Dc=qnuZ<{NrrCRb*J>+?{BQia$o-XEB^c5eYszMUL8F6 zxwQ7ozP|;}zwR~PJ@2i~dpW*0`>l5zEBspXE%#sT;=6I%ze&e@owrNuTJ_pL&$d?G zx$)%Xy~)SQPlOrVm7bXqX7~T=KKqM#%Xdvq{#qRO_+7-_jB5swRZlA}R%`#-TL1Tr z-QT7E?``}4&i;jc@Uv4#i&p==T)3`h%Idet3-?Ra%}L*3d;Xri#rd5@yJECHzr9%e zcIUIxIXi>$rY$VGaqCz5tG&NgSC{Ji-TZRC@Bf@%>6h(i|GW9+{IYs8Itb;aB)zJb z`#&uGe{95_nzG7Y@yl)UU!|;Xe7ZJx+u3aw_rE`T?9cU>(todu{`dWBl&k;q<;I`; zInVw-Gx~ph-`8h9u2mnnf9C(%<1_#7E}ijz_F16hzyH(e?-8(##{bP{|6ITG)t~FV X``B0`TZG6u literal 0 HcmV?d00001 diff --git a/ViewModels/LumiaFlashRomSourceSelectionViewModel.cs b/ViewModels/LumiaFlashRomSourceSelectionViewModel.cs index 5a9d00a..a4386cb 100644 --- a/ViewModels/LumiaFlashRomSourceSelectionViewModel.cs +++ b/ViewModels/LumiaFlashRomSourceSelectionViewModel.cs @@ -28,13 +28,14 @@ namespace WPinternals private PhoneNotifierViewModel PhoneNotifier; private Action FlashPartitionsCallback; private Action FlashFFUCallback; + private Action FlashMMOSCallback; private Action FlashArchiveCallback; internal Action SwitchToUnlockBoot; internal Action SwitchToUnlockRoot; internal Action SwitchToDumpFFU; internal Action SwitchToBackup; - internal LumiaFlashRomSourceSelectionViewModel(PhoneNotifierViewModel PhoneNotifier, Action SwitchToUnlockBoot, Action SwitchToUnlockRoot, Action SwitchToDumpFFU, Action SwitchToBackup, Action FlashPartitionsCallback, Action FlashArchiveCallback, Action FlashFFUCallback) + internal LumiaFlashRomSourceSelectionViewModel(PhoneNotifierViewModel PhoneNotifier, Action SwitchToUnlockBoot, Action SwitchToUnlockRoot, Action SwitchToDumpFFU, Action SwitchToBackup, Action FlashPartitionsCallback, Action FlashArchiveCallback, Action FlashFFUCallback, Action FlashMMOSCallback) : base() { this.PhoneNotifier = PhoneNotifier; @@ -45,6 +46,7 @@ namespace WPinternals this.FlashPartitionsCallback = FlashPartitionsCallback; this.FlashArchiveCallback = FlashArchiveCallback; this.FlashFFUCallback = FlashFFUCallback; + this.FlashMMOSCallback = FlashMMOSCallback; this.PhoneNotifier.NewDeviceArrived += NewDeviceArrived; this.PhoneNotifier.DeviceRemoved += DeviceRemoved; @@ -137,6 +139,23 @@ namespace WPinternals } } + private string _MMOSPath; + public string MMOSPath + { + get + { + return _MMOSPath; + } + set + { + if (value != _MMOSPath) + { + _MMOSPath = value; + OnPropertyChanged("MMOSPath"); + } + } + } + private bool _IsPhoneDisconnected; public bool IsPhoneDisconnected { @@ -214,6 +233,19 @@ namespace WPinternals } } + private DelegateCommand _FlashMMOSCommand; + public DelegateCommand FlashMMOSCommand + { + get + { + if (_FlashMMOSCommand == null) + { + _FlashMMOSCommand = new DelegateCommand(() => { FlashMMOSCallback(MMOSPath); }, () => ((MMOSPath != null) && (PhoneNotifier.CurrentInterface != null))); + } + return _FlashMMOSCommand; + } + } + private DelegateCommand _FlashArchiveCommand; public DelegateCommand FlashArchiveCommand { @@ -257,6 +289,7 @@ namespace WPinternals FlashPartitionsCommand.RaiseCanExecuteChanged(); FlashArchiveCommand.RaiseCanExecuteChanged(); FlashFFUCommand.RaiseCanExecuteChanged(); + FlashMMOSCommand.RaiseCanExecuteChanged(); } } } diff --git a/ViewModels/LumiaFlashRomViewModel.cs b/ViewModels/LumiaFlashRomViewModel.cs index 8e28c5f..faf69c7 100644 --- a/ViewModels/LumiaFlashRomViewModel.cs +++ b/ViewModels/LumiaFlashRomViewModel.cs @@ -57,7 +57,7 @@ namespace WPinternals return; if (SubContextViewModel == null) - ActivateSubContext(new LumiaFlashRomSourceSelectionViewModel(PhoneNotifier, SwitchToUnlockBoot, SwitchToUnlockRoot, SwitchToDumpFFU, SwitchToBackup, FlashPartitions, FlashArchive, FlashFFU)); + ActivateSubContext(new LumiaFlashRomSourceSelectionViewModel(PhoneNotifier, SwitchToUnlockBoot, SwitchToUnlockRoot, SwitchToDumpFFU, SwitchToBackup, FlashPartitions, FlashArchive, FlashFFU, FlashMMOS)); } // Called from an event-handler. So, "async void" is valid here. @@ -505,7 +505,7 @@ namespace WPinternals if (Info.FlashAppProtocolVersionMajor >= 2) { - byte[] GPTChunk = LumiaV2UnlockBootViewModel.GetGptChunk(Phone, 0x20000); // TODO: Get proper profile FFU and get ChunkSizeInBytes + byte[] GPTChunk = LumiaUnlockBootloaderViewModel.GetGptChunk(Phone, 0x20000); // TODO: Get proper profile FFU and get ChunkSizeInBytes GPT GPT = new GPT(GPTChunk); FlashPart Part; List FlashParts = new List(); @@ -606,6 +606,84 @@ namespace WPinternals }).Start(); } + // Called from an event-handler. So, "async void" is valid here. + internal async void FlashMMOS(string MMOSPath) + { + IsSwitchingInterface = true; // Prevents that a device is forced to Flash mode on this screen which is meant for flashing + try + { + await SwitchModeViewModel.SwitchToWithProgress(PhoneNotifier, PhoneInterfaces.Lumia_Flash, + (msg, sub) => + ActivateSubContext(new BusyViewModel(msg, sub))); + FlashMMOSTask(MMOSPath); + } + catch (Exception Ex) + { + ActivateSubContext(new MessageViewModel(Ex.Message, Callback)); + } + } + + internal void FlashMMOSTask(string MMOSPath) + { + NokiaFlashModel Phone = (NokiaFlashModel)PhoneNotifier.CurrentModel; + if (PhoneNotifier.CurrentInterface == PhoneInterfaces.Lumia_Bootloader) + Phone.SwitchToFlashAppContext(); + + new Thread(() => + { + bool Result = true; + + ActivateSubContext(new BusyViewModel("Initializing flash...")); + + string ErrorSubMessage = null; + + try + { + FileInfo info = new FileInfo(MMOSPath); + uint length = uint.Parse(info.Length.ToString()); + int maximumbuffersize = 0x00240000; + uint totalcounts = (uint)Math.Truncate((decimal)length / maximumbuffersize); + BusyViewModel Busy = new BusyViewModel("Flashing Test Mode package...", MaxProgressValue: totalcounts, UIContext: UIContext); + ActivateSubContext(Busy); + + Phone.FlashMMOS(MMOSPath, Busy.ProgressUpdater); + + ActivateSubContext(new BusyViewModel("And now booting phone to MMOS...", "If the phone stays on the lightning cog screen for a while, you may need to unplug and replug the phone to continue the boot process.")); + + PhoneNotifier.NewDeviceArrived += NewDeviceArrived; + } + catch (Exception Ex) + { + LogFile.LogException(Ex); + if (Ex is WPinternalsException) + ErrorSubMessage = ((WPinternalsException)Ex).SubMessage; + Result = false; + } + + if (!Result) + { + ExitFailure("Flash failed!", ErrorSubMessage); + return; + } + }).Start(); + } + + private void NewDeviceArrived(ArrivalEventArgs Args) + { + PhoneNotifier.NewDeviceArrived -= NewDeviceArrived; + + if (Args.NewInterface != PhoneInterfaces.Lumia_Label) + { + ExitFailure("Flash failed!", "Phone unexpectedly switched mode while booting MMOS image."); + return; + } + else + { + ExitSuccess("Flash successful!", null); + return; + } + } + // Called from an event-handler. So, "async void" is valid here. internal async void Exit() { diff --git a/ViewModels/LumiaModeViewModel.cs b/ViewModels/LumiaModeViewModel.cs index f7ccefc..4473ef9 100644 --- a/ViewModels/LumiaModeViewModel.cs +++ b/ViewModels/LumiaModeViewModel.cs @@ -90,7 +90,7 @@ namespace WPinternals ActivateSubContext(new NokiaModeLabelViewModel((NokiaPhoneModel)CurrentModel, OnModeSwitchRequested)); break; case PhoneInterfaces.Lumia_MassStorage: - ActivateSubContext(new NokiaModeMassStorageViewModel(null)); + ActivateSubContext(new NokiaModeMassStorageViewModel((MassStorage)CurrentModel, OnModeSwitchRequested)); break; }; } diff --git a/ViewModels/LumiaUnlockBootViewModel.cs b/ViewModels/LumiaUnlockBootViewModel.cs index b43c903..c97d756 100644 --- a/ViewModels/LumiaUnlockBootViewModel.cs +++ b/ViewModels/LumiaUnlockBootViewModel.cs @@ -22,9 +22,7 @@ using Microsoft.Win32; using System; using System.Collections; using System.Collections.Generic; -using System.IO; using System.Linq; -using System.Threading; using System.Threading.Tasks; namespace WPinternals @@ -37,15 +35,13 @@ namespace WPinternals internal enum MachineState { Default, - LumiaSpecAGetGPT, - LumiaSpecBUnlockBoot + LumiaUnlockBoot }; internal class LumiaUnlockBootViewModel : ContextViewModel { private PhoneNotifierViewModel PhoneNotifier; private Action Callback; - private bool IsFlashingDone = false; private string FFUPath; private string LoadersPath; private string SBL3Path; @@ -54,14 +50,6 @@ namespace WPinternals private string SupportedFFUPath; private bool IsBootLoaderUnlocked; private byte[] RootKeyHash = null; - private List PossibleLoaders; - private GPT NewGPT; - private byte[] GPT; - private byte[] ExtraSector; - private byte[] SBL2; - private byte[] SBL3; - private byte[] UEFI; - private FFU FFU; private Action SwitchToFlashRom; private Action SwitchToUndoRoot; private Action SwitchToDownload; @@ -94,58 +82,13 @@ namespace WPinternals PhoneNotifier.NewDeviceArrived -= NewDeviceArrived; } - internal void SendLoader() - { - // Assume 9008 mode - if (!((PhoneNotifier.CurrentModel is QualcommSerial) && (PossibleLoaders != null) && (PossibleLoaders.Count > 0))) - return; - - ActivateSubContext(new BusyViewModel("Sending loader...")); - LogFile.Log("Sending loader"); - - QualcommSerial Serial = (QualcommSerial)PhoneNotifier.CurrentModel; - QualcommDownload Download = new QualcommDownload(Serial); - if (Download.IsAlive()) - { - int Attempt = 1; - bool Result = false; - foreach (QualcommPartition Loader in PossibleLoaders) - { - LogFile.Log("Attempt " + Attempt.ToString()); - - try - { - Download.SendToPhoneMemory(0x2A000000, Loader.Binary); - Download.StartBootloader(0x2A000000); - Result = true; - LogFile.Log("Loader sent successfully"); - } - catch { } - - if (Result) - break; - - Attempt++; - } - Serial.Close(); - - if (!Result) - LogFile.Log("Loader failed"); - } - else - { - LogFile.Log("Failed to communicate to Qualcomm Emergency Download mode"); - throw new BadConnectionException(); - } - } - // Potentially blocking UI. Threadsafe. internal override void EvaluateViewState() { if (!IsActive) return; - if ((State == MachineState.LumiaSpecAGetGPT) || (State == MachineState.LumiaSpecBUnlockBoot)) + if (State == MachineState.LumiaUnlockBoot) return; lock (EvaluateViewStateLockObject) @@ -155,33 +98,17 @@ namespace WPinternals case PhoneInterfaces.Lumia_Normal: case PhoneInterfaces.Lumia_Label: IsSwitchingInterface = false; - if (IsFlashingDone) + if (DoUnlock) { - if (DoUnlock) - { - LogFile.Log("Bootloader successfully unlocked!"); - ActivateSubContext(new MessageViewModel("Bootloader succesfully unlocked!", Exit)); - } - else - { - LogFile.Log("Bootloader successfully restored!"); - ActivateSubContext(new MessageViewModel("Bootloader succesfully restored!", Exit)); - } + // Display View to switch to Flash mode + LogFile.Log("Start unlock. Phone needs to switch to Flash-mode"); + ActivateSubContext(new MessageViewModel("In order to start unlocking the bootloader, the phone needs to be switched to Flash-mode.", SwitchToFlashMode, Exit)); } else { - if (DoUnlock) - { - // Display View to switch to Flash mode - LogFile.Log("Start unlock. Phone needs to switch to Flash-mode"); - ActivateSubContext(new MessageViewModel("In order to start unlocking the bootloader, the phone needs to be switched to Flash-mode.", SwitchToFlashMode, Exit)); - } - else - { - // Display View to switch to Flash mode - LogFile.Log("Start boot restore. Phone needs to switch to Flash-mode"); - ActivateSubContext(new MessageViewModel("In order to start restoring the bootloader, the phone needs to be switched to Flash-mode.", SwitchToFlashMode, Exit)); - } + // Display View to switch to Flash mode + LogFile.Log("Start boot restore. Phone needs to switch to Flash-mode"); + ActivateSubContext(new MessageViewModel("In order to start restoring the bootloader, the phone needs to be switched to Flash-mode.", SwitchToFlashMode, Exit)); } break; case PhoneInterfaces.Lumia_Flash: @@ -194,179 +121,157 @@ namespace WPinternals // So this is not always in a thread from the threadpool. // So we need to avoid UI blocking code here. - // Flash Param "FS" is the Flash Status (4 bytes, Big Endian DWORD, values: 0 / 1) - // When flashing is done and phone is still in flash-mode, write raw dummy sector and restart phone - NokiaFlashModel FlashModel = (NokiaFlashModel)PhoneNotifier.CurrentModel; - if (IsFlashingDone && (FlashModel.ReadParam("FS")[3] > 0)) - { - if (DoUnlock) - { - IsSwitchingInterface = true; - LogFile.Log("Phone detected in Flash-in-progress-mode. Escaping from Flash-mode.;"); - ActivateSubContext(new BusyViewModel("Escaping from Flash mode...")); + IsSwitchingInterface = false; - new Thread(() => + int TestPos = 0; + + try // In case phone reboots during the time that status is being read + { + // Some phones, like Lumia 928 verizon, do not support the Terminal interface! + // To read the RootKeyHash we use ReadParam("RRKH"), instead of GetTerminalResponse().RootKeyHash. + RootKeyHash = ((NokiaFlashModel)PhoneNotifier.CurrentModel).ReadParam("RRKH"); + + TestPos = 1; + + UefiSecurityStatusResponse SecurityStatus = ((NokiaFlashModel)PhoneNotifier.CurrentModel).ReadSecurityStatus(); + IsBootLoaderUnlocked = (SecurityStatus.AuthenticationStatus || SecurityStatus.RdcStatus || !SecurityStatus.SecureFfuEfuseStatus); + + TestPos = 2; + + PhoneInfo Info = ((NokiaFlashModel)PhoneNotifier.CurrentModel).ReadPhoneInfo(); + + TestPos = 3; + + if (Info.FlashAppProtocolVersionMajor < 2) + { + // This action is executed after the resources are selected by the user. + Action ReturnFunction = (FFUPath, LoadersPath, SBL3Path, ProfileFFUPath, EDEPath, SupportedFFUPath, DoFixBoot) => + { + // Stop responding to device arrival here, because all connections are handled by subfunctions, not here. + IsSwitchingInterface = true; + State = MachineState.LumiaUnlockBoot; + + // This is a callback on the UI thread + // Resources are confirmed by user + this.FFUPath = FFUPath; + this.LoadersPath = LoadersPath; + this.SBL3Path = SBL3Path; + this.SupportedFFUPath = SupportedFFUPath; + StorePaths(); + + LogFile.Log("Processing resources:"); + LogFile.Log("FFU: " + FFUPath); + LogFile.Log("Loaders: " + LoadersPath); + if (SBL3Path == null) + LogFile.Log("No SBL3 specified"); + else + LogFile.Log("SBL3: " + SBL3Path); + + ActivateSubContext(new BusyViewModel("Processing resources...")); + + if (DoUnlock) { - RecoverFromFlashMode(); - }).Start(); + Task.Run(async () => + { + await LumiaUnlockBootloaderViewModel.LumiaV1UnlockFirmware(PhoneNotifier, FFUPath, LoadersPath, SBL3Path, SupportedFFUPath, SetWorkingStatus, UpdateWorkingStatus, ExitMessage, ExitMessage); + }); + } + else + { + Task.Run(async () => + { + await LumiaUnlockBootloaderViewModel.LumiaV1RelockFirmware(PhoneNotifier, FFUPath, LoadersPath, SetWorkingStatus, UpdateWorkingStatus, ExitMessage, ExitMessage); + }); + } + }; + + if (DoUnlock) + ActivateSubContext(new BootUnlockResourcesViewModel("Lumia Flash mode", RootKeyHash, SwitchToFlashRom, SwitchToUndoRoot, SwitchToDownload, ReturnFunction, Abort, IsBootLoaderUnlocked, false)); + else + ActivateSubContext(new BootRestoreResourcesViewModel("Lumia Flash mode", RootKeyHash, SwitchToFlashRom, SwitchToUndoRoot, SwitchToDownload, ReturnFunction, Abort, IsBootLoaderUnlocked, false)); } else { - IsSwitchingInterface = false; - ActivateSubContext(new MessageViewModel("Bootloader restored. You can now flash a stock ROM.", SwitchToFlashRom)); - } - } - else - { - IsSwitchingInterface = false; - - int TestPos = 0; - - try // In case phone reboots during the time that status is being read - { - // Some phones, like Lumia 928 verizon, do not support the Terminal interface! - // To read the RootKeyHash we use ReadParam("RRKH"), instead of GetTerminalResponse().RootKeyHash. - RootKeyHash = ((NokiaFlashModel)PhoneNotifier.CurrentModel).ReadParam("RRKH"); - - TestPos = 1; - - UefiSecurityStatusResponse SecurityStatus = ((NokiaFlashModel)PhoneNotifier.CurrentModel).ReadSecurityStatus(); - IsBootLoaderUnlocked = (SecurityStatus.AuthenticationStatus || SecurityStatus.RdcStatus || !SecurityStatus.SecureFfuEfuseStatus); - - TestPos = 2; - - PhoneInfo Info = ((NokiaFlashModel)PhoneNotifier.CurrentModel).ReadPhoneInfo(); - - TestPos = 3; - - if (Info.FlashAppProtocolVersionMajor < 2) + if (DoUnlock) { - // This action is executed after the resources are selected by the user. - Action ReturnFunction = (FFUPath, LoadersPath, SBL3Path, ProfileFFUPath, EDEPath, SupportedFFUPath, DoFixBoot) => + NokiaFlashModel FlashModel = (NokiaFlashModel)PhoneNotifier.CurrentModel; + GPT GPT = FlashModel.ReadGPT(); + if ((GPT.GetPartition("IS_UNLOCKED") != null) || (GPT.GetPartition("BACKUP_EFIESP") != null)) + { + ExitMessage("Phone is already unlocked", null); + return; + } + } + + TestPos = 4; + + // Stop responding to device arrival here, because all connections are handled by subfunctions, not here. + IsSwitchingInterface = true; + + // This action is executed after the resources are selected by the user. + Action ReturnFunction = (FFUPath, LoadersPath, SBL3Path, ProfileFFUPath, EDEPath, SupportedFFUPath, DoFixBoot) => + { + IsSwitchingInterface = true; + State = MachineState.LumiaUnlockBoot; + if (DoUnlock) { // This is a callback on the UI thread // Resources are confirmed by user - this.FFUPath = FFUPath; - this.LoadersPath = LoadersPath; - this.SBL3Path = SBL3Path; - StorePaths(); + this.ProfileFFUPath = ProfileFFUPath; + this.EDEPath = EDEPath; + this.SupportedFFUPath = SupportedFFUPath; + StorePaths(); - LogFile.Log("Processing resources:"); - LogFile.Log("FFU: " + FFUPath); - LogFile.Log("Loaders: " + LoadersPath); - if (SBL3Path == null) - LogFile.Log("No SBL3 specified"); - else - LogFile.Log("SBL3: " + SBL3Path); - - ActivateSubContext(new BusyViewModel("Processing resources...")); - - new Thread(() => - { - bool ResourcesVerified = false; - try - { - ResourcesVerified = EvaluateResources(); - if (!ResourcesVerified) - { - LogFile.Log("Processing resources failed."); - ActivateSubContext(new MessageViewModel("Invalid resources.", Abort)); - } - } - catch (Exception Ex) - { - LogFile.LogException(Ex); - ActivateSubContext(new MessageViewModel(Ex.Message, Abort)); - } - - if (ResourcesVerified) - { - if (IsBootLoaderUnlocked) - { - CustomFlashBootLoader(); - } - else - PerformSoftBrick(); - } - }).Start(); - }; - - if (DoUnlock) - ActivateSubContext(new BootUnlockResourcesViewModel("Lumia Flash mode", RootKeyHash, SwitchToFlashRom, SwitchToUndoRoot, SwitchToDownload, ReturnFunction, Abort, IsBootLoaderUnlocked, false)); - else - ActivateSubContext(new BootRestoreResourcesViewModel("Lumia Flash mode", RootKeyHash, SwitchToFlashRom, SwitchToUndoRoot, SwitchToDownload, ReturnFunction, Abort, IsBootLoaderUnlocked, false)); - } - else - { - if (DoUnlock) - { - GPT GPT = FlashModel.ReadGPT(); - if ((GPT.GetPartition("IS_UNLOCKED") != null) || (GPT.GetPartition("BACKUP_EFIESP") != null)) - { - ExitMessage("Phone is already unlocked", null); - return; - } - } - - TestPos = 4; - - // Stop responding to device arrival here, because all connections are handled by subfunctions, not here. - IsSwitchingInterface = true; - - // This action is executed after the resources are selected by the user. - Action ReturnFunction = (FFUPath, LoadersPath, SBL3Path, ProfileFFUPath, EDEPath, SupportedFFUPath, DoFixBoot) => - { - State = MachineState.LumiaSpecBUnlockBoot; - if (DoUnlock) - { - // This is a callback on the UI thread - // Resources are confirmed by user - this.ProfileFFUPath = ProfileFFUPath; - this.EDEPath = EDEPath; - this.SupportedFFUPath = SupportedFFUPath; - StorePaths(); - - if (DoFixBoot) - LogFile.Log("Fix Boot"); - else - LogFile.Log("Unlock Bootloader"); - - LogFile.Log("Processing resources:"); - LogFile.Log("Profile FFU: " + ProfileFFUPath); - LogFile.Log("EDE file: " + EDEPath); - if (SupportedFFUPath != null) - LogFile.Log("Donor-FFU with supported OS version: " + SupportedFFUPath); - - Task.Run(async () => - { - if (DoFixBoot) - await LumiaV2UnlockBootViewModel.LumiaV2FixBoot(PhoneNotifier, SetWorkingStatus, UpdateWorkingStatus, ExitMessage, ExitMessage); - else - await LumiaV2UnlockBootViewModel.LumiaV2UnlockBootloader(PhoneNotifier, ProfileFFUPath, EDEPath, SupportedFFUPath, SetWorkingStatus, UpdateWorkingStatus, ExitMessage, ExitMessage); - }); - } + if (DoFixBoot) + LogFile.Log("Fix Boot"); else - { - Task.Run(async () => + LogFile.Log("Unlock Bootloader"); + + LogFile.Log("Processing resources:"); + LogFile.Log("Profile FFU: " + ProfileFFUPath); + LogFile.Log("EDE file: " + EDEPath); + if (SupportedFFUPath != null) + LogFile.Log("Donor-FFU with supported OS version: " + SupportedFFUPath); + + Task.Run(async () => { - await LumiaV2UnlockBootViewModel.LumiaV2RelockPhone(PhoneNotifier, null, true, SetWorkingStatus, UpdateWorkingStatus, ExitMessage, ExitMessage); + if (DoFixBoot) + await LumiaV2UnlockBootViewModel.LumiaV2FixBoot(PhoneNotifier, SetWorkingStatus, UpdateWorkingStatus, ExitMessage, ExitMessage); + else + await LumiaUnlockBootloaderViewModel.LumiaV2UnlockUEFI(PhoneNotifier, ProfileFFUPath, EDEPath, SupportedFFUPath, SetWorkingStatus, UpdateWorkingStatus, ExitMessage, ExitMessage); }); - } - }; - - TestPos = 5; - - if (DoUnlock) - ActivateSubContext(new BootUnlockResourcesViewModel("Lumia Flash mode", RootKeyHash, SwitchToFlashRom, SwitchToUndoRoot, SwitchToDownload, ReturnFunction, Abort, IsBootLoaderUnlocked, true, Info.PlatformID, Info.Type)); + } else - ActivateSubContext(new BootRestoreResourcesViewModel("Lumia Flash mode", RootKeyHash, SwitchToFlashRom, SwitchToUndoRoot, SwitchToDownload, ReturnFunction, Abort, IsBootLoaderUnlocked, true, Info.PlatformID, Info.Type)); - } - } - catch (Exception Ex) - { - LogFile.LogException(Ex, LogType.FileAndConsole, TestPos.ToString()); + { + Task.Run(async () => + { + FFU ProfileFFU = null; + + List FFUs = App.Config.FFURepository.Where(e => (Info.PlatformID.StartsWith(e.PlatformID, StringComparison.OrdinalIgnoreCase) && e.Exists())).ToList(); + if (FFUs.Count() > 0) + ProfileFFU = new FFU(FFUs[0].Path); + else + throw new WPinternalsException("Profile FFU missing"); + + LogFile.Log("Profile FFU: " + ProfileFFU.Path); + + await LumiaUnlockBootloaderViewModel.LumiaV2RelockUEFI(PhoneNotifier, ProfileFFU.Path, true, SetWorkingStatus, UpdateWorkingStatus, ExitMessage, ExitMessage); + }); + } + }; + + TestPos = 5; + + if (DoUnlock) + ActivateSubContext(new BootUnlockResourcesViewModel("Lumia Flash mode", RootKeyHash, SwitchToFlashRom, SwitchToUndoRoot, SwitchToDownload, ReturnFunction, Abort, IsBootLoaderUnlocked, true, Info.PlatformID, Info.Type)); + else + ActivateSubContext(new BootRestoreResourcesViewModel("Lumia Flash mode", RootKeyHash, SwitchToFlashRom, SwitchToUndoRoot, SwitchToDownload, ReturnFunction, Abort, IsBootLoaderUnlocked, true, Info.PlatformID, Info.Type)); } } + catch (Exception Ex) + { + LogFile.LogException(Ex, LogType.FileAndConsole, TestPos.ToString()); + } break; case PhoneInterfaces.Qualcomm_Download: IsSwitchingInterface = false; @@ -383,76 +288,84 @@ namespace WPinternals return; } - if ((this.FFUPath == null) || (this.PossibleLoaders == null) || (this.PossibleLoaders.Count == 0)) + // This action is executed after the user selected the resources. + Action ReturnFunctionD = (FFUPath, LoadersPath, SBL3Path, ProfileFFUPath, EDEPath, SupportedFFUPath, DoFixBoot) => { - // This action is executed after the user selected the resources. - Action ReturnFunction = (FFUPath, LoadersPath, SBL3Path, ProfileFFUPath, EDEPath, SupportedFFUPath, DoFixBoot) => - { - // This is a callback on the UI thread - // Resources are confirmed by user - this.FFUPath = FFUPath; - this.LoadersPath = LoadersPath; - this.SBL3Path = SBL3Path; - StorePaths(); + IsSwitchingInterface = true; + State = MachineState.LumiaUnlockBoot; + // This is a callback on the UI thread + // Resources are confirmed by user + this.FFUPath = FFUPath; + this.LoadersPath = LoadersPath; + this.SBL3Path = SBL3Path; + StorePaths(); - LogFile.Log("Processing resources:"); - LogFile.Log("FFU: " + FFUPath); - LogFile.Log("Loaders: " + LoadersPath); - if (SBL3Path == null) - LogFile.Log("No SBL3 specified"); - else - LogFile.Log("SBL3: " + SBL3Path); + LogFile.Log("Processing resources:"); + LogFile.Log("FFU: " + FFUPath); + LogFile.Log("Loaders: " + LoadersPath); + if (SBL3Path == null) + LogFile.Log("No SBL3 specified"); + else + LogFile.Log("SBL3: " + SBL3Path); - ActivateSubContext(new BusyViewModel("Processing resources...")); - - new Thread(() => - { - bool ResourcesVerified = false; - try - { - ResourcesVerified = EvaluateResources(); - if (!ResourcesVerified) - { - LogFile.Log("Processing resources failed."); - ActivateSubContext(new MessageViewModel("Invalid resources.", Abort)); - } - } - catch (Exception Ex) - { - LogFile.LogException(Ex); - ActivateSubContext(new MessageViewModel(Ex.Message, Abort)); - } - - if (ResourcesVerified) - SendLoader(); - }).Start(); - }; + ActivateSubContext(new BusyViewModel("Processing resources...")); if (DoUnlock) - ActivateSubContext(new BootUnlockResourcesViewModel("Qualcomm Emergency Download mode", RootKeyHash, SwitchToFlashRom, SwitchToUndoRoot, SwitchToDownload, ReturnFunction, Abort, IsBootLoaderUnlocked, false)); - else - ActivateSubContext(new BootRestoreResourcesViewModel("Qualcomm Emergency Download mode", RootKeyHash, SwitchToFlashRom, SwitchToUndoRoot, SwitchToDownload, ReturnFunction, Abort, IsBootLoaderUnlocked, false)); - } - else - new Thread(() => { - SendLoader(); - }).Start(); + Task.Run(async () => + { + await LumiaUnlockBootloaderViewModel.LumiaV1UnlockFirmware(PhoneNotifier, FFUPath, LoadersPath, SBL3Path, SupportedFFUPath, SetWorkingStatus, UpdateWorkingStatus, ExitMessage, ExitMessage); + }); + } + else + { + Task.Run(async () => + { + await LumiaUnlockBootloaderViewModel.LumiaV1RelockFirmware(PhoneNotifier, FFUPath, LoadersPath, SetWorkingStatus, UpdateWorkingStatus, ExitMessage, ExitMessage); + }); + } + }; + + if (DoUnlock) + ActivateSubContext(new BootUnlockResourcesViewModel("Qualcomm Emergency Download mode", RootKeyHash, SwitchToFlashRom, SwitchToUndoRoot, SwitchToDownload, ReturnFunctionD, Abort, IsBootLoaderUnlocked, false)); + else + ActivateSubContext(new BootRestoreResourcesViewModel("Qualcomm Emergency Download mode", RootKeyHash, SwitchToFlashRom, SwitchToUndoRoot, SwitchToDownload, ReturnFunctionD, Abort, IsBootLoaderUnlocked, false)); + break; case PhoneInterfaces.Qualcomm_Flash: - new Thread(() => { - FlashBootLoader(); - }).Start(); - break; - case PhoneInterfaces.Lumia_Bootloader: - IsSwitchingInterface = true; - if (IsFlashingDone) - { - LogFile.Log("Booting phone"); - ActivateSubContext(new BusyViewModel("Booting phone...")); + IsSwitchingInterface = true; + State = MachineState.LumiaUnlockBoot; + ActivateSubContext(new BusyViewModel("Recovering resources...")); + + LogFile.Log("Phone was unexpectedly detected in this mode while resources were not loaded yet."); + LogFile.Log("WPInternals tool probably crashed in previous session."); + LogFile.Log("Trying to recover resources from the registry."); + + FFUPath = (string)Registry.GetValue(@"HKEY_CURRENT_USER\Software\WPInternals", "FFUPath", null); + SupportedFFUPath = (string)Registry.GetValue(@"HKEY_CURRENT_USER\Software\WPInternals", "SupportedFFUPath", null); + LoadersPath = (string)Registry.GetValue(@"HKEY_CURRENT_USER\Software\WPInternals", "LoadersPath", null); + if (DoUnlock) + SBL3Path = (string)Registry.GetValue(@"HKEY_CURRENT_USER\Software\WPInternals", "SBL3Path", null); + else + SBL3Path = null; + + if (DoUnlock) + { + Task.Run(async () => + { + await LumiaUnlockBootloaderViewModel.LumiaV1UnlockFirmware(PhoneNotifier, FFUPath, LoadersPath, SBL3Path, SupportedFFUPath, SetWorkingStatus, UpdateWorkingStatus, ExitMessage, ExitMessage); + }); + } + else + { + Task.Run(async () => + { + await LumiaUnlockBootloaderViewModel.LumiaV1RelockFirmware(PhoneNotifier, FFUPath, LoadersPath, SetWorkingStatus, UpdateWorkingStatus, ExitMessage, ExitMessage); + }); + } + break; } - break; default: // Show View "Waiting for connection" IsSwitchingInterface = false; @@ -508,7 +421,7 @@ namespace WPinternals } else Key.SetValue("FFUPath", FFUPath); - + if (LoadersPath == null) { if (Key.GetValue("LoadersPath") != null) @@ -528,9 +441,6 @@ namespace WPinternals Key.SetValue("SBL3Path", SBL3Path); } - NokiaFlashModel Model = (NokiaFlashModel)PhoneNotifier.CurrentModel; - PhoneInfo Info = Model.ReadPhoneInfo(); - if (ProfileFFUPath == null) { if (Key.GetValue("ProfileFFUPath") != null) @@ -543,16 +453,22 @@ namespace WPinternals App.Config.AddFfuToRepository(ProfileFFUPath); } - if (EDEPath == null) + if (PhoneNotifier.CurrentInterface != PhoneInterfaces.Qualcomm_Download && PhoneNotifier.CurrentInterface != PhoneInterfaces.Qualcomm_Flash) { - if (Key.GetValue("EDEPath") != null) - Key.DeleteValue("EDEPath"); - } - else - { - Key.SetValue("EDEPath", EDEPath); + NokiaFlashModel Model = (NokiaFlashModel)PhoneNotifier.CurrentModel; + PhoneInfo Info = Model.ReadPhoneInfo(); - App.Config.AddEmergencyToRepository(Info.Type, EDEPath, null); + if (EDEPath == null) + { + if (Key.GetValue("EDEPath") != null) + Key.DeleteValue("EDEPath"); + } + else + { + Key.SetValue("EDEPath", EDEPath); + + App.Config.AddEmergencyToRepository(Info.Type, EDEPath, null); + } } if (SupportedFFUPath != null) @@ -568,11 +484,9 @@ namespace WPinternals private void Exit() { IsSwitchingInterface = false; // From here on a device will be forced to Flash mode again on this screen which is meant for flashing - IsFlashingDone = false; FFUPath = null; LoadersPath = null; - PossibleLoaders = null; SBL3Path = null; Callback(); @@ -582,595 +496,20 @@ namespace WPinternals internal void ExitMessage(string Message, string SubMessage) { // SecureBoot Unlock v2 is done. Reactivate phone arrival events. - MessageViewModel SuccessMessageViewModel = new MessageViewModel(Message, () => { + MessageViewModel SuccessMessageViewModel = new MessageViewModel(Message, () => + { State = MachineState.Default; Exit(); }); SuccessMessageViewModel.SubMessage = SubMessage; ActivateSubContext(SuccessMessageViewModel); } - - // Potentially blocking UI. Threadsafe. - private bool EvaluateResources(bool Emergency = false) - { - bool Result = true; - - bool DumpPartitions = false; - string DumpFilePrefix = Environment.ExpandEnvironmentVariables("%ALLUSERSPROFILE%\\WPInternals\\") + DateTime.Now.ToString("yyyy-MM-dd hh.mm.ss") + " - "; - - if ((FFUPath == null) || (FFUPath.Length == 0)) - throw new Exception("Error: Path for FFU-file is mandatory."); - - if (DoUnlock && ((LoadersPath == null) || (LoadersPath.Length == 0))) - throw new Exception("Error: Path for Loaders is mandatory."); - - if (PhoneNotifier.CurrentModel is NokiaFlashModel) - { - FlashVersion FlashVersion = ((NokiaFlashModel)PhoneNotifier.CurrentModel).GetFlashVersion(); - if (FlashVersion == null) - throw new Exception("Error: The version of the Flash Application on the phone could not be determined."); - if ((FlashVersion.ApplicationMajor < 1) || ((FlashVersion.ApplicationMajor == 1) && (FlashVersion.ApplicationMinor < 28))) - throw new Exception("Error: The version of the Flash Application on the phone is too old. Update your phone using Windows Updates or flash a newer ROM to your phone. Then try again."); - } - - try - { - FFU = new FFU(FFUPath); - } - catch (Exception Ex) - { - LogFile.LogException(Ex); - throw new Exception("Error: Parsing FFU-file failed."); - } - - if (DumpPartitions) - { - try - { - File.WriteAllBytes(DumpFilePrefix + "01.bin", FFU.GetSectors(0, 34)); // Original GPT - } - catch (Exception Ex) - { - LogFile.LogException(Ex); - throw new Exception("Error: Writing binary for logging failed."); - } - } - - State = MachineState.LumiaSpecAGetGPT; // Stop handling arrival notifications in this screen - IsSwitchingInterface = true; // Stop handling arrival notifications in MainViewModel - SwitchModeViewModel.SwitchTo(PhoneNotifier, PhoneInterfaces.Lumia_Bootloader).Wait(); - NewGPT = ((NokiaFlashModel)PhoneNotifier.CurrentModel).ReadGPT(); - SwitchModeViewModel.SwitchTo(PhoneNotifier, PhoneInterfaces.Lumia_Flash).Wait(); - IsSwitchingInterface = true; - State = MachineState.Default; - - // Make sure all partitions are in range of the emergency flasher. - NewGPT.RestoreBackupPartitions(); - - // Magic! - // SecureBoot hack for Bootloader Spec A - if (DoUnlock) - { - try - { - this.GPT = NewGPT.InsertHack(); - } - catch (Exception Ex) - { - LogFile.LogException(Ex); - throw new Exception("Error: Parsing partitions failed."); - } - - if (DumpPartitions) - { - try - { - File.WriteAllBytes(DumpFilePrefix + "02.bin", this.GPT); // Patched GPT - } - catch (Exception Ex) - { - LogFile.LogException(Ex); - throw new Exception("Error: Writing binary for logging failed."); - } - } - } - else - { - NewGPT.RemoveHack(); - this.GPT = NewGPT.Rebuild(); - } - - SBL1 SBL1 = null; - try - { - SBL1 = new SBL1(FFU.GetPartition("SBL1")); - } - catch (Exception Ex) - { - LogFile.LogException(Ex); - throw new Exception("Error: Parsing SBL1 failed."); - } - - if (DumpPartitions) - { - try - { - File.WriteAllBytes(DumpFilePrefix + "03.bin", SBL1.Binary); // Original SBL1 - } - catch (Exception Ex) - { - LogFile.LogException(Ex); - throw new Exception("Error: Writing binary for logging failed."); - } - } - - if (!Emergency) - { - if (RootKeyHash == null) - { - Result = false; - throw new Exception("Error: Root Key Hash could not be retrieved from the phone."); - } - if (SBL1.RootKeyHash == null) - { - Result = false; - throw new Exception("Error: Root Key Hash could not be retrieved from FFU file."); - } - if (!StructuralComparisons.StructuralEqualityComparer.Equals(RootKeyHash, SBL1.RootKeyHash)) - { - LogFile.Log("Phone: " + Converter.ConvertHexToString(RootKeyHash, "")); - LogFile.Log("SBL1: " + Converter.ConvertHexToString(SBL1.RootKeyHash, "")); - Result = false; - throw new Exception("Error: Root Key Hash from phone and from FFU file do not match!"); - } - } - - SBL2 SBL2 = null; - try - { - SBL2 = new SBL2(FFU.GetPartition("SBL2")); - } - catch (Exception Ex) - { - LogFile.LogException(Ex); - throw new Exception("Error: Parsing SBL2 failed."); - } - - if (DumpPartitions) - { - try - { - File.WriteAllBytes(DumpFilePrefix + "05.bin", SBL2.Binary); // Original SBL2 - } - catch (Exception Ex) - { - LogFile.LogException(Ex); - throw new Exception("Error: Writing binary for logging failed."); - } - } - - if (DoUnlock) - { - try - { - this.SBL2 = SBL2.Patch(); - } - catch (Exception Ex) - { - LogFile.LogException(Ex); - throw new Exception("Error: Patching SBL2 failed."); - } - - if (DumpPartitions) - { - try - { - File.WriteAllBytes(DumpFilePrefix + "06.bin", SBL2.Binary); // Patched SBL2 - } - catch (Exception Ex) - { - LogFile.LogException(Ex); - throw new Exception("Error: Writing binary for logging failed."); - } - } - } - else - { - this.SBL2 = SBL2.Binary; - } - - this.ExtraSector = null; - if (DoUnlock) - { - try - { - byte[] PartitionHeader = new byte[0x0C]; - Buffer.BlockCopy(SBL2.Binary, 0, PartitionHeader, 0, 0x0C); - this.ExtraSector = SBL1.GenerateExtraSector(PartitionHeader); - } - catch (Exception Ex) - { - LogFile.LogException(Ex); - throw new Exception("Error: Code generation failed."); - } - - if (DumpPartitions) - { - try - { - File.WriteAllBytes(DumpFilePrefix + "04.bin", this.ExtraSector); // Extra sector - } - catch (Exception Ex) - { - LogFile.LogException(Ex); - throw new Exception("Error: Writing binary for logging failed."); - } - } - } - - SBL3 SBL3; - SBL3 OriginalSBL3; - - try - { - OriginalSBL3 = new SBL3(FFU.GetPartition("SBL3")); - } - catch (Exception Ex) - { - LogFile.LogException(Ex); - throw new Exception("Error: Parsing SBL3 from FFU failed."); - } - - if (SBL3Path == null) - { - SBL3 = OriginalSBL3; - LogFile.Log("Taking SBL3 from FFU"); - } - else - { - SBL3 = null; - try - { - SBL3 = new SBL3(SBL3Path); - } - catch (Exception Ex) - { - LogFile.LogException(Ex); - throw new Exception("Error: Parsing external SBL3 failed."); - } - - if (SBL3.Binary.Length > OriginalSBL3.Binary.Length) - { - throw new Exception("Error: Selected SBL3 is too large."); - } - LogFile.Log("Taking selected SBL3"); - } - - if (DumpPartitions) - { - try - { - File.WriteAllBytes(DumpFilePrefix + "07.bin", SBL3.Binary); // Original SBL3 - } - catch (Exception Ex) - { - LogFile.LogException(Ex); - throw new Exception("Error: Writing binary for logging failed."); - } - } - - if (DoUnlock) - { - try - { - this.SBL3 = SBL3.Patch(); - } - catch (Exception Ex) - { - LogFile.LogException(Ex); - throw new Exception("Error: Patching SBL3 failed."); - } - - if (DumpPartitions) - { - try - { - File.WriteAllBytes(DumpFilePrefix + "08.bin", SBL3.Binary); // Patched SBL3 - } - catch (Exception Ex) - { - LogFile.LogException(Ex); - throw new Exception("Error: Writing binary for logging failed."); - } - } - } - else - { - this.SBL3 = SBL3.Binary; - } - - UEFI UEFI = null; - try - { - UEFI = new UEFI(FFU.GetPartition("UEFI")); - } - catch (Exception Ex) - { - LogFile.LogException(Ex); - throw new Exception("Error: Parsing UEFI failed."); - } - - if (DumpPartitions) - { - try - { - File.WriteAllBytes(DumpFilePrefix + "09.bin", UEFI.Binary); // Original UEFI - } - catch (Exception Ex) - { - LogFile.LogException(Ex); - throw new Exception("Error: Writing binary for logging failed."); - } - } - - if (DoUnlock) - { - try - { - this.UEFI = UEFI.Patch(); - } - catch (Exception Ex) - { - LogFile.LogException(Ex); - throw new Exception("Error: Patching UEFI failed."); - } - - if (DumpPartitions) - { - try - { - File.WriteAllBytes(DumpFilePrefix + "0A.bin", UEFI.Binary); // Patched UEFI - } - catch (Exception Ex) - { - LogFile.LogException(Ex); - throw new Exception("Error: Writing binary for logging failed."); - } - } - } - else - { - this.UEFI = UEFI.Binary; - } - - if (!IsBootLoaderUnlocked) - { - try - { - PossibleLoaders = QualcommLoaders.GetPossibleLoadersForRootKeyHash(LoadersPath, this.RootKeyHash); - if (PossibleLoaders.Count == 0) - { - Result = false; - throw new Exception("Error: No matching loaders found for RootKeyHash."); - } - } - catch (Exception Ex) - { - LogFile.LogException(Ex); - Result = false; - throw new Exception("Error: Unexpected error during scanning for loaders."); - } - } - - return Result; - } - - // TODO: Add logging - private void PerformSoftBrick() - { - IsSwitchingInterface = true; - ActivateSubContext(new BusyViewModel("Switching to Emergency Download mode...")); - - // Send FFU headers - UInt64 CombinedFFUHeaderSize = this.FFU.HeaderSize; - byte[] FfuHeader = new byte[CombinedFFUHeaderSize]; - System.IO.FileStream FfuFile = new System.IO.FileStream(FFUPath, System.IO.FileMode.Open, System.IO.FileAccess.Read); - FfuFile.Read(FfuHeader, 0, (int)CombinedFFUHeaderSize); - FfuFile.Close(); - try - { - ((NokiaFlashModel)PhoneNotifier.CurrentModel).SendFfuHeaderV1(FfuHeader); - } - catch (Exception Ex) - { - IsSwitchingInterface = false; - LogFile.LogException(Ex); - ActivateSubContext(new MessageViewModel("Error using FFU. Try an FFU image which matches the phone.", Abort)); - return; - } - - // Send 1 empty chunk (according to layout in FFU headers, it will be written to first and last chunk) - byte[] EmptyChunk = new byte[0x20000]; - Array.Clear(EmptyChunk, 0, 0x20000); - ((NokiaFlashModel)PhoneNotifier.CurrentModel).SendFfuPayloadV1(EmptyChunk); - - // Reboot to Qualcomm Emergency mode - byte[] RebootCommand = new byte[] { 0x4E, 0x4F, 0x4B, 0x52 }; // NOKR - ((NokiaFlashModel)PhoneNotifier.CurrentModel).ExecuteRawVoidMethod(RebootCommand); - } - + void NewDeviceArrived(ArrivalEventArgs Args) { // Do not start on a new thread, because EvaluateViewState will also create new ViewModels and those should be created on the UI thread. EvaluateViewState(); } - - private void RecoverFromFlashMode() - { - IsSwitchingInterface = true; - LogFile.Log("Recover from Flash-mode"); - - // Flash dummy sector (only allowed when phone is authenticated) - byte[] EmptySector = new byte[0x200]; - Array.Clear(EmptySector, 0, 0x200); - ((NokiaFlashModel)PhoneNotifier.CurrentModel).FlashSectors(0x22, EmptySector); - - // Reboot to Qualcomm Emergency mode - byte[] RebootCommand = new byte[] { 0x4E, 0x4F, 0x4B, 0x52 }; // NOKR - ((NokiaFlashModel)PhoneNotifier.CurrentModel).ExecuteRawVoidMethod(RebootCommand); - } - - // Expected to be launched on worker-thread. - internal void FlashBootLoader() - { - IsSwitchingInterface = true; - LogFile.Log("Start flashing in Qualcomm Emergency Flash mode"); - - if (this.FFU == null) - { - ActivateSubContext(new BusyViewModel("Recovering resources...")); - - LogFile.Log("Phone was unexpectedly detected in this mode while resources were not loaded yet."); - LogFile.Log("WPInternals tool probably crashed in previous session."); - LogFile.Log("Trying to recover resources from the registry."); - - // In case tool was terminated - FFUPath = (string)Registry.GetValue(@"HKEY_CURRENT_USER\Software\WPInternals", "FFUPath", null); - LoadersPath = (string)Registry.GetValue(@"HKEY_CURRENT_USER\Software\WPInternals", "LoadersPath", null); - if (DoUnlock) - SBL3Path = (string)Registry.GetValue(@"HKEY_CURRENT_USER\Software\WPInternals", "SBL3Path", null); - else - SBL3Path = null; - - try - { - EvaluateResources(Emergency:true); - } - catch (Exception Ex) - { - LogFile.LogException(Ex); - ActivateSubContext(new MessageViewModel(Ex.Message, Abort)); - } - } - - QualcommSerial Serial = (QualcommSerial)PhoneNotifier.CurrentModel; - Serial.EncodeCommands = false; - - QualcommFlasher Flasher = new QualcommFlasher(Serial); - - UInt64 TotalSectorCount = (UInt64)1 + 0x21 + 1 + - (UInt64)(SBL2.Length / 0x200) + - (UInt64)(SBL3.Length / 0x200) + - (UInt64)(UEFI.Length / 0x200) + - NewGPT.GetPartition("SBL1").SizeInSectors - 1 + - NewGPT.GetPartition("TZ").SizeInSectors + - NewGPT.GetPartition("RPM").SizeInSectors + - NewGPT.GetPartition("WINSECAPP").SizeInSectors; - - if (DoUnlock) - ActivateSubContext(new BusyViewModel("Flashing unlocked bootloader...", MaxProgressValue: TotalSectorCount, UIContext: UIContext)); - else - ActivateSubContext(new BusyViewModel("Flashing original bootloader...", MaxProgressValue: TotalSectorCount, UIContext: UIContext)); - - ProgressUpdater Progress = ((BusyViewModel)SubContextViewModel).ProgressUpdater; - - Flasher.Hello(); - Flasher.SetSecurityMode(0); - Flasher.OpenPartition(0x21); - - LogFile.Log("Partition opened."); - - byte[] MBR = FFU.GetSectors(0, 1); - - if (ExtraSector != null) - { - LogFile.Log("Flash EXT at 0x" + ((UInt32)NewGPT.GetPartition("HACK").FirstSector * 0x200).ToString("X8")); - Flasher.Flash((uint)NewGPT.GetPartition("HACK").FirstSector * 0x200, ExtraSector, Progress); - } - - LogFile.Log("Flash MBR at 0x" + ((UInt32)0).ToString("X8")); - Flasher.Flash(0, MBR, Progress, 0, 0x200); - - LogFile.Log("Flash GPT at 0x" + ((UInt32)0x200).ToString("X8")); - Flasher.Flash(0x200, GPT, Progress, 0, 0x41FF); // Bad bounds-check in the flash-loader prohibits to write the last byte. - - LogFile.Log("Flash SBL2 at 0x" + ((UInt32)NewGPT.GetPartition("SBL2").FirstSector * 0x200).ToString("X8")); - Flasher.Flash((uint)NewGPT.GetPartition("SBL2").FirstSector * 0x200, SBL2, Progress); - LogFile.Log("Flash SBL3 at 0x" + ((UInt32)NewGPT.GetPartition("SBL3").FirstSector * 0x200).ToString("X8")); - Flasher.Flash((uint)NewGPT.GetPartition("SBL3").FirstSector * 0x200, SBL3, Progress); - LogFile.Log("Flash UEFI at 0x" + ((UInt32)NewGPT.GetPartition("UEFI").FirstSector * 0x200).ToString("X8")); - Flasher.Flash((uint)NewGPT.GetPartition("UEFI").FirstSector * 0x200, UEFI, Progress); - - // To minimize risk of brick also flash these partitions: - LogFile.Log("Flash SBL1 at 0x" + ((UInt32)NewGPT.GetPartition("SBL1").FirstSector * 0x200).ToString("X8")); - Flasher.Flash((uint)NewGPT.GetPartition("SBL1").FirstSector * 0x200, FFU.GetPartition("SBL1"), Progress, 0, ((UInt32)NewGPT.GetPartition("SBL1").SizeInSectors - 1) * 0x200); - LogFile.Log("Flash TZ at 0x" + ((UInt32)NewGPT.GetPartition("TZ").FirstSector * 0x200).ToString("X8")); - Flasher.Flash((uint)NewGPT.GetPartition("TZ").FirstSector * 0x200, FFU.GetPartition("TZ"), Progress); - LogFile.Log("Flash RPM at 0x" + ((UInt32)NewGPT.GetPartition("RPM").FirstSector * 0x200).ToString("X8")); - Flasher.Flash((uint)NewGPT.GetPartition("RPM").FirstSector * 0x200, FFU.GetPartition("RPM"), Progress); - - // Workaround for bad bounds-check in flash-loader - UInt32 Length = (UInt32)FFU.GetPartition("WINSECAPP").Length; - UInt32 Start = (UInt32)NewGPT.GetPartition("WINSECAPP").FirstSector * 0x200; - if ((Start + Length) > 0x1E7FE00) - Length = 0x1E7FE00 - Start; - LogFile.Log("Flash WINSECAPP at 0x" + ((UInt32)NewGPT.GetPartition("WINSECAPP").FirstSector * 0x200).ToString("X8")); - Flasher.Flash(Start, FFU.GetPartition("WINSECAPP"), Progress, 0, Length); - - Flasher.ClosePartition(); - - IsFlashingDone = true; - LogFile.Log("Partition closed. Flashing ready. Rebooting."); - - ActivateSubContext(new BusyViewModel("Flashing done. Rebooting...")); - Flasher.Reboot(); - - Flasher.CloseSerial(); - } - - // Expected to be launched on worker-thread. - internal void CustomFlashBootLoader() - { - IsSwitchingInterface = true; - LogFile.Log("Start flashing in Custom Flash mode"); - - NokiaFlashModel CurrentModel = (NokiaFlashModel)PhoneNotifier.CurrentModel; - - UInt64 TotalSectorCount = (UInt64)0x21 + 1 + - (UInt64)(SBL2.Length / 0x200) + - (UInt64)(SBL3.Length / 0x200) + - (UInt64)(UEFI.Length / 0x200); - - if (DoUnlock) - ActivateSubContext(new BusyViewModel("Flashing unlocked bootloader...", MaxProgressValue: TotalSectorCount, UIContext: UIContext)); - else - ActivateSubContext(new BusyViewModel("Flashing original bootloader...", MaxProgressValue: TotalSectorCount, UIContext: UIContext)); - - ProgressUpdater Progress = ((BusyViewModel)SubContextViewModel).ProgressUpdater; - - LogFile.Log("Flash GPT at 0x" + ((UInt32)0x200).ToString("X8")); - CurrentModel.FlashSectors(1, GPT, 0); - Progress.SetProgress(0x21); - - if (ExtraSector != null) - { - LogFile.Log("Flash EXT at 0x" + ((UInt32)NewGPT.GetPartition("HACK").FirstSector * 0x200).ToString("X8")); - CurrentModel.FlashRawPartition(ExtraSector, "HACK", Progress); - } - - LogFile.Log("Flash SBL2 at 0x" + ((UInt32)NewGPT.GetPartition("SBL2").FirstSector * 0x200).ToString("X8")); - CurrentModel.FlashRawPartition(SBL2, "SBL2", Progress); - LogFile.Log("Flash SBL3 at 0x" + ((UInt32)NewGPT.GetPartition("SBL3").FirstSector * 0x200).ToString("X8")); - CurrentModel.FlashRawPartition(SBL3, "SBL3", Progress); - LogFile.Log("Flash UEFI at 0x" + ((UInt32)NewGPT.GetPartition("UEFI").FirstSector * 0x200).ToString("X8")); - CurrentModel.FlashRawPartition(UEFI, "UEFI", Progress); - - IsFlashingDone = true; - - ActivateSubContext(new BusyViewModel("Flashing done. Rebooting...")); - byte[] RebootCommand = new byte[] { 0x4E, 0x4F, 0x4B, 0x52 }; // NOKR - CurrentModel.ExecuteRawVoidMethod(RebootCommand); - } } internal class BootUnlockResourcesViewModel : FlashResourcesViewModel @@ -1185,13 +524,13 @@ namespace WPinternals internal class BootRestoreResourcesViewModel : FlashResourcesViewModel { internal BootRestoreResourcesViewModel(string CurrentMode, byte[] RootKeyHash, Action SwitchToFlashRom, Action SwitchToUndoRoot, Action SwitchToDownload, Action Result, Action Abort, bool IsBootLoaderUnlocked, bool TargetHasNewFlashProtocol, string PlatformID = null, string ProductType = null) - : base(CurrentMode, RootKeyHash, SwitchToFlashRom, SwitchToUndoRoot, SwitchToDownload, Result, Abort, IsBootLoaderUnlocked, TargetHasNewFlashProtocol, PlatformID, ProductType) + : base(CurrentMode, RootKeyHash, SwitchToFlashRom, SwitchToUndoRoot, SwitchToDownload, Result, Abort, IsBootLoaderUnlocked, TargetHasNewFlashProtocol, PlatformID, ProductType) { SBL3Path = null; } } - internal class FlashResourcesViewModel: ContextViewModel + internal class FlashResourcesViewModel : ContextViewModel { internal Action SwitchToFlashRom; internal Action SwitchToUndoRoot; @@ -1200,7 +539,7 @@ namespace WPinternals private string ProductType; private string ValidatedSupportedFfuPath = null; - internal FlashResourcesViewModel(string CurrentMode, byte[] RootKeyHash, Action SwitchToFlashRom, Action SwitchToUndoRoot, Action SwitchToDownload, Action Result, Action Abort, bool IsBootLoaderUnlocked, bool TargetHasNewFlashProtocol, string PlatformID = null, string ProductType = null): base() + internal FlashResourcesViewModel(string CurrentMode, byte[] RootKeyHash, Action SwitchToFlashRom, Action SwitchToUndoRoot, Action SwitchToDownload, Action Result, Action Abort, bool IsBootLoaderUnlocked, bool TargetHasNewFlashProtocol, string PlatformID = null, string ProductType = null) : base() { IsSwitchingInterface = true; this.CurrentMode = CurrentMode; @@ -1212,7 +551,7 @@ namespace WPinternals this.SwitchToDownload = SwitchToDownload; this.IsBootLoaderUnlocked = IsBootLoaderUnlocked; OkCommand = new DelegateCommand(() => Result(FFUPath, LoadersPath, SBL3Path, ProfileFFUPath, EDEPath, IsSupportedFfuNeeded ? SupportedFFUPath : null, false), - () => (!TargetHasNewFlashProtocol || ((ProfileFFUPath != null) && (!IsSupportedFfuNeeded || (IsSupportedFfuValid && (SupportedFFUPath != null)))))); + () => (!TargetHasNewFlashProtocol && (!IsSupportedFfuNeeded || (IsSupportedFfuValid && (SupportedFFUPath != null))) || ((ProfileFFUPath != null) && (!IsSupportedFfuNeeded || (IsSupportedFfuValid && (SupportedFFUPath != null)))))); FixCommand = new DelegateCommand(() => Result(null, null, null, null, null, null, true)); CancelCommand = new DelegateCommand(Abort); this.TargetHasNewFlashProtocol = TargetHasNewFlashProtocol; @@ -1225,6 +564,7 @@ namespace WPinternals else { FFUPath = (string)Registry.GetValue(@"HKEY_CURRENT_USER\Software\WPInternals", "FFUPath", null); + SupportedFFUPath = (string)Registry.GetValue(@"HKEY_CURRENT_USER\Software\WPInternals", "SupportedFFUPath", null); LoadersPath = (string)Registry.GetValue(@"HKEY_CURRENT_USER\Software\WPInternals", "LoadersPath", null); } } @@ -1318,100 +658,193 @@ namespace WPinternals private void SetSupportedFFUPath() { - if ((this is BootRestoreResourcesViewModel) || (_ProfileFFUPath == null)) + if (!TargetHasNewFlashProtocol) { - IsSupportedFfuNeeded = false; - return; - } - - try - { - FFU ProfileFFU = new FFU(_ProfileFFUPath); - IsSupportedFfuNeeded = !(App.PatchEngine.PatchDefinitions.Where(p => p.Name == "SecureBootHack-V2-EFIESP").First().TargetVersions.Any(v => v.Description == ProfileFFU.GetOSVersion())); - } - catch - { - IsSupportedFfuNeeded = false; - return; - } - - if (IsSupportedFfuNeeded) - { - FFU SupportedFFU; - - try + if (this is BootRestoreResourcesViewModel) { - if (_SupportedFFUPath != null) - { - SupportedFFU = new FFU(_SupportedFFUPath); - if (App.PatchEngine.PatchDefinitions.Where(p => p.Name == "SecureBootHack-V2-EFIESP").First().TargetVersions.Any(v => v.Description == SupportedFFU.GetOSVersion())) - return; - } + IsSupportedFfuNeeded = false; + return; } - catch { } try { - string TempSupportedFFUPath = (string)Registry.GetValue(@"HKEY_CURRENT_USER\Software\WPInternals", "SupportedFFUPath", null); - if (TempSupportedFFUPath != null) + FFU FFU = new FFU(_FFUPath); + IsSupportedFfuNeeded = !(App.PatchEngine.PatchDefinitions.Where(p => p.Name == "SecureBootHack-V1.1-EFIESP").First().TargetVersions.Any(v => v.Description == FFU.GetOSVersion())); + } + catch + { + IsSupportedFfuNeeded = false; + return; + } + + if (IsSupportedFfuNeeded) + { + FFU SupportedFFU; + + try { - SupportedFFU = new FFU(TempSupportedFFUPath); - if (App.PatchEngine.PatchDefinitions.Where(p => p.Name == "SecureBootHack-V2-EFIESP").First().TargetVersions.Any(v => v.Description == SupportedFFU.GetOSVersion())) + if (_SupportedFFUPath != null) { - ValidatedSupportedFfuPath = TempSupportedFFUPath; - SupportedFFUPath = TempSupportedFFUPath; - IsSupportedFfuValid = true; - return; + SupportedFFU = new FFU(_SupportedFFUPath); + if (App.PatchEngine.PatchDefinitions.Where(p => p.Name == "SecureBootHack-V1.1-EFIESP").First().TargetVersions.Any(v => v.Description == SupportedFFU.GetOSVersion())) + return; } } - } - catch { } + catch { } - List FFUs = App.Config.FFURepository.Where(e => App.PatchEngine.PatchDefinitions.Where(p => p.Name == "SecureBootHack-V2-EFIESP").First().TargetVersions.Any(v => v.Description == e.OSVersion)).ToList(); - if (FFUs.Count() > 0) + try + { + string TempSupportedFFUPath = (string)Registry.GetValue(@"HKEY_CURRENT_USER\Software\WPInternals", "SupportedFFUPath", null); + if (TempSupportedFFUPath != null) + { + SupportedFFU = new FFU(TempSupportedFFUPath); + if (App.PatchEngine.PatchDefinitions.Where(p => p.Name == "SecureBootHack-V1.1-EFIESP").First().TargetVersions.Any(v => v.Description == SupportedFFU.GetOSVersion())) + { + ValidatedSupportedFfuPath = TempSupportedFFUPath; + SupportedFFUPath = TempSupportedFFUPath; + IsSupportedFfuValid = true; + return; + } + } + } + catch { } + + List FFUs = App.Config.FFURepository.Where(e => App.PatchEngine.PatchDefinitions.Where(p => p.Name == "SecureBootHack-V1.1-EFIESP").First().TargetVersions.Any(v => v.Description == e.OSVersion)).ToList(); + if (FFUs.Count() > 0) + { + ValidatedSupportedFfuPath = FFUs[0].Path; + SupportedFFUPath = FFUs[0].Path; + IsSupportedFfuValid = true; + } + } + } + else + { + if ((this is BootRestoreResourcesViewModel) || (_ProfileFFUPath == null)) { - ValidatedSupportedFfuPath = FFUs[0].Path; - SupportedFFUPath = FFUs[0].Path; - IsSupportedFfuValid = true; + IsSupportedFfuNeeded = false; + return; + } + + try + { + FFU ProfileFFU = new FFU(_ProfileFFUPath); + IsSupportedFfuNeeded = !(App.PatchEngine.PatchDefinitions.Where(p => p.Name == "SecureBootHack-V2-EFIESP").First().TargetVersions.Any(v => v.Description == ProfileFFU.GetOSVersion())); + } + catch + { + IsSupportedFfuNeeded = false; + return; + } + + if (IsSupportedFfuNeeded) + { + FFU SupportedFFU; + + try + { + if (_SupportedFFUPath != null) + { + SupportedFFU = new FFU(_SupportedFFUPath); + if (App.PatchEngine.PatchDefinitions.Where(p => p.Name == "SecureBootHack-V2-EFIESP").First().TargetVersions.Any(v => v.Description == SupportedFFU.GetOSVersion())) + return; + } + } + catch { } + + try + { + string TempSupportedFFUPath = (string)Registry.GetValue(@"HKEY_CURRENT_USER\Software\WPInternals", "SupportedFFUPath", null); + if (TempSupportedFFUPath != null) + { + SupportedFFU = new FFU(TempSupportedFFUPath); + if (App.PatchEngine.PatchDefinitions.Where(p => p.Name == "SecureBootHack-V2-EFIESP").First().TargetVersions.Any(v => v.Description == SupportedFFU.GetOSVersion())) + { + ValidatedSupportedFfuPath = TempSupportedFFUPath; + SupportedFFUPath = TempSupportedFFUPath; + IsSupportedFfuValid = true; + return; + } + } + } + catch { } + + List FFUs = App.Config.FFURepository.Where(e => App.PatchEngine.PatchDefinitions.Where(p => p.Name == "SecureBootHack-V2-EFIESP").First().TargetVersions.Any(v => v.Description == e.OSVersion)).ToList(); + if (FFUs.Count() > 0) + { + ValidatedSupportedFfuPath = FFUs[0].Path; + SupportedFFUPath = FFUs[0].Path; + IsSupportedFfuValid = true; + } } } } private void ValidateSupportedFfuPath() { - if (IsSupportedFfuNeeded) + try { - if (SupportedFFUPath == null) + if (IsSupportedFfuNeeded) { - IsSupportedFfuValid = true; // No visible warning when there is no SupportedFFU selected yet. - } - else - { - if (App.Config.FFURepository.Any(e => ((e.Path == SupportedFFUPath) && (App.PatchEngine.PatchDefinitions.Where(p => p.Name == "SecureBootHack-V2-EFIESP").First().TargetVersions.Any(v => v.Description == e.OSVersion))))) + if (SupportedFFUPath == null) { - IsSupportedFfuValid = true; + IsSupportedFfuValid = true; // No visible warning when there is no SupportedFFU selected yet. } else { - FFU SupportedFFU = new FFU(SupportedFFUPath); - if (App.PatchEngine.PatchDefinitions.Where(p => p.Name == "SecureBootHack-V2-EFIESP").First().TargetVersions.Any(v => v.Description == SupportedFFU.GetOSVersion())) + + if (!TargetHasNewFlashProtocol) { - IsSupportedFfuValid = true; + if (App.Config.FFURepository.Any(e => ((e.Path == SupportedFFUPath) && (App.PatchEngine.PatchDefinitions.Where(p => p.Name == "SecureBootHack-V1.1-EFIESP").First().TargetVersions.Any(v => v.Description == e.OSVersion))))) + { + IsSupportedFfuValid = true; + } + else + { + FFU SupportedFFU = new FFU(SupportedFFUPath); + if (App.PatchEngine.PatchDefinitions.Where(p => p.Name == "SecureBootHack-V1.1-EFIESP").First().TargetVersions.Any(v => v.Description == SupportedFFU.GetOSVersion())) + { + IsSupportedFfuValid = true; + } + else + { + IsSupportedFfuValid = false; + } + } } else { - IsSupportedFfuValid = false; + if (App.Config.FFURepository.Any(e => ((e.Path == SupportedFFUPath) && (App.PatchEngine.PatchDefinitions.Where(p => p.Name == "SecureBootHack-V2-EFIESP").First().TargetVersions.Any(v => v.Description == e.OSVersion))))) + { + IsSupportedFfuValid = true; + } + else + { + FFU SupportedFFU = new FFU(SupportedFFUPath); + if (App.PatchEngine.PatchDefinitions.Where(p => p.Name == "SecureBootHack-V2-EFIESP").First().TargetVersions.Any(v => v.Description == SupportedFFU.GetOSVersion())) + { + IsSupportedFfuValid = true; + } + else + { + IsSupportedFfuValid = false; + } + } } } } - } - else - { - IsSupportedFfuValid = true; - } + else + { + IsSupportedFfuValid = true; + } - if (IsSupportedFfuValid && (SupportedFFUPath != null)) - ValidatedSupportedFfuPath = SupportedFFUPath; + if (IsSupportedFfuValid && (SupportedFFUPath != null)) + ValidatedSupportedFfuPath = SupportedFFUPath; + } + catch + { + IsSupportedFfuValid = false; + } } private bool _IsSupportedFfuNeeded = false; @@ -1498,6 +931,8 @@ namespace WPinternals { _FFUPath = value; OnPropertyChanged("FFUPath"); + SetSupportedFFUPath(); + OkCommand.RaiseCanExecuteChanged(); } } diff --git a/ViewModels/LumiaUnlockBootloaderViewModel.cs b/ViewModels/LumiaUnlockBootloaderViewModel.cs new file mode 100644 index 0000000..7ea5b1c --- /dev/null +++ b/ViewModels/LumiaUnlockBootloaderViewModel.cs @@ -0,0 +1,2253 @@ +// Copyright (c) 2018, Rene Lergner - wpinternals.net - @Heathcliff74xda +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; + +namespace WPinternals +{ + internal class LumiaUnlockBootloaderViewModel + { + // TODO: Add logging + private static void PerformSoftBrick(PhoneNotifierViewModel Notifier, FFU FFU) + { + NokiaFlashModel FlashModel = (NokiaFlashModel)Notifier.CurrentModel; + + // Send FFU headers + UInt64 CombinedFFUHeaderSize = FFU.HeaderSize; + byte[] FfuHeader = new byte[CombinedFFUHeaderSize]; + System.IO.FileStream FfuFile = new System.IO.FileStream(FFU.Path, System.IO.FileMode.Open, System.IO.FileAccess.Read); + FfuFile.Read(FfuHeader, 0, (int)CombinedFFUHeaderSize); + FfuFile.Close(); + + FlashModel.SendFfuHeaderV1(FfuHeader); + + // Send 1 empty chunk (according to layout in FFU headers, it will be written to first and last chunk) + byte[] EmptyChunk = new byte[0x20000]; + Array.Clear(EmptyChunk, 0, 0x20000); + FlashModel.SendFfuPayloadV1(EmptyChunk); + + // Reboot to Qualcomm Emergency mode + byte[] RebootCommand = new byte[] { 0x4E, 0x4F, 0x4B, 0x52 }; // NOKR + FlashModel.ExecuteRawVoidMethod(RebootCommand); + } + + private static void SendLoader(PhoneNotifierViewModel PhoneNotifier, List PossibleLoaders) + { + // Assume 9008 mode + if (!((PhoneNotifier.CurrentModel is QualcommSerial) && (PossibleLoaders != null) && (PossibleLoaders.Count > 0))) + return; + + LogFile.Log("Sending loader"); + + QualcommSerial Serial = (QualcommSerial)PhoneNotifier.CurrentModel; + QualcommDownload Download = new QualcommDownload(Serial); + if (Download.IsAlive()) + { + int Attempt = 1; + bool Result = false; + foreach (QualcommPartition Loader in PossibleLoaders) + { + LogFile.Log("Attempt " + Attempt.ToString()); + + try + { + Download.SendToPhoneMemory(0x2A000000, Loader.Binary); + Download.StartBootloader(0x2A000000); + Result = true; + LogFile.Log("Loader sent successfully"); + } + catch { } + + if (Result) + break; + + Attempt++; + } + Serial.Close(); + + if (!Result) + LogFile.Log("Loader failed"); + } + else + { + LogFile.Log("Failed to communicate to Qualcomm Emergency Download mode"); + throw new BadConnectionException(); + } + } + + internal static async Task LumiaV2RelockUEFI(PhoneNotifierViewModel Notifier, string FFUPath = null, bool DoResetFirst = true, SetWorkingStatus SetWorkingStatus = null, UpdateWorkingStatus UpdateWorkingStatus = null, ExitSuccess ExitSuccess = null, ExitFailure ExitFailure = null) + { + if (SetWorkingStatus == null) SetWorkingStatus = (m, s, v, a, st) => { }; + if (UpdateWorkingStatus == null) UpdateWorkingStatus = (m, s, v, st) => { }; + if (ExitSuccess == null) ExitSuccess = (m, s) => { }; + if (ExitFailure == null) ExitFailure = (m, s) => { }; + + await LumiaUnlockBootloaderViewModel.LumiaRelockUEFI(Notifier, FFUPath, DoResetFirst, SetWorkingStatus, UpdateWorkingStatus, ExitSuccess, ExitFailure); + + SetWorkingStatus("Booting phone..."); + await Notifier.WaitForArrival(); + + if (Notifier.CurrentInterface == PhoneInterfaces.Lumia_Bootloader) + ((NokiaFlashModel)Notifier.CurrentModel).ContinueBoot(); + + if (Notifier.CurrentInterface != PhoneInterfaces.Lumia_Normal) + await Notifier.WaitForArrival(); + + if (Notifier.CurrentInterface == PhoneInterfaces.Lumia_Bootloader) + ((NokiaFlashModel)Notifier.CurrentModel).ContinueBoot(); + + if (Notifier.CurrentInterface != PhoneInterfaces.Lumia_Normal) + await Notifier.WaitForArrival(); + + if (Notifier.CurrentInterface == PhoneInterfaces.Lumia_Bootloader) + ((NokiaFlashModel)Notifier.CurrentModel).ContinueBoot(); + + if (Notifier.CurrentInterface != PhoneInterfaces.Lumia_Normal) + { + ExitFailure("Failed to relock phone", "Your phone is half relocked. You may need to reflash a stock ROM"); + return; + } + + ExitSuccess("Bootloader restored successfully!"); + } + + internal static async Task LumiaV2UnlockUEFI(PhoneNotifierViewModel Notifier, string ProfileFFUPath, string EDEPath, string SupportedFFUPath, SetWorkingStatus SetWorkingStatus = null, UpdateWorkingStatus UpdateWorkingStatus = null, ExitSuccess ExitSuccess = null, ExitFailure ExitFailure = null) + { + if (SetWorkingStatus == null) SetWorkingStatus = (m, s, v, a, st) => { }; + if (UpdateWorkingStatus == null) UpdateWorkingStatus = (m, s, v, st) => { }; + if (ExitSuccess == null) ExitSuccess = (m, s) => { }; + if (ExitFailure == null) ExitFailure = (m, s) => { }; + + await LumiaUnlockBootloaderViewModel.LumiaUnlockUEFI(Notifier, ProfileFFUPath, EDEPath, SupportedFFUPath, SetWorkingStatus, UpdateWorkingStatus, ExitSuccess, ExitFailure); + + SetWorkingStatus("Booting phone..."); + await Notifier.WaitForArrival(); + + if (Notifier.CurrentInterface == PhoneInterfaces.Lumia_Bootloader) + ((NokiaFlashModel)Notifier.CurrentModel).ContinueBoot(); + + if (Notifier.CurrentInterface != PhoneInterfaces.Lumia_Normal) + await Notifier.WaitForArrival(); + + if (Notifier.CurrentInterface == PhoneInterfaces.Lumia_Bootloader) + ((NokiaFlashModel)Notifier.CurrentModel).ContinueBoot(); + + if (Notifier.CurrentInterface != PhoneInterfaces.Lumia_Normal) + await Notifier.WaitForArrival(); + + if (Notifier.CurrentInterface == PhoneInterfaces.Lumia_Bootloader) + ((NokiaFlashModel)Notifier.CurrentModel).ContinueBoot(); + + if (Notifier.CurrentInterface != PhoneInterfaces.Lumia_Normal) + { + ExitFailure("Failed to unlock phone", "Your phone is half unlocked. You may need to reflash a stock ROM"); + return; + } + + ExitSuccess("Bootloader unlocked successfully!", null); + } + + // Magic! + // Platform Secure Boot Hack for Spec A devices + // + // Assumes phone in Flash mode + // in Qualcomm Dload + // in Qualcomm Flash + // + internal static async Task LumiaV1RelockFirmware(PhoneNotifierViewModel Notifier, string FFUPath, string LoadersPath, SetWorkingStatus SetWorkingStatus = null, UpdateWorkingStatus UpdateWorkingStatus = null, ExitSuccess ExitSuccess = null, ExitFailure ExitFailure = null) + { + LogFile.BeginAction("RelockBootloader"); + + if (SetWorkingStatus == null) SetWorkingStatus = (m, s, v, a, st) => { }; + if (UpdateWorkingStatus == null) UpdateWorkingStatus = (m, s, v, st) => { }; + if (ExitSuccess == null) ExitSuccess = (m, s) => { }; + if (ExitFailure == null) ExitFailure = (m, s) => { }; + + try + { + if (Notifier.CurrentModel is NokiaFlashModel) + { + await LumiaUnlockBootloaderViewModel.LumiaRelockUEFI(Notifier, FFUPath, true, SetWorkingStatus, UpdateWorkingStatus, null, (string Message, string SubMessage) => + { + ExitFailure(Message, SubMessage); + LogFile.EndAction("RelockBootloader"); + return; + }); + + if (Notifier.CurrentInterface != PhoneInterfaces.Lumia_Bootloader && Notifier.CurrentInterface != PhoneInterfaces.Lumia_Flash) + await Notifier.WaitForArrival(); + + if (Notifier.CurrentInterface == PhoneInterfaces.Lumia_Bootloader) + await SwitchModeViewModel.SwitchTo(Notifier, PhoneInterfaces.Lumia_Flash); + } + + LogFile.Log("Assembling data for relock", LogType.FileAndConsole); + SetWorkingStatus("Assembling data for relock", null, null); + + if ((FFUPath == null) || (FFUPath.Length == 0)) + throw new ArgumentNullException("FFU path is missing"); + + if (Notifier.CurrentInterface == PhoneInterfaces.Qualcomm_Download) + if ((LoadersPath == null) || (LoadersPath.Length == 0)) + throw new Exception("Error: Path for Loaders is mandatory."); + + bool DumpPartitions = false; + string DumpFilePrefix = Environment.ExpandEnvironmentVariables("%ALLUSERSPROFILE%\\WPInternals\\") + DateTime.Now.ToString("yyyy-MM-dd hh.mm.ss") + " - "; + bool IsBootLoaderUnlocked = false; + + FFU FFU = null; + try + { + FFU = new FFU(FFUPath); + } + catch (Exception Ex) + { + LogFile.LogException(Ex); + throw new Exception("Error: Parsing FFU-file failed."); + } + + if (Notifier.CurrentModel is NokiaFlashModel) + { + FlashVersion FlashVersion = ((NokiaFlashModel)Notifier.CurrentModel).GetFlashVersion(); + if (FlashVersion == null) + throw new Exception("Error: The version of the Flash Application on the phone could not be determined."); + if ((FlashVersion.ApplicationMajor < 1) || ((FlashVersion.ApplicationMajor == 1) && (FlashVersion.ApplicationMinor < 28))) + throw new Exception("Error: The version of the Flash Application on the phone is too old. Update your phone using Windows Updates or flash a newer ROM to your phone. Then try again."); + + UefiSecurityStatusResponse SecurityStatus = ((NokiaFlashModel)Notifier.CurrentModel).ReadSecurityStatus(); + IsBootLoaderUnlocked = (SecurityStatus.AuthenticationStatus || SecurityStatus.RdcStatus || !SecurityStatus.SecureFfuEfuseStatus); + } + + if (DumpPartitions) + { + try + { + File.WriteAllBytes(DumpFilePrefix + "01.bin", FFU.GetSectors(0, 34)); // Original GPT + } + catch (Exception Ex) + { + LogFile.LogException(Ex); + throw new Exception("Error: Writing binary for logging failed."); + } + } + + GPT NewGPT = null; + if (Notifier.CurrentModel is NokiaFlashModel) + { + ((NokiaFlashModel)Notifier.CurrentModel).ResetPhone(); + + if (Notifier.CurrentInterface != PhoneInterfaces.Lumia_Bootloader) + await Notifier.WaitForArrival(); + + if (Notifier.CurrentInterface != PhoneInterfaces.Lumia_Bootloader) + throw new WPinternalsException("Phone is in an unexpected mode."); + + NewGPT = ((NokiaFlashModel)Notifier.CurrentModel).ReadGPT(); + + await SwitchModeViewModel.SwitchTo(Notifier, PhoneInterfaces.Lumia_Flash); + + if (Notifier.CurrentInterface != PhoneInterfaces.Lumia_Flash) + await Notifier.WaitForArrival(); + + if (Notifier.CurrentInterface != PhoneInterfaces.Lumia_Flash) + throw new WPinternalsException("Phone is in an unexpected mode."); + } + else + { + NewGPT = FFU.GPT; + } + + // Make sure all partitions are in range of the emergency flasher. + NewGPT.RestoreBackupPartitions(); + + byte[] GPT = null; + try + { + NewGPT.RemoveHack(); + GPT = NewGPT.Rebuild(); + } + catch (Exception Ex) + { + LogFile.LogException(Ex); + throw new Exception("Error: Parsing partitions failed."); + } + + Partition IsUnlockedFlag = NewGPT.GetPartition("IS_UNLOCKED_SBL3"); + if (IsUnlockedFlag != null) + { + NewGPT.Partitions.Remove(IsUnlockedFlag); + GPT = NewGPT.Rebuild(); + } + + if (DumpPartitions) + { + try + { + File.WriteAllBytes(DumpFilePrefix + "02.bin", GPT); // Patched GPT + } + catch (Exception Ex) + { + LogFile.LogException(Ex); + throw new Exception("Error: Writing binary for logging failed."); + } + } + + SBL1 SBL1 = null; + try + { + SBL1 = new SBL1(FFU.GetPartition("SBL1")); + } + catch (Exception Ex) + { + LogFile.LogException(Ex); + throw new Exception("Error: Parsing SBL1 failed."); + } + + if (DumpPartitions) + { + try + { + File.WriteAllBytes(DumpFilePrefix + "03.bin", SBL1.Binary); // Original SBL1 + } + catch (Exception Ex) + { + LogFile.LogException(Ex); + throw new Exception("Error: Writing binary for logging failed."); + } + } + + byte[] RootKeyHash = null; + if (Notifier.CurrentInterface == PhoneInterfaces.Qualcomm_Download) + { + QualcommDownload Download = new QualcommDownload((QualcommSerial)Notifier.CurrentModel); + RootKeyHash = Download.GetRKH(); + } + else if (Notifier.CurrentInterface != PhoneInterfaces.Qualcomm_Flash) + { + RootKeyHash = ((NokiaFlashModel)Notifier.CurrentModel).ReadParam("RRKH"); + } + + if (Notifier.CurrentInterface != PhoneInterfaces.Qualcomm_Flash) + { + if (RootKeyHash == null) + { + throw new Exception("Error: Root Key Hash could not be retrieved from the phone."); + } + if (SBL1.RootKeyHash == null) + { + throw new Exception("Error: Root Key Hash could not be retrieved from FFU file."); + } + if (!StructuralComparisons.StructuralEqualityComparer.Equals(RootKeyHash, SBL1.RootKeyHash)) + { + LogFile.Log("Phone: " + Converter.ConvertHexToString(RootKeyHash, "")); + LogFile.Log("SBL1: " + Converter.ConvertHexToString(SBL1.RootKeyHash, "")); + throw new Exception("Error: Root Key Hash from phone and from FFU file do not match!"); + } + } + + SBL2 SBL2Partition = null; + try + { + SBL2Partition = new SBL2(FFU.GetPartition("SBL2")); + } + catch (Exception Ex) + { + LogFile.LogException(Ex); + throw new Exception("Error: Parsing SBL2 failed."); + } + + if (DumpPartitions) + { + try + { + File.WriteAllBytes(DumpFilePrefix + "05.bin", SBL2Partition.Binary); // Original SBL2 + } + catch (Exception Ex) + { + LogFile.LogException(Ex); + throw new Exception("Error: Writing binary for logging failed."); + } + } + + byte[] SBL2 = SBL2Partition.Binary; + + SBL3 SBL3Partition; + + try + { + SBL3Partition = new SBL3(FFU.GetPartition("SBL3")); + } + catch (Exception Ex) + { + LogFile.LogException(Ex); + throw new Exception("Error: Parsing SBL3 from FFU failed."); + } + + if (DumpPartitions) + { + try + { + File.WriteAllBytes(DumpFilePrefix + "07.bin", SBL3Partition.Binary); // Original SBL3 + } + catch (Exception Ex) + { + LogFile.LogException(Ex); + throw new Exception("Error: Writing binary for logging failed."); + } + } + + byte[] SBL3 = SBL3Partition.Binary; + + UEFI UEFIPartition = null; + try + { + UEFIPartition = new UEFI(FFU.GetPartition("UEFI")); + } + catch (Exception Ex) + { + LogFile.LogException(Ex); + throw new Exception("Error: Parsing UEFI failed."); + } + + if (DumpPartitions) + { + try + { + File.WriteAllBytes(DumpFilePrefix + "09.bin", UEFIPartition.Binary); // Original UEFI + } + catch (Exception Ex) + { + LogFile.LogException(Ex); + throw new Exception("Error: Writing binary for logging failed."); + } + } + + byte[] UEFI = UEFIPartition.Binary; + + List PossibleLoaders = null; + if (!IsBootLoaderUnlocked || Notifier.CurrentInterface == PhoneInterfaces.Qualcomm_Download) + { + try + { + PossibleLoaders = QualcommLoaders.GetPossibleLoadersForRootKeyHash(LoadersPath, RootKeyHash); + if (PossibleLoaders.Count == 0) + { + throw new Exception("Error: No matching loaders found for RootKeyHash."); + } + } + catch (Exception Ex) + { + LogFile.LogException(Ex); + throw new Exception("Error: Unexpected error during scanning for loaders."); + } + } + + if (IsBootLoaderUnlocked) + // Flash phone in Flash app + { + NokiaFlashModel CurrentModel = (NokiaFlashModel)Notifier.CurrentModel; + LogFile.Log("Start flashing in Custom Flash mode"); + + UInt64 TotalSectorCount = (UInt64)0x21 + 1 + + (UInt64)(SBL2.Length / 0x200) + + (UInt64)(SBL3.Length / 0x200) + + (UInt64)(UEFI.Length / 0x200); + + SetWorkingStatus("Flashing original bootloader...", MaxProgressValue: 100, Status: WPinternalsStatus.Flashing); + ProgressUpdater Progress = new ProgressUpdater(TotalSectorCount, (int ProgressPercentage, TimeSpan? TimeSpan) => UpdateWorkingStatus("Flashing original bootloader...", CurrentProgressValue: (ulong)ProgressPercentage, Status: WPinternalsStatus.Flashing)); + + LogFile.Log("Flash GPT at 0x" + ((UInt32)0x200).ToString("X8")); + CurrentModel.FlashSectors(1, GPT, 0); + Progress.SetProgress(0x21); + + LogFile.Log("Flash SBL2 at 0x" + ((UInt32)NewGPT.GetPartition("SBL2").FirstSector * 0x200).ToString("X8")); + CurrentModel.FlashRawPartition(SBL2, "SBL2", Progress); + LogFile.Log("Flash SBL3 at 0x" + ((UInt32)NewGPT.GetPartition("SBL3").FirstSector * 0x200).ToString("X8")); + CurrentModel.FlashRawPartition(SBL3, "SBL3", Progress); + LogFile.Log("Flash UEFI at 0x" + ((UInt32)NewGPT.GetPartition("UEFI").FirstSector * 0x200).ToString("X8")); + CurrentModel.FlashRawPartition(UEFI, "UEFI", Progress); + + // phone is in flash mode, we can exit + } + else if (Notifier.CurrentInterface == PhoneInterfaces.Lumia_Flash || Notifier.CurrentInterface == PhoneInterfaces.Qualcomm_Download || Notifier.CurrentInterface == PhoneInterfaces.Qualcomm_Flash) + { + // Switch to DLOAD + if (Notifier.CurrentInterface == PhoneInterfaces.Lumia_Flash) + { + SetWorkingStatus("Switching to Emergency Download mode..."); + PerformSoftBrick(Notifier, FFU); + if (Notifier.CurrentInterface != PhoneInterfaces.Qualcomm_Download) + await Notifier.WaitForArrival(); + if (Notifier.CurrentInterface != PhoneInterfaces.Qualcomm_Download) + throw new WPinternalsException("Phone failed to switch to DLOAD."); + } + + // Send loader + if (Notifier.CurrentInterface == PhoneInterfaces.Qualcomm_Download) + { + SetWorkingStatus("Sending loader..."); + SendLoader(Notifier, PossibleLoaders); + if (Notifier.CurrentInterface != PhoneInterfaces.Qualcomm_Flash) + await Notifier.WaitForArrival(); + if (Notifier.CurrentInterface != PhoneInterfaces.Qualcomm_Flash) + throw new WPinternalsException("Phone failed to switch to DLOAD."); + } + + // Flash bootloader + QualcommSerial Serial = (QualcommSerial)Notifier.CurrentModel; + Serial.EncodeCommands = false; + + QualcommFlasher Flasher = new QualcommFlasher(Serial); + + UInt64 TotalSectorCount = (UInt64)1 + 0x21 + 1 + + (UInt64)(SBL2.Length / 0x200) + + (UInt64)(SBL3.Length / 0x200) + + (UInt64)(UEFI.Length / 0x200) + + NewGPT.GetPartition("SBL1").SizeInSectors - 1 + + NewGPT.GetPartition("TZ").SizeInSectors + + NewGPT.GetPartition("RPM").SizeInSectors + + NewGPT.GetPartition("WINSECAPP").SizeInSectors; + + SetWorkingStatus("Flashing original bootloader...", MaxProgressValue: 100, Status: WPinternalsStatus.Flashing); + ProgressUpdater Progress = new ProgressUpdater(TotalSectorCount, (int ProgressPercentage, TimeSpan? TimeSpan) => UpdateWorkingStatus("Flashing original bootloader...", CurrentProgressValue: (ulong)ProgressPercentage, Status: WPinternalsStatus.Flashing)); + + Flasher.Hello(); + Flasher.SetSecurityMode(0); + Flasher.OpenPartition(0x21); + + LogFile.Log("Partition opened."); + + byte[] MBR = FFU.GetSectors(0, 1); + + LogFile.Log("Flash MBR at 0x" + ((UInt32)0).ToString("X8")); + Flasher.Flash(0, MBR, Progress, 0, 0x200); + + LogFile.Log("Flash GPT at 0x" + ((UInt32)0x200).ToString("X8")); + Flasher.Flash(0x200, GPT, Progress, 0, 0x41FF); // Bad bounds-check in the flash-loader prohibits to write the last byte. + + LogFile.Log("Flash SBL2 at 0x" + ((UInt32)NewGPT.GetPartition("SBL2").FirstSector * 0x200).ToString("X8")); + Flasher.Flash((uint)NewGPT.GetPartition("SBL2").FirstSector * 0x200, SBL2, Progress); + LogFile.Log("Flash SBL3 at 0x" + ((UInt32)NewGPT.GetPartition("SBL3").FirstSector * 0x200).ToString("X8")); + Flasher.Flash((uint)NewGPT.GetPartition("SBL3").FirstSector * 0x200, SBL3, Progress); + LogFile.Log("Flash UEFI at 0x" + ((UInt32)NewGPT.GetPartition("UEFI").FirstSector * 0x200).ToString("X8")); + Flasher.Flash((uint)NewGPT.GetPartition("UEFI").FirstSector * 0x200, UEFI, Progress); + + // To minimize risk of brick also flash these partitions: + LogFile.Log("Flash SBL1 at 0x" + ((UInt32)NewGPT.GetPartition("SBL1").FirstSector * 0x200).ToString("X8")); + Flasher.Flash((uint)NewGPT.GetPartition("SBL1").FirstSector * 0x200, FFU.GetPartition("SBL1"), Progress, 0, ((UInt32)NewGPT.GetPartition("SBL1").SizeInSectors - 1) * 0x200); + LogFile.Log("Flash TZ at 0x" + ((UInt32)NewGPT.GetPartition("TZ").FirstSector * 0x200).ToString("X8")); + Flasher.Flash((uint)NewGPT.GetPartition("TZ").FirstSector * 0x200, FFU.GetPartition("TZ"), Progress); + LogFile.Log("Flash RPM at 0x" + ((UInt32)NewGPT.GetPartition("RPM").FirstSector * 0x200).ToString("X8")); + Flasher.Flash((uint)NewGPT.GetPartition("RPM").FirstSector * 0x200, FFU.GetPartition("RPM"), Progress); + + // Workaround for bad bounds-check in flash-loader + UInt32 Length = (UInt32)FFU.GetPartition("WINSECAPP").Length; + UInt32 Start = (UInt32)NewGPT.GetPartition("WINSECAPP").FirstSector * 0x200; + if ((Start + Length) > 0x1E7FE00) + Length = 0x1E7FE00 - Start; + LogFile.Log("Flash WINSECAPP at 0x" + ((UInt32)NewGPT.GetPartition("WINSECAPP").FirstSector * 0x200).ToString("X8")); + Flasher.Flash(Start, FFU.GetPartition("WINSECAPP"), Progress, 0, Length); + + Flasher.ClosePartition(); + + LogFile.Log("Partition closed. Flashing ready. Rebooting."); + + // Reboot phone to Flash app + SetWorkingStatus("Flashing done. Rebooting..."); + Flasher.Reboot(); + + Flasher.CloseSerial(); + + if (Notifier.CurrentInterface != PhoneInterfaces.Lumia_Bootloader && Notifier.CurrentInterface != PhoneInterfaces.Lumia_Flash) + await Notifier.WaitForArrival(); + + if (Notifier.CurrentInterface != PhoneInterfaces.Lumia_Bootloader && Notifier.CurrentInterface != PhoneInterfaces.Lumia_Flash) + throw new WPinternalsException("Phone is in an unexpected mode."); + + if (Notifier.CurrentInterface == PhoneInterfaces.Lumia_Bootloader) + { + await SwitchModeViewModel.SwitchToWithStatus(Notifier, PhoneInterfaces.Lumia_Flash, SetWorkingStatus, UpdateWorkingStatus); + } + } + else + { + throw new WPinternalsException("Phone is in an unexpected mode."); + } + + SetWorkingStatus("Rebooting phone..."); + await SwitchModeViewModel.SwitchTo(Notifier, PhoneInterfaces.Lumia_Bootloader); + + if (Notifier.CurrentInterface != PhoneInterfaces.Lumia_Bootloader && Notifier.CurrentInterface != PhoneInterfaces.Lumia_Flash) + await Notifier.WaitForArrival(); + + if (Notifier.CurrentInterface != PhoneInterfaces.Lumia_Bootloader && Notifier.CurrentInterface != PhoneInterfaces.Lumia_Flash) + throw new WPinternalsException("Phone is in an unexpected mode."); + + NokiaFlashModel FlashModel = (NokiaFlashModel)Notifier.CurrentModel; + if (Notifier.CurrentInterface == PhoneInterfaces.Lumia_Flash && FlashModel.ReadParam("FS")[3] > 0) + ExitSuccess("Bootloader is restored", "NOTE: You need to flash a stock ROM because you recovered a phone from a bootloader unlock failure."); + else + { + SetWorkingStatus("Booting phone..."); + + if (Notifier.CurrentInterface == PhoneInterfaces.Lumia_Bootloader) + ((NokiaFlashModel)Notifier.CurrentModel).ContinueBoot(); + + if (Notifier.CurrentInterface != PhoneInterfaces.Lumia_Normal) + await Notifier.WaitForArrival(); + + if (Notifier.CurrentInterface == PhoneInterfaces.Lumia_Bootloader) + ((NokiaFlashModel)Notifier.CurrentModel).ContinueBoot(); + + if (Notifier.CurrentInterface != PhoneInterfaces.Lumia_Normal) + await Notifier.WaitForArrival(); + + if (Notifier.CurrentInterface == PhoneInterfaces.Lumia_Bootloader) + ((NokiaFlashModel)Notifier.CurrentModel).ContinueBoot(); + + if (Notifier.CurrentInterface != PhoneInterfaces.Lumia_Normal) + { + ExitFailure("Failed to relock phone", "Your phone is half relocked. You may need to reflash a stock ROM"); + LogFile.EndAction("RelockBootloader"); + return; + } + + LogFile.Log("Bootloader restored!", LogType.FileAndConsole); + ExitSuccess("Bootloader restored successfully!"); + } + } + catch (Exception Ex) + { + LogFile.LogException(Ex); + ExitFailure("Error: " + Ex.Message, null); + } + finally + { + LogFile.EndAction("RelockBootloader"); + } + } + + // Magic! + // Platform Secure Boot Hack for Spec A devices + // + // Assumes phone in Flash mode + // in Qualcomm Dload + // in Qualcomm Flash + // + internal static async Task LumiaV1UnlockFirmware(PhoneNotifierViewModel Notifier, string FFUPath, string LoadersPath, string SBL3Path, string SupportedFFUPath, SetWorkingStatus SetWorkingStatus = null, UpdateWorkingStatus UpdateWorkingStatus = null, ExitSuccess ExitSuccess = null, ExitFailure ExitFailure = null) + { + LogFile.BeginAction("UnlockBootloader"); + + if (SetWorkingStatus == null) SetWorkingStatus = (m, s, v, a, st) => { }; + if (UpdateWorkingStatus == null) UpdateWorkingStatus = (m, s, v, st) => { }; + if (ExitSuccess == null) ExitSuccess = (m, s) => { }; + if (ExitFailure == null) ExitFailure = (m, s) => { }; + + try + { + LogFile.Log("Assembling data for unlock", LogType.FileAndConsole); + SetWorkingStatus("Assembling data for unlock", null, null); + + if ((FFUPath == null) || (FFUPath.Length == 0)) + throw new ArgumentNullException("FFU path is missing"); + + if ((LoadersPath == null) || (LoadersPath.Length == 0)) + throw new Exception("Error: Path for Loaders is mandatory."); + + bool DumpPartitions = false; + string DumpFilePrefix = Environment.ExpandEnvironmentVariables("%ALLUSERSPROFILE%\\WPInternals\\") + DateTime.Now.ToString("yyyy-MM-dd hh.mm.ss") + " - "; + bool IsBootLoaderUnlocked = false; + + FFU FFU = null; + try + { + FFU = new FFU(FFUPath); + } + catch (Exception Ex) + { + LogFile.LogException(Ex); + throw new Exception("Error: Parsing FFU-file failed."); + } + + if (Notifier.CurrentModel is NokiaFlashModel) + { + FlashVersion FlashVersion = ((NokiaFlashModel)Notifier.CurrentModel).GetFlashVersion(); + if (FlashVersion == null) + throw new Exception("Error: The version of the Flash Application on the phone could not be determined."); + if ((FlashVersion.ApplicationMajor < 1) || ((FlashVersion.ApplicationMajor == 1) && (FlashVersion.ApplicationMinor < 28))) + throw new Exception("Error: The version of the Flash Application on the phone is too old. Update your phone using Windows Updates or flash a newer ROM to your phone. Then try again."); + + UefiSecurityStatusResponse SecurityStatus = ((NokiaFlashModel)Notifier.CurrentModel).ReadSecurityStatus(); + IsBootLoaderUnlocked = (SecurityStatus.AuthenticationStatus || SecurityStatus.RdcStatus || !SecurityStatus.SecureFfuEfuseStatus); + } + + FFU SupportedFFU = null; + if (App.PatchEngine.PatchDefinitions.Where(p => p.Name == "SecureBootHack-V1.1-EFIESP").First().TargetVersions.Any(v => v.Description == FFU.GetOSVersion())) + { + SupportedFFU = FFU; + } + else if (SupportedFFUPath == null) + { + throw new ArgumentNullException("Donor-FFU with supported OS version was not provided"); + } + else + { + try + { + SupportedFFU = new FFU(SupportedFFUPath); + } + catch (Exception Ex) + { + LogFile.LogException(Ex); + throw new Exception("Error: Parsing Supported FFU-file failed."); + } + if (!App.PatchEngine.PatchDefinitions.Where(p => p.Name == "SecureBootHack-V1.1-EFIESP").First().TargetVersions.Any(v => v.Description == SupportedFFU.GetOSVersion())) + throw new ArgumentNullException("Donor-FFU with supported OS version was not provided"); + } + + if (DumpPartitions) + { + try + { + File.WriteAllBytes(DumpFilePrefix + "01.bin", FFU.GetSectors(0, 34)); // Original GPT + } + catch (Exception Ex) + { + LogFile.LogException(Ex); + throw new Exception("Error: Writing binary for logging failed."); + } + } + + + GPT NewGPT = null; + if (Notifier.CurrentModel is NokiaFlashModel) + { + ((NokiaFlashModel)Notifier.CurrentModel).ResetPhone(); + + if (Notifier.CurrentInterface != PhoneInterfaces.Lumia_Bootloader) + await Notifier.WaitForArrival(); + + if (Notifier.CurrentInterface != PhoneInterfaces.Lumia_Bootloader) + throw new WPinternalsException("Phone is in an unexpected mode."); + + NewGPT = ((NokiaFlashModel)Notifier.CurrentModel).ReadGPT(); + + await SwitchModeViewModel.SwitchTo(Notifier, PhoneInterfaces.Lumia_Flash); + + if (Notifier.CurrentInterface != PhoneInterfaces.Lumia_Flash) + await Notifier.WaitForArrival(); + + if (Notifier.CurrentInterface != PhoneInterfaces.Lumia_Flash) + throw new WPinternalsException("Phone is in an unexpected mode."); + } + else + { + NewGPT = FFU.GPT; + } + + // Make sure all partitions are in range of the emergency flasher. + NewGPT.RestoreBackupPartitions(); + + byte[] GPT = null; + try + { + GPT = NewGPT.InsertHack(); + } + catch (Exception Ex) + { + LogFile.LogException(Ex); + throw new Exception("Error: Parsing partitions failed."); + } + + if (SBL3Path != null) + { + Partition IsUnlockedFlag = NewGPT.GetPartition("IS_UNLOCKED_SBL3"); + if (IsUnlockedFlag == null) + { + IsUnlockedFlag = new Partition(); + IsUnlockedFlag.Name = "IS_UNLOCKED_SBL3"; + IsUnlockedFlag.Attributes = 0; + IsUnlockedFlag.PartitionGuid = Guid.NewGuid(); + IsUnlockedFlag.PartitionTypeGuid = Guid.NewGuid(); + IsUnlockedFlag.FirstSector = 0x40; + IsUnlockedFlag.LastSector = 0x40; + NewGPT.Partitions.Add(IsUnlockedFlag); + GPT = NewGPT.Rebuild(); + } + } + + if (DumpPartitions) + { + try + { + File.WriteAllBytes(DumpFilePrefix + "02.bin", GPT); // Patched GPT + } + catch (Exception Ex) + { + LogFile.LogException(Ex); + throw new Exception("Error: Writing binary for logging failed."); + } + } + + SBL1 SBL1 = null; + try + { + SBL1 = new SBL1(FFU.GetPartition("SBL1")); + } + catch (Exception Ex) + { + LogFile.LogException(Ex); + throw new Exception("Error: Parsing SBL1 failed."); + } + + if (DumpPartitions) + { + try + { + File.WriteAllBytes(DumpFilePrefix + "03.bin", SBL1.Binary); // Original SBL1 + } + catch (Exception Ex) + { + LogFile.LogException(Ex); + throw new Exception("Error: Writing binary for logging failed."); + } + } + + byte[] RootKeyHash = null; + if (Notifier.CurrentInterface == PhoneInterfaces.Qualcomm_Download) + { + QualcommDownload Download = new QualcommDownload((QualcommSerial)Notifier.CurrentModel); + RootKeyHash = Download.GetRKH(); + } + else if (Notifier.CurrentInterface != PhoneInterfaces.Qualcomm_Flash) + { + RootKeyHash = ((NokiaFlashModel)Notifier.CurrentModel).ReadParam("RRKH"); + } + + if (Notifier.CurrentInterface != PhoneInterfaces.Qualcomm_Flash) + { + if (RootKeyHash == null) + { + throw new Exception("Error: Root Key Hash could not be retrieved from the phone."); + } + if (SBL1.RootKeyHash == null) + { + throw new Exception("Error: Root Key Hash could not be retrieved from FFU file."); + } + if (!StructuralComparisons.StructuralEqualityComparer.Equals(RootKeyHash, SBL1.RootKeyHash)) + { + LogFile.Log("Phone: " + Converter.ConvertHexToString(RootKeyHash, "")); + LogFile.Log("SBL1: " + Converter.ConvertHexToString(SBL1.RootKeyHash, "")); + throw new Exception("Error: Root Key Hash from phone and from FFU file do not match!"); + } + } + + SBL2 SBL2Partition = null; + try + { + SBL2Partition = new SBL2(FFU.GetPartition("SBL2")); + } + catch (Exception Ex) + { + LogFile.LogException(Ex); + throw new Exception("Error: Parsing SBL2 failed."); + } + + if (DumpPartitions) + { + try + { + File.WriteAllBytes(DumpFilePrefix + "05.bin", SBL2Partition.Binary); // Original SBL2 + } + catch (Exception Ex) + { + LogFile.LogException(Ex); + throw new Exception("Error: Writing binary for logging failed."); + } + } + + byte[] SBL2; + try + { + SBL2 = SBL2Partition.Patch(); + } + catch (Exception Ex) + { + LogFile.LogException(Ex); + throw new Exception("Error: Patching SBL2 failed."); + } + + if (DumpPartitions) + { + try + { + File.WriteAllBytes(DumpFilePrefix + "06.bin", SBL2Partition.Binary); // Patched SBL2 + } + catch (Exception Ex) + { + LogFile.LogException(Ex); + throw new Exception("Error: Writing binary for logging failed."); + } + } + + byte[] ExtraSector = null; + try + { + byte[] PartitionHeader = new byte[0x0C]; + Buffer.BlockCopy(SBL2Partition.Binary, 0, PartitionHeader, 0, 0x0C); + ExtraSector = SBL1.GenerateExtraSector(PartitionHeader); + } + catch (Exception Ex) + { + LogFile.LogException(Ex); + throw new Exception("Error: Code generation failed."); + } + + if (DumpPartitions) + { + try + { + File.WriteAllBytes(DumpFilePrefix + "04.bin", ExtraSector); // Extra sector + } + catch (Exception Ex) + { + LogFile.LogException(Ex); + throw new Exception("Error: Writing binary for logging failed."); + } + } + + SBL3 SBL3Partition; + SBL3 OriginalSBL3; + + try + { + OriginalSBL3 = new SBL3(FFU.GetPartition("SBL3")); + } + catch (Exception Ex) + { + LogFile.LogException(Ex); + throw new Exception("Error: Parsing SBL3 from FFU failed."); + } + + if (SBL3Path == null) + { + SBL3Partition = OriginalSBL3; + LogFile.Log("Taking SBL3 from FFU"); + } + else + { + SBL3Partition = null; + try + { + SBL3Partition = new SBL3(SBL3Path); + } + catch (Exception Ex) + { + LogFile.LogException(Ex); + throw new Exception("Error: Parsing external SBL3 failed."); + } + + if (SBL3Partition.Binary.Length > OriginalSBL3.Binary.Length) + { + throw new Exception("Error: Selected SBL3 is too large."); + } + LogFile.Log("Taking selected SBL3"); + } + + if (DumpPartitions) + { + try + { + File.WriteAllBytes(DumpFilePrefix + "07.bin", SBL3Partition.Binary); // Original SBL3 + } + catch (Exception Ex) + { + LogFile.LogException(Ex); + throw new Exception("Error: Writing binary for logging failed."); + } + } + + byte[] SBL3; + try + { + SBL3 = SBL3Partition.Patch(); + } + catch (Exception Ex) + { + LogFile.LogException(Ex); + throw new Exception("Error: Patching SBL3 failed."); + } + + if (DumpPartitions) + { + try + { + File.WriteAllBytes(DumpFilePrefix + "08.bin", SBL3Partition.Binary); // Patched SBL3 + } + catch (Exception Ex) + { + LogFile.LogException(Ex); + throw new Exception("Error: Writing binary for logging failed."); + } + } + + UEFI UEFIPartition = null; + try + { + UEFIPartition = new UEFI(FFU.GetPartition("UEFI")); + } + catch (Exception Ex) + { + LogFile.LogException(Ex); + throw new Exception("Error: Parsing UEFI failed."); + } + + if (DumpPartitions) + { + try + { + File.WriteAllBytes(DumpFilePrefix + "09.bin", UEFIPartition.Binary); // Original UEFI + } + catch (Exception Ex) + { + LogFile.LogException(Ex); + throw new Exception("Error: Writing binary for logging failed."); + } + } + + byte[] UEFI; + try + { + UEFI = UEFIPartition.Patch(); + } + catch (Exception Ex) + { + LogFile.LogException(Ex); + throw new Exception("Error: Patching UEFI failed."); + } + + if (DumpPartitions) + { + try + { + File.WriteAllBytes(DumpFilePrefix + "0A.bin", UEFIPartition.Binary); // Patched UEFI + } + catch (Exception Ex) + { + LogFile.LogException(Ex); + throw new Exception("Error: Writing binary for logging failed."); + } + } + + List PossibleLoaders = null; + if (!IsBootLoaderUnlocked || Notifier.CurrentInterface == PhoneInterfaces.Qualcomm_Download) + { + try + { + PossibleLoaders = QualcommLoaders.GetPossibleLoadersForRootKeyHash(LoadersPath, RootKeyHash); + if (PossibleLoaders.Count == 0) + { + throw new Exception("Error: No matching loaders found for RootKeyHash."); + } + } + catch (Exception Ex) + { + LogFile.LogException(Ex); + throw new Exception("Error: Unexpected error during scanning for loaders."); + } + } + + if (IsBootLoaderUnlocked) + // Flash phone in Flash app + { + NokiaFlashModel CurrentModel = (NokiaFlashModel)Notifier.CurrentModel; + LogFile.Log("Start flashing in Custom Flash mode"); + + UInt64 TotalSectorCount = (UInt64)0x21 + 1 + + (UInt64)(SBL2.Length / 0x200) + + (UInt64)(SBL3.Length / 0x200) + + (UInt64)(UEFI.Length / 0x200); + + SetWorkingStatus("Flashing unlocked bootloader (part 1)...", MaxProgressValue: 100, Status: WPinternalsStatus.Flashing); + ProgressUpdater Progress = new ProgressUpdater(TotalSectorCount, (int ProgressPercentage, TimeSpan? TimeSpan) => UpdateWorkingStatus("Flashing unlocked bootloader (part 1)...", CurrentProgressValue: (ulong)ProgressPercentage, Status: WPinternalsStatus.Flashing)); + + LogFile.Log("Flash GPT at 0x" + ((UInt32)0x200).ToString("X8")); + CurrentModel.FlashSectors(1, GPT, 0); + Progress.SetProgress(0x21); + + if (ExtraSector != null) + { + LogFile.Log("Flash EXT at 0x" + ((UInt32)NewGPT.GetPartition("HACK").FirstSector * 0x200).ToString("X8")); + CurrentModel.FlashRawPartition(ExtraSector, "HACK", Progress); + } + + LogFile.Log("Flash SBL2 at 0x" + ((UInt32)NewGPT.GetPartition("SBL2").FirstSector * 0x200).ToString("X8")); + CurrentModel.FlashRawPartition(SBL2, "SBL2", Progress); + LogFile.Log("Flash SBL3 at 0x" + ((UInt32)NewGPT.GetPartition("SBL3").FirstSector * 0x200).ToString("X8")); + CurrentModel.FlashRawPartition(SBL3, "SBL3", Progress); + LogFile.Log("Flash UEFI at 0x" + ((UInt32)NewGPT.GetPartition("UEFI").FirstSector * 0x200).ToString("X8")); + CurrentModel.FlashRawPartition(UEFI, "UEFI", Progress); + + // phone is in flash mode, we can exit + } + else if (Notifier.CurrentInterface == PhoneInterfaces.Lumia_Flash || Notifier.CurrentInterface == PhoneInterfaces.Qualcomm_Download || Notifier.CurrentInterface == PhoneInterfaces.Qualcomm_Flash) + { + // Switch to DLOAD + if (Notifier.CurrentInterface == PhoneInterfaces.Lumia_Flash) + { + SetWorkingStatus("Switching to Emergency Download mode..."); + PerformSoftBrick(Notifier, FFU); + if (Notifier.CurrentInterface != PhoneInterfaces.Qualcomm_Download) + await Notifier.WaitForArrival(); + if (Notifier.CurrentInterface != PhoneInterfaces.Qualcomm_Download) + throw new WPinternalsException("Phone failed to switch to DLOAD."); + } + + // Send loader + if (Notifier.CurrentInterface == PhoneInterfaces.Qualcomm_Download) + { + SetWorkingStatus("Sending loader..."); + SendLoader(Notifier, PossibleLoaders); + if (Notifier.CurrentInterface != PhoneInterfaces.Qualcomm_Flash) + await Notifier.WaitForArrival(); + if (Notifier.CurrentInterface != PhoneInterfaces.Qualcomm_Flash) + throw new WPinternalsException("Phone failed to switch to DLOAD."); + } + + // Flash bootloader + QualcommSerial Serial = (QualcommSerial)Notifier.CurrentModel; + Serial.EncodeCommands = false; + + QualcommFlasher Flasher = new QualcommFlasher(Serial); + + UInt64 TotalSectorCount = (UInt64)1 + 0x21 + 1 + + (UInt64)(SBL2.Length / 0x200) + + (UInt64)(SBL3.Length / 0x200) + + (UInt64)(UEFI.Length / 0x200) + + NewGPT.GetPartition("SBL1").SizeInSectors - 1 + + NewGPT.GetPartition("TZ").SizeInSectors + + NewGPT.GetPartition("RPM").SizeInSectors + + NewGPT.GetPartition("WINSECAPP").SizeInSectors; + + SetWorkingStatus("Flashing unlocked bootloader (part 1)...", MaxProgressValue: 100, Status: WPinternalsStatus.Flashing); + ProgressUpdater Progress = new ProgressUpdater(TotalSectorCount, (int ProgressPercentage, TimeSpan? TimeSpan) => UpdateWorkingStatus("Flashing unlocked bootloader (part 1)...", CurrentProgressValue: (ulong)ProgressPercentage, Status: WPinternalsStatus.Flashing)); + + Flasher.Hello(); + Flasher.SetSecurityMode(0); + Flasher.OpenPartition(0x21); + + LogFile.Log("Partition opened."); + + byte[] MBR = FFU.GetSectors(0, 1); + + if (ExtraSector != null) + { + LogFile.Log("Flash EXT at 0x" + ((UInt32)NewGPT.GetPartition("HACK").FirstSector * 0x200).ToString("X8")); + Flasher.Flash((uint)NewGPT.GetPartition("HACK").FirstSector * 0x200, ExtraSector, Progress); + } + + LogFile.Log("Flash MBR at 0x" + ((UInt32)0).ToString("X8")); + Flasher.Flash(0, MBR, Progress, 0, 0x200); + + LogFile.Log("Flash GPT at 0x" + ((UInt32)0x200).ToString("X8")); + Flasher.Flash(0x200, GPT, Progress, 0, 0x41FF); // Bad bounds-check in the flash-loader prohibits to write the last byte. + + LogFile.Log("Flash SBL2 at 0x" + ((UInt32)NewGPT.GetPartition("SBL2").FirstSector * 0x200).ToString("X8")); + Flasher.Flash((uint)NewGPT.GetPartition("SBL2").FirstSector * 0x200, SBL2, Progress); + LogFile.Log("Flash SBL3 at 0x" + ((UInt32)NewGPT.GetPartition("SBL3").FirstSector * 0x200).ToString("X8")); + Flasher.Flash((uint)NewGPT.GetPartition("SBL3").FirstSector * 0x200, SBL3, Progress); + LogFile.Log("Flash UEFI at 0x" + ((UInt32)NewGPT.GetPartition("UEFI").FirstSector * 0x200).ToString("X8")); + Flasher.Flash((uint)NewGPT.GetPartition("UEFI").FirstSector * 0x200, UEFI, Progress); + + // To minimize risk of brick also flash these partitions: + LogFile.Log("Flash SBL1 at 0x" + ((UInt32)NewGPT.GetPartition("SBL1").FirstSector * 0x200).ToString("X8")); + Flasher.Flash((uint)NewGPT.GetPartition("SBL1").FirstSector * 0x200, FFU.GetPartition("SBL1"), Progress, 0, ((UInt32)NewGPT.GetPartition("SBL1").SizeInSectors - 1) * 0x200); + LogFile.Log("Flash TZ at 0x" + ((UInt32)NewGPT.GetPartition("TZ").FirstSector * 0x200).ToString("X8")); + Flasher.Flash((uint)NewGPT.GetPartition("TZ").FirstSector * 0x200, FFU.GetPartition("TZ"), Progress); + LogFile.Log("Flash RPM at 0x" + ((UInt32)NewGPT.GetPartition("RPM").FirstSector * 0x200).ToString("X8")); + Flasher.Flash((uint)NewGPT.GetPartition("RPM").FirstSector * 0x200, FFU.GetPartition("RPM"), Progress); + + // Workaround for bad bounds-check in flash-loader + UInt32 Length = (UInt32)FFU.GetPartition("WINSECAPP").Length; + UInt32 Start = (UInt32)NewGPT.GetPartition("WINSECAPP").FirstSector * 0x200; + if ((Start + Length) > 0x1E7FE00) + Length = 0x1E7FE00 - Start; + LogFile.Log("Flash WINSECAPP at 0x" + ((UInt32)NewGPT.GetPartition("WINSECAPP").FirstSector * 0x200).ToString("X8")); + Flasher.Flash(Start, FFU.GetPartition("WINSECAPP"), Progress, 0, Length); + + Flasher.ClosePartition(); + + LogFile.Log("Partition closed. Flashing ready. Rebooting."); + + // Reboot phone to Flash app + SetWorkingStatus("Flashing done. Rebooting..."); + Flasher.Reboot(); + + Flasher.CloseSerial(); + + if (Notifier.CurrentInterface != PhoneInterfaces.Lumia_Bootloader && Notifier.CurrentInterface != PhoneInterfaces.Lumia_Flash) + await Notifier.WaitForArrival(); + + if (Notifier.CurrentInterface != PhoneInterfaces.Lumia_Bootloader && Notifier.CurrentInterface != PhoneInterfaces.Lumia_Flash) + throw new WPinternalsException("Phone is in an unexpected mode."); + + if (Notifier.CurrentInterface == PhoneInterfaces.Lumia_Bootloader) + { + await SwitchModeViewModel.SwitchToWithStatus(Notifier, PhoneInterfaces.Lumia_Flash, SetWorkingStatus, UpdateWorkingStatus); + } + + // phone is in flash mode, we can exit + } + else + { + throw new WPinternalsException("Phone is in an unexpected mode."); + } + + await LumiaUnlockBootloaderViewModel.LumiaUnlockUEFI(Notifier, FFUPath, LoadersPath, SupportedFFUPath, SetWorkingStatus, UpdateWorkingStatus, null, (string Message, string SubMessage) => + { + ExitFailure(Message, SubMessage); + LogFile.EndAction("UnlockBootloader"); + return; + }); + + SetWorkingStatus("Booting phone..."); + + if (Notifier.CurrentInterface == PhoneInterfaces.Lumia_Bootloader) + ((NokiaFlashModel)Notifier.CurrentModel).ContinueBoot(); + + if (Notifier.CurrentInterface != PhoneInterfaces.Lumia_Normal) + await Notifier.WaitForArrival(); + + if (Notifier.CurrentInterface == PhoneInterfaces.Lumia_Bootloader) + ((NokiaFlashModel)Notifier.CurrentModel).ContinueBoot(); + + if (Notifier.CurrentInterface != PhoneInterfaces.Lumia_Normal) + await Notifier.WaitForArrival(); + + if (Notifier.CurrentInterface == PhoneInterfaces.Lumia_Bootloader) + ((NokiaFlashModel)Notifier.CurrentModel).ContinueBoot(); + + if (Notifier.CurrentInterface != PhoneInterfaces.Lumia_Normal) + { + ExitFailure("Failed to unlock phone", "Your phone is half unlocked. You may need to reflash a stock ROM"); + LogFile.EndAction("UnlockBootloader"); + return; + } + + LogFile.Log("Bootloader unlocked!", LogType.FileAndConsole); + ExitSuccess("Bootloader unlocked successfully!", null); + } + catch (Exception Ex) + { + LogFile.LogException(Ex); + ExitFailure("Error: " + Ex.Message, null); + } + finally + { + LogFile.EndAction("UnlockBootloader"); + } + } + + internal static byte[] GetGptChunk(NokiaFlashModel FlashModel, UInt32 Size) + { + // This function is also used to generate a dummy chunk to flash for testing. + // The dummy chunk will contain the GPT, so it can be flashed to the first sectors for testing. + byte[] GPTChunk = new byte[Size]; + + PhoneInfo Info = FlashModel.ReadPhoneInfo(ExtendedInfo: false); + FlashAppType OriginalAppType = Info.App; + bool Switch = ((Info.App != FlashAppType.BootManager) && Info.SecureFfuEnabled && !Info.Authenticated && !Info.RdcPresent); + if (Switch) + FlashModel.SwitchToBootManagerContext(); + + byte[] Request = new byte[0x04]; + const string Header = "NOKT"; + + System.Buffer.BlockCopy(System.Text.Encoding.ASCII.GetBytes(Header), 0, Request, 0, Header.Length); + + byte[] Buffer = FlashModel.ExecuteRawMethod(Request); + if ((Buffer == null) || (Buffer.Length < 0x4408)) + throw new InvalidOperationException("Unable to read GPT!"); + + UInt16 Error = (UInt16)((Buffer[6] << 8) + Buffer[7]); + if (Error > 0) + throw new NotSupportedException("ReadGPT: Error 0x" + Error.ToString("X4")); + + System.Buffer.BlockCopy(Buffer, 8, GPTChunk, 0, 0x4400); + + if (Switch) + { + if (OriginalAppType == FlashAppType.FlashApp) + FlashModel.SwitchToFlashAppContext(); + else + FlashModel.SwitchToPhoneInfoAppContext(); + } + + return GPTChunk; + } + + // Magic! + // UEFI Secure Boot Hack for Spec A and Spec B devices + // + // Assumes phone in Flash mode + // + internal static async Task LumiaRelockUEFI(PhoneNotifierViewModel Notifier, string FFUPath = null, bool DoResetFirst = true, SetWorkingStatus SetWorkingStatus = null, UpdateWorkingStatus UpdateWorkingStatus = null, ExitSuccess ExitSuccess = null, ExitFailure ExitFailure = null) + { + if (SetWorkingStatus == null) SetWorkingStatus = (m, s, v, a, st) => { }; + if (UpdateWorkingStatus == null) UpdateWorkingStatus = (m, s, v, st) => { }; + if (ExitSuccess == null) ExitSuccess = (m, s) => { }; + if (ExitFailure == null) ExitFailure = (m, s) => { }; + + LogFile.BeginAction("RelockPhone"); + try + { + GPT GPT = null; + Partition Target = null; + NokiaFlashModel FlashModel = null; + + LogFile.Log("Command: Relock phone", LogType.FileAndConsole); + + if (Notifier.CurrentInterface == null) + await Notifier.WaitForArrival(); + + byte[] EFIESPBackup = null; + + PhoneInfo Info = ((NokiaFlashModel)Notifier.CurrentModel).ReadPhoneInfo(); + bool IsSpecB = Info.FlashAppProtocolVersionMajor >= 2; + bool UndoEFIESPPadding = false; + + if (!IsSpecB) + { + ((NokiaFlashModel)Notifier.CurrentModel).ResetPhone(); + + if (Notifier.CurrentInterface != PhoneInterfaces.Lumia_Bootloader) + await Notifier.WaitForArrival(); + + if (Notifier.CurrentInterface != PhoneInterfaces.Lumia_Bootloader) + throw new WPinternalsException("Phone is in an unexpected mode."); + } + + byte[] GPTChunk = GetGptChunk(((NokiaFlashModel)Notifier.CurrentModel), 0x20000); + GPT = new GPT(GPTChunk); + bool GPTChanged = false; + Partition IsUnlockedPartitionSBL3 = GPT.GetPartition("IS_UNLOCKED_SBL3"); + if (IsUnlockedPartitionSBL3 == null) + { + Partition BackNV = GPT.GetPartition("BACKUP_BS_NV"); + if (BackNV != null) + UndoEFIESPPadding = true; + } + + if (!IsSpecB) + { + await SwitchModeViewModel.SwitchTo(Notifier, PhoneInterfaces.Lumia_Flash); + + if (Notifier.CurrentInterface != PhoneInterfaces.Lumia_Flash) + await Notifier.WaitForArrival(); + + if (Notifier.CurrentInterface != PhoneInterfaces.Lumia_Flash) + throw new WPinternalsException("Phone is in an unexpected mode."); + } + + if (IsSpecB || IsUnlockedPartitionSBL3 != null) + { + try + { + if (Notifier.CurrentInterface != PhoneInterfaces.Lumia_MassStorage) + { + await SwitchModeViewModel.SwitchToWithStatus(Notifier, PhoneInterfaces.Lumia_MassStorage, SetWorkingStatus, UpdateWorkingStatus); + } + + if (!(Notifier.CurrentModel is MassStorage)) + throw new WPinternalsException("Failed to switch to Mass Storage mode"); + + SetWorkingStatus("Patching...", null, null, Status: WPinternalsStatus.Patching); + + // Now relock the phone + MassStorage Storage = (MassStorage)Notifier.CurrentModel; + + App.PatchEngine.TargetPath = Storage.Drive + "\\EFIESP\\"; + App.PatchEngine.Restore("SecureBootHack-V2-EFIESP"); + App.PatchEngine.Restore("SecureBootHack-V1.1-EFIESP"); + App.PatchEngine.Restore("SecureBootHack-V1-EFIESP"); + + App.PatchEngine.TargetPath = Storage.Drive + "\\"; + App.PatchEngine.Restore("SecureBootHack-MainOS"); + App.PatchEngine.Restore("RootAccess-MainOS"); + + // Edit BCD + LogFile.Log("Edit BCD"); + using (Stream BCDFileStream = new System.IO.FileStream(Storage.Drive + @"\EFIESP\efi\Microsoft\Boot\BCD", FileMode.Open, FileAccess.ReadWrite)) + { + using (DiscUtils.Registry.RegistryHive BCDHive = new DiscUtils.Registry.RegistryHive(BCDFileStream)) + { + DiscUtils.BootConfig.Store BCDStore = new DiscUtils.BootConfig.Store(BCDHive.Root); + DiscUtils.BootConfig.BcdObject MobileStartupObject = BCDStore.GetObject(new Guid("{01de5a27-8705-40db-bad6-96fa5187d4a6}")); + DiscUtils.BootConfig.Element NoCodeIntegrityElement = MobileStartupObject.GetElement(0x16000048); + if (NoCodeIntegrityElement != null) + MobileStartupObject.RemoveElement(0x16000048); + + DiscUtils.BootConfig.BcdObject WinLoadObject = BCDStore.GetObject(new Guid("{7619dcc9-fafe-11d9-b411-000476eba25f}")); + NoCodeIntegrityElement = WinLoadObject.GetElement(0x16000048); + if (NoCodeIntegrityElement != null) + WinLoadObject.RemoveElement(0x16000048); + } + } + + Partition EFIESPPartition = GPT.GetPartition("EFIESP"); + byte[] EFIESP = Storage.ReadSectors(EFIESPPartition.FirstSector, EFIESPPartition.SizeInSectors); + UInt32 EfiespSizeInSectors = (UInt32)EFIESPPartition.SizeInSectors; + + // + // (ByteOperations.ReadUInt32(EFIESP, 0x20) == (EfiespSizeInSectors / 2)) was originally present in this check, but it does not seem to be reliable with all cases + // It should be looked as why some phones have half the sector count in gpt, compared to the real partition. + // With that check added, the phone won't get back its original EFIESP partition, on phones like 650s. + // The second check should be more than enough in any case, if we find a header named MSDOS5.0 right in the middle of EFIESP, + // there's not many cases other than us splitting the partition in half to get this here. + // + if ((ByteOperations.ReadAsciiString(EFIESP, (UInt32)(EFIESP.Length / 2) + 3, 8)) == "MSDOS5.0") + { + EFIESPBackup = new byte[EfiespSizeInSectors * 0x200 / 2]; + Buffer.BlockCopy(EFIESP, (Int32)EfiespSizeInSectors * 0x200 / 2, EFIESPBackup, 0, (Int32)EfiespSizeInSectors * 0x200 / 2); + } + + if (ByteOperations.ReadUInt16(EFIESP, 0xE) == LumiaUnlockBootloaderViewModel.LumiaGetFirstEFIESPSectorCount(GPT, new FFU(FFUPath), IsSpecB)) + { + UndoEFIESPPadding = true; + } + + if (Storage.DoesDeviceSupportReboot()) + { + SetWorkingStatus("Rebooting phone..."); + await SwitchModeViewModel.SwitchTo(Notifier, PhoneInterfaces.Lumia_Flash); + } + else + { + LogFile.Log("The phone is currently in Mass Storage Mode", LogType.ConsoleOnly); + LogFile.Log("To continue the relock-sequence, the phone needs to be rebooted", LogType.ConsoleOnly); + LogFile.Log("Keep the phone connected to the PC", LogType.ConsoleOnly); + LogFile.Log("Reboot the phone manually by pressing and holding the power-button of the phone for about 10 seconds until it vibrates", LogType.ConsoleOnly); + LogFile.Log("The relock-sequence will resume automatically", LogType.ConsoleOnly); + LogFile.Log("Waiting for manual reset of the phone...", LogType.ConsoleOnly); + + SetWorkingStatus("You need to manually reset your phone now!", "The phone is currently in Mass Storage Mode. To continue the relock-sequence, the phone needs to be rebooted. Keep the phone connected to the PC. Reboot the phone manually by pressing and holding the power-button of the phone for about 10 seconds until it vibrates. The relock-sequence will resume automatically.", null, false, WPinternalsStatus.WaitingForManualReset); + + await Notifier.WaitForRemoval(); + + SetWorkingStatus("Rebooting phone..."); + + await Notifier.WaitForArrival(); + } + } + catch + { + // If switching to mass storage mode failed, then we just skip that part. This might be a half unlocked phone. + LogFile.Log("Skipping Mass Storage mode", LogType.FileAndConsole); + } + } + + // Phone can also be in normal mode if switching to Mass Storage Mode had failed. + if (Notifier.CurrentInterface == PhoneInterfaces.Lumia_Normal) + await SwitchModeViewModel.SwitchToWithStatus(Notifier, PhoneInterfaces.Lumia_Flash, SetWorkingStatus, UpdateWorkingStatus); + + if ((Notifier.CurrentInterface != PhoneInterfaces.Lumia_Bootloader) && (Notifier.CurrentInterface != PhoneInterfaces.Lumia_Flash)) + await Notifier.WaitForArrival(); + + SetWorkingStatus("Flashing...", "The phone may reboot a couple of times. Just wait for it.", null, Status: WPinternalsStatus.Initializing); + + ((NokiaFlashModel)Notifier.CurrentModel).SwitchToFlashAppContext(); + + List FlashParts = new List(); + + if (UndoEFIESPPadding) + FlashParts = LumiaUnlockBootloaderViewModel.LumiaGenerateUndoEFIESPFlashPayload(GPT, new FFU(FFUPath), IsSpecB); + + FlashPart Part; + + FlashModel = (NokiaFlashModel)Notifier.CurrentModel; + + // Remove IS_UNLOCKED flag in GPT + Partition IsUnlockedPartition = GPT.GetPartition("IS_UNLOCKED"); + if (IsUnlockedPartition != null) + { + GPT.Partitions.Remove(IsUnlockedPartition); + GPTChanged = true; + } + + Partition EfiEspBackupPartition = GPT.GetPartition("BACKUP_EFIESP"); + if (EfiEspBackupPartition != null) + { + // This must be a left over of a half unlocked bootloader + Partition EfiEspPartition = GPT.GetPartition("EFIESP"); + EfiEspBackupPartition.Name = "EFIESP"; + EfiEspBackupPartition.LastSector = EfiEspPartition.LastSector; + EfiEspBackupPartition.PartitionGuid = EfiEspPartition.PartitionGuid; + EfiEspBackupPartition.PartitionTypeGuid = EfiEspPartition.PartitionTypeGuid; + GPT.Partitions.Remove(EfiEspPartition); + GPTChanged = true; + } + + Partition NvBackupPartition = GPT.GetPartition("BACKUP_BS_NV"); + if (NvBackupPartition != null) + { + // This must be a left over of a half unlocked bootloader + Partition NvPartition = GPT.GetPartition("UEFI_BS_NV"); + NvBackupPartition.Name = "UEFI_BS_NV"; + NvBackupPartition.PartitionGuid = NvPartition.PartitionGuid; + NvBackupPartition.PartitionTypeGuid = NvPartition.PartitionTypeGuid; + GPT.Partitions.Remove(NvPartition); + GPTChanged = true; + LogFile.Log("BS Removed"); + } + + if (GPTChanged) + { + GPT.Rebuild(); + Part = new FlashPart(); + Part.StartSector = 0; + Part.Stream = new MemoryStream(GPTChunk); + FlashParts.Add(Part); + } + + if (EFIESPBackup != null) + { + Part = new FlashPart(); + Target = GPT.GetPartition("EFIESP"); + Part.StartSector = (UInt32)Target.FirstSector; + Part.Stream = new MemoryStream(EFIESPBackup); + FlashParts.Add(Part); + } + + // We should only clear NV if there was no backup NV to be restored and the current NV contains the SB unlock. + bool NvCleared = false; + Info = ((NokiaFlashModel)Notifier.CurrentModel).ReadPhoneInfo(); + if ((NvBackupPartition == null) && !Info.UefiSecureBootEnabled) + { + // ClearNV + Part = new FlashPart(); + Target = GPT.GetPartition("UEFI_BS_NV"); + Part.StartSector = (UInt32)Target.FirstSector; + Part.Stream = new MemoryStream(new byte[0x40000]); + FlashParts.Add(Part); + NvCleared = true; + } + + WPinternalsStatus LastStatus = WPinternalsStatus.Undefined; + ulong? MaxProgressValue = null; + await LumiaUnlockBootloaderViewModel.LumiaFlashParts(Notifier, FFUPath, false, false, FlashParts, DoResetFirst, ClearFlashingStatusAtEnd: !NvCleared, + SetWorkingStatus: (m, s, v, a, st) => + { + if (SetWorkingStatus != null) + { + if ((st == WPinternalsStatus.Scanning) || (st == WPinternalsStatus.WaitingForManualReset)) + SetWorkingStatus(m, s, v, a, st); + else if ((LastStatus == WPinternalsStatus.Scanning) || (LastStatus == WPinternalsStatus.WaitingForManualReset) || (LastStatus == WPinternalsStatus.Undefined)) + { + MaxProgressValue = v; + SetWorkingStatus("Flashing...", "The phone may reboot a couple of times. Just wait for it.", v, Status: WPinternalsStatus.Flashing); + } + LastStatus = st; + } + }, + UpdateWorkingStatus: (m, s, v, st) => + { + if (UpdateWorkingStatus != null) + { + if ((st == WPinternalsStatus.Scanning) || (st == WPinternalsStatus.WaitingForManualReset)) + UpdateWorkingStatus(m, s, v, st); + else if ((LastStatus == WPinternalsStatus.Scanning) || (LastStatus == WPinternalsStatus.WaitingForManualReset)) + SetWorkingStatus("Flashing...", "The phone may reboot a couple of times. Just wait for it.", MaxProgressValue, Status: WPinternalsStatus.Flashing); + else + UpdateWorkingStatus("Flashing...", "The phone may reboot a couple of times. Just wait for it.", v, Status: WPinternalsStatus.Flashing); + LastStatus = st; + } + }); + + if (NvBackupPartition != null && IsSpecB) + { + // An old NV backup was restored and it possibly contained the IsFlashing flag. + // Can't clear it immeadiately, so we need another flash. + + SetWorkingStatus("Flashing...", "The phone may reboot a couple of times. Just wait for it.", null, Status: WPinternalsStatus.Flashing); + + // If last flash was a normal flash, with no forced crash at the end (!NvCleared), then we have to wait for device arrival, because it could still be detected as Flash-mode from previous flash. + // When phone was forcably crashed, it can be in emergency mode, or still rebooting. Then also wait for device arrival. + // But it is also possible that it is already in bootmgr mode after being crashed (Lumia 950 / 950XL). In that case don't wait for arrival. + if (!NvCleared || ((Notifier.CurrentInterface != PhoneInterfaces.Lumia_Bootloader) && (Notifier.CurrentInterface != PhoneInterfaces.Lumia_Flash))) + await Notifier.WaitForArrival(); + + if (Notifier.CurrentInterface == PhoneInterfaces.Lumia_Bootloader) + ((NokiaFlashModel)Notifier.CurrentModel).SwitchToFlashAppContext(); + + if (Notifier.CurrentInterface == PhoneInterfaces.Lumia_Flash) + { + await LumiaUnlockBootloaderViewModel.LumiaFlashParts(Notifier, FFUPath, false, false, null, DoResetFirst, ClearFlashingStatusAtEnd: true, ShowProgress: false); + } + } + + LogFile.Log("Phone is relocked", LogType.FileAndConsole); + } + catch (Exception Ex) + { + LogFile.LogException(Ex); + ExitFailure("Error: " + Ex.Message, null); + } + finally + { + LogFile.EndAction("RelockPhone"); + } + } + + // Magic! + // UEFI Secure Boot Hack for Spec A and Spec B devices + // + // Assumes phone in Flash mode + // + internal static async Task LumiaUnlockUEFI(PhoneNotifierViewModel Notifier, string ProfileFFUPath, string EDEPath, string SupportedFFUPath, SetWorkingStatus SetWorkingStatus = null, UpdateWorkingStatus UpdateWorkingStatus = null, ExitSuccess ExitSuccess = null, ExitFailure ExitFailure = null, bool ExperimentalSpecBEFIESPUnlock = false, bool ExperimentalSpecAEFIESPUnlock = true) + { + LogFile.BeginAction("UnlockBootloader"); + NokiaFlashModel FlashModel = (NokiaFlashModel)Notifier.CurrentModel; + + if (SetWorkingStatus == null) SetWorkingStatus = (m, s, v, a, st) => { }; + if (UpdateWorkingStatus == null) UpdateWorkingStatus = (m, s, v, st) => { }; + if (ExitSuccess == null) ExitSuccess = (m, s) => { }; + if (ExitFailure == null) ExitFailure = (m, s) => { }; + + try + { + PhoneInfo Info = FlashModel.ReadPhoneInfo(); + bool IsSpecB = Info.FlashAppProtocolVersionMajor >= 2; + + bool IsBootLoaderSecure = !Info.Authenticated && !Info.RdcPresent && Info.SecureFfuEnabled; + + if (ProfileFFUPath == null) + throw new ArgumentNullException("Profile FFU path is missing"); + + FFU ProfileFFU = new FFU(ProfileFFUPath); + + if (IsBootLoaderSecure) + { + if (!Info.PlatformID.StartsWith(ProfileFFU.PlatformID, StringComparison.OrdinalIgnoreCase)) + throw new ArgumentNullException("Profile FFU has wrong Platform ID for connected phone"); + } + + string Patch = "SecureBootHack-V1.1-EFIESP"; + if (IsSpecB) + Patch = "SecureBootHack-V2-EFIESP"; + + FFU SupportedFFU = null; + if (App.PatchEngine.PatchDefinitions.Where(p => p.Name == Patch).First().TargetVersions.Any(v => v.Description == ProfileFFU.GetOSVersion())) + SupportedFFU = ProfileFFU; + else if (SupportedFFUPath == null) + throw new ArgumentNullException("Donor-FFU with supported OS version was not provided"); + else + { + SupportedFFU = new FFU(SupportedFFUPath); + if (!App.PatchEngine.PatchDefinitions.Where(p => p.Name == Patch).First().TargetVersions.Any(v => v.Description == SupportedFFU.GetOSVersion())) + throw new ArgumentNullException("Donor-FFU with supported OS version was not provided"); + } + + // TODO: Check EDE file + + LogFile.Log("Assembling data for unlock", LogType.FileAndConsole); + SetWorkingStatus("Assembling data for unlock", null, null); + byte[] UnlockedEFIESP = ProfileFFU.GetPartition("EFIESP"); + + LumiaUnlockBootloaderViewModel.LumiaPatchEFIESP(SupportedFFU, UnlockedEFIESP, IsSpecB); + + // Create backup-partition for EFIESP + byte[] GPTChunk = GetGptChunk(FlashModel, (UInt32)ProfileFFU.ChunkSize); + byte[] GPTChunkBackup = new byte[GPTChunk.Length]; + Buffer.BlockCopy(GPTChunk, 0, GPTChunkBackup, 0, GPTChunk.Length); + GPT GPT = new GPT(GPTChunk); + bool GPTChanged = false; + + bool SBL3Eng = GPT.GetPartition("IS_UNLOCKED_SBL3") != null; + + bool ShouldApplyOldEFIESPMethod = true; + if (IsSpecB) + ShouldApplyOldEFIESPMethod = !ExperimentalSpecBEFIESPUnlock; + else + ShouldApplyOldEFIESPMethod = !ExperimentalSpecAEFIESPUnlock; + + if (!IsSpecB && !SBL3Eng) + ShouldApplyOldEFIESPMethod = false; + + List Parts = ShouldApplyOldEFIESPMethod ? new List() : LumiaUnlockBootloaderViewModel.LumiaGenerateEFIESPFlashPayload(UnlockedEFIESP, GPT, ProfileFFU, IsSpecB); + FlashPart Part; + + UInt32 OriginalEfiespSizeInSectors = (UInt32)GPT.GetPartition("EFIESP").SizeInSectors; + UInt32 OriginalEfiespLastSector = (UInt32)GPT.GetPartition("EFIESP").LastSector; + if (ShouldApplyOldEFIESPMethod) + { + Partition BACKUP_EFIESP = GPT.GetPartition("BACKUP_EFIESP"); + Partition EFIESP; + + if (BACKUP_EFIESP == null) + { + BACKUP_EFIESP = GPT.GetPartition("EFIESP"); + Guid OriginalPartitionTypeGuid = BACKUP_EFIESP.PartitionTypeGuid; + Guid OriginalPartitionGuid = BACKUP_EFIESP.PartitionGuid; + BACKUP_EFIESP.Name = "BACKUP_EFIESP"; + BACKUP_EFIESP.LastSector = BACKUP_EFIESP.FirstSector + ((OriginalEfiespSizeInSectors) / 2) - 1; // Original is 0x10000 + BACKUP_EFIESP.PartitionGuid = Guid.NewGuid(); + BACKUP_EFIESP.PartitionTypeGuid = Guid.NewGuid(); + EFIESP = new Partition(); + EFIESP.Name = "EFIESP"; + EFIESP.Attributes = BACKUP_EFIESP.Attributes; + EFIESP.PartitionGuid = OriginalPartitionGuid; + EFIESP.PartitionTypeGuid = OriginalPartitionTypeGuid; + EFIESP.FirstSector = BACKUP_EFIESP.LastSector + 1; + EFIESP.LastSector = EFIESP.FirstSector + ((OriginalEfiespSizeInSectors) / 2) - 1; // Original is 0x10000 + GPT.Partitions.Add(EFIESP); + GPTChanged = true; + } + EFIESP = GPT.GetPartition("EFIESP"); + if ((UInt64)UnlockedEFIESP.Length > (EFIESP.SizeInSectors * 0x200)) + { + byte[] HalfEFIESP = new byte[EFIESP.SizeInSectors * 0x200]; + Buffer.BlockCopy(UnlockedEFIESP, 0, HalfEFIESP, 0, HalfEFIESP.Length); + UnlockedEFIESP = HalfEFIESP; + ByteOperations.WriteUInt32(UnlockedEFIESP, 0x20, (UInt32)EFIESP.SizeInSectors); // Correction of partitionsize + } + + Partition EFIESPPartition = GPT.GetPartition("EFIESP"); + if (EFIESPPartition == null) + throw new WPinternalsException("EFIESP partition not found!"); + + if ((UInt64)UnlockedEFIESP.Length != (EFIESPPartition.SizeInSectors * 0x200)) + throw new WPinternalsException("New EFIESP partition has wrong size. Size = 0x" + UnlockedEFIESP.Length.ToString("X8") + ". Expected size = 0x" + (EFIESPPartition.SizeInSectors * 0x200).ToString("X8")); + + Part = new FlashPart(); + Part.StartSector = (UInt32)EFIESPPartition.FirstSector; // GPT is prepared for 64-bit sector-offset, but flash app isn't. + Part.Stream = new MemoryStream(UnlockedEFIESP); + Parts.Add(Part); + } + + if (IsSpecB) + Parts[0].ProgressText = "Flashing unlocked bootloader (part 1)..."; + else + Parts[0].ProgressText = "Flashing unlocked bootloader (part 2)..."; + + // Now add NV partition + Partition BACKUP_BS_NV = GPT.GetPartition("BACKUP_BS_NV"); + Partition UEFI_BS_NV; + if (BACKUP_BS_NV == null) + { + BACKUP_BS_NV = GPT.GetPartition("UEFI_BS_NV"); + Guid OriginalPartitionTypeGuid = BACKUP_BS_NV.PartitionTypeGuid; + Guid OriginalPartitionGuid = BACKUP_BS_NV.PartitionGuid; + BACKUP_BS_NV.Name = "BACKUP_BS_NV"; + BACKUP_BS_NV.PartitionGuid = Guid.NewGuid(); + BACKUP_BS_NV.PartitionTypeGuid = Guid.NewGuid(); + UEFI_BS_NV = new Partition(); + UEFI_BS_NV.Name = "UEFI_BS_NV"; + UEFI_BS_NV.Attributes = BACKUP_BS_NV.Attributes; + UEFI_BS_NV.PartitionGuid = OriginalPartitionGuid; + UEFI_BS_NV.PartitionTypeGuid = OriginalPartitionTypeGuid; + UEFI_BS_NV.FirstSector = BACKUP_BS_NV.LastSector + 1; + UEFI_BS_NV.LastSector = UEFI_BS_NV.FirstSector + BACKUP_BS_NV.LastSector - BACKUP_BS_NV.FirstSector; + GPT.Partitions.Add(UEFI_BS_NV); + GPTChanged = true; + } + Part = new FlashPart(); + Partition TargetPartition = GPT.GetPartition("UEFI_BS_NV"); + Part.StartSector = (UInt32)TargetPartition.FirstSector; // GPT is prepared for 64-bit sector-offset, but flash app isn't. + string SBRes = IsSpecB ? "WPinternals.SB" : "WPinternals.SBA"; + Part.Stream = new SeekableStream(() => + { + var assembly = System.Reflection.Assembly.GetExecutingAssembly(); + + // Magic! + // The SB(A) resource is a compressed version of a raw NV-variable-partition. + // In this partition the SecureBoot variable is disabled. + // It overwrites the variable in a different NV-partition than where this variable is stored usually. + // This normally leads to endless-loops when the NV-variables are enumerated. + // But the partition contains an extra hack to break out the endless loops. + var stream = assembly.GetManifestResourceStream(SBRes); + + return new DecompressedStream(stream); + }); + Parts.Add(Part); + + if (GPTChanged) + { + GPT.Rebuild(); + Part = new FlashPart(); + Part.StartSector = 0; + Part.Stream = new MemoryStream(GPTChunk); + Parts.Add(Part); + } + + await LumiaUnlockBootloaderViewModel.LumiaFlashParts(Notifier, ProfileFFU.Path, false, false, Parts, true, false, true, true, false, SetWorkingStatus, UpdateWorkingStatus, null, null, EDEPath); + + if ((Notifier.CurrentInterface != PhoneInterfaces.Lumia_Bootloader) && (Notifier.CurrentInterface != PhoneInterfaces.Lumia_Flash)) + await Notifier.WaitForArrival(); + + if ((Notifier.CurrentInterface != PhoneInterfaces.Lumia_Bootloader) && (Notifier.CurrentInterface != PhoneInterfaces.Lumia_Flash)) + throw new WPinternalsException("Error: Phone is in wrong mode"); + + if (!IsSpecB && !SBL3Eng) + { + ((NokiaFlashModel)Notifier.CurrentModel).ResetPhone(); + + LogFile.Log("Bootloader unlocked!", LogType.FileAndConsole); + ExitSuccess("Bootloader unlocked successfully!", null); + + return; + } + + // Not going to retry in a loop because a second attempt will result in gears due to changed BootOrder. + // Just inform user of problem and revert. + // User can try again after revert. + bool IsPhoneInBadMassStorageMode = false; + string ErrorMessage = null; + try + { + await SwitchModeViewModel.SwitchToWithStatus(Notifier, PhoneInterfaces.Lumia_MassStorage, SetWorkingStatus, UpdateWorkingStatus); + } + catch (WPinternalsException Ex) + { + ErrorMessage = "Error: " + Ex.Message; + LogFile.LogException(Ex); + } + catch (Exception Ex) + { + LogFile.LogException(Ex); + } + + if (Notifier.CurrentInterface == PhoneInterfaces.Lumia_BadMassStorage) + { + SetWorkingStatus("You need to manually reset your phone now!", "The phone is currently in Mass Storage Mode, but the driver of the PC failed to start. Unfortunately this happens sometimes. You need to manually reset the phone now. Keep the phone connected to the PC. Reboot the phone manually by pressing and holding the power-button of the phone for about 10 seconds until it vibrates. Windows Phone Internals will automatically start to revert the changes. After the phone is fully booted again, you can retry to unlock the bootloader.", null, false, WPinternalsStatus.WaitingForManualReset); + await Notifier.WaitForArrival(); // Should be detected in Bootmanager mode + if (Notifier.CurrentInterface != PhoneInterfaces.Lumia_MassStorage) + IsPhoneInBadMassStorageMode = true; + } + + if (Notifier.CurrentInterface != PhoneInterfaces.Lumia_MassStorage) + { + // Probably the "BootOrder" prevents to boot to MobileStartup. Mass Storage mode depends on MobileStartup. + // In this case Bootarm boots straight to Winload. But Winload can't handle the change of the EFIESP partition. That will cause a bootloop. + + SetWorkingStatus("Problem detected, rolling back...", ErrorMessage); + await SwitchModeViewModel.SwitchTo(Notifier, PhoneInterfaces.Lumia_Flash); + Parts = new List(); + + // Restore original GPT, which will also reference the original NV. + Part = new FlashPart(); + Part.StartSector = 0; + Part.Stream = new MemoryStream(GPTChunkBackup); + Parts.Add(Part); + + await LumiaUnlockBootloaderViewModel.LumiaFlashParts(Notifier, ProfileFFU.Path, false, false, Parts, true, false, true, true, false, SetWorkingStatus, UpdateWorkingStatus, null, null, EDEPath); + + // An old NV backup was restored and it possibly contained the IsFlashing flag. + // Can't clear it immeadiately, so we need another flash. + if ((Notifier.CurrentInterface != PhoneInterfaces.Lumia_Bootloader) && (Notifier.CurrentInterface != PhoneInterfaces.Lumia_Flash)) + await Notifier.WaitForArrival(); + + if ((Notifier.CurrentInterface == PhoneInterfaces.Lumia_Bootloader) || (Notifier.CurrentInterface == PhoneInterfaces.Lumia_Flash)) + { + await LumiaUnlockBootloaderViewModel.LumiaFlashParts(Notifier, ProfileFFU.Path, false, false, null, true, true, true, true, false, SetWorkingStatus, UpdateWorkingStatus, null, null, EDEPath); + } + + if (IsPhoneInBadMassStorageMode) + ExitFailure("Failed to unlock the bootloader due to misbahaving driver. Wait for phone to boot to Windows and then try again.", "The Mass Storage driver of the PC failed to start. Unfortunately this happens sometimes. After the phone is fully booted again, you can retry to unlock the bootloader."); + else + ExitFailure("Failed to unlock the bootloader", "It is not possible to unlock the bootloader straight after flashing. NOTE: Fully reboot the phone and then properly shutdown the phone, before you can try to unlock again!"); + + return; + } + + SetWorkingStatus("Create backup partition...", null, null); + + MassStorage MassStorage = (MassStorage)Notifier.CurrentModel; + GPTChunk = MassStorage.ReadSectors(0, 0x100); + GPT = new GPT(GPTChunk); + + if (ShouldApplyOldEFIESPMethod) + { + Partition BACKUP_EFIESP = GPT.GetPartition("BACKUP_EFIESP"); + byte[] BackupEFIESP = MassStorage.ReadSectors(BACKUP_EFIESP.FirstSector, BACKUP_EFIESP.SizeInSectors); + + // Copy the backed up unlocked EFIESP for future use + byte[] BackupUnlockedEFIESP = new byte[UnlockedEFIESP.Length]; + Buffer.BlockCopy(BackupEFIESP, 0, BackupUnlockedEFIESP, 0, BackupEFIESP.Length); + + LumiaUnlockBootloaderViewModel.LumiaPatchEFIESP(SupportedFFU, BackupUnlockedEFIESP, IsSpecB); + + SetWorkingStatus("Boot optimization...", null, null); + + App.PatchEngine.TargetPath = MassStorage.Drive + "\\"; + App.PatchEngine.Patch("SecureBootHack-MainOS"); // Don't care about result here. Some phones do not need this. + + LogFile.Log("The phone is currently in Mass Storage Mode", LogType.ConsoleOnly); + LogFile.Log("To continue the unlock-sequence, the phone needs to be rebooted", LogType.ConsoleOnly); + LogFile.Log("Keep the phone connected to the PC", LogType.ConsoleOnly); + LogFile.Log("Reboot the phone manually by pressing and holding the power-button of the phone for about 10 seconds until it vibrates", LogType.ConsoleOnly); + LogFile.Log("The unlock-sequence will resume automatically", LogType.ConsoleOnly); + LogFile.Log("Waiting for manual reset of the phone...", LogType.ConsoleOnly); + + SetWorkingStatus("You need to manually reset your phone now!", "The phone is currently in Mass Storage Mode. To continue the unlock-sequence, the phone needs to be rebooted. Keep the phone connected to the PC. Reboot the phone manually by pressing and holding the power-button of the phone for about 10 seconds until it vibrates. The unlock-sequence will resume automatically.", null, false, WPinternalsStatus.WaitingForManualReset); + + await Notifier.WaitForRemoval(); + + SetWorkingStatus("Rebooting phone..."); + + await Notifier.WaitForArrival(); + if (Notifier.CurrentInterface != PhoneInterfaces.Lumia_Bootloader) + throw new WPinternalsException("Phone is in wrong mode"); + + ((NokiaFlashModel)Notifier.CurrentModel).SwitchToFlashAppContext(); + + // EFIESP is appended at the end of the GPT + // BACKUP_EFIESP is at original location in GPT + Partition EFIESP = GPT.GetPartition("EFIESP"); + UInt32 OriginalEfiespFirstSector = (UInt32)BACKUP_EFIESP.FirstSector; + BACKUP_EFIESP.Name = "EFIESP"; + BACKUP_EFIESP.LastSector = OriginalEfiespLastSector; + BACKUP_EFIESP.PartitionGuid = EFIESP.PartitionGuid; + BACKUP_EFIESP.PartitionTypeGuid = EFIESP.PartitionTypeGuid; + GPT.Partitions.Remove(EFIESP); + + Partition IsUnlockedFlag = GPT.GetPartition("IS_UNLOCKED"); + if (IsUnlockedFlag == null) + { + IsUnlockedFlag = new Partition(); + IsUnlockedFlag.Name = "IS_UNLOCKED"; + IsUnlockedFlag.Attributes = 0; + IsUnlockedFlag.PartitionGuid = Guid.NewGuid(); + IsUnlockedFlag.PartitionTypeGuid = Guid.NewGuid(); + IsUnlockedFlag.FirstSector = 0x40; + IsUnlockedFlag.LastSector = 0x40; + GPT.Partitions.Add(IsUnlockedFlag); + } + + Parts = new List(); + GPT.Rebuild(); + Part = new FlashPart(); + Part.StartSector = 0; + Part.Stream = new MemoryStream(GPTChunk); + Part.ProgressText = "Flashing unlocked bootloader (part 2)..."; + Parts.Add(Part); + Part = new FlashPart(); + Part.StartSector = OriginalEfiespFirstSector; + Part.Stream = new MemoryStream(BackupUnlockedEFIESP); // We must keep the Oiriginal EFIESP, but unlocked, for many reasons + Parts.Add(Part); + Part = new FlashPart(); + Part.StartSector = OriginalEfiespFirstSector + ((OriginalEfiespSizeInSectors) / 2); + Part.Stream = new MemoryStream(BackupEFIESP); + Parts.Add(Part); + + await LumiaUnlockBootloaderViewModel.LumiaFlashParts(Notifier, ProfileFFU.Path, false, false, Parts, true, true, true, true, false, SetWorkingStatus, UpdateWorkingStatus, null, null, EDEPath); + } + else + { + ulong FirstSector = GPT.GetPartition("EFIESP").FirstSector; + ulong SectorCount = LumiaUnlockBootloaderViewModel.LumiaGetFirstEFIESPSectorCount(GPT, ProfileFFU, IsSpecB); + byte[] BackupEFIESPAllocation = MassStorage.ReadSectors(FirstSector, SectorCount); + + // The backed up buffer includes our changed header done previously to have two EFIESPs in a single partition + // If we want to read the original partition we need to revert our changes to the first sector. + UnlockedEFIESP = new byte[GPT.GetPartition("EFIESP").SizeInSectors * 0x200]; + Buffer.BlockCopy(BackupEFIESPAllocation, 0, UnlockedEFIESP, 0, BackupEFIESPAllocation.Length); + ByteOperations.WriteUInt16(UnlockedEFIESP, 0xE, ByteOperations.ReadUInt16(ProfileFFU.GetPartition("EFIESP"), 0xE)); + + LogFile.Log("Unlocking backup partition", LogType.FileAndConsole); + SetWorkingStatus("Unlocking backup partition", null, null); + + LumiaUnlockBootloaderViewModel.LumiaPatchEFIESP(SupportedFFU, UnlockedEFIESP, IsSpecB); + + SetWorkingStatus("Boot optimization...", null, null); + + App.PatchEngine.TargetPath = MassStorage.Drive + "\\"; + App.PatchEngine.Patch("SecureBootHack-MainOS"); // Don't care about result here. Some phones do not need this. + + if (MassStorage.DoesDeviceSupportReboot()) + { + SetWorkingStatus("Rebooting phone..."); + await SwitchModeViewModel.SwitchTo(Notifier, PhoneInterfaces.Lumia_Flash); + } + else + { + LogFile.Log("The phone is currently in Mass Storage Mode", LogType.ConsoleOnly); + LogFile.Log("To continue the unlock-sequence, the phone needs to be rebooted", LogType.ConsoleOnly); + LogFile.Log("Keep the phone connected to the PC", LogType.ConsoleOnly); + LogFile.Log("Reboot the phone manually by pressing and holding the power-button of the phone for about 10 seconds until it vibrates", LogType.ConsoleOnly); + LogFile.Log("The unlock-sequence will resume automatically", LogType.ConsoleOnly); + LogFile.Log("Waiting for manual reset of the phone...", LogType.ConsoleOnly); + + SetWorkingStatus("You need to manually reset your phone now!", "The phone is currently in Mass Storage Mode. To continue the unlock-sequence, the phone needs to be rebooted. Keep the phone connected to the PC. Reboot the phone manually by pressing and holding the power-button of the phone for about 10 seconds until it vibrates. The unlock-sequence will resume automatically.", null, false, WPinternalsStatus.WaitingForManualReset); + + await Notifier.WaitForRemoval(); + + SetWorkingStatus("Rebooting phone..."); + + await Notifier.WaitForArrival(); + if (Notifier.CurrentInterface != PhoneInterfaces.Lumia_Bootloader) + throw new WPinternalsException("Phone is in wrong mode"); + } + ((NokiaFlashModel)Notifier.CurrentModel).SwitchToFlashAppContext(); + + Parts = LumiaUnlockBootloaderViewModel.LumiaGenerateEFIESPFlashPayload(UnlockedEFIESP, GPT, ProfileFFU, IsSpecB); + + if (IsSpecB) + Parts[0].ProgressText = "Flashing unlocked bootloader (part 2)..."; + else + Parts[0].ProgressText = "Flashing unlocked bootloader (part 3)..."; + + await LumiaUnlockBootloaderViewModel.LumiaFlashParts(Notifier, ProfileFFU.Path, false, false, Parts, true, true, true, true, false, SetWorkingStatus, UpdateWorkingStatus, null, null, EDEPath); + + if (!IsSpecB) + ((NokiaFlashModel)Notifier.CurrentModel).ResetPhone(); + } + + LogFile.Log("Bootloader unlocked!", LogType.FileAndConsole); + } + catch (Exception Ex) + { + LogFile.LogException(Ex); + ExitFailure(Ex.Message, Ex is WPinternalsException ? ((WPinternalsException)Ex).SubMessage : null); + } + LogFile.EndAction("UnlockBootloader"); + } + + // Magic! + // This function generates a flashing payload which allows us to write a new modified EFIESP to the phone, without changing the current EFIESP. + // This way we always have a backup copy of the original EFIESP partition, at the beginning of the partition, and our new EFIESP, at the end of the partition! + // The new EFIESP partition is also usable instantly without going to mass storage mode and attempt a partition place swap. + // This hack is usable on Spec A devices unlocked, engineering phones, and Spec B phones with the Custom flash exploit. + // Sector alignment and data length are ensured for the Custom flash exploit + // This hack doesn't require us to modify the GPT of the device at all, the new EFIESP is written around the middle of the old one, + // while keeping the first half of the partition intact, except the very first chunk + private static List LumiaGenerateEFIESPFlashPayload(byte[] NewEFIESP, GPT DeviceGPT, FFU DeviceFFU, bool IsSpecB) + { + uint SectorSize = 512; + + Partition EFIESP = DeviceGPT.GetPartition("EFIESP"); + UInt16 ReservedOGSectors = ByteOperations.ReadUInt16(DeviceFFU.GetPartition("EFIESP"), 0xE); + + UInt16 ReservedSectors = LumiaGetFirstEFIESPSectorCount(DeviceGPT, DeviceFFU, IsSpecB); + Int32 EFIESPFirstPartSize = IsSpecB ? DeviceFFU.ChunkSize : (int)SectorSize * ReservedOGSectors; + + byte[] FirstSector = DeviceFFU.GetPartition("EFIESP").Take(EFIESPFirstPartSize).ToArray(); + ByteOperations.WriteUInt16(FirstSector, 0xE, ReservedSectors); + + byte[] SecondEFIESP = NewEFIESP.Skip((int)SectorSize * ReservedOGSectors).Take((int)(NewEFIESP.Length - ReservedSectors * SectorSize)).ToArray(); + + List Parts = new List(); + + FlashPart Part = new FlashPart(); + Part.StartSector = (uint)EFIESP.FirstSector; + Part.Stream = new MemoryStream(FirstSector); + Parts.Add(Part); + + Part = new FlashPart(); + Part.StartSector = (uint)(EFIESP.FirstSector + ReservedSectors); + Part.Stream = new MemoryStream(SecondEFIESP); + Parts.Add(Part); + + return Parts; + } + + // Magic! + // This function generates a flashing payload which allows us to get back the original device EFIESP without ever going to mass storage mode. + private static List LumiaGenerateUndoEFIESPFlashPayload(GPT DeviceGPT, FFU DeviceFFU, bool IsSpecB) + { + uint SectorSize = 512; + + Partition EFIESP = DeviceGPT.GetPartition("EFIESP"); + UInt16 ReservedOGSectors = ByteOperations.ReadUInt16(DeviceFFU.GetPartition("EFIESP"), 0xE); + + UInt16 ReservedSectors = LumiaGetFirstEFIESPSectorCount(DeviceGPT, DeviceFFU, IsSpecB); + Int32 EFIESPFirstPartSize = IsSpecB ? DeviceFFU.ChunkSize : (int)SectorSize * ReservedOGSectors; + + byte[] FirstSector = DeviceFFU.GetPartition("EFIESP").Take(EFIESPFirstPartSize).ToArray(); + + List Parts = new List(); + + FlashPart Part = new FlashPart(); + Part.StartSector = (uint)EFIESP.FirstSector; + Part.Stream = new MemoryStream(FirstSector); + Parts.Add(Part); + + return Parts; + } + + // Magic! + // This function gets the first sector of the new EFIESP location without ever going to mass storage mode. + private static UInt16 LumiaGetFirstEFIESPSectorCount(GPT DeviceGPT, FFU DeviceFFU, bool IsSpecB) + { + uint SectorSize = 512; + Partition EFIESP = DeviceGPT.GetPartition("EFIESP"); + UInt16 ReservedOGSectors = ByteOperations.ReadUInt16(DeviceFFU.GetPartition("EFIESP"), 0xE); + + UInt64 numberofsectors = EFIESP.SizeInSectors; + UInt64 halfnumberofsectors = numberofsectors / 2; + UInt64 allocatednumberofsectors = halfnumberofsectors - ReservedOGSectors + 1; + + UInt16 ReservedSectors = 0xFFFF; + + if (allocatednumberofsectors < ReservedSectors) + { + UInt64 totalnumberofadditionalsectors = ReservedSectors - allocatednumberofsectors; + ReservedSectors -= (ushort)totalnumberofadditionalsectors; + } + + if (IsSpecB && (ReservedSectors % (DeviceFFU.ChunkSize / SectorSize) != 0)) + { + ReservedSectors -= (ushort)(ReservedSectors % (DeviceFFU.ChunkSize / SectorSize)); + } + + return ReservedSectors; + } + + private static async Task LumiaFlashParts(PhoneNotifierViewModel Notifier, string FFUPath, bool PerformFullFlashFirst, bool SkipWrite, List Parts, bool DoResetFirst = true, bool ClearFlashingStatusAtEnd = true, bool CheckSectorAlignment = true, bool ShowProgress = true, bool Experimental = false, SetWorkingStatus SetWorkingStatus = null, UpdateWorkingStatus UpdateWorkingStatus = null, ExitSuccess ExitSuccess = null, ExitFailure ExitFailure = null, string EDEPath = null) + { + PhoneInfo Info = ((NokiaFlashModel)Notifier.CurrentModel).ReadPhoneInfo(); + bool IsSpecA = Info.FlashAppProtocolVersionMajor < 2; + + if (IsSpecA) + LumiaV1FlashParts(Notifier, Parts, SetWorkingStatus, UpdateWorkingStatus); + else + await LumiaV2UnlockBootViewModel.LumiaV2CustomFlash(Notifier, FFUPath, PerformFullFlashFirst, SkipWrite, Parts, DoResetFirst, ClearFlashingStatusAtEnd, CheckSectorAlignment, ShowProgress, Experimental, SetWorkingStatus, UpdateWorkingStatus, ExitSuccess, ExitFailure, EDEPath); + } + + private static void LumiaV1FlashParts(PhoneNotifierViewModel Notifier, List FlashParts, SetWorkingStatus SetWorkingStatus = null, UpdateWorkingStatus UpdateWorkingStatus = null) + { + SetWorkingStatus("Initializing flash...", null, 100, Status: WPinternalsStatus.Initializing); + + NokiaFlashModel FlashModel = (NokiaFlashModel)Notifier.CurrentModel; + + UInt64 InputStreamLength = 0; + UInt64 totalwritten = 0; + int ProgressPercentage = 0; + + foreach (FlashPart Part in FlashParts) + InputStreamLength += (ulong)Part.Stream.Length; + + foreach (FlashPart Part in FlashParts) + { + Stream InputStream = new DecompressedStream(Part.Stream); + + if (InputStream != null) + { + using (InputStream) + { + const int FlashBufferSize = 0x200000; // Flash 8 GB phone -> buffersize 0x200000 = 11:45 min, buffersize 0x20000 = 12:30 min + byte[] FlashBuffer = new byte[FlashBufferSize]; + int BytesRead; + UInt64 i = 0; + do + { + BytesRead = InputStream.Read(FlashBuffer, 0, FlashBufferSize); + + byte[] FlashBufferFinalSize; + if (BytesRead > 0) + { + if (BytesRead == FlashBufferSize) + FlashBufferFinalSize = FlashBuffer; + else + { + FlashBufferFinalSize = new byte[BytesRead]; + Buffer.BlockCopy(FlashBuffer, 0, FlashBufferFinalSize, 0, BytesRead); + } + + FlashModel.FlashSectors((UInt32)(Part.StartSector + (i / 0x200)), FlashBufferFinalSize, ProgressPercentage); + } + + UpdateWorkingStatus(Part.ProgressText, null, (uint)ProgressPercentage, WPinternalsStatus.Flashing); + totalwritten += (UInt64)FlashBuffer.Length / 0x200; + ProgressPercentage = (int)((double)totalwritten / (UInt64)(InputStreamLength / 0x200) * 100); + + i += FlashBufferSize; + } + while (BytesRead == FlashBufferSize); + } + } + } + + UpdateWorkingStatus(null, null, 100, WPinternalsStatus.Flashing); + } + + private static void LumiaPatchEFIESP(FFU SupportedFFU, byte[] EFIESPPartition, bool SpecB) + { + using (DiscUtils.Fat.FatFileSystem EFIESPFileSystem = new DiscUtils.Fat.FatFileSystem(new MemoryStream(EFIESPPartition))) + { + App.PatchEngine.TargetImage = EFIESPFileSystem; + + string PatchDefinition = "SecureBootHack-V1.1-EFIESP"; + if (SpecB) + PatchDefinition = "SecureBootHack-V2-EFIESP"; + + bool PatchResult = App.PatchEngine.Patch(PatchDefinition); + if (!PatchResult) + { + LogFile.Log("Donor-FFU: " + SupportedFFU.Path); + byte[] SupportedEFIESP = SupportedFFU.GetPartition("EFIESP"); + + using (DiscUtils.Fat.FatFileSystem SupportedEFIESPFileSystem = new DiscUtils.Fat.FatFileSystem(new MemoryStream(SupportedEFIESP))) + using (DiscUtils.SparseStream SupportedMobileStartupStream = SupportedEFIESPFileSystem.OpenFile(@"\Windows\System32\Boot\mobilestartup.efi", FileMode.Open)) + using (MemoryStream SupportedMobileStartupMemStream = new MemoryStream()) + using (Stream MobileStartupStream = EFIESPFileSystem.OpenFile(@"Windows\System32\Boot\mobilestartup.efi", FileMode.Create, FileAccess.Write)) + { + SupportedMobileStartupStream.CopyTo(SupportedMobileStartupMemStream); + byte[] SupportedMobileStartup = SupportedMobileStartupMemStream.ToArray(); + + // Save supported mobilestartup.efi + LogFile.Log("Taking mobilestartup.efi from donor-FFU"); + MobileStartupStream.Write(SupportedMobileStartup, 0, SupportedMobileStartup.Length); + } + + PatchResult = App.PatchEngine.Patch(PatchDefinition); + if (!PatchResult) + throw new WPinternalsException("Failed to patch bootloader"); + } + + LogFile.Log("Edit BCD"); + using (Stream BCDFileStream = EFIESPFileSystem.OpenFile(@"efi\Microsoft\Boot\BCD", FileMode.Open, FileAccess.ReadWrite)) + { + using (DiscUtils.Registry.RegistryHive BCDHive = new DiscUtils.Registry.RegistryHive(BCDFileStream)) + { + DiscUtils.BootConfig.Store BCDStore = new DiscUtils.BootConfig.Store(BCDHive.Root); + DiscUtils.BootConfig.BcdObject MobileStartupObject = BCDStore.GetObject(new Guid("{01de5a27-8705-40db-bad6-96fa5187d4a6}")); + DiscUtils.BootConfig.Element NoCodeIntegrityElement = MobileStartupObject.GetElement(0x16000048); + if (NoCodeIntegrityElement != null) + NoCodeIntegrityElement.Value = DiscUtils.BootConfig.ElementValue.ForBoolean(true); + else + MobileStartupObject.AddElement(0x16000048, DiscUtils.BootConfig.ElementValue.ForBoolean(true)); + + DiscUtils.BootConfig.BcdObject WinLoadObject = BCDStore.GetObject(new Guid("{7619dcc9-fafe-11d9-b411-000476eba25f}")); + NoCodeIntegrityElement = WinLoadObject.GetElement(0x16000048); + if (NoCodeIntegrityElement != null) + NoCodeIntegrityElement.Value = DiscUtils.BootConfig.ElementValue.ForBoolean(true); + else + WinLoadObject.AddElement(0x16000048, DiscUtils.BootConfig.ElementValue.ForBoolean(true)); + } + } + } + } + } +} diff --git a/ViewModels/LumiaUnlockRootViewModel.cs b/ViewModels/LumiaUnlockRootViewModel.cs index 64989c2..c832de3 100644 --- a/ViewModels/LumiaUnlockRootViewModel.cs +++ b/ViewModels/LumiaUnlockRootViewModel.cs @@ -75,7 +75,10 @@ namespace WPinternals bool HasNewBootloader = HasNewBootloaderFromMassStorage(); string EFIESPPath = HasNewBootloader ? null : ((MassStorage)PhoneNotifier.CurrentModel).Drive + @"\EFIESP\"; string MainOSPath = ((MassStorage)PhoneNotifier.CurrentModel).Drive + @"\"; - StartPatch(EFIESPPath, MainOSPath, HasNewBootloader); + + bool HasV11Patches = HasV11PatchesFromMassStorage(); + + StartPatch(EFIESPPath, MainOSPath, HasNewBootloader, HasV11Patches); } catch (Exception Ex) { @@ -88,12 +91,12 @@ namespace WPinternals internal void DoUnlockImage(string EFIESPMountPoint, string MainOSMountPoint) { - StartPatch(EFIESPMountPoint, MainOSMountPoint, false); // Unlock image is only supported for Lumia's with bootloader Spec A. Due to complexity of Spec B bootloader hack, it cannot be applied on a mounted image. + StartPatch(EFIESPMountPoint, MainOSMountPoint, false, false); // Unlock image is only supported for Lumia's with bootloader Spec A. Due to complexity of Spec B bootloader hack, it cannot be applied on a mounted image. } // Magic! // Apply patches for Root Access - private void StartPatch(string EFIESP, string MainOS, bool HasNewBootloader) + private void StartPatch(string EFIESP, string MainOS, bool HasNewBootloader, bool HasV11Patches) { IsSwitchingInterface = false; new Thread(() => @@ -105,7 +108,7 @@ namespace WPinternals bool Result = false; - if (EFIESP != null) + if (EFIESP != null && !HasV11Patches) { if (DoUnlock) ActivateSubContext(new BusyViewModel("Enable Root Access on EFIESP...")); @@ -239,5 +242,18 @@ namespace WPinternals Phone.CloseVolume(); return Result; } + + private bool HasV11PatchesFromMassStorage() + { + bool Result = false; + MassStorage Phone = (MassStorage)PhoneNotifier.CurrentModel; + Phone.OpenVolume(false); + byte[] GPTBuffer = Phone.ReadSectors(1, 33); + GPT GPT = new WPinternals.GPT(GPTBuffer); + Partition Partition = GPT.GetPartition("BACKUP_BS_NV"); + Result = Partition != null; + Phone.CloseVolume(); + return Result; + } } } diff --git a/ViewModels/LumiaV2UnlockBootViewModel.cs b/ViewModels/LumiaV2UnlockBootViewModel.cs index 72acd9e..68ad50d 100644 --- a/ViewModels/LumiaV2UnlockBootViewModel.cs +++ b/ViewModels/LumiaV2UnlockBootViewModel.cs @@ -98,44 +98,6 @@ namespace WPinternals } } - internal static byte[] GetGptChunk(NokiaFlashModel FlashModel, UInt32 Size) - { - // This function is also used to generate a dummy chunk to flash for testing. - // The dummy chunk will contain the GPT, so it can be flashed to the first sectors for testing. - byte[] GPTChunk = new byte[Size]; - - PhoneInfo Info = FlashModel.ReadPhoneInfo(ExtendedInfo: false); - FlashAppType OriginalAppType = Info.App; - bool Switch = ((Info.App != FlashAppType.BootManager) && Info.SecureFfuEnabled && !Info.Authenticated && !Info.RdcPresent); - if (Switch) - FlashModel.SwitchToBootManagerContext(); - - byte[] Request = new byte[0x04]; - const string Header = "NOKT"; - - System.Buffer.BlockCopy(System.Text.Encoding.ASCII.GetBytes(Header), 0, Request, 0, Header.Length); - - byte[] Buffer = FlashModel.ExecuteRawMethod(Request); - if ((Buffer == null) || (Buffer.Length < 0x4408)) - throw new InvalidOperationException("Unable to read GPT!"); - - UInt16 Error = (UInt16)((Buffer[6] << 8) + Buffer[7]); - if (Error > 0) - throw new NotSupportedException("ReadGPT: Error 0x" + Error.ToString("X4")); - - System.Buffer.BlockCopy(Buffer, 8, GPTChunk, 0, 0x4400); - - if (Switch) - { - if (OriginalAppType == FlashAppType.FlashApp) - FlashModel.SwitchToFlashAppContext(); - else - FlashModel.SwitchToPhoneInfoAppContext(); - } - - return GPTChunk; - } - internal static async Task LumiaV2EnableTestSigning(System.Threading.SynchronizationContext UIContext, string FFUPath, bool DoResetFirst = true) { LogFile.BeginAction("EnableTestSigning"); @@ -151,7 +113,7 @@ namespace WPinternals // Use GetGptChunk() here instead of ReadGPT(), because ReadGPT() skips the first sector. // We need the fist sector if we want to write back the GPT. - byte[] GPTChunk = GetGptChunk(FlashModel, 0x20000); + byte[] GPTChunk = LumiaUnlockBootloaderViewModel.GetGptChunk(FlashModel, 0x20000); GPT GPT = new GPT(GPTChunk); bool GPTChanged = false; @@ -264,260 +226,6 @@ namespace WPinternals return Drive; } - internal static async Task LumiaV2RelockPhone(PhoneNotifierViewModel Notifier, string FFUPath = null, bool DoResetFirst = true, SetWorkingStatus SetWorkingStatus = null, UpdateWorkingStatus UpdateWorkingStatus = null, ExitSuccess ExitSuccess = null, ExitFailure ExitFailure = null) - { - if (SetWorkingStatus == null) SetWorkingStatus = (m, s, v, a, st) => { }; - if (UpdateWorkingStatus == null) UpdateWorkingStatus = (m, s, v, st) => { }; - if (ExitSuccess == null) ExitSuccess = (m, s) => { }; - if (ExitFailure == null) ExitFailure = (m, s) => { }; - - LogFile.BeginAction("RelockPhone"); - try - { - GPT GPT = null; - Partition Target = null; - NokiaFlashModel FlashModel = null; - - LogFile.Log("Command: Relock phone", LogType.FileAndConsole); - - if (Notifier.CurrentInterface == null) - await Notifier.WaitForArrival(); - - byte[] EFIESPBackup = null; - - try - { - if (Notifier.CurrentInterface != PhoneInterfaces.Lumia_MassStorage) - { - await SwitchModeViewModel.SwitchToWithStatus(Notifier, PhoneInterfaces.Lumia_MassStorage, SetWorkingStatus, UpdateWorkingStatus); - } - - if (!(Notifier.CurrentModel is MassStorage)) - throw new WPinternalsException("Failed to switch to Mass Storage mode"); - - SetWorkingStatus("Patching...", null, null, Status: WPinternalsStatus.Patching); - - // Now relock the phone - MassStorage Storage = (MassStorage)Notifier.CurrentModel; - - App.PatchEngine.TargetPath = Storage.Drive + "\\EFIESP\\"; - App.PatchEngine.Restore("SecureBootHack-V2-EFIESP"); - - App.PatchEngine.TargetPath = Storage.Drive + "\\"; - App.PatchEngine.Restore("SecureBootHack-MainOS"); - App.PatchEngine.Restore("RootAccess-MainOS"); - - // Edit BCD - LogFile.Log("Edit BCD"); - using (Stream BCDFileStream = new System.IO.FileStream(Storage.Drive + @"\EFIESP\efi\Microsoft\Boot\BCD", FileMode.Open, FileAccess.ReadWrite)) - { - using (DiscUtils.Registry.RegistryHive BCDHive = new DiscUtils.Registry.RegistryHive(BCDFileStream)) - { - DiscUtils.BootConfig.Store BCDStore = new DiscUtils.BootConfig.Store(BCDHive.Root); - DiscUtils.BootConfig.BcdObject MobileStartupObject = BCDStore.GetObject(new Guid("{01de5a27-8705-40db-bad6-96fa5187d4a6}")); - DiscUtils.BootConfig.Element NoCodeIntegrityElement = MobileStartupObject.GetElement(0x16000048); - if (NoCodeIntegrityElement != null) - MobileStartupObject.RemoveElement(0x16000048); - - DiscUtils.BootConfig.BcdObject WinLoadObject = BCDStore.GetObject(new Guid("{7619dcc9-fafe-11d9-b411-000476eba25f}")); - NoCodeIntegrityElement = WinLoadObject.GetElement(0x16000048); - if (NoCodeIntegrityElement != null) - WinLoadObject.RemoveElement(0x16000048); - } - } - - byte[] GPTBuffer = Storage.ReadSectors(0, 0x22); - GPT = new GPT(GPTBuffer); - Partition EFIESPPartition = GPT.GetPartition("EFIESP"); - byte[] EFIESP = Storage.ReadSectors(EFIESPPartition.FirstSector, EFIESPPartition.SizeInSectors); - UInt32 EfiespSizeInSectors = (UInt32)EFIESPPartition.SizeInSectors; - - // - // (ByteOperations.ReadUInt32(EFIESP, 0x20) == (EfiespSizeInSectors / 2)) was originally present in this check, but it does not seem to be reliable with all cases - // It should be looked as why some phones have half the sector count in gpt, compared to the real partition. - // With that check added, the phone won't get back its original EFIESP partition, on phones like 650s. - // The second check should be more than enough in any case, if we find a header named MSDOS5.0 right in the middle of EFIESP, - // there's not many cases other than us splitting the partition in half to get this here. - // - if ((ByteOperations.ReadAsciiString(EFIESP, (UInt32)(EFIESP.Length / 2) + 3, 8)) == "MSDOS5.0") - { - EFIESPBackup = new byte[EfiespSizeInSectors * 0x200 / 2]; - Buffer.BlockCopy(EFIESP, (Int32)EfiespSizeInSectors * 0x200 / 2, EFIESPBackup, 0, (Int32)EfiespSizeInSectors * 0x200 / 2); - } - - LogFile.Log("The phone is currently in Mass Storage Mode", LogType.ConsoleOnly); - LogFile.Log("To continue the relock-sequence, the phone needs to be rebooted", LogType.ConsoleOnly); - LogFile.Log("Keep the phone connected to the PC", LogType.ConsoleOnly); - LogFile.Log("Reboot the phone manually by pressing and holding the power-button of the phone for about 10 seconds until it vibrates", LogType.ConsoleOnly); - LogFile.Log("The relock-sequence will resume automatically", LogType.ConsoleOnly); - LogFile.Log("Waiting for manual reset of the phone...", LogType.ConsoleOnly); - - SetWorkingStatus("You need to manually reset your phone now!", "The phone is currently in Mass Storage Mode. To continue the relock-sequence, the phone needs to be rebooted. Keep the phone connected to the PC. Reboot the phone manually by pressing and holding the power-button of the phone for about 10 seconds until it vibrates. The relock-sequence will resume automatically.", null, false, WPinternalsStatus.WaitingForManualReset); - - await Notifier.WaitForRemoval(); - - SetWorkingStatus("Rebooting phone..."); - - await Notifier.WaitForArrival(); - } - catch - { - // If switching to mass storage mode failed, then we just skip that part. This might be a half unlocked phone. - LogFile.Log("Skipping Mass Storage mode", LogType.FileAndConsole); - } - - // Phone can also be in normal mode if switching to Mass Storage Mode had failed. - if (Notifier.CurrentInterface == PhoneInterfaces.Lumia_Normal) - await SwitchModeViewModel.SwitchToWithStatus(Notifier, PhoneInterfaces.Lumia_Flash, SetWorkingStatus, UpdateWorkingStatus); - - if ((Notifier.CurrentInterface != PhoneInterfaces.Lumia_Bootloader) && (Notifier.CurrentInterface != PhoneInterfaces.Lumia_Flash)) - await Notifier.WaitForArrival(); - - SetWorkingStatus("Flashing...", "The phone may reboot a couple of times. Just wait for it.", null, Status: WPinternalsStatus.Flashing); - - ((NokiaFlashModel)Notifier.CurrentModel).SwitchToFlashAppContext(); - - List FlashParts = new List(); - FlashPart Part; - - FlashModel = (NokiaFlashModel)Notifier.CurrentModel; - - // Remove IS_UNLOCKED flag in GPT - byte[] GPTChunk = GetGptChunk(FlashModel, 0x20000); // TODO: Get proper profile FFU and get ChunkSizeInBytes - GPT = new GPT(GPTChunk); - bool GPTChanged = false; - - Partition IsUnlockedPartition = GPT.GetPartition("IS_UNLOCKED"); - if (IsUnlockedPartition != null) - { - GPT.Partitions.Remove(IsUnlockedPartition); - GPTChanged = true; - } - - Partition EfiEspBackupPartition = GPT.GetPartition("BACKUP_EFIESP"); - if (EfiEspBackupPartition != null) - { - // This must be a left over of a half unlocked bootloader - Partition EfiEspPartition = GPT.GetPartition("EFIESP"); - EfiEspBackupPartition.Name = "EFIESP"; - EfiEspBackupPartition.LastSector = EfiEspPartition.LastSector; - EfiEspBackupPartition.PartitionGuid = EfiEspPartition.PartitionGuid; - EfiEspBackupPartition.PartitionTypeGuid = EfiEspPartition.PartitionTypeGuid; - GPT.Partitions.Remove(EfiEspPartition); - GPTChanged = true; - } - - Partition NvBackupPartition = GPT.GetPartition("BACKUP_BS_NV"); - if (NvBackupPartition != null) - { - // This must be a left over of a half unlocked bootloader - Partition NvPartition = GPT.GetPartition("UEFI_BS_NV"); - NvBackupPartition.Name = "UEFI_BS_NV"; - NvBackupPartition.PartitionGuid = NvPartition.PartitionGuid; - NvBackupPartition.PartitionTypeGuid = NvPartition.PartitionTypeGuid; - GPT.Partitions.Remove(NvPartition); - GPTChanged = true; - } - - if (GPTChanged) - { - GPT.Rebuild(); - Part = new FlashPart(); - Part.StartSector = 0; - Part.Stream = new MemoryStream(GPTChunk); - FlashParts.Add(Part); - } - - if (EFIESPBackup != null) - { - Part = new FlashPart(); - Target = GPT.GetPartition("EFIESP"); - Part.StartSector = (UInt32)Target.FirstSector; - Part.Stream = new MemoryStream(EFIESPBackup); - FlashParts.Add(Part); - } - - // We should only clear NV if there was no backup NV to be restored and the current NV contains the SB unlock. - bool NvCleared = false; - PhoneInfo Info = ((NokiaFlashModel)Notifier.CurrentModel).ReadPhoneInfo(); - if ((NvBackupPartition == null) && !Info.UefiSecureBootEnabled) - { - // ClearNV - Part = new FlashPart(); - Target = GPT.GetPartition("UEFI_BS_NV"); - Part.StartSector = (UInt32)Target.FirstSector; - Part.Stream = new MemoryStream(new byte[0x40000]); - FlashParts.Add(Part); - NvCleared = true; - } - - WPinternalsStatus LastStatus = WPinternalsStatus.Undefined; - ulong? MaxProgressValue = null; - await LumiaV2UnlockBootViewModel.LumiaV2CustomFlash(Notifier, FFUPath, false, false, FlashParts, DoResetFirst, ClearFlashingStatusAtEnd: !NvCleared, - SetWorkingStatus: (m, s, v, a, st) => - { - if (SetWorkingStatus != null) - { - if ((st == WPinternalsStatus.Scanning) || (st == WPinternalsStatus.WaitingForManualReset)) - SetWorkingStatus(m, s, v, a, st); - else if ((LastStatus == WPinternalsStatus.Scanning) || (LastStatus == WPinternalsStatus.WaitingForManualReset) || (LastStatus == WPinternalsStatus.Undefined)) - { - MaxProgressValue = v; - SetWorkingStatus("Flashing...", "The phone may reboot a couple of times. Just wait for it.", v, Status: WPinternalsStatus.Flashing); - } - LastStatus = st; - } - }, - UpdateWorkingStatus: (m, s, v, st) => - { - if (UpdateWorkingStatus != null) - { - if ((st == WPinternalsStatus.Scanning) || (st == WPinternalsStatus.WaitingForManualReset)) - UpdateWorkingStatus(m, s, v, st); - else if ((LastStatus == WPinternalsStatus.Scanning) || (LastStatus == WPinternalsStatus.WaitingForManualReset)) - SetWorkingStatus("Flashing...", "The phone may reboot a couple of times. Just wait for it.", MaxProgressValue, Status: WPinternalsStatus.Flashing); - else - UpdateWorkingStatus("Flashing...", "The phone may reboot a couple of times. Just wait for it.", v, Status: WPinternalsStatus.Flashing); - LastStatus = st; - } - }); - - if (NvBackupPartition != null) - { - // An old NV backup was restored and it possibly contained the IsFlashing flag. - // Can't clear it immeadiately, so we need another flash. - - SetWorkingStatus("Flashing...", "The phone may reboot a couple of times. Just wait for it.", null, Status: WPinternalsStatus.Flashing); - - // If last flash was a normal flash, with no forced crash at the end (!NvCleared), then we have to wait for device arrival, because it could still be detected as Flash-mode from previous flash. - // When phone was forcably crashed, it can be in emergency mode, or still rebooting. Then also wait for device arrival. - // But it is also possible that it is already in bootmgr mode after being crashed (Lumia 950 / 950XL). In that case don't wait for arrival. - if (!NvCleared || ((Notifier.CurrentInterface != PhoneInterfaces.Lumia_Bootloader) && (Notifier.CurrentInterface != PhoneInterfaces.Lumia_Flash))) - await Notifier.WaitForArrival(); - - if (Notifier.CurrentInterface == PhoneInterfaces.Lumia_Bootloader) - ((NokiaFlashModel)Notifier.CurrentModel).SwitchToFlashAppContext(); - - if (Notifier.CurrentInterface == PhoneInterfaces.Lumia_Flash) - { - await LumiaV2UnlockBootViewModel.LumiaV2CustomFlash(Notifier, FFUPath, false, false, null, DoResetFirst, ClearFlashingStatusAtEnd: true, ShowProgress: false); - } - } - - LogFile.Log("Phone is relocked", LogType.FileAndConsole); - ExitSuccess("The phone is relocked", "NOTE: Make sure the phone properly boots and shuts down at least once before you unlock it again"); - } - catch (Exception Ex) - { - LogFile.LogException(Ex); - ExitFailure("Error: " + Ex.Message, null); - } - finally - { - LogFile.EndAction("RelockPhone"); - } - } - internal static async Task LumiaV2ClearNV(System.Threading.SynchronizationContext UIContext, string FFUPath, bool DoResetFirst = true) { LogFile.BeginAction("ClearNV"); @@ -531,7 +239,7 @@ namespace WPinternals // Use GetGptChunk() here instead of ReadGPT(), because ReadGPT() skips the first sector. // We need the fist sector if we want to write back the GPT. - byte[] GPTChunk = GetGptChunk(FlashModel, 0x20000); + byte[] GPTChunk = LumiaUnlockBootloaderViewModel.GetGptChunk(FlashModel, 0x20000); GPT GPT = new GPT(GPTChunk); bool GPTChanged = false; Partition BACKUP_BS_NV = GPT.GetPartition("BACKUP_BS_NV"); @@ -604,7 +312,7 @@ namespace WPinternals // Use GetGptChunk() here instead of ReadGPT(), because ReadGPT() skips the first sector. // We need the fist sector if we want to write back the GPT. - byte[] GPTChunk = GetGptChunk(FlashModel, 0x20000); + byte[] GPTChunk = LumiaUnlockBootloaderViewModel.GetGptChunk(FlashModel, 0x20000); GPT GPT = new GPT(GPTChunk); Partition TargetPartition = GPT.GetPartition(PartitionName); @@ -699,7 +407,7 @@ namespace WPinternals PhoneInfo Info = FlashModel.ReadPhoneInfo(); byte[] Data = System.IO.File.ReadAllBytes(DataPath); - + await LumiaV2CustomFlash(Notifier, FFUPath, false, false, (UInt32)StartSector, Data, DoResetFirst); Notifier.Stop(); } @@ -792,7 +500,6 @@ namespace WPinternals if (UpdateType != 0) throw new WPinternalsException("Only Full Flash images supported"); - UInt32 ChunkCount = 1; // Always flash one extra chunk on the GPT (for purpose of testing and for making sure that first chunk does not contain all zero's). if (FlashParts != null) { foreach (FlashPart Part in FlashParts) @@ -808,8 +515,6 @@ namespace WPinternals if ((Part.Stream.Length % FFU.ChunkSize) != 0) throw new ArgumentException("Invalid Data length"); } - - ChunkCount += (UInt32)(Part.Stream.Length / FFU.ChunkSize); } } @@ -845,7 +550,7 @@ namespace WPinternals MaximumAttempts = (int)(((MaximumGapFill / FFU.ChunkSize) + 1) * 8); } - byte[] GPTChunk = GetGptChunk(Model, (UInt32)FFU.ChunkSize); + byte[] GPTChunk = LumiaUnlockBootloaderViewModel.GetGptChunk(Model, (UInt32)FFU.ChunkSize); // Start with a reset if (DoResetFirst) @@ -959,6 +664,16 @@ namespace WPinternals Model = (NokiaFlashModel)Notifier.CurrentModel; } + // The payloads must be ordered by the number of locations + // + // FlashApp processes payloads like this: + // - First payloads which are with one location, those can be sent in bulk + // - Then payloads with more than one location, those should not be sent in bulk + // + // If you do not order payloads like this, you will get an error, most likely hash mismatch + // + FlashingPayload[] payloads = GetNonOptimizedPayloads(FlashParts, FFU.ChunkSize, (uint)(Info.WriteBufferSize / FFU.ChunkSize), SetWorkingStatus, UpdateWorkingStatus).OrderBy(x => x.TargetLocations.Count()).ToArray(); + bool AssumeImageHeaderFallsInGap = true; bool AllocateAsyncBuffersOnPhone = true; bool AllocateBackupBuffersOnPhone = false; @@ -1043,7 +758,6 @@ namespace WPinternals StoreHeaderAllocation = null; PartialHeaderAllocation = null; UInt32 DestinationChunkIndex = 0; - UInt32 DestinationChunkOffset = 0; // Create memory map UefiMemorySim.Reset(); @@ -1062,13 +776,11 @@ namespace WPinternals CombinedFFUHeaderSize = FFU.HeaderSize; FfuHeader = new byte[CombinedFFUHeaderSize]; - UInt32 TotalChunkCount = ChunkCount; + UInt32 TotalPayloadCount = (uint)payloads.Count(); bool HeadersFull; int FlashingPhase = 0; - int FlashingPhaseStartStreamIndex = -1; - long FlashingPhaseStartStreamPosition = 0; - UInt32 FlashingPhaseStartChunkIndex = 0; - UInt32 FlashingPhaseChunkCount; + UInt32 FlashingPhaseStartPayloadIndex = 0; + UInt32 FlashingPhasePayloadCount = 0; bool FlashInProgress = false; byte[] Buffer = new byte[FFU.ChunkSize]; LastHeaderV2Size = 0; @@ -1114,7 +826,7 @@ namespace WPinternals UInt64 Position = CombinedFFUHeaderSize; byte[] FlashPayload; int ChunkIndex = 0; - TotalChunkCount += (UInt32)FFU.TotalChunkCount; + UInt32 TotalChunkCount = (UInt32)FFU.TotalChunkCount; // Protocol v2 FlashPayload = new byte[Info.WriteBufferSize]; @@ -1164,20 +876,34 @@ namespace WPinternals NewHashOffset += HashTableSize; } - // Determine number of chunks for this phase + // Determine available space and number of payloads to send for this phase UInt32 HashSpace = (UInt32)(FFU.SecurityHeader.Length - NewHashOffset); - UInt32 FreeHashCount = HashSpace / 0x20; // Round down automatically UInt32 DescriptorSpace = (UInt32)(FFU.StoreHeader.Length - NewWriteDescriptorOffset); - UInt32 FreeDescriptorCount = DescriptorSpace / 0x10; - FlashingPhaseChunkCount = FreeHashCount < FreeDescriptorCount ? FreeHashCount : FreeDescriptorCount; - if ((ChunkCount - FlashingPhaseStartChunkIndex) <= FlashingPhaseChunkCount) - FlashingPhaseChunkCount = ChunkCount - FlashingPhaseStartChunkIndex; - else - HeadersFull = true; - HashTableSize += (FlashingPhaseChunkCount * 0x20); - WriteDescriptorCount += FlashingPhaseChunkCount; - WriteDescriptorLength += (FlashingPhaseChunkCount * 0x10); + FlashingPhasePayloadCount = 0; + + // Always flash one extra chunk on the GPT (for purpose of testing and for making sure that first chunk does not contain all zero's). + UInt32 SecurityHeaderSize = FlashInProgress ? 0 : 0x20u; + UInt32 StoreHeaderSize = FlashInProgress ? 0 : 0x10u; + for (UInt32 i = FlashingPhaseStartPayloadIndex; i < payloads.Count(); i++) + { + UInt32 NewSecurityHeaderSize = SecurityHeaderSize + payloads[i].GetSecurityHeaderSize(); + UInt32 NewStoreHeaderSize = StoreHeaderSize + payloads[i].GetStoreHeaderSize(); + + if (NewSecurityHeaderSize > HashSpace || NewStoreHeaderSize > DescriptorSpace) + { + HeadersFull = true; + break; + } + + FlashingPhasePayloadCount += 1; + SecurityHeaderSize = NewSecurityHeaderSize; + StoreHeaderSize = NewStoreHeaderSize; + } + + HashTableSize += SecurityHeaderSize; + WriteDescriptorCount += (UInt32)FlashingPhasePayloadCount + (FlashInProgress ? 0 : 1u); + WriteDescriptorLength += StoreHeaderSize; if (!ClearFlashingStatusAtEnd || HeadersFull) WriteDescriptorCount++; @@ -1189,11 +915,9 @@ namespace WPinternals ByteOperations.WriteUInt32(UefiMemorySim.Buffer, StoreHeaderAllocation.ContentStart + 0xEC, 0); // FlashOnlyTableLength - Make flash progress bar white immediately. ByteOperations.WriteUInt32(UefiMemorySim.Buffer, StoreHeaderAllocation.ContentStart + 0xE8, 1); // FlashOnlyTableCount - UInt32 CustomChunkCount = FlashingPhaseChunkCount; - // Write new descriptors // First write descriptor and hash for the first GPT chunk - if (!FlashInProgress && (FlashingPhaseChunkCount > 0)) + if (!FlashInProgress) // We only send the first GPT chunk when flash is not in progress yet. { ByteOperations.WriteUInt32(UefiMemorySim.Buffer, StoreHeaderAllocation.ContentStart + NewWriteDescriptorOffset + 0x00, 0x00000001); // Location count ByteOperations.WriteUInt32(UefiMemorySim.Buffer, StoreHeaderAllocation.ContentStart + NewWriteDescriptorOffset + 0x04, 0x00000001); // Chunk count @@ -1203,45 +927,33 @@ namespace WPinternals byte[] GPTHashValue = System.Security.Cryptography.SHA256.Create().ComputeHash(GPTChunk, 0, FFU.ChunkSize); // Hash is 0x20 bytes System.Buffer.BlockCopy(GPTHashValue, 0, UefiMemorySim.Buffer, (int)(SecurityHeaderAllocation.ContentStart + NewHashOffset), 0x20); NewHashOffset += 0x20; - CustomChunkCount--; } - // TODO: Optimize: make multiple locations for chunks with same content. - Stream CurrentStream = null; - int StreamIndex = FlashingPhaseStartStreamIndex; - if (StreamIndex >= 0) + for (UInt32 i = 0; i < FlashingPhasePayloadCount; i++) { - CurrentStream = FlashParts[StreamIndex].Stream; - CurrentStream.Seek(FlashingPhaseStartStreamPosition, SeekOrigin.Begin); - } - byte[] PayloadBuffer = new byte[FFU.ChunkSize]; - for (int i = 0; i < CustomChunkCount; i++) - { - if ((CurrentStream == null) || (CurrentStream.Position == CurrentStream.Length)) + FlashingPayload payload = payloads[FlashingPhaseStartPayloadIndex + i]; + + ByteOperations.WriteUInt32(UefiMemorySim.Buffer, StoreHeaderAllocation.ContentStart + NewWriteDescriptorOffset + 0x00, (UInt32)payload.TargetLocations.Count()); // Location count + ByteOperations.WriteUInt32(UefiMemorySim.Buffer, StoreHeaderAllocation.ContentStart + NewWriteDescriptorOffset + 0x04, payload.ChunkCount); // Chunk count + NewWriteDescriptorOffset += 0x08; + + foreach (UInt32 location in payload.TargetLocations) { - StreamIndex++; - CurrentStream = FlashParts[StreamIndex].Stream; - CurrentStream.Seek(0, SeekOrigin.Begin); - DestinationChunkOffset = (UInt32)((Int64)FlashParts[StreamIndex].StartSector * 0x200 / FFU.ChunkSize); + ByteOperations.WriteUInt32(UefiMemorySim.Buffer, StoreHeaderAllocation.ContentStart + NewWriteDescriptorOffset + 0x00, 0x00000000); // Disk access method (0 = Begin, 2 = End) + ByteOperations.WriteUInt32(UefiMemorySim.Buffer, StoreHeaderAllocation.ContentStart + NewWriteDescriptorOffset + 0x04, location); // Chunk index + NewWriteDescriptorOffset += 0x08; } - ByteOperations.WriteUInt32(UefiMemorySim.Buffer, StoreHeaderAllocation.ContentStart + NewWriteDescriptorOffset + 0x00, 0x00000001); // Location count - ByteOperations.WriteUInt32(UefiMemorySim.Buffer, StoreHeaderAllocation.ContentStart + NewWriteDescriptorOffset + 0x04, 0x00000001); // Chunk count - ByteOperations.WriteUInt32(UefiMemorySim.Buffer, StoreHeaderAllocation.ContentStart + NewWriteDescriptorOffset + 0x08, 0x00000000); // Disk access method (0 = Begin, 2 = End) - ByteOperations.WriteUInt32(UefiMemorySim.Buffer, StoreHeaderAllocation.ContentStart + NewWriteDescriptorOffset + 0x0C, DestinationChunkOffset); // Chunk index - NewWriteDescriptorOffset += 0x10; - - // Write new hash - if ((CurrentStream.Length - CurrentStream.Position) < PayloadBuffer.Length) - Array.Clear(PayloadBuffer, 0, PayloadBuffer.Length); - CurrentStream.Read(PayloadBuffer, 0, FFU.ChunkSize); - byte[] HashValue = System.Security.Cryptography.SHA256.Create().ComputeHash(PayloadBuffer, 0, FFU.ChunkSize); // Hash is 0x20 bytes - System.Buffer.BlockCopy(HashValue, 0, UefiMemorySim.Buffer, (int)(SecurityHeaderAllocation.ContentStart + NewHashOffset), 0x20); - NewHashOffset += 0x20; - - DestinationChunkOffset++; + foreach (byte[] hashValue in payload.ChunkHashes) + { + // Write new hash + System.Buffer.BlockCopy(hashValue, 0, UefiMemorySim.Buffer, (Int32)(SecurityHeaderAllocation.ContentStart + NewHashOffset), 0x20); + NewHashOffset += 0x20; + } } + Stream CurrentStream = null; + int StreamIndex = 0; int Step = 0; try { @@ -1272,69 +984,124 @@ namespace WPinternals } // Send custom payload - // TODO: Optimize to send multiple chunks at once Step = 3; - DestinationChunkIndex = (PerformFullFlashFirst && (FlashingPhase == 0)) ? (UInt32)FFU.TotalChunkCount : 0; - - CurrentStream = null; - StreamIndex = FlashingPhaseStartStreamIndex; - if (StreamIndex >= 0) + Int32 payloadCount = 0; + byte[] payloadBuffer = new byte[Info.WriteBufferSize]; + bool sendPayload = false; + for (Int32 i = FlashInProgress ? 0 : -1; i < FlashingPhasePayloadCount; i++) { - CurrentStream = FlashParts[StreamIndex].Stream; - CurrentStream.Seek(FlashingPhaseStartStreamPosition, SeekOrigin.Begin); - } - - for (int i = 0; i < FlashingPhaseChunkCount; i++) - { - string NewProgressText = null; + string NewProgressText = "Flashing resources..."; if (!FlashInProgress) { // First send the GPT chunk Step = 4; - System.Buffer.BlockCopy(GPTChunk, 0, Buffer, 0, (int)FFU.ChunkSize); + System.Buffer.BlockCopy(GPTChunk, 0, Buffer, 0, FFU.ChunkSize); + + Step = 8; + // This may fail. Normally with WPinternalsException for Invalid Hash or Data not aligned. + // Or it may fail with a BadConnectionException when the phone crashes and drops the connection. + Model.SendFfuPayloadV1(Buffer, 0); + if (!FlashInProgress) + { + Step = 9; + if (ShowProgress) + LogFile.Log("Flashing in progress!", LogType.FileAndConsole); + FlashInProgress = true; + Scanning = false; + SetWorkingStatus(null, null, (UInt64?)payloads.Count(), Status: WPinternalsStatus.Flashing); + } } else { Step = 5; - if ((CurrentStream == null) || (CurrentStream.Position == CurrentStream.Length)) + + FlashingPayload payload = payloads[FlashingPhaseStartPayloadIndex + i]; + + if (payloadCount == (Info.WriteBufferSize / FFU.ChunkSize - 1)) { - StreamIndex++; - CurrentStream = FlashParts[StreamIndex].Stream; - CurrentStream.Seek(0, SeekOrigin.Begin); - NewProgressText = FlashParts[StreamIndex].ProgressText; + sendPayload = true; } - Step = 6; - if ((CurrentStream.Length - CurrentStream.Position) < Buffer.Length) - Array.Clear(Buffer, 0, Buffer.Length); + if (FlashingPhaseStartPayloadIndex + i + 1 >= FlashingPhasePayloadCount) + { + sendPayload = true; + byte[] tmpBuffer = new byte[(payloadCount + 1) * FFU.ChunkSize]; + System.Buffer.BlockCopy(payloadBuffer, 0, tmpBuffer, 0, payloadCount * FFU.ChunkSize); + payloadBuffer = tmpBuffer; + } - Step = 7; - CurrentStream.Read(Buffer, 0, FFU.ChunkSize); + // Check if the next payload contains more than one chunk + if (!sendPayload && FlashingPhaseStartPayloadIndex + i + 1 < FlashingPhasePayloadCount && (payloads[FlashingPhaseStartPayloadIndex + i + 1].ChunkCount != 1 || payloads[FlashingPhaseStartPayloadIndex + i + 1].TargetLocations.Count() != 1)) + { + sendPayload = true; + byte[] tmpBuffer = new byte[(payloadCount + 1) * FFU.ChunkSize]; + System.Buffer.BlockCopy(payloadBuffer, 0, tmpBuffer, 0, payloadCount * FFU.ChunkSize); + payloadBuffer = tmpBuffer; + } + + // We prepare the buffer setup above with all consecutive chunks we have to send in + // We can't send a single chunk otherwise we would get 0x1007: Payload data does not contain all data + if (payload.ChunkCount != 1) + { + NewProgressText = "Flashing common resources..."; + payloadBuffer = new byte[payload.ChunkCount * FFU.ChunkSize]; + for (uint j = 0; j < payload.ChunkCount; j++) + { + StreamIndex = (Int32)payload.StreamIndexes[j]; + FlashPart flashPart = FlashParts[StreamIndex]; + CurrentStream = flashPart.Stream; + CurrentStream.Seek(payload.StreamLocations[j], SeekOrigin.Begin); + + Step = 6; + Array.Clear(payloadBuffer, (Int32)(FFU.ChunkSize * j), FFU.ChunkSize); // Not really needed anymore? + + Step = 7; + CurrentStream.Read(payloadBuffer, (Int32)(FFU.ChunkSize * j), FFU.ChunkSize); + } + } + + if (payload.TargetLocations.Count() != 1) + { + NewProgressText = "Flashing common resources..."; + payloadBuffer = new byte[FFU.ChunkSize]; + } + + if (payload.ChunkCount == 1) + { + StreamIndex = (Int32)payload.StreamIndexes[0]; + FlashPart flashPart = FlashParts[StreamIndex]; + CurrentStream = flashPart.Stream; + CurrentStream.Seek(payload.StreamLocations[0], SeekOrigin.Begin); + + if (payload.TargetLocations.Count() == 1 && !string.IsNullOrEmpty(flashPart.ProgressText)) + NewProgressText = flashPart.ProgressText; + + CurrentStream.Read(payloadBuffer, (Int32)(FFU.ChunkSize * payloadCount), FFU.ChunkSize); + } + + Step = 8; + // This may fail. Normally with WPinternalsException for Invalid Hash or Data not aligned. + // Or it may fail with a BadConnectionException when the phone crashes and drops the connection. + + payloadCount++; } - Step = 8; - // This may fail. Normally with WPinternalsException for Invalid Hash or Data not aligned. - // Or it may fail with a BadConnectionException when the phone crashes and drops the connection. - Model.SendFfuPayloadV1(Buffer, ShowProgress ? (int)((FlashingPhaseStartChunkIndex + DestinationChunkIndex + 1) * 100 / TotalChunkCount) : 0); - if (!FlashInProgress) + UpdateWorkingStatus(NewProgressText, null, (UInt64?)(FlashingPhaseStartPayloadIndex + i + 1), WPinternalsStatus.Flashing); + + if (i != -1 && sendPayload) { - Step = 9; - if (ShowProgress) - LogFile.Log("Flashing in progress!", LogType.FileAndConsole); - FlashInProgress = true; - Scanning = false; - SetWorkingStatus(null, null, TotalChunkCount, Status: WPinternalsStatus.Flashing); + // This fails when sending multiple chunks per payload with 0x1003: Hash mismatch + Model.SendFfuPayloadV2(payloadBuffer, ShowProgress ? (Int32)((FlashingPhaseStartPayloadIndex + i + 1) * 100 / payloads.Count()) : 0); + sendPayload = false; + payloadCount = 0; + payloadBuffer = new byte[Info.WriteBufferSize]; } - UpdateWorkingStatus(NewProgressText, null, FlashingPhaseStartChunkIndex + DestinationChunkIndex + 1, WPinternalsStatus.Flashing); - NewProgressText = null; + DestinationChunkIndex++; } Step = 10; - FlashingPhaseStartChunkIndex += FlashingPhaseChunkCount; - FlashingPhaseStartStreamIndex = StreamIndex; - if (StreamIndex >= 0) - FlashingPhaseStartStreamPosition = CurrentStream.Position; + FlashingPhaseStartPayloadIndex += FlashingPhasePayloadCount; Step = 11; if (!HeadersFull) @@ -1345,6 +1112,11 @@ namespace WPinternals LogFile.Log("Custom flash succeeded!", LogType.FileAndConsole); Success = true; } + else + { + // At this point we're missing a few payloads, so we need to start again. + LogFile.Log("Reinitilizing a new flashing attempt because headers were full and we're not quite done yet!"); + } } catch (BadConnectionException) { @@ -1353,7 +1125,7 @@ namespace WPinternals StreamIndex.ToString() + " " + (CurrentStream == null ? "0" : CurrentStream.Position.ToString()) + " " + FlashingPhase.ToString() + " " + - FlashingPhaseStartChunkIndex.ToString() + " " + + FlashingPhaseStartPayloadIndex.ToString() + " " + DestinationChunkIndex.ToString()); LogFile.Log("Expect phone to reboot", LogType.FileAndConsole); WaitForReset = true; @@ -1372,7 +1144,7 @@ namespace WPinternals StreamIndex.ToString() + " " + (CurrentStream == null ? "0" : CurrentStream.Position.ToString()) + " " + FlashingPhase.ToString() + " " + - FlashingPhaseStartChunkIndex.ToString() + " " + + FlashingPhaseStartPayloadIndex.ToString() + " " + DestinationChunkIndex.ToString()); Abort = true; } @@ -1384,7 +1156,7 @@ namespace WPinternals StreamIndex.ToString() + " " + (CurrentStream == null ? "0" : CurrentStream.Position.ToString()) + " " + FlashingPhase.ToString() + " " + - FlashingPhaseStartChunkIndex.ToString() + " " + + FlashingPhaseStartPayloadIndex.ToString() + " " + DestinationChunkIndex.ToString()); } @@ -1728,6 +1500,128 @@ namespace WPinternals throw new WPinternalsException("Custom flash failed"); } + internal class FlashingPayload + { + public UInt32 ChunkCount; + public byte[][] ChunkHashes; + public UInt32[] TargetLocations; + public UInt32[] StreamIndexes; + public Int64[] StreamLocations; + + public FlashingPayload(UInt32 ChunkCount, byte[][] ChunkHashes, UInt32[] TargetLocations, UInt32[] StreamIndexes, Int64[] StreamLocations) + { + this.ChunkCount = ChunkCount; + this.ChunkHashes = ChunkHashes; + this.TargetLocations = TargetLocations; + this.StreamIndexes = StreamIndexes; + this.StreamLocations = StreamLocations; + } + + public UInt32 GetSecurityHeaderSize() + { + return 0x20 * (UInt32)ChunkHashes.Count(); + } + + public UInt32 GetStoreHeaderSize() + { + return 0x08 * ((UInt32)TargetLocations.Count() + 1); + } + } + + // + // Function to fall back into the legacy implementation of custom flash, to test the modifications done in the custom flash function + // in LumiaV2UnlockBootViewModel + // + internal static FlashingPayload[] GetNonOptimizedPayloads(List flashParts, Int32 chunkSize, UInt32 MaximumChunkCount, SetWorkingStatus SetWorkingStatus = null, UpdateWorkingStatus UpdateWorkingStatus = null) + { + long TotalProcess1 = 0; + for (Int32 j = 0; j < flashParts.Count; j++) + { + FlashPart flashPart = flashParts[j]; + TotalProcess1 += flashPart.Stream.Length / chunkSize; + } + + ulong CurrentProcess1 = 0; + SetWorkingStatus("Hashing resources...", "Initializing flash...", (UInt64)TotalProcess1, Status: WPinternalsStatus.Initializing); + + var crypto = System.Security.Cryptography.SHA256.Create(); + List flashingPayloads = new List(); + if (flashParts == null) + return flashingPayloads.ToArray(); + for (UInt32 j = 0; j < flashParts.Count; j++) + { + FlashPart flashPart = flashParts[(Int32)j]; + flashPart.Stream.Seek(0, SeekOrigin.Begin); + var totalChunkCount = flashPart.Stream.Length / chunkSize; + for (UInt32 i = 0; i < totalChunkCount; i++) + { + UpdateWorkingStatus("Hashing resources...", "Initializing flash...", (UInt64)CurrentProcess1, WPinternalsStatus.Initializing); + byte[] buffer = new byte[chunkSize]; + Int64 position = flashPart.Stream.Position; + flashPart.Stream.Read(buffer, 0, chunkSize); + flashingPayloads.Add(new FlashingPayload(1, new byte[][] { crypto.ComputeHash(buffer) }, new UInt32[] { (flashPart.StartSector * 0x200 / (UInt32)chunkSize) + i }, new UInt32[] { j }, new Int64[] { position })); + CurrentProcess1++; + } + } + + return flashingPayloads.ToArray(); + } + + // + // This function finds in an optimized way the number of duplicate chunks in a given stream, and returns + // a list of elements, defining a chunk occurence in said stream and the chunk precomputed SHA256 hash. + // + internal static FlashingPayload[] GetOptimizedPayloads(List flashParts, Int32 chunkSize, UInt32 MaximumChunkCount, SetWorkingStatus SetWorkingStatus = null, UpdateWorkingStatus UpdateWorkingStatus = null) + { + List flashingPayloads = new List(); + if (flashParts == null) + return flashingPayloads.ToArray(); + + long TotalProcess1 = 0; + for (Int32 j = 0; j < flashParts.Count; j++) + { + FlashPart flashPart = flashParts[j]; + TotalProcess1 += flashPart.Stream.Length / chunkSize; + } + + ulong CurrentProcess1 = 0; + SetWorkingStatus("Hashing resources...", "Initializing flash...", (UInt64)TotalProcess1, Status: WPinternalsStatus.Initializing); + + using (System.Security.Cryptography.SHA256 crypto = System.Security.Cryptography.SHA256.Create()) + { + for (UInt32 j = 0; j < flashParts.Count; j++) + { + FlashPart flashPart = flashParts[(Int32)j]; + flashPart.Stream.Seek(0, SeekOrigin.Begin); + var totalChunkCount = flashPart.Stream.Length / chunkSize; + for (UInt32 i = 0; i < totalChunkCount; i++) + { + UpdateWorkingStatus("Hashing resources...", "Initializing flash...", (UInt64)CurrentProcess1, WPinternalsStatus.Initializing); + byte[] buffer = new byte[chunkSize]; + Int64 position = flashPart.Stream.Position; + flashPart.Stream.Read(buffer, 0, chunkSize); + var hash = crypto.ComputeHash(buffer); + + if (flashingPayloads.Any(x => ByteOperations.Compare(x.ChunkHashes.First(), hash))) + { + var payloadIndex = flashingPayloads.FindIndex(x => ByteOperations.Compare(x.ChunkHashes.First(), hash)); + var locationList = flashingPayloads[payloadIndex].TargetLocations.ToList(); + locationList.Add((flashPart.StartSector * 0x200 / (UInt32)chunkSize) + i); + flashingPayloads[payloadIndex].TargetLocations = locationList.ToArray(); + } + else + { + flashingPayloads.Add(new FlashingPayload(1, new byte[][] { hash }, new UInt32[] { (flashPart.StartSector * 0x200 / (UInt32)chunkSize) + i }, new UInt32[] { j }, new Int64[] { position })); + } + + CurrentProcess1++; + } + } + } + + return flashingPayloads.ToArray(); + } + internal static string GetProgrammerPath(byte[] RKH, string Type) { IEnumerable RKHEntries = App.Config.EmergencyRepository.Where(e => (StructuralComparisons.StructuralEqualityComparer.Equals(e.RKH, RKH) && e.ProgrammerExists())); @@ -1765,7 +1659,7 @@ namespace WPinternals // Use GetGptChunk() here instead of ReadGPT(), because ReadGPT() skips the first sector. // We need the fist sector if we want to write back the GPT. - byte[] GPTChunk = GetGptChunk(FlashModel, 0x20000); + byte[] GPTChunk = LumiaUnlockBootloaderViewModel.GetGptChunk(FlashModel, 0x20000); GPT GPT = new GPT(GPTChunk); Partition Target; @@ -2070,430 +1964,6 @@ namespace WPinternals LogFile.EndAction("FixBoot"); } - - // Magic! - // Assumes phone with Flash protocol v2 - // Assumes phone is in flash mode - internal async static Task LumiaV2UnlockBootloader(PhoneNotifierViewModel Notifier, string ProfileFFUPath, string EDEPath, string SupportedFFUPath, SetWorkingStatus SetWorkingStatus = null, UpdateWorkingStatus UpdateWorkingStatus = null, ExitSuccess ExitSuccess = null, ExitFailure ExitFailure = null) - { - LogFile.BeginAction("UnlockBootloader"); - NokiaFlashModel FlashModel = (NokiaFlashModel)Notifier.CurrentModel; - - if (SetWorkingStatus == null) SetWorkingStatus = (m, s, v, a, st) => { }; - if (UpdateWorkingStatus == null) UpdateWorkingStatus = (m, s, v, st) => { }; - if (ExitSuccess == null) ExitSuccess = (m, s) => { }; - if (ExitFailure == null) ExitFailure = (m, s) => { }; - - try - { - PhoneInfo Info = FlashModel.ReadPhoneInfo(); - bool IsBootLoaderSecure = !Info.Authenticated && !Info.RdcPresent && Info.SecureFfuEnabled; - - if (ProfileFFUPath == null) - throw new ArgumentNullException("Profile FFU path is missing"); - - FFU ProfileFFU = new FFU(ProfileFFUPath); - - if (IsBootLoaderSecure) - { - if (!Info.PlatformID.StartsWith(ProfileFFU.PlatformID, StringComparison.OrdinalIgnoreCase)) - throw new ArgumentNullException("Profile FFU has wrong Platform ID for connected phone"); - } - - FFU SupportedFFU = null; - if (App.PatchEngine.PatchDefinitions.Where(p => p.Name == "SecureBootHack-V2-EFIESP").First().TargetVersions.Any(v => v.Description == ProfileFFU.GetOSVersion())) - SupportedFFU = ProfileFFU; - else if (SupportedFFUPath == null) - throw new ArgumentNullException("Donor-FFU with supported OS version was not provided"); - else - { - SupportedFFU = new FFU(SupportedFFUPath); - if (!App.PatchEngine.PatchDefinitions.Where(p => p.Name == "SecureBootHack-V2-EFIESP").First().TargetVersions.Any(v => v.Description == SupportedFFU.GetOSVersion())) - throw new ArgumentNullException("Donor-FFU with supported OS version was not provided"); - } - - // TODO: Check EDE file - - LogFile.Log("Assembling data for unlock", LogType.FileAndConsole); - SetWorkingStatus("Assembling data for unlock", null, null); - byte[] UnlockedEFIESP = ProfileFFU.GetPartition("EFIESP"); - - DiscUtils.Fat.FatFileSystem UnlockedEFIESPFileSystem = new DiscUtils.Fat.FatFileSystem(new MemoryStream(UnlockedEFIESP)); - - if (SupportedFFU.Path != ProfileFFU.Path) - { - LogFile.Log("Donor-FFU: " + SupportedFFU.Path); - byte[] SupportedEFIESP = SupportedFFU.GetPartition("EFIESP"); - DiscUtils.Fat.FatFileSystem SupportedEFIESPFileSystem = new DiscUtils.Fat.FatFileSystem(new MemoryStream(SupportedEFIESP)); - DiscUtils.SparseStream SupportedMobileStartupStream = SupportedEFIESPFileSystem.OpenFile(@"\Windows\System32\Boot\mobilestartup.efi", FileMode.Open); - MemoryStream SupportedMobileStartupMemStream = new MemoryStream(); - SupportedMobileStartupStream.CopyTo(SupportedMobileStartupMemStream); - byte[] SupportedMobileStartup = SupportedMobileStartupMemStream.ToArray(); - SupportedMobileStartupMemStream.Close(); - SupportedMobileStartupStream.Close(); - - // Save supported mobilestartup.efi - LogFile.Log("Taking mobilestartup.efi from donor-FFU"); - Stream MobileStartupStream = UnlockedEFIESPFileSystem.OpenFile(@"Windows\System32\Boot\mobilestartup.efi", FileMode.Create, FileAccess.Write); - MobileStartupStream.Write(SupportedMobileStartup, 0, SupportedMobileStartup.Length); - MobileStartupStream.Close(); - } - - // Magic! - // This patch contains multiple hacks to disable SecureBoot, disable Bootpolicies and allow Mass Storage Mode on retail phones - App.PatchEngine.TargetImage = UnlockedEFIESPFileSystem; - bool PatchResult = App.PatchEngine.Patch("SecureBootHack-V2-EFIESP"); - if (!PatchResult) - throw new WPinternalsException("Failed to patch bootloader"); - - // Edit BCD - LogFile.Log("Edit BCD"); - using (Stream BCDFileStream = UnlockedEFIESPFileSystem.OpenFile(@"efi\Microsoft\Boot\BCD", FileMode.Open, FileAccess.ReadWrite)) - { - using (DiscUtils.Registry.RegistryHive BCDHive = new DiscUtils.Registry.RegistryHive(BCDFileStream)) - { - DiscUtils.BootConfig.Store BCDStore = new DiscUtils.BootConfig.Store(BCDHive.Root); - DiscUtils.BootConfig.BcdObject MobileStartupObject = BCDStore.GetObject(new Guid("{01de5a27-8705-40db-bad6-96fa5187d4a6}")); - DiscUtils.BootConfig.Element NoCodeIntegrityElement = MobileStartupObject.GetElement(0x16000048); - if (NoCodeIntegrityElement != null) - NoCodeIntegrityElement.Value = DiscUtils.BootConfig.ElementValue.ForBoolean(true); - else - MobileStartupObject.AddElement(0x16000048, DiscUtils.BootConfig.ElementValue.ForBoolean(true)); - - DiscUtils.BootConfig.BcdObject WinLoadObject = BCDStore.GetObject(new Guid("{7619dcc9-fafe-11d9-b411-000476eba25f}")); - NoCodeIntegrityElement = WinLoadObject.GetElement(0x16000048); - if (NoCodeIntegrityElement != null) - NoCodeIntegrityElement.Value = DiscUtils.BootConfig.ElementValue.ForBoolean(true); - else - WinLoadObject.AddElement(0x16000048, DiscUtils.BootConfig.ElementValue.ForBoolean(true)); - } - } - - UnlockedEFIESPFileSystem.Dispose(); - - List Parts = new List(); - FlashPart Part; - GPT GPT = FlashModel.ReadGPT(); - - // Create backup-partition for EFIESP - byte[] GPTChunk = GetGptChunk(FlashModel, (UInt32)ProfileFFU.ChunkSize); - byte[] GPTChunkBackup = new byte[GPTChunk.Length]; - Buffer.BlockCopy(GPTChunk, 0, GPTChunkBackup, 0, GPTChunk.Length); - GPT = new GPT(GPTChunk); - bool GPTChanged = false; - Partition BACKUP_EFIESP = GPT.GetPartition("BACKUP_EFIESP"); - Partition EFIESP; - UInt32 OriginalEfiespSizeInSectors = (UInt32)GPT.GetPartition("EFIESP").SizeInSectors; - UInt32 OriginalEfiespLastSector = (UInt32)GPT.GetPartition("EFIESP").LastSector; - if (BACKUP_EFIESP == null) - { - BACKUP_EFIESP = GPT.GetPartition("EFIESP"); - Guid OriginalPartitionTypeGuid = BACKUP_EFIESP.PartitionTypeGuid; - Guid OriginalPartitionGuid = BACKUP_EFIESP.PartitionGuid; - BACKUP_EFIESP.Name = "BACKUP_EFIESP"; - BACKUP_EFIESP.LastSector = BACKUP_EFIESP.FirstSector + ((OriginalEfiespSizeInSectors) / 2) - 1; // Original is 0x10000 - BACKUP_EFIESP.PartitionGuid = Guid.NewGuid(); - BACKUP_EFIESP.PartitionTypeGuid = Guid.NewGuid(); - EFIESP = new Partition(); - EFIESP.Name = "EFIESP"; - EFIESP.Attributes = BACKUP_EFIESP.Attributes; - EFIESP.PartitionGuid = OriginalPartitionGuid; - EFIESP.PartitionTypeGuid = OriginalPartitionTypeGuid; - EFIESP.FirstSector = BACKUP_EFIESP.LastSector + 1; - EFIESP.LastSector = EFIESP.FirstSector + ((OriginalEfiespSizeInSectors) / 2) - 1; // Original is 0x10000 - GPT.Partitions.Add(EFIESP); - GPTChanged = true; - } - EFIESP = GPT.GetPartition("EFIESP"); - if ((UInt64)UnlockedEFIESP.Length > (EFIESP.SizeInSectors * 0x200)) - { - byte[] HalfEFIESP = new byte[EFIESP.SizeInSectors * 0x200]; - Buffer.BlockCopy(UnlockedEFIESP, 0, HalfEFIESP, 0, HalfEFIESP.Length); - UnlockedEFIESP = HalfEFIESP; - ByteOperations.WriteUInt32(UnlockedEFIESP, 0x20, (UInt32)EFIESP.SizeInSectors); // Correction of partitionsize - } - - Partition TargetPartition = GPT.GetPartition("EFIESP"); - if (TargetPartition == null) - throw new WPinternalsException("EFIESP partition not found!"); - - if ((UInt64)UnlockedEFIESP.Length != (TargetPartition.SizeInSectors * 0x200)) - throw new WPinternalsException("New EFIESP partition has wrong size. Size = 0x" + UnlockedEFIESP.Length.ToString("X8") + ". Expected size = 0x" + (TargetPartition.SizeInSectors * 0x200).ToString("X8")); - - Part = new FlashPart(); - Part.StartSector = (UInt32)TargetPartition.FirstSector; // GPT is prepared for 64-bit sector-offset, but flash app isn't. - Part.Stream = new MemoryStream(UnlockedEFIESP); - Part.ProgressText = "Flashing unlocked bootloader (part 1)..."; - Parts.Add(Part); - - // Now add NV partition - Partition BACKUP_BS_NV = GPT.GetPartition("BACKUP_BS_NV"); - Partition UEFI_BS_NV; - if (BACKUP_BS_NV == null) - { - BACKUP_BS_NV = GPT.GetPartition("UEFI_BS_NV"); - Guid OriginalPartitionTypeGuid = BACKUP_BS_NV.PartitionTypeGuid; - Guid OriginalPartitionGuid = BACKUP_BS_NV.PartitionGuid; - BACKUP_BS_NV.Name = "BACKUP_BS_NV"; - BACKUP_BS_NV.PartitionGuid = Guid.NewGuid(); - BACKUP_BS_NV.PartitionTypeGuid = Guid.NewGuid(); - UEFI_BS_NV = new Partition(); - UEFI_BS_NV.Name = "UEFI_BS_NV"; - UEFI_BS_NV.Attributes = BACKUP_BS_NV.Attributes; - UEFI_BS_NV.PartitionGuid = OriginalPartitionGuid; - UEFI_BS_NV.PartitionTypeGuid = OriginalPartitionTypeGuid; - UEFI_BS_NV.FirstSector = BACKUP_BS_NV.LastSector + 1; - UEFI_BS_NV.LastSector = UEFI_BS_NV.FirstSector + BACKUP_BS_NV.LastSector - BACKUP_BS_NV.FirstSector; - GPT.Partitions.Add(UEFI_BS_NV); - GPTChanged = true; - } - Part = new FlashPart(); - TargetPartition = GPT.GetPartition("UEFI_BS_NV"); - Part.StartSector = (UInt32)TargetPartition.FirstSector; // GPT is prepared for 64-bit sector-offset, but flash app isn't. - Part.Stream = new SeekableStream(() => - { - var assembly = System.Reflection.Assembly.GetExecutingAssembly(); - - // Magic! - // The SB resource is a compressed version of a raw NV-variable-partition. - // In this partition the SecureBoot variable is disabled. - // It overwrites the variable in a different NV-partition than where this variable is stored usually. - // This normally leads to endless-loops when the NV-variables are enumerated. - // But the partition contains an extra hack to break out the endless loops. - var stream = assembly.GetManifestResourceStream("WPinternals.SB"); - - return new DecompressedStream(stream); - }); - Parts.Add(Part); - - if (GPTChanged) - { - GPT.Rebuild(); - Part = new FlashPart(); - Part.StartSector = 0; - Part.Stream = new MemoryStream(GPTChunk); - Parts.Add(Part); - } - - await LumiaV2UnlockBootViewModel.LumiaV2CustomFlash(Notifier, ProfileFFU.Path, false, false, Parts, true, false, true, true, false, SetWorkingStatus, UpdateWorkingStatus, null, null, EDEPath); - - if ((Notifier.CurrentInterface != PhoneInterfaces.Lumia_Bootloader) && (Notifier.CurrentInterface != PhoneInterfaces.Lumia_Flash)) - await Notifier.WaitForArrival(); - - if ((Notifier.CurrentInterface != PhoneInterfaces.Lumia_Bootloader) && (Notifier.CurrentInterface != PhoneInterfaces.Lumia_Flash)) - throw new WPinternalsException("Error: Phone is in wrong mode"); - - // Not going to retry in a loop because a second attempt will result in gears due to changed BootOrder. - // Just inform user of problem and revert. - // User can try again after revert. - bool IsPhoneInBadMassStorageMode = false; - string ErrorMessage = null; - try - { - await SwitchModeViewModel.SwitchToWithStatus(Notifier, PhoneInterfaces.Lumia_MassStorage, SetWorkingStatus, UpdateWorkingStatus); - } - catch (WPinternalsException Ex) - { - ErrorMessage = "Error: " + Ex.Message; - LogFile.LogException(Ex); - } - catch (Exception Ex) - { - LogFile.LogException(Ex); - } - - if (Notifier.CurrentInterface == PhoneInterfaces.Lumia_BadMassStorage) - { - SetWorkingStatus("You need to manually reset your phone now!", "The phone is currently in Mass Storage Mode, but the driver of the PC failed to start. Unfortunately this happens sometimes. You need to manually reset the phone now. Keep the phone connected to the PC. Reboot the phone manually by pressing and holding the power-button of the phone for about 10 seconds until it vibrates. Windows Phone Internals will automatically start to revert the changes. After the phone is fully booted again, you can retry to unlock the bootloader.", null, false, WPinternalsStatus.WaitingForManualReset); - await Notifier.WaitForArrival(); // Should be detected in Bootmanager mode - if (Notifier.CurrentInterface != PhoneInterfaces.Lumia_MassStorage) - IsPhoneInBadMassStorageMode = true; - } - - if (Notifier.CurrentInterface != PhoneInterfaces.Lumia_MassStorage) - { - // Probably the "BootOrder" prevents to boot to MobileStartup. Mass Storage mode depends on MobileStartup. - // In this case Bootarm boots straight to Winload. But Winload can't handle the change of the EFIESP partition. That will cause a bootloop. - - SetWorkingStatus("Problem detected, rolling back...", ErrorMessage); - await SwitchModeViewModel.SwitchTo(Notifier, PhoneInterfaces.Lumia_Flash); - Parts = new List(); - - // Restore original GPT, which will also reference the original NV. - Part = new FlashPart(); - Part.StartSector = 0; - Part.Stream = new MemoryStream(GPTChunkBackup); - Parts.Add(Part); - - await LumiaV2UnlockBootViewModel.LumiaV2CustomFlash(Notifier, ProfileFFU.Path, false, false, Parts, true, false, true, true, false, SetWorkingStatus, UpdateWorkingStatus, null, null, EDEPath); - - // An old NV backup was restored and it possibly contained the IsFlashing flag. - // Can't clear it immeadiately, so we need another flash. - if ((Notifier.CurrentInterface != PhoneInterfaces.Lumia_Bootloader) && (Notifier.CurrentInterface != PhoneInterfaces.Lumia_Flash)) - await Notifier.WaitForArrival(); - - if ((Notifier.CurrentInterface == PhoneInterfaces.Lumia_Bootloader) || (Notifier.CurrentInterface == PhoneInterfaces.Lumia_Flash)) - { - await LumiaV2UnlockBootViewModel.LumiaV2CustomFlash(Notifier, ProfileFFU.Path, false, false, null, true, true, true, true, false, SetWorkingStatus, UpdateWorkingStatus, null, null, EDEPath); - } - - if (IsPhoneInBadMassStorageMode) - ExitFailure("Failed to unlock the bootloader due to misbahaving driver. Wait for phone to boot to Windows and then try again.", "The Mass Storage driver of the PC failed to start. Unfortunately this happens sometimes. After the phone is fully booted again, you can retry to unlock the bootloader."); - else - ExitFailure("Failed to unlock the bootloader", "It is not possible to unlock the bootloader straight after flashing. NOTE: Fully reboot the phone and then properly shutdown the phone, before you can try to unlock again!"); - - return; - } - - SetWorkingStatus("Create backup partition...", null, null); - - MassStorage MassStorage = (MassStorage)Notifier.CurrentModel; - GPTChunk = MassStorage.ReadSectors(0, 0x100); - GPT = new GPT(GPTChunk); - BACKUP_EFIESP = GPT.GetPartition("BACKUP_EFIESP"); - byte[] BackupEFIESP = MassStorage.ReadSectors(BACKUP_EFIESP.FirstSector, BACKUP_EFIESP.SizeInSectors); - - LogFile.Log("Unlocking backup partition", LogType.FileAndConsole); - SetWorkingStatus("Unlocking backup partition", null, null); - - // Copy the backed up unlocked EFIESP for future use - byte[] BackupUnlockedEFIESP = new byte[UnlockedEFIESP.Length]; - Buffer.BlockCopy(BackupEFIESP, 0, BackupUnlockedEFIESP, 0, BackupEFIESP.Length); - - DiscUtils.Fat.FatFileSystem UnlockedBackedEFIESPFileSystem = new DiscUtils.Fat.FatFileSystem(new MemoryStream(BackupUnlockedEFIESP)); - - // Magic! - // This patch contains multiple hacks to disable SecureBoot, disable Bootpolicies and allow Mass Storage Mode on retail phones - App.PatchEngine.TargetImage = UnlockedBackedEFIESPFileSystem; - PatchResult = App.PatchEngine.Patch("SecureBootHack-V2-EFIESP"); - - // The patch to mobilestartup failed, get a new mobilestartup from the donor FFU instead - if (!PatchResult) - { - LogFile.Log("Donor-FFU: " + SupportedFFU.Path); - byte[] SupportedEFIESP = SupportedFFU.GetPartition("EFIESP"); - DiscUtils.Fat.FatFileSystem SupportedEFIESPFileSystem = new DiscUtils.Fat.FatFileSystem(new MemoryStream(SupportedEFIESP)); - DiscUtils.SparseStream SupportedMobileStartupStream = SupportedEFIESPFileSystem.OpenFile(@"\Windows\System32\Boot\mobilestartup.efi", FileMode.Open); - MemoryStream SupportedMobileStartupMemStream = new MemoryStream(); - SupportedMobileStartupStream.CopyTo(SupportedMobileStartupMemStream); - byte[] SupportedMobileStartup = SupportedMobileStartupMemStream.ToArray(); - SupportedMobileStartupMemStream.Close(); - SupportedMobileStartupStream.Close(); - - // Save supported mobilestartup.efi - LogFile.Log("Taking mobilestartup.efi from donor-FFU"); - Stream MobileStartupStream = UnlockedBackedEFIESPFileSystem.OpenFile(@"Windows\System32\Boot\mobilestartup.efi", FileMode.Create, FileAccess.Write); - MobileStartupStream.Write(SupportedMobileStartup, 0, SupportedMobileStartup.Length); - MobileStartupStream.Close(); - - App.PatchEngine.TargetImage = UnlockedBackedEFIESPFileSystem; - PatchResult = App.PatchEngine.Patch("SecureBootHack-V2-EFIESP"); - - // We shouldn't be there - if (!PatchResult) - throw new WPinternalsException("Failed to patch bootloader"); - } - - // Edit BCD - LogFile.Log("Edit BCD"); - using (Stream BCDFileStream = UnlockedBackedEFIESPFileSystem.OpenFile(@"efi\Microsoft\Boot\BCD", FileMode.Open, FileAccess.ReadWrite)) - { - using (DiscUtils.Registry.RegistryHive BCDHive = new DiscUtils.Registry.RegistryHive(BCDFileStream)) - { - DiscUtils.BootConfig.Store BCDStore = new DiscUtils.BootConfig.Store(BCDHive.Root); - DiscUtils.BootConfig.BcdObject MobileStartupObject = BCDStore.GetObject(new Guid("{01de5a27-8705-40db-bad6-96fa5187d4a6}")); - DiscUtils.BootConfig.Element NoCodeIntegrityElement = MobileStartupObject.GetElement(0x16000048); - if (NoCodeIntegrityElement != null) - NoCodeIntegrityElement.Value = DiscUtils.BootConfig.ElementValue.ForBoolean(true); - else - MobileStartupObject.AddElement(0x16000048, DiscUtils.BootConfig.ElementValue.ForBoolean(true)); - - DiscUtils.BootConfig.BcdObject WinLoadObject = BCDStore.GetObject(new Guid("{7619dcc9-fafe-11d9-b411-000476eba25f}")); - NoCodeIntegrityElement = WinLoadObject.GetElement(0x16000048); - if (NoCodeIntegrityElement != null) - NoCodeIntegrityElement.Value = DiscUtils.BootConfig.ElementValue.ForBoolean(true); - else - WinLoadObject.AddElement(0x16000048, DiscUtils.BootConfig.ElementValue.ForBoolean(true)); - } - } - - UnlockedBackedEFIESPFileSystem.Dispose(); - - SetWorkingStatus("Boot optimization...", null, null); - - App.PatchEngine.TargetPath = MassStorage.Drive + "\\"; - App.PatchEngine.Patch("SecureBootHack-MainOS"); // Don't care about result here. Some phones do not need this. - - LogFile.Log("The phone is currently in Mass Storage Mode", LogType.ConsoleOnly); - LogFile.Log("To continue the unlock-sequence, the phone needs to be rebooted", LogType.ConsoleOnly); - LogFile.Log("Keep the phone connected to the PC", LogType.ConsoleOnly); - LogFile.Log("Reboot the phone manually by pressing and holding the power-button of the phone for about 10 seconds until it vibrates", LogType.ConsoleOnly); - LogFile.Log("The unlock-sequence will resume automatically", LogType.ConsoleOnly); - LogFile.Log("Waiting for manual reset of the phone...", LogType.ConsoleOnly); - - SetWorkingStatus("You need to manually reset your phone now!", "The phone is currently in Mass Storage Mode. To continue the unlock-sequence, the phone needs to be rebooted. Keep the phone connected to the PC. Reboot the phone manually by pressing and holding the power-button of the phone for about 10 seconds until it vibrates. The unlock-sequence will resume automatically.", null, false, WPinternalsStatus.WaitingForManualReset); - - await Notifier.WaitForRemoval(); - - SetWorkingStatus("Rebooting phone..."); - - await Notifier.WaitForArrival(); - if (Notifier.CurrentInterface != PhoneInterfaces.Lumia_Bootloader) - throw new WPinternalsException("Phone is in wrong mode"); - - ((NokiaFlashModel)Notifier.CurrentModel).SwitchToFlashAppContext(); - - // EFIESP is appended at the end of the GPT - // BACKUP_EFIESP is at original location in GPT - EFIESP = GPT.GetPartition("EFIESP"); - UInt32 OriginalEfiespFirstSector = (UInt32)BACKUP_EFIESP.FirstSector; - BACKUP_EFIESP.Name = "EFIESP"; - BACKUP_EFIESP.LastSector = OriginalEfiespLastSector; // Do not hardcode the length of the partition, some phones have bigger EFIESP partitions than others. - BACKUP_EFIESP.PartitionGuid = EFIESP.PartitionGuid; - BACKUP_EFIESP.PartitionTypeGuid = EFIESP.PartitionTypeGuid; - GPT.Partitions.Remove(EFIESP); - - Partition IsUnlockedFlag = GPT.GetPartition("IS_UNLOCKED"); - if (IsUnlockedFlag == null) - { - IsUnlockedFlag = new Partition(); - IsUnlockedFlag.Name = "IS_UNLOCKED"; - IsUnlockedFlag.Attributes = 0; - IsUnlockedFlag.PartitionGuid = Guid.NewGuid(); - IsUnlockedFlag.PartitionTypeGuid = Guid.NewGuid(); - IsUnlockedFlag.FirstSector = 0x40; - IsUnlockedFlag.LastSector = 0x40; - GPT.Partitions.Add(IsUnlockedFlag); - } - - Parts = new List(); - GPT.Rebuild(); - Part = new FlashPart(); - Part.StartSector = 0; - Part.Stream = new MemoryStream(GPTChunk); - Part.ProgressText = "Flashing unlocked bootloader (part 2)..."; - Parts.Add(Part); - Part = new FlashPart(); - Part.StartSector = OriginalEfiespFirstSector; - Part.Stream = new MemoryStream(BackupUnlockedEFIESP); // We must keep the Oiriginal EFIESP, but unlocked, for many reasons - Parts.Add(Part); - Part = new FlashPart(); - Part.StartSector = OriginalEfiespFirstSector + ((OriginalEfiespSizeInSectors) / 2); - Part.Stream = new MemoryStream(BackupEFIESP); - Parts.Add(Part); - - await LumiaV2UnlockBootViewModel.LumiaV2CustomFlash(Notifier, ProfileFFU.Path, false, false, Parts, true, true, true, true, false, SetWorkingStatus, UpdateWorkingStatus, null, null, EDEPath); - - LogFile.Log("Bootloader unlocked!", LogType.FileAndConsole); - ExitSuccess("Bootloader unlocked successfully!", null); - } - catch (Exception Ex) - { - LogFile.LogException(Ex); - ExitFailure(Ex.Message, Ex is WPinternalsException ? ((WPinternalsException)Ex).SubMessage : null); - } - LogFile.EndAction("UnlockBootloader"); - } // Assumes phone with Flash protocol v2 // Assumes phone is in flash mode @@ -2503,7 +1973,7 @@ namespace WPinternals // Use GetGptChunk() here instead of ReadGPT(), because ReadGPT() skips the first sector. // We need the fist sector if we want to write back the GPT. - byte[] GPTChunk = GetGptChunk(FlashModel, 0x20000); + byte[] GPTChunk = LumiaUnlockBootloaderViewModel.GetGptChunk(FlashModel, 0x20000); GPT GPT = new GPT(GPTChunk); Partition Target; diff --git a/ViewModels/NokiaFlashViewModel.cs b/ViewModels/NokiaFlashViewModel.cs index fa9d258..9745239 100644 --- a/ViewModels/NokiaFlashViewModel.cs +++ b/ViewModels/NokiaFlashViewModel.cs @@ -92,6 +92,22 @@ namespace WPinternals { SecurityFlags = (UInt32)CurrentModel.ReadSecurityFlags(); LogFile.Log("Security flags: 0x" + SecurityFlags.ToString("X8")); + + FinalConfigDakStatus = CurrentModel.ReadFuseStatus(NokiaFlashModel.Fuse.Dak); + FinalConfigFastBootStatus = CurrentModel.ReadFuseStatus(NokiaFlashModel.Fuse.FastBoot); + FinalConfigFfuVerifyStatus = CurrentModel.ReadFuseStatus(NokiaFlashModel.Fuse.FfuVerify); + FinalConfigJtagStatus = CurrentModel.ReadFuseStatus(NokiaFlashModel.Fuse.Jtag); + FinalConfigOemIdStatus = CurrentModel.ReadFuseStatus(NokiaFlashModel.Fuse.OemId); + FinalConfigProductionDoneStatus = CurrentModel.ReadFuseStatus(NokiaFlashModel.Fuse.ProductionDone); + FinalConfigPublicIdStatus = CurrentModel.ReadFuseStatus(NokiaFlashModel.Fuse.PublicId); + FinalConfigRkhStatus = CurrentModel.ReadFuseStatus(NokiaFlashModel.Fuse.Rkh); + FinalConfigRpmWdogStatus = CurrentModel.ReadFuseStatus(NokiaFlashModel.Fuse.RpmWdog); + FinalConfigSecGenStatus = CurrentModel.ReadFuseStatus(NokiaFlashModel.Fuse.SecGen); + FinalConfigSecureBootStatus = CurrentModel.ReadFuseStatus(NokiaFlashModel.Fuse.SecureBoot); + FinalConfigShkStatus = CurrentModel.ReadFuseStatus(NokiaFlashModel.Fuse.Shk); + FinalConfigSimlockStatus = CurrentModel.ReadFuseStatus(NokiaFlashModel.Fuse.Simlock); + FinalConfigSpdmSecModeStatus = CurrentModel.ReadFuseStatus(NokiaFlashModel.Fuse.SpdmSecMode); + FinalConfigSsmStatus = CurrentModel.ReadFuseStatus(NokiaFlashModel.Fuse.Ssm); } else LogFile.Log("Security flags could not be read"); @@ -173,6 +189,9 @@ namespace WPinternals eMMC = Manufacturer + " " + MemSizeDouble.ToString() + " GB"; SamsungWarningVisible = (MID == 0x0015); + ChargingStatus = CurrentModel.ReadCurrentChargeLevel() + "% - " + CurrentModel.ReadCurrentChargeCurrent() + " mA"; + LogFile.Log("Charging status: " + ChargingStatus); + PhoneInfo Info = CurrentModel.ReadPhoneInfo(true); if (Info.FlashAppProtocolVersionMajor < 2) BootloaderDescription = "Lumia Bootloader Spec A"; @@ -293,6 +312,20 @@ namespace WPinternals } } + private string _ChargingStatus = null; + public string ChargingStatus + { + get + { + return _ChargingStatus; + } + set + { + _ChargingStatus = value; + OnPropertyChanged("ChargingStatus"); + } + } + private bool _SamsungWarningVisible = false; public bool SamsungWarningVisible { @@ -419,6 +452,218 @@ namespace WPinternals } } + #region Final Config + private bool? _FinalConfigSecureBootStatus = null; + public bool? FinalConfigSecureBootStatus + { + get + { + return _FinalConfigSecureBootStatus; + } + set + { + _FinalConfigSecureBootStatus = value; + OnPropertyChanged("FinalConfigSecureBootStatus"); + } + } + + private bool? _FinalConfigFfuVerifyStatus = null; + public bool? FinalConfigFfuVerifyStatus + { + get + { + return _FinalConfigFfuVerifyStatus; + } + set + { + _FinalConfigFfuVerifyStatus = value; + OnPropertyChanged("FinalConfigFfuVerifyStatus"); + } + } + + private bool? _FinalConfigJtagStatus = null; + public bool? FinalConfigJtagStatus + { + get + { + return _FinalConfigJtagStatus; + } + set + { + _FinalConfigJtagStatus = value; + OnPropertyChanged("FinalConfigJtagStatus"); + } + } + + private bool? _FinalConfigShkStatus = null; + public bool? FinalConfigShkStatus + { + get + { + return _FinalConfigShkStatus; + } + set + { + _FinalConfigShkStatus = value; + OnPropertyChanged("FinalConfigShkStatus"); + } + } + + private bool? _FinalConfigSimlockStatus = null; + public bool? FinalConfigSimlockStatus + { + get + { + return _FinalConfigSimlockStatus; + } + set + { + _FinalConfigSimlockStatus = value; + OnPropertyChanged("FinalConfigSimlockStatus"); + } + } + + private bool? _FinalConfigProductionDoneStatus = null; + public bool? FinalConfigProductionDoneStatus + { + get + { + return _FinalConfigProductionDoneStatus; + } + set + { + _FinalConfigProductionDoneStatus = value; + OnPropertyChanged("FinalConfigProductionDoneStatus"); + } + } + + private bool? _FinalConfigRkhStatus = null; + public bool? FinalConfigRkhStatus + { + get + { + return _FinalConfigRkhStatus; + } + set + { + _FinalConfigRkhStatus = value; + OnPropertyChanged("FinalConfigRkhStatus"); + } + } + + private bool? _FinalConfigPublicIdStatus = null; + public bool? FinalConfigPublicIdStatus + { + get + { + return _FinalConfigPublicIdStatus; + } + set + { + _FinalConfigPublicIdStatus = value; + OnPropertyChanged("FinalConfigPublicIdStatus"); + } + } + + private bool? _FinalConfigDakStatus = null; + public bool? FinalConfigDakStatus + { + get + { + return _FinalConfigDakStatus; + } + set + { + _FinalConfigDakStatus = value; + OnPropertyChanged("FinalConfigDakStatus"); + } + } + + private bool? _FinalConfigSecGenStatus = null; + public bool? FinalConfigSecGenStatus + { + get + { + return _FinalConfigSecGenStatus; + } + set + { + _FinalConfigSecGenStatus = value; + OnPropertyChanged("FinalConfigSecGenStatus"); + } + } + + private bool? _FinalConfigOemIdStatus = null; + public bool? FinalConfigOemIdStatus + { + get + { + return _FinalConfigOemIdStatus; + } + set + { + _FinalConfigOemIdStatus = value; + OnPropertyChanged("FinalConfigOemIdStatus"); + } + } + + private bool? _FinalConfigFastBootStatus = null; + public bool? FinalConfigFastBootStatus + { + get + { + return _FinalConfigFastBootStatus; + } + set + { + _FinalConfigFastBootStatus = value; + OnPropertyChanged("FinalConfigFastBootStatus"); + } + } + + private bool? _FinalConfigSpdmSecModeStatus = null; + public bool? FinalConfigSpdmSecModeStatus + { + get + { + return _FinalConfigSpdmSecModeStatus; + } + set + { + _FinalConfigSpdmSecModeStatus = value; + OnPropertyChanged("FinalConfigSpdmSecModeStatus"); + } + } + + private bool? _FinalConfigRpmWdogStatus = null; + public bool? FinalConfigRpmWdogStatus + { + get + { + return _FinalConfigRpmWdogStatus; + } + set + { + _FinalConfigRpmWdogStatus = value; + OnPropertyChanged("FinalConfigRpmWdogStatus"); + } + } + + private bool? _FinalConfigSsmStatus = null; + public bool? FinalConfigSsmStatus + { + get + { + return _FinalConfigSsmStatus; + } + set + { + _FinalConfigSsmStatus = value; + OnPropertyChanged("FinalConfigSsmStatus"); + } + } + #endregion + internal void RebootTo(string Mode) { switch (Mode) diff --git a/ViewModels/NokiaModeMassStorageViewModel.cs b/ViewModels/NokiaModeMassStorageViewModel.cs index d155e2d..cc9d95e 100644 --- a/ViewModels/NokiaModeMassStorageViewModel.cs +++ b/ViewModels/NokiaModeMassStorageViewModel.cs @@ -18,46 +18,59 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. +using System; using System.Collections.Generic; namespace WPinternals { internal class NokiaModeMassStorageViewModel : ContextViewModel { - private NokiaPhoneModel CurrentModel; + private MassStorage CurrentModel; + private Action RequestModeSwitch; - internal NokiaModeMassStorageViewModel(NokiaPhoneModel CurrentModel) + internal NokiaModeMassStorageViewModel(NokiaPhoneModel CurrentModel, Action RequestModeSwitch) : base() { - this.CurrentModel = CurrentModel; + this.CurrentModel = (MassStorage)CurrentModel; + this.RequestModeSwitch = RequestModeSwitch; } - internal void RebootTo(string Mode) + private bool _SupportsReboot = false; + public bool SupportsReboot { - string DeviceMode; + get + { + return _SupportsReboot; + } + set + { + _SupportsReboot = value; + OnPropertyChanged("SupportsReboot"); + } + } + internal override void EvaluateViewState() + { + if (IsActive) + SupportsReboot = CurrentModel.DoesDeviceSupportReboot(); + } + + public void RebootTo(string Mode) + { switch (Mode) { - case "Flash": - DeviceMode = "Flash"; - LogFile.Log("Reboot to Flash"); + case "Normal": + RequestModeSwitch(PhoneInterfaces.Lumia_Normal); break; case "Label": - DeviceMode = "Test"; - LogFile.Log("Reboot to Label"); + RequestModeSwitch(PhoneInterfaces.Lumia_Label); break; - case "MassStorage": - DeviceMode = "Flash"; // TODO: implement folow-up - LogFile.Log("Reboot to Mass Storage"); + case "Flash": + RequestModeSwitch(PhoneInterfaces.Lumia_Flash); break; default: return; } - - Dictionary Params = new Dictionary(); - Params.Add("DeviceMode", DeviceMode); - Params.Add("ResetMethod", "HwReset"); - CurrentModel.ExecuteJsonMethodAsync("SetDeviceMode", Params); } } } diff --git a/ViewModels/NokiaNormalViewModel.cs b/ViewModels/NokiaNormalViewModel.cs index f6829cf..f5ae454 100644 --- a/ViewModels/NokiaNormalViewModel.cs +++ b/ViewModels/NokiaNormalViewModel.cs @@ -54,15 +54,28 @@ namespace WPinternals LogFile.Log("Product Code: " + ProductCode); Firmware = CurrentModel.ExecuteJsonMethodAsString("ReadSwVersion", "SwVersion"); // 3051.40000.1349.0007 LogFile.Log("Firmware: " + Firmware); + HWID = CurrentModel.ExecuteJsonMethodAsString("ReadHwVersion", "HWVersion"); // 1002 + LogFile.Log("HWID: " + HWID); + + IMEI = CurrentModel.ExecuteJsonMethodAsString("ReadSerialNumber", new System.Collections.Generic.Dictionary() { { "SubscriptionId", 0 } }, "SerialNumber"); // IMEI + string IMEI2 = CurrentModel.ExecuteJsonMethodAsString("ReadSerialNumber", new System.Collections.Generic.Dictionary() { { "SubscriptionId", 1 } }, "SerialNumber"); // IMEI 2 + + if (!string.IsNullOrEmpty(IMEI2)) + IMEI += "\n" + IMEI2; - IMEI = CurrentModel.ExecuteJsonMethodAsString("ReadSerialNumber", "SerialNumber"); // IMEI LogFile.Log("IMEI: " + IMEI); PublicID = CurrentModel.ExecuteJsonMethodAsBytes("ReadPublicId", "PublicId"); // 0x14 bytes: a5 e5 ... LogFile.Log("Public ID: " + Converter.ConvertHexToString(PublicID, " ")); BluetoothMac = CurrentModel.ExecuteJsonMethodAsBytes("ReadBtId", "BtId"); // 6 bytes: bc c6 ... LogFile.Log("Bluetooth MAC: " + Converter.ConvertHexToString(BluetoothMac, " ")); - WlanMac = CurrentModel.ExecuteJsonMethodAsBytes("ReadWlanMacAddress", "WlanMacAddress1"); // 6 bytes - LogFile.Log("WLAN MAC: " + Converter.ConvertHexToString(WlanMac, " ")); + WlanMac1 = CurrentModel.ExecuteJsonMethodAsBytes("ReadWlanMacAddress", "WlanMacAddress1"); // 6 bytes + LogFile.Log("WLAN MAC 1: " + Converter.ConvertHexToString(WlanMac1, " ")); + WlanMac2 = CurrentModel.ExecuteJsonMethodAsBytes("ReadWlanMacAddress", "WlanMacAddress2"); // 6 bytes + LogFile.Log("WLAN MAC 2: " + Converter.ConvertHexToString(WlanMac2, " ")); + WlanMac3 = CurrentModel.ExecuteJsonMethodAsBytes("ReadWlanMacAddress", "WlanMacAddress3"); // 6 bytes + LogFile.Log("WLAN MAC 3: " + Converter.ConvertHexToString(WlanMac3, " ")); + WlanMac4 = CurrentModel.ExecuteJsonMethodAsBytes("ReadWlanMacAddress", "WlanMacAddress4"); // 6 bytes + LogFile.Log("WLAN MAC 4: " + Converter.ConvertHexToString(WlanMac4, " ")); bool? ProductionDone = CurrentModel.ExecuteJsonMethodAsBoolean("ReadProductionDoneState", "ProductionDone"); if (ProductionDone == null) @@ -72,6 +85,23 @@ namespace WPinternals LogFile.Log("Bootloader Security: " + ((bool)IsBootloaderSecurityEnabled ? "Enabled" : "Disabled")); IsSimLocked = CurrentModel.ExecuteJsonMethodAsBoolean("ReadSimlockActive", "SimLockActive"); LogFile.Log("Simlock: " + ((bool)IsSimLocked ? "Active" : "Unlocked")); + + string BootPolicy = CurrentModel.ExecuteJsonMethodAsString("GetUefiCertificateStatus", "BootPolicy"); + string Db = CurrentModel.ExecuteJsonMethodAsString("GetUefiCertificateStatus", "Db"); + string Dbx = CurrentModel.ExecuteJsonMethodAsString("GetUefiCertificateStatus", "Dbx"); + string Kek = CurrentModel.ExecuteJsonMethodAsString("GetUefiCertificateStatus", "Kek"); + string Pk = CurrentModel.ExecuteJsonMethodAsString("GetUefiCertificateStatus", "Pk"); + + this.BootPolicy = BootPolicy; + LogFile.Log("Boot policy: " + BootPolicy); + this.Db = Db; + LogFile.Log("DB: " + Db); + this.Dbx = Dbx; + LogFile.Log("DBX: " + Dbx); + this.Kek = Kek; + LogFile.Log("KEK: " + Kek); + this.Pk = Pk; + LogFile.Log("PK: " + Pk); } catch { } } @@ -132,6 +162,20 @@ namespace WPinternals } } + private string _HWID = null; + public string HWID + { + get + { + return _HWID; + } + set + { + _HWID = value; + OnPropertyChanged("HWID"); + } + } + private string _IMEI = null; public string IMEI { @@ -146,6 +190,80 @@ namespace WPinternals } } + private string _BootPolicy = null; + public string BootPolicy + { + get + { + return _BootPolicy; + } + set + { + _BootPolicy = value; + OnPropertyChanged("BootPolicy"); + } + } + + + private string _Db = null; + public string Db + { + get + { + return _Db; + } + set + { + _Db = value; + OnPropertyChanged("Db"); + } + } + + + private string _Dbx = null; + public string Dbx + { + get + { + return _Dbx; + } + set + { + _Dbx = value; + OnPropertyChanged("Dbx"); + } + } + + + private string _Kek = null; + public string Kek + { + get + { + return _Kek; + } + set + { + _Kek = value; + OnPropertyChanged("Kek"); + } + } + + + private string _Pk = null; + public string Pk + { + get + { + return _Pk; + } + set + { + _Pk = value; + OnPropertyChanged("Pk"); + } + } + private byte[] _PublicID = null; public byte[] PublicID { @@ -160,17 +278,59 @@ namespace WPinternals } } - private byte[] _WlanMac = null; - public byte[] WlanMac + private byte[] _WlanMac1 = null; + public byte[] WlanMac1 { get { - return _WlanMac; + return _WlanMac1; } set { - _WlanMac = value; - OnPropertyChanged("WlanMac"); + _WlanMac1 = value; + OnPropertyChanged("WlanMac1"); + } + } + + private byte[] _WlanMac2 = null; + public byte[] WlanMac2 + { + get + { + return _WlanMac2; + } + set + { + _WlanMac2 = value; + OnPropertyChanged("WlanMac2"); + } + } + + private byte[] _WlanMac3 = null; + public byte[] WlanMac3 + { + get + { + return _WlanMac3; + } + set + { + _WlanMac3 = value; + OnPropertyChanged("WlanMac3"); + } + } + + private byte[] _WlanMac4 = null; + public byte[] WlanMac4 + { + get + { + return _WlanMac4; + } + set + { + _WlanMac4 = value; + OnPropertyChanged("WlanMac4"); } } diff --git a/ViewModels/PhoneNotifierViewModel.cs b/ViewModels/PhoneNotifierViewModel.cs index bb3aa76..3ba7db9 100644 --- a/ViewModels/PhoneNotifierViewModel.cs +++ b/ViewModels/PhoneNotifierViewModel.cs @@ -39,6 +39,7 @@ namespace WPinternals private USBNotifier StorageNotifier; private USBNotifier ComPortNotifier; private USBNotifier LumiaEmergencyNotifier; + private USBNotifier LumiaLabelNotifier; public PhoneInterfaces? CurrentInterface = null; private PhoneInterfaces? LastInterface = null; @@ -54,6 +55,7 @@ namespace WPinternals private Guid LumiaFlashInterfaceGuid = new Guid("{9e3bd5f7-9690-4fcc-8810-3e2650cd6ecc}"); private Guid ComPortInterfaceGuid = new Guid("{86E0D1E0-8089-11D0-9CE4-08003E301F73}"); private Guid LumiaEmergencyInterfaceGuid = new Guid("{71DE994D-8B7C-43DB-A27E-2AE7CD579A0C}"); + private Guid LumiaLabelInterfaceGuid = new Guid("{F4FE0C27-7304-4ED7-AAB5-130893B84B6F}"); private object ModelLock = new object(); @@ -61,6 +63,8 @@ namespace WPinternals private EventLogWatcher LogWatcher; + private string Qcom9006DevicePath; + internal void Start() { LumiaOldCombiNotifier = new USBNotifier(OldCombiInterfaceGuid); @@ -91,6 +95,10 @@ namespace WPinternals LumiaEmergencyNotifier.Arrival += LumiaNotifier_Arrival; LumiaEmergencyNotifier.Removal += LumiaNotifier_Removal; + LumiaLabelNotifier = new USBNotifier(LumiaLabelInterfaceGuid); + LumiaLabelNotifier.Arrival += LumiaNotifier_Arrival; + LumiaLabelNotifier.Removal += LumiaNotifier_Removal; + try { EventLogQuery LogQuery = new EventLogQuery("Microsoft-Windows-Kernel-PnP/Configuration", PathType.LogName, "*[System[(EventID = 411)]]"); @@ -143,7 +151,8 @@ namespace WPinternals { try { - if (e.DevicePath.IndexOf("VID_0421&PID_0660&MI_04", StringComparison.OrdinalIgnoreCase) >= 0) + if ((e.DevicePath.IndexOf("VID_0421&PID_0660&MI_04", StringComparison.OrdinalIgnoreCase) >= 0) || + (e.DevicePath.IndexOf("VID_0421&PID_0713&MI_04", StringComparison.OrdinalIgnoreCase) >= 0)) // for Spec B { CurrentInterface = PhoneInterfaces.Lumia_Label; CurrentModel = new NokiaPhoneModel(e.DevicePath); @@ -253,6 +262,10 @@ namespace WPinternals if (!(CurrentModel is MassStorage)) { MassStorage NewModel = new MassStorage(e.DevicePath); + + if (!string.IsNullOrEmpty(Qcom9006DevicePath)) + NewModel.AttachQualcommSerial(Qcom9006DevicePath); + if (NewModel.Drive != null) // When logical drive is already known, we use this model. Or else we wait for the logical drive to arrive. { CurrentInterface = PhoneInterfaces.Lumia_MassStorage; @@ -284,6 +297,10 @@ namespace WPinternals if (!(CurrentModel is MassStorage)) { MassStorage NewModel = new MassStorage(e.DevicePath); + + if (!string.IsNullOrEmpty(Qcom9006DevicePath)) + NewModel.AttachQualcommSerial(Qcom9006DevicePath); + if (NewModel.Drive != null) // When logical drive is already known, we use this model. Or else we wait for the logical drive to arrive. { CurrentInterface = PhoneInterfaces.Lumia_MassStorage; @@ -339,6 +356,8 @@ namespace WPinternals LogFile.Log("Device path: " + e.DevicePath, LogType.FileOnly); LogFile.Log("Connected device: Lumia", LogType.FileAndConsole); LogFile.Log("Mode: Qualcomm Emergency 9006", LogType.FileAndConsole); + + Qcom9006DevicePath = e.DevicePath; } } catch (Exception Ex) @@ -356,8 +375,14 @@ namespace WPinternals void LumiaNotifier_Removal(object sender, USBEvent e) { + if (e.DevicePath.IndexOf("VID_05C6&PID_9006", StringComparison.OrdinalIgnoreCase) >= 0) + { + Qcom9006DevicePath = null; + } + if ( (e.DevicePath.IndexOf("VID_0421&PID_0660&MI_04", StringComparison.OrdinalIgnoreCase) >= 0) || + (e.DevicePath.IndexOf("VID_0421&PID_0713&MI_04", StringComparison.OrdinalIgnoreCase) >= 0) || (e.DevicePath.IndexOf("VID_0421&PID_0661", StringComparison.OrdinalIgnoreCase) >= 0) || (e.DevicePath.IndexOf("VID_0421&PID_06FC", StringComparison.OrdinalIgnoreCase) >= 0) || (e.DevicePath.IndexOf("VID_0421&PID_066E", StringComparison.OrdinalIgnoreCase) >= 0) || diff --git a/ViewModels/SwitchModeViewModel.cs b/ViewModels/SwitchModeViewModel.cs index b83ff24..0c5f241 100644 --- a/ViewModels/SwitchModeViewModel.cs +++ b/ViewModels/SwitchModeViewModel.cs @@ -20,6 +20,7 @@ using System; using System.Collections.Generic; +using System.IO; using System.Threading; using System.Threading.Tasks; @@ -211,10 +212,10 @@ namespace WPinternals LogFile.Log("Rebooting phone to Flash mode", LogType.FileAndConsole); break; case PhoneInterfaces.Lumia_Label: - DeviceMode = "Test"; + DeviceMode = "Flash"; IsSwitchingInterface = true; - ModeSwitchProgressWrapper("Rebooting phone to Label mode...", null); - LogFile.Log("Rebooting phone to Label mode", LogType.FileAndConsole); + ModeSwitchProgressWrapper("First rebooting phone to Flash mode...", null); + LogFile.Log("First rebooting phone to Flash mode (then attempt Label mode)", LogType.FileAndConsole); break; case PhoneInterfaces.Lumia_MassStorage: DeviceMode = "Flash"; @@ -259,8 +260,11 @@ namespace WPinternals ((NokiaFlashModel)CurrentModel).Shutdown(); ModeSwitchProgressWrapper("Shutting down phone...", null); LogFile.Log("Shutting down phone", LogType.FileAndConsole); - PhoneNotifier.WaitForRemoval().Wait(); - ModeSwitchSuccessWrapper(); + new Thread(() => + { + PhoneNotifier.WaitForRemoval().Wait(); + //ModeSwitchSuccessWrapper(); TODO: Display UI + }).Start(); break; case PhoneInterfaces.Lumia_Normal: ((NokiaPhoneModel)CurrentModel).ExecuteRawVoidMethod(RebootCommand); @@ -275,12 +279,7 @@ namespace WPinternals LogFile.Log("Rebooting phone to Bootloader mode", LogType.FileAndConsole); break; case PhoneInterfaces.Lumia_Label: - BootModeFlagCommand[0x0F] = 0x59; - ((NokiaPhoneModel)CurrentModel).ExecuteRawMethod(BootModeFlagCommand); - ((NokiaPhoneModel)CurrentModel).ExecuteRawVoidMethod(RebootCommand); - PhoneNotifier.NewDeviceArrived += NewDeviceArrived; - ModeSwitchProgressWrapper("Rebooting phone to Label mode...", null); - LogFile.Log("Rebooting phone to Label mode", LogType.FileAndConsole); + SwitchFromFlashToLabelMode(); break; case PhoneInterfaces.Lumia_Flash: // attempt to boot from limited flash to full flash byte[] RebootToFlashCommand = new byte[] { 0x4E, 0x4F, 0x4B, 0x53 }; // NOKS @@ -312,7 +311,30 @@ namespace WPinternals } break; case PhoneInterfaces.Lumia_MassStorage: - // TODO: don't know how to switch from Mass Storage to other mode + IsSwitchingInterface = true; + switch (TargetMode) + { + case PhoneInterfaces.Lumia_Normal: + ((MassStorage)CurrentModel).Reboot(); + PhoneNotifier.NewDeviceArrived += NewDeviceArrived; + ModeSwitchProgressWrapper("Rebooting phone to Normal mode...", null); + LogFile.Log("Rebooting phone to Normal mode", LogType.FileAndConsole); + break; + case PhoneInterfaces.Lumia_Label: + ((MassStorage)CurrentModel).Reboot(); + PhoneNotifier.NewDeviceArrived += NewDeviceArrivedFromMassStorageMode; + ModeSwitchProgressWrapper("Rebooting phone to Label mode...", null); + LogFile.Log("Rebooting phone to Label mode...", LogType.FileAndConsole); + break; + case PhoneInterfaces.Lumia_Flash: + ((MassStorage)CurrentModel).Reboot(); + PhoneNotifier.NewDeviceArrived += NewDeviceArrivedFromMassStorageMode; + ModeSwitchProgressWrapper("Rebooting phone to Flash mode...", null); + LogFile.Log("Rebooting phone to Flash mode...", LogType.FileAndConsole); + break; + default: + return; + } break; case PhoneInterfaces.Qualcomm_Download: // TODO: don't know how to switch from Qualcomm Download mode to other mode @@ -320,6 +342,59 @@ namespace WPinternals } } + private void NewDeviceArrivedFromMassStorageMode(ArrivalEventArgs Args) + { + PhoneNotifier.NewDeviceArrived -= NewDeviceArrivedFromMassStorageMode; + + CurrentModel = (IDisposable)Args.NewModel; + CurrentMode = Args.NewInterface; + + // After the mass storage mode reboot command, the phone must be in Bootloader mode. + // If it isn't, something unexpected happened and the phone can't be switched. + // + if (CurrentMode == PhoneInterfaces.Lumia_Bootloader) + { + Task.Run(async () => + { + try + { + await SwitchToWithStatus(PhoneNotifier, TargetMode, SetWorkingStatus, UpdateWorkingStatus); + ModeSwitchSuccessWrapper(); + } + catch + { + switch (TargetMode) + { + case PhoneInterfaces.Lumia_Flash: + ModeSwitchErrorWrapper("Failed to switch to Flash mode"); + break; + case PhoneInterfaces.Lumia_Label: + ModeSwitchErrorWrapper("Failed to switch to Label mode"); + break; + case PhoneInterfaces.Lumia_Normal: + ModeSwitchErrorWrapper("Failed to switch to Normal mode"); + break; + } + } + }); + } + else + { + switch (TargetMode) + { + case PhoneInterfaces.Lumia_Flash: + ModeSwitchErrorWrapper("Failed to switch to Flash mode"); + break; + case PhoneInterfaces.Lumia_Label: + ModeSwitchErrorWrapper("Failed to switch to Label mode"); + break; + case PhoneInterfaces.Lumia_Normal: + ModeSwitchErrorWrapper("Failed to switch to Normal mode"); + break; + } + } + } + private void NewDeviceArrived(ArrivalEventArgs Args) { PhoneNotifier.NewDeviceArrived -= NewDeviceArrived; @@ -363,6 +438,10 @@ namespace WPinternals { SwitchFromFlashToMassStorageMode(Continuation: true); } + else if ((CurrentMode == PhoneInterfaces.Lumia_Flash) && (TargetMode == PhoneInterfaces.Lumia_Label)) + { + SwitchFromFlashToLabelMode(Continuation: true); + } else if ((CurrentMode == PhoneInterfaces.Lumia_Flash) && (TargetMode == PhoneInterfaces.Qualcomm_Download)) { byte[] RebootCommand = new byte[] { 0x4E, 0x4F, 0x4B, 0x52 }; @@ -407,6 +486,93 @@ namespace WPinternals } } + private void SwitchFromFlashToLabelMode(bool Continuation = false) + { + string ProgressText; + if (Continuation) + ProgressText = "And now preparing to boot the phone to Label mode..."; + else + ProgressText = "Preparing to boot the phone to Label mode..."; + + NokiaFlashModel FlashModel = (NokiaFlashModel)CurrentModel; + if (CurrentMode == PhoneInterfaces.Lumia_Bootloader) + { + try + { + FlashModel.SwitchToFlashAppContext(); + } + catch { } + } + PhoneInfo Info = FlashModel.ReadPhoneInfo(ExtendedInfo: true); + + if (Info.MmosOverUsbSupported) + { + new Thread(() => + { + LogFile.BeginAction("SwitchToLabelMode"); + + try + { + ModeSwitchProgressWrapper(ProgressText, null); + string TempFolder = Environment.GetEnvironmentVariable("TEMP") + @"\WPInternals"; + string ENOSWPackage = LumiaDownloadModel.SearchENOSW(Info.Type, Info.Firmware); + SetWorkingStatus("Downloading " + Info.Type + " Test Mode package...", MaxProgressValue: 100); + DownloadEntry downloadEntry = new DownloadEntry(ENOSWPackage, TempFolder, null, null, null); + + downloadEntry.PropertyChanged += (object sender, System.ComponentModel.PropertyChangedEventArgs e) => + { + if (e.PropertyName == "Progress") + { + int progress = (sender as DownloadEntry).Progress; + ulong progressret; + ulong.TryParse(progress.ToString(), out progressret); + UpdateWorkingStatus(null, CurrentProgressValue: progressret); + + if (progress == 100) + { + ModeSwitchProgressWrapper("Initializing Flash...", null); + + string MMOSPath = TempFolder + "\\" + (sender as DownloadEntry).Name; + + PhoneNotifier.NewDeviceArrived += NewDeviceArrived; + FileInfo info = new FileInfo(MMOSPath); + uint length = uint.Parse(info.Length.ToString()); + int maximumbuffersize = 0x00240000; + uint totalcounts = (uint)Math.Truncate((decimal)length / maximumbuffersize); + + SetWorkingStatus("Flashing Test Mode package...", MaxProgressValue: 100); + + ProgressUpdater progressUpdater = new ProgressUpdater(totalcounts + 1, (int i, TimeSpan? time) => UpdateWorkingStatus(null, CurrentProgressValue: (ulong)i)); + FlashModel.FlashMMOS(MMOSPath, progressUpdater); + + SetWorkingStatus("And now booting phone to MMOS...", "If the phone stays on the lightning cog screen for a while, you may need to unplug and replug the phone to continue the boot process."); + } + } + }; + } + catch (Exception Ex) + { + LogFile.LogException(Ex); + ModeSwitchErrorWrapper(Ex.Message); + } + + LogFile.EndAction("SwitchToLabelMode"); + }).Start(); + } + else + { + byte[] BootModeFlagCommand = new byte[] { 0x4E, 0x4F, 0x4B, 0x58, 0x46, 0x57, 0x00, 0x55, 0x42, 0x46, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00 }; // NOKFW UBF + byte[] RebootCommand = new byte[] { 0x4E, 0x4F, 0x4B, 0x52 }; // NOKR + + BootModeFlagCommand[0x0F] = 0x59; + ((NokiaPhoneModel)CurrentModel).ExecuteRawMethod(BootModeFlagCommand); + ((NokiaPhoneModel)CurrentModel).ExecuteRawVoidMethod(RebootCommand); + PhoneNotifier.NewDeviceArrived += NewDeviceArrived; + ModeSwitchProgressWrapper("Rebooting phone to Label mode", null); + LogFile.Log("Rebooting phone to Label mode", LogType.FileAndConsole); + } + } + private void SwitchFromFlashToMassStorageMode(bool Continuation = false) { string ProgressText; @@ -442,7 +608,7 @@ namespace WPinternals if (IsNewLumia) { GPT GPT = FlashModel.ReadGPT(); - IsUnlockedNew = ((GPT.GetPartition("IS_UNLOCKED") != null) || (GPT.GetPartition("BACKUP_EFIESP") != null)); + IsUnlockedNew = ((GPT.GetPartition("IS_UNLOCKED") != null) || (GPT.GetPartition("BACKUP_EFIESP") != null) || (GPT.GetPartition("BACKUP_BS_NV") != null)); } bool IsOriginalEngineeringLumia = ((!Info.SecureFfuEnabled || Info.Authenticated || Info.RdcPresent) && !IsUnlockedNew); diff --git a/Views/FlashResourcesView.xaml b/Views/FlashResourcesView.xaml index efe747c..8e27aad 100644 --- a/Views/FlashResourcesView.xaml +++ b/Views/FlashResourcesView.xaml @@ -100,6 +100,26 @@ DEALINGS IN THE SOFTWARE. + + + + + + + + + + + + + + + + + + + + diff --git a/Views/FlashRomView.xaml b/Views/FlashRomView.xaml index 238ed04..2c2d3ed 100644 --- a/Views/FlashRomView.xaml +++ b/Views/FlashRomView.xaml @@ -110,7 +110,7 @@ DEALINGS IN THE SOFTWARE. - + @@ -156,5 +156,39 @@ DEALINGS IN THE SOFTWARE. + + + + + + + + + + + + + + + + + + + + + + + + + + +