using System; using System.Text; using System.Collections; using System.Runtime.InteropServices; // // Types // // Used to define the column of a table type enum ColumnType { Module = 0x00, TypeRef = 0x01, Type = 0x02, FieldPtr = 0x03, Field = 0x04, MethodPtr = 0x05, Method = 0x06, ParamPtr = 0x07, Param = 0x08, InterfaceImpl = 0x09, MemberRef = 0x0a, Constant = 0x0b, CustomAttribute = 0x0c, FieldMarshal = 0x0d, DeclSecurity = 0x0e, ClassLayout = 0x0f, FieldLayout = 0x10, StandAloneSig = 0x11, EventMap = 0x12, EventPtr = 0x13, Event = 0x14, PropertyMap = 0x15, PropertyPtr = 0x16, Property = 0x17, MethodSemantics = 0x18, MethodImpl = 0x19, ModuleRef = 0x1a, TypeSpec = 0x1b, ImplMap = 0x1c, FieldRVA = 0x1d, ENCLog = 0x1e, ENCMap = 0x1f, Assembly = 0x20, AssemblyProcessor= 0x21, AssemblyOS = 0x22, AssemblyRef = 0x23, AssemblyRefProcessor = 0x24, AssemblyRefOS = 0x25, File = 0x26, ExportedType = 0x27, ManifestResource = 0x28, NestedClass = 0x29, TypeTyPar = 0x2a, MethodTyPar = 0x2b, // Generic types UInt16 = 64, UInt32 = 65, String = 66, Blob = 67, GUID = 68, UserString = 69, // Coded indices TypeDefOrRef = 70, HasConstant = 71, CustomAttributeType = 72, HasSemantics = 73, ResolutionScope = 74, HasFieldMarshal = 75, HasDeclSecurity = 76, MemberRefParent = 77, MethodDefOrRef = 78, MemberForwarded = 79, Implementation = 80, HasCustomAttribute = 81 } // // Metadata structures // [StructLayout(LayoutKind.Explicit)] public struct MetaDataHeader { [FieldOffset(0)] public UInt32 Signature; [FieldOffset(4)] public UInt16 MajorVersion; // currently 1 (but won't be checked) [FieldOffset(6)] public UInt16 MinorVersion; // currently 0 (but won't be checked) [FieldOffset(8)] public UInt32 Reserved; [FieldOffset(12)] public UInt32 VersionLength; [FieldOffset(16)] public byte Version; // start of UTF8-encoded version string //[FieldOffset(16 + VersionLength + x)] public UInt16 Flags; //[FieldOffset(18 + VersionLength + x)] public UInt16 StreamCount; //[FieldOffset(20 + VersionLength + x)] public StreamHeader[] StreamHeaders; } [StructLayout(LayoutKind.Explicit)] public struct StreamHeader { [FieldOffset(0)] public UInt32 DataOffset; [FieldOffset(4)] public UInt32 Size; [FieldOffset(8)] public byte Name; // start of null terminated variable length array } // // Exception handling structures // [StructLayout(LayoutKind.Explicit)] public struct TinyExceptionHandler { [FieldOffset(0)] public UInt16 Flags; [FieldOffset(2)] public UInt16 TryOffset; [FieldOffset(4)] public byte TryLength; [FieldOffset(5)] public UInt16 HandlerOffset; [FieldOffset(7)] public byte HandlerLength; [FieldOffset(8)] public UInt32 FilterOffsetOrToken; } [StructLayout(LayoutKind.Explicit)] public struct FatExceptionHandler { [FieldOffset(0)] public UInt32 Flags; [FieldOffset(4)] public UInt32 TryOffset; [FieldOffset(8)] public UInt32 TryLength; [FieldOffset(12)] public UInt32 HandlerOffset; [FieldOffset(16)] public UInt32 HandlerLength; [FieldOffset(20)] public UInt32 FilterOffsetOrToken; } // // Table structures/types // // Used to process a row of a table delegate void Handler(UInt32 RowIndex); unsafe class Table { // Data public string m_Name; public bool m_Present; public UInt32 m_RowCount; public UInt32 m_CellSize; public UInt32 m_TotalSize; public byte *m_Data; public Handler m_Handler; public Hashtable m_Columns = new Hashtable(); // Constructor public Table(string Name, Handler DataHandler, ColumnType[] Columns, string[] ColumnNames) { m_Present = false; m_Name = Name; m_Handler = DataHandler; if (Columns == null || ColumnNames == null) return; else if (Columns.Length != ColumnNames.Length) throw new Exception("Member types to names don't match"); for (int i = 0; i < Columns.Length; i++) m_Columns[ColumnNames[i]] = Columns[i]; } } [StructLayout(LayoutKind.Explicit)] public struct TablesHeader { [FieldOffset(0)] public UInt32 Reserved1; // always 0 [FieldOffset(4)] public byte MajorVersion; // currently 1 (but won't be checked) [FieldOffset(5)] public byte MinorVersion; // currently 0 (but won't be checked) [FieldOffset(6)] public byte HeapSizes; [FieldOffset(7)] public byte Reserved2; // always 1 [FieldOffset(8)] public UInt64 PresentTables; [FieldOffset(16)] public UInt64 SortedTables; [FieldOffset(24)] public UInt32 Rows; // start of 4 byte array of rows //[FieldOffset(?)] public Table[] Tables; } // // Personal classes // // Used to represent the #Strings and #Blob heaps unsafe struct Heap { public StreamHeader *Header; public UInt32 DataSize; // can also be read from Header->Size public byte *Data; } public struct Parameter { public string Name; public string TypeName; public UInt32 Type; public UInt32 Flags; } // This will be used to maintain a list of functions to hook sorted by // address, so that when writing the functions, they will already be in order public unsafe class HookedFunction : IComparable { public string m_Name = null; public string m_FullName = null; public string m_DeclaringTypeName; public UInt32 m_ReturnType; public string m_ReturnTypeName; public Parameter[] m_Parameters; public UInt32 m_OldRVA = 0; public UInt32 *m_pRVA = null; // this will overwrite the original entry public UInt32 m_OldFileOffset; public UInt32 m_FileOffset; public UInt32 m_HeaderSize = 0; public byte *m_Header; public UInt32 m_HookSize = 0; public byte[] m_Hook; public UInt32 m_CodeSize = 0; public byte[] m_Code; public UInt32 m_EHSize = 0; // exception handling data public byte *m_EHData; public int CompareTo(object Function) { if (m_OldRVA > ((HookedFunction)Function).m_OldRVA) return 1; else if (m_OldRVA < ((HookedFunction)Function).m_OldRVA) return -1; else return 0; } public void BuildName() { if (m_DeclaringTypeName == "") m_FullName = m_Name; else m_FullName = m_DeclaringTypeName + "::" + m_Name; m_FullName = m_ReturnTypeName + " " + m_FullName + "("; if (m_Parameters != null) { for (int i = 0; i < m_Parameters.Length; i++) { m_FullName += m_Parameters[i].TypeName; if (m_FullName[m_FullName.Length - 1] != '*' && m_FullName[m_FullName.Length - 1] != '&') m_FullName += " "; m_FullName += m_Parameters[i].Name; if (i != m_Parameters.Length - 1) m_FullName += ", "; } } m_FullName += ")"; } public void Dump() { Console.WriteLine("Hooking {0}", m_FullName); Console.WriteLine("Moving from RVA 0x{0:X4} (file offset 0x{1:X4}) to 0x{2:X4} (file offset 0x{3:X4})", m_OldRVA, m_OldFileOffset, *m_pRVA, m_FileOffset); Console.WriteLine("Original code is {0} bytes", m_CodeSize); Console.WriteLine("Inserted {0} bytes of hooking code", m_HookSize); if (m_Parameters != null) { Console.WriteLine("Parameters:"); foreach (Parameter param in m_Parameters) Console.WriteLine("{0} (0x{1:X4})", param.Name, param.Flags); } #if DEBUG Console.WriteLine("\nMethod header:"); HexDump.Dump(m_Header, m_HeaderSize); Console.WriteLine("\nHook code:"); HexDump.Dump(m_Hook, m_HookSize); Console.WriteLine("\nMethod code:"); HexDump.Dump(m_Code, m_CodeSize); Console.WriteLine("\nException handling data:"); HexDump.Dump(m_EHData, m_EHSize); #endif Console.Write("\n"); } } // // Class to handle meta data // public unsafe class MetaData { // Data UInt32 m_HeaderSize; Int32 m_HeaderRVA; MetaDataHeader *m_Header; byte *m_ImageBase; ArrayList m_HookedFunctions = new ArrayList(); ArrayList m_HookStrings = new ArrayList(); public HookFunction m_HookFunction; //----------------------------------------- // MetaData functions // Constructor public MetaData(HookFunction Function, byte *ImageBase, Int32 RVA, MetaDataHeader *MDHeader, UInt32 HeaderSize) { m_HeaderRVA = RVA; m_Header = MDHeader; m_ImageBase = ImageBase; m_HeaderSize = HeaderSize; m_HookFunction = Function; #if DEBUG Console.WriteLine("METADATA ({0} bytes)\n", HeaderSize); Console.WriteLine("Signature: 0x{0:X8}", MDHeader->Signature); #endif if (MDHeader->Signature != 0x424A5342) throw new Exception("Invalid metadata exception"); #if DEBUG Console.WriteLine("Assembly version: {0}.{1}", MDHeader->MajorVersion, MDHeader->MinorVersion); Console.WriteLine(".NET framework version: {0}", MakeStringFromUTF8((byte *)&MDHeader->VersionLength + 4)); #endif UInt16 *ptr16 = (UInt16 *)(&MDHeader->Version + MDHeader->VersionLength + (MDHeader->VersionLength % 4)); UInt16 Flags; UInt16 StreamCount; Flags = *(ptr16++); StreamCount = *(ptr16++); #if DEBUG Console.WriteLine("Flags: 0x{0:X4}", Flags); Console.WriteLine("\nStream count: {0}", StreamCount); #endif StreamHeader *Stream; string StreamName; TablesHeader *TableHeader = null; byte *ptr8 = (byte *)ptr16; for (int i = 0; i < StreamCount; i++) { Stream = (StreamHeader *)ptr8; StreamName = MakeStringFromANSI(&Stream->Name); if (StreamName[0] != '#') // hack to fix an occasional alignment problem { ptr8--; i--; continue; } #if DEBUG Console.WriteLine("Stream \"{0}\" at offset 0x{1:X4} ({2} bytes)", StreamName, Stream->DataOffset, Stream->Size); #endif byte *Data = (byte *)MDHeader + Stream->DataOffset; if (StreamName == "#Strings") { m_StringsHeap= new Heap(); m_StringsHeap.Header = Stream; m_StringsHeap.DataSize = Stream->Size; m_StringsHeap.Data = Data; } else if (StreamName == "#Blob") { m_BlobHeap = new Heap(); m_BlobHeap.Header = Stream; m_BlobHeap.DataSize = Stream->Size; m_BlobHeap.Data = Data; } else if (StreamName == "#~" || StreamName == "#-") { TableHeader = (TablesHeader *)Data; } // Skip to the start of the next stream ptr8 = &Stream->Name + StreamName.Length; while (*ptr8 == 0) ptr8++; } #if DEBUG Console.Write("\n"); #endif if (m_StringsHeap.Data == null) throw new Exception("Unable to find #Strings"); if (m_BlobHeap.Data == null) throw new Exception("Unable to find #Blob"); if (TableHeader == null) throw new Exception("Unable to find table data"); ProcessTableHeader(TableHeader); } void ProcessTableHeader(TablesHeader *TableHeader) { Int32 i; if ((TableHeader->HeapSizes & 0x01) > 0) m_LargeStringSize = true; else m_LargeStringSize = false; if ((TableHeader->HeapSizes & 0x02) > 0) m_LargeGUIDSize = true; else m_LargeGUIDSize = false; if ((TableHeader->HeapSizes & 0x04) > 0) m_LargeBlobSize = true; else m_LargeBlobSize = false; #if DEBUG Console.WriteLine("#String is using a {0} byte index", (m_LargeStringSize ? 4 : 2)); Console.WriteLine("#GUID is using a {0} byte index", (m_LargeGUIDSize ? 4 : 2)); Console.WriteLine("#Blob is using a {0} byte index\n", (m_LargeBlobSize ? 4 : 2)); #endif UInt64 bit = 0x01; UInt32 *ptr32 = &TableHeader->Rows; // Setup table and coded indices Initialize(); // Discover how many tables are present for (i = 0; i < 64; i++) { // Read in the row count if the table is present if ((TableHeader->PresentTables & bit) > 0) { m_Tables[i].m_Present = true; m_Tables[i].m_RowCount = *ptr32++; } bit <<= 1; } byte *Data = (byte *)ptr32; // Setup table sizes for (i = 0; i < 64; i++) { if (!m_Tables[i].m_Present) continue; SetTableSizes(m_Tables[i]); m_Tables[i].m_Data = Data; Data += m_Tables[i].m_TotalSize; } if (!m_Tables[TABLE_METHOD].m_Present) throw new Exception("No methods to hook"); Console.WriteLine("\nParsing the method table..."); for (i = 0; i < m_Tables[TABLE_METHOD].m_RowCount; i++) { Console.WriteLine("Method row {0}", i+1); ProcessMethod((UInt32)i); } Console.Write("\n"); } public UInt32 SizeOfHookedFunctions() { UInt32 BytesNeeded = 0; HookedFunction Function; // Sort functions by RVA m_HookedFunctions.Sort(); // Calculate the space needed for hooked function to be added for (int i = 0; i < m_HookedFunctions.Count; i++) { Function = (HookedFunction)m_HookedFunctions[i]; BytesNeeded += Function.m_HeaderSize; BytesNeeded += Function.m_HookSize; BytesNeeded += Function.m_CodeSize; BytesNeeded += Function.m_EHSize; } return BytesNeeded; } // Write the hooked functions to this location public void WriteHookFunctions(byte *Output, UInt32 RVA) { Int32 i, j; HookedFunction Function; for (i = 0; i < m_HookedFunctions.Count; i++) { Function = (HookedFunction)m_HookedFunctions[i]; *(Function.m_pRVA) = RVA; Function.m_FileOffset = (UInt32)(Output - m_ImageBase); Function.Dump(); // Copy the method header for (j = 0; j < Function.m_HeaderSize; j++) { *Output++ = Function.m_Header[j]; RVA++; } // Copy the hooking code for (j = 0; j < Function.m_HookSize; j++) { *Output++ = Function.m_Hook[j]; RVA++; } // Copy the method body for (j = 0; j < Function.m_CodeSize; j++) { *Output++ = Function.m_Code[j]; RVA++; } // Copy the exception handling data for (j = 0; j < Function.m_EHSize; j++) { *Output++ = Function.m_EHData[j]; RVA++; } } #if DEBUG Console.WriteLine("Hooked {0} function(s)", i); #endif } // // MetaData tables // Table[] m_Tables = new Table[64]; Hashtable m_CodedIndices = new Hashtable(); Int32[] m_CodedIndexTags = new int[] { 0, 1, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5 }; bool m_LargeStringSize, m_LargeGUIDSize, m_LargeBlobSize; Heap m_StringsHeap; Heap m_BlobHeap; // Table type constants public const int TABLE_MODULE = 0x00; public const int TABLE_TYPEREF = 0x01; public const int TABLE_TYPEDEF = 0x02; public const int TABLE_FIELDPTR = 0x03; public const int TABLE_FIELD = 0x04; public const int TABLE_METHODPTR = 0x05; public const int TABLE_METHOD = 0x06; public const int TABLE_PARAMPTR = 0x07; public const int TABLE_PARAM = 0x08; public const int TABLE_INTERFACEIMPL = 0x09; public const int TABLE_MEMBERREF = 0x0a; public const int TABLE_CONSTANT = 0x0b; public const int TABLE_CUSTOMATTRIBUTE = 0x0c; public const int TABLE_FIELDMARSHAL = 0x0d; public const int TABLE_DECLSECURITY = 0x0e; public const int TABLE_CLASSLAYOUT = 0x0f; public const int TABLE_FIELDLAYOUT = 0x10; public const int TABLE_STANDALONESIG = 0x11; public const int TABLE_EVENTMAP = 0x12; public const int TABLE_EVENTPTR = 0x13; public const int TABLE_EVENT = 0x14; public const int TABLE_PROPERTYMAP = 0x15; public const int TABLE_PROPERTYPTR = 0x16; public const int TABLE_PROPERTY = 0x17; public const int TABLE_METHODSEMANTICS = 0x18; public const int TABLE_METHODIMPL = 0x19; public const int TABLE_MODULEREF = 0x1a; public const int TABLE_TYPESPEC = 0x1b; public const int TABLE_IMPLMAP = 0x1c; public const int TABLE_FIELDRVA = 0x1d; public const int TABLE_ENCLOG = 0x1e; public const int TABLE_ENCMAP = 0x1f; public const int TABLE_ASSEMBLY = 0x20; public const int TABLE_ASSEMBLYPROCESSOR = 0x21; public const int TABLE_ASSEMBLYOS = 0x22; public const int TABLE_ASSEMBLYREF = 0x23; public const int TABLE_ASSEMBLYREFPROCESSOR = 0x24; public const int TABLE_ASSEMBLYREFOS = 0x25; public const int TABLE_FILE = 0x26; public const int TABLE_EXPORTEDTYPE = 0x27; public const int TABLE_MANIFESTRESOURCE = 0x28; public const int TABLE_NESTEDCLASS = 0x29; public const int TABLE_TYPETYPEPARAM = 0x2a; public const int TABLE_METHODTYPEPARAM = 0x2b; // Method constants const int METHOD_TINY_FORMAT = 0x02; const int METHOD_FAT_FORMAT = 0x03; // Method implementation flags const int METHOD_COMPILERCONTROLLED = 0x0000; const int METHOD_SPECIALNAME = 0x0800; // Method flags const int METHOD_NATIVE = 0x01; const int METHOD_UNMANAGED = 0x04; const int METHOD_UNMANAGEDEXPORT = 0x08; const int METHOD_FORWARDREF = 0x10; const int METHOD_INTERNALCALL = 0x1000; // Type information for signatures const int SIGNATURE_VARARG = 0x05; const int SIGNATURE_BYREF = 0x10; const int SIGNATURE_TYPEDBYREF = 0x16; const int SIGNATURE_HASTHIS = 0x20; const int SIGNATURE_EXPLICITTHIS = 0x40; // Types const int TYPE_END = 0x00; const int TYPE_VOID = 0x01; const int TYPE_BOOLEAN = 0x02; const int TYPE_CHAR = 0x03; const int TYPE_I1 = 0x04; const int TYPE_U1 = 0x05; const int TYPE_I2 = 0x06; const int TYPE_U2 = 0x07; const int TYPE_I4 = 0x08; const int TYPE_U4 = 0x09; const int TYPE_I8 = 0x0a; const int TYPE_U8 = 0x0b; const int TYPE_R4 = 0x0c; const int TYPE_R8 = 0x0d; const int TYPE_STRING = 0x0e; const int TYPE_POINTER = 0x0f; const int TYPE_BYREF = 0x10; const int TYPE_VALUETYPE = 0x11; const int TYPE_CLASS = 0x12; const int TYPE_ARRAY = 0x14; const int TYPE_TYPEDREFERENCE = 0x16; const int TYPE_INTPTR = 0x18; const int TYPE_UINTPTR = 0x19; const int TYPE_FUNCPTR = 0x1b; const int TYPE_OBJECT = 0x1c; const int TYPE_SZARRAY = 0x1d; const int TYPE_CUSTOMMODREQ = 0x1f; const int TYPE_CUSTOMMODOPT = 0x20; const int TYPE_INTERNAL = 0x21; const int TYPE_MODIFIER = 0x40; const int TYPE_SENTINEL = 0x41; const int TYPE_PINNED = 0x45; // IL opcodes public const int OPCODE_NOP = 0x00; public const int OPCODE_BREAK = 0x01; public const int OPCODE_LDARG_0 = 0x02; public const int OPCODE_LDARG_1 = 0x03; public const int OPCODE_LDARG_2 = 0x04; public const int OPCODE_LDARG_3 = 0x05; public const int OPCODE_LDARG_S = 0x0e; public const int OPCODE_LDARGA_S = 0x0f; public const int OPCODE_LDNULL = 0x14; public const int OPCODE_LDC_I4_0 = 0x16; public const int OPCODE_LDC_I4_1 = 0x17; public const int OPCODE_LDC_I4_2 = 0x18; public const int OPCODE_LDC_I4_3 = 0x19; public const int OPCODE_LDC_I4_4 = 0x1a; public const int OPCODE_LDC_I4_5 = 0x1b; public const int OPCODE_LDC_I4_6 = 0x1c; public const int OPCODE_LDC_I4_7 = 0x1d; public const int OPCODE_LDC_I4_8 = 0x1e; public const int OPCODE_LDC_I4_S = 0x1f; public const int OPCODE_LDC_I4 = 0x20; public const int OPCODE_LDC_I8 = 0x21; public const int OPCODE_LDC_R4 = 0x22; public const int OPCODE_LDC_R8 = 0x23; public const int OPCODE_DUP = 0x25; public const int OPCODE_POP = 0x26; public const int OPCODE_JMP = 0x27; public const int OPCODE_CALL = 0x28; public const int OPCODE_RET = 0x2a; public const int OPCODE_BR_S = 0x2b; public const int OPCODE_BRFALSE_S = 0x2c; public const int OPCODE_BRTRUE_S = 0x2d; public const int OPCODE_BEQ_S = 0x2e; public const int OPCODE_BGE_S = 0x2f; public const int OPCODE_BGT_S = 0x30; public const int OPCODE_BLE_S = 0x31; public const int OPCODE_BLT_S = 0x32; public const int OPCODE_BNE_UN_S = 0x33; public const int OPCODE_BGE_UN_S = 0x34; public const int OPCODE_BGT_UN_S = 0x35; public const int OPCODE_BLE_UN_S = 0x36; public const int OPCODE_BLT_UN_S = 0x37; public const int OPCODE_BR = 0x38; public const int OPCODE_BRFALSE = 0x39; public const int OPCODE_BRTRUE = 0x3a; public const int OPCODE_BEQ = 0x3b; public const int OPCODE_BGE = 0x3c; public const int OPCODE_BGT = 0x3d; public const int OPCODE_BLE = 0x3e; public const int OPCODE_BLT = 0x3f; public const int OPCODE_BNE_UN = 0x40; public const int OPCODE_BGE_UN = 0x41; public const int OPCODE_BGT_UN = 0x42; public const int OPCODE_BLE_UN = 0x43; public const int OPCODE_BLT_UN = 0x44; public const int OPCODE_ADD = 0x58; public const int OPCODE_SUB = 0x59; public const int OPCODE_MUL = 0x5a; public const int OPCODE_DIV = 0x5b; public const int OPCODE_DIV_UN = 0x5c; public const int OPCODE_REM = 0x5d; public const int OPCODE_REM_UN = 0x5e; public const int OPCODE_AND = 0x5f; public const int OPCODE_OR = 0x60; public const int OPCODE_XOR = 0x61; public const int OPCODE_SHL = 0x62; public const int OPCODE_SHR = 0x63; public const int OPCODE_SHR_UN = 0x64; public const int OPCODE_NEG = 0x65; public const int OPCODE_NOT = 0x66; public const int OPCODE_CONV_I1 = 0x67; public const int OPCODE_CONV_I2 = 0x68; public const int OPCODE_CONV_I4 = 0x69; public const int OPCODE_CONV_I8 = 0x6a; public const int OPCODE_CONV_R4 = 0x6b; public const int OPCODE_CONV_R8 = 0x6c; public const int OPCODE_CONV_U4 = 0x6d; public const int OPCODE_CONV_U8 = 0x6e; public const int OPCODE_LDOBJ = 0x71; public const int OPCODE_LDSTR = 0x72; public const int OPCODE_CONV_R_UN = 0x76; public const int OPCODE_UNBOX = 0x79; public const int OPCODE_CONV_OVF_I1_UN = 0x82; public const int OPCODE_CONV_OVF_I2_UN = 0x83; public const int OPCODE_CONV_OVF_I4_UN = 0x84; public const int OPCODE_CONV_OVF_I8_UN = 0x85; public const int OPCODE_CONV_OVF_U1_UN = 0x86; public const int OPCODE_CONV_OVF_U2_UN = 0x87; public const int OPCODE_CONV_OVF_U4_UN = 0x88; public const int OPCODE_CONV_OVF_U8_UN = 0x89; public const int OPCODE_CONV_OVF_I_UN = 0x8a; public const int OPCODE_CONV_OVF_U_UN = 0x8b; public const int OPCODE_BOX = 0x8c; public const int OPCODE_LDLEN = 0x8e; public const int OPCODE_LDELEM_I1 = 0x90; public const int OPCODE_LDELEM_U1 = 0x91; public const int OPCODE_LDELEM_I2 = 0x92; public const int OPCODE_LDELEM_U2 = 0x93; public const int OPCODE_LDELEM_I4 = 0x94; public const int OPCODE_LDELEM_U4 = 0x95; public const int OPCODE_LDELEM_I8 = 0x96; public const int OPCODE_LDELEM_I = 0x97; public const int OPCODE_LDELEM_R4 = 0x98; public const int OPCODE_LDELEM_R8 = 0x99; public const int OPCODE_LDELEM_REF = 0x9a; public const int OPCODE_CONV_OVF_I1 = 0xb3; public const int OPCODE_CONV_OVF_U1 = 0xb4; public const int OPCODE_CONV_OVF_I2 = 0xb5; public const int OPCODE_CONV_OVF_U2 = 0xb6; public const int OPCODE_CONV_OVF_I4 = 0xb7; public const int OPCODE_CONV_OVF_U4 = 0xb8; public const int OPCODE_CONV_OVF_I8 = 0xb9; public const int OPCODE_CONV_OVF_U8 = 0xba; public const int OPCODE_REFANYVAL = 0xc2; public const int OPCODE_MKREFANY = 0xc6; public const int OPCODE_LDTOKEN = 0xd0; public const int OPCODE_CONV_U2 = 0xd1; public const int OPCODE_CONV_U1 = 0xd2; public const int OPCODE_CONV_I = 0xd3; public const int OPCODE_CONV_OVF_I = 0xd4; public const int OPCODE_CONV_OVF_U = 0xd5; public const int OPCODE_ADD_OVF_U = 0xd6; public const int OPCODE_ADD_OVF_UN = 0xd7; public const int OPCODE_MUL_OVF_U = 0xd8; public const int OPCODE_MUL_OVF_UN = 0xd9; public const int OPCODE_SUB_OVF_U = 0xda; public const int OPCODE_SUB_OVF_UN = 0xdb; public const int OPCODE_CONV_U = 0xe0; //public const int OPCODE_LDARG = 0xfe 0x09 //public const int OPCODE_LDARGA = 0xfe 0xa //public const int OPCODE_UNALIGNED = 0xfe 0x12 //public const int OPCODE_VOLATILE = 0xfe 0x13 //public const int OPCODE_TAIL = 0xfe 0x14 //public const int OPCODE_SIZEOF = 0xfe 0x1c //public const int OPCODE_REFANYTYPE = 0xfe 0x1d // Initialize the known values (this is tedious) void Initialize() { // Tables m_Tables[TABLE_MODULE] = new Table("Module", new Handler(ProcessUnknown), new ColumnType[] { ColumnType.UInt16, ColumnType.String, ColumnType.GUID, ColumnType.GUID, ColumnType.GUID }, new String[] { "Generation", "Name", "Mvid", "EncId", "EncBaseId" }); m_Tables[TABLE_TYPEREF] = new Table("TypeRef", new Handler(ProcessUnknown), new ColumnType[] { ColumnType.ResolutionScope, ColumnType.String, ColumnType.String }, new String[] { "ResolutionScope", "Name", "Namespace" }); m_Tables[TABLE_TYPEDEF] = new Table("Type", new Handler(ProcessUnknown), new ColumnType[] { ColumnType.UInt32, ColumnType.String, ColumnType.String, ColumnType.TypeDefOrRef, ColumnType.Field, ColumnType.Method }, new String[] { "Flags", "Name", "Namespace", "Extends", "FieldList", "MethodList" }); m_Tables[TABLE_FIELDPTR] = new Table("FieldPtr", new Handler(ProcessUnknown), new ColumnType[] { ColumnType.Field }, new String[] { "Field" }); m_Tables[TABLE_FIELD] = new Table("Field", new Handler(ProcessUnknown), new ColumnType[] { ColumnType.UInt16, ColumnType.String, ColumnType.Blob }, new String[] { "Flags", "Name", "Signature" }); m_Tables[TABLE_METHODPTR] = new Table("MethodPtr", new Handler(ProcessUnknown), new ColumnType[] { ColumnType.Method }, new String[] { "Method" }); m_Tables[TABLE_METHOD] = new Table("Method", new Handler(ProcessMethod), new ColumnType[] { ColumnType.UInt32, ColumnType.UInt16, ColumnType.UInt16, ColumnType.String, ColumnType.Blob, ColumnType.Param }, new String[] { "RVA", "ImplFlags", "Flags", "Name", "Signature", "ParamList" }); m_Tables[TABLE_PARAMPTR] = new Table("ParamPtr", new Handler(ProcessUnknown), new ColumnType[] { ColumnType.Param }, new String[] { "Param" }); m_Tables[TABLE_PARAM] = new Table("Param", new Handler(ProcessUnknown), new ColumnType[] { ColumnType.UInt16, ColumnType.UInt16, ColumnType.String }, new String[] { "Flags", "Sequence", "Name" }); m_Tables[TABLE_INTERFACEIMPL] = new Table("InterfaceImpl", new Handler(ProcessUnknown), new ColumnType[] { ColumnType.Type, ColumnType.TypeDefOrRef }, new String[] { "Class", "Interface" }); m_Tables[TABLE_MEMBERREF] = new Table("MemberRef", new Handler(ProcessUnknown), new ColumnType[] { ColumnType.MemberRefParent, ColumnType.String, ColumnType.Blob }, new String[] { "Class", "Name", "Signature" }); m_Tables[TABLE_CONSTANT] = new Table("Constant", new Handler(ProcessUnknown), new ColumnType[] { ColumnType.UInt16, ColumnType.HasConstant, ColumnType.Blob }, new String[] { "Type", "Parent", "Value" }); m_Tables[TABLE_CUSTOMATTRIBUTE] = new Table("CustomAttribute", new Handler(ProcessUnknown), new ColumnType[] { ColumnType.HasCustomAttribute, ColumnType.CustomAttributeType, ColumnType.Blob }, new String[] { "Type", "Parent", "Value" }); m_Tables[TABLE_FIELDMARSHAL] = new Table("FieldMarshal", new Handler(ProcessUnknown), new ColumnType[] { ColumnType.HasFieldMarshal, ColumnType.Blob }, new String[] { "Parent", "Native" }); m_Tables[TABLE_DECLSECURITY] = new Table("DeclSecurity", new Handler(ProcessUnknown), new ColumnType[] { ColumnType.UInt16, ColumnType.HasDeclSecurity, ColumnType.Blob }, new String[] { "Action", "Parent", "PermissionSet" }); m_Tables[TABLE_CLASSLAYOUT] = new Table("ClassLayout", new Handler(ProcessUnknown), new ColumnType[] { ColumnType.UInt16, ColumnType.UInt32, ColumnType.Type }, new String[] { "PackingSize", "ClassSize", "Parent" }); m_Tables[TABLE_FIELDLAYOUT] = new Table("FieldLayout", new Handler(ProcessUnknown), new ColumnType[] { ColumnType.UInt32, ColumnType.Field }, new String[] { "Offset", "Field" }); m_Tables[TABLE_STANDALONESIG] = new Table("StandAloneSig", new Handler(ProcessUnknown), new ColumnType[] { ColumnType.Blob }, new String[] { "Signature" }); m_Tables[TABLE_EVENTMAP] = new Table("EventMap", new Handler(ProcessUnknown), new ColumnType[] { ColumnType.Type, ColumnType.Event }, new String[] { "Parent", "EventList" }); m_Tables[TABLE_EVENTPTR] = new Table("EventPtr", new Handler(ProcessUnknown), new ColumnType[] { ColumnType.Event }, new String[] { "Event" }); m_Tables[TABLE_EVENT] = new Table("Event", new Handler(ProcessUnknown), new ColumnType[] { ColumnType.UInt16, ColumnType.String, ColumnType.TypeDefOrRef }, new String[] { "EventFlags", "Name", "EventType" }); m_Tables[TABLE_PROPERTYMAP] = new Table("PropertyMap", new Handler(ProcessUnknown), new ColumnType[] { ColumnType.Type, ColumnType.Property }, new String[] { "Parent", "PropertyList" }); m_Tables[TABLE_PROPERTYPTR] = new Table("PropertyPtr", new Handler(ProcessUnknown), new ColumnType[] { ColumnType.Property }, new String[] { "Property" }); m_Tables[TABLE_PROPERTY] = new Table("Property", new Handler(ProcessUnknown), new ColumnType[] { ColumnType.UInt16, ColumnType.String, ColumnType.Blob }, new String[] { "PropFlags", "Name", "Type" }); m_Tables[TABLE_METHODSEMANTICS] = new Table("MethodSemantics", new Handler(ProcessUnknown), new ColumnType[] { ColumnType.UInt16, ColumnType.Method, ColumnType.HasSemantics }, new String[] { "Semantic", "Method", "Association" }); m_Tables[TABLE_METHODIMPL] = new Table("MethodImpl", new Handler(ProcessUnknown), new ColumnType[] { ColumnType.Type, ColumnType.MethodDefOrRef, ColumnType.MethodDefOrRef }, new String[] { "Class", "MethodBody", "MethodDeclaration" }); m_Tables[TABLE_MODULEREF] = new Table("ModuleRef", new Handler(ProcessUnknown), new ColumnType[] { ColumnType.String }, new String[] { "Name" }); m_Tables[TABLE_TYPESPEC] = new Table("TypeSpec", new Handler(ProcessUnknown), new ColumnType[] { ColumnType.Blob }, new String[] { "Signature" }); m_Tables[TABLE_IMPLMAP] = new Table("ImplMap", new Handler(ProcessUnknown), new ColumnType[] { ColumnType.UInt16, ColumnType.MemberForwarded, ColumnType.String, ColumnType.ModuleRef }, new String[] { "MappingFlags", "MemberForwarded", "ImportName", "ImportScope" }); m_Tables[TABLE_FIELDRVA] = new Table("FieldRVA", new Handler(ProcessUnknown), new ColumnType[] { ColumnType.UInt32, ColumnType.Field }, new String[] { "RVA", "Field" }); m_Tables[TABLE_ENCLOG] = new Table("EncLog", new Handler(ProcessUnknown), new ColumnType[] { ColumnType.UInt32, ColumnType.UInt32 }, new String[] { "Token", "FuncCode" }); m_Tables[TABLE_ENCMAP] = new Table("EncMap", new Handler(ProcessUnknown), new ColumnType[] { ColumnType.UInt32 }, new String[] { "Token" }); m_Tables[TABLE_ASSEMBLY] = new Table("Assembly", new Handler(ProcessUnknown), new ColumnType[] { ColumnType.UInt32, ColumnType.UInt16, ColumnType.UInt16, ColumnType.UInt16, ColumnType.UInt16, ColumnType.UInt32, ColumnType.Blob, ColumnType.String, ColumnType.String }, new String[] { "HashAlgId", "MajorVersion", "MinorVersion", "BuildNumber", "RevisionNumber", "Flags", "PublicKey", "Name", "Locale" }); m_Tables[TABLE_ASSEMBLYPROCESSOR] = new Table("AssemblyProcessor", new Handler(ProcessUnknown), new ColumnType[] { ColumnType.UInt32 }, new String[] { "Processor" }); m_Tables[TABLE_ASSEMBLYOS] = new Table("AssemblyOS", new Handler(ProcessUnknown), new ColumnType[] { ColumnType.UInt32, ColumnType.UInt32, ColumnType.UInt32 }, new String[] { "OSPlatformId", "MajorVersion", "MinorVersion" }); m_Tables[TABLE_ASSEMBLYREF] = new Table("AssemblyRef", new Handler(ProcessUnknown), new ColumnType[] { ColumnType.UInt16, ColumnType.UInt16, ColumnType.UInt16, ColumnType.UInt16, ColumnType.UInt32, ColumnType.Blob, ColumnType.String, ColumnType.String, ColumnType.Blob }, new String[] { "MajorVersion", "MinorVersion", "BuildNumber", "RevisionNumber", "Flags", "PublicKeyOrToken", "Name", "Locale", "HashValue" }); m_Tables[TABLE_ASSEMBLYREFPROCESSOR] = new Table("AssemblyRefProcessor", new Handler(ProcessUnknown), new ColumnType[] { ColumnType.UInt32, ColumnType.AssemblyRef }, new String[] { "Processor", "AssemblyRef" }); m_Tables[TABLE_ASSEMBLYREFOS] = new Table("AssemblyRefOS", new Handler(ProcessUnknown), new ColumnType[] { ColumnType.UInt32, ColumnType.UInt32, ColumnType.UInt32, ColumnType.AssemblyRef }, new String[] { "OSPlatformId", "OSMajorVersion", "OSMinorVersion", "AssemblyRef" }); m_Tables[TABLE_FILE] = new Table("File", new Handler(ProcessUnknown), new ColumnType[] { ColumnType.UInt32, ColumnType.String, ColumnType.Blob }, new String[] { "Flags", "Name", "HashValue" }); m_Tables[TABLE_EXPORTEDTYPE] = new Table("ExportedType", new Handler(ProcessUnknown), new ColumnType[] { ColumnType.UInt32, ColumnType.UInt32, ColumnType.String, ColumnType.String, ColumnType.Implementation }, new String[] { "Flags", "TypeDefId", "TypeName", "TypeNamespace", "TypeImplementation" }); m_Tables[TABLE_MANIFESTRESOURCE] = new Table("ManifestResource", new Handler(ProcessUnknown), new ColumnType[] { ColumnType.UInt32, ColumnType.UInt32, ColumnType.String, ColumnType.Implementation }, new String[] { "Offset", "Flags", "Name", "Implementation" }); m_Tables[TABLE_NESTEDCLASS] = new Table("NestedClass", new Handler(ProcessUnknown), new ColumnType[] { ColumnType.Type, ColumnType.Type }, new String[] { "Nested", "Enclosing"}); m_Tables[TABLE_TYPETYPEPARAM] = new Table("TypeTyParam", new Handler(ProcessUnknown), new ColumnType[] { ColumnType.UInt16, ColumnType.Type, ColumnType.TypeDefOrRef, ColumnType.String }, new String[] { "Number", "Class", "Bound", "Name" }); m_Tables[TABLE_METHODTYPEPARAM] = new Table("MethodTyParam", new Handler(ProcessUnknown), new ColumnType[] { ColumnType.UInt16, ColumnType.Method, ColumnType.TypeDefOrRef, ColumnType.String }, new String[] { "Number", "Method", "Bound", "Name" }); // Set the rest of the tables to the default for (int i = TABLE_METHODTYPEPARAM + 1; i < 64; i++) m_Tables[i] = new Table("Unknown", new Handler(ProcessUnknown), null, null); // Coded indices m_CodedIndices[ColumnType.TypeDefOrRef] = new ColumnType[] { ColumnType.Type, ColumnType.TypeRef, ColumnType.TypeSpec}; m_CodedIndices[ColumnType.HasConstant] = new ColumnType[] { ColumnType.Field, ColumnType.Param, ColumnType.Property }; m_CodedIndices[ColumnType.HasCustomAttribute] = new ColumnType[] { ColumnType.Method, ColumnType.Field, ColumnType.TypeRef, ColumnType.Type, ColumnType.Param, ColumnType.InterfaceImpl, ColumnType.MemberRef, ColumnType.Module, ColumnType.DeclSecurity, ColumnType.Property, ColumnType.Event, ColumnType.StandAloneSig, ColumnType.ModuleRef, ColumnType.TypeSpec, ColumnType.Assembly, ColumnType.AssemblyRef, ColumnType.File, ColumnType.ExportedType, ColumnType.ManifestResource }; m_CodedIndices[ColumnType.HasFieldMarshal] = new ColumnType[] { ColumnType.Field, ColumnType.Param }; m_CodedIndices[ColumnType.HasDeclSecurity] = new ColumnType[] { ColumnType.Type, ColumnType.Method, ColumnType.Assembly }; m_CodedIndices[ColumnType.MemberRefParent] = new ColumnType[] { ColumnType.Type, ColumnType.TypeRef, ColumnType.ModuleRef, ColumnType.Method, ColumnType.TypeSpec }; m_CodedIndices[ColumnType.HasSemantics] = new ColumnType[] { ColumnType.Event, ColumnType.Property }; m_CodedIndices[ColumnType.MethodDefOrRef] = new ColumnType[] { ColumnType.Method, ColumnType.MemberRef }; m_CodedIndices[ColumnType.MemberForwarded] = new ColumnType[] { ColumnType.Field, ColumnType.Method }; m_CodedIndices[ColumnType.Implementation] = new ColumnType[] { ColumnType.File, ColumnType.AssemblyRef, ColumnType.ExportedType }; m_CodedIndices[ColumnType.CustomAttributeType] = new ColumnType[] { ColumnType.TypeRef, ColumnType.Type, ColumnType.Method, ColumnType.MemberRef, ColumnType.UserString }; m_CodedIndices[ColumnType.ResolutionScope] = new ColumnType[] { ColumnType.Module, ColumnType.ModuleRef, ColumnType.AssemblyRef, ColumnType.TypeRef }; } void SetTableSizes(Table table) { if (table.m_Columns == null) throw new Exception(table.m_Name + " table has no columns defined"); UInt32 Size = 0; IDictionaryEnumerator ColumnEnumerator = table.m_Columns.GetEnumerator(); ColumnType Column; while (ColumnEnumerator.MoveNext()) { Column = (ColumnType)ColumnEnumerator.Value; Size += (UInt32)GetColumnSize(Column); } table.m_CellSize = (UInt32)Size; table.m_TotalSize = table.m_RowCount * table.m_CellSize; #if DEBUG Console.WriteLine("{0} table is {1} rows ({2} bytes per row)", table.m_Name, table.m_RowCount, table.m_CellSize); #endif } int GetColumnSize(ColumnType column) { // Generic types if (column == ColumnType.UInt16) return 2; else if (column == ColumnType.UInt32) return 4; else if (column == ColumnType.String) return (m_LargeStringSize ? 4 : 2); else if (column == ColumnType.Blob) return (m_LargeBlobSize ? 4 : 2); else if (column == ColumnType.GUID) return (m_LargeGUIDSize ? 4 : 2); // Simple index types int Index = (int)column; if (Index < 64) { if (m_Tables[Index].m_Name == "Unknown") throw new Exception("Unknown table " + Index); return (m_Tables[Index].m_RowCount > 65535 ? 4 : 2); } // User strings and coded index types (using type as the key into a hash table) ColumnType[] CodedIndex = (ColumnType[])m_CodedIndices[column]; if (CodedIndex == null) throw new Exception("Unknown coded index " + column); UInt32 RowCount = 0; foreach (ColumnType tmp in CodedIndex) { if (tmp == ColumnType.UserString) continue; // user string is treated exceptionally Index = (int)tmp; if (Index >= 64) throw new Exception("Invalid index " + Index); if (m_Tables[Index].m_Name == "Unknown") throw new Exception("Unknown table " + Index); RowCount = (RowCount > m_Tables[Index].m_RowCount) ? RowCount : m_Tables[Index].m_RowCount; // Sanity check if (m_Tables[Index].m_Name != tmp.ToString()) throw new Exception(m_Tables[Index].m_Name + " != " + tmp.ToString()); } RowCount = RowCount << m_CodedIndexTags[CodedIndex.Length]; return (RowCount > 65535) ? 4 : 2; } // // Handlers for tables // void ProcessMethod(UInt32 RowIndex) { UInt32 i; Table method = m_Tables[TABLE_METHOD]; if (!method.m_Present) throw new Exception(method.m_Name + " table not present"); byte *Data = method.m_Data + (RowIndex * method.m_CellSize); UInt32 *pRVA = (UInt32 *)Data; Int32 RVA; UInt16 ImplFlags, MethodFlags; string MethodName, TypeName = null; byte *Signature; UInt32 SignatureLength; Parameter[] Parameters = null; UInt32 ParameterIndex, ParameterCount; RVA = *((Int32 *)Data); Data += 4; ImplFlags = *((UInt16 *)Data); Data += 2; MethodFlags = *((UInt16 *)Data); Data += 2; if (RVA == 0) return; if ((ImplFlags & METHOD_NATIVE) > 0) return; // native if ((MethodFlags & METHOD_SPECIALNAME) > 0) return; // ctors, properties, events, etc. // Method name if (GetColumnSize(ColumnType.String) == 4) { UInt32 NameOffset; NameOffset = *((UInt32 *)Data); Data += 4; MethodName = MakeStringFromUTF8(m_StringsHeap.Data + NameOffset); } else { UInt16 NameOffset; NameOffset = *((UInt16 *)Data); Data += 2; MethodName = MakeStringFromUTF8(m_StringsHeap.Data + NameOffset); } // Method signature if (GetColumnSize(ColumnType.Blob) == 4) { UInt32 SignatureOffset; SignatureOffset = *((UInt32 *)Data); Data += 4; Signature = m_BlobHeap.Data + SignatureOffset; SignatureLength = DecodeBlobValue(Signature); Signature += GetBlobSize(Signature); } else { UInt16 SignatureOffset; SignatureOffset = *((UInt16 *)Data); Data += 2; Signature = m_BlobHeap.Data + SignatureOffset; SignatureLength = DecodeBlobValue(Signature); Signature += GetBlobSize(Signature); } // // Determine owner of this method // Int32 MethodRowEnd; Table type = m_Tables[TABLE_TYPEDEF]; // used to find the type owning this method if (!type.m_Present) throw new Exception(type.m_Name + " table not present"); bool FoundOwner = false; for (i = 0; i < type.m_RowCount; i++) { byte *TypeData = type.m_Data + (i * type.m_CellSize); TypeData += 4; // skip over Flags if (GetColumnSize(ColumnType.String) == 4) { UInt32 NameOffset = *((UInt32 *)TypeData); TypeName = MakeStringFromUTF8(m_StringsHeap.Data + NameOffset); TypeData += 4; } else { UInt16 NameOffset = *((UInt16 *)TypeData); TypeName = MakeStringFromUTF8(m_StringsHeap.Data + NameOffset); TypeData += 2; } TypeData += GetColumnSize(ColumnType.String); // skip over Namespace TypeData += GetColumnSize(ColumnType.TypeDefOrRef); // skip over Extends TypeData += GetColumnSize(ColumnType.Field); // skip over FieldList if (GetColumnSize(ColumnType.Method) == 4) { UInt32 MethodRowStart = *((UInt32 *)TypeData); MethodRowStart--; // numbering starts at 1 MethodRowEnd = GetLastMethod(i, MethodRowStart); if (RowIndex >= MethodRowStart && RowIndex <= MethodRowEnd) { FoundOwner = true; break; } } else { UInt16 MethodRowStart = *((UInt16 *)TypeData); MethodRowStart--; // numbering starts at 1 MethodRowEnd = GetLastMethod(i, (UInt32)MethodRowStart); if (RowIndex >= MethodRowStart && RowIndex <= MethodRowEnd) { FoundOwner = true; break; } } } if (!FoundOwner) throw new Exception("Unable to find owner of " + MethodName); // // Read the method parameter name and flags // if (GetColumnSize(ColumnType.Param) == 4) { ParameterIndex = *((UInt32 *)Data); ParameterIndex--; } else { ParameterIndex = *((UInt16 *)Data); ParameterIndex--; } // // Determine the method return and parameter types // UInt32 ReturnType; string ReturnTypeName; UInt32 CallingConvention; CallingConvention = *Signature++; ParameterCount = DecodeBlobValue(Signature); Signature += GetBlobSize(Signature); ReturnType = DecodeBlobValue(Signature); ReturnTypeName = GetTypeName(Signature); Signature += GetBlobSize(Signature); // Read in parameter information Parameters = GetParameters(ParameterIndex, ParameterCount); for (i = 0; i < ParameterCount; i++) { Parameters[i].Type = DecodeBlobValue(Signature); Parameters[i].TypeName = GetTypeName(Signature); Signature += GetBlobSize(Signature); } // Read in method body and setup hooked function information HookedFunction Function = new HookedFunction(); Function.m_DeclaringTypeName = TypeName; Function.m_Parameters = Parameters; Function.m_ReturnType = ReturnType; Function.m_ReturnTypeName = ReturnTypeName; Function.m_OldRVA = (UInt32)RVA; Function.m_pRVA = pRVA; Function.m_Name = MethodName; Function.BuildName(); RVA -= m_HeaderRVA; // assumes method bodies are in the same section as the metadata header Data = (byte *)m_Header + RVA; byte Header; Function.m_Header = Data; Function.m_OldFileOffset = (UInt32)(Data - m_ImageBase); Header = *Data++; Function.m_HeaderSize++; // // Store method header/body // if ((Header & 0x03) == METHOD_TINY_FORMAT) // tiny format { UInt32 CodeSize = (UInt32)(Header >> 2); // Copy the method body Function.m_CodeSize = CodeSize; Function.m_Code = new byte[CodeSize]; for (i = 0; i < CodeSize; i++) Function.m_Code[i] = Data[i]; // Generate hook code m_HookFunction(Function); if (Function.m_HookSize == 0) return; // If this will no longer fit in tiny format, convert it to fat format CodeSize += Function.m_HookSize; if (CodeSize >= 64) { #if DEBUG Console.WriteLine("Upgrading {0} to fat format", Function.m_FullName); #endif // TODO: something is broken here that appears to be causing it // to overwrite memory, thus the subsequent function fails with // "Invalid method header" Data--; Function.m_HeaderSize--; // now points back at the beginning of the header *Data++ = METHOD_FAT_FORMAT; Function.m_HeaderSize++; *Data++ = (3 << 4); Function.m_HeaderSize++; // high-order flags *((UInt16 *)Data) = 8; Data += 2; Function.m_HeaderSize += 2; // MaxStack *((UInt32 *)Data) = CodeSize; Data += 4; Function.m_HeaderSize += 4; // CodeSize *((UInt32 *)Data) = 0; Data += 4; Function.m_HeaderSize += 4; // LocalVarSigToken } else { // Update code length to reflect the hook code to be added Header = (byte)(CodeSize << 2); Header |= METHOD_TINY_FORMAT; *(Function.m_Header) = Header; } } else if ((Header & 0x03) == METHOD_FAT_FORMAT) // fat format { byte Header2; UInt16 Flags; UInt32 *CodeSize; Header2 = *Data++; Function.m_HeaderSize++; Data += 2; Function.m_HeaderSize += 2; // skip over MaxStack CodeSize = (UInt32 *)Data; Data += 4; Function.m_HeaderSize += 4; Data += 4; Function.m_HeaderSize += 4; // skip over LocalVarSigToken Flags = (UInt16)(Header | ((Header2 & 0x0F) << 8)); // Copy the method body Function.m_CodeSize = *CodeSize; Function.m_Code = new byte[*CodeSize]; for (i = 0; i < *CodeSize; i++) Function.m_Code[i] = *Data++; // Handle extra data sections if ((Flags & 0x08) > 0) { byte DataFlags; UInt32 Size; if (((Int32)Data % 4) != 0) { Size = (UInt32)(4 - ((Int32)Data % 4)); Data += Size; } Function.m_EHData = Data; do { DataFlags = *Data++; if ((Flags & 0x40) > 1) // fat data format { byte a, b, c; FatExceptionHandler *EH; c = *Data++; b = *Data++; a = *Data++; Size = (UInt32)(a << 12 | b << 8 | c); Function.m_EHSize += Size; EH = (FatExceptionHandler *)Data; for (i = 0; i < (Size - 4) / sizeof(FatExceptionHandler); i++) { EH->TryLength += Function.m_HookSize; EH->HandlerLength += Function.m_HookSize; // TODO: find some way to still get the finally clause to execute EH++; } } else // tiny data format { TinyExceptionHandler *EH; Size = *Data++; Function.m_EHSize += Size; EH = (TinyExceptionHandler *)Data; for (i = 0; i < (Size - 4) / sizeof(TinyExceptionHandler); i++) { EH->TryLength += (byte)Function.m_HookSize; EH->HandlerLength += (byte)Function.m_HookSize; // TODO: find some way to still get the finally clause to execute EH++; } } Data = Function.m_EHData + Size; if (((Int32)Data % 4) != 0) throw new Exception("Exception handling data is misaligned"); } while ((DataFlags & 0x80) > 0); // Generate hook code m_HookFunction(Function); if (Function.m_HookSize == 0) return; // Update code length to reflect the hook code to be added *CodeSize += Function.m_HookSize; } } else { throw new Exception("Invalid method header"); } m_HookedFunctions.Add(Function); } // Process all parameters between RowStart and RowEnd, inclusive Parameter[] GetParameters(UInt32 ParameterIndex, UInt32 Count) { Table table = m_Tables[TABLE_PARAM]; if (!table.m_Present) return null; byte *Data; Parameter param = new Parameter(); ArrayList Parameters = new ArrayList(); UInt32 tmp_Index = ParameterIndex; while (tmp_Index < ParameterIndex + Count) { Data = table.m_Data + (tmp_Index * table.m_CellSize); tmp_Index++; param.Flags = *((UInt16 *)Data); Data += 2; Data += 2; // skip over sequence if (GetColumnSize(ColumnType.String) == 4) { UInt32 NameOffset = *((UInt32 *)Data); param.Name = MakeStringFromUTF8(m_StringsHeap.Data + NameOffset); } else { UInt16 NameOffset = *((UInt16 *)Data); param.Name = MakeStringFromUTF8(m_StringsHeap.Data + NameOffset); } Parameters.Add(param); } return (Parameter[])Parameters.ToArray(typeof(Parameter)); } // Do nothing void ProcessUnknown(UInt32 RowIndex) { throw new Exception("This type is not being handled"); } // // Miscellaneous functions // // Find the last method of a type by finding the first method of the next Int32 GetLastMethod(UInt32 TypeIndex, UInt32 MethodRowStart) { Table method = m_Tables[TABLE_METHOD]; Table type = m_Tables[TABLE_TYPEDEF]; if (MethodRowStart >= method.m_RowCount) return -1; // this is the last method if (TypeIndex + 1 >= type.m_RowCount) return (Int32)method.m_RowCount - 1; // this is the last typedef // Get the address of the next type byte *Data = type.m_Data + ((TypeIndex+1) * type.m_CellSize); Data += 4; // skip over flags Data += GetColumnSize(ColumnType.String); // skip over name Data += GetColumnSize(ColumnType.String); // skip over namespace Data += GetColumnSize(ColumnType.TypeDefOrRef); // skip over extends Data += GetColumnSize(ColumnType.Field); // skip over field if (GetColumnSize(ColumnType.Method) == 4) { UInt32 NextMethodRowStart = *((UInt32 *)Data); NextMethodRowStart--; // numbering starts at 1 if (MethodRowStart == NextMethodRowStart) return -1; // the method takes no parameters else return (Int32)NextMethodRowStart - 1; } else { UInt16 NextMethodRowStart = *((UInt16 *)Data); NextMethodRowStart--; // numbering starts at 1 if (MethodRowStart == NextMethodRowStart) return -1; // the method takes no parameters else return (Int32)NextMethodRowStart - 1; } } UInt32 GetBlobSize(byte *Data) { Int32 Length = (Int32)(*Data++); if ((Length & 0x80) == 0) return 1; else if ((Length & 0xC0) == 0x80) return 2; else return 4; } UInt32 DecodeBlobValue(byte *Data) { Int32 Length = (Int32)(*Data++); if ((Length & 0x80) == 0) return (UInt32)Length; else if ((Length & 0xC0) == 0x80) return (UInt32)(((Length & 0x3F) << 8) | *Data++); else return (UInt32)(((Length & 0x3F) << 24) | (*Data++ << 16) | (*Data++ << 8) | *Data); } string GetTypeName(byte *Data) { UInt32 Type = DecodeBlobValue(Data); Data += GetBlobSize(Data); if (Type == TYPE_VOID) return "void"; else if (Type == TYPE_BOOLEAN) return "bool"; else if (Type == TYPE_CHAR) return "char"; else if (Type == TYPE_I1) return "sbyte"; else if (Type == TYPE_U1) return "byte"; else if (Type == TYPE_I2) return "Int16"; else if (Type == TYPE_U2) return "UInt16"; else if (Type == TYPE_I4) return "Int32"; else if (Type == TYPE_U4) return "UInt32"; else if (Type == TYPE_I8) return "Int64"; else if (Type == TYPE_U8) return "UInt64"; else if (Type == TYPE_R4) return "single"; else if (Type == TYPE_R8) return "double"; else if (Type == TYPE_STRING) return "string"; else if (Type == TYPE_OBJECT) return "object" ; else if (Type == TYPE_TYPEDREFERENCE) return "TypedReference"; else if (Type == TYPE_UINTPTR) return "UIntPtr"; else if (Type == TYPE_INTPTR) return "IntPtr"; else if (Type == TYPE_FUNCPTR) return "FUNCPTR"; // TODO: parse this out else if (Type == TYPE_POINTER) return GetTypeName(Data) + " *"; else if (Type == TYPE_BYREF) return GetTypeName(Data) + " &"; else if (Type == TYPE_ARRAY) return GetTypeName(Data) + "[]"; else if (Type == TYPE_SZARRAY) return GetTypeName(Data) + "[]"; else if (Type == TYPE_SENTINEL) return "params " + GetTypeName(Data); else if (Type == TYPE_PINNED) return "pinned " + GetTypeName(Data); else if ((Type == TYPE_VALUETYPE) || (Type == TYPE_CLASS)) { UInt32 Token = DecodeBlobValue(Data); Data += GetBlobSize(Data); ColumnType[] CodedIndex = (ColumnType[])m_CodedIndices[ColumnType.TypeDefOrRef]; if ((Token & 0x03) >= CodedIndex.Length) throw new Exception("Invalid coded index token"); UInt32 type = (UInt32)CodedIndex[Token & 0x03]; Token >>= 2; if (Token < 0) throw new Exception("Invalid index"); Token |= (type << 24); return GetNameFromTypeDefOrRef(Token); } else if ((Type == TYPE_CUSTOMMODREQ) || (Type == TYPE_CUSTOMMODOPT)) { Data += GetBlobSize(Data); // skip over custom modifier return GetTypeName(Data); } // TODO: handle types (classes, structs, etc.) else { #if DEBUG Console.WriteLine("Unknown element type 0x{0:X4}", Type); #endif return "UNKNOWN"; } } string GetNameFromTypeDefOrRef(UInt32 Token) { UInt32 Index = Token >> 24; if (Index == TABLE_TYPEDEF) return GetNameFromType(Token); else if (Index == TABLE_TYPEREF) return GetNameFromTypeRef(Token); else if (Index == TABLE_TYPESPEC)return GetNameFromTypeSpec(Token); else throw new Exception("Invalid token"); } string GetNameFromType(UInt32 Token) { return "UNKNOWN_TYPEDEF"; } string GetNameFromTypeRef(UInt32 Token) { return "UNKNOWN_TYPEREF"; } string GetNameFromTypeSpec(UInt32 Token) { return "UNKNOWN_TYPESPEC"; } string MakeStringFromANSI(byte *Text) { string tmp_String = ""; for (; *Text != 0; Text++) tmp_String += (char)*Text; return tmp_String; } string MakeStringFromUTF8(byte *Text) { ArrayList tmp_ArrayList = new ArrayList(); while (*Text != 0x00) { tmp_ArrayList.Add(*Text); Text++; } UTF8Encoding decoder = new UTF8Encoding(); byte[] tmp_ByteArray = (Byte[]) tmp_ArrayList.ToArray(typeof(byte)); return decoder.GetString(tmp_ByteArray); } byte[] MakeUTF8FromString(String Text) { UTF8Encoding encoder = new UTF8Encoding(); byte[] Output = new byte[encoder.GetByteCount(Text)]; encoder.GetBytes(Text, 0, Text.Length, Output, 0); return Output; } }