From af61c9ad29209d37a01ec34dfd4b7e66b089b67e Mon Sep 17 00:00:00 2001 From: Gustave Monce Date: Thu, 16 Apr 2026 18:46:00 +0200 Subject: [PATCH] Preliminary support for UFP --- WPinternals/Models/UFP/AppType.cs | 33 ++ WPinternals/Models/UFP/DeviceLogType.cs | 33 ++ WPinternals/Models/UFP/DeviceTargetingInfo.cs | 47 ++ WPinternals/Models/UFP/FfuProtocol.cs | 11 + WPinternals/Models/UFP/FlashAppInfo.cs | 41 ++ WPinternals/Models/UFP/Mode.cs | 31 + WPinternals/Models/UFP/ResetProtectionInfo.cs | 39 ++ WPinternals/Models/UFP/UEFI/BOOT_OPTION.cs | 85 +++ WPinternals/Models/UFP/UEFI/FILE_INFO.cs | 53 ++ WPinternals/Models/UFP/UEFI/FileAttribute.cs | 20 + .../Models/UFP/UEFI/LoadOptionAttribute.cs | 19 + WPinternals/Models/UFP/UEFI/UefiDateTime.cs | 62 ++ .../Models/UFP/UEFI/UefiVariableAttributes.cs | 41 ++ WPinternals/Models/UFP/USBSpeed.cs | 37 ++ WPinternals/Models/UFP/UefiVariable.cs | 42 ++ ...ifiedFlashingPlatformTransport.Commands.cs | 136 +++++ ...fiedFlashingPlatformTransport.ReadParam.cs | 534 +++++++++++++++++ .../UnifiedFlashingPlatformTransport.WPI.cs | 543 ++++++++++++++++++ ...iedFlashingPlatformTransport.WriteParam.cs | 61 ++ .../UFP/UnifiedFlashingPlatformTransport.cs | 359 ++++++++++++ WPinternals/ViewModels/LumiaInfoViewModel.cs | 4 + WPinternals/ViewModels/LumiaModeViewModel.cs | 4 + WPinternals/ViewModels/MainViewModel.cs | 3 +- .../ViewModels/PhoneNotifierViewModel.cs | 23 +- WPinternals/ViewModels/SwitchModeViewModel.cs | 31 + WPinternals/ViewModels/UFPModeViewModel.cs | 56 ++ WPinternals/ViewModels/UFPViewModel.cs | 258 +++++++++ WPinternals/Views/MainWindow.xaml | 6 + WPinternals/Views/NokiaModeFlashView.xaml | 10 +- WPinternals/Views/UFPModeView.xaml | 87 +++ WPinternals/Views/UFPModeView.xaml.cs | 50 ++ WPinternals/Views/UFPView.xaml | 125 ++++ WPinternals/Views/UFPView.xaml.cs | 51 ++ WPinternals/WPinternals.csproj | 3 + 34 files changed, 2931 insertions(+), 7 deletions(-) create mode 100644 WPinternals/Models/UFP/AppType.cs create mode 100644 WPinternals/Models/UFP/DeviceLogType.cs create mode 100644 WPinternals/Models/UFP/DeviceTargetingInfo.cs create mode 100644 WPinternals/Models/UFP/FfuProtocol.cs create mode 100644 WPinternals/Models/UFP/FlashAppInfo.cs create mode 100644 WPinternals/Models/UFP/Mode.cs create mode 100644 WPinternals/Models/UFP/ResetProtectionInfo.cs create mode 100644 WPinternals/Models/UFP/UEFI/BOOT_OPTION.cs create mode 100644 WPinternals/Models/UFP/UEFI/FILE_INFO.cs create mode 100644 WPinternals/Models/UFP/UEFI/FileAttribute.cs create mode 100644 WPinternals/Models/UFP/UEFI/LoadOptionAttribute.cs create mode 100644 WPinternals/Models/UFP/UEFI/UefiDateTime.cs create mode 100644 WPinternals/Models/UFP/UEFI/UefiVariableAttributes.cs create mode 100644 WPinternals/Models/UFP/USBSpeed.cs create mode 100644 WPinternals/Models/UFP/UefiVariable.cs create mode 100644 WPinternals/Models/UFP/UnifiedFlashingPlatformTransport.Commands.cs create mode 100644 WPinternals/Models/UFP/UnifiedFlashingPlatformTransport.ReadParam.cs create mode 100644 WPinternals/Models/UFP/UnifiedFlashingPlatformTransport.WPI.cs create mode 100644 WPinternals/Models/UFP/UnifiedFlashingPlatformTransport.WriteParam.cs create mode 100644 WPinternals/Models/UFP/UnifiedFlashingPlatformTransport.cs create mode 100644 WPinternals/ViewModels/UFPModeViewModel.cs create mode 100644 WPinternals/ViewModels/UFPViewModel.cs create mode 100644 WPinternals/Views/UFPModeView.xaml create mode 100644 WPinternals/Views/UFPModeView.xaml.cs create mode 100644 WPinternals/Views/UFPView.xaml create mode 100644 WPinternals/Views/UFPView.xaml.cs diff --git a/WPinternals/Models/UFP/AppType.cs b/WPinternals/Models/UFP/AppType.cs new file mode 100644 index 0000000..8c27002 --- /dev/null +++ b/WPinternals/Models/UFP/AppType.cs @@ -0,0 +1,33 @@ +/* +* MIT License +* +* Copyright (c) 2024 The DuoWOA authors +* +* 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 UnifiedFlashingPlatform +{ + public enum AppType : byte + { + Min, + UEFI, + BOOT, + Max + }; +} \ No newline at end of file diff --git a/WPinternals/Models/UFP/DeviceLogType.cs b/WPinternals/Models/UFP/DeviceLogType.cs new file mode 100644 index 0000000..bde23d6 --- /dev/null +++ b/WPinternals/Models/UFP/DeviceLogType.cs @@ -0,0 +1,33 @@ +/* +* MIT License +* +* Copyright (c) 2024 The DuoWOA authors +* +* 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 UnifiedFlashingPlatform +{ + public enum DeviceLogType + { + Min, + Flashing, + Servicing, + Max + } +} \ No newline at end of file diff --git a/WPinternals/Models/UFP/DeviceTargetingInfo.cs b/WPinternals/Models/UFP/DeviceTargetingInfo.cs new file mode 100644 index 0000000..fb0d5c3 --- /dev/null +++ b/WPinternals/Models/UFP/DeviceTargetingInfo.cs @@ -0,0 +1,47 @@ +/* +* MIT License +* +* Copyright (c) 2024 The DuoWOA authors +* +* 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 UnifiedFlashingPlatform +{ + public struct DeviceTargetingInfo + { + public string Manufacturer; + public string Family; + public string ProductName; + public string ProductVersion; + public string SKUNumber; + public string BaseboardManufacturer; + public string BaseboardProduct; + + public override readonly string ToString() + { + return "Manufacturer: " + Manufacturer + + " - Family: " + Family + + " - Product Name: " + ProductName + + " - Product Version: " + ProductVersion + + " - SKU Number: " + SKUNumber + + " - Baseboard Manufacturer: " + BaseboardManufacturer + + " - Baseboard Product: " + BaseboardProduct; + } + } +} \ No newline at end of file diff --git a/WPinternals/Models/UFP/FfuProtocol.cs b/WPinternals/Models/UFP/FfuProtocol.cs new file mode 100644 index 0000000..3511181 --- /dev/null +++ b/WPinternals/Models/UFP/FfuProtocol.cs @@ -0,0 +1,11 @@ +namespace UnifiedFlashingPlatform +{ + internal enum FfuProtocol + { + ProtocolSyncV1 = 1, + ProtocolAsyncV1 = 2, + ProtocolSyncV2 = 4, + ProtocolAsyncV2 = 8, + ProtocolAsyncV3 = 16 + } +} diff --git a/WPinternals/Models/UFP/FlashAppInfo.cs b/WPinternals/Models/UFP/FlashAppInfo.cs new file mode 100644 index 0000000..3914521 --- /dev/null +++ b/WPinternals/Models/UFP/FlashAppInfo.cs @@ -0,0 +1,41 @@ +/* +* MIT License +* +* Copyright (c) 2024 The DuoWOA authors +* +* 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 UnifiedFlashingPlatform +{ + public struct FlashAppInfo + { + public byte ProtocolMajorVersion; + public byte ProtocolMinorVersion; + public byte ImplementationMajorVersion; + public byte ImplementationMinorVersion; + + public override readonly string ToString() + { + return "ProtocolMajorVersion: " + ProtocolMajorVersion + + " - ProtocolMinorVersion: " + ProtocolMinorVersion + + " - ImplementationMajorVersion: " + ImplementationMajorVersion + + " - ImplementationMinorVersion: " + ImplementationMinorVersion; + } + } +} \ No newline at end of file diff --git a/WPinternals/Models/UFP/Mode.cs b/WPinternals/Models/UFP/Mode.cs new file mode 100644 index 0000000..ca1304d --- /dev/null +++ b/WPinternals/Models/UFP/Mode.cs @@ -0,0 +1,31 @@ +/* +* MIT License +* +* Copyright (c) 2024 The DuoWOA authors +* +* 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 UnifiedFlashingPlatform +{ + public enum Mode : byte + { + DiagnosticMode, + Max + } +} \ No newline at end of file diff --git a/WPinternals/Models/UFP/ResetProtectionInfo.cs b/WPinternals/Models/UFP/ResetProtectionInfo.cs new file mode 100644 index 0000000..9d7cdaa --- /dev/null +++ b/WPinternals/Models/UFP/ResetProtectionInfo.cs @@ -0,0 +1,39 @@ +/* +* MIT License +* +* Copyright (c) 2024 The DuoWOA authors +* +* 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 UnifiedFlashingPlatform +{ + public struct ResetProtectionInfo + { + public bool IsResetProtectionEnabled; + public uint MajorVersion; + public uint MinorVersion; + + public override readonly string ToString() + { + return "IsResetProtectionEnabled: " + IsResetProtectionEnabled + + " - MajorVersion: " + MajorVersion + + " - MinorVersion: " + MinorVersion; + } + } +} \ No newline at end of file diff --git a/WPinternals/Models/UFP/UEFI/BOOT_OPTION.cs b/WPinternals/Models/UFP/UEFI/BOOT_OPTION.cs new file mode 100644 index 0000000..bc1ac37 --- /dev/null +++ b/WPinternals/Models/UFP/UEFI/BOOT_OPTION.cs @@ -0,0 +1,85 @@ +using System; +using System.Drawing; +using System.Text; +using WPinternals.HelperClasses; + +namespace UnifiedFlashingPlatform.UEFI +{ + public struct BOOT_OPTION + { + public byte BootOrderIndex; + public ushort BootOption; + public LoadOptionAttribute Attributes; + public string Description; + public string DevicePath; + public string CommandLine; + + public ushort DescriptionStringOffset; + public ushort DevicePathStringOffset; + public ushort CommandLineStringOffset; + public ushort TotalSize; + + public static BOOT_OPTION ReadFromBuffer(byte[] Buffer, int offset) + { + byte BootOrderIndex = Buffer[offset]; + var Attributes = (LoadOptionAttribute)BigEndian.ToUInt32(Buffer, offset + 1); + ushort BootOption = BigEndian.ToUInt16(Buffer, offset + 5); + // Latest + ushort DescriptionStringOffset = BigEndian.ToUInt16(Buffer, offset + 7); + ushort DevicePathStringOffset = BigEndian.ToUInt16(Buffer, offset + 9); + ushort CommandLineStringOffset = BigEndian.ToUInt16(Buffer, offset + 11); + ushort TotalSize = BigEndian.ToUInt16(Buffer, offset + 13); + + string Description = Encoding.Unicode.GetString(Buffer, offset + DescriptionStringOffset, DevicePathStringOffset - DescriptionStringOffset); + string DevicePath; + string CommandLine = ""; + + if (CommandLineStringOffset == 0) + { + DevicePath = Encoding.Unicode.GetString(Buffer, offset + DevicePathStringOffset, TotalSize - DevicePathStringOffset); + } + else + { + DevicePath = Encoding.Unicode.GetString(Buffer, offset + DevicePathStringOffset, CommandLineStringOffset - DevicePathStringOffset); + CommandLine = Encoding.Unicode.GetString(Buffer, offset + CommandLineStringOffset, TotalSize - CommandLineStringOffset); + } + + // Older + /*ushort DescriptionStringLength = BigEndian.ToUInt16(Buffer, offset + 7); + ushort DevicePathStringLength = BigEndian.ToUInt16(Buffer, offset + 9); + + string Description = Encoding.Unicode.GetString(Buffer, offset + 11, DescriptionStringLength); + string DevicePath = Encoding.Unicode.GetString(Buffer, offset + 11 + DescriptionStringLength, DevicePathStringLength); + string CommandLine = ""; + + ushort DescriptionStringOffset = 11; + ushort DevicePathStringOffset = (ushort)(11 + DescriptionStringLength); + ushort CommandLineStringOffset = 0; + ushort TotalSize = (ushort)(11 + DescriptionStringLength + DevicePathStringLength);*/ + + return new BOOT_OPTION() + { + BootOrderIndex = BootOrderIndex, + Attributes = Attributes, + BootOption = BootOption, + Description = Description, + DevicePath = DevicePath, + CommandLine = CommandLine, + DescriptionStringOffset = DescriptionStringOffset, + DevicePathStringOffset = DevicePathStringOffset, + CommandLineStringOffset = CommandLineStringOffset, + TotalSize = TotalSize + }; + } + + public override readonly string ToString() + { + return "BootOrderIndex: " + BootOrderIndex + + " - Attributes: " + Attributes + + " - BootOption: " + BootOption + + " - Description: " + Description + + " - DevicePath: " + DevicePath + + " - CommandLine: " + CommandLine; + } + } +} diff --git a/WPinternals/Models/UFP/UEFI/FILE_INFO.cs b/WPinternals/Models/UFP/UEFI/FILE_INFO.cs new file mode 100644 index 0000000..e86d8ce --- /dev/null +++ b/WPinternals/Models/UFP/UEFI/FILE_INFO.cs @@ -0,0 +1,53 @@ +using System; +using System.Text; + +namespace UnifiedFlashingPlatform.UEFI +{ + public struct FILE_INFO + { + public ulong Size; + public ulong FileSize; + public ulong PhysicalSize; + public UefiDateTime CreateTime; + public UefiDateTime LastAccessTime; + public UefiDateTime ModificationTime; + public FileAttribute Attribute; + public string FileName; + + public static FILE_INFO ReadFromBuffer(byte[] Buffer, int offset) + { + ulong size = BitConverter.ToUInt64(Buffer, offset); + ulong fileSize = BitConverter.ToUInt64(Buffer, offset + 8); + ulong physicalSize = BitConverter.ToUInt64(Buffer, offset + 16); + UefiDateTime createTime = UefiDateTime.ReadFromBuffer(Buffer, offset + 24); + UefiDateTime lastAccessTime = UefiDateTime.ReadFromBuffer(Buffer, offset + 40); + UefiDateTime modificationTime = UefiDateTime.ReadFromBuffer(Buffer, offset + 56); + ulong attribute = BitConverter.ToUInt64(Buffer, offset + 72); + string fileName = Encoding.Unicode.GetString(Buffer, offset + 80, (int)(size - 80)); + + return new FILE_INFO() + { + Size = size, + FileSize = fileSize, + PhysicalSize = physicalSize, + CreateTime = createTime, + LastAccessTime = lastAccessTime, + ModificationTime = modificationTime, + Attribute = (FileAttribute)attribute, + FileName = fileName + }; + } + + public override readonly string ToString() + { + return "Size: " + Size + + "- File Size: " + FileSize + + "- Physical Size: " + PhysicalSize + + "- Create Time: " + CreateTime.ToDateTime() + + "- Last Access Time: " + LastAccessTime.ToDateTime() + + "- Modification Time: " + ModificationTime.ToDateTime() + + "- Attribute: " + Attribute + + "- FileName: " + FileName; + } + } +} diff --git a/WPinternals/Models/UFP/UEFI/FileAttribute.cs b/WPinternals/Models/UFP/UEFI/FileAttribute.cs new file mode 100644 index 0000000..71ee06a --- /dev/null +++ b/WPinternals/Models/UFP/UEFI/FileAttribute.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace UnifiedFlashingPlatform.UEFI +{ + [Flags] + public enum FileAttribute : ulong + { + EfiFileReadOnly = 1UL, + EfiFileHidden = 2UL, + EfiFileSystem = 4UL, + EfiFileReserved = 8UL, + EfiFileDirectory = 16UL, + EfiFileArchive = 32UL, + EfiFileValidAttr = 55UL + } +} diff --git a/WPinternals/Models/UFP/UEFI/LoadOptionAttribute.cs b/WPinternals/Models/UFP/UEFI/LoadOptionAttribute.cs new file mode 100644 index 0000000..b3d7be0 --- /dev/null +++ b/WPinternals/Models/UFP/UEFI/LoadOptionAttribute.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace UnifiedFlashingPlatform.UEFI +{ + [Flags] + public enum LoadOptionAttribute : uint + { + LoadOptionCategoryBoot = 0U, + LoadOptionActive = 1U, + LoadOptionForceReconnect = 2U, + LoadOptionHidden = 8U, + LoadOptionCategoryApp = 256U, + LoadOptionCategory = 7936U + } +} diff --git a/WPinternals/Models/UFP/UEFI/UefiDateTime.cs b/WPinternals/Models/UFP/UEFI/UefiDateTime.cs new file mode 100644 index 0000000..6d1343e --- /dev/null +++ b/WPinternals/Models/UFP/UEFI/UefiDateTime.cs @@ -0,0 +1,62 @@ +using System; + +namespace UnifiedFlashingPlatform.UEFI +{ + public struct UefiDateTime + { + public ushort year; + public byte month; + public byte day; + public byte hour; + public byte minute; + public byte second; + public uint nanosecond; + public short timezone; + public byte daylight; + + public static UefiDateTime ReadFromBuffer(byte[] Buffer, int offset) + { + ushort year = BitConverter.ToUInt16(Buffer, offset); + byte month = Buffer[offset + 2]; + byte day = Buffer[offset + 3]; + byte hour = Buffer[offset + 4]; + byte minute = Buffer[offset + 5]; + byte second = Buffer[offset + 6]; + uint nanosecond = BitConverter.ToUInt32(Buffer, offset + 8); + short timezone = BitConverter.ToInt16(Buffer, offset + 12); + byte daylight = Buffer[offset + 14]; + + return new UefiDateTime() + { + year = year, + month = month, + day = day, + hour = hour, + minute = minute, + second = second, + nanosecond = nanosecond, + timezone = timezone, + daylight = daylight + }; + } + + public readonly DateTime ToDateTime() + { + DateTime dateTime; + + byte month = this.month == 0 ? (byte)1 : this.month; + byte day = this.day == 0 ? (byte)1 : this.day; + + if (timezone == 2047) + { + dateTime = new DateTime(year, month, day, hour, minute, second, Convert.ToInt32(nanosecond / 1000000U), DateTimeKind.Local); + } + else + { + dateTime = new DateTime(year, month, day, hour, minute, second, Convert.ToInt32(nanosecond / 1000000U), DateTimeKind.Utc); + } + + return dateTime; + } + } +} \ No newline at end of file diff --git a/WPinternals/Models/UFP/UEFI/UefiVariableAttributes.cs b/WPinternals/Models/UFP/UEFI/UefiVariableAttributes.cs new file mode 100644 index 0000000..e62bdc3 --- /dev/null +++ b/WPinternals/Models/UFP/UEFI/UefiVariableAttributes.cs @@ -0,0 +1,41 @@ +/* +* MIT License +* +* Copyright (c) 2024 The DuoWOA authors +* +* 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; + +namespace UnifiedFlashingPlatform.UEFI +{ + [Flags] + public enum UefiVariableAttributes : uint + { + EfiVariableNone, + EfiVariableNonVolatile, + EfiVariableBootServiceAccess, + EfiVariableRuntimeAccess = 4U, + EfiVariableHardwareErrorRecord = 8U, + EfiVariableAuthenticatedWriteAccess = 16U, + EfiVariableTimeBasedAuthenticatedWriteAccess = 32U, + EfiVariableAppendWrite = 64U, + EfiVariableEnhancedAuthenticatedAccess = 128U + } +} \ No newline at end of file diff --git a/WPinternals/Models/UFP/USBSpeed.cs b/WPinternals/Models/UFP/USBSpeed.cs new file mode 100644 index 0000000..2423744 --- /dev/null +++ b/WPinternals/Models/UFP/USBSpeed.cs @@ -0,0 +1,37 @@ +/* +* MIT License +* +* Copyright (c) 2024 The DuoWOA authors +* +* 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 UnifiedFlashingPlatform +{ + public struct USBSpeed + { + public byte CurrentUSBSpeed; + public byte MaxUSBSpeed; + + public override readonly string ToString() + { + return "CurrentUSBSpeed: " + CurrentUSBSpeed + + " - MaxUSBSpeed: " + MaxUSBSpeed; + } + } +} \ No newline at end of file diff --git a/WPinternals/Models/UFP/UefiVariable.cs b/WPinternals/Models/UFP/UefiVariable.cs new file mode 100644 index 0000000..cc8923c --- /dev/null +++ b/WPinternals/Models/UFP/UefiVariable.cs @@ -0,0 +1,42 @@ +/* +* MIT License +* +* Copyright (c) 2024 The DuoWOA authors +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +*/ +using System; +using UnifiedFlashingPlatform.UEFI; + +namespace UnifiedFlashingPlatform +{ + public struct UefiVariable + { + public UefiVariableAttributes Attributes; + public uint DataSize; + public byte[] Data; + + public override readonly string ToString() + { + return "Attributes: " + Attributes + + " - DataSize: " + DataSize + + " - Data: " + BitConverter.ToString(Data); + } + } +} \ No newline at end of file diff --git a/WPinternals/Models/UFP/UnifiedFlashingPlatformTransport.Commands.cs b/WPinternals/Models/UFP/UnifiedFlashingPlatformTransport.Commands.cs new file mode 100644 index 0000000..04b8ee0 --- /dev/null +++ b/WPinternals/Models/UFP/UnifiedFlashingPlatformTransport.Commands.cs @@ -0,0 +1,136 @@ +namespace UnifiedFlashingPlatform +{ + public partial class UnifiedFlashingPlatformModel + { + // + // Not valid commands + // + /* NOK */ + private const string Signature = "NOK"; + /* NOKX */ + private const string ExtendedMessageSignature = $"{Signature}X"; + /* NOKXC */ + private const string CommonExtendedMessageSignature = $"{ExtendedMessageSignature}C"; + /* NOKXF */ + private const string UFPExtendedMessageSignature = $"{ExtendedMessageSignature}F"; + + // + // Normal commands + // + /* NOKF */ + private const string FlashSignature = $"{Signature}F"; + /* NOKI */ + private const string HelloSignature = $"{Signature}I"; + /* NOKM */ + private const string MassStorageSignature = $"{Signature}M"; + /* NOKN */ + private const string TelemetryEndSignature = $"{Signature}N"; + /* NOKR */ + private const string RebootSignature = $"{Signature}R"; + /* NOKS */ + private const string TelemetryStartSignature = $"{Signature}S"; + /* NOKT */ + private const string GetGPTSignature = $"{Signature}T"; + /* NOKV */ + private const string InfoQuerySignature = $"{Signature}V"; + /* NOKZ */ + private const string ShutdownSignature = $"{Signature}Z"; + + // + // Common extended commands + // + /* NOKXCB */ + private const string SwitchModeSignature = $"{CommonExtendedMessageSignature}B"; + /* NOKXCC */ + private const string ClearScreenSignature = $"{CommonExtendedMessageSignature}C"; + /* NOKXCD */ + private const string GetDirectoryEntriesSignature = $"{CommonExtendedMessageSignature}D"; + /* NOKXCE */ + private const string EchoSignature = $"{CommonExtendedMessageSignature}E"; + /* NOKXCF */ + private const string GetFileSignature = $"{CommonExtendedMessageSignature}F"; + /* NOKXCM */ + private const string DisplayCustomMessageSignature = $"{CommonExtendedMessageSignature}M"; + /* NOKXCP */ + private const string PutFileSignature = $"{CommonExtendedMessageSignature}P"; + /* NOKXCT */ + private const string BenchmarkTestsSignature = $"{CommonExtendedMessageSignature}T"; + + // + // UFP extended commands + // + /* NOKXFF */ + private const string AsyncFlashModeSignature = $"{UFPExtendedMessageSignature}F"; + /* NOKXFI */ + private const string UnlockSignature = $"{UFPExtendedMessageSignature}I"; + /* NOKXFO */ + private const string RelockSignature = $"{UFPExtendedMessageSignature}O"; + /* NOKXFR */ + private const string ReadParamSignature = $"{UFPExtendedMessageSignature}R"; + /* NOKXFS */ + private const string SecureFlashSignature = $"{UFPExtendedMessageSignature}S"; + /* NOKXFT */ + private const string TelemetryReadSignature = $"{UFPExtendedMessageSignature}T"; + /* NOKXFW */ + private const string WriteParamSignature = $"{UFPExtendedMessageSignature}W"; + /* NOKXFX */ + private const string GetLogsSignature = $"{UFPExtendedMessageSignature}X"; + + // + // UFP Read Params + // + private const string AppTypeReadParamSignature = "APPT"; + private const string ResetProtectionReadParamSignature = "ATRP"; + private const string BitlockerStateReadParamSignature = "BITL"; + private const string BuildInfoReadParamSignature = "BNFO"; + private const string CurrentBootOptionReadParamSignature = "CUFO"; + private const string AsyncProtocolSupportReadParamSignature = "DAS\0"; + private const string DirectoryEntriesSizeReadParamSignature = "DES\0"; + private const string DevicePlatformIDReadParamSignature = "DPI\0"; + private const string DevicePropertiesReadParamSignature = "DPR\0"; + private const string DeviceTargetInfoReadParamSignature = "DTI\0"; + private const string DataVerifySpeedReadParamSignature = "DTSP"; + private const string DeviceIDReadParamSignature = "DUI\0"; + private const string EMMCTestResultReadParamSignature = "EMMT"; + private const string EMMCSizeReadParamSignature = "EMS\0"; + private const string EMMCWriteSpeedReadParamSignature = "EMWS"; + private const string FlashAppInfoReadParamSignature = "FAI\0"; + private const string FlashAppOptionsReadParamSignature = "FO\0\0"; + private const string FlashingStatusReadParamSignature = "FS\0\0"; + private const string FileSizeReadParamSignature = "FZ\0\0"; + private const string SecureBootStatusReadParamSignature = "GSBS"; + private const string GetUEFIVariableReadParamSignature = "GUFV"; + private const string GetUEFIVariableSizeReadParamSignature = "GUVS"; + private const string LargestMemoryRegionReadParamSignature = "LGMR"; + private const string LogSizeReadParamSignature = "LZ\0\0"; + private const string MACAddressReadParamSignature = "MAC\0"; + private const string ModeDataReadParamSignature = "MODE"; + private const string ProcessorManufacturerReadParamSignature = "pm\0\0"; + private const string SDCardSizeReadParamSignature = "SDS\0"; + private const string SupportedSecureFFUProtocolsReadParamSignature = "SFPI"; + private const string SMBIOSDataReadParamSignature = "SMBD"; + private const string SerialNumberReadParamSignature = "SN\0\0"; + private const string SizeOfSystemMemoryReadParamSignature = "SOSM"; + private const string SecurityStatusReadParamSignature = "SS\0\0"; + private const string TelemetryLogSizeReadParamSignature = "TELS"; + private const string TransferSizeReadParamSignature = "TS\0\0"; + private const string UEFIBootFlagReadParamSignature = "UBF\0"; + private const string UEFIBootOptionsReadParamSignature = "UEBO"; + private const string UnlockIDReadParamSignature = "UKID"; + private const string UnlockTokenFilesReadParamSignature = "UKTF"; + private const string USBSpeedReadParamSignature = "USBS"; + private const string WriteBufferSizeReadParamSignature = "WBS\0"; + + // + // UFP Write Params + // + private const string BootOptionOptionalDataWriteParamSignature = "BOCL"; + private const string BootOptionAsFirstEntryWriteParamSignature = "BOF\0"; + private const string BootOptionAsLastEntryWriteParamSignature = "BOL\0"; + private const string FlashOptionsWriteParamSignature = "FO\0\0"; + private const string LogInsertWriteParamSignature = "LI\0\0"; + private const string ModeWriteParamSignature = "MODE"; + private const string OneTimeBootSequenceWriteParamSignature = "OBU\0"; + private const string SettingUEFIVariableWriteParamSignature = "SUFV"; + } +} diff --git a/WPinternals/Models/UFP/UnifiedFlashingPlatformTransport.ReadParam.cs b/WPinternals/Models/UFP/UnifiedFlashingPlatformTransport.ReadParam.cs new file mode 100644 index 0000000..15f1925 --- /dev/null +++ b/WPinternals/Models/UFP/UnifiedFlashingPlatformTransport.ReadParam.cs @@ -0,0 +1,534 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using UnifiedFlashingPlatform.UEFI; +using static System.Runtime.InteropServices.JavaScript.JSType; + +namespace UnifiedFlashingPlatform +{ + public partial class UnifiedFlashingPlatformModel + { + public byte[]? ReadParam(string Param) + { + byte[] Request = new byte[0x0B]; + const string Header = ReadParamSignature; // NOKXFR + + Buffer.BlockCopy(Encoding.ASCII.GetBytes(Header), 0, Request, 0, Header.Length); + Buffer.BlockCopy(Encoding.ASCII.GetBytes(Param), 0, Request, 7, Param.Length); + + byte[]? Response = ExecuteRawMethod(Request); + if ((Response == null) || (Response.Length < 0x10)) + { + return null; + } + + byte[] Result = new byte[Response[0x10]]; + Buffer.BlockCopy(Response, 0x11, Result, 0, Response[0x10]); + return Result; + } + + public string? ReadStringParam(string Param) + { + byte[]? Bytes = ReadParam(Param); + return Bytes == null ? null : Encoding.ASCII.GetString(Bytes).Trim('\0'); + } + + public AppType ReadAppType() + { + byte[]? Bytes = ReadParam(AppTypeReadParamSignature); + return Bytes == null ? AppType.Min : Bytes[0] == 1 ? AppType.UEFI : AppType.Min; + } + + public ResetProtectionInfo? ReadResetProtection() + { + byte[]? Bytes = ReadParam(ResetProtectionReadParamSignature); + return Bytes == null + ? null + : new ResetProtectionInfo() + { + IsResetProtectionEnabled = Bytes[0] == 1, + MajorVersion = BitConverter.ToUInt32(Bytes[1..5].Reverse().ToArray()), + MinorVersion = BitConverter.ToUInt32(Bytes[5..9].Reverse().ToArray()) + }; + } + + public bool? ReadBitlocker() + { + byte[]? Bytes = ReadParam(BitlockerStateReadParamSignature); + return Bytes == null ? null : Bytes[0] == 1; + } + + public string? ReadBuildInfo() + { + return ReadStringParam(BuildInfoReadParamSignature); + } + + public ushort? ReadCurrentBootOption() + { + byte[]? Bytes = ReadParam(CurrentBootOptionReadParamSignature); + return Bytes == null || Bytes.Length != 2 ? null : BitConverter.ToUInt16(Bytes.Reverse().ToArray()); + } + + public bool? ReadDeviceAsyncSupport() + { + byte[]? Bytes = ReadParam(AsyncProtocolSupportReadParamSignature); + return Bytes == null || Bytes.Length != 2 ? null : BitConverter.ToUInt16(Bytes.Reverse().ToArray()) == 1; + } + + public ulong? ReadDirectoryEntriesSize(string PartitionName, string DirectoryName) + { + if (PartitionName.Length > 35) + { + return null; + } + + byte[] PartitionNameBuffer = Encoding.Unicode.GetBytes(PartitionName); + byte[] DirectoryNameBuffer = Encoding.Unicode.GetBytes(DirectoryName); + + byte[] Request = new byte[87 + DirectoryNameBuffer.Length + 2]; + const string Header = ReadParamSignature; // NOKXFR + const string Param = DirectoryEntriesSizeReadParamSignature; + + Buffer.BlockCopy(Encoding.ASCII.GetBytes(Header), 0, Request, 0, Header.Length); + Buffer.BlockCopy(Encoding.ASCII.GetBytes(Param), 0, Request, 7, Param.Length); + + Buffer.BlockCopy(PartitionNameBuffer, 0, Request, 15, PartitionNameBuffer.Length); + Buffer.BlockCopy(DirectoryNameBuffer, 0, Request, 87, DirectoryNameBuffer.Length); + + byte[]? Response = ExecuteRawMethod(Request); + if ((Response == null) || (Response.Length < 0x10)) + { + return null; + } + + byte[] Result = new byte[Response[0x10]]; + Buffer.BlockCopy(Response, 0x11, Result, 0, Response[0x10]); + + return BitConverter.ToUInt64(Result.Reverse().ToArray()); + } + + public string? ReadDevicePlatformID() + { + return ReadStringParam(DevicePlatformIDReadParamSignature); + } + + // + // Reads the device properties from the UEFI Variable "MSRuntimeDeviceProperties" + // in the g_guidMSRuntimeDeviceProperties namespace and returns it as a string. + // + public string? ReadDeviceProperties() + { + return ReadStringParam(DevicePropertiesReadParamSignature); + } + + public DeviceTargetingInfo? ReadDeviceTargetInfo() + { + byte[]? Bytes = ReadParam(DeviceTargetInfoReadParamSignature); + if (Bytes == null) + { + return null; + } + + 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; + string Manufacturer = Encoding.ASCII.GetString(Bytes[CurrentOffset..(CurrentOffset + ManufacturerLength)]); + + CurrentOffset += ManufacturerLength; + string Family = Encoding.ASCII.GetString(Bytes[CurrentOffset..(CurrentOffset + FamilyLength)]); + + CurrentOffset += FamilyLength; + string ProductName = Encoding.ASCII.GetString(Bytes[CurrentOffset..(CurrentOffset + ProductNameLength)]); + + CurrentOffset += ProductNameLength; + string ProductVersion = Encoding.ASCII.GetString(Bytes[CurrentOffset..(CurrentOffset + ProductVersionLength)]); + + CurrentOffset += ProductVersionLength; + string SKUNumber = Encoding.ASCII.GetString(Bytes[CurrentOffset..(CurrentOffset + SKUNumberLength)]); + + CurrentOffset += SKUNumberLength; + string BaseboardManufacturer = Encoding.ASCII.GetString(Bytes[CurrentOffset..(CurrentOffset + BaseboardManufacturerLength)]); + + CurrentOffset += BaseboardManufacturerLength; + string BaseboardProduct = Encoding.ASCII.GetString(Bytes[CurrentOffset..(CurrentOffset + BaseboardProductLength)]); + + return new DeviceTargetingInfo() + { + Manufacturer = Manufacturer, + Family = Family, + ProductName = ProductName, + ProductVersion = ProductVersion, + SKUNumber = SKUNumber, + BaseboardManufacturer = BaseboardManufacturer, + BaseboardProduct = BaseboardProduct + }; + } + + // + // Gets the last FFU Flash Operation Data verify speed in KB/s + // + public uint? ReadDataVerifySpeed() + { + byte[]? Bytes = ReadParam(DataVerifySpeedReadParamSignature); + return Bytes == null || Bytes.Length != 4 ? null : BitConverter.ToUInt32(Bytes.Reverse().ToArray()); + } + + public Guid? ReadDeviceID() + { + byte[]? Bytes = ReadParam(DeviceIDReadParamSignature); + return Bytes == null || Bytes.Length != 16 ? null : new Guid(Bytes); + } + + public uint? ReadEmmcTestResult() + { + byte[]? Bytes = ReadParam(EMMCTestResultReadParamSignature); + return Bytes == null || Bytes.Length != 4 ? null : BitConverter.ToUInt32(Bytes.Reverse().ToArray()); + } + + // + // Gets the eMMC Size in sectors, if present + // + public uint? ReadEmmcSize() + { + byte[]? Bytes = ReadParam(EMMCSizeReadParamSignature); + return Bytes == null || Bytes.Length != 4 ? null : BitConverter.ToUInt32(Bytes.Reverse().ToArray()); + } + + // + // Gets the eMMC Write speed in KB/s + // + public uint? ReadEmmcWriteSpeed() + { + byte[]? Bytes = ReadParam(EMMCWriteSpeedReadParamSignature); + return Bytes == null || Bytes.Length != 4 ? null : BitConverter.ToUInt32(Bytes.Reverse().ToArray()); + } + + public FlashAppInfo? ReadFlashAppInfo() + { + byte[]? Bytes = ReadParam(FlashAppInfoReadParamSignature); + return Bytes == null || Bytes.Length != 6 || Bytes[0] != 2 + ? null + : new FlashAppInfo() + { + ProtocolMajorVersion = Bytes[1], + ProtocolMinorVersion = Bytes[2], + ImplementationMajorVersion = Bytes[3], + ImplementationMinorVersion = Bytes[4] + }; + } + + // + // Reads the device properties from the UEFI Variable "FfuConfigurationOptions" + // in the g_guidLumiaGuid namespace and returns it as a string. + // + public string? ReadFlashOptions() + { + return ReadStringParam(FlashAppOptionsReadParamSignature); + } + + public uint? ReadFlashingStatus() + { + byte[]? Bytes = ReadParam(FlashingStatusReadParamSignature); + return Bytes == null || Bytes.Length != 4 ? null : BitConverter.ToUInt32(Bytes.Reverse().ToArray()); + } + + public ulong? ReadFileSize(string PartitionName, string FileName) + { + if (PartitionName.Length > 35) + { + return null; + } + + byte[] PartitionNameBuffer = Encoding.Unicode.GetBytes(PartitionName); + byte[] FileNameBuffer = Encoding.Unicode.GetBytes(FileName); + + byte[] Request = new byte[87 + FileNameBuffer.Length + 2]; + const string Header = ReadParamSignature; // NOKXFR + const string Param = FileSizeReadParamSignature; + + Buffer.BlockCopy(Encoding.ASCII.GetBytes(Header), 0, Request, 0, Header.Length); + Buffer.BlockCopy(Encoding.ASCII.GetBytes(Param), 0, Request, 7, Param.Length); + + Buffer.BlockCopy(PartitionNameBuffer, 0, Request, 15, PartitionNameBuffer.Length); + Buffer.BlockCopy(FileNameBuffer, 0, Request, 87, FileNameBuffer.Length); + + byte[]? Response = ExecuteRawMethod(Request); + if ((Response == null) || (Response.Length < 0x10)) + { + return null; + } + + byte[] Result = new byte[Response[0x10]]; + Buffer.BlockCopy(Response, 0x11, Result, 0, Response[0x10]); + + return BitConverter.ToUInt64(Result.Reverse().ToArray()); + } + + public bool? ReadSecureBootStatus() + { + byte[]? Bytes = ReadParam(SecureBootStatusReadParamSignature); + return Bytes == null ? null : Bytes[0] == 1; + } + + public UefiVariable? ReadUEFIVariable(Guid Guid, string Name, uint Size) + { + byte[] Request = new byte[39 + ((Name.Length + 1) * 2)]; + const string Header = ReadParamSignature; // NOKXFR + string Param = GetUEFIVariableReadParamSignature; + + byte[] VariableNameBuffer = Encoding.Unicode.GetBytes(Name); + + Buffer.BlockCopy(Encoding.ASCII.GetBytes(Header), 0, Request, 0, Header.Length); + Buffer.BlockCopy(Encoding.ASCII.GetBytes(Param), 0, Request, 7, Param.Length); + + Buffer.BlockCopy(Guid.ToByteArray(), 0, Request, 15, 16); + Buffer.BlockCopy(BitConverter.GetBytes(Size).Reverse().ToArray(), 0, Request, 31, 4); + Buffer.BlockCopy(BitConverter.GetBytes((Name.Length + 1) * 2).Reverse().ToArray(), 0, Request, 35, 4); + Buffer.BlockCopy(VariableNameBuffer, 0, Request, 39, VariableNameBuffer.Length); + + byte[]? Response = ExecuteRawMethod(Request); + if ((Response == null) || (Response.Length < 0x10)) + { + return null; + } + + byte[] Result = new byte[Response[0x10]]; + Buffer.BlockCopy(Response, 0x11, Result, 0, Response[0x10]); + + return new UefiVariable() + { + Attributes = (UefiVariableAttributes)BitConverter.ToUInt32(Result[0..4].Reverse().ToArray()), + DataSize = BitConverter.ToUInt32(Result[4..8].Reverse().ToArray()), + Data = Result[8..^0] + }; + } + + public uint? ReadUEFIVariableSize(Guid Guid, string Name) + { + byte[] Request = new byte[39 + ((Name.Length + 1) * 2)]; + const string Header = ReadParamSignature; // NOKXFR + string Param = GetUEFIVariableSizeReadParamSignature; + + byte[] VariableNameBuffer = Encoding.Unicode.GetBytes(Name); + + Buffer.BlockCopy(Encoding.ASCII.GetBytes(Header), 0, Request, 0, Header.Length); + Buffer.BlockCopy(Encoding.ASCII.GetBytes(Param), 0, Request, 7, Param.Length); + + Buffer.BlockCopy(Guid.ToByteArray(), 0, Request, 15, 16); + + Buffer.BlockCopy(BitConverter.GetBytes((Name.Length + 1) * 2).Reverse().ToArray(), 0, Request, 35, 4); + Buffer.BlockCopy(VariableNameBuffer, 0, Request, 39, VariableNameBuffer.Length); + + byte[]? Response = ExecuteRawMethod(Request); + if ((Response == null) || (Response.Length < 0x10)) + { + return null; + } + + byte[] Result = new byte[Response[0x10]]; + Buffer.BlockCopy(Response, 0x11, Result, 0, Response[0x10]); + return Result == null || Result.Length != 4 ? null : BitConverter.ToUInt32(Result.Reverse().ToArray()); + } + + // + // Returns the largest memory region in bytes available for use by UFP + // + public ulong? ReadLargestMemoryRegion() + { + byte[]? Bytes = ReadParam(LargestMemoryRegionReadParamSignature); + return Bytes == null || Bytes.Length != 8 ? null : BitConverter.ToUInt64(Bytes.Reverse().ToArray()); + } + + public ulong? ReadLogSize(DeviceLogType LogType) + { + byte[] Request = new byte[0x10]; + const string Header = ReadParamSignature; // NOKXFR + const string Param = LogSizeReadParamSignature; + + Buffer.BlockCopy(Encoding.ASCII.GetBytes(Header), 0, Request, 0, Header.Length); + Buffer.BlockCopy(Encoding.ASCII.GetBytes(Param), 0, Request, 7, Param.Length); + + Request[15] = (byte)LogType; + + byte[]? Response = ExecuteRawMethod(Request); + if ((Response == null) || (Response.Length < 0x10)) + { + return 0; + } + + byte[] Result = new byte[Response[0x10]]; + Buffer.BlockCopy(Response, 0x11, Result, 0, Response[0x10]); + + return BitConverter.ToUInt64([.. Result.Reverse()], 0); + } + + // + // Reads the MAC Address in the following format: "%02x-%02x-%02x-%02x-%02x-%02x" + // + public string? ReadMacAddress() + { + return ReadStringParam(MACAddressReadParamSignature); + } + + public uint? ReadModeData(Mode Mode) + { + byte[] Request = new byte[0x10]; + const string Header = ReadParamSignature; // NOKXFR + string Param = ModeDataReadParamSignature; + + Buffer.BlockCopy(Encoding.ASCII.GetBytes(Header), 0, Request, 0, Header.Length); + Buffer.BlockCopy(Encoding.ASCII.GetBytes(Param), 0, Request, 7, Param.Length); + + Request[15] = (byte)Mode; + + byte[]? Response = ExecuteRawMethod(Request); + if ((Response == null) || (Response.Length < 0x10)) + { + return null; + } + + byte[] Result = new byte[Response[0x10]]; + Buffer.BlockCopy(Response, 0x11, Result, 0, Response[0x10]); + return Result == null || Result.Length != 4 ? null : BitConverter.ToUInt32(Result.Reverse().ToArray()); + } + + public string? ReadProcessorManufacturer() + { + return ReadStringParam(ProcessorManufacturerReadParamSignature); + } + + // + // Gets the SD Card Size in sectors, if present + // + public uint? ReadSDCardSize() + { + byte[]? Bytes = ReadParam(SDCardSizeReadParamSignature); + return Bytes == null || Bytes.Length != 4 ? null : BitConverter.ToUInt32(Bytes.Reverse().ToArray()); + } + + public string? ReadSupportedFFUProtocolInfo() + { + // TODO + return ReadStringParam(SupportedSecureFFUProtocolsReadParamSignature); + } + + public string? ReadSMBIOSData() + { + // TODO + return ReadStringParam(SMBIOSDataReadParamSignature); + } + + public Guid? ReadSerialNumber() + { + byte[]? Bytes = ReadParam(SerialNumberReadParamSignature); + return Bytes == null || Bytes.Length != 16 ? null : new Guid(Bytes); + } + + // + // Returns the size of system memory in kB + // + public ulong? ReadSizeOfSystemMemory() + { + byte[]? Bytes = ReadParam(SizeOfSystemMemoryReadParamSignature); + return Bytes == null || Bytes.Length != 8 ? null : BitConverter.ToUInt64(Bytes.Reverse().ToArray()); + } + + public string? ReadSecurityStatus() + { + // TODO + return ReadStringParam(SecurityStatusReadParamSignature); + } + + public string? ReadTelemetryLogSize() + { + // TODO + return ReadStringParam(TelemetryLogSizeReadParamSignature); + } + + public uint? ReadTransferSize() + { + byte[]? Bytes = ReadParam(TransferSizeReadParamSignature); + return Bytes == null || Bytes.Length != 4 ? null : BitConverter.ToUInt32(Bytes.Reverse().ToArray()); + } + + // + // Reads the UEFI Boot Flag variable content and returns it as a string. + // + public string? ReadUEFIBootFlag() + { + return ReadStringParam(UEFIBootFlagReadParamSignature); + } + + public BOOT_OPTION[]? ReadUEFIBootOptions() + { + byte[] Request = new byte[0x0B]; + const string Header = ReadParamSignature; // NOKXFR + + Buffer.BlockCopy(Encoding.ASCII.GetBytes(Header), 0, Request, 0, Header.Length); + Buffer.BlockCopy(Encoding.ASCII.GetBytes(UEFIBootOptionsReadParamSignature), 0, Request, 7, UEFIBootOptionsReadParamSignature.Length); + + byte[]? Response = ExecuteRawMethod(Request); + if ((Response == null) || (Response.Length < 0x10)) + { + return null; + } + + byte[] ResponseBuffer = Response[0x19..]; + + List bootOptions = []; + + int j = 0; + + while (j != ResponseBuffer.Length) + { + BOOT_OPTION bootOption = BOOT_OPTION.ReadFromBuffer(ResponseBuffer, j); + j += bootOption.TotalSize; + + bootOptions.Add(bootOption); + } + + return [.. bootOptions]; + } + + // + // Reads the device properties from the UEFI Variable "UnlockID" + // in the g_guidOfflineDUIdEfiNamespace namespace and returns it as a string. + // + public byte[] ReadUnlockID() + { + return ReadParam(UnlockIDReadParamSignature); + } + + public string? ReadUnlockTokenFiles() + { + // TODO + return ReadStringParam(UnlockTokenFilesReadParamSignature); + } + + public USBSpeed? ReadUSBSpeed() + { + byte[]? Bytes = ReadParam(USBSpeedReadParamSignature); + return Bytes == null || Bytes.Length != 2 + ? null + : new USBSpeed() + { + CurrentUSBSpeed = Bytes[0], + MaxUSBSpeed = Bytes[1] + }; + } + + public uint? ReadWriteBufferSize() + { + byte[]? Bytes = ReadParam(WriteBufferSizeReadParamSignature); + return Bytes == null || Bytes.Length != 4 ? null : BitConverter.ToUInt32(Bytes.Reverse().ToArray()); + } + } +} \ No newline at end of file diff --git a/WPinternals/Models/UFP/UnifiedFlashingPlatformTransport.WPI.cs b/WPinternals/Models/UFP/UnifiedFlashingPlatformTransport.WPI.cs new file mode 100644 index 0000000..ddc9ede --- /dev/null +++ b/WPinternals/Models/UFP/UnifiedFlashingPlatformTransport.WPI.cs @@ -0,0 +1,543 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Text; +using WPinternals; +using WPinternals.HelperClasses; + +namespace UnifiedFlashingPlatform +{ + public partial class UnifiedFlashingPlatformModel + { + private readonly PhoneInfo Info = new(); + + public void FlashSectors(uint StartSector, byte[] Data, byte TargetDevice = 0, int Progress = 0) + { + // Start sector is in UInt32, so max size of eMMC is 2 TB. + + byte[] Request = new byte[Data.Length + 0x40]; + + const string Header = FlashSignature; // NOKF + Buffer.BlockCopy(Encoding.ASCII.GetBytes(Header), 0, Request, 0, Header.Length); + Request[0x05] = TargetDevice; // Target device: 0: eMMC, 1: SDIO, 2: Other ???, 3: ??? + 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) + Request[0x18] = 0; // Verify needed + Request[0x19] = 0; // Skip write + + Buffer.BlockCopy(Data, 0, Request, 0x40, Data.Length); + + _ = ExecuteRawMethod(Request); + } + + public void Hello() + { + byte[] Request = new byte[4]; + ByteOperations.WriteAsciiString(Request, 0, HelloSignature); + byte[] Response = ExecuteRawMethod(Request) ?? 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."); + } + } + + public void ResetPhone() + { + 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"); + } + } + + public GPT ReadGPT() + { + // 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]; + const string Header = GetGPTSignature; + + System.Buffer.BlockCopy(Encoding.ASCII.GetBytes(Header), 0, Request, 0, Header.Length); + + byte[]? Buffer = ExecuteRawMethod(Request); + if ((Buffer == null) || (Buffer.Length < 0x4408)) + { + throw new InvalidOperationException("Unable to read GPT!"); + } + + ushort Error = (ushort)((Buffer[6] << 8) + Buffer[7]); + if (Error > 0) + { + throw new NotSupportedException($"ReadGPT: Error 0x{Error:X4}"); + } + + // 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) + + 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:X4}"); + + byte[] GPTBuffer = new byte[ReturnedGPTBufferLength - SectorSize]; + System.Buffer.BlockCopy(Buffer, 8 + (int)SectorSize, GPTBuffer, 0, (int)ReturnedGPTBufferLength - (int)SectorSize); + + /*if (Switch) + { + if (OriginalAppType == FlashAppType.FlashApp) + { + SwitchToFlashAppContext(); + } + else + { + SwitchToPhoneInfoAppContext(); + } + }*/ + + return new GPT(GPTBuffer);//, SectorSize); // NOKT message header and MBR are ignored + } + + private static 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!") + { + SubMessage = $"Error 0x{ErrorCode:X4}: {SubMessage}" + }; + + throw Ex; + } + + public void SendFfuHeaderV1(byte[] FfuHeader, int Progress = 0, byte Options = 0) + { + byte[] Request = new byte[FfuHeader.Length + 0x20]; + + const string Header = SecureFlashSignature; + Buffer.BlockCopy(Encoding.ASCII.GetBytes(Header), 0, Request, 0, Header.Length); + Buffer.BlockCopy(BigEndian.GetBytes(0x0001, 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(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) ?? throw new BadConnectionException(); + + int ResultCode = (Response[6] << 8) + Response[7]; + if (ResultCode != 0) + { + ThrowFlashError(ResultCode); + } + } + + public void SendFfuHeaderV2(uint TotalHeaderLength, uint OffsetForThisPart, byte[] FfuHeader, int Progress = 0, byte Options = 0) + { + byte[] Request = new byte[FfuHeader.Length + 0x3C]; + + const string Header = SecureFlashSignature; + Buffer.BlockCopy(Encoding.ASCII.GetBytes(Header), 0, Request, 0, Header.Length); + Buffer.BlockCopy(BigEndian.GetBytes(0x0002, 2), 0, Request, 0x06, 2); // Protocol version = 0x0002 + Request[0x08] = (byte)Progress; // Progress = 0% (0 - 100) + 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) ?? 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 = SecureFlashSignature; + Buffer.BlockCopy(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) ?? 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 = SecureFlashSignature; + Buffer.BlockCopy(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) ?? 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 = SecureFlashSignature; + Buffer.BlockCopy(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) ?? throw new BadConnectionException(); + + int ResultCode = (Response[6] << 8) + Response[7]; + if (ResultCode != 0) + { + ThrowFlashError(ResultCode); + } + } + + public PhoneInfo ReadPhoneInfo() + { + // 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, InfoQuerySignature); + 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]; + ushort SubblockLength = BigEndian.ToUInt16(Response, SubblockOffset + 0x01); + 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([' ', '\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([' ', '\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: + Debug.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; + + public uint TransferSize; + public bool MmosOverUsbSupported; + public uint SdCardSizeInSectors; + public uint WriteBufferSize; + public uint EmmcSizeInSectors; + public string? PlatformID; + public ushort SecureFfuSupportedProtocolMask; + 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; + public ulong LargestMemoryRegion; + public AppType AppType; + public List<(uint SectorCount, uint SectorSize, ushort FlashType, ushort FlashIndex, uint Unknown, string DevicePath)> BootDevices = []; + + public bool IsBootloaderSecure; + + public void Log() + { + switch (App) + { + case FlashAppType.FlashApp: + Debug.WriteLine($"Flash app: {FlashAppVersionMajor}.{FlashAppVersionMinor}"); + Debug.WriteLine($"Flash protocol: {FlashAppProtocolVersionMajor}.{FlashAppProtocolVersionMinor}"); + break; + } + + Debug.WriteLine($"SecureBoot: {((!PlatformSecureBootEnabled || !UefiSecureBootEnabled) ? "Disabled" : "Enabled")} (Platform Secure Boot: {(PlatformSecureBootEnabled ? "Enabled" : "Disabled")}, UEFI Secure Boot: {(UefiSecureBootEnabled ? "Enabled" : "Disabled")})"); + + Debug.WriteLine($"Flash app security: {(!IsBootloaderSecure ? "Disabled" : "Enabled")}"); + + Debug.WriteLine($"Flash app security: {(!IsBootloaderSecure ? "Disabled" : "Enabled")} (FFU security: {(SecureFfuEnabled ? "Enabled" : "Disabled")}, RDC: {(RdcPresent ? "Present" : "Not found")}, Authenticated: {(Authenticated ? "True" : "False")})"); + + Debug.WriteLine($"JTAG: {(JtagDisabled ? "Disabled" : "Enabled")}"); + } + } + + public void Shutdown() + { + byte[] Request = new byte[4]; + const string Header = ShutdownSignature; + Buffer.BlockCopy(Encoding.ASCII.GetBytes(Header), 0, Request, 0, Header.Length); + ExecuteRawVoidMethod(Request); + } + } +} diff --git a/WPinternals/Models/UFP/UnifiedFlashingPlatformTransport.WriteParam.cs b/WPinternals/Models/UFP/UnifiedFlashingPlatformTransport.WriteParam.cs new file mode 100644 index 0000000..a3195f3 --- /dev/null +++ b/WPinternals/Models/UFP/UnifiedFlashingPlatformTransport.WriteParam.cs @@ -0,0 +1,61 @@ +using System; +using System.Linq; +using System.Text; +using UnifiedFlashingPlatform.UEFI; +using WPinternals.HelperClasses; + +namespace UnifiedFlashingPlatform +{ + public partial class UnifiedFlashingPlatformModel + { + public void WriteParam(string Param, byte[] Data) + { + byte[] Request = new byte[0x0F + Data.Length]; + const string Header = WriteParamSignature; // NOKXFW + + Buffer.BlockCopy(Encoding.ASCII.GetBytes(Header), 0, Request, 0, Header.Length); + Buffer.BlockCopy(Encoding.ASCII.GetBytes(Param), 0, Request, 7, Param.Length); + // 4 empty bytes here + Buffer.BlockCopy(Data, 0, Request, 15, Data.Length); + + ExecuteRawMethod(Request); + } + + // TODO: Verify proper functionality + public void SetUEFIVariable(Guid Guid, string Name, UefiVariableAttributes Attributes, byte[] Data) + { + byte[] ParamBuffer = new byte[540 + Data.Length]; + + /* 15..30 */ Buffer.BlockCopy(Guid.ToByteArray(), 0, ParamBuffer, 0, 16); + /* 31..34 */ Buffer.BlockCopy(BigEndian.GetBytes(Math.Min(512, Name.Length * 2), 4), 0, ParamBuffer, 16, 4); + /* 35.. */ Buffer.BlockCopy(Encoding.Unicode.GetBytes(Name), 0, ParamBuffer, 20, Math.Min(512, Name.Length * 2)); // 256 Max Size for name (unicode) + /* 547..550 */ Buffer.BlockCopy(BigEndian.GetBytes(Attributes, 4), 0, ParamBuffer, 532, 4); + /* 551..554 */ Buffer.BlockCopy(BigEndian.GetBytes(Data.Length, 4), 0, ParamBuffer, 536, 4); + /* 555.. */ Buffer.BlockCopy(Data, 0, ParamBuffer, 540, Data.Length); + + WriteParam(SettingUEFIVariableWriteParamSignature, ParamBuffer); + } + + public void SetOneTimeBootSequence(ushort BootEntryID) + { + //WriteParam(OneTimeBootSequenceWriteParamSignature, BigEndian.GetBytes(BootEntryID, 2)); + WriteParam("UOBU", [.. BigEndian.GetBytes(BootEntryID, 2).Reverse()]); + } + + public void SetBootOptionAsFirstEntry(ushort BootEntryID) + { + //WriteParam(BootOptionAsFirstEntryWriteParamSignature, BigEndian.GetBytes(BootEntryID, 2)); + WriteParam("UBOF", [.. BigEndian.GetBytes(BootEntryID, 2).Reverse()]); + } + + public void SetBootOptionAsLastEntry(ushort BootEntryID) + { + WriteParam(BootOptionAsLastEntryWriteParamSignature, BigEndian.GetBytes(BootEntryID, 2)); + } + + public void SetProgressBar(uint Percentage) + { + WriteParam("PBI\0", BigEndian.GetBytes(Percentage, 1)); + } + } +} diff --git a/WPinternals/Models/UFP/UnifiedFlashingPlatformTransport.cs b/WPinternals/Models/UFP/UnifiedFlashingPlatformTransport.cs new file mode 100644 index 0000000..5f0630c --- /dev/null +++ b/WPinternals/Models/UFP/UnifiedFlashingPlatformTransport.cs @@ -0,0 +1,359 @@ +/* +* MIT License +* +* Copyright (c) 2024 The DuoWOA authors +* +* 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 MadWizard.WinUSBNet; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using UnifiedFlashingPlatform.UEFI; +using WPinternals.HelperClasses; + +namespace UnifiedFlashingPlatform +{ + public partial class UnifiedFlashingPlatformModel : IDisposable + { + private bool Disposed = false; + private readonly USBDevice USBDevice; + private readonly USBPipe InputPipe; + private readonly USBPipe OutputPipe; + private readonly object UsbLock = new(); + + public UnifiedFlashingPlatformModel(string DevicePath) + { + USBDevice = new USBDevice(DevicePath); + + foreach (USBPipe Pipe in USBDevice.Pipes) + { + if (Pipe.IsIn) + { + InputPipe = Pipe; + } + + if (Pipe.IsOut) + { + OutputPipe = Pipe; + } + } + + if (InputPipe == null || OutputPipe == null) + { + throw new Exception("Invalid USB device!"); + } + } + + public byte[]? ExecuteRawMethod(byte[] RawMethod) + { + return ExecuteRawMethod(RawMethod, RawMethod.Length); + } + + public byte[]? ExecuteRawMethod(byte[] RawMethod, int Length) + { + byte[] Buffer = new byte[0xF000]; // Should be at least 0x4408 for receiving the GPT packet. + byte[]? Result = null; + lock (UsbLock) + { + OutputPipe.Write(RawMethod, 0, Length); + try + { + int OutputLength = InputPipe.Read(Buffer); + Result = new byte[OutputLength]; + System.Buffer.BlockCopy(Buffer, 0, Result, 0, OutputLength); + } + catch { } // Reboot command looses connection + } + return Result; + } + + public void ExecuteRawVoidMethod(byte[] RawMethod) + { + ExecuteRawVoidMethod(RawMethod, RawMethod.Length); + } + + public void ExecuteRawVoidMethod(byte[] RawMethod, int Length) + { + lock (UsbLock) + { + OutputPipe.Write(RawMethod, 0, Length); + } + } + + public void Relock() + { + byte[] Request = new byte[7]; + const string Header = RelockSignature; // NOKXFO + Buffer.BlockCopy(Encoding.ASCII.GetBytes(Header), 0, Request, 0, Header.Length); + _ = ExecuteRawMethod(Request); + } + + public void MassStorage() + { + byte[] Request = new byte[7]; + const string Header = MassStorageSignature; // NOKM + Buffer.BlockCopy(Encoding.ASCII.GetBytes(Header), 0, Request, 0, Header.Length); + _ = ExecuteRawMethod(Request); + } + + public void RebootPhone() + { + byte[] Request = new byte[7]; + const string Header = $"{SwitchModeSignature}R"; // NOKXCBR + Buffer.BlockCopy(Encoding.ASCII.GetBytes(Header), 0, Request, 0, Header.Length); + _ = ExecuteRawMethod(Request); + } + + public void SwitchToUFP() + { + byte[] Request = new byte[7]; + const string Header = $"{SwitchModeSignature}U"; // NOKXCBU + Buffer.BlockCopy(Encoding.ASCII.GetBytes(Header), 0, Request, 0, Header.Length); + _ = ExecuteRawMethod(Request); + } + + public void ContinueBoot() + { + byte[] Request = new byte[7]; + const string Header = $"{SwitchModeSignature}W"; // NOKXCBW + Buffer.BlockCopy(Encoding.ASCII.GetBytes(Header), 0, Request, 0, Header.Length); + _ = ExecuteRawMethod(Request); + } + + public void PowerOff() + { + byte[] Request = new byte[7]; + const string Header = $"{SwitchModeSignature}Z"; // NOKXCBZ + Buffer.BlockCopy(Encoding.ASCII.GetBytes(Header), 0, Request, 0, Header.Length); + ExecuteRawVoidMethod(Request); + } + + public void TransitionToUFPBootApp() + { + byte[] Request = new byte[7]; + const string Header = $"{SwitchModeSignature}T"; // NOKXCBT + Buffer.BlockCopy(Encoding.ASCII.GetBytes(Header), 0, Request, 0, Header.Length); + ExecuteRawVoidMethod(Request); + } + + public void DisplayCustomMessage(string Message, ushort Row) + { + byte[] MessageBuffer = Encoding.Unicode.GetBytes(Message); + byte[] Request = new byte[8 + MessageBuffer.Length]; + const string Header = DisplayCustomMessageSignature; // NOKXCM + + Buffer.BlockCopy(Encoding.ASCII.GetBytes(Header), 0, Request, 0, Header.Length); + Buffer.BlockCopy(BitConverter.GetBytes(Row).Reverse().ToArray(), 0, Request, 6, 2); + Buffer.BlockCopy(MessageBuffer, 0, Request, 8, MessageBuffer.Length); + + _ = ExecuteRawMethod(Request); + } + + public void ClearScreen() + { + byte[] Request = new byte[6]; + const string Header = ClearScreenSignature; // NOKXCC + Buffer.BlockCopy(Encoding.ASCII.GetBytes(Header), 0, Request, 0, Header.Length); + _ = ExecuteRawMethod(Request); + } + + public byte[]? Echo(byte[] DataPayload) + { + byte[] Request = new byte[10 + DataPayload.Length]; + const string Header = EchoSignature; // NOKXCE + + Buffer.BlockCopy(Encoding.ASCII.GetBytes(Header), 0, Request, 0, Header.Length); + Buffer.BlockCopy(BitConverter.GetBytes(DataPayload.Length).Reverse().ToArray(), 0, Request, 6, 4); + Buffer.BlockCopy(DataPayload, 0, Request, 10, DataPayload.Length); + + byte[]? Response = ExecuteRawMethod(Request); + if ((Response == null) || (Response.Length < 6 + DataPayload.Length)) + { + return null; + } + + byte[] Result = new byte[DataPayload.Length]; + Buffer.BlockCopy(Response, 6, Result, 0, DataPayload.Length); + return Result; + } + + public FILE_INFO[]? GetDirectoryEntries(string PartitionName, string DirectoryName) + { + ulong? size = ReadDirectoryEntriesSize(PartitionName, DirectoryName); + if (size == null) + { + return null; + } + + return GetDirectoryEntries(PartitionName, DirectoryName, size.Value); + } + + private FILE_INFO[]? GetDirectoryEntries(string PartitionName, string DirectoryName, ulong DataStructSize) + { + if (PartitionName.Length > 35) + { + return null; + } + + byte[] PartitionNameBuffer = Encoding.Unicode.GetBytes(PartitionName); + byte[] DirectoryNameBuffer = Encoding.Unicode.GetBytes(DirectoryName); + + byte[] Request = new byte[1114]; + const string Header = GetDirectoryEntriesSignature; // NOKXCD + + Buffer.BlockCopy(Encoding.ASCII.GetBytes(Header), 0, Request, 0, Header.Length); + + Buffer.BlockCopy(PartitionNameBuffer, 0, Request, 6, PartitionNameBuffer.Length); + Buffer.BlockCopy(DirectoryNameBuffer, 0, Request, 78, DirectoryNameBuffer.Length); // 512 max size (1024 for unicode) + + Buffer.BlockCopy(BigEndian.GetBytes((int)DataStructSize, 4), 0, Request, 1102, 4); + + // TODO: Investigate data size + //Buffer.BlockCopy(BigEndian.GetBytes(360, 8), 0, Request, 1106, 8); + + byte[]? Response = ExecuteRawMethod(Request); + if ((Response == null) || (Response.Length < 0x10)) + { + return null; + } + + int ResultCode = (Response[6] << 8) + Response[7]; + if (ResultCode != 0) + { + ThrowFlashError(ResultCode); + } + + int ResponseLength = BigEndian.ToInt32(Response, 8); + + byte[] Result = new byte[ResponseLength]; + Buffer.BlockCopy(Response, 12, Result, 0, ResponseLength); + + List directoryEntries = []; + + int j = 0; + while (j != Result.Length) + { + FILE_INFO directoryEntry = FILE_INFO.ReadFromBuffer(Result, j); + j += (int)directoryEntry.Size; + + directoryEntries.Add(directoryEntry); + } + + return [.. directoryEntries]; + } + + public void TelemetryStart() + { + byte[] Request = new byte[4]; + const string Header = TelemetryStartSignature; // NOKS + Buffer.BlockCopy(Encoding.ASCII.GetBytes(Header), 0, Request, 0, Header.Length); + ExecuteRawVoidMethod(Request); + } + + public void TelemetryEnd() + { + byte[] Request = new byte[4]; + const string Header = TelemetryEndSignature; // NOKN + Buffer.BlockCopy(Encoding.ASCII.GetBytes(Header), 0, Request, 0, Header.Length); + ExecuteRawVoidMethod(Request); + } + + // WIP! + public string? ReadLog() + { + byte[] Request = new byte[0x13]; + const string Header = GetLogsSignature; + ulong BufferSize = 0xE000 - 0xC; + + ulong Length = ReadLogSize(DeviceLogType.Flashing)!.Value; + if (Length == 0) + { + return null; + } + + string LogContent = ""; + + for (ulong i = 0; i < Length; i += BufferSize) + { + if (i + BufferSize > Length) + { + BufferSize = Length - i; + } + uint BufferSizeInt = (uint)BufferSize; + + Buffer.BlockCopy(Encoding.ASCII.GetBytes(Header), 0, Request, 0, Header.Length); + Request[6] = 1; + Buffer.BlockCopy(BitConverter.GetBytes(BufferSizeInt).Reverse().ToArray(), 0, Request, 7, 4); + Buffer.BlockCopy(BitConverter.GetBytes(i).Reverse().ToArray(), 0, Request, 11, 8); + + byte[]? Response = ExecuteRawMethod(Request); + if ((Response == null) || (Response.Length < 0xC)) + { + return null; + } + + int ResultLength = Response.Length - 0xC; + byte[] Result = new byte[ResultLength]; + Buffer.BlockCopy(Response, 0xC, Result, 0, ResultLength); + + string PartialLogContent = Encoding.ASCII.GetString(Result); + + LogContent += PartialLogContent; + } + + return LogContent; + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + ~UnifiedFlashingPlatformModel() + { + Dispose(false); + } + + public void Close() + { + USBDevice?.Dispose(); + } + + protected virtual void Dispose(bool disposing) + { + if (Disposed) + { + return; + } + + if (disposing) + { + // Other disposables + } + + // Clean unmanaged resources here. + Close(); + + Disposed = true; + } + } +} \ No newline at end of file diff --git a/WPinternals/ViewModels/LumiaInfoViewModel.cs b/WPinternals/ViewModels/LumiaInfoViewModel.cs index 7a3aad0..0d5756d 100644 --- a/WPinternals/ViewModels/LumiaInfoViewModel.cs +++ b/WPinternals/ViewModels/LumiaInfoViewModel.cs @@ -19,6 +19,7 @@ // DEALINGS IN THE SOFTWARE. using System; +using UnifiedFlashingPlatform; using WPinternals.HelperClasses; using WPinternals.Models.Lumia.NCSd; using WPinternals.Models.SimpleIO; @@ -95,6 +96,9 @@ namespace WPinternals case PhoneInterfaces.SimpleIO: ActivateSubContext(new SimpleIOViewModel((SimpleIOModel)CurrentModel, ModeSwitchRequestCallback)); break; + case PhoneInterfaces.UFP: + ActivateSubContext(new UFPViewModel((UnifiedFlashingPlatformModel)CurrentModel, ModeSwitchRequestCallback)); + break; } } } diff --git a/WPinternals/ViewModels/LumiaModeViewModel.cs b/WPinternals/ViewModels/LumiaModeViewModel.cs index 68e4995..c06a30d 100644 --- a/WPinternals/ViewModels/LumiaModeViewModel.cs +++ b/WPinternals/ViewModels/LumiaModeViewModel.cs @@ -19,6 +19,7 @@ // DEALINGS IN THE SOFTWARE. using System; +using UnifiedFlashingPlatform; using WPinternals.HelperClasses; using WPinternals.Models.Lumia.NCSd; using WPinternals.Models.SimpleIO; @@ -113,6 +114,9 @@ namespace WPinternals case PhoneInterfaces.SimpleIO: ActivateSubContext(new SimpleIOModeViewModel((SimpleIOModel)CurrentModel, OnModeSwitchRequested)); break; + case PhoneInterfaces.UFP: + ActivateSubContext(new UFPModeViewModel((UnifiedFlashingPlatformModel)CurrentModel, OnModeSwitchRequested)); + break; } } diff --git a/WPinternals/ViewModels/MainViewModel.cs b/WPinternals/ViewModels/MainViewModel.cs index 1960e9d..84adfec 100644 --- a/WPinternals/ViewModels/MainViewModel.cs +++ b/WPinternals/ViewModels/MainViewModel.cs @@ -50,7 +50,8 @@ namespace WPinternals Qualcomm_Flash, Lumia_BadMassStorage, Lumia_PhoneInfo, - SimpleIO + SimpleIO, + UFP }; // Create this class on the UI thread, after the main-window of the application is initialized. diff --git a/WPinternals/ViewModels/PhoneNotifierViewModel.cs b/WPinternals/ViewModels/PhoneNotifierViewModel.cs index eb39289..37f5c3b 100644 --- a/WPinternals/ViewModels/PhoneNotifierViewModel.cs +++ b/WPinternals/ViewModels/PhoneNotifierViewModel.cs @@ -24,6 +24,7 @@ using System.Diagnostics.Eventing.Reader; using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; +using UnifiedFlashingPlatform; using WPinternals.HelperClasses; using WPinternals.Models.Lumia.NCSd; using WPinternals.Models.Lumia.UEFI; @@ -49,6 +50,7 @@ namespace WPinternals private USBNotifier LumiaLabelNotifier; private USBNotifier HidInterfaceNotifier; private USBNotifier SimpleIONotifier; + private USBNotifier UFPNotifier; public PhoneInterfaces? CurrentInterface = null; private PhoneInterfaces? LastInterface = null; @@ -71,6 +73,8 @@ namespace WPinternals private Guid SimpleIOInterfaceGuid = new("{82809dd0-51f5-11e1-b86c-0800200c9a66}"); + private Guid UFPInterfaceGuid = new("{9E3BD5F7-9690-4FCC-8810-3E2650CD6ECC}"); + private readonly object ModelLock = new(); private readonly EventWaitHandle NewInterfaceWaitHandle = new(false, EventResetMode.AutoReset); @@ -121,6 +125,10 @@ namespace WPinternals SimpleIONotifier.Arrival += LumiaNotifier_Arrival; SimpleIONotifier.Removal += LumiaNotifier_Removal; + UFPNotifier = new USBNotifier(UFPInterfaceGuid); + UFPNotifier.Arrival += LumiaNotifier_Arrival; + UFPNotifier.Removal += LumiaNotifier_Removal; + try { EventLogQuery LogQuery = new("Microsoft-Windows-Kernel-PnP/Configuration", PathType.LogName, "*[System[(EventID = 411)]]"); @@ -159,6 +167,7 @@ namespace WPinternals LumiaEmergencyNotifier.Dispose(); HidInterfaceNotifier.Dispose(); SimpleIONotifier.Dispose(); + UFPNotifier.Dispose(); LogWatcher.Dispose(); } @@ -185,7 +194,18 @@ namespace WPinternals if (e.DevicePath.Contains("VID_0421&", StringComparison.OrdinalIgnoreCase) || e.DevicePath.Contains("VID_045E&", StringComparison.OrdinalIgnoreCase)) { - if (e.DevicePath.Contains("&PID_062A", StringComparison.OrdinalIgnoreCase)) + if (e.DevicePath.Contains("&PID_0658", StringComparison.OrdinalIgnoreCase)) + { + CurrentModel = new UnifiedFlashingPlatformModel(e.DevicePath); + + CurrentInterface = PhoneInterfaces.UFP; + LogFile.Log("Found device on interface: " + ((USBNotifier)sender).Guid.ToString(), LogType.FileOnly); + LogFile.Log("Device path: " + e.DevicePath, LogType.FileOnly); + LogFile.Log("Connected device: Lumia", LogType.FileAndConsole); + LogFile.Log("Mode: UFP", LogType.FileAndConsole); + NewDeviceArrived(new ArrivalEventArgs((PhoneInterfaces)CurrentInterface, CurrentModel)); + } + else if (e.DevicePath.Contains("&PID_062A", StringComparison.OrdinalIgnoreCase)) { CurrentModel = new SimpleIOModel(e.DevicePath); @@ -551,6 +571,7 @@ namespace WPinternals e.DevicePath.Contains("VID_045E&PID_0A00", StringComparison.OrdinalIgnoreCase) || e.DevicePath.Contains("VID_045E&PID_0A02", StringComparison.OrdinalIgnoreCase) || e.DevicePath.Contains("VID_045E&PID_062A", StringComparison.OrdinalIgnoreCase) || + e.DevicePath.Contains("VID_045E&PID_0658", StringComparison.OrdinalIgnoreCase) || e.DevicePath.Contains("VID_05C6&PID_9008", StringComparison.OrdinalIgnoreCase) || e.DevicePath.Contains("DISK&VEN_QUALCOMM&PROD_MMC_STORAGE", StringComparison.OrdinalIgnoreCase) || e.DevicePath.Contains("DISK&VEN_MSFT&PROD_PHONE_MMC_STOR", StringComparison.OrdinalIgnoreCase) diff --git a/WPinternals/ViewModels/SwitchModeViewModel.cs b/WPinternals/ViewModels/SwitchModeViewModel.cs index da38ebb..bfee49b 100644 --- a/WPinternals/ViewModels/SwitchModeViewModel.cs +++ b/WPinternals/ViewModels/SwitchModeViewModel.cs @@ -23,6 +23,7 @@ using System.Collections.Generic; using System.IO; using System.Threading; using System.Threading.Tasks; +using UnifiedFlashingPlatform; using WPinternals.HelperClasses; using WPinternals.Models.Lumia; using WPinternals.Models.Lumia.NCSd; @@ -218,6 +219,36 @@ namespace WPinternals // Make switch and set message or navigate to error switch (CurrentMode) { + case PhoneInterfaces.UFP: + IsSwitchingInterface = true; + switch (TargetMode) + { + case null: + ((UnifiedFlashingPlatformModel)PhoneNotifier.CurrentModel).Shutdown(); + ModeSwitchProgressWrapper("Please disconnect your device. Waiting...", null); + LogFile.Log("Please disconnect your device. Waiting...", LogType.FileAndConsole); + new Thread(() => + { + PhoneNotifier.WaitForRemoval().Wait(); + ModeSwitchSuccessWrapper(); + }).Start(); + break; + case PhoneInterfaces.Lumia_Normal: + PhoneNotifier.NewDeviceArrived += NewDeviceArrived; + ((UnifiedFlashingPlatformModel)PhoneNotifier.CurrentModel).ResetPhone(); + ModeSwitchProgressWrapper("Rebooting phone to Normal mode...", null); + LogFile.Log("Rebooting phone to Normal mode", LogType.FileAndConsole); + break; + case PhoneInterfaces.Lumia_Bootloader: + PhoneNotifier.NewDeviceArrived += NewDeviceArrived; + ((UnifiedFlashingPlatformModel)PhoneNotifier.CurrentModel).ResetPhone(); + ModeSwitchProgressWrapper("Rebooting phone to Bootloader mode...", null); + LogFile.Log("Rebooting phone to Bootloader mode", LogType.FileAndConsole); + break; + default: + return; + } + break; case PhoneInterfaces.SimpleIO: IsSwitchingInterface = true; switch (TargetMode) diff --git a/WPinternals/ViewModels/UFPModeViewModel.cs b/WPinternals/ViewModels/UFPModeViewModel.cs new file mode 100644 index 0000000..47e6c91 --- /dev/null +++ b/WPinternals/ViewModels/UFPModeViewModel.cs @@ -0,0 +1,56 @@ +// 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; +using UnifiedFlashingPlatform; + +namespace WPinternals +{ + internal class UFPModeViewModel : ContextViewModel + { + private readonly UnifiedFlashingPlatformModel CurrentModel; + private readonly Action RequestModeSwitch; + + internal UFPModeViewModel(UnifiedFlashingPlatformModel CurrentModel, Action RequestModeSwitch) + : base() + { + this.CurrentModel = CurrentModel; + this.RequestModeSwitch = RequestModeSwitch; + } + + public void RebootTo(string Mode) + { + switch (Mode) + { + case "Normal": + RequestModeSwitch(PhoneInterfaces.Lumia_Normal); + break; + case "MassStorage": + RequestModeSwitch(PhoneInterfaces.Lumia_MassStorage); + break; + case "Shutdown": + RequestModeSwitch(null); + break; + default: + return; + } + } + } +} diff --git a/WPinternals/ViewModels/UFPViewModel.cs b/WPinternals/ViewModels/UFPViewModel.cs new file mode 100644 index 0000000..710badd --- /dev/null +++ b/WPinternals/ViewModels/UFPViewModel.cs @@ -0,0 +1,258 @@ +// 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; +using System.Threading; +using UnifiedFlashingPlatform; +using WPinternals.HelperClasses; + +namespace WPinternals +{ + // Create this class on the UI thread, after the main-window of the application is initialized. + // It is necessary to create the object on the UI thread, because notification events to the View need to be fired on that thread. + // The Model for this ViewModel communicates over USB and for that it uses the hWnd of the main window. + // Therefore the main window must be created before the ViewModel is created. + + internal class UFPViewModel : ContextViewModel + { + private readonly UnifiedFlashingPlatformModel CurrentModel; + private readonly Action RequestModeSwitch; + private readonly object LockDeviceInfo = new(); + private bool DeviceInfoLoaded = false; + + internal UFPViewModel(UnifiedFlashingPlatformModel CurrentModel, Action RequestModeSwitch) + : base() + { + this.RequestModeSwitch = RequestModeSwitch; + + this.CurrentModel = CurrentModel; + + new Thread(() => StartLoadDeviceInfo()).Start(); + } + + private void StartLoadDeviceInfo() + { + lock (LockDeviceInfo) + { + if (!DeviceInfoLoaded) + { + try + { + PlatformName = CurrentModel.ReadStringParam("DPI"); + LogFile.Log("Platform Name: " + PlatformName); + + // Some phones do not support the Terminal interface! (928 verizon) + // Instead read param RRKH to get the RKH. + PublicID = null; + byte[] RawPublicID = CurrentModel.ReadParam("PID"); + if (RawPublicID?.Length > 4) + { + PublicID = new byte[RawPublicID.Length - 4]; + Array.Copy(RawPublicID, 4, PublicID, 0, RawPublicID.Length - 4); + LogFile.Log("Public ID: " + Converter.ConvertHexToString(PublicID, " ")); + } + else + { + PublicID = new byte[20]; + LogFile.Log("Public ID: " + Converter.ConvertHexToString(PublicID, " ")); + } + RootKeyHash = CurrentModel.ReadParam("RRKH"); + if (RootKeyHash != null) + { + LogFile.Log("Root Key Hash: " + Converter.ConvertHexToString(RootKeyHash, " ")); + } + + byte[] EMS = CurrentModel.ReadParam("EMS"); + if (EMS != null) + { + UInt64 MemSize = (UInt64)(((UInt32)EMS[0] << 24) + ((UInt32)EMS[1] << 16) + ((UInt32)EMS[2] << 8) + EMS[3]) * 0x200; + double MemSizeDouble = (double)MemSize / 1024 / 1024 / 1024; + MemSizeDouble = (double)(int)(MemSizeDouble * 10) / 10; + string Manufacturer = null; + + eMMC = Manufacturer == null ? MemSizeDouble.ToString() + " GB" : Manufacturer + " " + MemSizeDouble.ToString() + " GB"; + } + else + { + eMMC = "Unknown"; + SamsungWarningVisible = true; + } + + UnifiedFlashingPlatformModel.PhoneInfo Info = CurrentModel.ReadPhoneInfo(); + BootloaderDescription = Info.FlashAppProtocolVersionMajor < 2 ? "Lumia Bootloader Spec A" : "Lumia Bootloader Spec B"; + + LogFile.Log("Bootloader: " + BootloaderDescription); + + ProductCode = "";//TODO: FIXME: Info.ProductCode; + LogFile.Log("ProductCode: " + ProductCode); + + ProductType = "";//TODO: FIXME: Info.Type; + LogFile.Log("ProductType: " + ProductType); + + if (PlatformName == null) + { + LogFile.Log("Platform Name was null. Gathering information from an alternative source."); + + PlatformName = Info.PlatformID; + LogFile.Log("Platform Name: " + PlatformName); + } + } + catch (Exception ex) + { + LogFile.Log("An unexpected error happened", LogType.FileAndConsole); + LogFile.Log(ex.GetType().ToString(), LogType.FileAndConsole); + LogFile.Log(ex.Message, LogType.FileAndConsole); + LogFile.Log(ex.StackTrace, LogType.FileAndConsole); + + LogFile.Log("Reading status from Flash interface was aborted."); + } + DeviceInfoLoaded = true; + } + } + } + + private byte[] _PublicID = null; + public byte[] PublicID + { + get + { + return _PublicID; + } + set + { + _PublicID = value; + OnPropertyChanged(nameof(PublicID)); + } + } + + private byte[] _RootKeyHash = null; + public byte[] RootKeyHash + { + get + { + return _RootKeyHash; + } + set + { + _RootKeyHash = value; + OnPropertyChanged(nameof(RootKeyHash)); + } + } + + private string _PlatformName = null; + public string PlatformName + { + get + { + return _PlatformName; + } + set + { + _PlatformName = value; + OnPropertyChanged(nameof(PlatformName)); + } + } + + private string _ProductType = null; + public string ProductType + { + get + { + return _ProductType; + } + set + { + _ProductType = value; + OnPropertyChanged(nameof(ProductType)); + } + } + + private string _ProductCode = null; + public string ProductCode + { + get + { + return _ProductCode; + } + set + { + _ProductCode = value; + OnPropertyChanged(nameof(ProductCode)); + } + } + + private string _eMMC = null; + public string eMMC + { + get + { + return _eMMC; + } + set + { + _eMMC = value; + OnPropertyChanged(nameof(eMMC)); + } + } + + private string _BootloaderDescription = null; + public string BootloaderDescription + { + get + { + return _BootloaderDescription; + } + set + { + _BootloaderDescription = value; + OnPropertyChanged(nameof(BootloaderDescription)); + } + } + + private bool _SamsungWarningVisible = false; + public bool SamsungWarningVisible + { + get + { + return _SamsungWarningVisible; + } + set + { + _SamsungWarningVisible = value; + OnPropertyChanged(nameof(SamsungWarningVisible)); + } + } + + public void RebootTo(string Mode) + { + switch (Mode) + { + case "Normal": + RequestModeSwitch(PhoneInterfaces.Lumia_Normal); + break; + case "MassStorage": + RequestModeSwitch(PhoneInterfaces.Lumia_MassStorage); + break; + default: + return; + } + } + } +} diff --git a/WPinternals/Views/MainWindow.xaml b/WPinternals/Views/MainWindow.xaml index d063a1d..7d5a6a0 100644 --- a/WPinternals/Views/MainWindow.xaml +++ b/WPinternals/Views/MainWindow.xaml @@ -121,6 +121,12 @@ DEALINGS IN THE SOFTWARE. + + + + + + + + + + + + + + + + + Switch to Normal-mode + + + + + Shutdown the phone + + + + + Switch to Mass-Storage-mode + + + + + + + + + + + + + + + + + + + + diff --git a/WPinternals/Views/UFPModeView.xaml.cs b/WPinternals/Views/UFPModeView.xaml.cs new file mode 100644 index 0000000..67e77e9 --- /dev/null +++ b/WPinternals/Views/UFPModeView.xaml.cs @@ -0,0 +1,50 @@ +// 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.Windows; +using System.Windows.Controls; +using System.Windows.Documents; + +namespace WPinternals +{ + /// + /// Interaction logic for UFPModeView.xaml + /// + public partial class UFPModeView : UserControl + { + public UFPModeView() + { + InitializeComponent(); + } + + private void HandleHyperlinkClick(object sender, RoutedEventArgs args) + { + if (args.Source is Hyperlink link) + { + (this.DataContext as UFPModeViewModel)?.RebootTo(link.NavigateUri.ToString()); + } + } + + private void Document_Loaded(object sender, RoutedEventArgs e) + { + (sender as FlowDocument)?.AddHandler(Hyperlink.ClickEvent, new RoutedEventHandler(HandleHyperlinkClick)); + } + } +} diff --git a/WPinternals/Views/UFPView.xaml b/WPinternals/Views/UFPView.xaml new file mode 100644 index 0000000..123df6b --- /dev/null +++ b/WPinternals/Views/UFPView.xaml @@ -0,0 +1,125 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Getting started + + + + + + + + + + Normal + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/WPinternals/Views/UFPView.xaml.cs b/WPinternals/Views/UFPView.xaml.cs new file mode 100644 index 0000000..5035f10 --- /dev/null +++ b/WPinternals/Views/UFPView.xaml.cs @@ -0,0 +1,51 @@ +// 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.Windows; +using System.Windows.Controls; +using System.Windows.Documents; + +namespace WPinternals +{ + /// + /// Interaction logic for UFPView.xaml + /// + public partial class UFPView : UserControl + { + public UFPView() + { + InitializeComponent(); + } + + private void HandleHyperlinkClick(object sender, RoutedEventArgs args) + { + Hyperlink link = args.Source as Hyperlink; + if (link?.NavigateUri != null) + { + (this.DataContext as UFPViewModel)?.RebootTo(link.NavigateUri.ToString()); + } + } + + private void Document_Loaded(object sender, RoutedEventArgs e) + { + (sender as FlowDocument)?.AddHandler(Hyperlink.ClickEvent, new RoutedEventHandler(HandleHyperlinkClick)); + } + } +} diff --git a/WPinternals/WPinternals.csproj b/WPinternals/WPinternals.csproj index 210bf13..be23441 100644 --- a/WPinternals/WPinternals.csproj +++ b/WPinternals/WPinternals.csproj @@ -344,6 +344,9 @@ + + + "C:\Program Files\PsTools\pskill.exe" XDesProc.exe 2>nul 1>nul