For a Security product I am currently writing in Delphi. I am using the below solution to use the ARP table to get MAC-Addresses from devices to detected what is on the network.
How to find MAC addresses from arp -a scan
I just do a range of Ping commands to fill the ARP table and read results from the ARP-table.
However when a Firewall on a machine is blocking Ping. Sometimes the MAC is still exposed in ARP, but not always. What is a better solution to detect all devices on a network and get MAC-Addresses from them?
//-----------------------------------------------------------------------------
{ ARP-table lists relations between remote IP and remote MAC-address.
NOTE: these are cached entries;when there is no more network traffic to a
node, entry is deleted after a few minutes.
}
//-----------------------------------------------------------------------------
procedure Get_ARPTable( aList: TStrings; aDeviceList : TObjectList<TNetworkDevice>);
var
lIPNetRow : TMibIPNetRow;
lTableSize : DWORD;
lNumEntries : DWORD;
lErrorCode : DWORD;
lIdx : Integer;
lPBuf : PAnsiChar;
lPhysAddr : TMACAddress;
lMacAddr2Str : string;
ldwAddr : string;
ldwType : string;
lNewDevice : TNetworkDevice;
begin
if not LoadIpHlp then Exit;
if not Assigned( aList ) then Exit;
if not Assigned( aDeviceList) then Exit;
Get_LocalNetworkDevices(aDeviceList);
aList.Clear;
lTableSize := 0;
lErrorCode := GetIPNetTable( Nil, @lTableSize, False );
//
if lErrorCode = ERROR_NO_DATA then
begin
aList.Add( ' ARP-cache empty.' );
EXIT;
end;
// get table
GetMem( lPBuf, lTableSize );
lNumEntries := 0;
try
lErrorCode := GetIpNetTable( PTMIBIPNetTable( lPBuf ), @lTableSize, False );
if lErrorCode = NO_ERROR then
begin
lNumEntries := PTMIBIPNetTable( lPBuf )^.dwNumEntries;
if lNumEntries > 0 then
begin
Inc( lPBuf, SizeOf( DWORD ) );
for lIdx := 1 to lNumEntries do
begin
lIPNetRow := PTMIBIPNetRow( lPBuf )^;
lMacAddr2Str := MacAddr2Str( lIPNetRow.bPhysAddr, lIPNetRow.dwPhysAddrLen );
lPhysAddr := lIPNetRow.bPhysAddr;
ldwAddr := IPAddr2StrTrunc(lIPNetRow.dwAddr);
ldwType := ARPEntryType[lIPNetRow.dwType];
lNewDevice := SeekDevice(aDeviceList, lMacAddr2Str);
if Assigned(lNewDevice) then
begin
lNewDevice.IP := ldwAddr;
lNewDevice.IsNew := False;
lNewDevice.EntryType := ARPEntryType[lIPNetRow.dwType];
if (lNewDevice.EntryType = 'Dynamic') or
(lNewDevice.EntryType = 'Static') then
lNewDevice.SetStamp;
end
else
begin
lNewDevice := TNetworkDevice.Create;
lNewDevice.IP := ldwAddr;
lNewDevice.EntryType := ARPEntryType[lIPNetRow.dwType];
lNewDevice.AddOrUpdate(lMacAddr2Str);
lNewDevice.SetFirstSeen;
lNewDevice.SetStamp;
lNewDevice.State := dtRogue;
lNewDevice.IsNew := True;
aDeviceList.Add(lNewDevice);
end;
with lIPNetRow do
begin
aList.Add( Format( '%8x | %12s | %16s| %10s',
[dwIndex, MacAddr2Str( bPhysAddr, dwPhysAddrLen ),
IPAddr2Str( dwAddr ), ARPEntryType[dwType]
]));
end;
Inc( lPBuf, SizeOf( lIPNetRow ) );
end;
end
else
aList.Add( ' ARP-cache empty.' );
end
else
aList.Add( SysErrorMessage( lErrorCode ) );
// we _must_ restore Pointer!
finally
Dec( lPBuf, SizeOf( DWORD ) + lNumEntries * SizeOf( lIPNetRow ) );
FreeMem( lPBuf );
end;
end;