Table of Contents

Finding the MAC address

by Richard Russell, May 2007

This article lists no fewer than five different methods of discovering the MAC address (Media Access Control address or physical address) of one or more network adaptors in the host system. The MAC address can be useful, amongst other things, as a unique identifier of the host PC. Each method has its own strengths and weaknesses.

Method 1


This method is the simplest, and works on all versions of Windows from Windows 95 to Windows Vista or later. However if two or more network adaptors are fitted it returns only the MAC address of the primary adaptor. Also, it relies on a feature of Windows which isn't guaranteed to be reliable, so should be used at your own risk!

        SYS "LoadLibrary", "RPCRT4.DLL" TO rpcrt4%
        SYS "GetProcAddress", rpcrt4%, "UuidCreate" TO UuidCreate%
        SYS "GetProcAddress", rpcrt4%, "UuidCreateSequential" TO UuidCreateSequential%
        IF UuidCreateSequential% THEN UuidCreate% = UuidCreateSequential%
 
        DIM uuid{skip&(9), macaddress&(5)}
 
        SYS UuidCreate%, uuid{}
 
        MACaddress$ = RIGHT$("0"+STR$~uuid.macaddress&(0), 2) + "-" + \
        \             RIGHT$("0"+STR$~uuid.macaddress&(1), 2) + "-" + \
        \             RIGHT$("0"+STR$~uuid.macaddress&(2), 2) + "-" + \
        \             RIGHT$("0"+STR$~uuid.macaddress&(3), 2) + "-" + \
        \             RIGHT$("0"+STR$~uuid.macaddress&(4), 2) + "-" + \
        \             RIGHT$("0"+STR$~uuid.macaddress&(5), 2)
 
        PRINT "MAC address = " MACaddress$


Method 2


This method is almost as simple as the preceding one, but works only under Windows 2000 Professional, Windows XP or Windows Vista or later. It also returns the MAC address of the primary adaptor, but uses documented functions which should be trustworthy:

        INSTALL @lib$+"SOCKLIB"
        PROC_initsockets
 
        SYS "LoadLibrary", "IPHLPAPI.DLL" TO iphlpapi%
        SYS "GetProcAddress", iphlpapi%, "SendARP" TO `SendARP`
 
        hostent% = FN_sethost(FN_gethostname)
        hostip% = !!hostent%!12
 
        DIM macaddress&(7)
        size% = 8
 
        SYS `SendARP`, hostip%, 0, ^macaddress&(0), ^size%
 
        MACaddress$ = RIGHT$("0"+STR$~macaddress&(0), 2) + "-" + \
        \             RIGHT$("0"+STR$~macaddress&(1), 2) + "-" + \
        \             RIGHT$("0"+STR$~macaddress&(2), 2) + "-" + \
        \             RIGHT$("0"+STR$~macaddress&(3), 2) + "-" + \
        \             RIGHT$("0"+STR$~macaddress&(4), 2) + "-" + \
        \             RIGHT$("0"+STR$~macaddress&(5), 2)
 
        PRINT "MAC address = " MACaddress$


Method 3


This method works on versions of Windows from Windows 95 to Windows XP, but only if NetBIOS is installed. It is also documented as being “unreliable” on Windows 95, 98 and Me. Therefore it is generally not a good choice:

        NCBNAMSZ = 16
        NCBRESET = &32
        NCBASTAT = &33
 
        SYS "LoadLibrary", "NETAPI32.DLL" TO netapi32%
        SYS "GetProcAddress", netapi32%, "Netbios" TO `Netbios`
 
        DIM ncb{command&, retcode&, lsn&, num&, buffer%, length{l&,h&}, \
        \       callname&(NCBNAMSZ-1), name&(NCBNAMSZ-1), rto&, sto&, \
        \       post%, lana_num&, cmd_cplt&, reserve&(9), event%}
 
        DIM ast{status&(59), namebuff&(18*30-1)}
 
        ncb.command& = NCBRESET
        SYS `Netbios`, ncb{}
 
        ncb.command& = NCBASTAT
        ncb.buffer% = ast{}
        ncb.length.l& = DIM(ast{})
        ncb.callname&() = "*               "
        ncb.lana_num& = 0
        SYS `Netbios`, ncb{}
 
        MACaddress$ = RIGHT$("0"+STR$~ast.status&(0), 2) + "-" + \
        \             RIGHT$("0"+STR$~ast.status&(1), 2) + "-" + \
        \             RIGHT$("0"+STR$~ast.status&(2), 2) + "-" + \
        \             RIGHT$("0"+STR$~ast.status&(3), 2) + "-" + \
        \             RIGHT$("0"+STR$~ast.status&(4), 2) + "-" + \
        \             RIGHT$("0"+STR$~ast.status&(5), 2)
 
        PRINT "MAC address = " MACaddress$


Method 4


This method can return the MAC address of multiple network adaptors, so is useful if you need information other than just for the primary adaptor. It works on versions of Windows from 98 to Vista or later (with acknowledgements to Jon Ripley on whose code this is based):

        SYS "LoadLibrary", "iphlpapi.dll" TO iphlpapi%
        SYS "GetProcAddress", iphlpapi%, "GetAdaptersInfo" TO GetAdaptersInfo%
 
        DIM _IP_ADDR_STRING{AdrNext%, IpAddress&(15), IpMask&(15), NTEcontext%}
 
        DIM _IP_ADAPTER_INFO{Next%, ComboIndex%, AdapterName&(259), Description&(131), \
        \                    MACadrLength%, MACaddress&(7), AdapterIndex%, \
        \                    AdapterType%, DhcpEnabled%, CurrentIpAddress%, \
        \                    IpAddressList{} = _IP_ADDR_STRING{}, \
        \                    GatewayList{} = _IP_ADDR_STRING{}, \
        \                    DhcpServer{} = _IP_ADDR_STRING{}, \
        \                    HaveWins%, \
        \                    PrimaryWinsServer{} = _IP_ADDR_STRING{}, \
        \                    SecondaryWinsServer{} = _IP_ADDR_STRING{}, \
        \                    LeaseObtained%, LeaseExpires%}
 
        SYS GetAdaptersInfo%, _IP_ADAPTER_INFO{}, ^OutBufLen%
        NumAdapters% = OutBufLen% DIV DIM(_IP_ADAPTER_INFO{})
 
        DIM AdapterInfo{(NumAdapters%-1)} = _IP_ADAPTER_INFO{}
        SYS GetAdaptersInfo%, AdapterInfo{(0)}, ^OutBufLen%
 
        FOR i% = 0 TO NumAdapters% - 1
          MACaddress$ = RIGHT$("0"+STR$~AdapterInfo{(i%)}.MACaddress&(0), 2) + "-" + \
          \             RIGHT$("0"+STR$~AdapterInfo{(i%)}.MACaddress&(1), 2) + "-" + \
          \             RIGHT$("0"+STR$~AdapterInfo{(i%)}.MACaddress&(2), 2) + "-" + \
          \             RIGHT$("0"+STR$~AdapterInfo{(i%)}.MACaddress&(3), 2) + "-" + \
          \             RIGHT$("0"+STR$~AdapterInfo{(i%)}.MACaddress&(4), 2) + "-" + \
          \             RIGHT$("0"+STR$~AdapterInfo{(i%)}.MACaddress&(5), 2)
 
          PRINT "MAC address = " MACaddress$
        NEXT i%


Method 5


This method uses SNMP (the Simple Network Management Protocol). It too can return the MAC address of more than one adaptor, and runs on Windows 2000, Windows XP and Windows Vista or later. It uses the most complex code of any of the methods:

        SYS "LoadLibrary", "INETMIB1.DLL" TO inetmib1%
        SYS "GetProcAddress", inetmib1%, "SnmpExtensionInit" TO `SnmpExtensionInit`
        SYS "GetProcAddress", inetmib1%, "SnmpExtensionQuery" TO `SnmpExtensionQuery`
 
        _ASN_RFC1157_GETNEXTREQUEST = &A1
 
        DIM OID_ifEntryNum%(7)
        OID_ifEntryNum%() = 1, 3, 6, 1, 2, 1, 2, 1
        SYS "GlobalAlloc", 0, 32 TO MIB_ifEntryNum%
        FOR I% = 0 TO 7
          MIB_ifEntryNum%!(4*I%) = OID_ifEntryNum%(I%)
        NEXT
 
        DIM OID_ifEntryType%(9)
        OID_ifEntryType%() = 1, 3, 6, 1, 2, 1, 2, 2, 1, 3
        SYS "GlobalAlloc", 0, 40 TO MIB_ifEntryType%
        FOR I% = 0 TO 9
          MIB_ifEntryType%!(4*I%) = OID_ifEntryType%(I%)
        NEXT
 
        DIM OID_ipMACEntAddr%(9)
        OID_ipMACEntAddr%() = 1, 3, 6, 1, 2, 1, 2, 2, 1, 6
        SYS "GlobalAlloc", 0, 40 TO MIB_ipMACEntAddr%
        FOR I% = 0 TO 9
          MIB_ipMACEntAddr%!(4*I%) = OID_ipMACEntAddr%(I%)
        NEXT
 
        DIM AsnObjectName{idLength%, pids%}
        DIM AsnObjectSyntax{asnType%, asnValue%, padding%(1)}
        DIM SupportedView{} = AsnObjectName{}
 
        DIM varBind{name0{}=AsnObjectName{}, value0{}=AsnObjectSyntax{}, \
        \           name1{}=AsnObjectName{}, value1{}=AsnObjectSyntax{}}
 
        DIM varBindList{list%, len%}
 
        SYS "GetTickCount" TO tick%
        SYS `SnmpExtensionInit`, tick%, ^hPollForTrapEvent%, SupportedView{}
 
        varBind.name0.idLength% = DIM(OID_ifEntryNum%(),1) + 1
        varBind.name0.pids% = MIB_ifEntryNum%
 
        varBindList.len% = 1
        varBindList.list% = varBind{}
 
        SYS `SnmpExtensionQuery`, _ASN_RFC1157_GETNEXTREQUEST, varBindList{}, \
        \                         ^ErrorStatus%, ^ErrorIndex%
 
        varBindList.len% = 2
        varBind.name0.idLength% = DIM(OID_ifEntryType%(),1) + 1
        varBind.name0.pids% = MIB_ifEntryType%
        varBind.name1.idLength% = DIM(OID_ipMACEntAddr%(),1) + 1
        varBind.name1.pids% = MIB_ipMACEntAddr%
 
        REPEAT
 
          SYS `SnmpExtensionQuery`, _ASN_RFC1157_GETNEXTREQUEST, varBindList{}, \
          \                         ^ErrorStatus%, ^ErrorIndex% TO ret%
 
          IF ret% IF varBind.name0.idLength% >= 10 THEN
            FOR I% = 0 TO 9
              ret% AND= (varBind.name0.pids%!(I%*4) = OID_ifEntryType%(I%))
            NEXT
            IF ret% IF varBind.name1.idLength% >= 10 THEN
              FOR I% = 0 TO 9
                ret% AND= (varBind.name1.pids%!(I%*4) = OID_ipMACEntAddr%(I%))
              NEXT
              IF ret% IF varBind.value0.asnValue% = 6 THEN
 
                MACaddress$ = RIGHT$("0"+STR$~varBind.value1.asnValue%?0, 2) + "-" + \
                \             RIGHT$("0"+STR$~varBind.value1.asnValue%?1, 2) + "-" + \
                \             RIGHT$("0"+STR$~varBind.value1.asnValue%?2, 2) + "-" + \
                \             RIGHT$("0"+STR$~varBind.value1.asnValue%?3, 2) + "-" + \
                \             RIGHT$("0"+STR$~varBind.value1.asnValue%?4, 2) + "-" + \
                \             RIGHT$("0"+STR$~varBind.value1.asnValue%?5, 2)
 
                PRINT "MAC address = " MACaddress$
 
              ENDIF
            ENDIF
          ENDIF
 
        UNTIL ret% = 0
 
        SYS "GlobalFree", varBind.name0.pids%
        SYS "GlobalFree", varBind.name1.pids%