Initial commit - WPinternals 2.6

This commit is contained in:
Rene Lergner
2018-10-25 22:35:49 +02:00
commit 396ae57f05
483 changed files with 159677 additions and 0 deletions
+381
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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) { }
}
}
+478
View File
@@ -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;
}
}
+461
View File
@@ -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();
}
}
}
+315
View File
@@ -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
+347
View File
@@ -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;
}
}
}
+597
View File
@@ -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);
}
}
}
}
+667
View File
@@ -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
}
}
+151
View File
@@ -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();
}
}
}
+226
View File
@@ -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 });
}
}
}
+109
View File
@@ -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;
}
}
}
+166
View File
@@ -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;
}
}
}
}
}
+313
View File
@@ -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);
}
}
}
+349
View File
@@ -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
View File
@@ -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;
}
}
}
+54
View File
@@ -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;
}
}
}
+87
View File
@@ -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
View File
@@ -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();
}
}
}