0x00 C# 反射注入总结
反射的流程一共有3种类型:CreateInstance类型,CallByName类型,Invoke类型。test
0x01 CreateInstance类型
采用CreateInstance的C#反射,最终会通过调用Activator.CreateInstance实现反射注入的目的,在C#中CreateInstance函数具有多种重载,其中被使用于C#的是如下原型:第一个参数为type,表示需要创建的Object的类型,第二个参数是有对象组成的数组。
可以采用CallByName函数获取对象的类型,CallByName可以在对象上执行方法,设置或者返回属性。
其函数原型如下:
- ObjectRef:是对于某个对象的引用
- ProcName:对象上的函数或者方法名,此处在反射中一般使用GetType
- UseCallType:一个枚举类型
- Args:一个对象数组1public static object? CallByName (object? ObjectRef, string ProcName, Microsoft.VisualBasic.CallType UseCallType, params object?[] Args);
CallByName第一个参数是ObjectRef,可以使用GetObjectValue获取,微软的解释,该函数的作用为Boxes a value type.,查看下面的返回值信息,可以了解,该函数可以返回Object本身(obj itself)
如此,整个逻辑就清楚了,首先通过GetObjectValue函数,创建一个Object,接着调用CallByName函数,或者该对象的类型,最后调用CreateInstance创建一个实例,完成反射。整个流程最终会通过调用CreateInstance实现,所以可以直接搜索该函数来提高分析进度
如图,通过搜索CreateInstance,借助Dnspy的分析功能,得到该样本反射注入的流程【图1】,通过分析在public DebuggableAttribute函数中,分别调用了az()函数,和imimimimim()函数,imimimimim()函数最终调用CreateInstance创建实例,而az()函数会调用xxxxxxxxxxxxxxxxxx()函数,
在xxxxxxxxxxxxxxxxxx()函数中,正如之前所说,会一次调用RuntimeHelpers.GetObjectValue(object_0)和Versioned.CallByName(object_0, string_0, callType_0, object_1)【分别在smethod_6和smethod_5中】;可以看到在smethod_6中,Args的反射注入特征很明显【图2】:
- 第一:new了一个对象,这其实是反射加载的程序集
- 第二:第一个参数为load字符串。
而在smethod_5中,new的object为一个字符串,实际是加载程序集的所在的class。上述都可以作为快速确定是否是C#反射的特征【图3】
0x02 CallByName类型
之前讲过,CallByName可以执行对象上的方法,如图所示,首先加载了位于资源文件中的图片资源,然后通过 MessageEnum.fgh进行解密,得到字节数组,之后便调用 AppDomain.CurrentDomain.Load()进行加载,接着分别调用GetTypes(),GetMethods()获取方法,然后采用CallByName执行【图4】
这种类型可以通过Load()函数进行识别,亦可通过CallByName(),GetTypes()识别
0x02 Invoke类型
Invoke()方法,可以执行指定的方法,如图所示,在smethod_58()最终调用Load()方法加载程序及,然后经过校验入口点之后,在smethod_61调用Invoke进行反射。【图5】
这种类型可以通过Load()方法和Invoke()方法进行识别。除此以外,还可以通过GetMethod函数获取Method,然后Invoke。
0x04 InvokeMember类型
InvokeMember(),可以执行指定的方法,和Invoke功能一样。如图所示,首先通过Load()加载程序集,然后调用GetTypes()获取类型,接着调用InvokeMember执行指定的函数。【图6】
这种类型和上述一样,可以通过Load()方法,GetTypes()方法,以及InvokeMember()方法进行识别,亦可通过InvokeMember方法的参数BindingFlags.InvokeMethod方法进行识别。