【问题标题】:How to extract computer/machine SID?如何提取计算机/机器 SID?
【发布时间】:2011-11-30 07:19:07
【问题描述】:

我正在寻找一种使用 Delphi 代码提取计算机 SID 的方法。 SysInternals 有一个名为 PsGetSid 的工具可以执行此操作,但我无法在我的应用程序中使用它。我在 Google 中搜索了一个代码示例,但没有找到。

如何在 Delphi 中实现这一点?

请帮忙。

【问题讨论】:

    标签: delphi winapi registry sid


    【解决方案1】:

    您可以通过LookupAccountName 获取它。第一个参数传入NULL,第二个参数传入机器名。

    【讨论】:

    • 应该传递的机器名称与您从 GetComputerName() 获得的机器名称相同还是应该采用其他格式?
    【解决方案2】:

    您可以使用Win32_Account WMI 类,从用户帐户 SID 中提取机器 SID。

    例如对于 SID 为的用户帐户

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

    机器 SID 将是

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

    检查此示例

    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.
    

    【讨论】:

      【解决方案3】:

      这是使用@MikeKwan 建议的LookupAccountName WinAPi 函数的示例。

      {$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.
      

      【讨论】:

      • 我更喜欢尽可能使用 Win API 而不是 WMI,这正是我所需要的。谢谢!
      • GetLocalComputerName 返回不正确的数据:如果 GetComputerName API 成功,nSize 包含数字或 TCHAR,复制到输出缓冲区(结果 var)。因此,必须在成功时执行 setLength(result, nSize)
      • 我认为应该在SIDToString的末尾添加以下内容:LocalFree(HLocal(StringSid));
      猜你喜欢
      • 2019-10-16
      • 2011-03-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-06-14
      • 1970-01-01
      • 1970-01-01
      • 2010-10-29
      相关资源
      最近更新 更多