10/26/2007
警告:本系列文章为本人原创,只作技术研究之用,您可以引用链接传播,禁止其它的转载方式,禁止用于商业或非法目的, 对于造成的一切后果本人概不负责
上回有一个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
}
但是... ....
警告:本系列文章为本人原创,只作技术研究之用,您可以引用链接传播,禁止其它的转载方式,禁止用于商业或非法目的, 对于造成的一切后果本人概不负责
#~流是保存元数据的地方, 元数据以表的形式进行组织
当找到#~流之后,我们需要先得到元数据表的基本信息, 用于推算出每个表的位置
这个#~流的顶部格式是:
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);