`
title: 通过注册表以及文件操作的方式获取当前正在实际使用的物理网卡MAC地址
author: Love02xp
date: 2019-06-18 10:13:23
category: [编程代码]
tags: [代码,VC6]
获取MAC地址的方法很多,网上流传的大多是:
1.通过GetAdaptersInfo获取。
2.通过ipconfig/all命令获取,然后通过管道方式获取输出结果,并对结果进行分析处理。
3.使用Windows的SNMP(简单网络管理协议)扩展来取得MAC地址,据说是只有安装了该协议的才能获取,本人没有测试。
4.使用Microsoft的Netbios API。 这是一套通过Winsock提供底层网络支持的命令。使用Netbios的最大缺点是您必须在系统中安装了Netbios服务。
5.使用COM GUID API。这种方法比较不靠谱,有时获取的是随机的十六进制数。
<!--more-->
参见:http://biancheng.dnbcw.info/vc/206196.html
通过比较以上方法可以发现,还是第一种方法比较简单和稳定一些。
但是现在的虚拟网卡特别多,尤其是安装了虚拟机等软件后。获取真实的网卡MAC地址就比较困难了,
有网友提出:
区分物理网卡和虚拟网卡:pAdapter->Description中包含"PCI"是:物理网卡。(试了3台机器可以)
区分无线网卡和有线网卡:pAdapter->Type为71的是:无线网卡。(试了2个无线网卡也可以)
经过本人的实践测试,以上说法均不对。
今天在分析逆向一个盗号木马时发现它使用了一种更为简单有效的方法:
通过遍历
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\NetworkCards
的子键,我这里是HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\NetworkCards\8
可见我的网卡列表很多,8号才是我当前使用物理网卡。
Description = 基于 Marvell Yukon 88E8057 PCI-E 的通用千兆以太网控制器
ServiceName = {0E2FF64C-9EA6-43B0-A9D4-673FAED6048C}
然后打开文件
\\.\{0E2FF64C-9EA6-43B0-A9D4-673FAED6048C}
通过DeviceIoControl获取MAC地址。具体代码如下(参考:http://bbs.pediy.com/showthread.php?t=109584):
// main.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <Windows.h>
#define OID_802_3_PERMANENT_ADDRESS 0x01010101
//#define OID_802_3_CURRENT_ADDRESS 0x01010102
#define IOCTL_NDIS_QUERY_GLOBAL_STATS 0x00170002
int GetMac(IN OUT TCHAR *szMac,IN int nBuffSize)
{
int nError = -1;
if ( szMac==NULL || nBuffSize<18 ){
return nError;
}
HKEY hKey = NULL;
HKEY hKey2 = NULL;
TCHAR szKey[MAX_PATH],szBuffer[MAX_PATH];
TCHAR szServiceName[MAX_PATH];
TCHAR szFileName[MAX_PATH] = {0};
DWORD dwRet = 0;
DWORD dwType = 0;
DWORD cbData = 0;
DWORD cName = _countof(szBuffer);
if ( RegOpenKey(HKEY_LOCAL_MACHINE,_T("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\NetworkCards\\"),&hKey)!=ERROR_SUCCESS ){
return nError;
}
for ( int i=0; RegEnumKeyEx(hKey,i,szBuffer,&cName,NULL,NULL,NULL,NULL)==ERROR_SUCCESS; ++i, cName = _countof(szBuffer) ){
_tcscpy_s(szKey,MAX_PATH,_T("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\NetworkCards\\"));
_tcscat_s(szKey,MAX_PATH,szBuffer);
if ( RegOpenKey(HKEY_LOCAL_MACHINE,szKey,&hKey2)!=ERROR_SUCCESS ){
continue;
}
dwType = REG_SZ;
cbData = MAX_PATH*sizeof(TCHAR);
if ( RegQueryValueEx(hKey2,_T("ServiceName"),NULL,&dwType,(LPBYTE)szServiceName,&cbData)==ERROR_SUCCESS ){
//RegQueryValueEx(hKey2,_T("Description"),NULL,&dwType,(LPBYTE)szDescription,&cbData)
//读取成功后关闭句柄
RegCloseKey(hKey2);
_tcscpy_s(szFileName,MAX_PATH,_T("\\\\.\\"));
_tcscat_s(szFileName,MAX_PATH,szServiceName);
HANDLE hFile = CreateFile(szFileName,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,0,NULL);
if ( hFile!=INVALID_HANDLE_VALUE ){
DWORD dwInBuff = OID_802_3_PERMANENT_ADDRESS;
BYTE outBuff[MAX_PATH];
dwRet = DeviceIoControl(hFile,IOCTL_NDIS_QUERY_GLOBAL_STATS,&dwInBuff,sizeof(dwInBuff),outBuff,sizeof(outBuff),&cbData,NULL);
//无论成功失败关闭文件句柄
CloseHandle(hFile);
hFile = INVALID_HANDLE_VALUE;
if ( dwRet ){
_stprintf_s(szMac,nBuffSize/sizeof(TCHAR),_T("%02X:%02X:%02X:%02X:%02X:%02X"),outBuff[0],outBuff[1],outBuff[2],outBuff[3],outBuff[4],outBuff[5]);
nError = 0;
break;
}
}
}else{
//读取失败关闭句柄
RegCloseKey(hKey2);
}
}//end for
if ( hKey!=NULL ){
RegCloseKey(hKey);
}
return nError;
}
int _tmain(int argc, _TCHAR* argv[])
{
TCHAR szMac[MAX_PATH] = {0};
if ( GetMac(szMac,MAX_PATH)==0 ){
_tprintf(_T("MAC: %s\n"),szMac);
}else{
_tprintf(_T("get MAC failed\n"));
}
return 0;
}
使用这种方法比上述5种方法都强大,而且获取的信息更准确。
后来使用MAC地址修改器把MAC地址修改后(需要重启后生效,机器仍然可以正常上网),使用cmd的ipconfig/all显示的MAC地址和上述方法对比:
发现cmd的ipconfig/all获取的是修改后的值,而上述方法获取的仍是原始值,真强大!
后面再贴一种常规方法的改进版本:获取当前正在使用的网卡MAC,此法经过测试用MAC地址修改工具修改后获取的就是修改后的值,原理同cmd的ipconfig/all。
// test.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <Windows.h>
#include <IPHlpApi.h>
#pragma comment(lib,"Iphlpapi.lib")
#pragma comment(lib,"Ws2_32.lib")
/*
根据IP地址获取网卡适配器设备名称
OUT AdapterName:适配器名称缓冲区 IN IP:IP地址(网络字节序)
返回值:TRUE 成功获取
*/
BOOL GetAdapterByIp(PCHAR AdapterName, ULONG IP)
{
ULONG ulAdapterInfoSize = sizeof(IP_ADAPTER_INFO);
IP_ADAPTER_INFO *pAdapterInfo = (IP_ADAPTER_INFO*)new char[ulAdapterInfoSize];
IP_ADAPTER_INFO *pAdapterInfoEnum = NULL;
if( GetAdaptersInfo(pAdapterInfo, &ulAdapterInfoSize) == ERROR_BUFFER_OVERFLOW ){
// 缓冲区不够大
delete[] pAdapterInfo;
pAdapterInfo = (IP_ADAPTER_INFO*)new char[ulAdapterInfoSize];
}
pAdapterInfoEnum = pAdapterInfo;
if( GetAdaptersInfo(pAdapterInfoEnum, &ulAdapterInfoSize) == ERROR_SUCCESS ){
do{ //遍历所有适配器
if( pAdapterInfoEnum->Type == MIB_IF_TYPE_ETHERNET ){
// 判断是否为以太网接口
//pAdapterInfoEnum->Description 是适配器描述
//pAdapterInfoEnum->AdapterName 是适配器名称
//pAdapterInfoEnum->Address 是Mac地址
if ( inet_addr(pAdapterInfoEnum->IpAddressList.IpAddress.String) == IP ){
printf( "%s IP: %s GATEIP: %s\n ", pAdapterInfoEnum->AdapterName,
pAdapterInfoEnum->IpAddressList.IpAddress.String, pAdapterInfoEnum->GatewayList.IpAddress.String );
printf( "MAC: %02X%02X%02X%02X%02X%02X\n", pAdapterInfoEnum->Address[0],pAdapterInfoEnum->Address[1],
pAdapterInfoEnum->Address[2],pAdapterInfoEnum->Address[3],pAdapterInfoEnum->Address[4],pAdapterInfoEnum->Address[5]);
break;
}
}
pAdapterInfoEnum = pAdapterInfoEnum->Next;
}while(pAdapterInfoEnum);
}
delete []pAdapterInfo;
return FALSE;
}
void test()
{
char szName[MAX_PATH] = {0};
int err = gethostname(szName, MAX_PATH);
if( err==SOCKET_ERROR ) {
printf("gethostname fail %08x\n", WSAGetLastError());
return;
}
hostent *pHostent = gethostbyname(szName);
ULONG ulAdapterIp = *(ULONG*)pHostent->h_addr_list[0];
GetAdapterByIp(NULL,ulAdapterIp);
}
int _tmain(int argc, _TCHAR* argv[])
{
WSADATA wsaData;
WSAStartup(MAKEWORD(2,2),&wsaData);
test();
system("pause");
return 0;
}