shuai's profile娱乐精神PhotosBlog Tools Help

Blog


    11/29/2007

    强暴.Net程序集 之十 (FieldDef 和 MethodDef 表结构)

     

    警告:本系列文章为本人原创,只作技术研究之用,您可以引用链接传播,禁止其它的转载方式,禁止用于商业或非法目的, 对于造成的一切后果本人概不负责

    FieldDef非常的简单, Partition II Metadata中描述为:

    22.15    Field : 0x04
    The Field table has the following columns:
    •    Flags (a 2-byte bitmask of type FieldAttributes, §23.1.5)
    •    Name (an index into the String heap)
    •    Signature (an index into the Blob heap)

    其中Flags 字段是一个位掩码, 描述了字段的属性
    corhdl.h中有它的定义
    typedef enum CorFieldAttr
    {
        // member access mask - Use this mask to retrieve accessibility information.
        fdFieldAccessMask           =   0x0007,
        fdPrivateScope              =   0x0000,     // Member not referenceable.
        fdPrivate                   =   0x0001,     // Accessible only by the parent type.
        fdFamANDAssem               =   0x0002,     // Accessible by sub-types only in this Assembly.
        fdAssembly                  =   0x0003,     // Accessibly by anyone in the Assembly.
        fdFamily                    =   0x0004,     // Accessible only by type and sub-types.
        fdFamORAssem                =   0x0005,     // Accessibly by sub-types anywhere, plus anyone in assembly.
        fdPublic                    =   0x0006,     // Accessibly by anyone who has visibility to this scope.
        // end member access mask

        // field contract attributes.
        fdStatic                    =   0x0010,     // Defined on type, else per instance.
        fdInitOnly                  =   0x0020,     // Field may only be initialized, not written to after init.
        fdLiteral                   =   0x0040,     // Value is compile time constant.
        fdNotSerialized             =   0x0080,     // Field does not have to be serialized when type is remoted.

        fdSpecialName               =   0x0200,     // field is special.  Name describes how.

        // interop attributes
        fdPinvokeImpl               =   0x2000,     // Implementation is forwarded through pinvoke.

        // Reserved flags for runtime use only.
        fdReservedMask              =   0x9500,
        fdRTSpecialName             =   0x0400,     // Runtime(metadata internal APIs) should check name encoding.
        fdHasFieldMarshal           =   0x1000,     // Field has marshalling information.
        fdHasDefault                =   0x8000,     // Field has default.
        fdHasFieldRVA               =   0x0100,     // Field has RVA.
    } CorFieldAttr;

    可以通过如下程序得到所有Field的属性, t为FieldDef表

    for(DWORD i = 0; i < t.Rows; ++i)
    {
        row = t.Address + t.RowSize * index;

        // Flags
        flags = *((USHORT *)row);
        row += 2;

        // Name
        if(t.HeapSizes & 0x01)
        {
            name = *((ULONG *)row);
            row += 4;
        }
        else
        {
            name = *((USHORT *)row);
            row += 2;
        }

        // Signature
        if(t.HeapSizes & 0x04)
        {
            signature = *((ULONG *)row);
            row += 4;
        }
        else
        {
            signature = *((USHORT *)row);
            row += 2;
        }
    }

    MethodDef 表在Partition II Metadata中描述为:
    22.26    MethodDef : 0x06
    The MethodDef table has the following columns:
    •    RVA (a 4-byte constant)
    •    ImplFlags (a 2-byte bitmask of type MethodImplAttributes, §23.1.10)
    •    Flags (a 2-byte bitmask of type MethodAttributes, §23.1.10)
    •    Name (an index into the String heap)
    •    Signature (an index into the Blob heap)
    •    ParamList (an index into the Param table).  It marks the first of a contiguous run of Parameters owned by this method.  The run continues to the smaller of:
    o    the last row of the Param table
    o    the next run of Parameters, found by inspecting the ParamList of the next row in the MethodDef  table

    对了, 别忘了Property(属性实际上就是get_和set_方法)
    其中Flags标记保存Method的一些可访问性及其它属性
    corhdr.h 中有它的定义
    typedef enum CorMethodAttr
    {
        // member access mask - Use this mask to retrieve accessibility information.
        mdMemberAccessMask          =   0x0007,
        mdPrivateScope              =   0x0000,     // Member not referenceable.
        mdPrivate                   =   0x0001,     // Accessible only by the parent type.
        mdFamANDAssem               =   0x0002,     // Accessible by sub-types only in this Assembly.
        mdAssem                     =   0x0003,     // Accessibly by anyone in the Assembly.
        mdFamily                    =   0x0004,     // Accessible only by type and sub-types.
        mdFamORAssem                =   0x0005,     // Accessibly by sub-types anywhere, plus anyone in assembly.
        mdPublic                    =   0x0006,     // Accessibly by anyone who has visibility to this scope.
        // end member access mask

        // method contract attributes.
        mdStatic                    =   0x0010,     // Defined on type, else per instance.
        mdFinal                     =   0x0020,     // Method may not be overridden.
        mdVirtual                   =   0x0040,     // Method virtual.
        mdHideBySig                 =   0x0080,     // Method hides by name+sig, else just by name.

        // vtable layout mask - Use this mask to retrieve vtable attributes.
        mdVtableLayoutMask          =   0x0100,
        mdReuseSlot                 =   0x0000,     // The default.
        mdNewSlot                   =   0x0100,     // Method always gets a new slot in the vtable.
        // end vtable layout mask

        // method implementation attributes.
        mdCheckAccessOnOverride     =   0x0200,     // Overridability is the same as the visibility.
        mdAbstract                  =   0x0400,     // Method does not provide an implementation.
        mdSpecialName               =   0x0800,     // Method is special.  Name describes how.

        // interop attributes
        mdPinvokeImpl               =   0x2000,     // Implementation is forwarded through pinvoke.
        mdUnmanagedExport           =   0x0008,     // Managed method exported via thunk to unmanaged code.

        // Reserved flags for runtime use only.
        mdReservedMask              =   0xd000,
        mdRTSpecialName             =   0x1000,     // Runtime should check name encoding.
        mdHasSecurity               =   0x4000,     // Method has security associate with it.
        mdRequireSecObject          =   0x8000,     // Method calls another method containing security code.

    } CorMethodAttr;

    值得说明的是ParamList字段
    它记录了Param表的记录索引,  指明所属方法的参数的开始位置, 结束于下一个方法参数的开始
    程序如下, t为MethodDef表
    for(DWORD i = 0; i < t.Rows; ++i)
    {
        row = t.Address + t.RowSize * index;

        // RVA
        rva = *((DWORD *)row);
        row += 4;

        // ImplFlags
        implFlags = *((USHORT *)row);
        row += 2;

        // Flags
        flags = *((USHORT *)row);
        row += 2;

        // Name
        if(t.HeapSizes & 0x01)
        {
            nameIndex = *((ULONG *)row);
            row += 4;
            wcscpy(lBuffer, pView->StringTableEntry(nameIndex).c_str());
        }
        else
        {
            nameIndex = *((USHORT *)row);
            row += 2;
            wcscpy(lBuffer, pView->StringTableEntry(nameIndex).c_str());
        }


        // Signature
        if(t.HeapSizes & 0x04)
        {
            signature = *((DWORD *)row);
            row += 4;
        }
        else
        {
            signature = *((USHORT *)row);
            row += 2;
        }

        // ParamList
        if(t.IndexSizes & 0x10)
        {
            params = *((DWORD *)row);

            if(index < t.Rows - 1)
                endparams = *((DWORD *)(row + t.RowSize));
            else
                endparams = 0;
            row += 4;
        }
        else
        {
            params = *((USHORT *)row);

            if(index < t.Rows - 1)
                endparams = *((USHORT *)(row + t.RowSize));
            else
                endparams = 0;
            row += 2;
        }
    }

    11/9/2007

    强暴.Net程序集 之九 (TypeDef表结构)


    警告:本系列文章为本人原创,只作技术研究之用,您可以引用链接传播,禁止其它的转载方式,禁止用于商业或非法目的, 对于造成的一切后果本人概不负责
     
    现在我们来看一个非常重要的元数据表TypeDef, 它定义了类型信息
    在Partition II Metadata中描述了它的格式:
    The TypeDef table has the following columns:
    • Flags (a 4-byte bitmask of type TypeAttributes, §23.1.15)
    • TypeName (an index into the String heap)
    • TypeNamespace (an index into the String heap)
    • Extends (an index into the TypeDef, TypeRef, or TypeSpec table; more precisely, a TypeDefOrRef  (§24.2.6) coded index)
    • FieldList (an index into the Field table; it marks the first of a contiguous run of Fields owned by this Type).  The run continues to the smaller of:
    o the last row of the Field table
    o the next run of Fields, found by inspecting the FieldList of the next row in this TypeDef table
    • MethodList (an index into the MethodDef table; it marks the first of a continguous run of Methods owned by this Type).  The run continues to the smaller of:
    o the last row of the MethodDef table
    o the next run of Methods, found by inspecting the MethodList of the next row in this TypeDef table
     
    其中:

    Flags 是一个位掩码, 描述了类型的一些属性信息, 文档中的23.1.15 Flags for types [TypeAttributes]一节中详情描述
    在corhdr.h中也有一个定义, 下面是一个节选的列表:
    typedef enum CorTypeAttr
    {
        // Use this mask to retrieve the type visibility information.
        tdVisibilityMask        =   0x00000007,
        tdNotPublic             =   0x00000000,     // Class is not public scope.
        tdPublic                =   0x00000001,     // Class is public scope.
        // Use this mask to retrieve class semantics information.
        tdClassSemanticsMask    =   0x00000060,
        tdClass                 =   0x00000000,     // Type is a class.
        tdInterface             =   0x00000020,     // Type is an interface.
        // end semantics mask
        // Special semantics in addition to class semantics.
        tdAbstract              =   0x00000080,     // Class is abstract
        tdSealed                =   0x00000100,     // Class is concrete and may not be extended
        tdSpecialName           =   0x00000400,     // Class name is special.  Name describes how.
     //其它略
    } CorTypeAttr;
     
    并且corhdr.h还有一系列宏, 用于方便的查询类型的一些属性,比如
    #define IsTdClass(x)                        (((x) & tdClassSemanticsMask) == tdClass)
     
    TypeName和TypeNamespace 都是一个String 堆的索引值, 要注意它的长度随HeapSizes的大小而变
     
    Extends 描述了类型的继承情况, 即指向基类的信息(接口为0), 但是这个值并不是一个元数据token, 而是一个对token进行压缩后的值, TypeDefOrRefEncoded一节中描述了这个值的算法
    比如token为0x01000012,那么它的TypeDefOrRefEncoded 值为
     encoded = ( 0x000012 << 2 ) |  0x01  (0x01 指 TypeRef 在TypeDefOrRef中的编号)
                = 0x48 | 0x01
                = 0x49
    其中TypeDefOrRef指将TypeDef, TypeRef, TypeSpec这一组号转为一个2位的编号:
    TypeDefOrRef    Tag
    TypeDef             0
    TypeRef             1
    TypeSpec           2
    可以看出, 这个压缩后的值, 最后两位描述了基类所处于的元数据表在TypeDefOrRef中的编号, 编号之前的位是在该在表中的索引号
     
    FieldList 和MethodList是一个指向Field和Method表中的起始索引的值, 从此开始遍历都是它的Field和Method, 结束于下一个类型的Field和Method表中的起始索引(相当不能超过RowSize), 在Partition II Metadata 的 Field 表一节中有详细的说明
    比如FieldList值为0x01, 而下一个类型的FieldList为0x03, 那么0x01和0x02都是该类型的Field索引
     
     
    下面我们通过代码看如何得到TypeDef 表中的这些值
     
            row = t.Address + t.RowSize * index; //每行的地址, index为类型在TypeDef中的索引

            // Flags
            flags = *((DWORD *)row);
            row += 4;

            // Name
            if(t.HeapSizes & 0x01)
            {
                    nameIndex = *((ULONG *)row);
                    row += 4;
                    nameSpaceIndex = *((ULONG *)row);
                    row += 4;
            }
            else
            {
                    nameIndex = *((USHORT *)row);
                    row += 2;
                    nameSpaceIndex = *((USHORT *)row);
                    row += 2;
            }
            //nameIndex 和nameSpaceIndex 为在String表中的索引

            // Extends
            if(t.IndexSizes & 0x08)
            {
                    extends = *((DWORD *)row);
                    row += 4;
            }
            else
            {
                    extends = *((USHORT *)row);
                    row += 2;
            }
            extendsTable = (BYTE)(extends & 0x3);
            extendsIndex = extends = extends >> 2;
            switch(extendsTable)
            {
            case 0:
                    // TypeDef
                    extends |= 0x02 << 24;
                    break;
            case 1:
                    // TypeRef
                    extends |= 0x01 << 24;
                    break;
            case 2:
                    // TypeSpec
                    extends |= 0x1b << 24;
                    break;
            }
            // 现在extends 为Extends转成token的格式

            // FieldList
            if(t.IndexSizes & 0x08)
            {
                    fields = *((DWORD *)row);
                    if(index < t.Rows - 1)
                            endFields = *((DWORD *)(row + t.RowSize));
                    else
                            endFields = 0;
                    row += 4;
            }
            else
            {
                    fields = *((USHORT *)row);
                    if(index < t.Rows - 1)
                            endFields = *((USHORT *)(row + t.RowSize));
                    else
                            endFields = 0;
                    row += 2;
            }
            // fields 为该类型FieldList的起始索引, endFields 为结束索引

            // MethodList
            if(t.IndexSizes & 0x08)
            {
                    methods = *((DWORD *)row);
                    if(index < t.Rows - 1)
                            endMethods = *((DWORD *)(row + t.RowSize));
                    else
                            endMethods = 0;
                    row += 4;
            }
            else
            {
                    methods = *((USHORT *)row);
                    if(index < t.Rows - 1)
                            endMethods = *((USHORT *)(row + t.RowSize));
                    else
                            endMethods = 0;
                    row += 2;
            }
            // methods 为该类型MethodList的起始索引, endMethods 为结束索引


     

    11/2/2007

    强暴.Net程序集 之八 (元数据Token和指定程序入口方法)

     

    警告:本系列文章为本人原创,只作技术研究之用,您可以引用链接传播,禁止其它的转载方式,禁止用于商业或非法目的, 对于造成的一切后果本人概不负责

    一般的.net程序都是从一个静态的Main方法开法运行的,下面我将演示如何指定程序集中的另一个方法作为入口方法

    假设有如下程序,我们要修改程序集让NewMain先运行
    using System;
    public class Program
    {
     public static void Main()
     {
      Console.WriteLine("Program::Main()");
     }

     public static void NewMain()
     {
      Console.WriteLine("Program::NewMain()");
     }
    }

    回想一下IMAGE_COR20_HEADER的格式,字段EntryPointToken指定了程序入口

    在Partition II Metadata中写道:
    Entry point metadata token
    The entry point token (§15.4.1.2) is always a MethodDef token (§22.26) or File token (§22.19 ) when the entry point for a multi-module assembly is not in the manifest assembly.  The signature and implementation flags in metadata for the method indicate how the entry is run

    它的值不是一个地址,而是一个元数据Token
    这个Token的格式是,前1个字节是元数据表的编号,最后一个字节是记录的索引
    比如Token为0x06000001,0x06指Method表,0x01指Method表的第一个记录,这通常指向到Main方法
    在元数据中大量使用了这种Token的方式,来引用其它表或流的项目

    我们只需要修改此Token就能指定新的入口方法,在我们这个演示中改为NewMain方法的Token,即0x06000002

    程序如下:
    pCorHeader->EntryPointToken = _htoi(szNewEntryPointToken);


    用ildasm打开后观察
    .method public hidebysig static void  NewMain() cil managed
    {
      .entrypoint
      // 代码大小       13 (0xd)
      .maxstack  8
      IL_0000:  nop
      IL_0001:  ldstr      "Program::NewMain()"
      IL_0006:  call       void [mscorlib]System.Console::WriteLine(string)
      IL_000b:  nop
      IL_000c:  ret
    } // end of method Program::NewMain

    可以看到.entrypoint伪指令已经在我们指定的NewMain方法中了

     

    11/1/2007

    强暴.Net程序集 之七 (强命名的移除)

     
    警告:本系列文章为本人原创,只作技术研究之用,您可以引用链接传播,禁止其它的转载方式,禁止用于商业或非法目的, 对于造成的一切后果本人概不负责
     
    先让我们重新看一下IMAGE_COR20_HEADER头的格式:
    typedef struct IMAGE_COR20_HEADER
    {
        // Header versioning
        DWORD                   cb;             
        WORD                    MajorRuntimeVersion;
        WORD                    MinorRuntimeVersion;
       
        // Symbol table and startup information
        IMAGE_DATA_DIRECTORY    MetaData;       
        DWORD                   Flags;          
        DWORD                   EntryPointToken;
       
        // Binding information
        IMAGE_DATA_DIRECTORY    Resources;
        IMAGE_DATA_DIRECTORY    StrongNameSignature;
        // Regular fixup and binding information
        IMAGE_DATA_DIRECTORY    CodeManagerTable;
        IMAGE_DATA_DIRECTORY    VTableFixups;
        IMAGE_DATA_DIRECTORY    ExportAddressTableJumps;
        // Precompiled image info (internal use only - set to zero)
        IMAGE_DATA_DIRECTORY    ManagedNativeHeader;
       
    } IMAGE_COR20_HEADER, *PIMAGE_COR20_HEADER;
     
    移除强名需要修改其中的这两个字段
    Flags
    StrongNameSignature
    其中Flags是一个按位或值,我们只需要移去COMIMAGE_FLAGS_STRONGNAMESIGNED
    在winnt.h中有其定义:
    typedef enum ReplacesCorHdrNumericDefines
    {
    // COM+ Header entry point flags.
        COMIMAGE_FLAGS_ILONLY               =0x00000001,
        COMIMAGE_FLAGS_32BITREQUIRED        =0x00000002,
        COMIMAGE_FLAGS_IL_LIBRARY           =0x00000004,
        COMIMAGE_FLAGS_STRONGNAMESIGNED     =0x00000008,
        COMIMAGE_FLAGS_TRACKDEBUGDATA       =0x00010000,
        //略
     }

    然后将StrongNameSignature和VirtualAddress和Size都置为0
     
    第二步是修改Assembly表
    将Flags字段的PublicKey位移去
     在Partition II Metadata中有描述:
     PublicKey   0x0001
     SideBySideCompatible  0x0000
     <reserved>   0x0030
     Retargetable   0x0100
     EnableJITcompileTracking  0x8000
     DisableJITcompileOptimizer 0x4000
     
    另外修改PublicKey字段,将其置为0
    代码为:
     pCorHeader->Flags &= ~COMIMAGE_FLAGS_STRONGNAMESIGNED;
     pCorHeader->StrongNameSignature.VirtualAddress = 0;
     pCorHeader->StrongNameSignature.Size = 0;
     PMETA_ASSEMBLY_TABLE assemblyTable = (PMETA_ASSEMBLY_TABLE)(tables[TableType::Assembly].Address);
     assemblyTable->Flags &= 0x1110;  //PublicKey = 0x0001
     assemblyTable->PublicKey = 0;
    这样我们就将到了一个没有强名的程序集

    在.net的非托管 API中提供了一系列和强名相关的函数:
    StrongNameCompareAssemblies   仅通过二进制签名确定两个指定的程序集是否不同。
    StrongNameErrorInfo    获取由强名称方法引发的上一个错误代码。
    StrongNameFreeBuffer    释放上一次调用强名称方法时分配的内存。
    StrongNameGetBlob    通过位于指定内存地址的可执行文件的二进制表示形式填充指定的缓冲区。
    StrongNameGetBlobFromImage   获取位于指定内存地址的程序集映像的二进制表示形式。
    StrongNameGetPublicKey    从私钥/公钥对中检索公钥。
    StrongNameHashSize    使用指定的哈希算法获取哈希值所需要的缓冲区大小。
    StrongNameKeyDelete    删除具有指定容器名称的公钥-私钥对。
    StrongNameKeyGen    创建一个用于强名称的公/私钥对。
    StrongNameKeyGenEx    创建具有指定密钥大小的公钥/私钥对,以供强名称使用。
    StrongNameKeyInstall    向容器中导入公钥-私钥对。
    StrongNameSignatureGeneration   生成指定程序集的强名称签名。
    StrongNameSignatureGenerationEx   使用指定的密钥容器名称获取指定程序集的强名称签名。
    StrongNameSignatureSize    返回强名称签名的大小。
    StrongNameSignatureVerification   获取一个指示所提供的可移植的可执行(.dll 或 .exe)文件的程序集清单是否包含强名称签名的值。
    StrongNameSignatureVerificationEx  获取一个指示所提供的可移植的可执行(.dll 或 .exe)文件的程序集清单是否包含强名称签名的值。
    StrongNameSignatureVerificationFromImage 验证已映射到内存的程序集对关联的公钥是否有效。
    StrongNameTokenFromAssembly   从指定的程序集文件中创建强名称标记。
    StrongNameTokenFromAssemblyEx   从指定的程序集文件创建强名称标记,并返回公钥。
    StrongNameTokenFromPublicKey   返回表示公钥的标记。