先上源码

#include "Inject_Main.h"
#include "resource.h"
#include <Windows.h>
#include <TlHelp32.h>
#include <string>
#include <TCHAR.H> using namespace std; /// <summary>
/// 通过进程名称获取该进程句柄
/// </summary>
/// <param name="processName"></param>
/// <returns>成功返回 DWORD,失败返回 0</returns>
DWORD GetProcessByName(CONST TCHAR* processName) {
// 获取到整个系统的进程
HANDLE processALL = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL); // 定义一个容器,该容器用来接收,进程信息
PROCESSENTRY32W processInfo = { 0 };
processInfo.dwSize = sizeof(PROCESSENTRY32W); // 根据进程名称,循环判断是否是指定的进程
do
{
if (_tcscmp(processInfo.szExeFile, processName) == 0)
{
// 释放进程快照,防止内存泄露
CloseHandle(processALL);
// 如果是返回指定进程句柄
return processInfo.th32ProcessID;
}
// 一个迭代函数
} while (Process32Next(processALL, &processInfo));
// 释放进程快照,防止内存泄露
CloseHandle(processALL);
return 0;
} /// <summary>
/// 获取指定 DLL 的内存地址
/// </summary>
/// <param name="pid"></param>
/// <param name="moduleName"></param>
/// <returns></returns>
HMODULE GetProcessModuleHandle(DWORD pid, CONST TCHAR* moduleName) {
MODULEENTRY32 moduleEntry;
HANDLE handle = NULL;
handle = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, pid);
if (!handle) {
CloseHandle(handle);
return NULL;
}
ZeroMemory(&moduleEntry, sizeof(MODULEENTRY32));
moduleEntry.dwSize = sizeof(MODULEENTRY32);
if (!Module32First(handle, &moduleEntry)) {
CloseHandle(handle);
return NULL;
}
do {
if (_tcscmp(moduleEntry.szModule, moduleName) == 0) {
// 释放进程快照,防止内存泄露
CloseHandle(handle);
return moduleEntry.hModule;
}
} while (Module32Next(handle, &moduleEntry));
CloseHandle(handle);
return 0;
} /// <summary>
/// 把指定DLL注入到指定进程中
/// </summary>
/// <param name="processName">processName 进程名称</param>
/// <param name="dllPath">dllPath dll路径</param>
void InjectDll(const wchar_t* processName, const char* dllPath) {
// 获取指定进程的句柄
DWORD dword = GetProcessByName(processName);
if (dword == 0)
{
MessageBox(NULL, TEXT("没有找到指定进程"), TEXT("错误"), 0);
return;
}
// 打开指定进程
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dword);
if (hProcess == NULL)
{
MessageBox(NULL, TEXT("指定进程打开失败"), TEXT("错误"), 0);
return;
}
/*
在指定进程的地址,开辟一块内存空间,用来保存 DLL的路径信息
LPVOID VirtualAllocEx(
[in] HANDLE hProcess, 在那个进程中开辟内存
[in, optional] LPVOID lpAddress, 开辟内存的起始地址 (NULL,不需要控制起始位置)
[in] SIZE_T dwSize, 开辟内存的大小(当前保存的内容是 DLL的路径)
[in] DWORD flAllocationType, 内存分配的类型。(开辟内存)
[in] DWORD flProtect,设置内存的权限 (可读可写)
);
*/
LPVOID DLLAddress = VirtualAllocEx(hProcess, NULL, strlen(dllPath), MEM_COMMIT, PAGE_READWRITE);
/*
把DLL的路径,写入到刚开辟出来的内存中
BOOL WriteProcessMemory(
[in] HANDLE hProcess, // 指定的进程
[in] LPVOID lpBaseAddress, // DLL路径字符串,写入的基址
[in] LPCVOID lpBuffer, // DLL路径字符串,的指针
[in] SIZE_T nSize, // 需要写入内存的字节长度
[out] SIZE_T *lpNumberOfBytesWritten // [out] 返回一个指针,不需要,NULL
);
*/ if (WriteProcessMemory(hProcess, DLLAddress, dllPath, strlen(dllPath), NULL) == 0)
{
MessageBox(NULL, TEXT("路径写入失败"), TEXT("错误"), 0);
return;
} // 获取 Kernel32.dll 这个模块
HMODULE k32 = GetModuleHandle(TEXT("Kernel32.dll"));
// 在 Kernel32.dll 模块中找到 LoadLibrary 这个函数的内存地址
LPVOID loadADD = GetProcAddress(k32, "LoadLibraryA");
/*
在指定进程中,创建一个线程
并通过这个线程,调用 LoadLibrary 函数
通过 LoadLibrary 函数,把 DLL 载入到目标进程中
HANDLE CreateRemoteThread(
[in] HANDLE hProcess, // 指定进程
[in] LPSECURITY_ATTRIBUTES lpThreadAttributes, // 设置线程安全属性,表示线程是否可以继承,NULL就够了
[in] SIZE_T dwStackSize, // 堆栈的初始大小,0 表示使用可执行文件的默认大小
[in] LPTHREAD_START_ROUTINE lpStartAddress, // 远程进程中,需要执行的那个函数的指针
[in] LPVOID lpParameter, // 目前进程中 DLL路径的指针
[in] DWORD dwCreationFlags, // 0 线程在创建后立即运行。
[out] LPDWORD lpThreadId // [out] 当前不需要这个返回值
);
*/
HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)loadADD, DLLAddress, 0, NULL); // 释放指定的模块
CloseHandle(hThread);
CloseHandle(hProcess); } /// <summary>
/// 把指定进程中的DLL卸载掉
/// </summary>
/// <param name="processName"></param>
/// <param name="dllPath"></param>
void UnInjectDll(const wchar_t* processName) {
// 通过进程名称获取该进程句柄
DWORD dword = GetProcessByName(processName);
if (dword == 0)
{
MessageBox(NULL, TEXT("没有找到指定进程"), TEXT("错误"), 0);
return;
}
// 获取指定进程中指定模块的内存地址
HMODULE hmodule = GetProcessModuleHandle(dword, L"WX_Read_Write.dll"); // 打开指定进程
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dword);
if (hProcess == NULL)
{
MessageBox(NULL, TEXT("指定进程打开失败"), TEXT("错误"), 0);
return;
} // 获取 Kernel32.dll 这个模块
HMODULE k32 = GetModuleHandle(TEXT("Kernel32.dll"));
// 在 Kernel32.dll 模块中找到 LoadLibrary 这个函数的内存地址
LPVOID loadADD = GetProcAddress(k32, "FreeLibrary"); HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)loadADD, (LPVOID)hmodule, 0, NULL); // 释放指定的模块
CloseHandle(hThread);
CloseHandle(hProcess);
} /// <summary>
///
/// </summary>
/// <param name="hwndDlg"></param>
/// <param name="uMsg"></param>
/// <param name="wParam"></param>
/// <param name="lParam"></param>
/// <returns></returns>
INT_PTR CALLBACK dialogProc(_In_ HWND hwndDlg, _In_ UINT uMsg, _In_ WPARAM wParam, _In_ LPARAM lParam)
{
wchar_t processName[100] = L"WeChat.exe";
char dllPath[400] = { "C://Users//qiaoas//documents//visual studio 2015//Projects//ConsoleApplication1//Debug//WX_Read_Write.dll" };
switch (uMsg)
{
case WM_INITDIALOG:
break;
case WM_CLOSE:
EndDialog(hwndDlg, 0); // 关闭窗体
break;
case WM_COMMAND:
/*GetDlgItemText(hwndDlg, Text_ProcessName, processName, sizeof(processName));
GetDlgItemText(hwndDlg, Text_DLLPath, (LPWSTR)dllPath, sizeof(dllPath));*/ if (wParam == Btn_Inject_DLL)
{
if (sizeof(processName) == 0)
{
MessageBox(NULL, TEXT("进程名称不能为空"), TEXT("错误"), 0);
}
if (sizeof(dllPath) == 0)
{
MessageBox(NULL, TEXT("DLL路径不能为空"), TEXT("错误"), 0);
}
InjectDll(processName, dllPath); // 注入DLL
}
if (wParam == Btn_unInject_DLL)
{
UnInjectDll(processName); // 卸载DLL
}
break;
default:
break;
}
return FALSE;
} /// <summary>
/// 初始化
/// </summary>
/// <param name="hInstance"></param>
/// <param name="hPrevInstance"></param>
/// <param name="lpCmdLine"></param>
/// <param name="nCmdShow"></param>
/// <returns></returns>
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
DialogBox(hInstance, MAKEINTRESOURCE(IDD_DIALOG1), NULL, &dialogProc);
return 0;
}

初学C++,代码可能有些地方写的不够好,但是注入卸载是完全没问题的。

注入逻辑解释:

使用 CreateRemoteThread 函数可以为目标进程创建一个新的线程。

在一个进程为另一个进程创建的线程就是远程线程。

使用 LoadLibrary 函数把指定的DLL加载到进程中

因此就可以在创建远程线程的同时调用 LoadLibrary 函数,把指定的DLL加载到目标进程中。

为什么创建远程线程的时候调用 LoadLibrary 函数就能把 DLL 注入到目标进程中

  LoadLibrary 函数是 Kernel32.dll 中的一个成员

  Kernel32.dll 这个DLL是创建进程必须的一个DLL,并且所有进程在内存中指向的 Kernel32.dll 是同一个地址

  所以只要获取到当前进程中 LoadLibrary 函数的地址就够了

为什么要在目标进程中开辟一块内存,再把DLL路径写入到块内存中

  LoadLibrary 函数需要一个参数,就是DLL的路径

  把当前进程中的一个地址传到另一个进程中,鬼知道另一个进程获取这个地址中的数据时,读取到的是否是我们想要的。

  因此需要把DLL的路径直接写入到目标进程中。

VirtualAllocEx 函数,在目标进程中开辟一块空间,用来存放DLL路径
WriteProcessMemory 函数,把DLL的路径写入进去 GetModuleHandle 获取 Kernel32.dll 模块
GetProcAddress 获取 LoadLibraryA 函数在内存中的地址

CreateRemoteThread 创建远程线程,并调用 LoadLibraryA 函数

LoadLibrary 、LoadLibraryA、LoadLibraryW 这三者的区别。

LoadLibrary 是一个宏,可以根据字符集的不同,自动决定是使用 LoadLibraryA 还是 LoadLibraryW

LoadLibrary 宏定义的源码
WINBASEAPI
_Ret_maybenull_
HMODULE
WINAPI
LoadLibraryA(
_In_ LPCSTR lpLibFileName
); WINBASEAPI
_Ret_maybenull_
HMODULE
WINAPI
LoadLibraryW(
_In_ LPCWSTR lpLibFileName
); #ifdef UNICODE
#define LoadLibrary LoadLibraryW
#else
#define LoadLibrary LoadLibraryA
#endif // !UNICODE

卸载逻辑:

使用 CreateRemoteThread 函数创建一个远程线程

调用 FreeLibrary 函数,卸载DLL

FreeLibrary 函数在 Kernel32.dll 模块中,逻辑同上

FreeLibrary 函数需要 DLL 的内存地址

遍历进程快照可以获取到指定模块的内存地址

卸载和注入的思路都是一样的

确认DLL是否注入到目标进程中

方式一:使用 procexp

方式二:Cheat Engine

确认 Kernel32.dll 中的 FreeLibrary 和 LoadLibraryA 在多个进程中是否指向同一块内存地址:

可以通过CE查看多个进程中  Kernel32.dll 的内存地址是否相同

再通过  Kernel32.dll 中函数的内存地址,确认 FreeLibrary 和 LoadLibraryA 这两个函数

C++ DLL注入工具完整源码的更多相关文章

  1. ASP.NET CORE小试牛刀:干货(完整源码)

    扯淡 .NET Core 的推出让开发者欣喜万分,从封闭到拥抱开源十分振奋人心.对跨平台的支持,也让咱.NET开发者体验了一把 Write once,run any where 的感觉!近期离职后,时 ...

  2. 微信小程序中如何使用WebSocket实现长连接(含完整源码)

    本文由腾讯云技术团队原创,感谢作者的分享. 1.前言   微信小程序提供了一套在微信上运行小程序的解决方案,有比较完整的框架.组件以及 API,在这个平台上面的想象空间很大.腾讯云研究了一番之后,发现 ...

  3. 3D游戏《龙心传奇Dragona》全套完整源码

    <龙心传奇Dragona>全套完整源码,<Dragona Online>也叫<龙心传奇>是一款3D中古奇幻风格网络游戏,描写叙述一个充满剑.魔法.恶魔和龙的魔法大陆 ...

  4. 适合新手:从零开发一个IM服务端(基于Netty,有完整源码)

    本文由“yuanrw”分享,博客:juejin.im/user/5cefab8451882510eb758606,收录时内容有改动和修订. 0.引言 站长提示:本文适合IM新手阅读,但最好有一定的网络 ...

  5. teprunner测试平台Django引入pytest完整源码

    本文开发内容 pytest登场!本文将在Django中引入pytest,原理是先执行tep startproject命令创建pytest项目文件,然后从数据库中拉取代码写入文件,最后调用pytest命 ...

  6. 一篇文章看懂TPCx-BB(大数据基准测试工具)源码

    TPCx-BB是大数据基准测试工具,它通过模拟零售商的30个应用场景,执行30个查询来衡量基于Hadoop的大数据系统的包括硬件和软件的性能.其中一些场景还用到了机器学习算法(聚类.线性回归等).为了 ...

  7. 可视化工具gephi源码探秘(二)---导入netbeans

    在上篇<可视化工具gephi源码探秘(一)>中主要介绍了如何将gephi的源码导入myeclipse中遇到的一些问题,此篇接着上篇而来,主要讲解当下通过myeclipse导入gephi源码 ...

  8. 网狐6603 cocos2dx 棋牌、捕鱼、休闲类游戏《李逵捕鱼》手机端完整源码分析及分享

    该资源说明: cocos2d 棋牌.捕鱼.休闲类游戏<李逵捕鱼>手机端完整源码,网狐6603配套手机版源码,可以选桌子,适合新手学习参考,小编已亲测试,绝对完整可编译手机端,下载后将文件考 ...

  9. 在WebBrowser中执行javascript脚本的几种方法整理(execScript/InvokeScript/NavigateScript) 附完整源码

    [实例简介] 涵盖了几种常用的 webBrowser执行javascript的方法,详见示例截图以及代码 [实例截图] [核心代码] execScript方式: 1 2 3 4 5 6 7 8 9 1 ...

随机推荐

  1. 【LeetCode】224. Basic Calculator 解题报告(Python)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 栈 参考资料 日期 题目地址:https://lee ...

  2. A1. 道路修建 Small(BNUOJ)

    A1. 道路修建 Small Time Limit: 1000ms Memory Limit: 131072KB 64-bit integer IO format: %lld      Java cl ...

  3. 论文翻译:2020_ACOUSTIC ECHO CANCELLATION WITH THE DUAL-SIGNAL TRANSFORMATION LSTM NETWORK

    论文地址:https://ieeexplore.ieee.org/abstract/document/9413510 基于双信号变换LSTM网络的回声消除 摘要 本文将双信号变换LSTM网络(DTLN ...

  4. Android物联网应用程序开发(智慧城市)—— 查询购物信息界面开发

    效果: 布局代码: <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xm ...

  5. 云南农职《JavaScript交互式网页设计》 综合机试试卷①——实现购物车的结算

    一.语言和环境 实现语言:javascript.html.css. 开发环境:HBuilder. 二.题目2(100分) 1.功能需求: 马上过节了,电商网站要进行促销活动,需要实现该商城购物车的商品 ...

  6. 制作四个选项卡页 Tab,用户可以通过切换不同的 Tab 页查看不同类别的新闻信息,每个 Tab 有对应的内容版块,点击某个选项卡时,显示对应的内容版块,隐藏其他内容版块,并且为了突出当前的选项卡,还

    查看本章节 查看作业目录 需求说明: 制作四个选项卡页 Tab,用户可以通过切换不同的 Tab 页查看不同类别的新闻信息,每个 Tab 有对应的内容版块,点击某个选项卡时,显示对应的内容版块,隐藏其他 ...

  7. 制作登录页面,点击键盘的 Enter 键或者单击“登录”按钮,验证用户输入的邮箱和密码是否正确

    查看本章节 查看作业目录 需求说明: 制作登录页面 点击键盘的 Enter 键或者单击"登录"按钮,验证用户输入的邮箱和密码是否正确 实现思路: 准备登录的静态页面 在页面中嵌入脚 ...

  8. VMware客户端vSphere Web Client新建虚拟机

    1.说明 vSphere Web Client是为管理员提供的一款通用的. 基于浏览器的VMware管理工具, 能够监控并管理VMware基础设施. 由于需要登录的宿主机安装的是ESXi-6.5.0, ...

  9. Ranger-Sqoop2插件实现详解

    1.组件和插件介绍 1.1.Ranger介绍 Apache Ranger能够监控和管理整个Hadoop平台的综合数据安全, 目前作为Apache Top Level Project(TLP顶级项目), ...

  10. JZOJ5966. [NOIP2018TGD2T3] 保卫王国 (动态DP做法)

    题目大意 这还不是人尽皆知? 有一棵树, 每个节点放军队的代价是\(a_i\), 一条边连接的两个点至少有一个要放军队, 还有\(q\)次询问, 每次规定其中的两个一定需要/不可放置军队, 问这样修改 ...