3

我正在开发一个在后台运行(可能)很长时间的实用程序,并在系统重新启动时自动恢复。该实用程序需要提升权限才能运行(即“以管理员身份运行”),因此我不能依赖注册表(HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run)的“运行”部分,因为它只会启动具有默认权限的进程。

因此,我正在使用带有“登录”触发器的计划任务,该触发器使用 TaskScheduler API,运行级别为“TASK_RUNLEVEL_HIGHEST”,它以提升的权限启动应用程序(并且没有 UAC,这是必需的)。到目前为止,一切都很好。

问题是此应用程序还将凭据存储在凭据管理器中,并在重新启动后重新读取它们(例如,当触发上述计划任务时)。这是使用 advapi32.dll 中的 CredRead()/CredWrite() 函数完成的。由于某种原因,当使用 RUNLEVEL_HIGHEST 启动任务时,CredRead() 函数返回 false,并且 GetLastError() 返回 1168(“未找到元素”)。

根据文档,CredWrite() 存储的凭据与当前用户令牌的登录会话相关联;我使用持久类型“LOCAL_MACHINE”存储了我的凭据,即它应该在当前会话之外持续存在 - 事实上,即使任务无法读取它,它也会显示在凭据管理器中。

因此,似乎以最高权限启动的任务导致它无法看到最初启动实用程序时创建的凭据 - 即使应用程序必须以提升的权限启动(否则它甚至在之前由于不相关的原因而失败存储凭证)。

FWIW,我认为问题在于,由于从“以管理员身份运行”命令提示符运行而导致的提升,以及由于作为具有“以最高权限运行”的任务而启动的提升在某种程度上导致了不同的“级别”海拔。我觉得我只是缺少有关如何创建计划任务或 CredWrite/CredRead 的东西。任何帮助深表感谢!

更新:根据 CodyGray 在评论中的建议,我尝试在应用清单中设置 requiredExecutionLevel。这并没有改变我原来的问题的行为。

我也查过,当前WindowsIdentity( WindowsIdentity.GetCurrent())的token、user SID、各种“IsAuthenticated”等属性在工作和非工作情况下都是一样的。

我还添加了对 CredEnumerate() 的调用,它返回“false”并将错误代码设置为 1168(“未找到”,与上面的 CredRead() 相同)。因此,这表明该过程无法找到该用户的任何存储凭据,而不仅仅是我的应用程序的凭据。

最后,如果我从任务计划程序手动运行任务,它会按预期工作并找到存储的凭据。该问题似乎仅在登录时触发任务时出现。

更新#2

我(简要地)能够在开发机器上重现该问题,这是我以前无法做到的。似乎只有当用户是两个组(例如“用户”和“管理员”)的成员时才会出现问题。我的默认开发箱登录名只是“管理员”的成员。当我创建一个属于这两个组的新用户时,我重现了这个问题。但是,在仅用户/仅管理员/两者之间来回切换用户后,我无法再重现该用户的问题(即使他们又回到两个组中)。

我找到了一种解决方法,即将凭证存储在其他地方(目前可以接受,不是理想的长期,但最终这些都不是超敏感的凭证)。但是,我仍然想了解这里发生了什么。我有时间会继续试验,但同时感谢任何见解!

4

1 回答 1

2

我终于想通了:在 Windows 任务计划程序中右键单击您的任务->单击属性打开一个新窗口。导航到触发器选项卡。点击编辑。在高级设置下,勾选“延迟任务”复选框并指定一个时间,比如 10 分钟。单击确定以保存您的设置。现在关闭任务计划程序。您的计划任务应该能够从凭证管理器中读取凭证。此设置也可以通过使用任务调度程序 api 的代码进行配置。

于 2015-09-08T09:18:58.490 回答