API/库调用指令的最后一个字节总是 0x0A
这是因为调用需要将方法 (ref) 作为参数,并且方法在具有 id 的表中定义0x0A
。
有这样的调用字节280600000A
让我们一一进行。
0x28
- 是操作码“调用”的值,它需要一个操作数。
- 操作码的其余部分是元数据令牌,所以基本上是应该调用哪个方法的信息
但为什么0x0A
是最后呢?它应该被读作一个 little-endian,所以值应该是0x0A000006
. 但是字节是什么?
来自维基百科:
当 CIL 代码使用元数据时,它是通过元数据令牌来实现的。这是一个 32 位值,其中前 8 位标识适当的元数据表,其余 24 位给出表中元数据的索引。
所以第一个值是表 id - 正如我所提到的,成员 ref 表的 id 是 value 0x0A
。其余的是表中的索引(在我们的例子中0x6
)。
更多关于桌子的信息?这些基本上是 .NET 元数据信息的一部分,当您在 ie dnSpy 中打开时可以看到这些信息

如您所见,0x0A
被分配给了MemberRef
(包含方法和字段引用)
ldstr 指令的操作数呢?
在这种情况下,指令具有以下结构72XXXX0070
并0x70
指示不同的流 - 即用户定义的字符串或#US
. 其余的(再次解释为一个小结尾的 32 位 val)是表中的偏移量(以字节为单位)#US
。
一般来说,dnSpy 是一个很好的工具,可以像元数据表一样验证这些值。
链接的文章(以及整个系列是一个很好的资源)它确实解释了(我认为)结构,但可能是间接的 - 只需找到有关元数据令牌的信息。
我们可以从中推断出元数据令牌的整个结构。