Skip to content

Commit 6d66e6b

Browse files
committed
SecondaryExecutableVerifier v1.0.3
1 parent ddbe1b7 commit 6d66e6b

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+3651
-0
lines changed
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Text;
4+
using Utilities;
5+
6+
namespace QualcommLibrary
7+
{
8+
public class ApplicationExecutableHeader
9+
{
10+
public const int CodeStartOffset = 0x28;
11+
public const int Length = 0x28;
12+
13+
public uint Identifier;
14+
public uint Version;
15+
public uint LoadingAddress;
16+
public uint FileSize; // (header + body + signature + certificate store)
17+
public uint CodeSize; // The size of the executable (without the header / signature / certificate store)
18+
public uint SignatureAddress;
19+
public uint SignatureLength;
20+
public uint CertificateStoreAddress;
21+
public uint CertificateStoreLength;
22+
23+
public ApplicationExecutableHeader(byte[] buffer)
24+
{
25+
Identifier = LittleEndianConverter.ToUInt32(buffer, 0x00);
26+
Version = LittleEndianConverter.ToUInt32(buffer, 0x04);
27+
LoadingAddress = LittleEndianConverter.ToUInt32(buffer, 0x0C);
28+
FileSize = LittleEndianConverter.ToUInt32(buffer, 0x10);
29+
CodeSize = LittleEndianConverter.ToUInt32(buffer, 0x14);
30+
SignatureAddress = LittleEndianConverter.ToUInt32(buffer, 0x18);
31+
SignatureLength = LittleEndianConverter.ToUInt32(buffer, 0x1C);
32+
CertificateStoreAddress = LittleEndianConverter.ToUInt32(buffer, 0x20);
33+
CertificateStoreLength = LittleEndianConverter.ToUInt32(buffer, 0x24);
34+
}
35+
36+
public uint SignatureOffset
37+
{
38+
get
39+
{
40+
return CodeStartOffset + SignatureAddress - LoadingAddress;
41+
}
42+
}
43+
44+
public uint CertificateStoreOffset
45+
{
46+
get
47+
{
48+
return CodeStartOffset + CertificateStoreAddress - LoadingAddress;
49+
}
50+
}
51+
52+
public static bool IsChainedExecutable(byte[] imageBytes)
53+
{
54+
uint version = LittleEndianConverter.ToUInt32(imageBytes, 0x04);
55+
uint expectedVersion = 0x03;
56+
return version == expectedVersion;
57+
}
58+
}
59+
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.IO;
4+
using System.Text;
5+
using Utilities;
6+
7+
namespace QualcommLibrary
8+
{
9+
public class ApplicationExecutableHelper
10+
{
11+
public static byte[] ExtractCode(byte[] imageBytes)
12+
{
13+
ApplicationExecutableHeader header = new ApplicationExecutableHeader(imageBytes);
14+
return ByteReader.ReadBytes(imageBytes, (int)ApplicationExecutableHeader.CodeStartOffset, (int)header.CodeSize);
15+
}
16+
17+
public static byte[] ExtractSignature(byte[] imageBytes)
18+
{
19+
ApplicationExecutableHeader header = new ApplicationExecutableHeader(imageBytes);
20+
return ByteReader.ReadBytes(imageBytes, (int)header.SignatureOffset, (int)header.SignatureLength);
21+
}
22+
23+
public static List<byte[]> ExtractCertificates(byte[] imageBytes)
24+
{
25+
List<byte[]> certificates = new List<byte[]>();
26+
ApplicationExecutableHeader header = new ApplicationExecutableHeader(imageBytes);
27+
byte[] certificateStoreBytes = ByteReader.ReadBytes(imageBytes, (int)header.CertificateStoreOffset, (int)header.CertificateStoreLength);
28+
int offset = 0;
29+
while (offset < certificateStoreBytes.Length - 4)
30+
{
31+
ushort type = BigEndianReader.ReadUInt16(certificateStoreBytes, ref offset);
32+
if (type == 0x3082) // ASN.1 SEQUENCE
33+
{
34+
ushort length = BigEndianReader.ReadUInt16(certificateStoreBytes, ref offset);
35+
offset -= 4;
36+
byte[] certificateBytes = ByteReader.ReadBytes(certificateStoreBytes, ref offset, length + 4);
37+
certificates.Add(certificateBytes);
38+
}
39+
else
40+
{
41+
break;
42+
}
43+
}
44+
return certificates;
45+
}
46+
47+
public static void ExtractFileComponents(string imagePath, string outputDirectory)
48+
{
49+
string filenameWithoutExtention = Path.GetFileNameWithoutExtension(imagePath);
50+
if (!outputDirectory.EndsWith("\\"))
51+
{
52+
outputDirectory += "\\";
53+
}
54+
byte[] imageBytes = File.ReadAllBytes(imagePath);
55+
ApplicationExecutableHeader header = new ApplicationExecutableHeader(imageBytes);
56+
string codeFilename = String.Format("{0}\\{1}-code-0x{2}.bin", outputDirectory, filenameWithoutExtention, header.LoadingAddress.ToString("X"));
57+
string signatureFilename = String.Format("{0}\\{1}-Signature.bin", outputDirectory, filenameWithoutExtention);
58+
59+
byte[] code = ExtractCode(imageBytes);
60+
File.WriteAllBytes(codeFilename, code);
61+
byte[] signatureBytes = ExtractSignature(imageBytes);
62+
File.WriteAllBytes(signatureFilename, signatureBytes);
63+
List<byte[]> certificates = ExtractCertificates(imageBytes);
64+
for (int index = 0; index < certificates.Count; index++)
65+
{
66+
string certificateFilename = String.Format("{0}\\{1}-Certificate-L{2}.cer", outputDirectory, filenameWithoutExtention, index + 1);
67+
File.WriteAllBytes(certificateFilename, certificates[index]);
68+
}
69+
}
70+
}
71+
}
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.IO;
4+
using System.Security.Cryptography;
5+
using System.Text;
6+
using Utilities;
7+
8+
namespace QualcommLibrary
9+
{
10+
public class ApplicationExecutableVerification
11+
{
12+
public static byte[] GetRootCertificateHash(byte[] imageBytes)
13+
{
14+
List<byte[]> certificates = ApplicationExecutableHelper.ExtractCertificates(imageBytes);
15+
int rootCertificateIndex = CertificateValidationHelper.GetRootCertificateIndex(certificates);
16+
if (rootCertificateIndex == -1)
17+
{
18+
return null;
19+
}
20+
21+
byte[] rootCertificate = certificates[rootCertificateIndex];
22+
return SHA256.Create().ComputeHash(rootCertificate);
23+
}
24+
25+
public static bool VerifyRootCertificateHash(byte[] imageBytes, byte[] oemPKHash)
26+
{
27+
byte[] rootCertificateHash = GetRootCertificateHash(imageBytes);
28+
if (rootCertificateHash == null)
29+
{
30+
return false;
31+
}
32+
return ByteUtils.AreByteArraysEqual(rootCertificateHash, oemPKHash);
33+
}
34+
35+
public static bool VerifyCertificateStore(byte[] imageBytes)
36+
{
37+
List<byte[]> certificates = ApplicationExecutableHelper.ExtractCertificates(imageBytes);
38+
return CertificateValidationHelper.VerifyCertificateChain(certificates);
39+
}
40+
41+
public static bool VerifyImageSignature(byte[] imageBytes, byte[] softwareID, byte[] hardwareID)
42+
{
43+
if (softwareID.Length != 8)
44+
{
45+
throw new ArgumentException("SoftwareID should be 8 bytes long");
46+
}
47+
48+
if (hardwareID.Length != 8)
49+
{
50+
throw new ArgumentException("HardwareID should be 8 bytes long");
51+
}
52+
53+
byte[] expectedHash = DecryptFileSignature(imageBytes);
54+
55+
byte[] imageHeader = ByteReader.ReadBytes(imageBytes, 0, ApplicationExecutableHeader.Length);
56+
byte[] codeBytes = ApplicationExecutableHelper.ExtractCode(imageBytes);
57+
58+
HashAlgorithm hashAlgorithm;
59+
if (expectedHash.Length == 20)
60+
{
61+
hashAlgorithm = SHA1.Create();
62+
}
63+
else if (expectedHash.Length == 32)
64+
{
65+
hashAlgorithm = SHA256.Create();
66+
}
67+
else
68+
{
69+
throw new Exception("Unknown hash algorithm");
70+
}
71+
byte[] message = hashAlgorithm.ComputeHash(ByteUtils.Concatenate(imageHeader, codeBytes));
72+
byte[] hash = HMAC(softwareID, hardwareID, message, hashAlgorithm);
73+
return ByteUtils.AreByteArraysEqual(expectedHash, hash);
74+
}
75+
76+
public static byte[] DecryptFileSignature(byte[] imageBytes)
77+
{
78+
List<byte[]> certificates = ApplicationExecutableHelper.ExtractCertificates(imageBytes);
79+
if (certificates.Count > 0)
80+
{
81+
byte[] certificateBytes = certificates[0];
82+
byte[] signatureBytes = ApplicationExecutableHelper.ExtractSignature(imageBytes);
83+
RSAParameters rsaParameters = CertificateHelper.GetRSAParameters(certificateBytes);
84+
byte[] decodedHash = RSAHelper.DecryptSignature(signatureBytes, rsaParameters);
85+
return decodedHash;
86+
}
87+
else
88+
{
89+
throw new Exception("According to the header, the file does not contain a certificate");
90+
}
91+
}
92+
93+
private static byte[] HMAC(byte[] i_key, byte[] o_key, byte[] message, HashAlgorithm hashAlgorithm)
94+
{
95+
if (i_key.Length != 8 || o_key.Length != 8)
96+
{
97+
throw new ArgumentException("i_key and o_key must be 8 bytes long");
98+
}
99+
byte[] i_key_pad = new byte[8];
100+
byte[] o_key_pad = new byte[8];
101+
for (int index = 0; index < 8; index++)
102+
{
103+
i_key_pad[index] = (byte)(i_key[index] ^ 0x36);
104+
o_key_pad[index] = (byte)(o_key[index] ^ 0x5C);
105+
}
106+
107+
byte[] innerHash = hashAlgorithm.ComputeHash(ByteUtils.Concatenate(i_key_pad, message));
108+
byte[] outerHash = hashAlgorithm.ComputeHash(ByteUtils.Concatenate(o_key_pad, innerHash));
109+
return outerHash;
110+
}
111+
}
112+
}
1.43 MB
Binary file not shown.
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.IO;
4+
using System.Security;
5+
using System.Security.Cryptography;
6+
using System.Security.Cryptography.X509Certificates;
7+
using System.Text;
8+
using Utilities;
9+
10+
namespace QualcommLibrary
11+
{
12+
public class CertificateHelper
13+
{
14+
public static RSAParameters GetRSAParameters(string certificatePath)
15+
{
16+
byte[] certificateBytes = File.ReadAllBytes(certificatePath);
17+
return GetRSAParameters(certificateBytes);
18+
}
19+
20+
public static RSAParameters GetRSAParameters(byte[] certificateBytes)
21+
{
22+
X509Certificate2 x509certificate = new X509Certificate2(certificateBytes);
23+
if (x509certificate.HasPrivateKey)
24+
{
25+
RSACryptoServiceProvider rsa = (RSACryptoServiceProvider)x509certificate.PrivateKey;
26+
return rsa.ExportParameters(true);
27+
}
28+
else
29+
{
30+
RSACryptoServiceProvider rsa = (RSACryptoServiceProvider)x509certificate.PublicKey.Key;
31+
return rsa.ExportParameters(false);
32+
}
33+
}
34+
35+
public static X509Certificate2 GetX509Certificate2(string certificatePath)
36+
{
37+
byte[] certificateBytes = File.ReadAllBytes(certificatePath);
38+
X509Certificate2 x509certificate = new X509Certificate2(certificateBytes);
39+
return x509certificate;
40+
}
41+
42+
/// <summary>
43+
/// TBS: To Be Signed - the portion of the X.509 certificate that is signed with the CA's private key.
44+
/// </summary>
45+
// http://www.codeproject.com/Questions/252741/Where-is-the-signature-value-in-the-certificate
46+
public static byte[] ExtractTbsCertificate(byte[] certificateBytes)
47+
{
48+
if (certificateBytes[0] == 0x30 && certificateBytes[1] == 0x82)
49+
{
50+
if (certificateBytes[4] == 0x30 && certificateBytes[5] == 0x82)
51+
{
52+
ushort length = BigEndianConverter.ToUInt16(certificateBytes, 6);
53+
return ByteReader.ReadBytes(certificateBytes, 4, 4 + (int)length);
54+
}
55+
}
56+
throw new ArgumentException("The given certificate is not a BER-encoded ASN.1 X.509 certificate");
57+
}
58+
}
59+
}
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Security;
4+
using System.Security.Cryptography;
5+
using System.Security.Cryptography.X509Certificates;
6+
using System.Text;
7+
using Utilities;
8+
9+
namespace QualcommLibrary
10+
{
11+
public class CertificateValidationHelper
12+
{
13+
public static byte[] SHA_160_PKCS_ID = new byte[] { 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2B, 0x0E, 0x03, 0x02, 0x1A, 0x05, 0x00, 0x04, 0x14 };
14+
public static byte[] SHA_256_PKCS_ID = new byte[] { 0x30, 0x31, 0x30, 0x0D, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20 };
15+
16+
public static int GetRootCertificateIndex(List<byte[]> certificates)
17+
{
18+
int index;
19+
for (index = 0; index < certificates.Count; index++)
20+
{
21+
byte[] certificateBytes = certificates[index];
22+
X509Certificate2 certificate = new X509Certificate2(certificateBytes);
23+
if (certificate.Issuer == certificate.Subject)
24+
{
25+
return index;
26+
}
27+
}
28+
return -1;
29+
}
30+
31+
public static bool VerifyCertificateChain(List<byte[]> certificates)
32+
{
33+
int rootCertificateIndex = GetRootCertificateIndex(certificates);
34+
if (rootCertificateIndex == -1)
35+
{
36+
return false;
37+
}
38+
39+
for (int index = 0; index < rootCertificateIndex; index++)
40+
{
41+
byte[] certificateBytes = certificates[index];
42+
X509Certificate2 certificate = new X509Certificate2(certificateBytes);
43+
byte[] issuingCertificateBytes = certificates[index + 1];
44+
bool isValid = ValidateCertificate(issuingCertificateBytes, certificateBytes);
45+
if (!isValid)
46+
{
47+
return false;
48+
}
49+
}
50+
51+
return ValidateCertificate(certificates[rootCertificateIndex], certificates[rootCertificateIndex]);
52+
}
53+
54+
public static bool ValidateCertificate(byte[] issuingCertificate, byte[] certificateToValidate)
55+
{
56+
RSAParameters rsaParameters = CertificateHelper.GetRSAParameters(issuingCertificate);
57+
byte[] certificateSignature = ByteReader.ReadBytes(certificateToValidate, certificateToValidate.Length - 256, 256);
58+
byte[] decodedSignature = RSAHelper.DecryptSignature(certificateSignature, rsaParameters);
59+
byte[] tbsCertificate = CertificateHelper.ExtractTbsCertificate(certificateToValidate);
60+
if (StartsWith(decodedSignature, SHA_256_PKCS_ID))
61+
{
62+
byte[] expectedHash = ByteReader.ReadBytes(decodedSignature, SHA_256_PKCS_ID.Length, 32);
63+
byte[] hash = SHA256Managed.Create().ComputeHash(tbsCertificate);
64+
return ByteUtils.AreByteArraysEqual(hash, expectedHash);
65+
66+
}
67+
else if (StartsWith(decodedSignature, SHA_160_PKCS_ID))
68+
{
69+
byte[] expectedHash = ByteReader.ReadBytes(decodedSignature, SHA_160_PKCS_ID.Length, 20);
70+
byte[] hash = SHA1Managed.Create().ComputeHash(tbsCertificate);
71+
return ByteUtils.AreByteArraysEqual(hash, expectedHash);
72+
}
73+
else
74+
{
75+
throw new NotImplementedException("Unsupported Signature PKCS ID");
76+
}
77+
}
78+
79+
public static bool StartsWith(byte[] array1, byte[] array2)
80+
{
81+
if (array1.Length >= array2.Length)
82+
{
83+
byte[] start = ByteReader.ReadBytes(array1, 0, array2.Length);
84+
return ByteUtils.AreByteArraysEqual(start, array2);
85+
}
86+
return false;
87+
}
88+
}
89+
}

0 commit comments

Comments
 (0)