2008年11月19日星期三

Windows在没有TCP/IP协议的情况下获取网卡MAC地址

Win NT/XP中,获得网卡MAC地址,一般是通过netapi32.dll中的NetBIOS API发送NCBASTAT命令来获取,但这样做的前提是网络连接启用了TCP/IP协议,否则的话就不起作用,而且即使用ipconfig /all命令也看不到任何内容。

但MAC地址是固化在网卡硬件中的一个序列号,即使不安装任何网络协议也应该能读取,最简单的办法是用Windows的wmic命令进行查询:

wmic nic where netconnectionid!=NULL get macaddress


很多人比较关心如何从注册表中获取MAC地址,其实注册表中的MountPoints2项中的确是保存有MAC地址,但很难从其它设备中查找出来,比较好的办法是先从注册表中找到网卡的设备编号,再通过DeviceIoControl查找硬件的MAC地址,以下是Perl Win32的实现:

#!/usr/bin/perl -w

use strict;

use Win32API::File qw(CreateFile DeviceIoControl :FILE_SHARE_ :Misc);
use Win32::TieRegistry;

#use Data::Dump qw(dump);

sub IOCTL_NDIS_QUERY_GLOBAL_STATS () { 0x17 << 16 | 2 };
sub OID_802_3_PERMANENT_ADDRESS () { 0x01010101 };
sub OID_802_3_CURRENT_ADDRESS () { 0x01010102 };

sub NDIS_Query
{
my ($handle, $oid) = @_;

my $nBytes = 0;
my $buf = "\0"x10;

DeviceIoControl($handle,
IOCTL_NDIS_QUERY_GLOBAL_STATS(),
pack("L", $oid), 0,
$buf, length($buf),
$nBytes,
[] );

return join "-", unpack("(a2)*", unpack("H*", $buf) );
}

my $adapters = $Registry->Open("LMachine/SOFTWARE/Microsoft/Windows NT/CurrentVersion/NetworkCards", { Access => "KEY_READ", Delimiter => "/" } );

#print dump($adapters);

foreach(keys %$adapters)
{
my $adapterName = $adapters->{$_}->{ServiceName};
print "Adapter name = $adapterName\n";

my $hMAC = CreateFile("//./$adapterName", 0, FILE_SHARE_READ(), [], OPEN_EXISTING(), 0, []);

for (
[ "permanent" => OID_802_3_PERMANENT_ADDRESS() ],
[ "current " => OID_802_3_CURRENT_ADDRESS() ],
) {
my ($type, $oid) = @$_;

my $mac = NDIS_Query($hMAC, $oid);
print "MAC $type = $mac\n";
}
}

没有评论: