.f90文件批量转为dll文件_微信 Hook 实战记录 2: 动手实现恶意 dll 内存插入器

Hook 任何软件,整体思路都是通过内存调试软件对软件运行时内存进行断点调试,找到想要 hook 的内存地址,转为可以通过程序主 dll 可以获得的相对地址,然后再此处插入自己的恶意汇编代码,如果代码比较复杂,还需要编写寄存器保护逻辑,避免自己的恶意代码修改了寄存器中的值后,程序正常的逻辑无法运行。

前言


在上一篇文章「微信 Hook 实战记录 1: 找到个人信息」中,介绍了如何使用 OD 与 CE 来找到微信中存放个人信息的内存位置,本篇文章尝试通过 C++ 编写一个简单的内存注入工具,将我们自己的恶意 dll 注入到微信的内存中。
本文记录了大量细节,完全可以模仿复现相同的效果。

内存注入工具的编写


打开 Visual Studio 2019 (下载的时候勾选 C++ 桌面支持,6.9G 左右),选择创建新项目

4cdb5aae1fbaabb7aea5afb61da1905e.png


选择创建 Windows 桌面向导

247c1a5c2ea7be4dcb5632670344384c.png


然后创建就可以了,这里创面项目名为 Project1
创建完后,VS 会为我们创建一个默认的界面,我们在 解决方案 -> 源文件 中找到 Project1.cpp,这个文件就是我们要写逻辑的主要界面

b359da0091a49a16476cfc2a157ed529.png


将其中多余的代码删除,就留下如下代码

  1. // Project1.cpp : 定义应用程序的入口点。
  2. //
  3. #include "framework.h"
  4. #include "Project1.h"
  5. int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
  6. _In_opt_ HINSTANCE hPrevInstance,
  7. _In_ LPWSTR lpCmdLine,
  8. _In_ int nCmdShow)
  9. {
  10. return 0;
  11. }


wWinMain 是界面程序的入口,所有不必删除
接着将 解决方案 --> 资源文件 中的内容删除,这是 VS 默认创建好的,删除后,自己新建一个资源文件

acef7b53a5825ac093e9f6d86527b749.png


选择 Dialog,即弹出框

95f911297d15619d39b991c57c6c7d26.png


调整一下创建出的按钮位置以及整体大小

dfc6ed3ea9992cc4bb2a4b5095c06d1a.png


要设置按钮与整体的属性,如按钮要设置其内容与 ID,而整体 Dialog 也要设置一下其 ID,可以右键点击 --> 属性

33cca02a8baa71242e903b8ee1700263.png

bdc9b2d8a19ae73e58f48d5a0d5f5712.png


其他控制以相同的方式去设置,设置后后,就可以写代码了
首先修改一下 Project1.h 中的内容,如下

  1. #pragma once
  2. #include "resource1.h"


具体内容要看你 VS 为你生成了什么,前面我们通过图像界面操作后,VS 会生成的相应的头文件,这里我生成了 resource1.h 的头文件,其内容如下:

  1. //{{NO_DEPENDENCIES}}
  2. // Microsoft Visual C++ 生成的包含文件。
  3. // 供 Project1.rc 使用
  4. //
  5. #define ID_MAIN 101
  6. #define UN_DLL 1001
  7. #define INJECT_DLL 1002
  8. // Next default values for new objects
  9. //
  10. #ifdef APSTUDIO_INVOKED
  11. #ifndef APSTUDIO_READONLY_SYMBOLS
  12. #define _APS_NEXT_RESOURCE_VALUE 103
  13. #define _APS_NEXT_COMMAND_VALUE 40001
  14. #define _APS_NEXT_CONTROL_VALUE 1003
  15. #define _APS_NEXT_SYMED_VALUE 101
  16. #endif
  17. #endif
  18. #endif


可以看出,其实就是我们设置的 ID 等内容
接着来修改一下 Project1.cpp, 其 wWinMain 方法修改如下:

  1. #include "framework.h"
  2. #include "Project1.h"
  3. #include <TlHelp32.h>
  4. #include <stdio.h>
  5. // 微信进程名
  6. #define WECHAT_PROCESS_NAME "WeChat.exe"
  7. int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
  8. _In_opt_ HINSTANCE hPrevInstance,
  9. _In_ LPWSTR lpCmdLine,
  10. _In_ int nCmdShow)
  11. {
  12. // hInstance 将句柄传递给弹窗
  13. // 传入 Dialgo 主体ID
  14. // DialogProc 回调函数,是一个指针,即该函数的地址
  15. DialogBox(hInstance, MAKEINTRESOURCE(ID_MAIN), NULL, &DialogProc);
  16. return 0;
  17. }


因为创建的资源是 Dialog ,所以这里通过 DialogBox 来使用它,它在 Windows.h 的头文件中,如果你使用的 VS 2019,其 framework.h 文件就包含 Windows.h 了。
DialogBox 的参数可以通过微软的文档来查看

  1. void DialogBoxA(
  2. hInstance,
  3. lpTemplate,
  4. hWndParent,
  5. lpDialogFunc
  6. );


hInstance,类型:HINSTANCE,包含对话框模板的模块句柄。如果此参数为 NULL,则使用当前可执行文件。lpTemplate,类型:LPCTSTR,对话框模板。可以使用 MAKEINTRESOURCE 宏来创建此值。hWndParent,类型:HWND,拥有该对话框的窗口的句柄。lpDialogFunc,类型:DLGPROC,指向对话框过程的指针。
根据文档提示,完成代码

  1. #include "framework.h"
  2. #include "Project1.h"
  3. #include <TlHelp32.h>
  4. #include <stdio.h>
  5. #define WECHAT_PROCESS_NAME "WeChat.exe"
  6. // 注册函数,方便其他模块使用
  7. INT_PTR CALLBACK DialogProc(_In_ HWND hwndDlg, _In_ UINT uMsg, _In_ WPARAM wParam, _In_ LPARAM lParam);
  8. VOID InjectDll();
  9. int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
  10. _In_opt_ HINSTANCE hPrevInstance,
  11. _In_ LPWSTR lpCmdLine,
  12. _In_ int nCmdShow)
  13. {
  14. DialogBox(hInstance, MAKEINTRESOURCE(ID_MAIN), NULL, &DialogProc);
  15. return 0;
  16. }
  17. INT_PTR CALLBACK DialogProc(_In_ HWND hwndDlg, _In_ UINT uMsg, _In_ WPARAM wParam, _In_ LPARAM lParam)
  18. {
  19. if (uMsg == WM_INITDIALOG) {
  20. MessageBox(NULL, "首次加载", "标题", 0);
  21. }
  22. // 关闭界面操作
  23. if (uMsg == WM_CLOSE) {
  24. EndDialog(hwndDlg, 0);
  25. }
  26. // 所有的界面上的按钮事件都会走这个宏
  27. if (uMsg == WM_COMMAND) {
  28. if (wParam == INJECT_DLL) {
  29. InjectDll(); //注入 Dll
  30. }
  31. if (wParam == UN_DLL) {
  32. }
  33. }
  34. return FALSE;
  35. }


接着来实现 InjectDll () 方法,完成注入 DLL 的功能,整体分 3 步走。
1. 获取微信的 PID,通过 PID 获得微信进程的句柄,从而拥有了权限 2. 申请内存,内存大小为 DLL 路径大小 3. 将 DLL 路径注入到微信进程中

  1. DWORD ProcessNameFindPID(LPCSTR ProcessName)
  2. {
  3. // #include <TlHelp32.h>
  4. // 获取进程快照
  5. HANDLE ProcessAll = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);// 获取整个系统的进程快照
  6. // 在快照中对比过 processname 进程名称
  7. PROCESSENTRY32 processInfo = { 0 };
  8. processInfo.dwSize = sizeof(PROCESSENTRY32);
  9. do
  10. {
  11. // WeChat.exe
  12. // th32ProcessID 进程ID, szExeFile进程名
  13. if (strcmp(ProcessName, (char*)processInfo.szExeFile) == 0) {
  14. return processInfo.th32ProcessID;
  15. }
  16. } while (Process32Next(ProcessAll, &processInfo));
  17. return 0;
  18. }


上述方法,需要 windows 平台 C++ 基础。其实主要就是对 Windows.h 中方法的调用,接着完成如下效果

  1. VOID InjectDll()
  2. {
  3. // 自己恶意dll的路径
  4. CHAR pathStr[0x100] = { "D://GetWeChatInfo.dll" };
  5. // 获取微信PID
  6. DWORD PID = ProcessNameFindPID(WECHAT_PROCESS_NAME);
  7. if (PID == 0) {
  8. MessageBox(NULL, "没有找到微信进程或微信没有启动", "错误", 0);
  9. return;
  10. }
  11. // 通过PID获取句柄 C++中句柄类型为 HANDLE
  12. HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, PID);
  13. if (NULL == hProcess) {
  14. MessageBox(NULL, "进程打开失败,可能权限不足或关闭了微信应用", "错误", 0);
  15. return;
  16. }
  17. //申请内存,内存大小为我们要注入 DLL 的大小
  18. LPVOID dllAdd = VirtualAllocEx(hProcess, NULL, sizeof(pathStr), MEM_COMMIT, PAGE_READWRITE);
  19. if (NULL == dllAdd) {
  20. MessageBox(NULL, "内存分配失败", "错误", 0);
  21. return;
  22. }
  23. // 将 DLL 写入到对应的内存地址中
  24. if(WriteProcessMemory(hProcess, dllAdd, pathStr, strlen(pathStr), NULL) == 0) {
  25. MessageBox(NULL, "路径写入失败", "错误", 0);
  26. return;
  27. }
  28. // 创建一块内存用来打印一段文本,方便看效果
  29. CHAR test[0x100] = { 0 };
  30. sprintf_s(test, "写入的地址为:%p", dllAdd);
  31. OutputDebugString(test);
  32. }


完成后,直接编译运行,如果编译后的效果依旧是之前代码的效果,你可以 重新生成解决方案,然后再编译。

be1ce1bd9f521cd98f39892d7dac5d37.png


点击注入,获得注入的位置,通过 Cheat Engine 来查看该位置的内容

139a7f87de9074773b8c1bdab5860494.png


通过 CE 查看相应的内存地址,发现该内存地址为 dll 的路径,注入 dll 成功

eaa4fa3676a4d057425f410b4d1bf635.png


注入成功后,还需要调用 Kernel32.dll 中的方法,调用该路径下的 dll 就完成 dll 的注入了,代码如下:

  1. // 省略显示前面的代码
  2. // 创建一块内存用来打印一段文本,方便看效果
  3. CHAR test[0x100] = { 0 };
  4. sprintf_s(test, "写入的地址为:%p", dllAdd);
  5. OutputDebugString(test);
  6. //将地址写入后,通过 Kernel32.dll 调用该地址的 DLL 就完成了
  7. HMODULE k32 = GetModuleHandle("Kernel32.dll");
  8. LPVOID loadAdd = GetProcAddress(k32, "LoadLibraryA");
  9. // 远程执行我们的dll,通过注入的dll地址以及CreateRemoteThread方法让微信进程调用起我们的进程
  10. HANDLE exec = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)loadAdd, dllAdd, 0, NULL);
  11. if (exec == NULL) {
  12. MessageBox(NULL, "远程注入失败", "错误", 0);
  13. return;
  14. }


此时 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 还可以实现很多复杂的功能,比如监控聊天信息、自动收红包、自动踢人等各种功能,这些功能在后面有机会再分享给大家。
如果本文对你有帮助,点击「在看」支持二两,下篇文章见。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值