首页 > 其他分享 >Win32API之实现远程线程注入(九)

Win32API之实现远程线程注入(九)

时间:2023-04-16 23:22:15浏览次数:36  
标签:函数 线程 进程 DWORD Win32API 远程 注入

什么是注入

注入是一种在不知情或未经许可的情况下向其他进程中注入模块并试图执行它们的技术

常见的注入方式有:远程线程注入、APC注入、消息钩子注入、注册表注入、导入表注入、输入法注入等等


什么是远程线程注入

远程线程注入是一种技术,可以将一个动态链接库(DLL)注入到另一个进程的地址空间中,并在该进程中创建一个远程线程来执行该 DLL 中的代码。这种技术通常用于恶意软件攻击,也可以用于调试和监视进程。远程线程注入涉及到许多操作系统底层的概念,包括内存映射、线程创建、函数调用等等。为了成功进行远程线程注入,攻击者需要克服许多障碍,例如安全软件、权限限制、代码签名等等


CreateRemoteThreaad

函数描述

CreateRemoteThread函数用于在另一个进程的地址空间中创建一个新线程。CreateRemoteThread函数通常用于远程线程注入和Hook技术等高级应用场景。其原理是通过在目标进程中分配内存,将需要执行的代码和参数写入该内存,然后在该内存中创建新线程并启动执行

其函数原型如下:

HANDLE CreateRemoteThread(
  HANDLE                 hProcess,  //目标进程的句柄
  LPSECURITY_ATTRIBUTES  lpThreadAttributes,  //线程安全描述符
  SIZE_T                 dwStackSize,  //线程的初始堆栈大小,通常为0表示使用默认值
  LPTHREAD_START_ROUTINE lpStartAddress,  //新线程的入口点,即线程执行的第一个函数
  LPVOID                 lpParameter, //传递给线程函数的参数
  DWORD                  dwCreationFlags, //线程创建的标志,例如是否立即运行等
  LPDWORD                lpThreadId //返回新线程的ID
);

注意:lpStartAddress参数表示目标进程所拥有的线程函数,而不是CreateRemoteThread函数所在进程的线程函数


使用实例

如下代码用于表示被远程注入的目标进程,首先通过调试进程获取到线程函数的地址为0x00981f70,然后运行此段代码

include <iostream>
include <windows.h>


DWORD WINAPI ThreadProc(LPVOID lpParameter)
{
	for (int i = 0; i < 5; i++)
	{
		printf("线程正在执行!\n");
	}
	return 0;
}


int main()
{	
	HANDLE hThread = CreateThread(NULL, 0, ThreadProc, 0, 0, NULL);
	getchar();

	CloseHandle(hThread);
	
}

1


如下代码用于实现远程线程注入,运行后会发现目标进程的线程函数会再次执行

include <iostream>
include <windows.h>
include <TlHelp32.h>
include <string>

// 远程线程注入函数
BOOL MyCreateRemoteThread(DWORD ProcessID, DWORD AddrThreadProc) {
	
	HANDLE hProcess;
	HANDLE hThread;
	DWORD ThreadID;

	// 打开目标进程
	hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, ProcessID);
	if (hProcess == NULL)
	{
		OutputDebugString("OpenProcess Error!\n");
		return FALSE;
	}
	
	// 在目标进程中创建新的远程线程
	hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)AddrThreadProc, NULL, 0, &ThreadID);
	if (hThread == NULL)
	{
		OutputDebugString("CreateRemoteThread Error!\n");
		return FALSE;
	}
	// 关闭进程和线程句柄
	CloseHandle(hThread);
	CloseHandle(hProcess);
	return true;
}

// 通过进程名称获取进程ID的函数
DWORD GetProcessIdByName(const std::wstring& name) {
	DWORD pid = 0;
	HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
	if (snap != INVALID_HANDLE_VALUE) {
		PROCESSENTRY32W entry = { sizeof(entry) };
		// 遍历进程快照
		if (Process32FirstW(snap, &entry)) {
			do {
				// 检查进程名称是否匹配
				if (std::wstring(entry.szExeFile) == name) {
					pid = entry.th32ProcessID;
					break;
				}
			} while (Process32NextW(snap, &entry));
		}
		// 关闭快照句柄
		CloseHandle(snap);
	}
	return pid;
}


int main()
{	
	// 通过进程名获取进程ID,并打印
	std::wstring process_name = L"test2.exe";
	DWORD pid = GetProcessIdByName(process_name);
	if (pid != 0) {
		printf("Process ID of %ls: %lu\n", process_name.c_str(), pid);
	}
	else {
		printf("Failed to get process ID of %ls\n", process_name.c_str());
	}

	// 在获取到的进程ID中执行远程线程注入
	MyCreateRemoteThread(pid, 0x00981f70);
	return 0;
}

1


远程线程注入的思路

通过上述CreateRemoteThread的远程线程实例可知, 若要实现远程线程注入, 只能限制于目标进程的线程函数, 而不能执行自己的代码

若想目标进程执行自己的代码,可以利用Dll注入来实现。首先了解ThreadProc线程函数的语法格式, 此函数需要一个四字节的参数以及四字节的返回值, 也就是说, 只要有符合这种格式的函数,那么它就能代替线程函数并传递给CreateRemoteThread函数调用

DWORD WINAPI ThreadProc(LPVOID lpParameter)
{
    return 0;
}

比如LoadLibrary函数, 此函数与线程函数的格式一样,都是四字节的参数和返回值。只要将CreateRemoteThread函数的线程函数地址替换成LoadLibrary的函数地址, 以及传递dll的路径作为LoadLibrary函数的参数, 在dll中写入我们自己的代码,这样就能实现完整的远程线程注入

HINSTANCE LoadLibrary( 
  LPCTSTR lpLibFileName;
) 

总结以上几点, 远程注入的流程如下所示:

  • 在目标进程分配内存空间, 用于存储dll的路径
  • 获取LoadLibrary函数的地址
  • 创建远程线程, 并执行LoadLibrary函数

远程线程注入实例

代码

如下代码是DLL文件,要注意一点,若目标进程是32位的,那么创建的dll也应该是32位的, 将要执行的代码写在DLL_PROCESS_ATTACH处, 即dll被加载后会自动执行这段代码

//Mydll.dll
include "pch.h"
include <stdio.h>

DWORD WINAPI ThreadProc(LPVOID lpParameter)
{	
	for (int i = 0; i < 5; i++)
	{	
		Sleep(1000);
		printf("远程线程注入成功!\n");
	}
	return 0;
}


<br>

BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
		CreateThread(NULL, 0, ThreadProc, NULL, 0, NULL);
		break;
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}	

如下代码是被远程注入的进程B

//进程B

include <iostream>
include <windows.h>

DWORD WINAPI ThreadProc(LPVOID lpParameter)
{
	for (int i = 0; i < 5; i++)
	{
		printf("线程正在执行!\n");
	}
	return 0;
}


int main()
{	
	HANDLE hThread = CreateThread(NULL, 0, ThreadProc, 0, 0, NULL);
	getchar();

	CloseHandle(hThread);
}	

如下代码用于实现远程线程注入的进程A。

关于Dll文件的路径问题, Dll的路径若要使用相对路径, 那么dll文件就必须放在进程B(目标进程)的工作目录下, 而不是进程A的工作目录

//进程A

include <iostream>
include <windows.h>
include <TlHelp32.h>
include <string>

//获取进程ID的函数
DWORD GetProcessIdByName(const std::wstring& name) {
	DWORD pid = 0;
	HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
	if (snap != INVALID_HANDLE_VALUE) {
		PROCESSENTRY32W entry = { sizeof(entry) };
		if (Process32FirstW(snap, &entry)) {
			do {
				if (std::wstring(entry.szExeFile) == name) {
					pid = entry.th32ProcessID;
					break;
				}
			} while (Process32NextW(snap, &entry));
		}
		CloseHandle(snap);
	}
	return pid;
}


BOOL RemoteInject(DWORD pid, char* dllPath)
{
	DWORD DllNameLength = strlen(dllPath);

	//1 获取目的进程句柄
	HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
	
	//2 为目的进程分配内存,用于存放Loadlibrary传入的参数,即dll的路径
	VOID* paraAddr = VirtualAllocEx(hProcess, NULL, DllNameLength + 1, MEM_COMMIT, PAGE_READWRITE);

	//3 将DLL的路径写到目标进程的内存
	if (!WriteProcessMemory(hProcess, paraAddr, dllPath, DllNameLength + 1, NULL))
	{
		printf("写入内存失败!\n");
		return false;
	}
	
	//4 获取loadlibrary函数的地址
	HINSTANCE LibHandle = LoadLibrary("kernel32");
	DWORD ProcAdd = (DWORD)GetProcAddress(LibHandle, "LoadLibraryA");
	if (!ProcAdd)
	{
		printf("获取LoadLibraryA失败!\n");
		return false;
	}

	//5 创建远程线程
	DWORD threadid = 0;
	HANDLE hRemoteThread = INVALID_HANDLE_VALUE;
	hRemoteThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)ProcAdd, paraAddr, 0, &threadid);
	if (NULL == hRemoteThread)
	{
		printf("目标进程中创建线程失败!\n");
		CloseHandle(hProcess);
		return FALSE;
	}
	WaitForSingleObject(hRemoteThread, INFINITE);
	
	//释放句柄
	CloseHandle(hRemoteThread);
	CloseHandle(hProcess);
}


int main(int argc, char*argv[])
{	
	//获取进程ID
	std::wstring process_name = L"test2.exe";
	DWORD ProcessID = GetProcessIdByName(process_name);
	
	//远程线程注入
	RemoteInject(ProcessID, (char*)"E:\\Mydll.dll");
	
	return 0;
}

执行结果

将生成的dll文件(MyDll.dll)放到E盘目录, 先运行进程B的代码, 随后运行进程A的代码, 可以发现dll文件的线程代码在进程A执行了

动画

标签:函数,线程,进程,DWORD,Win32API,远程,注入
From: https://www.cnblogs.com/henry666/p/17324410.html

相关文章

  • C 语言版线程池
    一、初始线程池1.1何为线程池?我们先来打个比方,线程池就好像一个工具箱,我们每次需要拧螺丝的时候都要从工具箱里面取出一个螺丝刀来。有时候需要取出一个来拧,有时候螺丝多的时候需要多个人取出多个来拧,拧完自己的螺丝那么就会把螺丝刀再放回去,然后别人下次用的时候再取出来用。......
  • telnet(远程登录协议)
    1.telnet配置的三种方式none:表示下次使用该用户线登录时不需要进行用户名和密码认证,任何人都可以登录设备。password:表示下次使用该用户线登录时,需要输入密码。scheme:表示下次使用该用户线登录设备时需要进行用户名和密码认证。2.用户角色network-admin:具有最高权限,可操作系统所......
  • 线程
    1.轻量级锁(自旋锁)无等待队列,忙等待。场景:线程执行时间短,等待线程少。2.重量级锁有等待队列,通过操作系统调度。场景:线程执行时间长,等待线程多。  关键字valatile:......
  • 使用Python代码远程连接服务器
    目录一、paramiko模块的介绍二、基本使用(用户名密码登录)三、用公钥私钥连接一、paramiko模块的介绍模块介绍使用Python的第三方模块paramiko实现远程连接服务器功能:通过python代码连接服务器并执行相关操作并且支持用户名密码连接和公钥私钥连接模块安装pipinstall......
  • 人体红外夜间远程监控【远程车子夜间监控】[车内监控][车内监视器]【远程夜间汽车监控
    (接上一篇:https://www.cnblogs.com/liuguiqing/p/17114911.html)(本篇讲解:实践操作) 调试设备:(摄像机部分) 调试设备:(网络部分)注意:共两路,一路控制wifi网卡(供摄像机上网);一路控制wifi摄像机。控制电路一样。 组装调试: 实际应用:手机app远程查看监控,效果: ......
  • C++实现多线程
    #include<iostream>#include<chrono>#include<thread>voidprintNumbers1(){for(inti=1;i<=10000;i++){std::cout<<"Thread1:"<<i<<std::endl;}}voidprintNumbers2(){for......
  • rce远程代码执行漏洞
    (176条消息)RCE漏洞_Poolhuang的博客-CSDN博客1、定义RCE(remotecommand/codeexecute)RCE漏洞,可以让攻击者直接向后台服务器远程注入操作系统命令或者代码,从而控制后台系统。 一般出现这种漏洞,是因为应用系统从设计上需要给用户提供指定的远程命令操作的接口,比如我们常见......
  • Java中创建线程的方式以及线程池创建的方式、推荐使用ThreadPoolExecutor以及示例
    场景Java中创建线程的方式有三种1、通过继承Thread类来创建线程定义一个线程类使其继承Thread类,并重写其中的run方法,run方法内部就是线程要完成的任务,因此run方法也被称为执行体,使用start方法来启动线程。2、通过实现Runanle接口来创建线程首先定义Runnable接口,并重写Runnable接口......
  • 远程调用之负载均衡Ribbon-No instances available for localhost ribbon
    如果使用的是多个实例情况,远程调用就不能写地址,否则远程调用报Noinstancesavailableforlocalhostribbon而是应该写成服务的名称 ......
  • 【CVE-2017-12615】Tomcat 远程代码执行漏洞复现
    0x00环境搭建用vulhub的环境查看配置文件conf/web.xml中readonly的设置0x01漏洞复现访问主页,抓包后修改数据包可通过PUT方式创建一个JSP文件。虽然Tomcat对文件后缀有一定检测(不能直接写jsp),但我们使用一些文件系统的特性(如Linux下可用/)来绕过了限制。改完包的时候......