How to extract computer/machine SID?
Asked Answered
N

3

7

I'm looking for a way to extract the computer SID using Delphi code. There's a tool called PsGetSid from SysInternals that does this, but I can't use it in my application. I searched for a code example in Google and couldn't find one.

How can I achieve this in Delphi?

Please help.

Normally answered 3/10, 2011 at 23:23 Comment(0)
D
11

This is a sample using the LookupAccountName WinAPi function as @MikeKwan suggest.

{$APPTYPE CONSOLE}

uses
  Windows,
  SysUtils;


function ConvertSidToStringSid(Sid: PSID; out StringSid: PChar): BOOL; stdcall;  external 'ADVAPI32.DLL' name {$IFDEF UNICODE} 'ConvertSidToStringSidW'{$ELSE} 'ConvertSidToStringSidA'{$ENDIF};

function SIDToString(ASID: PSID): string;
var
  StringSid : PChar;
begin
  if not ConvertSidToStringSid(ASID, StringSid) then
    RaiseLastWin32Error;

  Result := string(StringSid);
end;

function GetLocalComputerName: string;
var
  nSize: DWORD;
begin
  nSize := MAX_COMPUTERNAME_LENGTH + 1;
  SetLength(Result, nSize);
  if not GetComputerName(PChar(Result), {var}nSize) then
  begin
    Result := '';
    Exit;
  end;

  SetLength(Result, nSize);
end;

function GetComputerSID:string;
var
  Sid: PSID;
  cbSid: DWORD;
  cbReferencedDomainName : DWORD;
  ReferencedDomainName: string;
  peUse: SID_NAME_USE;
  Success: BOOL;
  lpSystemName : string;
  lpAccountName: string;
begin
  Sid:=nil;
  try
    lpSystemName:='';
    lpAccountName:=GetLocalComputerName;

    cbSid := 0;
    cbReferencedDomainName := 0;
    // First call to LookupAccountName to get the buffer sizes.
    Success := LookupAccountName(PChar(lpSystemName), PChar(lpAccountName), nil, cbSid, nil, cbReferencedDomainName, peUse);
    if (not Success) and (GetLastError = ERROR_INSUFFICIENT_BUFFER) then
    begin
      SetLength(ReferencedDomainName, cbReferencedDomainName);
      Sid := AllocMem(cbSid);
      // Second call to LookupAccountName to get the SID.
      Success := LookupAccountName(PChar(lpSystemName), PChar(lpAccountName), Sid, cbSid, PChar(ReferencedDomainName), cbReferencedDomainName, peUse);
      if not Success then
      begin
        FreeMem(Sid);
        Sid := nil;
        RaiseLastOSError;
      end
      else
        Result := SIDToString(Sid);
    end
    else
      RaiseLastOSError;
  finally
    if Assigned(Sid) then
     FreeMem(Sid);
  end;
end;


begin
 try
   Writeln(GetComputerSID);
 except
    on E:Exception do
        Writeln(E.Classname, ':', E.Message);
 end;
 Writeln('Press Enter to exit');
 Readln;
end.
Dealfish answered 4/10, 2011 at 4:40 Comment(3)
I prefer using the Win API instead of WMI when ever possible and this is exactly what I needed. Thanks!Normally
GetLocalComputerName returns incorrect data: if GetComputerName API succeeds nSize contains the number or TCHARs, copied to the output buffer (result var). Therefore one has to do setLength(result, nSize) on successRetrogressive
I think there should be added following at the end of SIDToString : LocalFree(HLocal(StringSid));Thickness
D
4

You can use the Win32_Account WMI class, extracting the machine SID from an user account SID.

for example for a user account where the SID is

S-1-5-21-1299824301-1797996836-594316699-1009

The Machine SID will be

S-1-5-21-1299824301-1797996836-594316699

Check this sample

program GetWMI_Info;

{$APPTYPE CONSOLE}

uses
  SysUtils,
  ActiveX,
  ComObj,
  Variants;

function  GetComputerSID:string;
const
  WbemUser            ='';
  WbemPassword        ='';
  WbemComputer        ='localhost';
  wbemFlagForwardOnly = $00000020;
var
  FSWbemLocator : OLEVariant;
  FWMIService   : OLEVariant;
  FWbemObjectSet: OLEVariant;
  FWbemObject   : OLEVariant;
  oEnum         : IEnumvariant;
  iValue        : LongWord;
begin;
  FSWbemLocator := CreateOleObject('WbemScripting.SWbemLocator');
  FWMIService   := FSWbemLocator.ConnectServer(WbemComputer, 'root\CIMV2', WbemUser, WbemPassword);
  FWbemObjectSet:= FWMIService.ExecQuery('SELECT SID FROM Win32_Account Where SIDType=1','WQL',wbemFlagForwardOnly);
  oEnum         := IUnknown(FWbemObjectSet._NewEnum) as IEnumVariant;
  if oEnum.Next(1, FWbemObject, iValue) = 0 then
  begin
    Result:=FWbemObject.SID;
    Result:=Copy(Result,1,LastDelimiter('-',Result)-1);
    FWbemObject:=Unassigned;
  end;
end;


begin
 try
    CoInitialize(nil);
    try
      Writeln(GetComputerSID);
    finally
      CoUninitialize;
    end;
 except
    on E:EOleException do
        Writeln(Format('EOleException %s %x', [E.Message,E.ErrorCode]));
    on E:Exception do
        Writeln(E.Classname, ':', E.Message);
 end;
 Writeln('Press Enter to exit');
 Readln;
end.
Dealfish answered 4/10, 2011 at 1:18 Comment(0)
W
0

You can get it with LookupAccountName. Pass in NULL for the first parameter and the machine name for the second.

Wellordered answered 3/10, 2011 at 23:35 Comment(1)
the machine name that should be passed is the same one you get from GetComputerName() or should it be in some other format?Normally

© 2022 - 2024 — McMap. All rights reserved.