shuai's profile娱乐精神PhotosBlog Tools Help

Blog


    10/26/2007

    强暴.net程序集 之六 (Assembly表的结构和修改)


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

    上回有一个TableRowSizes函数没有说,它是用来得到指定元数据表的行大小
    以Assembly表为例,在Partition II Metadata中描述了它的结构:

    The Assembly table has the following columns:
    • HashAlgId (a 4-byte constant of type AssemblyHashAlgorithm, §23.1.1)
    • MajorVersion, MinorVersion, BuildNumber, RevisionNumber (each being 2-byte constants)
    • Flags (a 4-byte bitmask of type AssemblyFlags, §23.1.2)
    • PublicKey (an index into the Blob heap)
    • Name (an index into the String heap)
    • Culture (an index into the String heap)

    HashAlgId 4个字节
    MajorVersion, MinorVersion, BuildNumber, RevisionNumber 共8个字节
    Flags 4个字节,指定了在Side By Side模式运行时的限制
    PublicKey、Name、Culture、是#Blob heap或#String heap的索引,Public Key是用于assembly的完整性及发布者验证;Name是Assembly的名字;Culture是此Assembly所支持的语言,要注意这几个字段都不是固定大小,而是能过HeapSize推算出来的

    对于Blob heap或String heap的大小的值是通过META_COMPOSITE_HEADER的HeapSizes计算出来的
    在Partition II Metadata有描述:
    The HeapSizes field is a bitvector that encodes the width of indexes into the various heaps.  If bit 0 is set, indexes into the “#String” heap are 4 bytes wide; if bit 1 is set, indexes into the “#GUID” heap are 4 bytes wide; if bit 2 is set, indexes into the “#Blob” heap are 4 bytes wide.  Conversely, if the HeapSize bit for a particular heap is not set, indexes into that heap are 2 bytes wide.
    Heap size flag Description
    0x01 Size of “#String” stream >= 216.
    0x02 Size of “#GUID” stream >= 216.
    0x04 Size of “#Blob” stream >= 216.

    这些信息正好是ildasm对.assembly 伪指令的输出,比如
    .assembly CountDown
    { .hash algorithm 32772
      .ver 1:0:0:0
    }

    于是我们就可以求得总的行大小(Assembly表只有这一行)
      bytes = 4;
      bytes += 8;
      bytes += 4;
      bytes += (heapSizes & 0x04) ? 4 : 2;
      bytes += (heapSizes & 0x01) ? 4 : 2;
      bytes += (heapSizes & 0x01) ? 4 : 2;

    现在我们改一下主版本号,注意我用的是内存映射文件
     PMETA_ASSEMBLY_TABLE assemblyTable = (PMETA_ASSEMBLY_TABLE)(tables[TableType::Assembly].Address);
     WORD* pVersion = &(assemblyTable->MajorVersion);
     *pVersion = atoi(szBuffer);

    用ildasm看已经修改过来了:
    .assembly query
    {
      .ver 8:0:0:0
    }

    但是... ....

     

    强暴.net程序集 之五 (#~流的元数据表)

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

    #~流是保存元数据的地方, 元数据以表的形式进行组织
    当找到#~流之后,我们需要先得到元数据表的基本信息, 用于推算出每个表的位置
    这个#~流的顶部格式是:
    typedef struct _META_COMPOSITE_HEADER
    {
        DWORD Reserved; 
        BYTE MajorVersion;
        BYTE MinorVersion;
        BYTE HeapSizes;
        BYTE Padding;
        ULONGLONG Valid;
        ULONGLONG Sorted;
    } META_COMPOSITE_HEADER, *PMETA_COMPOSITE_HEADER;

    在Partition II Metadata描述为:
    Offset   Size     Field         Description
    0        4        Reserved      Reserved, always 0 (§24.1).
    4        1        MajorVersion  Major version of table schemata; shall be 2 (§24.1).
    5        1        MinorVersion  Minor version of table schemata; shall be 0 (§24.1).
    6        1        HeapSizes     Bit vector for heap sizes.
    7        1        Reserved      Reserved, always 1 (§24.1).
    8        8        Valid         Bit vector of present tables, let n be the number of bits that are 1.
    16       8        Sorted        Bit vector of sorted tables.
    24       4*n      Rows          Array of n 4-byte unsigned integers indicating the number of rows for each present table.
    24+4*n            Tables        The sequence of physical tables.

    最值得注意是
    Valid字段,它的每一位代表了相应的元数据表是否存在,比如第21位表示Assembly表存在(索引编号为0x20,对应关系见TableType)
    typedef enum TableType
    {
        Module          = 0x00,
        TypeRef         = 0x01,
        TypeDef         = 0x02,
        FieldDef        = 0x04,
        MethodDef       = 0x06,
        ParamDef        = 0x08,
        //略
        Assembly        = 0x20,
        //略
        Unassigned      = -1
    } TableType;

    Rows数组的大小为有效表的个数,每一个Rows元素记录了该表的记录的行数
    Tables是实际元数据表的起始位置

    通过计算记录行数和每行大小可以得到每个表的地址

    tableID = 0;
    valid = compositeTable->Valid;
    PDWORD rows = (PDWORD)(compositeTable + 1); //Rows的位置
    PBYTE address = (PBYTE)(rows + tableCount); //第一个表的位置
    while(valid)
    {
     if(valid & 0x1)
     {
      tables[(TableType)tableID] = tableEntry;
      tables[(TableType)tableID].Address = address;
      tables[(TableType)tableID].Table = (TableType)tableID;
      tables[(TableType)tableID].Rows = rows[tableCount];
      tables[(TableType)tableID].HeapSizes = compositeTable->HeapSizes;
      tables[(TableType)tableID].Address = address;
      tables[(TableType)tableID].RowSize = TableRowSizes((TableType)tableID);

      //下一个表的位置
      address += tables[(TableType)tableID].RowSize * tables[(TableType)tableID].Rows;
     }
     valid = valid >> 1;
     tableID++;
    }

    上面程序有一个TableRowSizes函数用于计算某个表的行大小, 它将放在下一篇文章去描述

    当然如果要使用元数据接口就更加方便, 可以调用 IMetaDataTables::GetTableInfo
    获取位于指定表索引处的表的行大小、名称、行数和列数。
        HRESULT GetTableInfo (
            ULONG       ixTbl,
            ULONG       *pcbRow,
            ULONG       *pcRows,
            ULONG       *pcCols,
            ULONG       *piKey,
            const char  **ppName
        );
    参数       说明 
    ixTbl   [in]   要返回其属性的表的索引。
    pcbRow  [out] 表行的大小(以字节为单位)。
    pcRows  [out] 表中的行数。
    pcCols  [out] 表中的列数。
    piKey   [out] 键列的索引,或在表没有键列时为 -1。
    ppName  [out] 表名称。

    以及 IMetaDataTables::GetNumTables
    获取在当前 IMetaDataTables 实例范围内的表个数。
        HRESULT GetNumTables (
            ULONG   *pcTables
        );
    参数        说明 
    pcTables [out] 在当前实例范围内的表的计数。

    在ildasm中的dasm.cpp文件DumpStatistics 和 DumpTable 函数有一个实际调用的示例
    g_pPubImport->QueryInterface(IID_IMetaDataTables, (void**)&pITables))
    pITables->GetNumTables(&count);
    pITables->GetTableInfo(TBL_Method, &sizeRec, NULL, NULL, NULL, NULL);