CSharp 反射注入总结

0x00 C# 反射注入总结

      反射的流程一共有3种类型:CreateInstance类型,CallByName类型,Invoke类型。test

0x01 CreateInstance类型

      采用CreateInstance的C#反射,最终会通过调用Activator.CreateInstance实现反射注入的目的,在C#中CreateInstance函数具有多种重载,其中被使用于C#的是如下原型:第一个参数为type,表示需要创建的Object的类型,第二个参数是有对象组成的数组。

1
public static object? CreateInstance (Type type, params object?[]? args);

      可以采用CallByName函数获取对象的类型,CallByName可以在对象上执行方法,设置或者返回属性。
其函数原型如下:

  • ObjectRef:是对于某个对象的引用
  • ProcName:对象上的函数或者方法名,此处在反射中一般使用GetType
  • UseCallType:一个枚举类型
  • Args:一个对象数组
    1
    public static object? CallByName (object? ObjectRef, string ProcName, Microsoft.VisualBasic.CallType UseCallType, params object?[] Args);

      CallByName第一个参数是ObjectRef,可以使用GetObjectValue获取,微软的解释,该函数的作用为Boxes a value type.,查看下面的返回值信息,可以了解,该函数可以返回Object本身(obj itself)

1
public static object? GetObjectValue (object? obj);

      如此,整个逻辑就清楚了,首先通过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方法进行识别。

Ref:如何分析各种类型的恶意样本之–C#恶意样本分析技巧