[譯]獲取系統網絡MAC地址的三種方法

[原文]
[未經許可,不得轉載]


[風焱注:這是鄙人第一篇除英語作業以外的翻譯,目的是練手,所以選了一篇極其簡單的湊數。]


獲取系統網絡MAC地址的三種方法--Borland開發人員支持團隊

摘要: 本文描述了通過編程獲取系統網卡(NIC,Network interface Card)的MAC地址的三種不同方法。

type="text/css" rel="stylesheet" src="http://community.borland.com/styles/article.css" />

三種方法得到系統MAC地址

曾經在新聞組和網頁上不停的搜索,試圖找到一種簡單的方法來得到系統中的MAC地址。[譯註:原文將MAC的全稱錯寫成網絡適配器(Network Interface Card)。MAC是介質訪問控制(Media Access Control)的簡稱,它是固化在網卡EEPROM中的物理地址。]你也許猜想網上有很多可用的例子,因爲非常多的人(尤其在新聞組中)需要這個問題答案。但事實並非這樣。這些例子是我嘿咻嘿咻搜索的成果和一些我自己的實踐。 注意:這三個例子中沒有一個是通過粘貼ipconfig.exe /all的輸出來實現。


目的
篇文章的目的是給出一些得知你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






using namespace std;


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 = ;
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);

if (Netbios(&Ncb) == 0)
{
char acMAC[18];
sprintf(acMAC, ,
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 = ;
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 << << int (AdapterList.lana[i]) <<
<< mac_addr << endl;
}
else
{
cerr << << endl;
cerr << << 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




using namespace std;

int main()
{
cout << ;


GUID uuid;
CoCreateGuid(&uuid);

char mac_addr[18];
sprintf(mac_addr,,
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




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;


m_hInst = LoadLibrary();
if (m_hInst < (HINSTANCE) HINSTANCE_ERROR)
{
m_hInst = NULL;
return;
}
m_Init =
(pSnmpExtensionInit) GetProcAddress(m_hInst, );
m_InitEx =
(pSnmpExtensionInitEx) GetProcAddress(m_hInst,
);
m_Query =
(pSnmpExtensionQuery) GetProcAddress(m_hInst,
);
m_Trap =
(pSnmpExtensionTrap) GetProcAddress(m_hInst, );
m_Init(GetTickCount(), &PollForTrapEvent, &SupportedView);


varBindList.list = varBind;
varBind[0].name = MIB_NULL;
varBind[1].name = MIB_NULL;


varBindList.len = 1;
SNMP_oidcpy(&varBind[0].name, &MIB_ifEntryNum);
ret =
m_Query(ASN_RFC1157_GETNEXTREQUEST, &varBindList, &errorStatus,
&errorIndex);
printf(,
varBind[0].value.asnValue.number);
varBindList.len = 2;


SNMP_oidcpy(&varBind[0].name, &MIB_ifEntryType);


SNMP_oidcpy(&varBind[1].name, &MIB_ifMACEntAddr);

do
{


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(, j, dtmp);


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(, 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))
{

printf(, j);
continue;
}
sprintf(TempEthernet, ,
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(, j,
TempEthernet);}
}
}
} while (!ret);
getch();

FreeLibrary(m_hInst);

SNMP_FreeVarBind(&varBind[0]);
SNMP_FreeVarBind(&varBind[1]);
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章