0

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;

1 Answers1

0

To complement ping, you can try to establish a TCP connection on any port, for example port 80 (HTTP). Of course you'll get mostly connection denied but you'll still be able to find an entry in the ARP table.

Depending on the firewall, you may also simply get to response at all. Then you have to try another port. I would suggest 135 (RPC) or 445 or 139 (Both SMB) which should be open is network shares are enabled. Another one in 3389 used by RDP if enabled.

This is practical only if the subnet is small (Class-C, 256 addresses) or medium (Class-B, 16K addresses). Anyway, it would take a lot of time.

fpiette
  • 11,983
  • 1
  • 24
  • 46