Skip to content

Commit

Permalink
2004-09-07 Sebastien Pouliot <sebastien@ximian.com>
Browse files Browse the repository at this point in the history
	* AuthenticodeBase.cs: Merge optimizations from HEAD.
	* AuthenticodeDeformatter.cs: Merge optimizations from HEAD.
	* AuthenticodeFormatter.cs: Merge optimizations from HEAD.

svn path=/branches/mono-1-0/mcs/; revision=33507
  • Loading branch information
Sebastien Pouliot committed Sep 7, 2004
1 parent f7ffe59 commit 0f56a66
Show file tree
Hide file tree
Showing 4 changed files with 235 additions and 89 deletions.
184 changes: 143 additions & 41 deletions mcs/class/Mono.Security/Mono.Security.Authenticode/AuthenticodeBase.cs
Expand Up @@ -5,9 +5,7 @@
// 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
// a copy of this software and associated documentation files (the
Expand Down Expand Up @@ -58,63 +56,167 @@ class AuthenticodeBase {

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;
}
}
}
}

0 comments on commit 0f56a66

Please sign in to comment.