DLL注入实战:3招轻松攻破进程
DLL(动态链接库) 是一种包含可由多个程序同时使用的代码和数据的库。它们最早出现在 Windows 3.1 操作系统中,旨在解决内存资源不足的问题。DLL 文件的主要作用包括提高程序的可重用性、促进代码重用和内存的有效使用。例如,Windows 中的 Comdlg32 DLL 执行与对话框有关的常见函数,允许多个程序共享这些功能。
DLL 注入实战指南(可直接上手版)
DLL 注入是 Windows 环境下将自定义动态链接库(DLL)强制加载到目标进程地址空间的技术,常用于进程监控、功能扩展、调试分析等场景。本文基于《深入浅出 Windows API 程序设计:核心编程篇》的底层原理,提供3 种可直接上手的 DLL 注入方案,包含完整代码、操作步骤及注意事项,确保新手也能快速实现。
一、核心前置知识
在动手前需明确 2 个关键概念,避免操作出错:
DLL 加载原理:进程通过函数加载 DLL 时,会将 DLL 文件映射到自身虚拟地址空间,同时执行 DLL 的
LoadLibrary初始化函数。注入的本质就是 “让目标进程主动调用
DllMain加载我们的 DLL”。权限要求:注入需获取目标进程的有效句柄,且句柄需包含
LoadLibrary(创建线程)、
PROCESS_CREATE_THREAD(查询信息)、
PROCESS_QUERY_INFORMATION(内存操作)等权限,否则会失败。
PROCESS_VM_OPERATION
二、方案 1:远程线程注入(最通用,推荐新手)
原理
通过函数在目标进程中创建一个新线程,让该线程调用
CreateRemoteThread加载指定 DLL。这是 Windows 最经典、兼容性最强的注入方案,支持 32/64 位进程(需注意 DLL 与目标进程位数一致)。
LoadLibrary
操作步骤(共 5 步)
步骤 1:编写待注入的 DLL(核心功能代码)
创建一个 C++ 项目(选择 “动态链接库 (DLL)” 模板),实现注入后要执行的功能(示例:弹出消息框 + 监控目标进程窗口)。
// InjectDll.cpp
#include <windows.h>
#include <tchar.h>
// DLL加载时执行(注入成功后触发)
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) {
switch (ul_reason_for_call) {
case DLL_PROCESS_ATTACH:
// 1. 弹出注入成功提示(仅作演示,实际可替换为监控/挂钩等逻辑)
MessageBox(NULL, TEXT("DLL注入成功!"), TEXT("注入提示"), MB_OK | MB_ICONINFORMATION);
// 2. (可选)监控目标进程窗口(示例:获取目标窗口标题)
HWND hTargetWnd = GetForegroundWindow(); // 获取当前激活窗口(即目标进程窗口)
TCHAR szWndTitle[256] = {0};
GetWindowText(hTargetWnd, szWndTitle, 256);
TCHAR szMsg[512] = {0};
_stprintf_s(szMsg, TEXT("目标进程窗口标题:%s"), szWndTitle);
MessageBox(NULL, szMsg, TEXT("进程监控"), MB_OK);
// 注意:DllMain中避免复杂操作(如创建窗口、长时间循环),否则可能导致目标进程崩溃
break;
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
步骤 2:编译 DLL(关键:匹配目标进程位数)
若目标进程是 32 位(如记事本):在 VS 中设置 “解决方案平台” 为,编译生成
x86。若目标进程是 64 位(如 64 位浏览器):设置平台为
InjectDll.dll,编译生成 64 位 DLL。编译后记录 DLL 的完整路径(如
x64),后续注入时需用到。
C:ToolsInjectDll.dll
步骤 3:编写注入器程序(核心注入逻辑)
创建一个 C++ 控制台项目,实现 “找到目标进程→申请内存→写入 DLL 路径→创建远程线程” 的完整流程。
// Injector.cpp
#include <windows.h>
#include <TlHelp32.h>
#include <tchar.h>
#include <iostream>
using namespace std;
// 1. 根据进程名找到目标进程ID(如“notepad.exe”)
DWORD FindTargetProcessId(LPCTSTR lpProcessName) {
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (hSnapshot == INVALID_HANDLE_VALUE) {
_tprintf(TEXT("创建进程快照失败,错误码:%d
"), GetLastError());
return 0;
}
PROCESSENTRY32 pe = { sizeof(PROCESSENTRY32) };
if (Process32First(hSnapshot, &pe)) {
do {
// 比较进程名(不区分大小写)
if (_tcsicmp(pe.szExeFile, lpProcessName) == 0) {
CloseHandle(hSnapshot);
return pe.th32ProcessID; // 返回目标进程ID
}
} while (Process32Next(hSnapshot, &pe));
}
_tprintf(TEXT("未找到进程:%s
"), lpProcessName);
CloseHandle(hSnapshot);
return 0;
}
// 2. 核心注入函数
BOOL RemoteThreadInject(DWORD dwProcessId, LPCTSTR lpDllPath) {
// 步骤1:打开目标进程,获取带权限的句柄
HANDLE hProcess = OpenProcess(
PROCESS_CREATE_THREAD | // 允许创建远程线程
PROCESS_QUERY_INFORMATION |// 允许查询进程信息
PROCESS_VM_OPERATION | // 允许内存操作(申请/写入)
PROCESS_VM_WRITE | // 允许写入内存
PROCESS_VM_READ, // 允许读取内存
FALSE,
dwProcessId
);
if (hProcess == NULL) {
_tprintf(TEXT("打开目标进程失败,错误码:%d
"), GetLastError());
return FALSE;
}
// 步骤2:在目标进程中申请内存,用于存放DLL路径(需包含字符串终止符'')
DWORD dwDllPathSize = (_tcslen(lpDllPath) + 1) * sizeof(TCHAR); // 计算路径总字节数
LPVOID lpRemoteAddr = VirtualAllocEx(
hProcess,
NULL, // 让系统自动分配内存地址
dwDllPathSize, // 内存大小
MEM_COMMIT | MEM_RESERVE, // 提交+预订内存
PAGE_READWRITE // 内存权限:可读可写
);
if (lpRemoteAddr == NULL) {
_tprintf(TEXT("申请远程内存失败,错误码:%d
"), GetLastError());
CloseHandle(hProcess);
return FALSE;
}
// 步骤3:将DLL路径写入目标进程的远程内存
BOOL bWrite = WriteProcessMemory(
hProcess,
lpRemoteAddr, // 目标进程的内存地址
lpDllPath, // 本地DLL路径缓冲区
dwDllPathSize, // 写入字节数
NULL // 不需要返回实际写入字节数
);
if (!bWrite) {
_tprintf(TEXT("写入远程内存失败,错误码:%d
"), GetLastError());
VirtualFreeEx(hProcess, lpRemoteAddr, 0, MEM_RELEASE); // 释放申请的内存
CloseHandle(hProcess);
return FALSE;
}
// 步骤4:获取LoadLibrary函数地址(系统函数地址在所有进程中相同)
LPVOID lpLoadLibAddr = (LPVOID)GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), "LoadLibraryW");
if (lpLoadLibAddr == NULL) {
_tprintf(TEXT("获取LoadLibrary地址失败,错误码:%d
"), GetLastError());
VirtualFreeEx(hProcess, lpRemoteAddr, 0, MEM_RELEASE);
CloseHandle(hProcess);
return FALSE;
}
// 步骤5:在目标进程中创建远程线程,执行LoadLibrary(lpRemoteAddr)
HANDLE hRemoteThread = CreateRemoteThread(
hProcess,
NULL, // 默认安全属性
0, // 线程栈大小(默认)
(LPTHREAD_START_ROUTINE)lpLoadLibAddr, // 线程函数(LoadLibrary)
lpRemoteAddr, // 线程参数(DLL路径)
0, // 立即运行线程
NULL // 不需要返回线程ID
);
if (hRemoteThread == NULL) {
_tprintf(TEXT("创建远程线程失败,错误码:%d
"), GetLastError());
VirtualFreeEx(hProcess, lpRemoteAddr, 0, MEM_RELEASE);
CloseHandle(hProcess);
return FALSE;
}
// 步骤6:等待远程线程执行完成(可选,避免资源泄漏)
WaitForSingleObject(hRemoteThread, INFINITE);
// 步骤7:释放资源
CloseHandle(hRemoteThread);
VirtualFreeEx(hProcess, lpRemoteAddr, 0, MEM_RELEASE);
CloseHandle(hProcess);
_tprintf(TEXT("注入成功!
"));
return TRUE;
}
// 主函数(入口)
int _tmain(int argc, TCHAR* argv[]) {
// 配置:目标进程名 + 待注入DLL路径
LPCTSTR lpTargetProcess = TEXT("notepad.exe"); // 目标进程(如记事本)
LPCTSTR lpDllPath = TEXT("C:\Tools\InjectDll.dll"); // 替换为你的DLL路径
// 1. 找到目标进程ID
DWORD dwProcessId = FindTargetProcessId(lpTargetProcess);
if (dwProcessId == 0) {
system("pause");
return 1;
}
_tprintf(TEXT("找到目标进程,ID:%d
"), dwProcessId);
// 2. 执行注入
BOOL bSuccess = RemoteThreadInject(dwProcessId, lpDllPath);
if (!bSuccess) {
system("pause");
return 1;
}
system("pause");
return 0;
}
步骤 4:编译注入器
同样需匹配目标进程位数(与 DLL 位数一致),编译生成。
Injector.exe
步骤 5:执行注入(实际操作)
启动目标进程:打开记事本(),确保进程正在运行。运行注入器:双击
notepad.exe,若控制台输出 “注入成功!”,记事本会弹出 “DLL 注入成功!” 的消息框,说明注入完成。
Injector.exe
三、方案 2:进程创建时注入(通过修改启动参数)
原理
利用函数创建目标进程时,通过
CreateProcess结构体的
LPSTARTUPINFO/
hStdInput等句柄,强制目标进程加载指定 DLL(适用于 “从启动开始就监控目标进程” 的场景)。
hStdOutput
核心代码(注入器)
// CreateProcessInject.cpp
#include <windows.h>
#include <tchar.h>
BOOL CreateProcessInject(LPCTSTR lpExePath, LPCTSTR lpDllPath) {
// 1. 构造“加载DLL”的启动参数(通过环境变量传递DLL路径)
TCHAR szEnv[1024] = {0};
_stprintf_s(szEnv, TEXT("INJECT_DLL=%s"), lpDllPath);
if (!SetEnvironmentVariable(TEXT("INJECT_DLL"), szEnv)) {
_tprintf(TEXT("设置环境变量失败,错误码:%d
"), GetLastError());
return FALSE;
}
// 2. 创建目标进程(挂起模式)
STARTUPINFO si = { sizeof(STARTUPINFO) };
PROCESS_INFORMATION pi = {0};
BOOL bCreate = CreateProcess(
lpExePath, // 目标进程路径(如“C:\Windows\notepad.exe”)
NULL, // 命令行参数(默认)
NULL, // 进程安全属性
NULL, // 线程安全属性
TRUE, // 继承句柄
CREATE_SUSPENDED, // 挂起进程(注入完成后再恢复)
NULL, // 继承父进程环境变量
NULL, // 当前目录
&si,
&pi
);
if (!bCreate) {
_tprintf(TEXT("创建进程失败,错误码:%d
"), GetLastError());
return FALSE;
}
// 3. 在目标进程中申请内存并写入DLL路径(逻辑同方案1的步骤2-3)
DWORD dwDllPathSize = (_tcslen(lpDllPath) + 1) * sizeof(TCHAR);
LPVOID lpRemoteAddr = VirtualAllocEx(pi.hProcess, NULL, dwDllPathSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
if (lpRemoteAddr == NULL) {
_tprintf(TEXT("申请内存失败,错误码:%d
"), GetLastError());
TerminateProcess(pi.hProcess, 0);
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
return FALSE;
}
WriteProcessMemory(pi.hProcess, lpRemoteAddr, lpDllPath, dwDllPathSize, NULL);
// 4. 创建远程线程加载DLL(逻辑同方案1的步骤4-5)
LPVOID lpLoadLibAddr = (LPVOID)GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), "LoadLibraryW");
HANDLE hRemoteThread = CreateRemoteThread(pi.hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)lpLoadLibAddr, lpRemoteAddr, 0, NULL);
if (hRemoteThread == NULL) {
_tprintf(TEXT("创建线程失败,错误码:%d
"), GetLastError());
VirtualFreeEx(pi.hProcess, lpRemoteAddr, 0, MEM_RELEASE);
TerminateProcess(pi.hProcess, 0);
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
return FALSE;
}
// 5. 等待线程完成,恢复目标进程
WaitForSingleObject(hRemoteThread, INFINITE);
ResumeThread(pi.hThread); // 恢复进程运行
// 6. 释放资源
CloseHandle(hRemoteThread);
VirtualFreeEx(pi.hProcess, lpRemoteAddr, 0, MEM_RELEASE);
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
_tprintf(TEXT("进程创建时注入成功!
"));
return TRUE;
}
// 主函数
int _tmain() {
LPCTSTR lpExePath = TEXT("C:\Windows\notepad.exe"); // 目标进程路径
LPCTSTR lpDllPath = TEXT("C:\Tools\InjectDll.dll"); // 你的DLL路径
CreateProcessInject(lpExePath, lpDllPath);
system("pause");
return 0;
}
操作步骤
无需提前启动目标进程,直接运行注入器。注入器会自动创建目标进程(如记事本),并在进程启动时加载 DLL,适用于监控进程初始化过程的场景。
四、方案 3:注册表注入(开机 / 进程启动自动注入)
原理
利用 Windows 注册表的键值,让系统在加载
AppInit_DLLs时(几乎所有带 UI 的进程都会加载
user32.dll),自动加载该键值中配置的 DLL。适用于全局注入(所有进程启动时自动加载),但需管理员权限。
user32.dll
操作步骤(无需编写注入器,仅需配置注册表)
步骤 1:准备 DLL
使用方案 1 中的(确保 DLL 能兼容大多数进程,避免崩溃)。
InjectDll.dll
步骤 2:修改注册表(需管理员权限)
按下,输入
Win+R打开注册表编辑器。定位到以下路径(32 位系统与 64 位系统路径不同):
regedit
32 位系统:64 位系统:
HKEY_LOCAL_MACHINESOFTWAREMicrosoftWindows NTCurrentVersionWindows(32 位 DLL);
HKEY_LOCAL_MACHINESOFTWAREWow6432NodeMicrosoftWindows NTCurrentVersionWindows(64 位 DLL) 找到
HKEY_LOCAL_MACHINESOFTWAREMicrosoftWindows NTCurrentVersionWindows键值(类型为
AppInit_DLLs),双击修改其值为你的 DLL 完整路径(如
REG_SZ)。找到
C:ToolsInjectDll.dll键值,确保其值为
LoadAppInit_DLLs(启用自动加载)。重启电脑或重启目标进程,进程启动时会自动加载 DLL。
1
步骤 3:恢复注册表(避免全局影响)
测试完成后,需将值清空,
AppInit_DLLs设为
LoadAppInit_DLLs,否则会导致所有进程都加载你的 DLL,可能引发系统不稳定。
0
五、关键注意事项(避坑指南)
位数一致性:DLL、注入器、目标进程三者的位数必须一致(32 位→32 位,64 位→64 位),否则注入会失败(错误码通常为)。权限问题:
ERROR_BAD_EXE_FORMAT
注入系统进程(如、
svchost.exe)需以管理员身份运行注入器,否则
explorer.exe会失败(错误码
OpenProcess)。Windows 10/11 的 “用户账户控制(UAC)” 会阻止普通权限进程注入高权限进程,需关闭 UAC 或提升注入器权限。 DLL 稳定性:
ERROR_ACCESS_DENIED
中禁止执行复杂逻辑(如创建窗口、循环、调用耗时 API),否则会导致目标进程卡死或崩溃。避免在 DLL 中使用全局变量(多线程可能冲突),如需共享数据需用线程安全的结构(如
DllMain)。 杀毒软件拦截:部分杀毒软件会将 DLL 注入判定为恶意行为,测试时需临时关闭杀毒软件,或添加信任。注入后验证:可通过 “任务管理器→详细信息→右键目标进程→打开文件位置→查看是否加载你的 DLL”,或使用
CriticalSection工具查看进程的模块列表。
Process Explorer
六、工具推荐(辅助调试)
Process Explorer:查看进程加载的 DLL 列表,验证注入是否成功(下载地址:微软官网)。OllyDbg/x64dbg:调试目标进程,查看 DLL 的是否正常执行(新手推荐 x64dbg,支持 32/64 位)。Dependency Walker:检查 DLL 的依赖项(如是否缺少
DllMain、
kernel32.dll等系统库),避免 DLL 加载失败。
user32.dll
通过以上方案,你可以快速实现 DLL 注入并验证效果。建议从 “远程线程注入” 开始练习,熟悉后再尝试其他方案。实际应用中需遵守法律法规,不得用于未经授权的进程操作。


