如何获取网卡物理地址(转载)

 来源:http://www.codeguru.com/network/GetMAC.html

Environment: Compiled on: Visual Studio .NET & Windows XP Pro. Tested on Win2K Server & WinXP Pro

I recently wrote two articles referenced below on how to get your IP address in C++ and C# respectively. I found that people commented wondering how to get their MAC address, so I decided that it might be wise to follow up with an in depth discussion of the issue at hand.

 

OSI 7 Layer Model

As you may know there are 7 layers in the OSI (Open Systems Interconnection) model. Specifically one can describe them as below.

 

  • Layer 7: Application Layer
  • Layer 6: Presentation Layer
  • Layer 5: Session Layer
  • Layer 4: Transport Layer
  • Layer 3: Network Layer
  • Layer 2: Data Link Layer
  • Layer 1: Physical Layer
Now we can interpret the Physical Layer as the point at which the wires are connecting the network together. It is the physical, raw cabling and the ones and zeros going over the analog wiring at a given rate. One can have the internet running on many different types of networks, and the cabling at the end comes down to a pair of wires or more depending on the standard being used (eg. ethernet, IBM token ring, PPP...). The Physical Layer encompasses the electrical signals and cabling issues.

The Data Link Layer is responsible for transfering discreet packets of information over the physical layer. This layer must be error free and does not deal with routing issues.

It can be described as the method in which two PCs communicate over the physical network layer. Let us take the ethernet cards for example. Apart from every computer on the internet having an IP address, each ethernet computer has a 48-bit MAC address. This is the Media Access Control Layer and along with the LLC (logic link layer) compose the entire Data Link Layer in an ethernet example. The MAC is the layer that deals with full duplex or half duplex ethernet cards, 10/100 or gigabit ethernet speed transfers; it also the one dealing with point to point addressing. MAC can have several types of packets running on top of it including IP, IPX, AppleTalk, ATM, X. 25 and LAN Manager. LLC deals with frame synchronization, flow control and error checking.

The Network Layer is essentially handled by the IP layer in a TCP/IP stack. IPX/SPX would be handling the network layer and transport layer in an Novell IPX world. The network layer deals with routing issues, forwarding issues and making sure that the packets are within the maximum packet size (MTU) and fragmenting them if not. It also deals with reassembling them at the point of entry. The Network Layer also has the essence of IP which is IPv4 addressing. The Network Layer also deals with things such as ICMP, ARP and RARP as well as the issues mentioned above.

The last layer that is encompassed by TCP/IP is the Transport Layer. By far the most widely used MAC type used is IP, where TCP and UDP are derived from; the standard IPv4 dotted notation 32-bit IP address. The Transport Layer corresponds to the TCP of TCP/IP; TCP stands for Transmission Control Protocol and UDP for User Datagram Protocol. One can also consider UDP as an alternate method of transport, although it is connectionless and does not handle errorless transfers (can drop packets). The transport layer is responsible for end to end communication and errorless transfer of data including flow control, TCP is the primary handler of this task and performs it well. The TCP/IP stack needs to deal with an extra level of packet sequencing because of the windowing protocol, along with flow control issues and retransmission of lost packets.

Finally the Session, Presentation and Application Layer have not yet been solidified in any real manner (eg. like the other layers have been with TCP/IP, ethernet, etc). People seem to consider protocols such as FTP, SMTP, telnet, POP3 and such as being on the Application layer or in a composition of all three layers. On top of that, it is the programmer's prerogative of layering the software in a modular approach.

 

Why do I want a MAC address?

When computers talk over TCP/IP or UDP/IP the computers whom wish to speak to the destination IP computer ARP for the destination computer's MAC address. The TCP/IP implementations require the IP to MAC translation and in reverse (RARP). This is the only way data gets transferred over the internet, by going through the layers from layer 7 to layer 1 and back. Hence when you send a packet to an IP address, the Network layer finds the destination MAC and sends the packets to that MAC address. In a LAN setting, you usually hit the destination ethernet card yourself through maybe some switches or hubs. In an internet setting, you go through various routers which do some analysis of TCP/IP headers and it finally arrives at the destination ethernet card. You must realize that every ethernet card in the world has a unique MAC address.

When you are a Winsock programmer dealing exclusively with sockets, I doubt you would care what the MAC addresses of the related NICs (Network Interface Card) are because you would never need to know about them. This is a low level issue which one needs not be exposed to in a network programming environment.

There are specific reasons why you may need to know the MAC address. Personally I had to write the Media Access Control Layer in an HDL language at one point. I had hard coded the FPGA to a certain MAC and created a static ARP so that I could write sockets software on my PC which would send UDP packets to my MAC. The MAC would then decipher and verify the checksum, and CRC of the UDP and MAC packets. It would then take the payload and forward it along to an RS232 interface. The RSR232 interface was connected to a Bluetooth device via RFCOMM. Your reason for knowing a MAC address may be different.

We will deal with enumerating the MAC address of all the NICs in your computer in a Windows environment.

 

Deciphering the 48-bit MAC address

The 48-bit MAC address is a globally unique identifier. Each ethernet card in the world has a unique MAC address. The first 24 bits correspond to the Organizationally Unique Identifier. The second 24 bits is administered by the company or organization that the OUI has been assigned to. You will notice that all 3Com cards, for example, will have the same OUI, the first 3 octets in a MAC address.

 

How do I get a MAC address via command line?

Okay, there are several command line utilities to get your MAC address. The first one that comes to mind is GetMAC. Simply open a command prompt and type GetMAC and it will return your 48-bit MAC address in the following format:

 

Physical Address   Transport Name
================== ====================================================
00-40-CA-B5-5B-06  /Device/Tcpip_{B249BB63-9574-4061-817A-D62E1D12072F}
The next method of doing it is writing IPCONFIG /ALL, this will also get all the MAC addresses of your ethernet cards along with all the IP addresses setup for each ethernet card. Information such as your Gateway, WINS server, DNS server, subnet mask, and all the IPs associated with your each NIC.

An interesting way to discover what MAC addresses you know of other people on your network is to type ARP -a in a command prompt and you should get a listing that is similar to this.

 

Interface: 192.168.1.102 --- 0x2
  Internet Address      Physical Address      Type
  192.168.1.1           00-20-78-d9-5c-b3     dynamic
  192.168.1.100         00-50-ba-b3-55-ec     dynamic
  192.168.1.101         00-a0-cc-7a-7d-6d     dynamic

How do I get a MAC in C or C++, Win32 environment?

There are numerous ways to do this. As I was on my investigation, I realized that there was no clear and simple way to do this. A lot of people said to use NETSTAT and to parse the result. Some people said to create a UUID and pull the MAC from there. A couple of people said to use NetBIOS, and finally one said to query the NDIS miniport driver itself.

Although the last solution sounded the coolest, apparently doing that from user mode wasn't the easiest thing to do. I'll give you a set of solutions from worst to best and then a quick discussion of the Miniport method.

 

Method One: UuidCreate

One quick way to find out your MAC address, which is very hacky and I wouldn't recommend would be to create a sequential Uuid. Apparently Microsoft uses your MAC address to help it create a universally unique identifier.

All you have to do is check out bytes 2 through 8 and you are done. The code is below and the downloadable EXE and sample code is listed at the end of the article.

 

// Fetches the MAC address and prints it
static void GetMACaddress(void)
{
  unsigned char MACData[6];

  UUID uuid;
  UuidCreateSequential( &uuid );    // Ask OS to create UUID

  for (int i=2; i<8; i++)  // Bytes 2 through 7 inclusive
                           // are MAC address
    MACData[i - 2] = uuid.Data4[i];

  PrintMACaddress(MACData);         // Print MAC address
}
This code will only work in Windows 2000/XP since Microsoft replaced UuidCreate in Windows 2000/XP with one that doesn't use the PC's MAC address. UuidCreate with your MAC address can easily be considered a security risk since you are distributing your ethernet card's address.

Microsoft created UuidCreateSequential in Win2K and XP to do what the old UuidCreate did on Windows 95/98/Me. The current UuidCreate in Windows 2000/XP is not composed of a number which includes the MAC address of your primary NIC, hence they moved over that functionality to UuidCreateSequential. On the older OSes you may still use the UuidCreate function to obtain its MAC address.

This example only supports one NIC card on your PC.

 

Method Two: Use NetBIOS

This solution is a lot more complicated then the final solution. It supports multiple NIC cards, but requires NetBIOS to be installed on the computer. It also requires you to have the cable connected to a valid NetBIOS network. It works great under all OSes including 95/98/Me/NT/2000/XP.

 

// Fetches the MAC address and prints it
static void GetMACaddress(void)
{
  unsigned char MACData[8];      // Allocate data structure
                                 // for MAC (6 bytes needed)

  WKSTA_TRANSPORT_INFO_0 *pwkti; // Allocate data structure
                                 // for NetBIOS
  DWORD dwEntriesRead;
  DWORD dwTotalEntries;
  BYTE *pbBuffer;

  // Get MAC address via NetBIOS's enumerate function
  NET_API_STATUS dwStatus = NetWkstaTransportEnum(
   NULL,                 // [in]  server name
   0,                    // [in]  data structure to return
   &pbBuffer,            // [out] pointer to buffer
   MAX_PREFERRED_LENGTH, // [in]  maximum length
   &dwEntriesRead,       // [out] counter of elements
                         //       actually enumerated
   &dwTotalEntries,      // [out] total number of elements
                         //       that could be enumerated
   NULL);                // [in/out] resume handle
  assert(dwStatus == NERR_Success);

  pwkti = (WKSTA_TRANSPORT_INFO_0 *)pbBuffer; // type cast the buffer

  for(DWORD i=1; i< dwEntriesRead; i++)  // first address is
                                            // 00000000, skip it
  {                                         // enumerate MACs & print
    swscanf((wchar_t *)pwkti[i].wkti0_transport_address,
            L"%2hx%2hx%2hx%2hx%2hx%2hx",
            &MACData[0],
            &MACData[1],
            &MACData[2],
            &MACData[3],
            &MACData[4],
            &MACData[5]);
    PrintMACaddress(MACData);
  }

  // Release pbBuffer allocated by above function
  dwStatus = NetApiBufferFree(pbBuffer);
  assert(dwStatus == NERR_Success);
}
As you can tell converting the wide string which is returned from NetWkstaTransportEnum to an BYTE based array is a mess in itself. The great thing about this is that it goes through all the NICs located on your PC. You are also able to easily query other people's PCs by passing in a NetBIOS computer name as the first parameter to this function.

 

Method Three: Use GetAdaptersInfo

The cleanest way I could find to get all the MAC addresses located on a PC was to use the GetAdaptersInfo method. It includes almost as much information as IPCONFIG /ALL including your DHCP server, Gateway, IP address list, subnet mask and WINS servers. It also enumerates all the NICs on your PC and is supported in 95/98/Me/NT/2000/XP. Finally it also works if your NICs are not connected to valid networks (eg. wires are not even hooked up), but the NICs do have to be "enabled" in Windows.

 

// Fetches the MAC address and prints it
static void GetMACaddress(void)
{
  IP_ADAPTER_INFO AdapterInfo[16];       // Allocate information
                                         // for up to 16 NICs
  DWORD dwBufLen = sizeof(AdapterInfo);  // Save memory size of buffer

  DWORD dwStatus = GetAdaptersInfo(      // Call GetAdapterInfo
    AdapterInfo,                 // [out] buffer to receive data
    &dwBufLen);                  // [in] size of receive data buffer
  assert(dwStatus == ERROR_SUCCESS);  // Verify return value is
                                      // valid, no buffer overflow

  PIP_ADAPTER_INFO pAdapterInfo = AdapterInfo; // Contains pointer to
                                               // current adapter info
  do {
    PrintMACaddress(pAdapterInfo->Address); // Print MAC address
    pAdapterInfo = pAdapterInfo->Next;    // Progress through
                                          // linked list
  }
  while(pAdapterInfo);                    // Terminate if last adapter
}
I probably should mention that statically allocating an array for up to 16 NICs is not the best way to do this. It is a quick and dirty solution that should show you essentially how to get and enumerate all the MAC addresses on your PC.

 

Method Four: The Miniport Driver

I never implemented this method since it required some very low level coding. It also probably wouldn't be a good method since you are directly talking to the underlying NDIS miniport driver. The basic concept of this is to hit the miniport driver with an OIS query of OID_802_3_CURRENT_ADDRESS. This should return a buffer with the current MAC address.

The way problem with this solution is that there is no easy way to do this from user mode, which is exactly the mode we are coding all our apps in, this is as opposed to kernal mode which drivers reside in.

 

Conclusion

Although there are no functions named GetMACaddress in the Win32 API, ATL, MFC, or C#, it is fairly easy to find and associate your MAC address with its related IP addresses by calling GetAdaptersInfo(). GetAdaptersInfo is located in the Platform SDK.

I hope you had an interesting time reading this article.

Reference: Get IP address in C#
Reference: Get IP address in C++

 

About the Author

Khalid Shaikh is a software engineer currently contracting for HP in Palo Alto. He has worked at such companies such as Microsoft, Nvidia, and several silicon valley startups. Khalid has been featured in PC Gamer for innovative driver development techniques and is currently co-founder of HTTP-Tunnel Corp.

If you have any questions, don't hestitate to e-mail me.

 

Downloads

Download demo project and source (GetMACUUID) - 19 Kb
Download demo project and source (GetMACNetBIOS) - 23 Kb
Download demo project and source (GetMACAdapters) - 19 Kb

你可能感兴趣的:(如何获取网卡物理地址(转载))