原始套接字来两种类型:1)在IP头中使用预定义的协议,ICMP
2) 在IP头中使用自定义的协议
创建可以是 socket / WSASocket 只不过要将套接字设置为 SOCK_RAW
注意 WIDNOWS XP SP2 已经不再支持原始TCP数据的发送了
下面的内容MSDB没有显示完全 ,小菜在各种网络搜索得到:
int setsockopt(
__in SOCKET s, //<span style="color: rgb(51, 51, 51); font-family: Arial; font-size: 14.44444465637207px; line-height: 26px;">s(套接字): 指向一个打开的套接口描述字</span>
__in int level,
__in int optname,
__in const char *optval,
__in int optlen
);
level:(级别): 指定选项代码的类型。 4种类型:
SOL_SOCKET: 基本套接口
IPPROTO_IP: IPv4套接口
IPPROTO_IPV6: IPv6套接口
IPPROTO_TCP: TCP套接口
optname(选项名):选项名称 每种level 对应几种选项名
SOL_SOCKET:
SO_BROADCAST 允许发送广播数据 int
适用於 UDP socket。其意义是允许 UDP socket 「广播」(broadcast)讯息到网路上。
SO_DEBUG 允许调试 int
SO_DONTROUTE 不查找路由 int
SO_ERROR 获得套接字错误 int
SO_KEEPALIVE 保持连接 int
检测对方主机是否崩溃,避免(服务器)永远阻塞于TCP连接的输入。 设置该选项后,如果2小时内在此套接口的任一方向都没有数据交换,TCP就自动给对方 发一个保持存活探测分节(keepalive probe)。这是一个对方必须响应的TCP分节.它会导致以下三种情况: 对方接收一切正常:以期望的 ACK响应。2小时后,TCP将发出另一个探测分节。 对方已崩溃且已重新启动:以RST响应。套接口的待处理错误被置为ECONNRESET,套接 口本身则被关闭。 对方无任何响应:源自berkeley的TCP发送另外8个探测分节,相隔75秒一个,试图得到 一个响应。在发出第一个探测分节11分钟15秒后若仍无响应就放弃。套接口的待处理错 误被置为ETIMEOUT,套接口本身则被关闭。如ICMP错误是“host unreachable (主机不 可达)”,说明对方主机并没有崩溃,但是不可达,这种情况下待处理错误被置为 EHOSTUNREACH。
SO_DONTLINGER 若为真,则SO_LINGER选项被禁止。
SO_LINGER 延迟关闭连接 struct linger
上面这两个选项影响close行为
选项 间隔 关闭方式 等待关闭与否
SO_DONTLINGER 不关心 优雅 否
SO_LINGER 零 强制 否
SO_LINGER 非零 优雅 是
若 设置了SO_LINGER(亦即linger结构中的l_onoff域设为非零,参见2.4,4.1.7和4.1.21各节),并设置了零超时间隔,则 closesocket()不被阻塞立即执行,不论是否有排队数据未发送或未被确认。这种关闭方式称为“强制”或“失效”关闭,因为套接口的虚电路立即被 复位,且丢失了未发送的数据。在远端的recv()调用将以WSAECONNRESET出错。
若设置了SO_LINGER并确定了非零的超时间 隔,则closesocket()调用阻塞进程,直到所剩数据发送完毕或超时。这种关闭称为“优雅的”关闭。请注意如果套接口置为非阻塞且 SO_LINGER设为非零超时,则closesocket()调用将以WSAEWOULDBLOCK错误返回。
若在一个流类套接口上设置了 SO_DONTLINGER(也就是说将linger结构的l_onoff域设为零,则 closesocket()调用立即返回。但是,如果可能,排队的数据将在套接口关闭前发送。请注意,在这种情况下WINDOWS套接口实现将在一段不确 定的时间内保留套接口以及其他资源,这对于想用所以套接口的应用程序来说有一定影响。
IPPROTO_IP:
IP_HDRINCL 在数据包中包含IP首部 int
这个选项常用于黑客技术中,隐藏自己的IP地址
IP_OPTINOS IP首部选项 int
IP_TOS 服务类型
IP_TTL 生存时间 int
TCP_NODELAY
TCP_CORK
CP_NODELAY 和 TCP_CORK,
这 两个选项都对网络连接的行为具有重要的作用。许多UNIX系统都实现了TCP_NODELAY选项,但是,TCP_CORK则是Linux系统所独有的而 且相对较新;它首先在内核版本2.4上得以实现。此外,其他UNIX系统版本也有功能类似的选项,值得注意的是,在某种由BSD派生的系统上的 TCP_NOPUSH选项其实就是TCP_CORK的一部分具体实现。
TCP_NODELAY和TCP_CORK基本上控制了包的 “Nagle化”,Nagle化在这里的含义是采用Nagle算法把较小的包组装为更大的帧。John Nagle是Nagle算法的发明人,后者就是用他的名字来命名的,他在1984年首次用这种方法来尝试解决福特汽车公司的网络拥塞问题(欲了解详情请参 看IETF RFC 896)。他解决的问题就是所谓的silly window syndrome ,中文称“愚蠢窗口症候群”,具体含义是,因为普遍终端应用程序每产生一次击键操作就会发送一个包,而典型情况下一个包会拥有一个字节的数据载荷以及40 个字节长的包头,于是产生4000%的过载,很轻易地就能令网络发生拥塞,。 Nagle化后来成了一种标准并且立即在因特网上得以实现。它现在已经成为缺省配置了,但在我们看来,有些场合下把这一选项关掉也是合乎需要的。
现 在让我们假设某个应用程序发出了一个请求,希望发送小块数据。我们可以选择立即发送数据或者等待产生更多的数据然后再一次发送两种策略。如果我们马上发送 数据,那么交互性的以及客户/服务器型的应用程序将极大地受益。例如,当我们正在发送一个较短的请求并且等候较大的响应时,相关过载与传输的数据总量相比 就会比较低,而且,如果请求立即发出那么响应时间也会快一些。以上操作可以通过设置套接字的TCP_NODELAY选项来完成,这样就禁用了Nagle算 法。
另外一种情况则需要我们等到数据量达到最大时才通过网络一次发送全部数据,这种数据传输方式有益于大量数据的通信性能,典型的应用就是文件服 务器。应用 Nagle算法在这种情况下就会产生问题。但是,如果你正在发送大量数据,你可以设置TCP_CORK选项禁用Nagle化,其方式正好同 TCP_NODELAY相反(TCP_CORK 和 TCP_NODELAY 是互相排斥的)。下面就让我们仔细分析下其工作原理。
假设应用程序 使用sendfile()函数来转移大量数据。应用协议通常要求发送某些信息来预先解释数据,这些信息其实就是报头内容。典型情况下报头很小,而且套接字 上设置了TCP_NODELAY。有报头的包将被立即传输,在某些情况下(取决于内部的包计数器),因为这个包成功地被对方收到后需要请求对方确认。这 样,大量数据的传输就会被推迟而且产生了不必要的网络流量交换。
但是,如果我们在套接字上设置了TCP_CORK(可以比喻为在管道上插入 “塞子”)选项,具有报头的包就会填补大量的数据,所有的数据都根据大小自动地通过包传输出去。当数据传输完成时,最好取消TCP_CORK 选项设置给连接“拔去塞子”以便任一部分的帧都能发送出去。这同“塞住”网络连接同等重要。
总而言之,如果你肯定能一起发送多个数据集合(例如HTTP响应的头和正文),那么我们建议你设置TCP_CORK选项,这样在这些数据之间不存在延迟。能极大地有益于WWW、FTP以及文件服务器的性能,同时也简化了你的工作。示例代码如下:
intfd, on = 1;
…
/* 此处是创建套接字等操作,出于篇幅的考虑省略*/
…
setsockopt (fd, SOL_TCP, TCP_CORK, &on, sizeof (on)); /* cork */
write (fd, …);
fprintf (fd, …);
sendfile (fd, …);
write (fd, …);
sendfile (fd, …);
…
on = 0;
setsockopt (fd, SOL_TCP, TCP_CORK, &on, sizeof (on)); /* 拔去塞子 */
optval(选项值):是一个指向变量的指针 类型:整形,套接口结构, 其他结构类型:linger{}, timeval{ }
optlen(选项长度) :optval 的大小
int WSAIoctl(
__in SOCKET s,
__in DWORD dwIoControlCode,
__in LPVOID lpvInBuffer,
__in DWORD cbInBuffer,
__out LPVOID lpvOutBuffer,
__in DWORD cbOutBuffer,
__out LPDWORD lpcbBytesReturned,
__in LPWSAOVERLAPPED lpOverlapped,
__in LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine
)
dwIoControlCode:
SIO_RCVALL Enables a socket to receive all IPv4 or IPv6 packets passing throuigh a network interface
下面学习 IP TCP 结构:
版本号(Version):长度4比特。标识目前采用的IP协议的版本号。一般的值为0100(IPv4),IPv6的值(0110)。
IP包头长度(Header Length):长度4比特。这个字段的作用是为了描述IP包头的长度,因为在IP包头中有变长的可选部分。该部分占4个bit位,单位为32bit(4个字节),即本区域值= IP头部长度(单位为bit)/(8*4),因此,一个IP包头的长度最长为“1111”,即15*4=60个字节。IP包头最小长度为20字节。对于标准ipv4报头,这个字段的值肯定是20/4=5(10进制)=0101(2进制)。
服务类型(Type of Service):长度8比特。这个子段可以拆分成两个部分:Precedence和TOS。TOS目前不太使用。而Precedence则用于QOS("Quality of Service",中文名为"服务质量")应用。(TOS字段的详细描述RFC 1340 1349)。
IP包总长(Total Length):长度16比特。以字节为单位计算的IP包的长度 (包括头部和数据),所以IP包最大长度65535字节。
标识符(Identifier):长度16比特。该字段和Flag和Fragment Offest字段联合使用,对大的上层数据包进行分段(fragment)操作。
标记(Flag):长度3比特。该字段第一位不使用。第二位是DF位,DF位设为1时表明路由器不能对该上层数据包分段。如果一个上层数据包无法在不分段的情况下进行转发,则路由器会丢弃该上层数据包并返回一个错误信息。第三位是MF位,当路由器对一个上层数据包分段,则路由器会在最后一个分段的IP包的包头中将MF位设为0,其余IP包的包头中将MF设为1。
Bit 0: reserved, must be zero
Bit 1: (DF) 0 = May Fragment, 1 = Don't Fragment.
Bit 2: (MF) 0 = Last Fragment, 1 = More Fragments.
分段序号(Fragment Offset):长度13比特。该字段对包含分段的上层数据包的IP包赋予序号。由于IP包在网络上传送的时候不一定能按顺序到达,这个字段保证了目标路由器在接受到IP包之后能够还原分段的上层数据包。到某个包含分段的上层数据包的IP包在传送是丢失,则整个一系列包含分段的上层数据包的IP包都会被要求重传。
生存时间(Time to Live):长度8比特。当IP包进行传送时,先会对该字段赋予某个特定的值。当IP包经过每一个沿途的路由器的时候,每个沿途的路由器会将IP包的TTL值减少1。如果TTL减少为0,则该IP包会被丢弃。这个字段可以防止由于故障而导致IP包在网络中不停被转发。
协议(Protocol):长度8比特。标识了上层所使用的协议。
头部校验(Header Checksum):长度16比特,由于IP包头是变长的,所以提供一个头部校验来保证IP包头中信息的正确性。
源IP地址(Source IP Addresses):长度16比特。标识了这个IP包的源IP地址。
目的IP地址(Destination IP Address):长度16比特。标识了这个IP包的目的IP地址。
可选项(Options):这是一个可变长的字段,长度为0或32bit的整倍数,最大320bit,如果不足则填充到满。该字段由起源设备根据需要改写。可选项目包含以下内容:
松散源路由(Loose source routing):给出一连串路由器接口的IP地址。IP包必须沿着这些IP地址传送,但是允许在相继的两个IP地址之间跳过多个路由器。
严格源路由(Strict source routing):给出一连串路由器接口的IP地址。IP包必须沿着这些IP地址传送,如果下一跳不在IP地址表中则表示发生错误。
路由记录(Record route):当IP包离开每个路由器的时候记录路由器的出站接口的IP地址。
时间戳(Timestamps):当IP包离开每个路由器的时候记录时间。
填充(Padding):因为IP包头长度(Header Length)部分的单位为32bit,所以IP包头的长度必须为32bit的整数倍。因此,在可选项后面,IP协议会填充若干个0,以达到32bit的整数倍。
源端口号(Source Port):16位,标识主机上发起传送的应用程序;
目的端口(Destonation Port):16位,标识主机上传送要到达的应用程序。
源端,目的端的端口号,用于寻找发端和收端应用进程。这两个值加上IP首部中的源端IP地址和目的端IP地址唯一确定一个TCP连接。一个IP地址和一个端口号有时也称为一个插口(Socket),插口对(Socket Pair)(包含客户IP地址、客户端口号、服务器 IP地址和服务器端口号的四元组)可唯一确定互联网络中每个TCP连接的双方。IP+TCP端口唯一确定一个TCP连接。
TCP协议通过使用"端口"来标识源端和目标端的应用进程。端口号可以使用0到65535之间的任何数字。在收到服务请求时,操作系统动态地为客户端的应用程序分配端口号。在服务器端,每种服务在"众所周知的端口"(Well-Know Port)为用户提供服务。
序列码(Sequence Number):32位,当SYN出现,序列码实际上是初始序列码(ISN),而第一个数据字节是ISN+1。用来标识从TCP源端向TCP目标端发送的数据字节流,它表示在这个报文段中的第一个数据字节。
确认码(Acknowledgment Numbwe):32位,如果设置了ACK控制位,这个值表示一个准备接收的包的序列码,只有ACK标志为1时,确认号字段才有效。它包含目标端所期望收到源端的下一个数据字节。
数据偏移量(Data Offset):4位,也就是头部长度,指出TCP负载(数据)的开始位置。以4字节为单位,如"0101"表示20字节位置的数据为负载开始,也就是头部长度为20字节。
保留(Reserved):6位,这些位必须是0;
控制标记(Contral Flag):6位, 2表示第一次握手 18表示 第二次握手
URG(Urgent data):紧急指针(urgent pointer)有效。如果URG为1,表示这是一个携有紧急资料的封包。
ACK(Acknowledgment field significant):确认序号有效。如果ACK为1,表示此封包属于一个要回应的封包。一般都会为1。
PSH(Push function):接收方应该尽快将这个报文段交给应用层。如果PSH为1,此封包所携带的数据会直接上传给上层应用程序而无需经过TCP处理。
RST(Reset):重建连接。如果RST为1,要求重传。表示要求重新设定封包再重新传递。
SYN(Synchronize sequence number):发起一个连接。如果SYN为1,表示要求双方进行同步沟通。
FIN(Finish-No more data for sender):释放一个连接。如果FIN为1,表示传送结束,然後双方发出结束回应进而正式终止一个TCP传送过程。
窗口(Window):16位,接收窗口大小。此字段用来进行流量控制,单位为字节数,这个值是本机期望一次接收的字节数。这里一般称为“滑动视窗(Sliding Window)”。
正如您刚才看到的TCP封包会通过SQN和ACK序号来确保传送的正确性,但如果每一个封包都要等上一个封包的回应才被发送出去的话实在是太慢和难以接受的。这样我们可以利用Sliding Window在传送两端划分出一个范围,规定出可以一次性发送的最大封包数目。
当TCP传送建立起来之後两端都会将window的设定值还原到初始值比方说每次传送3个封包。然后发送端就一次发送三个封包出去,然后视窗则会往後移动三个封包填补发送出去之封包的空缺。如果接收端够顺利也能一次处理接收下来的三个封包的话,就会告诉发送端的window值为3,但如果接收端太忙或是其它因素影响暂时只能处理两个封包,那么在视窗里面就剩下一个封包,然后就会告诉发送端window值为2。这个时候发送端就只送出两个封包而视窗就会往後移动两个封包填补发送出去的空缺。
校验位(Checksum):16位,对整个TCP报文段,即TCP头部和TCP数据进行校验和计算,并由目标端进行验证。
当资料要传送出去的时候发送端会计算好封包资料大小然後得出这个检验值封包一起发送当接收端收到封包之後会再对资料大小进行计算看看是否和检验值一致如果结果不相称则被视为残缺封包会要求对方重发该个封包。
紧急指针(Urgent Pointer):16位,它是一个偏移量。指向后面是优先数据的字节,紧急指针指出在本报文段中的紧急数据的最后一个字节的序号,和序号字段中的值相加表示紧急数据最后一个字节的序号。
还记得刚才讲到Control Flag的时候我们提到一个URG的标记吗如果URG被设定为1的时候,这里就会指示出紧急资料所在位置。不过这种情形非常少见,例如:当资料流量超出频宽的时候系统要求网路主机暂缓发送资料所有主机收到这样的信息都需要优先处理。
选项(Options):长度不定;但长度必须以字节记;选项的具体内容我们结合具体命令来看。
这个选项也比较少用。当那些需要使用同步动作的程式如Telnet要处理好终端的交互模式就会使用到option来指定资料封包的大小因为telnet使用的资料封包都很少但又需要即时回应。
填充(Padding):不定长,填充的内容必须为0,它是为了保证包头的结合和数据的开始处偏移量能够被32整除。
SYN(synchronous)是TCP/IP建立连接时使用的握手信号。在客户机和服务器之间建立正常的TCP网络连接时,客户机首先发出一个SYN消息,服务器使用SYN+ACK应答表示接收到了这个消息,最后客户机再以ACK消息响应。这样在客户机和服务器之间才能建立起可靠的TCP连接,数据才可以在客户机和服务器之间传递。
SYN攻击属于DDoS攻击的一种,它利用TCP协议缺陷,通过发送大量的半连接请求,耗费CPU和内存资源。SYN攻击除了能影响主机外,还可以危害路由器、防火墙等网络系统,事实上SYN攻击并不管目标是什么系统,只要这些系统打开TCP服务就可以实施。服务器接收到连接请求(syn= j),将此信息加入未连接队列,并发送请求包给客户(syn=k,ack=j+1),此时进入SYN_RECV状态。当服务器未收到客户端的确认包时,重发请求包,一直到超时,才将此条目从未连接队列删除。配合IP欺骗,SYN攻击能达到很好的效果,通常,客户端在短时间内伪造大量不存在的IP地址,向服务器不断地发送syn包,服务器回复确认包,并等待客户的确认,由于源地址是不存在的,服务器需要不断的重发直至超时,这些伪造的SYN包将长时间占用未连接队列,正常的SYN请求被丢弃,目标系统运行缓慢,严重者引起网络堵塞甚至系统瘫痪。
检测SYN攻击:
当你在服务器上看到大量的半连接状态时,特别是源IP地址是随机的,基本上可以断定这是一次SYN攻击。我们使用系统自带的netstat 工具来检测SYN攻击:
# netstat -n -p TCP
-n 以数字形式显示地址和端口号。
-p proto 显示 proto 指定的协议的连接;proto 可以是下列任
何一个: TCP、UDP、TCPv6 或 UDPv6。如果与 -s 选
项一起用来显示每个协议的统计,proto 可以是下列任
何一个: IP、IPv6、ICMP、ICMPv6、TCP、TCPv6、UDP
TCP 192.168.0.101:27309 192.168.0.101:168 SYN_SENT
TCP 192.168.0.101:27310 192.168.0.101:169 SYN_SENT
TCP 192.168.0.101:27311 192.168.0.101:170 SYN_SENT
TCP 192.168.0.101:27312 192.168.0.101:171 SYN_SENT
TCP 192.168.0.101:27313 192.168.0.101:172 SYN_SENT
TCP 192.168.0.101:27314 192.168.0.101:173 SYN_SENT
TCP 192.168.0.101:27315 192.168.0.101:174 SYN_SENT
TCP 192.168.0.101:27316 192.168.0.101:175 SYN_SENT
TCP 192.168.0.101:27317 192.168.0.101:176 SYN_SENT
TCP 192.168.0.101:27318 192.168.0.101:177 SYN_SENT
TCP 192.168.0.101:27319 192.168.0.101:178 SYN_SENT
TCP 192.168.0.101:27320 192.168.0.101:179 SYN_SENT
TCP 192.168.0.101:27321 192.168.0.101:180 SYN_SENT
TCP 192.168.0.101:27322 192.168.0.101:181 SYN_SENT
TCP 192.168.0.101:27323 192.168.0.101:182 SYN_SENT
TCP 192.168.0.101:27324 192.168.0.101:183 SYN_SENT
TCP 192.168.0.101:27325 192.168.0.101:184 SYN_SENT
TCP 192.168.0.101:27326 192.168.0.101:185 SYN_SENT
在WINDOWS系统中是SYN_RECEIVED状态,源IP地址都是随机的,表明这是一种带有IP欺骗的SYN攻击。
netstat -n -p TCP | find "192.168.0.101" 查看全部链接
防范技术:
关于SYN攻击防范技术,人们研究得比较早。归纳起来,主要有两大类,一类是通过防火墙、路由器等过滤网关防护,另一类是通过加固TCP/IP协议栈防范
丰富带宽资源
不难看出syn攻击消耗带宽资源所以要想防御synflood一个丰富的带宽资源是必要的,通常的流量攻击,攻击者也是利用肉鸡的带宽资源来达到攻击堵死网络的,所以这个是一个前提
防火墙
利用防火墙来进行防护攻击是目前最有效的方法
SYN代码:
#include "stdafx.h"
#include <string.h>
#include <iostream>
#include <winsock2.h>
#pragma comment(lib,"WS2_32")
#include <WS2TCPIP.h>
#include <time.h>
// New WSAIoctl Options
#define SIO_RCVALL _WSAIOW(IOC_VENDOR,1)
#define SIO_RCVALL_MCAST _WSAIOW(IOC_VENDOR,2)
#define SIO_RCVALL_IGMPMCAST _WSAIOW(IOC_VENDOR,3)
#define SIO_KEEPALIVE_VALS _WSAIOW(IOC_VENDOR,4)
#define SIO_ABSORB_RTRALERT _WSAIOW(IOC_VENDOR,5)
#define SIO_UCAST_IF _WSAIOW(IOC_VENDOR,6)
#define SIO_LIMIT_BROADCASTS _WSAIOW(IOC_VENDOR,7)
#define SIO_INDEX_BIND _WSAIOW(IOC_VENDOR,8)
#define SIO_INDEX_MCASTIF _WSAIOW(IOC_VENDOR,9)
#define SIO_INDEX_ADD_MCAST _WSAIOW(IOC_VENDOR,10)
#define SIO_INDEX_DEL_MCAST _WSAIOW(IOC_VENDOR,11)
typedef struct _iphdr
{
unsigned char h_lenver; //4位首部长度+4位IP版本号
unsigned char tos; //8位服务类型TOS
unsigned short total_len; //16位总长度(字节)
unsigned short ident; //16位标识
unsigned short frag_and_flags; //3位标志位
unsigned char ttl; //8位生存时间 TTL
unsigned char proto; //8位协议 (TCP, UDP 或其他)
unsigned short checksum; //16位IP首部校验和
unsigned int sourceIP; //32位源IP地址
unsigned int destIP; //32位目的IP地址
}IP_HEADER;
typedef struct _tcphdr //定义TCP首部
{
USHORT th_sport; //16位源端口
USHORT th_dport; //16位目的端口
unsigned int th_seq; //32位序列号
unsigned int th_ack; //32位确认号
unsigned char th_lenres; //4位首部长度/6位保留字
unsigned char th_flag; //6位标志位
USHORT th_win; //16位窗口大小
USHORT th_sum; //16位校验和
USHORT th_urp; //16位紧急数据偏移量
}TCP_HEADER;
struct //定义TCP伪首部
{
unsigned long saddr; //源地址
unsigned long daddr; //目的地址
char mbz;
char ptcl; //协议类型
unsigned short tcpl; //TCP长度
}psd_header;
SOCKET sockRaw = INVALID_SOCKET,sockListen = INVALID_SOCKET;
int BeginPort,EndPort;
char *HOST;
int iErrorCode;
struct sockaddr_in dest;
BOOL StopScan = FALSE;
#define SEQ 0x28376839
void CheckSockError(int ierror,char *pErrorMsg)
{
if (ierror == SOCKET_ERROR)
{
printf("%s ErrorCode:%d\n",pErrorMsg,ierror);
closesocket(sockRaw);
ExitProcess(-1);
}
}
void meesage()
{
printf("\t-------syn by panda--------------------------\n");
printf("\t-------syn_test [ip] [port-port]-------------\n");
printf("\t-------example: syn_test 127.0.0.1 1-1000----\n");
}
BOOL DecodeIPHeader(char *RecvBuf)
{
IP_HEADER *iphdr = (IP_HEADER*)RecvBuf;
unsigned short iphdrlen = sizeof(unsigned long) * (iphdr->h_lenver&0xf);
TCP_HEADER* tcphdr = (TCP_HEADER*)(RecvBuf + iphdrlen);
if (iphdr->sourceIP != dest.sin_addr.s_addr)
{
return FALSE;
}
if (ntohl(tcphdr->th_ack) != (SEQ+1) && ntohl(tcphdr->th_ack) != SEQ)
{
return FALSE;
}
if (tcphdr->th_flag == 18)
{
printf("\t%d open \n",ntohs(tcphdr->th_sport));
return true;
}
return FALSE;
}
DWORD WINAPI RecvThread(LPVOID para)//接受数据线程
{
//监听本机套接字
sockListen = socket(AF_INET,SOCK_RAW,IPPROTO_IP);
CheckSockError(sockListen,"RecvThread` socket");
BOOL bOpt =true;
iErrorCode = setsockopt(sockRaw,IPPROTO_IP,IP_HDRINCL,(char*)&bOpt,sizeof(bOpt));
CheckSockError(iErrorCode,"RecvThread setsockopt");
//获得本地IP
char LocalName[256];
gethostname(LocalName,sizeof(LocalName));
struct hostent * my_hostent = gethostbyname(LocalName);
SOCKADDR_IN sa;
memcpy(&sa.sin_addr.S_un.S_addr,my_hostent->h_addr_list[0],my_hostent->h_length);
sa.sin_family = AF_INET;
sa.sin_port = htons(8000);
iErrorCode = bind(sockListen,(sockaddr*)&sa,sizeof(sa));
CheckSockError(iErrorCode,"bind");
//设置SOCK_RAW为SIO_RCVALL,以便接收所有的IP包
DWORD dwBufferInLen = 1;
DWORD dwBufferLen[10];
DWORD dwBytesReturned = 0;
iErrorCode = WSAIoctl(sockListen,SIO_RCVALL,\
&dwBufferInLen,sizeof(dwBufferInLen),\
&dwBufferLen,sizeof(dwBufferLen),&dwBytesReturned,NULL,NULL);
CheckSockError(iErrorCode,"RecvThread WSAIoctl");
char RecvBuf[65535]={0};
memset(RecvBuf,0,sizeof(RecvBuf));
while (1)//循环监听 本地 数据包
{
iErrorCode = recv(sockListen,RecvBuf,sizeof(RecvBuf),0);
//CheckSockError(iErrorCode,"RecvThread recv");
DecodeIPHeader(RecvBuf);
if (StopScan == TRUE)
{
closesocket(sockListen);
return 0;
}
}
return 0;
}
USHORT CalcCheckSum(USHORT *buffer,int size)
{
unsigned long cksum = 0;
while (size > 1)
{
cksum += *buffer++;
size -= sizeof(USHORT);
}
if (size)
{
cksum += *(USHORT*)buffer;
}
cksum = (cksum >> 16) + (cksum &0xffff);
cksum += (cksum >>16);
return (USHORT)(~cksum);
}
int play=0;
void progressbar(void)
{
// 进度条
char *plays[12]=
{
" | ",
" / ",
" - ",
" \\ ",
" | ",
" / ",
" - ",
" \\ ",
" | ",
" / ",
" - ",
" \\ ",
};
printf(" =%s=\r", plays[play]);
play = (play==11)?0:play+1;
Sleep(2);
}
int main(int argc, char* argv[])
{
char *p;
if (argc != 3)
{
meesage();
return 0;
}
p = argv[2];
if (strstr(argv[2],"-"))
{
BeginPort = atoi(argv[2]);
while (*p)
{
if (*(p++) == '-')
{
break;
}
}
EndPort = atoi(p);
if (BeginPort <1 || BeginPort>65535 ||EndPort<1|| EndPort >65535|| EndPort<EndPort)
{
meesage();
return 0;
}
}
HOST = argv[1];
meesage();
WSADATA wsadata;
iErrorCode = WSAStartup(MAKEWORD(2,2),&wsadata);
CheckSockError(iErrorCode, "WsaStartup()");
//
sockRaw = socket(AF_INET,SOCK_RAW,IPPROTO_IP);
CheckSockError(sockRaw, "socket()");
//设置IP头操作选项 是发送TCP报文的套接字
BOOL bOpt = true;
setsockopt(sockRaw,IPPROTO_IP,IP_HDRINCL,(char*)&bOpt,sizeof(bOpt));
CheckSockError(sockRaw,"setsockopt()");
//获得目标主机IP ,通过发送主机 第一次握手包
memset(&dest,0,sizeof(dest));
dest.sin_family = AF_INET;
dest.sin_port = ntohs(BeginPort);
struct hostent *my_hostent;
if ((dest.sin_addr.s_addr = inet_addr(HOST)) == INADDR_NONE)
{
if ((my_hostent = gethostbyname(HOST)) == NULL)
{
memcpy(&(dest.sin_addr),my_hostent->h_addr_list[0],my_hostent->h_length);
dest.sin_family = my_hostent->h_addrtype;
printf("dest.sin_addr = %s",inet_ntoa(dest.sin_addr));
}
else
{
CheckSockError(SOCKET_ERROR,"gethostbyname");
}
}
//
sockListen = socket(AF_INET , SOCK_RAW , IPPROTO_IP);
CheckSockError(sockListen, "socket");
//获得本地IP
SOCKADDR_IN sa;
unsigned char LocalName[256];
struct hostent *hp;
iErrorCode = gethostname((char*)LocalName, sizeof(LocalName)-1);
CheckSockError(iErrorCode, "gethostname()");
if((hp = gethostbyname((char*)LocalName)) == NULL)
{
CheckSockError(SOCKET_ERROR, "gethostbyname()");
}
memcpy(&sa.sin_addr.S_un.S_addr, hp->h_addr_list[0],hp->h_length);
sa.sin_family = AF_INET;
sa.sin_port = htons(8000);
iErrorCode = bind(sockListen, (PSOCKADDR)&sa, sizeof(sa));
CheckSockError(iErrorCode, "bind");
//开启本地监听 第二次握手包 线程
HANDLE Thread = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)RecvThread,0,0,0);
Sleep(1000);
//
//发送第一次握手包
IP_HEADER ip_header;
TCP_HEADER tcp_header;
//填充IP首部 一个IP包头的长度最长为“1111”,即15*4=60个字节。IP包头最小长度为20字节。对于标准ipv4报头,这个字段的值肯定是20/4=5(10进制)=0101(2进制)。
ip_header.h_lenver = (4<<4 | sizeof(ip_header)/sizeof(unsigned long));
//高四位IP版本号,低四位首部长度
ip_header.total_len=htons(sizeof(IP_HEADER)+sizeof(TCP_HEADER)); //16位总长度(字节)
ip_header.ident=1; //16位标识
ip_header.frag_and_flags=0; //3位标志位
ip_header.ttl=128; //8位生存时间TTL
ip_header.proto=IPPROTO_TCP; //8位协议(TCP,UDP…)
ip_header.checksum=0; //16位IP首部校验和
ip_header.sourceIP=sa.sin_addr.s_addr; //32位源IP地址
ip_header.destIP=dest.sin_addr.s_addr; //32位目的IP地址
//填充TCP首部
tcp_header.th_sport = htons(8000);//源端口号
tcp_header.th_lenres = (sizeof(TCP_HEADER)/4<<4 | 0);//TCP长度和保留位
tcp_header.th_win=htons(16384);
//填充TCP伪首部(用于计算校验和,并不真正发送)
psd_header.saddr=ip_header.sourceIP;
psd_header.daddr=ip_header.destIP;
psd_header.mbz=0;
psd_header.ptcl=IPPROTO_TCP;
psd_header.tcpl=htons(sizeof(tcp_header));
printf("\n");
printf("Scaning %s port : %d-%d\n",HOST,BeginPort,EndPort);
clock_t start,end;//程序运行的起始和结束时间
start=clock();//开始计时
//开始发包~~~~
char SendBuf[128] = {0};
for (;BeginPort < EndPort;BeginPort++)
{
// 进度条
progressbar();
tcp_header.th_dport = htons(BeginPort); //目的端口号
tcp_header.th_ack=0; //ACK序列号置为0
tcp_header.th_flag=2; //SYN 标志
tcp_header.th_seq=htonl(SEQ); //SYN序列号
tcp_header.th_urp=0; //偏移
tcp_header.th_sum=0; //校验和
//计算TCP校验和 即TCP头部和TCP数据进行校验和计算,并由目标端进行验证。
memcpy(SendBuf,&psd_header,sizeof(psd_header));
memcpy(SendBuf+sizeof(psd_header), &tcp_header,sizeof(tcp_header));
tcp_header.th_sum = CalcCheckSum((USHORT *)SendBuf,sizeof(psd_header)+sizeof(tcp_header));
//计算IP校验和 IP包头是变长的,所以提供一个头部校验来保证IP包头中信息的正确性。
memcpy(SendBuf,&ip_header,sizeof(ip_header));
memcpy(SendBuf+sizeof(ip_header),&tcp_header,sizeof(tcp_header));
memset(SendBuf+sizeof(ip_header)+sizeof(tcp_header),0,4);
ip_header.checksum = CalcCheckSum((USHORT *)SendBuf,sizeof(ip_header)+sizeof(tcp_header));
//填充发送缓冲区
memcpy(SendBuf,&ip_header,sizeof(ip_header));
//发送TCP报文
iErrorCode=sendto(sockRaw,SendBuf,sizeof(ip_header)+sizeof(tcp_header),0,\
(struct sockaddr*) &dest,
sizeof(dest));
CheckSockError(iErrorCode, "sendto()");
}
//结束发包~~~~
end=clock();//计时结束
StopScan = TRUE;
printf("Closeing Scan.....\n");
WaitForSingleObject(Thread,5000);
CloseHandle(Thread);
printf("Cost time: %f Sec",(float)(end-start) / CLOCKS_PER_SEC/*1000*/);
if (sockRaw != INVALID_SOCKET)
{
closesocket(sockRaw);
}
if (sockListen!= INVALID_SOCKET)
{
closesocket(sockListen);
}
WSACleanup();
return 0;
}