/* * 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 { /**************************************************************************/ /// /// Base class for scopes (extended by Module, ModuleRef, Assembly, AssemblyRef) /// public abstract class ResolutionScope : MetaDataElement { internal protected uint nameIx = 0; internal protected string name; internal protected ArrayList classes = new ArrayList(); internal protected bool readAsDef = false; /*-------------------- Constructors ---------------------------------*/ internal ResolutionScope(string name) { this.name = name; } internal virtual void AddToClassList(Class aClass) { classes.Add(aClass); } internal Class GetExistingClass(string nameSpace, string name) { for (int i = 0; i < classes.Count; i++) { Class aClass = (Class)classes[i]; if ((aClass.Name() == name) && (aClass.NameSpace() == nameSpace)) return aClass; } return null; } protected Class GetClass(string nameSpace, string name, bool both) { for (int i = 0; i < classes.Count; i++) { Object aClass = classes[i]; if ((((Class)aClass).Name() == name) && (!both || (both && (((Class)aClass).NameSpace() == nameSpace)))) return (Class)aClass; } return null; } /// /// Delete a class from this module /// /// The name of the class to be deleted public void RemoveClass(Class aClass) { classes.Remove(aClass); } /// /// Delete the class at an index in the class array /// /// The index of the class to be deleted (from 0) public void RemoveClass(int ix) { classes.RemoveAt(ix); } public string Name() { return name; } internal override string NameString() { return "[" + name + "]"; } } /**************************************************************************/ /// /// A scope for descriptors which are referenced /// public abstract class ReferenceScope : ResolutionScope { /// /// A default class decriptor for globals /// protected ClassRef defaultClass; /*-------------------- Constructors ---------------------------------*/ internal ReferenceScope(string name) : base(name) { defaultClass = new ClassRef(this, "", ""); defaultClass.MakeSpecial(); } internal void ReadAsDef() { readAsDef = true; } internal ClassRef GetDefaultClass() { return defaultClass; } internal void SetDefaultClass(ClassRef dClass) { defaultClass = dClass; } internal override void AddToClassList(Class aClass) { ((ClassRef)aClass).SetScope(this); classes.Add(aClass); } internal void ReplaceClass(Class aClass) { bool found = false; for (int i = 0; (i < classes.Count) && !found; i++) { if (((Class)classes[i]).Name() == aClass.Name()) { found = true; } } if (!found) classes.Add(aClass); } internal bool isDefaultClass(ClassRef aClass) { return aClass == defaultClass; } /// /// Add a class to this Scope. If this class already exists, throw /// an exception /// /// The class to be added public void AddClass(ClassRef newClass) { ClassRef aClass = (ClassRef)GetClass(newClass.NameSpace(), newClass.Name(), true); if (aClass != null) throw new DescriptorException("Class " + newClass.NameString()); if (Diag.DiagOn) Console.WriteLine("Adding class " + newClass.Name() + " to ResolutionScope " + name); classes.Add(newClass); // Change Refs to Defs here newClass.SetScope(this); } /// /// Add a class to this Scope. If the class already exists, /// throw an exception. /// /// name space name /// class name /// a descriptor for this class in another module public virtual ClassRef AddClass(string nsName, string name) { ClassRef aClass = GetClass(nsName, name); if (aClass != null) { if ((aClass is SystemClass) && (!((SystemClass)aClass).added)) ((SystemClass)aClass).added = true; else throw new DescriptorException("Class " + aClass.NameString()); } else { aClass = new ClassRef(this, nsName, name); classes.Add(aClass); } return aClass; } /// /// Add a value class to this scope. If the class already exists, /// throw an exception. /// /// name space name /// class name /// public virtual ClassRef AddValueClass(string nsName, string name) { ClassRef aClass = AddClass(nsName, name); aClass.MakeValueClass(); return aClass; } /// /// Get a class of this scope, if it exists. /// /// The name of the class. /// ClassRef for "name". public ClassRef GetClass(string name) { return (ClassRef)GetClass(null, name, false); } /// /// Get a class of this scope, if it exists. /// /// The namespace of the class. /// The name of the class. /// ClassRef for "nsName.name". public ClassRef GetClass(string nsName, string name) { return (ClassRef)GetClass(nsName, name, true); } /// /// Get all the classes in this scope. /// /// An array of all the classes in this scope. public ClassRef[] GetClasses() { return (ClassRef[])classes.ToArray(typeof(ClassRef)); } /// /// Fetch a MethodRef descriptor for the method "retType name (pars)". /// If one exists, it is returned, else one is created. /// /// method name /// return type /// method parameter types /// a descriptor for this method in anther module public MethodRef AddMethod(string name, Type retType, Type[] pars) { MethodRef meth = defaultClass.AddMethod(name, retType, pars); return meth; } /// /// Fetch a MethodRef descriptor for the method "retType name (pars, optPars)". /// If one exists, it is returned, else one is created. /// /// method name /// return type /// parameter types /// optional param types for this vararg method /// a descriptor for this method public MethodRef AddVarArgMethod(string name, Type retType, Type[] pars, Type[] optPars) { MethodRef meth = defaultClass.AddVarArgMethod(name, retType, pars, optPars); return meth; } /// /// Add a method to this scope. /// /// The method to be added. public void AddMethod(MethodRef meth) { defaultClass.AddMethod(meth); } // internal void CheckAddMethod(MethodRef meth) { // defaultClass.CheckAddMethod(meth); // } /* internal void CheckAddMethods(ArrayList meths) { for (int i=0; i < meths.Count; i++) { Method meth = (Method)meths[i]; defaultClass.CheckAddMethod(meth); meth.SetParent(this); } } internal MethodRef GetMethod(string name, uint sigIx) { return defaultClass.GetMethod(name,sigIx); } */ /// /// Get a method of this scope, if it exists. /// /// The name of the method. /// MethodRef for "name", or null if none exists. public MethodRef GetMethod(string name) { return defaultClass.GetMethod(name); } /// /// Get all the methods with a specified name in this scope. /// /// The name of the method(s). /// An array of all the methods called "name". public MethodRef[] GetMethods(string name) { return defaultClass.GetMethods(name); } /// /// Get a method of this scope, if it exists. /// /// The name of the method /// The signature of the method. /// MethodRef for name(parTypes). public MethodRef GetMethod(string name, Type[] parTypes) { return defaultClass.GetMethod(name, parTypes); } /// /// Get a vararg method of this scope, if it exists. /// /// The name of the method. /// The signature of the method. /// The optional parameters of the vararg method. /// MethodRef for name(parTypes,optPars). public MethodRef GetMethod(string name, Type[] parTypes, Type[] optPars) { return defaultClass.GetMethod(name, parTypes, optPars); } /// /// Get all the methods in this module /// /// Array of the methods of this module public MethodRef[] GetMethods() { return defaultClass.GetMethods(); } /// /// Delete a method from this scope. /// /// The method to be deleted. public void RemoveMethod(MethodRef meth) { defaultClass.RemoveMethod(meth); } /// /// Delete a method from this scope. If there are multiple methods with /// the same name, the first on the list will be deleted. /// /// The name of the method to delete. public void RemoveMethod(string name) { defaultClass.RemoveMethod(name); } /// /// Delete a method from this scope. /// /// 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 (vararg) method from this scope. /// /// The name of the method to be deleted. /// The signature of the method to be deleted. /// The optional parameters of the vararg method. public void RemoveMethod(string name, Type[] parTypes, Type[] optTypes) { defaultClass.RemoveMethod(name, parTypes, optTypes); } /// /// Delete a method from this scope. /// /// The index of the method to be deleted. Index /// into array returned by GetMethods(). public void RemoveMethod(int index) { defaultClass.RemoveMethod(index); } /// /// Add a field to this scope. /// /// field name /// field type /// a descriptor for the field "name" in this scope public FieldRef AddField(string name, Type fType) { FieldRef field = defaultClass.AddField(name, fType); return field; } /// /// Add a field to this scope. /// /// The field to be added public void AddField(FieldRef fld) { defaultClass.AddField(fld); } /// /// Add a number of fields to this scope. /// /// The fields to be added. internal void AddFields(ArrayList flds) { for (int i = 0; i < flds.Count; i++) { FieldRef fld = (FieldRef)flds[i]; defaultClass.AddField(fld); } } /// /// Fetch the FieldRef descriptor for the field "name" in this module, /// if one exists /// /// field name /// FieldRef descriptor for "name" or null public FieldRef GetField(string name) { return defaultClass.GetField(name); } /// /// Get all the fields of this module /// /// Array of FieldRefs for this module public FieldRef[] GetFields() { return defaultClass.GetFields(); } internal void AddToMethodList(MethodRef meth) { defaultClass.AddToMethodList(meth); } internal void AddToFieldList(FieldRef fld) { defaultClass.AddToFieldList(fld); } internal MethodRef GetMethod(MethSig mSig) { return (MethodRef)defaultClass.GetMethod(mSig); } } /**************************************************************************/ /// /// A reference to an external assembly (.assembly extern) /// public class AssemblyRef : ReferenceScope { private ushort major, minor, build, revision; uint flags, keyIx, hashIx, cultIx; bool hasVersion = false, isKeyToken = false; byte[] keyBytes, hashBytes; string culture; /*-------------------- Constructors ---------------------------------*/ internal AssemblyRef(string name) : base(name) { tabIx = MDTable.AssemblyRef; } internal AssemblyRef(string name, ushort maj, ushort min, ushort bldNo, ushort rev, uint flags, byte[] kBytes, string cult, byte[] hBytes) : base(name) { tabIx = MDTable.AssemblyRef; major = maj; minor = min; build = bldNo; revision = rev; this.flags = flags; // check keyBytes = kBytes; // need to set is token or full key if (keyBytes != null) isKeyToken = keyBytes.Length <= 8; culture = cult; hashBytes = hBytes; tabIx = MDTable.AssemblyRef; } internal static AssemblyRef Read(PEReader buff) { 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(); byte[] hBytes = buff.GetBlob(); AssemblyRef assemRef; if (name.ToLower() == "mscorlib") { assemRef = MSCorLib.mscorlib; assemRef.AddVersionInfo(majVer, minVer, bldNo, revNo); assemRef.AddHash(hBytes); 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, hBytes); } return assemRef; } internal static void Read(PEReader buff, TableRow[] table) { for (int i = 0; i < table.Length; i++) table[i] = Read(buff); } /*------------------------- public set and get methods --------------------------*/ /// /// Add version information about this external assembly /// /// Major Version /// Minor Version /// Build Number /// Revision Number public void AddVersionInfo(int majVer, int minVer, int bldNo, int revNo) { major = (ushort)majVer; minor = (ushort)minVer; build = (ushort)bldNo; revision = (ushort)revNo; hasVersion = true; } /// /// Get the major version for this external assembly /// /// major version number public int MajorVersion() { return major; } /// /// Get the minor version for this external assembly /// /// minor version number public int MinorVersion() { return minor; } /// /// Get the build number for this external assembly /// /// build number public int BuildNumber() { return build; } /// /// Get the revision number for this external assembly /// /// revision number public int RevisionNumber() { return revision; } /// /// Check if this external assembly has any version information /// public bool HasVersionInfo() { return hasVersion; } /// /// Add the hash value for this external assembly /// /// bytes of the hash value public void AddHash(byte[] hash) { hashBytes = hash; } /// /// Get the hash value for this external assembly /// /// public byte[] GetHash() { return hashBytes; } /// /// Set the culture for this external assembly /// /// the culture string public void AddCulture(string cult) { culture = cult; } public string GetCulture() { return culture; } /// /// Add the full public key for this external assembly /// /// bytes of the public key public void AddKey(byte[] key) { flags |= 0x0001; // full public key keyBytes = key; } /// /// Add the public key token (low 8 bytes of the public key) /// /// low 8 bytes of public key public void AddKeyToken(byte[] key) { keyBytes = key; isKeyToken = true; } /// /// Get the public key token /// /// bytes of public key public byte[] GetKey() { return keyBytes; } /// /// Make an AssemblyRef for "name". /// /// The name of the assembly /// AssemblyRef for "name". public static AssemblyRef MakeAssemblyRef(string name) { AssemblyRef assemRef = new AssemblyRef(name); return assemRef; } public static AssemblyRef MakeAssemblyRef( string name, int majVer, int minVer, int bldNum, int revNum, byte[] key) { AssemblyRef assemRef = new AssemblyRef(name); assemRef.AddVersionInfo(majVer, minVer, bldNum, revNum); if (key.Length > 8) assemRef.AddKey(key); else assemRef.AddKeyToken(key); return assemRef; } /*------------------------ internal functions ----------------------------*/ internal void SetFlags(uint flags) { this.flags = flags; } internal string AssemblyString() { string result = name; if (hasVersion) result = result + ", Version=" + major + "." + minor + "." + build + "." + revision; if (keyBytes != null) { string tokenStr = "="; if (isKeyToken) tokenStr = "Token="; result = result + ", PublicKey" + tokenStr; for (int i = 0; i < keyBytes.Length; i++) { result = result + Hex.Byte(keyBytes[i]); } } if (culture != null) result = result + ", Culture=" + culture; return result; } internal static uint Size(MetaData md) { return 12 + 2 * md.StringsIndexSize() + 2 * md.BlobIndexSize(); } internal sealed override void BuildTables(MetaDataOut md) { md.AddToTable(MDTable.AssemblyRef, this); keyIx = md.AddToBlobHeap(keyBytes); nameIx = md.AddToStringsHeap(name); cultIx = md.AddToStringsHeap(culture); hashIx = md.AddToBlobHeap(hashBytes); } internal sealed override void Write(PEWriter output) { output.Write(major); output.Write(minor); output.Write(build); output.Write(revision); output.Write(flags); output.BlobIndex(keyIx); output.StringsIndex(nameIx); output.StringsIndex(cultIx); output.BlobIndex(hashIx); } internal override void Write(CILWriter output) { output.WriteLine(".assembly extern " + name + " { }"); } internal sealed override uint GetCodedIx(CIx code) { switch (code) { case (CIx.ResolutionScope): return 2; case (CIx.HasCustomAttr): return 15; case (CIx.Implementation): return 1; } return 0; } } /**************************************************************************/ /// /// The assembly for mscorlib. /// public sealed class MSCorLib : AssemblyRef { internal static MSCorLib mscorlib = new MSCorLib(); internal SystemClass ObjectClass; private ClassRef valueType; internal MSCorLib() : base("mscorlib") { classes.Add(new SystemClass(this, PrimitiveType.Void)); classes.Add(new SystemClass(this, PrimitiveType.Boolean)); classes.Add(new SystemClass(this, PrimitiveType.Char)); classes.Add(new SystemClass(this, PrimitiveType.Int8)); classes.Add(new SystemClass(this, PrimitiveType.UInt8)); classes.Add(new SystemClass(this, PrimitiveType.Int16)); classes.Add(new SystemClass(this, PrimitiveType.UInt16)); classes.Add(new SystemClass(this, PrimitiveType.Int32)); classes.Add(new SystemClass(this, PrimitiveType.UInt32)); classes.Add(new SystemClass(this, PrimitiveType.Int64)); classes.Add(new SystemClass(this, PrimitiveType.UInt64)); classes.Add(new SystemClass(this, PrimitiveType.Float32)); classes.Add(new SystemClass(this, PrimitiveType.Float64)); classes.Add(new SystemClass(this, PrimitiveType.IntPtr)); classes.Add(new SystemClass(this, PrimitiveType.UIntPtr)); classes.Add(new SystemClass(this, PrimitiveType.String)); classes.Add(new SystemClass(this, PrimitiveType.TypedRef)); ObjectClass = new SystemClass(this, PrimitiveType.Object); classes.Add(ObjectClass); valueType = new ClassRef(this, "System", "ValueType"); valueType.MakeValueClass(); classes.Add(valueType); } internal ClassRef ValueType() { return valueType; } } /**************************************************************************/ /// /// Descriptor for a module in an assembly /// public class ModuleRef : ReferenceScope { ArrayList exportedClasses = new ArrayList(); internal ModuleFile modFile; internal Module defOf; internal bool ismscorlib = false; /*-------------------- Constructors ---------------------------------*/ internal ModuleRef(string name, bool entryPoint, byte[] hashValue) : base(name) { modFile = new ModuleFile(name, hashValue, entryPoint); ismscorlib = name.ToLower() == "mscorlib.dll"; tabIx = MDTable.ModuleRef; } internal ModuleRef(string name) : base(name) { ismscorlib = name.ToLower() == "mscorlib.dll"; tabIx = MDTable.ModuleRef; } internal ModuleRef(ModuleFile file) : base(file.Name()) { modFile = file; tabIx = MDTable.ModuleRef; } internal static void Read(PEReader buff, TableRow[] mods, bool resolve) { for (int i = 0; i < mods.Length; i++) { string name = buff.GetString(); ModuleRef mRef = new ModuleRef(name); if (resolve) mRef.modFile = buff.GetFileDesc(name); mods[i] = mRef; } } internal sealed override void Resolve(PEReader buff) { modFile = buff.GetFileDesc(name); if (modFile != null) modFile.fileModule = this; } /*------------------------- public set and get methods --------------------------*/ /// /// Add a class which is declared public in this external module of /// THIS assembly. This class will be exported from this assembly. /// The ilasm syntax for this is .extern class /// /// attributes of the class to be exported /// name space name /// external class name /// the file where the class is declared /// is this class a value type? /// a descriptor for this external class public ClassRef AddExternClass(TypeAttr attrSet, string nsName, string name, bool isValueClass, PEFile pefile) { ClassRef cRef = new ClassRef(this, nsName, name); if (isValueClass) cRef.MakeValueClass(); ExternClass eClass = new ExternClass(attrSet, nsName, name, modFile); exportedClasses.Add(eClass); cRef.SetExternClass(eClass); classes.Add(cRef); return cRef; } public static ModuleRef MakeModuleRef(string name, bool entryPoint, byte[] hashValue) { ModuleRef mRef = new ModuleRef(name, entryPoint, hashValue); return mRef; } public void SetEntryPoint() { modFile.SetEntryPoint(); } public void SetHash(byte[] hashVal) { modFile.SetHash(hashVal); } /*------------------------- internal functions --------------------------*/ /* internal void AddMember(Member memb) { if (memb is Method) { Method existing = GetMethod(memb.Name(),((Method)memb).GetParTypes()); if (existing == null) methods.Add(memb); } else { Field existing = GetField(memb.Name()); if (existing == null) fields.Add(memb); } } */ internal void AddToExportedClassList(ClassRef exClass) { if (exportedClasses.Contains(exClass)) return; exportedClasses.Add(exClass); } internal void AddExternClass(ExternClass eClass) { exportedClasses.Add(eClass); } internal static uint Size(MetaData md) { return md.StringsIndexSize(); } internal sealed override void BuildTables(MetaDataOut md) { md.AddToTable(MDTable.ModuleRef, this); nameIx = md.AddToStringsHeap(name); if (modFile != null) modFile.BuildMDTables(md); for (int i = 0; i < exportedClasses.Count; i++) ((ExternClass)exportedClasses[i]).BuildMDTables(md); } internal sealed override void Write(PEWriter output) { output.StringsIndex(nameIx); } internal sealed override uint GetCodedIx(CIx code) { switch (code) { case (CIx.HasCustomAttr): return 12; case (CIx.MemberRefParent): return 2; case (CIx.ResolutionScope): return 1; } return 0; } } }