让我们一起学MetaData API(2)
上次简单地介绍了在win32下使用MetaData API的方法,本章在深入应用它的各种功能前,先介绍在C# 下运用的方法。因为C#用于.net编程实在是太方便了!本章的示例代码来自开源软件DILE(dotnet il editor),下载地址http://dile.sourceforge.net。1、 在C#中引入COM
C#中使用COM要用到.net与COM的Interop,所有的内容全部都是公开的,所以直接看代码。一般,需要引用什么接口,就建立一个相应的文件,比如要引用IMetaDataDispenserEx接口,我们就在工程中新建一个IMetaDataDispenserEx.cs,代码如下(代码引用自我写的injectReflector):
namespace injectReflector.com
{
[ComImport, GuidAttribute("31BCFCE2-DAFB-11D2-9F81-00C04F79A0A3"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IMetaDataDispenserEx
{
uint DefineScope(ref Guid rclsid, uint dwCreateFlags, ref Guid riid, [MarshalAs(UnmanagedType.Interface)]out object ppIUnk);
uint OpenScope([MarshalAs(UnmanagedType.LPWStr)]string szScope, uint dwOpenFlags, ref Guid riid, [MarshalAs(UnmanagedType.Interface)] out object ppIUnk);
uint OpenScopeOnMemory(IntPtr pData, uint cbData, uint dwOpenFlags, ref Guid riid, [MarshalAs(UnmanagedType.Interface)]out object ppIUnk);
uint SetOption(ref Guid optionid, [MarshalAs(UnmanagedType.Struct)]object value);
uint GetOption(ref Guid optionid, [MarshalAs(UnmanagedType.Struct)]out object pvalue);
uint OpenScopeOnITypeInfo([MarshalAs(UnmanagedType.Interface)]ITypeInfo pITI, uint dwOpenFlags, ref Guid riid, [MarshalAs(UnmanagedType.Interface)]out object ppIUnk);
uint GetCORSystemDirectory([MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)]char[] szBuffer, uint cchBuffer, out uint pchBuffer);
uint FindAssembly([MarshalAs(UnmanagedType.LPWStr)]string szAppBase, [MarshalAs(UnmanagedType.LPWStr)]string szPrivateBin, [MarshalAs(UnmanagedType.LPWStr)]string szGlobalBin, [MarshalAs(UnmanagedType.LPWStr)]string szAssemblyName, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 4)]char[] szName, uint cchName, out uint pcName);
uint FindAssemblyModule([MarshalAs(UnmanagedType.LPWStr)]string szAppBase, [MarshalAs(UnmanagedType.LPWStr)]string szPrivateBin, [MarshalAs(UnmanagedType.LPWStr)]string szGlobalBin, [MarshalAs(UnmanagedType.LPWStr)]string szAssemblyName, [MarshalAs(UnmanagedType.LPWStr)]string szModuleName, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 5)]char[] szName, uint cchName, out uint pcName);
}
}
关键的几处,一是ComImport属性中的定义,包话GUID值和接口类型(IUnknown),这样我们的接口就定义OK了。随后就是在接口中定义COM中的所有函数,由于是Interface,所以这里的定义中不包含任何代码。2、 C#中使用本地数据类型
接口函数的定义中用到了许多MarshalAs,这是C#与本地数据类型的转换。《COM本质论》中给出了一张表,列举了所有的需要转换和C#自动转换的数据类型。看下代码中用到的:
无需转换:uint、Guid
需要转换:
object [MarshalAs(UnmanagedType.Interface)]
object [MarshalAs(UnmanagedType.Struct)]
string [MarshalAs(UnmanagedType.LPWStr)]
char[] [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 5或4)]
其中string的转换也许是最常用的,特别是在调用本地dll时。指针倒反而简单了,IntPtr直接使用。3、 使用Interface
定义完接口后,在代码中如何使用呢?首先要定义这些接口的变量:
public static IMetaDataDispenserEx dispenser;
public static IMetaDataImport import;
随后就可以直接使用接口的方法。看下示例,同样来自injectReflector中:
public static string GetMethodDefName(uint token)
{
string szRet;
uint bclass=0;
char[] szName=new char[MAX_NAME_LENGTH];
uint ccount;
uint pAttr;
IntPtr pBlob;
uint bcount;
uint pRVA;
uint flags;
import.GetMethodProps(token, out bclass, szName, (uint)MAX_NAME_LENGTH, out ccount, out pAttr, out pBlob, out bcount, out pRVA, out flags);
if(ccount==0)
{
szRet = token.ToString();
}
else
{
szRet = new string(szName, 0, Convert.ToInt32(ccount) - 1);
}
if(bclass!=0)
{
szRet = GetTypeDefRefName(bclass) +"."+ szRet;
}
return szRet;
}
这段代码是根据一个token取得该token所对应的方法的名称。值得注意的仍然是类型的转换,看下IMetaDataImport::GetMethodProps的原型定义:
HRESULT GetMethodProps (
mdMethodDef mb,
mdTypeDef *pClass,
LPWSTR szMethod,
ULONG cchMethod,
ULONG *pchMethod,
DWORD *pdwAttr,
PCCOR_SIGNATURE *ppvSigBlob,
ULONG *pcbSigBlob,
ULONG *pulCodeRVA,
DWORD *pdwImplFlags
);
对照代码可以看到uint代替了大多数定义,包括mdMethodDef、mdTypeDef、ulong、dword,还有两个转换是LPWSTR就直接传递char[],指针PCCOR_SIGNATURE直接用IntPtr代替。所有的MetaData接口用到的类型基本上就这么多了。
最后,需要使用哪个接口的某个方法,就直接用C#的语法进行调用:
import.GetMethodProps
import.GetFieldProps
import.GetTypeSpecFromToken4、 小结
完整的代码示例请参照开始给出的DILE。本文没有什么难度,只是介绍下C#中使用MetaData COM接口的方法,但是MetaData API的威力确是无穷的。以后有时间再介绍吧。
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)