`

获取系统网络MAC地址的三种方法

 
阅读更多
目的
篇文章的目的是给出一些得知你MAC地址的简单方法。我会解释这些代码是如何工作的,并给出一些简单的例子来阐述。我假设你已经掌握了下面的概念:
  • Borland C++Builder
  • 简单的网络概念
  • 一些 Win32 API

方法一 - 用Netbios API

个方法是通过微软的Netbios API来得到你机器的MAC地址。这些API是一组提供比所谓的Winsock更底层的网络支持的命令。通过Netbios来得到地质这种方法的确定就是你必须安装了Netbios(如果你在一个Windows网络上并使用了文件共享,就没有这个问题)。另外,这个方法快速又准确。

Netbios API只包含了一个简称为Netbios的函数。这个函数通过一个网络控制块结构作为参数,来告诉函数需要做什么。这个结构的定义如下:

typedef struct _NCB { 
UCHAR ncb_command;
UCHAR ncb_retcode;
UCHAR ncb_lsn;
UCHAR ncb_num;
PUCHAR ncb_buffer;
WORD ncb_length;
UCHAR ncb_callname[NCBNAMSZ];
UCHAR ncb_name[NCBNAMSZ];
UCHAR ncb_rto;
UCHAR ncb_sto;
void (CALLBACK *ncb_post) (struct _NCB *);
UCHAR ncb_lana_num;
UCHAR ncb_cmd_cplt;
#ifdef _WIN64
UCHAR ncb_reserve[18];
#else
UCHAR ncb_reserve[10];
#endif
HANDLE ncb_event;
} NCB, *PNCB;

特别需要注意的是ncb_command成员。就是这个成员来告诉Netbios要做什么。我们将用三条指令来得到MAC地址。这些命令在MSDN中的定义如下:

Command Description
NCBENUM Windows NT/2000: Enumerates LAN adapter (LANA) numbers. When this code is specified, the ncb_buffer member points to a buffer to be filled with a LANA_ENUM structure.

NCBENUM is not a standard NetBIOS 3.0 command.
NCBRESET Resets a LAN adapter. An adapter must be reset before it can accept any other NCB command that specifies the same number in the ncb_lana_num member.
NCBASTAT Retrieves the status of either a local or remote adapter. When this code is specified, the ncb_buffer member points to a buffer to be filled with an ADAPTER_STATUS structure, followed by an array of NAME_BUFFER structures.

这就是得到一个或多个系统MAC地址的步骤:

  • 枚举所有的网络适配器
  • 重启每一个适配器以便得到它的正确信息
  • 查询该适配器来得到MAC地址并将地址填入标准的colon-separated格式(指用冒号分割的格式)
下面的代码简单是这些概念的简单示例。有关Netbios函数的更多信息请参考微软帮助文件或MSDN。

netbios.cpp
#include <windows.h>
#include <stdlib.h>
#include <stdio.h>
#include <iostream>
#include <string>

using namespace std;
#define bzero(thing,sz) memset(thing,0,sz)

bool GetAdapterInfo(int adapter_num, string &mac_addr)
{
// 重启网络适配器以使我们能开始对它查询
NCB Ncb;
memset(&Ncb, 0, sizeof(Ncb));
Ncb.ncb_command = NCBRESET;
Ncb.ncb_lana_num = adapter_num;
if (Netbios(&Ncb) != NRC_GOODRET) {
mac_addr = "bad (NCBRESET): ";
mac_addr += string(Ncb.ncb_retcode);
return false;
}

// 准备得到适配器状态快
bzero(&Ncb,sizeof(Ncb);
Ncb.ncb_command = NCBASTAT;
Ncb.ncb_lana_num = adapter_num;
strcpy((char *) Ncb.ncb_callname, "*");
struct ASTAT
{
ADAPTER_STATUS adapt;
NAME_BUFFER NameBuff[30];
} Adapter;
bzero(&Adapter,sizeof(Adapter));
Ncb.ncb_buffer = (unsigned char *)&Adapter;
Ncb.ncb_length = sizeof(Adapter);

// 取适配器信息,如果成功按标准colon-delimited格式返回它
if (Netbios(&Ncb) == 0)
{
char acMAC[18];
sprintf(acMAC, "%02X:%02X:%02X:%02X:%02X:%02X",
int (Adapter.adapt.adapter_address[0]),
int (Adapter.adapt.adapter_address[1]),
int (Adapter.adapt.adapter_address[2]),
int (Adapter.adapt.adapter_address[3]),
int (Adapter.adapt.adapter_address[4]),
int (Adapter.adapt.adapter_address[5]));
mac_addr = acMAC;
return true;
}
else
{
mac_addr = "bad (NCBASTAT): ";
mac_addr += string(Ncb.ncb_retcode);
return false;
}
}

int main()
{
// 得到适配器列表
LANA_ENUM AdapterList;
NCB Ncb;
memset(&Ncb, 0, sizeof(NCB));
Ncb.ncb_command = NCBENUM;
Ncb.ncb_buffer = (unsigned char *)&AdapterList;
Ncb.ncb_length = sizeof(AdapterList);
Netbios(&Ncb);

// 得到所有的本地以太网地址
string mac_addr;
for (int i = 0; i < AdapterList.length - 1; ++i)
{
if (GetAdapterInfo(AdapterList.lana[i], mac_addr))
{
cout << "Adapter " << int (AdapterList.lana[i]) <<
"'s MAC is " << mac_addr << endl;
}
else
{
cerr << "Failed to get MAC address! Do you" << endl;
cerr << "have the NetBIOS protocol installed?" << endl;
break;
}
}

return 0;
}


//---------------------------------------------------------------------------


方法二 - COM GUID API

个方法用COM API来创建一个GUID(globably unique identifier,全局唯一标识符)并且从那里得到MAC地址。GUID是用来一般地标识系统中的COM组件或者其他对象。他们通过MAC地址(再加上其他东西)计算出来的,并且在表面上看还将地址保留在GUID中。我说表面上的原因是这还不确定。我提供这个方法作主要是作为一个不要做什么的例子。按这种方法你有可能最终得到MAC地址,但也可能你最后会得到一些随机的十六进制数。

这个方法非常的简单,并不需要太多的解释。我们通过CoCreateGuid创建一个GUID并将最后的6字节存入一个字符串。这应该就是MAC地址,但就像我说的一样,并没有方法可以保证。

uuid.cpp
#include <windows.h>
#include <iostream>
#include <conio.h>

using namespace std;

int main()
{
cout << "MAC address is: ";

// 请求COM为我们创建一个UUID。如果本机有一个以太网适配器,UUID的最后
// 6字节(包含Data4的2-7字节)应该就是本地以太网适配器的MAC地址。
GUID uuid;
CoCreateGuid(&uuid);
// 将地址分割出来
char mac_addr[18];
sprintf(mac_addr,"%02X:%02X:%02X:%02X:%02X:%02X",
uuid.Data4[2],uuid.Data4[3],uuid.Data4[4],
uuid.Data4[5],uuid.Data4[6],uuid.Data4[7]);
cout << mac_addr << endl;
getch();
return 0;
}
方法三 - 用SNMP扩展API

要谈的第三种方法是通过使用windows中的SNMP(Simple Network Management Protocol,简单网络管理协议)扩展来得到系统的地址。据个人经验,SNMP很复杂,不过下面的代吗应该可以轻松读懂。基本上这些步骤和使用Netbios时的相同:

  • 得到适配器列表
  • 查询每一个适配器的类型和MAC地址
  • 将实际是NIC的适配器保存
我个人并不太熟悉SNMP,但一如我之前所讲,代码非常的清晰。参考以下的地址可获得更多信息:

SNMP Functions
SNMP Variable Types and Request PDU Types
SNMP Structures

snmp.cpp
#include <snmp.h>
#include <conio.h>
#include <stdio.h>

typedef bool(WINAPI * pSnmpExtensionInit) (
IN DWORD dwTimeZeroReference,
OUT HANDLE * hPollForTrapEvent,
OUT AsnObjectIdentifier * supportedView);

typedef bool(WINAPI * pSnmpExtensionTrap) (
OUT AsnObjectIdentifier * enterprise,
OUT AsnInteger * genericTrap,
OUT AsnInteger * specificTrap,
OUT AsnTimeticks * timeStamp,
OUT RFC1157VarBindList * variableBindings);

typedef bool(WINAPI * pSnmpExtensionQuery) (
IN BYTE requestType,
IN OUT RFC1157VarBindList * variableBindings,
OUT AsnInteger * errorStatus,
OUT AsnInteger * errorIndex);

typedef bool(WINAPI * pSnmpExtensionInitEx) (
OUT AsnObjectIdentifier * supportedView);

void main()
{
HINSTANCE m_hInst;
pSnmpExtensionInit m_Init;
pSnmpExtensionInitEx m_InitEx;
pSnmpExtensionQuery m_Query;
pSnmpExtensionTrap m_Trap;
HANDLE PollForTrapEvent;
AsnObjectIdentifier SupportedView;
UINT OID_ifEntryType[] = {1, 3, 6, 1, 2, 1, 2, 2, 1, 3};
UINT OID_ifEntryNum[] = {1, 3, 6, 1, 2, 1, 2, 1};
UINT OID_ipMACEntAddr[] = {1, 3, 6, 1, 2, 1, 2, 2, 1, 6};
AsnObjectIdentifier MIB_ifMACEntAddr =
{ sizeof(OID_ipMACEntAddr) sizeof(UINT), OID_ipMACEntAddr };
AsnObjectIdentifier MIB_ifEntryType =
{sizeof(OID_ifEntryType) sizeof(UINT), OID_ifEntryType};
AsnObjectIdentifier MIB_ifEntryNum =
{sizeof(OID_ifEntryNum) sizeof(UINT), OID_ifEntryNum};
RFC1157VarBindList varBindList;
RFC1157VarBind varBind[2];
AsnInteger errorStatus;
AsnInteger errorIndex;
AsnObjectIdentifier MIB_NULL = {0, 0};
int ret;
int dtmp;
int i = 0, j = 0;
bool found = false;
char TempEthernet[13];
m_Init = NULL;
m_InitEx = NULL;
m_Query = NULL;
m_Trap = NULL;

/* 加载SNMP动态链接库并得到所需函数的地址 */
m_hInst = LoadLibrary("inetmib1.dll");
if (m_hInst < (HINSTANCE) HINSTANCE_ERROR)
{
m_hInst = NULL;
return;
}
m_Init =
(pSnmpExtensionInit) GetProcAddress(m_hInst, "SnmpExtensionInit");
m_InitEx =
(pSnmpExtensionInitEx) GetProcAddress(m_hInst,
"SnmpExtensionInitEx");
m_Query =
(pSnmpExtensionQuery) GetProcAddress(m_hInst,
"SnmpExtensionQuery");
m_Trap =
(pSnmpExtensionTrap) GetProcAddress(m_hInst, "SnmpExtensionTrap");
m_Init(GetTickCount(), &PollForTrapEvent, &SupportedView);

/* 初始化m_Query将传回的变量列表 */
varBindList.list = varBind;
varBind[0].name = MIB_NULL;
varBind[1].name = MIB_NULL;

/* 拷贝OID以在接口表中查找到表目(适配器)数量 */
varBindList.len = 1; /* 仅处理一条记录 */
SNMP_oidcpy(&varBind[0].name, &MIB_ifEntryNum);
ret =
m_Query(ASN_RFC1157_GETNEXTREQUEST, &varBindList, &errorStatus,
&errorIndex);
printf("# of adapters in this system : %in",
varBind[0].value.asnValue.number);
varBindList.len = 2;

/* 拷贝入OID接口类型ifType */
SNMP_oidcpy(&varBind[0].name, &MIB_ifEntryType);

/* 拷贝入OID地址ifPhysAddress */
SNMP_oidcpy(&varBind[1].name, &MIB_ifMACEntAddr);

do
{

/* 提交查询。应答将被填入varBindList。 Submit the query. Responses will be loaded into varBindList.
我们可以认为后续调用的执行次数与系统所报告的适配器数目相同。 */

ret =
m_Query(ASN_RFC1157_GETNEXTREQUEST, &varBindList, &errorStatus,
&errorIndex);
if (!ret)
ret = 1;
else
/* 确认已经返回正确的类型 */
ret =
SNMP_oidncmp(&varBind[0].name, &MIB_ifEntryType,
MIB_ifEntryType.idLength); if (!ret) {
j++;
dtmp = varBind[0].value.asnValue.number;
printf("Interface #%i type : %in", j, dtmp);

/* 类型6标识以太网接口 */
if (dtmp == 6)
{

/* 这里确认我们有了一个地址 */
ret =
SNMP_oidncmp(&varBind[1].name, &MIB_ifMACEntAddr,
MIB_ifMACEntAddr.idLength);
if ((!ret) && (varBind[1].value.asnValue.address.stream != NULL))
{
if((varBind[1].value.asnValue.address.stream[0] == 0x44)
&& (varBind[1].value.asnValue.address.stream[1] == 0x45)
&& (varBind[1].value.asnValue.address.stream[2] == 0x53)
&& (varBind[1].value.asnValue.address.stream[3] == 0x54)
&& (varBind[1].value.asnValue.address.stream[4] == 0x00))
{
/* 忽略所有的拨号网络适配器 */
printf("Interface #%i is a DUN adaptern", j);
continue;
}
if ((varBind[1].value.asnValue.address.stream[0] == 0x00)
&& (varBind[1].value.asnValue.address.stream[1] == 0x00)
&& (varBind[1].value.asnValue.address.stream[2] == 0x00)
&& (varBind[1].value.asnValue.address.stream[3] == 0x00)
&& (varBind[1].value.asnValue.address.stream[4] == 0x00)
&& (varBind[1].value.asnValue.address.stream[5] == 0x00))
{
/* 忽略其他网络接口返回的NULL地址 */
printf("Interface #%i is a NULL addressn", j);
continue;
}
sprintf(TempEthernet, "%02x%02x%02x%02x%02x%02x",
varBind[1].value.asnValue.address.stream[0],
varBind[1].value.asnValue.address.stream[1],
varBind[1].value.asnValue.address.stream[2],
varBind[1].value.asnValue.address.stream[3],
varBind[1].value.asnValue.address.stream[4],
varBind[1].value.asnValue.address.stream[5]);
printf("MAC Address of interface #%i: %sn", j,
TempEthernet);}
}
}
} while (!ret); /* 遇到错误时停止。当我们遍历完所有需被测试的接口后会产生一个错误。 */
getch();

FreeLibrary(m_hInst);
/* 释放邦定 */
SNMP_FreeVarBind(&varBind[0]);
SNMP_FreeVarBind(&varBind[1]);
}
<script type="text/javascript"> new Ad(4, 'ad_cen'); </script>
查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
<script type="text/javascript"> var fileName = '3461377'; var commentscount = 0; var islock = false </script><script type="text/javascript" src="http://static.blog.csdn.net/scripts/comment.js"></script>
<script type="text/javascript"> new Ad(5, 'ad_bot'); </script>
    个人资料

    vrix
    • 访问:227496次
    • 积分:4527分
    • 排名:第579名
    • 原创:129篇
    • 转载:442篇
    • 译文:4篇
    • 评论:127条
    文章搜索
      推荐文章
      <script type="text/javascript" src="http://static.blog.csdn.net/scripts/ad.js"></script><script type="text/javascript"> new Ad(12, 'ad_commend'); </script>
        最新评论
      <script type="text/javascript" src="http://static.blog.csdn.net/scripts/SyntaxHighlighter/shCore-src.js"></script><script type="text/javascript" src="http://static.blog.csdn.net/scripts/ZeroClipboard/ZeroClipboard.js"></script><script type="text/javascript" src="http://static.blog.csdn.net/scripts/article_code.js"></script><script type="text/javascript" src="http://medal.blog.csdn.net/scripts/show.js"></script><script type="text/javascript" src="http://medal.blog.csdn.net/showblogmedal.ashx?blogid=72975"></script><script type="text/javascript">document.write("<img src=http://counter.csdn.net/pv.aspx?id=24 border=0 width=0 height=0>");</script><script type="text/javascript" src="http://www.csdn.net/ui/scripts/Csdn/counter.js"></script><script type="text/javascript" src="http://csdnimg.cn/pubfooter/js/publib_footer.js"></script>
      分享到:
      评论

      相关推荐

        C语言去获取Linux系统的IP地址和MAC地址的方法

        C语言获取mac地址和IP地址的源代码,Linux系统下面。

        嵌入式系统/ARM技术中的Linux下读取网卡默认MAC地址的方法

         这里主要介绍读取网卡MAC地址的方法,适用于EasyARM-i.MX287A开发套件,其应用原理及配套示例也适用于下表1.1所列出的产品型号。 表1.1 适用产品型号  1.2 原理介绍  MAC(Media ...

        网络游戏-MAC地址获取方法、网关设备、网络认证设备及网络系统.zip

        网络游戏-MAC地址获取方法、网关设备、网络认证设备及网络系统.zip

        网络游戏-MAC地址获取方法及系统、网络安全设备及可读存储介质.zip

        网络游戏-MAC地址获取方法及系统、网络安全设备及可读存储介质.zip

        获取IP地址与MAC地址对应关系实验报告1

        摘要在以太网中,获取 MAC 地址常常是其他工作的前提,本实验要求使用系统提供的命令和利用 WinPcap 编程两种方式获取以太网中的主机的 MAC 地址,通过

        Android 判断网络是否可用 & 获取IP地址 & 获取以太网口MAC地址

        判断网络是否可用: 注意!是判断网络是否可用,但网络可用不代表一定能上外网的! public static boolean isNetworkAvailable(Context context) { ConnectivityManager manager = (ConnectivityManager) context...

        虚拟机下CentOS 6.5配置IP地址的三种方法.doc

        CentOS 6.5 在虚拟机中配置 IP 地址的三种...在 CentOS 6.5 中配置 IP 地址有三种方法:自动获取 IP 地址、手动设置 IP 地址和使用 NAT 模式。每种方法都有其优点和缺点,需要根据自己的需求和环境来选择合适的方法。

        获取本地MAC地址小程序

        这是一个获取本地MAC地址小程序,实用性比较强,大家快来下载

        基于MAC地址的Radius认证在Linux下的实现.pdf

        Radius认证服务器是计算机网络安全中的一种非常重要的技术,基于MAC地址的Radius认证可以提供一种安全的客户认证方式,防止未经授权的访问和攻击。 Radius认证服务器可以使用开源软件来实现,并可以使用PHP语言实现...

        CentOS 6.5 配置IP地址.DNS的三种方法

        CentOS 6.5 配置 IP 地址和 DNS 的三种方法 在 CentOS 6.5 系统中,配置 IP 地址和 DNS 是一个必不可少的步骤。这里我们将介绍三种配置 IP 地址和 DNS 的方法。 方法一:自动获取 IP 地址 在虚拟机中,我们可以...

        MAC地址查询扫描器 V4.2

        MAC地址查询扫描器采用了多种扫描技术,能够快速的扫描网络中设备,通过网络辅助进行设备识别 1.支持局域网内以及跨网段扫描 2.多种MAC地址扫描技术智能介入扫描 3.五级变速,适应不同的系统资源和网络带宽 4.支持多...

        node-macaddress:获取主机网络接口的MAC地址(硬件地址)

        该库允许发现每个网络接口的MAC地址,如果您只需要一个用于标识主机系统的MAC地址,则可以选择适当的接口(请参见下面的API + Examples )。 关于此库的一个常见误解是它报告访问某种后端的客户端的mac地址。 它...

        如何查看自己手机的MAC地址.doc

        此外,在查看 MAC 地址时,需要确保手机已经连接到网络,以便能够获取正确的 MAC 地址信息。 在实际应用中,MAC 地址常用于网络设备的身份验证和网络管理。例如,在 Wi-Fi 网络中,路由器可以根据 MAC 地址来过滤...

        网络嗅探器在Linux系统中的实现.pdf

        混杂模式下,以太网卡可以接收到所有网络中的数据包,而不仅仅是发送到自己的MAC地址的数据包。Sniffer 就是利用混杂模式来收集网络中的数据的。 知识点二:Sniffer 的基本原理 Sniffer 是一种网络嗅探器,可以...

        VC++ 获取网络配置及网卡MAC和IP地址

        内容索引:VC/C++源码,系统相关,网卡,MAC VC++ 获取网络配置及网卡MAC和IP地址,同时获得主机名、独立工作域、绑定的Ip地址、默认网关以及DNS等。

        linux中mac地址绑定方法

        一台linux服务器受到ARP攻击,在使用... 您可能感兴趣的文章:php获取网卡的MAC地址支持WIN/LINUX系统详解Linux系统中网卡MAC地址克隆方法linux下修改MAC地址问题解决方法linux修改mac地址方法分享linux手动、自动更改网

        JAVA得到网卡物理地址(windows和Linux)

        在上面的代码中,我们首先使用 System.getProperty("os.name") 方法来获取当前操作系统的名称,然后根据操作系统的名称来选择不同的获取网卡物理地址的方法。在 Windows 操作系统中,我们使用 ipconfig 命令来获取...

        探究银行系统计算机网络安全技术策略.pdf

        (二)MAC与DHCP攻击:攻击者可以使用MAC地址来攻击银行系统的网络设备。为了防止这种攻击,可以限制MAC地址在客户端接口位置,并且使用DHCP Snooping功能来绑定MAC与IP地址。 (三)ARP攻击:攻击者可以使用ARP...

        nbtscan局域网MAC地址查询工具

        一款LAN下产看所欲计算机IP对应的MAC地址小工具,CMD模式下使用.WIN2000-WIN8系统下适用。(2分) 可执行 nbtscan -r 192.168.1.0/24 解压包里的cygwin1.dll要和nbtscan.exe放在一起

        计算机网络系统设计项目方案.doc

        其中Cisco公司的千兆网络产品功能非常全面,可以解决用户在网络方面需要的可靠 性,线路冗余和平衡负载,故障迅速恢复连接,以及MAC地址在交换机上的端口限制,解 决网络接入的安全性问题。Cisco的GEC(Gigabit ...

      Global site tag (gtag.js) - Google Analytics