一些系统默认.lnk文件的问题从模拟用户下启动

时间:2015-08-03 12:49:44

标签: c++ windows winapi impersonation lnk

我正在编写32位服务应用,我希望能够为已记录的用户启动“开始”菜单项。我设法通过模拟用户并使用 CreateProcessAsUser 和命令行%windir%\system32\cmd /c " start /b /i "" "<path-to-lnk-file>" "启动选定的.lnk文件来完成此任务。除了来自附件文件夹的系统快捷方式(例如Sticky Notes.lnk,Snipping Tool.lnk)之外,它几乎适用于所有快捷方式。在启动截图工具期间,我收到了来自cmd的错误信息框:

  

Windows找不到&#39; C:\ ProgramData \ Microsoft \ Windows \ Start Menu \ Programs \ Accessories \ Snipping Tool.lnk&#39;。确保正确输入名称,然后重试。

但是.lnk文件存在于这个目录中!

要点:

  • 服务是32位
  • Windows 8 Pro x64
  • 通过用户模拟启动快捷方式,使用命令行%windir%\system32\cmd /c " start /b /i "" "<path-to-lnk-file>" "启动CreateProcessAsUser
  • 方法适用于“开始”菜单中的几乎所有快捷方式,但“开始”/“附件”文件夹中的某些快捷方式除外(不是全部,例如Paint.lnk打开正常)
  • 示例代码:

    int launchAppForCurrentLoggedUser() 
    {
        HANDLE userToken = WTSApiHelper::currentLoggedUserToken();
        if (userToken == INVALID_HANDLE_VALUE) {
            return -1;
        }
    
        //Duplicating token with access TOKEN_DUPLICATE | TOKEN_ALL_ACCESS, 
        //impersonation level SecurityImpersonation and token type TokenPrimary.
        //Also closing original userToken
        HANDLE dup = WTSApiHelper::duplicateToken(userToken);
        if (dup == INVALID_HANDLE_VALUE) {
            return -1;
        }
    
        int res = -1;
    
        uint8 *env = NULL;
        BOOL succeeded = CreateEnvironmentBlock((LPVOID *)&env, dup, FALSE);
        if (!succeeded) {
            Log("failed to get environment variables for user (error 0x%x).", GetLastError());
        }
    
        PROCESS_INFORMATION pi;
        memset(&pi, 0, sizeof(PROCESS_INFORMATION));
    
        STARTUPINFOW si;
        memset(&si, 0, sizeof(STARTUPINFOW));
        si.cb = sizeof(STARTUPINFOW);
        si.lpDesktop = L"winsta0\\Default";
        WCHAR params[] = L"/c \" start /b /i \"\" \"C:\\ProgramData\\Microsoft\\Windows\\Start Menu\\Programs\\Accessories\\Snipping Tool.lnk\" \" ";
        WCHAR cmd[] = L"C:\\Windows\\system32\\cmd.exe";
    
        DWORD flags = env ? CREATE_UNICODE_ENVIRONMENT : 0;
        succeeded = CreateProcessAsUserW(dup, cmd, params, NULL, NULL, FALSE, flags | CREATE_NO_WINDOW, env, NULL, &si, &pi);
        if (!succeeded) {
            Log("cannot launch process for user with error 0x%x.", GetLastError());
        } else {
            nres = 0;
        }
    
        DestroyEnvironmentBlock(env);
        CloseHandle(dup);
    
        return nres;
    }
    

我在这里想念什么?

2 个答案:

答案 0 :(得分:3)

它不是丢失的LNK文件,而是它的目标。

看起来像WOW64问题 - 对于32位服务,%WINDIR%\System32实际上重定向到SysWOW64,并且那里不存在这些可执行文件。

嗯,实际上你的32位服务正在查找cmd.exe中存在的32位SysWOW64,然后在查找路径时32位cmd.exe出现上述问题在快捷方式中找到%windir%\system32\SnippingTool.exe

我可以使用32位命令提示符重现该问题。尝试使用这些快捷方式的32位进程只会失败。

尝试使用cmd.exe

生成%WINDIR%\SysNative\cmd.exe的原生版本(系统上为64位)

此外,您还有引用问题。您试图嵌套引号,但实际发生的是第二个引号与第一个引号匹配并退出引用,而不是嵌套。

将来,当服务失败时,从普通的控制台应用程序运行相同的调用会很有帮助。在这种情况下,您会立即发现该问题与模仿完全无关。第二步,如果它从运行in-profile的控制台应用程序工作,将使用&#34; Run As&#34;使用控制台应用程序来测试模拟逻辑,仍然没有服务环境的额外复杂性。

答案 1 :(得分:0)

  

CreateProcessAsUser不会将指定用户的配置文件加载到HKEY_USERS注册表项中。因此,要访问HKEY_CURRENT_USER注册表项中的信息,必须在调用CreateProcessAsUser之前使用LoadUserProfile函数将用户的配置文件信息加载到HKEY_USERS中。确保在新进程退出后调用UnloadUserProfile。

According to the msdn page

MSDN建议使用CreateProcessWithLogonW或CreateProcessWithTokenW,或手动加载用户的个人资料信息。

还有:

  

CreateProcessAsUser允许您在调用者或目标用户的安全上下文中访问指定的目录和可执行映像。默认情况下,CreateProcessAsUser访问调用方的安全上下文中的目录和可执行映像。在这种情况下,如果调用者无权访问目录和可执行映像,则该函数将失败。要使用目标用户的安全上下文访问目录和可执行映像,请在调用CreateProcessAsUser之前在 ImpersonateLoggedOnUser 函数的调用中指定hToken。