I would like to know how I can find out which ports a program / process uses. I want to know the used ports from one process and write then in a label.
Is there a unit or function that is available?
You can use the GetExtendedTcpTable function passing the TCP_TABLE_OWNER_PID_ALL TableClass value , this will return a MIB_TCPTABLE_OWNER_PID structure which is an array to the MIB_TCPROW_OWNER_PID record , this structure contains the port number (dwLocalPort) and the PID (dwOwningPid) of the process, you can resolve the name of the PID using the CreateToolhelp32Snapshot function.
Sample
{$APPTYPE CONSOLE}
uses
WinSock,
TlHelp32,
Classes,
Windows,
SysUtils;
const
ANY_SIZE = 1;
iphlpapi = 'iphlpapi.dll';
TCP_TABLE_OWNER_PID_ALL = 5;
type
TCP_TABLE_CLASS = Integer;
PMibTcpRowOwnerPid = ^TMibTcpRowOwnerPid;
TMibTcpRowOwnerPid = packed record
dwState : DWORD;
dwLocalAddr : DWORD;
dwLocalPort : DWORD;
dwRemoteAddr: DWORD;
dwRemotePort: DWORD;
dwOwningPid : DWORD;
end;
PMIB_TCPTABLE_OWNER_PID = ^MIB_TCPTABLE_OWNER_PID;
MIB_TCPTABLE_OWNER_PID = packed record
dwNumEntries: DWORD;
table: Array [0..ANY_SIZE - 1] of TMibTcpRowOwnerPid;
end;
var
GetExtendedTcpTable:function (pTcpTable: Pointer; dwSize: PDWORD; bOrder: BOOL; lAf: ULONG; TableClass: TCP_TABLE_CLASS; Reserved: ULONG): DWord; stdcall;
function GetPIDName(hSnapShot: THandle; PID: DWORD): string;
var
ProcInfo: TProcessEntry32;
begin
ProcInfo.dwSize := SizeOf(ProcInfo);
if not Process32First(hSnapShot, ProcInfo) then
Result := 'Unknow'
else
repeat
if ProcInfo.th32ProcessID = PID then
Result := ProcInfo.szExeFile;
until not Process32Next(hSnapShot, ProcInfo);
end;
procedure ShowTCPPortsUsed(const AppName : string);
var
Error : DWORD;
TableSize : DWORD;
i : integer;
pTcpTable : PMIB_TCPTABLE_OWNER_PID;
SnapShot : THandle;
LAppName : string;
LPorts : TStrings;
begin
LPorts:=TStringList.Create;
try
TableSize := 0;
//Get the size o the tcp table
Error := GetExtendedTcpTable(nil, #TableSize, False, AF_INET, TCP_TABLE_OWNER_PID_ALL, 0);
if Error <> ERROR_INSUFFICIENT_BUFFER then exit;
GetMem(pTcpTable, TableSize);
try
SnapShot := CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
try
//get the tcp table data
if GetExtendedTcpTable(pTcpTable, #TableSize, TRUE, AF_INET, TCP_TABLE_OWNER_PID_ALL, 0) = NO_ERROR then
for i := 0 to pTcpTable.dwNumEntries - 1 do
begin
LAppName:=GetPIDName(SnapShot, pTcpTable.Table[i].dwOwningPid);
if SameText(LAppName, AppName) and (LPorts.IndexOf(IntToStr(pTcpTable.Table[i].dwLocalPort))=-1) then
LPorts.Add(IntToStr(pTcpTable.Table[i].dwLocalPort));
end;
finally
CloseHandle(SnapShot);
end;
finally
FreeMem(pTcpTable);
end;
Writeln(LPorts.Text);
finally
LPorts.Free;
end;
end;
var
hModule : THandle;
begin
try
hModule := LoadLibrary(iphlpapi);
try
GetExtendedTcpTable := GetProcAddress(hModule, 'GetExtendedTcpTable');
ShowTCPPortsUsed('Skype.exe');
finally
FreeLibrary(hModule);
end;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
Readln;
end.
In order to get the correct Port number you have to use ntohs()
if SameText(LAppName, AppName) and
(LPorts.IndexOf(IntToStr(pTcpTable.Table[i].dwLocalPort))=-1) then
LPorts.Add(IntToStr(ntohs(pTcpTable.Table[i].dwLocalPort)));
more info here
Related
How can I correctly call wine_nt_to_unix_file_name from WINE's ntdll.dll in Delphi (10.4)?
In the web I found the definition to be like this:
NTSTATUS wine_nt_to_unix_file_name(const UNICODE_STRING *nameW, ANSI_STRING *unix_name_ret, UINT disposition, BOOLEAN check_case)
Disposition changes the return result for non existent last path part and check_case is self explanatory.
I would like to use this function to display real unix paths of my application to the user when running in WINE. This should make it more easy for a medium user to find a folder to share data between native apps and the WINE environment.
What I tried:
type
TWineGetVersion = function: PAnsiChar; stdcall;
TWineNTToUnixFileName = procedure(pIn: Pointer; pOut: Pointer; aParam: integer; caseSens: Boolean); stdcall;
...
initialization
try
LHandle := LoadLibrary('ntdll.dll');
if LHandle > 32 then
begin
LWineGetVersion := GetProcAddress(LHandle, 'wine_get_version');
LWineNTToUnixFileName := GetProcAddress(LHandle, 'wine_nt_to_unix_file_name');
end;
except
LWineGetVersion := nil;
LWineNTToUnixFileName := nil;
end;
Retrieving the WINE version works great but I cannot get the path conversion up and running as I don't know how to handle the returned Pointer to ANSI_STRING what seems to be a Windows structure like this:
typedef struct _STRING {
USHORT Length;
USHORT MaximumLength;
PCHAR Buffer;
} STRING;
I tried to approach the problem this way:
MyBuffer: array [0 .. 2048] of AnsiChar;
LWineNTToUnixFileName(PChar(aWinPath), #MyBuffer, 0, true);
But the function is returning total garbage in the buffer when output byte by byte.
Update
Following the hint to the current Wine source and the hint with the structure I tried this version, unfortunately delivering garbage. The first parameter is a UNICODE STRING structure, the second a simple ansistring. The third parameter receives the length of the returned buffer.
type
TWineNTToUnixFileName = procedure(pIn: Pointer; pOut: Pointer; aLen: Pointer); stdcall;
TWineUnicodeString = packed record
Len: Word;
MaxLen: Word;
Buffer: PWideChar;
end;
function WinePath(const aWinPath: String): String;
var
inString: TWineUnicodeString;
MyBuffer: array [0 .. 2048] of AnsiChar;
aLen,i: integer;
begin
inString.Buffer := PChar(aWinPath);
inString.Len := length(aWinPath);
inString.MaxLen := inString.Len;
LWineNTToUnixFileName(#inString, #MyBuffer, #aLen);
result := '';
for i := 1 to 20 do
result := result + MyBuffer[i];
end;
Based on Zeds great answer i created this function that automatically tries the new API call if the old one fails
type
TWineAnsiString = packed record
Len: Word;
MaxLen: Word;
Buffer: PAnsiChar;
end;
PWineAnsiString = ^TWineAnsiString;
TWineUnicodeString = packed record
Len: Word;
MaxLen: Word;
Buffer: PWideChar;
end;
PWineUnicodeString = ^TWineUnicodeString;
var
wine_get_version: function: PAnsiChar; cdecl;
// Both are assigned to the function in ntdll.dll to be able to try both alternatives
wine_nt_to_unix_file_name: function(const nameW: PWineUnicodeString; unix_name_ret: PWineAnsiString; disposition: Cardinal): Cardinal; cdecl;
wine_nt_to_unix_file_name_1: function(const nameW: PWineUnicodeString; nameA: PAnsiChar; Sz: PCardinal; disposition: Cardinal): Cardinal; cdecl;
LHandle: THandle;
function WinePath(const aPathIn: String): String;
var
VSz: Cardinal;
VNameA: AnsiString;
VNameW: TWineUnicodeString;
VUnixNameRet: TWineAnsiString;
VStatus: Cardinal;
aPath: String;
newVersion: Boolean;
begin
if not assigned(wine_nt_to_unix_file_name) then
begin
Result := 'n/a';
exit;
end;
aPath := '\??\' + aPathIn;
Result := '?';
newVersion := false;
VNameW.Len := Length(aPath) * SizeOf(WideChar);
VNameW.MaxLen := VNameW.Len;
VNameW.Buffer := PWideChar(aPath);
VUnixNameRet.Len := 0;
VUnixNameRet.MaxLen := 0;
VUnixNameRet.Buffer := nil;
VStatus := wine_nt_to_unix_file_name(#VNameW, #VUnixNameRet, 0);
if VStatus <> 0 then
begin
VSz := 255;
SetLength(VNameA, VSz);
ZeroMemory(Pointer(VNameA), VSz);
VStatus := wine_nt_to_unix_file_name_1(#VNameW, Pointer(VNameA), #VSz, 0);
newVersion := true;
end;
if VStatus <> 0 then
begin
Result := 'Error ' + IntToStr(Status);
exit;
end;
if not newVersion then
begin
VSz := VUnixNameRet.Len;
SetString(VNameA, VUnixNameRet.Buffer, VSz);
// ToDo: RtlFreeAnsiString(#VUnixNameRet)
end
else
SetLength(VNameA, VSz);
Result := StringReplace(VNameA, '/dosdevices/c:/', '/drive_c/', [rfIgnoreCase]);
end;
Try this type for MyBuffer:
type
TWineString = packed record
Len : Word;
MaxLen : Word;
Buffer : PAnsiChar;
end;
Also you can't pass PChar as input string because it isn't a UNICODE_STRING as defined in wine:
typedef struct _UNICODE_STRING {
USHORT Length; /* bytes */
USHORT MaximumLength; /* bytes */
PWSTR Buffer;
} UNICODE_STRING, *PUNICODE_STRING;
You should use this equivalent:
type
TWineUnicodeString = packed record
Len : Word;
MaxLen : Word;
Buffer : PWideChar;
end;
Update: This function has changed its API 6 months ago, so depending on wine version you should use one of two ways: define USE_WINE_STABLE if you are on stable wine v5.0 or undefine it if you use newer version:
program WineTest;
{$APPTYPE CONSOLE}
{$R *.res}
uses
Winapi.Windows,
System.SysUtils;
{$DEFINE USE_WINE_STABLE}
type
{$IFDEF USE_WINE_STABLE}
TWineAnsiString = packed record
Len : Word;
MaxLen : Word;
Buffer : PAnsiChar;
end;
PWineAnsiString = ^TWineAnsiString;
{$ENDIF}
TWineUnicodeString = packed record
Len : Word;
MaxLen : Word;
Buffer : PWideChar;
end;
PWineUnicodeString = ^TWineUnicodeString;
var
wine_get_version: function: PAnsiChar; cdecl;
{$IFDEF USE_WINE_STABLE}
wine_nt_to_unix_file_name: function(const nameW: PWineUnicodeString;
unix_name_ret: PWineAnsiString; disposition: Cardinal): Cardinal; cdecl;
{$ELSE}
wine_nt_to_unix_file_name: function(const nameW: PWineUnicodeString;
nameA: PAnsiChar; Sz: PCardinal; disposition: Cardinal): Cardinal; cdecl;
{$ENDIF}
procedure TestWinePath(const APath: string);
var
VSz: Cardinal;
VNameA: AnsiString;
VNameW: TWineUnicodeString;
{$IFDEF USE_WINE_STABLE}
VUnixNameRet: TWineAnsiString;
{$ENDIF}
VStatus: Cardinal;
begin
VNameW.Len := Length(APath) * SizeOf(WideChar);
VNameW.MaxLen := VNameW.Len;
VNameW.Buffer := PWideChar(APath);
{$IFDEF USE_WINE_STABLE}
VUnixNameRet.Len := 0;
VUnixNameRet.MaxLen := 0;
VUnixNameRet.Buffer := nil;
VStatus := wine_nt_to_unix_file_name(#VNameW, #VUnixNameRet, 0);
{$ELSE}
VSz := 255;
SetLength(VNameA, VSz);
ZeroMemory(Pointer(VNameA), VSz);
VStatus := wine_nt_to_unix_file_name(#VNameW, Pointer(VNameA), #VSz, 0);
{$ENDIF}
Writeln('wine_nt_to_unix_file_name:');
Writeln('status = 0x', IntToHex(VStatus, 8));
if VStatus <> 0 then begin
Exit;
end;
{$IFDEF USE_WINE_STABLE}
VSz := VUnixNameRet.Len;
SetString(VNameA, VUnixNameRet.Buffer, VSz);
// ToDo: RtlFreeAnsiString(#VUnixNameRet)
{$ELSE}
SetLength(VNameA, VSz);
{$ENDIF}
Writeln('unix len = ', VSz);
Writeln('unix: ', VNameA);
Writeln('nt: ', APath);
end;
function LoadProc(const AHandle: THandle; const AName: string): Pointer;
begin
Result := GetProcAddress(AHandle, PChar(AName));
if Result = nil then begin
raise Exception.CreateFmt('Can''t load function: "%s"', [AName]);
end;
end;
var
LHandle: THandle;
LNtFileName: string;
begin
try
LNtFileName := ParamStr(1);
if LNtFileName = '' then begin
Writeln('Usage: ', ExtractFileName(ParamStr(0)), ' NtFileName');
Exit;
end;
LHandle := LoadLibrary('ntdll.dll');
if LHandle > 32 then begin
wine_get_version := LoadProc(LHandle, 'wine_get_version');
Writeln('wine version = ', wine_get_version() );
wine_nt_to_unix_file_name := LoadProc(LHandle, 'wine_nt_to_unix_file_name');
TestWinePath(LNtFileName);
end;
except
on E: Exception do begin
Writeln(E.ClassName, ': ', E.Message);
end;
end;
end.
Output (tested on Ubuntu 20.04):
$ wine WineTest.exe "\??\c:\windows\notepad.exe"
wine version = 5.0
wine_nt_to_unix_file_name:
status = 0x00000000
unix len = 49
unix: /home/zed/.wine/dosdevices/c:/windows/notepad.exe
nt: \??\c:\windows\notepad.exe
I'm hooking GetWindowThreadProcessId() with sucess using the following code.
Now i want check if dwProcessID parameter corresponds to id of determinated process and in positive case prevent execute original function:
Result := OldGetWindowThreadProcessId(hWnd, dwProcessID);
I tried this, but not worked:
if dwProcessID = 12345 then exit;
Here is my complete code:
library MyLIB;
uses
Windows,
ImageHlp;
{$R *.res}
type
PGetWindowThreadProcessId = function(hWnd: THandle; dwProcessID: DWord)
: DWord; stdcall;
var
OldGetWindowThreadProcessId: PGetWindowThreadProcessId;
function HookGetWindowThreadProcessId(hWnd: THandle; dwProcessID: DWord)
: DWord; stdcall;
begin
try
// Check if is some process
except
MessageBox(0, 'Error', 'HookGetWindowThreadProcessId Error', 0);
end;
Result := OldGetWindowThreadProcessId(hWnd, dwProcessID);
end;
procedure PatchIAT(strMod: PAnsichar; Alt, Neu: Pointer);
var
pImportDir: pImage_Import_Descriptor;
size: CardinaL;
Base: CardinaL;
pThunk: PDWORD;
begin
Base := GetModuleHandle(nil);
pImportDir := ImageDirectoryEntryToData(Pointer(Base), True,
IMAGE_DIRECTORY_ENTRY_IMPORT, size);
while pImportDir^.Name <> 0 Do
begin
If (lstrcmpiA(PAnsichar(pImportDir^.Name + Base), strMod) = 0) then
begin
pThunk := PDWORD(Base + pImportDir^.FirstThunk);
While pThunk^ <> 0 Do
begin
if DWord(Alt) = pThunk^ Then
begin
pThunk^ := CardinaL(Neu);
end;
Inc(pThunk);
end;
end;
Inc(pImportDir);
end;
end;
procedure DllMain(reason: Integer);
begin
case reason of
DLL_PROCESS_ATTACH:
begin
OldGetWindowThreadProcessId := GetProcAddress(GetModuleHandle(user32),
'GetWindowThreadProcessId');
PatchIAT(user32, GetProcAddress(GetModuleHandle(user32),
'GetWindowThreadProcessId'), #HookGetWindowThreadProcessId);
end;
DLL_PROCESS_DETACH:
begin
end;
end;
end;
begin
DllProc := #DllMain;
DllProc(DLL_PROCESS_ATTACH);
end.
Your PGetWindowThreadProcessId type and HookGetWindowThreadProcessId() function are both declaring the dwProcessID parameter incorrectly. It is an output parameter, so it needs to be declared as either var dwProcessID: DWord or as dwProcessID: PDWord.
And then you need to call OldGetWindowThreadProcessId() to retrieve the actual PID before you can then compare it to anything. So your requirement of "in positive case prevent execute original function" is not realistic, because you need to execute the original function in order to determine the dwProcessID value to compare with.
Try this instead:
type
PGetWindowThreadProcessId = function(hWnd: THandle; var dwProcessID: DWord): DWord; stdcall;
...
function HookGetWindowThreadProcessId(hWnd: THandle; var dwProcessID: DWord): DWord; stdcall;
begin
Result := OldGetWindowThreadProcessId(hWnd, dwProcessID);
try
if dwProcessID = ... then
...
except
MessageBox(0, 'Error', 'HookGetWindowThreadProcessId Error', 0);
end;
end;
How to ping an IP address (or by server name) in Delphi 10.1 without using Indy components? TIdICMPClient works with elevated privileges but I want to do it as a normal user.
The other answers had some things missing from them.
Here is a complete unit that does the trick:
unit Ping2;
interface
function PingHost(const HostName: AnsiString; TimeoutMS: cardinal = 500): boolean;
implementation
uses Windows, SysUtils, WinSock;
function IcmpCreateFile: THandle; stdcall; external 'iphlpapi.dll';
function IcmpCloseHandle(icmpHandle: THandle): boolean; stdcall;
external 'iphlpapi.dll';
function IcmpSendEcho(icmpHandle: THandle; DestinationAddress: In_Addr;
RequestData: Pointer; RequestSize: Smallint; RequestOptions: Pointer;
ReplyBuffer: Pointer; ReplySize: DWORD; Timeout: DWORD): DWORD; stdcall;
external 'iphlpapi.dll';
type
TEchoReply = packed record
Addr: In_Addr;
Status: DWORD;
RoundTripTime: DWORD;
end;
PEchoReply = ^TEchoReply;
var
WSAData: TWSAData;
procedure Startup;
begin
if WSAStartup($0101, WSAData) <> 0 then
raise Exception.Create('WSAStartup');
end;
procedure Cleanup;
begin
if WSACleanup <> 0 then
raise Exception.Create('WSACleanup');
end;
function PingHost(const HostName: AnsiString;
TimeoutMS: cardinal = 500): boolean;
const
rSize = $400;
var
e: PHostEnt;
a: PInAddr;
h: THandle;
d: string;
r: array [0 .. rSize - 1] of byte;
i: cardinal;
begin
Startup;
e := gethostbyname(PAnsiChar(HostName));
if e = nil then
RaiseLastOSError;
if e.h_addrtype = AF_INET then
Pointer(a) := e.h_addr^
else
raise Exception.Create('Name doesn''t resolve to an IPv4 address');
d := FormatDateTime('yyyymmddhhnnsszzz', Now);
h := IcmpCreateFile;
if h = INVALID_HANDLE_VALUE then
RaiseLastOSError;
try
i := IcmpSendEcho(h, a^, PChar(d), Length(d), nil, #r[0], rSize, TimeoutMS);
Result := (i <> 0) and (PEchoReply(#r[0]).Status = 0);
finally
IcmpCloseHandle(h);
end;
Cleanup;
end;
end.
You can call it with a click event like this:
procedure TForm1.button1Click(Sender: TObject);
begin
if PingHost('172.16.24.2') then
ShowMessage('WORKED')
else
ShowMessage('FAILED');
end;
Remember to add the "Ping2" unit in your uses list.
Use the Windows API.
Something like this crude translation from: https://msdn.microsoft.com/en-us/library/windows/desktop/aa366050(v=vs.85).aspx
Should do the trick.
var
ICMPFile: THandle;
IpAddress: ULONG;
SendData: array[0..31] of AnsiChar;
ReplyBuffer: PICMP_ECHO_REPLY;
ReplySize: DWORD;
NumResponses: DWORD;
begin
IpAddress:= inet_addr('127.0.0.1');
SendData := 'Data Buffer';
IcmpFile := IcmpCreateFile;
if IcmpFile <> INVALID_HANDLE_VALUE then
try
ReplySize:= SizeOf(ICMP_ECHO_REPLY) + SizeOf(SendData);
GetMem(ReplyBuffer, ReplySize);
try
NumResponses := IcmpSendEcho(IcmpFile, IPAddress, #SendData, SizeOf(SendData),
nil, ReplyBuffer, ReplySize, 1000);
if (NumResponses <> 0) then begin
Writeln(Format('Received %d icmp message responses', [NumResponses]));
Writeln('Information from the first response:');
Writeln(Format('Received from %s', [inet_ntoa(in_addr(ReplyBuffer.Address))]));
Writeln(Format('Data: %s', [PAnsiChar(ReplyBuffer.Data)]));
Writeln(Format('Status = %d', [ReplyBuffer.Status]));
WriteLn(Format('Roundtrip time = %d milliseconds',[ReplyBuffer.RoundTripTime]));
end else begin
WriteLn('Call to IcmpSendEcho failed');
WriteLn(Format('IcmpSendEcho returned error: %d', [GetLastError]));
end;
finally
FreeMem(ReplyBuffer);
end;
finally
IcmpCloseHandle(IcmpFile);
end
else begin
Writeln('Unable to open handle');
Writeln(Format('IcmpCreateFile returned error: %d', [GetLastError]));
end;
Here is a Delphi unit which does the ping with a timeout:
unit Ping2;
interface
function PingHost(const HostName:string;TimeoutMS:cardinal=500):boolean;
implementation
uses Windows, SysUtils, WinSock, Sockets;
function IcmpCreateFile:THandle; stdcall; external 'iphlpapi.dll';
function IcmpCloseHandle(icmpHandle:THandle):boolean; stdcall; external 'iphlpapi.dll'
function IcmpSendEcho(IcmpHandle:THandle;DestinationAddress:In_Addr;RequestData:Pointer;
RequestSize:Smallint;RequestOptions:pointer;ReplyBuffer:Pointer;ReplySize:DWORD;
Timeout:DWORD):DWORD; stdcall; external 'iphlpapi.dll';
type
TEchoReply=packed record
Addr:in_addr;
Status:DWORD;
RoundTripTime:DWORD;
//DataSize:
//Reserved:
//Data:pointer;
//Options:
end;
PEchoReply=^TEchoReply;
function PingHost(const HostName:string;TimeoutMS:cardinal=500):boolean;
const
rSize=$400;
var
e:PHostEnt;
a:PInAddr;
h:THandle;
d:string;
r:array[0..rSize-1] of byte;
i:cardinal;
begin
//assert WSAStartup called
e:=gethostbyname(PChar(HostName));
if e=nil then RaiseLastOSError;
if e.h_addrtype=AF_INET then pointer(a):=e.h_addr^ else raise Exception.Create('Name doesn''t resolve to an IPv4 address');
d:=FormatDateTime('yyyymmddhhnnsszzz',Now);
h:=IcmpCreateFile;
if h=INVALID_HANDLE_VALUE then RaiseLastOSError;
try
i:=IcmpSendEcho(h,a^,PChar(d),Length(d),nil,#r[0],rSize,TimeoutMS);
Result:=(i<>0) and (PEchoReply(#r[0]).Status=0);
finally
IcmpCloseHandle(h);
end;
end;
end.
I'm trying to write a function which tells me if a specific user has a specific rights on a folder. So far I have found an example on how to do this here so I tried to write this code in delphi.
unit SysCommonUnit;
interface
uses
SysUtils,
Classes,
System.Math,
Winapi.Windows,
WinTypes;
const
NERR_SUCCESS = 0;
MAX_NR_USERS = 1000;
FILTER_TEMP_DUPLICATE_ACCOUNT = $0001;
FILTER_NORMAL_ACCOUNT = $0002;
FILTER_PROXY_ACCOUNT = $0004;
FILTER_INTERDOMAIN_TRUST_ACCOUNT = $0008;
FILTER_WORKSTATION_TRUST_ACCOUNT = $0010;
FILTER_SERVER_TRUST_ACCOUNT = $0020;
AUTHZ_RM_FLAG_NO_AUDIT = $1;
{$EXTERNALSYM AUTHZ_RM_FLAG_NO_AUDIT}
FILE_READ_DATA = $0001; // file & pipe
FILE_LIST_DIRECTORY = $0001; // directory
FILE_WRITE_DATA = $0002; // file & pipe
FILE_ADD_FILE = $0002; // directory
FILE_APPEND_DATA = $0004; // file
FILE_ADD_SUBDIRECTORY = $0004; // directory
FILE_CREATE_PIPE_INSTANCE = $0004; // named pipe
FILE_READ_EA = $0008; // file & directory
FILE_WRITE_EA = $0010; // file & directory
FILE_EXECUTE = $0020; // file
FILE_TRAVERSE = $0020; // directory
FILE_DELETE_CHILD = $0040; // directory
FILE_READ_ATTRIBUTES = $0080; // all
FILE_WRITE_ATTRIBUTES = $0100; // all
FILE_ALL_ACCESS = STANDARD_RIGHTS_REQUIRED or
SYNCHRONIZE or
$1FF;
FILE_GENERIC_READ = STANDARD_RIGHTS_READ or
FILE_READ_DATA or
FILE_READ_ATTRIBUTES or
FILE_READ_EA or
SYNCHRONIZE;
FILE_GENERIC_WRITE = STANDARD_RIGHTS_WRITE or
FILE_WRITE_DATA or
FILE_WRITE_ATTRIBUTES or
FILE_WRITE_EA or
FILE_APPEND_DATA or
SYNCHRONIZE;
FILE_GENERIC_EXECUTE = STANDARD_RIGHTS_EXECUTE or
FILE_READ_ATTRIBUTES or
FILE_EXECUTE or
SYNCHRONIZE;
type
ACE_HEADER = record
AceType: BYTE;
AceFlags: BYTE;
AceSize: WORD;
end;
PPSECURITY_DESCRIPTOR = ^PSECURITY_DESCRIPTOR;
PACE_HEADER = ^ACE_HEADER;
PAUTHZ_ACCESS_REQUEST = ^AUTHZ_ACCESS_REQUEST;
POBJECT_TYPE_LIST = ^OBJECT_TYPE_LIST;
_OBJECT_TYPE_LIST = record
Level: WORD;
Sbz: WORD;
ObjectType: PGUID;
end;
OBJECT_TYPE_LIST = _OBJECT_TYPE_LIST;
TObjectTypeList = OBJECT_TYPE_LIST;
PObjectTypeList = POBJECT_TYPE_LIST;
_AUTHZ_ACCESS_REQUEST = record
DesiredAccess: ACCESS_MASK;
PrincipalSelfSid: PSID;
ObjectTypeList: POBJECT_TYPE_LIST;
ObjectTypeListLength: DWORD;
OptionalArguments: PVOID;
end;
AUTHZ_ACCESS_REQUEST = _AUTHZ_ACCESS_REQUEST;
TAuthzAccessRequest = AUTHZ_ACCESS_REQUEST;
PAuthzAccessRequest = PAUTHZ_ACCESS_REQUEST;
PAUTHZ_ACCESS_REPLY = ^AUTHZ_ACCESS_REPLY;
_AUTHZ_ACCESS_REPLY = record
ResultListLength: DWORD;
GrantedAccessMask: PACCESS_MASK;
SaclEvaluationResults: PDWORD;
Error: PDWORD;
end;
AUTHZ_ACCESS_REPLY = _AUTHZ_ACCESS_REPLY;
TAuthzAccessReply = AUTHZ_ACCESS_REPLY;
PAuthzAccessReply = PAUTHZ_ACCESS_REPLY;
TCHAR = char;
AUTHZ_RESOURCE_MANAGER_HANDLE = THANDLE;
AUTHZ_CLIENT_CONTEXT_HANDLE = THANDLE;
AUTHZ_AUDIT_EVENT_HANDLE = THANDLE;
PAUTHZ_RESOURCE_MANAGER_HANDLE = ^AUTHZ_RESOURCE_MANAGER_HANDLE;
PAUTHZ_CLIENT_CONTEXT_HANDLE = ^AUTHZ_CLIENT_CONTEXT_HANDLE;
PFN_AUTHZ_DYNAMIC_ACCESS_CHECK = function(hAuthzClientContext: AUTHZ_CLIENT_CONTEXT_HANDLE;
pAce: PACE_HEADER;
pArgs: PVOID;
var pbAceApplicable: BOOL): BOOL; stdcall;
PFnAuthzDynamicAccessCheck = PFN_AUTHZ_DYNAMIC_ACCESS_CHECK;
PFN_AUTHZ_COMPUTE_DYNAMIC_GROUPS = function(hAuthzClientContext: AUTHZ_CLIENT_CONTEXT_HANDLE;
Args: PVOID;
var pSidAttrArray: PSIDAndAttributes;
var pSidCount: DWORD;
var pRestrictedSidAttrArray: PSIDAndAttributes;
var pRestrictedSidCount: DWORD): BOOL; stdcall;
PFnAuthzComputeDynamicGroups = PFN_AUTHZ_COMPUTE_DYNAMIC_GROUPS;
PFN_AUTHZ_FREE_DYNAMIC_GROUPS = procedure(pSidAttrArray: PSIDAndAttributes); stdcall;
PFnAuthzFreeDynamicGroups = PFN_AUTHZ_FREE_DYNAMIC_GROUPS;
AUTHZ_ACCESS_CHECK_RESULTS_HANDLE = THANDLE;
PAUTHZ_ACCESS_CHECK_RESULTS_HANDLE = ^AUTHZ_ACCESS_CHECK_RESULTS_HANDLE;
SE_OBJECT_TYPE = (SE_UNKNOWN_OBJECT_TYPE,
SE_FILE_OBJECT,
SE_SERVICE,
SE_PRINTER,
SE_REGISTRY_KEY,
SE_LMSHARE,
SE_KERNEL_OBJECT,
SE_WINDOW_OBJECT,
SE_DS_OBJECT,
SE_DS_OBJECT_ALL,
SE_PROVIDER_DEFINED_OBJECT,
SE_WMIGUID_OBJECT);
function GetNamedSecurityInfoW( pObjectName: PWideChar;
ObjectType: SE_OBJECT_TYPE;
SecurityInfo: SECURITY_INFORMATION;
var ppSidOwner: PSID;
var ppSidGroup: PSID;
var ppDacl: PACL;
var ppSacl: PACL;
var ppSecurityDescriptor: PSECURITY_DESCRIPTOR): DWORD; stdcall; external 'Advapi32.dll';
function AuthzInitializeResourceManagerWrapper( nFlags: DWORD;
pfnDynamicAccessCheck: PFN_AUTHZ_DYNAMIC_ACCESS_CHECK;
pfnComputeDynamicGroups: PFN_AUTHZ_COMPUTE_DYNAMIC_GROUPS;
pfnFreeDynamicGroups: PFN_AUTHZ_FREE_DYNAMIC_GROUPS;
szResourceManagerName: string;
var hAuthzResourceManager: AUTHZ_RESOURCE_MANAGER_HANDLE): Boolean;
function AuthzInitializeContextFromSidWrapper(Flags: DWORD;
UserSid: PSID;
hAuthzResourceManager: AUTHZ_RESOURCE_MANAGER_HANDLE;
pExpirationTime: PLargeInteger;
Identifier: LUID;
DynamicGroupArgs: PVOID;
var hAuthzClientContext: AUTHZ_CLIENT_CONTEXT_HANDLE): Boolean;
function AuthzFreeResourceManagerWrapper(hAuthzResourceManager: AUTHZ_RESOURCE_MANAGER_HANDLE): Boolean;
function AuthzFreeContextWrapper(hAuthzClientContext: AUTHZ_CLIENT_CONTEXT_HANDLE): Boolean;
function AuthzAccessCheckWrapper( Flags: DWORD;
hAuthzClientContext: AUTHZ_CLIENT_CONTEXT_HANDLE;
var pRequest: AUTHZ_ACCESS_REQUEST;
hAuditEvent: AUTHZ_AUDIT_EVENT_HANDLE;
var pSecurityDescriptor: SECURITY_DESCRIPTOR;
var OptionalSecurityDescriptorArray: PSECURITY_DESCRIPTOR;
OptionalSecurityDescriptorCount: DWORD;
var pReply: AUTHZ_ACCESS_REPLY;
var phAccessCheckResultsOPTIONAL: AUTHZ_ACCESS_CHECK_RESULTS_HANDLE): Boolean;
function ConvertUsernameToBinarySID(p_pAccountName: string): PSID;
function HasRightsForUser(p_hManager: AUTHZ_RESOURCE_MANAGER_HANDLE;
p_oPsd: PSECURITY_DESCRIPTOR;
p_sUsername: string;
p_nDesiredRights: DWORD): Boolean;
function HasAccess(p_hAuthzClient: AUTHZ_CLIENT_CONTEXT_HANDLE; p_oPsd: PSECURITY_DESCRIPTOR; p_nDesiredRights: DWORD): Boolean;
function HasAccessRights(p_nDesiredRights: Integer; p_sFileName: string; p_sUsername: string): Boolean;
implementation
function AuthzInitializeResourceManagerWrapper( nFlags: DWORD;
pfnDynamicAccessCheck: PFN_AUTHZ_DYNAMIC_ACCESS_CHECK;
pfnComputeDynamicGroups: PFN_AUTHZ_COMPUTE_DYNAMIC_GROUPS;
pfnFreeDynamicGroups: PFN_AUTHZ_FREE_DYNAMIC_GROUPS;
szResourceManagerName: string;
var hAuthzResourceManager: AUTHZ_RESOURCE_MANAGER_HANDLE): Boolean;
var
DLLHandle : THandle;
wResourceManagerName : array[0..1024] of Widechar;
AuthzInitializeResourceManager : function (nFlags: DWORD;
pfnDynamicAccessCheck: PFN_AUTHZ_DYNAMIC_ACCESS_CHECK;
pfnComputeDynamicGroups: PFN_AUTHZ_COMPUTE_DYNAMIC_GROUPS;
pfnFreeDynamicGroups: PFN_AUTHZ_FREE_DYNAMIC_GROUPS;
szResourceManagerName: PWideChar;
phAuthzResourceManager: PAUTHZ_RESOURCE_MANAGER_HANDLE): BOOL; cdecl stdcall;
begin
Result := False;
DLLHandle := LoadLibrary('authz.dll');
if DLLHandle >= 32 then
begin
#AuthzInitializeResourceManager := GetProcAddress(DLLHandle, 'AuthzInitializeResourceManager');
StringToWideChar(szResourceManagerName, wResourceManagerName, sizeof(wResourceManagerName));
Result := AuthzInitializeResourceManager( nFlags,
pfnDynamicAccessCheck,
pfnComputeDynamicGroups,
pfnFreeDynamicGroups,
wResourceManagerName,
#hAuthzResourceManager);
FreeLibrary(DLLHandle);
end;
end;
function AuthzInitializeContextFromSidWrapper(Flags: DWORD;
UserSid: PSID;
hAuthzResourceManager: AUTHZ_RESOURCE_MANAGER_HANDLE;
pExpirationTime: PLargeInteger;
Identifier: LUID;
DynamicGroupArgs: PVOID;
var hAuthzClientContext: AUTHZ_CLIENT_CONTEXT_HANDLE): Boolean;
var
DLLHandle : THandle;
AuthzInitializeContextFromSid : function (Flags: DWORD;
UserSid: PSID;
hAuthzResourceManager: AUTHZ_RESOURCE_MANAGER_HANDLE;
pExpirationTime: PLargeInteger;
Identifier: LUID;
DynamicGroupArgs: PVOID;
hAuthzClientContext: PAUTHZ_CLIENT_CONTEXT_HANDLE): BOOL; cdecl stdcall;
begin
Result := False;
DLLHandle := LoadLibrary('authz.dll');
if DLLHandle >= 32 then
begin
#AuthzInitializeContextFromSid := GetProcAddress(DLLHandle, 'AuthzInitializeContextFromSid');
Result := AuthzInitializeContextFromSid(Flags,
UserSid,
hAuthzResourceManager,
pExpirationTime,
Identifier,
DynamicGroupArgs,
#hAuthzClientContext);
FreeLibrary(DLLHandle);
end;
end;
function AuthzFreeResourceManagerWrapper(hAuthzResourceManager: AUTHZ_RESOURCE_MANAGER_HANDLE): Boolean;
var
DLLHandle : THandle;
AuthzFreeResourceManager : function(hAuthzResourceManager: AUTHZ_RESOURCE_MANAGER_HANDLE): BOOL; cdecl stdcall;
begin
Result := False;
DLLHandle := LoadLibrary('authz.dll');
if DLLHandle >= 32 then
begin
#AuthzFreeResourceManager := GetProcAddress(DLLHandle, 'AuthzFreeResourceManager');
Result := AuthzFreeResourceManager(hAuthzResourceManager);
FreeLibrary(DLLHandle);
end;
end;
function AuthzFreeContextWrapper(hAuthzClientContext: AUTHZ_CLIENT_CONTEXT_HANDLE): Boolean;
var
DLLHandle : THandle;
AuthzFreeContext : function(hAuthzClientContext: AUTHZ_CLIENT_CONTEXT_HANDLE): BOOL; cdecl stdcall;
begin
Result := False;
DLLHandle := LoadLibrary('authz.dll');
if DLLHandle >= 32 then
begin
#AuthzFreeContext := GetProcAddress(DLLHandle, 'AuthzFreeResourceManager');
Result := AuthzFreeContext(hAuthzClientContext);
FreeLibrary(DLLHandle);
end;
end;
function AuthzAccessCheckWrapper( Flags: DWORD;
hAuthzClientContext: AUTHZ_CLIENT_CONTEXT_HANDLE;
var pRequest: AUTHZ_ACCESS_REQUEST;
hAuditEvent: AUTHZ_AUDIT_EVENT_HANDLE;
var pSecurityDescriptor: SECURITY_DESCRIPTOR;
var OptionalSecurityDescriptorArray: PSECURITY_DESCRIPTOR;
OptionalSecurityDescriptorCount: DWORD;
var pReply: AUTHZ_ACCESS_REPLY;
var phAccessCheckResultsOPTIONAL: AUTHZ_ACCESS_CHECK_RESULTS_HANDLE): Boolean;
var
nError : Integer;
DLLHandle : THandle;
AuthzAccessCheck : function( Flags: DWORD;
hAuthzClientContext: AUTHZ_CLIENT_CONTEXT_HANDLE;
pRequest: PAUTHZ_ACCESS_REQUEST;
hAuditEvent: AUTHZ_AUDIT_EVENT_HANDLE;
pSecurityDescriptor: PSECURITY_DESCRIPTOR ;
OptionalSecurityDescriptorArray: PPSECURITY_DESCRIPTOR;
OptionalSecurityDescriptorCount: DWORD;
pReply: PAUTHZ_ACCESS_REPLY;
phAccessCheckResultsOPTIONAL: PAUTHZ_ACCESS_CHECK_RESULTS_HANDLE): BOOL; cdecl stdcall;
begin
Result := False;
DLLHandle := LoadLibrary('authz.dll');
if DLLHandle >= 32 then
begin
#AuthzAccessCheck := GetProcAddress(DLLHandle, 'AuthzAccessCheck');
Result := AuthzAccessCheck(Flags,
hAuthzClientContext,
#pRequest,
hAuditEvent,
#pSecurityDescriptor,
#OptionalSecurityDescriptorArray,
OptionalSecurityDescriptorCount,
#pReply,
#phAccessCheckResultsOPTIONAL);
if not Result then
nError := GetLastError;
FreeLibrary(DLLHandle);
end;
end;
function HasAccessRights(p_nDesiredRights: Integer; p_sFileName: string; p_sUsername: string): Boolean;
var
nDW : DWORD;
pSidOwner: PSID;
pSidGroup: PSID;
pPsd : PSECURITY_DESCRIPTOR;
oDAcl : PACL;
oSAcl : PACL;
hManager : AUTHZ_RESOURCE_MANAGER_HANDLE;
bRes : Boolean;
begin
oSAcl := nil;
oDAcl := nil;
pSidOwner := nil;
pSidGroup := nil;
pPsd := nil;
hManager := 0;
Result := False;
try
nDW := GetNamedSecurityInfoW( PWideChar(p_sFileName),
SE_FILE_OBJECT,
DACL_SECURITY_INFORMATION or OWNER_SECURITY_INFORMATION or GROUP_SECURITY_INFORMATION,
pSidOwner,
pSidGroup,
oDAcl,
oSAcl,
pPsd);
if nDW <> ERROR_SUCCESS then
Exit;
bRes := AuthzInitializeResourceManagerWrapper(AUTHZ_RM_FLAG_NO_AUDIT, nil, nil, nil, PWideChar(EmptyStr), hManager);
if not bRes then
Exit;
bRes := HasRightsForUser(hManager, pPsd, p_sUsername, p_nDesiredRights);
if not bRes then
Exit;
Result := True;
finally
AuthzFreeResourceManagerWrapper(hManager);
if Assigned(pPsd) then
LocalFree(HLOCAL(pPsd));
end;
end;
function HasRightsForUser(p_hManager: AUTHZ_RESOURCE_MANAGER_HANDLE;
p_oPsd: PSECURITY_DESCRIPTOR;
p_sUsername: string;
p_nDesiredRights: DWORD): Boolean;
var
hAuthzClientContext: AUTHZ_CLIENT_CONTEXT_HANDLE;
bResult : Boolean;
n_UnusedID : LUID;
oSid : PSID;
begin
hAuthzClientContext := 0;
Result := false;
n_UnusedID.LowPart := 0;
n_UnusedID.HighPart := 0;
oSid := ConvertUsernameToBinarySID(p_sUsername);
if Assigned(oSid) then
begin
try
bResult := AuthzInitializeContextFromSidWrapper(0, oSid, p_hManager, nil, n_UnusedID, nil, hAuthzClientContext);
if not bResult then
Exit;
bResult := HasAccess(hAuthzClientContext, p_oPsd, p_nDesiredRights);
if bResult then
Result := True;
finally
if Assigned(oSid) then
LocalFree(HLOCAL(oSid));
AuthzFreeContextWrapper(hAuthzClientContext);
end;
end;
end;
function ConvertUsernameToBinarySID(p_pAccountName: string): PSID;
var
psDomainName : LPTSTR;
nDomainNameSize: DWORD;
oSid : PSID;
nSidSize : DWORD;
eSidType : SID_NAME_USE;
bResult : Boolean;
begin
Result := nil;
psDomainName := nil;
nDomainNameSize := 0;
oSid := nil;
bResult := false;
try
LookupAccountName(nil, // lpServerName: look up on local system
PWideChar(p_pAccountName), oSid, // buffer to receive name
nSidSize, psDomainName, nDomainNameSize, eSidType);
if GetLastError = ERROR_INSUFFICIENT_BUFFER then
begin
oSid := LPTSTR(LocalAlloc(LPTR, nSidSize * SizeOf(TCHAR)));
if not Assigned(oSid) then
Exit;
psDomainName := LPTSTR(LocalAlloc(LPTR, nDomainNameSize * SizeOf(TCHAR)));
if not Assigned(psDomainName) then
Exit;
bResult := LookupAccountName( nil, // lpServerName: look up on local system
PWideChar(p_pAccountName),
oSid, // buffer to receive name
nSidSize,
psDomainName,
nDomainNameSize,
eSidType);
if bResult then
Result := oSid;
end
else
Exit;
finally
if Assigned(psDomainName) then
begin
LocalFree(HLOCAL(psDomainName));
end;
// Free pSid only if failed;
// otherwise, the caller has to free it after use.
if (bResult = false) and Assigned(oSid) then
begin
LocalFree(HLOCAL(oSid));
end;
end;
end;
function HasAccess(p_hAuthzClient: AUTHZ_CLIENT_CONTEXT_HANDLE; p_oPsd: PSECURITY_DESCRIPTOR; p_nDesiredRights: DWORD): Boolean;
var
oDescArray : Pointer;
oCheckResults : AUTHZ_ACCESS_CHECK_RESULTS_HANDLE;
oAccessRequest: AUTHZ_ACCESS_REQUEST;
oAccessReply : AUTHZ_ACCESS_REPLY;
a_nBuffer : array [0 .. 1024] of BYTE;
bResult : Boolean;
oPsd : SECURITY_DESCRIPTOR;
begin
Result := False;
// Do AccessCheck.
oAccessRequest.DesiredAccess := FILE_TRAVERSE;
oAccessRequest.PrincipalSelfSid := nil;
oAccessRequest.ObjectTypeList := nil;
oAccessRequest.OptionalArguments := nil;
oAccessRequest.ObjectTypeListLength := 0;
ZeroMemory(#a_nBuffer, sizeof(a_nBuffer));
oAccessReply.ResultListLength := 1;
oAccessReply.GrantedAccessMask := PACCESS_MASK(#a_nBuffer);
oAccessReply.Error := PDWORD(Cardinal(#a_nBuffer) + sizeof(ACCESS_MASK));
oPsd := SECURITY_DESCRIPTOR(p_oPsd^);
bResult := AuthzAccessCheckWrapper( 0,
p_hAuthzClient,
oAccessRequest,
0,
oPsd,
oDescArray,
0,
oAccessReply,
oCheckResults);
if bResult then
Result := True;
end;
end.
My problem is on line 348 in AuthzAccessCheckWrapper
Result := AuthzAccessCheck(Flags,
hAuthzClientContext,
#pRequest,
hAuditEvent,
#pSecurityDescriptor,
#OptionalSecurityDescriptorArray,
OptionalSecurityDescriptorCount,
#pReply,
#phAccessCheckResultsOPTIONAL);
if not Result then
nError := GetLastError;
Where I get the error 87 (ERROR_INVALID_PARAMETER)
I'm quite new to Delphi and this may be a beginner's error but I don't have any idea how to solve this so any help or suggestion will be greatly appreciated.
If you want only write a simple function to retrieve the users permissions over a folder or file, you can try the WMI, in this case to get the security settings for a logical file or directory you can use the Win32_LogicalFileSecuritySetting WMI Class with the GetSecurityDescriptor method.
Check this sample code. This will check if a particular user had access in a folder (or file).
{$APPTYPE CONSOLE}
uses
SysUtils,
ActiveX,
ComObj,
Windows,
Variants;
procedure GetDirectoryAccess(const Path, UserName : string);
const
WbemUser ='';
WbemPassword ='';
WbemComputer ='localhost';
wbemFlagForwardOnly = $00000020;
var
FSWbemLocator : OLEVariant;
FWMIService : OLEVariant;
FWbemObjectSet: OLEVariant;
FWbemObject : OLEVariant;
objSD : OleVariant;
LIndex : Integer;
LAccessMask : DWORD;
objAce : OleVariant;
begin;
FSWbemLocator := CreateOleObject('WbemScripting.SWbemLocator');
FWMIService := FSWbemLocator.ConnectServer(WbemComputer, 'root\cimv2', WbemUser, WbemPassword);
FWbemObjectSet:= FWMIService.Get(Format('Win32_LogicalFileSecuritySetting="%s"', [StringReplace(Path,'\','\\', [rfReplaceAll])]));
if FWbemObjectSet.GetSecurityDescriptor(objSD)=0 then
for LIndex:= VarArrayLowBound(objSD.DACL,1) to VarArrayHighBound(objSD.DACL,1) do
if SameText(UserName, objSD.DACL[LIndex].Trustee.Name) then
begin
objAce:=objSD.DACL[LIndex];
Writeln(Format('Trustee Name %s',[objAce.Trustee.Name]));
Writeln(Format('Trustee Domain %s',[objAce.Trustee.Domain]));
Writeln(Format('Ace Flags %d',[Integer(objAce.AceFlags)]));
Writeln(Format('Access Mask %d',[Integer(objAce.AccessMask)]));
LAccessMask:=objAce.AccessMask;
if (LAccessMask and 1048576)=1048576 then
Writeln(' Synchronize');
if (LAccessMask and 524288 )=524288 then
Writeln(' Write Owner');
if (LAccessMask and 262144)=262144 Then
Writeln(' Write ACL');
if (LAccessMask and 131072)=131072 Then
Writeln(' Read Security');
if (LAccessMask and 65536)=65536 Then
Writeln(' Delete');
if (LAccessMask and 256)=256 Then
Writeln(' Write Attributes');
if (LAccessMask and 128)=128 Then
Writeln(' Read Attributes');
if (LAccessMask and 64)=64 Then
Writeln(' Delete Dir');
if (LAccessMask and 32)=32 Then
Writeln(' Execute');
if (LAccessMask and 16)=16 Then
Writeln(' Write extended attributes');
if (LAccessMask and 8)=8 Then
Writeln(' Read extended attributes');
if (LAccessMask and 4)=4 Then
Writeln(' Append');
if (LAccessMask and 2)=2 Then
Writeln(' Write');
if (LAccessMask and 1)=1 Then
Writeln(' Read');
end;
end;
begin
try
CoInitialize(nil);
try
GetDirectoryAccess('c:\lazarus','RRUZ');;
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.
Note: Is nothing wrong with use the WinAPI too, but this sample shows how easy can be resolved this task using the WMI.
I would like to perform a thorough LAN devices discovery, so that I can create a diagram similar to the one attached, but with additional information like IP and MAC addresses.
I've tried the code from Torry:
type
PNetResourceArray = ^TNetResourceArray;
TNetResourceArray = array[0..100] of TNetResource;
function CreateNetResourceList(ResourceType: DWord;
NetResource: PNetResource;
out Entries: DWord;
out List: PNetResourceArray): Boolean;
var
EnumHandle: THandle;
BufSize: DWord;
Res: DWord;
begin
Result := False;
List := Nil;
Entries := 0;
if WNetOpenEnum(RESOURCE_GLOBALNET,
ResourceType,
0,
NetResource,
EnumHandle) = NO_ERROR then begin
try
BufSize := $4000; // 16 kByte
GetMem(List, BufSize);
try
repeat
Entries := DWord(-1);
FillChar(List^, BufSize, 0);
Res := WNetEnumResource(EnumHandle, Entries, List, BufSize);
if Res = ERROR_MORE_DATA then
begin
ReAllocMem(List, BufSize);
end;
until Res <> ERROR_MORE_DATA;
Result := Res = NO_ERROR;
if not Result then
begin
FreeMem(List);
List := Nil;
Entries := 0;
end;
except
FreeMem(List);
raise;
end;
finally
WNetCloseEnum(EnumHandle);
end;
end;
end;
procedure ScanNetworkResources(ResourceType, DisplayType: DWord; List: TStrings);
procedure ScanLevel(NetResource: PNetResource);
var
Entries: DWord;
NetResourceList: PNetResourceArray;
i: Integer;
begin
if CreateNetResourceList(ResourceType, NetResource, Entries, NetResourceList) then try
for i := 0 to Integer(Entries) - 1 do
begin
if (DisplayType = RESOURCEDISPLAYTYPE_GENERIC) or
(NetResourceList[i].dwDisplayType = DisplayType) then begin
List.AddObject(NetResourceList[i].lpRemoteName,
Pointer(NetResourceList[i].dwDisplayType));
end;
if (NetResourceList[i].dwUsage and RESOURCEUSAGE_CONTAINER) <> 0 then
ScanLevel(#NetResourceList[i]);
end;
finally
FreeMem(NetResourceList);
end;
end;
begin
ScanLevel(Nil);
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
ScanNetworkResources(RESOURCETYPE_DISK, RESOURCEDISPLAYTYPE_SERVER, ListBox1.Items);
end;
But it only returns the names of the computers in the network, without the router and their IP address. So that's not really a solution.
Could you please tell me what is a good way to enumerate all devices (routers, computers, printers) in a local network, along with their IP and MAC addresses?
Thank you.
I modified you code adding the function GetHostName and inet_ntoa to get the ip address and the SendARP function to get the MAC address of a network resource.
{$APPTYPE CONSOLE}
{$R *.res}
uses
StrUtils,
Windows,
WinSock,
SysUtils;
type
PNetResourceArray = ^TNetResourceArray;
TNetResourceArray = array[0..1023] of TNetResource;
function SendArp(DestIP,SrcIP:ULONG;pMacAddr:pointer;PhyAddrLen:pointer) : DWord; StdCall; external 'iphlpapi.dll' name 'SendARP';
function GetIPAddress(const HostName: AnsiString): AnsiString;
var
HostEnt: PHostEnt;
Host: AnsiString;
SockAddr: TSockAddrIn;
begin
Result := '';
Host := HostName;
if Host = '' then
begin
SetLength(Host, MAX_PATH);
GetHostName(PAnsiChar(Host), MAX_PATH);
end;
HostEnt := GetHostByName(PAnsiChar(Host));
if HostEnt <> nil then
begin
SockAddr.sin_addr.S_addr := Longint(PLongint(HostEnt^.h_addr_list^)^);
Result := inet_ntoa(SockAddr.sin_addr);
end;
end;
function GetMacAddr(const IPAddress: AnsiString; var ErrCode : DWORD): AnsiString;
var
MacAddr : Array[0..5] of Byte;
DestIP : ULONG;
PhyAddrLen : ULONG;
begin
Result :='';
ZeroMemory(#MacAddr,SizeOf(MacAddr));
DestIP :=inet_addr(PAnsiChar(IPAddress));
PhyAddrLen:=SizeOf(MacAddr);
ErrCode :=SendArp(DestIP,0,#MacAddr,#PhyAddrLen);
if ErrCode = S_OK then
Result:=AnsiString(Format('%2.2x-%2.2x-%2.2x-%2.2x-%2.2x-%2.2x',[MacAddr[0], MacAddr[1],MacAddr[2], MacAddr[3], MacAddr[4], MacAddr[5]]));
end;
function ParseRemoteName(Const lpRemoteName : string) : string;
begin
Result:=lpRemoteName;
if StartsStr('\\', lpRemoteName) and (Length(lpRemoteName)>2) and (LastDelimiter('\', lpRemoteName)>2) then
Result:=Copy(lpRemoteName, 3, PosEx('\', lpRemoteName,3)-3)
else
if StartsStr('\\', lpRemoteName) and (Length(lpRemoteName)>2) and (LastDelimiter('\', lpRemoteName)=2) then
Result:=Copy(lpRemoteName, 3, length(lpRemoteName));
end;
function CreateNetResourceList(ResourceType: DWord;
NetResource: PNetResource;
out Entries: DWord;
out List: PNetResourceArray): Boolean;
var
EnumHandle: THandle;
BufSize: DWord;
Res: DWord;
begin
Result := False;
List := Nil;
Entries := 0;
if WNetOpenEnum(RESOURCE_GLOBALNET, ResourceType, 0, NetResource, EnumHandle) = NO_ERROR then
begin
try
BufSize := $4000; // 16 kByte
GetMem(List, BufSize);
try
repeat
Entries := DWord(-1);
FillChar(List^, BufSize, 0);
Res := WNetEnumResource(EnumHandle, Entries, List, BufSize);
if Res = ERROR_MORE_DATA then
begin
ReAllocMem(List, BufSize);
end;
until Res <> ERROR_MORE_DATA;
Result := Res = NO_ERROR;
if not Result then
begin
FreeMem(List);
List := Nil;
Entries := 0;
end;
except
FreeMem(List);
raise;
end;
finally
WNetCloseEnum(EnumHandle);
end;
end;
end;
procedure ScanNetworkResources(ResourceType, DisplayType: DWord);
procedure ScanLevel(NetResource: PNetResource);
var
Entries: DWord;
NetResourceList: PNetResourceArray;
i: Integer;
IPAddress, MacAddress : AnsiString;
ErrCode : DWORD;
begin
if CreateNetResourceList(ResourceType, NetResource, Entries, NetResourceList) then try
for i := 0 to Integer(Entries) - 1 do
begin
if (DisplayType = RESOURCEDISPLAYTYPE_GENERIC) or
(NetResourceList[i].dwDisplayType = DisplayType) then
begin
IPAddress :=GetIPAddress(ParseRemoteName(AnsiString(NetResourceList[i].lpRemoteName)));
MacAddress :=GetMacAddr(IPAddress, ErrCode);
Writeln(Format('Remote Name %s Ip %s MAC %s',[NetResourceList[i].lpRemoteName, IPAddress, MacAddress]));
end;
if (NetResourceList[i].dwUsage and RESOURCEUSAGE_CONTAINER) <> 0 then
ScanLevel(#NetResourceList[i]);
end;
finally
FreeMem(NetResourceList);
end;
end;
begin
ScanLevel(Nil);
end;
var
WSAData: TWSAData;
begin
try
if WSAStartup($0101, WSAData)=0 then
try
ScanNetworkResources(RESOURCETYPE_ANY, RESOURCEDISPLAYTYPE_SERVER);
Writeln('Done');
finally
WSACleanup;
end;
except
on E:Exception do
Writeln(E.Classname, ': ', E.Message);
end;
Readln;
end.