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

2023-05-16

This article describes how to launch an application from session 0 to session 1 under the local system account using a service helper application
  • Download source files (SessionLauncher.zip) - 23.0 KB
  • Download source files (a.zip) - 4.8 KB

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 | Copy Code

#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 | Copy Code

//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 | Copy Code

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 | Copy Code

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 | Copy Code

//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.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Launch your application in Vista under the local system account without the UAC popup 的相关文章

随机推荐

  • linux.和stm32驱动的区别

    linux下的驱动和stm32驱动的区别 xff1f stm32的驱动分为2部分 xff0c 初始化和发送接收数据 xff0c 这个理论概括了spi xff0c iic xff0c uart xff0c sdio xff0c usb 在li
  • SUMO入门(四) - 需求建模 车辆和路线的定义

    SUMO入门 四 需求建模 车辆和路线的定义 Demand Introduction to demand modelling in SUMO 在生成网络之后 xff0c 可以使用SUMO GUI来查看它 xff0c 但是没有汽车可以行驶 人
  • 图像处理特征可视化方法总结(特征图、卷积核、类可视化CAM)(附代码)

    一 前言 众所周知 xff0c 深度学习是一个 34 黑盒 34 系统 它通过 end to end 的方式来工作 xff0c 输入数据例如RGB图像 xff0c 输出目标例如类别标签 回归值等 xff0c 中间过程不可得知 如何才能打开
  • deian10 安装aptitude

    debian10系统不会默认安装aptitude xff0c 导致需要处理大量依赖关系的软件安装变得很麻烦 常规的apt源可以安装aptitude xff0c 但是需要手动处理大量的依赖包安装 可以通过如下apt源使用apt get来安装a
  • DJI OSDK开发笔记(N3飞控)(1)——开发工作流程

    DJI OSDK开发笔记 xff08 N3飞控 xff09 xff08 1 xff09 开发工作流程 API层次结构硬件设置一般设置数据串口 连接器引脚排列连接到记载计算机 软件环境设置所有平台下载SDK和所需工具更新固件启用OSDK AP
  • Windows Vista 交互式服务编程

    Windows Vista 对快速用户切换 xff0c 用户账户权限 xff0c 以及服务程序所运行的会话空间都作了很大的改动 xff0c 致使一些原本可以工作的程序不再能够正常工作了 xff0c 我们不得不进行一些改进以跟上 Vista
  • Windows2000 服务器端应用程序开发设计指南-信任成员的管理

    Microsoft的开发者已经完成Microsoft Windows 2000安全性特色的设计工作 xff0c 这些安全性特色比大多数人所习惯的环境更复杂且更有弹性 事实上 xff0c 若加上适当的管理和软件开发 xff0c Windows
  • NoDriveTypeAutoRun键值的作用

    常见的Autorun inf文件格式大致如下 xff1a AutoRun 表示AutoRun部分开始 xff0c 必须输入 icon 61 C ixigua ico 指定给C盘一个个性化的盘符图标C ico open 61 C ixigua
  • Windows系统调用架构分析—也谈KiFastCallEntry函数地址的获取 .

    为什么要写这篇文章 1 因为最近在学习 软件调试 这本书 xff0c 看到书中的某个调试历程中讲了Windows 的系统调用的实现机制 xff0c 其中讲到了从Ring3 跳转到Ring0 之后直接进入了KiFastCallEntry 这个
  • ubuntu rc.local不能正常运行

    查了下rc local有时不能正常运行的原因 xff1a Ubuntu默认将 bin sh链接到 bin dash xff0c 而 etc rc local脚本中用的正是 bin sh xff0c 导致出错 将默认的shell改成bash的
  • 关于建设symbol store的建议

    xfeff xfeff 一 symbol store的需求分析 xff1a 1 我们现在的调试环境严重依赖开发人员自己使用的开发环境 xff0c 缺点在于其他人要进行调试要么搭建一个同样的环境 xff0c 严重地占去大家不必要花费的工作时间
  • 进程防结束之PS_CROSS_THREAD_FLAGS_SYSTEM

    有人投到黑防去了 xff0c 不过黑防不厚道 xff0c 竟然没给完整的代码 xff0c 自己整理一份备用吧 xff0c 驱网 DebugMan 邪八的那群人直接飘过吧 这种方法的关键在于给线程的ETHREAD CrossThreadFla
  • CNN实现入侵检测(kdd99)

    文章目录 1 实验说明2 实验过程2 1 数据预处理2 1 1 导入数据2 1 2 one hot编码2 1 3 归一化2 1 4 标签编码 2 2 数据加载2 3 搭建模型2 4 模型训练 3 实验结果4 完整代码 1 实验说明 CNN模
  • All about VDIs

    This tutorial is an experiment to see if forum users find it useful to have a single collected reference for some common
  • PUTTY无法远程连接服务器故障解决

    对于一个刚刚了解putty工具的新手来说 xff0c 在putty工具使用中有时出现了问题而无法解决 今天就来介绍怎么解决putty无法远程连接服务器的故障 用putty远程连接服务器时 提示错误 server unexpectedlycl
  • 驱动中获取进程名的正确方法

    这是个古老的话题 xff0c 没有技术含量 xff0c 只不过看到网上很多驱动代码在获取进程名时总喜欢去读偏移 EPROCESS ImageFileName xff0c 感觉比较误导新人 这个方法其实很不科学 xff0c 硬编码偏移带来的兼
  • C语言写二进制数据到mysql的Blob字段

    引子 由于调试需要 xff0c 需直接往数据库里写入二进制数据 本来这些数据是由上层软件来写的 xff0c 用的是 C 为了熟悉 C 语言的数据库操作 xff0c 还是决定用 C 来写这段调试代码 概况 xff1a 表名 xff1a Tas
  • 编译器 链接 选项解释:link incrementally的作用

    What is ILT xff08 Incremental Link Table 这两天研究了一下DLL的import export原理 xff0c 看了一些资料 xff0c 无意中发现网上有一篇文章存在错误 xff0c 而这篇文章流传还甚
  • 关于涉密信息系统分级保护的几个问题

    2003年9月7日 xff0c 中共中央办公厅 国务院办公厅转发了 国家信息化领导小组关于加强国家信息安全保障工作的意见 xff0c 其中明确提出了开展信息安全等级保护的任务 xff0c 并指出涉及国家秘密的信息系统 xff08 以下简称涉
  • Launch your application in Vista under the local system account without the UAC popup

    This article describes how to launch an application from session 0 to session 1 under the local system account using a s