描述
- 通过CreateProcessAsUser函数,在一个服务进程中创建用户进程,实现ui交互
准备知识
- CFF Explorer:PE查看器,可查看PE文件的导入导出表等信息
- Windows窗口回调
- Windows操作系统向应用程序发送一系列消息,如左键按下和左键抬起,应用程序将通过GetMessage等方法,从消息队列中取出消息,将其提交到窗口过程(WndProc)指向一个应用程序定义的窗口过程的指针
- 每个窗口会有一个称为窗口过程的回调函数,其原型为:
LRESULT CALLBACK WndProc( //WndProc名称可自由定义
HWND hwnd, //窗口句柄(Window Handle)
UINT uMsg, //消息ID(Message ID)
WPARAM wParam, //两个消息参数(wParam, lParam)
LPARAM lParam
);
- 消息的获取和分发
while(GetMessage(&msg,NULL,0,0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
- Qt自己有一套消息处理机制(信号槽),但是也可以使用Windows原生的消息机制,链接
代码
注册服务程序入口函数
int _tmain(int argc, _TCHAR* argv[])
{
SERVICE_TABLE_ENTRY stDispatchTable[] = { {g_szServiceName, (LPSERVICE_MAIN_FUNCTION)ServiceMain}, {NULL, NULL} };
::StartServiceCtrlDispatcher(stDispatchTable);
return 0;
}
服务入口函数
- 注册服务控制处理函数ServiceCtrlHandle
- 完成服务主体工作
void __stdcall ServiceMain(DWORD dwArgc, char* lpszArgv)
{
g_ServiceStatus.dwServiceType = SERVICE_WIN32;
g_ServiceStatus.dwCurrentState = SERVICE_START_PENDING;
g_ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
g_ServiceStatus.dwWin32ExitCode = 0;
g_ServiceStatus.dwServiceSpecificExitCode = 0;
g_ServiceStatus.dwCheckPoint = 0;
g_ServiceStatus.dwWaitHint = 0;
g_ServiceStatusHandle = ::RegisterServiceCtrlHandler(g_szServiceName, ServiceCtrlHandle);
g_ServiceStatus.dwCurrentState = SERVICE_RUNNING;
g_ServiceStatus.dwCheckPoint = 0;
g_ServiceStatus.dwWaitHint = 0;
::SetServiceStatus(g_ServiceStatusHandle, &g_ServiceStatus);
DoTask();
}
服务控制处理函数
- 接收来自ControlService发出的服务控制请求,按照基本控制码进行状态判断,通过SetServiceStatus通知SCM对服务状态进行更新
void __stdcall ServiceCtrlHandle(DWORD dwOperateCode)
{
switch (dwOperateCode)
{
case SERVICE_CONTROL_PAUSE:
{
// 暂停
g_ServiceStatus.dwCurrentState = SERVICE_PAUSED;
break;
}
case SERVICE_CONTROL_CONTINUE:
{
// 继续
g_ServiceStatus.dwCurrentState = SERVICE_RUNNING;
break;
}
case SERVICE_CONTROL_STOP:
{
// 停止
g_ServiceStatus.dwWin32ExitCode = 0;
g_ServiceStatus.dwCurrentState = SERVICE_STOPPED;
g_ServiceStatus.dwCheckPoint = 0;
g_ServiceStatus.dwWaitHint = 0;
::SetServiceStatus(g_ServiceStatusHandle, &g_ServiceStatus);
break;
}
case SERVICE_CONTROL_INTERROGATE:
{
// 询问
break;
}
default:
break;
}
}
服务主体工作
- 调用CreateProcessAsUser函数,创建用户态进程
- 通过WTSSendMessage弹出一个用户态提示窗口
void DoTask()
{
ShowMessage("Hello Z5onk0\nThis Message Is From Session 0 Service!\n", "Session 0");
CreateUserProcess("E:\\Project\\Cpp\\BlackHat\\ResourceFree\\result\\ResourceFree.exe");
}
void ShowMessage(TCHAR* lpszMessage, TCHAR* lpszTitle)
{
DWORD dwSessionId = ::WTSGetActiveConsoleSessionId();
DWORD dwResponse = 0;
::WTSSendMessage(WTS_CURRENT_SERVER_HANDLE, dwSessionId,
lpszTitle, (1 + ::lstrlen(lpszTitle)),
lpszMessage, (1 + ::lstrlen(lpszMessage)),
0, 0, &dwResponse, FALSE);
}
BOOL CreateUserProcess(char* lpszFileName)
{
BOOL bRet = TRUE;
DWORD dwSessionID = 0;
HANDLE hToken = NULL;
HANDLE hDuplicatedToken = NULL;
LPVOID lpEnvironment = NULL;
STARTUPINFO si = { 0 };
PROCESS_INFORMATION pi = { 0 };
si.cb = sizeof(si);
do
{
// 获得当前Session ID
dwSessionID = ::WTSGetActiveConsoleSessionId();
// 获得当前Session的用户令牌
if (FALSE == ::WTSQueryUserToken(dwSessionID, &hToken))
{
ShowMessage("WTSQueryUserToken", "ERROR");
bRet = FALSE;
break;
}
// 复制令牌
if (FALSE == ::DuplicateTokenEx(hToken, MAXIMUM_ALLOWED, NULL,
SecurityIdentification, TokenPrimary, &hDuplicatedToken))
{
ShowMessage("DuplicateTokenEx", "ERROR");
bRet = FALSE;
break;
}
// 创建用户Session环境
if (FALSE == ::CreateEnvironmentBlock(&lpEnvironment,
hDuplicatedToken, FALSE))
{
ShowMessage("CreateEnvironmentBlock", "ERROR");
bRet = FALSE;
break;
}
// 在复制的用户Session下执行应用程序,创建进程
if (FALSE == ::CreateProcessAsUser(hDuplicatedToken,
lpszFileName, NULL, NULL, NULL, FALSE,
NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE | CREATE_UNICODE_ENVIRONMENT,
lpEnvironment, NULL, &si, &pi))
{
ShowMessage("CreateProcessAsUser", "ERROR");
bRet = FALSE;
break;
}
} while (FALSE);
// 关闭句柄, 释放资源
if (lpEnvironment)
{
::DestroyEnvironmentBlock(lpEnvironment);
}
if (hDuplicatedToken)
{
::CloseHandle(hDuplicatedToken);
}
if (hToken)
{
::CloseHandle(hToken);
}
return bRet;
}
结果
- 用服务加载器来加载和启动服务
- 服务一启动后,立马弹出用户窗口,并且成功运行带ui的用户进程
- 查看ProcMon,可以看到位于session 0的服务进程成功创建了位于session 9的用户进程