mirror of
https://github.com/ReneLergner/WPinternals.git
synced 2026-06-18 13:20:11 +10:00
Initial commit - WPinternals 2.6
This commit is contained in:
@@ -0,0 +1,381 @@
|
||||
// Copyright (c) 2018, Rene Lergner - wpinternals.net - @Heathcliff74xda
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
// These functions assume same endianness for the CPU architecture and the raw data it reads from or writes to.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace WPinternals
|
||||
{
|
||||
internal static class ByteOperations
|
||||
{
|
||||
internal static string ReadAsciiString(byte[] ByteArray, UInt32 Offset, UInt32 Length)
|
||||
{
|
||||
byte[] Bytes = new byte[Length];
|
||||
Buffer.BlockCopy(ByteArray, (int)Offset, Bytes, 0, (int)Length);
|
||||
return System.Text.Encoding.ASCII.GetString(Bytes);
|
||||
}
|
||||
|
||||
internal static string ReadUnicodeString(byte[] ByteArray, UInt32 Offset, UInt32 Length)
|
||||
{
|
||||
byte[] Bytes = new byte[Length];
|
||||
Buffer.BlockCopy(ByteArray, (int)Offset, Bytes, 0, (int)Length);
|
||||
return System.Text.Encoding.Unicode.GetString(Bytes);
|
||||
}
|
||||
|
||||
internal static void WriteAsciiString(byte[] ByteArray, UInt32 Offset, string Text, UInt32? MaxBufferLength = null)
|
||||
{
|
||||
if (MaxBufferLength != null)
|
||||
Array.Clear(ByteArray, (int)Offset, (int)MaxBufferLength);
|
||||
|
||||
byte[] TextBytes = System.Text.ASCIIEncoding.ASCII.GetBytes(Text);
|
||||
int WriteLength = TextBytes.Length;
|
||||
if (WriteLength > MaxBufferLength)
|
||||
WriteLength = (int)MaxBufferLength;
|
||||
|
||||
Buffer.BlockCopy(TextBytes, 0, ByteArray, (int)Offset, WriteLength);
|
||||
}
|
||||
|
||||
internal static void WriteUnicodeString(byte[] ByteArray, UInt32 Offset, string Text, UInt32? MaxBufferLength = null)
|
||||
{
|
||||
if (MaxBufferLength != null)
|
||||
Array.Clear(ByteArray, (int)Offset, (int)MaxBufferLength);
|
||||
|
||||
byte[] TextBytes = System.Text.UnicodeEncoding.Unicode.GetBytes(Text);
|
||||
int WriteLength = TextBytes.Length;
|
||||
if (WriteLength > MaxBufferLength)
|
||||
WriteLength = (int)MaxBufferLength;
|
||||
|
||||
Buffer.BlockCopy(TextBytes, 0, ByteArray, (int)Offset, WriteLength);
|
||||
}
|
||||
|
||||
internal static UInt32 ReadUInt32(byte[] ByteArray, UInt32 Offset)
|
||||
{
|
||||
return BitConverter.ToUInt32(ByteArray, (int)Offset);
|
||||
}
|
||||
|
||||
internal static void WriteUInt32(byte[] ByteArray, UInt32 Offset, UInt32 Value)
|
||||
{
|
||||
System.Buffer.BlockCopy(BitConverter.GetBytes(Value), 0, ByteArray, (int)Offset, 4);
|
||||
}
|
||||
|
||||
internal static Int32 ReadInt32(byte[] ByteArray, UInt32 Offset)
|
||||
{
|
||||
return BitConverter.ToInt32(ByteArray, (int)Offset);
|
||||
}
|
||||
|
||||
internal static void WriteInt32(byte[] ByteArray, UInt32 Offset, Int32 Value)
|
||||
{
|
||||
System.Buffer.BlockCopy(BitConverter.GetBytes(Value), 0, ByteArray, (int)Offset, 4);
|
||||
}
|
||||
|
||||
internal static UInt16 ReadUInt16(byte[] ByteArray, UInt32 Offset)
|
||||
{
|
||||
return BitConverter.ToUInt16(ByteArray, (int)Offset);
|
||||
}
|
||||
|
||||
internal static void WriteUInt16(byte[] ByteArray, UInt32 Offset, UInt16 Value)
|
||||
{
|
||||
System.Buffer.BlockCopy(BitConverter.GetBytes(Value), 0, ByteArray, (int)Offset, 2);
|
||||
}
|
||||
|
||||
internal static Int16 ReadInt16(byte[] ByteArray, UInt32 Offset)
|
||||
{
|
||||
return BitConverter.ToInt16(ByteArray, (int)Offset);
|
||||
}
|
||||
|
||||
internal static void WriteInt16(byte[] ByteArray, UInt32 Offset, Int16 Value)
|
||||
{
|
||||
System.Buffer.BlockCopy(BitConverter.GetBytes(Value), 0, ByteArray, (int)Offset, 2);
|
||||
}
|
||||
|
||||
internal static byte ReadUInt8(byte[] ByteArray, UInt32 Offset)
|
||||
{
|
||||
return ByteArray[Offset];
|
||||
}
|
||||
|
||||
internal static void WriteUInt8(byte[] ByteArray, UInt32 Offset, byte Value)
|
||||
{
|
||||
ByteArray[Offset] = Value;
|
||||
}
|
||||
|
||||
internal static UInt32 ReadUInt24(byte[] ByteArray, UInt32 Offset)
|
||||
{
|
||||
return (UInt32)(ByteArray[Offset] + (ByteArray[Offset + 1] << 8) + (ByteArray[Offset + 2] << 16));
|
||||
}
|
||||
|
||||
internal static void WriteUInt24(byte[] ByteArray, UInt32 Offset, UInt32 Value)
|
||||
{
|
||||
System.Buffer.BlockCopy(BitConverter.GetBytes(Value), 0, ByteArray, (int)Offset, 3);
|
||||
}
|
||||
|
||||
internal static UInt64 ReadUInt64(byte[] ByteArray, UInt32 Offset)
|
||||
{
|
||||
return BitConverter.ToUInt64(ByteArray, (int)Offset);
|
||||
}
|
||||
|
||||
internal static void WriteUInt64(byte[] ByteArray, UInt32 Offset, UInt64 Value)
|
||||
{
|
||||
System.Buffer.BlockCopy(BitConverter.GetBytes(Value), 0, ByteArray, (int)Offset, 8);
|
||||
}
|
||||
|
||||
internal static Guid ReadGuid(byte[] ByteArray, UInt32 Offset)
|
||||
{
|
||||
byte[] GuidBuffer = new byte[0x10];
|
||||
Buffer.BlockCopy(ByteArray, (int)Offset, GuidBuffer, 0, 0x10);
|
||||
return new Guid(GuidBuffer);
|
||||
}
|
||||
|
||||
internal static void WriteGuid(byte[] ByteArray, UInt32 Offset, Guid Value)
|
||||
{
|
||||
Buffer.BlockCopy(Value.ToByteArray(), 0, ByteArray, (int)Offset, 0x10);
|
||||
}
|
||||
|
||||
internal static UInt32 Align(UInt32 Base, UInt32 Offset, UInt32 Alignment)
|
||||
{
|
||||
if (((Offset - Base) % Alignment) == 0)
|
||||
return Offset;
|
||||
else
|
||||
return ((((Offset - Base) / Alignment) + 1) * Alignment) + Base;
|
||||
}
|
||||
|
||||
internal static UInt32? FindPatternInFile(string FileName, byte[] Pattern, byte[] Mask, out byte[] OutPattern)
|
||||
{
|
||||
// The mask is optional.
|
||||
// In the mask 0x00 means the value must match, and 0xFF means that this position is a wildcard.
|
||||
|
||||
UInt32? Result = null;
|
||||
|
||||
FileStream Stream = new FileStream(FileName, FileMode.Open, FileAccess.Read);
|
||||
|
||||
byte[] Buffer = new byte[0x10000 + Pattern.Length - 1];
|
||||
UInt32 BufferReadPosition = 0; // Position in buffer where file-chunk is being read.
|
||||
UInt32 BytesInBuffer = 0;
|
||||
UInt32 BytesRead;
|
||||
UInt32 SearchPositionFile = 0;
|
||||
UInt32 SearchPositionBuffer = 0;
|
||||
UInt32 BufferFileOffset = 0; // Offset in file where data from buffer is located.
|
||||
bool Match = false;
|
||||
int i;
|
||||
|
||||
OutPattern = null;
|
||||
|
||||
while (SearchPositionFile <= (Stream.Length - Pattern.Length))
|
||||
{
|
||||
if ((SearchPositionBuffer + Pattern.Length) > BytesInBuffer)
|
||||
{
|
||||
// Need to read next chunk
|
||||
if ((BytesInBuffer - SearchPositionBuffer) > 0)
|
||||
{
|
||||
System.Buffer.BlockCopy(Buffer, (int)SearchPositionBuffer, Buffer, 0, (int)(BytesInBuffer - SearchPositionBuffer));
|
||||
}
|
||||
BufferReadPosition = BytesInBuffer - SearchPositionBuffer;
|
||||
BytesInBuffer -= SearchPositionBuffer;
|
||||
BufferFileOffset += SearchPositionBuffer;
|
||||
SearchPositionBuffer = 0;
|
||||
|
||||
BytesRead = (UInt32)Stream.Read(Buffer, (int)BufferReadPosition, Buffer.Length - (int)BufferReadPosition);
|
||||
BytesInBuffer += BytesRead;
|
||||
}
|
||||
|
||||
Match = true;
|
||||
for (i = 0; i < Pattern.Length; i++)
|
||||
{
|
||||
if (Buffer[SearchPositionBuffer + i] != Pattern[i])
|
||||
if ((Mask == null) || (Mask[i] == 0))
|
||||
{
|
||||
Match = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (Match)
|
||||
{
|
||||
Result = SearchPositionFile;
|
||||
|
||||
OutPattern = new byte[Pattern.Length];
|
||||
System.Buffer.BlockCopy(Buffer, (int)SearchPositionBuffer, OutPattern, 0, Pattern.Length);
|
||||
break;
|
||||
}
|
||||
|
||||
SearchPositionBuffer++;
|
||||
SearchPositionFile++;
|
||||
}
|
||||
|
||||
Stream.Close();
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal static UInt32? FindAscii(byte[] SourceBuffer, string Pattern)
|
||||
{
|
||||
return FindPattern(SourceBuffer, System.Text.ASCIIEncoding.ASCII.GetBytes((string)Pattern), null, null);
|
||||
}
|
||||
|
||||
internal static UInt32? FindUnicode(byte[] SourceBuffer, string Pattern)
|
||||
{
|
||||
return FindPattern(SourceBuffer, System.Text.UnicodeEncoding.Unicode.GetBytes((string)Pattern), null, null);
|
||||
}
|
||||
|
||||
internal static UInt32? FindUint(byte[] SourceBuffer, UInt32 Pattern)
|
||||
{
|
||||
return FindPattern(SourceBuffer, BitConverter.GetBytes((UInt32)Pattern), null, null);
|
||||
}
|
||||
|
||||
internal static UInt32? FindPattern(byte[] SourceBuffer, byte[] Pattern, byte[] Mask, byte[] OutPattern)
|
||||
{
|
||||
return FindPattern(SourceBuffer, 0, null, Pattern, Mask, OutPattern);
|
||||
}
|
||||
|
||||
internal static bool Compare(byte[] Array1, byte[] Array2)
|
||||
{
|
||||
return System.Collections.StructuralComparisons.StructuralEqualityComparer.Equals(Array1, Array2);
|
||||
}
|
||||
|
||||
internal static UInt32? FindPattern(byte[] SourceBuffer, uint SourceOffset, uint? SourceSize, byte[] Pattern, byte[] Mask, byte[] OutPattern)
|
||||
{
|
||||
// The mask is optional.
|
||||
// In the mask 0x00 means the value must match, and 0xFF means that this position is a wildcard.
|
||||
|
||||
UInt32? Result = null;
|
||||
|
||||
UInt32 SearchPosition = SourceOffset;
|
||||
bool Match = false;
|
||||
int i;
|
||||
|
||||
while ((SearchPosition <= (SourceBuffer.Length - Pattern.Length)) && ((SourceSize == null) || (SearchPosition <= (SourceOffset + SourceSize - Pattern.Length))))
|
||||
{
|
||||
Match = true;
|
||||
for (i = 0; i < Pattern.Length; i++)
|
||||
{
|
||||
if (SourceBuffer[SearchPosition + i] != Pattern[i])
|
||||
if ((Mask == null) || (Mask[i] == 0))
|
||||
{
|
||||
Match = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (Match)
|
||||
{
|
||||
Result = SearchPosition;
|
||||
|
||||
if (OutPattern != null)
|
||||
System.Buffer.BlockCopy(SourceBuffer, (int)SearchPosition, OutPattern, 0, Pattern.Length);
|
||||
break;
|
||||
}
|
||||
|
||||
SearchPosition++;
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal static byte CalculateChecksum8(byte[] Buffer, UInt32 Offset, UInt32 Size)
|
||||
{
|
||||
byte Checksum = 0;
|
||||
|
||||
for (UInt32 i = Offset; i < (Offset + Size); i++)
|
||||
Checksum += Buffer[i];
|
||||
|
||||
return (byte)(0x100 - Checksum);
|
||||
}
|
||||
|
||||
internal static UInt16 CalculateChecksum16(byte[] Buffer, UInt32 Offset, UInt32 Size)
|
||||
{
|
||||
UInt16 Checksum = 0;
|
||||
|
||||
for (UInt32 i = Offset; i < (Offset + Size - 1); i += 2)
|
||||
Checksum += BitConverter.ToUInt16(Buffer, (int)i);
|
||||
|
||||
return (UInt16)(0x10000 - Checksum);
|
||||
}
|
||||
|
||||
private static UInt32[] CRC32Table = new UInt32[] {
|
||||
0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F,
|
||||
0xE963A535, 0x9E6495A3, 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988,
|
||||
0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, 0x1DB71064, 0x6AB020F2,
|
||||
0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
|
||||
0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9,
|
||||
0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172,
|
||||
0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 0x35B5A8FA, 0x42B2986C,
|
||||
0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
|
||||
0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423,
|
||||
0xCFBA9599, 0xB8BDA50F, 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924,
|
||||
0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, 0x76DC4190, 0x01DB7106,
|
||||
0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
|
||||
0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D,
|
||||
0x91646C97, 0xE6635C01, 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E,
|
||||
0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950,
|
||||
0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
|
||||
0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7,
|
||||
0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0,
|
||||
0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, 0x5005713C, 0x270241AA,
|
||||
0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
|
||||
0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81,
|
||||
0xB7BD5C3B, 0xC0BA6CAD, 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A,
|
||||
0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, 0xE3630B12, 0x94643B84,
|
||||
0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
|
||||
0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB,
|
||||
0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC,
|
||||
0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 0xD6D6A3E8, 0xA1D1937E,
|
||||
0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
|
||||
0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55,
|
||||
0x316E8EEF, 0x4669BE79, 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236,
|
||||
0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, 0xC5BA3BBE, 0xB2BD0B28,
|
||||
0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
|
||||
0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F,
|
||||
0x72076785, 0x05005713, 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38,
|
||||
0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242,
|
||||
0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
|
||||
0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69,
|
||||
0x616BFFD3, 0x166CCF45, 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2,
|
||||
0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC,
|
||||
0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
|
||||
0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693,
|
||||
0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94,
|
||||
0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D
|
||||
};
|
||||
|
||||
internal static UInt32 CRC32(byte[] Input, UInt32 Offset, UInt32 Length)
|
||||
{
|
||||
if ((Input == null) || ((Offset + Length) > Input.Length))
|
||||
throw new ArgumentException();
|
||||
|
||||
unchecked
|
||||
{
|
||||
uint crc = (uint)(((uint)0) ^ (-1));
|
||||
for (var i = Offset; i < (Offset + Length); i++)
|
||||
{
|
||||
crc = (crc >> 8) ^ CRC32Table[ (crc ^ Input[i]) & 0xFF ];
|
||||
}
|
||||
crc = (uint)(crc ^ (-1));
|
||||
|
||||
if (crc < 0)
|
||||
{
|
||||
crc += (uint)4294967296;
|
||||
}
|
||||
|
||||
return crc;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+449
@@ -0,0 +1,449 @@
|
||||
// Copyright (c) 2018, Rene Lergner - wpinternals.net - @Heathcliff74xda
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace WPinternals
|
||||
{
|
||||
internal class FFU
|
||||
{
|
||||
internal int ChunkSize;
|
||||
internal string Path;
|
||||
internal byte[] SecurityHeader;
|
||||
internal byte[] ImageHeader;
|
||||
internal byte[] StoreHeader;
|
||||
private int?[] ChunkIndexes;
|
||||
private FileStream FFUFile = null;
|
||||
private int FileOpenCount = 0;
|
||||
|
||||
internal string PlatformID;
|
||||
internal GPT GPT;
|
||||
|
||||
internal UInt64 TotalSize;
|
||||
internal UInt64 HeaderSize;
|
||||
internal UInt64 PayloadSize;
|
||||
internal UInt64 TotalChunkCount;
|
||||
|
||||
internal FFU(string Path)
|
||||
{
|
||||
this.Path = Path;
|
||||
|
||||
try
|
||||
{
|
||||
OpenFile();
|
||||
|
||||
// Read Security Header
|
||||
byte[] ShortSecurityHeader = new byte[0x20];
|
||||
FFUFile.Read(ShortSecurityHeader, 0, 0x20);
|
||||
if (ByteOperations.ReadAsciiString(ShortSecurityHeader, 0x04, 0x0C) != "SignedImage ")
|
||||
throw new BadImageFormatException();
|
||||
ChunkSize = ByteOperations.ReadInt32(ShortSecurityHeader, 0x10) * 1024;
|
||||
UInt32 SecurityHeaderSize = ByteOperations.ReadUInt32(ShortSecurityHeader, 0x00);
|
||||
UInt32 CatalogSize = ByteOperations.ReadUInt32(ShortSecurityHeader, 0x18);
|
||||
UInt32 HashTableSize = ByteOperations.ReadUInt32(ShortSecurityHeader, 0x1C);
|
||||
SecurityHeader = new byte[RoundUpToChunks(SecurityHeaderSize + CatalogSize + HashTableSize)];
|
||||
FFUFile.Seek(0, SeekOrigin.Begin);
|
||||
FFUFile.Read(SecurityHeader, 0, SecurityHeader.Length);
|
||||
|
||||
// Read Image Header
|
||||
byte[] ShortImageHeader = new byte[0x1C];
|
||||
FFUFile.Read(ShortImageHeader, 0, 0x1C);
|
||||
if (ByteOperations.ReadAsciiString(ShortImageHeader, 0x04, 0x0C) != "ImageFlash ")
|
||||
throw new BadImageFormatException();
|
||||
UInt32 ImageHeaderSize = ByteOperations.ReadUInt32(ShortImageHeader, 0x00);
|
||||
UInt32 ManifestSize = ByteOperations.ReadUInt32(ShortImageHeader, 0x10);
|
||||
ImageHeader = new byte[RoundUpToChunks(ImageHeaderSize + ManifestSize)];
|
||||
FFUFile.Seek(SecurityHeader.Length, SeekOrigin.Begin);
|
||||
FFUFile.Read(ImageHeader, 0, ImageHeader.Length);
|
||||
|
||||
// Read Store Header
|
||||
byte[] ShortStoreHeader = new byte[248];
|
||||
FFUFile.Read(ShortStoreHeader, 0, 248);
|
||||
PlatformID = ByteOperations.ReadAsciiString(ShortStoreHeader, 0x0C, 192).TrimEnd(new char[] { (char)0, ' ' });
|
||||
int WriteDescriptorCount = ByteOperations.ReadInt32(ShortStoreHeader, 208);
|
||||
UInt32 WriteDescriptorLength = ByteOperations.ReadUInt32(ShortStoreHeader, 212);
|
||||
UInt32 ValidateDescriptorLength = ByteOperations.ReadUInt32(ShortStoreHeader, 220);
|
||||
StoreHeader = new byte[RoundUpToChunks(248 + WriteDescriptorLength + ValidateDescriptorLength)];
|
||||
FFUFile.Seek(SecurityHeader.Length + ImageHeader.Length, SeekOrigin.Begin);
|
||||
FFUFile.Read(StoreHeader, 0, StoreHeader.Length);
|
||||
|
||||
// Parse Chunk Indexes
|
||||
int HighestChunkIndex = 0;
|
||||
UInt32 LocationCount;
|
||||
int ChunkIndex;
|
||||
int ChunkCount;
|
||||
int DiskAccessMethod;
|
||||
UInt32 WriteDescriptorEntryOffset = 248 + ValidateDescriptorLength;
|
||||
int FFUChunkIndex = 0;
|
||||
for (int i = 0; i < WriteDescriptorCount; i++)
|
||||
{
|
||||
LocationCount = ByteOperations.ReadUInt32(StoreHeader, WriteDescriptorEntryOffset + 0x00);
|
||||
ChunkCount = ByteOperations.ReadInt32(StoreHeader, WriteDescriptorEntryOffset + 0x04);
|
||||
|
||||
for (int j = 0; j < LocationCount; j++)
|
||||
{
|
||||
DiskAccessMethod = ByteOperations.ReadInt32(StoreHeader, (UInt32)(WriteDescriptorEntryOffset + 0x08 + (j * 0x08)));
|
||||
ChunkIndex = ByteOperations.ReadInt32(StoreHeader, (UInt32)(WriteDescriptorEntryOffset + 0x0C + (j * 0x08)));
|
||||
|
||||
if (DiskAccessMethod == 0) // 0 = From begin, 2 = From end. We ignore chunks at end of disk. These contain secondairy GPT.
|
||||
{
|
||||
if ((ChunkIndex + ChunkCount - 1) > HighestChunkIndex)
|
||||
HighestChunkIndex = ChunkIndex + ChunkCount - 1;
|
||||
}
|
||||
}
|
||||
WriteDescriptorEntryOffset += 8 + (LocationCount * 0x08);
|
||||
FFUChunkIndex += ChunkCount;
|
||||
}
|
||||
ChunkIndexes = new int?[HighestChunkIndex + 1];
|
||||
WriteDescriptorEntryOffset = 248 + ValidateDescriptorLength;
|
||||
FFUChunkIndex = 0;
|
||||
for (int i = 0; i < WriteDescriptorCount; i++)
|
||||
{
|
||||
LocationCount = ByteOperations.ReadUInt32(StoreHeader, WriteDescriptorEntryOffset + 0x00);
|
||||
ChunkCount = ByteOperations.ReadInt32(StoreHeader, WriteDescriptorEntryOffset + 0x04);
|
||||
|
||||
for (int j = 0; j < LocationCount; j++)
|
||||
{
|
||||
DiskAccessMethod = ByteOperations.ReadInt32(StoreHeader, (UInt32)(WriteDescriptorEntryOffset + 0x08 + (j * 0x08)));
|
||||
ChunkIndex = ByteOperations.ReadInt32(StoreHeader, (UInt32)(WriteDescriptorEntryOffset + 0x0C + (j * 0x08)));
|
||||
|
||||
if (DiskAccessMethod == 0) // 0 = From begin, 2 = From end. We ignore chunks at end of disk. These contain secondairy GPT.
|
||||
{
|
||||
for (int k = 0; k < ChunkCount; k++)
|
||||
{
|
||||
ChunkIndexes[ChunkIndex + k] = FFUChunkIndex + k;
|
||||
}
|
||||
}
|
||||
}
|
||||
WriteDescriptorEntryOffset += 8 + (LocationCount * 0x08);
|
||||
FFUChunkIndex += ChunkCount;
|
||||
}
|
||||
|
||||
byte[] GPTBuffer = GetSectors(0x01, 0x21);
|
||||
GPT = new GPT(GPTBuffer);
|
||||
|
||||
HeaderSize = (UInt64)(SecurityHeader.Length + ImageHeader.Length + StoreHeader.Length);
|
||||
|
||||
TotalChunkCount = (UInt64)FFUChunkIndex;
|
||||
PayloadSize = TotalChunkCount * (UInt64)ChunkSize;
|
||||
TotalSize = HeaderSize + PayloadSize;
|
||||
|
||||
if (TotalSize != (UInt64)FFUFile.Length)
|
||||
throw new WPinternalsException("Bad FFU file", "Bad FFU file: " + Path + "." + Environment.NewLine + "Expected size: " + TotalSize.ToString() + ". Actual size: " + FFUFile.Length + ".");
|
||||
}
|
||||
catch (WPinternalsException)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
catch (Exception Ex)
|
||||
{
|
||||
throw new WPinternalsException("Bad FFU file", "Bad FFU file: " + Path + "." + Environment.NewLine + Ex.Message, Ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
CloseFile();
|
||||
}
|
||||
}
|
||||
|
||||
internal static bool IsFFU(string FileName)
|
||||
{
|
||||
bool Result = false;
|
||||
|
||||
FileStream FFUFile = new FileStream(FileName, FileMode.Open, FileAccess.Read);
|
||||
|
||||
byte[] Signature = new byte[0x10];
|
||||
FFUFile.Read(Signature, 0, 0x10);
|
||||
|
||||
Result = (ByteOperations.ReadAsciiString(Signature, 0x04, 0x0C) == "SignedImage ");
|
||||
|
||||
FFUFile.Close();
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
private void OpenFile()
|
||||
{
|
||||
if (FFUFile == null)
|
||||
{
|
||||
FFUFile = new FileStream(Path, FileMode.Open, FileAccess.Read);
|
||||
FileOpenCount = 0;
|
||||
}
|
||||
FileOpenCount++;
|
||||
}
|
||||
|
||||
private void CloseFile()
|
||||
{
|
||||
FileOpenCount--;
|
||||
if (FileOpenCount == 0)
|
||||
{
|
||||
FFUFile.Close();
|
||||
FFUFile = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void FileSeek(long Position)
|
||||
{
|
||||
// https://social.msdn.microsoft.com/Forums/vstudio/en-US/2e67ca57-3556-4275-accd-58b7df30d424/unnecessary-filestreamseek-and-setting-filestreamposition-has-huge-effect-on-performance?forum=csharpgeneral
|
||||
|
||||
if (FFUFile != null)
|
||||
{
|
||||
if (FFUFile.Position != Position)
|
||||
FFUFile.Seek(Position, SeekOrigin.Begin);
|
||||
}
|
||||
}
|
||||
|
||||
internal UInt32 RoundUpToChunks(UInt32 Size)
|
||||
{
|
||||
if ((Size % ChunkSize) > 0)
|
||||
return (UInt32)(((Size / ChunkSize) + 1) * ChunkSize);
|
||||
else
|
||||
return Size;
|
||||
}
|
||||
|
||||
internal UInt32 RoundDownToChunks(UInt32 Size)
|
||||
{
|
||||
if ((Size % ChunkSize) > 0)
|
||||
return (UInt32)((Size / ChunkSize) * ChunkSize);
|
||||
else
|
||||
return Size;
|
||||
}
|
||||
|
||||
internal byte[] GetSectors(int StartSector, int SectorCount)
|
||||
{
|
||||
int FirstChunk = GetChunkIndexFromSectorIndex(StartSector);
|
||||
int LastChunk = GetChunkIndexFromSectorIndex(StartSector + SectorCount - 1);
|
||||
|
||||
byte[] Buffer = new byte[ChunkSize];
|
||||
|
||||
OpenFile();
|
||||
|
||||
byte[] Result = new byte[SectorCount * 0x200];
|
||||
|
||||
int ResultOffset = 0;
|
||||
|
||||
for (int j = FirstChunk; j <= LastChunk; j++)
|
||||
{
|
||||
GetChunk(Buffer, j);
|
||||
|
||||
int FirstSector = 0;
|
||||
int LastSector = (ChunkSize / 0x200) - 1;
|
||||
|
||||
if (j == FirstChunk)
|
||||
FirstSector = GetSectorNumberInChunkFromSectorIndex(StartSector);
|
||||
|
||||
if (j == LastChunk)
|
||||
LastSector = GetSectorNumberInChunkFromSectorIndex(StartSector + SectorCount - 1);
|
||||
|
||||
int Offset = FirstSector * 0x200;
|
||||
int Size = (LastSector - FirstSector + 1) * 0x200;
|
||||
|
||||
System.Buffer.BlockCopy(Buffer, Offset, Result, ResultOffset, Size);
|
||||
|
||||
ResultOffset += Size;
|
||||
}
|
||||
|
||||
CloseFile();
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal byte[] GetPartition(string Name)
|
||||
{
|
||||
Partition Target = GPT.Partitions.Where(p => (string.Compare(p.Name, Name, true) == 0)).FirstOrDefault();
|
||||
if (Target == null)
|
||||
throw new ArgumentOutOfRangeException();
|
||||
return GetSectors((int)Target.FirstSector, (int)(Target.LastSector - Target.FirstSector + 1));
|
||||
}
|
||||
|
||||
internal void WritePartition(string Name, string FilePath, bool Compress = false)
|
||||
{
|
||||
WritePartition(Name, FilePath, null, null, Compress);
|
||||
}
|
||||
|
||||
internal void WritePartition(string Name, string FilePath, Action<int, TimeSpan?> ProgressUpdateCallback, bool Compress = false)
|
||||
{
|
||||
WritePartition(Name, FilePath, ProgressUpdateCallback, null, Compress);
|
||||
}
|
||||
|
||||
internal void WritePartition(string Name, string FilePath, ProgressUpdater UpdaterPerSector, bool Compress = false)
|
||||
{
|
||||
WritePartition(Name, FilePath, null, UpdaterPerSector, Compress);
|
||||
}
|
||||
|
||||
private void WritePartition(string Name, string FilePath, Action<int, TimeSpan?> ProgressUpdateCallback, ProgressUpdater UpdaterPerSector, bool Compress = false)
|
||||
{
|
||||
Partition Target = GPT.Partitions.Where(p => (string.Compare(p.Name, Name, true) == 0)).FirstOrDefault();
|
||||
if (Target == null)
|
||||
throw new ArgumentOutOfRangeException();
|
||||
|
||||
int FirstChunk = GetChunkIndexFromSectorIndex((int)Target.FirstSector);
|
||||
int LastChunk = GetChunkIndexFromSectorIndex((int)Target.LastSector);
|
||||
|
||||
ProgressUpdater Updater = UpdaterPerSector;
|
||||
if ((Updater == null) && (ProgressUpdateCallback != null))
|
||||
Updater = new ProgressUpdater(Target.LastSector - Target.FirstSector + 1, ProgressUpdateCallback);
|
||||
|
||||
byte[] Buffer = new byte[ChunkSize];
|
||||
|
||||
OpenFile();
|
||||
|
||||
FileStream OutputFile = new FileStream(FilePath, FileMode.Create, FileAccess.Write);
|
||||
Stream OutStream = OutputFile;
|
||||
|
||||
// We use gzip compression
|
||||
//
|
||||
// LZMA is about 60 times slower (compression is twice as good, but compressed size is already really small, so it doesnt matter much)
|
||||
// OutStream = new LZMACompressionStream(OutputFile, System.IO.Compression.CompressionMode.Compress, false);
|
||||
//
|
||||
// DeflateStream is a raw compression stream without recognizable header
|
||||
// Deflate has almost no performance penalty
|
||||
// OutStream = new DeflateStream(OutputFile, CompressionLevel.Optimal, false);
|
||||
//
|
||||
// GZip can be recognized. It always starts with 1F 8B 08 (1F 8B is the magic value, 08 is the Deflate compression method)
|
||||
// With GZip compression, dump time goes from 1m to 1m37s. So that doesnt matter much.
|
||||
if (Compress)
|
||||
{
|
||||
OutStream = new CompressedStream(OutputFile, (Target.LastSector - Target.FirstSector + 1) * 0x200);
|
||||
}
|
||||
|
||||
for (int j = FirstChunk; j <= LastChunk; j++)
|
||||
{
|
||||
GetChunk(Buffer, j);
|
||||
|
||||
int FirstSector = 0;
|
||||
int LastSector = (ChunkSize / 0x200) - 1;
|
||||
|
||||
if (j == FirstChunk)
|
||||
FirstSector = GetSectorNumberInChunkFromSectorIndex((int)Target.FirstSector);
|
||||
|
||||
if (j == LastChunk)
|
||||
LastSector = GetSectorNumberInChunkFromSectorIndex((int)Target.LastSector);
|
||||
|
||||
int Offset = FirstSector * 0x200;
|
||||
int Size = (LastSector - FirstSector + 1) * 0x200;
|
||||
|
||||
OutStream.Write(Buffer, Offset, Size);
|
||||
|
||||
if (Updater != null)
|
||||
Updater.IncreaseProgress((UInt64)(ChunkSize / 0x200));
|
||||
}
|
||||
|
||||
OutStream.Close();
|
||||
|
||||
CloseFile();
|
||||
}
|
||||
|
||||
private byte[] GetChunk(int ChunkIndex)
|
||||
{
|
||||
long BaseOffset = (long)SecurityHeader.Length + ImageHeader.Length + StoreHeader.Length;
|
||||
if (ChunkIndexes[ChunkIndex] == null)
|
||||
return new byte[ChunkSize];
|
||||
else
|
||||
{
|
||||
OpenFile();
|
||||
FileSeek(BaseOffset + ((long)ChunkIndexes[ChunkIndex] * ChunkSize));
|
||||
byte[] Chunk = new byte[ChunkSize];
|
||||
FFUFile.Read(Chunk, 0, ChunkSize);
|
||||
CloseFile();
|
||||
return Chunk;
|
||||
}
|
||||
}
|
||||
|
||||
private void GetChunk(byte[] Chunk, int ChunkIndex)
|
||||
{
|
||||
long BaseOffset = SecurityHeader.Length + ImageHeader.Length + StoreHeader.Length;
|
||||
if (ChunkIndexes[ChunkIndex] == null)
|
||||
Array.Clear(Chunk, 0, ChunkSize);
|
||||
else
|
||||
{
|
||||
OpenFile();
|
||||
FileSeek(BaseOffset + ((long)ChunkIndexes[ChunkIndex] * ChunkSize));
|
||||
FFUFile.Read(Chunk, 0, ChunkSize);
|
||||
CloseFile();
|
||||
}
|
||||
}
|
||||
|
||||
private int GetChunkIndexFromSectorIndex(int SectorIndex)
|
||||
{
|
||||
int SectorsPerChunk = ChunkSize / 0x200;
|
||||
return SectorIndex / SectorsPerChunk;
|
||||
}
|
||||
|
||||
private int GetSectorNumberInChunkFromSectorIndex(int SectorIndex)
|
||||
{
|
||||
int SectorsPerChunk = ChunkSize / 0x200;
|
||||
return SectorIndex % SectorsPerChunk;
|
||||
}
|
||||
|
||||
internal bool IsPartitionPresentInFFU(string PartitionName)
|
||||
{
|
||||
Partition Target = GPT.GetPartition(PartitionName);
|
||||
if (Target == null)
|
||||
throw new InvalidOperationException("Partitionname is not found!");
|
||||
int ChunkIndex = GetChunkIndexFromSectorIndex((int)Target.FirstSector);
|
||||
return (ChunkIndexes[ChunkIndex] != null);
|
||||
}
|
||||
|
||||
private int GetChunkIndexFromSectorIndex(ulong p)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
internal string GetFirmwareVersion()
|
||||
{
|
||||
string Result = null;
|
||||
|
||||
Partition Plat = GPT.GetPartition("PLAT");
|
||||
if (Plat != null)
|
||||
{
|
||||
byte[] Data = GetPartition("PLAT");
|
||||
uint? Offset = ByteOperations.FindAscii(Data, "SWVERSION=");
|
||||
if (Offset != null)
|
||||
{
|
||||
uint Start = (uint)Offset + 10;
|
||||
uint Length = (uint)ByteOperations.FindPattern(Data, Start, 0x100, new byte[] { 0x00 }, null, null) - Start;
|
||||
uint? Offset0D = ByteOperations.FindPattern(Data, Start, 0x100, new byte[] { 0x0D }, null, null);
|
||||
if ((Offset0D != null) && (Offset0D < (Start + Length)))
|
||||
Length = (uint)Offset0D - Start;
|
||||
Result = ByteOperations.ReadAsciiString(Data, Start, Length);
|
||||
}
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal string GetOSVersion()
|
||||
{
|
||||
byte[] efiesp = GetPartition("EFIESP");
|
||||
MemoryStream s = new MemoryStream(efiesp);
|
||||
DiscUtils.Fat.FatFileSystem fs = new DiscUtils.Fat.FatFileSystem(s);
|
||||
Stream mss = fs.OpenFile(@"Windows\System32\Boot\mobilestartup.efi", FileMode.Open, FileAccess.Read);
|
||||
MemoryStream msms = new MemoryStream();
|
||||
mss.CopyTo(msms);
|
||||
byte[] mobilestartup = msms.ToArray();
|
||||
Version OSVersion = PE.GetProductVersion(mobilestartup);
|
||||
s.Close();
|
||||
|
||||
return OSVersion.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
+644
@@ -0,0 +1,644 @@
|
||||
// Copyright (c) 2018, Rene Lergner - wpinternals.net - @Heathcliff74xda
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Linq;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace WPinternals
|
||||
{
|
||||
[XmlType("Partitions")]
|
||||
public class GPT
|
||||
{
|
||||
private byte[] GPTBuffer;
|
||||
private UInt32 HeaderOffset;
|
||||
private UInt32 HeaderSize;
|
||||
private UInt32 TableOffset;
|
||||
private UInt32 TableSize;
|
||||
private UInt32 PartitionEntrySize;
|
||||
private UInt32 MaxPartitions;
|
||||
internal UInt64 FirstUsableSector;
|
||||
internal UInt64 LastUsableSector;
|
||||
internal bool HasChanged = false;
|
||||
|
||||
[XmlElement("Partition")]
|
||||
public List<Partition> Partitions = new List<Partition>();
|
||||
|
||||
public GPT() // Only for serialization
|
||||
{
|
||||
}
|
||||
|
||||
internal GPT(byte[] GPTBuffer)
|
||||
{
|
||||
this.GPTBuffer = GPTBuffer;
|
||||
UInt32? TempHeaderOffset = ByteOperations.FindAscii(GPTBuffer, "EFI PART");
|
||||
if (TempHeaderOffset == null)
|
||||
throw new WPinternalsException("Bad GPT");
|
||||
HeaderOffset = (UInt32)TempHeaderOffset;
|
||||
HeaderSize = ByteOperations.ReadUInt32(GPTBuffer, HeaderOffset + 0x0C);
|
||||
TableOffset = HeaderOffset + 0x200;
|
||||
FirstUsableSector = ByteOperations.ReadUInt64(GPTBuffer, HeaderOffset + 0x28);
|
||||
LastUsableSector = ByteOperations.ReadUInt64(GPTBuffer, HeaderOffset + 0x30);
|
||||
MaxPartitions = ByteOperations.ReadUInt32(GPTBuffer, HeaderOffset + 0x50);
|
||||
PartitionEntrySize = ByteOperations.ReadUInt32(GPTBuffer, HeaderOffset + 0x54);
|
||||
TableSize = MaxPartitions * PartitionEntrySize;
|
||||
if ((TableOffset + TableSize) > GPTBuffer.Length)
|
||||
throw new WPinternalsException("Bad GPT");
|
||||
|
||||
UInt32 PartitionOffset = TableOffset;
|
||||
|
||||
while (PartitionOffset < (TableOffset + TableSize))
|
||||
{
|
||||
string Name = ByteOperations.ReadUnicodeString(GPTBuffer, PartitionOffset + 0x38, 0x48).TrimEnd(new char[] { (char)0, ' ' });
|
||||
if (Name.Length == 0)
|
||||
break;
|
||||
Partition CurrentPartition = new Partition();
|
||||
CurrentPartition.Name = Name;
|
||||
CurrentPartition.FirstSector = ByteOperations.ReadUInt64(GPTBuffer, PartitionOffset + 0x20);
|
||||
CurrentPartition.LastSector = ByteOperations.ReadUInt64(GPTBuffer, PartitionOffset + 0x28);
|
||||
CurrentPartition.PartitionTypeGuid = ByteOperations.ReadGuid(GPTBuffer, PartitionOffset + 0x00);
|
||||
CurrentPartition.PartitionGuid = ByteOperations.ReadGuid(GPTBuffer, PartitionOffset + 0x10);
|
||||
CurrentPartition.Attributes = ByteOperations.ReadUInt64(GPTBuffer, PartitionOffset + 0x30);
|
||||
Partitions.Add(CurrentPartition);
|
||||
PartitionOffset += PartitionEntrySize;
|
||||
}
|
||||
|
||||
HasChanged = false;
|
||||
}
|
||||
|
||||
internal Partition GetPartition(string Name)
|
||||
{
|
||||
return Partitions.Where(p => (string.Compare(p.Name, Name, true) == 0)).FirstOrDefault();
|
||||
}
|
||||
|
||||
// Magic!
|
||||
// SecureBoot hack for Bootloader Spec A starts here
|
||||
internal byte[] InsertHack()
|
||||
{
|
||||
Partition HackPartition = Partitions.Where(p => (p.Name == "HACK")).FirstOrDefault();
|
||||
Partition SBL1 = Partitions.Where(p => (p.Name == "SBL1")).FirstOrDefault();
|
||||
Partition SBL2 = Partitions.Where(p => (p.Name == "SBL2")).FirstOrDefault();
|
||||
|
||||
if ((SBL1 == null) || (SBL2 == null))
|
||||
throw new WPinternalsException("Bad GPT");
|
||||
|
||||
if (HackPartition == null)
|
||||
{
|
||||
HackPartition = new Partition();
|
||||
HackPartition.Name = "HACK";
|
||||
HackPartition.Attributes = SBL2.Attributes;
|
||||
HackPartition.FirstSector = SBL1.LastSector;
|
||||
HackPartition.LastSector = SBL1.LastSector;
|
||||
|
||||
HackPartition.PartitionTypeGuid = SBL2.PartitionTypeGuid;
|
||||
HackPartition.PartitionGuid = SBL2.PartitionGuid;
|
||||
|
||||
Partitions.Add(HackPartition);
|
||||
|
||||
SBL1.LastSector--;
|
||||
|
||||
SBL2.PartitionTypeGuid = new Guid(new byte[] { 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74 });
|
||||
SBL2.PartitionGuid = new Guid(new byte[] { 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74 });
|
||||
}
|
||||
|
||||
HasChanged = true;
|
||||
|
||||
return Rebuild();
|
||||
}
|
||||
|
||||
internal byte[] RemoveHack()
|
||||
{
|
||||
Partition HackPartition = Partitions.Where(p => (p.Name == "HACK")).FirstOrDefault();
|
||||
Partition SBL1 = Partitions.Where(p => (p.Name == "SBL1")).FirstOrDefault();
|
||||
Partition SBL2 = Partitions.Where(p => (p.Name == "SBL2")).FirstOrDefault();
|
||||
|
||||
if ((SBL1 == null) || (SBL2 == null))
|
||||
throw new WPinternalsException("Bad GPT");
|
||||
|
||||
if (HackPartition != null)
|
||||
{
|
||||
SBL2.PartitionTypeGuid = HackPartition.PartitionTypeGuid;
|
||||
SBL2.PartitionGuid = HackPartition.PartitionGuid;
|
||||
|
||||
Partitions.Remove(HackPartition);
|
||||
|
||||
SBL1.LastSector++;
|
||||
}
|
||||
|
||||
HasChanged = true;
|
||||
|
||||
return Rebuild();
|
||||
}
|
||||
|
||||
internal byte[] Rebuild()
|
||||
{
|
||||
if (GPTBuffer == null)
|
||||
{
|
||||
TableSize = 0x4200;
|
||||
TableOffset = 0;
|
||||
GPTBuffer = new byte[TableSize];
|
||||
}
|
||||
else
|
||||
Array.Clear(GPTBuffer, (int)TableOffset, (int)TableSize);
|
||||
|
||||
UInt32 PartitionOffset = TableOffset;
|
||||
foreach (Partition CurrentPartition in Partitions)
|
||||
{
|
||||
ByteOperations.WriteGuid(GPTBuffer, PartitionOffset + 0x00, CurrentPartition.PartitionTypeGuid);
|
||||
ByteOperations.WriteGuid(GPTBuffer, PartitionOffset + 0x10, CurrentPartition.PartitionGuid);
|
||||
ByteOperations.WriteUInt64(GPTBuffer, PartitionOffset + 0x20, CurrentPartition.FirstSector);
|
||||
ByteOperations.WriteUInt64(GPTBuffer, PartitionOffset + 0x28, CurrentPartition.LastSector);
|
||||
ByteOperations.WriteUInt64(GPTBuffer, PartitionOffset + 0x30, CurrentPartition.Attributes);
|
||||
ByteOperations.WriteUnicodeString(GPTBuffer, PartitionOffset + 0x38, CurrentPartition.Name, 0x48);
|
||||
|
||||
PartitionOffset += PartitionEntrySize;
|
||||
}
|
||||
|
||||
ByteOperations.WriteUInt32(GPTBuffer, HeaderOffset + 0x58, ByteOperations.CRC32(GPTBuffer, TableOffset, TableSize));
|
||||
ByteOperations.WriteUInt32(GPTBuffer, HeaderOffset + 0x10, 0);
|
||||
ByteOperations.WriteUInt32(GPTBuffer, HeaderOffset + 0x10, ByteOperations.CRC32(GPTBuffer, HeaderOffset, HeaderSize));
|
||||
|
||||
return GPTBuffer;
|
||||
}
|
||||
|
||||
internal void MergePartitionsFromFile(string Path, bool RoundToChunks)
|
||||
{
|
||||
MergePartitions(File.ReadAllText(Path), RoundToChunks);
|
||||
}
|
||||
|
||||
internal void MergePartitionsFromStream(Stream Partitions, bool RoundToChunks)
|
||||
{
|
||||
using (TextReader tr = new StreamReader(Partitions))
|
||||
{
|
||||
MergePartitions(tr.ReadToEnd(), RoundToChunks);
|
||||
}
|
||||
}
|
||||
|
||||
internal void MergePartitions(string Xml, bool RoundToChunks, ZipArchive Archive = null)
|
||||
{
|
||||
GPT GptToMerge;
|
||||
if (Xml == null)
|
||||
GptToMerge = new GPT();
|
||||
else
|
||||
{
|
||||
XmlSerializer x = new XmlSerializer(typeof(GPT), "");
|
||||
MemoryStream s = new MemoryStream(System.Text.Encoding.ASCII.GetBytes(Xml));
|
||||
GptToMerge = (GPT)x.Deserialize(s);
|
||||
s.Dispose();
|
||||
}
|
||||
|
||||
if (Archive != null)
|
||||
{
|
||||
foreach (Partition NewPartition in GptToMerge.Partitions)
|
||||
{
|
||||
ZipArchiveEntry Entry = Archive.Entries.Where(e => ((string.Compare(e.Name, NewPartition.Name, true) == 0) || (e.Name.StartsWith(NewPartition.Name + ".", true, System.Globalization.CultureInfo.GetCultureInfo("en-US"))))).FirstOrDefault();
|
||||
if (Entry == null)
|
||||
{
|
||||
// There is a partition entry in the xml, but this partition is not present in the archive.
|
||||
|
||||
Partition OldPartition = GetPartition(NewPartition.Name);
|
||||
if (OldPartition == null)
|
||||
{
|
||||
// The partition entry in the xml is also not present in the current partition table.
|
||||
// It must have a know position and length.
|
||||
|
||||
if (NewPartition.LastSector == 0)
|
||||
{
|
||||
throw new WPinternalsException("Unknown length for partition \"" + NewPartition.Name + "\"");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// The partition entry in the xml is also present in the current partition table.
|
||||
// But since the partition is not present in the archive, the partition cannot be relocated.
|
||||
// If the location of the new partition is specified, it must be the same as the current partition.
|
||||
|
||||
if ((NewPartition.FirstSector != 0) && (NewPartition.FirstSector != OldPartition.FirstSector))
|
||||
throw new WPinternalsException("Incorrect location for partition \"" + NewPartition.Name + "\"");
|
||||
if ((NewPartition.LastSector != 0) && (NewPartition.LastSector != OldPartition.LastSector))
|
||||
throw new WPinternalsException("Incorrect length for partition \"" + NewPartition.Name + "\"");
|
||||
|
||||
NewPartition.FirstSector = OldPartition.FirstSector;
|
||||
NewPartition.LastSector = OldPartition.LastSector;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// The partition in the xml is also present in the archive.
|
||||
// If the length is specified in the xml, it must match the file in the archive.
|
||||
|
||||
ulong StreamLengthInSectors = (ulong)Entry.Length / 0x200;
|
||||
using (DecompressedStream DecompressedStream = new DecompressedStream(Entry.Open()))
|
||||
{
|
||||
try
|
||||
{
|
||||
StreamLengthInSectors = (ulong)DecompressedStream.Length / 0x200;
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
if (NewPartition.LastSector == 0)
|
||||
{
|
||||
NewPartition.SizeInSectors = StreamLengthInSectors;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (NewPartition.SizeInSectors != StreamLengthInSectors)
|
||||
throw new WPinternalsException("Inconsistent length specified for partition \"" + NewPartition.Name + "\"");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (Partition NewPartition in GptToMerge.Partitions)
|
||||
{
|
||||
// This is a partition entry in the xml, and there is no archive.
|
||||
|
||||
Partition OldPartition = GetPartition(NewPartition.Name);
|
||||
if (OldPartition == null)
|
||||
{
|
||||
// The partition entry in the xml is also not present in the current partition table.
|
||||
// It must have a known position and length.
|
||||
|
||||
if (NewPartition.LastSector == 0)
|
||||
{
|
||||
throw new WPinternalsException("Unknown length for partition \"" + NewPartition.Name + "\"");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// The partition entry in the xml is also present in the current partition table.
|
||||
// But since the partition is not present in the archive, the partition cannot be relocated.
|
||||
// If the location of the new partition is specified, it must be the same as the current partition.
|
||||
|
||||
if ((NewPartition.FirstSector != 0) && (NewPartition.FirstSector != OldPartition.FirstSector))
|
||||
throw new WPinternalsException("Incorrect location for partition \"" + NewPartition.Name + "\"");
|
||||
if ((NewPartition.LastSector != 0) && (NewPartition.LastSector != OldPartition.LastSector))
|
||||
throw new WPinternalsException("Incorrect length for partition \"" + NewPartition.Name + "\"");
|
||||
|
||||
NewPartition.FirstSector = OldPartition.FirstSector;
|
||||
NewPartition.LastSector = OldPartition.LastSector;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
List<Partition> DynamicPartitions = new List<Partition>();
|
||||
if (Archive != null)
|
||||
{
|
||||
// Partitions which are present in the archive, and which have no start-sector in the new GPT data (dynamic relocation),
|
||||
// and which can be clustered to the end of emmc, are first removed from the existing GPT.
|
||||
IEnumerable<Partition> SortedPartitions = Partitions.OrderBy(p => p.FirstSector);
|
||||
for (int i = SortedPartitions.Count() - 1; i >= 0; i--)
|
||||
{
|
||||
Partition OldPartition = SortedPartitions.ElementAt(i);
|
||||
|
||||
// Present in archive?
|
||||
ZipArchiveEntry Entry = Archive.Entries.Where(e => ((string.Compare(e.Name, OldPartition.Name, true) == 0) || (e.Name.StartsWith(OldPartition.Name + ".", true, System.Globalization.CultureInfo.GetCultureInfo("en-US"))))).FirstOrDefault();
|
||||
if (Entry != null)
|
||||
{
|
||||
// Not present in new GPT or present in GPT without FirstSector?
|
||||
Partition NewPartition = GptToMerge.GetPartition(OldPartition.Name);
|
||||
if ((NewPartition == null) || (NewPartition.FirstSector == 0))
|
||||
{
|
||||
DynamicPartitions.Insert(0, OldPartition);
|
||||
this.Partitions.Remove(OldPartition);
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// All partitions in the new GPT data should have a start-sector and end-sector by now.
|
||||
// The partitions in the new GPT data will be applied to the current partition-table.
|
||||
// Existing partitions, which are overwritten by the new partitions will be removed from the existing GPT.
|
||||
// Existing partition with the same name in the existing GPT is reused (guids and attribs remain, if not specified).
|
||||
UInt64 LowestSector = 0;
|
||||
Partition DPP = this.GetPartition("DPP");
|
||||
if (DPP != null)
|
||||
LowestSector = DPP.LastSector + 1;
|
||||
foreach (Partition NewPartition in GptToMerge.Partitions)
|
||||
{
|
||||
// If the new partition is a dynamic partition, then skip it here. It will be added later.
|
||||
if (DynamicPartitions.Select(p => p.Name).Any(n => string.Compare(n, NewPartition.Name, true) == 0))
|
||||
continue;
|
||||
|
||||
// Sanity check
|
||||
if (NewPartition.FirstSector < LowestSector)
|
||||
throw new WPinternalsException("Bad sector alignment for partition: " + NewPartition.Name);
|
||||
|
||||
Partition CurrentPartition = this.GetPartition(NewPartition.Name);
|
||||
if (CurrentPartition == null)
|
||||
{
|
||||
CurrentPartition = new Partition();
|
||||
CurrentPartition.Name = NewPartition.Name;
|
||||
this.Partitions.Add(CurrentPartition);
|
||||
HasChanged = true;
|
||||
}
|
||||
|
||||
if ((NewPartition.FirstSector != 0) && (NewPartition.FirstSector != CurrentPartition.FirstSector))
|
||||
{
|
||||
CurrentPartition.FirstSector = NewPartition.FirstSector;
|
||||
HasChanged = true;
|
||||
}
|
||||
if ((NewPartition.LastSector != 0) && (NewPartition.LastSector != CurrentPartition.LastSector))
|
||||
{
|
||||
CurrentPartition.LastSector = NewPartition.LastSector;
|
||||
HasChanged = true;
|
||||
}
|
||||
if ((NewPartition.Attributes != 0) && (CurrentPartition.Attributes != NewPartition.Attributes))
|
||||
{
|
||||
CurrentPartition.Attributes = NewPartition.Attributes;
|
||||
HasChanged = true;
|
||||
}
|
||||
|
||||
if ((NewPartition.PartitionGuid == null) || (NewPartition.PartitionGuid != CurrentPartition.PartitionGuid))
|
||||
HasChanged = true;
|
||||
if (NewPartition.PartitionGuid != null)
|
||||
CurrentPartition.PartitionGuid = NewPartition.PartitionGuid;
|
||||
if (CurrentPartition.PartitionGuid == null)
|
||||
CurrentPartition.PartitionGuid = Guid.NewGuid();
|
||||
|
||||
if ((NewPartition.PartitionTypeGuid == null) || (NewPartition.PartitionTypeGuid != CurrentPartition.PartitionTypeGuid))
|
||||
HasChanged = true;
|
||||
if (NewPartition.PartitionTypeGuid != null)
|
||||
CurrentPartition.PartitionTypeGuid = NewPartition.PartitionTypeGuid;
|
||||
if (CurrentPartition.PartitionTypeGuid == null)
|
||||
CurrentPartition.PartitionTypeGuid = Guid.NewGuid();
|
||||
|
||||
for (int i = this.Partitions.Count - 1; i >= 0; i--)
|
||||
{
|
||||
if (this.Partitions[i] != CurrentPartition)
|
||||
{
|
||||
if ((CurrentPartition.FirstSector <= this.Partitions[i].LastSector) && (CurrentPartition.LastSector >= this.Partitions[i].FirstSector))
|
||||
{
|
||||
this.Partitions.RemoveAt(i);
|
||||
HasChanged = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Archive != null)
|
||||
{
|
||||
// All partitions listed in the archive, which are present in the existing GPT, should overwrite the existing partition.
|
||||
// Check if the sizes of the partitions in the archive do not exceed the size of the partition, as listed in the GPT.
|
||||
foreach (Partition OldPartition in this.Partitions)
|
||||
{
|
||||
ZipArchiveEntry Entry = Archive.Entries.Where(e => ((string.Compare(e.Name, OldPartition.Name, true) == 0) || (e.Name.StartsWith(OldPartition.Name + ".", true, System.Globalization.CultureInfo.GetCultureInfo("en-US"))))).FirstOrDefault();
|
||||
if (Entry != null)
|
||||
{
|
||||
DecompressedStream DecompressedStream = new DecompressedStream(Entry.Open());
|
||||
ulong StreamLengthInSectors = (ulong)Entry.Length / 0x200;
|
||||
try
|
||||
{
|
||||
StreamLengthInSectors = (ulong)DecompressedStream.Length / 0x200;
|
||||
}
|
||||
catch { }
|
||||
DecompressedStream.Close();
|
||||
|
||||
UInt64 MaxPartitionSizeInSectors = OldPartition.SizeInSectors;
|
||||
Partition NextPartition = this.Partitions.Where(p => p.FirstSector > OldPartition.FirstSector).OrderBy(p => p.FirstSector).FirstOrDefault();
|
||||
if (NextPartition != null)
|
||||
MaxPartitionSizeInSectors = NextPartition.FirstSector - OldPartition.FirstSector;
|
||||
if (StreamLengthInSectors > MaxPartitionSizeInSectors)
|
||||
{
|
||||
throw new WPinternalsException("Incorrect length for partition \"" + OldPartition.Name + "\"");
|
||||
}
|
||||
|
||||
if (OldPartition.SizeInSectors != StreamLengthInSectors)
|
||||
{
|
||||
OldPartition.SizeInSectors = StreamLengthInSectors;
|
||||
HasChanged = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// All remaining partitions in the archive, which were listed in the original GPT,
|
||||
// should be added at the end of the partition-table.
|
||||
UInt64 FirstFreeSector = 0x5000;
|
||||
if (this.Partitions.Count > 0)
|
||||
{
|
||||
FirstFreeSector = this.Partitions.Max(p => p.LastSector) + 1;
|
||||
|
||||
// Always start a new partition on a new chunk (0x100 sector boundary), to be more flexible during custom flash
|
||||
if (RoundToChunks && (((double)FirstFreeSector % 0x100) != 0))
|
||||
FirstFreeSector = (UInt64)(Math.Ceiling((double)FirstFreeSector / 0x100) * 0x100);
|
||||
|
||||
}
|
||||
foreach (Partition NewPartition in DynamicPartitions)
|
||||
{
|
||||
if (NewPartition.FirstSector != FirstFreeSector)
|
||||
{
|
||||
NewPartition.FirstSector = FirstFreeSector;
|
||||
HasChanged = true;
|
||||
}
|
||||
ZipArchiveEntry Entry = Archive.Entries.Where(e => ((string.Compare(e.Name, NewPartition.Name, true) == 0) || (e.Name.StartsWith(NewPartition.Name + ".", true, System.Globalization.CultureInfo.GetCultureInfo("en-US"))))).FirstOrDefault();
|
||||
DecompressedStream DecompressedStream = new DecompressedStream(Entry.Open());
|
||||
ulong StreamLengthInSectors = (ulong)Entry.Length / 0x200;
|
||||
try
|
||||
{
|
||||
StreamLengthInSectors = (ulong)DecompressedStream.Length / 0x200;
|
||||
}
|
||||
catch { }
|
||||
DecompressedStream.Close();
|
||||
if (NewPartition.SizeInSectors != StreamLengthInSectors)
|
||||
{
|
||||
NewPartition.SizeInSectors = StreamLengthInSectors;
|
||||
HasChanged = true;
|
||||
}
|
||||
this.Partitions.Add(NewPartition);
|
||||
FirstFreeSector += StreamLengthInSectors;
|
||||
|
||||
// Always start a new partition on a new chunk (0x100 sector boundary), to be more flexible during custom flash
|
||||
if (RoundToChunks && (((double)FirstFreeSector % 0x100) != 0))
|
||||
FirstFreeSector = (UInt64)(Math.Ceiling((double)FirstFreeSector / 0x100) * 0x100);
|
||||
}
|
||||
}
|
||||
|
||||
Rebuild();
|
||||
}
|
||||
|
||||
internal void WritePartitions(string Path)
|
||||
{
|
||||
string DirPath = System.IO.Path.GetDirectoryName(Path);
|
||||
if (!Directory.Exists(DirPath))
|
||||
Directory.CreateDirectory(DirPath);
|
||||
|
||||
XmlSerializer x = new XmlSerializer(typeof(GPT), "");
|
||||
|
||||
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
|
||||
ns.Add("", "");
|
||||
System.IO.StreamWriter FileWriter = new System.IO.StreamWriter(Path);
|
||||
x.Serialize(FileWriter, this, ns);
|
||||
FileWriter.Close();
|
||||
}
|
||||
|
||||
internal static GPT ReadPartitions(string Path)
|
||||
{
|
||||
XmlSerializer x = new XmlSerializer(typeof(GPT), "");
|
||||
using (FileStream s = new FileStream(Path, FileMode.Open))
|
||||
{
|
||||
return (GPT)x.Deserialize(s);
|
||||
}
|
||||
}
|
||||
|
||||
internal void RestoreBackupPartitions()
|
||||
{
|
||||
// This is necessary, because the partitions and backup-partitions can exchange.
|
||||
// This may cause the startsector to be higher than the maximum allowed sector for flashing with a Lumia V1 programmer (hardcoded in programmer)
|
||||
List<string> RevisePartitions = new List<string>(new string[] { "SBL1", "SBL2", "SBL3", "UEFI", "TZ", "RPM", "WINSECAPP" });
|
||||
foreach (string RevisePartitionName in RevisePartitions)
|
||||
{
|
||||
Partition RevisePartition = GetPartition(RevisePartitionName);
|
||||
Partition ReviseBackupPartition = GetPartition("BACKUP_" + RevisePartitionName);
|
||||
if ((RevisePartition != null) && (ReviseBackupPartition != null) && (RevisePartition.FirstSector > ReviseBackupPartition.FirstSector))
|
||||
{
|
||||
ulong OriginalFirstSector = RevisePartition.FirstSector;
|
||||
ulong OriginalLastSector = RevisePartition.LastSector;
|
||||
RevisePartition.FirstSector = ReviseBackupPartition.FirstSector;
|
||||
RevisePartition.LastSector = ReviseBackupPartition.LastSector;
|
||||
ReviseBackupPartition.FirstSector = OriginalFirstSector;
|
||||
ReviseBackupPartition.LastSector = OriginalLastSector;
|
||||
|
||||
HasChanged = true;
|
||||
}
|
||||
|
||||
if (RevisePartition.LastSector >= 0xF400)
|
||||
throw new WPinternalsException("Unsupported partition layout!");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public class Partition
|
||||
{
|
||||
private UInt64 _SizeInSectors;
|
||||
private UInt64 _FirstSector;
|
||||
private UInt64 _LastSector;
|
||||
|
||||
public string Name; // 0x48
|
||||
public Guid PartitionTypeGuid; // 0x10
|
||||
public Guid PartitionGuid; // 0x10
|
||||
[XmlIgnore]
|
||||
internal UInt64 Attributes; // 0x08
|
||||
|
||||
[XmlIgnore]
|
||||
internal UInt64 SizeInSectors
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_SizeInSectors != 0)
|
||||
return _SizeInSectors;
|
||||
else
|
||||
return LastSector - FirstSector + 1;
|
||||
}
|
||||
set
|
||||
{
|
||||
_SizeInSectors = value;
|
||||
if (FirstSector != 0)
|
||||
LastSector = FirstSector + _SizeInSectors - 1;
|
||||
}
|
||||
}
|
||||
|
||||
[XmlIgnore]
|
||||
internal UInt64 FirstSector // 0x08
|
||||
{
|
||||
get
|
||||
{
|
||||
return _FirstSector;
|
||||
}
|
||||
set
|
||||
{
|
||||
_FirstSector = value;
|
||||
if (_SizeInSectors != 0)
|
||||
_LastSector = FirstSector + _SizeInSectors - 1;
|
||||
}
|
||||
}
|
||||
|
||||
[XmlIgnore]
|
||||
internal UInt64 LastSector // 0x08
|
||||
{
|
||||
get
|
||||
{
|
||||
return _LastSector;
|
||||
}
|
||||
set
|
||||
{
|
||||
_LastSector = value;
|
||||
_SizeInSectors = 0;
|
||||
}
|
||||
}
|
||||
|
||||
[XmlIgnore]
|
||||
public string Volume
|
||||
{
|
||||
get
|
||||
{
|
||||
return @"\\?\Volume" + PartitionGuid.ToString("b") + @"\";
|
||||
}
|
||||
}
|
||||
|
||||
[XmlElement(ElementName = "FirstSector")]
|
||||
public string FirstSectorAsString
|
||||
{
|
||||
get
|
||||
{
|
||||
return "0x" + FirstSector.ToString("X16");
|
||||
}
|
||||
set
|
||||
{
|
||||
FirstSector = Convert.ToUInt64(value, 16);
|
||||
}
|
||||
}
|
||||
|
||||
[XmlElement(ElementName = "LastSector")]
|
||||
public string LastSectorAsString
|
||||
{
|
||||
get
|
||||
{
|
||||
return "0x" + LastSector.ToString("X16");
|
||||
}
|
||||
set
|
||||
{
|
||||
LastSector = Convert.ToUInt64(value, 16);
|
||||
}
|
||||
}
|
||||
|
||||
[XmlElement(ElementName = "Attributes")]
|
||||
public string AttributesAsString
|
||||
{
|
||||
get
|
||||
{
|
||||
return "0x" + Attributes.ToString("X16");
|
||||
}
|
||||
set
|
||||
{
|
||||
Attributes = Convert.ToUInt64(value, 16);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+287
@@ -0,0 +1,287 @@
|
||||
// Copyright (c) 2018, Rene Lergner - wpinternals.net - @Heathcliff74xda
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
// SevenZip LZMA SDK: http://www.7-zip.org/download.html
|
||||
// Usage: http://stackoverflow.com/questions/7646328/how-to-use-the-7z-sdk-to-compress-and-decompress-a-file
|
||||
|
||||
using SevenZip;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
|
||||
namespace WPinternals
|
||||
{
|
||||
internal static class LZMA
|
||||
{
|
||||
internal static byte[] Decompress(byte[] Input, UInt32 Offset, UInt32 InputSize)
|
||||
{
|
||||
byte[] Properties = new byte[5];
|
||||
Buffer.BlockCopy(Input, (int)Offset, Properties, 0, 5);
|
||||
|
||||
UInt64 OutputSize = ByteOperations.ReadUInt64(Input, Offset + 5);
|
||||
|
||||
SevenZip.Compression.LZMA.Decoder Coder = new SevenZip.Compression.LZMA.Decoder();
|
||||
Coder.SetDecoderProperties(Properties);
|
||||
|
||||
MemoryStream InStream = new MemoryStream(Input, (int)Offset + 0x0D, (int)InputSize - 0x0D);
|
||||
|
||||
byte[] Output = new byte[OutputSize];
|
||||
MemoryStream OutStream = new MemoryStream(Output, true);
|
||||
|
||||
Coder.Code(InStream, OutStream, (Int64)InputSize - 0x0D, (Int64)OutputSize, null);
|
||||
|
||||
OutStream.Flush();
|
||||
OutStream.Close();
|
||||
InStream.Close();
|
||||
|
||||
return Output;
|
||||
}
|
||||
|
||||
internal static byte[] Compress(byte[] Input, UInt32 Offset, UInt32 InputSize)
|
||||
{
|
||||
SevenZip.Compression.LZMA.Encoder Coder = new SevenZip.Compression.LZMA.Encoder();
|
||||
|
||||
MemoryStream InStream = new MemoryStream(Input, (int)Offset, (int)InputSize);
|
||||
MemoryStream OutStream = new MemoryStream();
|
||||
|
||||
// Write the encoder properties
|
||||
Coder.WriteCoderProperties(OutStream);
|
||||
|
||||
// Write the decompressed file size
|
||||
OutStream.Write(BitConverter.GetBytes(InStream.Length), 0, 8);
|
||||
|
||||
// Encode the file
|
||||
Coder.Code(InStream, OutStream, (Int64)InputSize, -1, null);
|
||||
|
||||
byte[] Output = new byte[OutStream.Length];
|
||||
Buffer.BlockCopy(OutStream.GetBuffer(), 0, Output, 0, (int)OutStream.Length);
|
||||
|
||||
OutStream.Flush();
|
||||
OutStream.Close();
|
||||
InStream.Close();
|
||||
|
||||
return Output;
|
||||
}
|
||||
}
|
||||
|
||||
public class LZMACompressionStream : Stream
|
||||
{
|
||||
private SevenZip.Compression.LZMA.Encoder Encoder = null;
|
||||
private SevenZip.Compression.LZMA.Decoder Decoder = null;
|
||||
private PumpStream BufferStream;
|
||||
private Stream stream;
|
||||
private bool LeaveOpen;
|
||||
private Thread WorkThread;
|
||||
|
||||
public LZMACompressionStream(Stream stream, CompressionMode mode, bool LeaveOpen, int DictionarySize, int PosStateBits,
|
||||
int LitContextBits, int LitPosBits, int Algorithm, int NumFastBytes, string MatchFinder, bool EndMarker)
|
||||
{
|
||||
this.stream = stream;
|
||||
this.LeaveOpen = LeaveOpen;
|
||||
BufferStream = new PumpStream();
|
||||
|
||||
if (mode == CompressionMode.Compress)
|
||||
{
|
||||
Encoder = new SevenZip.Compression.LZMA.Encoder();
|
||||
if (DictionarySize != 0)
|
||||
Encoder.SetCoderProperties(
|
||||
new SevenZip.CoderPropID[8] {CoderPropID.DictionarySize, CoderPropID.PosStateBits, CoderPropID.LitContextBits,
|
||||
CoderPropID.LitPosBits, CoderPropID.Algorithm, CoderPropID.NumFastBytes, CoderPropID.MatchFinder, CoderPropID.EndMarker},
|
||||
new object[8] { DictionarySize, PosStateBits, LitContextBits, LitPosBits, Algorithm, NumFastBytes, MatchFinder, EndMarker });
|
||||
Encoder.WriteCoderProperties(stream);
|
||||
WorkThread = new Thread(new ThreadStart(Encode));
|
||||
}
|
||||
else
|
||||
{
|
||||
byte[] DecoderProperties = new byte[5];
|
||||
stream.Read(DecoderProperties, 0, 5);
|
||||
Decoder = new SevenZip.Compression.LZMA.Decoder();
|
||||
Decoder.SetDecoderProperties(DecoderProperties);
|
||||
WorkThread = new Thread(new ThreadStart(Decode));
|
||||
}
|
||||
|
||||
WorkThread.Start();
|
||||
}
|
||||
|
||||
public LZMACompressionStream(Stream stream, CompressionMode mode, bool LeaveOpen)
|
||||
: this(stream, mode, LeaveOpen, 0, 0, 0, 0, 0, 0, null, false)
|
||||
{
|
||||
}
|
||||
|
||||
private void Encode()
|
||||
{
|
||||
Encoder.Code(BufferStream, stream, -1, -1, null);
|
||||
if (LeaveOpen == false)
|
||||
stream.Close();
|
||||
}
|
||||
|
||||
private void Decode()
|
||||
{
|
||||
Decoder.Code(stream, BufferStream, -1, -1, null);
|
||||
BufferStream.Close();
|
||||
if (LeaveOpen == false)
|
||||
stream.Close();
|
||||
}
|
||||
|
||||
public override void Close()
|
||||
{
|
||||
if (Encoder != null)
|
||||
BufferStream.Close();
|
||||
else if (WorkThread.IsAlive)
|
||||
WorkThread.Abort();
|
||||
}
|
||||
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
return BufferStream.Read(buffer, offset, count);
|
||||
}
|
||||
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
BufferStream.Write(buffer, offset, count);
|
||||
}
|
||||
|
||||
public override bool CanRead { get { return (Decoder != null); } }
|
||||
public override bool CanSeek { get { return false; } }
|
||||
public override bool CanWrite { get { return (Encoder != null); } }
|
||||
public override void Flush() { }
|
||||
public override long Length { get { return 0; } }
|
||||
public override long Position { get { return 0; } set { } }
|
||||
public override long Seek(long offset, SeekOrigin origin) { return 0; }
|
||||
public override void SetLength(long value) { }
|
||||
}
|
||||
|
||||
public class PumpStream : Stream
|
||||
{
|
||||
private Queue<byte[]> BufferQueue;
|
||||
private int BufferOffset;
|
||||
private long MaxBufferSize;
|
||||
private long BufferSize;
|
||||
private bool Closed;
|
||||
private bool EOF;
|
||||
|
||||
public PumpStream(long MaxBufferSize, int ReadTimeout, int WriteTimeout)
|
||||
{
|
||||
this.MaxBufferSize = MaxBufferSize;
|
||||
this.ReadTimeout = ReadTimeout;
|
||||
this.WriteTimeout = WriteTimeout;
|
||||
BufferQueue = new Queue<byte[]>();
|
||||
BufferOffset = 0;
|
||||
BufferSize = 0;
|
||||
Closed = false;
|
||||
EOF = false;
|
||||
}
|
||||
|
||||
public PumpStream()
|
||||
: this(16777216, Timeout.Infinite, Timeout.Infinite)
|
||||
{
|
||||
}
|
||||
|
||||
public new void Dispose()
|
||||
{
|
||||
BufferQueue.Clear();
|
||||
}
|
||||
|
||||
public override void Close()
|
||||
{
|
||||
Closed = true;
|
||||
lock (BufferQueue)
|
||||
Monitor.Pulse(BufferQueue);
|
||||
}
|
||||
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
int BytesRead = 0;
|
||||
lock (BufferQueue)
|
||||
{
|
||||
while (BytesRead < count && EOF == false)
|
||||
{
|
||||
if (BufferQueue.Count > 0)
|
||||
{
|
||||
byte[] b = BufferQueue.Peek();
|
||||
|
||||
if ((b.Length - BufferOffset) <= (count - BytesRead))
|
||||
{
|
||||
Array.Copy(b, BufferOffset, buffer, offset + BytesRead, (b.Length - BufferOffset));
|
||||
|
||||
BufferQueue.Dequeue();
|
||||
BufferSize -= b.Length;
|
||||
Monitor.Pulse(BufferQueue);
|
||||
|
||||
BytesRead += (b.Length - BufferOffset);
|
||||
BufferOffset = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
Array.Copy(b, BufferOffset, buffer, offset + BytesRead, (count - BytesRead));
|
||||
|
||||
BufferOffset += (count - BytesRead);
|
||||
BytesRead += (count - BytesRead);
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Closed == false)
|
||||
{
|
||||
if (Monitor.Wait(BufferQueue, ReadTimeout) == false)
|
||||
throw new IOException("Could not read from stream: Timeout expired waiting for data to be written.");
|
||||
}
|
||||
else
|
||||
EOF = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return BytesRead;
|
||||
}
|
||||
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
lock (BufferQueue)
|
||||
{
|
||||
while (BufferSize >= MaxBufferSize)
|
||||
if (Monitor.Wait(BufferQueue, WriteTimeout) == false)
|
||||
throw new IOException("Could not write to stream: Timeout expired waiting for data to be read.");
|
||||
|
||||
byte[] b = new byte[count];
|
||||
Array.Copy(buffer, offset, b, 0, count);
|
||||
BufferQueue.Enqueue(b);
|
||||
BufferSize += b.Length;
|
||||
|
||||
Monitor.Pulse(BufferQueue);
|
||||
}
|
||||
}
|
||||
|
||||
public override int ReadTimeout { get; set; }
|
||||
public override int WriteTimeout { get; set; }
|
||||
public override bool CanRead { get { return true; } }
|
||||
public override bool CanSeek { get { return false; } }
|
||||
public override bool CanWrite { get { return true; } }
|
||||
public override void Flush() { }
|
||||
public override long Length { get { return 0; } }
|
||||
public override long Position { get { return 0; } set { } }
|
||||
public override long Seek(long offset, SeekOrigin origin) { return 0; }
|
||||
public override void SetLength(long value) { }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,478 @@
|
||||
// Copyright (c) 2018, Rene Lergner - wpinternals.net - @Heathcliff74xda
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Runtime.Serialization.Json;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Headers;
|
||||
using System.IO;
|
||||
using System.Xml;
|
||||
|
||||
namespace WPinternals
|
||||
{
|
||||
internal static class LumiaDownloadModel
|
||||
{
|
||||
internal static string SearchFFU(string ProductType, string ProductCode, string OperatorCode)
|
||||
{
|
||||
string FoundProductType;
|
||||
return SearchFFU(ProductType, ProductCode, OperatorCode, out FoundProductType);
|
||||
}
|
||||
|
||||
internal static string SearchFFU(string ProductType, string ProductCode, string OperatorCode, out string FoundProductType)
|
||||
{
|
||||
if (ProductType == "")
|
||||
ProductType = null;
|
||||
if (ProductCode == "")
|
||||
ProductCode = null;
|
||||
if (OperatorCode == "")
|
||||
OperatorCode = null;
|
||||
|
||||
if (ProductCode != null)
|
||||
{
|
||||
ProductCode = ProductCode.ToUpper();
|
||||
ProductType = null;
|
||||
OperatorCode = null;
|
||||
}
|
||||
if (ProductType != null)
|
||||
{
|
||||
ProductType = ProductType.ToUpper();
|
||||
if (ProductType.StartsWith("RM") && !ProductType.StartsWith("RM-"))
|
||||
ProductType = "RM-" + ProductType.Substring(2);
|
||||
}
|
||||
if (OperatorCode != null)
|
||||
OperatorCode = OperatorCode.ToUpper();
|
||||
|
||||
DiscoveryQueryParameters DiscoveryQueryParams = new DiscoveryQueryParameters
|
||||
{
|
||||
manufacturerName = "Microsoft",
|
||||
manufacturerProductLine = "Lumia",
|
||||
packageType = "Firmware",
|
||||
packageClass = "Public",
|
||||
manufacturerHardwareModel = ProductType,
|
||||
manufacturerHardwareVariant = ProductCode,
|
||||
operatorName = OperatorCode
|
||||
};
|
||||
DiscoveryParameters DiscoveryParams = new DiscoveryParameters
|
||||
{
|
||||
query = DiscoveryQueryParams
|
||||
};
|
||||
|
||||
DataContractJsonSerializer Serializer1 = new DataContractJsonSerializer(typeof(DiscoveryParameters));
|
||||
MemoryStream JsonStream1 = new MemoryStream();
|
||||
Serializer1.WriteObject(JsonStream1, DiscoveryParams);
|
||||
JsonStream1.Seek(0L, SeekOrigin.Begin);
|
||||
string JsonContent = new StreamReader(JsonStream1).ReadToEnd();
|
||||
|
||||
Uri RequestUri = new Uri("https://api.swrepository.com/rest-api/discovery/1/package");
|
||||
|
||||
HttpClient HttpClient = new HttpClient();
|
||||
HttpClient.DefaultRequestHeaders.UserAgent.TryParseAdd("SoftwareRepository");
|
||||
HttpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
|
||||
|
||||
Task<HttpResponseMessage> HttpPostTask = HttpClient.PostAsync(RequestUri, new StringContent(JsonContent, Encoding.UTF8, "application/json"));
|
||||
HttpPostTask.Wait();
|
||||
HttpResponseMessage Response = HttpPostTask.Result;
|
||||
|
||||
string JsonResultString = "";
|
||||
if (Response.StatusCode == HttpStatusCode.OK)
|
||||
{
|
||||
Task<string> ReadResponseTask = Response.Content.ReadAsStringAsync();
|
||||
ReadResponseTask.Wait();
|
||||
JsonResultString = ReadResponseTask.Result;
|
||||
}
|
||||
|
||||
SoftwarePackage Package = null;
|
||||
using (MemoryStream JsonStream2 = new MemoryStream(Encoding.UTF8.GetBytes(JsonResultString)))
|
||||
{
|
||||
DataContractJsonSerializer Serializer2 = new DataContractJsonSerializer(typeof(SoftwarePackages));
|
||||
SoftwarePackages SoftwarePackages = (SoftwarePackages)Serializer2.ReadObject(JsonStream2);
|
||||
if (SoftwarePackages != null)
|
||||
{
|
||||
Package = SoftwarePackages.softwarePackages.FirstOrDefault<SoftwarePackage>();
|
||||
}
|
||||
}
|
||||
|
||||
if (Package == null)
|
||||
throw new WPinternalsException("FFU not found");
|
||||
|
||||
FoundProductType = Package.manufacturerHardwareModel[0];
|
||||
|
||||
SoftwareFile FileInfo = Package.files.Where(f => f.fileName.EndsWith(".ffu", StringComparison.OrdinalIgnoreCase)).First();
|
||||
|
||||
Uri FileInfoUri = new Uri("https://api.swrepository.com/rest-api/discovery/fileurl/1/" + Package.id + "/" + FileInfo.fileName);
|
||||
Task<string> GetFileInfoTask = HttpClient.GetStringAsync(FileInfoUri);
|
||||
GetFileInfoTask.Wait();
|
||||
string FileInfoString = GetFileInfoTask.Result;
|
||||
|
||||
string FfuUrl = "";
|
||||
FileUrlResult FileUrl = null;
|
||||
using (MemoryStream JsonStream3 = new MemoryStream(Encoding.UTF8.GetBytes(FileInfoString)))
|
||||
{
|
||||
DataContractJsonSerializer Serializer3 = new DataContractJsonSerializer(typeof(FileUrlResult));
|
||||
FileUrl = (FileUrlResult)Serializer3.ReadObject(JsonStream3);
|
||||
if (FileUrl != null)
|
||||
{
|
||||
FfuUrl = FileUrl.url;
|
||||
}
|
||||
}
|
||||
|
||||
HttpClient.Dispose();
|
||||
|
||||
return FfuUrl;
|
||||
}
|
||||
|
||||
internal static string[] SearchEmergencyFiles(string ProductType)
|
||||
{
|
||||
ProductType = ProductType.ToUpper();
|
||||
if (ProductType.StartsWith("RM") && !ProductType.StartsWith("RM-"))
|
||||
ProductType = "RM-" + ProductType.Substring(2);
|
||||
|
||||
LogFile.Log("Getting Emergency files for: " + ProductType, LogType.FileAndConsole);
|
||||
|
||||
if ((ProductType == "RM-1072") || (ProductType == "RM-1073"))
|
||||
{
|
||||
LogFile.Log("Due to mix-up in online-repository, redirecting to emergency files of RM-1113", LogType.FileAndConsole);
|
||||
ProductType = "RM-1113";
|
||||
}
|
||||
|
||||
List<string> Result = new List<string>();
|
||||
|
||||
WebClient Client = new WebClient();
|
||||
string Src;
|
||||
string FileName;
|
||||
string Config = null;
|
||||
try
|
||||
{
|
||||
Config = Client.DownloadString(@"https://repairavoidance.blob.core.windows.net/packages/EmergencyFlash/" + ProductType + "/emergency_flash_config.xml");
|
||||
}
|
||||
catch
|
||||
{
|
||||
LogFile.Log("Emergency files for " + ProductType + " not found", LogType.FileAndConsole);
|
||||
return null;
|
||||
}
|
||||
Client.Dispose();
|
||||
|
||||
XmlDocument Doc = new XmlDocument();
|
||||
Doc.LoadXml(Config);
|
||||
|
||||
// Hex
|
||||
XmlNode Node = Doc.SelectSingleNode("//emergency_flash_config/hex_flasher");
|
||||
if (Node != null)
|
||||
{
|
||||
FileName = Node.Attributes["image_path"].InnerText;
|
||||
Src = @"https://repairavoidance.blob.core.windows.net/packages/EmergencyFlash/" + ProductType + "/" + FileName;
|
||||
LogFile.Log("Hex-file: " + Src);
|
||||
Result.Add(Src);
|
||||
}
|
||||
|
||||
// Mbn
|
||||
Node = Doc.SelectSingleNode("//emergency_flash_config/mbn_image");
|
||||
if (Node != null)
|
||||
{
|
||||
FileName = Node.Attributes["image_path"].InnerText;
|
||||
Src = @"https://repairavoidance.blob.core.windows.net/packages/EmergencyFlash/" + ProductType + "/" + FileName;
|
||||
LogFile.Log("Mbn-file: " + Src);
|
||||
Result.Add(Src);
|
||||
}
|
||||
|
||||
// Ede
|
||||
foreach (XmlNode SubNode in Doc.SelectNodes("//emergency_flash_config/first_boot_images/first_boot_image"))
|
||||
{
|
||||
FileName = SubNode.Attributes["image_path"].InnerText;
|
||||
Src = @"https://repairavoidance.blob.core.windows.net/packages/EmergencyFlash/" + ProductType + "/" + FileName;
|
||||
LogFile.Log("Firehose-programmer-file: " + Src);
|
||||
Result.Add(Src);
|
||||
}
|
||||
|
||||
// Edp
|
||||
foreach (XmlNode SubNode in Doc.SelectNodes("//emergency_flash_config/second_boot_firehose_single_image/firehose_image"))
|
||||
{
|
||||
FileName = SubNode.Attributes["image_path"].InnerText;
|
||||
Src = @"https://repairavoidance.blob.core.windows.net/packages/EmergencyFlash/" + ProductType + "/" + FileName;
|
||||
LogFile.Log("Firehose-payload-file: " + Src);
|
||||
Result.Add(Src);
|
||||
}
|
||||
|
||||
return Result.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
#pragma warning disable 0649
|
||||
[DataContract]
|
||||
internal class FileUrlResult
|
||||
{
|
||||
[DataMember]
|
||||
internal string url;
|
||||
|
||||
[DataMember]
|
||||
internal List<string> alternateUrl;
|
||||
|
||||
[DataMember]
|
||||
internal long fileSize;
|
||||
|
||||
[DataMember]
|
||||
internal List<SoftwareFileChecksum> checksum;
|
||||
}
|
||||
#pragma warning restore 0649
|
||||
|
||||
[DataContract]
|
||||
public class DiscoveryQueryParameters
|
||||
{
|
||||
[DataMember(EmitDefaultValue = false)]
|
||||
public string customerName;
|
||||
|
||||
[DataMember(EmitDefaultValue = false)]
|
||||
public ExtendedAttributes extendedAttributes;
|
||||
|
||||
[DataMember(EmitDefaultValue = false)]
|
||||
public string manufacturerHardwareModel;
|
||||
|
||||
[DataMember(EmitDefaultValue = false)]
|
||||
public string manufacturerHardwareVariant;
|
||||
|
||||
[DataMember(EmitDefaultValue = false)]
|
||||
public string manufacturerModelName;
|
||||
|
||||
[DataMember]
|
||||
public string manufacturerName;
|
||||
|
||||
[DataMember(EmitDefaultValue = false)]
|
||||
public string manufacturerPackageId;
|
||||
|
||||
[DataMember(EmitDefaultValue = false)]
|
||||
public string manufacturerPlatformId;
|
||||
|
||||
[DataMember]
|
||||
public string manufacturerProductLine;
|
||||
|
||||
[DataMember(EmitDefaultValue = false)]
|
||||
public string manufacturerVariantName;
|
||||
|
||||
[DataMember(EmitDefaultValue = false)]
|
||||
public string operatorName;
|
||||
|
||||
[DataMember]
|
||||
public string packageClass;
|
||||
|
||||
[DataMember(EmitDefaultValue = false)]
|
||||
public string packageRevision;
|
||||
|
||||
[DataMember(EmitDefaultValue = false)]
|
||||
public string packageState;
|
||||
|
||||
[DataMember(EmitDefaultValue = false)]
|
||||
public string packageSubRevision;
|
||||
|
||||
[DataMember(EmitDefaultValue = false)]
|
||||
public string packageSubtitle;
|
||||
|
||||
[DataMember(EmitDefaultValue = false)]
|
||||
public string packageTitle;
|
||||
|
||||
[DataMember]
|
||||
public string packageType;
|
||||
}
|
||||
|
||||
[DataContract]
|
||||
public class DiscoveryParameters
|
||||
{
|
||||
[DataMember(Name = "api-version")]
|
||||
public string apiVersion;
|
||||
|
||||
[DataMember]
|
||||
public DiscoveryQueryParameters query;
|
||||
|
||||
[DataMember]
|
||||
public List<string> condition;
|
||||
|
||||
[DataMember]
|
||||
public List<string> response;
|
||||
|
||||
public DiscoveryParameters(): this(DiscoveryCondition.Default)
|
||||
{
|
||||
}
|
||||
|
||||
public DiscoveryParameters(DiscoveryCondition Condition)
|
||||
{
|
||||
this.apiVersion = "1";
|
||||
this.query = new DiscoveryQueryParameters();
|
||||
this.condition = new List<string>();
|
||||
if (Condition == DiscoveryCondition.All)
|
||||
{
|
||||
this.condition.Add("all");
|
||||
return;
|
||||
}
|
||||
if (Condition == DiscoveryCondition.Latest)
|
||||
{
|
||||
this.condition.Add("latest");
|
||||
return;
|
||||
}
|
||||
this.condition.Add("default");
|
||||
}
|
||||
}
|
||||
|
||||
public enum DiscoveryCondition
|
||||
{
|
||||
Default,
|
||||
All,
|
||||
Latest
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class ExtendedAttributes : ISerializable
|
||||
{
|
||||
public Dictionary<string, string> Dictionary;
|
||||
|
||||
public ExtendedAttributes()
|
||||
{
|
||||
this.Dictionary = new Dictionary<string, string>();
|
||||
}
|
||||
|
||||
protected ExtendedAttributes(SerializationInfo info, StreamingContext context)
|
||||
{
|
||||
if (info != null)
|
||||
{
|
||||
this.Dictionary = new Dictionary<string, string>();
|
||||
SerializationInfoEnumerator Enumerator = info.GetEnumerator();
|
||||
while (Enumerator.MoveNext())
|
||||
{
|
||||
this.Dictionary.Add(Enumerator.Current.Name, (string)Enumerator.Current.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void GetObjectData(SerializationInfo info, StreamingContext context)
|
||||
{
|
||||
if (info != null)
|
||||
{
|
||||
foreach (string Current in this.Dictionary.Keys)
|
||||
{
|
||||
info.AddValue(Current, this.Dictionary[Current]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[DataContract]
|
||||
public class SoftwareFileChecksum
|
||||
{
|
||||
[DataMember]
|
||||
public string type;
|
||||
|
||||
[DataMember]
|
||||
public string value;
|
||||
}
|
||||
|
||||
[DataContract]
|
||||
public class SoftwarePackages
|
||||
{
|
||||
[DataMember]
|
||||
public List<SoftwarePackage> softwarePackages;
|
||||
}
|
||||
|
||||
[DataContract]
|
||||
public class SoftwarePackage
|
||||
{
|
||||
[DataMember]
|
||||
public List<string> customerName;
|
||||
|
||||
[DataMember]
|
||||
public ExtendedAttributes extendedAttributes;
|
||||
|
||||
[DataMember]
|
||||
public List<SoftwareFile> files;
|
||||
|
||||
[DataMember]
|
||||
public string id;
|
||||
|
||||
[DataMember]
|
||||
public List<string> manufacturerHardwareModel;
|
||||
|
||||
[DataMember]
|
||||
public List<string> manufacturerHardwareVariant;
|
||||
|
||||
[DataMember]
|
||||
public List<string> manufacturerModelName;
|
||||
|
||||
[DataMember]
|
||||
public string manufacturerName;
|
||||
|
||||
[DataMember]
|
||||
public string manufacturerPackageId;
|
||||
|
||||
[DataMember]
|
||||
public List<string> manufacturerPlatformId;
|
||||
|
||||
[DataMember]
|
||||
public string manufacturerProductLine;
|
||||
|
||||
[DataMember]
|
||||
public List<string> manufacturerVariantName;
|
||||
|
||||
[DataMember]
|
||||
public List<string> operatorName;
|
||||
|
||||
[DataMember]
|
||||
public List<string> packageClass;
|
||||
|
||||
[DataMember]
|
||||
public string packageDescription;
|
||||
|
||||
[DataMember]
|
||||
public string packageRevision;
|
||||
|
||||
[DataMember]
|
||||
public string packageState;
|
||||
|
||||
[DataMember]
|
||||
public string packageSubRevision;
|
||||
|
||||
[DataMember]
|
||||
public string packageSubtitle;
|
||||
|
||||
[DataMember]
|
||||
public string packageTitle;
|
||||
|
||||
[DataMember]
|
||||
public string packageType;
|
||||
}
|
||||
|
||||
[DataContract]
|
||||
public class SoftwareFile
|
||||
{
|
||||
[DataMember]
|
||||
public List<SoftwareFileChecksum> checksum;
|
||||
|
||||
[DataMember]
|
||||
public string fileName;
|
||||
|
||||
[DataMember]
|
||||
public long fileSize;
|
||||
|
||||
[DataMember]
|
||||
public string fileType;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,461 @@
|
||||
// Copyright (c) 2018, Rene Lergner - wpinternals.net - @Heathcliff74xda
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Management;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace WPinternals
|
||||
{
|
||||
internal class MassStorage: NokiaPhoneModel
|
||||
{
|
||||
internal string Drive = null;
|
||||
internal string PhysicalDrive = null;
|
||||
internal string VolumeLabel = null;
|
||||
internal IntPtr hVolume = (IntPtr)(-1);
|
||||
internal IntPtr hDrive = (IntPtr)(-1);
|
||||
private bool OpenWithWriteAccess;
|
||||
|
||||
internal MassStorage(string DevicePath): base(DevicePath)
|
||||
{
|
||||
try
|
||||
{
|
||||
ManagementObjectCollection coll = new ManagementObjectSearcher("select * from Win32_LogicalDisk").Get();
|
||||
foreach (ManagementObject logical in coll)
|
||||
{
|
||||
System.Diagnostics.Debug.Print(logical["Name"].ToString());
|
||||
|
||||
string Label = "";
|
||||
foreach (ManagementObject partition in logical.GetRelated("Win32_DiskPartition"))
|
||||
{
|
||||
foreach (ManagementObject drive in partition.GetRelated("Win32_DiskDrive"))
|
||||
{
|
||||
if ((drive["PNPDeviceID"].ToString().IndexOf("VEN_QUALCOMM&PROD_MMC_STORAGE") >= 0) ||
|
||||
(drive["PNPDeviceID"].ToString().IndexOf("VEN_MSFT&PROD_PHONE_MMC_STOR") >= 0))
|
||||
{
|
||||
Label = (logical["VolumeName"] == null ? "" : logical["VolumeName"].ToString());
|
||||
if ((Drive == null) || (string.Compare(Label, "MainOS", true) == 0)) // Always prefer the MainOS drive-mapping
|
||||
{
|
||||
Drive = logical["Name"].ToString();
|
||||
PhysicalDrive = drive["DeviceID"].ToString();
|
||||
VolumeLabel = Label;
|
||||
}
|
||||
if (string.Compare(Label, "MainOS", true) == 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (string.Compare(Label, "MainOS", true) == 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (Disposed)
|
||||
return;
|
||||
|
||||
if (disposing)
|
||||
{
|
||||
CloseVolume();
|
||||
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
}
|
||||
|
||||
internal void OpenVolume(bool WriteAccess)
|
||||
{
|
||||
OpenWithWriteAccess = false;
|
||||
|
||||
if (IsVolumeOpen())
|
||||
throw new Exception("Volume already opened");
|
||||
|
||||
if (WriteAccess)
|
||||
{
|
||||
// Unmounting the volume does not have the desired effect.
|
||||
// It does not unmount the mountpoints on the phone.
|
||||
// So the sectors of the filesystems of EFIESP, Data, etc cannot be written.
|
||||
// Unmounting the mounting points would alter the NTFS structure, which is also an undesired effect.
|
||||
// Restoring partitions with file-systems can better be done using Flash mode!
|
||||
|
||||
// Open volume
|
||||
hVolume = NativeMethods.CreateFile(
|
||||
PhysicalDrive,
|
||||
NativeMethods.GENERIC_READ | NativeMethods.GENERIC_WRITE,
|
||||
NativeMethods.FILE_SHARE_READ | NativeMethods.FILE_SHARE_WRITE,
|
||||
IntPtr.Zero,
|
||||
NativeMethods.OPEN_EXISTING,
|
||||
NativeMethods.FILE_FLAG_WRITE_THROUGH | NativeMethods.FILE_FLAG_NO_BUFFERING, // !!!
|
||||
IntPtr.Zero);
|
||||
}
|
||||
else
|
||||
{
|
||||
hVolume = NativeMethods.CreateFile(
|
||||
// @"\\.\" + Drive,
|
||||
PhysicalDrive,
|
||||
NativeMethods.GENERIC_READ,
|
||||
NativeMethods.FILE_SHARE_READ | NativeMethods.FILE_SHARE_WRITE,
|
||||
IntPtr.Zero,
|
||||
NativeMethods.OPEN_EXISTING,
|
||||
0,
|
||||
IntPtr.Zero);
|
||||
}
|
||||
|
||||
if ((int)hVolume == -1)
|
||||
throw new Exception(Marshal.GetLastWin32Error().ToString());
|
||||
|
||||
OpenWithWriteAccess = WriteAccess;
|
||||
}
|
||||
|
||||
internal bool IsVolumeOpen()
|
||||
{
|
||||
return (int)(hVolume) != -1;
|
||||
}
|
||||
|
||||
internal void CloseVolume()
|
||||
{
|
||||
if ((int)hDrive != -1)
|
||||
{
|
||||
NativeMethods.CloseHandle(hDrive); // This reloads the logical drive!!
|
||||
hDrive = new IntPtr(-1);
|
||||
}
|
||||
|
||||
if ((int)hVolume != -1)
|
||||
{
|
||||
NativeMethods.CloseHandle(hVolume);
|
||||
hVolume = new IntPtr(-1);
|
||||
}
|
||||
}
|
||||
|
||||
internal void SetSectorPosition(UInt64 Sector)
|
||||
{
|
||||
if (!IsVolumeOpen())
|
||||
throw new Exception("Volume is not opened");
|
||||
|
||||
int High = (int)(Sector >> (32 - 9));
|
||||
NativeMethods.SetFilePointer(hVolume, (int)(Sector << 9), ref High, EMoveMethod.Begin);
|
||||
}
|
||||
|
||||
internal void WriteSector(byte[] buffer)
|
||||
{
|
||||
if (!IsVolumeOpen())
|
||||
throw new Exception("Volume is not opened");
|
||||
|
||||
if (!OpenWithWriteAccess)
|
||||
throw new Exception("Volume is not opened with Write Acces");
|
||||
|
||||
uint count = 0;
|
||||
bool result = NativeMethods.WriteFile(hVolume, buffer, 512, out count, IntPtr.Zero);
|
||||
if (!result)
|
||||
throw new Exception("Exception: 0x" + Marshal.GetLastWin32Error().ToString("X8"));
|
||||
}
|
||||
|
||||
internal void ReadSector(byte[] buffer)
|
||||
{
|
||||
if (!IsVolumeOpen())
|
||||
throw new Exception("Volume is not opened");
|
||||
|
||||
uint count = 1;
|
||||
bool result = NativeMethods.ReadFile(hVolume, buffer, 512, out count, IntPtr.Zero);
|
||||
if (!result)
|
||||
throw new Exception("Exception: 0x" + Marshal.GetLastWin32Error().ToString("X8"));
|
||||
}
|
||||
|
||||
internal void ReadSectors(byte[] buffer, out uint ActualSectorsRead, uint SizeInSectors = uint.MaxValue)
|
||||
{
|
||||
if (!IsVolumeOpen())
|
||||
throw new Exception("Volume is not opened");
|
||||
|
||||
uint count = 1;
|
||||
bool Result = NativeMethods.ReadFile(hVolume, buffer, (SizeInSectors * 0x200) < buffer.Length ? (SizeInSectors * 0x200) : (uint)buffer.Length, out count, IntPtr.Zero);
|
||||
ActualSectorsRead = count / 0x200;
|
||||
if (!Result)
|
||||
throw new Exception("Failed to read sectors. Exception: 0x" + Marshal.GetLastWin32Error().ToString("X8"));
|
||||
}
|
||||
|
||||
internal byte[] ReadSectors(UInt64 StartSector, UInt64 SectorCount)
|
||||
{
|
||||
byte[] Result = new byte[SectorCount * 0x200];
|
||||
|
||||
bool VolumeWasOpen = IsVolumeOpen();
|
||||
if (!VolumeWasOpen)
|
||||
OpenVolume(false);
|
||||
|
||||
SetSectorPosition(StartSector);
|
||||
uint SectorsRead;
|
||||
ReadSectors(Result, out SectorsRead);
|
||||
|
||||
if (!VolumeWasOpen)
|
||||
CloseVolume();
|
||||
|
||||
if (Result == null)
|
||||
throw new Exception("Failed to read from phone");
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal void DumpSectors(UInt64 StartSector, UInt64 SectorCount, string Path)
|
||||
{
|
||||
DumpSectors(StartSector, SectorCount, Path, null, null, null);
|
||||
}
|
||||
|
||||
internal void DumpSectors(UInt64 StartSector, UInt64 SectorCount, string Path, Action<int, TimeSpan?> ProgressUpdateCallback)
|
||||
{
|
||||
DumpSectors(StartSector, SectorCount, Path, null, ProgressUpdateCallback, null);
|
||||
}
|
||||
|
||||
internal void DumpSectors(UInt64 StartSector, UInt64 SectorCount, string Path, ProgressUpdater UpdaterPerSector)
|
||||
{
|
||||
DumpSectors(StartSector, SectorCount, Path, null, null, UpdaterPerSector);
|
||||
}
|
||||
|
||||
internal void DumpSectors(UInt64 StartSector, UInt64 SectorCount, Stream OutputStream)
|
||||
{
|
||||
DumpSectors(StartSector, SectorCount, null, OutputStream, null, null);
|
||||
}
|
||||
|
||||
internal void DumpSectors(UInt64 StartSector, UInt64 SectorCount, Stream OutputStream, Action<int, TimeSpan?> ProgressUpdateCallback)
|
||||
{
|
||||
DumpSectors(StartSector, SectorCount, null, OutputStream, ProgressUpdateCallback, null);
|
||||
}
|
||||
|
||||
internal void DumpSectors(UInt64 StartSector, UInt64 SectorCount, Stream OutputStream, ProgressUpdater UpdaterPerSector)
|
||||
{
|
||||
DumpSectors(StartSector, SectorCount, null, OutputStream, null, UpdaterPerSector);
|
||||
}
|
||||
|
||||
private void DumpSectors(UInt64 StartSector, UInt64 SectorCount, string Path, Stream OutputStream, Action<int, TimeSpan?> ProgressUpdateCallback, ProgressUpdater UpdaterPerSector)
|
||||
{
|
||||
bool VolumeWasOpen = IsVolumeOpen();
|
||||
if (!VolumeWasOpen)
|
||||
OpenVolume(false);
|
||||
|
||||
SetSectorPosition(StartSector);
|
||||
ProgressUpdater Progress = UpdaterPerSector;
|
||||
if ((Progress == null) && (ProgressUpdateCallback != null))
|
||||
Progress = new ProgressUpdater(SectorCount, ProgressUpdateCallback);
|
||||
|
||||
byte[] Buffer;
|
||||
if (SectorCount >= 0x80)
|
||||
Buffer = new byte[0x10000];
|
||||
else
|
||||
Buffer = new byte[SectorCount * 0x200];
|
||||
|
||||
Stream Stream;
|
||||
if (Path == null)
|
||||
Stream = OutputStream;
|
||||
else
|
||||
Stream = File.Open(Path, FileMode.Create);
|
||||
|
||||
using (BinaryWriter Writer = new BinaryWriter(Stream))
|
||||
{
|
||||
uint ActualSectorsRead;
|
||||
for (UInt64 i = 0; i < SectorCount; i += 0x80)
|
||||
{
|
||||
// TODO: Reading sectors and writing to compressed stream should be on different threads.
|
||||
// Backup of 3 partitions without compression takes about 40 minutes.
|
||||
// Backup of same partitions with compression takes about 70 minutes.
|
||||
// Separation reading and compression could potentially speed up a lot.
|
||||
// BinaryWriter doesnt support async.
|
||||
// Calling async directly on the EntryStream of a Ziparchive blocks.
|
||||
|
||||
ReadSectors(Buffer, out ActualSectorsRead, (SectorCount - i) >= 0x80 ? 0x80 : (uint)(SectorCount - i));
|
||||
Writer.Write(Buffer, 0, (int)ActualSectorsRead * 0x200);
|
||||
if (Progress != null)
|
||||
Progress.IncreaseProgress(ActualSectorsRead);
|
||||
}
|
||||
Stream.Flush();
|
||||
}
|
||||
|
||||
if (!VolumeWasOpen)
|
||||
CloseVolume();
|
||||
}
|
||||
|
||||
internal void BackupPartition(string PartitionName, string Path)
|
||||
{
|
||||
BackupPartition(PartitionName, Path, null, null, null);
|
||||
}
|
||||
|
||||
internal void BackupPartition(string PartitionName, string Path, Action<int, TimeSpan?> ProgressUpdateCallback)
|
||||
{
|
||||
BackupPartition(PartitionName, Path, null, ProgressUpdateCallback, null);
|
||||
}
|
||||
|
||||
internal void BackupPartition(string PartitionName, string Path, ProgressUpdater UpdaterPerSector)
|
||||
{
|
||||
BackupPartition(PartitionName, Path, null, null, UpdaterPerSector);
|
||||
}
|
||||
|
||||
internal void BackupPartition(string PartitionName, Stream OutputStream)
|
||||
{
|
||||
BackupPartition(PartitionName, null, OutputStream, null, null);
|
||||
}
|
||||
|
||||
internal void BackupPartition(string PartitionName, Stream OutputStream, Action<int, TimeSpan?> ProgressUpdateCallback)
|
||||
{
|
||||
BackupPartition(PartitionName, null, OutputStream, ProgressUpdateCallback, null);
|
||||
}
|
||||
|
||||
internal void BackupPartition(string PartitionName, Stream OutputStream, ProgressUpdater UpdaterPerSector)
|
||||
{
|
||||
BackupPartition(PartitionName, null, OutputStream, null, UpdaterPerSector);
|
||||
}
|
||||
|
||||
private void BackupPartition(string PartitionName, string Path, Stream OutputStream, Action<int, TimeSpan?> ProgressUpdateCallback, ProgressUpdater UpdaterPerSector)
|
||||
{
|
||||
bool VolumeWasOpen = IsVolumeOpen();
|
||||
if (!VolumeWasOpen)
|
||||
OpenVolume(false);
|
||||
|
||||
SetSectorPosition(1);
|
||||
byte[] GPTBuffer = ReadSectors(1, 33);
|
||||
GPT GPT = new GPT(GPTBuffer);
|
||||
Partition Partition = GPT.Partitions.Where((p) => p.Name == PartitionName).First();
|
||||
|
||||
DumpSectors(Partition.FirstSector, Partition.LastSector - Partition.FirstSector + 1, Path, OutputStream, ProgressUpdateCallback, UpdaterPerSector);
|
||||
|
||||
if (!VolumeWasOpen)
|
||||
CloseVolume();
|
||||
}
|
||||
|
||||
internal void WriteSectors(UInt64 StartSector, byte[] Buffer)
|
||||
{
|
||||
bool Result = true;
|
||||
|
||||
bool VolumeWasOpen = IsVolumeOpen();
|
||||
if (!VolumeWasOpen)
|
||||
OpenVolume(true);
|
||||
|
||||
SetSectorPosition(StartSector);
|
||||
|
||||
uint count = 1;
|
||||
Result = NativeMethods.WriteFile(hVolume, Buffer, (uint)Buffer.Length, out count, IntPtr.Zero);
|
||||
|
||||
if (!VolumeWasOpen)
|
||||
CloseVolume();
|
||||
|
||||
if (!Result)
|
||||
throw new Exception("Failed to write sectors.");
|
||||
}
|
||||
|
||||
internal void WriteSectors(byte[] Buffer, uint Length = uint.MaxValue)
|
||||
{
|
||||
if (!IsVolumeOpen())
|
||||
throw new Exception("Volume is not opened");
|
||||
|
||||
uint count = 1;
|
||||
bool Result = NativeMethods.WriteFile(hVolume, Buffer, (Length < Buffer.Length ? Length : (uint)Buffer.Length), out count, IntPtr.Zero);
|
||||
if (!Result)
|
||||
throw new Exception("Failed to write sectors. Exception: 0x" + Marshal.GetLastWin32Error().ToString("X8"));
|
||||
}
|
||||
|
||||
internal void WriteSectors(UInt64 StartSector, string Path)
|
||||
{
|
||||
WriteSectors(StartSector, Path, null, null);
|
||||
}
|
||||
|
||||
internal void WriteSectors(UInt64 StartSector, string Path, Action<int, TimeSpan?> ProgressUpdateCallback)
|
||||
{
|
||||
WriteSectors(StartSector, Path, ProgressUpdateCallback, null);
|
||||
}
|
||||
|
||||
internal void WriteSectors(UInt64 StartSector, string Path, ProgressUpdater UpdaterPerSector)
|
||||
{
|
||||
WriteSectors(StartSector, Path, null, UpdaterPerSector);
|
||||
}
|
||||
|
||||
private void WriteSectors(UInt64 StartSector, string Path, Action<int, TimeSpan?> ProgressUpdateCallback, ProgressUpdater UpdaterPerSector)
|
||||
{
|
||||
bool VolumeWasOpen = IsVolumeOpen();
|
||||
if (!VolumeWasOpen)
|
||||
OpenVolume(true);
|
||||
|
||||
SetSectorPosition(StartSector);
|
||||
|
||||
byte[] Buffer;
|
||||
|
||||
using (BinaryReader Reader = new BinaryReader(File.Open(Path, FileMode.Open)))
|
||||
{
|
||||
ProgressUpdater Progress = UpdaterPerSector;
|
||||
if ((Progress == null) && (ProgressUpdateCallback != null))
|
||||
Progress = new ProgressUpdater((UInt64)(Reader.BaseStream.Length / 0x200), ProgressUpdateCallback);
|
||||
|
||||
if (Reader.BaseStream.Length >= 0x10000)
|
||||
Buffer = new byte[0x10000];
|
||||
else
|
||||
Buffer = new byte[Reader.BaseStream.Length];
|
||||
|
||||
int Count;
|
||||
for (UInt64 i = 0; i < (UInt64)(Reader.BaseStream.Length / 0x200); i += 0x80)
|
||||
{
|
||||
Count = Reader.Read(Buffer, 0, Buffer.Length);
|
||||
|
||||
WriteSectors(Buffer, (uint)Count);
|
||||
|
||||
if (Progress != null)
|
||||
Progress.IncreaseProgress((ulong)Count / 0x200);
|
||||
}
|
||||
}
|
||||
|
||||
if (!VolumeWasOpen)
|
||||
CloseVolume();
|
||||
}
|
||||
|
||||
internal void RestorePartition(string Path, string PartitionName)
|
||||
{
|
||||
RestorePartition(Path, PartitionName, null, null);
|
||||
}
|
||||
|
||||
internal void RestorePartition(string Path, string PartitionName, Action<int, TimeSpan?> ProgressUpdateCallback)
|
||||
{
|
||||
RestorePartition(Path, PartitionName, ProgressUpdateCallback, null);
|
||||
}
|
||||
|
||||
internal void RestorePartition(string Path, string PartitionName, ProgressUpdater UpdaterPerSector)
|
||||
{
|
||||
RestorePartition(Path, PartitionName, null, UpdaterPerSector);
|
||||
}
|
||||
|
||||
private void RestorePartition(string Path, string PartitionName, Action<int, TimeSpan?> ProgressUpdateCallback, ProgressUpdater UpdaterPerSector)
|
||||
{
|
||||
bool VolumeWasOpen = IsVolumeOpen();
|
||||
if (!VolumeWasOpen)
|
||||
OpenVolume(true);
|
||||
|
||||
SetSectorPosition(1);
|
||||
byte[] GPTBuffer = ReadSectors(1, 33);
|
||||
GPT GPT = new GPT(GPTBuffer);
|
||||
Partition Partition = GPT.Partitions.Where((p) => p.Name == PartitionName).First();
|
||||
ulong PartitionSize = (Partition.LastSector - Partition.FirstSector + 1) * 0x200;
|
||||
ulong FileSize = (ulong)new FileInfo(Path).Length;
|
||||
if (FileSize > PartitionSize)
|
||||
throw new InvalidOperationException("Partition can not be restored, because its size is too big!");
|
||||
|
||||
WriteSectors(Partition.FirstSector, Path, ProgressUpdateCallback);
|
||||
|
||||
if (!VolumeWasOpen)
|
||||
CloseVolume();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,315 @@
|
||||
// Copyright (c) 2018, Rene Lergner - wpinternals.net - @Heathcliff74xda
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.ConstrainedExecution;
|
||||
using Microsoft.Win32.SafeHandles;
|
||||
using System.Security;
|
||||
|
||||
namespace WPinternals
|
||||
{
|
||||
[Flags]
|
||||
internal enum TokenAccessLevels
|
||||
{
|
||||
AssignPrimary = 0x00000001,
|
||||
Duplicate = 0x00000002,
|
||||
Impersonate = 0x00000004,
|
||||
Query = 0x00000008,
|
||||
QuerySource = 0x00000010,
|
||||
AdjustPrivileges = 0x00000020,
|
||||
AdjustGroups = 0x00000040,
|
||||
AdjustDefault = 0x00000080,
|
||||
AdjustSessionId = 0x00000100,
|
||||
|
||||
Read = 0x00020000 | Query,
|
||||
|
||||
Write = 0x00020000 | AdjustPrivileges | AdjustGroups | AdjustDefault,
|
||||
|
||||
AllAccess = 0x000F0000 |
|
||||
AssignPrimary |
|
||||
Duplicate |
|
||||
Impersonate |
|
||||
Query |
|
||||
QuerySource |
|
||||
AdjustPrivileges |
|
||||
AdjustGroups |
|
||||
AdjustDefault |
|
||||
AdjustSessionId,
|
||||
|
||||
MaximumAllowed = 0x02000000
|
||||
}
|
||||
|
||||
internal enum SecurityImpersonationLevel
|
||||
{
|
||||
Anonymous = 0,
|
||||
Identification = 1,
|
||||
Impersonation = 2,
|
||||
Delegation = 3,
|
||||
}
|
||||
|
||||
internal enum TokenType
|
||||
{
|
||||
Primary = 1,
|
||||
Impersonation = 2,
|
||||
}
|
||||
|
||||
internal enum EMoveMethod : uint
|
||||
{
|
||||
Begin = 0,
|
||||
Current = 1,
|
||||
End = 2
|
||||
}
|
||||
|
||||
internal sealed class NativeMethods
|
||||
{
|
||||
internal const uint SE_PRIVILEGE_DISABLED = 0x00000000;
|
||||
internal const uint SE_PRIVILEGE_ENABLED = 0x00000002;
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
|
||||
internal struct LUID
|
||||
{
|
||||
internal uint LowPart;
|
||||
internal uint HighPart;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
|
||||
internal struct LUID_AND_ATTRIBUTES
|
||||
{
|
||||
internal LUID Luid;
|
||||
internal uint Attributes;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
|
||||
internal struct TOKEN_PRIVILEGE
|
||||
{
|
||||
internal uint PrivilegeCount;
|
||||
internal LUID_AND_ATTRIBUTES Privilege;
|
||||
}
|
||||
|
||||
internal const string ADVAPI32 = "advapi32.dll";
|
||||
internal const string KERNEL32 = "kernel32.dll";
|
||||
|
||||
internal const int ERROR_SUCCESS = 0x0;
|
||||
internal const int ERROR_ACCESS_DENIED = 0x5;
|
||||
internal const int ERROR_NOT_ENOUGH_MEMORY = 0x8;
|
||||
internal const int ERROR_NO_TOKEN = 0x3f0;
|
||||
internal const int ERROR_NOT_ALL_ASSIGNED = 0x514;
|
||||
internal const int ERROR_NO_SUCH_PRIVILEGE = 0x521;
|
||||
internal const int ERROR_CANT_OPEN_ANONYMOUS = 0x543;
|
||||
|
||||
[DllImport(
|
||||
KERNEL32,
|
||||
SetLastError = true)]
|
||||
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
|
||||
internal static extern bool CloseHandle(IntPtr handle);
|
||||
|
||||
[DllImport(
|
||||
ADVAPI32,
|
||||
CharSet = CharSet.Unicode,
|
||||
SetLastError = true)]
|
||||
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
|
||||
internal static extern bool AdjustTokenPrivileges(
|
||||
[In] SafeTokenHandle TokenHandle,
|
||||
[In] bool DisableAllPrivileges,
|
||||
[In] ref TOKEN_PRIVILEGE NewState,
|
||||
[In] uint BufferLength,
|
||||
[In, Out] ref TOKEN_PRIVILEGE PreviousState,
|
||||
[In, Out] ref uint ReturnLength);
|
||||
|
||||
[DllImport(
|
||||
ADVAPI32,
|
||||
CharSet = CharSet.Auto,
|
||||
SetLastError = true)]
|
||||
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
|
||||
internal static extern
|
||||
bool RevertToSelf();
|
||||
|
||||
[DllImport(
|
||||
ADVAPI32,
|
||||
EntryPoint = "LookupPrivilegeValueW",
|
||||
CharSet = CharSet.Auto,
|
||||
SetLastError = true)]
|
||||
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
|
||||
internal static extern
|
||||
bool LookupPrivilegeValue(
|
||||
[In] string lpSystemName,
|
||||
[In] string lpName,
|
||||
[In, Out] ref LUID Luid);
|
||||
|
||||
[DllImport(
|
||||
KERNEL32,
|
||||
CharSet = CharSet.Auto,
|
||||
SetLastError = true)]
|
||||
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
|
||||
internal static extern
|
||||
IntPtr GetCurrentProcess();
|
||||
|
||||
[DllImport(
|
||||
KERNEL32,
|
||||
CharSet = CharSet.Auto,
|
||||
SetLastError = true)]
|
||||
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
|
||||
internal static extern
|
||||
IntPtr GetCurrentThread();
|
||||
|
||||
[DllImport(
|
||||
ADVAPI32,
|
||||
CharSet = CharSet.Unicode,
|
||||
SetLastError = true)]
|
||||
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
|
||||
internal static extern
|
||||
bool OpenProcessToken(
|
||||
[In] IntPtr ProcessToken,
|
||||
[In] TokenAccessLevels DesiredAccess,
|
||||
[In, Out] ref SafeTokenHandle TokenHandle);
|
||||
|
||||
[DllImport
|
||||
(ADVAPI32,
|
||||
CharSet = CharSet.Unicode,
|
||||
SetLastError = true)]
|
||||
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
|
||||
internal static extern
|
||||
bool OpenThreadToken(
|
||||
[In] IntPtr ThreadToken,
|
||||
[In] TokenAccessLevels DesiredAccess,
|
||||
[In] bool OpenAsSelf,
|
||||
[In, Out] ref SafeTokenHandle TokenHandle);
|
||||
|
||||
[DllImport
|
||||
(ADVAPI32,
|
||||
CharSet = CharSet.Unicode,
|
||||
SetLastError = true)]
|
||||
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
|
||||
internal static extern
|
||||
bool DuplicateTokenEx(
|
||||
[In] SafeTokenHandle ExistingToken,
|
||||
[In] TokenAccessLevels DesiredAccess,
|
||||
[In] IntPtr TokenAttributes,
|
||||
[In] SecurityImpersonationLevel ImpersonationLevel,
|
||||
[In] TokenType TokenType,
|
||||
[In, Out] ref SafeTokenHandle NewToken);
|
||||
|
||||
[DllImport
|
||||
(ADVAPI32,
|
||||
CharSet = CharSet.Unicode,
|
||||
SetLastError = true)]
|
||||
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
|
||||
internal static extern
|
||||
bool SetThreadToken(
|
||||
[In] IntPtr Thread,
|
||||
[In] SafeTokenHandle Token);
|
||||
|
||||
internal const uint FILE_SHARE_READ = 0x00000001;
|
||||
internal const uint FILE_SHARE_WRITE = 0x00000002;
|
||||
internal const uint FILE_SHARE_DELETE = 0x00000004;
|
||||
internal const uint OPEN_EXISTING = 3;
|
||||
|
||||
internal const uint GENERIC_READ = (0x80000000);
|
||||
internal const uint GENERIC_WRITE = (0x40000000);
|
||||
|
||||
internal const uint FILE_FLAG_WRITE_THROUGH = 0x80000000;
|
||||
internal const uint FILE_FLAG_NO_BUFFERING = 0x20000000;
|
||||
internal const uint FILE_READ_ATTRIBUTES = (0x0080);
|
||||
internal const uint FILE_WRITE_ATTRIBUTES = 0x0100;
|
||||
internal const uint ERROR_INSUFFICIENT_BUFFER = 122;
|
||||
internal const uint FILE_BEGIN = 0;
|
||||
internal const uint FSCTL_LOCK_VOLUME = 0x00090018;
|
||||
internal const uint FSCTL_DISMOUNT_VOLUME = 0x00090020;
|
||||
internal const uint FSCTL_UNLOCK_VOLUME = 0x00090022;
|
||||
internal const uint IOCTL_STORAGE_EJECT_MEDIA = 0x2D4808;
|
||||
internal const uint IOCTL_STORAGE_LOAD_MEDIA = 0x2D480C;
|
||||
|
||||
internal const Int32 INVALID_HANDLE_VALUE = -1;
|
||||
internal const Int32 FILE_ATTRIBUTE_NORMAL = 1;
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
internal static extern bool WriteFile(IntPtr hFile, byte[] lpBuffer, uint nNumberOfBytesToWrite, out uint lpNumberOfBytesWritten, IntPtr lpOverlapped);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
internal static extern bool ReadFile(IntPtr hFile, [Out] byte[] lpBuffer, uint nNumberOfBytesToRead, out uint lpNumberOfBytesRead, IntPtr lpOverlapped);
|
||||
|
||||
[DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
|
||||
internal static extern uint SetFilePointer(
|
||||
[In] IntPtr hFile,
|
||||
[In] int lDistanceToMove,
|
||||
[In, Out] ref int lpDistanceToMoveHigh,
|
||||
[In] EMoveMethod dwMoveMethod);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
internal static extern IntPtr CreateFile(
|
||||
string lpFileName,
|
||||
uint dwDesiredAccess,
|
||||
uint dwShareMode,
|
||||
IntPtr lpSecurityAttributes,
|
||||
uint dwCreationDisposition,
|
||||
uint dwFlagsAndAttributes,
|
||||
IntPtr hTemplateFile);
|
||||
|
||||
[DllImport("Kernel32.dll", SetLastError = false, CharSet = CharSet.Auto)]
|
||||
public static extern bool DeviceIoControl(
|
||||
IntPtr hDevice,
|
||||
uint IoControlCode,
|
||||
[MarshalAs(UnmanagedType.AsAny)]
|
||||
[In] object InBuffer,
|
||||
uint nInBufferSize,
|
||||
[MarshalAs(UnmanagedType.AsAny)]
|
||||
[Out] object OutBuffer,
|
||||
uint nOutBufferSize,
|
||||
ref uint pBytesReturned,
|
||||
IntPtr Overlapped
|
||||
);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
public static extern bool FlushFileBuffers(IntPtr hFile);
|
||||
|
||||
static NativeMethods()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class SafeTokenHandle : SafeHandleZeroOrMinusOneIsInvalid
|
||||
{
|
||||
private SafeTokenHandle() : base(true) { }
|
||||
|
||||
// 0 is an Invalid Handle
|
||||
internal SafeTokenHandle(IntPtr handle)
|
||||
: base(true)
|
||||
{
|
||||
SetHandle(handle);
|
||||
}
|
||||
|
||||
internal static SafeTokenHandle InvalidHandle
|
||||
{
|
||||
get { return new SafeTokenHandle(IntPtr.Zero); }
|
||||
}
|
||||
|
||||
[DllImport(NativeMethods.KERNEL32, SetLastError = true),
|
||||
SuppressUnmanagedCodeSecurity,
|
||||
ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
|
||||
private static extern bool CloseHandle(IntPtr handle);
|
||||
|
||||
override protected bool ReleaseHandle()
|
||||
{
|
||||
return CloseHandle(handle);
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,347 @@
|
||||
// Copyright (c) 2018, Rene Lergner - wpinternals.net - @Heathcliff74xda
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
using MadWizard.WinUSBNet;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace WPinternals
|
||||
{
|
||||
internal class NokiaPhoneModel: IDisposable
|
||||
{
|
||||
protected bool Disposed = false;
|
||||
private USBDevice Device = null;
|
||||
private int MessageId = 0;
|
||||
private object UsbLock = new object();
|
||||
|
||||
public NokiaPhoneModel(string DevicePath)
|
||||
{
|
||||
// Mass Storage device is not WinUSB
|
||||
try
|
||||
{
|
||||
Device = new USBDevice(DevicePath);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
private JToken ExecuteJsonMethodAsJsonToken(string JsonMethod, Dictionary<string, object> Params, string ResultElement)
|
||||
{
|
||||
byte[] Buffer;
|
||||
int Length;
|
||||
|
||||
lock (UsbLock)
|
||||
{
|
||||
string jsonrpc = "2.0";
|
||||
int id = MessageId++;
|
||||
string method = JsonMethod;
|
||||
Dictionary<string, object> @params = new Dictionary<string, object>();
|
||||
if (Params != null)
|
||||
{
|
||||
foreach (KeyValuePair<string, object> Param in Params)
|
||||
{
|
||||
if (Param.Value is byte[])
|
||||
@params.Add(Param.Key, ((byte[])Param.Value).Select(b => (int)b).ToArray()); // convert to int-array
|
||||
else
|
||||
@params.Add(Param.Key, Param.Value);
|
||||
}
|
||||
}
|
||||
@params.Add("MessageVersion", 0);
|
||||
string Request = JsonConvert.SerializeObject(new { jsonrpc, id, method, @params });
|
||||
Device.OutputPipe.Write(System.Text.Encoding.ASCII.GetBytes(Request));
|
||||
|
||||
Buffer = new byte[0x10000];
|
||||
Length = Device.InputPipe.Read(Buffer);
|
||||
}
|
||||
|
||||
Newtonsoft.Json.Linq.JObject ResultMessage = Newtonsoft.Json.Linq.JObject.Parse(System.Text.ASCIIEncoding.ASCII.GetString(Buffer, 0, Length));
|
||||
|
||||
JToken ResultToken = ResultMessage.Root.SelectToken("result");
|
||||
if ((ResultToken == null) || (ResultElement == null)) return null;
|
||||
return ResultToken.SelectToken(ResultElement);
|
||||
}
|
||||
|
||||
public void ExecuteJsonMethod(string JsonMethod, Dictionary<string, object> Params)
|
||||
{
|
||||
JToken Token = ExecuteJsonMethodAsJsonToken(JsonMethod, Params, null);
|
||||
}
|
||||
|
||||
public string ExecuteJsonMethodAsString(string JsonMethod, Dictionary<string, object> Params, string ResultElement)
|
||||
{
|
||||
JToken Token = ExecuteJsonMethodAsJsonToken(JsonMethod, Params, ResultElement);
|
||||
if (Token == null) return null;
|
||||
return Token.Value<string>();
|
||||
}
|
||||
|
||||
public string ExecuteJsonMethodAsString(string JsonMethod, string ResultElement)
|
||||
{
|
||||
JToken Token = ExecuteJsonMethodAsJsonToken(JsonMethod, null, ResultElement);
|
||||
if (Token == null) return null;
|
||||
return Token.Value<string>();
|
||||
}
|
||||
|
||||
public int ExecuteJsonMethodAsInteger(string JsonMethod, Dictionary<string, object> Params, string ResultElement)
|
||||
{
|
||||
JToken Token = ExecuteJsonMethodAsJsonToken(JsonMethod, Params, ResultElement);
|
||||
if (Token == null) return 0;
|
||||
return Token.Value<int>();
|
||||
}
|
||||
|
||||
public int ExecuteJsonMethodAsInteger(string JsonMethod, string ResultElement)
|
||||
{
|
||||
JToken Token = ExecuteJsonMethodAsJsonToken(JsonMethod, null, ResultElement);
|
||||
if (Token == null) return 0;
|
||||
return Token.Value<int>();
|
||||
}
|
||||
|
||||
public byte[] ExecuteJsonMethodAsBytes(string JsonMethod, Dictionary<string, object> Params, string ResultElement)
|
||||
{
|
||||
JToken Token = ExecuteJsonMethodAsJsonToken(JsonMethod, Params, ResultElement);
|
||||
if (Token == null) return null;
|
||||
return Token.Values<byte>().ToArray();
|
||||
}
|
||||
|
||||
public byte[] ExecuteJsonMethodAsBytes(string JsonMethod, string ResultElement)
|
||||
{
|
||||
JToken Token = ExecuteJsonMethodAsJsonToken(JsonMethod, null, ResultElement);
|
||||
if (Token == null) return null;
|
||||
return Token.Values<byte>().ToArray();
|
||||
}
|
||||
|
||||
public bool? ExecuteJsonMethodAsBoolean(string JsonMethod, string ResultElement)
|
||||
{
|
||||
JToken Token = ExecuteJsonMethodAsJsonToken(JsonMethod, null, ResultElement);
|
||||
if (Token == null) return null;
|
||||
return Token.Value<bool>();
|
||||
}
|
||||
|
||||
public void ExecuteJsonMethodAsync(string JsonMethod, Dictionary<string, object> Params)
|
||||
{
|
||||
lock (UsbLock)
|
||||
{
|
||||
string jsonrpc = "2.0";
|
||||
int id = MessageId++;
|
||||
string method = JsonMethod;
|
||||
Dictionary<string, object> @params = new Dictionary<string, object>();
|
||||
if (Params != null)
|
||||
{
|
||||
foreach (KeyValuePair<string, object> Param in Params)
|
||||
{
|
||||
if (Param.Value is byte[])
|
||||
@params.Add(Param.Key, ((byte[])Param.Value).Select(b => (int)b).ToArray()); // convert to int-array
|
||||
else
|
||||
@params.Add(Param.Key, Param.Value);
|
||||
}
|
||||
}
|
||||
@params.Add("MessageVersion", 0);
|
||||
string Request = JsonConvert.SerializeObject(new { jsonrpc, id, method, @params });
|
||||
|
||||
byte[] OutBuffer = System.Text.Encoding.ASCII.GetBytes(Request);
|
||||
Device.OutputPipe.BeginWrite(OutBuffer, 0, OutBuffer.Length, (AsyncResultWrite) =>
|
||||
{
|
||||
Device.OutputPipe.EndWrite(AsyncResultWrite);
|
||||
}, null);
|
||||
}
|
||||
}
|
||||
|
||||
public void ExecuteJsonMethodAsync(string JsonMethod)
|
||||
{
|
||||
ExecuteJsonMethod(JsonMethod, null);
|
||||
}
|
||||
|
||||
public delegate void JsonMethodCallbackString(object State, string Result);
|
||||
|
||||
public void ExecuteJsonMethodAsStringAsync(string JsonMethod, Dictionary<string, object> Params, string ResultElement, object State, JsonMethodCallbackString Callback)
|
||||
{
|
||||
ExecuteJsonMethodAsTokenAsync(JsonMethod, Params, ResultElement, State, (ReturnState, Token) => { Callback(ReturnState, Token.Value<string>()); });
|
||||
}
|
||||
|
||||
public void ExecuteJsonMethodAsStringAsync(string JsonMethod, string ResultElement, object State, JsonMethodCallbackString Callback)
|
||||
{
|
||||
ExecuteJsonMethodAsTokenAsync(JsonMethod, null, ResultElement, State, (ReturnState, Token) => { Callback(ReturnState, Token.Value<string>()); });
|
||||
}
|
||||
|
||||
public delegate void JsonMethodCallbackBoolean(object State, bool Result);
|
||||
|
||||
public void ExecuteJsonMethodAsBooleanAsync(string JsonMethod, Dictionary<string, object> Params, string ResultElement, object State, JsonMethodCallbackBoolean Callback)
|
||||
{
|
||||
ExecuteJsonMethodAsTokenAsync(JsonMethod, Params, ResultElement, State, (ReturnState, Token) => { Callback(ReturnState, Token.Value<bool>()); });
|
||||
}
|
||||
|
||||
public void ExecuteJsonMethodAsBooleanAsync(string JsonMethod, string ResultElement, object State, JsonMethodCallbackBoolean Callback)
|
||||
{
|
||||
ExecuteJsonMethodAsTokenAsync(JsonMethod, null, ResultElement, State, (ReturnState, Token) => { Callback(ReturnState, Token.Value<bool>()); });
|
||||
}
|
||||
|
||||
public delegate void JsonMethodCallbackBytes(object State, byte[] Result);
|
||||
|
||||
public void ExecuteJsonMethodAsBytesAsync(string JsonMethod, Dictionary<string, object> Params, string ResultElement, object State, JsonMethodCallbackBytes Callback)
|
||||
{
|
||||
ExecuteJsonMethodAsTokenAsync(JsonMethod, Params, ResultElement, State, (ReturnState, Token) => { Callback(ReturnState, Token.Values<byte>().ToArray()); });
|
||||
}
|
||||
|
||||
public void ExecuteJsonMethodAsBytesAsync(string JsonMethod, string ResultElement, object State, JsonMethodCallbackBytes Callback)
|
||||
{
|
||||
ExecuteJsonMethodAsTokenAsync(JsonMethod, null, ResultElement, State, (ReturnState, Token) => { Callback(ReturnState, Token.Values<byte>().ToArray()); });
|
||||
}
|
||||
|
||||
public delegate void JsonMethodCallbackInteger(object State, int Result);
|
||||
|
||||
public void ExecuteJsonMethodAsIntegerAsync(string JsonMethod, Dictionary<string, object> Params, string ResultElement, object State, JsonMethodCallbackInteger Callback)
|
||||
{
|
||||
ExecuteJsonMethodAsTokenAsync(JsonMethod, Params, ResultElement, State, (ReturnState, Token) => { Callback(ReturnState, Token.Value<int>()); });
|
||||
}
|
||||
|
||||
public void ExecuteJsonMethodAsIntegerAsync(string JsonMethod, string ResultElement, object State, JsonMethodCallbackInteger Callback)
|
||||
{
|
||||
ExecuteJsonMethodAsTokenAsync(JsonMethod, null, ResultElement, State, (ReturnState, Token) => { Callback(ReturnState, Token.Value<int>()); });
|
||||
}
|
||||
|
||||
public delegate void JsonMethodCallbackToken(object State, JToken Result);
|
||||
|
||||
public void ExecuteJsonMethodAsTokenAsync(string JsonMethod, Dictionary<string, object> Params, string ResultElement, object State, JsonMethodCallbackToken Callback)
|
||||
{
|
||||
byte[] Buffer;
|
||||
int Length;
|
||||
|
||||
lock (UsbLock)
|
||||
{
|
||||
string jsonrpc = "2.0";
|
||||
int id = MessageId++;
|
||||
string method = JsonMethod;
|
||||
Dictionary<string, object> @params = new Dictionary<string, object>();
|
||||
if (Params != null)
|
||||
{
|
||||
foreach (KeyValuePair<string, object> Param in Params)
|
||||
{
|
||||
if (Param.Value is byte[])
|
||||
@params.Add(Param.Key, ((byte[])Param.Value).Select(b => (int)b).ToArray()); // convert to int-array
|
||||
else
|
||||
@params.Add(Param.Key, Param.Value);
|
||||
}
|
||||
}
|
||||
@params.Add("MessageVersion", 0);
|
||||
string Request = JsonConvert.SerializeObject(new { jsonrpc, id, method, @params });
|
||||
|
||||
byte[] OutBuffer = System.Text.Encoding.ASCII.GetBytes(Request);
|
||||
Device.OutputPipe.BeginWrite(OutBuffer, 0, OutBuffer.Length, (AsyncResultWrite) =>
|
||||
{
|
||||
Device.OutputPipe.EndWrite(AsyncResultWrite);
|
||||
Buffer = new byte[0x10000];
|
||||
Device.InputPipe.BeginRead(Buffer, 0, 0x10000, (AsyncResultRead) =>
|
||||
{
|
||||
Length = Device.InputPipe.EndRead(AsyncResultRead);
|
||||
|
||||
Newtonsoft.Json.Linq.JObject ResultMessage = Newtonsoft.Json.Linq.JObject.Parse(System.Text.ASCIIEncoding.ASCII.GetString(Buffer, 0, Length));
|
||||
|
||||
JToken ResultToken = ResultMessage.Root.SelectToken("result");
|
||||
if ((ResultToken == null) || (ResultElement == null)) Callback(AsyncResultRead.AsyncState, null);
|
||||
Callback(AsyncResultRead.AsyncState, ResultToken.SelectToken(ResultElement));
|
||||
}, AsyncResultWrite.AsyncState);
|
||||
}, State);
|
||||
}
|
||||
}
|
||||
|
||||
public void ExecuteJsonMethodAsTokenAsync(string JsonMethod, string ResultElement, object State, JsonMethodCallbackToken Callback)
|
||||
{
|
||||
ExecuteJsonMethodAsTokenAsync(JsonMethod, null, ResultElement, State, Callback);
|
||||
}
|
||||
|
||||
public byte[] ExecuteRawMethod(byte[] RawMethod)
|
||||
{
|
||||
return ExecuteRawMethod(RawMethod, RawMethod.Length);
|
||||
}
|
||||
|
||||
public byte[] ExecuteRawMethod(byte[] RawMethod, int Length)
|
||||
{
|
||||
byte[] Buffer = new byte[0x8000]; // Should be at least 0x4408 for receiving the GPT packet.
|
||||
byte[] Result = null;
|
||||
lock (UsbLock)
|
||||
{
|
||||
Device.OutputPipe.Write(RawMethod, 0, Length);
|
||||
try
|
||||
{
|
||||
int OutputLength = Device.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)
|
||||
{
|
||||
Device.OutputPipe.Write(RawMethod, 0, Length);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes the UsbDevice including all unmanaged WinUSB handles. This function
|
||||
/// should be called when the UsbDevice object is no longer in use, otherwise
|
||||
/// unmanaged handles will remain open until the garbage collector finalizes the
|
||||
/// object.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finalizer for the UsbDevice. Disposes all unmanaged handles.
|
||||
/// </summary>
|
||||
~NokiaPhoneModel()
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes the object
|
||||
/// </summary>
|
||||
/// <param name="disposing">Indicates wether Dispose was called manually (true) or by
|
||||
/// the garbage collector (false) via the destructor.</param>
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (Disposed)
|
||||
return;
|
||||
|
||||
if (disposing)
|
||||
{
|
||||
if (Device != null)
|
||||
Device.Dispose();
|
||||
}
|
||||
|
||||
// Clean unmanaged resources here.
|
||||
// (none currently)
|
||||
|
||||
Disposed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,597 @@
|
||||
// Copyright (c) 2018, Rene Lergner - wpinternals.net - @Heathcliff74xda
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Security.AccessControl;
|
||||
using System.Security.Cryptography;
|
||||
using System.Security.Principal;
|
||||
using System.Xml;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace WPinternals
|
||||
{
|
||||
internal class PatchEngine
|
||||
{
|
||||
internal List<PatchDefinition> PatchDefinitions = new List<PatchDefinition>();
|
||||
internal readonly List<TargetRedirection> TargetRedirections = new List<TargetRedirection>();
|
||||
|
||||
internal PatchEngine() { }
|
||||
|
||||
internal PatchEngine(string PatchDefinitionsXmlString)
|
||||
{
|
||||
XmlSerializer x = new XmlSerializer(PatchDefinitions.GetType(), null, new Type[] { }, new XmlRootAttribute("PatchDefinitions"), "");
|
||||
MemoryStream s = new MemoryStream(System.Text.Encoding.ASCII.GetBytes(PatchDefinitionsXmlString));
|
||||
PatchDefinitions = (List<PatchDefinition>)x.Deserialize(s);
|
||||
}
|
||||
|
||||
internal void WriteDefinitions(string FilePath)
|
||||
{
|
||||
XmlSerializer x = new XmlSerializer(PatchDefinitions.GetType(), null, new Type[] { }, new XmlRootAttribute("PatchDefinitions"), "");
|
||||
|
||||
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
|
||||
ns.Add("", "");
|
||||
|
||||
System.IO.StreamWriter FileWriter = new System.IO.StreamWriter(FilePath);
|
||||
XmlWriter XmlWriter = XmlWriter.Create(FileWriter, new XmlWriterSettings() { OmitXmlDeclaration = true, Indent = true, NewLineHandling = NewLineHandling.Entitize });
|
||||
|
||||
FileWriter.WriteLine("<?xml version=\"1.0\" encoding=\"utf-8\"?>");
|
||||
FileWriter.WriteLine("");
|
||||
FileWriter.WriteLine("<!--");
|
||||
FileWriter.WriteLine("Copyright(c) 2018, Rene Lergner - wpinternals.net - @Heathcliff74xda");
|
||||
FileWriter.WriteLine("");
|
||||
FileWriter.WriteLine("Permission is hereby granted, free of charge, to any person obtaining a");
|
||||
FileWriter.WriteLine("copy of this software and associated documentation files(the \"Software\"),");
|
||||
FileWriter.WriteLine("to deal in the Software without restriction, including without limitation");
|
||||
FileWriter.WriteLine("the rights to use, copy, modify, merge, publish, distribute, sublicense,");
|
||||
FileWriter.WriteLine("and / or sell copies of the Software, and to permit persons to whom the");
|
||||
FileWriter.WriteLine("Software is furnished to do so, subject to the following conditions:");
|
||||
FileWriter.WriteLine("");
|
||||
FileWriter.WriteLine("The above copyright notice and this permission notice shall be included in");
|
||||
FileWriter.WriteLine("all copies or substantial portions of the Software.");
|
||||
FileWriter.WriteLine("");
|
||||
FileWriter.WriteLine("THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR");
|
||||
FileWriter.WriteLine("IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,");
|
||||
FileWriter.WriteLine("FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE");
|
||||
FileWriter.WriteLine("AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER");
|
||||
FileWriter.WriteLine("LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING");
|
||||
FileWriter.WriteLine("FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER");
|
||||
FileWriter.WriteLine("DEALINGS IN THE SOFTWARE.");
|
||||
FileWriter.WriteLine("-->");
|
||||
FileWriter.WriteLine("");
|
||||
|
||||
x.Serialize(XmlWriter, PatchDefinitions, ns);
|
||||
|
||||
FileWriter.Close();
|
||||
}
|
||||
|
||||
private string _TargetPath = null;
|
||||
internal string TargetPath
|
||||
{
|
||||
get
|
||||
{
|
||||
return _TargetPath;
|
||||
}
|
||||
set
|
||||
{
|
||||
_TargetPath = value.TrimEnd(new char[] { '\\' });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private DiscUtils.DiscFileSystem _TargetImage = null;
|
||||
internal DiscUtils.DiscFileSystem TargetImage
|
||||
{
|
||||
get
|
||||
{
|
||||
return _TargetImage;
|
||||
}
|
||||
set
|
||||
{
|
||||
_TargetImage = value;
|
||||
_TargetPath = "";
|
||||
}
|
||||
}
|
||||
|
||||
internal bool Patch(string PatchDefinition)
|
||||
{
|
||||
bool Result = false;
|
||||
List<FilePatcher> LoadedFiles = new List<FilePatcher>();
|
||||
|
||||
LogFile.Log("Attempt patch: " + PatchDefinition);
|
||||
|
||||
// Find a matching TargetVersion
|
||||
PatchDefinition Definition = PatchDefinitions.Single(d => string.Compare(d.Name, PatchDefinition, true) == 0);
|
||||
TargetVersion MatchedVersion = null;
|
||||
int VersionIndex = 0;
|
||||
foreach (TargetVersion CurrentVersion in Definition.TargetVersions)
|
||||
{
|
||||
bool Match = true;
|
||||
int FileIndex = 0;
|
||||
|
||||
foreach (TargetFile CurrentTargetFile in CurrentVersion.TargetFiles)
|
||||
{
|
||||
// Determine target path
|
||||
string TargetPath = null;
|
||||
foreach (TargetRedirection CurrentRedirection in TargetRedirections)
|
||||
{
|
||||
if (CurrentTargetFile.Path.StartsWith(CurrentRedirection.RelativePath, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
TargetPath = Path.Combine(CurrentRedirection.TargetPath + "\\", CurrentTargetFile.Path);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (TargetPath == null)
|
||||
TargetPath = Path.Combine(this.TargetPath + "\\", CurrentTargetFile.Path);
|
||||
|
||||
// Lookup file
|
||||
FilePatcher CurrentFile = LoadedFiles.SingleOrDefault(f => string.Compare(f.FilePath, TargetPath, true) == 0);
|
||||
if (CurrentFile == null)
|
||||
{
|
||||
if ((TargetImage != null) && (!TargetPath.Contains(':')))
|
||||
CurrentFile = new FilePatcher(TargetPath, TargetImage.OpenFile(TargetPath, FileMode.Open, FileAccess.ReadWrite));
|
||||
else
|
||||
CurrentFile = new FilePatcher(TargetPath);
|
||||
LoadedFiles.Add(CurrentFile);
|
||||
}
|
||||
|
||||
// Compare hash
|
||||
if (!StructuralComparisons.StructuralEqualityComparer.Equals(CurrentFile.Hash, CurrentTargetFile.HashOriginal) &&
|
||||
!StructuralComparisons.StructuralEqualityComparer.Equals(CurrentFile.Hash, CurrentTargetFile.HashPatched))
|
||||
{
|
||||
Match = false;
|
||||
|
||||
foreach (TargetFile CurrentObsoleteFile in CurrentTargetFile.Obsolete)
|
||||
{
|
||||
if (StructuralComparisons.StructuralEqualityComparer.Equals(CurrentFile.Hash, CurrentObsoleteFile.HashPatched))
|
||||
{
|
||||
Match = true; // Found match after all. File is patched with an obsolete version of this patch.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!Match)
|
||||
{
|
||||
LogFile.Log("Pattern: " + VersionIndex.ToString() + ", " + FileIndex.ToString());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
FileIndex++;
|
||||
}
|
||||
|
||||
if (Match)
|
||||
{
|
||||
MatchedVersion = CurrentVersion;
|
||||
break;
|
||||
}
|
||||
|
||||
VersionIndex++;
|
||||
}
|
||||
|
||||
if (MatchedVersion != null)
|
||||
{
|
||||
LogFile.Log("Apply: " + MatchedVersion.Description);
|
||||
|
||||
foreach (TargetFile CurrentTargetFile in MatchedVersion.TargetFiles)
|
||||
{
|
||||
FilePatcher CurrentFile = LoadedFiles.SingleOrDefault(f => string.Compare(f.FilePath, Path.Combine(TargetPath + "\\", CurrentTargetFile.Path), true) == 0);
|
||||
|
||||
if (!StructuralComparisons.StructuralEqualityComparer.Equals(CurrentFile.Hash, CurrentTargetFile.HashPatched))
|
||||
{
|
||||
CurrentFile.StartPatching();
|
||||
|
||||
if (!StructuralComparisons.StructuralEqualityComparer.Equals(CurrentFile.Hash, CurrentTargetFile.HashOriginal))
|
||||
{
|
||||
// File is already patched, but with an older version of this patch.
|
||||
// First unpatch back to original.
|
||||
|
||||
foreach (TargetFile CurrentObsoleteFile in CurrentTargetFile.Obsolete)
|
||||
{
|
||||
if (StructuralComparisons.StructuralEqualityComparer.Equals(CurrentFile.Hash, CurrentObsoleteFile.HashPatched))
|
||||
{
|
||||
foreach (Patch CurrentPatch in CurrentObsoleteFile.Patches)
|
||||
{
|
||||
CurrentFile.ApplyPatch(CurrentPatch.Address, CurrentPatch.OriginalBytes);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (Patch CurrentPatch in CurrentTargetFile.Patches)
|
||||
{
|
||||
CurrentFile.ApplyPatch(CurrentPatch.Address, CurrentPatch.PatchedBytes);
|
||||
}
|
||||
|
||||
CurrentFile.FinishPatching();
|
||||
}
|
||||
}
|
||||
|
||||
Result = true;
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal void Restore(string PatchDefinition)
|
||||
{
|
||||
List<FilePatcher> LoadedFiles = new List<FilePatcher>();
|
||||
|
||||
try
|
||||
{
|
||||
// Find a matching TargetVersion
|
||||
PatchDefinition Definition = PatchDefinitions.Single(d => string.Compare(d.Name, PatchDefinition, true) == 0);
|
||||
TargetVersion MatchedVersion = null;
|
||||
foreach (TargetVersion CurrentVersion in Definition.TargetVersions)
|
||||
{
|
||||
bool Match = true;
|
||||
|
||||
foreach (TargetFile CurrentTargetFile in CurrentVersion.TargetFiles)
|
||||
{
|
||||
// Determine target path
|
||||
string TargetPath = null;
|
||||
foreach (TargetRedirection CurrentRedirection in TargetRedirections)
|
||||
{
|
||||
if (CurrentTargetFile.Path.StartsWith(CurrentRedirection.RelativePath, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
TargetPath = Path.Combine(CurrentRedirection.TargetPath, CurrentTargetFile.Path);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (TargetPath == null)
|
||||
TargetPath = Path.Combine(this.TargetPath, CurrentTargetFile.Path);
|
||||
|
||||
// Lookup file
|
||||
FilePatcher CurrentFile = LoadedFiles.SingleOrDefault(f => string.Compare(f.FilePath, TargetPath, true) == 0);
|
||||
if (CurrentFile == null)
|
||||
{
|
||||
CurrentFile = new FilePatcher(TargetPath);
|
||||
LoadedFiles.Add(CurrentFile);
|
||||
}
|
||||
|
||||
// Compare hash
|
||||
if (!StructuralComparisons.StructuralEqualityComparer.Equals(CurrentFile.Hash, CurrentTargetFile.HashOriginal) &&
|
||||
!StructuralComparisons.StructuralEqualityComparer.Equals(CurrentFile.Hash, CurrentTargetFile.HashPatched))
|
||||
{
|
||||
Match = false;
|
||||
|
||||
foreach (TargetFile CurrentObsoleteFile in CurrentTargetFile.Obsolete)
|
||||
{
|
||||
if (StructuralComparisons.StructuralEqualityComparer.Equals(CurrentFile.Hash, CurrentObsoleteFile.HashPatched))
|
||||
{
|
||||
Match = true; // Found match after all. File is patched with an obsolete version of this patch.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!Match)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (Match)
|
||||
{
|
||||
MatchedVersion = CurrentVersion;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (MatchedVersion != null)
|
||||
{
|
||||
foreach (TargetFile CurrentTargetFile in MatchedVersion.TargetFiles)
|
||||
{
|
||||
FilePatcher CurrentFile = LoadedFiles.SingleOrDefault(f => string.Compare(f.FilePath, Path.Combine(TargetPath, CurrentTargetFile.Path), true) == 0);
|
||||
|
||||
if (!StructuralComparisons.StructuralEqualityComparer.Equals(CurrentFile.Hash, CurrentTargetFile.HashOriginal))
|
||||
{
|
||||
CurrentFile.StartPatching();
|
||||
|
||||
if (StructuralComparisons.StructuralEqualityComparer.Equals(CurrentFile.Hash, CurrentTargetFile.HashPatched))
|
||||
{
|
||||
foreach (Patch CurrentPatch in CurrentTargetFile.Patches)
|
||||
{
|
||||
CurrentFile.ApplyPatch(CurrentPatch.Address, CurrentPatch.OriginalBytes);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (TargetFile CurrentObsoleteFile in CurrentTargetFile.Obsolete)
|
||||
{
|
||||
if (StructuralComparisons.StructuralEqualityComparer.Equals(CurrentFile.Hash, CurrentObsoleteFile.HashPatched))
|
||||
{
|
||||
foreach (Patch CurrentPatch in CurrentObsoleteFile.Patches)
|
||||
{
|
||||
CurrentFile.ApplyPatch(CurrentPatch.Address, CurrentPatch.OriginalBytes);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CurrentFile.FinishPatching();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception Ex)
|
||||
{
|
||||
LogFile.LogException(Ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class TargetRedirection
|
||||
{
|
||||
private string _RelativePath;
|
||||
private string _TargetPath;
|
||||
|
||||
internal TargetRedirection(string RelativePath, string TargetPath)
|
||||
{
|
||||
this.RelativePath = RelativePath;
|
||||
this.TargetPath = TargetPath;
|
||||
}
|
||||
|
||||
internal string RelativePath
|
||||
{
|
||||
get
|
||||
{
|
||||
return _RelativePath;
|
||||
}
|
||||
set
|
||||
{
|
||||
_RelativePath = value.TrimStart(new char[] { '\\' }).TrimEnd(new char[] { '\\' });
|
||||
}
|
||||
}
|
||||
|
||||
internal string TargetPath
|
||||
{
|
||||
get
|
||||
{
|
||||
return _TargetPath;
|
||||
}
|
||||
set
|
||||
{
|
||||
_TargetPath = value.TrimEnd(new char[] { '\\' });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class FilePatcher
|
||||
{
|
||||
internal byte[] Hash = null;
|
||||
internal string FilePath;
|
||||
internal FileSecurity OriginalACL;
|
||||
internal Privilege TakeOwnershipPrivilege;
|
||||
internal Privilege RestorePrivilege;
|
||||
internal Stream Stream = null;
|
||||
|
||||
internal FilePatcher(string FilePath)
|
||||
{
|
||||
this.FilePath = FilePath;
|
||||
using (FileStream stream = File.OpenRead(FilePath))
|
||||
{
|
||||
SHA1Managed sha = new SHA1Managed();
|
||||
Hash = sha.ComputeHash(stream);
|
||||
}
|
||||
}
|
||||
|
||||
internal FilePatcher(string FilePath, Stream FileStream)
|
||||
{
|
||||
if (!FileStream.CanSeek || !FileStream.CanWrite)
|
||||
throw new WPinternalsException("Incorrect filestream");
|
||||
|
||||
this.FilePath = FilePath;
|
||||
this.Stream = FileStream;
|
||||
FileStream.Position = 0;
|
||||
SHA1Managed sha = new SHA1Managed();
|
||||
Hash = sha.ComputeHash(FileStream);
|
||||
FileStream.Position = 0;
|
||||
}
|
||||
|
||||
internal void StartPatching()
|
||||
{
|
||||
if (FilePath.Contains(':'))
|
||||
{
|
||||
// Enable Take Ownership AND Restore ownership to original owner
|
||||
// Take Ownership Privilge is not enough.
|
||||
// We need Restore Privilege.
|
||||
RestorePrivilege = new Privilege(Privilege.Restore);
|
||||
RestorePrivilege.Enable();
|
||||
|
||||
if ((Environment.OSVersion.Version.Major == 6) && (Environment.OSVersion.Version.Minor <= 1))
|
||||
{
|
||||
// On Vista or 7
|
||||
TakeOwnershipPrivilege = new Privilege(Privilege.TakeOwnership);
|
||||
TakeOwnershipPrivilege.Enable();
|
||||
}
|
||||
|
||||
// Backup original owner and ACL
|
||||
OriginalACL = File.GetAccessControl(FilePath);
|
||||
|
||||
// And take the original security to create new security rules.
|
||||
FileSecurity NewACL = File.GetAccessControl(FilePath);
|
||||
|
||||
// Take ownership
|
||||
NewACL.SetOwner(WindowsIdentity.GetCurrent().User);
|
||||
File.SetAccessControl(FilePath, NewACL);
|
||||
|
||||
// And create a new access rule
|
||||
NewACL.SetAccessRule(new FileSystemAccessRule(WindowsIdentity.GetCurrent().User, FileSystemRights.FullControl, AccessControlType.Allow));
|
||||
File.SetAccessControl(FilePath, NewACL);
|
||||
|
||||
// Open the file for patching
|
||||
Stream = new FileStream(FilePath, FileMode.Open, FileAccess.ReadWrite);
|
||||
}
|
||||
}
|
||||
|
||||
internal void ApplyPatch(UInt32 Offset, byte[] Bytes)
|
||||
{
|
||||
Stream.Position = Offset;
|
||||
Stream.Write(Bytes, 0, Bytes.Length);
|
||||
}
|
||||
|
||||
internal void FinishPatching()
|
||||
{
|
||||
// Close file
|
||||
Stream.Close();
|
||||
|
||||
if (FilePath.Contains(':'))
|
||||
{
|
||||
// Restore original owner and access rules.
|
||||
// The OriginalACL cannot be reused directly.
|
||||
FileSecurity NewACL = File.GetAccessControl(FilePath);
|
||||
NewACL.SetSecurityDescriptorBinaryForm(OriginalACL.GetSecurityDescriptorBinaryForm());
|
||||
File.SetAccessControl(FilePath, NewACL);
|
||||
|
||||
// Revert to self
|
||||
RestorePrivilege.Revert();
|
||||
RestorePrivilege.Disable();
|
||||
|
||||
if ((Environment.OSVersion.Version.Major == 6) && (Environment.OSVersion.Version.Minor <= 1))
|
||||
{
|
||||
// On Vista or 7
|
||||
TakeOwnershipPrivilege.Revert();
|
||||
TakeOwnershipPrivilege.Disable();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class PatchDefinition // Must be public to be serializable
|
||||
{
|
||||
[XmlAttribute]
|
||||
public string Name;
|
||||
|
||||
public List<TargetVersion> TargetVersions = new List<TargetVersion>();
|
||||
}
|
||||
|
||||
public class TargetVersion // Must be public to be serializable
|
||||
{
|
||||
[XmlAttribute]
|
||||
public string Description;
|
||||
|
||||
public List<TargetFile> TargetFiles = new List<TargetFile>();
|
||||
}
|
||||
|
||||
public class TargetFile // Must be public to be serializable
|
||||
{
|
||||
private string _Path;
|
||||
[XmlAttribute]
|
||||
public string Path
|
||||
{
|
||||
get
|
||||
{
|
||||
return _Path;
|
||||
}
|
||||
set
|
||||
{
|
||||
_Path = value.TrimStart(new char[] { '\\' });
|
||||
}
|
||||
}
|
||||
|
||||
[XmlIgnore]
|
||||
public byte[] HashOriginal;
|
||||
[XmlAttribute("HashOriginal")]
|
||||
public string HashOriginalAsString
|
||||
{
|
||||
get
|
||||
{
|
||||
return Converter.ConvertHexToString(HashOriginal, "");
|
||||
}
|
||||
set
|
||||
{
|
||||
HashOriginal = Converter.ConvertStringToHex(value);
|
||||
}
|
||||
}
|
||||
|
||||
[XmlIgnore]
|
||||
public byte[] HashPatched;
|
||||
[XmlAttribute("HashPatched")]
|
||||
public string HashPatchedAsString
|
||||
{
|
||||
get
|
||||
{
|
||||
return Converter.ConvertHexToString(HashPatched, "");
|
||||
}
|
||||
set
|
||||
{
|
||||
HashPatched = Converter.ConvertStringToHex(value);
|
||||
}
|
||||
}
|
||||
|
||||
public List<Patch> Patches = new List<Patch>();
|
||||
public List<TargetFile> Obsolete = new List<TargetFile>();
|
||||
}
|
||||
|
||||
public class Patch // Must be public to be serializable
|
||||
{
|
||||
[XmlIgnore]
|
||||
public UInt32 Address;
|
||||
[XmlAttribute("Address")]
|
||||
public string AddressAsString
|
||||
{
|
||||
get
|
||||
{
|
||||
return "0x" + Address.ToString("X8");
|
||||
}
|
||||
set
|
||||
{
|
||||
string NewValue = value;
|
||||
if (NewValue.StartsWith("0x", StringComparison.OrdinalIgnoreCase))
|
||||
NewValue = NewValue.Substring(2);
|
||||
Address = Convert.ToUInt32(NewValue, 16);
|
||||
}
|
||||
}
|
||||
|
||||
[XmlIgnore]
|
||||
public byte[] OriginalBytes;
|
||||
[XmlAttribute("OriginalBytes")]
|
||||
public string OriginalBytesAsString
|
||||
{
|
||||
get
|
||||
{
|
||||
return Converter.ConvertHexToString(OriginalBytes, "");
|
||||
}
|
||||
set
|
||||
{
|
||||
OriginalBytes = Converter.ConvertStringToHex(value);
|
||||
}
|
||||
}
|
||||
|
||||
[XmlIgnore]
|
||||
public byte[] PatchedBytes;
|
||||
[XmlAttribute("PatchedBytes")]
|
||||
public string PatchedBytesAsString
|
||||
{
|
||||
get
|
||||
{
|
||||
return Converter.ConvertHexToString(PatchedBytes, "");
|
||||
}
|
||||
set
|
||||
{
|
||||
PatchedBytes = Converter.ConvertStringToHex(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,667 @@
|
||||
// Copyright (c) 2018, Rene Lergner - wpinternals.net - @Heathcliff74xda
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
using System;
|
||||
using System.Collections.Specialized;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.ConstrainedExecution;
|
||||
using System.Threading;
|
||||
|
||||
namespace WPinternals
|
||||
{
|
||||
using Luid = NativeMethods.LUID;
|
||||
using Win32Exception = System.ComponentModel.Win32Exception;
|
||||
using PrivilegeNotHeldException = System.Security.AccessControl.PrivilegeNotHeldException;
|
||||
|
||||
internal delegate void PrivilegedCallback(object state);
|
||||
|
||||
internal sealed class Privilege
|
||||
{
|
||||
#region Private static members
|
||||
private static LocalDataStoreSlot tlsSlot = Thread.AllocateDataSlot();
|
||||
private static HybridDictionary privileges = new HybridDictionary();
|
||||
private static HybridDictionary luids = new HybridDictionary();
|
||||
private static ReaderWriterLock privilegeLock = new ReaderWriterLock();
|
||||
#endregion
|
||||
|
||||
#region Private members
|
||||
private bool needToRevert = false;
|
||||
private bool initialState = false;
|
||||
private bool stateWasChanged = false;
|
||||
private Luid luid;
|
||||
private readonly Thread currentThread = Thread.CurrentThread;
|
||||
private TlsContents tlsContents = null;
|
||||
#endregion
|
||||
|
||||
#region Privilege names
|
||||
public const string CreateToken = "SeCreateTokenPrivilege";
|
||||
public const string AssignPrimaryToken = "SeAssignPrimaryTokenPrivilege";
|
||||
public const string LockMemory = "SeLockMemoryPrivilege";
|
||||
public const string IncreaseQuota = "SeIncreaseQuotaPrivilege";
|
||||
public const string UnsolicitedInput = "SeUnsolicitedInputPrivilege";
|
||||
public const string MachineAccount = "SeMachineAccountPrivilege";
|
||||
public const string TrustedComputingBase = "SeTcbPrivilege";
|
||||
public const string Security = "SeSecurityPrivilege";
|
||||
public const string TakeOwnership = "SeTakeOwnershipPrivilege";
|
||||
public const string LoadDriver = "SeLoadDriverPrivilege";
|
||||
public const string SystemProfile = "SeSystemProfilePrivilege";
|
||||
public const string SystemTime = "SeSystemtimePrivilege";
|
||||
public const string ProfileSingleProcess = "SeProfileSingleProcessPrivilege";
|
||||
public const string IncreaseBasePriority = "SeIncreaseBasePriorityPrivilege";
|
||||
public const string CreatePageFile = "SeCreatePagefilePrivilege";
|
||||
public const string CreatePermanent = "SeCreatePermanentPrivilege";
|
||||
public const string Backup = "SeBackupPrivilege";
|
||||
public const string Restore = "SeRestorePrivilege";
|
||||
public const string Shutdown = "SeShutdownPrivilege";
|
||||
public const string Debug = "SeDebugPrivilege";
|
||||
public const string Audit = "SeAuditPrivilege";
|
||||
public const string SystemEnvironment = "SeSystemEnvironmentPrivilege";
|
||||
public const string ChangeNotify = "SeChangeNotifyPrivilege";
|
||||
public const string RemoteShutdown = "SeRemoteShutdownPrivilege";
|
||||
public const string Undock = "SeUndockPrivilege";
|
||||
public const string SyncAgent = "SeSyncAgentPrivilege";
|
||||
public const string EnableDelegation = "SeEnableDelegationPrivilege";
|
||||
public const string ManageVolume = "SeManageVolumePrivilege";
|
||||
public const string Impersonate = "SeImpersonatePrivilege";
|
||||
public const string CreateGlobal = "SeCreateGlobalPrivilege";
|
||||
public const string TrustedCredentialManagerAccess = "SeTrustedCredManAccessPrivilege";
|
||||
public const string ReserveProcessor = "SeReserveProcessorPrivilege";
|
||||
#endregion
|
||||
|
||||
#region LUID caching logic
|
||||
|
||||
//
|
||||
// This routine is a wrapper around a hashtable containing mappings
|
||||
// of privilege names to luids
|
||||
//
|
||||
|
||||
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
|
||||
private static Luid LuidFromPrivilege(string privilege)
|
||||
{
|
||||
Luid luid;
|
||||
luid.LowPart = 0;
|
||||
luid.HighPart = 0;
|
||||
|
||||
//
|
||||
// Look up the privilege LUID inside the cache
|
||||
//
|
||||
|
||||
RuntimeHelpers.PrepareConstrainedRegions();
|
||||
|
||||
try
|
||||
{
|
||||
privilegeLock.AcquireReaderLock(Timeout.Infinite);
|
||||
|
||||
if (luids.Contains(privilege))
|
||||
{
|
||||
luid = (Luid)luids[privilege];
|
||||
|
||||
privilegeLock.ReleaseReaderLock();
|
||||
}
|
||||
else
|
||||
{
|
||||
privilegeLock.ReleaseReaderLock();
|
||||
|
||||
if (false == NativeMethods.LookupPrivilegeValue(null, privilege, ref luid))
|
||||
{
|
||||
int error = Marshal.GetLastWin32Error();
|
||||
|
||||
if (error == NativeMethods.ERROR_NOT_ENOUGH_MEMORY)
|
||||
{
|
||||
throw new OutOfMemoryException();
|
||||
}
|
||||
else if (error == NativeMethods.ERROR_ACCESS_DENIED)
|
||||
{
|
||||
throw new UnauthorizedAccessException("Caller does not have the rights to look up privilege local unique identifier");
|
||||
}
|
||||
else if (error == NativeMethods.ERROR_NO_SUCH_PRIVILEGE)
|
||||
{
|
||||
throw new ArgumentException(
|
||||
string.Format("{0} is not a valid privilege name", privilege),
|
||||
"privilege");
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Win32Exception(error);
|
||||
}
|
||||
}
|
||||
|
||||
privilegeLock.AcquireWriterLock(Timeout.Infinite);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (privilegeLock.IsReaderLockHeld)
|
||||
{
|
||||
privilegeLock.ReleaseReaderLock();
|
||||
}
|
||||
|
||||
if (privilegeLock.IsWriterLockHeld)
|
||||
{
|
||||
if (!luids.Contains(privilege))
|
||||
{
|
||||
luids[privilege] = luid;
|
||||
privileges[luid] = privilege;
|
||||
}
|
||||
|
||||
privilegeLock.ReleaseWriterLock();
|
||||
}
|
||||
}
|
||||
|
||||
return luid;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Nested classes
|
||||
private sealed class TlsContents : IDisposable
|
||||
{
|
||||
private bool disposed = false;
|
||||
private int referenceCount = 1;
|
||||
private SafeTokenHandle threadHandle = new SafeTokenHandle(IntPtr.Zero);
|
||||
private bool isImpersonating = false;
|
||||
|
||||
private static SafeTokenHandle processHandle = new SafeTokenHandle(IntPtr.Zero);
|
||||
private static readonly object syncRoot = new object();
|
||||
|
||||
#region Constructor and finalizer
|
||||
public TlsContents()
|
||||
{
|
||||
int error = 0;
|
||||
int cachingError = 0;
|
||||
bool success = true;
|
||||
|
||||
if (processHandle.IsInvalid)
|
||||
{
|
||||
lock (syncRoot)
|
||||
{
|
||||
if (processHandle.IsInvalid)
|
||||
{
|
||||
if (false == NativeMethods.OpenProcessToken(
|
||||
NativeMethods.GetCurrentProcess(),
|
||||
TokenAccessLevels.Duplicate,
|
||||
ref processHandle))
|
||||
{
|
||||
cachingError = Marshal.GetLastWin32Error();
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RuntimeHelpers.PrepareConstrainedRegions();
|
||||
|
||||
try
|
||||
{
|
||||
// Open the thread token; if there is no thread token,
|
||||
// copy the process token onto the thread
|
||||
|
||||
if (false == NativeMethods.OpenThreadToken(
|
||||
NativeMethods.GetCurrentThread(),
|
||||
TokenAccessLevels.Query | TokenAccessLevels.AdjustPrivileges,
|
||||
true,
|
||||
ref this.threadHandle))
|
||||
{
|
||||
if (success == true)
|
||||
{
|
||||
error = Marshal.GetLastWin32Error();
|
||||
|
||||
if (error != NativeMethods.ERROR_NO_TOKEN)
|
||||
{
|
||||
success = false;
|
||||
}
|
||||
|
||||
if (success == true)
|
||||
{
|
||||
error = 0;
|
||||
|
||||
if (false == NativeMethods.DuplicateTokenEx(
|
||||
processHandle,
|
||||
TokenAccessLevels.Impersonate | TokenAccessLevels.Query | TokenAccessLevels.AdjustPrivileges,
|
||||
IntPtr.Zero,
|
||||
SecurityImpersonationLevel.Impersonation,
|
||||
TokenType.Impersonation,
|
||||
ref this.threadHandle))
|
||||
{
|
||||
error = Marshal.GetLastWin32Error();
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (success == true)
|
||||
{
|
||||
if (false == NativeMethods.SetThreadToken(
|
||||
IntPtr.Zero,
|
||||
this.threadHandle))
|
||||
{
|
||||
error = Marshal.GetLastWin32Error();
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (success == true)
|
||||
{
|
||||
// This thread is now impersonating; it needs to be reverted to its original state
|
||||
|
||||
this.isImpersonating = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
error = cachingError;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
success = true;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (!success)
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
if (error == NativeMethods.ERROR_NOT_ENOUGH_MEMORY)
|
||||
{
|
||||
throw new OutOfMemoryException();
|
||||
}
|
||||
else if (error == NativeMethods.ERROR_ACCESS_DENIED ||
|
||||
error == NativeMethods.ERROR_CANT_OPEN_ANONYMOUS)
|
||||
{
|
||||
throw new UnauthorizedAccessException("The caller does not have the rights to perform the operation");
|
||||
}
|
||||
else if (error != 0)
|
||||
{
|
||||
throw new Win32Exception(error);
|
||||
}
|
||||
}
|
||||
|
||||
~TlsContents()
|
||||
{
|
||||
if (!this.disposed)
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region IDisposable implementation
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
if (this.disposed) return;
|
||||
|
||||
if (this.threadHandle != null)
|
||||
{
|
||||
this.threadHandle.Dispose();
|
||||
this.threadHandle = null;
|
||||
}
|
||||
|
||||
if (this.isImpersonating)
|
||||
{
|
||||
NativeMethods.RevertToSelf();
|
||||
}
|
||||
|
||||
this.disposed = true;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Reference-counting
|
||||
public void IncrementReferenceCount()
|
||||
{
|
||||
this.referenceCount++;
|
||||
}
|
||||
|
||||
public int DecrementReferenceCount()
|
||||
{
|
||||
int result = --this.referenceCount;
|
||||
|
||||
if (result == 0)
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public int ReferenceCountValue
|
||||
{
|
||||
get { return this.referenceCount; }
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
public SafeTokenHandle ThreadHandle
|
||||
{
|
||||
get { return this.threadHandle; }
|
||||
}
|
||||
|
||||
public bool IsImpersonating
|
||||
{
|
||||
get { return this.isImpersonating; }
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
public Privilege(string privilegeName)
|
||||
{
|
||||
if (privilegeName == null)
|
||||
{
|
||||
throw new ArgumentNullException("privilegeName");
|
||||
}
|
||||
|
||||
this.luid = LuidFromPrivilege(privilegeName);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Public methods and properties
|
||||
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
|
||||
public void Enable()
|
||||
{
|
||||
this.ToggleState(true);
|
||||
}
|
||||
|
||||
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
|
||||
public void Disable()
|
||||
{
|
||||
this.ToggleState(false);
|
||||
}
|
||||
|
||||
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
|
||||
public void Revert()
|
||||
{
|
||||
int error = 0;
|
||||
|
||||
// All privilege operations must take place on the same thread
|
||||
|
||||
if (!this.currentThread.Equals(Thread.CurrentThread))
|
||||
{
|
||||
throw new InvalidOperationException("Operation must take place on the thread that created the object");
|
||||
}
|
||||
|
||||
if (!this.NeedToRevert)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// This code must be eagerly prepared and non-interruptible.
|
||||
|
||||
RuntimeHelpers.PrepareConstrainedRegions();
|
||||
|
||||
try
|
||||
{
|
||||
// The payload is entirely in the finally block
|
||||
// This is how we ensure that the code will not be
|
||||
// interrupted by catastrophic exceptions
|
||||
}
|
||||
finally
|
||||
{
|
||||
bool success = true;
|
||||
|
||||
try
|
||||
{
|
||||
// Only call AdjustTokenPrivileges if we're not going to be reverting to self,
|
||||
// on this Revert, since doing the latter obliterates the thread token anyway
|
||||
|
||||
if (this.stateWasChanged &&
|
||||
(this.tlsContents.ReferenceCountValue > 1 ||
|
||||
!this.tlsContents.IsImpersonating))
|
||||
{
|
||||
NativeMethods.TOKEN_PRIVILEGE newState = new NativeMethods.TOKEN_PRIVILEGE();
|
||||
newState.PrivilegeCount = 1;
|
||||
newState.Privilege.Luid = this.luid;
|
||||
newState.Privilege.Attributes = (this.initialState ? NativeMethods.SE_PRIVILEGE_ENABLED : NativeMethods.SE_PRIVILEGE_DISABLED);
|
||||
|
||||
NativeMethods.TOKEN_PRIVILEGE previousState = new NativeMethods.TOKEN_PRIVILEGE();
|
||||
uint previousSize = 0;
|
||||
|
||||
if (false == NativeMethods.AdjustTokenPrivileges(
|
||||
this.tlsContents.ThreadHandle,
|
||||
false,
|
||||
ref newState,
|
||||
(uint)Marshal.SizeOf(previousState),
|
||||
ref previousState,
|
||||
ref previousSize))
|
||||
{
|
||||
error = Marshal.GetLastWin32Error();
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (success)
|
||||
{
|
||||
this.Reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (error == NativeMethods.ERROR_NOT_ENOUGH_MEMORY)
|
||||
{
|
||||
throw new OutOfMemoryException();
|
||||
}
|
||||
else if (error == NativeMethods.ERROR_ACCESS_DENIED)
|
||||
{
|
||||
throw new UnauthorizedAccessException("Caller does not have the permission to change the privilege");
|
||||
}
|
||||
else if (error != 0)
|
||||
{
|
||||
throw new Win32Exception(error);
|
||||
}
|
||||
}
|
||||
|
||||
public bool NeedToRevert
|
||||
{
|
||||
get { return this.needToRevert; }
|
||||
}
|
||||
|
||||
public static void RunWithPrivilege(string privilege, bool enabled, PrivilegedCallback callback, object state)
|
||||
{
|
||||
if (callback == null)
|
||||
{
|
||||
throw new ArgumentNullException("callback");
|
||||
}
|
||||
|
||||
Privilege p = new Privilege(privilege);
|
||||
|
||||
RuntimeHelpers.PrepareConstrainedRegions();
|
||||
|
||||
try
|
||||
{
|
||||
if (enabled)
|
||||
{
|
||||
p.Enable();
|
||||
}
|
||||
else
|
||||
{
|
||||
p.Disable();
|
||||
}
|
||||
|
||||
callback(state);
|
||||
}
|
||||
catch
|
||||
{
|
||||
p.Revert();
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
{
|
||||
p.Revert();
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Private implementation
|
||||
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
|
||||
private void ToggleState(bool enable)
|
||||
{
|
||||
int error = 0;
|
||||
|
||||
// All privilege operations must take place on the same thread
|
||||
|
||||
if (!this.currentThread.Equals(Thread.CurrentThread))
|
||||
{
|
||||
throw new InvalidOperationException("Operation must take place on the thread that created the object");
|
||||
}
|
||||
|
||||
// This privilege was already altered and needs to be reverted before it can be altered again
|
||||
|
||||
if (this.NeedToRevert)
|
||||
{
|
||||
throw new InvalidOperationException("Must revert the privilege prior to attempting this operation");
|
||||
}
|
||||
|
||||
// Need to make this block of code non-interruptible so that it would preserve
|
||||
// consistency of thread oken state even in the face of catastrophic exceptions
|
||||
|
||||
RuntimeHelpers.PrepareConstrainedRegions();
|
||||
|
||||
try
|
||||
{
|
||||
// The payload is entirely in the finally block
|
||||
// This is how we ensure that the code will not be
|
||||
// interrupted by catastrophic exceptions
|
||||
}
|
||||
finally
|
||||
{
|
||||
try
|
||||
{
|
||||
// Retrieve TLS state
|
||||
|
||||
this.tlsContents = Thread.GetData(tlsSlot) as TlsContents;
|
||||
|
||||
if (this.tlsContents == null)
|
||||
{
|
||||
this.tlsContents = new TlsContents();
|
||||
Thread.SetData(tlsSlot, this.tlsContents);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.tlsContents.IncrementReferenceCount();
|
||||
}
|
||||
|
||||
NativeMethods.TOKEN_PRIVILEGE newState = new NativeMethods.TOKEN_PRIVILEGE();
|
||||
newState.PrivilegeCount = 1;
|
||||
newState.Privilege.Luid = this.luid;
|
||||
newState.Privilege.Attributes = enable ? NativeMethods.SE_PRIVILEGE_ENABLED : NativeMethods.SE_PRIVILEGE_DISABLED;
|
||||
|
||||
NativeMethods.TOKEN_PRIVILEGE previousState = new NativeMethods.TOKEN_PRIVILEGE();
|
||||
uint previousSize = 0;
|
||||
|
||||
// Place the new privilege on the thread token and remember the previous state.
|
||||
|
||||
if (false == NativeMethods.AdjustTokenPrivileges(
|
||||
this.tlsContents.ThreadHandle,
|
||||
false,
|
||||
ref newState,
|
||||
(uint)Marshal.SizeOf(previousState),
|
||||
ref previousState,
|
||||
ref previousSize))
|
||||
{
|
||||
error = Marshal.GetLastWin32Error();
|
||||
}
|
||||
else if (NativeMethods.ERROR_NOT_ALL_ASSIGNED == Marshal.GetLastWin32Error())
|
||||
{
|
||||
error = NativeMethods.ERROR_NOT_ALL_ASSIGNED;
|
||||
}
|
||||
else
|
||||
{
|
||||
// This is the initial state that revert will have to go back to
|
||||
|
||||
this.initialState = ((previousState.Privilege.Attributes & NativeMethods.SE_PRIVILEGE_ENABLED) != 0);
|
||||
|
||||
// Remember whether state has changed at all
|
||||
|
||||
this.stateWasChanged = (this.initialState != enable);
|
||||
|
||||
// If we had to impersonate, or if the privilege state changed we'll need to revert
|
||||
|
||||
this.needToRevert = this.tlsContents.IsImpersonating || this.stateWasChanged;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (!this.needToRevert)
|
||||
{
|
||||
this.Reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (error == NativeMethods.ERROR_NOT_ALL_ASSIGNED)
|
||||
{
|
||||
throw new PrivilegeNotHeldException(privileges[this.luid] as string);
|
||||
}
|
||||
if (error == NativeMethods.ERROR_NOT_ENOUGH_MEMORY)
|
||||
{
|
||||
throw new OutOfMemoryException();
|
||||
}
|
||||
else if (error == NativeMethods.ERROR_ACCESS_DENIED ||
|
||||
error == NativeMethods.ERROR_CANT_OPEN_ANONYMOUS)
|
||||
{
|
||||
throw new UnauthorizedAccessException("The caller does not have the right to change the privilege");
|
||||
}
|
||||
else if (error != 0)
|
||||
{
|
||||
throw new Win32Exception(error);
|
||||
}
|
||||
}
|
||||
|
||||
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
|
||||
private void Reset()
|
||||
{
|
||||
RuntimeHelpers.PrepareConstrainedRegions();
|
||||
|
||||
try
|
||||
{
|
||||
// Payload is in the finally block
|
||||
// as a way to guarantee execution
|
||||
}
|
||||
finally
|
||||
{
|
||||
this.stateWasChanged = false;
|
||||
this.initialState = false;
|
||||
this.needToRevert = false;
|
||||
|
||||
if (this.tlsContents != null)
|
||||
{
|
||||
if (0 == this.tlsContents.DecrementReferenceCount())
|
||||
{
|
||||
this.tlsContents = null;
|
||||
Thread.SetData(tlsSlot, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,151 @@
|
||||
// Copyright (c) 2018, Rene Lergner - wpinternals.net - @Heathcliff74xda
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace WPinternals
|
||||
{
|
||||
internal class QualcommDownload
|
||||
{
|
||||
private QualcommSerial Serial;
|
||||
|
||||
public QualcommDownload(QualcommSerial Serial)
|
||||
{
|
||||
this.Serial = Serial;
|
||||
}
|
||||
|
||||
public bool IsAlive()
|
||||
{
|
||||
try
|
||||
{
|
||||
Serial.SendCommand(new byte[] { 0x06 }, new byte[] { 0x02 });
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void SendToPhoneMemory(UInt32 Address, Stream Data, UInt32 Length = UInt32.MaxValue)
|
||||
{
|
||||
long Remaining;
|
||||
if (Length > (Data.Length - Data.Position))
|
||||
Remaining = Data.Length - Data.Position;
|
||||
else
|
||||
Remaining = Length;
|
||||
UInt32 CurrentLength;
|
||||
byte[] Buffer = new byte[0x107];
|
||||
Buffer[0] = 0x0F;
|
||||
System.Buffer.BlockCopy(BitConverter.GetBytes((UInt16)0x100).Reverse().ToArray(), 0, Buffer, 5, 2); // Length is in Big Endian
|
||||
UInt32 CurrentAddress = Address;
|
||||
while (Remaining > 0)
|
||||
{
|
||||
System.Buffer.BlockCopy(BitConverter.GetBytes(CurrentAddress).Reverse().ToArray(), 0, Buffer, 1, 4); // Address is in Big Endian
|
||||
|
||||
if (Remaining >= 0x100)
|
||||
CurrentLength = 0x100;
|
||||
else
|
||||
CurrentLength = (UInt32)Remaining;
|
||||
CurrentLength = (UInt32)(Data.Read(Buffer, 7, (int)CurrentLength));
|
||||
Serial.SendCommand(Buffer, new byte[] { 0x02 });
|
||||
|
||||
CurrentAddress += CurrentLength;
|
||||
Remaining -= CurrentLength;
|
||||
}
|
||||
}
|
||||
|
||||
public void SendToPhoneMemory(UInt32 Address, byte[] Data, UInt32 Offset = 0, UInt32 Length = UInt32.MaxValue)
|
||||
{
|
||||
long Remaining;
|
||||
if (Offset > (Data.Length - 1))
|
||||
throw new ArgumentException("Wrong offset");
|
||||
if (Length > (Data.Length - Offset))
|
||||
Remaining = Data.Length - Offset;
|
||||
else
|
||||
Remaining = Length;
|
||||
UInt32 CurrentLength;
|
||||
UInt32 CurrentOffset = Offset;
|
||||
byte[] Buffer = new byte[0x107];
|
||||
UInt32 CurrentAddress = Address;
|
||||
byte[] CurrentBytes;
|
||||
while (Remaining > 0)
|
||||
{
|
||||
|
||||
if (Remaining >= 0x100)
|
||||
{
|
||||
CurrentLength = 0x100;
|
||||
CurrentBytes = Buffer;
|
||||
}
|
||||
else
|
||||
{
|
||||
CurrentLength = (UInt32)Remaining;
|
||||
CurrentBytes = new byte[CurrentLength + 7];
|
||||
}
|
||||
CurrentBytes[0] = 0x0F;
|
||||
System.Buffer.BlockCopy(BitConverter.GetBytes(CurrentAddress).Reverse().ToArray(), 0, CurrentBytes, 1, 4); // Address is in Big Endian
|
||||
System.Buffer.BlockCopy(BitConverter.GetBytes((UInt16)CurrentLength).Reverse().ToArray(), 0, CurrentBytes, 5, 2); // Length is in Big Endian
|
||||
System.Buffer.BlockCopy(Data, (int)CurrentOffset, CurrentBytes, 7, (int)CurrentLength);
|
||||
|
||||
Serial.SendCommand(CurrentBytes, new byte[] { 0x02 });
|
||||
|
||||
CurrentAddress += CurrentLength;
|
||||
CurrentOffset += CurrentLength;
|
||||
Remaining -= CurrentLength;
|
||||
}
|
||||
}
|
||||
|
||||
public void StartBootloader(UInt32 Address)
|
||||
{
|
||||
byte[] Buffer = new byte[5];
|
||||
Buffer[0] = 0x05;
|
||||
System.Buffer.BlockCopy(BitConverter.GetBytes(Address).Reverse().ToArray(), 0, Buffer, 1, 4); // Address is in Big Endian
|
||||
Serial.SendCommand(Buffer, new byte[] { 0x02 });
|
||||
}
|
||||
|
||||
// Reset interface. Interface becomes unresponsive.
|
||||
public void Reset()
|
||||
{
|
||||
Serial.SendCommand(new byte[] { 0x0A }, new byte[] { 0x02 });
|
||||
}
|
||||
|
||||
// This also resets interface. This does not actually reboot the phone. The interface becomes unresponsive.
|
||||
public void Shutdown()
|
||||
{
|
||||
Serial.SendCommand(new byte[] { 0x0E }, new byte[] { 0x02 });
|
||||
}
|
||||
|
||||
// This command only works on 9008 interface.
|
||||
public byte[] GetRKH()
|
||||
{
|
||||
byte[] Response = Serial.SendCommand(new byte[] { 0x18 }, new byte[] { 0x18, 0x01, 0x00 });
|
||||
byte[] Result = new byte[0x20];
|
||||
Buffer.BlockCopy(Response, 3, Result, 0, 0x20);
|
||||
return Result;
|
||||
}
|
||||
|
||||
public void CloseSerial()
|
||||
{
|
||||
Serial.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,226 @@
|
||||
// Copyright (c) 2018, Rene Lergner - wpinternals.net - @Heathcliff74xda
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace WPinternals
|
||||
{
|
||||
internal enum FlashUnit
|
||||
{
|
||||
Bytes,
|
||||
Sectors
|
||||
}
|
||||
|
||||
internal class QualcommFlasher
|
||||
{
|
||||
private QualcommSerial Serial;
|
||||
|
||||
public QualcommFlasher(QualcommSerial Serial)
|
||||
{
|
||||
this.Serial = Serial;
|
||||
}
|
||||
|
||||
public void CloseSerial()
|
||||
{
|
||||
Serial.Close();
|
||||
}
|
||||
|
||||
public void Hello()
|
||||
{
|
||||
byte[] Command = new byte[]
|
||||
{
|
||||
0x01, // Hello command
|
||||
0x51, 0x43, 0x4F, 0x4D, 0x20, 0x66, 0x61, 0x73, 0x74, 0x20, 0x64, 0x6F, 0x77, 0x6E, 0x6C, 0x6F, // "QCOM fast download protocol host"
|
||||
0x61, 0x64, 0x20, 0x70, 0x72, 0x6F, 0x74, 0x6F, 0x63, 0x6F, 0x6C, 0x20, 0x68, 0x6F, 0x73, 0x74,
|
||||
0x02,
|
||||
0x02, // Protocol version - Must be at least 0x02
|
||||
0x01
|
||||
};
|
||||
|
||||
Serial.SendCommand(Command, new byte[] { 0x02 });
|
||||
}
|
||||
|
||||
public void SetSecurityMode(byte Mode)
|
||||
{
|
||||
byte[] Command = new byte[2];
|
||||
Command[0] = 0x17;
|
||||
Command[1] = Mode;
|
||||
|
||||
Serial.SendCommand(Command, new byte[] { 0x18 });
|
||||
}
|
||||
|
||||
// Use PartitionID 0x21
|
||||
public void OpenPartition(byte PartitionID)
|
||||
{
|
||||
byte[] Command = new byte[2];
|
||||
Command[0] = 0x1B;
|
||||
Command[1] = PartitionID;
|
||||
|
||||
Serial.SendCommand(Command, new byte[] { 0x1C });
|
||||
}
|
||||
|
||||
public void ClosePartition()
|
||||
{
|
||||
Serial.SendCommand(new byte[] { 0x15 }, new byte[] { 0x16 });
|
||||
}
|
||||
|
||||
public void Flash(UInt32 StartInBytes, Stream Data, UInt32 LengthInBytes = UInt32.MaxValue)
|
||||
{
|
||||
Flash(StartInBytes, Data, null, null, LengthInBytes);
|
||||
}
|
||||
|
||||
public void Flash(UInt32 StartInBytes, Stream Data, Action<int, TimeSpan?> ProgressUpdateCallback, UInt32 LengthInBytes = UInt32.MaxValue)
|
||||
{
|
||||
Flash(StartInBytes, Data, ProgressUpdateCallback, null, LengthInBytes);
|
||||
}
|
||||
|
||||
public void Flash(UInt32 StartInBytes, Stream Data, ProgressUpdater UpdaterPerSector, UInt32 LengthInBytes = UInt32.MaxValue)
|
||||
{
|
||||
Flash(StartInBytes, Data, null, UpdaterPerSector, LengthInBytes);
|
||||
}
|
||||
|
||||
public void Flash(UInt32 StartInBytes, Stream Data, Action<int, TimeSpan?> ProgressUpdateCallback, ProgressUpdater UpdaterPerSector, UInt32 LengthInBytes = UInt32.MaxValue)
|
||||
{
|
||||
long Remaining;
|
||||
if ((LengthInBytes == UInt32.MaxValue) || (LengthInBytes > (Data.Length - Data.Position)))
|
||||
Remaining = Data.Length - Data.Position;
|
||||
else
|
||||
Remaining = LengthInBytes;
|
||||
UInt32 CurrentLength;
|
||||
byte[] Buffer = new byte[0x405];
|
||||
byte[] ResponsePattern = new byte[5];
|
||||
byte[] FinalCommand;
|
||||
Buffer[0] = 0x07;
|
||||
ResponsePattern[0] = 0x08;
|
||||
UInt32 CurrentPosition = StartInBytes;
|
||||
|
||||
ProgressUpdater Progress = UpdaterPerSector;
|
||||
if ((Progress == null) && (ProgressUpdateCallback != null))
|
||||
Progress = new ProgressUpdater(GetSectorCount((UInt64)Remaining), ProgressUpdateCallback);
|
||||
|
||||
while (Remaining > 0)
|
||||
{
|
||||
System.Buffer.BlockCopy(BitConverter.GetBytes(CurrentPosition), 0, Buffer, 1, 4); // Start is in bytes and in Little Endian (on Samsung devices start is in sectors!)
|
||||
System.Buffer.BlockCopy(BitConverter.GetBytes(CurrentPosition), 0, ResponsePattern, 1, 4); // Start is in bytes and in Little Endian (on Samsung devices start is in sectors!)
|
||||
|
||||
if (Remaining >= 0x400)
|
||||
CurrentLength = 0x400;
|
||||
else
|
||||
CurrentLength = (UInt32)Remaining;
|
||||
|
||||
CurrentLength = (uint)Data.Read(Buffer, 5, (int)CurrentLength);
|
||||
|
||||
if (CurrentLength < 0x400)
|
||||
{
|
||||
FinalCommand = new byte[CurrentLength + 5];
|
||||
System.Buffer.BlockCopy(Buffer, 0, FinalCommand, 0, (int)CurrentLength + 5);
|
||||
}
|
||||
else
|
||||
FinalCommand = Buffer;
|
||||
|
||||
Serial.SendCommand(FinalCommand, ResponsePattern);
|
||||
|
||||
CurrentPosition += CurrentLength;
|
||||
Remaining -= CurrentLength;
|
||||
|
||||
if (Progress != null)
|
||||
Progress.IncreaseProgress(GetSectorCount(CurrentLength));
|
||||
}
|
||||
}
|
||||
|
||||
public void Flash(UInt32 StartInBytes, byte[] Data, UInt32 OffsetInBytes = 0, UInt32 LengthInBytes = UInt32.MaxValue)
|
||||
{
|
||||
Flash(StartInBytes, Data, null, null, OffsetInBytes, LengthInBytes);
|
||||
}
|
||||
|
||||
public void Flash(UInt32 StartInBytes, byte[] Data, Action<int, TimeSpan?> ProgressUpdateCallback, UInt32 OffsetInBytes = 0, UInt32 LengthInBytes = UInt32.MaxValue)
|
||||
{
|
||||
Flash(StartInBytes, Data, ProgressUpdateCallback, null, OffsetInBytes, LengthInBytes);
|
||||
}
|
||||
|
||||
public void Flash(UInt32 StartInBytes, byte[] Data, ProgressUpdater UpdaterPerSector, UInt32 OffsetInBytes = 0, UInt32 LengthInBytes = UInt32.MaxValue)
|
||||
{
|
||||
Flash(StartInBytes, Data, null, UpdaterPerSector, OffsetInBytes, LengthInBytes);
|
||||
}
|
||||
|
||||
public void Flash(UInt32 StartInBytes, byte[] Data, Action<int, TimeSpan?> ProgressUpdateCallback, ProgressUpdater UpdaterPerSector, UInt32 OffsetInBytes = 0, UInt32 LengthInBytes = UInt32.MaxValue)
|
||||
{
|
||||
long RemainingBytes;
|
||||
if (OffsetInBytes > (Data.Length - 1))
|
||||
throw new ArgumentException("Wrong offset");
|
||||
if ((LengthInBytes == UInt32.MaxValue) || (LengthInBytes > (Data.Length - OffsetInBytes)))
|
||||
RemainingBytes = Data.Length - OffsetInBytes;
|
||||
else
|
||||
RemainingBytes = LengthInBytes;
|
||||
UInt32 CurrentLength;
|
||||
UInt32 CurrentOffset = OffsetInBytes;
|
||||
byte[] Buffer = new byte[0x405];
|
||||
byte[] ResponsePattern = new byte[5];
|
||||
byte[] FinalCommand;
|
||||
Buffer[0] = 0x07;
|
||||
ResponsePattern[0] = 0x08;
|
||||
UInt32 CurrentPosition = StartInBytes;
|
||||
|
||||
ProgressUpdater Progress = UpdaterPerSector;
|
||||
if ((Progress == null) && (ProgressUpdateCallback != null))
|
||||
Progress = new ProgressUpdater(GetSectorCount((UInt64)RemainingBytes), ProgressUpdateCallback);
|
||||
|
||||
while (RemainingBytes > 0)
|
||||
{
|
||||
System.Buffer.BlockCopy(BitConverter.GetBytes(CurrentPosition), 0, Buffer, 1, 4); // Start position is in bytes and in Little Endian (on Samsung phones the start position is in Sectors!!)
|
||||
System.Buffer.BlockCopy(BitConverter.GetBytes(CurrentPosition), 0, ResponsePattern, 1, 4); // Start position is in bytes and in Little Endian (on Samsung phones the start position is in Sectors!!)
|
||||
|
||||
if (RemainingBytes >= 0x400)
|
||||
CurrentLength = 0x400;
|
||||
else
|
||||
CurrentLength = (UInt32)RemainingBytes;
|
||||
System.Buffer.BlockCopy(Data, (int)CurrentOffset, Buffer, 5, (int)CurrentLength);
|
||||
|
||||
if (CurrentLength < 0x400)
|
||||
{
|
||||
FinalCommand = new byte[CurrentLength + 5];
|
||||
System.Buffer.BlockCopy(Buffer, 0, FinalCommand, 0, (int)CurrentLength + 5);
|
||||
}
|
||||
else
|
||||
FinalCommand = Buffer;
|
||||
|
||||
Serial.SendCommand(FinalCommand, ResponsePattern);
|
||||
|
||||
CurrentPosition += CurrentLength;
|
||||
CurrentOffset += CurrentLength;
|
||||
RemainingBytes -= CurrentLength;
|
||||
|
||||
if (Progress != null)
|
||||
Progress.IncreaseProgress(GetSectorCount(CurrentLength));
|
||||
}
|
||||
}
|
||||
|
||||
public UInt64 GetSectorCount(UInt64 ByteCount)
|
||||
{
|
||||
return (ByteCount / 0x200) + ((ByteCount % 0x200) > 0 ? (UInt64)1 : (UInt64)0);
|
||||
}
|
||||
|
||||
public void Reboot()
|
||||
{
|
||||
Serial.SendCommand(new byte[] { 0x0B }, new byte[] { 0x0C });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
// Copyright (c) 2018, Rene Lergner - wpinternals.net - @Heathcliff74xda
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace WPinternals
|
||||
{
|
||||
internal static class QualcommLoaders
|
||||
{
|
||||
internal static List<QualcommPartition> GetPossibleLoadersForRootKeyHash(string Path, byte[] RootKeyHash)
|
||||
{
|
||||
List<QualcommPartition> Result = new List<QualcommPartition>();
|
||||
|
||||
try
|
||||
{
|
||||
IEnumerable<string> FilePaths = Directory.EnumerateFiles(Path);
|
||||
foreach (string FilePath in FilePaths)
|
||||
{
|
||||
try
|
||||
{
|
||||
FileInfo Info = new FileInfo(FilePath);
|
||||
if (Info.Length <= 0x80000)
|
||||
{
|
||||
QualcommPartition Loader;
|
||||
|
||||
#if DEBUG
|
||||
System.Diagnostics.Debug.Print("Evaluating loader: " + FilePath);
|
||||
#endif
|
||||
|
||||
byte[] Binary = ParseAsHexFile(FilePath);
|
||||
if (Binary == null)
|
||||
Loader = new QualcommPartition(FilePath);
|
||||
else
|
||||
Loader = new QualcommPartition(Binary);
|
||||
|
||||
if ((StructuralComparisons.StructuralEqualityComparer.Equals(Loader.RootKeyHash, RootKeyHash))
|
||||
&& (ByteOperations.FindUnicode(Loader.Binary, "QHSUSB_ARMPRG") != null)) // To detect that this is a loader, and not SBL1 or something. V1 loaders are QHSUSB_ARMPRG. V2 loaders are QHSUSB__BULK. Only V1 supported for now, because V2 only accepts signed payload.
|
||||
{
|
||||
Result.Add(Loader);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
internal static byte[] ParseAsHexFile(string FilePath)
|
||||
{
|
||||
byte[] Result = null;
|
||||
|
||||
try
|
||||
{
|
||||
string[] Lines = File.ReadAllLines(FilePath);
|
||||
byte[] Buffer = null;
|
||||
int BufferSize = 0;
|
||||
|
||||
foreach (string Line in Lines)
|
||||
{
|
||||
if (Line[0] != ':')
|
||||
throw new BadImageFormatException();
|
||||
|
||||
byte[] LineBytes = Converter.ConvertStringToHex(Line.Substring(1));
|
||||
|
||||
if ((LineBytes[0] + 5) != LineBytes.Length)
|
||||
throw new BadImageFormatException();
|
||||
|
||||
if (Buffer == null)
|
||||
Buffer = new byte[0x40000];
|
||||
|
||||
if (LineBytes[3] == 0) // This is mem data
|
||||
{
|
||||
System.Buffer.BlockCopy(LineBytes, 4, Buffer, BufferSize, LineBytes[0]);
|
||||
BufferSize += LineBytes[0];
|
||||
}
|
||||
}
|
||||
|
||||
Result = new byte[BufferSize];
|
||||
System.Buffer.BlockCopy(Buffer, 0, Result, 0, BufferSize);
|
||||
}
|
||||
catch { }
|
||||
|
||||
return Result;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,166 @@
|
||||
// Copyright (c) 2018, Rene Lergner - wpinternals.net - @Heathcliff74xda
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Security.Cryptography;
|
||||
|
||||
namespace WPinternals
|
||||
{
|
||||
internal enum QualcommPartitionHeaderType
|
||||
{
|
||||
Long,
|
||||
Short
|
||||
};
|
||||
|
||||
internal class QualcommPartition
|
||||
{
|
||||
internal byte[] Binary;
|
||||
internal uint HeaderOffset;
|
||||
internal QualcommPartitionHeaderType HeaderType;
|
||||
internal uint ImageOffset;
|
||||
internal uint ImageAddress;
|
||||
internal uint ImageSize;
|
||||
internal uint CodeSize;
|
||||
internal uint SignatureAddress;
|
||||
internal uint SignatureSize;
|
||||
internal uint SignatureOffset;
|
||||
internal uint CertificatesAddress;
|
||||
internal uint CertificatesSize;
|
||||
internal uint CertificatesOffset;
|
||||
internal byte[] RootKeyHash = null;
|
||||
|
||||
internal QualcommPartition(string Path): this(File.ReadAllBytes(Path)) { }
|
||||
|
||||
internal QualcommPartition(byte[] Binary, uint Offset = 0)
|
||||
{
|
||||
#if DEBUG
|
||||
System.Diagnostics.Debug.Print("Loader: " + Converter.ConvertHexToString(new SHA256Managed().ComputeHash(Binary, 0, Binary.Length), ""));
|
||||
#endif
|
||||
|
||||
this.Binary = Binary;
|
||||
|
||||
byte[] LongHeaderPattern = new byte[] { 0xD1, 0xDC, 0x4B, 0x84, 0x34, 0x10, 0xD7, 0x73, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
|
||||
byte[] LongHeaderMask = new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
||||
|
||||
if (ByteOperations.FindPattern(Binary, Offset, 4, new byte[] { 0x7F, 0x45, 0x4C, 0x46 }, new byte[] { 0x00, 0x00, 0x00, 0x00 }, null) == 0)
|
||||
{
|
||||
// This is an ELF image
|
||||
// First program header is a reference to the elf-header
|
||||
// Second program header is a reference to the signed hash-table
|
||||
HeaderType = QualcommPartitionHeaderType.Short;
|
||||
UInt32 ProgramHeaderOffset;
|
||||
UInt16 ProgramHeaderEntrySize;
|
||||
UInt32 HashTableProgramHeaderOffset;
|
||||
if (Binary[Offset + 0x04] == 1)
|
||||
{
|
||||
// 32-bit elf image
|
||||
ProgramHeaderOffset = Offset + ByteOperations.ReadUInt32(Binary, Offset + 0x1c);
|
||||
ProgramHeaderEntrySize = ByteOperations.ReadUInt16(Binary, Offset + 0x2a);
|
||||
HashTableProgramHeaderOffset = ProgramHeaderOffset + ProgramHeaderEntrySize;
|
||||
ImageOffset = Offset + ByteOperations.ReadUInt32(Binary, HashTableProgramHeaderOffset + 0x04);
|
||||
HeaderOffset = ImageOffset + 8;
|
||||
}
|
||||
else if (Binary[Offset + 0x04] == 2)
|
||||
{
|
||||
// 64-bit elf image
|
||||
ProgramHeaderOffset = Offset + ByteOperations.ReadUInt32(Binary, Offset + 0x20);
|
||||
ProgramHeaderEntrySize = ByteOperations.ReadUInt16(Binary, Offset + 0x36);
|
||||
HashTableProgramHeaderOffset = ProgramHeaderOffset + ProgramHeaderEntrySize;
|
||||
ImageOffset = Offset + (UInt32)ByteOperations.ReadUInt64(Binary, HashTableProgramHeaderOffset + 0x08);
|
||||
HeaderOffset = ImageOffset + 8;
|
||||
}
|
||||
else
|
||||
throw new WPinternalsException("Invalid programmer");
|
||||
}
|
||||
else if (ByteOperations.FindPattern(Binary, Offset, (uint)LongHeaderPattern.Length, LongHeaderPattern, LongHeaderMask, null) == null)
|
||||
{
|
||||
HeaderType = QualcommPartitionHeaderType.Short;
|
||||
ImageOffset = Offset;
|
||||
HeaderOffset = ImageOffset + 8;
|
||||
}
|
||||
else
|
||||
{
|
||||
HeaderType = QualcommPartitionHeaderType.Long;
|
||||
ImageOffset = Offset;
|
||||
HeaderOffset = ImageOffset + (uint)LongHeaderPattern.Length;
|
||||
}
|
||||
|
||||
if (ByteOperations.ReadUInt32(Binary, HeaderOffset + 0X00) != 0)
|
||||
ImageOffset = ByteOperations.ReadUInt32(Binary, HeaderOffset + 0X00);
|
||||
else if (HeaderType == QualcommPartitionHeaderType.Short)
|
||||
ImageOffset += 0x28;
|
||||
else
|
||||
ImageOffset += 0x50;
|
||||
|
||||
ImageAddress = ByteOperations.ReadUInt32(Binary, HeaderOffset + 0X04);
|
||||
ImageSize = ByteOperations.ReadUInt32(Binary, HeaderOffset + 0X08);
|
||||
CodeSize = ByteOperations.ReadUInt32(Binary, HeaderOffset + 0X0C);
|
||||
SignatureAddress = ByteOperations.ReadUInt32(Binary, HeaderOffset + 0X10);
|
||||
SignatureSize = ByteOperations.ReadUInt32(Binary, HeaderOffset + 0X14);
|
||||
SignatureOffset = SignatureAddress - ImageAddress + ImageOffset;
|
||||
CertificatesAddress = ByteOperations.ReadUInt32(Binary, HeaderOffset + 0X18);
|
||||
CertificatesSize = ByteOperations.ReadUInt32(Binary, HeaderOffset + 0X1C);
|
||||
CertificatesOffset = CertificatesAddress - ImageAddress + ImageOffset;
|
||||
|
||||
uint CurrentCertificateOffset = CertificatesOffset;
|
||||
uint CertificateSize = 0;
|
||||
while (CurrentCertificateOffset < (CertificatesOffset + CertificatesSize))
|
||||
{
|
||||
if ((Binary[CurrentCertificateOffset] == 0x30) && (Binary[CurrentCertificateOffset + 1] == 0x82))
|
||||
{
|
||||
CertificateSize = (uint)(Binary[CurrentCertificateOffset + 2] * 0x100) + Binary[CurrentCertificateOffset + 3] + 4; // Big endian!
|
||||
|
||||
if ((CurrentCertificateOffset + CertificateSize) == (CertificatesOffset + CertificatesSize))
|
||||
{
|
||||
// This is the last certificate. So this is the root key.
|
||||
RootKeyHash = new SHA256Managed().ComputeHash(Binary, (int)CurrentCertificateOffset, (int)CertificateSize);
|
||||
|
||||
#if DEBUG
|
||||
System.Diagnostics.Debug.Print("RKH: " + Converter.ConvertHexToString(RootKeyHash, ""));
|
||||
#endif
|
||||
}
|
||||
#if DEBUG
|
||||
else
|
||||
{
|
||||
System.Diagnostics.Debug.Print("Cert: " + Converter.ConvertHexToString(new SHA256Managed().ComputeHash(Binary, (int)CurrentCertificateOffset, (int)CertificateSize), ""));
|
||||
}
|
||||
#endif
|
||||
CurrentCertificateOffset += CertificateSize;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((RootKeyHash == null) && (CurrentCertificateOffset > CertificatesOffset))
|
||||
{
|
||||
CurrentCertificateOffset -= CertificateSize;
|
||||
|
||||
// This is the last certificate. So this is the root key.
|
||||
RootKeyHash = new SHA256Managed().ComputeHash(Binary, (int)CurrentCertificateOffset, (int)CertificateSize);
|
||||
|
||||
#if DEBUG
|
||||
System.Diagnostics.Debug.Print("RKH: " + Converter.ConvertHexToString(RootKeyHash, ""));
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,313 @@
|
||||
// Copyright (c) 2018, Rene Lergner - wpinternals.net - @Heathcliff74xda
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace WPinternals
|
||||
{
|
||||
internal enum SaharaMode: uint
|
||||
{
|
||||
ImageTransferPending = 0x00,
|
||||
ImagetransferComplete = 0x01,
|
||||
MemoryDebug = 0x02,
|
||||
Command = 0x03
|
||||
}
|
||||
|
||||
internal delegate void ReadyHandler();
|
||||
|
||||
internal class QualcommSahara
|
||||
{
|
||||
private QualcommSerial Serial;
|
||||
|
||||
public QualcommSahara(QualcommSerial Serial)
|
||||
{
|
||||
Serial.EncodeCommands = false;
|
||||
Serial.DecodeResponses = false;
|
||||
this.Serial = Serial;
|
||||
}
|
||||
|
||||
public bool SendImage(string Path)
|
||||
{
|
||||
bool Result = true;
|
||||
|
||||
LogFile.Log("Sending programmer: " + Path, LogType.FileOnly);
|
||||
|
||||
int Step = 0;
|
||||
UInt32 Offset = 0;
|
||||
UInt32 Length = 0;
|
||||
|
||||
byte[] ImageBuffer = null;
|
||||
try
|
||||
{
|
||||
Step = 1;
|
||||
byte[] Hello = null;
|
||||
Hello = Serial.GetResponse(new byte[] { 0x01, 0x00, 0x00, 0x00 });
|
||||
|
||||
// Incoming Hello packet:
|
||||
// 00000001 = Hello command id
|
||||
// xxxxxxxx = Length
|
||||
// xxxxxxxx = Protocol version
|
||||
// xxxxxxxx = Supported version
|
||||
// xxxxxxxx = Max packet length
|
||||
// xxxxxxxx = Expected mode
|
||||
// 6 dwords reserved space
|
||||
LogFile.Log("Protocol: 0x" + ByteOperations.ReadUInt32(Hello, 0x08).ToString("X8"), LogType.FileOnly);
|
||||
LogFile.Log("Supported: 0x" + ByteOperations.ReadUInt32(Hello, 0x0C).ToString("X8"), LogType.FileOnly);
|
||||
LogFile.Log("MaxLength: 0x" + ByteOperations.ReadUInt32(Hello, 0x10).ToString("X8"), LogType.FileOnly);
|
||||
LogFile.Log("Mode: 0x" + ByteOperations.ReadUInt32(Hello, 0x14).ToString("X8"), LogType.FileOnly);
|
||||
|
||||
// Packet:
|
||||
// 00000002 = Hello response command id
|
||||
// 00000030 = Length
|
||||
// 00000002 = Protocol version
|
||||
// 00000001 = Supported version
|
||||
// 00000000 = Status OK
|
||||
// 00000000 = Mode
|
||||
// rest is reserved space
|
||||
Step = 2;
|
||||
byte[] HelloResponse = new byte[] {
|
||||
0x02, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||
};
|
||||
Serial.SendData(HelloResponse);
|
||||
|
||||
Step = 3;
|
||||
using (System.IO.FileStream FileStream = new System.IO.FileStream(Path, System.IO.FileMode.Open, System.IO.FileAccess.Read))
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
Step = 4;
|
||||
byte[] ReadDataRequest = Serial.GetResponse(null);
|
||||
UInt32 ResponseID = ByteOperations.ReadUInt32(ReadDataRequest, 0);
|
||||
if (ResponseID == 4)
|
||||
break;
|
||||
if (ResponseID != 3)
|
||||
{
|
||||
Step = 5;
|
||||
throw new BadConnectionException();
|
||||
}
|
||||
|
||||
Offset = ByteOperations.ReadUInt32(ReadDataRequest, 0x0C);
|
||||
Length = ByteOperations.ReadUInt32(ReadDataRequest, 0x10);
|
||||
if ((ImageBuffer == null) || (ImageBuffer.Length != Length))
|
||||
ImageBuffer = new byte[Length];
|
||||
if (FileStream.Position != Offset)
|
||||
FileStream.Seek(Offset, System.IO.SeekOrigin.Begin);
|
||||
|
||||
Step = 6;
|
||||
FileStream.Read(ImageBuffer, 0, (int)Length);
|
||||
|
||||
Step = 7;
|
||||
Serial.SendData(ImageBuffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception Ex)
|
||||
{
|
||||
LogFile.LogException(Ex, LogType.FileAndConsole, Step.ToString() + " 0x" + Offset.ToString("X8") + " 0x" + Length.ToString("X8"));
|
||||
Result = false;
|
||||
}
|
||||
|
||||
if (Result)
|
||||
LogFile.Log("Programmer loaded into phone memory", LogType.FileOnly);
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
public bool Handshake()
|
||||
{
|
||||
bool Result = true;
|
||||
|
||||
try
|
||||
{
|
||||
byte[] Hello = Serial.GetResponse(new byte[] { 0x01, 0x00, 0x00, 0x00 });
|
||||
|
||||
// Incoming Hello packet:
|
||||
// 00000001 = Hello command id
|
||||
// xxxxxxxx = Length
|
||||
// xxxxxxxx = Protocol version
|
||||
// xxxxxxxx = Supported version
|
||||
// xxxxxxxx = Max packet length
|
||||
// xxxxxxxx = Expected mode
|
||||
// 6 dwords reserved space
|
||||
LogFile.Log("Protocol: 0x" + ByteOperations.ReadUInt32(Hello, 0x08).ToString("X8"), LogType.FileOnly);
|
||||
LogFile.Log("Supported: 0x" + ByteOperations.ReadUInt32(Hello, 0x0C).ToString("X8"), LogType.FileOnly);
|
||||
LogFile.Log("MaxLength: 0x" + ByteOperations.ReadUInt32(Hello, 0x10).ToString("X8"), LogType.FileOnly);
|
||||
LogFile.Log("Mode: 0x" + ByteOperations.ReadUInt32(Hello, 0x14).ToString("X8"), LogType.FileOnly);
|
||||
|
||||
byte[] HelloResponse = new byte[] {
|
||||
0x02, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||
};
|
||||
|
||||
byte[] Ready = Serial.SendCommand(HelloResponse, new byte[] { 0x03, 0x00, 0x00, 0x00 });
|
||||
}
|
||||
catch
|
||||
{
|
||||
Result = false;
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
public void ResetSahara()
|
||||
{
|
||||
Serial.SendCommand(new byte[] { 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00 }, new byte[] { 0x08, 0x00, 0x00, 0x00 });
|
||||
}
|
||||
|
||||
public bool ConnectToProgrammerInTestMode()
|
||||
{
|
||||
// Behaviour of old firehose:
|
||||
// Takes about 20 ms to be started.
|
||||
// Then PC has to start talking to the phone.
|
||||
// Behaviour of new firehose:
|
||||
// After 2000 ms the firehose starts talking to the PC
|
||||
//
|
||||
// For the duration of 2.5 seconds we will send Hello packages
|
||||
// And also wait for incoming messages
|
||||
// An incoming message can be a response to our outgoing Hello packet (read incoming until "response value")
|
||||
// Or it can be an incoming Hello-packet from the programmer (always 2 packets, starting with "Chip serial num")
|
||||
// Sending the hello-packet can succeed immediately, or it can timeout.
|
||||
// When sending succeeds, an answer should be incoming immediately to complete the handshake.
|
||||
// When an incoming Hello was received, the phone still expects to receive another Hello.
|
||||
|
||||
byte[] HelloPacketFromPcToProgrammer = new byte[0x20C];
|
||||
ByteOperations.WriteUInt32(HelloPacketFromPcToProgrammer, 0, 0x57503730);
|
||||
ByteOperations.WriteUInt32(HelloPacketFromPcToProgrammer, 0x28, 0x57503730);
|
||||
ByteOperations.WriteUInt32(HelloPacketFromPcToProgrammer, 0x208, 0x57503730);
|
||||
ByteOperations.WriteUInt16(HelloPacketFromPcToProgrammer, 0x48, 0x4445);
|
||||
|
||||
int HelloSendCount = 0;
|
||||
bool HandshakeCompleted = false;
|
||||
string Incoming;
|
||||
do
|
||||
{
|
||||
Serial.SetTimeOut(200);
|
||||
HelloSendCount++;
|
||||
try
|
||||
{
|
||||
LogFile.Log("Send Hello to programmer (" + HelloSendCount.ToString() + ")", LogType.FileOnly);
|
||||
Serial.SendData(HelloPacketFromPcToProgrammer);
|
||||
LogFile.Log("Hello packet accepted", LogType.FileOnly);
|
||||
}
|
||||
catch
|
||||
{
|
||||
LogFile.Log("Hello packet not accepted", LogType.FileOnly);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Serial.SetTimeOut(500);
|
||||
Incoming = System.Text.Encoding.ASCII.GetString(Serial.GetResponse(null));
|
||||
LogFile.Log("In: " + Incoming, LogType.FileOnly);
|
||||
Serial.SetTimeOut(200);
|
||||
if (Incoming.Contains("Chip serial num"))
|
||||
{
|
||||
Incoming = System.Text.Encoding.ASCII.GetString(Serial.GetResponse(null));
|
||||
LogFile.Log("In: " + Incoming, LogType.FileOnly);
|
||||
LogFile.Log("Incoming Hello-packets received", LogType.FileOnly);
|
||||
}
|
||||
|
||||
while (Incoming.IndexOf("response value") < 0)
|
||||
{
|
||||
Incoming = System.Text.Encoding.ASCII.GetString(Serial.GetResponse(null));
|
||||
LogFile.Log("In: " + Incoming, LogType.FileOnly);
|
||||
};
|
||||
|
||||
LogFile.Log("Incoming Hello-response received", LogType.FileOnly);
|
||||
HandshakeCompleted = true;
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
while (!HandshakeCompleted && (HelloSendCount < 6));
|
||||
|
||||
if (HandshakeCompleted)
|
||||
LogFile.Log("Handshake completed with programmer in testmode", LogType.FileOnly);
|
||||
else
|
||||
LogFile.Log("Handshake with programmer failed", LogType.FileOnly);
|
||||
|
||||
return HandshakeCompleted;
|
||||
}
|
||||
|
||||
public async Task<bool> Reset(string ProgrammerPath)
|
||||
{
|
||||
bool SendImageResult = await Task.Run(() => SendImage(ProgrammerPath));
|
||||
if (!SendImageResult)
|
||||
return false;
|
||||
|
||||
await Task.Run(() => StartProgrammer());
|
||||
|
||||
bool Connected = await Task.Run(() => ConnectToProgrammerInTestMode());
|
||||
if (!Connected)
|
||||
return false;
|
||||
|
||||
LogFile.Log("Rebooting phone", LogType.FileAndConsole);
|
||||
string Command03 = "<?xml version=\"1.0\" ?><data><power value=\"reset\"/></data>";
|
||||
LogFile.Log("Out: " + Command03, LogType.FileOnly);
|
||||
Serial.SendData(System.Text.Encoding.ASCII.GetBytes(Command03));
|
||||
|
||||
string Incoming;
|
||||
do
|
||||
{
|
||||
Incoming = System.Text.Encoding.ASCII.GetString(Serial.GetResponse(null));
|
||||
LogFile.Log("In: " + Incoming, LogType.FileOnly);
|
||||
}
|
||||
while (Incoming.IndexOf("response value") < 0);
|
||||
|
||||
// Workaround for problem
|
||||
// SerialPort is sometimes not disposed correctly when the device is already removed.
|
||||
// So explicitly dispose here
|
||||
Serial.Close();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void SwitchMode(SaharaMode Mode)
|
||||
{
|
||||
byte[] SwitchModeCommand = new byte[] { 0x0C, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
||||
ByteOperations.WriteUInt32(SwitchModeCommand, 8, (UInt32)Mode);
|
||||
byte[] ResponsePattern = null;
|
||||
switch (Mode)
|
||||
{
|
||||
case SaharaMode.ImageTransferPending:
|
||||
ResponsePattern = new byte[] { 0x04, 0x00, 0x00, 0x00 };
|
||||
break;
|
||||
case SaharaMode.MemoryDebug:
|
||||
ResponsePattern = new byte[] { 0x09, 0x00, 0x00, 0x00 };
|
||||
break;
|
||||
case SaharaMode.Command:
|
||||
ResponsePattern = new byte[] { 0x0B, 0x00, 0x00, 0x00 };
|
||||
break;
|
||||
}
|
||||
Serial.SendCommand(SwitchModeCommand, ResponsePattern);
|
||||
}
|
||||
|
||||
public void StartProgrammer()
|
||||
{
|
||||
LogFile.Log("Starting programmer", LogType.FileAndConsole);
|
||||
byte[] DoneCommand = new byte[] { 0x05, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00 };
|
||||
byte[] DoneResponse = Serial.SendCommand(DoneCommand, new byte[] { 0x06, 0x00, 0x00, 0x00 });
|
||||
LogFile.Log("Programmer being launched on phone", LogType.FileOnly);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,349 @@
|
||||
// Copyright (c) 2018, Rene Lergner - wpinternals.net - @Heathcliff74xda
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
using MadWizard.WinUSBNet;
|
||||
using System;
|
||||
using System.IO.Ports;
|
||||
|
||||
namespace WPinternals
|
||||
{
|
||||
internal class QualcommSerial: IDisposable
|
||||
{
|
||||
private bool Disposed = false;
|
||||
private SerialPort Port = null;
|
||||
private USBDevice USBDevice = null;
|
||||
private CRC16 CRC16;
|
||||
|
||||
public bool EncodeCommands = true;
|
||||
public bool DecodeResponses = true;
|
||||
|
||||
public QualcommSerial(string DevicePath)
|
||||
{
|
||||
CRC16 = new CRC16(0x1189, 0xFFFF, 0xFFFF);
|
||||
|
||||
string[] DevicePathElements = DevicePath.Split(new char[] { '#' });
|
||||
if (string.Compare(DevicePathElements[3], "{86E0D1E0-8089-11D0-9CE4-08003E301F73}", true) == 0)
|
||||
{
|
||||
string PortName = (string)Microsoft.Win32.Registry.GetValue(@"HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\USB\" + DevicePathElements[1] + @"\" + DevicePathElements[2] + @"\Device Parameters", "PortName", null);
|
||||
if (PortName != null)
|
||||
{
|
||||
Port = new SerialPort(PortName, 115200);
|
||||
Port.ReadTimeout = 1000;
|
||||
Port.WriteTimeout = 1000;
|
||||
Port.Open();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
this.USBDevice = new USBDevice(DevicePath);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
|
||||
public void SendData(byte[] Data)
|
||||
{
|
||||
byte[] FormattedData;
|
||||
if (EncodeCommands)
|
||||
FormattedData = FormatCommand(Data);
|
||||
else
|
||||
FormattedData = Data;
|
||||
|
||||
if (Port != null)
|
||||
Port.Write(FormattedData, 0, FormattedData.Length);
|
||||
if (USBDevice != null)
|
||||
USBDevice.OutputPipe.Write(FormattedData);
|
||||
}
|
||||
|
||||
public byte[] SendCommand(byte[] Command, byte[] ResponsePattern)
|
||||
{
|
||||
byte[] FormattedCommand;
|
||||
if (EncodeCommands)
|
||||
FormattedCommand = FormatCommand(Command);
|
||||
else
|
||||
FormattedCommand = Command;
|
||||
|
||||
if (Port != null)
|
||||
Port.Write(FormattedCommand, 0, FormattedCommand.Length);
|
||||
if (USBDevice != null)
|
||||
USBDevice.OutputPipe.Write(FormattedCommand);
|
||||
|
||||
return GetResponse(ResponsePattern);
|
||||
}
|
||||
|
||||
internal byte[] GetResponse(byte[] ResponsePattern)
|
||||
{
|
||||
byte[] ResponseBuffer = new byte[0x2000];
|
||||
int Length = 0;
|
||||
bool IsIncomplete = false;
|
||||
|
||||
do
|
||||
{
|
||||
IsIncomplete = false;
|
||||
|
||||
try
|
||||
{
|
||||
int BytesRead = 0;
|
||||
|
||||
if (Port != null)
|
||||
BytesRead = Port.Read(ResponseBuffer, Length, ResponseBuffer.Length - Length);
|
||||
if (USBDevice != null)
|
||||
BytesRead = USBDevice.InputPipe.Read(ResponseBuffer);
|
||||
|
||||
if (BytesRead == 0)
|
||||
{
|
||||
LogFile.Log("Emergency mode of phone is ignoring us", LogType.FileAndConsole);
|
||||
throw new BadMessageException();
|
||||
}
|
||||
|
||||
Length += BytesRead;
|
||||
byte[] DecodedResponse;
|
||||
if (DecodeResponses)
|
||||
{
|
||||
DecodedResponse = DecodeResponse(ResponseBuffer, (UInt32)Length);
|
||||
}
|
||||
else
|
||||
{
|
||||
DecodedResponse = new byte[Length];
|
||||
Buffer.BlockCopy(ResponseBuffer, 0, DecodedResponse, 0, Length);
|
||||
}
|
||||
|
||||
if (ResponsePattern != null)
|
||||
{
|
||||
for (int i = 0; i < ResponsePattern.Length; i++)
|
||||
if (DecodedResponse[i] != ResponsePattern[i])
|
||||
{
|
||||
byte[] LogResponse = new byte[DecodedResponse.Length < 0x10 ? DecodedResponse.Length: 0x10];
|
||||
LogFile.Log("Qualcomm serial response: " + Converter.ConvertHexToString(LogResponse, ""), LogType.FileOnly);
|
||||
LogFile.Log("Expected: " + Converter.ConvertHexToString(ResponsePattern, ""), LogType.FileOnly);
|
||||
throw new BadMessageException();
|
||||
}
|
||||
}
|
||||
|
||||
return DecodedResponse;
|
||||
}
|
||||
catch (IncompleteMessageException)
|
||||
{
|
||||
IsIncomplete = true;
|
||||
}
|
||||
catch { } // Will be rethrown as BadConnectionException
|
||||
}
|
||||
while (IsIncomplete);
|
||||
|
||||
if (Port != null)
|
||||
Port.DiscardInBuffer();
|
||||
if (USBDevice != null)
|
||||
USBDevice.InputPipe.Flush();
|
||||
|
||||
throw new BadConnectionException();
|
||||
}
|
||||
|
||||
private byte[] FormatCommand(byte[] Command)
|
||||
{
|
||||
if ((Command == null) || (Command.Length == 0))
|
||||
throw new BadMessageException();
|
||||
|
||||
byte[] Decoded = new byte[(Command.Length * 2) + 4];
|
||||
int Length = 0;
|
||||
|
||||
Decoded[Length++] = 0x7E;
|
||||
|
||||
for (int i = 0; i < Command.Length; i++)
|
||||
{
|
||||
if ((Command[i] == 0x7D) || (Command[i] == 0x7E))
|
||||
{
|
||||
Decoded[Length++] = 0x7D;
|
||||
Decoded[Length++] = (byte)(Command[i] ^ 0x20);
|
||||
}
|
||||
else
|
||||
Decoded[Length++] = Command[i];
|
||||
}
|
||||
|
||||
UInt16 Checksum = CRC16.CalculateChecksum(Command);
|
||||
if (((byte)(Checksum & 0xFF) == 0x7D) || ((byte)(Checksum & 0xFF) == 0x7E))
|
||||
{
|
||||
Decoded[Length++] = 0x7D;
|
||||
Decoded[Length++] = (byte)((Checksum & 0xFF) ^ 0x20);
|
||||
}
|
||||
else
|
||||
Decoded[Length++] = (byte)(Checksum & 0xFF);
|
||||
if (((byte)(Checksum >> 8) == 0x7D) || ((byte)(Checksum >> 8) == 0x7E))
|
||||
{
|
||||
Decoded[Length++] = 0x7D;
|
||||
Decoded[Length++] = (byte)((Checksum >> 8) ^ 0x20);
|
||||
}
|
||||
else
|
||||
Decoded[Length++] = (byte)(Checksum >> 8);
|
||||
Decoded[Length++] = 0x7E;
|
||||
|
||||
if (Length > 0)
|
||||
{
|
||||
byte[] Result = new byte[Length];
|
||||
Buffer.BlockCopy(Decoded, 0, Result, 0, Length);
|
||||
return Result;
|
||||
}
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
private byte[] DecodeResponse(byte[] Response, UInt32 Length)
|
||||
{
|
||||
if ((Response == null) || (Response.Length == 0) || (Response[0] != 0x7E))
|
||||
throw new BadMessageException();
|
||||
|
||||
UInt32 SourceLength = Length;
|
||||
Length = 0;
|
||||
UInt32 SourcePos = 1;
|
||||
|
||||
byte[] Message = new byte[SourceLength];
|
||||
|
||||
while (SourcePos < SourceLength)
|
||||
{
|
||||
if (Response[SourcePos] == 0x7E)
|
||||
break;
|
||||
if (Response[SourcePos] == 0x7D)
|
||||
Message[Length++] = (byte)(Response[++SourcePos] ^ 0x20);
|
||||
else
|
||||
Message[Length++] = Response[SourcePos];
|
||||
SourcePos++;
|
||||
}
|
||||
|
||||
if (SourcePos == SourceLength) throw new IncompleteMessageException();
|
||||
|
||||
if (Length < 3) throw new BadMessageException();
|
||||
|
||||
byte[] TrimmedMessage = new byte[Length - 2];
|
||||
Buffer.BlockCopy(Message, 0, TrimmedMessage, 0, (int)(Length - 2));
|
||||
|
||||
UInt16 Checksum = CRC16.CalculateChecksum(TrimmedMessage);
|
||||
if (((byte)(Checksum & 0xFF) != Message[Length - 2]) || ((byte)(Checksum >> 8) != Message[Length - 1]))
|
||||
throw new BadMessageException();
|
||||
|
||||
return TrimmedMessage;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
~QualcommSerial()
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
if (Port != null) Port.Close();
|
||||
if (USBDevice != null) USBDevice.Dispose();
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (Disposed)
|
||||
return;
|
||||
|
||||
if (disposing)
|
||||
{
|
||||
// Other disposables
|
||||
}
|
||||
|
||||
// Clean unmanaged resources here.
|
||||
Close();
|
||||
|
||||
Disposed = true;
|
||||
}
|
||||
|
||||
internal void SetTimeOut(int v)
|
||||
{
|
||||
if (USBDevice != null)
|
||||
USBDevice.ControlPipeTimeout = v;
|
||||
if (Port != null)
|
||||
{
|
||||
Port.ReadTimeout = v;
|
||||
Port.WriteTimeout = v;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class IncompleteMessageException : Exception { };
|
||||
public class BadMessageException : Exception { };
|
||||
public class BadConnectionException : Exception { };
|
||||
|
||||
public class CRC16
|
||||
{
|
||||
private UInt16[] ChecksumTable =
|
||||
new UInt16[] {
|
||||
0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
|
||||
0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
|
||||
0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
|
||||
0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
|
||||
0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
|
||||
0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
|
||||
0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
|
||||
0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
|
||||
0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
|
||||
0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
|
||||
0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
|
||||
0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
|
||||
0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
|
||||
0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
|
||||
0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
|
||||
0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
|
||||
0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
|
||||
0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
|
||||
0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
|
||||
0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
|
||||
0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
|
||||
0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
|
||||
0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
|
||||
0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
|
||||
0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
|
||||
0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
|
||||
0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
|
||||
0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
|
||||
0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
|
||||
0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
|
||||
0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
|
||||
0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
|
||||
};
|
||||
|
||||
private UInt16 Seed, FinalXor;
|
||||
|
||||
public CRC16(UInt16 Polynomial, UInt16 Seed, UInt16 FinalXor)
|
||||
{
|
||||
this.Seed = Seed;
|
||||
this.FinalXor = FinalXor;
|
||||
}
|
||||
|
||||
public UInt16 CalculateChecksum(byte[] Bytes)
|
||||
{
|
||||
UInt16 Crc = Seed;
|
||||
for (int i = 0; i < Bytes.Length; ++i)
|
||||
{
|
||||
Crc = (UInt16)((Crc >> 8) ^ ChecksumTable[(byte)(Crc ^ Bytes[i])]); // Qualcomm implementation
|
||||
}
|
||||
return (UInt16)(Crc ^ FinalXor);
|
||||
}
|
||||
}
|
||||
}
|
||||
+113
@@ -0,0 +1,113 @@
|
||||
// Copyright (c) 2018, Rene Lergner - wpinternals.net - @Heathcliff74xda
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
using System;
|
||||
|
||||
namespace WPinternals
|
||||
{
|
||||
internal class SBL1: QualcommPartition
|
||||
{
|
||||
internal SBL1(byte[] Binary): base(Binary, 0x2800) { }
|
||||
|
||||
internal byte[] GenerateExtraSector(byte[] PartitionHeader)
|
||||
{
|
||||
UInt32? Offset = ByteOperations.FindPattern(Binary,
|
||||
new byte[] {
|
||||
0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||
}, null, null);
|
||||
if (Offset == null)
|
||||
throw new BadImageFormatException();
|
||||
|
||||
UInt32 PartitionLoaderTableOffset = (UInt32)Offset;
|
||||
|
||||
byte[] FoundPattern = new byte[0x10];
|
||||
Offset = ByteOperations.FindPattern(Binary,
|
||||
new byte[] {
|
||||
0x04, 0x00, 0x9F, 0xE5, 0x28, 0x00, 0xD0, 0xE5, 0x1E, 0xFF, 0x2F, 0xE1, 0xFF, 0xFF, 0xFF, 0xFF
|
||||
},
|
||||
new byte[] {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF
|
||||
},
|
||||
FoundPattern);
|
||||
if (Offset == null)
|
||||
throw new BadImageFormatException();
|
||||
|
||||
UInt32 SharedMemoryAddress = ByteOperations.ReadUInt32(FoundPattern, 0x0C);
|
||||
UInt32 GlobalIsSecurityEnabledAddress = SharedMemoryAddress + 0x28;
|
||||
|
||||
Offset = ByteOperations.FindPattern(Binary,
|
||||
new byte[] {
|
||||
0x01, 0xFF, 0xA0, 0xE3, 0xFF, 0xFF, 0xA0, 0xE1, 0x1C, 0xD0, 0x8D, 0xE2, 0xF0, 0x4F, 0xBD, 0xE8,
|
||||
0x1E, 0xFF, 0x2F, 0xE1
|
||||
},
|
||||
new byte[] {
|
||||
0x00, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00
|
||||
},
|
||||
null);
|
||||
if (Offset == null)
|
||||
throw new BadImageFormatException();
|
||||
|
||||
UInt32 ReturnAddress = (UInt32)Offset - ImageOffset + ImageAddress;
|
||||
|
||||
byte[] Sector = new byte[0x200];
|
||||
Array.Clear(Sector, 0, 0x200);
|
||||
|
||||
byte[] Content = new byte[] {
|
||||
0x16, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x28, 0xBD, 0x02, 0x00,
|
||||
0xD8, 0x01, 0x00, 0x00, 0xD8, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA0, 0xE3, 0x3C, 0x10, 0x9F, 0xE5,
|
||||
0x00, 0x00, 0xC1, 0xE5, 0x38, 0x00, 0x9F, 0xE5, 0x38, 0x10, 0x9F, 0xE5, 0x00, 0x00, 0x81, 0xE5,
|
||||
0x34, 0x10, 0x9F, 0xE5, 0x00, 0x00, 0x81, 0xE5, 0x30, 0x00, 0x9F, 0xE5, 0x20, 0x10, 0x9F, 0xE5,
|
||||
0x2C, 0x30, 0x9F, 0xE5, 0x00, 0x20, 0x90, 0xE5, 0x00, 0x20, 0x81, 0xE5, 0x04, 0x00, 0x80, 0xE2,
|
||||
0x04, 0x10, 0x81, 0xE2, 0x03, 0x00, 0x50, 0xE1, 0xF9, 0xFF, 0xFF, 0xBA, 0x14, 0xF0, 0x9F, 0xE5,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x02, 0x00, 0x90, 0xBF, 0x02, 0x00, 0xD0, 0xBF, 0x02, 0x00,
|
||||
0xA0, 0xBD, 0x02, 0x00, 0xA0, 0xBE, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||
};
|
||||
|
||||
byte[] PartitionTypeGuid = new byte[] {
|
||||
0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74
|
||||
};
|
||||
|
||||
Buffer.BlockCopy(Content, 0, Sector, 0, Content.Length);
|
||||
|
||||
// Overwrite first part of partition-header with model-specific header
|
||||
Buffer.BlockCopy(PartitionHeader, 0, Sector, 0, PartitionHeader.Length);
|
||||
|
||||
ByteOperations.WriteUInt32(Sector, 0x70, GlobalIsSecurityEnabledAddress);
|
||||
ByteOperations.WriteUInt32(Sector, 0x88, ReturnAddress);
|
||||
|
||||
Buffer.BlockCopy(Binary, (int)PartitionLoaderTableOffset, Sector, 0xA0, 0x50);
|
||||
ByteOperations.WriteUInt32(Sector, 0xA0 + 0x30, 0);
|
||||
|
||||
Buffer.BlockCopy(Binary, (int)PartitionLoaderTableOffset, Sector, 0xF0, 0x50);
|
||||
ByteOperations.WriteUInt32(Sector, 0xF0 + 0x2C, 0);
|
||||
ByteOperations.WriteUInt32(Sector, 0xF0 + 0x38, 0x210F0);
|
||||
|
||||
Buffer.BlockCopy(PartitionTypeGuid, 0, Sector, 0x190, 0x10);
|
||||
|
||||
ByteOperations.WriteUInt32(Sector, 0x1FC, 0x0002BD28);
|
||||
|
||||
return Sector;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
// Copyright (c) 2018, Rene Lergner - wpinternals.net - @Heathcliff74xda
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
using System;
|
||||
|
||||
namespace WPinternals
|
||||
{
|
||||
internal class SBL2
|
||||
{
|
||||
internal byte[] Binary;
|
||||
|
||||
internal SBL2(byte[] Binary)
|
||||
{
|
||||
this.Binary = Binary;
|
||||
}
|
||||
|
||||
// Magic!
|
||||
internal byte[] Patch()
|
||||
{
|
||||
UInt32? PatchOffset = ByteOperations.FindPattern(Binary,
|
||||
new byte[] {
|
||||
0xFF, 0xFF, 0xFF, 0xE3, 0x01, 0x0E, 0x42, 0xE3, 0x28, 0x00, 0xD0, 0xE5, 0x1E, 0xFF, 0x2F, 0xE1
|
||||
},
|
||||
new byte[] {
|
||||
0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||
},
|
||||
null);
|
||||
|
||||
if (PatchOffset == null)
|
||||
throw new BadImageFormatException();
|
||||
|
||||
Buffer.BlockCopy(new byte[] { 0x00, 0x00, 0xA0, 0xE3 }, 0, Binary, (int)PatchOffset + 8, 4);
|
||||
|
||||
return Binary;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
// Copyright (c) 2018, Rene Lergner - wpinternals.net - @Heathcliff74xda
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace WPinternals
|
||||
{
|
||||
internal class SBL3
|
||||
{
|
||||
internal byte[] Binary;
|
||||
|
||||
internal SBL3(byte[] Binary)
|
||||
{
|
||||
this.Binary = Binary;
|
||||
}
|
||||
|
||||
internal SBL3(string FileName)
|
||||
{
|
||||
Binary = null;
|
||||
|
||||
// First try to parse as FFU
|
||||
try
|
||||
{
|
||||
if (FFU.IsFFU(FileName))
|
||||
{
|
||||
FFU FFUFile = new FFU(FileName);
|
||||
Binary = FFUFile.GetPartition("SBL3");
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
|
||||
// If not succeeded, then try to parse it as raw image
|
||||
if (Binary == null)
|
||||
{
|
||||
byte[] SBL3Header;
|
||||
byte[] SBL3Pattern = new byte[] { 0x18, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x8F, 0xFF, 0xFF, 0xFF, 0xFF };
|
||||
byte[] SBL3Mask = new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF };
|
||||
|
||||
UInt32? Offset = ByteOperations.FindPatternInFile(FileName, SBL3Pattern, SBL3Mask, out SBL3Header);
|
||||
|
||||
if (Offset != null)
|
||||
{
|
||||
UInt32 Length = ByteOperations.ReadUInt32(SBL3Header, 0x10) + 0x28; // SBL3 Image Size + Header Size
|
||||
Binary = new byte[Length];
|
||||
|
||||
FileStream Stream = new FileStream(FileName, FileMode.Open, FileAccess.Read);
|
||||
Stream.Seek((long)Offset, SeekOrigin.Begin);
|
||||
Stream.Read(Binary, 0, (int)Length);
|
||||
Stream.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Magic!
|
||||
internal byte[] Patch()
|
||||
{
|
||||
UInt32? PatchOffset = ByteOperations.FindPattern(Binary,
|
||||
new byte[] { 0x04, 0x00, 0x9F, 0xE5, 0x28, 0x00, 0xD0, 0xE5, 0x1E, 0xFF, 0x2F, 0xE1 },
|
||||
null, null);
|
||||
|
||||
if (PatchOffset == null)
|
||||
throw new BadImageFormatException();
|
||||
|
||||
Buffer.BlockCopy(new byte[] { 0x00, 0x00, 0xA0, 0xE3 }, 0, Binary, (int)PatchOffset + 4, 4);
|
||||
|
||||
return Binary;
|
||||
}
|
||||
}
|
||||
}
|
||||
+591
@@ -0,0 +1,591 @@
|
||||
// Copyright (c) 2018, Rene Lergner - wpinternals.net - @Heathcliff74xda
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace WPinternals
|
||||
{
|
||||
internal class EFI
|
||||
{
|
||||
internal Guid Guid;
|
||||
internal string Name;
|
||||
internal int Type;
|
||||
internal UInt32 Size;
|
||||
internal UInt32 FileOffset;
|
||||
internal UInt32 SectionOffset;
|
||||
internal UInt32 BinaryOffset;
|
||||
}
|
||||
|
||||
internal class UEFI
|
||||
{
|
||||
internal byte[] Binary;
|
||||
private byte[] DecompressedImage;
|
||||
internal List<EFI> EFIs = new List<EFI>();
|
||||
private byte PaddingByteValue = 0xFF;
|
||||
private UInt32 DecompressedVolumeSectionHeaderOffset;
|
||||
private UInt32 DecompressedVolumeHeaderOffset;
|
||||
private UInt32 VolumeSize;
|
||||
private UInt16 VolumeHeaderSize;
|
||||
private UInt32 FileHeaderOffset;
|
||||
private UInt32 SectionHeaderOffset;
|
||||
private UInt32 CompressedSubImageOffset;
|
||||
private UInt32 CompressedSubImageSize;
|
||||
|
||||
// First 0x28 bytes are Qualcomm partition header
|
||||
// Inside the attributes of the VolumeHeader, the Volume-alignment is set to 8 (on Windows Phone UEFI images)
|
||||
// The Volume always starts right after the Qualcomm header at position 0x28.
|
||||
// So the VolumeHeader-alignment is always complied.
|
||||
private UInt32 VolumeHeaderOffset = 0x28;
|
||||
|
||||
internal UEFI(byte[] UefiBinary)
|
||||
{
|
||||
Binary = UefiBinary;
|
||||
|
||||
string VolumeHeaderMagic;
|
||||
UInt32? Offset = ByteOperations.FindAscii(Binary, "_FVH");
|
||||
if (Offset == null)
|
||||
throw new BadImageFormatException();
|
||||
else
|
||||
VolumeHeaderOffset = (UInt32)Offset - 0x28;
|
||||
|
||||
if (!VerifyVolumeChecksum(Binary, VolumeHeaderOffset))
|
||||
throw new BadImageFormatException();
|
||||
|
||||
VolumeSize = ByteOperations.ReadUInt32(Binary, VolumeHeaderOffset + 0x20); // TODO: This is actually a QWORD
|
||||
VolumeHeaderSize = ByteOperations.ReadUInt16(Binary, VolumeHeaderOffset + 0x30);
|
||||
PaddingByteValue = (ByteOperations.ReadUInt32(Binary, VolumeHeaderOffset + 0x2C) & 0x00000800) > 0 ? (byte)0xFF : (byte)0x00; // EFI_FVB_ERASE_POLARITY = 0x00000800
|
||||
|
||||
// In the volume look for a file of type EFI_FV_FILETYPE_FIRMWARE_VOLUME_IMAGE (0x0B)
|
||||
|
||||
FileHeaderOffset = VolumeHeaderOffset + VolumeHeaderSize;
|
||||
bool VolumeFound = false;
|
||||
int FileType;
|
||||
UInt32 FileSize;
|
||||
do
|
||||
{
|
||||
if (!VerifyFileChecksum(Binary, FileHeaderOffset))
|
||||
throw new BadImageFormatException();
|
||||
|
||||
FileType = ByteOperations.ReadUInt8(Binary, FileHeaderOffset + 0x12);
|
||||
FileSize = ByteOperations.ReadUInt24(Binary, FileHeaderOffset + 0x14);
|
||||
|
||||
if (FileType == 0x0B) // EFI_FV_FILETYPE_FIRMWARE_VOLUME_IMAGE
|
||||
{
|
||||
VolumeFound = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
FileHeaderOffset += FileSize;
|
||||
|
||||
// FileHeaderOffset in Volume-body must be Align 8
|
||||
// In the file-header-attributes the file-alignment relative to the start of the volume is always set to 1,
|
||||
// so that alignment can be ignored.
|
||||
FileHeaderOffset = ByteOperations.Align(VolumeHeaderOffset + VolumeHeaderSize, FileHeaderOffset, 8);
|
||||
}
|
||||
}
|
||||
while (!VolumeFound && (FileHeaderOffset < (VolumeHeaderOffset + VolumeSize)));
|
||||
|
||||
if (!VolumeFound)
|
||||
throw new BadImageFormatException();
|
||||
|
||||
// Look in file for section of type EFI_SECTION_GUID_DEFINED (0x02)
|
||||
|
||||
SectionHeaderOffset = FileHeaderOffset + 0x18;
|
||||
int SectionType;
|
||||
UInt32 SectionSize;
|
||||
UInt16 SectionHeaderSize = 0;
|
||||
|
||||
bool DecompressedVolumeFound = false;
|
||||
do
|
||||
{
|
||||
SectionType = ByteOperations.ReadUInt8(Binary, SectionHeaderOffset + 0x03);
|
||||
SectionSize = ByteOperations.ReadUInt24(Binary, SectionHeaderOffset + 0x00);
|
||||
|
||||
if (SectionType == 0x02) // EFI_SECTION_GUID_DEFINED
|
||||
{
|
||||
SectionHeaderSize = ByteOperations.ReadUInt16(Binary, SectionHeaderOffset + 0x14);
|
||||
DecompressedVolumeFound = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
SectionHeaderOffset += SectionSize;
|
||||
|
||||
// SectionHeaderOffset in File-body must be Align 4
|
||||
SectionHeaderOffset = ByteOperations.Align(FileHeaderOffset + 0x18, SectionHeaderOffset, 4);
|
||||
}
|
||||
}
|
||||
while (!DecompressedVolumeFound && (SectionHeaderOffset < (FileHeaderOffset + FileSize)));
|
||||
|
||||
if (!DecompressedVolumeFound)
|
||||
throw new BadImageFormatException();
|
||||
|
||||
// Decompress subvolume
|
||||
CompressedSubImageOffset = SectionHeaderOffset + SectionHeaderSize;
|
||||
CompressedSubImageSize = SectionSize - SectionHeaderSize;
|
||||
|
||||
// DECOMPRESS HERE
|
||||
DecompressedImage = LZMA.Decompress(Binary, CompressedSubImageOffset, CompressedSubImageSize);
|
||||
|
||||
// Extracted volume contains Sections at its root level
|
||||
|
||||
DecompressedVolumeSectionHeaderOffset = 0;
|
||||
DecompressedVolumeFound = false;
|
||||
do
|
||||
{
|
||||
SectionType = ByteOperations.ReadUInt8(DecompressedImage, DecompressedVolumeSectionHeaderOffset + 0x03);
|
||||
SectionSize = ByteOperations.ReadUInt24(DecompressedImage, DecompressedVolumeSectionHeaderOffset + 0x00);
|
||||
SectionHeaderSize = ByteOperations.ReadUInt16(DecompressedImage, DecompressedVolumeSectionHeaderOffset + 0x14);
|
||||
|
||||
if (SectionType == 0x17) // EFI_SECTION_FIRMWARE_VOLUME_IMAGE
|
||||
{
|
||||
DecompressedVolumeFound = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
DecompressedVolumeSectionHeaderOffset += SectionSize;
|
||||
|
||||
// SectionHeaderOffset in File-body must be Align 4
|
||||
DecompressedVolumeSectionHeaderOffset = ByteOperations.Align(FileHeaderOffset + 0x18, DecompressedVolumeSectionHeaderOffset, 4);
|
||||
}
|
||||
}
|
||||
while (!DecompressedVolumeFound && (DecompressedVolumeSectionHeaderOffset < DecompressedImage.Length));
|
||||
|
||||
if (!DecompressedVolumeFound)
|
||||
throw new BadImageFormatException();
|
||||
|
||||
DecompressedVolumeHeaderOffset = DecompressedVolumeSectionHeaderOffset + 4;
|
||||
|
||||
// PARSE COMPRESSED VOLUME
|
||||
VolumeHeaderMagic = ByteOperations.ReadAsciiString(DecompressedImage, DecompressedVolumeHeaderOffset + 0x28, 0x04);
|
||||
if (VolumeHeaderMagic != "_FVH")
|
||||
throw new BadImageFormatException();
|
||||
|
||||
if (!VerifyVolumeChecksum(DecompressedImage, DecompressedVolumeHeaderOffset))
|
||||
throw new BadImageFormatException();
|
||||
|
||||
Int32 DecompressedVolumeSize = ByteOperations.ReadInt32(DecompressedImage, DecompressedVolumeHeaderOffset + 0x20); // TODO: This is actually a QWORD
|
||||
UInt16 DecompressedVolumeHeaderSize = ByteOperations.ReadUInt16(DecompressedImage, DecompressedVolumeHeaderOffset + 0x30);
|
||||
|
||||
// The files in this decompressed volume are the real EFI's.
|
||||
UInt32 DecompressedFileHeaderOffset = DecompressedVolumeHeaderOffset + DecompressedVolumeHeaderSize;
|
||||
EFI CurrentEFI;
|
||||
do
|
||||
{
|
||||
if ((DecompressedFileHeaderOffset + 0x18) >= (DecompressedVolumeHeaderOffset + DecompressedVolumeSize))
|
||||
break;
|
||||
|
||||
bool ContentFound = false;
|
||||
for (int i = 0; i < 0x18; i++)
|
||||
{
|
||||
if (DecompressedImage[DecompressedFileHeaderOffset + i] != PaddingByteValue)
|
||||
{
|
||||
ContentFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!ContentFound)
|
||||
break;
|
||||
|
||||
FileSize = ByteOperations.ReadUInt24(DecompressedImage, DecompressedFileHeaderOffset + 0x14);
|
||||
|
||||
if ((DecompressedFileHeaderOffset + FileSize) >= (DecompressedVolumeHeaderOffset + DecompressedVolumeSize))
|
||||
break;
|
||||
|
||||
if (!VerifyFileChecksum(DecompressedImage, DecompressedFileHeaderOffset))
|
||||
throw new BadImageFormatException();
|
||||
|
||||
CurrentEFI = new EFI();
|
||||
|
||||
CurrentEFI.Type = ByteOperations.ReadUInt8(DecompressedImage, DecompressedFileHeaderOffset + 0x12);
|
||||
byte[] FileGuidBytes = new byte[0x10];
|
||||
System.Buffer.BlockCopy(DecompressedImage, (int)DecompressedFileHeaderOffset + 0x00, FileGuidBytes, 0, 0x10);
|
||||
CurrentEFI.Guid = new Guid(FileGuidBytes);
|
||||
|
||||
// Parse sections of the EFI
|
||||
CurrentEFI.FileOffset = DecompressedFileHeaderOffset;
|
||||
UInt32 DecompressedSectionHeaderOffset = DecompressedFileHeaderOffset + 0x18;
|
||||
do
|
||||
{
|
||||
SectionType = ByteOperations.ReadUInt8(DecompressedImage, DecompressedSectionHeaderOffset + 0x03);
|
||||
SectionSize = ByteOperations.ReadUInt24(DecompressedImage, DecompressedSectionHeaderOffset + 0x00);
|
||||
|
||||
// SectionTypes that are relevant here:
|
||||
// 0x10 = PE File
|
||||
// 0x19 = RAW
|
||||
// 0x15 = Description
|
||||
// Not all section headers in the UEFI specs are 4 bytes long,
|
||||
// but the sections that are used in Windows Phone EFI's all have a header of 4 bytes.
|
||||
if (SectionType == 0x15)
|
||||
{
|
||||
CurrentEFI.Name = ByteOperations.ReadUnicodeString(DecompressedImage, DecompressedSectionHeaderOffset + 0x04, SectionSize - 0x04).TrimEnd(new char[] { (char)0, ' ' });
|
||||
}
|
||||
else if ((SectionType == 0x10) || (SectionType == 0x19))
|
||||
{
|
||||
CurrentEFI.SectionOffset = DecompressedSectionHeaderOffset;
|
||||
CurrentEFI.BinaryOffset = DecompressedSectionHeaderOffset + 0x04;
|
||||
CurrentEFI.Size = SectionSize - 0x04;
|
||||
}
|
||||
|
||||
DecompressedSectionHeaderOffset += SectionSize;
|
||||
|
||||
// SectionHeaderOffset in File-body must be Align 4
|
||||
DecompressedSectionHeaderOffset = ByteOperations.Align(DecompressedFileHeaderOffset + 0x18, DecompressedSectionHeaderOffset, 4);
|
||||
}
|
||||
while (DecompressedSectionHeaderOffset < (DecompressedFileHeaderOffset + FileSize));
|
||||
|
||||
DecompressedFileHeaderOffset += FileSize;
|
||||
|
||||
// FileHeaderOffset in Volume-body must be Align 8
|
||||
// In the file-header-attributes the file-alignment relative to the start of the volume is always set to 1,
|
||||
// so that alignment can be ignored.
|
||||
DecompressedFileHeaderOffset = ByteOperations.Align(DecompressedVolumeHeaderOffset + DecompressedVolumeHeaderSize, DecompressedFileHeaderOffset, 8);
|
||||
|
||||
EFIs.Add(CurrentEFI);
|
||||
}
|
||||
while (DecompressedFileHeaderOffset < (DecompressedVolumeHeaderOffset + DecompressedVolumeSize));
|
||||
}
|
||||
|
||||
internal byte[] GetFile(string Name)
|
||||
{
|
||||
EFI File = EFIs.Where(f => (string.Compare(Name, f.Name, true) == 0) || (string.Compare(Name, f.Guid.ToString(), true) == 0)).FirstOrDefault();
|
||||
if (File == null)
|
||||
return null;
|
||||
|
||||
byte[] Bytes = new byte[File.Size];
|
||||
Buffer.BlockCopy(DecompressedImage, (int)File.BinaryOffset, Bytes, 0, (int)File.Size);
|
||||
|
||||
return Bytes;
|
||||
}
|
||||
|
||||
internal byte[] GetFile(Guid Guid)
|
||||
{
|
||||
EFI File = EFIs.Where(f => (Guid == f.Guid)).FirstOrDefault();
|
||||
if (File == null)
|
||||
return null;
|
||||
|
||||
byte[] Bytes = new byte[File.Size];
|
||||
Buffer.BlockCopy(DecompressedImage, (int)File.BinaryOffset, Bytes, 0, (int)File.Size);
|
||||
|
||||
return Bytes;
|
||||
}
|
||||
|
||||
internal void ReplaceFile(string Name, byte[] Binary)
|
||||
{
|
||||
EFI File = EFIs.Where(f => (string.Compare(Name, f.Name, true) == 0) || (string.Compare(Name, f.Guid.ToString(), true) == 0)).FirstOrDefault();
|
||||
if (File == null)
|
||||
throw new ArgumentOutOfRangeException();
|
||||
|
||||
UInt32 OldBinarySize = File.Size;
|
||||
UInt32 NewBinarySize = (UInt32)Binary.Length;
|
||||
|
||||
UInt32 OldSectionPadding = ByteOperations.Align(0, OldBinarySize, 4) - OldBinarySize;
|
||||
UInt32 NewSectionPadding = ByteOperations.Align(0, NewBinarySize, 4) - NewBinarySize;
|
||||
|
||||
UInt32 OldFileSize = ByteOperations.ReadUInt24(DecompressedImage, File.FileOffset + 0x14);
|
||||
UInt32 NewFileSize = OldFileSize - OldBinarySize - OldSectionPadding + NewBinarySize + NewSectionPadding;
|
||||
|
||||
UInt32 OldFilePadding = ByteOperations.Align(0, OldFileSize, 8) - OldFileSize;
|
||||
UInt32 NewFilePadding = ByteOperations.Align(0, NewFileSize, 8) - NewFileSize;
|
||||
|
||||
if ((OldBinarySize + OldSectionPadding) != (NewBinarySize + NewSectionPadding))
|
||||
{
|
||||
byte[] NewImage = new byte[DecompressedImage.Length - OldFileSize - OldFilePadding + NewFileSize + NewFilePadding]; // Also preserve space for File-alignement here
|
||||
|
||||
// Copy Volume-head and File-head
|
||||
Buffer.BlockCopy(DecompressedImage, 0, NewImage, 0, (int)File.BinaryOffset);
|
||||
|
||||
// Copy new binary
|
||||
Buffer.BlockCopy(Binary, 0, NewImage, (int)File.BinaryOffset, Binary.Length);
|
||||
|
||||
// Insert section-padding
|
||||
for (int i = 0; i < NewSectionPadding; i++)
|
||||
NewImage[File.BinaryOffset + NewBinarySize + i] = PaddingByteValue;
|
||||
|
||||
// Copy file-tail
|
||||
Buffer.BlockCopy(
|
||||
DecompressedImage,
|
||||
(int)(File.BinaryOffset + OldBinarySize + OldSectionPadding),
|
||||
NewImage,
|
||||
(int)(File.BinaryOffset + NewBinarySize + NewSectionPadding),
|
||||
(int)(File.FileOffset + OldFileSize - File.BinaryOffset - OldBinarySize - OldSectionPadding));
|
||||
|
||||
// Insert file-padding
|
||||
for (int i = 0; i < NewFilePadding; i++)
|
||||
NewImage[File.BinaryOffset + NewFileSize + i] = PaddingByteValue;
|
||||
|
||||
// Copy volume-tail
|
||||
Buffer.BlockCopy(
|
||||
DecompressedImage,
|
||||
(int)(File.FileOffset + OldFileSize + OldFilePadding),
|
||||
NewImage,
|
||||
(int)(File.FileOffset + NewFileSize + NewFilePadding),
|
||||
(int)(DecompressedImage.Length - File.FileOffset - OldFileSize - OldFilePadding));
|
||||
|
||||
Int32 NewOffset = (int)(NewFileSize + NewFilePadding) - (int)(OldFileSize - OldFilePadding);
|
||||
|
||||
// Fix section-size
|
||||
ByteOperations.WriteUInt24(NewImage, File.SectionOffset, (UInt32)(ByteOperations.ReadUInt24(NewImage, File.SectionOffset) + NewOffset));
|
||||
|
||||
// Fix file-size
|
||||
ByteOperations.WriteUInt24(NewImage, File.FileOffset + 0x14, (UInt32)(ByteOperations.ReadUInt24(NewImage, File.FileOffset + 0x14) + NewOffset));
|
||||
|
||||
// Fix volume-size - TODO: This is actually a QWORD
|
||||
ByteOperations.WriteUInt32(NewImage, DecompressedVolumeHeaderOffset + 0x20, (UInt32)(ByteOperations.ReadUInt32(NewImage, DecompressedVolumeHeaderOffset + 0x20) + NewOffset));
|
||||
|
||||
// Fix section-size
|
||||
ByteOperations.WriteUInt24(NewImage, DecompressedVolumeSectionHeaderOffset, (UInt32)(ByteOperations.ReadUInt24(NewImage, DecompressedVolumeSectionHeaderOffset) + NewOffset));
|
||||
|
||||
DecompressedImage = NewImage;
|
||||
|
||||
// Modify all sizes in EFI's
|
||||
foreach (EFI CurrentFile in EFIs)
|
||||
{
|
||||
if (CurrentFile.FileOffset > File.FileOffset)
|
||||
{
|
||||
CurrentFile.FileOffset = (UInt32)(CurrentFile.FileOffset + NewOffset);
|
||||
CurrentFile.SectionOffset = (UInt32)(CurrentFile.SectionOffset + NewOffset);
|
||||
CurrentFile.BinaryOffset = (UInt32)(CurrentFile.BinaryOffset + NewOffset);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Buffer.BlockCopy(Binary, 0, DecompressedImage, (int)File.BinaryOffset, Binary.Length);
|
||||
for (int i = 0; i < NewSectionPadding; i++)
|
||||
DecompressedImage[File.BinaryOffset + Binary.Length + i] = PaddingByteValue;
|
||||
}
|
||||
|
||||
// Calculate File-checksum
|
||||
CalculateFileChecksum(DecompressedImage, File.FileOffset);
|
||||
|
||||
// Calculate Volume-checksum
|
||||
CalculateVolumeChecksum(DecompressedImage, DecompressedVolumeHeaderOffset);
|
||||
}
|
||||
|
||||
internal byte[] Rebuild()
|
||||
{
|
||||
// The new binary will include the Qualcomm header, but not the signature and certificates, because they won't match anyway.
|
||||
byte[] NewBinary = new byte[Binary.Length];
|
||||
Buffer.BlockCopy(Binary, 0, NewBinary, 0, (int)CompressedSubImageOffset);
|
||||
|
||||
ByteOperations.WriteUInt32(NewBinary, 0x10, ByteOperations.ReadUInt32(NewBinary, 0x14)); // Complete image size - does not include signature and certs anymore
|
||||
ByteOperations.WriteUInt32(NewBinary, 0x18, 0x00000000); // Address of signature
|
||||
ByteOperations.WriteUInt32(NewBinary, 0x1C, 0x00000000); // Signature length
|
||||
ByteOperations.WriteUInt32(NewBinary, 0x20, 0x00000000); // Address of certificate
|
||||
ByteOperations.WriteUInt32(NewBinary, 0x24, 0x00000000); // Certificate length
|
||||
|
||||
// Compress volume
|
||||
byte[] NewCompressedImage = LZMA.Compress(DecompressedImage, 0, (UInt32)DecompressedImage.Length);
|
||||
|
||||
// Replace compressed volume and add correct padding
|
||||
// First copy new image
|
||||
Buffer.BlockCopy(NewCompressedImage, 0, NewBinary, (int)CompressedSubImageOffset, NewCompressedImage.Length);
|
||||
|
||||
// Determine padding
|
||||
UInt32 OldSectionPadding = ByteOperations.Align(0, CompressedSubImageSize, 4) - CompressedSubImageSize;
|
||||
UInt32 NewSectionPadding = ByteOperations.Align(0, (UInt32)NewCompressedImage.Length, 4) - (UInt32)NewCompressedImage.Length;
|
||||
UInt32 OldFileSize = ByteOperations.ReadUInt24(Binary, FileHeaderOffset + 0x14);
|
||||
|
||||
// Filesize includes fileheader. But it does not include the padding-bytes. Not even the padding bytes of the last section.
|
||||
UInt32 NewFileSize;
|
||||
if ((CompressedSubImageOffset + CompressedSubImageSize + OldSectionPadding) >= (FileHeaderOffset + OldFileSize))
|
||||
// Compressed image is the last section of this file
|
||||
NewFileSize = CompressedSubImageOffset - FileHeaderOffset + (UInt32)NewCompressedImage.Length;
|
||||
else
|
||||
// Compressed image is NOT the last section of this file
|
||||
NewFileSize = OldFileSize - CompressedSubImageSize - OldSectionPadding + (UInt32)NewCompressedImage.Length + NewSectionPadding;
|
||||
|
||||
// Add section padding
|
||||
for (int i = 0; i < NewSectionPadding; i++)
|
||||
NewBinary[CompressedSubImageOffset + NewCompressedImage.Length + i] = PaddingByteValue;
|
||||
|
||||
// If there are more bytes after the section padding of the compressed image, then copy the trailing sections
|
||||
if (((Int32)FileHeaderOffset + OldFileSize - CompressedSubImageOffset - CompressedSubImageSize - OldSectionPadding) > 0)
|
||||
Buffer.BlockCopy(Binary, (int)(CompressedSubImageOffset + CompressedSubImageSize + OldSectionPadding), NewBinary,
|
||||
(int)(CompressedSubImageOffset + NewCompressedImage.Length + NewSectionPadding),
|
||||
(int)(FileHeaderOffset + OldFileSize - CompressedSubImageOffset - CompressedSubImageSize - OldSectionPadding));
|
||||
|
||||
// Add file padding
|
||||
// Filesize does not include last section padding or file padding
|
||||
UInt32 OldFilePadding = ByteOperations.Align(0, OldFileSize, 8) - OldFileSize;
|
||||
UInt32 NewFilePadding = ByteOperations.Align(0, NewFileSize, 8) - NewFileSize;
|
||||
for (int i = 0; i < NewFilePadding; i++)
|
||||
NewBinary[FileHeaderOffset + NewFileSize + i] = PaddingByteValue;
|
||||
|
||||
if (NewCompressedImage.Length > CompressedSubImageSize)
|
||||
{
|
||||
Buffer.BlockCopy(Binary, (int)(FileHeaderOffset + OldFileSize + OldFilePadding), NewBinary, (int)(FileHeaderOffset + NewFileSize + NewFilePadding),
|
||||
(int)(VolumeHeaderOffset + VolumeSize - FileHeaderOffset - NewFileSize - NewFilePadding));
|
||||
}
|
||||
else
|
||||
{
|
||||
Buffer.BlockCopy(Binary, (int)(FileHeaderOffset + OldFileSize + OldFilePadding), NewBinary, (int)(FileHeaderOffset + NewFileSize + NewFilePadding),
|
||||
(int)(VolumeHeaderOffset + VolumeSize - FileHeaderOffset - OldFileSize - OldFilePadding));
|
||||
for (int i = (int)(VolumeHeaderOffset + VolumeSize - OldFileSize - OldFilePadding + NewFileSize + NewFilePadding); i < VolumeHeaderOffset + VolumeSize; i++)
|
||||
NewBinary[i] = PaddingByteValue;
|
||||
}
|
||||
CompressedSubImageSize = (UInt32)NewCompressedImage.Length;
|
||||
|
||||
// Fix section
|
||||
ByteOperations.WriteUInt24(NewBinary, SectionHeaderOffset, CompressedSubImageSize + ByteOperations.ReadUInt16(Binary, SectionHeaderOffset + 0x14));
|
||||
|
||||
// Fix file
|
||||
ByteOperations.WriteUInt24(NewBinary, FileHeaderOffset + 0x14, NewFileSize);
|
||||
CalculateFileChecksum(NewBinary, FileHeaderOffset);
|
||||
|
||||
// Fix volume (volume size is fixed)
|
||||
CalculateVolumeChecksum(NewBinary, VolumeHeaderOffset);
|
||||
|
||||
Binary = NewBinary;
|
||||
return Binary;
|
||||
}
|
||||
|
||||
private void CalculateVolumeChecksum(byte[] Image, UInt32 Offset)
|
||||
{
|
||||
UInt16 VolumeHeaderSize = ByteOperations.ReadUInt16(Image, Offset + 0x30);
|
||||
ByteOperations.WriteUInt16(Image, Offset + 0x32, 0); // Clear checksum
|
||||
UInt16 NewChecksum = ByteOperations.CalculateChecksum16(Image, Offset, VolumeHeaderSize);
|
||||
ByteOperations.WriteUInt16(Image, Offset + 0x32, NewChecksum);
|
||||
}
|
||||
|
||||
private bool VerifyVolumeChecksum(byte[] Image, UInt32 Offset)
|
||||
{
|
||||
UInt16 VolumeHeaderSize = ByteOperations.ReadUInt16(Image, Offset + 0x30);
|
||||
byte[] Header = new byte[VolumeHeaderSize];
|
||||
System.Buffer.BlockCopy(Image, (int)Offset, Header, 0, VolumeHeaderSize);
|
||||
ByteOperations.WriteUInt16(Header, 0x32, 0); // Clear checksum
|
||||
UInt16 CurrentChecksum = ByteOperations.ReadUInt16(Image, Offset + 0x32);
|
||||
UInt16 NewChecksum = ByteOperations.CalculateChecksum16(Header, 0, VolumeHeaderSize);
|
||||
return (CurrentChecksum == NewChecksum);
|
||||
}
|
||||
|
||||
private void CalculateFileChecksum(byte[] Image, UInt32 Offset)
|
||||
{
|
||||
UInt16 FileHeaderSize = 0x18;
|
||||
UInt32 FileSize = ByteOperations.ReadUInt24(Image, Offset + 0x14);
|
||||
|
||||
ByteOperations.WriteUInt16(Image, Offset + 0x10, 0); // Clear checksum
|
||||
byte NewChecksum = ByteOperations.CalculateChecksum8(Image, Offset, (UInt32)FileHeaderSize - 1);
|
||||
ByteOperations.WriteUInt8(Image, Offset + 0x10, NewChecksum); // File-Header checksum
|
||||
|
||||
byte FileAttribs = ByteOperations.ReadUInt8(Image, Offset + 0x13);
|
||||
if ((FileAttribs & 0x40) > 0)
|
||||
{
|
||||
// Calculate file checksum
|
||||
byte CalculatedFileChecksum = ByteOperations.CalculateChecksum8(Image, Offset + FileHeaderSize, FileSize - FileHeaderSize);
|
||||
ByteOperations.WriteUInt8(Image, Offset + 0x11, CalculatedFileChecksum);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Fixed file checksum
|
||||
ByteOperations.WriteUInt8(Image, Offset + 0x11, 0xAA);
|
||||
}
|
||||
}
|
||||
|
||||
private bool VerifyFileChecksum(byte[] Image, UInt32 Offset)
|
||||
{
|
||||
// This function only checks fixed checksum-values 0x55 and 0xAA.
|
||||
|
||||
UInt16 FileHeaderSize = 0x18;
|
||||
UInt32 FileSize = ByteOperations.ReadUInt24(Image, Offset + 0x14);
|
||||
|
||||
byte[] Header = new byte[FileHeaderSize - 1];
|
||||
System.Buffer.BlockCopy(Image, (int)Offset, Header, 0, FileHeaderSize - 1);
|
||||
ByteOperations.WriteUInt16(Header, 0x10, 0); // Clear checksum
|
||||
byte CurrentHeaderChecksum = ByteOperations.ReadUInt8(Image, Offset + 0x10);
|
||||
byte CalculatedHeaderChecksum = ByteOperations.CalculateChecksum8(Header, 0, (UInt32)FileHeaderSize - 1);
|
||||
|
||||
if (CurrentHeaderChecksum != CalculatedHeaderChecksum)
|
||||
return false;
|
||||
|
||||
byte FileAttribs = ByteOperations.ReadUInt8(Image, Offset + 0x13);
|
||||
byte CurrentFileChecksum = ByteOperations.ReadUInt8(Image, Offset + 0x11);
|
||||
if ((FileAttribs & 0x40) > 0)
|
||||
{
|
||||
// Calculate file checksum
|
||||
byte CalculatedFileChecksum = ByteOperations.CalculateChecksum8(Image, Offset + FileHeaderSize, FileSize - FileHeaderSize);
|
||||
if (CurrentFileChecksum != CalculatedFileChecksum)
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Fixed file checksum
|
||||
if ((CurrentFileChecksum != 0xAA) && (CurrentFileChecksum != 0x55))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void ClearEfiChecksum(byte[] EfiFile)
|
||||
{
|
||||
UInt32 PEHeaderOffset = ByteOperations.ReadUInt32(EfiFile, 0x3C);
|
||||
ByteOperations.WriteUInt32(EfiFile, PEHeaderOffset + 0x58, 0);
|
||||
}
|
||||
|
||||
// Magic!
|
||||
internal byte[] Patch()
|
||||
{
|
||||
byte[] SecurityDxe = GetFile("SecurityDxe");
|
||||
ClearEfiChecksum(SecurityDxe);
|
||||
|
||||
UInt32? PatchOffset = ByteOperations.FindPattern(SecurityDxe,
|
||||
new byte[] { 0xF0, 0x41, 0x2D, 0xE9, 0xFF, 0xFF, 0xB0, 0xE1, 0x28, 0xD0, 0x4D, 0xE2, 0xFF, 0xFF, 0xA0, 0xE1, 0x00, 0x00, 0xFF, 0x13, 0x20, 0xFF, 0xA0, 0xE3 },
|
||||
new byte[] { 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0xFF, 0x00, 0x00 },
|
||||
null);
|
||||
|
||||
if (PatchOffset == null)
|
||||
throw new BadImageFormatException();
|
||||
|
||||
Buffer.BlockCopy(new byte[] { 0x00, 0x00, 0xA0, 0xE3, 0x1E, 0xFF, 0x2F, 0xE1 }, 0, SecurityDxe, (int)PatchOffset, 8);
|
||||
|
||||
ReplaceFile("SecurityDxe", SecurityDxe);
|
||||
|
||||
byte[] SecurityServicesDxe = GetFile("SecurityServicesDxe");
|
||||
ClearEfiChecksum(SecurityServicesDxe);
|
||||
|
||||
PatchOffset = ByteOperations.FindPattern(SecurityServicesDxe,
|
||||
new byte[] { 0x10, 0xFF, 0xFF, 0xE5, 0x80, 0xFF, 0x10, 0xE3, 0xFF, 0xFF, 0xFF, 0x0A },
|
||||
new byte[] { 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x00 },
|
||||
null);
|
||||
|
||||
if (PatchOffset == null)
|
||||
throw new BadImageFormatException();
|
||||
|
||||
ByteOperations.WriteUInt8(SecurityServicesDxe, (UInt32)PatchOffset + 0x0B, 0xEA);
|
||||
|
||||
PatchOffset = ByteOperations.FindPattern(SecurityServicesDxe,
|
||||
new byte[] { 0x11, 0xFF, 0xFF, 0xE5, 0x40, 0xFF, 0x10, 0xE3, 0xFF, 0xFF, 0xFF, 0x0A },
|
||||
new byte[] { 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x00 },
|
||||
null);
|
||||
|
||||
if (PatchOffset == null)
|
||||
throw new BadImageFormatException();
|
||||
|
||||
ByteOperations.WriteUInt8(SecurityServicesDxe, (UInt32)PatchOffset + 0x0B, 0xEA);
|
||||
|
||||
ReplaceFile("SecurityServicesDxe", SecurityServicesDxe);
|
||||
|
||||
return Rebuild();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user