Launch your application in Vista under the local system account without the UAC popup

原文地址:http://www.codeproject.com/KB/vista-security/VistaSessions.aspx

Introduction

In Vista, I came across a problem where I wanted to launch an exe under an administrator account to perform certain tasks. I could not do that because the UAC elevation dialog box would appear for which the user would have to respond. To work around that problem, I came up with the solution of launching the required application from a service into the current user session with local system account privileges.This would not require us to respond to the UAC dialog box. This way I could run my application with the highest privilege possible, avoiding the UAC dialog box. In my case this was necessary since I needed to control/communicate with applications running with administrative rights without user intervention and perform some administrative tasks in the current user session on system startup.

Background

The concept which I use is very straightforward. I have a normal user mode application which communicates with a service through custom messages. On receiving the custom message, the service will launch the requested application (that I want) under the system account. To get hold of a local system account I used the winlogon's token in the required session since winlogon.exe runs under the local system account. (In Vista, services are present in session 0 and the first user logs on in session 1 and so on, unlike in XP)

Using the code

First let us review the file CustomMessageSender.cpp. This is the usermode application which communicates with the service.This application can be any normal application without any special privileges.

Collapse
#define SERVICE_NAME _T("CustomSvc")
//CUSTOM MESSAGE FOR SERVICE TO LAUNCH THE PROCESS INTO SESSION 1

#define SERVICE_CONTROL_CUSTOM_MESSAGE 0x0085

int _tmain(int argc, _TCHAR* argv[])
{
SC_HANDLE hMyService,hSCM;
BOOL bSuccess;
SERVICE_STATUS status;
hSCM = OpenSCManager(0,0,SC_MANAGER_CONNECT);
if(!hSCM)
{
printf("Open SCM failed with error %u",GetLastError());
}
hMyService = OpenService(hSCM,SERVICE_NAME,SERVICE_USER_DEFINED_CONTROL);
if(!hMyService)
{
printf("Open SCM failed with error %u",GetLastError());
}
bSuccess = ControlService(hMyService,SERVICE_CONTROL_CUSTOM_MESSAGE,&status);
if(!bSuccess)
{
printf("Control Service failed with error %u",GetLastError());
}
CloseServiceHandle(hMyService);
CloseServiceHandle(hSCM);
return 0;
}

The code above is very simple and straightforward. I use SERVICE_USER_DEFINED_CONTROL and SC_MANAGER_CONNECT access permission because any user mode applications can connect to our service to send customize messages to it. No admin privileges are required. So this application sends the SERVICE_CONTROL_CUSTOM_MESSAGE to the service. The service part of the code which receives the messages and launches the application is below. I have used a sample service posted by Anish in Code Project and added my features into it.

Collapse
//CUSTOM MESSAGE FOR SERVICE TO LAUNCH AN APP INTO SESSION 1

#define SERVICE_CONTROL_CUSTOM_MESSAGE 0x0085

//Method to launch an application into session 1 under

//the local system account


BOOL LaunchAppIntoDifferentSession();
.
.
.
.
.
.
.
.
.
void WINAPI ServiceCtrlHandler(DWORD Opcode)
{
switch(Opcode)
{
//////////////////////////////////////////////////////////////////////////

//Added By Jaisvar on 04/11/07 to receive a custom message from a user app


case SERVICE_CONTROL_CUSTOM_MESSAGE:
LaunchAppIntoDifferentSession();
break;

In the above code snippet, I have declared the custom message and the prototype of the method which I will be executing on receiving the custom message. Let me describe the LaunchAppIntoDifferentSession Method before I display the code snippet. To launch a process under the local system account I perform the following steps:

  1. Get the Active Console SessionId using WTSGetActiveConsoleSessionId
  2. Since I need to launch the application under a system account, I use the token from Winlogon, since Winlogon runs under the system account. So I obtain the process ID of Winlogon and Duplicate the token.
  3. Then I make sure I sent the startupinfo parameter lpDesktop to winsta0/Default since I need to launch my process there.
  4. Then I use CreateProcessAsUser with Winlogon's duplicate token to launch my process into session 1.
  5. That's all. I am done.
Collapse
BOOL LaunchAppIntoDifferentSession()
{
PROCESS_INFORMATION pi;
STARTUPINFO si;
BOOL bResult = FALSE;
DWORD dwSessionId,winlogonPid;
HANDLE hUserToken,hUserTokenDup,hPToken,hProcess;
DWORD dwCreationFlags;

// Log the client on to the local computer.


dwSessionId = WTSGetActiveConsoleSessionId();

//////////////////////////////////////////

// Find the winlogon process

////////////////////////////////////////


PROCESSENTRY32 procEntry;

HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (hSnap == INVALID_HANDLE_VALUE)
{
return 1 ;
}

procEntry.dwSize = sizeof(PROCESSENTRY32);

if (!Process32First(hSnap, &procEntry))
{
return 1 ;
}

do
{
if (_stricmp(procEntry.szExeFile, "winlogon.exe") == 0)
{
// We found a winlogon process...

// make sure it's running in the console session

DWORD winlogonSessId = 0;
if (ProcessIdToSessionId(procEntry.th32ProcessID, &winlogonSessId)
&& winlogonSessId == dwSessionId)
{
winlogonPid = procEntry.th32ProcessID;
break;
}
}

} while (Process32Next(hSnap, &procEntry));

////////////////////////////////////////////////////////////////////////


WTSQueryUserToken(dwSessionId,&hUserToken);
dwCreationFlags = NORMAL_PRIORITY_CLASS|CREATE_NEW_CONSOLE;
ZeroMemory(&si, sizeof(STARTUPINFO));
si.cb= sizeof(STARTUPINFO);
si.lpDesktop = "winsta0//default";
ZeroMemory(&pi, sizeof(pi));
TOKEN_PRIVILEGES tp;
LUID luid;
hProcess = OpenProcess(MAXIMUM_ALLOWED,FALSE,winlogonPid);

if(!::OpenProcessToken(hProcess,TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY
|TOKEN_DUPLICATE|TOKEN_ASSIGN_PRIMARY|TOKEN_ADJUST_SESSIONID
|TOKEN_READ|TOKEN_WRITE,&hPToken))
{
int abcd = GetLastError();
printf("Process token open Error: %u/n",GetLastError());
}

if (!LookupPrivilegeValue(NULL,SE_DEBUG_NAME,&luid))
{
printf("Lookup Privilege value Error: %u/n",GetLastError());
}
tp.PrivilegeCount =1;
tp.Privileges[0].Luid =luid;
tp.Privileges[0].Attributes =SE_PRIVILEGE_ENABLED;

DuplicateTokenEx(hPToken,MAXIMUM_ALLOWED,NULL,
SecurityIdentification,TokenPrimary,&hUserTokenDup);
int dup = GetLastError();

//Adjust Token privilege

SetTokenInformation(hUserTokenDup,
TokenSessionId,(void*)dwSessionId,sizeof(DWORD));

if (!AdjustTokenPrivileges(hUserTokenDup,FALSE,&tp,sizeof(TOKEN_PRIVILEGES),
(PTOKEN_PRIVILEGES)NULL,NULL))
{
int abc =GetLastError();
printf("Adjust Privilege value Error: %u/n",GetLastError());
}

if (GetLastError()== ERROR_NOT_ALL_ASSIGNED)
{
printf("Token does not have the provilege/n");
}

LPVOID pEnv =NULL;

if(CreateEnvironmentBlock(&pEnv,hUserTokenDup,TRUE))
{
dwCreationFlags|=CREATE_UNICODE_ENVIRONMENT;
}
else
pEnv=NULL;

// Launch the process in the client's logon session.


bResult = CreateProcessAsUser(
hUserTokenDup, // client's access token

_T("C://SessionLauncher//a.exe"), // file to execute

NULL, // 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

);
// End impersonation of client.


//GetLastError Shud be 0


int iResultOfCreateProcessAsUser = GetLastError();

//Perform All the Close Handles tasks


CloseHandle(hProcess);
CloseHandle(hUserToken);
CloseHandle(hUserTokenDup);
CloseHandle(hPToken);

return 0;
}

This way a normal user mode application can send a custom message to a service to launch itself under the local system account without the UAC dialog box popping up.

Some readers wanted to know how to:

  1. Get GetUserName() API to return the current logged on user
  2. Access the HKCU under the system account

Well, it is virtually impossible from the userland to surpass the UAC dialog when we launch a process which requires elevation, since it is designed that way by Microsoft. Although it may be possible to effect these changes through writing some kernel mode code (DKOM concepts). But still from the userland, the best I could think about was to impersonate the system account to act as the logged on user to access the HKCU. I use the Explorer process for this purpose since it runs under the user account.

Impersonating the user token will cause the current worker thread to run under the user context. Note that if you use CreateProcess() it will still spawn a process under the System Account since our overall process is still running under the local system account.

The code to do that is listed in the snippet below. This code needs to be written into the application which will be launched by the service. I have not included this piece of code in the "SessionLaucher.zip"

Collapse
DWORD dwSessionId,dwExplorerLogonPid,dwSize,dwRegDataSize;
HANDLE hProcess,hPToken;
char szUserName[MAX_PATH];
char szRegData[MAX_PATH];
char szRegPath[500] = "Software//Microsoft//Windows//CurrentVersion//Run";
HKEY hKey; //Handle to registry Key

long lRegResult; //Registry operation result


//Get the active desktop session id

dwSessionId = WTSGetActiveConsoleSessionId();

//We find the explorer process since it will have the user token


//////////////////////////////////////////

// Find the explorer process

////////////////////////////////////////


PROCESSENTRY32 procEntry;

HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (hSnap == INVALID_HANDLE_VALUE)
{
return 1 ;
}

procEntry.dwSize = sizeof(PROCESSENTRY32);

if (!Process32First(hSnap, &procEntry))
{
return 1 ;
}

do
{
if (_stricmp(procEntry.szExeFile, "explorer.exe") == 0)
{
DWORD dwExplorerSessId = 0;
if (ProcessIdToSessionId(procEntry.th32ProcessID, &dwExplorerSessId)
&& dwExplorerSessId == dwSessionId)
{
dwExplorerLogonPid = procEntry.th32ProcessID;
break;
}
}

} while (Process32Next(hSnap, &procEntry));

////////////////////////////////////////////////////////////////////////

hProcess = OpenProcess(MAXIMUM_ALLOWED,FALSE,dwExplorerLogonPid);

if(!::OpenProcessToken(hProcess,TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY
|TOKEN_DUPLICATE|TOKEN_ASSIGN_PRIMARY|TOKEN_ADJUST_SESSIONID
|TOKEN_READ|TOKEN_WRITE,&hPToken))
{
int abcd = GetLastError();
printf("Process token open Error: %u/n",GetLastError());
}

We need to impersonate the service token to run as a user to access the Registry. This will cause our worker thread to run in the users token's context.

Collapse
//Impersonate the explorer token which runs under the user account

ImpersonateLoggedOnUser(hPToken);

int iImpersonateResult = GetLastError();

if(iImpersonateResult == ERROR_SUCCESS)
{
//GetUserName will now return the username

GetUserName(szUserName,&dwSize);

//Since the thread is running as the user we can access the HKCU now

dwRegDataSize = sizeof(szRegData);
lRegResult = RegOpenKeyEx(HKEY_CURRENT_USER,
szRegPath,0,KEY_QUERY_VALUE,&hKey);
if (lRegResult == ERROR_SUCCESS)
RegQueryValueEx(hKey,_T("SideBar"),NULL,NULL,
(LPBYTE)&szRegData,&dwRegDataSize);
}
//Once the operation is over revert back to system account.

RevertToSelf();

I have taken a simple service posted by V.Anish to add my code to obtain the Winlogon's token and launch the application. It can be installed up typing service.exe -i. Then you need to start the service to get it running.

If you have any doubts, you can contact me at jaisvar@gmail.com.

In my next article, I will discuss some more concepts about tweaking the UAC.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

智能交通灯设计是现代城市交通管理中的重要环节,利用STM32单片机进行智能交通灯控制能够提高交通效率,减少交通事故。STM32是一款基于ARM Cortex-M内核的微控制器,具有高性能、低功耗的特点,广泛应用于各种嵌入式系统设计。本项目将介绍如何使用STM32单片机配合Proteus仿真软件来实现智能交通灯系统的设计。 我们需要了解STM32的基本结构和工作原理。STM32家族包含了多种型号,它们拥有不同的内存大小、外设接口和性能等级。在这个项目中,我们可能使用的是STM32F10x系列,它具备GPIO、定时器、串行通信接口等丰富的外设资源,适合交通灯控制的需求。 智能交通灯系统通常由红绿黄三色灯组成,通过特定的时序来控制各个方向的车辆和行人通行。在设计时,我们需要考虑以下几个关键知识点: 1. **硬件接口设计**:STM32通过GPIO口连接到交通灯的LED驱动电路,设置GPIO的工作模式(如推挽输出或开漏输出),并根据交通规则控制LED灯的亮灭。 2. **定时器配置**:利用STM32的定时器功能设定交通灯各阶段的持续时间。可以使用定时器的中断功能,在特定时间点切换交通灯状态。 3. **程序逻辑**:编写C语言程序实现交通灯的逻辑控制。这包括初始化GPIO和定时器,设置交通灯状态的切换逻辑,并处理中断服务函数。 4. **Proteus仿真**:Proteus是一款强大的电子电路仿真软件,可以模拟硬件电路运行和程序执行。在这里,我们将STM32单片机模型和交通灯模型添加到仿真环境中,运行程序并观察交通灯的正确运行。 5. **调试与优化**:在Proteus中,可以通过查看虚拟示波器或逻辑分析仪来检查信号波形,帮助定位程序中的错误。通过反复调试,优化交通灯的控制算法,确保其符合实际交通需求。 6. **全套资料**:压缩包内的资料可能包括源代码
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值