在Windows操作系统中,createprocessasuser函数是一种非常重要的系统调用函数。通常,我们使用createprocessasuser函数来创建一个用户进程,这个进程可以被指定的用户账户所拥有,并且可以在其权限范围内运行。在本文中,我们将会着重讲解如何使用createprocessasuser函数在Windows中创建一个用户进程。
一、createprocessasuser函数简介
在深入了解如何使用createprocessasuser函数之前,我们先来了解一下这个函数的基本信息。
createprocessasuser函数是Windows API提供的一种进程创造函数,可以在指定的用户账户权限下创建进程。当系统需要创建一个新的进程时,createprocessasuser函数会向指定的用户账户发送创建进程请求,操作系统会在该账户权限的限制下创建新的进程。
createprocessasuser函数所需参数如下:
BOOL CreateProcessAsUser(
HANDLE hToken,
LPCTSTR lpApplicationName,
LPTSTR lpCommandLine,
LPSECURITY_ATTRIBUTES lpProcessAttributes,
LPSECURITY_ATTRIBUTES lpThreadAttributes,
BOOL bInheritHandles,
DWORD dwCreationFlags,
LPVOID lpEnvironment,
LPCTSTR lpCurrentDirectory,
LPSTARTUPINFO lpStartupInfo,
LPPROCESS_INFORMATION lpProcessInformation
);
通过下面的解释,您会更加了解这些参数的含义和作用:
1. 第一个参数hToken,表示启动该进程的用户。该参数可以是账户的令牌hanlde,也可以是一个模拟令牌的hanlde。
2. 第二个参数lpApplicationName,表示要启动的应用程序的名称。
3. 第三个参数lpCommandLine,表示启动应用程序所需要的命令行参数,可以缺省。
4. 第四个参数lpProcessAttributes,表示进程安全性属性,默认为NULL。
5. 第五个参数lpThreadAttributes,表示线程安全性属性,默认为NULL。
6. 第六个参数bInheritHandles,表示是否集成标准输入、输出、错误输出流,默认为FALSE。
7. 第七个参数dwCreationFlags,表示进程创建时的标志位,比如创建进程的方式,优先级,是否悬挂等。默认为0。
8. 第八个参数lpEnvironment,表示该进程的环境变量,定义环境变量字符串数组,加必须以NULL结尾。
9. 第九个参数lpCurrentDirectory,表示进程的当前工作目录,缺省为起始目录。
10. 第十个参数lpStartupInfo,表示进程的启动信息结构体,比如标题、窗口位置、显示方式等。
11. 第十一个参数lpProcessInformation,表示进程信息结构体,返回新的进程的句柄,进程标识符等信息。
二、使用createprocessasuser函数创建用户进程
进程是计算机中的程序执行环境,是操作系统资源的有效占用,创建进程时需要充分考虑程序所需的资源。下面我们来详细了解如何正确、安全地使用createprocessasuser函数来创建一个用户进程。
1. 获取目标账户的令牌句柄
由于createprocessasuser函数需要指定用于创建进程的用户,因此我们要先获取目标用户账户的安全令牌引用。Windows提供了很多API函数来获取用户账户的令牌,通常我们使用以下两个API函数:
1.1 获取当前用户的令牌
BOOL OpenProcessToken(HANDLE ProcessHandle, DWORD DesiredAccess, PHANDLE TokenHandle);
使用OpenProcessToken函数可以打开指定进程的访问令牌,将其保存到TokenHandle缓冲区中。在获取自己进程的令牌时,需要使用下面的函数:
HANDLE hToken;
OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken);
1.2 获取指定用户的令牌
BOOL LogonUser(LPTSTR lpszUsername, LPTSTR lpszDomain, LPTSTR lpszPassword, DWORD dwLogonType, DWORD dwLogonProvider, PHANDLE PhToken);
使用LogonUser函数可以模拟登陆某个账户,获取账户的访问令牌,返回的令牌用于OpenProcessAsUser函数创建进程。函数参数解释如下:
lpszusername参数为指定的用户名,该参数不得为NULL;
lpszdomain参数指定的计算机或域名,该参数可以为NULL;
lpszpassword参数指定的用户密码,该参数不得为NULL;
dwlogontype参数,指定登陆操作类型,该参数须为下列的一个或多个常数之一或其相应之值的组合;
dwlogonprovider参数,指定提供登陆服务的dll的名称。
2. 使用createprocessasuser函数创建进程
要使用createprocessasuser函数创建进程,需要满足以下基本条件:
2.1 创建进程的用户账户必须具有启动所需应用程序的权限。可以通过资源管理器或命令行获得应用程序文件的名称和路径,来确定所需的启动用户账户权限。
2.2 确保当前用户具备运行createprocessasuser函数的权限。通常情况下,获取当前用户令牌时指定TOKEN_ADJUST_PRIVILEGES权限。
3. 代码实现
以下是createprocessasuser函数完整示例代码:
#include
#include
BOOL ImpersonateLogonUser(LPWSTR szUsername, LPWSTR szDomain, LPWSTR szPassword);
BOOL ReImpersonateLoggedOnUser();
BOOL CreateProcessAsUserWrapper(
LPWSTR szCommandLine,
ULONG ulSessionId
);
HANDLE g_hToken = NULL;
HANDLE g_hImpersonationToken = NULL;
int wmain(UINT argc, LPWSTR argv[])
{
if(argc != 3)
{
wprintf(TEXT("Usage: %s sessionId command\n"), argv[0]);
return 1;
}
ULONG ulSessionId = _wtoi(argv[1]);
LPWSTR szCommandLine = argv[2];
if(ImpersonateLoggedOnUser(g_hToken))
{
if(CreateProcessAsUserWrapper(szCommandLine, ulSessionId))
{
wprintf(TEXT("Success\n"));
return 0;
}
}
wprintf(TEXT("Failed\n"));
return 1;
}
BOOL ImpersonateLoggedOnUserWrapper(LPWSTR szUsername, LPWSTR szDomain, LPWSTR szPassword)
{
if(!LogonUser(szUsername, szDomain, szPassword, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, &g_hImpersonationToken))
{
wprintf(TEXT("LogonUser Failed\n"));
return FALSE;
}
if(!ImpersonateLoggedOnUser(g_hImpersonationToken))
{
wprintf(TEXT("ImpersonateLoggedOnUser Failed\n"));
return FALSE;
}
return TRUE;
}
BOOL ReImpersonateLoggedOnUser()
{
if(!SetThreadToken(NULL, g_hToken))
{
wprintf(TEXT("SetThreadToken Failed\n"));
return FALSE;
}
return TRUE;
}
BOOL CreateProcessAsUserWrapper(LPWSTR szCommandLine, ULONG ulSessionId)
{
DWORD dwSessionId = 0;
HWND hMainWindow = NULL;
dwSessionId = WTSGetActiveConsoleSessionId();
if(dwSessionId == 0xFFFFFFFF)
{
wprintf(TEXT("WTSGetActiveConsoleSessionId Failed\n"));
return FALSE;
}
if(!WTSQueryUserToken(dwSessionId, &g_hToken))
{
wprintf(TEXT("WTSQueryUserToken Failed\n"));
return FALSE;
}
if(!ImpersonateLoggedOnUserWrapper(TEXT("Administrator"), NULL, NULL))
{
wprintf(TEXT("ImpersonateLoggedOnUserWrapper Failed\n"));
return FALSE;
}
HANDLE hToken = NULL;
LUID luid = {0};
if(!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luid))
{
wprintf(TEXT("LookupPrivilegeValue Failed\n"));
return FALSE;
}
DWORD dwAttributes = 0;
TOKEN_PRIVILEGES TokenPriv = {0};
TokenPriv.PrivilegeCount = 1;
TokenPriv.Privileges[0].Luid = luid;
TokenPriv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
if(!OpenThreadToken(GetCurrentThread(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, FALSE, &hToken))
{
if(GetLastError() == ERROR_NO_TOKEN)
{
if(!ImpersonateLoggedOnUserWrapper(TEXT("Administrator"), NULL, NULL))
{
wprintf(TEXT("ImpersonateLoggedOnUserWrapper Failed\n"));
return FALSE;
}
if(!OpenThreadToken(GetCurrentThread(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, FALSE, &hToken))
{
wprintf(TEXT("OpenThreadToken Failed\n"));
return FALSE;
}
}
else
{
wprintf(TEXT("OpenThreadToken Failed\n"));
return FALSE;
}
}
if(!AdjustTokenPrivileges(hToken, FALSE, &TokenPriv, sizeof(TOKEN_PRIVILEGES), NULL, NULL))
{
wprintf(TEXT("AdjustTokenPrivileges Failed\n"));
return FALSE;
}
CloseHandle(hToken);
SECURITY_ATTRIBUTES saProcess = {0};
SECURITY_ATTRIBUTES saThread = {0};
saProcess.nLength = sizeof(SECURITY_ATTRIBUTES);
saProcess.bInheritHandle = FALSE;
saProcess.lpSecurityDescriptor = NULL;
saThread.nLength = sizeof(SECURITY_ATTRIBUTES);
saThread.bInheritHandle = TRUE;
saThread.lpSecurityDescriptor = NULL;
STARTUPINFO si = {0};
si.cb = sizeof(STARTUPINFO);
si.lpDesktop = NULL;
si.lpTitle = NULL;
si.dwFlags = STARTF_USESHOWWINDOW;
si.wShowWindow = SW_SHOWNORMAL;
PROCESS_INFORMATION pi = {0};
if(!CreateProcessAsUser(
g_hToken, // Token handle
NULL, // Application name
szCommandLine , // Command line
&saProcess, // Process security attributes
&saThread, // Thread security attributes
FALSE, // Inheritable handles
CREATE_NEW_CONSOLE | // On the console
NORMAL_PRIORITY_CLASS, // Priority class
NULL, // Environment
NULL, // Current directory
&si, // Startup info structure
&pi // Process information structure
))
{
wprintf(TEXT("CreateProcessAsUser failed (%d)\n"), GetLastError());
return FALSE;
}
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
ReImpersonateLoggedOnUser();
return TRUE;
}
三、总结
创建一个用户进程需要获取目标用户的令牌,确定要启动的应用程序的文件名和路径,然后使用createprocessasuser函数创建进程。本文详细讲解了使用createprocessasuser函数创建用户进程的方法,并提供了使用C语言实现的示例代码,希望对使用createprocessasuser函数创建用户进程有所帮助。