Mick
Mick

Reputation: 13475

Is it possible to read/write the registry of a remote machine with different credentials?

I am using Delphi to remotely read and write the registry of a remote machine. This works when my account on my machine has admin access to the remote machine.

However, I'd like to be able to specify a username / pwd when connecting to read the registry so I can connect with alternate credentials.

With the file-system I called the following (with the username and password) and was able to establish a connection to the remote system and perform filesystem related functions. However, this does not appear to work with the registry.

var
  netResource       : TNetResource;
begin

  FillChar(netResource, SizeOf(netResource), 0);
  netResource.dwScope := RESOURCE_GLOBALNET;
  netResource.dwType := RESOURCETYPE_DISK;
  netResource.dwDisplayType := RESOURCEDISPLAYTYPE_SHARE;
  netResource.dwUsage := RESOURCEUSAGE_CONNECTABLE;
  netResource.lpRemoteName := PChar('\\192.168.1.105\IPC$');

  WNetAddConnection2(netResource, PChar(password), PChar(username), 0);
end;

...

And here is the example of the function I'd like to be able to call, but specify the credentials with access to the remote machine:

procedure TForm1.SetWallpaperKey() ;
var
   reg:TRegistry;
begin
   reg:=TRegistry.Create;

   with reg do begin
    try
     if RegistryConnect('192.168.1.105') then
       if OpenKey('\Control Panel\desktop', False) then begin
       //change wallpaper and tile it
        reg.WriteString ('Wallpaper','c:\windows\CIRCLES.bmp') ;
        reg.WriteString ('TileWallpaper','1') ;
        //disable screen saver//('0'=disable, '1'=enable)
        reg.WriteString('ScreenSaveActive','0') ;
       end
    finally
      reg.Free;
    end;
   end;
end;

Upvotes: 5

Views: 6138

Answers (4)

C Sharp Conner
C Sharp Conner

Reputation: 378

In case anyone wants to use WNetAddConnection2 correctly in C#, here's some code. The problem is WNetAddConnection2 expects an IntPtr which holds the buffer of the NETRESOURCE, otherwise you get return code of 487 (0x1E7) which is ERROR_INVALID_ADDRESS. Attempt to access invalid address.

I personally was trying to get registry key that honored elevated privileges and thought by connecting to IPC$ it would work, but of course not, seems only way is using RegCreateKeyEx with the base key from RegConnectRegistry... which is acceptable as long as it actually works once I get around to it, haha!

public class IPCShareSession : IDisposable
{
    #region PInvoke Functions,Structs,Enums

    [DllImport("mpr.dll", CharSet = CharSet.Unicode)]
    private static extern int WNetAddConnection2(IntPtr NetResource, string Password, string Username, WNetConnect Flags);

    [DllImport("mpr.dll", CharSet = CharSet.Unicode)]
    private static extern int WNetCancelConnection2(string ServerName, WNetDisconnect Flags, bool Force);

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
    private class NETRESOURCE
    {
        public WNetResourceScope dwScope = 0;
        public WNetResourceType dwType = 0;
        public WNetResourceDisplayType dwDisplayType = 0;
        public WNetResourceUsage dwUsage = 0;
        public string lpLocalName = null;
        public string lpRemoteName = null;
        public string lpComment = null;
        public string lpProvider = null;
    };

    private enum WNetResourceScope
    {
        Connected = 0x1,
        GlobalNetwork = 0x2,
        Remembered = 0x3,
        Recent = 0x4,
        Context = 0x5
    }

    public enum WNetResourceType
    {
        Any = 0,
        Disk = 1,
        Print = 2,
        Reserved = 8
    }

    private enum WNetResourceUsage
    {
        Connectable = 0x1,
        Container = 0x2,
        NoLocalDevice = 0x4,
        Sibling = 0x8,
        Attached = 0x10,
        All = Connectable | Container | Attached
    }

    private enum WNetResourceDisplayType
    {
        Generic = 0x0,
        Domain = 0x1,
        Server = 0x2,
        Share = 0x3,
        File = 0x4,
        Group = 0x5,
        Network = 0x6,
        Root = 0x7,
        ShareAdmin = 0x8,
        Directory = 0x9,
        Tree = 0xa,
        NDSContainer = 0xb
    }

    private enum WNetConnect : int
    {
        CONNECT_UPDATE_PROFILE = 0x00000001,
        CONNECT_UPDATE_RECENT = 0x00000002,
        CONNECT_TEMPORARY = 0x00000004,
        CONNECT_INTERACTIVE = 0x00000008,
        CONNECT_PROMPT = 0x00000010,
        CONNECT_REDIRECT = 0x00000080,
        CONNECT_CURRENT_MEDIA = 0x00000200,
        CONNECT_COMMANDLINE = 0x00000800,
        CONNECT_CMD_SAVECRED = 0x00001000,
        CONNECT_CRED_RESET = 0x00002000
    }

    private enum WNetDisconnect
    {
        DISCONNECT=0,
        DISCONNECT_AND_REMOVE=1
    }

    #endregion

    private string ServerShare = null;
    private NETRESOURCE serverNR = null;
    private bool bConnected = false;

    public IPCShareSession(string ServerName, string ShareName = "IPC$")
    {
        ServerShare = string.Format(@"\\{0}\{1}", ServerName, ShareName);
        //Setup nr to be a IPC share session
        serverNR = new NETRESOURCE
        {
            dwType = WNetResourceType.Disk,
            lpLocalName = null,
            lpRemoteName = ServerShare,
            lpProvider = null,
            dwDisplayType = WNetResourceDisplayType.ShareAdmin,
            dwUsage = WNetResourceUsage.Connectable,
        };
    }

    public int Connect()
    {
        if(!bConnected)
        {
            IntPtr pNR = Marshal.AllocHGlobal(Marshal.SizeOf(serverNR));
            Marshal.StructureToPtr(serverNR, pNR, false);
            int ret = WNetAddConnection2(pNR, null, null, WNetConnect.CONNECT_TEMPORARY);
            //Free our unmanaged pointer now that we're done with it.
            Marshal.FreeHGlobal(pNR);
            bConnected = ret == 0;
            return ret;
        }
        else
        {
            return -1;
        }
    }

    public int Disconnect()
    {
        int ret = 0;
        if (bConnected)
        {
            ret = WNetCancelConnection2(ServerShare, WNetDisconnect.DISCONNECT, true);
            bConnected = false;
        }
        else
        {
            ret = -1;
        }
        return ret;
    }

    #region IDisposable Support

    private bool disposedValue = false; // To detect redundant calls

    protected virtual void Dispose(bool disposing)
    {
        if (!disposedValue)
        {
            if (disposing)
            {
                if (bConnected)
                {
                    Disconnect();
                }
            }
            disposedValue = true;
        }
    }

    public void Dispose()
    {
        Dispose(true);
    }

    #endregion


}

Upvotes: 0

RRUZ
RRUZ

Reputation: 136391

Now using the windows API , the TRegistry.RegistryConnect function internally call the RegConnectRegistry windows function , the documentation says :

If the current user does not have proper access to the remote computer, the call to RegConnectRegistry fails. To connect to a remote registry, call LogonUser with LOGON32_LOGON_NEW_CREDENTIALS and ImpersonateLoggedOnUser before calling RegConnectRegistry.

Windows 2000:  One possible workaround is to establish a session

to an administrative share such as IPC$ using a different set of credentials. To specify credentials other than those of the current user, use the WNetAddConnection2 function to connect to the share. When you have finished accessing the registry, cancel the connection.

Windows XP Home Edition:  You cannot use this function to connect to

a remote computer running Windows XP Home Edition. This function does work with the name of the local computer even if it is running Windows XP Home Edition because this bypasses the authentication layer.

check the next samples

using the windows api registry functions

uses
  Windows,
  Registry,
  SysUtils;

procedure AccessRemoteRegistry(const lpMachineName, lpszUsername , lpszPassword: PChar);
const
 LOGON32_LOGON_NEW_CREDENTIALS = 9;
 REG_OPTION_OPEN_LINK          = $00000008;
var
  netResource : TNetResource;
  dwFlags     : DWORD;
  dwRetVal    : DWORD;
  phToken     : THandle;
  phkResult   : HKEY;
  phkResult2  : HKEY;
  lpType      : DWORD;
  lpData      : PByte;
  lpcbData    : DWORD;
begin
  ZeroMemory(@netResource, SizeOf(netResource));
  netResource.dwType      := RESOURCETYPE_ANY;
  netResource.lpLocalName := nil;
  netResource.lpRemoteName:= lpMachineName;
  netResource.lpProvider  := nil;
  dwFlags  := CONNECT_UPDATE_PROFILE;

  dwRetVal := WNetAddConnection2(netResource, lpszPassword, lpszUsername, dwFlags);
  if dwRetVal=NO_ERROR then
  begin
    try
     Writeln('Connected');
     if LogonUser(lpszUsername, nil, lpszPassword, LOGON32_LOGON_NEW_CREDENTIALS, LOGON32_PROVIDER_WINNT50,phToken) then
     begin
       try
          if ImpersonateLoggedOnUser(phToken) then
          begin
           try
             dwRetVal:=RegConnectRegistry(lpMachineName,HKEY_LOCAL_MACHINE,phkResult);
             if dwRetVal = ERROR_SUCCESS then
             begin
               dwRetVal:=RegOpenKeyEx(phkResult,PChar('SOFTWARE\Borland\Delphi\5.0'), REG_OPTION_OPEN_LINK, KEY_QUERY_VALUE, phkResult2);
               if dwRetVal = ERROR_SUCCESS then
               begin
                try
                 lpType:=REG_SZ;
                 //get the size of the buffer
                 dwRetVal:=RegQueryValueEx(phkResult2, PChar('App'), nil, @lpType, nil, @lpcbData);
                 if dwRetVal = ERROR_SUCCESS then
                 begin
                   GetMem(lpData,lpcbData);
                   try
                     dwRetVal:=RegQueryValueEx(phkResult2, 'App', nil, @lpType, lpData, @lpcbData);
                     if dwRetVal = ERROR_SUCCESS then
                      WriteLn(PChar(lpData))
                     else
                     Writeln(Format('RegQueryValueEx  error %d',[dwRetVal]));
                   finally
                     FreeMem(lpData);
                   end;
                 end
                 else
                 Writeln(Format('RegQueryValueEx  error %d',[dwRetVal]));
                finally
                 RegCloseKey(phkResult2);
                end;
               end
               else
               Writeln(Format('RegOpenKeyEx error %d',[dwRetVal]));
             end
             else
              Writeln(Format('RegConnectRegistry error %d',[dwRetVal]));
           finally
              RevertToSelf;
           end;
          end
          else
          RaiseLastOSError;
       finally
         CloseHandle(phToken);
       end;
     end
     else
       RaiseLastOSError;
    finally
       dwRetVal:=WNetCancelConnection2(netResource.lpRemoteName, CONNECT_UPDATE_PROFILE, false);
       if dwRetVal<>NO_ERROR then
         Writeln(Format('WNetCancelConnection2 error %d',[dwRetVal]));
    end;
  end
  else
    Writeln(Format('WNetAddConnection2 Connection error %d',[dwRetVal]));
end;

use in this way

AccessRemoteRegistry('\\192.168.52.128','NormalUser','password');

using the TRegistry delphi class

procedure AccessRemoteRegistry2(const lpMachineName, lpszUsername , lpszPassword: PChar);
const
 LOGON32_LOGON_NEW_CREDENTIALS = 9;
 REG_OPTION_OPEN_LINK          = $00000008;
var
  netResource : TNetResource;
  dwFlags     : DWORD;
  dwRetVal    : DWORD;
  phToken     : THandle;
  Reg         : TRegistry;
begin
  ZeroMemory(@netResource, SizeOf(netResource));
  netResource.dwType      := RESOURCETYPE_ANY;
  netResource.lpLocalName := nil;
  netResource.lpRemoteName:= lpMachineName;
  netResource.lpProvider  := nil;
  dwFlags  := CONNECT_UPDATE_PROFILE;
  dwRetVal := WNetAddConnection2(netResource, lpszPassword, lpszUsername, dwFlags);
  if dwRetVal=NO_ERROR then
  begin
    try
     Writeln('Connected');
     if LogonUser(lpszUsername, nil, lpszPassword, LOGON32_LOGON_NEW_CREDENTIALS, LOGON32_PROVIDER_WINNT50,phToken) then
     begin
       try
        if ImpersonateLoggedOnUser(phToken) then
        begin
         try
           reg:=TRegistry.Create;
           try
             reg.RootKey:=HKEY_LOCAL_MACHINE;
             if reg.RegistryConnect(lpMachineName) then
              if reg.OpenKey('SOFTWARE\Borland\Delphi\5.0',False) then
                WriteLn(reg.ReadString('App'));
           finally
             reg.CloseKey;
             reg.Free;
           end;
         finally
            RevertToSelf;
         end;
        end
        else
        RaiseLastOSError;
       finally
         CloseHandle(phToken);
       end;
     end
     else
     RaiseLastOSError;
    finally
       dwRetVal:=WNetCancelConnection2(netResource.lpRemoteName, CONNECT_UPDATE_PROFILE, false);
       if dwRetVal<>NO_ERROR then
         Writeln(Format('WNetCancelConnection2 error %d',[dwRetVal]));
    end;
  end
  else
    Writeln(Format('WNetAddConnection2 Connection error %d',[dwRetVal]));
end;

use in this way

AccessRemoteRegistry2('\\192.168.52.128','NormalUser','password');

Upvotes: 3

RRUZ
RRUZ

Reputation: 136391

Mick , i can't resist give you a WMI solution for you problem ;) , the wmi have a class called StdRegProv which allow you to access the registry in local and remote machines. A key point is the namespace where the class is located, that depends of the version of windows installed in the remote machine. so for Windows Server 2003, Windows XP, Windows 2000, Windows NT 4.0, and Windows Me/98/95 the StdRegProv class is available in the root\default namespace and for others versions like windows Vista/7 the namespace is root\CIMV2.

Now to configure the credentials to access the registry, you must set these values in the SWbemLocator.ConnectServer method in this way :

FSWbemLocator.ConnectServer(Server, 'root\default', User, Pass);

Another impor point is which this class just exposes methods to access the registry not properties, so you cannot use a wmi query, instead you must execute wmi methods.

check the next samples to see how it works.

checking if you have permissions over a key

uses
  Windows,
  SysUtils,
  ActiveX,
  ComObj;

// The CheckAccess method verifies that the user possesses the specified
// permissions. The method returns a uint32 which is 0 if successful or some other
// value if any other error occurred.

procedure  Invoke_StdRegProv_CheckAccess;
const
  Server = '192.168.52.128';
  User   = 'Administrator';
  Pass   = 'password';
var
  FSWbemLocator   : OLEVariant;
  FWMIService     : OLEVariant;
  FWbemObjectSet  : OLEVariant;
  FInParams       : OLEVariant;
  FOutParams      : OLEVariant;
begin
  FSWbemLocator := CreateOleObject('WbemScripting.SWbemLocator');
  //http://msdn.microsoft.com/en-us/library/aa393664%28v=vs.85%29.aspx
  //StdRegProv is preinstalled in the WMI namespaces root\default and root\cimv2.
  //Windows Server 2003, Windows XP, Windows 2000, Windows NT 4.0, and Windows Me/98/95:  StdRegProv is available only in root\default namespace.
  FWMIService   := FSWbemLocator.ConnectServer(Server, 'root\default', User, Pass);
  //For Windows Vista or Windows 7 you must use the  root\CIMV2 namespace
  //FWMIService   := FSWbemLocator.ConnectServer(Server, 'root\CIMV2', User, Pass);
  FWbemObjectSet:= FWMIService.Get('StdRegProv');
  FInParams     := FWbemObjectSet.Methods_.Item('CheckAccess').InParameters.SpawnInstance_();
  FInParams.hDefKey:=HKEY_LOCAL_MACHINE;
  FInParams.sSubKeyName:='SYSTEM\CurrentControlSet';
  FInParams.uRequired:=KEY_QUERY_VALUE;

  FOutParams    := FWMIService.ExecMethod('StdRegProv', 'CheckAccess', FInParams);
  Writeln(Format('bGranted              %s',[FOutParams.bGranted]));
  Writeln(Format('ReturnValue           %s',[FOutParams.ReturnValue]));
end;

reading a string value

// The GetStringValue method returns the data value for a named value whose data 
// type is REG_SZ. 
procedure  Invoke_StdRegProv_GetStringValue;
const
  Server = '192.168.52.128';
  User   = 'Administrator';
  Pass   = 'password';
var
  FSWbemLocator   : OLEVariant;
  FWMIService     : OLEVariant;
  FWbemObjectSet  : OLEVariant;
  FInParams       : OLEVariant;
  FOutParams      : OLEVariant;
begin
  FSWbemLocator := CreateOleObject('WbemScripting.SWbemLocator');
  //http://msdn.microsoft.com/en-us/library/aa393664%28v=vs.85%29.aspx
  //StdRegProv is preinstalled in the WMI namespaces root\default and root\cimv2.
  //Windows Server 2003, Windows XP, Windows 2000, Windows NT 4.0, and Windows Me/98/95:  StdRegProv is available only in root\default namespace.
  FWMIService   := FSWbemLocator.ConnectServer(Server, 'root\default', User, Pass);
  //For Windows Vista or Windows 7 you must use the  root\CIMV2 namespace
  //FWMIService   := FSWbemLocator.ConnectServer(Server, 'root\CIMV2', User, Pass);
  FWbemObjectSet:= FWMIService.Get('StdRegProv');
  FInParams     := FWbemObjectSet.Methods_.Item('GetStringValue').InParameters.SpawnInstance_();
  FInParams.hDefKey:=HKEY_LOCAL_MACHINE;
  FInParams.sSubKeyName:='SOFTWARE\Borland\Delphi\5.0';
  FInParams.sValueName:='App';

  FOutParams    := FWMIService.ExecMethod('StdRegProv', 'GetStringValue', FInParams);
  Writeln(Format('sValue                %s',[FOutParams.sValue]));
  Writeln(Format('ReturnValue           %s',[FOutParams.ReturnValue]));
end;

writing a string value

// The SetStringValue method sets the data value for a named value whose data type 
// is REG_SZ.
procedure  Invoke_StdRegProv_SetStringValue;
const
  Server = '192.168.52.128';
  User   = 'Administrator';
  Pass   = 'password';
var
  FSWbemLocator   : OLEVariant;
  FWMIService     : OLEVariant;
  FWbemObjectSet  : OLEVariant;
  FInParams       : OLEVariant;
  FOutParams      : OLEVariant;
begin
  FSWbemLocator := CreateOleObject('WbemScripting.SWbemLocator');
  FWMIService   := FSWbemLocator.ConnectServer(Server, 'root\default', User, Pass);
  FWbemObjectSet:= FWMIService.Get('StdRegProv');
  FInParams     := FWbemObjectSet.Methods_.Item('SetStringValue').InParameters.SpawnInstance_();
  FInParams.hDefKey:=HKEY_LOCAL_MACHINE;
  FInParams.sSubKeyName:='SOFTWARE\Borland\Delphi\5.0';
  FInParams.sValueName:='Dummy';
  FInParams.sValue:='ADummyValue';

  FOutParams    := FWMIService.ExecMethod('StdRegProv', 'SetStringValue', FInParams);
  Writeln(Format('ReturnValue           %s',[FOutParams.ReturnValue]));
end;

For more options you must check the documentation about this class.

I hope this help you.

Upvotes: 7

Warren  P
Warren P

Reputation: 68862

Maybe you need WNetAddConnection3? I don't think it enables remote registry though.

Personally though I would look into WMI, and network-named-pipes, which are methods of remote registry access that Windows supports out of the box.

Also, you know about TRegistry.RegistryConnect. It doesn't seem to have the userid/password, thing you want, but I am still not clear why that isn't enough.

Upvotes: 0

Related Questions