00-进程注入基础概念
阅读时间: 3-4 分钟
前置知识: C++ 基础、Windows API 基础
学习目标: 理解进程注入原理,掌握远程线程注入技术
📺 配套视频教程
本文配套视频教程已发布在 B 站,建议结合视频学习效果更佳:
💡 提示: 点击视频右下角可全屏观看,建议配合文章食用!
视频链接: https://www.bilibili.com/video/BV1wGnozPEVA/
课程引入:为什么要学习进程注入?
在 Windows 系统安全、游戏安全、恶意软件分析等领域,进程注入(Process Injection)是一个绕不开的核心技术。
你可能见过这些场景:
- 🎮 游戏外挂修改游戏逻辑,实现透视、自瞄
- 🛡️ 杀毒软件注入监控模块,实时检测恶意行为
- 🔧 调试器(如 Visual Studio)注入调试引擎,实现断点、单步执行
- 💉 恶意软件注入正常进程,隐藏自己逃避检测
这些都是进程注入技术的实际应用。
本课程的学习目标:
- 理解进程注入的原理与实现
- 掌握远程线程注入的完整流程
- 为后续的攻防对抗课程打下基础
重要声明: 本课程仅用于防御性安全研究与教学,所有技术均应用于提升安全防护能力,严禁用于非法用途。
什么是进程注入
定义
进程注入(Process Injection)是一种将自己的代码插入到其他正在运行的进程中,并让目标进程执行这些代码的技术。
简单来说,就是:
借用别人的进程来运行自己的代码
生活类比:玩具工厂的故事
想象一家正常运营的玩具工厂,每天按照正常流程生产各种玩具。
正常情况:
- 工厂接收订单 → 按图纸生产 → 出厂检验 → 发货
进程注入的场景:
现在有个人想生产假冒产品,但又不想被发现。于是他:
潜入工厂(获取进程访问权限)
- 伪装成工人,混进工厂
- 对应代码:
OpenProcess
占据生产线空位(分配内存)
- 在车间找个空闲的工作台
- 对应代码:
VirtualAllocEx
偷运假冒图纸(写入代码/数据)
- 把假冒产品的设计图纸带进来
- 对应代码:
WriteProcessMemory
启动生产(创建线程执行)
- 启动生产线,开始生产假冒产品
- 对应代码:
CreateRemoteThread
这样,工厂表面上还是在正常生产玩具,但实际上也在偷偷生产假冒产品。**外界看到的是”正常工厂”,内部却在执行”恶意操作”**。
技术本质
从技术角度看,进程注入利用了 Windows 进程的以下特性:
进程间访问机制
- Windows 允许具有足够权限的进程访问其他进程的内存
内存空间独立性
- 每个进程有独立的虚拟地址空间
- 但具有权限的进程可以操作其他进程的内存
线程执行机制
- 进程通过线程执行代码
- 可以在目标进程中创建新线程来执行注入的代码
进程注入的典型应用场景
合法用途
调试与逆向工程
- 调试器(如 Visual Studio、WinDbg)通过注入调试引擎实现断点、单步执行
- 性能分析工具(如 Intel VTune)注入性能监控代码
安全监控与防护
- 杀毒软件注入监控模块,实时检测恶意行为
- DLP(数据防泄漏)软件注入监控敏感数据操作
辅助功能与增强工具
- 输入法程序注入到应用,提供输入支持
- 截图工具、翻译工具注入目标程序获取界面内容
攻击用途(仅供防御学习)
游戏外挂
- 注入作弊代码,实现透视、自瞄、无敌等功能
恶意软件
- 木马、后门程序注入正常进程隐藏自己
- 窃取敏感数据(如银行账号、密码)
绕过安全检测
- 通过注入到白名单进程,绕过安全软件检测
重要声明: 本课程仅用于防御性安全研究与教学,所有技术均应用于提升安全防护能力,严禁用于非法用途。
进程注入的四个核心步骤
无论使用哪种注入技术,核心流程都可以归纳为四个步骤:
1 | 1. 获取访问权限(OpenProcess) |
接下来,我们用远程线程注入(最经典的注入方式)来详细讲解这四个步骤。
准备工作:明确目标与资源
确定注入目标
在开始注入之前,我们需要明确两个关键要素:
1. 目标进程
- 进程 ID(PID)或进程名
- 本例使用 PID
1234作为演示
2. 注入内容
- DLL 文件路径:
C:\injection.dll - DLL 功能:弹出”注入成功”提示框
注入成功的验证标准
- ✅ 目标进程成功加载
injection.dll - ✅ 弹出 MessageBox 提示”注入成功”
- ✅ 目标进程未崩溃,功能正常
步骤一:获取目标进程的访问权限
原理讲解
在 Windows 中,进程之间是相互隔离的。要操作其他进程,必须先获得进程句柄(Process Handle),它类似于”访问通行证”。
进程句柄的作用:
- 标识目标进程
- 携带访问权限信息
- 后续所有操作都需要通过这个句柄
核心 API:OpenProcess
1 | HANDLE OpenProcess( |
参数说明:
dwDesiredAccess:请求的权限PROCESS_ALL_ACCESS:完全访问权限(包括读写内存、创建线程)PROCESS_VM_WRITE:写入内存权限PROCESS_VM_OPERATION:内存操作权限PROCESS_CREATE_THREAD:创建线程权限
bInheritHandle:通常设为FALSEdwProcessId:目标进程的 PID
代码实现
1 | // 目标进程 PID |
权限要求
管理员权限:
- 访问系统进程或高权限进程时,需要以管理员身份运行注入程序
权限不足的典型错误:
- 错误码
5(ERROR_ACCESS_DENIED):访问被拒绝 - 原因:当前进程权限不足,无法访问目标进程
步骤二:在目标进程中分配内存空间
原理讲解
获得进程句柄后,我们需要在目标进程的虚拟地址空间中分配一块内存,用于存放 DLL 路径字符串。
为什么需要分配内存?
- 目标进程有独立的虚拟地址空间
- 我们的 DLL 路径字符串在注入程序的内存中
- 需要在目标进程中分配空间,把路径复制过去
类比:
- 就像在工厂(目标进程)里找个空地方(分配内存)
- 用来存放设备(DLL 路径)
核心 API:VirtualAllocEx
1 | LPVOID VirtualAllocEx( |
参数说明:
hProcess:目标进程句柄(步骤一获得)lpAddress:指定分配地址,通常为NULL(让系统自动选择)dwSize:分配大小(DLL 路径长度)flAllocationType:MEM_COMMIT | MEM_RESERVE:提交并预留内存
flProtect:PAGE_READWRITE:可读可写
代码实现
1 | // DLL 完整路径 |
内存保护属性说明
| 属性 | 含义 | 用途 |
|---|---|---|
PAGE_READWRITE |
可读可写 | 存放数据(如 DLL 路径) |
PAGE_EXECUTE_READWRITE |
可读可写可执行 | 存放代码(如 Shellcode) |
PAGE_READONLY |
只读 | 存放常量数据 |
本例中,我们只需要存放 DLL 路径字符串,因此使用 PAGE_READWRITE 即可。
步骤三:写入 DLL 路径到目标进程
原理讲解
内存分配完成后,我们需要把 DLL 路径字符串从注入程序的内存复制到目标进程的内存中。
跨进程内存写入:
- 源地址:注入程序的内存(
dllPath变量) - 目标地址:目标进程的内存(
pRemoteMemory) - 操作方式:使用
WriteProcessMemory
核心 API:WriteProcessMemory
1 | BOOL WriteProcessMemory( |
参数说明:
hProcess:目标进程句柄lpBaseAddress:目标地址(步骤二分配的远程内存地址)lpBuffer:源数据(DLL 路径字符串)nSize:写入大小(路径长度)lpNumberOfBytesWritten:实际写入字节数,可为NULL
代码实现
1 | // 将 DLL 路径写入目标进程 |
验证写入是否成功
可选:使用 ReadProcessMemory 读取刚才写入的内容,验证是否正确:
1 | char readBuffer[MAX_PATH] = {0}; |
步骤四:获取 LoadLibrary 函数地址
原理讲解
现在,DLL 路径已经写入目标进程的内存中。接下来需要让目标进程加载这个 DLL。
核心问题:
- 如何让目标进程执行”加载 DLL”的操作?
解决方案:
- 使用 Windows API
LoadLibraryA - 这个函数的作用就是加载指定路径的 DLL
LoadLibraryA 函数原型:
1 | HMODULE LoadLibraryA(LPCSTR lpLibFileName); |
- 参数:DLL 文件路径(字符串指针)
- 返回值:DLL 模块句柄
关键技巧:
LoadLibraryA位于kernel32.dll中kernel32.dll在所有进程中加载地址相同- 因此,在注入程序中获取的
LoadLibraryA地址,在目标进程中同样有效
核心 API:GetModuleHandle 和 GetProcAddress
1 | // 1. 获取 kernel32.dll 模块句柄 |
GetModuleHandleA:
- 获取指定模块的句柄
- 参数:模块名(如
"kernel32.dll") - 返回值:模块基地址
GetProcAddress:
- 获取指定函数的地址
- 参数:模块句柄、函数名
- 返回值:函数地址
代码实现
1 | // 获取 kernel32.dll 模块句柄 |
为什么 kernel32.dll 地址相同?
Windows 系统特性:
kernel32.dll、ntdll.dll等系统 DLL 在所有进程中的加载地址相同- 原因:系统优化,减少内存占用
- 因此,在注入程序中获取的函数地址,在目标进程中同样有效
步骤五:创建远程线程执行注入
原理讲解
现在我们已经准备好了所有资源:
- ✅ 目标进程句柄(步骤一)
- ✅ 远程内存地址,存放 DLL 路径(步骤二、三)
- ✅
LoadLibraryA函数地址(步骤四)
最后一步:
- 在目标进程中创建一个新线程
- 让这个线程执行
LoadLibraryA(dllPath) - 从而加载我们的 DLL
线程的本质:
- 线程是进程的执行单元
- 线程从指定的函数地址开始执行
- 我们指定的起始地址是
LoadLibraryA - 线程的参数是 DLL 路径(远程内存地址)
核心 API:CreateRemoteThread
1 | HANDLE CreateRemoteThread( |
参数说明:
hProcess:目标进程句柄lpThreadAttributes:线程安全属性,通常为NULLdwStackSize:栈大小,0表示使用默认大小lpStartAddress:线程起始地址(LoadLibraryA地址)lpParameter:线程参数(DLL 路径的远程内存地址)dwCreationFlags:创建标志,0表示立即执行lpThreadId:线程 ID,可为NULL
代码实现
1 | // 创建远程线程 |
执行流程分析
1 | 1. CreateRemoteThread 被调用 |
资源清理:善后工作
注入完成后,需要清理分配的资源,避免内存泄漏。
清理步骤
1 | // 1. 关闭远程线程句柄 |
注意事项
- ✅ 必须清理:避免句柄泄漏,耗尽系统资源
- ✅ 清理顺序:先关闭线程句柄,再释放内存,最后关闭进程句柄
- ⚠️ DLL 仍然加载:即使释放了 DLL 路径的内存,DLL 已经加载到目标进程,不会被卸载
完整代码示例
1 | /** |
运行与验证
编译程序
1 | cl /std:c++17 /EHsc remote_thread_injection.cpp |
准备 DLL
创建一个简单的 DLL(injection.dll),在 DllMain 中弹出提示:
1 |
|
测试步骤
- 启动目标进程(如
notepad.exe),记录 PID - 运行注入程序,输入目标 PID
- 观察结果:目标进程弹出 “DLL 注入成功” 提示框
效果验证
成功运行注入程序后,可以看到以下效果:

验证要点:
- ✅ 目标进程成功加载了注入的 DLL
- ✅ 弹出 MessageBox 显示”DLL 注入成功!”
- ✅ 目标进程继续正常运行,未崩溃
- ✅ 可以使用 Process Explorer 等工具查看目标进程已加载的模块列表,确认 DLL 已被加载
总结
核心知识点
进程注入的本质
- 跨进程执行代码的技术
- 借用目标进程的执行环境
远程线程注入的五个步骤
- OpenProcess → VirtualAllocEx → WriteProcessMemory → GetProcAddress → CreateRemoteThread
关键 API
OpenProcess:获取进程访问权限VirtualAllocEx:分配远程内存WriteProcessMemory:跨进程写入数据GetProcAddress:获取函数地址CreateRemoteThread:创建远程线程
核心原理
kernel32.dll在所有进程中地址相同- 线程从指定地址开始执行,参数通过寄存器传递
互动讨论
欢迎在评论区讨论以下问题:
- 你在实际应用中遇到过哪些进程注入的场景(合法或非法)?
- 除了 DLL 注入,还能注入什么类型的代码?
- 如果你是防御者,你会如何检测这种注入?
⚠️ 本课程内容仅供防御性安全研究与教学使用,请勿用于非法用途。




