/*
* PERWAPI - An API for Reading and Writing PE Files
*
* Copyright (c) Diane Corney, Queensland University of Technology, 2004.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the PERWAPI Copyright as included with this
* distribution in the file PERWAPIcopyright.rtf.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY as is explained in the copyright notice.
*
* The author may be contacted at d.corney@qut.edu.au
*
* Version Date: 26/01/07
*/
using System;
using System.IO;
using System.Collections;
using System.Security.Cryptography;
namespace QUT.PERWAPI
{
/**************************************************************************/
///
/// A scope for definitions
///
public abstract class DefiningScope : ResolutionScope
{
/*-------------------- Constructors ---------------------------------*/
internal DefiningScope(string name)
: base(name)
{
readAsDef = true;
}
internal override void AddToClassList(Class aClass)
{
((ClassDef)aClass).SetScope((PEFile)this);
classes.Add(aClass);
}
}
/**************************************************************************/
///
/// Descriptor for an assembly (.assembly)
///
public class Assembly : DefiningScope
{
//internal static Hashtable Assemblies = new Hashtable();
ushort majorVer, minorVer, buildNo, revisionNo;
uint flags;
HashAlgorithmType hashAlgId = HashAlgorithmType.None;
uint keyIx = 0, cultIx = 0;
byte[] publicKey;
string culture;
internal AssemblyRef refOf;
ArrayList security;
internal PEFile pefile;
/*-------------------- Constructors ---------------------------------*/
internal Assembly(string name, PEFile pefile)
: base(name)
{
this.pefile = pefile;
tabIx = MDTable.Assembly;
}
internal Assembly(string name, HashAlgorithmType hashAlgId, ushort majVer,
ushort minVer, ushort bldNo, ushort revNo, uint flags, byte[] pKey,
string cult, PEFile pefile)
: base(name)
{
this.hashAlgId = hashAlgId;
this.majorVer = majVer;
this.minorVer = minVer;
this.buildNo = bldNo;
this.revisionNo = revNo;
this.flags = flags;
this.publicKey = pKey;
this.culture = cult;
tabIx = MDTable.Assembly;
}
internal static AssemblyRef ReadAssemblyRef(PEReader buff)
{
buff.SetElementPosition(MDTable.Assembly, 1);
HashAlgorithmType hAlg = (HashAlgorithmType)buff.ReadUInt32();
ushort majVer = buff.ReadUInt16();
ushort minVer = buff.ReadUInt16();
ushort bldNo = buff.ReadUInt16();
ushort revNo = buff.ReadUInt16();
uint flags = buff.ReadUInt32();
byte[] pKey = buff.GetBlob();
string name = buff.GetString();
string cult = buff.GetString();
AssemblyRef assemRef = null;
if (name.ToLower() == "mscorlib")
{
assemRef = MSCorLib.mscorlib;
assemRef.AddVersionInfo(majVer, minVer, bldNo, revNo);
if (pKey.Length > 8) assemRef.AddKey(pKey);
else assemRef.AddKeyToken(pKey);
assemRef.AddCulture(cult);
assemRef.SetFlags(flags);
}
else
{
assemRef = new AssemblyRef(name, majVer, minVer, bldNo, revNo, flags, pKey, cult, null);
}
//AssemblyRef assemRef = new AssemblyRef(name,majVer,minVer,bldNo,revNo,flags,pKey,cult,null);
assemRef.ReadAsDef();
return assemRef;
}
internal static void Read(PEReader buff, TableRow[] table, PEFile pefile)
{
for (int i = 0; i < table.Length; i++)
{
HashAlgorithmType hAlg = (HashAlgorithmType)buff.ReadUInt32();
ushort majVer = buff.ReadUInt16();
ushort minVer = buff.ReadUInt16();
ushort bldNo = buff.ReadUInt16();
ushort revNo = buff.ReadUInt16();
uint flags = buff.ReadUInt32();
byte[] pKey = buff.GetBlob();
string name = buff.GetString();
string cult = buff.GetString();
table[i] = new Assembly(name, hAlg, majVer, minVer, bldNo, revNo, flags, pKey, cult, pefile);
}
}
/*------------------------- public set and get methods --------------------------*/
///
/// Add details about an assembly
///
/// Major Version
/// Minor Version
/// Build Number
/// Revision Number
/// Hash Key
/// Hash Algorithm
/// Culture
public void AddAssemblyInfo(int majVer, int minVer, int bldNo, int revNo,
byte[] key, HashAlgorithmType hash, string cult)
{
majorVer = (ushort)majVer;
minorVer = (ushort)minVer;
buildNo = (ushort)bldNo;
revisionNo = (ushort)revNo;
hashAlgId = hash;
publicKey = key;
culture = cult;
}
///
/// Get the major version number for this Assembly
///
/// major version number
public int MajorVersion() { return majorVer; }
///
/// Get the minor version number for this Assembly
///
/// minor version number
public int MinorVersion() { return minorVer; }
///
/// Get the build number for this Assembly
///
/// build number
public int BuildNumber() { return buildNo; }
///
/// Get the revision number for this Assembly
///
/// revision number
public int RevisionNumber() { return revisionNo; }
///
/// Get the public key for this Assembly
///
/// public key bytes
public byte[] Key() { return publicKey; }
///
/// Get the public key token for this assembly
/// or null if the assembly is not signed
///
/// Key token or null
public byte[] KeyTokenBytes()
{
if (this.publicKey != null &&
this.publicKey.Length != 0 &&
this.hashAlgId == HashAlgorithmType.SHA1)
{
int ix, ofst;
byte[] token = new byte[8];
HashAlgorithm sha = new SHA1CryptoServiceProvider();
byte[] hash = sha.ComputeHash(publicKey);
for (ix = 0, ofst = hash.Length - 8; ix < 8; ix++)
token[ix] = hash[ix + ofst];
return token;
}
else
return null;
}
///
/// Returns Public Key Token as Int64
///
///
public long KeyTokenAsLong()
{
byte[] token = KeyTokenBytes();
return (token == null ? 0 : BitConverter.ToInt64(token, 0));
}
///
/// Get the type of the hash algorithm for this Assembly
///
/// hash algorithm type
public HashAlgorithmType HashAlgorithm() { return hashAlgId; }
///
/// Get the culture information for this Assembly
///
/// culture string
public string Culture() { return culture; }
///
/// Add some security action(s) to this Assembly
///
public void AddSecurity(SecurityAction act, byte[] permissionSet)
{
AddSecurity(new DeclSecurity(this, act, permissionSet));
// securityActions = permissionSet;
}
///
/// Get the security information for this assembly
///
/// security information
public DeclSecurity[] GetSecurity()
{
if (security == null) return null;
return (DeclSecurity[])security.ToArray(typeof(DeclSecurity));
}
///
/// Check if this assembly has security information
///
public bool HasSecurity() { return security != null; }
///
/// Set the attributes for this assembly
///
/// assembly attribute
public void SetAssemblyAttr(AssemAttr aa)
{
flags = (uint)aa;
}
///
/// Add an attribute for this assembly
///
/// assembly attribute
public void AddAssemblyAttr(AssemAttr aa)
{
flags |= (uint)aa;
}
///
/// Get the attributes of this assembly
///
/// assembly attributes
public AssemAttr AssemblyAttributes()
{
return (AssemAttr)flags;
}
///
/// Make an AssemblyRef descriptor for this Assembly
///
/// AssemblyRef descriptor for this Assembly
public AssemblyRef MakeRefOf()
{
if (refOf == null)
{
refOf = new AssemblyRef(name, majorVer, minorVer, buildNo, revisionNo,
flags, publicKey, culture, null);
}
return refOf;
}
/*------------------------ internal functions ----------------------------*/
internal void AddSecurity(DeclSecurity sec)
{
if (security == null) security = new ArrayList();
security.Add(sec);
}
internal static uint Size(MetaData md)
{
return 16 + md.BlobIndexSize() + 2 * md.StringsIndexSize();
}
internal sealed override void BuildTables(MetaDataOut md)
{
md.AddToTable(MDTable.Assembly, this);
nameIx = md.AddToStringsHeap(name);
cultIx = md.AddToStringsHeap(culture);
keyIx = md.AddToBlobHeap(publicKey);
if (security != null)
{
for (int i = 0; i < security.Count; i++)
{
((DeclSecurity)security[i]).BuildMDTables(md);
}
}
}
internal sealed override void Write(PEWriter output)
{
//Console.WriteLine("Writing assembly element with nameIx of " + nameIx + " at file offset " + output.Seek(0,SeekOrigin.Current));
output.Write((uint)hashAlgId);
output.Write(majorVer);
output.Write(minorVer);
output.Write(buildNo);
output.Write(revisionNo);
output.Write(flags);
output.BlobIndex(keyIx);
output.StringsIndex(nameIx);
output.StringsIndex(cultIx);
}
internal override void Write(CILWriter output)
{
output.WriteLine(".assembly " + name + " { }");
}
internal sealed override uint GetCodedIx(CIx code)
{
switch (code)
{
case (CIx.HasCustomAttr): return 14;
case (CIx.HasDeclSecurity): return 2;
}
return 0;
}
}
/**************************************************************************/
///
/// Descriptor for a module
///
public abstract class Module : DefiningScope
{
Guid mvid;
uint mvidIx = 0;
internal ModuleRef refOf;
///
/// The default class "Module" for globals
///
protected ClassDef defaultClass;
///
/// Is this module a .dll or .exe
///
//protected bool isDLL;
///
/// Is this module mscorlib.dll
///
protected bool ismscorlib = false;
///
/// Managed resources for this module
///
protected ArrayList resources = new ArrayList();
/*-------------------- Constructors ---------------------------------*/
internal Module(string mName)
: base(GetBaseName(mName))
{
mvid = Guid.NewGuid();
//isDLL = name.EndsWith(".dll") || name.EndsWith(".DLL");
defaultClass = new ClassDef((PEFile)this, TypeAttr.Private, "", "");
defaultClass.MakeSpecial();
tabIx = MDTable.Module;
ismscorlib = name.ToLower() == "mscorlib.dll";
if (Diag.DiagOn) Console.WriteLine("Module name = " + name);
}
internal void Read(PEReader buff)
{
buff.ReadZeros(2);
name = buff.GetString();
mvid = buff.GetGUID();
uint junk = buff.GetGUIDIx();
junk = buff.GetGUIDIx();
if (Diag.DiagOn) Console.WriteLine("Reading module with name " + name + " and Mvid = " + mvid);
ismscorlib = name.ToLower() == "mscorlib.dll";
}
internal static ModuleRef ReadModuleRef(PEReader buff)
{
buff.ReadZeros(2);
string name = buff.GetString();
uint junk = buff.GetGUIDIx();
junk = buff.GetGUIDIx();
junk = buff.GetGUIDIx();
ModuleRef mRef = new ModuleRef(new ModuleFile(name, null));
mRef.ReadAsDef();
return mRef;
}
/*------------------------- public set and get methods --------------------------*/
///
/// Add a class to this Module
/// If this class already exists, throw an exception
///
/// attributes of this class
/// name space name
/// class name
/// a descriptor for this new class
public ClassDef AddClass(TypeAttr attrSet, string nsName, string name)
{
ClassDef aClass = GetClass(nsName, name);
if (aClass != null)
throw new DescriptorException("Class " + aClass.NameString());
aClass = new ClassDef((PEFile)this, attrSet, nsName, name);
classes.Add(aClass);
return aClass;
}
///
/// Add a class which extends System.ValueType to this Module
/// If this class already exists, throw an exception
///
/// attributes of this class
/// name space name
/// class name
/// a descriptor for this new class
public ClassDef AddValueClass(TypeAttr attrSet, string nsName, string name)
{
ClassDef aClass = AddClass(attrSet, nsName, name);
aClass.SuperType = MSCorLib.mscorlib.ValueType();
aClass.MakeValueClass();
return aClass;
}
///
/// Add a class to this PE File
///
/// attributes of this class
/// name space name
/// class name
/// super type of this class (extends)
/// a descriptor for this new class
public ClassDef AddClass(TypeAttr attrSet, string nsName, string name, Class superType)
{
ClassDef aClass = AddClass(attrSet, nsName, name);
aClass.SuperType = superType;
return aClass;
}
///
/// Add a class to this module
/// If this class already exists, throw an exception
///
/// The class to be added
public void AddClass(ClassDef aClass)
{
ClassDef eClass = GetClass(aClass.NameSpace(), aClass.Name());
if (eClass != null)
throw new DescriptorException("Class " + aClass.NameString());
classes.Add(aClass);
// MERGE change Refs to Defs here, fix this
aClass.SetScope((PEFile)this);
}
///
/// Get a class of this module, if no class exists, return null
///
/// The name of the class to get
/// ClassDef for name or null
public ClassDef GetClass(string name)
{
return (ClassDef)GetClass(null, name, false);
}
///
/// Get a class of this module, if no class exists, return null
///
/// The namespace of the class
/// The name of the class to get
/// ClassDef for nsName.name or null
public ClassDef GetClass(string nsName, string name)
{
return (ClassDef)GetClass(nsName, name, true);
}
///
/// Get all the classes of this module
///
/// An array containing a ClassDef for each class of this module
public ClassDef[] GetClasses()
{
return (ClassDef[])classes.ToArray(typeof(ClassDef));
}
///
/// Add a "global" method to this module
///
/// method name
/// return type
/// method parameters
/// a descriptor for this new "global" method
public MethodDef AddMethod(string name, Type retType, Param[] pars)
{
MethodDef newMeth = defaultClass.AddMethod(name, retType, pars);
return newMeth;
}
///
/// Add a "global" method to this module
///
/// method name
/// generic parameters
/// return type
/// method parameters
/// a descriptor for this new "global" method
public MethodDef AddMethod(string name, GenericParam[] genPars, Type retType, Param[] pars)
{
MethodDef newMeth = defaultClass.AddMethod(name, genPars, retType, pars);
return newMeth;
}
///
/// Add a "global" method to this module
///
/// method attributes
/// method implementation attributes
/// method name
/// return type
/// method parameters
/// a descriptor for this new "global" method
public MethodDef AddMethod(MethAttr mAtts, ImplAttr iAtts, string name, Type retType, Param[] pars)
{
MethodDef newMeth = defaultClass.AddMethod(mAtts, iAtts, name, retType, pars);
return newMeth;
}
///
/// Add a "global" method to this module
///
/// method attributes
/// method implementation attributes
/// method name
/// generic parameters
/// return type
/// method parameters
/// a descriptor for this new "global" method
public MethodDef AddMethod(MethAttr mAtts, ImplAttr iAtts, string name, GenericParam[] genPars, Type retType, Param[] pars)
{
MethodDef newMeth = defaultClass.AddMethod(mAtts, iAtts, name, genPars, retType, pars);
return newMeth;
}
///
/// Add a "global" method to this module
///
/// The method to be added
public void AddMethod(MethodDef meth)
{
defaultClass.AddMethod(meth);
}
///
/// Get a method of this module, if it exists
///
/// The name of the method to get
/// MethodDef for name, or null if one does not exist
public MethodDef GetMethod(string name)
{
return defaultClass.GetMethod(name);
}
///
/// Get all the methods of this module with a specified name
///
/// The name of the method(s)
/// An array of all the methods of this module called "name"
public MethodDef[] GetMethods(string name)
{
return defaultClass.GetMethods(name);
}
///
/// Get a method of this module, if it exists
///
/// The name of the method to get
/// The signature of the method
/// MethodDef for name(parTypes), or null if one does not exist
public MethodDef GetMethod(string name, Type[] parTypes)
{
return defaultClass.GetMethod(name, parTypes);
}
///
/// Get all the methods of this module
///
/// An array of all the methods of this module
public MethodDef[] GetMethods()
{
return defaultClass.GetMethods();
}
///
/// Delete a method from this module
///
/// The method to be deleted
public void RemoveMethod(MethodDef meth)
{
defaultClass.RemoveMethod(meth);
}
///
/// Delete a method from this module
///
/// The name of the method to be deleted
public void RemoveMethod(string name)
{
defaultClass.RemoveMethod(name);
}
///
/// Delete a method from this module
///
/// The name of the method to be deleted
/// The signature of the method to be deleted
public void RemoveMethod(string name, Type[] parTypes)
{
defaultClass.RemoveMethod(name, parTypes);
}
///
/// Delete a method from this module
///
/// The index of the method (in the method array
/// returned by GetMethods()) to be deleted
public void RemoveMethod(int ix)
{
defaultClass.RemoveMethod(ix);
}
///
/// Add a "global" field to this module
///
/// field name
/// field type
/// a descriptor for this new "global" field
public FieldDef AddField(string name, Type fType)
{
FieldDef newField = defaultClass.AddField(name, fType);
return newField;
}
///
/// Add a "global" field to this module
///
/// attributes of this field
/// field name
/// field type
/// a descriptor for this new "global" field
public FieldDef AddField(FieldAttr attrSet, string name, Type fType)
{
FieldDef newField = defaultClass.AddField(attrSet, name, fType);
return newField;
}
///
/// Add a "global" field to this module
///
/// The field to be added
public void AddField(FieldDef fld)
{
defaultClass.AddField(fld);
}
///
/// Get a field of this module, if it exists
///
/// The name of the field
/// FieldDef for "name", or null if one doesn't exist
public FieldDef GetField(string name)
{
return defaultClass.GetField(name);
}
///
/// Get all the fields of this module
///
/// An array of all the fields of this module
public FieldDef[] GetFields()
{
return defaultClass.GetFields();
}
///
/// Make a ModuleRef for this Module.
///
/// ModuleRef for this Module
public ModuleRef MakeRefOf(/*bool hasEntryPoint, byte[] hashValue*/)
{
if (refOf == null)
{
refOf = new ModuleRef(name/*,hasEntryPoint,hashValue*/);
refOf.defOf = this;
}/* else { // fix this
if (hasEntryPoint)
refOf.SetEntryPoint();
refOf.SetHash(hashValue);
}*/
return refOf;
}
///
/// Set the name for this module
///
/// New module name
public void SetName(string newName)
{
name = newName;
//isDLL = name.EndsWith(".dll") || name.EndsWith(".DLL");
}
public void SetMVid(Guid guid)
{
mvid = guid;
}
public Guid GetMVid()
{
return mvid;
}
/*------------------------- internal functions --------------------------*/
internal bool isMSCorLib() { return ismscorlib; }
internal bool isDefaultClass(ClassDef aClass) { return aClass == defaultClass; }
private static string GetBaseName(string name)
{
// more to this??
if (name.IndexOf("\\") != -1)
name = name.Substring(name.LastIndexOf("\\") + 1);
return name;
}
internal void SetDefaultClass(ClassDef dClass)
{
defaultClass = dClass;
}
internal sealed override void BuildTables(MetaDataOut md)
{
md.AddToTable(MDTable.Module, this);
nameIx = md.AddToStringsHeap(name);
mvidIx = md.AddToGUIDHeap(mvid);
defaultClass.BuildTables(md);
for (int i = 0; i < classes.Count; i++)
{
((Class)classes[i]).BuildMDTables(md);
}
for (int i = 0; i < resources.Count; i++)
{
((ManifestResource)resources[i]).BuildMDTables(md);
}
}
internal static uint Size(MetaData md)
{
return 2 + md.StringsIndexSize() + 3 * md.GUIDIndexSize();
}
internal sealed override void Write(PEWriter output)
{
output.Write((short)0);
output.StringsIndex(nameIx);
output.GUIDIndex(mvidIx);
output.GUIDIndex(0);
output.GUIDIndex(0);
}
internal sealed override uint GetCodedIx(CIx code)
{
switch (code)
{
case (CIx.HasCustomAttr): return 7;
case (CIx.ResolutionScope): return 0;
}
return 0;
}
}
}