原创
编程获取网卡MAC地址
2008-4-7 13:20
7435
4
5
分类:
软件与OS
网卡的MAC地址是一个很好的标志信息,软件注册认证等等往往需要一个唯一的身份信息,这样可以防止
软件的非法使用。编程获得网卡的MAC地址是个老话题。这两天没事就写了个库实现收集系统信息的功能,
MAC地址当然是其中之一,此外,CPU的ID信息,硬盘的序列号等等。准备把这个做成dll,以后万一要用到
这些信息进行软件注册,就省的再写了。
有两种方法可以获得MAC地址(当然用delphi,asp里面的控件和支持库也可以做到,我这里只是指用c/c++来实现)
第一种用到Netbios,我写的代码是可以正常工作,但是出现一个奇怪的现象,在我办公室的机器上,只有一块
网卡,这种方法却返回两个网卡和两个MAC地址(当然这两个地址是完全相同的)。用了winpcap提供的函数
就更加古怪,它读取register下面HKLM\\SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E972-E325-11CE-BFC1-08002BE10318
的所有子键值,这样通常都能读到7-8条网卡信息,但是只有两条是真正安装在我的机器上的网卡信息,而且正好
对应于Netbios方法查询到的那两条重复信息。
代码如下:
/* return 0 successful,others failed*/
SYSINFO_API UCHAR NetbiosEnumAdapter(LANA_ENUM *pLanaEnum)
{
NCB ncb;
UCHAR uRet;
LANA_ENUM tmp;
memset(&ncb,0,sizeof(ncb));
ncb.ncb_command=NCBENUM;
ncb.ncb_buffer=(unsigned char *)&tmp;
ncb.ncb_length=sizeof(tmp);
uRet=Netbios(&ncb);
if(uRet==NRC_GOODRET){
*pLanaEnum=tmp;
return 0;
}
return uRet;
}
该函数枚举所有网卡,得到LANA_ENUM信息后,可以挨个获取它们的MAC地址,获取MAC地址的代码如下:
/* Get the computer's Network Adapter's MAC address
* Argument:
* AdapterNum---the Adapter number ,possible there are more than one adapter
* LanEnum--- LANA_ENUM struct,contains the numbers for the current LAN adapters
* pAdapterStatus---the return MAC address
* return:
* others--failed
* 0--successful, then pAdapterStatus->adapter_address contains the MAC address
*/
SYSINFO_API UCHAR NetbiosGetMacAddress(int AdapterNum, LANA_ENUM LanEnum, PADAPTER_STATUS pAdapterStatus)
{
UCHAR uRet=0;
NCB ncb;
/* First reset the adapter*/
memset(&ncb,0,sizeof(ncb));
ncb.ncb_command=NCBRESET;
ncb.ncb_lana_num =LanEnum.lana[AdapterNum];
uRet=Netbios(&ncb);
if(uRet!=NRC_GOODRET)
return uRet;
/* send NCBASTAT command to get mac address*/
memset(&ncb,0,sizeof(ncb));
ncb.ncb_command=NCBASTAT;
ncb.ncb_lana_num=LanEnum.lana[AdapterNum];
strcpy((char*)ncb.ncb_callname,"* ");
ncb.ncb_buffer=(unsigned char *)&Adapter;
ncb.ncb_length=sizeof(Adapter);
uRet=Netbios(&ncb);
if(uRet!=NRC_GOODRET)
return uRet;
*pAdapterStatus=Adapter.adapter;
return 0;
}
这种方法另一个恶心的缺点是,如果系统没有安装Netbios协议,则完全没用,而目前许多计算机确实没有安装
netbios协议。想了想,写出下面的方法,也没什么新意,网上早就有人讨论了这种方法:通过 发送ioctl给
ndis驱动程序获得MAC地址。
首先,枚举系统所有网卡:
SYSINFO_API int NdisEnumAdapter()
{
HKEY RootKey, SubKey;
TCHAR SubKeyName[64],AdapterName[256];
LONG status;
int i=0,ret=0;
DWORD len;
status=RegOpenKeyEx(HKEY_LOCAL_MACHINE,WINNT_QUERY_ROOT_KEY,0,KEY_READ,&RootKey);
if(status!=ERROR_SUCCESS){
SysInfoDebug("NdisEnumAdapter-Open Root Key failed");
return 0;
}
while(RegEnumKey(RootKey,i,SubKeyName,sizeof(SubKeyName)/2)==ERROR_SUCCESS)
{
i++;
status=RegOpenKeyEx(RootKey,SubKeyName,0,KEY_READ,&SubKey);
if(status!=ERROR_SUCCESS){
SysInfoDebug("NdisEnumAdapter-Open Sub Key Failed");
continue;
}
len=sizeof(AdapterName);
status=RegQueryValueEx(SubKey,TEXT("ServiceName"),NULL,0,(PBYTE)AdapterName,&len);
if(status!=ERROR_SUCCESS){
SysInfoDebug("NdisEnumAdapter-Query ServiceName Failed");
continue;
}
lstrcpy(g_AdapterName[ret].name,TEXT("\\\\.\\"));
lstrcat(g_AdapterName[ret].name,AdapterName);
g_AdapterName[ret].index=ret;
if((++ret)>=20)
break;
}
return ret;
}
然后调用下面函数获得每个网卡的地址:
/*AdaptNum--Adapter number
* MacAddr--return Mac address
*/
SYSINFO_API BOOL NdisGetMacAddress(int AdaptNum, UCHAR *MacAddr)
{
TCHAR SymLink[256];
HANDLE hFile;
ULONG oid=OID_802_3_CURRENT_ADDRESS;
UCHAR data[6];
DWORD ByteRet;
if(AdaptNum>20||AdaptNum<1)
return FALSE;
wsprintf(SymLink,"%s",g_AdapterName[AdaptNum-1].name);
hFile=CreateFile(SymLink,GENERIC_READ|GENERIC_WRITE,0,NULL,OPEN_EXISTING,0,0);
if(hFile!=INVALID_HANDLE_VALUE){
SysInfoDebug("NdisGetMacAddress-CreateFile Success");
if(DeviceIoControl(hFile,IOCTL_NDIS_QUERY_GLOBAL_STATS,&oid,sizeof(oid),data,
sizeof(data),&ByteRet,NULL)==0){
SysInfoDebug("Can't get MAC address");
CloseHandle(hFile);
return FALSE;
}
memcpy(MacAddr,data,sizeof(data));
CloseHandle(hFile);
return TRUE;
}
SysInfoDebug("Open Adapter Failed");
return FALSE;
}
用户178916 2008-10-6 23:45