I'm unable to take ownership of some keys in the registry with my C# code.
I'm trying take ownership on
Software\Microsoft\Windows\CurrentVersion\Explorer\MyComputer\NameSpace{1CF1260C-4DD0-4ebb-811F-33C572699FDE}
My app is launched as administrator with the requireAdministrator flag on the app.manifest.
UAC is disabled to test without this security that might interfere.
I'm setting the token SeTakeOwnershipPrivilege properly on my process (Process Explorer confirms me that is enabled on my process).
But when I'm running my code it returns a null key at the OpenSubKey call.
Here is my method :
public void takeKeyOwnership(RegistryHive root, string path)
{
RegistryKey RootKey = GetKey(root);
var key = RootKey.OpenSubKey(path, RegistryKeyPermissionCheck.ReadWriteSubTree, RegistryRights.TakeOwnership);
if(key == null)
{
logger.Warn("[takeKeyOwnership] Unable to take ownership on key " + root + "\\" + path);
return;
}
var acl = key.GetAccessControl(AccessControlSections.None);
var localAdmins = new SecurityIdentifier(Constants.LocalAdministratorsSID);
acl.SetOwner(localAdmins);
key.SetAccessControl(acl);
key.Close();
}
My question is different from : How do I programmatically give ownership of a Registry Key to Administrators? as I've set the SeTakeOwnerShipPrivilege as specified earlier.
Related
I have written a C# application to change registry values AutoLogon, DefaultUserName, and DefaultPassword on Windows 7.
The complete path would be "HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\WinLogon"
RegistryKey key = Registry.LocalMachine.OpenSubKey("Software\\Microsoft\\Windows NT\\CurrentVersion\\WinLogon", true);
if(key != null)
{
key.SetValue("DefaultUserName", "username");
key.SetValue("DefaultPassword", "password");
}
This code executes without any error or exception, But there are no changes affected in registry.
I have executed as Admin and the system on which this gets executed has one user and it is admin.
Make sure you use key.Close() as it would never be saved otherwise.
Like this:
RegistryKey key = Registry.LocalMachine.OpenSubKey("Software\\Microsoft\\Windows NT\\CurrentVersion\\WinLogon", true);
if(key != null)
{
key.SetValue("DefaultUserName", "username");
key.SetValue("DefaultPassword", "password");
key.Close();
}
Here's a link from MS: https://msdn.microsoft.com/en-GB/library/h5e7chcf.aspx
When I run the following command, rKey has two values.
RegistryKey sqlServer = Registry.LocalMachine.OpenSubKey( #"SOFTWARE\Microsoft\Microsoft SQL Server", false);
When I run either of the following commands (on the same machine as the same user) I find no values;
RegistryKey sqlServer64 = RegistryKey.OpenBaseKey( RegistryHive.LocalMachine, RegistryView.Registry64);
RegistryKey sqlServer32 = RegistryKey.OpenBaseKey( RegistryHive.LocalMachine, RegistryView.Registry32 );
Can anyone point me to the answer or a description of the hives vs plain registry access?
Edit:
What I do afterwards is :
StringBuilder sbKeys = new StringBuilder();
foreach (var key in sqlServer.GetValueNames() )
{
sbKeys.AppendLine( key );
}
For all RegistryKeys. For sqlServer I see two Values, for sqlServer32 and SqlServer64 there are no values.
The problem in your second variant is that you have failed to open a sub key. Your sqlServer.GetValueNames() call operates at the root level of a particular hive.
You need it to be like this:
RegistryKey root = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64);
RegistryKey sqlServer = root.OpenSubKey(#"SOFTWARE\Microsoft\Microsoft SQL Server");
foreach (var key in sqlServer.GetValueNames())
{ .... }
Done this way it's no different from your first variant (apart from the registry view). I expect that using the appropriate registry view will lead to the solution to your other question.
Naturally you'll want to add some error checking to the code above.
I would to get installed version of an application (say, MyApp) using C#.
I will do this much,
1. Create a 'Set Up' for MyApp of version 5.6
2. Install MyApp.
I will create another application (say VersionTracker)to get the version of installed applications. So if I pass the name 'MyApp' I would like to get the version as '5.6'. If another application say Adobe Reader is installed in my system, I want to get the version of Adobe Reader if I pass 'Adobe Reader'.
I need to know how to build 'VersionTracker'
The first and the most important thing is that not all applications do save their version somewhere in the system. To be honest, only a few of them do that. The place where you should look are the Windows Registry. Most of installed applications put their installation data into the following place:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall
However, it's not that easy - on 64bit Windows, the 32bit (x86) applications save their installation data into another key, which is:
HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall
In these keys there are many keys, some of them have got "easy-readable" name, such as Google Chrome, some of them got names such as {63E5CDBF-8214-4F03-84F8-CD3CE48639AD}. You must parse all these keys into your application and start looking for the application names. There are usually in DisplayName value, but it's not always true. The version of the application is usually in DisplayVersion value, but some installers do use another values, such as Inno Setup: Setup Version, ... Some application do have their version written in their name, so it's possible that the application version is already in the DisplayName value.
Note: It's not easy to parse all these registry keys and values and to "pick" the correct values. Not all installers save the application data into these keys, some of them do not save the application version there, etcetera. However, it's usual that the application use these registry keys. [Source: StackOverflow: Detecting installed programs via registry, browsing my own registry]
Alright, so now when you know where you should look, you have to program it all in C#. I won't write the application for you, but I'll tell you what classes you should use and how to. First, you need these:
using System;
using Microsoft.Win32;
To get to your HKEY_LOCAL_MACHINE, create a RegistryKey like this:
RegistryKey baseRegistryKey = Registry.LocalMachine;
Now you need to define subkeys:
string subKey = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall";
// or "SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall"
Now you need to go to the subkey, so create a new RegistryKey:
RegistryKey uninstallKey = baseRegistryKey.OpenSubKey(subKey);
Now you need to go thru all the subkeys that are there, so first we get the names of all the subkeys:
string[] allApplications = uninstallKey.GetSubKeyNames();
Now you must go thru all the subkeys yourself, one by one, by creating a new registry key (you don't have to, but I'll do it):
RegistryKey appKey = baseRegistryKey.OpenSubKey(subKey + "\\" + applicationSubKeyName);
where applicationSubKeyName is the name of the subkey you're currently checking. I recommend foreach statement, which helps you (you must however have some experience with C# already, I'm not going to tell you how to use foreach here).
Now check the application's name and compare it with name of your desired application (you cannot rely on the subkey name, because, as I already said, they can be called for example {63E5CDBF-8214-4F03-84F8-CD3CE48639AD}, so you must check the name here):
string appName = (string)appKey.GetValue("DisplayName");
If it's the correct application (you must check it yourself), find the version:
string appVersion = (string)appKey.GetValue("DisplayVersion");
Et voilĂ , you have the version. At least there's like a 60 - 80% chance you have...
Remember! If some key or value doesn't exist, the method returns null. Remember to check if the returned value is null everytime, otherwise your application will crash.
Where to find more? The Code Project: Read, write and delete from registry with C#
I really hope I helped you. And if you wanted to know something else and I didn't understand your question, then, please, ask better next time. :)
///
/// Author : Muhammed Rauf K
/// Date : 03/07/2011
/// A Simple console application to create and display registry sub keys
///
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
// it's required for reading/writing into the registry:
using Microsoft.Win32;
namespace InstallationInfoConsole
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Registry Information ver 1.0");
Console.WriteLine("----------------------------");
Console.Write("Input application name to get the version info. (for example 'Nokia PC Suite'): ");
string nameToSearch = Console.ReadLine();
GetVersion(nameToSearch);
Console.WriteLine("----------------------------");
Console.ReadKey();
}
///
/// Author : Muhammed Rauf K
/// Date : 03/07/2011
/// Create registry items
///
static void Create()
{
try
{
Console.WriteLine("Creating registry...");
// Create a subkey named Test9999 under HKEY_CURRENT_USER.
string subKey;
Console.Write("Input registry sub key :");
subKey = Console.ReadLine();
RegistryKey testKey = Registry.CurrentUser.CreateSubKey(subKey);
Console.WriteLine("Created sub key {0}", subKey);
Console.WriteLine();
// Create two subkeys under HKEY_CURRENT_USER\Test9999. The
// keys are disposed when execution exits the using statement.
Console.Write("Input registry sub key 1:");
subKey = Console.ReadLine();
using (RegistryKey testKey1 = testKey.CreateSubKey(subKey))
{
testKey1.SetValue("name", "Justin");
}
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
static void GetVersion(string nameToSearch)
{
// Get HKEY_LOCAL_MACHINE
RegistryKey baseRegistryKey = Registry.LocalMachine;
// If 32-bit OS
string subKey
//= "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall";
// If 64-bit OS
= "SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall";
RegistryKey unistallKey = baseRegistryKey.OpenSubKey(subKey);
string[] allApplications = unistallKey.GetSubKeyNames();
foreach (string s in allApplications)
{
RegistryKey appKey = baseRegistryKey.OpenSubKey(subKey + "\\" + s);
string appName = (string)appKey.GetValue("DisplayName");
if(appName==nameToSearch)
{
string appVersion = (string)appKey.GetValue("DisplayVersion");
Console.WriteLine("Name:{0}, Version{1}", appName, appVersion);
break;
}
}
}
static void ListAll()
{
// Get HKEY_LOCAL_MACHINE
RegistryKey baseRegistryKey = Registry.LocalMachine;
// If 32-bit OS
string subKey
//= "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall";
// If 64-bit OS
= "SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall";
RegistryKey unistallKey = baseRegistryKey.OpenSubKey(subKey);
string[] allApplications = unistallKey.GetSubKeyNames();
foreach (string s in allApplications)
{
RegistryKey appKey = baseRegistryKey.OpenSubKey(subKey + "\\" + s);
string appName = (string)appKey.GetValue("DisplayName");
string appVersion = (string)appKey.GetValue("DisplayVersion");
Console.WriteLine("Name:{0}, Version{1}", appName, appVersion);
}
}
}
}
Next code base on similar solution is working for me:
var version = GetApplicationVersion("Windows Application Driver");
string GetApplicationVersion(string appName)
{
string displayName;
// search in: CurrentUser
var key = Registry.CurrentUser.OpenSubKey(#"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall");
foreach (var keyName in key.GetSubKeyNames())
{
var subKey = key.OpenSubKey(keyName);
displayName = subKey.GetValue("DisplayName") as string;
if (appName.Equals(displayName, StringComparison.OrdinalIgnoreCase))
return subKey.GetValue("DisplayVersion").ToString();
}
// search in: LocalMachine_32
key = Registry.LocalMachine.OpenSubKey(#"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall");
foreach (var keyName in key.GetSubKeyNames())
{
var subKey = key.OpenSubKey(keyName);
displayName = subKey.GetValue("DisplayName") as string;
if (appName.Equals(displayName, StringComparison.OrdinalIgnoreCase))
return subKey.GetValue("DisplayVersion").ToString();
}
// search in: LocalMachine_64
key = Registry.LocalMachine.OpenSubKey(#"SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall");
foreach (var keyName in key.GetSubKeyNames())
{
var subKey = key.OpenSubKey(keyName);
displayName = subKey.GetValue("DisplayName") as string;
if (appName.Equals(displayName, StringComparison.OrdinalIgnoreCase))
return subKey.GetValue("DisplayVersion").ToString();
}
// NOT FOUND
return null;
}
I am working on File Associations. I have identified that there is a key called UserChoice in:
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\[ext].
I have been able to read from and write to the UserChoice key provided that I create it and that it has not already been created by Windows. However, if the UserChoice key has already been created by Windows, then I need to run as Administrator to get access to the key. My ultimate goal is to delete the UserChoice key.
I have noted that Windows places a Deny rule on the UserChoice key which is preventing me from deleting that key. If I can succeed in removing that rule, I believe that I'll be able to delete the UserChoice key. Here is the code that I have tried:
public static void ShowSecurity(RegistryKey regKeyRoot, string user) {
RegistrySecurity security = regKeyRoot.GetAccessControl(AccessControlSections.All);
foreach (RegistryAccessRule ar in
security.GetAccessRules(true, true, typeof(NTAccount))) {
if (ar.IdentityReference.Value.Contains(User) &&
ar.AccessControlType.ToString().ToLower() == "deny") {
security.RemoveAccessRuleSpecific(ar);
regKeyRoot.SetAccessControl(security);
}
}
}
When Windows creates the UserChoice key it adds a security rule for the current user of Type Deny; permission: Special. This rule is not inherited and applies to the UserChoice key only.
With some messing about and running as Administrator I am able to access that RegistryAccessRule. However even running as Administrator, I cannot remove this rule. I have read somewhere in my research that there is not a programmatic way to do it. I can remove this rule via RegEdit. I can also remove the UserChoice key using File Types Manager from NirSoft. So I assume there is some way to do this.
Summary: Is there a way that I can remove the Deny rule so that I can delete the UserChoice key?
Your code example and the revisions suggested in the answer by #ali lead me to a solution for overcoming the security setting that Windows places on the UserChoice key which enabled me to delete that key.
My solution presumes that the UserChoice key is present in the HKEY_CURRENT_USER (HKCU) hive. If that is the case, the user owns the UserChoice key and therefore has the necessary privileges to change the security settings on that key and ultimately delete it. (This means that the user does not need to be a member of the Administrators group.)
The extensionKey parameter of this method is the parent key of the UserChoice key.
static void DeleteUserChoiceKey(RegistryKey extensionKey)
{
const string userChoiceKeyName = "UserChoice";
using (RegistryKey userChoiceKey =
extensionKey.OpenSubKey(userChoiceKeyName,
RegistryKeyPermissionCheck.ReadWriteSubTree,
RegistryRights.ChangePermissions))
{
if (userChoiceKey == null) { return; }
string userName = WindowsIdentity.GetCurrent().Name;
RegistrySecurity security = userChoiceKey.GetAccessControl();
AuthorizationRuleCollection accRules =
security.GetAccessRules(true, true, typeof(NTAccount));
foreach (RegistryAccessRule ar in accRules)
{
if (ar.IdentityReference.Value == userName &&
ar.AccessControlType == AccessControlType.Deny)
{
security.RemoveAccessRuleSpecific(ar); // remove the 'Deny' permission
}
}
userChoiceKey.SetAccessControl(security); // restore all original permissions
// *except* for the 'Deny' permission
}
extensionKey.DeleteSubKeyTree(userChoiceKeyName, true);
}
A quick thought. Does it work if you take ownership og the regKey, before changing the rules on it
public static void ShowSecurity(RegistryKey regKeyRoot, string user)
{
regKeyRoot.OpenSubKey("", RegistryKeyPermissionCheck.ReadWriteSubTree,
RegistryRights.ChangePermissions);
RegistrySecurity security = regKeyRoot.GetAccessControl(AccessControlSections.All);
security.SetGroup( new NTAccount("Administrators") );
security.SetOwner( new NTAccount("ali") ); //Your account name
security.SetAccessRuleProtection(true, false);
regKeyRoot.SetAccessControl(security);
//---------
foreach (RegistryAccessRule ar in security.GetAccessRules(true, true, typeof(NTAccount)))
{
if (ar.IdentityReference.Value.Contains(User) && ar.AccessControlType == AccessControlType.Deny )
security.RemoveAccessRuleSpecific(ar);
}
regKeyRoot.SetAccessControl(security);
}
I am trying to query the following registry key values:
HKLM\SOFTWARE\Microsoft\MSSQLServer\Client\SharedMemoryOn
HKLM\SOFTWARE\Microsoft\MSSQLServer\Client\SuperSocketNetLib\ProtocolOrder
But depending on which machine I'm running the program the query returns null. When I debug on my local machine and I inspect the value for ValueCount for:
HKLM\SOFTWARE\Microsoft\MSSQLServer\Client
HKLM\SOFTWARE\Microsoft\MSSQLServer\Client\SuperSocketNetLib
The count is 0 and OpenSubKey returns null.
I am a domain admin, in the local administrators group and have added the following to my app.manifest:
<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
Any idea why?
private static void ValidateSqlClientSettings()
{
Console.WriteLine("\r\n/////////////// LOCAL SQL CLIENT PROTOCOLS ////////////////");
RegistryKey keyHKLM = Registry.LocalMachine;
///TODO: nullreferenceexception - connect to remote machine and find out why
RegistryKey sqlClientKey = keyHKLM.OpenSubKey(#"SOFTWARE\Microsoft\MSSQLServer\Client");
if (sqlClientKey == null)
{
WriteLine2Console(#"WARNING: unable to read registry key '{0}\SOFTWARE\Microsoft\MSSQLServer\Client'", ConsoleColor.Yellow);
}
var cliKeyNames = from k in sqlClientKey.GetSubKeyNames()
where k == "SuperSocketNetLib"
select k;
///TODO: find out why these values are always missing (even if I can see them in regedit)
Console.Write("Shared Memory Disabled (cliconfg): ");
if (Convert.ToBoolean(sqlClientKey.GetValue("SharedMemoryOn")))
WriteLine2Console("FAILED", ConsoleColor.Red);
else if(sqlClientKey.GetValue("SharedMemoryOn") == null)
WriteLine2Console(String.Format("WARNING - unable to read '{0}\\SharedMemoryOn'", sqlClientKey.Name), ConsoleColor.Yellow);
else
WriteLine2Console("PASS", ConsoleColor.Green);
Console.Write("Client Protocol Order (cliconfg - tcp first): ");
foreach (string cliKey in cliKeyNames)
{
RegistryKey subKey = sqlClientKey.OpenSubKey(cliKey);
object order = subKey.GetValue("ProtocolOrder");
if (order != null && order.ToString().StartsWith("tcp") == false)
{
WriteLine2Console("FAILED", ConsoleColor.Red);
}
else if (order == null)
{
WriteLine2Console(String.Format("WARNING - unable to read '{0}\\ProtocolOrder'", subKey.Name), ConsoleColor.Yellow);
}
else
{
WriteLine2Console("PASS", ConsoleColor.Green);
}
subKey.Close();
}
sqlClientKey.Close();
keyHKLM.Close();
}
Another factor you should pay attention to is application bitness.
On Windows x64, your x86 build will look for the registry keys under (in regedit) "SOFTWARE\WOW6432Node\Microsoft\MSSQLServer\Client", when you specify (in code) "Software\Microsoft\MSSQLServer\Client"
That's a WOW64 redirection for registry keys.
As Visual Studio 2010 by default creates exe in x86 mode, you should pay attention to this tip.
Try changing your sqlclientkey to the following:
RegistryKey sqlClientKey = keyHKLM.OpenSubKey(#"SOFTWARE\\Microsoft\\MSSQLServer\\Client");