mirror of
https://github.com/ReneLergner/WPinternals.git
synced 2026-06-14 03:16:40 +10:00
1653 lines
69 KiB
C#
1653 lines
69 KiB
C#
// 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 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
|
|
{
|
|
// Create this class on the UI thread, after the main-window of the application is initialized.
|
|
// It is necessary to create the object on the UI thread, because notification events to the View need to be fired on that thread.
|
|
// The Model for this ViewModel communicates over USB and for that it uses the hWnd of the main window.
|
|
// Therefore the main window must be created before the ViewModel is created.
|
|
|
|
internal enum MachineState
|
|
{
|
|
Default,
|
|
LumiaSpecAGetGPT,
|
|
LumiaSpecBUnlockBoot
|
|
};
|
|
|
|
internal class LumiaUnlockBootViewModel : ContextViewModel
|
|
{
|
|
private PhoneNotifierViewModel PhoneNotifier;
|
|
private Action Callback;
|
|
private bool IsFlashingDone = false;
|
|
private string FFUPath;
|
|
private string LoadersPath;
|
|
private string SBL3Path;
|
|
private string ProfileFFUPath;
|
|
private string EDEPath;
|
|
private string SupportedFFUPath;
|
|
private bool IsBootLoaderUnlocked;
|
|
private byte[] RootKeyHash = null;
|
|
private List<QualcommPartition> 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;
|
|
private bool DoUnlock;
|
|
private MachineState State;
|
|
private object EvaluateViewStateLockObject = new object();
|
|
|
|
internal LumiaUnlockBootViewModel(PhoneNotifierViewModel PhoneNotifier, Action SwitchToFlashRom, Action SwitchToUndoRoot, Action SwitchToDownload, bool DoUnlock, Action Callback)
|
|
: base()
|
|
{
|
|
IsSwitchingInterface = false;
|
|
IsFlashModeOperation = true;
|
|
|
|
this.PhoneNotifier = PhoneNotifier;
|
|
this.SwitchToFlashRom = SwitchToFlashRom;
|
|
this.SwitchToUndoRoot = SwitchToUndoRoot;
|
|
this.SwitchToDownload = SwitchToDownload;
|
|
this.DoUnlock = DoUnlock;
|
|
this.Callback = Callback;
|
|
|
|
State = MachineState.Default;
|
|
|
|
this.PhoneNotifier.NewDeviceArrived += NewDeviceArrived;
|
|
|
|
// ViewState will be evaluated as soon as this object is set as DataContext
|
|
}
|
|
|
|
~LumiaUnlockBootViewModel()
|
|
{
|
|
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))
|
|
return;
|
|
|
|
lock (EvaluateViewStateLockObject)
|
|
{
|
|
switch (PhoneNotifier.CurrentInterface)
|
|
{
|
|
case PhoneInterfaces.Lumia_Normal:
|
|
case PhoneInterfaces.Lumia_Label:
|
|
IsSwitchingInterface = false;
|
|
if (IsFlashingDone)
|
|
{
|
|
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));
|
|
}
|
|
}
|
|
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));
|
|
}
|
|
}
|
|
break;
|
|
case PhoneInterfaces.Lumia_Flash:
|
|
// Display View with device info and request for resources
|
|
// Click on "Continue" will start processing all resources
|
|
// Processing may fail with error message
|
|
// Or processing will succeed and user will again be asked to continue with Bricking-procedure (to switch to emergency mode)
|
|
|
|
// This code is not always invoked by OnArrival event.
|
|
// 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..."));
|
|
|
|
new Thread(() =>
|
|
{
|
|
RecoverFromFlashMode();
|
|
}).Start();
|
|
}
|
|
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)
|
|
{
|
|
// This action is executed after the resources are selected by the user.
|
|
Action<string, string, string, string, string, string, bool> 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();
|
|
|
|
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<string, string, string, string, string, string, bool> 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);
|
|
});
|
|
}
|
|
else
|
|
{
|
|
Task.Run(async () =>
|
|
{
|
|
await LumiaV2UnlockBootViewModel.LumiaV2RelockPhone(PhoneNotifier, null, 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;
|
|
|
|
// If resources are not confirmed yet, then display view with device info and request for resources.
|
|
QualcommDownload Download = new QualcommDownload((QualcommSerial)PhoneNotifier.CurrentModel);
|
|
byte[] QualcommRootKeyHash = Download.GetRKH();
|
|
if (RootKeyHash == null)
|
|
RootKeyHash = QualcommRootKeyHash;
|
|
else if (!StructuralComparisons.StructuralEqualityComparer.Equals(RootKeyHash, QualcommRootKeyHash))
|
|
{
|
|
LogFile.Log("Error: Root Key Hash in Qualcomm Emergency mode does not match!");
|
|
ActivateSubContext(new MessageViewModel("Error: Root Key Hash in Qualcomm Emergency mode does not match!", Callback));
|
|
return;
|
|
}
|
|
|
|
if ((this.FFUPath == null) || (this.PossibleLoaders == null) || (this.PossibleLoaders.Count == 0))
|
|
{
|
|
// This action is executed after the user selected the resources.
|
|
Action<string, string, string, string, string, string, bool> 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();
|
|
|
|
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();
|
|
};
|
|
|
|
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();
|
|
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..."));
|
|
}
|
|
break;
|
|
default:
|
|
// Show View "Waiting for connection"
|
|
IsSwitchingInterface = false;
|
|
ActivateSubContext(null);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
private void SwitchToFlashMode()
|
|
{
|
|
// SwitchModeViewModel must be created on the UI thread
|
|
IsSwitchingInterface = true;
|
|
UIContext.Post(async (t) =>
|
|
{
|
|
LogFile.Log("Switching to Flash-mode");
|
|
|
|
try
|
|
{
|
|
await SwitchModeViewModel.SwitchToWithProgress(PhoneNotifier, PhoneInterfaces.Lumia_Flash,
|
|
(msg, sub) =>
|
|
ActivateSubContext(new BusyViewModel(msg, sub)));
|
|
}
|
|
catch (Exception Ex)
|
|
{
|
|
ActivateSubContext(new MessageViewModel(Ex.Message, Callback));
|
|
}
|
|
}, null);
|
|
}
|
|
|
|
private void Abort()
|
|
{
|
|
// SwitchModeViewModel must be created on the UI thread
|
|
IsSwitchingInterface = false;
|
|
UIContext.Post((t) =>
|
|
{
|
|
StorePaths();
|
|
LogFile.Log("Aborting.");
|
|
Exit();
|
|
}, null);
|
|
}
|
|
|
|
private void StorePaths()
|
|
{
|
|
RegistryKey Key = Registry.CurrentUser.OpenSubKey(@"Software\WPInternals", true);
|
|
if (Key == null)
|
|
Key = Registry.CurrentUser.CreateSubKey(@"Software\WPInternals");
|
|
|
|
if (FFUPath == null)
|
|
{
|
|
if (Key.GetValue("FFUPath") != null)
|
|
Key.DeleteValue("FFUPath");
|
|
}
|
|
else
|
|
Key.SetValue("FFUPath", FFUPath);
|
|
|
|
if (LoadersPath == null)
|
|
{
|
|
if (Key.GetValue("LoadersPath") != null)
|
|
Key.DeleteValue("LoadersPath");
|
|
}
|
|
else
|
|
Key.SetValue("LoadersPath", LoadersPath);
|
|
|
|
if (DoUnlock)
|
|
{
|
|
if (SBL3Path == null)
|
|
{
|
|
if (Key.GetValue("SBL3Path") != null)
|
|
Key.DeleteValue("SBL3Path");
|
|
}
|
|
else
|
|
Key.SetValue("SBL3Path", SBL3Path);
|
|
}
|
|
|
|
NokiaFlashModel Model = (NokiaFlashModel)PhoneNotifier.CurrentModel;
|
|
PhoneInfo Info = Model.ReadPhoneInfo();
|
|
|
|
if (ProfileFFUPath == null)
|
|
{
|
|
if (Key.GetValue("ProfileFFUPath") != null)
|
|
Key.DeleteValue("ProfileFFUPath");
|
|
}
|
|
else
|
|
{
|
|
Key.SetValue("ProfileFFUPath", ProfileFFUPath);
|
|
|
|
App.Config.AddFfuToRepository(ProfileFFUPath);
|
|
}
|
|
|
|
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)
|
|
{
|
|
Key.SetValue("SupportedFFUPath", SupportedFFUPath);
|
|
|
|
App.Config.AddFfuToRepository(SupportedFFUPath);
|
|
}
|
|
|
|
Key.Close();
|
|
}
|
|
|
|
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();
|
|
ActivateSubContext(null);
|
|
}
|
|
|
|
internal void ExitMessage(string Message, string SubMessage)
|
|
{
|
|
// SecureBoot Unlock v2 is done. Reactivate phone arrival events.
|
|
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
|
|
{
|
|
internal BootUnlockResourcesViewModel(string CurrentMode, byte[] RootKeyHash, Action SwitchToFlashRom, Action SwitchToUndoRoot, Action SwitchToDownload, Action<string, string, string, string, string, string, bool> 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)
|
|
{
|
|
SBL3Path = (string)Registry.GetValue(@"HKEY_CURRENT_USER\Software\WPInternals", "SBL3Path", null);
|
|
}
|
|
}
|
|
|
|
internal class BootRestoreResourcesViewModel : FlashResourcesViewModel
|
|
{
|
|
internal BootRestoreResourcesViewModel(string CurrentMode, byte[] RootKeyHash, Action SwitchToFlashRom, Action SwitchToUndoRoot, Action SwitchToDownload, Action<string, string, string, string, string, string, bool> 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)
|
|
{
|
|
SBL3Path = null;
|
|
}
|
|
}
|
|
|
|
internal class FlashResourcesViewModel: ContextViewModel
|
|
{
|
|
internal Action SwitchToFlashRom;
|
|
internal Action SwitchToUndoRoot;
|
|
internal Action SwitchToDownload;
|
|
private string PlatformID;
|
|
private string ProductType;
|
|
private string ValidatedSupportedFfuPath = null;
|
|
|
|
internal FlashResourcesViewModel(string CurrentMode, byte[] RootKeyHash, Action SwitchToFlashRom, Action SwitchToUndoRoot, Action SwitchToDownload, Action<string, string, string, string, string, string, bool> Result, Action Abort, bool IsBootLoaderUnlocked, bool TargetHasNewFlashProtocol, string PlatformID = null, string ProductType = null): base()
|
|
{
|
|
IsSwitchingInterface = true;
|
|
this.CurrentMode = CurrentMode;
|
|
this.RootKeyHash = RootKeyHash;
|
|
this.PlatformID = PlatformID;
|
|
this.ProductType = ProductType;
|
|
this.SwitchToFlashRom = SwitchToFlashRom;
|
|
this.SwitchToUndoRoot = SwitchToUndoRoot;
|
|
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))))));
|
|
FixCommand = new DelegateCommand(() => Result(null, null, null, null, null, null, true));
|
|
CancelCommand = new DelegateCommand(Abort);
|
|
this.TargetHasNewFlashProtocol = TargetHasNewFlashProtocol;
|
|
|
|
if (TargetHasNewFlashProtocol)
|
|
{
|
|
SetProfileFFUPath();
|
|
SetEDEPath();
|
|
}
|
|
else
|
|
{
|
|
FFUPath = (string)Registry.GetValue(@"HKEY_CURRENT_USER\Software\WPInternals", "FFUPath", null);
|
|
LoadersPath = (string)Registry.GetValue(@"HKEY_CURRENT_USER\Software\WPInternals", "LoadersPath", null);
|
|
}
|
|
}
|
|
|
|
private void SetProfileFFUPath()
|
|
{
|
|
FFU ProfileFFU;
|
|
|
|
try
|
|
{
|
|
if (_ProfileFFUPath != null)
|
|
{
|
|
ProfileFFU = new FFU(_ProfileFFUPath);
|
|
if (PlatformID.StartsWith(ProfileFFU.PlatformID, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
IsProfileFfuValid = true;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
catch { }
|
|
|
|
try
|
|
{
|
|
string TempProfileFFUPath = (string)Registry.GetValue(@"HKEY_CURRENT_USER\Software\WPInternals", "ProfileFFUPath", null);
|
|
if (TempProfileFFUPath != null)
|
|
{
|
|
ProfileFFU = new FFU(TempProfileFFUPath);
|
|
if (PlatformID.StartsWith(ProfileFFU.PlatformID, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
ProfileFFUPath = TempProfileFFUPath;
|
|
IsProfileFfuValid = true;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
catch { }
|
|
|
|
List<FFUEntry> FFUs = App.Config.FFURepository.Where(e => (PlatformID.StartsWith(e.PlatformID, StringComparison.OrdinalIgnoreCase) && e.Exists())).ToList();
|
|
if (FFUs.Count() > 0)
|
|
{
|
|
IsProfileFfuValid = true;
|
|
ProfileFFUPath = FFUs[0].Path;
|
|
}
|
|
else
|
|
IsProfileFfuValid = false;
|
|
}
|
|
|
|
private void SetEDEPath()
|
|
{
|
|
QualcommPartition Programmer;
|
|
string TempEDEPath;
|
|
|
|
try
|
|
{
|
|
if (_EDEPath != null)
|
|
{
|
|
Programmer = new QualcommPartition(_EDEPath);
|
|
if (ByteOperations.Compare(Programmer.RootKeyHash, RootKeyHash))
|
|
return;
|
|
}
|
|
}
|
|
catch { }
|
|
|
|
try
|
|
{
|
|
TempEDEPath = (string)Registry.GetValue(@"HKEY_CURRENT_USER\Software\WPInternals", "EDEPath", null);
|
|
if (TempEDEPath != null)
|
|
{
|
|
Programmer = new QualcommPartition(TempEDEPath);
|
|
if (ByteOperations.Compare(Programmer.RootKeyHash, RootKeyHash))
|
|
{
|
|
EDEPath = TempEDEPath;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
catch { }
|
|
|
|
TempEDEPath = LumiaV2UnlockBootViewModel.GetProgrammerPath(RootKeyHash, ProductType);
|
|
if (TempEDEPath != null)
|
|
{
|
|
Programmer = new QualcommPartition(TempEDEPath);
|
|
if (ByteOperations.Compare(Programmer.RootKeyHash, RootKeyHash))
|
|
{
|
|
EDEPath = TempEDEPath;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
private void SetSupportedFFUPath()
|
|
{
|
|
if ((this is BootRestoreResourcesViewModel) || (_ProfileFFUPath == null))
|
|
{
|
|
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<FFUEntry> 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)
|
|
{
|
|
if (SupportedFFUPath == null)
|
|
{
|
|
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)))))
|
|
{
|
|
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;
|
|
}
|
|
|
|
if (IsSupportedFfuValid && (SupportedFFUPath != null))
|
|
ValidatedSupportedFfuPath = SupportedFFUPath;
|
|
}
|
|
|
|
private bool _IsSupportedFfuNeeded = false;
|
|
public bool IsSupportedFfuNeeded
|
|
{
|
|
get
|
|
{
|
|
return _IsSupportedFfuNeeded;
|
|
}
|
|
set
|
|
{
|
|
_IsSupportedFfuNeeded = value;
|
|
OnPropertyChanged("IsSupportedFfuNeeded");
|
|
OkCommand.RaiseCanExecuteChanged();
|
|
}
|
|
}
|
|
|
|
private bool _IsSupportedFfuValid = true;
|
|
public bool IsSupportedFfuValid
|
|
{
|
|
get
|
|
{
|
|
return _IsSupportedFfuValid;
|
|
}
|
|
set
|
|
{
|
|
_IsSupportedFfuValid = value;
|
|
OnPropertyChanged("IsSupportedFfuValid");
|
|
OkCommand.RaiseCanExecuteChanged();
|
|
}
|
|
}
|
|
|
|
private bool _IsProfileFfuValid = true;
|
|
public bool IsProfileFfuValid
|
|
{
|
|
get
|
|
{
|
|
return _IsProfileFfuValid;
|
|
}
|
|
set
|
|
{
|
|
_IsProfileFfuValid = value;
|
|
OnPropertyChanged("IsProfileFfuValid");
|
|
}
|
|
}
|
|
|
|
private bool _TargetHasNewFlashProtocol = false;
|
|
public bool TargetHasNewFlashProtocol
|
|
{
|
|
get
|
|
{
|
|
return _TargetHasNewFlashProtocol;
|
|
}
|
|
set
|
|
{
|
|
_TargetHasNewFlashProtocol = value;
|
|
OnPropertyChanged("TargetHasNewFlashProtocol");
|
|
OkCommand.RaiseCanExecuteChanged();
|
|
}
|
|
}
|
|
|
|
private bool _IsBootLoaderUnlocked = false;
|
|
public bool IsBootLoaderUnlocked
|
|
{
|
|
get
|
|
{
|
|
return _IsBootLoaderUnlocked;
|
|
}
|
|
set
|
|
{
|
|
_IsBootLoaderUnlocked = value;
|
|
OnPropertyChanged("IsBootLoaderUnlocked");
|
|
}
|
|
}
|
|
|
|
private string _FFUPath = null;
|
|
public string FFUPath
|
|
{
|
|
get
|
|
{
|
|
return _FFUPath;
|
|
}
|
|
set
|
|
{
|
|
_FFUPath = value;
|
|
OnPropertyChanged("FFUPath");
|
|
}
|
|
}
|
|
|
|
private string _ProfileFFUPath = null;
|
|
public string ProfileFFUPath
|
|
{
|
|
get
|
|
{
|
|
return _ProfileFFUPath;
|
|
}
|
|
set
|
|
{
|
|
if (_ProfileFFUPath != value)
|
|
{
|
|
_ProfileFFUPath = value;
|
|
OnPropertyChanged("ProfileFFUPath");
|
|
SetSupportedFFUPath();
|
|
OkCommand.RaiseCanExecuteChanged();
|
|
}
|
|
}
|
|
}
|
|
|
|
private string _SupportedFFUPath = null;
|
|
public string SupportedFFUPath
|
|
{
|
|
get
|
|
{
|
|
return _SupportedFFUPath;
|
|
}
|
|
set
|
|
{
|
|
if (_SupportedFFUPath != value)
|
|
{
|
|
_SupportedFFUPath = value;
|
|
OnPropertyChanged("SupportedFFUPath");
|
|
|
|
if (value != ValidatedSupportedFfuPath)
|
|
ValidateSupportedFfuPath();
|
|
}
|
|
}
|
|
}
|
|
|
|
private string _LoadersPath = null;
|
|
public string LoadersPath
|
|
{
|
|
get
|
|
{
|
|
return _LoadersPath;
|
|
}
|
|
set
|
|
{
|
|
_LoadersPath = value;
|
|
OnPropertyChanged("LoadersPath");
|
|
}
|
|
}
|
|
|
|
private string _EDEPath = null;
|
|
public string EDEPath
|
|
{
|
|
get
|
|
{
|
|
return _EDEPath;
|
|
}
|
|
set
|
|
{
|
|
_EDEPath = value;
|
|
OnPropertyChanged("EDEPath");
|
|
}
|
|
}
|
|
|
|
private string _SBL3Path = null;
|
|
public string SBL3Path
|
|
{
|
|
get
|
|
{
|
|
return _SBL3Path;
|
|
}
|
|
set
|
|
{
|
|
_SBL3Path = value;
|
|
OnPropertyChanged("SBL3Path");
|
|
}
|
|
}
|
|
|
|
private string _CurrentMode = null;
|
|
public string CurrentMode
|
|
{
|
|
get
|
|
{
|
|
return _CurrentMode;
|
|
}
|
|
set
|
|
{
|
|
_CurrentMode = value;
|
|
OnPropertyChanged("CurrentMode");
|
|
}
|
|
}
|
|
|
|
private byte[] _RootKeyHash = null;
|
|
public byte[] RootKeyHash
|
|
{
|
|
get
|
|
{
|
|
return _RootKeyHash;
|
|
}
|
|
set
|
|
{
|
|
_RootKeyHash = value;
|
|
OnPropertyChanged("RootKeyHash");
|
|
}
|
|
}
|
|
|
|
private DelegateCommand _OkCommand = null;
|
|
public DelegateCommand OkCommand
|
|
{
|
|
get
|
|
{
|
|
return _OkCommand;
|
|
}
|
|
private set
|
|
{
|
|
_OkCommand = value;
|
|
}
|
|
}
|
|
|
|
private DelegateCommand _CancelCommand = null;
|
|
public DelegateCommand CancelCommand
|
|
{
|
|
get
|
|
{
|
|
return _CancelCommand;
|
|
}
|
|
private set
|
|
{
|
|
_CancelCommand = value;
|
|
}
|
|
}
|
|
|
|
private DelegateCommand _FixCommand = null;
|
|
public DelegateCommand FixCommand
|
|
{
|
|
get
|
|
{
|
|
return _FixCommand;
|
|
}
|
|
private set
|
|
{
|
|
_FixCommand = value;
|
|
}
|
|
}
|
|
}
|
|
}
|