袁哥 < yuange@163.net >
大家经常讨论SNIFFER,觉得还是很多人没有真正理解SNIFFER,所以把我的一点理解写出来大家共享。
先讲讲HUB的工作原理吧。由于以太网等很多网络(常见共享HUB连接的内部网)是基于总线方式,物理上是广播的,就是一个机器发给另一个机器的数据,共享HUB先收到然后把它接收到的数据再发给别的(来的那个口不发了)每一个口,所以在共享HUB下面同一网段的所有机器的网卡都能接收到数据。交换式HUB的内部单片程序能记住每个口的MAC地址,以后就该哪个机器接收就发往哪个口,而不是像共享HUB那样发给所有的口,所以交换HUB下只有该接收数据的机器的网卡能接收到数据,当然广播包还是发往所有口。显然共享HUB的工作模式使得两个机器传输数据的时候别的口也占用了,所以共享HUB决定了同一网段同一时间只能有两个机器进行数据通信,而交换HUB两个机器传输数据的时候别的口没有占用,所以别的口之间也可以同时传输。这就是共享HUB与交换HUB不同的两个地方,共享HUB是同一时间只能一个机器发数据并且所有机器都可以接收,只要不是广播数据交换HUB同一时间可以有对机器进行数据传输并且数据是私有的。
再讲讲网卡的工作原理。网卡收到传输来的数据,网卡内的单片程序先接收数据头的目的MAC地址,根据计算机上的网卡驱动程序设置的接收模式判断该不该接收,认为该接收就接收后产生中断信号通知CPU,认为不该接收就丢掉不管,所以不该接收的数据网卡就截断了,计算机根本就不知道。CPU得到中断信号产生中断,操作系统就根据网卡的驱动程序设置的网卡中断程序地址调用驱动程序接收数据,驱动程序接收数据后放入信号堆栈让操作系统处理。
有了这HUB、网卡的工作原理就可以讲SNIFFER了。首先,要知道要SNIFFER的东西必须是要物理信号你能收到的东西。显然只要通知网卡接收其收到的所有包(一般叫作乱模式),在共享HUB下就能接收到这个网段的所有包,但是交换HUB下就只能是自己的包加上广播包。知道了原理那就好办。要想在交换HUB下接收别人的包,那就要让其发往你的机器所在口。交换HUB记住一个口的MAC是通过接收到来至于那个口的数据后记住其源MAC,就像一个机器的IP与MAC对应的ARP列表,交换HUB维护一个物理口(就是HUB上的网线插口,这而的所有HUB口都是指这)与MAC的表,所以可以欺骗交换HUB的。那样你发一个包设置源MAC是你想接收的机器的MAC,那么交换HUB就把你机器的网线插的物理口与那个MAC对应起来了,以后发给那个MAC的包就发往你的网线插口了,也就是你的网卡可以SNIFFER到了。注意这物理口与MAC的表与机器的ARP表一样是动态刷新的,那机器发包后交换HUB就又记住他的口了,所以实际上是两个在争,这只能应用在只要收听少量包就可以的场合,或者干脆你弄死要冒牌的机器。还有内部网嘛基于IP的通信你可以用ARP欺骗别人机器让其发给你的机器就可以了,如果要想不影响原来两方的通信,可以欺骗两方,让其都发给你的机器你的机器再转发,就是做中间人,这用ARP加上编程很容易实现。还有现在很多设备支持远程管理,有很多交换HUB可以设置一个口监听别的口,不过这就要管理权限了。
拨号用户嘛就是拨号服务器接收到包根据IP分析是哪个拨号用户的包,可能也是通过查找分配拨号用户的IP与拨号电话线的一个列表,然后就发往那个拨号用户的电话线,所以其相当于交换HUB,但这儿是用的IP,交换HUB是用的MAC,所以拨号用户能接收到自己的包。相应的要想接收别的包就得欺骗拨号服务器,当然这不是交换HUB那么好欺骗,因为显然拨号服务器的IP与电话线的列表不是交换HUB那么维护的,可以看能不能破了拨号服务器改其驱动让其所有包都发给你,显然不太可能了。:(
下面是3COM网卡驱动程序,结合此程序讲讲网卡的原理,你可以对照程序理解。可能有的网卡有些不一样,但大致原理一样,所以理解很多东西不能完全照搬。
一般网卡有个网卡地址MAC,这地址网卡厂家得申请,每个厂家得到一段地址,不同厂家不同就像IP地址的分配一样,然后用这段地址分配给其生产的每个网卡一个地址。一般说来网卡厂家保证每个网卡地址不同,实际上网卡地址一般不是放在网卡内部程序里,因为网卡内程序一般都是固化的,相同网卡所有网卡的程序全部一样,还有很多网卡可以配置支持很多方式,所以一般网卡都带一个EEPROM存储器,网卡地址MAC就放这里面。其实网卡接收包有几种方式,接收指定MAC地址的包、广播包、组播包、所有的包等,可以对其编程。通常网卡驱动程序设置的是接收指定MAC地址的包和广播包,如果设置了接收所有的包,就是我们常说的用于SNIFFER的乱模式。网卡内单片接收指定MAC地址的包的MAC也不是内部程序直接通过EEPROM的数据得到,而是驱动程序通过读E2PROM得到MAC,再把这MAC告诉网卡的MAC寄存器,网卡的内部程序通过MAC寄存器得到的,并且以此为标准。所以只要改驱动程序把你想要的MAC告诉MAC寄存器那么就得到你想要的MAC了。
一、关于MAC:
1。for (i = 0; i < 3; i++)
phys_addr[i] = htons(read_eeprom(ioaddr, i));
读EEPROM中的MAC,有差不多的两个地方,可能是ISA、PCI总线网卡的差别,每次读出来的是字(两个字节)。
static ushort read_eeprom(short ioaddr, int index)
{
outw(EEPROM_READ + index, ioaddr + 10);
/* Pause for at least 162 us. for the read to take place. */
udelay (500);
return inw(ioaddr + 12);
}
这是读EEPROM的数据的函数调用,看这很可能有可以写EEPROM数据的可能,就是E2PROM里面的MAC数据可以改写,原来很多网卡有设置程序可以指定其MAC地址的,所以。。。
2。memcpy(dev->dev_addr, phys_addr, sizeof(phys_addr));
EEPROM中读出来的MAC地址拷贝到DEV_ADDR中。
3。for (i = 0; i < 6; i++)
outb(dev->dev_addr[i], ioaddr + i);
MAC写入MAC寄存器中,网卡是以写入MAC寄存器的内容为标准,实际上与网卡EEPROM数据中的MAC无关。还有发包的时候与源MAC无关,就和现在的IP包发包的时候的源IP可以随便填一样,只是接收包的时候网卡通过MAC寄存器内容和接收模式判断该不该接收。当然别人接收包后回复一般都是把收到的包的源MAC当目的MAC,所以你发包的源MAC是假的话可能也收不到回复包。
还有要改MAC只要这儿的DEV_ADDR为你想要的MAC就可以,这儿可以从一个文件读入MAC地址或者WINDOWS那样从注册表里面读取MAC内容,这就要看驱动程序额外提供的接口了。
二、关于接收模式:
enum RxFilter {
RxStation = 1, RxMulticast = 2, RxBroadcast = 4, RxProm = 8 };
是定义的接收模式。
SetRxFilter = 16<<11,
是定义的设置接收模式的命令
outw(SetRxFilter | RxStation | RxBroadcast, ioaddr + EL3_CMD);
是设置接收模式,加上RxMulticast还是RxProm应该就是乱模式了。
3COM的网卡驱动程序:
/* 3c509.c: A 3c509 EtherLink3 ethernet driver for linux. */
/*
Written 1993-1997 by Donald Becker.
Copyright 1994-1997 by Donald Becker.
Copyright 1993 United States Government as represented by the
Director, National Security Agency. This software may be used and
distributed according to the terms of the GNU Public License,
incorporated herein by reference.
This driver is for the 3Com EtherLinkIII series.
The author may be reached as becker@cesdis.gsfc.nasa.gov or
C/O Center of Excellence in Space Data and Information Sciences
Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771
Known limitations:
Because of the way 3c509 ISA detection works it's difficult to predict
a priori which of several ISA-mode cards will be detected first.
This driver does not use predictive interrupt mode, resulting in higher
packet latency but lower overhead. If interrupts are disabled for an
unusually long time it could also result in missed packets, but in
practice this rarely happens.
FIXES:
Alan Cox: Removed the 'Unexpected interrupt' bug.
Michael Meskes: Upgraded to Donald Becker's version 1.07.
Alan Cox: Increased the eeprom delay. Regardless of
what the docs say some people definitely
get problems with lower (but in card spec)
delays
v1.10 4/21/97 Fixed module code so that multiple cards may be detected,
other cleanups. -djb
Andrea Arcangeli: Upgraded to Donald Becker's version 1.12.
*/
static char *version = "3c509.c:1.12 6/4/97 becker@cesdis.gsfc.nasa.gov\n";
/* A few values that may be tweaked. */
/* Time in jiffies before concluding the transmitter is hung. */
#define TX_TIMEOUT (400*HZ/1000)
/* Maximum events (Rx packets, etc.) to handle at each interrupt. */
#define INTR_WORK 10
#include <linux/module.h>
#include <linux/config.h> /* for CONFIG_MCA */
#include <linux/sched.h>
#include <linux/string.h>
#include <linux/interrupt.h>
#include <linux/errno.h>
#include <linux/in.h>
#include <linux/malloc.h>
#include <linux/ioport.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <linux/delay.h> /* for udelay() */
#include <asm/bitops.h>
#include <asm/io.h>
#ifdef EL3_DEBUG
int el3_debug = EL3_DEBUG;
#else
int el3_debug = 2;
#endif
/* To minimize the size of the driver source I only define operating
constants if they are used several times. You'll need the manual
anyway if you want to understand driver details. */
/* Offsets from base I/O address. */
#define EL3_DATA 0x00
#define EL3_CMD 0x0e
#define EL3_STATUS 0x0e
#define EEPROM_READ 0x80
#define EL3_IO_EXTENT 16
#define EL3WINDOW(win_num) outw(SelectWindow + (win_num), ioaddr + EL3_CMD)
/* The top five bits written to EL3_CMD are a command, the lower
11 bits are the parameter, if applicable. */
enum c509cmd {
TotalReset = 0<<11, SelectWindow = 1<<11, StartCoax = 2<<11,
RxDisable = 3<<11, RxEnable = 4<<11, RxReset = 5<<11, RxDiscard = 8<<11,
TxEnable = 9<<11, TxDisable = 10<<11, TxReset = 11<<11,
FakeIntr = 12<<11, AckIntr = 13<<11, SetIntrEnb = 14<<11,
SetStatusEnb = 15<<11, SetRxFilter = 16<<11, SetRxThreshold = 17<<11,
SetTxThreshold = 18<<11, SetTxStart = 19<<11, StatsEnable = 21<<11,
StatsDisable = 22<<11, StopCoax = 23<<11,};
enum c509status {
IntLatch = 0x0001, AdapterFailure = 0x0002, TxComplete = 0x0004,
TxAvailable = 0x0008, RxComplete = 0x0010, RxEarly = 0x0020,
IntReq = 0x0040, StatsFull = 0x0080, CmdBusy = 0x1000, };
/* The SetRxFilter command accepts the following classes: */
enum RxFilter {
RxStation = 1, RxMulticast = 2, RxBroadcast = 4, RxProm = 8 };
/* Register window 1 offsets, the window used in normal operation. */
#define TX_FIFO 0x00
#define RX_FIFO 0x00
#define RX_STATUS 0x08
#define TX_STATUS 0x0B
#define TX_FREE 0x0C /* Remaining free bytes in Tx buffer. */
#define WN0_IRQ 0x08 /* Window 0: Set IRQ line in bits 12-15. */
#define WN4_MEDIA 0x0A /* Window 4: Various transcvr/media bits. */
#define MEDIA_TP 0x00C0 /* Enable link beat and jabber for 10baseT. */
/*
* Must be a power of two (we use a binary and in the
* circular queue)
*/
#define SKB_QUEUE_SIZE 64
struct el3_private {
struct enet_statistics stats;
struct device *next_dev;
/* skb send-queue */
int head, size;
struct sk_buff *queue[SKB_QUEUE_SIZE];
};
static int id_port = 0x100;
static struct device *el3_root_dev = NULL;
static ushort id_read_eeprom(int index);
static ushort read_eeprom(short ioaddr, int index);
static int el3_open(struct device *dev);
static int el3_start_xmit(struct sk_buff *skb, struct device *dev);
static void el3_interrupt(int irq, void *dev_id, struct pt_regs *regs);
static void update_stats(struct device *dev);
static struct enet_statistics *el3_get_stats(struct device *dev);
static int el3_rx(struct device *dev);
static int el3_close(struct device *dev);
static void set_multicast_list(struct device *dev);
int el3_probe(struct device *dev)
{
short lrs_state = 0xff, i;
ushort ioaddr, irq, if_port;
short phys_addr[3];
static int current_tag = 0;
/* First check all slots of the EISA bus. The next slot address to
probe is kept in 'eisa_addr' to support multiple probe() calls. */
if (EISA_bus) {
static int eisa_addr = 0x1000;
while (eisa_addr < 0x9000) {
ioaddr = eisa_addr;
eisa_addr += 0x1000;
/* Check the standard EISA ID register for an encoded '3Com'. */
if (inw(ioaddr + 0xC80) != 0x6d50)
continue;
/* Change the register set to the configuration window 0. */
outw(SelectWindow | 0, ioaddr + 0xC80 + EL3_CMD);
irq = inw(ioaddr + WN0_IRQ) >> 12;
if_port = inw(ioaddr + 6)>>14;
for (i = 0; i < 3; i++)
phys_addr[i] = htons(read_eeprom(ioaddr, i));
/* Restore the "Product ID" to the EEPROM read register. */
read_eeprom(ioaddr, 3);
/* Was the EISA code an add-on hack? Nahhhhh... */
goto found;
}
}
#ifdef CONFIG_MCA
if (MCA_bus) {
mca_adaptor_select_mode(1);
for (i = 0; i < 8; i++)
if ((mca_adaptor_id(i) | 1) == 0x627c) {
ioaddr = mca_pos_base_addr(i);
irq = inw(ioaddr + WN0_IRQ) >> 12;
if_port = inw(ioaddr + 6)>>14;
for (i = 0; i < 3; i++)
phys_addr[i] = htons(read_eeprom(ioaddr, i));
mca_adaptor_select_mode(0);
goto found;
}
mca_adaptor_select_mode(0);
}
#endif
/* Reset the ISA PnP mechanism on 3c509b. */
outb(0x02, 0x279); /* Select PnP config control register. */
outb(0x02, 0xA79); /* Return to WaitForKey state. */
/* Select an open I/O location at 0x1*0 to do contention select. */
for (id_port = 0x100; id_port < 0x200; id_port += 0x10) {
if (check_region(id_port, 1))
continue;
outb(0x00, id_port);
outb(0xff, id_port);
if (inb(id_port) & 0x01)
break;
}
if (id_port >= 0x200) { /* GCC optimizes this test out. */
/* Rare -- do we really need a warning? */
printk(" WARNING: No I/O port available for 3c509 activation.\n");
return -ENODEV;
}
/* Next check for all ISA bus boards by sending the ID sequence to the
ID_PORT. We find cards past the first by setting the 'current_tag'
on cards as they are found. Cards with their tag set will not
respond to subsequent ID sequences. */
outb(0x00, id_port);
outb(0x00, id_port);
for(i = 0; i < 255; i++) {
outb(lrs_state, id_port);
lrs_state <<= 1;
lrs_state = lrs_state & 0x100 ? lrs_state ^ 0xcf : lrs_state;
}
/* For the first probe, clear all board's tag registers. */
if (current_tag == 0)
outb(0xd0, id_port);
else /* Otherwise kill off already-found boards. */
outb(0xd8, id_port);
if (id_read_eeprom(7) != 0x6d50) {
return -ENODEV;
}
/* Read in EEPROM data, which does contention-select.
Only the lowest address board will stay "on-line".
3Com got the byte order backwards. */
for (i = 0; i < 3; i++) {
phys_addr[i] = htons(id_read_eeprom(i));
}
{
unsigned short iobase = id_read_eeprom(8);
if_port = iobase >> 14;
ioaddr = 0x200 + ((iobase & 0x1f) << 4);
}
if (dev && dev->irq > 1 && dev->irq < 16)
irq = dev->irq;
else
irq = id_read_eeprom(9) >> 12;
if (dev && dev->base_addr != 0
&& dev->base_addr != (unsigned short)ioaddr) {
return -ENODEV;
}
/* Set the adaptor tag so that the next card can be found. */
outb(0xd0 + ++current_tag, id_port);
/* Activate the adaptor at the EEPROM location. */
outb((ioaddr >> 4) | 0xe0, id_port);
EL3WINDOW(0);
if (inw(ioaddr) != 0x6d50)
return -ENODEV;
/* Free the interrupt so that some other card can use it. */
outw(0x0f00, ioaddr + WN0_IRQ);
found:
if (dev == NULL) {
dev = init_etherdev(dev, sizeof(struct el3_private));
}
memcpy(dev->dev_addr, phys_addr, sizeof(phys_addr));
dev->base_addr = ioaddr;
dev->irq = irq;
dev->if_port = (dev->mem_start & 0x1f) ? dev->mem_start & 3 : if_port;
request_region(dev->base_addr, EL3_IO_EXTENT, "3c509");
{
const char *if_names[] = {"10baseT", "AUI", "undefined", "BNC"};
printk("%s: 3c509 at %#3.3lx tag %d, %s port, address ",
dev->name, dev->base_addr, current_tag, if_names[dev->if_port]);
}
/* Read in the station address. */
for (i = 0; i < 6; i++)
printk(" %2.2x", dev->dev_addr[i]);
printk(", IRQ %d.\n", dev->irq);
/* Make up a EL3-specific-data structure. */
if (dev->priv == NULL)
dev->priv = kmalloc(sizeof(struct el3_private), GFP_KERNEL);
if (dev->priv == NULL)
return -ENOMEM;
memset(dev->priv, 0, sizeof(struct el3_private));
((struct el3_private *)dev->priv)->next_dev = el3_root_dev;
el3_root_dev = dev;
if (el3_debug > 0)
printk(version);
/* The EL3-specific entries in the device structure. */
dev->open = &el3_open;
dev->hard_start_xmit = &el3_start_xmit;
dev->stop = &el3_close;
dev->get_stats = &el3_get_stats;
dev->set_multicast_list = &set_multicast_list;
/* Fill in the generic fields of the device structure. */
ether_setup(dev);
return 0;
}
/* Read a word from the EEPROM using the regular EEPROM access register.
Assume that we are in register window zero.
*/
static ushort read_eeprom(short ioaddr, int index)
{
outw(EEPROM_READ + index, ioaddr + 10);
/* Pause for at least 162 us. for the read to take place. */
udelay (500);
return inw(ioaddr + 12);
}
/* Read a word from the EEPROM when in the ISA ID probe state. */
static ushort id_read_eeprom(int index)
{
int bit, word = 0;
/* Issue read command, and pause for at least 162 us. for it to complete.
Assume extra-fast 16Mhz bus. */
outb(EEPROM_READ + index, id_port);
/* Pause for at least 162 us. for the read to take place. */
udelay (500);
for (bit = 15; bit >= 0; bit--)
word = (word << 1) + (inb(id_port) & 0x01);
if (el3_debug > 3)
printk(" 3c509 EEPROM word %d %#4.4x.\n", index, word);
return word;
}
static int
el3_open(struct device *dev)
{
int ioaddr = dev->base_addr;
int i;
outw(TxReset, ioaddr + EL3_CMD);
outw(RxReset, ioaddr + EL3_CMD);
outw(SetStatusEnb | 0x00, ioaddr + EL3_CMD);
if (request_irq(dev->irq, &el3_interrupt, 0, "3c509", dev)) {
return -EAGAIN;
}
EL3WINDOW(0);
if (el3_debug > 3)
printk("%s: Opening, IRQ %d status@%x %4.4x.\n", dev->name,
dev->irq, ioaddr + EL3_STATUS, inw(ioaddr + EL3_STATUS));
/* Activate board: this is probably unnecessary. */
outw(0x0001, ioaddr + 4);
/* Set the IRQ line. */
outw((dev->irq << 12) | 0x0f00, ioaddr + WN0_IRQ);
/* Set the station address in window 2 each time opened. */
EL3WINDOW(2);
for (i = 0; i < 6; i++)
outb(dev->dev_addr[i], ioaddr + i);
if (dev->if_port == 3)
/* Start the thinnet transceiver. We should really wait 50ms...*/
outw(StartCoax, ioaddr + EL3_CMD);
else if (dev->if_port == 0) {
/* 10baseT interface, enabled link beat and jabber check. */
EL3WINDOW(4);
outw(inw(ioaddr + WN4_MEDIA) | MEDIA_TP, ioaddr + WN4_MEDIA);