吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 9213|回复: 14
上一主题 下一主题
收起左侧

[PC样本分析] Cobalt Strike剖析及免杀系列(二)浅谈ReflectiveDLLInjection

  [复制链接]
跳转到指定楼层
楼主
GalaXY403 发表于 2022-3-21 15:02 回帖奖励
使用论坛附件上传样本压缩包时必须使用压缩密码保护,压缩密码:52pojie,否则会导致论坛被杀毒软件等误报,论坛有权随时删除相关附件和帖子!
病毒分析分区附件样本、网址谨慎下载点击,可能对计算机产生破坏,仅供安全人员在法律允许范围内研究,禁止非法用途!
禁止求非法渗透测试、非法网络攻击、获取隐私等违法内容,即使对方是非法内容,也应向警方求助!

前言

关于免杀与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

免费评分

参与人数 6吾爱币 +11 热心值 +6 收起 理由
自强 + 1 + 1 谢谢@Thanks!
Hmily + 7 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
61893595 + 1 + 1 认真向大佬学习
HongHu106 + 1 谢谢@Thanks!
雪流星 + 1 + 1 谢谢@Thanks!
wildbloom + 1 + 1 用心讨论,共获提升!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

推荐
lyu3452008 发表于 2023-9-20 18:02
补充一下LZ的编译环境应该是VS2019,我尝试在VS2022上面编译运行没法正确获取PE头的基址
3#
wildbloom 发表于 2022-3-21 16:29
4#
zhouxue1028 发表于 2022-3-21 18:07
5#
CSAPPYYDS 发表于 2022-3-21 20:39
先回后学,这就开学
6#
third1979 发表于 2022-3-22 08:25
感觉好专业的……。只会用工具的我来说太难了
7#
咔c君 发表于 2022-3-23 14:26
不错厉害了
8#
c1ear 发表于 2022-9-3 00:14
学习了!!!
9#
181912068 发表于 2023-6-27 15:41
感谢分享
10#
onemores 发表于 2023-9-25 23:00
大佬大佬 谢了
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-22 11:08

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表