Получение информации о пользователях и группах домена
01.01.2007
//////////////////////////////////////////////////////////////////////////////// // // **************************************************************************** // * Project : DomainInfo // * Unit Name : uMain // * Purpose : Демо получения информации о пользователях и группах домена // * Author : Александр (Rouse_) Багель // * Version : 1.00 // **************************************************************************** // // Спасибо милой девушке Ане и группе "Машина Времени" за моральную поддержку... // unit uMain; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ExtCtrls, ComCtrls {$IFDEF VER150} , XPMan {$ENDIF}; const netapi32lib = 'netapi32.dll'; NERR_Success = NO_ERROR; type // Структура для получения информации о рабочей станции PWkstaInfo100 = ^TWkstaInfo100; TWkstaInfo100 = record wki100_platform_id : DWORD; wki100_computername : PWideChar; wki100_langroup : PWideChar; wki100_ver_major : DWORD; wki100_ver_minor : DWORD; end; // Итруктура для определения DNS имени контролера домена TDomainControllerInfoA = record DomainControllerName: LPSTR; DomainControllerAddress: LPSTR; DomainControllerAddressType: ULONG; DomainGuid: TGUID; DomainName: LPSTR; DnsForestName: LPSTR; Flags: ULONG; DcSiteName: LPSTR; ClientSiteName: LPSTR; end; PDomainControllerInfoA = ^TDomainControllerInfoA; // Структура для отображения пользователей PNetDisplayUser = ^TNetDisplayUser; TNetDisplayUser = record usri1_name: LPWSTR; usri1_comment: LPWSTR; usri1_flags: DWORD; usri1_full_name: LPWSTR; usri1_user_id: DWORD; usri1_next_index: DWORD; end; // Структура для отображения рабочих станций PNetDisplayMachine = ^TNetDisplayMachine; TNetDisplayMachine = record usri2_name: LPWSTR; usri2_comment: LPWSTR; usri2_flags: DWORD; usri2_user_id: DWORD; usri2_next_index: DWORD; end; // Структура для отображения групп PNetDisplayGroup = ^TNetDisplayGroup; TNetDisplayGroup = record grpi3_name: LPWSTR; grpi3_comment: LPWSTR; grpi3_group_id: DWORD; grpi3_attributes: DWORD; grpi3_next_index: DWORD; end; // Структура для отображения пользователей принадлежащих группе // или групп в которые входит пользователь PGroupUsersInfo0 = ^TGroupUsersInfo0; TGroupUsersInfo0 = record grui0_name: LPWSTR; end; TfrmDomainInfo = class(TForm) Button1: TButton; gbCurrent: TGroupBox; gbDomainResList: TGroupBox; ledCompName: TLabeledEdit; ledUserName: TLabeledEdit; ledDomainName: TLabeledEdit; ledControllerName: TLabeledEdit; lvUsers: TListView; gbInfo: TGroupBox; lbInfo: TListBox; VSplitter: TSplitter; pcRes: TPageControl; TabSheet1: TTabSheet; TabSheet2: TTabSheet; TabSheet3: TTabSheet; lvWorkStation: TListView; lvGroups: TListView; Label1: TLabel; memTrustedDomains: TMemo; ledDNSName: TLabeledEdit; procedure FormCreate(Sender: TObject); procedure lvGroupsClick(Sender: TObject); private CurrentDomainName: String; function GetCurrentUserName: String; function GetCurrentComputerName: String; function GetDomainController(const DomainName: String): String; function GetDNSDomainName(const DomainName: String): String; function EnumAllTrustedDomains: Boolean; function EnumAllUsers: Boolean; function EnumAllGroups: Boolean; function EnumAllWorkStation: Boolean; function GetSID(const SecureObject: String): String; function GetAllGroupUsers(const GroupName: String): Boolean; function GetAllUserGroups(const UserName: String): Boolean; end; // Функции которые предоставят нам возможность получения информации function NetApiBufferFree(Buffer: Pointer): DWORD; stdcall; external netapi32lib; function NetWkstaGetInfo(ServerName: PWideChar; Level: DWORD; Bufptr: Pointer): DWORD; stdcall; external netapi32lib; function NetGetDCName(ServerName: PWideChar; DomainName: PWideChar; var Bufptr: PWideChar): DWORD; stdcall; external netapi32lib; function DsGetDcName(ComputerName, DomainName: PChar; DomainGuid: PGUID; SiteName: PChar; Flags: ULONG; var DomainControllerInfo: PDomainControllerInfoA): DWORD; stdcall; external netapi32lib name 'DsGetDcNameA'; function NetQueryDisplayInformation(ServerName: PWideChar; Level: DWORD; Index: DWORD; EntriesRequested: DWORD; PreferredMaximumLength: DWORD; var ReturnedEntryCount: DWORD; SortedBuffer: Pointer): DWORD; stdcall; external netapi32lib; function NetGroupGetUsers(ServerName: PWideChar; GroupName: PWideChar; Level: DWORD; var Bufptr: Pointer; PrefMaxLen: DWORD; var EntriesRead: DWORD; var TotalEntries: DWORD; ResumeHandle: PDWORD): DWORD; stdcall; external netapi32lib; function NetUserGetGroups(ServerName: PWideChar; UserName: PWideChar; Level: DWORD; var Bufptr: Pointer; PrefMaxLen: DWORD; var EntriesRead: DWORD; var TotalEntries: DWORD): DWORD; stdcall; external netapi32lib; function NetEnumerateTrustedDomains(ServerName: PWideChar; DomainNames: PWideChar): DWORD; stdcall; external netapi32lib; procedure ConvertSidToStringSid(SID: PSID; var StringSid: LPSTR); stdcall; external advapi32 name 'ConvertSidToStringSidA'; var frmDomainInfo: TfrmDomainInfo; implementation {$R *.dfm} // Данная функция получает информацию о всех группах присутствующих в домене // ============================================================================= function TfrmDomainInfo.EnumAllGroups: Boolean; var Tmp, Info: PNetDisplayGroup; I, CurrIndex, EntriesRequest, PreferredMaximumLength, ReturnedEntryCount: Cardinal; Error: DWORD; begin CurrIndex := 0; repeat Info := nil; // NetQueryDisplayInformation возвращает информацию только о 100-а записях // для того чтобы получить всю информацию используется третий параметр, // передаваемый функции, который определяет с какой записи продолжать // вывод информации EntriesRequest := 100; PreferredMaximumLength := EntriesRequest * SizeOf(TNetDisplayGroup); ReturnedEntryCount := 0; // Для выполнения функции, в нее нужно передать DNS имя контролера домена // (или его IP адрес), с которого мы хочем получить информацию // Для получения информации о группах используется структура NetDisplayGroup // и ее идентификатор 3 (тройка) во втором параметре Error := NetQueryDisplayInformation(StringToOleStr(ledControllerName.Text), 3, CurrIndex, EntriesRequest, PreferredMaximumLength, ReturnedEntryCount, @Info); // При безошибочном выполнении фунции будет результат либо // 1. NERR_Success - все записи возвращены // 2. ERROR_MORE_DATA - записи возвращены, но остались еще и нужно вызывать функцию повторно if Error in [NERR_Success, ERROR_MORE_DATA] then try Tmp := Info; // Выводим информацию которую вернула функция в структуру for I := 0 to ReturnedEntryCount - 1 do begin with lvGroups.Items.Add do begin Caption := Tmp^.grpi3_name; // Имя группы SubItems.Add(Tmp^.grpi3_comment); // Комментарий SubItems.Add(GetSID(Caption)); // SID группы // Запоминаем индекс с которым будем вызывать повторно функцию (если нужно) CurrIndex := Tmp^.grpi3_next_index; end; Inc(Tmp); end; finally // Чтобы небыло утечки ресурсов, освобождаем память занятую функцией под структуру NetApiBufferFree(Info); end; // Если результат выполнения функции ERROR_MORE_DATA - вызываем функцию повторно until Error in [NERR_Success, ERROR_ACCESS_DENIED]; // Ну и возвращаем результат всего что мы тут накодили Result := Error = NERR_Success; end; // Данная функция получает информацию о всех доверенных доменах // ============================================================================= function TfrmDomainInfo.EnumAllTrustedDomains: Boolean; var Tmp, DomainList: PWideChar; begin // Используем недокументированную функцию NetEnumerateTrustedDomains // (только не пойму, с какого перепуга она не документирована?) // Тут все очень просто, на вход имя контролера домена, ны выход - список доверенных доменов Result := NetEnumerateTrustedDomains(StringToOleStr(ledControllerName.Text), @DomainList) = NERR_Success; // Если вызов функции успешен, то... if Result then try Tmp := DomainList; while Length(Tmp) > 0 do begin memTrustedDomains.Lines.Add(Tmp); // Банально выводим список на экран Tmp := Tmp + Length(Tmp) + 1; end; finally // Не забываем про память NetApiBufferFree(DomainList); end; end; // Данная функция получает информацию о всех пользователях присутствующих в домене // ============================================================================= function TfrmDomainInfo.EnumAllUsers: Boolean; var Tmp, Info: PNetDisplayUser; I, CurrIndex, EntriesRequest, PreferredMaximumLength, ReturnedEntryCount: Cardinal; Error: DWORD; begin CurrIndex := 0; repeat Info := nil; // NetQueryDisplayInformation возвращает информацию только о 100-а записях // для того чтобы получить всю информацию используется третий параметр, // передаваемый функции, который определяет с какой записи продолжать // вывод информации EntriesRequest := 100; PreferredMaximumLength := EntriesRequest * SizeOf(TNetDisplayUser); ReturnedEntryCount := 0; // Для выполнения функции, в нее нужно передать DNS имя контролера домена // (или его IP адрес), с которого мы хочем получить информацию // Для получения информации о пользователях используется структура NetDisplayUser // и ее идентификатор 1 (единица) во втором параметре Error := NetQueryDisplayInformation(StringToOleStr(ledControllerName.Text), 1, CurrIndex, EntriesRequest, PreferredMaximumLength, ReturnedEntryCount, @Info); // При безошибочном выполнении фунции будет результат либо // 1. NERR_Success - все записи возвращены // 2. ERROR_MORE_DATA - записи возвращены, но остались еще и нужно вызывать функцию повторно if Error in [NERR_Success, ERROR_MORE_DATA] then try Tmp := Info; // Выводим информацию которую вернула функция в структуру for I := 0 to ReturnedEntryCount - 1 do begin with lvUsers.Items.Add do begin Caption := Tmp^.usri1_name; // Имя пользователя SubItems.Add(Tmp^.usri1_comment); // Комментарий SubItems.Add(GetSID(Caption)); // Его SID // Запоминаем индекс с которым будем вызывать повторно функцию (если нужно) CurrIndex := Tmp^.usri1_next_index; end; Inc(Tmp); end; finally // Грохаем выделенную при вызове NetQueryDisplayInformation память NetApiBufferFree(Info); end; // Если результат выполнения функции ERROR_MORE_DATA // (т.е. есть еще данные) - вызываем функцию повторно until Error in [NERR_Success, ERROR_ACCESS_DENIED]; // Ну и возвращаем результат всего что мы тут накодили Result := Error = NERR_Success; end; // Данная функция получает информацию о всех рабочих станциях присутствующих в домене // Вообщето так делать немного не верно, дело в том что рабочие станции могут // присутствовать в списке не только те, которые завел сисадмин (но для демки сойдет и так) // ============================================================================= function TfrmDomainInfo.EnumAllWorkStation: Boolean; var Tmp, Info: PNetDisplayMachine; I, CurrIndex, EntriesRequest, PreferredMaximumLength, ReturnedEntryCount: Cardinal; Error: DWORD; begin CurrIndex := 0; repeat Info := nil; // NetQueryDisplayInformation возвращает информацию только о 100-а записях // для того чтобы получить всю информацию используется третий параметр, // передаваемый функции, который определяет с какой записи продолжать // вывод информации EntriesRequest := 100; PreferredMaximumLength := EntriesRequest * SizeOf(TNetDisplayMachine); ReturnedEntryCount := 0; // Для выполнения функции, в нее нужно передать DNS имя контролера домена // (или его IP адрес), с которого мы хочем получить информацию // Для получения информации о рабочих станциях используется структура NetDisplayMachine // и ее идентификатор 2 (двойка) во втором параметре Error := NetQueryDisplayInformation(StringToOleStr(ledControllerName.Text), 2, CurrIndex, EntriesRequest, PreferredMaximumLength, ReturnedEntryCount, @Info); // При безошибочном выполнении фунции будет результат либо // 1. NERR_Success - все записи возвращены // 2. ERROR_MORE_DATA - записи возвращены, но остались еще и нужно вызывать функцию повторно if Error in [NERR_Success, ERROR_MORE_DATA] then try Tmp := Info; // Выводим информацию которую вернула функция в структуру for I := 0 to ReturnedEntryCount - 1 do begin with lvWorkStation.Items.Add do begin Caption := Tmp^.usri2_name; // Имя рабочей станции SubItems.Add(Tmp^.usri2_comment); // Комментарий SubItems.Add(GetSID(Caption)); // Её SID // Запоминаем индекс с которым будем вызывать повторно функцию (если нужно) CurrIndex := Tmp^.usri2_next_index; end; Inc(Tmp); end; finally // Дабы небыло утечек NetApiBufferFree(Info); end; // Если результат выполнения функции ERROR_MORE_DATA // (т.е. есть еще данные) - вызываем функцию повторно until Error in [NERR_Success, ERROR_ACCESS_DENIED]; // Ну и возвращаем результат всего что мы тут накодили Result := Error = NERR_Success; end; procedure TfrmDomainInfo.FormCreate(Sender: TObject); begin // Просто вызываем все функции подряд (не делал проверок на результат функций) ledUserName.Text := GetCurrentUserName; ledCompName.Text := GetCurrentComputerName; ledDomainName.Text := CurrentDomainName; ledControllerName.Text := GetDomainController(CurrentDomainName); // Единственно, если нет контролера домена, то дальше определять бесполезно if ledControllerName.Text = '' then Exit; ledDNSName.Text := GetDNSDomainName(CurrentDomainName); EnumAllTrustedDomains; EnumAllUsers; EnumAllWorkStation; EnumAllGroups; end; // Довольно простая функция, возвращает только имена пользователей принадлезжащих группе // ============================================================================= function TfrmDomainInfo.GetAllGroupUsers(const GroupName: String): Boolean; var Tmp, Info: PGroupUsersInfo0; PrefMaxLen, EntriesRead, TotalEntries, ResumeHandle: DWORD; I: Integer; begin // На вход подается список который мы будем заполнять lbInfo.Items.Clear; // Обязательная инициализация ResumeHandle := 0; PrefMaxLen := DWORD(-1); // Выполняем Result := NetGroupGetUsers(StringToOleStr(ledControllerName.Text), StringToOleStr(GroupName), 0, Pointer(Info), PrefMaxLen, EntriesRead, TotalEntries, @ResumeHandle) = NERR_Success; // Смотрим результат... if Result then try Tmp := Info; for I := 0 to EntriesRead - 1 do begin lbInfo.Items.Add(Tmp^.grui0_name); // Банально выводим результат из структуры Inc(Tmp); end; finally // Не забываем, ибо может быть склероз :) NetApiBufferFree(Info); end; end; // Аналогично предыдущей функции (заметьте - структура таже) // ============================================================================= function TfrmDomainInfo.GetAllUserGroups(const UserName: String): Boolean; var Tmp, Info: PGroupUsersInfo0; PrefMaxLen, EntriesRead, TotalEntries: DWORD; I: Integer; begin lbInfo.Items.Clear; PrefMaxLen := DWORD(-1); Result := NetUserGetGroups(StringToOleStr(ledControllerName.Text), StringToOleStr(UserName), 0, Pointer(Info), PrefMaxLen, EntriesRead, TotalEntries) = NERR_Success; if Result then try Tmp := Info; for I := 0 to EntriesRead - 1 do begin lbInfo.Items.Add(Tmp^.grui0_name); Inc(Tmp); end; finally NetApiBufferFree(Info); end; end; // Получаем имя компьютера и имя домена // ============================================================================= function TfrmDomainInfo.GetCurrentComputerName: String; var Info: PWkstaInfo100; Error: DWORD; begin // А для этого мы воспользуемся следующей функцией Error := NetWkstaGetInfo(nil, 100, @Info); if Error <> 0 then raise Exception.Create(SysErrorMessage(Error)); // Как видно, вызов который возвращает обычную структуру, из которой и прочитаем, все что нужно :) // А именно имя компьютера в сети Result := Info^.wki100_computername; // И где он находиться CurrentDomainName := info^.wki100_langroup; end; // Без комментариев // ============================================================================= function TfrmDomainInfo.GetCurrentUserName: String; var Size: Cardinal; begin Size := MAXCHAR; SetLength(Result, Size); GetUserName(PChar(Result), Size); SetLength(Result, Size); end; // Получаем DNS имя контроллера домена // ============================================================================= function TfrmDomainInfo.GetDNSDomainName(const DomainName: String): String; const DS_IS_FLAT_NAME = $00010000; DS_RETURN_DNS_NAME = $40000000; var GUID: PGUID; DomainControllerInfo: PDomainControllerInfoA; begin GUID := nil; // Для большинства операций нам потребуется IP адрес контроллера домена // или его DNS имя, которое мы получим вот так: if DsGetDcName(nil, PChar(CurrentDomainName), GUID, nil, DS_IS_FLAT_NAME or DS_RETURN_DNS_NAME, DomainControllerInfo) = NERR_Success then // Параметры которые мы передаем означают: // DS_IS_FLAT_NAME - передаем просто имя домена // DS_RETURN_DNS_NAME - ждем получения DNS имени try Result := DomainControllerInfo^.DomainControllerName; // Результат собсно тут... finally // Склероз это болезнь, ее нужно лечить... NetApiBufferFree(DomainControllerInfo); end; end; // Ну тут без комментариев - просто получаем имя контроллера домена // ============================================================================= function TfrmDomainInfo.GetDomainController(const DomainName: String): String; var Domain: WideString; Server: PWideChar; begin Domain := StringToOleStr(DomainName); if NetGetDCName(nil, @Domain[1], Server) = NERR_Success then try Result := Server; finally NetApiBufferFree(Server); end; end; // Не знаю зачем добавил это, ну раз добавил - получение SID объекта // Без комментариев... // ============================================================================= function TfrmDomainInfo.GetSID(const SecureObject: String): String; var SID: PSID; StringSid: PChar; ReferencedDomain: String; cbSid, cbReferencedDomain:DWORD; peUse: SID_NAME_USE; begin cbSID := 128; cbReferencedDomain := 16; GetMem(SID, cbSid); try SetLength(ReferencedDomain, cbReferencedDomain); if LookupAccountName(PChar(ledDNSName.Text), PChar(SecureObject), SID, cbSid, @ReferencedDomain[1], cbReferencedDomain, peUse) then begin ConvertSidToStringSid(SID, StringSid); Result := StringSid; end; finally FreeMem(SID); end; end; procedure TfrmDomainInfo.lvGroupsClick(Sender: TObject); var Value: String; begin if (Sender as TListView).Selected = nil then Exit; Value := (Sender as TListView).Selected.Caption; case (Sender as TListView).Tag of 0: begin gbInfo.Caption := Format('Группы в которые входит пользователь "%s"', [Value]); GetAllUserGroups(Value); end; 1: begin gbInfo.Caption := Format('Группы в которые входит рабочая станция "%s"', [Value]); GetAllUserGroups(Value); end; 2: begin gbInfo.Caption := Format('Объекты входящие в группу "%s"', [Value]); GetAllGroupUsers(Value); end; end; end; end.
Проект также доступен по адресу: http://rouse.front.ru/domaininfo.zip
Взято из https://forum.sources.ru