C++ DLL注入原理以及示例

0、 前言

0.1 什么是DLL注入

        DLL(动态链接库)注入是一种技术,通过将外部的 DLL 文件强行加载到目标进程的地址空间中,使得外部代码可以执行。这种技术常用于修改或扩展应用程序的行为,甚至用于恶意攻击。

0.2 DLL注入的应用场景

通过DLL注入可以改变程序原来的一些行为,比如安全方面可以阻止某些程序打开,游戏方面通过外挂进行游戏作弊等。通常应用在以下场景:

0.2.1. 调试和逆向工程

  • 逆向工程:开发者可以使用 DLL 注入来注入自定义的 DLL,修改目标程序的行为。这常用于破解软件、分析恶意软件行为等。
  • 调试工具:调试人员可以利用 DLL 注入在目标应用程序中添加日志记录、修改代码执行流程等,帮助分析和调试。

0.2.2 性能监控和分析

  • 一些性能分析工具(如性能监视器、反病毒软件等)通过 DLL 注入来捕获目标进程的运行时数据,记录函数调用、内存分配等,从而提供有关程序性能的反馈。

0.2.3. 功能扩展和修改

  • 功能扩展:一些第三方工具或游戏作弊程序利用 DLL 注入技术来修改应用程序的行为。例如,修改游戏中的某些参数、提高程序的功能、或者添加新功能。
  • 破解保护机制:一些商业软件使用 DLL 注入来绕过版权保护和反作弊系统。

0.2.4. 恶意软件(病毒、木马)

  • 恶意软件和病毒经常利用 DLL 注入来获取对目标程序的控制,执行不需要用户授权的恶意行为,如窃取密码、监视用户输入、截获敏感信息等。

0.2.5. 游戏作弊

  • 游戏作弊程序常通过 DLL 注入修改游戏中的行为,例如修改玩家的血量、金钱,或者直接修改游戏逻辑,以达到不正当的游戏优势。

1、DLL注入的基本原理和过程

1.1 基本原理

        DLL 注入的核心原理是让目标进程加载外部的 DLL 文件,通常通过一些系统函数来实现。注入后的 DLL 会被执行,其中的代码可以影响目标进程的行为。

        我们编写程序时可以通过LoadLibrary这个函数来加载DLL,DLL注入也是基于此。想要在目标进程中强制加载待注入的DLL,核心就是在目标进程中调用LoadLibrary来加载待注入的DLL。

1.2 基本步骤

  1. 获取目标进程的进程 ID:首先,我们需要知道目标进程的进程 ID(PID)。这可以通过查找目标进程的名称或直接从操作系统的进程列表中获得。

  2. 打开目标进程:使用 Windows API 中的 OpenProcess 函数以适当的权限打开目标进程,通常需要 PROCESS_ALL_ACCESS 权限。

  3. 分配内存:使用 VirtualAllocEx 在目标进程的地址空间中分配一块内存空间来存放 DLL 的路径。

  4. 写入 DLL 路径:使用 WriteProcessMemory 将 DLL 的文件路径写入到目标进程的内存中。

  5. 获取并调用 LoadLibrary 地址:通过 GetProcAddress 获取 LoadLibraryALoadLibraryW 函数的地址,LoadLibrary 是用来加载 DLL 的标准函数。

  6. 创建远程线程:通过 CreateRemoteThread 在目标进程中创建一个线程来执行 LoadLibrary,并传入之前写入的 DLL 路径。这样,目标进程就会加载外部 DLL。

  7. DLL 被加载并执行:一旦目标进程加载了 DLL,DLL 中的代码就会执行,从而实现对目标进程行为的修改或扩展。

2、DLL注入示例

        要将DLL注入到目标进程,需要完成两件事:

  • 编写待注入的DLL
  • 编写注入程序

2.1 编写待注入DLL

        编写待注入的DLL与实际需要完成的功能有关,这里只是为了演示注入DLL过程,编写一个最简单的DLL,被注入到进程时,弹出一个messagebox。

通过visual studio创建一个DLL项目,代码内容如下:

代码内容非常简单,就是待注入DLL被目标进程加载时,会有一个messagebox弹窗提时。

// 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:MessageBoxW(NULL,L"有DLL注入!",L"注入 提示",MB_OK | MB_ICONINFORMATION);case DLL_THREAD_ATTACH:case DLL_THREAD_DETACH:case DLL_PROCESS_DETACH:break;}return TRUE;
}

2.2 编写注入程序

要将待注入DLL注入的目标进程,需要按照1.2中的过程进行编码,完成每一个步骤即可。

注入程序的所有代码如下:

以注入到windows记事本程序(notepad.exe)为例,先通过FindNotepadPID()函数查找notepad进程的PID,然后进行注入。

需要注意的时,程序中待注入的DLL路径需要替换为自己实际的DLL路径。

// InjectDllMain.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//#include <windows.h>
#include <tlhelp32.h>
#include <iostream>DWORD FindNotepadPID() {HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);if (snapshot == INVALID_HANDLE_VALUE) return 0;PROCESSENTRY32 pe = { sizeof(PROCESSENTRY32) };DWORD pid = 0;if (Process32First(snapshot, &pe)) {do {if (_wcsicmp(pe.szExeFile, L"notepad.exe") == 0) {pid = pe.th32ProcessID;break;}} while (Process32Next(snapshot, &pe));}CloseHandle(snapshot);return pid;
}bool InjectDLL(DWORD pid, const char* dllPath) {HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);if (!hProcess) return false;LPVOID pRemoteMem = VirtualAllocEx(hProcess, NULL, strlen(dllPath) + 1, MEM_COMMIT, PAGE_READWRITE);if (!pRemoteMem) {CloseHandle(hProcess);return false;}WriteProcessMemory(hProcess, pRemoteMem, dllPath, strlen(dllPath) + 1, NULL);HMODULE hKernel32 = GetModuleHandleA("Kernel32");LPTHREAD_START_ROUTINE loadLib = (LPTHREAD_START_ROUTINE)GetProcAddress(hKernel32, "LoadLibraryA");HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0, loadLib, pRemoteMem, 0, NULL);if (!hThread) {VirtualFreeEx(hProcess, pRemoteMem, 0, MEM_RELEASE);CloseHandle(hProcess);return false;}WaitForSingleObject(hThread, INFINITE);VirtualFreeEx(hProcess, pRemoteMem, 0, MEM_RELEASE);CloseHandle(hThread);CloseHandle(hProcess);return true;
}int main() {DWORD pid = FindNotepadPID();if (!pid) {std::cerr << "Notepad not running!" << std::endl;return 1;}char dllPath[MAX_PATH];GetCurrentDirectoryA(MAX_PATH, dllPath);strcat_s(dllPath, "\\InjectDll.dll");// 这里需要替换为实际的DLL路径if (InjectDLL(pid, "C:\\InjectMessagebox\\x64\\Release\\InjectMessagebox.dll")) {std::cout << "Injection successful!" << std::endl;}else {std::cerr << "Injection failed!" << std::endl;}return 0;
}

2.3 演示效果

编译完待注入DLL和注入程序之后,打开windows记事本,然后运行注入程序,演示效果如下:

2.4 结合程序的一些原理分析

        注入程序中,最核心的核心函数是 CreateRemoteThread。这个函数干了两件事:

  • 在目标进程中创建一个新线程
  • 新线程调用LoardLibrary函数去加载待注入的DLL

问题1:为什么要创建一个新的线程来调用LoardLibrary函数,而不是直接调用LoardLibrary?

        我个人理解是为了不影响目标进程原来的一些行为和功能。

问题2:CreateRemoteThread中参数为什么要传入pRemoteMem,而不是直接传入dllPath?

        首先来看一下微软官方文档对这个函数CreateRemoteThread参数的解释:

HANDLE CreateRemoteThread(
  [in]  HANDLE                 hProcess,
  [in]  LPSECURITY_ATTRIBUTES  lpThreadAttributes,
  [in]  SIZE_T                 dwStackSize,
  [in]  LPTHREAD_START_ROUTINE lpStartAddress,
  [in]  LPVOID                 lpParameter,
  [in]  DWORD                  dwCreationFlags,
  [out] LPDWORD                lpThreadId
);

[in] hProcess

要在其中创建线程的进程句柄。 句柄必须具有 PROCESS_CREATE_THREADPROCESS_QUERY_INFORMATIONPROCESS_VM_OPERATIONPROCESS_VM_WRITE和 PROCESS_VM_READ 访问权限,并且在某些平台上没有这些权限可能会失败。 有关详细信息,请参阅 进程安全性和访问权限。

[in] lpThreadAttributes

指向 SECURITY_ATTRIBUTES 结构的指针,该结构指定新线程的安全描述符,并确定子进程是否可以继承返回的句柄。 如果 lpThreadAttributes 为 NULL,则线程将获取默认的安全描述符,并且无法继承句柄。 线程的默认安全描述符中的访问控制列表(ACL)来自创建者的主要令牌。

Windows XP:线程的默认安全描述符中的 ACL 来自创建者的主令牌或模拟令牌。 此行为随 SP2 和 Windows Server 2003 的 Windows XP 发生更改。

[in] dwStackSize

堆栈的初始大小(以字节为单位)。 系统将此值舍入到最近的页面。 如果此参数为 0(零),则新线程使用可执行文件的默认大小。 有关详细信息,请参阅 线程堆栈大小。

[in] lpStartAddress

指向由线程执行的 LPTHREAD_START_ROUTINE 类型的应用程序定义的函数的指针,表示远程进程中线程的起始地址。 函数必须存在于远程进程中。 有关详细信息,请参阅 ThreadProc。

[in] lpParameter

指向要传递给线程函数的变量的指针。

[in] dwCreationFlags

控制线程创建的标志。

【参考链接】

CreateRemoteThread 函数 (processthreadsapi.h) - Win32 apps | Microsoft Learn

我们重点关注 lpStartAddress以及lpParameter 这两个参数, lpStartAddress是线程要调用的函数(即LoadLibraryA)的起始地址,lpParameter是调用函数的参数(即待加载的库的路径)。

而dllPath不就是动态库的路径吗?为什么参数是pRemoteMem?

这是因为dllPath是动态库的路径,只是在注入程序中(即InjectDllMain),而目标程序(notepad)根本不认识dllPath。我们的目的是要目标进程调用LoadLibraryA, 所以我们需要在目标进程中开辟一块内存空间pRemoteMem,在这块内存空间写入要注入的DLL路径。

问题3:怎么知道目标进程中LoadLibraryA?

        注意到,我们是在注入程序中或取的LoadLibraryA的地址,但是实际上我们应该获取目标进程中LoadLibraryA的地址才对。

LoadLibraryA这个函数是在Kernel32.dll这个核心DLL里的,而这个DLL很特殊,不管对于哪个进程,Windows总是把它加载到相同的地址上去。因此你的进程中LoadLibraryA的地址和目标进程中LoadLibraryA的地址是相同的(其实,这个DLL里的所有函数都是如此)。

【参考文章】

DLL注入

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/7879.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MATLAB绘图:随机彩色圆点图

这段代码在MATLAB中生成并绘制了500个随机位置和颜色的散点图。通过随机生成的x和y坐标以及颜色&#xff0c;用户可以直观地观察到随机点的分布。这种可视化方式在数据分析、统计学和随机过程的演示中具有广泛的应用。 文章目录 运行结果代码代码讲解 运行结果 代码 clc; clea…

关于使用PHP时WordPress排错——“这意味着您在wp-config.php文件中指定的用户名和密码信息不正确”的解决办法

本来是看到一位好友的自己建站&#xff0c;所以突发奇想&#xff0c;在本地装个WordPress玩玩吧&#xff0c;就尝试着装了一下&#xff0c;因为之前电脑上就有MySQL&#xff0c;所以在自己使用PHP建立MySQL时报错了。 最开始是我的php启动mysql时有问题&#xff0c;也就是启动过…

RabbitMQ 架构分析

文章目录 前言一、RabbitMQ架构分析1、Broker2、Vhost3、Producer4、Messages5、Connections6、Channel7、Exchange7、Queue8、Consumer 二、消息路由机制1、Direct Exchange2、Topic Exchange3、Fanout Exchange4、Headers Exchange5、notice5.1、备用交换机&#xff08;Alter…

【Uniapp-Vue3】setTabBar设置TabBar和下拉刷新API

一、setTabBar设置 uni.setTabBarItem({ index:"需要修改第几个", text:"修改后的文字内容" }) 二、tabBar的隐藏和显式 // 隐藏tabBar uni.hideTabBar(); // 显示tabBar uni.showTabBar(); 三、为tabBar右上角添加文本 uni.setTabBarBadge({ index:"…

routeros7 adguardhome添加规则报错certificate expired

mikrokit routeros 7添加adguardhome容器。 /container/add remote-imageadguard/adguardhome:latest interfaceveth1 root-dircontainer/adgurdhome loggingyes结果发现添加不了规则&#xff0c;报证书过期。 Error: control/filtering/add_url | Couldn’t fetch filter fro…

壁纸设计过程中如何增加氛围感

在壁纸设计过程中&#xff0c;增加氛围感是提升整体视觉效果和情感传达的关键。以下是一些具体的方法和技巧&#xff0c;帮助你在设计中营造出强烈的氛围感&#xff1a; 一、色彩运用 选择主题色&#xff1a; 根据你想要传达的情感选择主色调。例如&#xff0c;温暖的色调&…

RabbitMQ模块新增消息转换器

文章目录 1.目录结构2.代码1.pom.xml 排除logging2.RabbitMQConfig.java3.RabbitMQAutoConfiguration.java 1.目录结构 2.代码 1.pom.xml 排除logging <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/PO…

2024年度总结:技术探索与个人成长的交织

文章目录 前言年度创作回顾&#xff1a;技术深耕与分享数据库技术&#xff1a;MySQL 与 MyBatisJava 及相关技术栈计算机网络&#xff1a;构建网络知识体系思维方式的转变&#xff1a;构建技术知识体系的桥梁 项目实践&#xff1a;人工智能与智慧医疗的碰撞生活与博客的融合与平…

python:taichi 模拟一维波场

在 Taichi 中模拟一维波场&#xff0c;通常是利用 Taichi 编程语言的特性来对一维空间中的波动现象进行数值模拟&#xff0c;以下是相关介绍&#xff1a; 原理基础 波动方程&#xff1a;一维波动方程的一般形式为 &#xff0c;其中 u(x,t) 表示在位置x 和时间t 处的波的状态&…

基于回归分析法的光伏发电系统最大功率计算simulink建模与仿真

目录 1.课题概述 2.系统仿真结果 3.核心程序与模型 4.系统原理简介 5.完整工程文件 1.课题概述 基于回归分析法的光伏发电系统最大功率计算simulink建模与仿真。选择回归法进行最大功率点的追踪&#xff0c;使用光强和温度作为影响因素&#xff0c;电压作为输出进行建模。…

深入MapReduce——引入

引入 前面我们已经深入了HDFS的设计与实现&#xff0c;对于分布式系统也有了不错的理解。 但HDFS仅仅解决了海量数据存储和读写的问题。要想让数据产生价值&#xff0c;一定是需要从数据中挖掘出价值才行&#xff0c;这就需要我们拥有海量数据的计算处理能力。 下面我们还是…

Vue 引入及简单示例

Vue 渐进式JavaScript 框架 学习笔记 - Vue 引入及简单示例 目录 与jquery区别 Vue引入 两种方式引入 下载到本地 代码结构 简单示例 Style中引入vue.js 对vue语法进行解析 对三目运算符支持 设置变量&#xff08;状态&#xff09; 总结 与jquery区别 不需要手动操…

系统思考—问题分析

很多中小企业都在面对转型的难题&#xff1a;市场变化快&#xff0c;资源有限&#xff0c;团队协作不畅……这些问题似乎总是困扰着我们。就像最近和一位企业主交流时&#xff0c;他提到&#xff1a;“我们团队每天都很忙&#xff0c;但效率始终没见提升&#xff0c;感觉像是在…

π0:仅有3B数据模型打通Franka等7种机器人形态适配,实现0样本的完全由模型自主控制方法

Chelsea Finn引领的Physical Intelligence公司&#xff0c;专注于打造先进的机器人大模型&#xff0c;近日迎来了一个令人振奋的里程碑。在短短不到一年的时间内&#xff0c;该公司成功推出了他们的首个演示版本。这一成就不仅展示了团队的卓越技术实力&#xff0c;也预示着机器…

第二十一周:Mask R-CNN

Mask R-CNN 摘要Abstract文章信息研究动机Mask RCNNRoIPool与RoIAlign 双线性插值Mask Branch(FCN)其他细节Mask RCNN损失Mask分支预测 网络搭建创新点与不足总结 摘要 本篇博客介绍了Mask R-CNN&#xff0c;这是一种用于实例分割的模型&#xff0c;能够在目标检测的基础上实现…

Windows本地部署(DeepSeek-R1-Distill-Qwen-1.5B)模型

文章目录 Windows本地部署&#xff08;DeepSeek-R1-Distill-Qwen-1.5B&#xff09;模型本机环境运行环境安装安装 WSL2&#xff0c;启用linux 系统进入linux 系统后&#xff0c;安装以下软件安装 Anaconda3安装 CUDA安装 pip创建虚拟环境并安装 vllm 模型下载模型运行部署模型测…

Java 大视界 -- Java 大数据在元宇宙中的关键技术与应用场景(65)

&#x1f496;亲爱的朋友们&#xff0c;热烈欢迎来到 青云交的博客&#xff01;能与诸位在此相逢&#xff0c;我倍感荣幸。在这飞速更迭的时代&#xff0c;我们都渴望一方心灵净土&#xff0c;而 我的博客 正是这样温暖的所在。这里为你呈上趣味与实用兼具的知识&#xff0c;也…

【QT】 控件 -- 显示类

&#x1f525; 目录 [TOC]( &#x1f525; 目录) 1. 前言 2. 显示类控件2.1 Label 1、显示不同文本2、显示图片3、文本对齐、自动换行、缩进、边距4、设置伙伴 3.2 LCD Number 3.3 ProgressBar 3.4 Calendar Widget 3. 共勉 &#x1f525; 1. 前言 之前我在上一篇文章【QT】…

大数据之路:阿里巴巴大数据实践(1)

第一章 总述 第二章 日志采集 2.1 浏览器的页面日志采集 览器的页面型产品/服务的日志采集可分为如下两大类 &#xff08;1&#xff09;页面浏览&#xff08;展现&#xff09;日志采集。顾名思义&#xff0c;页面浏览日志是指&#xff1a;一个页面被浏览器加载呈现时采集的日…

定时器按键tim_key模版

低优先级放在高优先级内势必是程序卡死 把高优先级放到低优先级内&#xff0c;会使程序卡死 可修改 Debuger调试方法 Pwm rcc #include "my_main.h" uint8_t led_sta0x10; char text[30]; void LED_Disp(uint8_t dsLED) {HAL_GPIO_WritePin(GPIOC,GPIO_PIN_All,GPI…