NetBIOS编程获取本机MAC地址及一个小坑

0x00:NetBIOS网络编程

参考资料:NetBIOS及其协议的应用

0x01:获取本机MAC地址

《Windows 网络编程》(第二版)书中 p175 的源代码如下(有清晰注释):

// GetMacAddress.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include 
#include 
#include 

#pragma comment(lib, "netapi32.lib")

typedef struct _ASTAT_
{
    ADAPTER_STATUS adapt;
    NAME_BUFFER NameBuff [30];
}ASTAT, * PASTAT;

ASTAT Adapter;

int _tmain(int argc, _TCHAR* argv[])
{
	NCB ncb;					// NCB结构体,用于设置执行的NetBIOS命令和参数
    UCHAR uRetCode;							// 执行Netbios()函数的返回值
    memset( &ncb, 0, sizeof(ncb) );		// 初始化ncb结构体
    ncb.ncb_command = NCBRESET;		// 设置执行NCBRESET
    ncb.ncb_lana_num = 0;					// 设置LANA编号
	// 调用Netbios()函数,执行NCBRESET命令
    uRetCode = Netbios( &ncb );
	// 输出执行NCBRESET命令的结果
    printf( "The NCBRESET return code is: 0x%x \n", uRetCode );
	
    memset( &ncb, 0, sizeof(ncb) );		// 初始化ncb
    ncb.ncb_command = NCBASTAT;		// 执行NCBASTAT命令
    ncb.ncb_lana_num = 0;					// 设置LANA编号
	// 设置执行NCBASTAT命令的参数,将获取到的网络适配器数据保存到Adapter结构体中
    memcpy( &ncb.ncb_callname, "*               ", 16 );
    ncb.ncb_buffer = (UCHAR*) &Adapter;
    ncb.ncb_length = sizeof(Adapter);
	// 调用Netbios()函数,执行NCBASTAT命令
    uRetCode = Netbios( &ncb );
    printf( "The NCBASTAT return code is: 0x%x \n", uRetCode );
    if ( uRetCode == 0 )
    {
		// 输出MAC地址
        printf( "The Ethernet Number is: %02x-%02x-%02x-%02x-%02x-%02x\n",
                Adapter.adapt.adapter_address[0],
                Adapter.adapt.adapter_address[1],
                Adapter.adapt.adapter_address[2],
                Adapter.adapt.adapter_address[3],
                Adapter.adapt.adapter_address[4],
                Adapter.adapt.adapter_address[5] );
    }
	system("pause");
	return 0;
}

执行过后的结果如下
NetBIOS编程获取本机MAC地址及一个小坑_第1张图片
显然没有成功获取到MAC地址,再看打印的内容,本来 Netbios() 函数执行过后的返回值应该是0x00,但是结果返回的是0x23,于是在VS中右键Netbios函数 -> 转到定义,上下翻了一下,发现,找到了NETBIOS返回值及含义,如下图
NetBIOS编程获取本机MAC地址及一个小坑_第2张图片
具体所有NETBIOS返回值及含义如下

00h : 成功地完成,成功返回
01h : 无效的缓冲区
03h : 无效的命令
05h : 命令超时
06h : 不完整地接收消息
07h : 本地No-Ack命令失败
08h : 无效的本地会话
09h : 没有可使用的资源
0Ah : 会话已关闭
0Bh : 命令已撤消
0Dh : 本地NetBIOS命名表中名字重复
0Eh : NetBIOS命名表满
0Fh : 名字具有活动会话,现被撤消登记
11h : NetBIOS 本地会话表满了
12h : 没有挂起的Listen 命令,所有拒绝断开会话
13h : 非法名字编号
14h : 不能找到被调用名字或无回答
15h : 找不到命令,或不能把*号或00h指定ncb_name的首字节,或名字已被撤消而不能再使用
17h : 名字已被删除18h : 会话非正常结束
19h : 检测到名字冲突
1Ah : 不兼容的远程设备
21h : 接口忙
22h : 挂起的命令太多
23h : 在ncb_lana_num域中无效的编号
24h : 产生取消时,命令已完成
25h : 字节组名命令指定了保留名字
26h : 命令不能被撤消
30h : 被另一个进程定义了名字
34h : NetBIOS环境未被定义
35h : 所用的操作系统资源用尽
36h : 超出最大应用个数
37h : NetBIOS无可以使用的SAP
38h : 无可以使用的请求资源
40h : 系统错误
41h : 检测到远程适配器的热载波
42h : 检测到本地适配器的热载波
43h : 未检测到载波
4Eh : 状态位12、14、或15被置位的时间超过 1 min
4Fh : 状态位8--11中的一个或多个被置位
50h-F6h: 适配器发生故障
F7h : 隐式DIR-INITIALIZE错误
F8h : 隐式DIR-OPEN-ADAPTER 错误
F9h : IBM LAN支持程序内部错误
Fah : 适配器检查
FBh : NetBIOS 程序未被装入PC
FCh : DIR-OPEN-ADAPTER 或 DIR-OPEN-SAP失败
FDh : 不期望关闭适配器
FFh : 命令挂起状态

0x02:脱坑

所以具体原因是出现了在ncb_lana_num域中无效的编号这种错误,即程序中ncb.ncb_lana_num = 0; // 设置LANA编号这句赋值是有问题的,我们需要自己获取本机的网卡信息,如有多少个网卡,每个网卡的编号等,具体代码如下(原来的代码并没有删除,而是注释掉了,方便对比)

// GetMacAddress.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include 
#include 
#include 
#include 

#pragma comment(lib, "netapi32.lib")

typedef struct _ASTAT_
{
	ADAPTER_STATUS adapt;
	NAME_BUFFER NameBuff[30];
}ASTAT, *PASTAT;

ASTAT Adapter;

int _tmain(int argc, _TCHAR* argv[])
{
	NCB ncb;											// NCB结构体,用于设置执行的NetBIOS命令和参数
	
	LANA_ENUM lana_enum;    //包含当前LAN适配器的数量
	memset(&lana_enum, 0, sizeof(lana_enum));

	UCHAR uRetCode;							// 执行Netbios()函数的返回值
	memset(&ncb, 0, sizeof(ncb));		// 初始化ncb结构体
	
	ncb.ncb_command = NCBENUM; //统计系统中网卡的数量 指定指令为NCBENUM,用于获取网卡的数量及编号  向网卡发送NCBENUM命令,以获取当前机器的网卡信息,如有多少个网卡,每个网卡的编号(MAC地址)
	ncb.ncb_buffer = (unsigned char *)&lana_enum;
	ncb.ncb_length = sizeof(LANA_ENUM);
	uRetCode = Netbios(&ncb);
	if (uRetCode != NRC_GOODRET)
		exit(-1);

	printf("MAC :\n");
	//二、NCBREST:重置LAN适配器
	for (int lana = 0; lana<lana_enum.length; lana++) 
	{
		ncb.ncb_command = NCBRESET; //初始化逻辑网卡命令 
		ncb.ncb_lana_num = lana_enum.lana[lana]; 
		uRetCode = Netbios(&ncb); 
		memset(&ncb, 0, sizeof(ncb));		// 初始化ncb
		ncb.ncb_command = NCBASTAT;		// 执行NCBASTAT命令
		ncb.ncb_lana_num = lana_enum.lana[lana];	// 设置LANA编号
												// 设置执行NCBASTAT命令的参数,将获取到的网络适配器数据保存到Adapter结构体中
		strcpy((char*)ncb.ncb_callname, "*");
		//memcpy(&ncb.ncb_callname, "*               ", 16);
		ncb.ncb_buffer = (UCHAR*)&Adapter;
		ncb.ncb_length = sizeof(Adapter);
		// 调用Netbios()函数,执行NCBASTAT命令
		uRetCode = Netbios(&ncb);

		//printf("The NCBASTAT return code is: 0x%x \n", uRetCode);
		if (uRetCode == 0)
		{
			// 输出MAC地址
			printf(" %02x-%02x-%02x-%02x-%02x-%02x\n",
				Adapter.adapt.adapter_address[0],
				Adapter.adapt.adapter_address[1],
				Adapter.adapt.adapter_address[2],
				Adapter.adapt.adapter_address[3],
				Adapter.adapt.adapter_address[4],
				Adapter.adapt.adapter_address[5]);
		}
	}
	system("pause");
	return 0;
}

最终结果如下:
NetBIOS编程获取本机MAC地址及一个小坑_第3张图片
当然,你也可以在代码中加入自己想获取的信息,如打印网卡数量,打印 Netbios() 返回值等。

最后贴两篇我也不知道有没有用的博客:

https://blog.csdn.net/pbl18392021230/article/details/72673417?utm_source=blogxgwz1
https://blog.csdn.net/qq_37711989/article/details/80877905

你可能感兴趣的:(网络编程)