diff options
Diffstat (limited to 'mcs/class/corlib/Mono.Security.Authenticode/AuthenticodeBase.cs')
-rwxr-xr-x | mcs/class/corlib/Mono.Security.Authenticode/AuthenticodeBase.cs | 184 |
1 files changed, 142 insertions, 42 deletions
diff --git a/mcs/class/corlib/Mono.Security.Authenticode/AuthenticodeBase.cs b/mcs/class/corlib/Mono.Security.Authenticode/AuthenticodeBase.cs index 9e5923667b5..c2d609ecec2 100755 --- a/mcs/class/corlib/Mono.Security.Authenticode/AuthenticodeBase.cs +++ b/mcs/class/corlib/Mono.Security.Authenticode/AuthenticodeBase.cs @@ -5,10 +5,6 @@ // Sebastien Pouliot <sebastien@ximian.com> // // (C) 2003 Motus Technologies Inc. (http://www.motus.com) -// (C) 2004 Novell (http://www.novell.com) -// - -// // Copyright (C) 2004 Novell, Inc (http://www.novell.com) // // Permission is hereby granted, free of charge, to any person obtaining @@ -60,63 +56,167 @@ namespace Mono.Security.Authenticode { public const string spcIndirectDataContext = "1.3.6.1.4.1.311.2.1.4"; - internal byte[] rawData; + private byte[] fileblock; + private FileStream fs; + private int blockNo; + private int blockLength; + private int peOffset; + private int dirSecurityOffset; + private int dirSecuritySize; public AuthenticodeBase () { + fileblock = new byte [4096]; } - protected byte[] HashFile (string fileName, string hashName) + internal void Open (string filename) { - FileStream fs = new FileStream (fileName, FileMode.Open, FileAccess.Read, FileShare.Read); - byte[] file = new byte [fs.Length]; - fs.Read (file, 0, file.Length); - fs.Close (); + if (fs != null) + Close (); + fs = new FileStream (filename, FileMode.Open, FileAccess.Read, FileShare.Read); + } - // MZ - DOS header - if (BitConverterLE.ToUInt16 (file, 0) != 0x5A4D) - return null; + internal void Close () + { + if (fs != null) { + fs.Close (); + fs = null; + blockNo = 0; + } + } - // find offset of PE header - int peOffset = BitConverterLE.ToInt32 (file, 60); - if (peOffset > file.Length) - return null; + internal bool ReadFirstBlock () + { + if (fs == null) + return false; + + fs.Position = 0; + // read first block - it will include (100% sure) + // the MZ header and (99.9% sure) the PE header + blockLength = fs.Read (fileblock, 0, fileblock.Length); + blockNo = 1; + if (blockLength < 64) + return false; // invalid PE file + + // 1. Validate the MZ header informations + // 1.1. Check for magic MZ at start of header + if (BitConverterLE.ToUInt16 (fileblock, 0) != 0x5A4D) + return false; + + // 1.2. Find the offset of the PE header + peOffset = BitConverterLE.ToInt32 (fileblock, 60); + if (peOffset > fileblock.Length) { + // just in case (0.1%) this can actually happen + string msg = String.Format (Locale.GetText ( + "Header size too big (> {0} bytes)."), + fileblock.Length); + throw new NotSupportedException (msg); + } + if (peOffset > fs.Length) + return false; - // PE - NT header - if (BitConverterLE.ToUInt16 (file, peOffset) != 0x4550) - return null; + // 2. Read between DOS header and first part of PE header + // 2.1. Check for magic PE at start of header + if (BitConverterLE.ToUInt16 (fileblock, peOffset) != 0x4550) + return false; - // IMAGE_DIRECTORY_ENTRY_SECURITY - int dirSecurityOffset = BitConverterLE.ToInt32 (file, peOffset + 152); - int dirSecuritySize = BitConverterLE.ToInt32 (file, peOffset + 156); + // 2.2. Locate IMAGE_DIRECTORY_ENTRY_SECURITY (offset and size) + dirSecurityOffset = BitConverterLE.ToInt32 (fileblock, peOffset + 152); + dirSecuritySize = BitConverterLE.ToInt32 (fileblock, peOffset + 156); + + return true; + } + + internal byte[] GetSecurityEntry () + { + if (blockNo < 1) + ReadFirstBlock (); if (dirSecuritySize > 8) { - rawData = new byte [dirSecuritySize - 8]; - Buffer.BlockCopy (file, dirSecurityOffset + 8, rawData, 0, rawData.Length); -/* DEBUG - FileStream debug = new FileStream (fileName + ".sig", FileMode.Create, FileAccess.Write); - debug.Write (rawData, 0, rawData.Length); - debug.Close ();*/ + // remove header from size (not ASN.1 based) + byte[] secEntry = new byte [dirSecuritySize - 8]; + // position after header and read entry + fs.Position = dirSecurityOffset + 8; + fs.Read (secEntry, 0, secEntry.Length); + return secEntry; + } + return null; + } + + // returns null if the file isn't signed + internal byte[] GetHash (HashAlgorithm hash) + { + if (blockNo < 1) + ReadFirstBlock (); + fs.Position = blockLength; + + // hash the rest of the file + long n = fs.Length - blockLength; + // minus any authenticode signature (with 8 bytes header) + if (dirSecurityOffset > 0) { + // it is also possible that the signature block + // starts within the block in memory (small EXE) + if (dirSecurityOffset < blockLength) { + blockLength = dirSecurityOffset; + n = 0; + } + else + n -= (dirSecuritySize); } - else - rawData = null; - HashAlgorithm hash = HashAlgorithm.Create (hashName); - // 0 to 215 (216) then skip 4 (checksum) + // Authenticode(r) gymnastics + // Hash from (generally) 0 to 215 (216 bytes) int pe = peOffset + 88; - hash.TransformBlock (file, 0, pe, file, 0); + hash.TransformBlock (fileblock, 0, pe, fileblock, 0); + // then skip 4 for checksum pe += 4; - // 220 to 279 (60) then skip 8 (IMAGE_DIRECTORY_ENTRY_SECURITY) - hash.TransformBlock (file, pe, 60, file, pe); + // Continue hashing from (generally) 220 to 279 (60 bytes) + hash.TransformBlock (fileblock, pe, 60, fileblock, pe); + // then skip 8 bytes for IMAGE_DIRECTORY_ENTRY_SECURITY pe += 68; - // 288 to end of file - int n = file.Length - pe; - // minus any authenticode signature (with 8 bytes header) - if (dirSecurityOffset != 0) - n -= (dirSecuritySize); - hash.TransformFinalBlock (file, pe, n); + // everything is present so start the hashing + if (n == 0) { + // hash the (only) block + hash.TransformFinalBlock (fileblock, pe, blockLength - pe); + } + else { + // hash the last part of the first (already in memory) block + hash.TransformBlock (fileblock, pe, blockLength - pe, fileblock, 0); + + // hash by blocks of 4096 bytes + long blocks = (n >> 12); + int remainder = (int)(n - (blocks << 12)); + if (remainder == 0) { + blocks--; + remainder = 4096; + } + // blocks + while (blocks-- > 0) { + fs.Read (fileblock, 0, fileblock.Length); + hash.TransformBlock (fileblock, 0, fileblock.Length, fileblock, 0); + } + // remainder + if (fs.Read (fileblock, 0, remainder) != remainder) + return null; + hash.TransformFinalBlock (fileblock, 0, remainder); + } return hash.Hash; } + + // for compatibility only + protected byte[] HashFile (string fileName, string hashName) + { + try { + Open (fileName); + HashAlgorithm hash = HashAlgorithm.Create (hashName); + byte[] result = GetHash (hash); + Close (); + return result; + } + catch { + return null; + } + } } } |