2024-06-19 22:48:39 +02:00
using Img2Ffu.Reader.Data ;
using System ;
2024-03-08 23:08:44 +01:00
using System.Collections.Generic ;
2024-03-04 21:50:33 +01:00
using System.Diagnostics ;
2024-06-19 22:48:39 +02:00
using System.IO ;
2024-03-08 23:08:44 +01:00
using System.Linq ;
using System.Text ;
2024-03-04 21:50:33 +01:00
namespace UnifiedFlashingPlatform
{
2024-06-19 22:48:39 +02:00
internal enum FfuProtocol
{
ProtocolSyncV1 = 1 ,
ProtocolAsyncV1 = 2 ,
ProtocolSyncV2 = 4 ,
ProtocolAsyncV2 = 8 ,
ProtocolAsyncV3 = 16
}
2024-03-04 21:50:33 +01:00
public partial class UnifiedFlashingPlatformTransport
{
2024-03-08 23:08:44 +01:00
private readonly PhoneInfo Info = new ();
2024-03-06 23:05:17 +01:00
public void FlashSectors ( uint StartSector , byte [] Data , byte TargetDevice = 0 , int Progress = 0 )
2024-03-04 21:50:33 +01:00
{
// Start sector is in UInt32, so max size of eMMC is 2 TB.
byte [] Request = new byte [ Data . Length + 0x40 ];
2024-03-04 23:24:28 +01:00
string Header = FlashSignature ; // NOKF
2024-03-08 23:08:44 +01:00
Buffer . BlockCopy ( Encoding . ASCII . GetBytes ( Header ), 0 , Request , 0 , Header . Length );
2024-03-05 20:34:12 +01:00
Request [ 0x05 ] = TargetDevice ; // Target device: 0: eMMC, 1: SDIO, 2: Other ???, 3: ???
2024-03-04 21:50:33 +01:00
Buffer . BlockCopy ( BigEndian . GetBytes ( StartSector , 4 ), 0 , Request , 0x0B , 4 ); // Start sector
Buffer . BlockCopy ( BigEndian . GetBytes ( Data . Length / 0x200 , 4 ), 0 , Request , 0x0F , 4 ); // Sector count
Request [ 0x13 ] = ( byte ) Progress ; // Progress (0 - 100)
2024-03-04 23:24:28 +01:00
Request [ 0x18 ] = 0 ; // Verify needed
Request [ 0x19 ] = 0 ; // Skip write
2024-03-04 21:50:33 +01:00
Buffer . BlockCopy ( Data , 0 , Request , 0x40 , Data . Length );
2024-03-06 23:05:17 +01:00
_ = ExecuteRawMethod ( Request );
2024-03-04 21:50:33 +01:00
}
2024-03-04 23:24:28 +01:00
public void Hello ()
2024-03-04 21:50:33 +01:00
{
byte [] Request = new byte [ 4 ];
ByteOperations . WriteAsciiString ( Request , 0 , HelloSignature );
byte [] Response = ExecuteRawMethod ( Request );
if ( Response == null )
{
throw new BadConnectionException ();
}
if ( ByteOperations . ReadAsciiString ( Response , 0 , 4 ) != HelloSignature )
{
throw new WPinternalsException ( "Bad response from phone!" , "The phone did not answer properly to the Hello message sent." );
}
}
2024-03-04 23:24:28 +01:00
public void ResetPhone ()
2024-03-04 21:50:33 +01:00
{
Debug . WriteLine ( "Rebooting phone" );
try
{
byte [] Request = new byte [ 4 ];
ByteOperations . WriteAsciiString ( Request , 0 , RebootSignature );
ExecuteRawVoidMethod ( Request );
}
catch
{
Debug . WriteLine ( "Sending reset-request failed" );
Debug . WriteLine ( "Assuming automatic reset already in progress" );
}
}
2024-03-04 23:24:28 +01:00
public GPT ReadGPT ()
2024-03-04 21:50:33 +01:00
{
// If this function is used with a locked BootMgr v1,
// then the mode-switching should be done outside this function,
// because the context-switches that are used here are not supported on BootMgr v1.
// Only works in BootLoader-mode or on unlocked bootloaders in Flash-mode!!
/*PhoneInfo Info = ReadPhoneInfo(ExtendedInfo: false);
FlashAppType OriginalAppType = Info.App;
bool Switch = (Info.App != FlashAppType.BootManager) && Info.IsBootloaderSecure;
if (Switch)
{
SwitchToBootManagerContext();
}*/
byte [] Request = new byte [ 0x04 ];
string Header = GetGPTSignature ;
2024-03-08 23:08:44 +01:00
System . Buffer . BlockCopy ( Encoding . ASCII . GetBytes ( Header ), 0 , Request , 0 , Header . Length );
2024-03-04 21:50:33 +01:00
byte [] Buffer = ExecuteRawMethod ( Request );
if (( Buffer == null ) || ( Buffer . Length < 0x4408 ))
{
throw new InvalidOperationException ( "Unable to read GPT!" );
}
2024-03-06 23:05:17 +01:00
ushort Error = ( ushort )(( Buffer [ 6 ] << 8 ) + Buffer [ 7 ]);
2024-03-04 21:50:33 +01:00
if ( Error > 0 )
{
throw new NotSupportedException ( "ReadGPT: Error 0x" + Error . ToString ( "X4" ));
}
2024-03-05 20:34:12 +01:00
// Length: 0x4400 for 512 (0x200) Sector Size (from sector 0 to sector 34)
// Length: 0x6000 for 4096 (0x1000) Sector Size (from sector 0 to sector 6)
2024-03-06 23:05:17 +01:00
uint ReturnedGPTBufferLength = ( uint ) Buffer . Length - 8 ;
uint SectorSize = Buffer . Length == 0x4408
? 512
: Buffer . Length == 0x6008
? ( uint ) 4096
: throw new NotSupportedException ( "ReadGPT: Unsupported output size! 0x" + ReturnedGPTBufferLength . ToString ( "X4" ));
2024-03-05 20:34:12 +01:00
byte [] GPTBuffer = new byte [ ReturnedGPTBufferLength - SectorSize ];
2024-03-06 23:05:17 +01:00
System . Buffer . BlockCopy ( Buffer , 8 + ( int ) SectorSize , GPTBuffer , 0 , ( int ) ReturnedGPTBufferLength - ( int ) SectorSize );
2024-03-04 21:50:33 +01:00
/*if (Switch)
{
if (OriginalAppType == FlashAppType.FlashApp)
{
SwitchToFlashAppContext();
}
else
{
SwitchToPhoneInfoAppContext();
}
}*/
2024-03-05 20:34:12 +01:00
return new GPT ( GPTBuffer , SectorSize ); // NOKT message header and MBR are ignored
2024-03-04 21:50:33 +01:00
}
2024-06-19 22:48:39 +02:00
private void ThrowFlashError ( int ErrorCode )
{
string SubMessage = ErrorCode switch
{
0x0008 => "Unsupported protocol / Invalid options" ,
0x000F => "Invalid sub block count" ,
0x0010 => "Invalid sub block length" ,
0x0012 => "Authentication required" ,
0x000E => "Invalid sub block type" ,
0x0013 => "Failed async message" ,
0x1000 => "Invalid header type" ,
0x1001 => "FFU header contain unknown extra data" ,
0x0001 => "Couldn't allocate memory" ,
0x1106 => "Security header validation failed" ,
0x1105 => "Invalid hash table size" ,
0x1104 => "Invalid catalog size" ,
0x1103 => "Invalid chunk size" ,
0x1102 => "Unsupported algorithm" ,
0x1101 => "Invalid struct size" ,
0x1100 => "Invalid signature" ,
0x1202 => "Invalid struct size" ,
0x1203 => "Unsupported algorithm" ,
0x1204 => "Invalid chunk size" ,
0x1005 => "Data not aligned correctly" ,
0x0009 => "Locate protocol failed" ,
0x1003 => "Hash mismatch" ,
0x1006 => "Couldn't find hash from security header for index" ,
0x1004 => "Security header import missing / All FFU headers have not been imported" ,
0x1304 => "Invalid platform ID" ,
0x1307 => "Invalid write descriptor info" ,
0x1306 => "Invalid write descriptor info" ,
0x1305 => "Invalid block size" ,
0x1303 => "Unsupported FFU version" ,
0x1302 => "Unsupported struct version" ,
0x1301 => "Invalid update type" ,
0x100B => "Too much payload data, all data has already been written" ,
0x1008 => "Internal error" ,
0x1007 => "Payload data does not contain all data" ,
0x0004 => "Flash write failed" ,
0x000D => "Flash verify failed" ,
0x0002 => "Flash read failed" ,
_ => "Unknown error" ,
};
WPinternalsException Ex = new ( "Flash failed!" );
Ex . SubMessage = "Error 0x" + ErrorCode . ToString ( "X4" ) + ": " + SubMessage ;
throw Ex ;
}
public void SendFfuHeaderV1 ( byte [] FfuHeader , byte Options = 0 )
{
byte [] Request = new byte [ FfuHeader . Length + 0x20 ];
const string Header = "NOKXFS" ;
Buffer . BlockCopy ( System . Text . Encoding . ASCII . GetBytes ( Header ), 0 , Request , 0 , Header . Length );
Buffer . BlockCopy ( BigEndian . GetBytes ( 0x0001 , 2 ), 0 , Request , 0x06 , 2 ); // Protocol version = 0x0001
Request [ 0x08 ] = 0 ; // Progress = 0%
Request [ 0x0B ] = 1 ; // Subblock count = 1
Buffer . BlockCopy ( BigEndian . GetBytes ( 0x0000000B , 4 ), 0 , Request , 0x0C , 4 ); // Subblock type for header = 0x0B
Buffer . BlockCopy ( BigEndian . GetBytes ( FfuHeader . Length + 0x0C , 4 ), 0 , Request , 0x10 , 4 ); // Subblock length = length of header + 0x0C
Buffer . BlockCopy ( BigEndian . GetBytes ( 0x00000000 , 4 ), 0 , Request , 0x14 , 4 ); // Header type = 0
Buffer . BlockCopy ( BigEndian . GetBytes ( FfuHeader . Length , 4 ), 0 , Request , 0x18 , 4 ); // Payload length = length of header
Request [ 0x1C ] = Options ; // Header options = 0
Buffer . BlockCopy ( FfuHeader , 0 , Request , 0x20 , FfuHeader . Length );
byte [] Response = ExecuteRawMethod ( Request );
if ( Response == null )
{
throw new BadConnectionException ();
}
int ResultCode = ( Response [ 6 ] << 8 ) + Response [ 7 ];
if ( ResultCode != 0 )
{
ThrowFlashError ( ResultCode );
}
}
public void SendFfuHeaderV2 ( uint TotalHeaderLength , uint OffsetForThisPart , byte [] FfuHeader , byte Options = 0 )
{
byte [] Request = new byte [ FfuHeader . Length + 0x3C ];
const string Header = "NOKXFS" ;
Buffer . BlockCopy ( System . Text . Encoding . ASCII . GetBytes ( Header ), 0 , Request , 0 , Header . Length );
Buffer . BlockCopy ( BigEndian . GetBytes (( int ) FfuProtocol . ProtocolSyncV2 , 2 ), 0 , Request , 0x06 , 2 ); // Protocol version = 0x0001
Request [ 0x08 ] = 0 ; // Progress = 0%
Request [ 0x0B ] = 1 ; // Subblock count = 1
Buffer . BlockCopy ( BigEndian . GetBytes ( 0x00000021 , 4 ), 0 , Request , 0x0C , 4 ); // Subblock type for header v2 = 0x21
Buffer . BlockCopy ( BigEndian . GetBytes ( FfuHeader . Length + 0x28 , 4 ), 0 , Request , 0x10 , 4 ); // Subblock starts at 0x14, payload starts at 0x3C.
Buffer . BlockCopy ( BigEndian . GetBytes ( 0x00000000 , 4 ), 0 , Request , 0x14 , 4 ); // Header type = 0
Buffer . BlockCopy ( BigEndian . GetBytes ( TotalHeaderLength , 4 ), 0 , Request , 0x18 , 4 ); // Payload length = length of header
Request [ 0x1C ] = Options ; // Header options = 0
Buffer . BlockCopy ( BigEndian . GetBytes ( OffsetForThisPart , 4 ), 0 , Request , 0x1D , 4 );
Buffer . BlockCopy ( BigEndian . GetBytes ( FfuHeader . Length , 4 ), 0 , Request , 0x21 , 4 );
Request [ 0x25 ] = 0 ; // No Erase
Buffer . BlockCopy ( FfuHeader , 0 , Request , 0x3C , FfuHeader . Length );
byte [] Response = ExecuteRawMethod ( Request );
if ( Response == null )
{
throw new BadConnectionException ();
}
if ( Response . Length == 4 )
{
throw new WPinternalsException ( "Flash protocol v2 not supported" , "The device reported that the Flash protocol v2 was not supported while sending the FFU header." );
}
int ResultCode = ( Response [ 6 ] << 8 ) + Response [ 7 ];
if ( ResultCode != 0 )
{
ThrowFlashError ( ResultCode );
}
}
public void SendFfuPayloadV1 ( byte [] FfuChunk , int Progress = 0 , byte Options = 0 )
{
byte [] Request = new byte [ FfuChunk . Length + 0x1C ];
const string Header = "NOKXFS" ;
Buffer . BlockCopy ( System . Text . Encoding . ASCII . GetBytes ( Header ), 0 , Request , 0 , Header . Length );
Buffer . BlockCopy ( BigEndian . GetBytes (( int ) FfuProtocol . ProtocolSyncV1 , 2 ), 0 , Request , 0x06 , 2 ); // Protocol version = 0x0001
Request [ 0x08 ] = ( byte ) Progress ; // Progress = 0% (0 - 100)
Request [ 0x0B ] = 1 ; // Subblock count = 1
Buffer . BlockCopy ( BigEndian . GetBytes ( 0x0000000C , 4 ), 0 , Request , 0x0C , 4 ); // Subblock type for ChunkData = 0x0C
Buffer . BlockCopy ( BigEndian . GetBytes ( FfuChunk . Length + 0x08 , 4 ), 0 , Request , 0x10 , 4 ); // Subblock length = length of chunk + 0x08
Buffer . BlockCopy ( BigEndian . GetBytes ( FfuChunk . Length , 4 ), 0 , Request , 0x14 , 4 ); // Payload length = length of chunk
Request [ 0x18 ] = Options ; // Data options = 0 (1 = verify)
Buffer . BlockCopy ( FfuChunk , 0 , Request , 0x1C , FfuChunk . Length );
byte [] Response = ExecuteRawMethod ( Request );
if ( Response == null )
{
throw new BadConnectionException ();
}
int ResultCode = ( Response [ 6 ] << 8 ) + Response [ 7 ];
if ( ResultCode != 0 )
{
ThrowFlashError ( ResultCode );
}
}
public void SendFfuPayloadV2 ( byte [] FfuChunk , int Progress = 0 , byte Options = 0 )
{
byte [] Request = new byte [ FfuChunk . Length + 0x20 ];
const string Header = "NOKXFS" ;
Buffer . BlockCopy ( System . Text . Encoding . ASCII . GetBytes ( Header ), 0 , Request , 0 , Header . Length );
Buffer . BlockCopy ( BigEndian . GetBytes (( int ) FfuProtocol . ProtocolSyncV2 , 2 ), 0 , Request , 0x06 , 2 ); // Protocol
Request [ 0x08 ] = ( byte ) Progress ; // Progress = 0% (0 - 100)
Request [ 0x0B ] = 1 ; // Subblock count = 1
Buffer . BlockCopy ( BigEndian . GetBytes ( 0x0000001B , 4 ), 0 , Request , 0x0C , 4 ); // Subblock type for Payload v2 = 0x1B
Buffer . BlockCopy ( BigEndian . GetBytes ( FfuChunk . Length + 0x0C , 4 ), 0 , Request , 0x10 , 4 ); // Subblock length = length of chunk + 0x08
Buffer . BlockCopy ( BigEndian . GetBytes ( FfuChunk . Length , 4 ), 0 , Request , 0x14 , 4 ); // Payload length = length of chunk
Request [ 0x18 ] = Options ; // Data options = 0 (1 = verify)
Buffer . BlockCopy ( FfuChunk , 0 , Request , 0x20 , FfuChunk . Length );
byte [] Response = ExecuteRawMethod ( Request );
if ( Response == null )
{
throw new BadConnectionException ();
}
int ResultCode = ( Response [ 6 ] << 8 ) + Response [ 7 ];
if ( ResultCode != 0 )
{
ThrowFlashError ( ResultCode );
}
}
public void SendFfuPayloadV3 ( byte [] FfuChunk , uint WriteDescriptorIndex , uint CRC , int Progress = 0 , byte Options = 0 )
{
byte [] Request = new byte [ FfuChunk . Length + 0x20 ];
const string Header = "NOKXFS" ;
Buffer . BlockCopy ( System . Text . Encoding . ASCII . GetBytes ( Header ), 0 , Request , 0 , Header . Length );
Buffer . BlockCopy ( BigEndian . GetBytes (( int ) FfuProtocol . ProtocolAsyncV3 , 2 ), 0 , Request , 0x06 , 2 ); // Protocol
Request [ 0x08 ] = ( byte ) Progress ; // Progress = 0% (0 - 100)
Request [ 0x0B ] = 1 ; // Subblock count = 1
Buffer . BlockCopy ( BigEndian . GetBytes ( 0x0000001D , 4 ), 0 , Request , 0x0C , 4 ); // Subblock type for Payload v2 = 0x1B
Buffer . BlockCopy ( BigEndian . GetBytes ( FfuChunk . Length + 0x2C , 4 ), 0 , Request , 0x10 , 4 ); // Subblock length = length of chunk + 0x08
Buffer . BlockCopy ( BigEndian . GetBytes ( FfuChunk . Length , 4 ), 0 , Request , 0x14 , 4 ); // Payload length = length of chunk
Request [ 0x18 ] = Options ; // Data options = 0 (1 = verify)
Buffer . BlockCopy ( BigEndian . GetBytes ( WriteDescriptorIndex , 4 ), 0 , Request , 0x19 , 4 ); // Payload length = length of chunk
Buffer . BlockCopy ( BigEndian . GetBytes ( CRC , 4 ), 0 , Request , 0x1D , 4 ); // Payload length = length of chunk
Buffer . BlockCopy ( FfuChunk , 0 , Request , 0x40 , FfuChunk . Length );
byte [] Response = ExecuteRawMethod ( Request );
if ( Response == null )
{
throw new BadConnectionException ();
}
int ResultCode = ( Response [ 6 ] << 8 ) + Response [ 7 ];
if ( ResultCode != 0 )
{
ThrowFlashError ( ResultCode );
}
}
public class ProgressUpdater
{
private readonly DateTime InitTime ;
private DateTime LastUpdateTime ;
private readonly ulong MaxValue ;
private readonly Action < int , TimeSpan ?> ProgressUpdateCallback ;
public int ProgressPercentage ;
public ProgressUpdater ( ulong MaxValue , Action < int , TimeSpan ?> ProgressUpdateCallback )
{
InitTime = DateTime . Now ;
LastUpdateTime = DateTime . Now ;
this . MaxValue = MaxValue ;
this . ProgressUpdateCallback = ProgressUpdateCallback ;
SetProgress ( 0 );
}
private ulong _Progress ;
public ulong Progress
{
get
{
return _Progress ;
}
}
public void SetProgress ( ulong NewValue )
{
if ( _Progress != NewValue )
{
int PreviousProgressPercentage = ( int )(( double ) _Progress / MaxValue * 100 );
ProgressPercentage = ( int )(( double ) NewValue / MaxValue * 100 );
_Progress = NewValue ;
if ((( DateTime . Now - LastUpdateTime ) > TimeSpan . FromSeconds ( 0.5 )) || ( ProgressPercentage == 100 ))
{
#if DEBUG
Console . WriteLine ( "Init time: " + InitTime . ToShortTimeString () + " / Now: " + DateTime . Now . ToString () + " / NewValue: " + NewValue . ToString () + " / MaxValue: " + MaxValue . ToString () + " ->> Percentage: " + ProgressPercentage . ToString () + " / Remaining: " + TimeSpan . FromTicks (( long )(( DateTime . Now - InitTime ). Ticks / (( double ) NewValue / MaxValue ) * ( 1 - (( double ) NewValue / MaxValue )))). ToString ());
#endif
if ((( DateTime . Now - InitTime ) < TimeSpan . FromSeconds ( 30 )) && ( ProgressPercentage < 15 ))
{
ProgressUpdateCallback ( ProgressPercentage , null );
}
else
{
ProgressUpdateCallback ( ProgressPercentage , TimeSpan . FromTicks (( long )(( DateTime . Now - InitTime ). Ticks / (( double ) NewValue / MaxValue ) * ( 1 - (( double ) NewValue / MaxValue )))));
}
LastUpdateTime = DateTime . Now ;
}
}
}
public void IncreaseProgress ( ulong Progress )
{
SetProgress ( _Progress + Progress );
}
}
public void FlashFFU ( string FFUPath , bool ResetAfterwards = true , byte Options = 0 )
{
using FileStream FfuFile = new ( FFUPath , FileMode . Open , FileAccess . Read );
FlashFFU ( FfuFile , ResetAfterwards , Options );
}
public void FlashFFU ( FileStream FfuFile , bool ResetAfterwards = true , byte Options = 0 )
{
FlashFFU ( FfuFile , null , ResetAfterwards , Options );
}
public void FlashFFU ( FileStream FfuFile , ProgressUpdater UpdaterPerChunk , bool ResetAfterwards = true , byte Options = 0 )
{
ProgressUpdater Progress = UpdaterPerChunk ;
PhoneInfo Info = ReadPhoneInfo ();
if (( Info . SecureFfuSupportedProtocolMask & (( ushort ) FfuProtocol . ProtocolSyncV1 | ( ushort ) FfuProtocol . ProtocolSyncV2 )) == 0 )
{
throw new WPinternalsException ( "Flash failed!" , "Protocols not supported. The device reports that both Protocol Sync v1 and Protocol Sync v2 are not supported for FFU flashing. Is this an old device?" );
}
long position = FfuFile . Position ;
SignedImage FFU = new ( FfuFile );
int chunkSize = FFU . ChunkSize ;
ulong totalChunkCount = FFU . TotalChunkCount ;
ulong CombinedFFUHeaderSize = FFU . HeaderSize ;
FfuFile . Seek ( position , SeekOrigin . Begin );
byte [] FfuHeader = new byte [ CombinedFFUHeaderSize ];
FfuFile . Read ( FfuHeader , 0 , ( int ) CombinedFFUHeaderSize );
SendFfuHeaderV1 ( FfuHeader , Options );
ulong Position = CombinedFFUHeaderSize ;
byte [] Payload ;
int ChunkCount = 0 ;
if (( Info . SecureFfuSupportedProtocolMask & ( ushort ) FfuProtocol . ProtocolSyncV2 ) == 0 )
{
// Protocol v1
Payload = new byte [ chunkSize ];
while ( Position < ( ulong ) FfuFile . Length )
{
FfuFile . Read ( Payload , 0 , Payload . Length );
ChunkCount ++;
SendFfuPayloadV1 ( Payload , ( int )(( double ) ChunkCount * 100 / totalChunkCount ), 0 );
Position += ( ulong ) Payload . Length ;
Progress ?. IncreaseProgress ( 1 );
}
}
else
{
// Protocol v2
Payload = new byte [ Info . WriteBufferSize ];
while ( Position < ( ulong ) FfuFile . Length )
{
uint PayloadSize = Info . WriteBufferSize ;
if ((( ulong ) FfuFile . Length - Position ) < PayloadSize )
{
PayloadSize = ( uint )(( ulong ) FfuFile . Length - Position );
Payload = new byte [ PayloadSize ];
}
FfuFile . Read ( Payload , 0 , ( int ) PayloadSize );
ChunkCount += ( int )( PayloadSize / chunkSize );
SendFfuPayloadV2 ( Payload , ( int )(( double ) ChunkCount * 100 / totalChunkCount ), 0 );
Position += PayloadSize ;
Progress ?. IncreaseProgress (( ulong )( PayloadSize / chunkSize ));
}
}
if ( ResetAfterwards )
{
ResetPhone ();
}
}
2024-03-08 23:08:44 +01:00
public PhoneInfo ReadPhoneInfo ()
2024-03-04 23:24:28 +01:00
{
2024-03-08 23:08:44 +01:00
// NOKV = Info Query
bool PhoneInfoLogged = Info . State != PhoneInfoState . Empty ;
PhoneInfo Result = Info ;
if ( Result . State == PhoneInfoState . Empty )
{
byte [] Request = new byte [ 4 ];
ByteOperations . WriteAsciiString ( Request , 0 , "NOKV" );
byte [] Response = ExecuteRawMethod ( Request );
if (( Response != null ) && ( ByteOperations . ReadAsciiString ( Response , 0 , 4 ) != "NOKU" ))
{
Result . App = ( FlashAppType ) Response [ 5 ];
switch ( Result . App )
{
case FlashAppType . FlashApp :
Result . FlashAppProtocolVersionMajor = Response [ 6 ];
Result . FlashAppProtocolVersionMinor = Response [ 7 ];
Result . FlashAppVersionMajor = Response [ 8 ];
Result . FlashAppVersionMinor = Response [ 9 ];
break ;
}
byte SubblockCount = Response [ 10 ];
int SubblockOffset = 11 ;
for ( int i = 0 ; i < SubblockCount ; i ++)
{
byte SubblockID = Response [ SubblockOffset + 0x00 ];
2024-06-19 22:48:39 +02:00
ushort SubblockLength = BigEndian . ToUInt16 ( Response , SubblockOffset + 0x01 );
2024-03-08 23:08:44 +01:00
int SubblockPayloadOffset = SubblockOffset + 3 ;
byte SubblockVersion ;
switch ( SubblockID )
{
case 0x01 :
Result . TransferSize = BigEndian . ToUInt32 ( Response , SubblockPayloadOffset );
break ;
case 0x02 :
Result . WriteBufferSize = BigEndian . ToUInt32 ( Response , SubblockPayloadOffset );
break ;
case 0x03 :
Result . EmmcSizeInSectors = BigEndian . ToUInt32 ( Response , SubblockPayloadOffset );
break ;
case 0x04 :
if ( Result . App == FlashAppType . FlashApp )
{
Result . SdCardSizeInSectors = BigEndian . ToUInt32 ( Response , SubblockPayloadOffset );
}
break ;
case 0x05 :
Result . PlatformID = ByteOperations . ReadAsciiString ( Response , ( uint ) SubblockPayloadOffset , SubblockLength ). Trim ( new char [] { ' ' , '\0' });
break ;
case 0x0D :
Result . AsyncSupport = Response [ SubblockPayloadOffset + 1 ] == 1 ;
break ;
case 0x0F : // Supported but check parsing below pls
SubblockVersion = Response [ SubblockPayloadOffset ]; // 0x03
Result . PlatformSecureBootEnabled = Response [ SubblockPayloadOffset + 0x01 ] == 0x01 ;
Result . SecureFfuEnabled = Response [ SubblockPayloadOffset + 0x02 ] == 0x01 ;
Result . JtagDisabled = Response [ SubblockPayloadOffset + 0x03 ] == 0x01 ;
Result . RdcPresent = Response [ SubblockPayloadOffset + 0x04 ] == 0x01 ;
Result . Authenticated = ( Response [ SubblockPayloadOffset + 0x05 ] == 0x01 ) || ( Response [ SubblockPayloadOffset + 0x05 ] == 0x02 );
Result . UefiSecureBootEnabled = Response [ SubblockPayloadOffset + 0x06 ] == 0x01 ;
Result . SecondaryHardwareKeyPresent = Response [ SubblockPayloadOffset + 0x07 ] == 0x01 ;
break ;
case 0x10 : // Also check to be sure
SubblockVersion = Response [ SubblockPayloadOffset ]; // 0x01
Result . SecureFfuSupportedProtocolMask = BigEndian . ToUInt16 ( Response , SubblockPayloadOffset + 0x01 );
break ;
case 0x1F : // Recheck too
Result . MmosOverUsbSupported = Response [ SubblockPayloadOffset ] == 1 ;
break ;
case 0x20 :
// CRC header info
break ;
case 0x22 :
uint SectorCount = BigEndian . ToUInt32 ( Response , SubblockPayloadOffset );
uint SectorSize = BigEndian . ToUInt32 ( Response , SubblockPayloadOffset + 4 );
ushort FlashType = BigEndian . ToUInt16 ( Response , SubblockPayloadOffset + 8 );
ushort FlashTypeIndex = BigEndian . ToUInt16 ( Response , SubblockPayloadOffset + 10 );
uint Unknown = BigEndian . ToUInt32 ( Response , SubblockPayloadOffset + 12 );
string DevicePath = ByteOperations . ReadUnicodeString ( Response , ( uint ) SubblockPayloadOffset + 16 , ( uint ) SubblockLength - 16 ). Trim ( new char [] { ' ' , '\0' });
Result . BootDevices . Add (( SectorCount , SectorSize , FlashType , FlashTypeIndex , Unknown , DevicePath ));
break ;
case 0x23 :
byte [] Bytes = Response [ SubblockPayloadOffset ..( SubblockPayloadOffset + SubblockLength )];
ushort ManufacturerLength = BitConverter . ToUInt16 ( Bytes [ 0. . 2 ]. Reverse (). ToArray ());
ushort FamilyLength = BitConverter . ToUInt16 ( Bytes [ 2. . 4 ]. Reverse (). ToArray ());
ushort ProductNameLength = BitConverter . ToUInt16 ( Bytes [ 4. . 6 ]. Reverse (). ToArray ());
ushort ProductVersionLength = BitConverter . ToUInt16 ( Bytes [ 6. . 8 ]. Reverse (). ToArray ());
ushort SKUNumberLength = BitConverter . ToUInt16 ( Bytes [ 8. . 10 ]. Reverse (). ToArray ());
ushort BaseboardManufacturerLength = BitConverter . ToUInt16 ( Bytes [ 10. . 12 ]. Reverse (). ToArray ());
ushort BaseboardProductLength = BitConverter . ToUInt16 ( Bytes [ 12. . 14 ]. Reverse (). ToArray ());
int CurrentOffset = 14 ;
Result . Manufacturer = Encoding . ASCII . GetString ( Bytes [ CurrentOffset ..( CurrentOffset + ManufacturerLength )]);
CurrentOffset += ManufacturerLength ;
Result . Family = Encoding . ASCII . GetString ( Bytes [ CurrentOffset ..( CurrentOffset + FamilyLength )]);
CurrentOffset += FamilyLength ;
Result . ProductName = Encoding . ASCII . GetString ( Bytes [ CurrentOffset ..( CurrentOffset + ProductNameLength )]);
CurrentOffset += ProductNameLength ;
Result . ProductVersion = Encoding . ASCII . GetString ( Bytes [ CurrentOffset ..( CurrentOffset + ProductVersionLength )]);
CurrentOffset += ProductVersionLength ;
Result . SKUNumber = Encoding . ASCII . GetString ( Bytes [ CurrentOffset ..( CurrentOffset + SKUNumberLength )]);
CurrentOffset += SKUNumberLength ;
Result . BaseboardManufacturer = Encoding . ASCII . GetString ( Bytes [ CurrentOffset ..( CurrentOffset + BaseboardManufacturerLength )]);
CurrentOffset += BaseboardManufacturerLength ;
Result . BaseboardProduct = Encoding . ASCII . GetString ( Bytes [ CurrentOffset ..( CurrentOffset + BaseboardProductLength )]);
break ;
case 0x24 :
Result . LargestMemoryRegion = BitConverter . ToUInt64 ( Response [ SubblockPayloadOffset ..( SubblockPayloadOffset + 8 )]. Reverse (). ToArray ());
break ;
case 0x25 :
Result . AppType = ( AppType ) Response [ SubblockPayloadOffset ];
break ;
default :
Console . WriteLine ( $"Unknown Subblock: ID: 0x{SubblockID:X2} Length: 0x{SubblockLength:X4}" );
break ;
}
SubblockOffset += SubblockLength + 3 ;
}
}
Result . State = PhoneInfoState . Basic ;
}
Result . IsBootloaderSecure = !( Info . Authenticated || Info . RdcPresent || ! Info . SecureFfuEnabled );
if (! PhoneInfoLogged )
{
Result . Log ();
}
return Result ;
}
public enum FlashAppType
{
FlashApp = 2
};
public enum PhoneInfoState
{
Empty ,
Basic
};
public class PhoneInfo
{
public PhoneInfoState State = PhoneInfoState . Empty ;
public FlashAppType App ;
public byte FlashAppVersionMajor ;
public byte FlashAppVersionMinor ;
public byte FlashAppProtocolVersionMajor ;
public byte FlashAppProtocolVersionMinor ;
2024-06-19 22:48:39 +02:00
public uint TransferSize ;
2024-03-08 23:08:44 +01:00
public bool MmosOverUsbSupported ;
2024-06-19 22:48:39 +02:00
public uint SdCardSizeInSectors ;
public uint WriteBufferSize ;
public uint EmmcSizeInSectors ;
2024-03-08 23:08:44 +01:00
public string PlatformID ;
2024-06-19 22:48:39 +02:00
public ushort SecureFfuSupportedProtocolMask ;
2024-03-08 23:08:44 +01:00
public bool AsyncSupport ;
public bool PlatformSecureBootEnabled ;
public bool SecureFfuEnabled ;
public bool JtagDisabled ;
public bool RdcPresent ;
public bool Authenticated ;
public bool UefiSecureBootEnabled ;
public bool SecondaryHardwareKeyPresent ;
public string Manufacturer ;
public string Family ;
public string ProductName ;
public string ProductVersion ;
public string SKUNumber ;
public string BaseboardManufacturer ;
public string BaseboardProduct ;
2024-06-19 22:48:39 +02:00
public ulong LargestMemoryRegion ;
2024-03-08 23:08:44 +01:00
public AppType AppType ;
public List <( uint SectorCount , uint SectorSize , ushort FlashType , ushort FlashIndex , uint Unknown , string DevicePath )> BootDevices = new ();
public bool IsBootloaderSecure ;
public void Log ()
{
switch ( App )
{
case FlashAppType . FlashApp :
Console . WriteLine ( "Flash app: " + FlashAppVersionMajor + "." + FlashAppVersionMinor );
Console . WriteLine ( "Flash protocol: " + FlashAppProtocolVersionMajor + "." + FlashAppProtocolVersionMinor );
break ;
}
Console . WriteLine ( "SecureBoot: " + ((! PlatformSecureBootEnabled || ! UefiSecureBootEnabled ) ? "Disabled" : "Enabled" ) + " (Platform Secure Boot: " + ( PlatformSecureBootEnabled ? "Enabled" : "Disabled" ) + ", UEFI Secure Boot: " + ( UefiSecureBootEnabled ? "Enabled" : "Disabled" ) + ")" );
Console . WriteLine ( "Flash app security: " + (! IsBootloaderSecure ? "Disabled" : "Enabled" ));
Console . WriteLine ( "Flash app security: " + (! IsBootloaderSecure ? "Disabled" : "Enabled" ) + " (FFU security: " + ( SecureFfuEnabled ? "Enabled" : "Disabled" ) + ", RDC: " + ( RdcPresent ? "Present" : "Not found" ) + ", Authenticated: " + ( Authenticated ? "True" : "False" ) + ")" );
Console . WriteLine ( "JTAG: " + ( JtagDisabled ? "Disabled" : "Enabled" ));
}
2024-03-04 23:24:28 +01:00
}
2024-03-04 21:50:33 +01:00
public void Shutdown ()
{
byte [] Request = new byte [ 4 ];
string Header = ShutdownSignature ;
2024-03-08 23:08:44 +01:00
Buffer . BlockCopy ( Encoding . ASCII . GetBytes ( Header ), 0 , Request , 0 , Header . Length );
2024-03-04 23:24:28 +01:00
ExecuteRawVoidMethod ( Request );
2024-03-04 21:50:33 +01:00
}
}
}