Col*_*n B 2 windows batch-file
脚本
我有一台远程计算机,我想以编程方式运行安装程序(任意可执行文件).这些安装程序需要两件事:
事实证明这非常具有挑战性.
看起来好像有一些外部工具可以做到这一点,但我正在寻找Windows附带的解决方案.
这个问题的有效解决方案是什么样的
从提升的上下文(例如,提升的批处理文件或可执行程序),有效的解决方案应该能够以管理员模式在另一个用户上下文下以编程方式启动进程.假设其他用户的ID和密码可用,而另一个用户是Administrators组的成员.附加限制:
请在发布前测试您的解决方案以确保其有效!如果您要提供指向其他解决方案的链接,请在发布之前验证链接的解决方案是否有效.许多声称已经解决这个问题的人实际上没有.
我试过了什么
我尝试过使用Batch Scripts,PowerShell和C#.据我所知,这些技术都不能完成任务.它们都遭受同样的基本问题 - 以另一个用户身份运行任务并且在管理员模式下是相互排斥的进程.让我更具体一点:
为何不批量
用于在不同用户上下文下运行的命令是Runas,它不会启动提升的进程.有几种外部工具可以解决这个问题,但如前所述,这些工具是不允许的.
为什么不PowerShell
启动新进程Start-Process的命令可以提升新进程并以不同用户身份运行,但不能同时运行.我在这里提到了一个未解决的问题.不幸的是,没有人提供解决方案,这让我相信这是不可能的.
为什么不C#
这似乎也是不可能的,因为Process类似乎不支持在管理员模式下以及在不同用户的凭据下启动进程.
为什么不是外部工具?
这迫使我依赖别人的代码来做正确的事情,我宁愿自己编码而不是这样做.事实上,我有一个比依赖别人更好的解决方案,但是相当hackish:
提前感谢任何试图帮助的人!非常感谢,我希望如果没有别的,其他人能够找到这个任务计划程序的工作.
好的,事实证明该CreateProcessWithLogonW功能可以过滤用户令牌,也是如此LogonUser.这似乎让我们坚持,因为我们没有正确的权限来解决该问题(见注),但事实证明,LogonUser确实没有,如果你使用过滤令牌LOGON32_LOGON_BATCH,而不是LOGON32_LOGON_INTERACTIVE.
这是一些实际可行的代码.我们使用该CreateProcessAsTokenW函数来启动进程,因为此特定变体仅需要SE_IMPERSONATE_NAME特权,默认情况下授予管理员帐户.
此示例程序启动一个子进程,该子进程创建一个目录c:\windows\system32,如果子进程未提升则无法实现.
#define _WIN32_WINNT 0x0501
#include <Windows.h>
#include <Sddl.h>
#include <conio.h>
#include <stdio.h>
wchar_t command[] = L"c:\\windows\\system32\\cmd.exe /c md c:\\windows\\system32\\proof-that-i-am-an-admin";
int main(int argc, char **argv)
{
HANDLE usertoken;
STARTUPINFO sinfo;
PROCESS_INFORMATION pinfo;
ZeroMemory(&sinfo, sizeof(sinfo));
sinfo.cb = sizeof(sinfo);
if (!LogonUser(L"username", L"domain", L"password", LOGON32_LOGON_BATCH, LOGON32_PROVIDER_DEFAULT, &usertoken))
{
printf("LogonUser: %u\n", GetLastError());
return 1;
}
if (!CreateProcessWithTokenW(usertoken, LOGON_WITH_PROFILE, L"c:\\windows\\system32\\cmd.exe", command, 0, NULL, NULL, &sinfo, &pinfo))
{
printf("CreateProcess: %u\n", GetLastError());
return 1;
}
return 0;
}
Run Code Online (Sandbox Code Playgroud)
但是,如果目标进程是GUI进程(包括具有可见控制台的进程),则它将无法正常显示.显然CreateProcessWithTokenW只分配运行进程所需的最低桌面和窗口站权限,这不足以实际显示GUI.
即使您实际上不需要查看输出,也可能会导致损坏的GUI导致程序出现功能问题.
因此,除非目标进程在后台运行,否则我们应该适当地分配权限.通常,最好创建一个新的窗口站和一个新的桌面,以隔离目标进程; 但是,在这种情况下,目标进程将以管理员身份运行,所以没有意义 - 只需更改现有窗口站和桌面的权限,我们就可以简化生活.
编辑2014年11月24日:更正了窗口站ACE中的访问权限,以便它们适用于非管理用户.请注意,这样做可能会使有问题的非管理员用户破坏目标会话中的进程.
#define _WIN32_WINNT 0x0501
#include <Windows.h>
#include <AccCtrl.h>
#include <Aclapi.h>
#include <stdio.h>
wchar_t command[] = L"c:\\windows\\system32\\notepad.exe";
int main(int argc, char **argv)
{
HANDLE usertoken;
STARTUPINFO sinfo;
PROCESS_INFORMATION pinfo;
HDESK desktop;
EXPLICIT_ACCESS explicit_access;
BYTE buffer_token_user[SECURITY_MAX_SID_SIZE];
PTOKEN_USER token_user = (PTOKEN_USER)buffer_token_user;
PSECURITY_DESCRIPTOR existing_sd;
SECURITY_DESCRIPTOR new_sd;
PACL existing_dacl, new_dacl;
BOOL dacl_present, dacl_defaulted;
SECURITY_INFORMATION sec_info_dacl = DACL_SECURITY_INFORMATION;
DWORD dw, size;
HWINSTA window_station;
if (!LogonUser(L"username", L"domain", L"password", LOGON32_LOGON_BATCH, LOGON32_PROVIDER_DEFAULT, &usertoken))
{
printf("LogonUser: %u\n", GetLastError());
return 1;
}
if (!GetTokenInformation(usertoken, TokenUser, buffer_token_user, sizeof(buffer_token_user), &dw))
{
printf("GetTokenInformation(TokenUser): %u\n", GetLastError());
return 1;
}
window_station = GetProcessWindowStation();
if (window_station == NULL)
{
printf("GetProcessWindowStation: %u\n", GetLastError());
return 1;
}
if (!GetUserObjectSecurity(window_station, &sec_info_dacl, &dw, sizeof(dw), &size) && GetLastError() != ERROR_INSUFFICIENT_BUFFER)
{
printf("GetUserObjectSecurity(window_station) call 1: %u\n", GetLastError());
return 1;
}
existing_sd = malloc(size);
if (existing_sd == NULL)
{
printf("malloc failed\n");
return 1;
}
if (!GetUserObjectSecurity(window_station, &sec_info_dacl, existing_sd, size, &dw))
{
printf("GetUserObjectSecurity(window_station) call 2: %u\n", GetLastError());
return 1;
}
if (!GetSecurityDescriptorDacl(existing_sd, &dacl_present, &existing_dacl, &dacl_defaulted))
{
printf("GetSecurityDescriptorDacl(window_station): %u\n", GetLastError());
return 1;
}
if (!dacl_present)
{
printf("no DACL present on window station\n");
return 1;
}
explicit_access.grfAccessMode = SET_ACCESS;
explicit_access.grfAccessPermissions = WINSTA_ALL_ACCESS | READ_CONTROL;
explicit_access.grfInheritance = NO_INHERITANCE;
explicit_access.Trustee.pMultipleTrustee = NULL;
explicit_access.Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE;
explicit_access.Trustee.TrusteeForm = TRUSTEE_IS_SID;
explicit_access.Trustee.TrusteeType = TRUSTEE_IS_USER;
explicit_access.Trustee.ptstrName = (LPTSTR)token_user->User.Sid;
dw = SetEntriesInAcl(1, &explicit_access, existing_dacl, &new_dacl);
if (dw != ERROR_SUCCESS) {
printf("SetEntriesInAcl(window_station): %u\n", dw);
return 1;
}
if (!InitializeSecurityDescriptor(&new_sd, SECURITY_DESCRIPTOR_REVISION))
{
printf("InitializeSecurityDescriptor(window_station): %u\n", GetLastError());
return 1;
}
if (!SetSecurityDescriptorDacl(&new_sd, TRUE, new_dacl, FALSE))
{
printf("SetSecurityDescriptorDacl(window_station): %u\n", GetLastError());
return 1;
}
if (!SetUserObjectSecurity(window_station, &sec_info_dacl, &new_sd))
{
printf("SetUserObjectSecurity(window_station): %u\n", GetLastError());
return 1;
}
free(existing_sd);
LocalFree(new_dacl);
desktop = GetThreadDesktop(GetCurrentThreadId());
if (desktop == NULL)
{
printf("GetThreadDesktop: %u\n", GetLastError());
return 1;
}
if (!GetUserObjectSecurity(desktop, &sec_info_dacl, &dw, sizeof(dw), &size) && GetLastError() != ERROR_INSUFFICIENT_BUFFER)
{
printf("GetUserObjectSecurity(desktop) call 1: %u\n", GetLastError());
return 1;
}
existing_sd = malloc(size);
if (existing_sd == NULL)
{
printf("malloc failed\n");
return 1;
}
if (!GetUserObjectSecurity(desktop, &sec_info_dacl, existing_sd, size, &dw))
{
printf("GetUserObjectSecurity(desktop) call 2: %u\n", GetLastError());
return 1;
}
if (!GetUserObjectSecurity(desktop, &sec_info_dacl, existing_sd, 4096, &dw))
{
printf("GetUserObjectSecurity: %u\n", GetLastError());
return 1;
}
if (!GetSecurityDescriptorDacl(existing_sd, &dacl_present, &existing_dacl, &dacl_defaulted))
{
printf("GetSecurityDescriptorDacl: %u\n", GetLastError());
return 1;
}
if (!dacl_present)
{
printf("no DACL present\n");
return 1;
}
explicit_access.grfAccessMode = SET_ACCESS;
explicit_access.grfAccessPermissions = GENERIC_ALL;
explicit_access.grfInheritance = NO_INHERITANCE;
explicit_access.Trustee.pMultipleTrustee = NULL;
explicit_access.Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE;
explicit_access.Trustee.TrusteeForm = TRUSTEE_IS_SID;
explicit_access.Trustee.TrusteeType = TRUSTEE_IS_USER;
explicit_access.Trustee.ptstrName = (LPTSTR)token_user->User.Sid;
dw = SetEntriesInAcl(1, &explicit_access, existing_dacl, &new_dacl);
if (dw != ERROR_SUCCESS) {
printf("SetEntriesInAcl: %u\n", dw);
return 1;
}
if (!InitializeSecurityDescriptor(&new_sd, SECURITY_DESCRIPTOR_REVISION))
{
printf("InitializeSecurityDescriptor: %u\n", GetLastError());
return 1;
}
if (!SetSecurityDescriptorDacl(&new_sd, TRUE, new_dacl, FALSE))
{
printf("SetSecurityDescriptorDacl: %u\n", GetLastError());
return 1;
}
if (!SetUserObjectSecurity(desktop, &sec_info_dacl, &new_sd))
{
printf("SetUserObjectSecurity(window_station): %u\n", GetLastError());
return 1;
}
free(existing_sd);
LocalFree(new_dacl);
ZeroMemory(&sinfo, sizeof(sinfo));
sinfo.cb = sizeof(sinfo);
if (!CreateProcessWithTokenW(usertoken, LOGON_WITH_PROFILE, L"c:\\windows\\system32\\notepad.exe", command, 0, NULL, NULL, &sinfo, &pinfo))
{
printf("CreateProcess: %u\n", GetLastError());
return 1;
}
return 0;
}
Run Code Online (Sandbox Code Playgroud)
注意使用LOGON_WITH_PROFILE.这不是显示GUI的必要条件,它会大大减慢启动过程,因此如果您不需要它,请将其删除 - 但如果您是管理员,则最有可能的原因是您以不同的管理员身份启动流程是你需要管理员的用户档案中的东西.(另一种情况可能是您需要使用特定的域帐户才能访问另一台计算机上的资源.)
脚注:
具体来说,您需要SeTcbPrivilege使用GetTokenInformation和TokenLinkedToken获取LogonUser生成的提升令牌的可用句柄.遗憾的是,此权限通常仅在您作为本地系统运行时才可用.
如果没有,SeTcbPrivilege您仍然可以获得链接令牌的副本,但在这种情况下,它是SecurityIdentification级别的模拟令牌,因此在创建新进程时没有用处.感谢RbMm帮助我澄清这一点.
| 归档时间: |
|
| 查看次数: |
3150 次 |
| 最近记录: |