CreateProcessAsUser和CreateProcessWithLogonW的简单案列

介绍6种比较常用的运行(执行)程序的方法:
包括WinExec、ShellExecute、CreateProcess、CreateProcessAsUser、CreateProcessWithLogonW、CreateProcessWithTokenW

一、 CreateProcessAsUser创建一个新进程及其主线程。

1、创建当前登录(活跃)用户下的进程

// pch.cpp: 与预编译标头对应的源文件;编译成功所必需的

#include "pch.h"

#include <windows.h>
#include <stdio.h>
#include <userenv.h>
#include <iostream>
#include <Wtsapi32.h>
#pragma comment(lib, "Wtsapi32.lib")
#pragma comment(lib, "Userenv.lib")
#include<tchar.h>
#include <atlconv.h>
#include <tlhelp32.h>

void main(int argc, WCHAR *argv[])
{
	
	//BOOL CreateProcessAsUser(
		//HANDLE hToken,//处理表示登录用户的标记
		//LPCTSTR lpApplicationName,//指向可执行模块名称的指针
		//LPTSTR lpCommandLine,//指向命令行字符串的指针
		//LPSECURITY_ATTRIBUTES lpProcessAttributes,//处理安全属性
		//LPSECURITY_ATTRIBUTES lpThreadAttributes, //线程安全属性
		//BOOL bInheritHandles,//新进程是否继承处理
		//DWORD dwCreationFlags,//创建标志
		//LPVOID lpEnvironment,//指向新环境块的
		//指针LPCTSTR lpCurrentDirectory,//指向当前目录名称的
		//指针LPSTARTUPINFO lpStartupInfo,//指向STARTUPINFO的指针
		//LPPROCESS_INFORMATION lpProcessInformation //指向PROCESS_INFORMATION的指针
		//);

	HANDLE hToken = NULL;
	BOOL bResult = FALSE;
	PROCESS_INFORMATION pi;
	STARTUPINFO         si;
	ZeroMemory(&si, sizeof(STARTUPINFO));//清空内存
	si.cb = sizeof(STARTUPINFO);
	si.lpDesktop = L"winsta0\\default";

	
	LPVOID environment;
	BOOL blockRet = CreateEnvironmentBlock(&environment, hToken, FALSE);
	if (!blockRet)
	{
		printf("could not create environment block (error: %i)",GetLastError());
	}
	else
	{
		TCHAR Cmdline[MAX_PATH] = _T("C:\\Windows\\system32\\notepad.exe");
		DWORD creationFlags = NORMAL_PRIORITY_CLASS | CREATE_NO_WINDOW | CREATE_UNICODE_ENVIRONMENT;

		bResult = CreateProcessAsUser(
			hToken,
			NULL,
			Cmdline,
			NULL,
			NULL,
			FALSE,
			creationFlags,
			environment,
			NULL,
			&si,
			&pi
		);
		if (bResult)
		{
			printf("CreateProcessAsUser成功 = %d", bResult);
		}
		else
		{
			printf("CreateProcessAsUser失败 = %d", GetLastError());
		}
		

		// close the handles
		if (bResult && pi.hProcess != INVALID_HANDLE_VALUE)
		{
			WaitForSingleObject(pi.hProcess, INFINITE);
			CloseHandle(pi.hProcess);
		}
		else
		{
			printf("CreateProcessAsUser1 = %d", GetLastError());
		}
		if (pi.hThread != INVALID_HANDLE_VALUE)
			CloseHandle(pi.hThread);
		DestroyEnvironmentBlock(environment);
	}

	RevertToSelf();
	CloseHandle(hToken);

}

2、指定一个特定的用户创建一个进程(我这里最终是做成服务的形式了,所以有提升服务的一些权限设置):

 

#include "pch.h"

#include <windows.h>
#include <stdio.h>
#include <userenv.h>
#include <iostream>
#include <Wtsapi32.h>
#pragma comment(lib, "Wtsapi32.lib")
#pragma comment(lib, "Userenv.lib")
#include<tchar.h>
#include <atlconv.h>
#include <tlhelp32.h>

#define G_LEN_CMD_LINE  1024    //< 命令行最大长度
// 计算wchar_t 数组的size, 以wchar_t为单位
#define SIZE_OF_WCHAR_ARRAY(x) (sizeof(x) / sizeof(wchar_t))

/*
注意:
1、是“服务”进程!如果需要以用户身份运行,必须在前面执行LogonUser来获取用户令牌
2、此种设置只能在服务中使用,如果用编译器来执行会报只CreateProcessAsUser: 客户端没有所需的特权
3、如果做好后,启动服务,出现一个交互式的界面的话。需要:保留。不需要,查看下面的解决之法
*/

//用CreateProcessAsUse函数实现的(可用,做成服务不可用)
BOOL CreateProcessAsUse(wchar_t * pcLoginUsr,
	wchar_t * pcLoginPwd,
	wchar_t * pcPePathName,
	DWORD &   dwSubPID,
	DWORD &   dwSubTID)
{
	BOOL                    bRc = FALSE;
	// 检查用户登录权
	HANDLE hUserToken = NULL;
	if (LogonUser(pcLoginUsr, L".", pcLoginPwd, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, &hUserToken))
	{
		PROCESS_INFORMATION pi;
		STARTUPINFO si;
		DWORD dwCreationFlags = NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE;
		ZeroMemory(&si, sizeof(STARTUPINFO));
		si.cb = sizeof(STARTUPINFO);
		//si.lpDesktop = L"winsta0\\default";
		ZeroMemory(&pi, sizeof(pi));

		TOKEN_PRIVILEGES tp;
		LUID luid;
		if (!LookupPrivilegeValue(NULL, SE_RESTORE_NAME, &luid))//查询进程的权限
		{
			std::cout << "LookupPrivilegeValue: " << GetLastError() << std::endl;
			return false;
		}
		else
		{
			tp.PrivilegeCount = 1;
			tp.Privileges[0].Luid = luid;
			tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
			//判断(调整)令牌权限
			if (!AdjustTokenPrivileges(hUserToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), (PTOKEN_PRIVILEGES)NULL, NULL))
			{
				std::cout << "AdjustTokenPrivileges: " << GetLastError() << std::endl;
				return false;
			}
			else
			{
				//创建环境
				LPVOID pEnv = NULL;
				if (CreateEnvironmentBlock(&pEnv, hUserToken, TRUE))
				{
					dwCreationFlags |= CREATE_UNICODE_ENVIRONMENT;
					bRc = TRUE;
					if (bRc == CreateProcessAsUser(hUserToken, // client's access token
						NULL,        // file to execute
						pcPePathName,   // command line
						NULL,          // pointer to process SECURITY_ATTRIBUTES
						NULL,          // pointer to thread SECURITY_ATTRIBUTES
						FALSE,          // handles are not inheritable
						dwCreationFlags,// creation flags
						pEnv,          // pointer to new environment block
						NULL,          // name of current directory
						&si,            // pointer to STARTUPINFO structure
						&pi            // receives information about new process
					)) 
					{
						dwSubPID = pi.dwProcessId;
						dwSubTID = pi.dwThreadId;

						::CloseHandle(pi.hProcess);
						::CloseHandle(pi.hThread);
					}
					else
					{
						std::cout << "CreateProcessAsUser: " << GetLastError() << std::endl;
					}
				}
				else {
					std::cout << "CreateEnvironmentBlock: " << GetLastError() << std::endl;
					
				}
			}
		}
	}
	else
	{
		std::cout << "LogonUser: " << GetLastError() << std::endl;
		
	}

	//执行所有Close句柄任务
	if (NULL != hUserToken)
	{
		CloseHandle(hUserToken);
	}

	return bRc;
}


void processExe() {
	BOOL    bRc = FALSE;
	DWORD   dwSubPID = 0;
	DWORD   dwSubTID = 0;
	wchar_t cPePathName[G_LEN_CMD_LINE] = { 0 };

	setlocale(LC_CTYPE, ".936");//控制台为中文输出

	//_tcscpy_s实际是一个宏,根据是多字节编码或者是Unicode编码而调用不同的函数
	_tcscpy_s(cPePathName, SIZE_OF_WCHAR_ARRAY(cPePathName), G_LOGIN_PATH);
	bRc = CreateProcessAsUse(L"wyg",
		L"123456",
		L"C:\Windows\system32\notepad.exe",
		dwSubPID,
		dwSubTID);
	if (bRc)
	{
		std::cout << "成功!" << std::endl;
		
	}
	
}

 根据问题:出现交互式界面解决取消或者勾选--->允许服务与桌面!

 

 

二、 CreateProcessWithLogonW创建一个新进程及其主线程。

1、新进程在指定凭据(用户,域和密码)的安全上下文中运行指定的可执行文件。它可以选择加载指定用户的用户配置文件。传一个命令和一个用户名启动一个应用进程,进程和用户名一一对应,如任务管理器中显示的。

// pch.cpp: 与预编译标头对应的源文件;编译成功所必需的

#include "pch.h"

#include <windows.h>
#include <stdio.h>
#include <userenv.h>
#include <iostream>
#include <Wtsapi32.h>
#pragma comment(lib, "Wtsapi32.lib")
#pragma comment(lib, "Userenv.lib")
#include<tchar.h>
#include <atlconv.h>
#include <tlhelp32.h>

void DisplayError(LPWSTR pszTip)
{
	LPVOID lpvMessageBuffer;

	FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
		FORMAT_MESSAGE_FROM_SYSTEM,
		NULL, GetLastError(),
		MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
		(LPWSTR)&lpvMessageBuffer, 0, NULL);

	/// now display this string
	_tprintf(L"ERROR: API        = %s.\n", pszTip);
	_tprintf(L"       error code = 0x%X.\n", GetLastError());
	_tprintf(L"       message    = %s.\n", (LPWSTR)lpvMessageBuffer);

	/// Free the buffer allocated by the system
	LocalFree(lpvMessageBuffer);
}

#define G_LOGIN_NAME    L"wyg1"
#define G_LOGIN_PWD     L"123456"
#define G_PROG_NAME  L"C:\\Windows\\system32\\notepad.exe"
#define G_LEN_CMD_LINE  1024    //< 命令行最大长度

// 计算wchar_t 数组的size, 以wchar_t为单位
#define SIZE_OF_WCHAR_ARRAY(x) (sizeof(x) / sizeof(wchar_t))

BOOL CreateProcessWithLoginEx(wchar_t * pcLoginUsr,
	wchar_t * pcLoginPwd,
	wchar_t * pcPePathName,
	BOOL      bWaitProcessEnd,
	DWORD &   dwSubPID,
	DWORD &   dwSubTID)
{
	BOOL                    bRc = FALSE;
	DWORD                   dwLenName = 0;

	wchar_t cComputerName[MAX_PATH];

	STARTUPINFO             si;
	PROCESS_INFORMATION     pi;

	::ZeroMemory(&si, sizeof(STARTUPINFO));
	si.wShowWindow = SW_SHOW;
	
	si.lpDesktop = NULL;
	si.cb = sizeof(STARTUPINFO);
	si.dwFlags = STARTF_USESHOWWINDOW;

	::ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
	dwLenName = SIZE_OF_WCHAR_ARRAY(cComputerName);
	if (FALSE == ::GetComputerNameW(cComputerName, &dwLenName)) {
		DisplayError(L"GetComputerNameW");
	}
	/**
	备注 : lpCommandLine 传进来时, 应该是一个最大1024长度的Buffer, 不能是常量串

	要执行的命令行。这个字符串的最大长度是1024个字符。如果lpApplicationName为NULL,则模块名部分为lpCommandLine被限制为MAX_PATH字符。

	CreateProcessWithLogonW 参数6 lpCommandLine
	函数可以修改这个字符串的内容。因此,此参数不能是只读内存(如const)的指针变量或文字字符串)。如果该参数是常量字符串,则函数可能导致访问冲突。
	*/
	bRc = CreateProcessWithLogonW(//1058
		pcLoginUsr,
		cComputerName,
		pcLoginPwd,
		NULL,
		NULL,
		pcPePathName,
		CREATE_DEFAULT_ERROR_MODE,
		NULL,
		NULL,
		&si,
		&pi);
	
	if (bRc)
	{
		dwSubPID = pi.dwProcessId;
		dwSubTID = pi.dwThreadId;
		if (bWaitProcessEnd)
			::WaitForSingleObject(pi.hProcess, INFINITE);

		::CloseHandle(pi.hProcess);
		::CloseHandle(pi.hThread);
	}

	return bRc;
}
void main(int argc, WCHAR *argv[])
{

	//BOOL CreateProcessWithLogonW(
	//	LPCWSTR               lpUsername,//用户名
	//	LPCWSTR               lpDomain,//帐户的域或服务器的名称
	//	LPCWSTR               lpPassword,//密码
	//	DWORD                 dwLogonFlags,//登录选项
	//	LPCWSTR               lpApplicationName,//要执行的模块的名称
	//	LPWSTR                lpCommandLine,//要执行的命令行
	//	DWORD                 dwCreationFlags,//控制进程创建方式的标志
	//	LPVOID                lpEnvironment,//指向新进程的环境块的指针
	//	LPCWSTR               lpCurrentDirectory,//进程当前目录的完整路径
	//	LPSTARTUPINFOW        lpStartupInfo,//指向STARTUPINFO结构的指针
	//	LPPROCESS_INFORMATION lpProcessInformation//指向PROCESS_INFORMATION结构的指针,该 结构接收新进程的标识信息,包括进程的句柄。
	//);



BOOL    bRc = FALSE;
BOOL    bWaitProcessEnd = TRUE;
DWORD   dwSubPID = 0;
DWORD   dwSubTID = 0;
wchar_t cPePathName[G_LEN_CMD_LINE] = { 0 };

setlocale(LC_CTYPE, ".936");//控制台为中文输出

	/// 执行完 CreateProcessWithLoginEx 后, cPePathName 有可能被改掉
	_tcscpy_s(cPePathName, SIZE_OF_WCHAR_ARRAY(cPePathName), G_PROG_NAME);
	bRc = CreateProcessWithLoginEx(G_LOGIN_NAME,
		G_LOGIN_PWD,
		cPePathName,
		TRUE,
		dwSubPID,
		dwSubTID);
	if (bRc)
	{
		_tprintf(L"Process[PID:%d, TID:%d] execute OK\r\n",dwSubPID, dwSubTID);
	}
	else
	{
		/// 只要系统服务都恢复启动了, 就不会出现0x422错误
		DisplayError(L"CreateProcessWithLogonW");
	}


}


 

2、当然如下这样写也是可行的:

// pch.cpp: 与预编译标头对应的源文件;编译成功所必需的

#include "pch.h"
#include <windows.h>
#include <stdio.h>
#include <userenv.h>
#include <iostream>
#include <Wtsapi32.h>
#pragma comment(lib, "Wtsapi32.lib")
#pragma comment(lib, "Userenv.lib")
#include<tchar.h>

#include <tlhelp32.h>
#include <windows.h>    
#include <iostream>    
using namespace std;

#pragma comment(lib, "Userenv.lib")

#ifdef UNICODE
#define RunAsUser RunAsUserW
#else
#define RunAsUser RunAsUserA
#endif

BOOL WINAPI RunAsUserW(LPCWSTR userName, LPCWSTR pswd, LPWSTR application)
{
	BOOL bRet = FALSE;

	do
	{
		DWORD     dwSize = MAX_PATH;
		HANDLE    hToken;
		LPVOID    lpvEnv;
		PROCESS_INFORMATION pi = { 0 };
		STARTUPINFOW si = { sizeof(STARTUPINFO) };
		wchar_t szUserProfile[MAX_PATH] = { 0 };

		if (!LogonUserW(userName,
			L".",
			pswd,
			LOGON32_LOGON_INTERACTIVE,
			LOGON32_PROVIDER_DEFAULT,
			&hToken))
		{
			break;
		}

		if (!CreateEnvironmentBlock(&lpvEnv, hToken, TRUE))
		{
			break;
		}
		
		if (!GetUserProfileDirectoryW(hToken, szUserProfile, &dwSize))
		{
			break;
		}
		
		if (!CreateProcessWithLogonW(userName,
			NULL,
			pswd,
			LOGON_WITH_PROFILE,
			NULL,
			application,
			CREATE_UNICODE_ENVIRONMENT,
			NULL,
			szUserProfile,
			&si,
			&pi))
		{//1058
			DestroyEnvironmentBlock(lpvEnv);
		    std::cout << "Error=" << GetLastError()<<std::endl;
			break;
		}
		DestroyEnvironmentBlock(lpvEnv);
		CloseHandle(hToken);
		CloseHandle(pi.hProcess);
		CloseHandle(pi.hThread);

		bRet = TRUE;
	} while (FALSE);

	return bRet;
}

BOOL WINAPI RunAsUserA(LPCSTR userName, LPCSTR pswd, LPSTR application)
{
	BOOL bRet = FALSE;
	LPWSTR wUName = NULL;
	LPWSTR wPswd = NULL;
	LPWSTR wApp = NULL;

	do
	{
		unsigned int nameLen = strlen(userName);
		unsigned int pswdLen = strlen(pswd);
		unsigned int appLen = strlen(application);

		wUName = (LPWSTR)malloc((nameLen + 1) * sizeof(wchar_t));
		wPswd = (LPWSTR)malloc((pswdLen + 1) * sizeof(wchar_t));
		wApp = (LPWSTR)malloc((appLen + 1) * sizeof(wchar_t));
		if (!wUName || !wPswd || !wApp)
		{
			break;
		}

		memset(wUName, 0, (nameLen + 1) * sizeof(wchar_t));
		memset(wPswd, 0, (pswdLen + 1) * sizeof(wchar_t));
		memset(wApp, 0, (appLen + 1) * sizeof(wchar_t));

		MultiByteToWideChar(GetACP(), 0, userName, nameLen, wUName, nameLen + 1);
		MultiByteToWideChar(GetACP(), 0, pswd, pswdLen, wPswd, pswdLen + 1);
		MultiByteToWideChar(GetACP(), 0, application, appLen, wApp, appLen + 1);

		bRet = RunAsUserW(wUName, wPswd, wApp);
	} while (FALSE);

	if (wUName)
	{
		free(wUName);
	}
	if (wPswd)
	{
		free(wPswd);
	}
	if (wApp)
	{
		free(wApp);
	}
	return bRet;
}

int main(int argc, char** argv)
{
	
	if (!RunAsUser(L"wyg1", L"123456", L"C:\\Windows\\system32\\notepad.exe"))
	{
		printf("创建进程失败\n");
	}

	return 0;
}

在使用我们在用CreateProcessAsUserA、CreateProcessWithLogonW、CreateProcessWithTokenW函数编写程序时,经常会出现1058或者0x422(无法启动服务,原因可能是已被禁用或与其相关联的设备没有启动)的错误,解决方法可以看我这篇博客!https://blog.csdn.net/qq_34227896/article/details/87621458

参考文献:

https://www.cnblogs.com/FCoding/archive/2012/07/01/2572280.html

https://www.jb51.net/article/82757.htm

https://ask.helplib.com/windows/post_4134087

https://blog.csdn.net/u011471873/article/details/51862786

  • 6
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值