Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Win32/Win64 API (native code) (https://www.delphipraxis.net/17-win32-win64-api-native-code/)
-   -   Delphi Benutzernamen von Prozessen ermitteln (https://www.delphipraxis.net/71133-benutzernamen-von-prozessen-ermitteln.html)

peanut 9. Jun 2006 12:14


Benutzernamen von Prozessen ermitteln
 
Hallo!

Ich möchte gerne alle Prozesse auf einem System auflisten und außerdem
anzeigen lassen, unter welchem Benutzer ein jeweiliger Prozess ausgeführt
wird. Mittels CreateToolhelp32Snapshot kommt man zwar sehr leicht an
alle laufenden Prozesse, jedoch nicht an die Benutzernamen.

Ich habe schon mal versucht folgendermaßen vorzugehen:

i) OpenProcess(...mit jeweiliger ProzessId)
ii) GetSecurityInfo(...SE_KERNEL_OBJECT...OWNER_SECURI TY_INFORMATION...)
iii) die in ii) erhaltene SID einem Benutzer zuordnen


Sieht umständlich aus - ist es auch. :wall: Außerdem klappt das insbesondere
bei Systemprozessen nicht immer reibungslos?!

Weiß jemand, wie man elegant an den Namen des Benutzers kommt, der einen
Prozess ausführt?

Besten Dank im Voraus!

NicoDE 9. Jun 2006 12:38

Re: Benutzernamen von Prozessen ermitteln
 
Zitat:

Zitat von peanut
Außerdem klappt das insbesondere bei Systemprozessen nicht immer reibungslos?!

Ist das ein Problem bei (ii) oder (iii)?

peanut 9. Jun 2006 15:12

Re: Benutzernamen von Prozessen ermitteln
 
Zitat:

Zitat von NicoDE
Zitat:

Zitat von peanut
Außerdem klappt das insbesondere bei Systemprozessen nicht immer reibungslos?!

Ist das ein Problem bei (ii) oder (iii)?

Bei (ii) gibt es hier schon Probleme - erhalte keine SID.

NicoDE 9. Jun 2006 16:38

Re: Benutzernamen von Prozessen ermitteln
 
Schon mit OpenProcess(PROCESS_QUERY_INFORMATION)/OpenProcessToken(TOKEN_QUERY)/QueryTokenInformation(TokenUser) probiert?
(eventuell vorher noch das Debug-Privileg für den eigenen Prozess aktivieren)

peanut 10. Jun 2006 15:17

Re: Benutzernamen von Prozessen ermitteln
 
Habe SeDebugPrivilege und Prozess-Token wurde mit
PROCESS_QUERY_INFORMATION geöffnet. Klappt einwandfrei bei
Prozessen von normalen Benutzern, nicht aber bei Netzwerk-
und Systemdiensten.

NicoDE 12. Jun 2006 08:31

Re: Benutzernamen von Prozessen ermitteln
 
Zitat:

Zitat von peanut
Habe SeDebugPrivilege und Prozess-Token wurde mit
PROCESS_QUERY_INFORMATION geöffnet. Klappt einwandfrei bei
Prozessen von normalen Benutzern, nicht aber bei Netzwerk-
und Systemdiensten.

Eine alternative kann ich dir leider nicht anbieten. Der TaskManager von Microsoft verwendet(e) offensichtlich eine undokumentierte API dazu...

Olli 12. Jun 2006 11:48

Re: Benutzernamen von Prozessen ermitteln
 
Aus diesem Grund zeigt der TaskMgr nur den Benutzernamen an, wenn er auf einem Terminalserver-System läuft. Ansonsten bleibt diese Option deaktiviert!!!

Also, keine Chance, es sei denn du ermittelst dir irgendwie die Prototypen der beiden Verdächtigen, einer Funktion WinStationGetProcessSid() aus WINSTA.DLL und einer CachedGetUserFromSid() aus UTILDLL.DLL?! Ich schau's mir nachher nochmal an, die aus WINSTA.DLL brauchst du jedenfalls definitiv, die andere offensichtlich nur wenn du einfach an den Benutzernamen kommen willst.

Die letzten beiden Parameter bei Bei Google suchenWinStationGetProcessSid sind IMO der SID (als PSID) und die Größe des SID in Bytes (als DWORD oder so). Die anderen Parameter konnte ich nicht entziffern. Ist wie schon von Nico erwähnt alles undokumentiert. Selbst bei ReactOS (sinst eine gute Quelle bei undokumentierten Funktionen) scheint man noch drüber zu grübeln ...
Sicher ist, daß TaskMgr diese Funktion auf einem TS aufruft. Unter W2K ist die Funktion per GetProcAddress eingebunden, bei WXP über Delay Loading ...

In meinem Disassemblat sehe ich sogar, daß offensichtlich vom Task-Manager auf TS eine Funktion WinStationTerminateProcess() benutzt wird um Prozesse zu beenden. Wo der Sinn ist, bleibt mir noch rätselhaft. Vielleicht ist das nur Terminal-Session-übergreifend nötig oder so?!? Jedenfalls konnte ich in einem russischen Forum den Hinweis auf WinStationGetProcessSid() finden Kak uznat' imja jusera, wo aber der Fragesteller auch nur nach Hilfe fragt ... :pale:

NicoDE 12. Jun 2006 12:00

Re: Benutzernamen von Prozessen ermitteln
 
Zitat:

Zitat von Olli
WinStationGetProcessSid() aus WINSTA.DLL

Zur eindeutigen Identifizierung des Zielprozesses benötigt man für diese Funktion außer der ProcessId auch noch die CreationTime (GetProcessTimes oder native API). Zudem muss man sich erstmal ein RPC-Handle besorgen (WinStationConnect).

Zitat:

Zitat von Olli
CachedGetUserFromSid() aus UTILDLL.DLL?

Das ist nur eine optimierte Variante, die letztendlich LookupAccountSid verwendet und die Daten der letzten Aufrufe zwiswchenspeichert.

peanut 12. Jun 2006 12:05

Re: Benutzernamen von Prozessen ermitteln
 
Hallo!

Danke für die vielen Hinweise, ich werde mal versuchen damit so weit wie möglich zu
kommen. Wenn etwas einigermaßen brauchbares rausspringt, kommt der Quellcode hier
rein :-D

Viele Grüße

peanut.

Olli 12. Jun 2006 12:30

Re: Benutzernamen von Prozessen ermitteln
 
Zitat:

Zitat von NicoDE
Zitat:

Zitat von Olli
WinStationGetProcessSid() aus WINSTA.DLL

Zur eindeutigen Identifizierung des Zielprozesses benötigt man für diese Funktion außer der ProcessId auch noch die CreationTime (GetProcessTimes oder native API). Zudem muss man sich erstmal ein RPC-Handle besorgen (WinStationConnect).

Wer zuviel weiß, kommt in die Suppe. Demnächst gibt's Nico-Eintopf ... :mrgreen:
Aber schon interessant. Würdest du dich dazu herablassen zu dieser Funktion und WinStationConnect die C- oder Delphi-Prototypen hier hinzuklimpern? :tongue:

Zitat:

Zitat von NicoDE
Zitat:

Zitat von Olli
CachedGetUserFromSid() aus UTILDLL.DLL?

Das ist nur eine optimierte Variante, die letztendlich LookupAccountSid verwendet und die Daten der letzten Aufrufe zwiswchenspeichert.

Das dachte ich mir schon. Wenn die über Ordinals importiert worden wären, hätte es schon düsterer ausgesehen. Da kann man dann nicht mal einfach so vom Namen auf die Funktionalität schließen.

NicoDE 12. Jun 2006 13:13

Re: Benutzernamen von Prozessen ermitteln
 
Zitat:

Zitat von Olli
Würdest du dich dazu herablassen zu dieser Funktion und WinStationConnect die C- oder Delphi-Prototypen hier hinzuklimpern?

Dann lasse ich mich mal herab ;)
(wer undokumentierte Funktionen benutzt, sollte Fallbacks implementieren)
Delphi-Quellcode:
// winsta.dll

const
  LOGONID_CURRENT = ULONG(-1);
  SERVERNAME_CURRENT = THandle(nil);

type
  TFNWinStationConnectW = function(
    hServer       : THandle;
    SessionId     : ULONG;
    TargetSessionId: ULONG;
    pPassword     : PWideChar; // use L"" instead of NULL
    bWait         : BOOLEAN
  ): BOOLEAN; stdcall;

type
  TFNWinStationDisconnect = function(
    hServer : THandle;
    SessionId: ULONG;
    bWait   : BOOLEAN
    ): BOOLEAN; stdcall;

type
  TFNWinStationGetProcessSid = function(
    hServer        : THandle;
    ProcessId      : ULONG;
    ProcessStartTime: TFileTime;
    pProcessUserSid : PSID;
    var dwSidSize  : ULONG
    ): BOOLEAN; stdcall;

// utildll.dll

type
  TFNCachedGetUserFromSid = procedure(
    pSid         : PSID;
    pUserName    : PWideChar;
    var cbUserName: ULONG
  ); stdcall;
Wie immer ohne Gewähr...

ps: Für WinStationGetProcessSid braucht man kein WinStationConnect, allerdings muss die CreateTime stimmen, ansonsten gibt die WinStationGetProcessSid 'ungültiges Handle' zurück.

Olli 12. Jun 2006 14:22

Re: Benutzernamen von Prozessen ermitteln
 
Zitat:

Zitat von NicoDE
ps: Für WinStationGetProcessSid braucht man kein WinStationConnect, allerdings muss die CreateTime stimmen, ansonsten gibt die WinStationGetProcessSid 'ungültiges Handle' zurück.

Ick wollt schon sagen, da wird doch ne 0 übergeben ...

Na denn: sssänk juh wärrie matsch :mrgreen: :mrgreen: :mrgreen: :mrgreen: :mrgreen:

(BTW: Bekommst heute noch Mail bzgl. Objektnamen und Treiber)

peanut 12. Jun 2006 17:10

Programm lp
 
War auch etwas fleißig:

Delphi-Quellcode:
program lp; {$APPTYPE CONSOLE}

uses
  SysUtils,
  Windows,
  tlhelp32,
  psapi;

type
  LPByte = ^Byte;

  TNET_DISPLAY_USER = record
    usri1_name     : LPWSTR;
    usri1_comment  : LPWSTR;
    usri1_flags    : DWORD;
    usri1_full_name : LPWSTR;
    usri1_user_id  : DWORD;
    usri1_next_index: DWORD;
  end;
  PNET_DISPLAY_USER = ^TNET_DISPLAY_USER;


  function ConvertSidToStringSidA(Sid: PSID; var StringSid: LPTSTR): LongBool; stdcall; external 'advapi32.dll';
  function GetSecurityInfo(handle: THandle; ObjectType: DWord;   SecurityInfo: SECURITY_INFORMATION; ppsidOwner: PSID; ppsidGroup: PSID;   ppDacl: PACL;   ppSacl: PACL; ppSecurityDescriptor: PSECURITY_DESCRIPTOR): DWORD; stdcall; external 'advapi32.dll';

  function NetQueryDisplayInformation(ServerName: LPWSTR; Level, Index, EntriesRequested, ReferredMaximumLength: DWORD; var ReturnedEntryCount: DWORD; var SortedBuffer: LPBYTE): LongWord; stdcall; external 'Netapi32.dll';
  function NetApiBufferFree(Buffer: Pointer): DWORD; stdcall; external 'Netapi32.dll';

const
  SE_UNKNOWN_OBJECT_TYPE: DWord = 0;
  SE_FILE_OBJECT: DWord = 1;
  SE_SERVICE: DWord = 2;
  SE_PRINTER: DWord = 3;
  SE_REGISTRY_KEY: DWord = 4;
  SE_LMSHARE: DWord = 5;
  SE_KERNEL_OBJECT: DWord = 6;
  SE_WINDOW_OBJECT: DWord = 7;

function AdjustToken(sPrivilege: String; boEnable: Boolean): Boolean;
var
  hToken     : Cardinal;
  lpLuid     : Int64;
  NewPState  : TOKEN_PRIVILEGES;
  ReturnLength: DWORD;
begin
  Result := False;
  try
    if (OpenProcessToken(GetCurrentProcess, TOKEN_ADJUST_PRIVILEGES or TOKEN_QUERY, hToken)) then
    begin
      if (LookupPrivilegeValue(nil, PCHAR(sPrivilege), lpLuid)) then
      begin
        ZeroMemory(@NewPState, SizeOf(TOKEN_PRIVILEGES));
        NewPState.PrivilegeCount := 1;
        NewPState.Privileges[0].Luid := lpLuid;
        if (boEnable) then
          NewPState.Privileges[0].Attributes := SE_PRIVILEGE_ENABLED;
        ReturnLength := 0;
        if (AdjustTokenPrivileges(hToken, False, NewPState, 0, nil, ReturnLength)) then
          Result := True;
      end;
      CloseHandle(hToken);
    end;
  except
  end;
end;

function GetEXEByPID(pid: DWord): String;
var
  h: THandle;
begin
  Result := '';
  h := OpenProcess(PROCESS_QUERY_INFORMATION or PROCESS_VM_READ, False, pid);
  if (h <> 0) then
  try
    SetLength(Result, MAX_PATH);
    ZeroMemory(@Result[1], MAX_PATH);
    GetModuleFileNameEx(h, 0, PChar(Result), MAX_PATH);
    Result:=TrimRight(Result);
  finally
    CloseHandle(h);
  end;
  Result := LowerCase(Result);
end;

function SidToString(ASID: PSID): String;
var
  sDummy: LPTSTR;
begin
  ConvertSidToStringSidA(ASID, sDummy);
  Result := String(sDummy);
end;

function GetUserBySID(ASID: PSID): String;
var
  total: DWord;
  sBuffer: LPByte;
  UserInfo: PNET_DISPLAY_USER;
  UserSID : PSID;
  cbUserSID: Cardinal;
  sDomain : String;
  sUserName: String;
  cbsDomain: Cardinal;
  pNameUse : SID_NAME_USE;

begin
  Result := '';
  NetQueryDisplayInformation('\\.', 1, 0, 8192, 65536, total, sBuffer);
  UserInfo := @sBuffer^;
  while (total > 0) do
  begin
    sUserName := String(UserInfo.usri1_name);
    cbUserSID := 0; UserSID := nil;
    sDomain := ''; cbsDomain := 0;
    LookupAccountName(PChar('\\.'),
                      PChar(sUserName),
                      UserSID,
                      cbUserSID,
                      @sDomain[1],
                      cbsDomain,
                      pNameUse);
    GetMem(UserSID, cbUserSID);
    ZeroMemory(UserSID, cbUserSID);
    SetLength(sDomain, cbsDomain);
    ZeroMemory(@sDomain[1], Length(sDomain));
    if (LookupAccountName(PChar('\\.'),
                          PChar(sUserName),
                          UserSID,
                          cbUserSID,
                          @sDomain[1],
                          cbsDomain,
                          pNameUse)) then
    begin
      ReallocMem(UserSID, cbUserSID);
      if IsValidSid(UserSID) and (SidToString(ASID) = SidToString(UserSID)) then
      begin
        Result := sUserName;
        break;
      end;
    end;
    dec(total);
    inc(UserInfo);
  end;
  NetApiBufferFree(sBuffer);
end;

function GetUserByPID(pid: DWord): String;
var
  phandle: THandle;
  ppsidOwner: PSID;
  SecDescriptor: PSECURITY_DESCRIPTOR;
  nResult: DWord;
begin
  Result := '';
  try
    phandle := OpenProcess(PROCESS_QUERY_INFORMATION or GENERIC_READ, False, pid);
    if (phandle <> 0) then
    begin
      nResult := GetSecurityInfo(phandle,
                                 SE_KERNEL_OBJECT,
                                 OWNER_SECURITY_INFORMATION,
                                 @ppsidOwner,
                                 nil,
                                 nil,
                                 nil,
                                 @SecDescriptor);
      if (nResult = 0) then
      begin
        Result := GetUserBySID(ppsidOwner); // SidToString(ppsidOwner); (*)
        LocalFree(Cardinal(SecDescriptor));
      end;
    end;
  except
  end;
end;

function GetProcessesAndUserNames: Integer;
var
  hProcSnap: THandle;
  pe32     : TProcessEntry32;
  sEXEname : String;
  sUserName: String;
begin
  Result := 0;

  AdjustToken('SeDebugPrivilege', True);

  // Perform snapshot
  hProcSnap := CreateToolHelp32SnapShot(TH32CS_SNAPALL, 0);
  if hProcSnap = INVALID_HANDLE_VALUE then exit;

  pe32.dwSize := SizeOf(pe32);
  if (Process32First(hProcSnap, pe32)) then
  begin
    // let`s go...
    writeln(UpperCase(pe32.szExeFile));
    inc(Result);
    while (Process32Next(hProcSnap, pe32) = true) and (pe32.th32ProcessID <> GetCurrentProcessId) do
    begin
      sEXEname := Trim(LowerCase(GetEXEByPID(pe32.th32ProcessID)));
      sUserName := Trim(LowerCase(GetUserByPID(pe32.th32ProcessID)));

      if (sEXEname <> '') and (sEXEname <> '?') then
        writeln(sEXEname + ' | ' + sUserName)
      else
        writeln('['+UpperCase(pe32.szExeFile)+']' + ' | ' + sUserName);

      inc(Result);
    end;
  end;
  CloseHandle(hProcSnap);

  AdjustToken('SeDebugPrivilege', False);
end;

begin
  GetProcessesAndUserNames;
  readln;
end.
Wie man testenkann (*), komme ich jetzt auch an alle SIDs, leider klappt das mit dem mapping nicht so toll und man erhält häufig nur leere Strings.

Luckie 15. Sep 2006 10:52

Re: Benutzernamen von Prozessen ermitteln
 
Liste der Anhänge anzeigen (Anzahl: 1)
Ich habe mir das mal angeguckt. Zum einem ist es sehr umständlich, zu mal du auch interne Funktionen benutzt und zum anderen gibst du Speicher nicht mehr frei, den du mit GetMem alloziierst bzw. du schließt die Handle nicht mehr, die du mit OpenProcess öffnest.

Ich habe das mal etwas umgeschrieben und verbesser. Unit im Anhang.

peanut 15. Sep 2006 11:24

Re: Benutzernamen von Prozessen ermitteln
 
Hallo Michael,

Zitat:

Zitat von Luckie
Zum einem ist es sehr umständlich

jetzt wo ich Deine Bibliothek habe, sehe ich das auch so :)

Zitat:

Zitat von Luckie
gibst du Speicher nicht mehr frei, den du mit GetMem alloziierst

habe ich jetzt auch gesehen :shock: ups.

Danke für die Mühe!

Viele Grüße peanut.


Alle Zeitangaben in WEZ +1. Es ist jetzt 00:25 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz