前言
关于免杀与rootkit技术大家应该不会陌生,在红蓝对抗的时候有一款能够免杀绝大多数杀软的壳也能让我们的钓鱼事半功倍,今天就来聊聊关于免杀和用户级rootkit开发过程中比较重要的一门技术,ReflectiveDLLInjection反射型dll注入。
简介
随着杀软的发展,特征库的不断填充,对于各种特征检测越来越完善,对于文件中导入表的危险函数更是查了又查,一些像loadlibrary、getprocaddress等动态加载dll的危险函数很容易触发杀软的危险警报,但是又因为传输过程效率和杀软对于exe文件扫描过程的限制我们的木马尽量需要做到短小,所以网络传输无文件落地的dll注入比较受欢迎,并且也是各种远控马经常使用的技术。
开始
我们首先不要去管什么反射型dll注入还是什么注入,就单纯的从dll注入最原始的目的开始往下进行,dll注入首先是把dll注入到其他安全进程中去,目的也是为了隐藏进程与行为,最普通的dll注入是直接loadlibrary然后将此函数返回值的句柄传递到CreateRemoteThread()等远程注入相关函数,但这个句柄值是内核维持的句柄表,但事实并不是这样,句柄在内存中也是一个32位/64位的数据,只不过方便使用被定义成了句柄,而句柄又分为各种窗口句柄,文件句柄等,但是不是每个32位的数据都是索引值,我们需要根据函数返回值来确定。
不妨我们做个实验
首先写一个简单的验证dll
然后再写一个装有loadlibrary函数并且显示返回值的test
生成dll并放入到生成的release文件夹下
把此文件夹放入到装有od的虚拟机中,当然使用windbg调试也可以,我个人比较喜欢使用od图形化界面
都做完以后使用od打开生成的test然后运行
比对之后发现返回值也就是dll加载的基址,所以我们内存dll加载后再注入到其他进程的预测也就成立
接下来我们只需要写一个自己的dll文件加载器(loadlibrary)和一个函数调用器(getprocaddress)就可以完成整个反射型dll注入工作。
手写ReflectiveDLLInjection
这里我们手写两个主要函数用于完成相应的功能,两个函数是loadlibrary和getprocaddress,两个动态dll加载函数,只不过这两个系统dll函数是硬盘dll库文件加载,我们需要写两个内存dll库加载。
自己写的这个函数第一是可以自定义功能与实现,第二的话自己写的dll可以在源码改函数名,对于导出表与目标壳程序的导入表隐藏有很好的效果。
先说下对于第一个函数loadlibrary,这个函数所要完成的工作是
1.根据pe头数据进行数据的转移,并重置各区段的大小与内存页大小对齐。
2.根据重定位表数据对pe文件中的对应地址数据进行重定位。
再说下第二个函数getprocaddress,这个函数需要实现的功能就只是简单的根据导出表内容找到对应函数地址,并返回一个函数指针供上层程序调用
这里制作相应dll的细节我就不多说了,只是点一下需要注意的问题:
1.x86cpu架构的大端内存读取处理,所有相应的地址及数据处理在x86的架构上都是大端低地址,低端高地址,所以对应数据处理cpu会自动进行,我们所要重写的是操作系统函数,所以不需要对数据进行过多处理
像这种大家应该都清楚这一行的最后32位是段偏移,也就是00 10 00 00 但其实这个数用十六进制表示应该是 0x00001000,所以在数据的存取时应注意。
这里也是用一个小实验验证此说法的正确性
#include <iostream>
#include<windows.h>
int main()
{
printf("=======================\n");
printf("天河GalaXY\n");
printf("=======================\n");
int num = 10;
BYTE* a = (BYTE*)malloc(4);//分配所需的内存空间,并返回一个指向它的指针(Void*类型)
void* b = a;
*a = 0x00;
a++;
*a = 0x10;
a++;
*a = 0x00;
a++;
*a = 0x00;
int* o = (int*)b;
printf("%x", *o);
}
这里首先向内存中存入单个字节数据,顺序为 00 10 00 00,然后输出这个四个字节强转int类型的数据
结果如下
输出0x1000,证明说法正确。
2.注意地址内容与地址互相之间的关系,有时候会容易把地址内容强转地址之间犯错,并且这种错误只会报读取或写入错误,不大容易看。
3.对于修改代码段内存页保护的操作需要在重定向处理完后进行。
对于函数的编写过程中c语言的一些细节不再赘述。
调试过程
首先手写一个test dll
生成dll
010editor打开dll并复制全部16进制数据
复制完成后在notepad++中加工
加工成c语言识别的字节类型,然后合并行,复制进入vs中
在c中导入库里面的这两个函数
在c中调用函数
函数名对于vs的dll会自动加_和在尾部添加@8需要注意调用函数名。
输出结果如下
函数调用成功。
接下来结合前面的dll验证,向远程调用系统dll中放入dll地址就可以,并且后面的一些函数hook也是同理的传入地址,对于传入地址的所有操作都可以使用,不在赘述。
我把这个dll及其源码传到了github上,有需要的小伙伴可以自取
https://github.com/bestspear/GalaXY-ReflectiveDLLInjection