来源: (1条消息)微信Hook实战记录2:动手实现恶意dll内存插入器_C/C++_github_39463644的博客-CSDN博客
Hook任何软件,整体思路都是通过内存调试软件对软件运行时内存进行断点调试,找到想要hook的内存地址,转为可以通过程序主dll可以获得的相对地址,然后再此处插入自己的恶意汇编代码,如果代码比较复杂,还需要编写寄存器保护逻辑,避免自己的恶意代码修改了寄存器中的值后,程序正常的逻辑无法运行。
前言
在上一篇文章「微信Hook实战记录 1:找到个人信息」中,介绍了如何使用OD与CE来找到微信中存放个人信息的内存位置,本篇文章尝试通过C++编写一个简单的内存注入工具,将我们自己的恶意dll注入到微信的内存中。
本文记录了大量细节,完全可以模仿复现相同的效果。
内存注入工具的编写
打开 Visual Studio 2019(下载的时候勾选 C++ 桌面支持,6.9G左右),选择创建新项目
选择创建 Windows 桌面向导
然后创建就可以了,这里创面项目名为 Project1
创建完后,VS 会为我们创建一个默认的界面,我们在 解决方案 -> 源文件 中找到 Project1.cpp,这个文件就是我们要写逻辑的主要界面
将其中多余的代码删除,就留下如下代码
// Project1.cpp : 定义应用程序的入口点。
//
#include “framework.h”
#include “Project1.h”
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
wWinMain 是界面程序的入口,所有不必删除
接着将 解决方案 –> 资源文件 中的内容删除,这是 VS 默认创建好的,删除后,自己新建一个资源文件
选择 Dialog,即弹出框
调整一下创建出的按钮位置以及整体大小
要设置按钮与整体的属性,如按钮要设置其内容与ID,而整体Dialog也要设置一下其ID,可以右键点击 –> 属性
其他控制以相同的方式去设置,设置后后,就可以写代码了
首先修改一下 Project1.h 中的内容,如下
#pragma once
#include “resource1.h”
1
2
3
具体内容要看你VS为你生成了什么,前面我们通过图像界面操作后,VS会生成的相应的头文件,这里我生成了 resource1.h 的头文件,其内容如下:
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ 生成的包含文件。
// 供 Project1.rc 使用
//
#define ID_MAIN 101
#define UN_DLL 1001
#define INJECT_DLL 1002
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 103
#define _APS_NEXT_COMMAND_VALUE 40001
#define _APS_NEXT_CONTROL_VALUE 1003
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif
#endif
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
可以看出,其实就是我们设置的ID等内容
接着来修改一下 Project1.cpp, 其 wWinMain 方法修改如下:
#include “framework.h”
#include “Project1.h”
#include <TlHelp32.h>
#include <stdio.h>
// 微信进程名
#define WECHAT_PROCESS_NAME “WeChat.exe”
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
// hInstance 将句柄传递给弹窗
// 传入 Dialgo 主体ID
// DialogProc 回调函数,是一个指针,即该函数的地址
DialogBox(hInstance, MAKEINTRESOURCE(ID_MAIN), NULL, &DialogProc);
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
因为创建的资源是 Dialog ,所以这里通过 DialogBox 来使用它,它在 Windows.h 的头文件中,如果你使用的 VS 2019,其 framework.h 文件就包含Windows.h了。
DialogBox的参数可以通过微软的文档来查看
void DialogBoxA(
hInstance,
lpTemplate,
hWndParent,
lpDialogFunc
);
1
2
3
4
5
6
hInstance,类型:HINSTANCE,包含对话框模板的模块句柄。如果此参数为 NULL,则使用当前可执行文件。
lpTemplate,类型:LPCTSTR,对话框模板。可以使用MAKEINTRESOURCE宏来创建此值。
hWndParent,类型:HWND,拥有该对话框的窗口的句柄。
lpDialogFunc,类型:DLGPROC,指向对话框过程的指针。
根据文档提示,完成代码
#include “framework.h”
#include “Project1.h”
#include <TlHelp32.h>
#include <stdio.h>
#define WECHAT_PROCESS_NAME “WeChat.exe”
// 注册函数,方便其他模块使用
INT_PTR CALLBACK DialogProc(_In_ HWND hwndDlg, _In_ UINT uMsg, _In_ WPARAM wParam, _In_ LPARAM lParam);
VOID InjectDll();
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
DialogBox(hInstance, MAKEINTRESOURCE(ID_MAIN), NULL, &DialogProc);
return 0;
}
INT_PTR CALLBACK DialogProc(_In_ HWND hwndDlg, _In_ UINT uMsg, _In_ WPARAM wParam, _In_ LPARAM lParam)
{
if (uMsg == WM_INITDIALOG) {
MessageBox(NULL, “首次加载”, “标题”, 0);
}
// 关闭界面操作
if (uMsg == WM_CLOSE) {
EndDialog(hwndDlg, 0);
}
// 所有的界面上的按钮事件都会走这个宏
if (uMsg == WM_COMMAND) {
if (wParam == INJECT_DLL) {
InjectDll(); //注入 Dll
}
if (wParam == UN_DLL) {
}
}
return FALSE;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
接着来实现 InjectDll() 方法,完成注入 DLL 的功能,整体分 3 步走。
1.获取微信的PID,通过PID获得微信进程的句柄,从而拥有了权限
2.申请内存,内存大小为 DLL 路径大小
3.将 DLL 路径注入到微信进程中
DWORD ProcessNameFindPID(LPCSTR ProcessName)
{
// #include <TlHelp32.h>
// 获取进程快照
HANDLE ProcessAll = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);// 获取整个系统的进程快照
// 在快照中对比过 processname 进程名称
PROCESSENTRY32 processInfo = { 0 };
processInfo.dwSize = sizeof(PROCESSENTRY32);
do
{
// WeChat.exe
// th32ProcessID 进程ID, szExeFile进程名
if (strcmp(ProcessName, (char*)processInfo.szExeFile) == 0) {
return processInfo.th32ProcessID;
}
} while (Process32Next(ProcessAll, &processInfo));
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
上述方法,需要 windows 平台 C++ 基础。其实主要就是对 Windows.h 中方法的调用,接着完成如下效果
VOID InjectDll()
{
// 自己恶意dll的路径
CHAR pathStr[0x100] = { “D://GetWeChatInfo.dll” };
// 获取微信PID
DWORD PID = ProcessNameFindPID(WECHAT_PROCESS_NAME);
if (PID == 0) {
MessageBox(NULL, “没有找到微信进程或微信没有启动”, “错误”, 0);
return;
}
// 通过PID获取句柄 C++中句柄类型为 HANDLE
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, PID);
if (NULL == hProcess) {
MessageBox(NULL, “进程打开失败,可能权限不足或关闭了微信应用”, “错误”, 0);
return;
}
//申请内存,内存大小为我们要注入 DLL 的大小
LPVOID dllAdd = VirtualAllocEx(hProcess, NULL, sizeof(pathStr), MEM_COMMIT, PAGE_READWRITE);
if (NULL == dllAdd) {
MessageBox(NULL, “内存分配失败”, “错误”, 0);
return;
}
// 将 DLL 写入到对应的内存地址中
if(WriteProcessMemory(hProcess, dllAdd, pathStr, strlen(pathStr), NULL) == 0) {
MessageBox(NULL, “路径写入失败”, “错误”, 0);
return;
}
// 创建一块内存用来打印一段文本,方便看效果
CHAR test[0x100] = { 0 };
sprintf_s(test, “写入的地址为:%p”, dllAdd);
OutputDebugString(test);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
完成后,直接编译运行,如果编译后的效果依旧是之前代码的效果,你可以 重新生成解决方案,然后再编译。
点击注入,获得注入的位置,通过 Cheat Engine 来查看该位置的内容
注入dll成功
注入成功后,还需要调用 Kernel32.dll 中的方法,调用该路径下的 dll 就完成 dll的注入了,代码如下:
// 省略显示前面的代码
// 创建一块内存用来打印一段文本,方便看效果
CHAR test[0x100] = { 0 };
sprintf_s(test, “写入的地址为:%p”, dllAdd);
OutputDebugString(test);
//将地址写入后,通过 Kernel32.dll 调用该地址的 DLL 就完成了
HMODULE k32 = GetModuleHandle(“Kernel32.dll”);
LPVOID loadAdd = GetProcAddress(k32, “LoadLibraryA”);
// 远程执行我们的dll,通过注入的dll地址以及CreateRemoteThread方法让微信进程调用起我们的进程
HANDLE exec = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)loadAdd, dllAdd, 0, NULL);
if (exec == NULL) {
MessageBox(NULL, “远程注入失败”, “错误”, 0);
return;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
此时 DLL 就相当于在微信进程中了。
DLL中的函数就可以操作微信进程中的内存了,从而实现获取微信信息的目的。
如果没有调用其DLL,则需要注意一下 LoadLibraryA 方法,该方法会调用 ASCII的路径地址,如果你是unicode的话,则需要调用 LoadLibraryW 方法,如果不成功,可以尝试替换一下。
没有Windows平台开发C++的朋友看完上面代码可能会感到疑惑,为什么将恶意dll路径插入内存后,可以让微信进程自己去载入这个恶意dll呢?
简单解释一下,在windows中,每个进程在启动时都会载入Kernel32.dll,微信进程会载入Kernel32.dll,浏览器进程也会载入Kernel32.dll,而我们可以通过Kernel32.dll中的LoadLibraryA()方法或LoadLibraryW()方法去动态载入新的dll,需要解释一下,我们在自己的进程中获取LoadLibraryA()方法的内存位置,这个内存位置对windows中其他进程而言是相同的。
但关键点是通过微信进程去使用Kernel32.dll中的LoadLibraryA()方法载入新的dll,为了实现这个目的,就需要使用CreateRemoteThread()方法,该方法可以远程调用其他进程的中的方法,前提是要有该进程的句柄(即执行权限),而我们在一开始就获得了微信进程的执行权限。
总结一下整个流程:
1.将恶意dll路径插入到微信进程运行内存中
2.获取当前进程(我们自己的进程,而非微信进程)Kernel32.dll中LoadLibraryA()方法或LoadLibraryW()方法,因为微信进程也载入了Kernel32.dll,所以微信进程可以在相同的内存地址处找到LoadLibraryA()方法或LoadLibraryW()方法
3.通过CreateRemoteThread()方法,让微信进程去调用LoadLibraryA()方法载入恶意dll
结尾
本文并没有给出恶意dll的具体实现逻辑,后面将实现一个简单的dll,来获取微信的个人信息,其实基于上一篇文章,找到了个人信息的具体微信红藕,该dll要做的只是从中读取信息,仅此而已。
但恶意dll还可以实现很多复杂的功能,比如监控聊天信息、自动收红包、自动踢人等各种功能,这些功能在后面有机会再分享给大家。
如果本文对你有帮助,点击「在看」支持二两,下篇文章见。
————————————————
版权声明:本文为CSDN博主「二两hackpython」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/github_39463644/article/details/103451010