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