gpt4 book ai didi

c# - 编辑新创建用户的注册表值

转载 作者:行者123 更新时间:2023-11-30 21:52:58 29 4
gpt4 key购买 nike

我有一个.NET应用程序,它可以像这样创建一个新的本地用户:

var principalContext = new PrincipalContext(ContextType.Machine);
var userPrincipal = new UserPrincipal(principalContext);
userPrincipal.Name = StandardUserName.Text;
userPrincipal.Description = "New local user";
userPrincipal.UserCannotChangePassword = true;
userPrincipal.PasswordNeverExpires = true;
userPrincipal.Save();

// Add user to the users group
var usersGroupPrincipal = GroupPrincipal.FindByIdentity(principalContext, UserGroupName.Text);
usersGroupPrincipal.Members.Add(userPrincipal);
usersGroupPrincipal.Save();


接下来,我要为该用户设置一些注册表值。为此,我需要用户的SID:

private string GetSidForStandardUser()
{
var principalContext = new PrincipalContext(ContextType.Machine);
var standardUser = UserPrincipal.FindByIdentity(principalContext, StandardUserName.Text);
return standardUser.Sid.ToString();
}


并创建一个新的子项:

var key = string.Format("{0}{1}", GetSidForStandardUser(), keyString);
var subKey = Registry.Users.CreateSubKey(key, RegistryKeyPermissionCheck.ReadWriteSubTree);


但是,我在对CreateSubKey的调用上收到IOException,该异常告诉我Parameter无效。这是因为该用户的子项在用户首次登录之前尚不存在。如果在以新用户身份登录之前检查regedit(具有管理员权限),则可以看到SID在HKEY_Users下不存在。如果我以新用户身份登录,然后注销并以我的原始用户身份重新登录并刷新regedit,则新的SID存在。

我的问题是:有没有办法为尚未登录的用户添加子项?我想避免必须以新用户身份登录,然后在过程中半途退出。

最佳答案

从那以后,我找到了解决问题的方法,但是它并不漂亮,而且会引起您必须处理的各种新问题。仍然可以。我将解决方案发布在这里,以供我自己以及将来可能需要的其他人参考。

问题在于用户的注册表配置单元位于其用户配置文件文件夹(例如c:\ users \ username)中的NTUSER.DAT文件中。但是,用户的用户配置文件文件夹只有在他们登录后才会创建,因此在创建新用户时,尚无用户配置文件,也没有包含其注册表配置单元的NTUSER.DAT文件,因此您无法编辑其任何注册表设置。

不过,这有个窍门:当您在该用户的凭据下运行某些内容时,会创建用户配置文件。有一个名为runas.exe的可执行文件,可让您在指定用户的凭据下运行第二个可执行文件。如果创建一个新用户并使其运行,请说cmd.exe,如下所示:

runas /user:newuser cmd.exe


...它将打开一个Cmd实例,但更重要的是,在\ users文件夹(包括NTUSER.DAT)中创建newuser的配置文件。

现在,Cmd.exe使命令窗口保持打开状态,您可以手动关闭该命令窗口,但这有点笨拙。 https://superuser.com/a/389288将我指向rundll32.exe,当运行该程序时不带任何参数,则不执行任何操作并立即退出。此外,每个Windows安装都可以使用它。

因此,通过调用runas并告诉它以新用户身份运行rundll32.exe,我们可以创建用户的配置文件,而无需任何进一步的交互:

Process.Start("runas", string.Format("/user:{0} rundll32", "newuser"));


好吧...几乎没有互动。 Runas将打开一个控制台窗口,即使没有设置密码,它也要求您输入用户的密码(它希望您仅按Enter键)。这很烦人,但是可以通过巧妙地使用Pinvoke和可选的System.Windows.Forms来将窗口置于前台并发送一些按键来解决:

var createProfileProcess = Process.Start("runas", 
string.Format("/user:{0} rundll32",
"newuser"));

IntPtr hWnd;
do
{
createProfileProcess.Refresh();
hWnd = createProfileProcess.MainWindowHandle;
} while (hWnd.ToInt32() == 0);

SetForegroundWindow(hWnd);
System.Windows.Forms.SendKeys.SendWait("{ENTER}");


这将创建配置文件,等到窗口具有句柄后,再调用Win32函数SetForegroundWindow()将其置于前台。然后,它使用SendKeys.SendWait将Enter键发送到该窗口。如果您不想使用WinForms DLL,则可以使用Win32函数对此进行PInvoke,但是对于这种特殊情况,我发现Winforms的方法更加快捷,简便。

这行得通,但揭示了另一个问题:runas不允许您在没有密码的帐户下运行内容。超级用户再次进行救援; https://superuser.com/a/470539指出,存在一个名为“限制本地帐户使用空白密码来控制台登录”的本地策略,该策略只能被禁用以允许这种情况。我不希望用户必须手动禁用此策略,因此我在HKEY_LOCAL_MACHINE \ SYSTEM \ CurrentControlSet \ Control \ Lsa中使用了相应的注册表值LimitBlankPasswordUse。

我现在通过将注册表值设置为0来禁用该策略,运​​行runas命令来创建配置文件,然后再将其设置为1来重新启用该策略(首先检查该值,然后重新检查该值可能更清洁-如果首先设置它,则启用它,但是出于演示目的,它将执行以下操作:

    const string keyName = "HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Control\\Lsa";
Registry.SetValue(keyName, "LimitBlankPasswordUse", 0);

var createProfileProcess = Process.Start("runas",
string.Format("/user:{0} rundll32",
"newuser"));

IntPtr hWnd;
do
{
createProfileProcess.Refresh();
hWnd = createProfileProcess.MainWindowHandle;
} while (hWnd.ToInt32() == 0);

SetForegroundWindow(hWnd);
System.Windows.Forms.SendKeys.SendWait("{ENTER}");

Registry.SetValue(keyName, "LimitBlankPasswordUse ", "1");


这可行!但是,该用户的注册表配置单元尚未加载,因此您仍然无法对其进行读写。为此,该过程需要几个特权,您可以使用一些Win32方法再次提供这些特权:

        OpenProcessToken(GetCurrentProcess(), 
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
out _myToken);
LookupPrivilegeValue(null, SE_RESTORE_NAME, out _restoreLuid);
LookupPrivilegeValue(null, SE_BACKUP_NAME, out _backupLuid);

_tokenPrivileges.Attr = SE_PRIVILEGE_ENABLED;
_tokenPrivileges.Luid = _restoreLuid;
_tokenPrivileges.Count = 1;

_tokenPrivileges2.Attr = SE_PRIVILEGE_ENABLED;
_tokenPrivileges2.Luid = _backupLuid;
_tokenPrivileges2.Count = 1;

AdjustTokenPrivileges(_myToken,
false,
ref _tokenPrivileges,
0,
IntPtr.Zero,
IntPtr.Zero);
AdjustTokenPrivileges(_myToken,
false,
ref _tokenPrivileges2,
0,
IntPtr.Zero,
IntPtr.Zero);


最后,使用新用户的SID加载配置单元:

        // Load the hive
var principalContext = new PrincipalContext(ContextType.Machine);
var standardUser = UserPrincipal.FindByIdentity(principalContext, "newuser");
var sid = standardUser.Sid.ToString();

StringBuilder path = new StringBuilder(4 * 1024);
int size = path.Capacity;
GetProfilesDirectory(path, ref size);

var filename = Path.Combine(path.ToString(), "newuser", "NTUSER.DAT");

Thread.Sleep(2000);
int retVal = RegLoadKey(HKEY_USERS, sid, filename);


我在 Load registry hive from C# fails中找到了大部分代码。

RegLoadKey成功应返回0。我注意到,有时候,它会由于没有明显的原因而无法加载配置单元。考虑到可能尚未在用户配置文件中创建必需文件,我在加载配置单元之前添加了Thread.Sleep(2000),以便Windows有时间创建所有必需文件。可能有一种更整洁的方法可以执行此操作,但是现在可以使用。

现在,您可以使用newuser的SID为newuser加载和设置注册表值,例如:

var subKeyString = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Advanced";
var keyString = string.Format("{0}{1}", sid, subKeyString);
var subKey = Registry.Users.CreateSubKey(keyString,
RegistryKeyPermissionCheck.ReadWriteSubTree);
subKey.SetValue("EnableBalloonTips", 0, RegistryValueKind.DWord);


可以肯定的是,完成后,我还卸载了注册表配置单元。我不确定是否需要它,但是看起来很整洁:

var retVal = RegUnLoadKey(HKEY_USERS, GetSidForStandardUser());

关于c# - 编辑新创建用户的注册表值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34288818/

29 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com