取得系統中網卡MAC位址的三種方法 |
|
conundrum
尊榮會員 發表:893 回覆:1272 積分:643 註冊:2004-01-06 發送簡訊給我 |
取得系統中網卡MAC位址的三種方法
http://www.legalsoft.com.cn/Articles/ArticleBrowse.asp?idArticle=357
做好的程式師一如做人。多看多想或許他山之石可以攻玉,但永遠不要成為代碼的奴隸。 CKER
原著:Borland
Translated by CKER 第一種方法使用Microsoft的Netbios API。 這是一套通過Winsock提供底層網路支援的命令。使用Netbios的最大缺點是您必須在系統中安裝了Netbios服務(如果您在windows網路中啟用了檔共用的話,這就不是問題了)。除此此外,這種方法又快又準確。 Netbios API只包括了一個函數,就叫做Netbios。這個函數使用網路控制塊(network control block)結構作為參數,這個結構告訴函數要做什麼。結構的定義如下:
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的定義如下:
命令描述:
NCBENUM Windows NT/2000: 列舉系統中網卡的數量。使用此命令後,ncb_buffer成員指向由LANA_ENUM結構填充的緩衝區。
NCBENUM 不是標準的 NetBIOS 3.0 命令。 NCBRESET 重置網卡。網卡在接受新的NCB命令之前必須重置。
NCBASTAT 接受本地或遠端介面卡的狀態。使用此命令後,ncb_buffer成員指向由ADAPTER_STATUS結構填充的緩衝區,隨後是NAME_BUFFER結構的陣列。 下面就是取得您系統MAC位址的步驟:
1》列舉所有的介面卡。
2》重置每塊卡以取得它的正確資訊。
3》查詢介面卡,取得MAC位址並生成標準的冒號分隔格式。 下面就是實例根源程式。
netbios.cpp #include
|
dllee
站務副站長 發表:321 回覆:2519 積分:1711 註冊:2002-04-15 發送簡訊給我 |
請 conundrum 大大在貼 BCB 原始碼時,記得使用 [ code ]
原始碼
[ /code ] 的方式來貼,否則 include 的東西都不見了... 謝謝您轉貼的資料 < href="http://www.ViewMove.com" target="blank">視動科技 VMASK - ViewMove Automation Software Kernel 發表人 - dllee 於 2004/07/27 13:24:17
------
http://www.ViewMove.com |
conundrum
尊榮會員 發表:893 回覆:1272 積分:643 註冊:2004-01-06 發送簡訊給我 |
|
lovejingtao
一般會員 發表:10 回覆:33 積分:13 註冊:2003-04-16 發送簡訊給我 |
嗯.我补上一个方法,是利用ARP数据包.
=====================================================================
unit Unit_GetMacByIp; interface
uses
Windows, SysUtils,winsock; function My_GetMacByIp(const ipaddress: WideString):String;
implementation
type
ipaddr = longint;
pulong = ^u_long; function SendARP(DestIP: ipaddr; SrcIP: ipaddr; pMacAddr: pulong;
PhyAddrLen:pulong): DWORD; stdcall; external 'IPHLPAPI.DLL'; procedure GetMacByIp(const ipaddress: WideString; var macaddress: OleVariant);
var
destip: integer;
pmacaddr: pulong;
addrlen: u_long;
macaddr: array[1..6] of byte;
p: pbyte;
s: string;
i: integer;
ipstr: string; begin
IPstr := IPaddress; DestIP := inet_addr(pchar(IPstr)); //目标机器的IP地址
pMacAddr := pulong(@MacAddr);
AddrLen := sizeof(MacAddr);
if SendARP(DestIP, 0, pMacAddr, @AddrLen) = 0 then
begin
s := '';
p := pbyte(pMacAddr);
if ((p <> nil) and (AddrLen > 0)) then
begin
for i := 1 to AddrLen do
begin
s := s IntToHex(p^, 2) '.';
p := Pointer(Integer(p) SizeOf(Byte));
end;
SetLength(s, Length(s) - 1);
end;
end;
macaddress := s;
end; function My_GetMacByIp(const ipaddress: WideString):String;
var
S:OleVariant;
begin
GetMacByIp(ipaddress,s);
Result:=s;
end;
end. =====================================================================
不过ARP方式好像无法垮网段.所以,还可以利用UDP发送数据包.一些扫描网络MAC地址的软件就是用UDP协议的.下面这个忘记谁写的了. {
设置IP后调用GetMac方法,取结果代码放在OnReceive事件里。
}
unit GetMac; interface uses
Windows, Messages, SysUtils,Forms, Classes, WinSock, Dialogs;
const
WM_SOCK = WM_USER $0001; //自定义windows消息
UDPPort = 6767; //设定本端UDP端口号
NBTPort = 137; //设定对端UDP端口号 var
MyByte: array [0..3]of byte;
WAIT_ACK_EVENT: Thandle;
MySock: TSocket;
MyAddr: TSockAddr;
FSockAddrIn : TSockAddrIn; type
TGetMac = class(TComponent)
private
{ Private declarations }
FIP:string; //输入的欲取得MAC地址的机子的IP地址
FWorkGroup:string; //返回的工作组名称
FHostName:string; //返回的主机名
FUserName:string; //返回的用户名
FMacAddress:string; //返回的网卡Mac地址
FHandle: HWnd; //非可视构件消息处理使用
FOnReceive: TNotifyEvent;//接收到返回结果触发的事件 Binded: Boolean; //对输入的IP地址进行合法性检查并赋给FIP变量
procedure SetFIP(Value: string);
//判断IP地址是否合法
Function IsLegalIP(IP:string):Boolean;
//将IP地址分成四部份
procedure GetAddrByte(IP:string;var B:array of byte); protected
{ Protected declarations }
procedure ReceiveReturn; dynamic;
//利用消息实时获知UDP消息
procedure ReadData(var Msg: TMessage);//message WM_SOCK;
//分析返回的消息内容
procedure RecvNbMsg(buffer: Array of byte;len:integer;IP:string);
public
{ Public declarations }
procedure GetMac; //先对IP属性赋值后调用此方法取得相关资料
constructor Create(AOwner: TComponent); override;
destructor Destroy; override; published
{ Published declarations }
property IP: string read FIP write SetFIP;
property WorkGroup: string read FWorkGroup;// write FWorkGroup;
property UserName: string read FUserName;// write FUserName;
property HostName: string read FHostName;// write FHostName;
property MacAddress: string read FMacAddress;// write FMacAddress;
property OnReceive: TNotifyEvent read FOnReceive write FOnReceive;
end; procedure Register; implementation //接收到返回的数据后内部处理完毕执行用户定义的代码
procedure TGetMac.ReceiveReturn;
begin
if Assigned(FOnReceive) then FOnReceive(Self);
end; //检测输入的IP地址是否合法并赋值给FIP变量
procedure TGetMac.SetFIP(Value: string);
begin
if (not IsLegalIP(Value)) then
begin
ShowMessage('非法IP地址!');
FIP:='127.0.0.1';
exit;
end
else
begin
GetAddrByte(Value,MyByte);
FIP:=format('%d.%d.%d.%d',[MyByte[0],MyByte[1],MyByte[2],MyByte[3]]);
end; end; //判断IP地址是否合法
Function TGetMac.IsLegalIP(IP:string):Boolean;
begin if inet_addr(pchar(IP))=INADDR_NONE then
begin
Result:=False;
exit;
end
else result:=true; end; //将IP地址分成四部份
procedure TGetMac.GetAddrByte(IP:string;var B:array of byte);
var i,j:integer;
sTemp:string;
begin sTemp:='';
j:=0;
IP:=IP '.';
for i:=1 to length(IP)do
begin
if IP[i]<>'.' then sTemp:=sTemp IP[i]
else
begin
B[j]:=byte(strtoint(sTemp));
inc(j);
sTemp:='';
end;
end; end; //开始取网卡地址
procedure TGetMac.GetMac;
//欲发送的数据包内容
const NbtstatPacket:array[0..49]of byte
=($0,$0,$0,$0,$0,$1,
$0,$0,$0,$0,$0,$0,$20,$43,$4b,
$41,$41,$41,$41,$41,$41,$41,$41,
$41,$41,$41,$41,$41,$41,$41,$41,
$41,$41,$41,$41,$41,$41,$41,$41,
$41,$41,$41,$41,$41,$41,$0,$0,$21,$0,$1);
var
len: integer;
TempWSAData: TWSAData;
begin
if not Binded then begin
// 初始化SOCKET,0成功,使用WinSock1.1版本$0001是1.0版本$0002是2.0版本
if WSAStartup($0101, TempWSAData)<>ERROR_SUCCESS then
ShowMessage('启动错误'); MySock := Socket(AF_INET, SOCK_DGRAM, 0);
if (MySock = INVALID_SOCKET) then //Socket创建失败
begin
ShowMessage(inttostr(WSAGetLastError()) ' Socket创建失败!');
CloseSocket(MySock);
end;
//本机SockAddr绑定
MyAddr.sin_family := AF_INET;
MyAddr.sin_addr.S_addr := INADDR_ANY;
MyAddr.sin_port := htons(UDPPORT);
if Bind(MySock, MyAddr, sizeof(MyAddr)) <> 0 then
begin
ShowMessage('绑定错误!');
Binded:=False;
end
else Binded:=True; WSAAsyncSelect(MySock, FHandle, WM_SOCK, FD_READ);
WAIT_ACK_EVENT:=CreateEvent(nil,true,false,pchar('WAIT_ACK'));
//ResetEvent(WAIT_ACK_EVENT);}
end; //向对方主机UDP指定的端口发送数据包
FSockAddrIn.SIn_Addr.S_addr := inet_addr(pchar(FIP));
FSockAddrIn.SIn_Family := AF_INET;
FSockAddrIn.SIn_Port := htons(NBTPORT);
len := SendTo(MySock, NbtstatPacket[0],50, 0, FSockAddrIn, sizeof(FSockAddrIn));
//if (WSAGetLastError() <> WSAEWOULDBLOCK) and (WSAGetLastError() <> 0) then showmessage(inttostr(WSAGetLastError()));
if len = SOCKET_ERROR then ShowMessage('SOCKET_ERROR,发送失败!');
if len <> 50 then ShowMessage('数据没有全部发送!'); // WaitForSingleObject(WAIT_ACK_EVENT,WaitTime);
ResetEvent(WAIT_ACK_EVENT); end; //接收返回的消息数据
procedure TGetMac.ReadData(var Msg:TMessage);
var
buffer: Array [1..500] of byte;
flen,len: integer;
Event: word;
IP:string;
begin
if Msg.msg<>WM_SOCK then exit;
flen:=sizeof(FSockAddrIn);
FSockAddrIn.SIn_Family := AF_INET;
FSockAddrIn.SIn_Port := htons(NBTPORT);
Event := WSAGetSelectEvent(Msg.LParam);
if Event = FD_READ then
begin
len := recvfrom(MySock, buffer, sizeof(buffer), 0, FSockAddrIn, flen);
if len> 0 then
begin //FSockAddrIn.sin_addr.S_un_b.s_b1
with FSockAddrIn.sin_addr.S_un_b
do IP:=format('%d.%d.%d.%d',[ord(s_b1),ord(s_b2),ord(s_b3),ord(s_b4)]); RecvNbMsg(buffer,len,IP); end;
SetEvent(WAIT_ACK_EVENT); end;
//触发事件,执行用户指定的代码
ReceiveReturn;
end; //分析返回的消息
procedure TGetMac.RecvNbMsg(buffer: Array of byte;len:integer;IP:string);
var
TempStr:string;
i,j,pos,name_num: integer;
begin name_num:=0;
for i:=1 to len do
begin
if((buffer[i]=$21)and(buffer[i 1]=$00)and(buffer[i 2]=$01))
then
begin
name_num:=buffer[i 9];
break;
end;
end; if name_num=0 then exit;
pos:=i 10; TempStr:='';
for i:=pos to (pos 18*name_num-1) do
begin
if (((i-pos)mod 18) =0) then
begin
for j:=0 to 14 do
begin
if trim(char(buffer[i j]))='' then buffer[i j]:=ord(' ');
TempStr:=TempStr char(buffer[i j]);
end; if (buffer[i 16] and $80)=$80 then
begin
if buffer[i 15]=$0 then FWorkGroup:=TempStr;
end
else
begin
if buffer[i 15]=$20 then FHostName:=TempStr
else
if buffer[i 15]=$3 then FUserName:=TempStr; end;
TempStr:='';
end;
end;
//取得网卡地址
for i:=0 to 5 do
begin
TempStr:=TempStr format('%.2x.',[buffer[i pos 18*name_num]]);
end;
delete(TempStr,length(TempStr),1);
FMacAddress:=TempStr; end; //构件创建
constructor TGetMac.Create;
begin
inherited Create(AOwner); FHandle := AllocateHWnd(ReadData); //初始化属性值
FIP:='127.0.0.1';
FUserName:='';
FHostName:='';
FWorkGroup:='';
FMacAddress:='';
Binded:=False;
end; //构件消毁
destructor TGetMac.Destroy;
begin
CloseSocket(MySock);
WSACleanup();
DeallocateHWnd(FHandle);
inherited Destroy;
end; //登记控件
procedure Register;
begin
RegisterComponents('FastNet', [TGetMac]);
end; end.
=====================================================================
当然,ARP数据包用处也是很大的.例如,在局部网内,你可以利用ARP包欺骗其它电脑,让它无法上网.或者SNIFF----------对于交换机来说,传统的SNIFF已经不起作用了,而利用ARP欺骗达到SNIFF的目的是比较高效的.当然前提是你绑定的BUF要正确. 冷静的思考问题
充满激情的工作
|
tokezmax
一般會員 發表:1 回覆:2 積分:0 註冊:2004-09-22 發送簡訊給我 |
|
暗黑破壞神
版主 發表:9 回覆:2301 積分:1627 註冊:2004-10-04 發送簡訊給我 |
|
conundrum
尊榮會員 發表:893 回覆:1272 積分:643 註冊:2004-01-06 發送簡訊給我 |
本站聲明 |
1. 本論壇為無營利行為之開放平台,所有文章都是由網友自行張貼,如牽涉到法律糾紛一切與本站無關。 2. 假如網友發表之內容涉及侵權,而損及您的利益,請立即通知版主刪除。 3. 請勿批評中華民國元首及政府或批評各政黨,是藍是綠本站無權干涉,但這裡不是政治性論壇! |