一:背景前段时间在训练营上课的时候就有朋友提到一个问题 , 为什么 Windbg 附加到 C# 程序后 , 程序就处于中断状态了?它到底是如何实现的?其实简而言之就是线程的远程注入 , 这一篇就展开说一下 。
二:实现原理1. 基本思路WinDbg 在附加进程的时候 , 会注入一个线程到 C# 进程
中 , 注入成功后 , 会执行一个DbgBreakPoint
函数 , 其实就是int 3
, 这时候 CPU 就会执行 3 号中断函数 , 将当前进程的所有线程进行暂停 , 文字不好理解的话 , 画一个图大概就是这样 。
文章插图
口说无凭 , 接下来用上一个简单案例演示一下 。
2. 案例演示首先写一个简单的 C# 程序 , 不断的输出时间和标号 , 代码如下:
internal class Program{static void Main(string[] args){for (int i = 0; i < 10000; i++){Console.WriteLine($"{DateTime.Now},i={i}");Thread.Sleep(1000);}}}
把程序跑起来后 , 使用 WinDbg 附加 , 你可以发现 Command 自动切换到了 8
号线程 , 通过 k 命令可以看到最上面是一个 int 3 中断 , 截图如下:文章插图
这里就有一个想法了 , 既然 WinDbg 可以注入 , 为何我的程序就注入不得呢?既然我的程序可以注入 , 那就可以做一些我想做的事情 。
3. 自定义注入有了自定义注入的想法 , 接下来的实现步骤大概是这样的 。
- 注入一个线程到 C# 程序中 。
- 让程序加载一个 dll 文件 。
- 在 dll 中做一些我想做的业务逻辑 。
DLL_PROCESS_ATTACH
事件中写一个 printf 函数 , 如果在 C# 程序中输出来了 , 就算成功注入了 , 参考代码如下:【如何在 C# 程序中注入恶意 DLL ?】
#include <windows.h>#include <stdio.h>BOOL APIENTRY DllMain(HMODULE hModule,Dword ul_reason_for_call,LPVOID lpReserved){switch (ul_reason_for_call){case DLL_PROCESS_ATTACH:printf(" 总部 , 总部 , 我已经成功打入内部!ul_reason_for_call=%dn ", ul_reason_for_call);break;}return TRUE;}
要被加载的 MyInject.dll
已经构建完毕 , 接下来就用 Win32 API 的CreateRemoteThread
实现远程注入 , 但注入之前需要做三件事情 。- 获取 C# 程序的 进程句柄 。
- 在 C# 进程中申请一块内存空间 , 存放加载的 path 路径 。
- 调用 LoadLibraryW 函数在 C# 进程中实现 dll 加载 。
ConsoleApplication1.exe
, 整体的参考代码如下:#include <IOStream>#include <Windows.h>#include <stdio.h>#include <stdlib.h>#include <Tlhelp32.h>DWORD GetPid(const WCHAR* szName){HANDLE hprocessSnap = ;PROCESSENTRY32 pe32 = { 0 };hprocessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);pe32.dwSize = sizeof(PROCESSENTRY32);if (Process32First(hprocessSnap, &pe32)){do {if (!wcscmp(szName, pe32.szExeFile)) {return (int)pe32.th32ProcessID;}} while (Process32Next(hprocessSnap, &pe32));}else{CloseHandle(hprocessSnap);}return 0;}int main{const wchar_t* path = L"D:.NET6\ConsoleApp1\x64\Debug\MyInject.dll"; //要注入的dll文件地址//1. 获取进程IDDWORD procID = GetPid(L"ConsoleApp4.exe");//2. 获取进程句柄HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, procID);//3. 在目标进程中开辟一块空间LPVOID pRemoteAdress = VirtualAllocEx(hProcess, , wcslen(path) * 2, MEM_COMMIT, PAGE_READWRITE);//4. 将 path 写入到这块空间中BOOL bRet = WriteProcessMemory(hProcess, pRemoteAdress, path, wcslen(path) * 2, );//5. 让目标线程调用 LoadLibraryW 加载我们注入的 dllHMODULE hModule = GetModuleHandle(L"kernel32.dll");LPTHREAD_START_ROUTINE dwLoadAddr = (LPTHREAD_START_ROUTINE)GetProcAddress(hModule, "LoadLibraryW");HANDLE hThread = CreateRemoteThread(hProcess,,0,(LPTHREAD_START_ROUTINE)dwLoadAddr,pRemoteAdress,,);//6. 函数执行完后 , 释放这块空间 。WaitForSingleObject(hThread, -1);VirtualFreeEx(hProcess, pRemoteAdress, 1, MEM_DECOMMIT);system("pause");return 0;}
推荐阅读
- 如何解决使用网络代理软件之后网络不能使用的问题
- 海外社媒如何设定发布频率?什么时间发布更好?
- 如何设计一个IM单聊架构
- 开发一个简单的热搜微信小程序
- 这次彻底读透 Redis !
- 出现“白肺”怎么治?如何避免新冠感染出现肺炎?解答来了
- 购买二手车遭店家隐瞒车况,该如何维权?
- 如何在电子社保卡中完成养老保险关系转移申请?
- 英雄联盟如何出装 英雄联盟英雄出装网站
- 化“冻”师恩如山 师恩如山