mirror of
https://github.com/ReneLergner/WPinternals.git
synced 2026-06-14 03:16:40 +10:00
Refactor Qualcomm Sahara and Qualcomm Firehose code
This commit is contained in:
@@ -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<bool> Reset()
|
||||||
|
{
|
||||||
|
bool Connected = await Task.Run(() => ConnectToProgrammerInTestMode());
|
||||||
|
if (!Connected)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
LogFile.Log("Rebooting phone", LogType.FileAndConsole);
|
||||||
|
const string Command03 = "<?xml version=\"1.0\" ?><data><power value=\"reset\"/></data>";
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -52,7 +52,7 @@ namespace WPinternals
|
|||||||
internal QualcommPartition(byte[] Binary, uint Offset = 0)
|
internal QualcommPartition(byte[] Binary, uint Offset = 0)
|
||||||
{
|
{
|
||||||
#if DEBUG
|
#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
|
#endif
|
||||||
|
|
||||||
this.Binary = Binary;
|
this.Binary = Binary;
|
||||||
@@ -162,7 +162,7 @@ namespace WPinternals
|
|||||||
if ((CurrentCertificateOffset + CertificateSize) == (CertificatesOffset + CertificatesSize))
|
if ((CurrentCertificateOffset + CertificateSize) == (CertificatesOffset + CertificatesSize))
|
||||||
{
|
{
|
||||||
// This is the last certificate. So this is the root key.
|
// 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
|
#if DEBUG
|
||||||
System.Diagnostics.Debug.Print("RKH: " + Converter.ConvertHexToString(RootKeyHash, ""));
|
System.Diagnostics.Debug.Print("RKH: " + Converter.ConvertHexToString(RootKeyHash, ""));
|
||||||
@@ -171,7 +171,7 @@ namespace WPinternals
|
|||||||
#if DEBUG
|
#if DEBUG
|
||||||
else
|
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
|
#endif
|
||||||
CurrentCertificateOffset += CertificateSize;
|
CurrentCertificateOffset += CertificateSize;
|
||||||
@@ -183,7 +183,7 @@ namespace WPinternals
|
|||||||
CurrentCertificateOffset -= CertificateSize;
|
CurrentCertificateOffset -= CertificateSize;
|
||||||
|
|
||||||
// This is the last certificate. So this is the root key.
|
// 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
|
#if DEBUG
|
||||||
System.Diagnostics.Debug.Print("RKH: " + Converter.ConvertHexToString(RootKeyHash, ""));
|
System.Diagnostics.Debug.Print("RKH: " + Converter.ConvertHexToString(RootKeyHash, ""));
|
||||||
|
|||||||
@@ -19,48 +19,12 @@
|
|||||||
// DEALINGS IN THE SOFTWARE.
|
// DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Windows.Documents;
|
|
||||||
|
|
||||||
namespace WPinternals
|
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 delegate void ReadyHandler();
|
||||||
|
|
||||||
internal class QualcommSahara
|
internal class QualcommSahara
|
||||||
@@ -74,7 +38,7 @@ namespace WPinternals
|
|||||||
this.Serial = Serial;
|
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 CommandID = (uint)SaharaCommand;
|
||||||
UInt32 CommandBufferLength = 0;
|
UInt32 CommandBufferLength = 0;
|
||||||
@@ -96,7 +60,7 @@ namespace WPinternals
|
|||||||
return Packet;
|
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;
|
UInt32 Mode = (uint)SaharaMode;
|
||||||
|
|
||||||
@@ -118,24 +82,24 @@ namespace WPinternals
|
|||||||
ByteOperations.WriteUInt32(Hello, 0x20, 0);
|
ByteOperations.WriteUInt32(Hello, 0x20, 0);
|
||||||
ByteOperations.WriteUInt32(Hello, 0x24, 0);
|
ByteOperations.WriteUInt32(Hello, 0x24, 0);
|
||||||
|
|
||||||
return BuildCommandPacket(SaharaCommand.HelloResponse, Hello);
|
return BuildCommandPacket(QualcommSaharaCommand.HelloResponse, Hello);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static byte[] BuildExecuteRequestPacket(UInt32 RequestID)
|
private static byte[] BuildExecuteRequestPacket(UInt32 RequestID)
|
||||||
{
|
{
|
||||||
byte[] Execute = new byte[0x04];
|
byte[] Execute = new byte[0x04];
|
||||||
ByteOperations.WriteUInt32(Execute, 0x00, RequestID);
|
ByteOperations.WriteUInt32(Execute, 0x00, RequestID);
|
||||||
return BuildCommandPacket(SaharaCommand.ExecuteRequest, Execute);
|
return BuildCommandPacket(QualcommSaharaCommand.ExecuteRequest, Execute);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static byte[] BuildExecuteDataPacket(UInt32 RequestID)
|
private static byte[] BuildExecuteDataPacket(UInt32 RequestID)
|
||||||
{
|
{
|
||||||
byte[] Execute = new byte[0x04];
|
byte[] Execute = new byte[0x04];
|
||||||
ByteOperations.WriteUInt32(Execute, 0x00, RequestID);
|
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));
|
Serial.SendData(BuildExecuteRequestPacket(0x3));
|
||||||
|
|
||||||
@@ -187,7 +151,7 @@ namespace WPinternals
|
|||||||
LogFile.Log("Mode: 0x" + ByteOperations.ReadUInt32(Hello, 0x14).ToString("X8"), LogType.FileOnly);
|
LogFile.Log("Mode: 0x" + ByteOperations.ReadUInt32(Hello, 0x14).ToString("X8"), LogType.FileOnly);
|
||||||
|
|
||||||
Step = 2;
|
Step = 2;
|
||||||
byte[] HelloResponse = BuildHelloResponsePacket(SaharaMode.Command);
|
byte[] HelloResponse = BuildHelloResponsePacket(QualcommSaharaMode.Command);
|
||||||
Serial.SendData(HelloResponse);
|
Serial.SendData(HelloResponse);
|
||||||
|
|
||||||
Step = 3;
|
Step = 3;
|
||||||
@@ -200,7 +164,7 @@ namespace WPinternals
|
|||||||
}
|
}
|
||||||
|
|
||||||
Step = 4;
|
Step = 4;
|
||||||
byte[][] RKHs = GetRootKeyHashes();
|
byte[][] RKHs = GetRKHs();
|
||||||
return RKHs[0];
|
return RKHs[0];
|
||||||
}
|
}
|
||||||
catch (Exception Ex)
|
catch (Exception Ex)
|
||||||
@@ -241,7 +205,7 @@ namespace WPinternals
|
|||||||
LogFile.Log("Mode: 0x" + ByteOperations.ReadUInt32(Hello, 0x14).ToString("X8"), LogType.FileOnly);
|
LogFile.Log("Mode: 0x" + ByteOperations.ReadUInt32(Hello, 0x14).ToString("X8"), LogType.FileOnly);
|
||||||
|
|
||||||
Step = 2;
|
Step = 2;
|
||||||
byte[] HelloResponse = BuildHelloResponsePacket(SaharaMode.ImageTransferPending);
|
byte[] HelloResponse = BuildHelloResponsePacket(QualcommSaharaMode.ImageTransferPending);
|
||||||
Serial.SendData(HelloResponse);
|
Serial.SendData(HelloResponse);
|
||||||
|
|
||||||
Step = 3;
|
Step = 3;
|
||||||
@@ -316,7 +280,7 @@ namespace WPinternals
|
|||||||
LogFile.Log("MaxLength: 0x" + ByteOperations.ReadUInt32(Hello, 0x10).ToString("X8"), LogType.FileOnly);
|
LogFile.Log("MaxLength: 0x" + ByteOperations.ReadUInt32(Hello, 0x10).ToString("X8"), LogType.FileOnly);
|
||||||
LogFile.Log("Mode: 0x" + ByteOperations.ReadUInt32(Hello, 0x14).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]);
|
byte[] Ready = Serial.SendCommand(HelloResponse, [0x03, 0x00, 0x00, 0x00]);
|
||||||
}
|
}
|
||||||
@@ -330,155 +294,25 @@ namespace WPinternals
|
|||||||
|
|
||||||
public void ResetSahara()
|
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)
|
public void SwitchMode(QualcommSaharaMode Mode)
|
||||||
{
|
|
||||||
// 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<bool> 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 = "<?xml version=\"1.0\" ?><data><power value=\"reset\"/></data>";
|
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
byte[] SwitchMode = new byte[0x04];
|
byte[] SwitchMode = new byte[0x04];
|
||||||
ByteOperations.WriteUInt32(SwitchMode, 0x00, (UInt32)Mode);
|
ByteOperations.WriteUInt32(SwitchMode, 0x00, (UInt32)Mode);
|
||||||
byte[] SwitchModeCommand = BuildCommandPacket(SaharaCommand.SwitchMode, SwitchMode);
|
byte[] SwitchModeCommand = BuildCommandPacket(QualcommSaharaCommand.SwitchMode, SwitchMode);
|
||||||
|
|
||||||
byte[] ResponsePattern = null;
|
byte[] ResponsePattern = null;
|
||||||
switch (Mode)
|
switch (Mode)
|
||||||
{
|
{
|
||||||
case SaharaMode.ImageTransferPending:
|
case QualcommSaharaMode.ImageTransferPending:
|
||||||
ResponsePattern = [0x04, 0x00, 0x00, 0x00];
|
ResponsePattern = [0x04, 0x00, 0x00, 0x00];
|
||||||
break;
|
break;
|
||||||
case SaharaMode.MemoryDebug:
|
case QualcommSaharaMode.MemoryDebug:
|
||||||
ResponsePattern = [0x09, 0x00, 0x00, 0x00];
|
ResponsePattern = [0x09, 0x00, 0x00, 0x00];
|
||||||
break;
|
break;
|
||||||
case SaharaMode.Command:
|
case QualcommSaharaMode.Command:
|
||||||
ResponsePattern = [0x0B, 0x00, 0x00, 0x00];
|
ResponsePattern = [0x0B, 0x00, 0x00, 0x00];
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -488,7 +322,7 @@ namespace WPinternals
|
|||||||
public void StartProgrammer()
|
public void StartProgrammer()
|
||||||
{
|
{
|
||||||
LogFile.Log("Starting programmer", LogType.FileAndConsole);
|
LogFile.Log("Starting programmer", LogType.FileAndConsole);
|
||||||
byte[] DoneCommand = BuildCommandPacket(SaharaCommand.DoneRequest);
|
byte[] DoneCommand = BuildCommandPacket(QualcommSaharaCommand.DoneRequest);
|
||||||
bool Started = false;
|
bool Started = false;
|
||||||
int count = 0;
|
int count = 0;
|
||||||
do
|
do
|
||||||
@@ -512,236 +346,15 @@ namespace WPinternals
|
|||||||
LogFile.Log("Programmer being launched on phone", LogType.FileOnly);
|
LogFile.Log("Programmer being launched on phone", LogType.FileOnly);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<bool> SendEdPayload(string ProgrammerPath, string PayloadPath)
|
public async Task<bool> 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));
|
bool SendImageResult = await Task.Run(() => SendImage(ProgrammerPath));
|
||||||
if (!SendImageResult)
|
if (!SendImageResult)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start the emergency programmer on the phone
|
|
||||||
await Task.Run(() => StartProgrammer());
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -272,8 +272,9 @@ namespace WPinternals
|
|||||||
// Send and start programmer
|
// Send and start programmer
|
||||||
QualcommSerial Serial = (QualcommSerial)Notifier.CurrentModel;
|
QualcommSerial Serial = (QualcommSerial)Notifier.CurrentModel;
|
||||||
QualcommSahara Sahara = new(Serial);
|
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);
|
LogFile.Log("Emergency programmer test succeeded", LogType.FileAndConsole);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -724,9 +724,11 @@ namespace WPinternals
|
|||||||
if (ProgrammerPath != null)
|
if (ProgrammerPath != null)
|
||||||
{
|
{
|
||||||
QualcommSahara Sahara = new((QualcommSerial)Notifier.CurrentModel);
|
QualcommSahara Sahara = new((QualcommSerial)Notifier.CurrentModel);
|
||||||
|
QualcommFirehose Firehose = new((QualcommSerial)Notifier.CurrentModel);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await Sahara.Reset(ProgrammerPath);
|
await Sahara.LoadProgrammer(ProgrammerPath);
|
||||||
|
await Firehose.Reset();
|
||||||
await Notifier.WaitForArrival();
|
await Notifier.WaitForArrival();
|
||||||
}
|
}
|
||||||
catch (BadConnectionException)
|
catch (BadConnectionException)
|
||||||
@@ -1425,9 +1427,11 @@ namespace WPinternals
|
|||||||
if (ProgrammerPath != null)
|
if (ProgrammerPath != null)
|
||||||
{
|
{
|
||||||
QualcommSahara Sahara = new((QualcommSerial)Notifier.CurrentModel);
|
QualcommSahara Sahara = new((QualcommSerial)Notifier.CurrentModel);
|
||||||
|
QualcommFirehose Firehose = new((QualcommSerial)Notifier.CurrentModel);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await Sahara.Reset(ProgrammerPath);
|
await Sahara.LoadProgrammer(ProgrammerPath);
|
||||||
|
await Firehose.Reset();
|
||||||
await Notifier.WaitForArrival();
|
await Notifier.WaitForArrival();
|
||||||
}
|
}
|
||||||
catch (BadConnectionException)
|
catch (BadConnectionException)
|
||||||
@@ -1691,9 +1695,11 @@ namespace WPinternals
|
|||||||
if (ProgrammerPath != null)
|
if (ProgrammerPath != null)
|
||||||
{
|
{
|
||||||
QualcommSahara Sahara = new((QualcommSerial)Notifier.CurrentModel);
|
QualcommSahara Sahara = new((QualcommSerial)Notifier.CurrentModel);
|
||||||
|
QualcommFirehose Firehose = new((QualcommSerial)Notifier.CurrentModel);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await Sahara.Reset(ProgrammerPath);
|
await Sahara.LoadProgrammer(ProgrammerPath);
|
||||||
|
await Firehose.Reset();
|
||||||
await Notifier.WaitForArrival();
|
await Notifier.WaitForArrival();
|
||||||
}
|
}
|
||||||
catch (BadConnectionException)
|
catch (BadConnectionException)
|
||||||
|
|||||||
Reference in New Issue
Block a user