反射dll注入(内嵌式)
简介
与常规的DLL加载不同的是,反射DLL注入技术不依赖LoadLibrary函数来加载DLL,而是使用一个自定义的加载器ReflectiveLoader(通常嵌在DLL本身中)来执行加载过程,这个加载器负责解析DLL文件的头信息、导入函数的地址、处理重定位等初始化操作
这种技术的关键优势在于其隐蔽性,由于没有使用标准的加载API,因此不会在进程的模块列表中注册DLL这使得这种技术很难被基于这些特征的安全软件检测到
以下是ReflectiveLoader大致实现思路的思维导图:

ReflectiveLoader实现思路
1.定位DLL在内存中的基址
在ReflectiveLoader函数的代码实现中,首先调用caller函数来定位DLL在内存中的基址
以下是caller函数的定义,调用_ReturnAddress函数获取当前调用函数的返回地址,也就是caller函数下一条指令的地址
上述得到返回地址后,随着这个地址往上逐字节遍历,直到找到PE头格式的字节为止,就是我们DLL的基址
2.获取所需的系统API
首先得到PEB的基址,再通过PEB找寻找到LDR
遍历InMemoryOrderModuleList链表来查找kernel32.dll和ntdll.dll的基址,随后遍历这两个dll的导出函数来获取所需的API:
kernel32:
LoadLibrary、GetProcAddress、VirtualAllocntdll:
NtFlushInstructionCache
3.申请加载DLL所需的内存区域
申请一片新的内存区域来装载DLL,内存区域的大小取决于PE结构中IMAGE_OPTIONAL_HEADER结构体中的SizeOfImage成员
注:虽然DLL已经加载进目标进程的内存里了,但为了正确执行DLL中的代码,ReflectiveLoader需要自行完成加载过程,例如内存分配、重定位和导入解析等步骤。这就是为什么还需要重新申请一块具有执行权限的内存区域的原因
4.复制所有头+节表+节数据
以下代码负责将DLL文件从临时加载的位置复制到新分配的内存区域,首先它复制PE头和所有节头,然后遍历所有节,将它们复制到新位置
5.处理DLL的导入表
此过程的关键在于动态地解析并加载当前DLL依赖的外部DLL,并修正当前DLL的导入表,使其能够正确调用外部DLL的导出函数
6.处理DLL的重定位表
当DLL被加载到内存时,如果它们不能加载到预设的基地址上,操作系统就需要对它们进行重定位操作,这时我们就要修改DLL的重定位表
7.调用DLL入口点
最后ReflectiveLoader将控制权转移给DLL文件的入口点,入口点可以通过扩展PE头的AddressOfEntryPoint获取
Inject的实现思路
1.将DLL写入当前进程的内存
将反射dll的文件内容写入当前进程的内存
2.提升进程权限
提升当前进程的权限,使其获得调试其他进程得能力
3.使用LoadRemoteLibrary注入dll
如下代码演示了DLL的注入过程,从打开目标进程,到将DLL注入该进程,主要依赖LoadRemoteLibraryR这个自定义函数,后面讲解此函数的实现思路
3.1获取ReflectiveLoader函数的偏移
让我们来看看LoadRemoteLibrary是如何实现的,首先使用GetReflectiveLoaderOffset函数来获取ReflectiveLoader在DLL中的偏移
GetReflectiveLoaderOffset函数首先确认DLL的架构与当前环境匹配,然后访问导出表,遍历所有导出函数名称直到找到"ReflectiveLoader"。
一旦找到,函数计算并返回ReflectiveLoader相对于DLL基地址的偏移量,这一偏移量对于实现DLL的反射注入至关重要,因为它使得注入代码能够在不使用传统加载方法的情况下,直接调用并执行DLL内的ReflectiveLoader函数
3.2将DLL写入目标进程内存
3.3调用ReflectiveLoader函数
操作演示
在ReflectiveDll.c编写DLL的功能代码,例如此处写了一个弹框代码

执行如下命令,将dll注入到记事本进程,可以发现记事本出现弹框

参考文章
https://idiotc4t.com/defense-evasion/reflectivedllinjection-variation
https://yaoyue123.github.io/2021/01/31/Windows-Reflective-dllinject/#%E6%A0%B8%E5%BF%83%E6%80%9D%E8%B7%AF
最后更新于