From 293c649edb8d3c58be51db46e5b588eb79398de7 Mon Sep 17 00:00:00 2001 From: Gustave Monce Date: Sun, 26 Nov 2023 18:37:14 +0100 Subject: [PATCH] Refactor Qualcomm Sahara and Qualcomm Firehose code --- WPinternals/Models/QualcommFirehose.cs | 384 ++++++++++++++++ WPinternals/Models/QualcommPartition.cs | 8 +- WPinternals/Models/QualcommSahara.cs | 423 +----------------- WPinternals/Models/QualcommSaharaCommand.cs | 45 ++ WPinternals/Models/QualcommSaharaMode.cs | 30 ++ WPinternals/TestCode.cs | 3 +- .../ViewModels/LumiaV2UnlockBootViewModel.cs | 12 +- 7 files changed, 492 insertions(+), 413 deletions(-) create mode 100644 WPinternals/Models/QualcommFirehose.cs create mode 100644 WPinternals/Models/QualcommSaharaCommand.cs create mode 100644 WPinternals/Models/QualcommSaharaMode.cs diff --git a/WPinternals/Models/QualcommFirehose.cs b/WPinternals/Models/QualcommFirehose.cs new file mode 100644 index 0000000..59a4aa9 --- /dev/null +++ b/WPinternals/Models/QualcommFirehose.cs @@ -0,0 +1,384 @@ +// Copyright (c) 2018, Rene Lergner - @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.Collections; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace WPinternals +{ + internal class QualcommFirehose + { + private readonly QualcommSerial Serial; + + public QualcommFirehose(QualcommSerial Serial) + { + this.Serial = Serial; + } + + public bool ConnectToProgrammer(byte[] PacketFromPcToProgrammer) + { + // Behaviour of old firehose: + // Takes about 20 ms to be started. + // Then PC has to start talking to the phone. + // Behaviour of new firehose: + // After 2000 ms the firehose starts talking to the PC + // + // For the duration of 2.5 seconds we will send Hello packages + // And also wait for incoming messages + // An incoming message can be a response to our outgoing Hello packet (read incoming until "response value") + // Or it can be an incoming Hello-packet from the programmer (always 2 packets, starting with "Chip serial num") + // Sending the hello-packet can succeed immediately, or it can timeout. + // When sending succeeds, an answer should be incoming immediately to complete the handshake. + // When an incoming Hello was received, the phone still expects to receive another Hello. + + int HelloSendCount = 0; + bool HandshakeCompleted = false; + string Incoming; + do + { + Serial.SetTimeOut(200); + HelloSendCount++; + try + { + LogFile.Log("Send Hello to programmer (" + HelloSendCount.ToString() + ")", LogType.FileOnly); + Serial.SendData(PacketFromPcToProgrammer); + LogFile.Log("Hello packet accepted", LogType.FileOnly); + } + catch + { + LogFile.Log("Hello packet not accepted", LogType.FileOnly); + } + + try + { + Serial.SetTimeOut(500); + Incoming = Encoding.ASCII.GetString(Serial.GetResponse(null)); + LogFile.Log("In: " + Incoming, LogType.FileOnly); + Serial.SetTimeOut(200); + if (Incoming.Contains("Chip serial num")) + { + Incoming = Encoding.ASCII.GetString(Serial.GetResponse(null)); + LogFile.Log("In: " + Incoming, LogType.FileOnly); + LogFile.Log("Incoming Hello-packets received", LogType.FileOnly); + } + + while (Incoming.IndexOf("response value") < 0) + { + Incoming = Encoding.ASCII.GetString(Serial.GetResponse(null)); + LogFile.Log("In: " + Incoming, LogType.FileOnly); + } + + LogFile.Log("Incoming Hello-response received", LogType.FileOnly); + + if (!Incoming.Contains("Failed to authenticate Digital Signature.")) + { + HandshakeCompleted = true; + } + else + { + LogFile.Log("Programmer failed to authenticate Digital Signature", LogType.FileOnly); + } + } + catch { } + } + while (!HandshakeCompleted && (HelloSendCount < 6)); + + return HandshakeCompleted; + } + + public bool ConnectToProgrammerInTestMode() + { + byte[] HelloPacketFromPcToProgrammer = new byte[0x20C]; + ByteOperations.WriteUInt32(HelloPacketFromPcToProgrammer, 0, 0x57503730); + ByteOperations.WriteUInt32(HelloPacketFromPcToProgrammer, 0x28, 0x57503730); + ByteOperations.WriteUInt32(HelloPacketFromPcToProgrammer, 0x208, 0x57503730); + ByteOperations.WriteUInt16(HelloPacketFromPcToProgrammer, 0x48, 0x4445); + + bool HandshakeCompleted = ConnectToProgrammer(HelloPacketFromPcToProgrammer); + + if (HandshakeCompleted) + { + LogFile.Log("Handshake completed with programmer in testmode", LogType.FileOnly); + } + else + { + LogFile.Log("Handshake with programmer failed", LogType.FileOnly); + } + + return HandshakeCompleted; + } + + public async Task Reset() + { + bool Connected = await Task.Run(() => ConnectToProgrammerInTestMode()); + if (!Connected) + { + return false; + } + + LogFile.Log("Rebooting phone", LogType.FileAndConsole); + const string Command03 = ""; + LogFile.Log("Out: " + Command03, LogType.FileOnly); + Serial.SendData(Encoding.ASCII.GetBytes(Command03)); + + string Incoming; + do + { + Incoming = Encoding.ASCII.GetString(Serial.GetResponse(null)); + LogFile.Log("In: " + Incoming, LogType.FileOnly); + } + while (Incoming.IndexOf("response value") < 0); + + // Workaround for problem + // SerialPort is sometimes not disposed correctly when the device is already removed. + // So explicitly dispose here + Serial.Close(); + + return true; + } + + public bool SendEdPayload(string ProgrammerPath, string PayloadPath) + { + // First, let's read the Emergency Download payload header and verify its validity + FileStream PayloadStream = File.OpenRead(PayloadPath); + + byte[] ValidReferencePayloadHeader = [0x45, 0x6D, 0x65, 0x72, 0x67, 0x65, 0x6E, 0x63, 0x79, 0x20, 0x50, 0x61, 0x79, 0x6C, 0x6F, 0x61, 0x64]; + + byte[] PayloadHeader = new byte[17]; + PayloadStream.Read(PayloadHeader, 0, 17); + + bool IsValidEdPayloadImage = StructuralComparisons.StructuralEqualityComparer.Equals(PayloadHeader, ValidReferencePayloadHeader); + if (!IsValidEdPayloadImage) + { + return false; + } + + // Now let's read the information block + PayloadStream.Seek(0x64, SeekOrigin.Begin); + byte[] PayloadInformationBlock = new byte[0x64]; + + PayloadStream.Read(PayloadInformationBlock, 0, 0x64); + string buildtime = Encoding.ASCII.GetString(PayloadInformationBlock).Trim('\0'); + + PayloadStream.Read(PayloadInformationBlock, 0, 0x64); + string builddate = Encoding.ASCII.GetString(PayloadInformationBlock).Trim('\0'); + + PayloadStream.Read(PayloadInformationBlock, 0, 0x64); + string version = Encoding.ASCII.GetString(PayloadInformationBlock).Trim('\0'); + + PayloadInformationBlock = new byte[0x670]; + PayloadStream.Read(PayloadInformationBlock, 0, 0x670); + string Info = Encoding.ASCII.GetString(PayloadInformationBlock).Trim('\0'); + + // Print some information about the payload + LogFile.Log("Emerency flasher version 0.1", LogType.FileAndConsole); + LogFile.Log("Programmer information:", LogType.FileAndConsole); + LogFile.Log("Build time: " + buildtime, LogType.FileAndConsole); + LogFile.Log("Build date: " + builddate, LogType.FileAndConsole); + LogFile.Log("Version: " + version, LogType.FileAndConsole); + LogFile.Log("Info: " + Info, LogType.FileAndConsole); + + // Wait a few seconds before sending commands + LogFile.Log("Waiting...", LogType.FileAndConsole); + Thread.Sleep(2000); + LogFile.Log("Waiting...OK", LogType.FileAndConsole); + + bool Terminated = false; + bool Connected = false; + bool ProgrammerRawMode = false; + + string Incoming; + + while (!Terminated) + { + PayloadInformationBlock = new byte[0x200]; + PayloadStream.Read(PayloadInformationBlock, 0, 0x200); + string ProgrammerCommand = Encoding.ASCII.GetString(PayloadInformationBlock.Skip(0xC).ToArray()).Trim('\0'); + + LogFile.Log(ProgrammerCommand, LogType.FileAndConsole); + + byte[] PacketFromPcToProgrammer = []; + byte[] temp = new byte[0x200]; + + while (true) + { + if (PayloadStream.Position == PayloadStream.Length) + { + Terminated = true; + break; + } + + PayloadStream.Read(temp, 0, 0x200); + + if (temp[12] == 77 && temp[13] == 83 && temp[14] == 71 && temp[15] == 95) + { + PayloadStream.Seek(-0x200, SeekOrigin.Current); + break; + } + + PacketFromPcToProgrammer = [.. PacketFromPcToProgrammer, .. temp]; + } + + bool ExpectingReplyFromProgrammer = false; + + if (ProgrammerCommand.Contains("XML")) + { + string Outgoing = Encoding.ASCII.GetString(PacketFromPcToProgrammer).Trim('\0'); + PacketFromPcToProgrammer = Encoding.ASCII.GetBytes(Outgoing); + LogFile.Log("Out: " + Outgoing, LogType.FileAndConsole); + } + + if (!ProgrammerCommand.Contains("RAW_DATA") && !ProgrammerRawMode) + { + ExpectingReplyFromProgrammer = true; + } + + if (ProgrammerCommand.Contains("LAST") && ProgrammerRawMode) + { + ExpectingReplyFromProgrammer = true; + } + + if (ProgrammerCommand.Contains("DATA_ALL") && ProgrammerRawMode) + { + ExpectingReplyFromProgrammer = true; + } + + if (ProgrammerCommand.Contains("RAW_DATA") && !ProgrammerRawMode) + { + LogFile.Log("Phone is not in raw mode ON, leaving...", LogType.FileAndConsole); + + // Workaround for problem + // SerialPort is sometimes not disposed correctly when the device is already removed. + // So explicitly dispose here + Serial.Close(); + + LogFile.Log("Phone has been emergency flashed unsuccessfully!", LogType.FileAndConsole); + PayloadStream.Dispose(); + + return false; + } + + if (!ProgrammerCommand.Contains("RAW_DATA") && ProgrammerRawMode) + { + LogFile.Log("Phone is not in raw mode ON, leaving...", LogType.FileAndConsole); + + // Workaround for problem + // SerialPort is sometimes not disposed correctly when the device is already removed. + // So explicitly dispose here + Serial.Close(); + + LogFile.Log("Phone has been emergency flashed unsuccessfully!", LogType.FileAndConsole); + PayloadStream.Dispose(); + + return false; + } + + if (Connected) + { + Serial.SendData(PacketFromPcToProgrammer); + } + + if (ExpectingReplyFromProgrammer) + { + if (!Connected) + { + Connected = ConnectToProgrammer(PacketFromPcToProgrammer); + + if (Connected) + { + LogFile.Log("Handshake completed with programmer in validated image programming (VIP) mode", LogType.FileAndConsole); + } + else + { + LogFile.Log("Handshake with programmer failed", LogType.FileAndConsole); + } + + if (!Connected) + { + LogFile.Log("Phone programmer is now ignoring us, leaving...", LogType.FileAndConsole); + + // Workaround for problem + // SerialPort is sometimes not disposed correctly when the device is already removed. + // So explicitly dispose here + Serial.Close(); + + LogFile.Log("Phone has been emergency flashed unsuccessfully!", LogType.FileAndConsole); + PayloadStream.Dispose(); + + return false; + } + } + else + { + do + { + Serial.SetTimeOut(500); + Incoming = Encoding.ASCII.GetString(Serial.GetResponse(null)); + Serial.SetTimeOut(200); + LogFile.Log("In: " + Incoming, LogType.FileAndConsole); + } + while (Incoming.IndexOf("response value") < 0); + + if (Incoming.Contains("rawmode=\"false\"")) + { + ProgrammerRawMode = false; + LogFile.Log("Raw mode: OFF", LogType.FileAndConsole); + } + + if (Incoming.Contains("rawmode=\"true\"")) + { + ProgrammerRawMode = true; + LogFile.Log("Raw mode: ON", LogType.FileAndConsole); + } + + if (!Incoming.Contains("ACK")) + { + LogFile.Log("Phone programmer is now ignoring us, leaving...", LogType.FileAndConsole); + + // Workaround for problem + // SerialPort is sometimes not disposed correctly when the device is already removed. + // So explicitly dispose here + Serial.Close(); + + LogFile.Log("Phone has been emergency flashed unsuccessfully!", LogType.FileAndConsole); + PayloadStream.Dispose(); + + return false; + } + } + } + } + + // Workaround for problem + // SerialPort is sometimes not disposed correctly when the device is already removed. + // So explicitly dispose here + Serial.Close(); + + LogFile.Log("Phone has been emergency flashed successfully!", LogType.FileAndConsole); + PayloadStream.Dispose(); + + return true; + } + } +} \ No newline at end of file diff --git a/WPinternals/Models/QualcommPartition.cs b/WPinternals/Models/QualcommPartition.cs index 0f8b83b..fba1433 100644 --- a/WPinternals/Models/QualcommPartition.cs +++ b/WPinternals/Models/QualcommPartition.cs @@ -52,7 +52,7 @@ namespace WPinternals internal QualcommPartition(byte[] Binary, uint Offset = 0) { #if DEBUG - System.Diagnostics.Debug.Print("Loader: " + Converter.ConvertHexToString(new SHA256Managed().ComputeHash(Binary, 0, Binary.Length), "")); + System.Diagnostics.Debug.Print("Loader: " + Converter.ConvertHexToString(SHA256.HashData(Binary.AsSpan(0, Binary.Length)), "")); #endif this.Binary = Binary; @@ -162,7 +162,7 @@ namespace WPinternals if ((CurrentCertificateOffset + CertificateSize) == (CertificatesOffset + CertificatesSize)) { // This is the last certificate. So this is the root key. - RootKeyHash = new SHA256Managed().ComputeHash(Binary, (int)CurrentCertificateOffset, (int)CertificateSize); + RootKeyHash = SHA256.HashData(Binary.AsSpan((int)CurrentCertificateOffset, (int)CertificateSize)); #if DEBUG System.Diagnostics.Debug.Print("RKH: " + Converter.ConvertHexToString(RootKeyHash, "")); @@ -171,7 +171,7 @@ namespace WPinternals #if DEBUG else { - System.Diagnostics.Debug.Print("Cert: " + Converter.ConvertHexToString(new SHA256Managed().ComputeHash(Binary, (int)CurrentCertificateOffset, (int)CertificateSize), "")); + System.Diagnostics.Debug.Print("Cert: " + Converter.ConvertHexToString(SHA256.HashData(Binary.AsSpan((int)CurrentCertificateOffset, (int)CertificateSize)), "")); } #endif CurrentCertificateOffset += CertificateSize; @@ -183,7 +183,7 @@ namespace WPinternals CurrentCertificateOffset -= CertificateSize; // This is the last certificate. So this is the root key. - RootKeyHash = new SHA256Managed().ComputeHash(Binary, (int)CurrentCertificateOffset, (int)CertificateSize); + RootKeyHash = SHA256.HashData(Binary.AsSpan((int)CurrentCertificateOffset, (int)CertificateSize)); #if DEBUG System.Diagnostics.Debug.Print("RKH: " + Converter.ConvertHexToString(RootKeyHash, "")); diff --git a/WPinternals/Models/QualcommSahara.cs b/WPinternals/Models/QualcommSahara.cs index ce23ab1..f23c18c 100644 --- a/WPinternals/Models/QualcommSahara.cs +++ b/WPinternals/Models/QualcommSahara.cs @@ -19,48 +19,12 @@ // DEALINGS IN THE SOFTWARE. using System; -using System.Collections; using System.Collections.Generic; using System.IO; -using System.Linq; -using System.Text; -using System.Threading; using System.Threading.Tasks; -using System.Windows.Documents; namespace WPinternals { - internal enum SaharaMode : uint - { - ImageTransferPending = 0x00, - ImagetransferComplete = 0x01, - MemoryDebug = 0x02, - Command = 0x03 - } - - internal enum SaharaCommand : uint - { - HelloRequest = 0x01, - HelloResponse = 0x02, - ReadData = 0x03, - EndTransfer = 0x04, - DoneRequest = 0x05, - DoneResponse = 0x06, - ResetRequest = 0x07, - ResetResponse = 0x08, - MemoryDebug = 0x09, - MemoryRead = 0x0A, - CommandReady = 0x0B, - SwitchMode = 0x0C, - ExecuteRequest = 0x0D, - ExecuteResponse = 0x0E, - ExecuteData = 0x0F, - MemoryDebug64 = 0x10, - MemoryRead64 = 0x11, - MemoryReadData64 = 0x12, - ResetStateMachineIdentifier = 0x13 - } - internal delegate void ReadyHandler(); internal class QualcommSahara @@ -74,7 +38,7 @@ namespace WPinternals this.Serial = Serial; } - private static byte[] BuildCommandPacket(SaharaCommand SaharaCommand, byte[] CommandBuffer = null) + private static byte[] BuildCommandPacket(QualcommSaharaCommand SaharaCommand, byte[] CommandBuffer = null) { UInt32 CommandID = (uint)SaharaCommand; UInt32 CommandBufferLength = 0; @@ -96,7 +60,7 @@ namespace WPinternals return Packet; } - private static byte[] BuildHelloResponsePacket(SaharaMode SaharaMode, UInt32 ProtocolVersion = 2, UInt32 SupportedVersion = 1, UInt32 MaxPacketLength = 0 /* 0: Status OK */) + private static byte[] BuildHelloResponsePacket(QualcommSaharaMode SaharaMode, UInt32 ProtocolVersion = 2, UInt32 SupportedVersion = 1, UInt32 MaxPacketLength = 0 /* 0: Status OK */) { UInt32 Mode = (uint)SaharaMode; @@ -118,24 +82,24 @@ namespace WPinternals ByteOperations.WriteUInt32(Hello, 0x20, 0); ByteOperations.WriteUInt32(Hello, 0x24, 0); - return BuildCommandPacket(SaharaCommand.HelloResponse, Hello); + return BuildCommandPacket(QualcommSaharaCommand.HelloResponse, Hello); } private static byte[] BuildExecuteRequestPacket(UInt32 RequestID) { byte[] Execute = new byte[0x04]; ByteOperations.WriteUInt32(Execute, 0x00, RequestID); - return BuildCommandPacket(SaharaCommand.ExecuteRequest, Execute); + return BuildCommandPacket(QualcommSaharaCommand.ExecuteRequest, Execute); } private static byte[] BuildExecuteDataPacket(UInt32 RequestID) { byte[] Execute = new byte[0x04]; ByteOperations.WriteUInt32(Execute, 0x00, RequestID); - return BuildCommandPacket(SaharaCommand.ExecuteData, Execute); + return BuildCommandPacket(QualcommSaharaCommand.ExecuteData, Execute); } - private byte[][] GetRootKeyHashes() + public byte[][] GetRKHs() { Serial.SendData(BuildExecuteRequestPacket(0x3)); @@ -187,7 +151,7 @@ namespace WPinternals LogFile.Log("Mode: 0x" + ByteOperations.ReadUInt32(Hello, 0x14).ToString("X8"), LogType.FileOnly); Step = 2; - byte[] HelloResponse = BuildHelloResponsePacket(SaharaMode.Command); + byte[] HelloResponse = BuildHelloResponsePacket(QualcommSaharaMode.Command); Serial.SendData(HelloResponse); Step = 3; @@ -200,7 +164,7 @@ namespace WPinternals } Step = 4; - byte[][] RKHs = GetRootKeyHashes(); + byte[][] RKHs = GetRKHs(); return RKHs[0]; } catch (Exception Ex) @@ -241,7 +205,7 @@ namespace WPinternals LogFile.Log("Mode: 0x" + ByteOperations.ReadUInt32(Hello, 0x14).ToString("X8"), LogType.FileOnly); Step = 2; - byte[] HelloResponse = BuildHelloResponsePacket(SaharaMode.ImageTransferPending); + byte[] HelloResponse = BuildHelloResponsePacket(QualcommSaharaMode.ImageTransferPending); Serial.SendData(HelloResponse); Step = 3; @@ -316,7 +280,7 @@ namespace WPinternals LogFile.Log("MaxLength: 0x" + ByteOperations.ReadUInt32(Hello, 0x10).ToString("X8"), LogType.FileOnly); LogFile.Log("Mode: 0x" + ByteOperations.ReadUInt32(Hello, 0x14).ToString("X8"), LogType.FileOnly); - byte[] HelloResponse = BuildHelloResponsePacket(SaharaMode.ImageTransferPending); + byte[] HelloResponse = BuildHelloResponsePacket(QualcommSaharaMode.ImageTransferPending); byte[] Ready = Serial.SendCommand(HelloResponse, [0x03, 0x00, 0x00, 0x00]); } @@ -330,155 +294,25 @@ namespace WPinternals public void ResetSahara() { - Serial.SendCommand(BuildCommandPacket(SaharaCommand.ResetRequest), [0x08, 0x00, 0x00, 0x00]); + Serial.SendCommand(BuildCommandPacket(QualcommSaharaCommand.ResetRequest), [0x08, 0x00, 0x00, 0x00]); } - public bool ConnectToProgrammer(byte[] PacketFromPcToProgrammer) - { - // Behaviour of old firehose: - // Takes about 20 ms to be started. - // Then PC has to start talking to the phone. - // Behaviour of new firehose: - // After 2000 ms the firehose starts talking to the PC - // - // For the duration of 2.5 seconds we will send Hello packages - // And also wait for incoming messages - // An incoming message can be a response to our outgoing Hello packet (read incoming until "response value") - // Or it can be an incoming Hello-packet from the programmer (always 2 packets, starting with "Chip serial num") - // Sending the hello-packet can succeed immediately, or it can timeout. - // When sending succeeds, an answer should be incoming immediately to complete the handshake. - // When an incoming Hello was received, the phone still expects to receive another Hello. - - int HelloSendCount = 0; - bool HandshakeCompleted = false; - string Incoming; - do - { - Serial.SetTimeOut(200); - HelloSendCount++; - try - { - LogFile.Log("Send Hello to programmer (" + HelloSendCount.ToString() + ")", LogType.FileOnly); - Serial.SendData(PacketFromPcToProgrammer); - LogFile.Log("Hello packet accepted", LogType.FileOnly); - } - catch - { - LogFile.Log("Hello packet not accepted", LogType.FileOnly); - } - - try - { - Serial.SetTimeOut(500); - Incoming = Encoding.ASCII.GetString(Serial.GetResponse(null)); - LogFile.Log("In: " + Incoming, LogType.FileOnly); - Serial.SetTimeOut(200); - if (Incoming.Contains("Chip serial num")) - { - Incoming = Encoding.ASCII.GetString(Serial.GetResponse(null)); - LogFile.Log("In: " + Incoming, LogType.FileOnly); - LogFile.Log("Incoming Hello-packets received", LogType.FileOnly); - } - - while (Incoming.IndexOf("response value") < 0) - { - Incoming = Encoding.ASCII.GetString(Serial.GetResponse(null)); - LogFile.Log("In: " + Incoming, LogType.FileOnly); - } - - LogFile.Log("Incoming Hello-response received", LogType.FileOnly); - - if (!Incoming.Contains("Failed to authenticate Digital Signature.")) - { - HandshakeCompleted = true; - } - else - { - LogFile.Log("Programmer failed to authenticate Digital Signature", LogType.FileOnly); - } - } - catch { } - } - while (!HandshakeCompleted && (HelloSendCount < 6)); - - return HandshakeCompleted; - } - - public bool ConnectToProgrammerInTestMode() - { - byte[] HelloPacketFromPcToProgrammer = new byte[0x20C]; - ByteOperations.WriteUInt32(HelloPacketFromPcToProgrammer, 0, 0x57503730); - ByteOperations.WriteUInt32(HelloPacketFromPcToProgrammer, 0x28, 0x57503730); - ByteOperations.WriteUInt32(HelloPacketFromPcToProgrammer, 0x208, 0x57503730); - ByteOperations.WriteUInt16(HelloPacketFromPcToProgrammer, 0x48, 0x4445); - - bool HandshakeCompleted = ConnectToProgrammer(HelloPacketFromPcToProgrammer); - - if (HandshakeCompleted) - { - LogFile.Log("Handshake completed with programmer in testmode", LogType.FileOnly); - } - else - { - LogFile.Log("Handshake with programmer failed", LogType.FileOnly); - } - - return HandshakeCompleted; - } - - public async Task Reset(string ProgrammerPath) - { - bool SendImageResult = await Task.Run(() => SendImage(ProgrammerPath)); - if (!SendImageResult) - { - return false; - } - - await Task.Run(() => StartProgrammer()); - - bool Connected = await Task.Run(() => ConnectToProgrammerInTestMode()); - if (!Connected) - { - return false; - } - - LogFile.Log("Rebooting phone", LogType.FileAndConsole); - const string Command03 = ""; - LogFile.Log("Out: " + Command03, LogType.FileOnly); - Serial.SendData(Encoding.ASCII.GetBytes(Command03)); - - string Incoming; - do - { - Incoming = Encoding.ASCII.GetString(Serial.GetResponse(null)); - LogFile.Log("In: " + Incoming, LogType.FileOnly); - } - while (Incoming.IndexOf("response value") < 0); - - // Workaround for problem - // SerialPort is sometimes not disposed correctly when the device is already removed. - // So explicitly dispose here - Serial.Close(); - - return true; - } - - public void SwitchMode(SaharaMode Mode) + public void SwitchMode(QualcommSaharaMode Mode) { byte[] SwitchMode = new byte[0x04]; ByteOperations.WriteUInt32(SwitchMode, 0x00, (UInt32)Mode); - byte[] SwitchModeCommand = BuildCommandPacket(SaharaCommand.SwitchMode, SwitchMode); + byte[] SwitchModeCommand = BuildCommandPacket(QualcommSaharaCommand.SwitchMode, SwitchMode); byte[] ResponsePattern = null; switch (Mode) { - case SaharaMode.ImageTransferPending: + case QualcommSaharaMode.ImageTransferPending: ResponsePattern = [0x04, 0x00, 0x00, 0x00]; break; - case SaharaMode.MemoryDebug: + case QualcommSaharaMode.MemoryDebug: ResponsePattern = [0x09, 0x00, 0x00, 0x00]; break; - case SaharaMode.Command: + case QualcommSaharaMode.Command: ResponsePattern = [0x0B, 0x00, 0x00, 0x00]; break; } @@ -488,7 +322,7 @@ namespace WPinternals public void StartProgrammer() { LogFile.Log("Starting programmer", LogType.FileAndConsole); - byte[] DoneCommand = BuildCommandPacket(SaharaCommand.DoneRequest); + byte[] DoneCommand = BuildCommandPacket(QualcommSaharaCommand.DoneRequest); bool Started = false; int count = 0; do @@ -512,236 +346,15 @@ namespace WPinternals LogFile.Log("Programmer being launched on phone", LogType.FileOnly); } - public async Task SendEdPayload(string ProgrammerPath, string PayloadPath) + public async Task LoadProgrammer(string ProgrammerPath) { - // First, let's read the Emergency Download payload header and verify its validity - FileStream PayloadStream = File.OpenRead(PayloadPath); - - byte[] ValidReferencePayloadHeader = [0x45, 0x6D, 0x65, 0x72, 0x67, 0x65, 0x6E, 0x63, 0x79, 0x20, 0x50, 0x61, 0x79, 0x6C, 0x6F, 0x61, 0x64]; - - byte[] PayloadHeader = new byte[17]; - PayloadStream.Read(PayloadHeader, 0, 17); - - bool IsValidEdPayloadImage = StructuralComparisons.StructuralEqualityComparer.Equals(PayloadHeader, ValidReferencePayloadHeader); - if (!IsValidEdPayloadImage) - { - return false; - } - - // Now let's read the information block - PayloadStream.Seek(0x64, SeekOrigin.Begin); - byte[] PayloadInformationBlock = new byte[0x64]; - - PayloadStream.Read(PayloadInformationBlock, 0, 0x64); - string buildtime = Encoding.ASCII.GetString(PayloadInformationBlock).Trim('\0'); - - PayloadStream.Read(PayloadInformationBlock, 0, 0x64); - string builddate = Encoding.ASCII.GetString(PayloadInformationBlock).Trim('\0'); - - PayloadStream.Read(PayloadInformationBlock, 0, 0x64); - string version = Encoding.ASCII.GetString(PayloadInformationBlock).Trim('\0'); - - PayloadInformationBlock = new byte[0x670]; - PayloadStream.Read(PayloadInformationBlock, 0, 0x670); - string Info = Encoding.ASCII.GetString(PayloadInformationBlock).Trim('\0'); - - // Print some information about the payload - LogFile.Log("Emerency flasher version 0.1", LogType.FileAndConsole); - LogFile.Log("Programmer information:", LogType.FileAndConsole); - LogFile.Log("Build time: " + buildtime, LogType.FileAndConsole); - LogFile.Log("Build date: " + builddate, LogType.FileAndConsole); - LogFile.Log("Version: " + version, LogType.FileAndConsole); - LogFile.Log("Info: " + Info, LogType.FileAndConsole); - - // Send the emergency programmer to the phone bool SendImageResult = await Task.Run(() => SendImage(ProgrammerPath)); if (!SendImageResult) { return false; } - // Start the emergency programmer on the phone await Task.Run(() => StartProgrammer()); - - // Wait a few seconds before sending commands - LogFile.Log("Waiting...", LogType.FileAndConsole); - Thread.Sleep(2000); - LogFile.Log("Waiting...OK", LogType.FileAndConsole); - - bool Terminated = false; - bool Connected = false; - bool ProgrammerRawMode = false; - - string Incoming; - - while (!Terminated) - { - PayloadInformationBlock = new byte[0x200]; - PayloadStream.Read(PayloadInformationBlock, 0, 0x200); - string ProgrammerCommand = Encoding.ASCII.GetString(PayloadInformationBlock.Skip(0xC).ToArray()).Trim('\0'); - - LogFile.Log(ProgrammerCommand, LogType.FileAndConsole); - - byte[] PacketFromPcToProgrammer = []; - byte[] temp = new byte[0x200]; - - while (true) - { - if (PayloadStream.Position == PayloadStream.Length) - { - Terminated = true; - break; - } - - PayloadStream.Read(temp, 0, 0x200); - - if (temp[12] == 77 && temp[13] == 83 && temp[14] == 71 && temp[15] == 95) - { - PayloadStream.Seek(-0x200, SeekOrigin.Current); - break; - } - - PacketFromPcToProgrammer = [.. PacketFromPcToProgrammer, .. temp]; - } - - bool ExpectingReplyFromProgrammer = false; - - if (ProgrammerCommand.Contains("XML")) - { - string Outgoing = Encoding.ASCII.GetString(PacketFromPcToProgrammer).Trim('\0'); - PacketFromPcToProgrammer = Encoding.ASCII.GetBytes(Outgoing); - LogFile.Log("Out: " + Outgoing, LogType.FileAndConsole); - } - - if (!ProgrammerCommand.Contains("RAW_DATA") && !ProgrammerRawMode) - { - ExpectingReplyFromProgrammer = true; - } - - if (ProgrammerCommand.Contains("LAST") && ProgrammerRawMode) - { - ExpectingReplyFromProgrammer = true; - } - - if (ProgrammerCommand.Contains("DATA_ALL") && ProgrammerRawMode) - { - ExpectingReplyFromProgrammer = true; - } - - if (ProgrammerCommand.Contains("RAW_DATA") && !ProgrammerRawMode) - { - LogFile.Log("Phone is not in raw mode ON, leaving...", LogType.FileAndConsole); - - // Workaround for problem - // SerialPort is sometimes not disposed correctly when the device is already removed. - // So explicitly dispose here - Serial.Close(); - - LogFile.Log("Phone has been emergency flashed unsuccessfully!", LogType.FileAndConsole); - PayloadStream.Dispose(); - - return false; - } - - if (!ProgrammerCommand.Contains("RAW_DATA") && ProgrammerRawMode) - { - LogFile.Log("Phone is not in raw mode ON, leaving...", LogType.FileAndConsole); - - // Workaround for problem - // SerialPort is sometimes not disposed correctly when the device is already removed. - // So explicitly dispose here - Serial.Close(); - - LogFile.Log("Phone has been emergency flashed unsuccessfully!", LogType.FileAndConsole); - PayloadStream.Dispose(); - - return false; - } - - if (Connected) - { - Serial.SendData(PacketFromPcToProgrammer); - } - - if (ExpectingReplyFromProgrammer) - { - if (!Connected) - { - Connected = ConnectToProgrammer(PacketFromPcToProgrammer); - - if (Connected) - { - LogFile.Log("Handshake completed with programmer in validated image programming (VIP) mode", LogType.FileAndConsole); - } - else - { - LogFile.Log("Handshake with programmer failed", LogType.FileAndConsole); - } - - if (!Connected) - { - LogFile.Log("Phone programmer is now ignoring us, leaving...", LogType.FileAndConsole); - - // Workaround for problem - // SerialPort is sometimes not disposed correctly when the device is already removed. - // So explicitly dispose here - Serial.Close(); - - LogFile.Log("Phone has been emergency flashed unsuccessfully!", LogType.FileAndConsole); - PayloadStream.Dispose(); - - return false; - } - } - else - { - do - { - Serial.SetTimeOut(500); - Incoming = Encoding.ASCII.GetString(Serial.GetResponse(null)); - Serial.SetTimeOut(200); - LogFile.Log("In: " + Incoming, LogType.FileAndConsole); - } - while (Incoming.IndexOf("response value") < 0); - - if (Incoming.Contains("rawmode=\"false\"")) - { - ProgrammerRawMode = false; - LogFile.Log("Raw mode: OFF", LogType.FileAndConsole); - } - - if (Incoming.Contains("rawmode=\"true\"")) - { - ProgrammerRawMode = true; - LogFile.Log("Raw mode: ON", LogType.FileAndConsole); - } - - if (!Incoming.Contains("ACK")) - { - LogFile.Log("Phone programmer is now ignoring us, leaving...", LogType.FileAndConsole); - - // Workaround for problem - // SerialPort is sometimes not disposed correctly when the device is already removed. - // So explicitly dispose here - Serial.Close(); - - LogFile.Log("Phone has been emergency flashed unsuccessfully!", LogType.FileAndConsole); - PayloadStream.Dispose(); - - return false; - } - } - } - } - - // Workaround for problem - // SerialPort is sometimes not disposed correctly when the device is already removed. - // So explicitly dispose here - Serial.Close(); - - LogFile.Log("Phone has been emergency flashed successfully!", LogType.FileAndConsole); - PayloadStream.Dispose(); - return true; } } diff --git a/WPinternals/Models/QualcommSaharaCommand.cs b/WPinternals/Models/QualcommSaharaCommand.cs new file mode 100644 index 0000000..4f6af62 --- /dev/null +++ b/WPinternals/Models/QualcommSaharaCommand.cs @@ -0,0 +1,45 @@ +// Copyright (c) 2018, Rene Lergner - @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. + +namespace WPinternals +{ + internal enum QualcommSaharaCommand : uint + { + HelloRequest = 0x01, + HelloResponse = 0x02, + ReadData = 0x03, + EndTransfer = 0x04, + DoneRequest = 0x05, + DoneResponse = 0x06, + ResetRequest = 0x07, + ResetResponse = 0x08, + MemoryDebug = 0x09, + MemoryRead = 0x0A, + CommandReady = 0x0B, + SwitchMode = 0x0C, + ExecuteRequest = 0x0D, + ExecuteResponse = 0x0E, + ExecuteData = 0x0F, + MemoryDebug64 = 0x10, + MemoryRead64 = 0x11, + MemoryReadData64 = 0x12, + ResetStateMachineIdentifier = 0x13 + } +} diff --git a/WPinternals/Models/QualcommSaharaMode.cs b/WPinternals/Models/QualcommSaharaMode.cs new file mode 100644 index 0000000..2249cf6 --- /dev/null +++ b/WPinternals/Models/QualcommSaharaMode.cs @@ -0,0 +1,30 @@ +// Copyright (c) 2018, Rene Lergner - @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. + +namespace WPinternals +{ + internal enum QualcommSaharaMode : uint + { + ImageTransferPending = 0x00, + ImagetransferComplete = 0x01, + MemoryDebug = 0x02, + Command = 0x03 + } +} diff --git a/WPinternals/TestCode.cs b/WPinternals/TestCode.cs index 44fd6b4..aa28243 100644 --- a/WPinternals/TestCode.cs +++ b/WPinternals/TestCode.cs @@ -272,8 +272,9 @@ namespace WPinternals // Send and start programmer QualcommSerial Serial = (QualcommSerial)Notifier.CurrentModel; QualcommSahara Sahara = new(Serial); + QualcommFirehose Firehose = new(Serial); - if (await Sahara.Reset(ProgrammerPath)) + if (await Sahara.LoadProgrammer(ProgrammerPath) && await Firehose.Reset()) { LogFile.Log("Emergency programmer test succeeded", LogType.FileAndConsole); } diff --git a/WPinternals/ViewModels/LumiaV2UnlockBootViewModel.cs b/WPinternals/ViewModels/LumiaV2UnlockBootViewModel.cs index 6c27361..934ed70 100644 --- a/WPinternals/ViewModels/LumiaV2UnlockBootViewModel.cs +++ b/WPinternals/ViewModels/LumiaV2UnlockBootViewModel.cs @@ -724,9 +724,11 @@ namespace WPinternals if (ProgrammerPath != null) { QualcommSahara Sahara = new((QualcommSerial)Notifier.CurrentModel); + QualcommFirehose Firehose = new((QualcommSerial)Notifier.CurrentModel); try { - await Sahara.Reset(ProgrammerPath); + await Sahara.LoadProgrammer(ProgrammerPath); + await Firehose.Reset(); await Notifier.WaitForArrival(); } catch (BadConnectionException) @@ -1425,9 +1427,11 @@ namespace WPinternals if (ProgrammerPath != null) { QualcommSahara Sahara = new((QualcommSerial)Notifier.CurrentModel); + QualcommFirehose Firehose = new((QualcommSerial)Notifier.CurrentModel); try { - await Sahara.Reset(ProgrammerPath); + await Sahara.LoadProgrammer(ProgrammerPath); + await Firehose.Reset(); await Notifier.WaitForArrival(); } catch (BadConnectionException) @@ -1691,9 +1695,11 @@ namespace WPinternals if (ProgrammerPath != null) { QualcommSahara Sahara = new((QualcommSerial)Notifier.CurrentModel); + QualcommFirehose Firehose = new((QualcommSerial)Notifier.CurrentModel); try { - await Sahara.Reset(ProgrammerPath); + await Sahara.LoadProgrammer(ProgrammerPath); + await Firehose.Reset(); await Notifier.WaitForArrival(); } catch (BadConnectionException)