提权相关函数

来源:互联网 发布:百度软件下载中心mac 编辑:程序博客网 时间:2024/05/21 10:58

//提升权限bool EnableSpecificPrivilege(LPCTSTR lpPrivilegeName){HANDLE hToken = NULL;TOKEN_PRIVILEGES Token_Privilege;BOOL bRet = TRUE;do {if (0 == OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)){OutputDebugView(_T("OpenProcessToken Error"));bRet = FALSE;break;}if (0 == LookupPrivilegeValue(NULL, lpPrivilegeName, &Token_Privilege.Privileges[0].Luid)){OutputDebugView(_T("LookupPrivilegeValue Error"));bRet = FALSE;break;}Token_Privilege.PrivilegeCount = 1;Token_Privilege.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;//Token_Privilege.Privileges[0].Luid.LowPart=17;//SE_BACKUP_PRIVILEGE//Token_Privilege.Privileges[0].Luid.HighPart=0;if (0 == AdjustTokenPrivileges(hToken, FALSE, &Token_Privilege, sizeof(Token_Privilege), NULL,NULL)){OutputDebugView(_T("AdjustTokenPrivileges Error"));bRet = FALSE;break;}} while (false);if (NULL != hToken){CloseHandle(hToken);}return bRet;}

lpPrivilegeName 取值:

#define SE_CREATE_TOKEN_NAME              TEXT("SeCreateTokenPrivilege")#define SE_ASSIGNPRIMARYTOKEN_NAME        TEXT("SeAssignPrimaryTokenPrivilege")#define SE_LOCK_MEMORY_NAME               TEXT("SeLockMemoryPrivilege")#define SE_INCREASE_QUOTA_NAME            TEXT("SeIncreaseQuotaPrivilege")#define SE_UNSOLICITED_INPUT_NAME         TEXT("SeUnsolicitedInputPrivilege")#define SE_MACHINE_ACCOUNT_NAME           TEXT("SeMachineAccountPrivilege")#define SE_TCB_NAME                       TEXT("SeTcbPrivilege")#define SE_SECURITY_NAME                  TEXT("SeSecurityPrivilege")#define SE_TAKE_OWNERSHIP_NAME            TEXT("SeTakeOwnershipPrivilege")#define SE_LOAD_DRIVER_NAME               TEXT("SeLoadDriverPrivilege")#define SE_SYSTEM_PROFILE_NAME            TEXT("SeSystemProfilePrivilege")#define SE_SYSTEMTIME_NAME                TEXT("SeSystemtimePrivilege")#define SE_PROF_SINGLE_PROCESS_NAME       TEXT("SeProfileSingleProcessPrivilege")#define SE_INC_BASE_PRIORITY_NAME         TEXT("SeIncreaseBasePriorityPrivilege")#define SE_CREATE_PAGEFILE_NAME           TEXT("SeCreatePagefilePrivilege")#define SE_CREATE_PERMANENT_NAME          TEXT("SeCreatePermanentPrivilege")#define SE_BACKUP_NAME                    TEXT("SeBackupPrivilege")#define SE_RESTORE_NAME                   TEXT("SeRestorePrivilege")#define SE_SHUTDOWN_NAME                  TEXT("SeShutdownPrivilege")#define SE_DEBUG_NAME                     TEXT("SeDebugPrivilege")#define SE_AUDIT_NAME                     TEXT("SeAuditPrivilege")#define SE_SYSTEM_ENVIRONMENT_NAME        TEXT("SeSystemEnvironmentPrivilege")#define SE_CHANGE_NOTIFY_NAME             TEXT("SeChangeNotifyPrivilege")#define SE_REMOTE_SHUTDOWN_NAME           TEXT("SeRemoteShutdownPrivilege")#define SE_UNDOCK_NAME                    TEXT("SeUndockPrivilege")#define SE_SYNC_AGENT_NAME                TEXT("SeSyncAgentPrivilege")#define SE_ENABLE_DELEGATION_NAME         TEXT("SeEnableDelegationPrivilege")#define SE_MANAGE_VOLUME_NAME             TEXT("SeManageVolumePrivilege")#define SE_IMPERSONATE_NAME               TEXT("SeImpersonatePrivilege")#define SE_CREATE_GLOBAL_NAME             TEXT("SeCreateGlobalPrivilege")#define SE_TRUSTED_CREDMAN_ACCESS_NAME    TEXT("SeTrustedCredManAccessPrivilege")#define SE_RELABEL_NAME                   TEXT("SeRelabelPrivilege")#define SE_INC_WORKING_SET_NAME           TEXT("SeIncreaseWorkingSetPrivilege")#define SE_TIME_ZONE_NAME                 TEXT("SeTimeZonePrivilege")#define SE_CREATE_SYMBOLIC_LINK_NAME      TEXT("SeCreateSymbolicLinkPrivilege")

Note:访问令牌说简单了就是个访问权限的数据集合,令牌中包含用户所有的权限,校验令牌可以识别用户是否有权限访问他要访问的位置
LookupPrivilegeValue函数查看系统权限的特权值,返回信息到一个LUID结构体里。
  BOOL LookupPrivilegeValue(LPCTSTR lpSystemName,LPCTSTR lpName,PLUID lpLuid);
  第一个参数表示所要查看的系统,本地系统直接用NULL
  第二个参数表示所要查看的特权信息的名称,定义在winnt.h中,具体指请MSDN索引“windows nt privileges”
  第三个参数用来接收所返回的制定特权名称的信息。
  函数调用成功后,信息存入第三个类型为LUID的结构体中,并且函数返回非0。
  函数定义在winbase.h中,链接使用advapi32.lib库。
AdjustTokenPrivileges 函数启用或禁止 指定访问令牌的特权。
启用或禁用特权一个有TOKEN_ADJUST_PRIVILEGES访问的访问令牌.
  BOOL AdjustTokenPrivileges(
  HANDLE TokenHandle//包含特权的句柄
  BOOL DisableAllPrivileges,
  //禁用所有权限标志
  PTOKEN_PRIVILEGES NewState,
  //新特权信息的指针(结构体)
  DWORD BufferLength//大小,以字节为单位的PreviousState的缓存区(sizeof)
  PTOKEN_PRIVILEGES PreviousState,
  //接收被改变特权当前状态的Buffer
  PDWORD ReturnLength//接收PreviousState缓存区要求的大小
  );
  参数
  TokenHandle
  包含要修改特权的访问令牌的标识(句柄).这个句柄必须有TOKEN_ADJUST_PRIVILEGES访问令牌.如果PreviousState不是NULL,这个句柄还必须有TOKEN_QUERY访问特权.
  DisableAllPrivileges
  标志这个函数是否禁用该令牌的所有特权.如果为TRUE,这个函数禁用所有特权,NewState参数无效.如果为假,以NewState参数指针的信息为基础来修改特权.
  NewState
  一个TOKEN_PRIVILEGES结构体的指针指定了一组特权和他们的属性.
  如果参数DisableAllPrivileges为FALSE,AdjustTokenPrivileges启用或禁用这些令牌的特权.
  如果你给一个特权设置了SE_PRIVILEGE_ENABLED的属性,这个函数将启动特权,否则禁用特权.
  如果DisableAllPrivileges为TRUE,这个参数无效.
  BufferLength
  标志参数PreviousState指针以字节大小缓存区(sizeof).
  如果参数PreviousState是NULL,这个参数可以为NULL.
  PreviousState
  这个函数填充一个TOKEN_PRIVILEGES结构体【指针】,它包括该函数修改之前任何特权状态.这个参数可以为NULL.
  如果指定的缓冲区太小,无法收到完整的修改权限列表,这个函数失败并不会修改任何特权.
  这个函数设置了一个 拥有修改权限完成列表【 参数ReturnLength 】的字节数 的指针变量.[结果的Buffer]
  ReturnLength
  接收 参数PreviousState的缓存区指针的 字节大小 的 变量指针(长度指针).
  如果PreviousState为NULL,这个参数可以为NULL.
  返回值
  如果这个函数成功,返回非0.为了确定这个函数是否修改了所有指定的特权,可以调用GetLastError函数,当这个函数返回下面的值之一时就代表函数成功:

提权函数之 RtlAdjustPrivilege()

RtlAdjustPrivilege() 这玩意是在 NTDLL.DLL 里的一个不为人知的函数,MS没有公开,原因就是这玩意实在是太NB了,以至于不需要任何其他函数的帮助,仅凭这一个函数就可以获得进程ACL的任意权限!下面是函数定义:NTSTATUS RtlAdjustPrivilege(         ULONG   Privilege,        BOOLEANEnable,        BOOLEANCurrentThread,        PBOOLEANEnabled)参数的含义:Privilege [In] Privilege index to change.                         // 所需要的权限名称,可以到 MSDN 查找关于 Process Token & Privilege 内容可以查到Enable [In] If TRUE, then enable the privilege otherwise disable. // 如果为True 就是打开相应权限,如果为False 则是关闭相应权限CurrentThread [In] If TRUE, then enable in calling thread, otherwise process. // 如果为True 则仅提升当前线程权限,否则提升整个进程的权限Enabled [Out] Whether privilege was previously enabled or disabled.// 输出原来相应权限的状态(打开 | 关闭), 注意:该参数赋予空指针会出错,我测试过。eg:RtlAdjustPrivilege(SE_DEBUG_PRIVILEGE,1,0,NULL);比用 AdjustTokenPrivileges 来提升进程权限方便很多,所以自己整理下备忘这个函数封装在NtDll.dll中(在所有DLL加载之前加载),被微软严格保密,就是说你在MSDN上查不到关于他的任何信息。

常用:常量 SE_BACKUP_PRIVILEGE = 0x11h常量 SE_RESTORE_PRIVILEGE = 0x12h常量 SE_SHUTDOWN_PRIVILEGE = 0x13h常量 SE_DEBUG_PRIVILEGE = 0x14h

全部:

//// These must be converted to LUIDs before use.//#define SE_MIN_WELL_KNOWN_PRIVILEGE         (2L)#define SE_CREATE_TOKEN_PRIVILEGE           (2L)#define SE_ASSIGNPRIMARYTOKEN_PRIVILEGE     (3L)#define SE_LOCK_MEMORY_PRIVILEGE            (4L)#define SE_INCREASE_QUOTA_PRIVILEGE         (5L)#define SE_MACHINE_ACCOUNT_PRIVILEGE        (6L)#define SE_TCB_PRIVILEGE                    (7L)#define SE_SECURITY_PRIVILEGE               (8L)#define SE_TAKE_OWNERSHIP_PRIVILEGE         (9L)#define SE_LOAD_DRIVER_PRIVILEGE            (10L)#define SE_SYSTEM_PROFILE_PRIVILEGE         (11L)#define SE_SYSTEMTIME_PRIVILEGE             (12L)#define SE_PROF_SINGLE_PROCESS_PRIVILEGE    (13L)#define SE_INC_BASE_PRIORITY_PRIVILEGE      (14L)#define SE_CREATE_PAGEFILE_PRIVILEGE        (15L)#define SE_CREATE_PERMANENT_PRIVILEGE       (16L)#define SE_BACKUP_PRIVILEGE                 (17L)#define SE_RESTORE_PRIVILEGE                (18L)#define SE_SHUTDOWN_PRIVILEGE               (19L)#define SE_DEBUG_PRIVILEGE                  (20L)#define SE_AUDIT_PRIVILEGE                  (21L)#define SE_SYSTEM_ENVIRONMENT_PRIVILEGE     (22L)#define SE_CHANGE_NOTIFY_PRIVILEGE          (23L)#define SE_REMOTE_SHUTDOWN_PRIVILEGE        (24L)#define SE_UNDOCK_PRIVILEGE                 (25L)#define SE_SYNC_AGENT_PRIVILEGE             (26L)#define SE_ENABLE_DELEGATION_PRIVILEGE      (27L)#define SE_MANAGE_VOLUME_PRIVILEGE          (28L)#define SE_IMPERSONATE_PRIVILEGE            (29L)#define SE_CREATE_GLOBAL_PRIVILEGE          (30L)#define SE_TRUSTED_CREDMAN_ACCESS_PRIVILEGE (31L)#define SE_RELABEL_PRIVILEGE                (32L)#define SE_INC_WORKING_SET_PRIVILEGE        (33L)#define SE_TIME_ZONE_PRIVILEGE              (34L)#define SE_CREATE_SYMBOLIC_LINK_PRIVILEGE   (35L)#define SE_MAX_WELL_KNOWN_PRIVILEGE         (SE_CREATE_SYMBOLIC_LINK_PRIVILEGE)

关机代码

#include <windows.h>const unsigned int SE_SHUTDOWN_PRIVILEGE = 0x13;int main(){HMODULE hDll = ::LoadLibrary("ntdll.dll");typedef int (* type_RtlAdjustPrivilege)(int, bool, bool, int*);typedef int (* type_ZwShutdownSystem)(int);type_RtlAdjustPrivilege RtlAdjustPrivilege = (type_RtlAdjustPrivilege)GetProcAddress(hDll, "RtlAdjustPrivilege");type_ZwShutdownSystem ZwShutdownSystem = (type_ZwShutdownSystem)GetProcAddress(hDll, "ZwShutdownSystem");int nEn = 0;int nResult = RtlAdjustPrivilege(SE_SHUTDOWN_PRIVILEGE, true, true, &nEn);if(nResult == 0x0c000007c){nResult = RtlAdjustPrivilege(SE_SHUTDOWN_PRIVILEGE, true, false, &nEn);}nResult = ZwShutdownSystem(2);FreeLibrary(hDll);return 0;} 

==============================================

以不同用户身份运行程序

一直想方便的处理CCProxy代理的帐号管理,所以梦想做一个比较好的管理工具。但一个最麻烦的问题就是帐号的更新,CCProxy有一个网页管理功能,可以加帐号,但加的帐号就是不可以立即更新。中午上网的时候发现CCProxy有一功能就是支持命令行的操作,如:
  CCProxy -reboot  重启软件
  CCProxy -reset   更新配置
  CCProxy -update  更新帐号
   
   试着改了AccInfo.ini中帐号信息,在DOS中运行CCProxy -update的确更新了账号,所以开始用PHP做管理工具,做到调用CCProxy -update时,用了PHP中的exec(),system()等函数一直没有效果,后又通过调用批处理文件来调用命令行参数都不行。 处理得正没耐心的时候,一气之下狂刷新PHP网页,电脑卡死,用进程管理器查看时发现打开了多个CCProxy进程,认真一看,除了一个CCProxy是用户进程外其它CCProxy全是system进程。认真一想有可能是运行用户身份不同所产生的结果。
  Apache服务调用的外部程序以system身份运行,自己双击运行的程序以用户身份运行。 如果CCProxy -update以用户身份运行是不是就可以了呢?本人在网络上找到了runas这个命令,的确可以指定以哪个用户运行,但是每次都要输密码,没有密码的帐号就要加上密码才可以用,“/savecred”这个参数可以用,只要输入一次密码就可以了,但在PHP中发现要以system的身份输入一次才行,根本没有机会输入。打算用C程序来处理这个问题。可是发现用WinExec(),ShellExecute(),CreateProcess()都不好处理这个问题,好在发现了CreateProcessAsUser()这个函数。把网络上的程序改了几处,编译后一试问题终于解决。
   以下为相关代码:
// Update.cpp : 定义控制台应用程序的入口点。

#include "stdafx.h"  #include <windows.h>  #include <tlhelp32.h>    BOOL GetTokenByName(HANDLE &hToken,LPSTR lpName)  {      if(!lpName)          return FALSE;            HANDLE         hProcessSnap = NULL;       BOOL           bRet      = FALSE;       PROCESSENTRY32 pe32      = {0};             hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);       if (hProcessSnap == INVALID_HANDLE_VALUE)           return (FALSE);             pe32.dwSize = sizeof(PROCESSENTRY32);             if (Process32First(hProcessSnap, &pe32))       {            do           {              if(!strcmp(_strupr(pe32.szExeFile),_strupr(lpName)))              {                  HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION,                      FALSE,pe32.th32ProcessID);                  bRet = OpenProcessToken(hProcess,TOKEN_ALL_ACCESS,&hToken);                  CloseHandle (hProcessSnap);                   return (bRet);              }          }           while (Process32Next(hProcessSnap, &pe32));           bRet = TRUE;       }        else           bRet = FALSE;            CloseHandle (hProcessSnap);       return (bRet);  }    BOOL RunProcess(LPCSTR lpImage,LPSTR lpCommandLine)  {      if(!lpImage)          return FALSE;            HANDLE hToken;      if(!GetTokenByName(hToken,"EXPLORER.EXE"))          return FALSE;            STARTUPINFO si;      PROCESS_INFORMATION pi;            ZeroMemory(&si, sizeof(STARTUPINFO));      si.cb= sizeof(STARTUPINFO);      si.lpDesktop = TEXT("winsta0\\default");            BOOL bResult = CreateProcessAsUser(hToken,lpImage, lpCommandLine,NULL,NULL,          FALSE,NORMAL_PRIORITY_CLASS,NULL,NULL,&si,&pi);      CloseHandle(hToken);      if(bResult)      {          OutputDebugString("CreateProcessAsUser ok!\r\n");          printf("CreateProcessAsUser ok!\r\n");      }      else      {          OutputDebugString("CreateProcessAsUse* **lse!\r\n");          printf("CreateProcessAsUse* **lse!\r\n");      }      return bResult;  }    int _tmain(int argc, _TCHAR* argv[])  {      RunProcess("CCProxy.exe"," -update");      return 0;  }


=====================================================================


用CreateProcessAsUser 创建最低权限进程


我从msdn上找到的资料,你看看
高 管理权限(进程可以将文件安装到“Program Files”文件夹,并写入敏感注册表区域,如 HKEY_LOCAL_MACHINE。)
中 用户权限(进程可在用户的“文档”文件夹创建和修改文件,并写入用户指定的注册表区域,如 HKEY_CURRENT_USER。)
低 不受信任权限(进程只能写入低完整性位置,例如 Temporary Internet Files\Low 文件夹或 HKEY_CURRENT_USER\Software\LowRegistry key)
启动低完整性进程
1.重复中等完整性进程的处理。  
2.使用 SetTokenInformation 将进程处理降低为低完整性。  
3.使用 CreateProcessAsUser 创建使用低完整性处理的新进程。

uses
  windows;
   
  function ConvertStringSidToSidA(StringSid: LPCTSTR; Sid:TSIDIdentifierAuthority): BOOL; stdcall; external 'Advapi32.dll' name 'ConvertStringSidToSidA';

const
  SE_GROUP_INTEGRITY = 1; 

#include "winnt.h"   
   
BOOL b;   
HANDLE hToken;   
HANDLE hNewToken;   
PWSTR szProcessName = "LowClient"; // 例如   
PWSTR szIntegritySid = "S-1-16-4096"; // 低完整性 SID   
PSID pIntegritySid = NULL;   
TOKEN_MANDATORY_LABEL TIL = {0};   
PROCESS_INFORMATION ProcInfo = {0};   
STARTUPINFO StartupInfo = {0};   
ULONG ExitCode = 0;   
   
b = OpenProcessToken(GetCurrentProcess(), MAXIMUM_ALLOWED,   
  &hToken);   
b = DuplicateTokenEx(hToken, MAXIMUM_ALLOWED, NULL,   
  SecurityImpersonation, TokenPrimary, &hNewToken);   
b = ConvertStringSidToSid(szIntegritySid, &pIntegritySid);   
TIL.Label.Attributes = SE_GROUP_INTEGRITY;   
TIL.Label.Sid = pIntegritySid;   
   
// 设置进程完整性级别   
b = SetTokenInformation(hNewToken, TokenIntegrityLevel, &TIL,   
  sizeof(TOKEN_MANDATORY_LABEL) + RtlLengthSid(pIntegritySid));   
   
// 设置进程的 UI 权限级别   
b = SetTokenInformation(hNewToken, TokenIntegrityLevelDesktop,   
  &TIL, sizeof(TOKEN_MANDATORY_LABEL) + RtlLengthSid(pIntegritySid));   
   
// 以低完整性创建新进程   
b = CreateProcessAsUser(hNewToken, NULL, szProcessName, NULL, NULL,   
  FALSE, 0, NULL, NULL, &StartupInfo, &ProcInfo);


实际中遇到的问题描:SKype在一个本地登录帐户里只能运行一个实例,我需要运行多个实例,那么就需要以其他帐户的身份运行skype 


有个方法: 
调用runas.exe程序。。 
只需要以runas /user:anotheruser "password" whatisgoingtorun 
运行一个新进程就解决了 
而windows默认的runas.exe是交互式输入密码, 看雪上有人将它改为了参数式输入密码(即上面的形式) 
详见看雪论坛 
以下是该帖子网址: 
http://www.pediy.com/bbshtml/bbs8/pediy8-45.htm


============================================================


微软从XP/2003开始为我们提供了一套Windows Terminal Service 的相关API,这些API都以WTS开头(请安装MSDN2005以查阅相关说明),要获得活动Session也不止一个途径,最简单的就是直接使用  
DWORD WTSGetActiveConsoleSessionId(void);  
来获得活动Session Id 。要在程序中使用这些API需要最新的Platform SDK(如果你正在使用Visual Studio 2005那么它已经具备了相关头文件和库文件可以直接使用了),如果你在使用VC++ 6.0 你也没有或者不打算安装最新的SDK那么你可以直接使用LoadLibrary() 装载wtsapi32.dll然后使用GetProcAddress()获得相关函数的地址以调用它们。我们获得了活动SessionId后就可以使用  
BOOL WTSQueryUserToken(  
ULONG SessionId,  
PHANDLE phToken  
);  
来获取当前活动Session中的用户令牌(Token),有了这个Token我们的就可以在活动Session中创建新进程了,  
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  
);  
将我们获得的Token作为此API的第一个参数即可,你可以先尝试一下运行一个notepad.exe看看,怎么样?你可以在控制台桌面上看到新进程了。再查看一下进程列表,该进程的用户名是当前控制台登录的用户。可是这里我们又遇到一个问题,我们需要收集当前交本机互式登录用户的一些信息,而有些操作需要很高的权限才能完成,而Vista下即使是Administraotrs用户组成员默认也是以Users权限启动进程的,所以我们创建的新进程只有Users权限,无法完成一些操作,当然我们可以使用Vista所提供的UI来询问用户以提升至管理员权限,可有些操作甚至是管理员Token也无法完成的,而且需要用户确认实在在易用性上大打折扣,所以我决定在活动Session中以SYSTEM权限启动我们的用户交互程序。显然 WTSQueryUserToken() 是不好用了。  
之前,我们提到过进程所属的Session是由进程Token中的TokenSessionId来决定的,那么我们是不是可以复制服务进程的Token然后修改其中的TokenSessionId,从而在用户桌面上创建一个具有SYSTEM权限的新进程呢?答案是肯定的。一下是实现这个操作的代码,为了缩小篇幅我删除了异常处理代码  
HANDLEhTokenThis = NULL;  
HANDLEhTokenDup = NULL;  
HANDLEhThisProcess = GetCurrentProcess();  
OpenProcessToken(hThisProcess, TOKEN_ALL_ACCESS, &hTokenThis);  
DuplicateTokenEx(hTokenThis, MAXIMUM_ALLOWED,NULL, SecurityIdentification, TokenPrimary, &hTokenDup); 
DWORDdwSessionId = WTSGetActiveConsoleSessionId();  
SetTokenInformation(hTokenDup, TokenSessionId, &dwSessionId, sizeof(DWORD));  

STARTUPINFOsi;  
PROCESS_INFORMATION pi;  
ZeroMemory(&si, sizeof(STARTUPINFO));  
ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));  
si.cb = sizeof(STARTUPINFO);  
si.lpDesktop = "WinSta0//Default";  

LPVOIDpEnv = NULL;  
DWORDdwCreationFlag = NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE;  

CreateEnvironmentBlock(&pEnv, hTokenDup, FALSE);  

CreateProcessAsUser(  
  hTokenDup,  
  NULL,  
  (char *)"notepad",  
  NULL,  
  NULL,  
  FALSE,  
  dwCreationFlag,  
  pEnv,  
  NULL,  
  &si,  
  &pi);


原创粉丝点击