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